@kill-switch/agent-guard 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,40 +4,46 @@
4
4
  * Ground truth for plan limits lives in the `anthropic-ratelimit-unified-*`
5
5
  * response headers, which only the proxy sees. A user running just the Claude
6
6
  * Code hook (the common, zero-config setup) never sees those headers — so when
7
- * they've told us their plan tier, we *estimate* where they stand by summing the
8
- * tokens the ledger recorded inside each rolling window and dividing by a
9
- * per-tier token budget.
7
+ * they've told us their plan tier, we *estimate* where they stand.
10
8
  *
11
- * This is deliberately approximate and always labelled as such:
12
- * - Anthropic meters opaque "prompts" / "active hours", not tokens, so the
13
- * token budgets below are calibrated rough equivalents, not contractual.
14
- * - The ledger stores a session's cumulative tokens against a single
15
- * `lastAt`, not a time series, so a long session is counted wholesale into
16
- * whichever window its last activity falls in.
9
+ * We estimate from the ledger's **cost** (`costUSD`), not its token counts. That
10
+ * matters: a coding agent's volume is dominated by cache reads/writes, and the
11
+ * ledger only stores non-cache input/output token counts — so a token-based
12
+ * estimate undercounts real throughput (and thus rate-limit consumption) by a
13
+ * large factor. `costUSD` is priced from the *full* usage including cache, so it
14
+ * tracks actual consumption far better. We compare it against a rough per-tier
15
+ * **API-equivalent** dollar ceiling for each window.
17
16
  *
18
- * It exists to give hook-only users *a* signal and to nudge them toward
19
- * `ks guard proxy` for exact numbers never to block (subscription mode is
20
- * alert-only). When in doubt it under-claims utilization so it won't cry wolf.
17
+ * This is still deliberately approximate and always labelled as such:
18
+ * - Anthropic meters opaque "prompts" / "active hours", and the dollar ceilings
19
+ * below are rough API-equivalent calibrations, not contractual.
20
+ * - The ledger stores a session's cumulative cost against a single `lastAt`,
21
+ * not a time series, so a long session is counted wholesale into whichever
22
+ * window its last activity falls in.
23
+ *
24
+ * It exists to give hook-only users *a* directional signal and to nudge them
25
+ * toward `ks guard proxy` for exact numbers — never to block (subscription mode
26
+ * is alert-only).
21
27
  */
22
28
  import { type LimitSnapshot } from "./limits.js";
23
29
  import type { Ledger } from "./ledger.js";
24
30
  export type PlanTier = "pro" | "max5" | "max20";
25
31
  /**
26
- * Rough per-tier token-equivalent budgets per window. Pro is the published
27
- * baseline; Max 5x / 20x scale the 5-hour burst ~linearly with the multiplier,
28
- * while the weekly cap scales more conservatively (Anthropic's weekly multiplier
29
- * is smaller than the per-session one). Tune via config if your mileage differs.
32
+ * Rough per-tier **API-equivalent USD** ceilings per window what the plan's
33
+ * rate limit lets you consume before lock-out, expressed in the same list-price
34
+ * dollars the ledger meters. Scaled by plan: Pro is the baseline, Max 5x/20x lift
35
+ * the burst (5h) roughly with the multiplier and the weekly cap more
36
+ * conservatively. These are estimates; the proxy's real headers override them.
30
37
  */
31
38
  export interface TierBudget {
32
- fiveHourTokens: number;
33
- weeklyTokens: number;
39
+ fiveHourUSD: number;
40
+ weeklyUSD: number;
34
41
  }
35
42
  export declare const TIER_BUDGETS: Record<PlanTier, TierBudget>;
36
43
  /**
37
44
  * Build an estimated {@link LimitSnapshot} from the ledger for a known tier.
38
- * Reset times are derived from the rolling window assumption (oldest in-window
39
- * activity + window length is unknowable here, so we report the window end from
40
- * `now` as a conservative upper bound on time remaining).
45
+ * `resetAt` is null (the true rolling reset is unknowable without a per-event
46
+ * time series), so pacing reports utilization only no fabricated reset/lockout.
41
47
  */
42
48
  export declare function estimateSnapshot(ledger: Ledger, tier: PlanTier, now: number, budgets?: Record<PlanTier, TierBudget>): LimitSnapshot;
43
49
  /** True when a snapshot came from {@link estimateSnapshot} rather than real headers. */
package/dist/estimate.js CHANGED
@@ -4,57 +4,57 @@
4
4
  * Ground truth for plan limits lives in the `anthropic-ratelimit-unified-*`
5
5
  * response headers, which only the proxy sees. A user running just the Claude
6
6
  * Code hook (the common, zero-config setup) never sees those headers — so when
7
- * they've told us their plan tier, we *estimate* where they stand by summing the
8
- * tokens the ledger recorded inside each rolling window and dividing by a
9
- * per-tier token budget.
7
+ * they've told us their plan tier, we *estimate* where they stand.
10
8
  *
11
- * This is deliberately approximate and always labelled as such:
12
- * - Anthropic meters opaque "prompts" / "active hours", not tokens, so the
13
- * token budgets below are calibrated rough equivalents, not contractual.
14
- * - The ledger stores a session's cumulative tokens against a single
15
- * `lastAt`, not a time series, so a long session is counted wholesale into
16
- * whichever window its last activity falls in.
9
+ * We estimate from the ledger's **cost** (`costUSD`), not its token counts. That
10
+ * matters: a coding agent's volume is dominated by cache reads/writes, and the
11
+ * ledger only stores non-cache input/output token counts — so a token-based
12
+ * estimate undercounts real throughput (and thus rate-limit consumption) by a
13
+ * large factor. `costUSD` is priced from the *full* usage including cache, so it
14
+ * tracks actual consumption far better. We compare it against a rough per-tier
15
+ * **API-equivalent** dollar ceiling for each window.
17
16
  *
18
- * It exists to give hook-only users *a* signal and to nudge them toward
19
- * `ks guard proxy` for exact numbers never to block (subscription mode is
20
- * alert-only). When in doubt it under-claims utilization so it won't cry wolf.
17
+ * This is still deliberately approximate and always labelled as such:
18
+ * - Anthropic meters opaque "prompts" / "active hours", and the dollar ceilings
19
+ * below are rough API-equivalent calibrations, not contractual.
20
+ * - The ledger stores a session's cumulative cost against a single `lastAt`,
21
+ * not a time series, so a long session is counted wholesale into whichever
22
+ * window its last activity falls in.
23
+ *
24
+ * It exists to give hook-only users *a* directional signal and to nudge them
25
+ * toward `ks guard proxy` for exact numbers — never to block (subscription mode
26
+ * is alert-only).
21
27
  */
22
28
  import { WINDOW_MS } from "./limits.js";
23
29
  export const TIER_BUDGETS = {
24
- // Calibrated rough equivalents Pro ≈ 45 prompts / 5h, modest weekly cap.
25
- pro: { fiveHourTokens: 8_000_000, weeklyTokens: 120_000_000 },
26
- max5: { fiveHourTokens: 40_000_000, weeklyTokens: 480_000_000 },
27
- max20: { fiveHourTokens: 160_000_000, weeklyTokens: 1_400_000_000 },
30
+ pro: { fiveHourUSD: 16, weeklyUSD: 100 },
31
+ max5: { fiveHourUSD: 80, weeklyUSD: 500 },
32
+ max20: { fiveHourUSD: 300, weeklyUSD: 2000 },
28
33
  };
29
34
  const FIVE_HOUR_MS = WINDOW_MS["5h"];
30
35
  const WEEK_MS = WINDOW_MS.weekly;
31
- /** Sum tokens (input+output) across sessions whose last activity is within `windowMs`. */
32
- function tokensInWindow(ledger, now, windowMs) {
36
+ /** Sum metered cost across sessions whose last activity is within `windowMs`. */
37
+ function costInWindow(ledger, now, windowMs) {
33
38
  let total = 0;
34
39
  for (const s of Object.values(ledger.sessions)) {
35
40
  if (now - s.lastAt < windowMs)
36
- total += (s.inputTokens || 0) + (s.outputTokens || 0);
41
+ total += s.costUSD || 0;
37
42
  }
38
43
  return total;
39
44
  }
40
45
  /**
41
46
  * Build an estimated {@link LimitSnapshot} from the ledger for a known tier.
42
- * Reset times are derived from the rolling window assumption (oldest in-window
43
- * activity + window length is unknowable here, so we report the window end from
44
- * `now` as a conservative upper bound on time remaining).
47
+ * `resetAt` is null (the true rolling reset is unknowable without a per-event
48
+ * time series), so pacing reports utilization only no fabricated reset/lockout.
45
49
  */
46
50
  export function estimateSnapshot(ledger, tier, now, budgets = TIER_BUDGETS) {
47
51
  const b = budgets[tier];
48
- const fiveTokens = tokensInWindow(ledger, now, FIVE_HOUR_MS);
49
- const weekTokens = tokensInWindow(ledger, now, WEEK_MS);
52
+ const fiveUSD = costInWindow(ledger, now, FIVE_HOUR_MS);
53
+ const weekUSD = costInWindow(ledger, now, WEEK_MS);
50
54
  const clamp = (n) => Math.max(0, Math.min(1, n));
51
- // resetAt is null, not fabricated: we have no per-event time series, so the
52
- // true rolling reset is unknowable. A null reset means pacing reports
53
- // utilization only (no burn-rate, no lockout projection, no bogus reset time) —
54
- // the honest behaviour for an estimate.
55
55
  return {
56
- fiveHour: { utilization: clamp(fiveTokens / b.fiveHourTokens), resetAt: null, status: "estimated" },
57
- weekly: { utilization: clamp(weekTokens / b.weeklyTokens), resetAt: null, status: "estimated" },
56
+ fiveHour: { utilization: clamp(fiveUSD / b.fiveHourUSD), resetAt: null, status: "estimated" },
57
+ weekly: { utilization: clamp(weekUSD / b.weeklyUSD), resetAt: null, status: "estimated" },
58
58
  status: "estimated",
59
59
  observedAt: now,
60
60
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kill-switch/agent-guard",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Kill Switch for coding agents — stop runaway Claude Code / Cursor / Aider sessions from racking up an LLM bill. Native hook + token-metering proxy with per-session and daily-rolling budgets.",
5
5
  "type": "module",
6
6
  "bin": {