```ts
export function applyCharges(caps: Record<string, [number, number]>, charges: [string, string, number][]): { left: Record<string, number>; refused: [string, string][] } {
  const left: Record<string, number> = {};
  const refused: [string, string][] = [];

  for (const [id, bucket, amount] of charges) {
    if (!Number.isInteger(amount) || amount <= 0) {
      throw new Error("Invalid charge amount");
    }

    const capInfo = caps[bucket];
    if (!capInfo) {
      refused.push([id, "unknown"]);
      continue;
    }

    const [allowance, singleCharge] = capInfo;
    if (amount > singleCharge) {
      refused.push([id, "single"]);
      continue;
    }
    if (allowance < amount) {
      refused.push([id, "cap"]);
      continue;
    }

    left[bucket] = (left[bucket] || 0) + amount;
  }

  return { left, refused };
}
```