```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 = 0;

  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 = time - lastTime;
    tokens += timePassed * refill;
    tokens = Math.min(tokens, capacity);

    lastTime = time;

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