audit-tools 0.33.9 → 0.33.11

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.
Files changed (77) hide show
  1. package/dist/audit/orchestrator/rollingDispatch.js +1 -1
  2. package/dist/audit/orchestrator/rollingDispatch.js.map +1 -1
  3. package/dist/remediate/steps/nextStep.js +1 -1
  4. package/dist/remediate/steps/nextStep.js.map +1 -1
  5. package/dist/shared/dispatch/admissionLeaseReconcile.js +1 -1
  6. package/dist/shared/dispatch/admissionLeaseReconcile.js.map +1 -1
  7. package/dist/shared/dispatch/admissionLoop.d.ts +42 -10
  8. package/dist/shared/dispatch/admissionLoop.d.ts.map +1 -1
  9. package/dist/shared/dispatch/admissionLoop.js +74 -7
  10. package/dist/shared/dispatch/admissionLoop.js.map +1 -1
  11. package/dist/shared/dispatch/dispatchQuotaContract.d.ts +82 -7
  12. package/dist/shared/dispatch/dispatchQuotaContract.d.ts.map +1 -1
  13. package/dist/shared/dispatch/hostDispatchWall.d.ts.map +1 -1
  14. package/dist/shared/dispatch/hostDispatchWall.js +8 -0
  15. package/dist/shared/dispatch/hostDispatchWall.js.map +1 -1
  16. package/dist/shared/dispatch/rollingDispatch.d.ts +17 -10
  17. package/dist/shared/dispatch/rollingDispatch.d.ts.map +1 -1
  18. package/dist/shared/dispatch/rollingDispatch.js +49 -33
  19. package/dist/shared/dispatch/rollingDispatch.js.map +1 -1
  20. package/dist/shared/dispatch/unifiedRolling.d.ts +3 -2
  21. package/dist/shared/dispatch/unifiedRolling.d.ts.map +1 -1
  22. package/dist/shared/dispatch/unifiedRolling.js +12 -4
  23. package/dist/shared/dispatch/unifiedRolling.js.map +1 -1
  24. package/dist/shared/index.d.ts +2 -2
  25. package/dist/shared/index.d.ts.map +1 -1
  26. package/dist/shared/index.js +5 -1
  27. package/dist/shared/index.js.map +1 -1
  28. package/dist/shared/providers/providerConfirmation.d.ts +1 -1
  29. package/dist/shared/providers/providerConfirmation.d.ts.map +1 -1
  30. package/dist/shared/providers/providerConfirmation.js +11 -2
  31. package/dist/shared/providers/providerConfirmation.js.map +1 -1
  32. package/dist/shared/providers/sharedProviderConfirmation.d.ts +20 -5
  33. package/dist/shared/providers/sharedProviderConfirmation.d.ts.map +1 -1
  34. package/dist/shared/providers/sharedProviderConfirmation.js +108 -34
  35. package/dist/shared/providers/sharedProviderConfirmation.js.map +1 -1
  36. package/dist/shared/quota/accountId.d.ts +70 -0
  37. package/dist/shared/quota/accountId.d.ts.map +1 -1
  38. package/dist/shared/quota/accountId.js +82 -5
  39. package/dist/shared/quota/accountId.js.map +1 -1
  40. package/dist/shared/quota/apiPool.d.ts +2 -1
  41. package/dist/shared/quota/apiPool.d.ts.map +1 -1
  42. package/dist/shared/quota/apiPool.js +14 -2
  43. package/dist/shared/quota/apiPool.js.map +1 -1
  44. package/dist/shared/quota/capacity.d.ts +82 -0
  45. package/dist/shared/quota/capacity.d.ts.map +1 -1
  46. package/dist/shared/quota/capacity.js +42 -1
  47. package/dist/shared/quota/capacity.js.map +1 -1
  48. package/dist/shared/quota/claudeOAuthQuotaSource.d.ts.map +1 -1
  49. package/dist/shared/quota/claudeOAuthQuotaSource.js +46 -8
  50. package/dist/shared/quota/claudeOAuthQuotaSource.js.map +1 -1
  51. package/dist/shared/quota/quotaSource.d.ts +73 -2
  52. package/dist/shared/quota/quotaSource.d.ts.map +1 -1
  53. package/dist/shared/quota/quotaSource.js +57 -2
  54. package/dist/shared/quota/quotaSource.js.map +1 -1
  55. package/dist/shared/quota/reservationLedger.d.ts +98 -26
  56. package/dist/shared/quota/reservationLedger.d.ts.map +1 -1
  57. package/dist/shared/quota/reservationLedger.js +109 -37
  58. package/dist/shared/quota/reservationLedger.js.map +1 -1
  59. package/dist/shared/quota/scheduler.d.ts +25 -1
  60. package/dist/shared/quota/scheduler.d.ts.map +1 -1
  61. package/dist/shared/quota/scheduler.js +115 -10
  62. package/dist/shared/quota/scheduler.js.map +1 -1
  63. package/dist/shared/quota/state.d.ts +24 -9
  64. package/dist/shared/quota/state.d.ts.map +1 -1
  65. package/dist/shared/quota/state.js +43 -16
  66. package/dist/shared/quota/state.js.map +1 -1
  67. package/dist/shared/quota/types.d.ts +57 -6
  68. package/dist/shared/quota/types.d.ts.map +1 -1
  69. package/dist/shared/quota/types.js.map +1 -1
  70. package/dist/shared/quota/windowConstraints.d.ts +56 -0
  71. package/dist/shared/quota/windowConstraints.d.ts.map +1 -0
  72. package/dist/shared/quota/windowConstraints.js +71 -0
  73. package/dist/shared/quota/windowConstraints.js.map +1 -0
  74. package/dist/shared/types/providerConfirmation.d.ts +31 -1
  75. package/dist/shared/types/providerConfirmation.d.ts.map +1 -1
  76. package/docs/audit-pkg/operator-guide.md +4 -1
  77. package/package.json +1 -1
@@ -4,27 +4,93 @@ import { z } from "zod";
4
4
  * that scale on DIFFERENT denominators (e.g. Claude's 5-hour `session` vs 7-day
5
5
  * `weekly`; Codex's primary vs secondary). The same N tokens is a large percent
6
6
  * of a small window and a tiny percent of a big one, so tokens→percent slope is
7
- * learned per (pool, window-label) and the pool's budget is the MIN across
8
- * windows. `label` is an AGNOSTIC string ("session","weekly",…) — never a
7
+ * learned per (pool, {@link windowSlopeKey}) and each window meters against its own
8
+ * allowance. `label` is an AGNOSTIC string ("session","weekly",…) — never a
9
9
  * provider/model identity — so any source can add its own windows.
10
10
  */
11
+ /**
12
+ * Whether a window's allowance is shared by every model on the credential, or
13
+ * belongs to one model alone. This is the METERING PARTITION and it is decided
14
+ * HERE, by the producer that knows — never re-derived downstream from pool
15
+ * identity, which is what four refused repair attempts each tried and got wrong.
16
+ *
17
+ * - `account` — one allowance shared across all of the credential's models
18
+ * (Claude's `five_hour`/`seven_day`, an unscoped `limits[]` entry). Meters on
19
+ * `(accountKey, label)`: N models on one credential draw down ONE budget.
20
+ * - `model` — applies to this model alone (a `limits[]` entry carrying
21
+ * `scope.model`). Meters on `(poolId, label)`: sharing it would falsely
22
+ * throttle siblings that the limit does not cover.
23
+ *
24
+ * REQUIRED, with no default, on every window from every source. A source that
25
+ * cannot distinguish the two emits `account` for all of its windows — that IS
26
+ * the fallback ("provider exposes only account-wide quota ⇒ it applies to all
27
+ * their models"), stated rather than assumed. The absence of a default is
28
+ * deliberate: a default is how the next provider gets silently special-cased.
29
+ *
30
+ * Orthogonal to the learned slope. `tokens_per_pct` stays per (pool, window)
31
+ * unconditionally — even under an account-scoped window, where N models share
32
+ * one percent allowance but each converts it to tokens at its OWN rate. Scope
33
+ * partitions the ALLOWANCE; the slope prices it per model.
34
+ */
35
+ export declare const QuotaWindowScopeSchema: z.ZodEnum<["account", "model"]>;
36
+ export type QuotaWindowScope = z.infer<typeof QuotaWindowScopeSchema>;
37
+ /**
38
+ * A `tokens_per_pct` map key, as produced by {@link windowSlopeKey}. Branded so the
39
+ * slope map cannot be read or written with a bare window label: the two key spaces
40
+ * are both strings, and an unbranded parameter left the guarantee to whoever
41
+ * remembered it — which a test promptly got wrong, writing under `"weekly"` where
42
+ * production only ever reads `"account:weekly"`.
43
+ */
44
+ export type WindowSlopeKey = string & {
45
+ readonly __windowSlopeKey: unique symbol;
46
+ };
47
+ /**
48
+ * The key a window's learned tokens-per-percent slope is stored under, within a
49
+ * pool's quota-state entry. **The single resolution point for slope lookup** — the
50
+ * only way to obtain a {@link WindowSlopeKey}, and every read and write of
51
+ * `tokens_per_pct` requires one, so the map cannot be keyed one way by the producer
52
+ * and another by the consumer.
53
+ *
54
+ * Keyed by `(scope, label)`, not label alone: an account-scoped and a model-scoped
55
+ * window can share a group name (both `session`) while pricing different allowances,
56
+ * and collapsing them onto one entry would silently blend two exchange rates into a
57
+ * single wrong one.
58
+ *
59
+ * ⚠ Changing this key ORPHANS previously-learned slopes (they were stored under the
60
+ * bare label). That is deliberate and self-healing: an orphaned window reads as
61
+ * uncalibrated, which routes the pool through the cold-start probe path and re-learns
62
+ * within a few dispatches. A migration shim would instead carry a slope forward onto a
63
+ * partition it was never measured against.
64
+ *
65
+ * This is also the seam for the deferred sibling-derived slope prior (design of
66
+ * record, "Deferred seam"): a prior can be injected at this one lookup point without
67
+ * touching admission.
68
+ */
69
+ export declare function windowSlopeKey(scope: QuotaWindowScope, label: string): WindowSlopeKey;
11
70
  export declare const QuotaWindowSchema: z.ZodObject<{
12
71
  label: z.ZodString;
72
+ scope: z.ZodEnum<["account", "model"]>;
13
73
  remaining_pct: z.ZodNullable<z.ZodNumber>;
14
74
  reset_at: z.ZodNullable<z.ZodString>;
15
75
  tokens_remaining: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
16
76
  }, "strict", z.ZodTypeAny, {
17
77
  label: string;
78
+ scope: "model" | "account";
18
79
  remaining_pct: number | null;
19
80
  reset_at: string | null;
20
81
  tokens_remaining?: number | null | undefined;
21
82
  }, {
22
83
  label: string;
84
+ scope: "model" | "account";
23
85
  remaining_pct: number | null;
24
86
  reset_at: string | null;
25
87
  tokens_remaining?: number | null | undefined;
26
88
  }>;
27
89
  export type QuotaWindow = z.infer<typeof QuotaWindowSchema>;
90
+ /** Whether a window carries a usable metering scope. */
91
+ export declare function hasWindowScope(window: {
92
+ scope?: unknown;
93
+ }): boolean;
28
94
  export declare const QuotaUsageSnapshotSchema: z.ZodObject<{
29
95
  remaining_pct: z.ZodNullable<z.ZodNumber>;
30
96
  reset_at: z.ZodNullable<z.ZodString>;
@@ -40,16 +106,19 @@ export declare const QuotaUsageSnapshotSchema: z.ZodObject<{
40
106
  */
41
107
  windows: z.ZodOptional<z.ZodArray<z.ZodObject<{
42
108
  label: z.ZodString;
109
+ scope: z.ZodEnum<["account", "model"]>;
43
110
  remaining_pct: z.ZodNullable<z.ZodNumber>;
44
111
  reset_at: z.ZodNullable<z.ZodString>;
45
112
  tokens_remaining: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
46
113
  }, "strict", z.ZodTypeAny, {
47
114
  label: string;
115
+ scope: "model" | "account";
48
116
  remaining_pct: number | null;
49
117
  reset_at: string | null;
50
118
  tokens_remaining?: number | null | undefined;
51
119
  }, {
52
120
  label: string;
121
+ scope: "model" | "account";
53
122
  remaining_pct: number | null;
54
123
  reset_at: string | null;
55
124
  tokens_remaining?: number | null | undefined;
@@ -63,6 +132,7 @@ export declare const QuotaUsageSnapshotSchema: z.ZodObject<{
63
132
  source: string;
64
133
  windows?: {
65
134
  label: string;
135
+ scope: "model" | "account";
66
136
  remaining_pct: number | null;
67
137
  reset_at: string | null;
68
138
  tokens_remaining?: number | null | undefined;
@@ -76,6 +146,7 @@ export declare const QuotaUsageSnapshotSchema: z.ZodObject<{
76
146
  source: string;
77
147
  windows?: {
78
148
  label: string;
149
+ scope: "model" | "account";
79
150
  remaining_pct: number | null;
80
151
  reset_at: string | null;
81
152
  tokens_remaining?: number | null | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"quotaSource.d.ts","sourceRoot":"","sources":["../../../src/shared/quota/quotaSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAOnB,CAAC;AACZ,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,wBAAwB;;;;;;;IAQjC;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGI,CAAC;AACZ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAChF;;;;;;OAMG;IACH,UAAU,CAAC,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjE;;;;;;;OAOG;IACH,gBAAgB,CAAC,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpE;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAQ3B"}
1
+ {"version":3,"file":"quotaSource.d.ts","sourceRoot":"","sources":["../../../src/shared/quota/quotaSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,sBAAsB,iCAA+B,CAAC;AACnE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,gBAAgB,EAAE,OAAO,MAAM,CAAA;CAAE,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAErF;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;EAQnB,CAAC;AACZ,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,wDAAwD;AACxD,wBAAgB,cAAc,CAAC,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAEnE;AAED,eAAO,MAAM,wBAAwB;;;;;;;IAQjC;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGI,CAAC;AACZ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAChF;;;;;;OAMG;IACH,UAAU,CAAC,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjE;;;;;;;OAOG;IACH,gBAAgB,CAAC,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpE;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAQ3B"}
@@ -4,18 +4,73 @@ import { z } from "zod";
4
4
  * that scale on DIFFERENT denominators (e.g. Claude's 5-hour `session` vs 7-day
5
5
  * `weekly`; Codex's primary vs secondary). The same N tokens is a large percent
6
6
  * of a small window and a tiny percent of a big one, so tokens→percent slope is
7
- * learned per (pool, window-label) and the pool's budget is the MIN across
8
- * windows. `label` is an AGNOSTIC string ("session","weekly",…) — never a
7
+ * learned per (pool, {@link windowSlopeKey}) and each window meters against its own
8
+ * allowance. `label` is an AGNOSTIC string ("session","weekly",…) — never a
9
9
  * provider/model identity — so any source can add its own windows.
10
10
  */
11
+ /**
12
+ * Whether a window's allowance is shared by every model on the credential, or
13
+ * belongs to one model alone. This is the METERING PARTITION and it is decided
14
+ * HERE, by the producer that knows — never re-derived downstream from pool
15
+ * identity, which is what four refused repair attempts each tried and got wrong.
16
+ *
17
+ * - `account` — one allowance shared across all of the credential's models
18
+ * (Claude's `five_hour`/`seven_day`, an unscoped `limits[]` entry). Meters on
19
+ * `(accountKey, label)`: N models on one credential draw down ONE budget.
20
+ * - `model` — applies to this model alone (a `limits[]` entry carrying
21
+ * `scope.model`). Meters on `(poolId, label)`: sharing it would falsely
22
+ * throttle siblings that the limit does not cover.
23
+ *
24
+ * REQUIRED, with no default, on every window from every source. A source that
25
+ * cannot distinguish the two emits `account` for all of its windows — that IS
26
+ * the fallback ("provider exposes only account-wide quota ⇒ it applies to all
27
+ * their models"), stated rather than assumed. The absence of a default is
28
+ * deliberate: a default is how the next provider gets silently special-cased.
29
+ *
30
+ * Orthogonal to the learned slope. `tokens_per_pct` stays per (pool, window)
31
+ * unconditionally — even under an account-scoped window, where N models share
32
+ * one percent allowance but each converts it to tokens at its OWN rate. Scope
33
+ * partitions the ALLOWANCE; the slope prices it per model.
34
+ */
35
+ export const QuotaWindowScopeSchema = z.enum(["account", "model"]);
36
+ /**
37
+ * The key a window's learned tokens-per-percent slope is stored under, within a
38
+ * pool's quota-state entry. **The single resolution point for slope lookup** — the
39
+ * only way to obtain a {@link WindowSlopeKey}, and every read and write of
40
+ * `tokens_per_pct` requires one, so the map cannot be keyed one way by the producer
41
+ * and another by the consumer.
42
+ *
43
+ * Keyed by `(scope, label)`, not label alone: an account-scoped and a model-scoped
44
+ * window can share a group name (both `session`) while pricing different allowances,
45
+ * and collapsing them onto one entry would silently blend two exchange rates into a
46
+ * single wrong one.
47
+ *
48
+ * ⚠ Changing this key ORPHANS previously-learned slopes (they were stored under the
49
+ * bare label). That is deliberate and self-healing: an orphaned window reads as
50
+ * uncalibrated, which routes the pool through the cold-start probe path and re-learns
51
+ * within a few dispatches. A migration shim would instead carry a slope forward onto a
52
+ * partition it was never measured against.
53
+ *
54
+ * This is also the seam for the deferred sibling-derived slope prior (design of
55
+ * record, "Deferred seam"): a prior can be injected at this one lookup point without
56
+ * touching admission.
57
+ */
58
+ export function windowSlopeKey(scope, label) {
59
+ return `${scope}:${label}`;
60
+ }
11
61
  export const QuotaWindowSchema = z
12
62
  .object({
13
63
  label: z.string(),
64
+ scope: QuotaWindowScopeSchema,
14
65
  remaining_pct: z.number().nullable(),
15
66
  reset_at: z.string().nullable(),
16
67
  tokens_remaining: z.number().int().nullable().optional(),
17
68
  })
18
69
  .strict();
70
+ /** Whether a window carries a usable metering scope. */
71
+ export function hasWindowScope(window) {
72
+ return window.scope === "account" || window.scope === "model";
73
+ }
19
74
  export const QuotaUsageSnapshotSchema = z
20
75
  .object({
21
76
  remaining_pct: z.number().nullable(),
@@ -1 +1 @@
1
- {"version":3,"file":"quotaSource.js","sourceRoot":"","sources":["../../../src/shared/quota/quotaSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,CAAC;IACN,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACzD,CAAC;KACD,MAAM,EAAE,CAAC;AAGZ,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC/C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB;;;;;OAKG;IACH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE;CAC/C,CAAC;KACD,MAAM,EAAE,CAAC;AA2DZ;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAmB,EACnB,gBAAwB;IAExB,IAAI,CAAC,MAAM,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAmB,EACnB,gBAAwB;IAExB,IAAI,MAAM,CAAC,UAAU;QAAE,OAAO,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;QAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAChD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"quotaSource.js","sourceRoot":"","sources":["../../../src/shared/quota/quotaSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;AAYnE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAuB,EAAE,KAAa;IACnE,OAAO,GAAG,KAAK,IAAI,KAAK,EAAoB,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,CAAC;IACN,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,KAAK,EAAE,sBAAsB;IAC7B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACzD,CAAC;KACD,MAAM,EAAE,CAAC;AAGZ,wDAAwD;AACxD,MAAM,UAAU,cAAc,CAAC,MAA2B;IACxD,OAAO,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,OAAO,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC/C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB;;;;;OAKG;IACH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE;CAC/C,CAAC;KACD,MAAM,EAAE,CAAC;AA2DZ;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAmB,EACnB,gBAAwB;IAExB,IAAI,CAAC,MAAM,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAmB,EACnB,gBAAwB;IAExB,IAAI,MAAM,CAAC,UAAU;QAAE,OAAO,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;QAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -19,25 +19,85 @@ export interface ReservationLease {
19
19
  /** Epoch ms after which the lease no longer counts toward outstanding. */
20
20
  expiresAt: number;
21
21
  }
22
- /** Outcome of an admission attempt against one resourceKey's shared budget. */
22
+ /**
23
+ * One metered allowance a dispatch must fit inside. A packet is admitted only if it
24
+ * clears EVERY constraint that applies to it — an account-wide window shared with
25
+ * every sibling model on the credential, plus any window scoped to this model alone.
26
+ *
27
+ * ⚠ **`budget` and `cost` are in the WINDOW's own unit, and the caller owns the
28
+ * conversion.** There is no unit shared across windows: a 5-hour `session` and a
29
+ * 7-day `weekly` scale on different denominators, so the same N tokens is a
30
+ * different fraction of each. The ledger is deliberately unit-agnostic — it only
31
+ * compares `cost` against `budget - Σ outstanding` for one key — which is exactly
32
+ * what lets an account-scoped window meter in the shared percent while each sibling
33
+ * converts its own tokens at its own learned rate.
34
+ */
35
+ export interface AdmitConstraint {
36
+ /** The metered allowance's identity — `(accountKey, label)` for an account-wide window, `(poolId, label)` for a model-scoped one. */
37
+ resourceKey: string;
38
+ /** Caller-computed live remaining allowance for `resourceKey`. Non-finite ⇒ optimistic (unbounded). */
39
+ budget: number;
40
+ /** This dispatch's draw against `resourceKey`, in the same unit as `budget`. */
41
+ cost: number;
42
+ }
43
+ /** How one constraint evaluated during an admission attempt. */
44
+ export interface ConstraintOutcome {
45
+ resourceKey: string;
46
+ /** Live budget minus `outstandingBefore` — so it carries that field's carve-out too. */
47
+ headroomBefore: number;
48
+ /**
49
+ * Outstanding (non-expired) leases counted against this key when it was
50
+ * evaluated. This is everyone else's in-flight total — PLUS any draw an earlier
51
+ * constraint in this same attempt already made on the same key (see the
52
+ * duplicate-key note on {@link ReservationLedger.admit}), so it is what this
53
+ * constraint was actually measured against rather than a pure "before" reading.
54
+ */
55
+ outstandingBefore: number;
56
+ /** The cost this attempt tried to reserve against this key (clamped to >= 0). */
57
+ cost: number;
58
+ /** Whether this constraint alone had room. Admission requires ALL to be true. */
59
+ cleared: boolean;
60
+ }
61
+ /** Outcome of an admission attempt against every constraint that applies. */
23
62
  export interface AdmitDecision {
24
63
  admitted: boolean;
25
64
  /** Minted only when `admitted`; pass back to `reconcile` on completion. */
26
65
  leaseId: string | null;
27
- /** Live budget minus everyone's outstanding leases, BEFORE this attempt. */
28
- headroomBefore: number;
29
- /** Sum of everyone's outstanding (non-expired) leases, BEFORE this attempt. */
30
- outstandingBefore: number;
31
- /** The cost this attempt tried to reserve (echoed for the explain-artifact). */
32
- cost: number;
66
+ /** Every constraint's evaluation, in input order — the per-admission explain record. */
67
+ constraints: ConstraintOutcome[];
68
+ /**
69
+ * The TIGHTEST constraint — the one closest to blocking this dispatch, by the
70
+ * dimensionless ratio `cost / headroomBefore`. This is the "binding window" a
71
+ * report should name. Null only when no constraints were supplied.
72
+ *
73
+ * ⚠ Deliberately a RATIO, not the smallest `headroomBefore`. Constraints meter in
74
+ * different windows' units, so raw headroom is not comparable across them — "40
75
+ * left of a session window" vs "80 left of a weekly one" says nothing about which
76
+ * binds. The ratio is dimensionless and therefore is comparable. A blocked
77
+ * constraint always outranks a cleared one (its ratio exceeds 1), so this is also
78
+ * independent of the order the caller supplied the constraints in.
79
+ */
80
+ binding: ConstraintOutcome | null;
81
+ /**
82
+ * Whether ANY constraint had outstanding leases against it.
83
+ *
84
+ * This exists because `binding.outstandingBefore` is one key's total and must
85
+ * never be read as "is anything in flight anywhere" — the liveness backstop in
86
+ * the rolling dispatcher needs exactly that aggregate to decide whether waiting
87
+ * could ever help. Given as a computed boolean rather than left to the consumer
88
+ * to derive, so the predicate cannot be reconstructed wrongly from a per-key
89
+ * number (it was, and a blocked model window with an idle account window would
90
+ * have force-dispatched into overshoot).
91
+ */
92
+ anyOutstanding: boolean;
33
93
  }
34
94
  export interface AdmitInput {
35
- /** `provider#account/model` — the real metered account the budget belongs to. */
36
- resourceKey: string;
37
- /** Total reservation: input estimate + output envelope. */
38
- cost: number;
39
- /** Caller-computed live remaining tokens for `resourceKey`. Non-finite ⇒ optimistic (unbounded). */
40
- budget: number;
95
+ /**
96
+ * Every allowance this dispatch draws against. Admission is ALL-OR-NOTHING: one
97
+ * lease id is recorded under each key, or none are. An empty array is admitted
98
+ * unmetered (a lease is still minted so completion reconciles symmetrically).
99
+ */
100
+ constraints: AdmitConstraint[];
41
101
  /** Pool id for diagnostics. */
42
102
  poolId: string;
43
103
  /** Lease lifetime in ms; defaults to the ledger's TTL. */
@@ -64,24 +124,36 @@ export declare class ReservationLedger {
64
124
  private readonly defaultTtlMs;
65
125
  constructor(ledgerPath: string, now?: () => number, defaultTtlMs?: number);
66
126
  /**
67
- * Attempt to reserve `cost` tokens against `resourceKey`'s shared budget.
68
- * Admitted iff `budget - Σ outstanding_leases(resourceKey) >= cost`, evaluated
69
- * atomically under the lock so concurrent admitters serialize and each sees the
70
- * others' in-flight reservations. A non-finite `budget` is treated as unbounded
71
- * (optimistic start — the reactive floor still corrects). A non-positive `cost`
72
- * is always admitted with a lease so completion still reconciles symmetrically.
73
- * The returned `headroomBefore`/`outstandingBefore`/`cost` feed the per-admission
74
- * explain record on the dispatch-quota artifact.
127
+ * Attempt to reserve against EVERY constraint that applies to this dispatch.
128
+ * Each is admitted iff `budget - Σ outstanding_leases(resourceKey) >= cost`, and
129
+ * the dispatch is admitted iff every one clears — **all-or-nothing**: either one
130
+ * lease id is recorded under each key, or nothing is written. A partial
131
+ * reservation (fits the account window, overruns its model window) is therefore
132
+ * unrepresentable, which is what makes `reconcile` by lease id total.
133
+ *
134
+ * The whole evaluation runs atomically under the lock, so concurrent admitters
135
+ * serialize and each sees the others' in-flight reservations across every key.
136
+ * A non-finite `budget` is unbounded (optimistic start — the reactive floor still
137
+ * corrects); a non-positive `cost` always clears its own constraint so completion
138
+ * still reconciles symmetrically. Every constraint is evaluated even after one
139
+ * blocks, so the explain record shows the full picture rather than the first
140
+ * failure.
141
+ *
142
+ * Two constraints on the same `resourceKey` accumulate within the attempt — the
143
+ * second sees the first's draw as outstanding — so a caller that supplies a key
144
+ * twice cannot double-spend one allowance.
75
145
  */
76
146
  admit(input: AdmitInput): Promise<AdmitDecision>;
77
147
  /**
78
148
  * Free a reservation on completion (success OR failure — the request is no longer
79
- * in flight either way). Token-checked by `leaseId`: a no-op if the lease is
80
- * already gone (expired / reconciled). Returns whether a lease was removed. The
81
- * lease's real cost surfaces in the provider's next quota snapshot; the ledger's
82
- * job is only to stop reserving it.
149
+ * in flight either way). Token-checked by `leaseId` and swept across EVERY key, so
150
+ * a multi-constraint reservation is released in full or not at all — the caller
151
+ * cannot leak one window's reservation by forgetting which keys it took. A no-op
152
+ * if the lease is already gone (expired / reconciled). Returns whether anything was
153
+ * removed. The lease's real cost surfaces in the provider's next quota snapshot;
154
+ * the ledger's job is only to stop reserving it.
83
155
  */
84
- reconcile(resourceKey: string, leaseId: string): Promise<boolean>;
156
+ reconcile(leaseId: string): Promise<boolean>;
85
157
  /** Sum of outstanding (non-expired) reservations for `resourceKey`. Read under the lock. */
86
158
  outstanding(resourceKey: string): Promise<number>;
87
159
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"reservationLedger.d.ts","sourceRoot":"","sources":["../../../src/shared/quota/reservationLedger.ts"],"names":[],"mappings":"AAqCA;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,4EAA4E;IAC5E,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,oGAAoG;IACpG,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;AA+EpD;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IALtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAGnB,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAM,MAAyB,EACrD,YAAY,GAAE,MAAsB;IAMtC;;;;;;;;;OASG;IACG,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IA6BtD;;;;;;OAMG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBvE,4FAA4F;IACtF,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQvD;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IASvC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,SAAS,CAAC;CAOrC;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAUjD;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,IAAI,iBAAiB,CAE3D"}
1
+ {"version":3,"file":"reservationLedger.d.ts","sourceRoot":"","sources":["../../../src/shared/quota/reservationLedger.ts"],"names":[],"mappings":"AAqCA;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,qIAAqI;IACrI,WAAW,EAAE,MAAM,CAAC;IACpB,uGAAuG;IACvG,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,gEAAgE;AAChE,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,wFAAwF;IACxF,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClC;;;;;;;;;;OAUG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAmHpD;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IALtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAGnB,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAM,MAAyB,EACrD,YAAY,GAAE,MAAsB;IAMtC;;;;;;;;;;;;;;;;;;;OAmBG;IACG,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAqDtD;;;;;;;;OAQG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBlD,4FAA4F;IACtF,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQvD;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IASvC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,SAAS,CAAC;CAOrC;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAUjD;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,IAAI,iBAAiB,CAE3D"}
@@ -110,6 +110,44 @@ function sumOutstanding(leases, now) {
110
110
  }
111
111
  return total;
112
112
  }
113
+ /**
114
+ * How close a constraint came to blocking, as a dimensionless ratio of the draw to
115
+ * the room available for it. Comparable ACROSS windows that meter in different
116
+ * units, which raw headroom is not. Higher = tighter; > 1 means it blocked.
117
+ */
118
+ function tightness(outcome) {
119
+ // Order matters. A zero-cost constraint ALWAYS clears — including on an exhausted
120
+ // window — so it must rank loosest, not tightest. Testing headroom first returned
121
+ // +Infinity for `cost 0, headroom 0` and let a constraint that cleared outrank one
122
+ // that actually blocked. With this order the invariant below is exact: cleared
123
+ // ⇒ tightness <= 1 (cost <= 0 ⇒ 0, else headroom >= cost ⇒ ratio <= 1), and
124
+ // blocked ⇒ > 1 or +Infinity.
125
+ if (outcome.cost <= 0)
126
+ return 0;
127
+ if (outcome.headroomBefore <= 0)
128
+ return Number.POSITIVE_INFINITY;
129
+ return outcome.cost / outcome.headroomBefore;
130
+ }
131
+ /**
132
+ * The constraint closest to blocking — what a report should name as the binding
133
+ * window. Replaces the MIN-collapse that used to happen before the ledger was
134
+ * reached. Ranked by {@link tightness}, so the result does not depend on the order
135
+ * the caller listed the constraints in, and a blocked constraint always wins.
136
+ */
137
+ function pickBinding(outcomes) {
138
+ if (outcomes.length === 0)
139
+ return null;
140
+ return outcomes.reduce((tightest, o) => {
141
+ const a = tightness(o);
142
+ const b = tightness(tightest);
143
+ if (a !== b)
144
+ return a > b ? o : tightest;
145
+ // Ties are reachable (two exhausted windows both score +Infinity; equal ratios).
146
+ // Break them on the key so the answer is a property of the ledger state, not of
147
+ // the order the caller happened to list the constraints in.
148
+ return o.resourceKey < tightest.resourceKey ? o : tightest;
149
+ });
150
+ }
113
151
  /** Drop expired leases from every key, returning the trimmed map and how many were dropped. */
114
152
  function pruneExpired(ledger, now) {
115
153
  let dropped = 0;
@@ -147,65 +185,99 @@ export class ReservationLedger {
147
185
  this.defaultTtlMs = defaultTtlMs;
148
186
  }
149
187
  /**
150
- * Attempt to reserve `cost` tokens against `resourceKey`'s shared budget.
151
- * Admitted iff `budget - Σ outstanding_leases(resourceKey) >= cost`, evaluated
152
- * atomically under the lock so concurrent admitters serialize and each sees the
153
- * others' in-flight reservations. A non-finite `budget` is treated as unbounded
154
- * (optimistic start — the reactive floor still corrects). A non-positive `cost`
155
- * is always admitted with a lease so completion still reconciles symmetrically.
156
- * The returned `headroomBefore`/`outstandingBefore`/`cost` feed the per-admission
157
- * explain record on the dispatch-quota artifact.
188
+ * Attempt to reserve against EVERY constraint that applies to this dispatch.
189
+ * Each is admitted iff `budget - Σ outstanding_leases(resourceKey) >= cost`, and
190
+ * the dispatch is admitted iff every one clears — **all-or-nothing**: either one
191
+ * lease id is recorded under each key, or nothing is written. A partial
192
+ * reservation (fits the account window, overruns its model window) is therefore
193
+ * unrepresentable, which is what makes `reconcile` by lease id total.
194
+ *
195
+ * The whole evaluation runs atomically under the lock, so concurrent admitters
196
+ * serialize and each sees the others' in-flight reservations across every key.
197
+ * A non-finite `budget` is unbounded (optimistic start — the reactive floor still
198
+ * corrects); a non-positive `cost` always clears its own constraint so completion
199
+ * still reconciles symmetrically. Every constraint is evaluated even after one
200
+ * blocks, so the explain record shows the full picture rather than the first
201
+ * failure.
202
+ *
203
+ * Two constraints on the same `resourceKey` accumulate within the attempt — the
204
+ * second sees the first's draw as outstanding — so a caller that supplies a key
205
+ * twice cannot double-spend one allowance.
158
206
  */
159
207
  async admit(input) {
160
208
  const ttl = input.leaseTtlMs ?? this.defaultTtlMs;
161
209
  return withFileLock(this.lockPath, async () => {
162
210
  const now = this.now();
163
211
  const { ledger } = pruneExpired(await readLedger(this.ledgerPath), now);
164
- const outstandingBefore = sumOutstanding(ledger[input.resourceKey], now);
165
- const budget = Number.isFinite(input.budget) ? input.budget : Number.POSITIVE_INFINITY;
166
- const headroomBefore = budget - outstandingBefore;
167
- const cost = Number.isFinite(input.cost) ? input.cost : 0;
168
- const admitted = cost <= 0 || headroomBefore >= cost;
212
+ // Draws already committed by EARLIER constraints in this same attempt, so a
213
+ // repeated resourceKey is metered once against its real allowance.
214
+ const pendingByKey = new Map();
215
+ const outcomes = [];
216
+ let anyOutstanding = false;
217
+ for (const constraint of input.constraints) {
218
+ const pending = pendingByKey.get(constraint.resourceKey) ?? 0;
219
+ const priorOutstanding = sumOutstanding(ledger[constraint.resourceKey], now);
220
+ if (priorOutstanding > 0)
221
+ anyOutstanding = true;
222
+ const outstandingBefore = priorOutstanding + pending;
223
+ const budget = Number.isFinite(constraint.budget) ? constraint.budget : Number.POSITIVE_INFINITY;
224
+ const headroomBefore = budget - outstandingBefore;
225
+ // Normalize ONCE: the value validated against headroom, accumulated for a
226
+ // repeated key, and persisted on the lease must be the same number. A
227
+ // negative cost reaching the lease would subtract from the key's outstanding
228
+ // total and manufacture headroom that does not exist for every peer.
229
+ const cost = Number.isFinite(constraint.cost) ? Math.max(0, constraint.cost) : 0;
230
+ outcomes.push({
231
+ resourceKey: constraint.resourceKey,
232
+ headroomBefore,
233
+ outstandingBefore,
234
+ cost,
235
+ cleared: cost <= 0 || headroomBefore >= cost,
236
+ });
237
+ pendingByKey.set(constraint.resourceKey, pending + cost);
238
+ }
239
+ const admitted = outcomes.every((o) => o.cleared);
240
+ const binding = pickBinding(outcomes);
169
241
  if (!admitted) {
170
242
  // Persist the prune even on a blocked admission so expired leases don't
171
243
  // linger and depress headroom for the next attempt.
172
244
  await writeLedger(this.ledgerPath, ledger);
173
- return { admitted: false, leaseId: null, headroomBefore, outstandingBefore, cost };
245
+ return { admitted: false, leaseId: null, constraints: outcomes, binding, anyOutstanding };
174
246
  }
175
247
  const leaseId = mintLeaseId();
176
- const lease = {
177
- leaseId,
178
- cost,
179
- poolId: input.poolId,
180
- expiresAt: now + ttl,
181
- };
182
- ledger[input.resourceKey] = [...(ledger[input.resourceKey] ?? []), lease];
248
+ const expiresAt = now + ttl;
249
+ for (const outcome of outcomes) {
250
+ const lease = { leaseId, cost: outcome.cost, poolId: input.poolId, expiresAt };
251
+ ledger[outcome.resourceKey] = [...(ledger[outcome.resourceKey] ?? []), lease];
252
+ }
183
253
  await writeLedger(this.ledgerPath, ledger);
184
- return { admitted: true, leaseId, headroomBefore, outstandingBefore, cost };
254
+ return { admitted: true, leaseId, constraints: outcomes, binding, anyOutstanding };
185
255
  });
186
256
  }
187
257
  /**
188
258
  * Free a reservation on completion (success OR failure — the request is no longer
189
- * in flight either way). Token-checked by `leaseId`: a no-op if the lease is
190
- * already gone (expired / reconciled). Returns whether a lease was removed. The
191
- * lease's real cost surfaces in the provider's next quota snapshot; the ledger's
192
- * job is only to stop reserving it.
259
+ * in flight either way). Token-checked by `leaseId` and swept across EVERY key, so
260
+ * a multi-constraint reservation is released in full or not at all — the caller
261
+ * cannot leak one window's reservation by forgetting which keys it took. A no-op
262
+ * if the lease is already gone (expired / reconciled). Returns whether anything was
263
+ * removed. The lease's real cost surfaces in the provider's next quota snapshot;
264
+ * the ledger's job is only to stop reserving it.
193
265
  */
194
- async reconcile(resourceKey, leaseId) {
266
+ async reconcile(leaseId) {
195
267
  return withFileLock(this.lockPath, async () => {
196
268
  const now = this.now();
197
269
  const { ledger } = pruneExpired(await readLedger(this.ledgerPath), now);
198
- const leases = ledger[resourceKey];
199
- if (!leases) {
200
- await writeLedger(this.ledgerPath, ledger);
201
- return false;
270
+ let removed = false;
271
+ for (const [resourceKey, leases] of Object.entries(ledger)) {
272
+ const remaining = leases.filter((l) => l.leaseId !== leaseId);
273
+ if (remaining.length === leases.length)
274
+ continue;
275
+ removed = true;
276
+ if (remaining.length > 0)
277
+ ledger[resourceKey] = remaining;
278
+ else
279
+ delete ledger[resourceKey];
202
280
  }
203
- const remaining = leases.filter((l) => l.leaseId !== leaseId);
204
- const removed = remaining.length !== leases.length;
205
- if (remaining.length > 0)
206
- ledger[resourceKey] = remaining;
207
- else
208
- delete ledger[resourceKey];
209
281
  await writeLedger(this.ledgerPath, ledger);
210
282
  return removed;
211
283
  });
@@ -1 +1 @@
1
- {"version":3,"file":"reservationLedger.js","sourceRoot":"","sources":["../../../src/shared/quota/reservationLedger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reservationLedger.js","sourceRoot":"","sources":["../../../src/shared/quota/reservationLedger.ts"],"names":[],"mappings":"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"}