```ts
/**
 * Implements a token-bucket rate limiter to replay a request log.
 * @param capacity - The maximum number of tokens the bucket can hold.
 * @param refill - The number of tokens added per time unit.
 * @param requests - An array of [time, cost] pairs representing request arrivals and costs.
 * @returns An array of labels "grant" or "refuse" corresponding to each request.
 */
export function tokenBucket(capacity: number, refill: number, requests: number[][]): string[] {
    if (!Number.isInteger(capacity) || capacity <= 0) {
        throw new Error("Capacity must be a positive integer.");
    }
    if (!Number.isInteger(refill) || refill < 0) {
        throw new Error("Refill rate must be a non-negative integer.");
    }

    let tokens = capacity;
    let lastTime = -1;

    return requests.map(([time, cost]) => {
        if (!Number.isInteger(time) || time < 0) {
            throw new Error("Arrival time must be a non-negative integer.");
        }
        if (!Number.isInteger(cost) || cost <= 0) {
            throw new Error("Cost must be a positive integer.");
        }
        if (time < lastTime) {
            throw new Error("Arrival times must not go backwards.");
        }

        // Accrue tokens since the last request
        const timePassed = Math.max(0, time - lastTime);
        tokens += refill * timePassed;
        tokens = Math.min(tokens, capacity);

        lastTime = time;

        if (tokens >= cost) {
            tokens -= cost;
            return "grant";
        } else {
            return "refuse";
        }
    });
}
```