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

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

    if (!caps.hasOwnProperty(bucket)) {
      result.refused.push([id, "unknown"]);
      continue;
    }

    const [allowance, maxSingleCharge] = caps[bucket];
    if (amount > maxSingleCharge) {
      result.refused.push([id, "single"]);
      continue;
    }
    if (amount > allowance) {
      result.refused.push([id, "cap"]);
      continue;
    }

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

  for (const bucket in caps) {
    if (!result.left.hasOwnProperty(bucket)) {
      result.left[bucket] = caps[bucket][0];
    }
  }

  return result;
}
```