@claudexor/daemon 3.20.1 → 3.23.0

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 (107) hide show
  1. package/dist/client-errors.d.ts +9 -0
  2. package/dist/client-errors.d.ts.map +1 -0
  3. package/dist/client-errors.js +15 -0
  4. package/dist/client-errors.js.map +1 -0
  5. package/dist/client.d.ts +3 -5
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +21 -13
  8. package/dist/client.js.map +1 -1
  9. package/dist/command-activity.d.ts +14 -0
  10. package/dist/command-activity.d.ts.map +1 -0
  11. package/dist/command-activity.js +16 -0
  12. package/dist/command-activity.js.map +1 -0
  13. package/dist/command-list-projection.d.ts +8 -0
  14. package/dist/command-list-projection.d.ts.map +1 -0
  15. package/dist/command-list-projection.js +43 -0
  16. package/dist/command-list-projection.js.map +1 -0
  17. package/dist/command-list-select.d.ts +8 -0
  18. package/dist/command-list-select.d.ts.map +1 -0
  19. package/dist/command-list-select.js +127 -0
  20. package/dist/command-list-select.js.map +1 -0
  21. package/dist/command-retention.d.ts +3 -18
  22. package/dist/command-retention.d.ts.map +1 -1
  23. package/dist/command-retention.js +24 -40
  24. package/dist/command-retention.js.map +1 -1
  25. package/dist/command-scope-roots.d.ts +6 -0
  26. package/dist/command-scope-roots.d.ts.map +1 -1
  27. package/dist/command-scope-roots.js +13 -0
  28. package/dist/command-scope-roots.js.map +1 -1
  29. package/dist/command-store.d.ts +2 -5
  30. package/dist/command-store.d.ts.map +1 -1
  31. package/dist/command-store.js +4 -5
  32. package/dist/command-store.js.map +1 -1
  33. package/dist/continuation-admission.d.ts +11 -0
  34. package/dist/continuation-admission.d.ts.map +1 -0
  35. package/dist/continuation-admission.js +16 -0
  36. package/dist/continuation-admission.js.map +1 -0
  37. package/dist/credential-unusable-ledger.d.ts +3 -8
  38. package/dist/credential-unusable-ledger.d.ts.map +1 -1
  39. package/dist/credential-unusable-ledger.js +5 -8
  40. package/dist/credential-unusable-ledger.js.map +1 -1
  41. package/dist/daemon-health.d.ts +13 -0
  42. package/dist/daemon-health.d.ts.map +1 -1
  43. package/dist/daemon-health.js +2 -0
  44. package/dist/daemon-health.js.map +1 -1
  45. package/dist/index.d.ts +3 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +3 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/journal-fold-policy.d.ts.map +1 -1
  50. package/dist/journal-fold-policy.js +12 -1
  51. package/dist/journal-fold-policy.js.map +1 -1
  52. package/dist/memory-facts.d.ts +5 -0
  53. package/dist/memory-facts.d.ts.map +1 -0
  54. package/dist/memory-facts.js +23 -0
  55. package/dist/memory-facts.js.map +1 -0
  56. package/dist/project-partition-preparation.d.ts +8 -1
  57. package/dist/project-partition-preparation.d.ts.map +1 -1
  58. package/dist/project-partition-preparation.js +41 -3
  59. package/dist/project-partition-preparation.js.map +1 -1
  60. package/dist/project-partitions.d.ts +5 -0
  61. package/dist/project-partitions.d.ts.map +1 -1
  62. package/dist/project-partitions.js +7 -0
  63. package/dist/project-partitions.js.map +1 -1
  64. package/dist/project-remove.d.ts.map +1 -1
  65. package/dist/project-remove.js +2 -4
  66. package/dist/project-remove.js.map +1 -1
  67. package/dist/quota-poll-lanes.d.ts +25 -7
  68. package/dist/quota-poll-lanes.d.ts.map +1 -1
  69. package/dist/quota-poll-lanes.js +90 -16
  70. package/dist/quota-poll-lanes.js.map +1 -1
  71. package/dist/quota-poll-pacer.d.ts +33 -36
  72. package/dist/quota-poll-pacer.d.ts.map +1 -1
  73. package/dist/quota-poll-pacer.js +115 -63
  74. package/dist/quota-poll-pacer.js.map +1 -1
  75. package/dist/quota-registry-support.d.ts +74 -0
  76. package/dist/quota-registry-support.d.ts.map +1 -1
  77. package/dist/quota-registry-support.js +153 -1
  78. package/dist/quota-registry-support.js.map +1 -1
  79. package/dist/quota-registry.d.ts +26 -4
  80. package/dist/quota-registry.d.ts.map +1 -1
  81. package/dist/quota-registry.js +60 -76
  82. package/dist/quota-registry.js.map +1 -1
  83. package/dist/rpc-problem.d.ts +10 -0
  84. package/dist/rpc-problem.d.ts.map +1 -0
  85. package/dist/rpc-problem.js +22 -0
  86. package/dist/rpc-problem.js.map +1 -0
  87. package/dist/runless-turn-recovery.d.ts +30 -0
  88. package/dist/runless-turn-recovery.d.ts.map +1 -0
  89. package/dist/runless-turn-recovery.js +38 -0
  90. package/dist/runless-turn-recovery.js.map +1 -0
  91. package/dist/server.d.ts.map +1 -1
  92. package/dist/server.js +12 -24
  93. package/dist/server.js.map +1 -1
  94. package/dist/terminal-authority.d.ts +1 -0
  95. package/dist/terminal-authority.d.ts.map +1 -1
  96. package/dist/thread-store-support.d.ts +13 -2
  97. package/dist/thread-store-support.d.ts.map +1 -1
  98. package/dist/thread-store-support.js +48 -3
  99. package/dist/thread-store-support.js.map +1 -1
  100. package/dist/thread-worktree-state.d.ts.map +1 -1
  101. package/dist/thread-worktree-state.js +5 -0
  102. package/dist/thread-worktree-state.js.map +1 -1
  103. package/dist/threads.d.ts +5 -1
  104. package/dist/threads.d.ts.map +1 -1
  105. package/dist/threads.js +18 -33
  106. package/dist/threads.js.map +1 -1
  107. package/package.json +6 -5
@@ -1,42 +1,30 @@
1
+ import type { QuotaSubject } from "@claudexor/schema";
1
2
  /**
2
- * Daemon-private persistence for one pacer fact: the per-vendor rate-limit
3
- * floor. Deliberately OUTSIDE the quota journal and every quota projection
3
+ * Daemon-private persistence for poll floors: current subject/route floors and
4
+ * legacy per-vendor floors retained through their recorded deadlines. Deliberately OUTSIDE the quota journal and every quota projection
4
5
  * (owner decision 7=A): a throttled POLL is pacing state, never quota truth —
5
6
  * journaling it as a cooldown would read as "window exhausted" to rotation
6
7
  * and to external consumers of the quota surface.
7
8
  */
8
9
  export interface QuotaPacerStateStore {
9
- /** Millisecond epoch before which the vendor must not be polled; 0 = none. */
10
+ /** Legacy vendor floor, retained for upgrades; 0 = none. */
10
11
  load(vendor: string): number;
11
- save(vendor: string, notBeforeMs: number): void;
12
+ loadSubject?(subject: QuotaSubject): number;
13
+ saveSubject?(subject: QuotaSubject, notBeforeMs: number): void;
12
14
  }
13
15
  /** File-backed store under the daemon dir. Best-effort durability: a missing,
14
16
  * corrupt, or unwritable file only forgets the floor (fail-open to polling),
15
17
  * it never breaks the poll cycle. */
16
18
  export declare function quotaPacerFileStore(dir: string): QuotaPacerStateStore;
17
- /**
18
- * Completion-anchored poll pacing for ONE vendor lane of the quota registry
19
- * (one instance per vendor with a registered refresher), so a permanently
20
- * unsatisfiable subject of one vendor can no longer pin every other vendor's
21
- * refresh cadence at the 15-minute ceiling. Two independent gates:
22
- *
23
- * - the credential-demand backoff (`failures`/`notBefore`): in-memory,
24
- * exponential per unsatisfied cycle, reset by a credential change — the
25
- * pre-existing semantics, now per lane;
26
- * - the vendor rate-limit floor (`rateLimitedNotBefore`): armed when a cycle
27
- * observes a typed `rate_limited` absence for this vendor, honoring the
28
- * vendor's Retry-After when known (max with the exponential ladder, 60s
29
- * minimum). Persisted through the daemon-private store so a restart is not
30
- * a 429 amplifier, and deliberately NOT reset by a credential change —
31
- * logging in again does not un-rate-limit the vendor endpoint.
32
- *
33
- * Evidence/demand semantics stay with QuotaRegistry; this class owns only
34
- * scheduling state. Single-flight of the poll sweep also lives in the
35
- * registry, which drives every lane from one sweep.
36
- */
19
+ /** One vendor lane's demand ladder plus subject/route poll floors. Healthy
20
+ * renewals retain the existing single serial sweep. Only legacy persisted
21
+ * vendor floors suppress the whole lane; new 429s never infer vendor scope. */
37
22
  export declare class QuotaPollPacer {
38
23
  private readonly vendor;
39
24
  private readonly store?;
25
+ private readonly subjectFloors;
26
+ private credentialAliases;
27
+ private readonly subjectFailures;
40
28
  private failures;
41
29
  private notBefore;
42
30
  /** Completion instant of the cycle that armed the current retry ladder
@@ -47,10 +35,8 @@ export declare class QuotaPollPacer {
47
35
  /** When the active floor was observed (0 = unknown, e.g. store-loaded). */
48
36
  private rateLimitedSince;
49
37
  constructor(vendor?: string | null, store?: QuotaPacerStateStore | undefined);
50
- /** Credential/routability change: drop only the credential-demand backoff.
51
- * The vendor rate-limit floor survives — it is about the vendor endpoint,
52
- * not about which credentials exist (and a daemon restart or profile toggle
53
- * must not become a 429 amplifier). */
38
+ /** Credential/routability changes reset retry ladders, never recorded poll
39
+ * floors: a profile toggle or restart must not become a 429 amplifier. */
54
40
  noteCredentialChange(): void;
55
41
  /** May the lane poll now? The vendor rate-limit floor is absolute. The retry
56
42
  * ladder is not: `renewalDueObservedAt` — the latest observation instant of
@@ -60,14 +46,23 @@ export declare class QuotaPollPacer {
60
46
  * ladder was armed and the ladder must not postpone its renewal. Evidence
61
47
  * the arming cycle itself produced is capped by `armBackoff` instead. */
62
48
  pollEligible(now: number, renewalDueObservedAt?: number | null): boolean;
63
- /** The active vendor rate-limit floor, or null when none is in effect. */
64
- rateLimitCooldownUntil(now: number): number | null;
49
+ /** Active floor for a subject, or legacy whole-lane floor when omitted. */
50
+ rateLimitCooldownUntil(now: number, subject?: QuotaSubject): number | null;
51
+ /** Bind the current source's token-identical rows without retaining tokens or
52
+ * hashes on disk. Pre-binding the complete read makes profile order irrelevant
53
+ * after restart: an alias preceding the cooled row inherits its saved floor. */
54
+ bindCredentials(bindings: ReadonlyArray<{
55
+ subject: QuotaSubject;
56
+ credentialHash: string;
57
+ }>): void;
58
+ private subjectFloor;
59
+ private saveSubjectFloor;
65
60
  /** Stable observation stamp for the ACTIVE floor's derived gap rows: the
66
61
  * instant the floor was observed. A store-loaded floor (daemon restart)
67
62
  * has no recorded observation, so the first read anchors it — stability of
68
63
  * the projection signature matters more than the exact historical instant,
69
64
  * and the anchor is honest ("known paused since at least then"). */
70
- rateLimitObservedAt(now: number): number;
65
+ rateLimitObservedAt(now: number, subject?: QuotaSubject): number;
71
66
  /** A cycle for this lane completed: reset on fully satisfied demand, else
72
67
  * exponential backoff anchored at completion (never the stale start) and
73
68
  * capped at `renewalNotBefore` — the tick by which evidence this cycle DID
@@ -77,11 +72,13 @@ export declare class QuotaPollPacer {
77
72
  * the 15-minute ceiling). Null = no satisfied evidence is due later. */
78
73
  notePollSuccess(completedAt: number, demandRemains: boolean, renewalNotBefore?: number | null): void;
79
74
  notePollFailure(completedAt: number, renewalNotBefore?: number | null): void;
80
- /** A cycle observed a typed `rate_limited` absence for this vendor: arm the
81
- * floor at max(exponential ladder, vendor Retry-After), monotonic, and
82
- * persist it (a null Retry-After — Anthropic does not always send one —
83
- * still arms the exponential-derived floor). */
84
- noteRateLimited(observedAt: number, retryAfterMs: number | null): void;
75
+ /** A primary observation resets this subject's missing-header retry ladder.
76
+ * Its already-recorded Retry-After floor is still honored until its deadline. */
77
+ noteSubjectSuccess(subject: QuotaSubject): void;
78
+ /** A 429 paces only its subject and known token-identical aliases. The
79
+ * no-header exponential ladder belongs to that identity too: a healthy
80
+ * sibling's successful renewal must neither reset it nor acquire it. */
81
+ noteRateLimited(observedAt: number, retryAfterMs: number | null, subject: QuotaSubject): void;
85
82
  private armBackoff;
86
83
  }
87
84
  //# sourceMappingURL=quota-poll-pacer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"quota-poll-pacer.d.ts","sourceRoot":"","sources":["../src/quota-poll-pacer.ts"],"names":[],"mappings":"AAWA;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD;AAED;;qCAEqC;AACrC,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAgCrE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,cAAc;IAYvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IAZzB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAK;IACtB;;mCAE+B;IAC/B,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,oBAAoB,CAAK;IACjC,2EAA2E;IAC3E,OAAO,CAAC,gBAAgB,CAAK;IAE7B,YACmB,MAAM,GAAE,MAAM,GAAG,IAAW,EAC5B,KAAK,CAAC,EAAE,oBAAoB,YAAA,EAS9C;IAED;;;2CAGuC;IACvC,oBAAoB,IAAI,IAAI,CAI3B;IAED;;;;;;6EAMyE;IACzE,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,oBAAoB,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAI7E;IAED,0EAA0E;IAC1E,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjD;IAED;;;;wEAIoE;IACpE,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGvC;IAED;;;;;;4EAMwE;IACxE,eAAe,CACb,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,OAAO,EACtB,gBAAgB,GAAE,MAAM,GAAG,IAAW,GACrC,IAAI,CAQN;IAED,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,gBAAgB,GAAE,MAAM,GAAG,IAAW,GAAG,IAAI,CAEjF;IAED;;;oDAGgD;IAChD,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAiBrE;IAED,OAAO,CAAC,UAAU;CAOnB"}
1
+ {"version":3,"file":"quota-poll-pacer.d.ts","sourceRoot":"","sources":["../src/quota-poll-pacer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAUtD;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,WAAW,CAAC,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC;IAC5C,WAAW,CAAC,CAAC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAChE;AAED;;qCAEqC;AACrC,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CA0CrE;AAMD;;+EAE+E;AAC/E,qBAAa,cAAc;IAevB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IAfzB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0D;IACxF,OAAO,CAAC,iBAAiB,CAA8C;IACvE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA4D;IAC5F,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAK;IACtB;;mCAE+B;IAC/B,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,oBAAoB,CAAK;IACjC,2EAA2E;IAC3E,OAAO,CAAC,gBAAgB,CAAK;IAE7B,YACmB,MAAM,GAAE,MAAM,GAAG,IAAW,EAC5B,KAAK,CAAC,EAAE,oBAAoB,YAAA,EAS9C;IAED;8EAC0E;IAC1E,oBAAoB,IAAI,IAAI,CAK3B;IAED;;;;;;6EAMyE;IACzE,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,oBAAoB,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAI7E;IAED,2EAA2E;IAC3E,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAIzE;IAED;;oFAEgF;IAChF,eAAe,CACb,QAAQ,EAAE,aAAa,CAAC;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC,GACzE,IAAI,CAsBN;IAED,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,gBAAgB;IAUxB;;;;wEAIoE;IACpE,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CAQ/D;IAED;;;;;;4EAMwE;IACxE,eAAe,CACb,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,OAAO,EACtB,gBAAgB,GAAE,MAAM,GAAG,IAAW,GACrC,IAAI,CAQN;IAED,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,gBAAgB,GAAE,MAAM,GAAG,IAAW,GAAG,IAAI,CAEjF;IAED;qFACiF;IACjF,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAI9C;IAED;;4EAEwE;IACxE,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAU5F;IAED,OAAO,CAAC,UAAU;CAOnB"}
@@ -15,55 +15,50 @@ export function quotaPacerFileStore(dir) {
15
15
  const read = () => {
16
16
  try {
17
17
  const parsed = JSON.parse(readFileSync(path, "utf8"));
18
- return parsed?.version === 1 &&
19
- parsed.vendors !== null &&
20
- typeof parsed.vendors === "object" &&
21
- !Array.isArray(parsed.vendors)
22
- ? parsed.vendors
18
+ const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value)
19
+ ? value
23
20
  : {};
21
+ return parsed?.version === 1 || parsed?.version === 2
22
+ ? {
23
+ vendors: record(parsed.vendors),
24
+ subjects: record(parsed.subjects),
25
+ }
26
+ : { vendors: {}, subjects: {} };
24
27
  }
25
28
  catch {
26
- return {};
29
+ return { vendors: {}, subjects: {} };
27
30
  }
28
31
  };
32
+ const load = (section, key) => {
33
+ const iso = read()[section][key]?.not_before;
34
+ const at = typeof iso === "string" ? Date.parse(iso) : Number.NaN;
35
+ return Number.isFinite(at) ? at : 0;
36
+ };
37
+ const save = (section, key, until) => {
38
+ const state = read();
39
+ state[section][key] = { not_before: new Date(until).toISOString() };
40
+ const tmp = `${path}.tmp.${process.pid}`;
41
+ writeFileSync(tmp, `${JSON.stringify({ version: 2, ...state }, null, 2)}\n`);
42
+ renameSync(tmp, path);
43
+ };
29
44
  return {
30
- load(vendor) {
31
- const iso = read()[vendor]?.not_before;
32
- const at = typeof iso === "string" ? Date.parse(iso) : Number.NaN;
33
- return Number.isFinite(at) ? at : 0;
34
- },
35
- save(vendor, notBeforeMs) {
36
- const vendors = read();
37
- vendors[vendor] = { not_before: new Date(notBeforeMs).toISOString() };
38
- const tmp = `${path}.tmp.${process.pid}`;
39
- writeFileSync(tmp, `${JSON.stringify({ version: 1, vendors }, null, 2)}\n`);
40
- renameSync(tmp, path);
41
- },
45
+ load: (vendor) => load("vendors", vendor),
46
+ loadSubject: (subject) => load("subjects", subjectKey(subject)),
47
+ saveSubject: (subject, until) => save("subjects", subjectKey(subject), until),
42
48
  };
43
49
  }
44
- /**
45
- * Completion-anchored poll pacing for ONE vendor lane of the quota registry
46
- * (one instance per vendor with a registered refresher), so a permanently
47
- * unsatisfiable subject of one vendor can no longer pin every other vendor's
48
- * refresh cadence at the 15-minute ceiling. Two independent gates:
49
- *
50
- * - the credential-demand backoff (`failures`/`notBefore`): in-memory,
51
- * exponential per unsatisfied cycle, reset by a credential change — the
52
- * pre-existing semantics, now per lane;
53
- * - the vendor rate-limit floor (`rateLimitedNotBefore`): armed when a cycle
54
- * observes a typed `rate_limited` absence for this vendor, honoring the
55
- * vendor's Retry-After when known (max with the exponential ladder, 60s
56
- * minimum). Persisted through the daemon-private store so a restart is not
57
- * a 429 amplifier, and deliberately NOT reset by a credential change —
58
- * logging in again does not un-rate-limit the vendor endpoint.
59
- *
60
- * Evidence/demand semantics stay with QuotaRegistry; this class owns only
61
- * scheduling state. Single-flight of the poll sweep also lives in the
62
- * registry, which drives every lane from one sweep.
63
- */
50
+ function subjectKey(subject) {
51
+ return JSON.stringify([subject.harness, subject.credential_route, subject.subject_id]);
52
+ }
53
+ /** One vendor lane's demand ladder plus subject/route poll floors. Healthy
54
+ * renewals retain the existing single serial sweep. Only legacy persisted
55
+ * vendor floors suppress the whole lane; new 429s never infer vendor scope. */
64
56
  export class QuotaPollPacer {
65
57
  vendor;
66
58
  store;
59
+ subjectFloors = new Map();
60
+ credentialAliases = new Map();
61
+ subjectFailures = new Map();
67
62
  failures = 0;
68
63
  notBefore = 0;
69
64
  /** Completion instant of the cycle that armed the current retry ladder
@@ -85,14 +80,13 @@ export class QuotaPollPacer {
85
80
  }
86
81
  }
87
82
  }
88
- /** Credential/routability change: drop only the credential-demand backoff.
89
- * The vendor rate-limit floor survives — it is about the vendor endpoint,
90
- * not about which credentials exist (and a daemon restart or profile toggle
91
- * must not become a 429 amplifier). */
83
+ /** Credential/routability changes reset retry ladders, never recorded poll
84
+ * floors: a profile toggle or restart must not become a 429 amplifier. */
92
85
  noteCredentialChange() {
93
86
  this.failures = 0;
94
87
  this.notBefore = 0;
95
88
  this.armedAt = 0;
89
+ this.subjectFailures.clear();
96
90
  }
97
91
  /** May the lane poll now? The vendor rate-limit floor is absolute. The retry
98
92
  * ladder is not: `renewalDueObservedAt` — the latest observation instant of
@@ -108,16 +102,74 @@ export class QuotaPollPacer {
108
102
  return true;
109
103
  return renewalDueObservedAt !== null && renewalDueObservedAt > this.armedAt;
110
104
  }
111
- /** The active vendor rate-limit floor, or null when none is in effect. */
112
- rateLimitCooldownUntil(now) {
113
- return now < this.rateLimitedNotBefore ? this.rateLimitedNotBefore : null;
105
+ /** Active floor for a subject, or legacy whole-lane floor when omitted. */
106
+ rateLimitCooldownUntil(now, subject) {
107
+ const floor = subject === undefined ? 0 : this.subjectFloor(subject).until;
108
+ const until = Math.max(this.rateLimitedNotBefore, floor);
109
+ return now < until ? until : null;
110
+ }
111
+ /** Bind the current source's token-identical rows without retaining tokens or
112
+ * hashes on disk. Pre-binding the complete read makes profile order irrelevant
113
+ * after restart: an alias preceding the cooled row inherits its saved floor. */
114
+ bindCredentials(bindings) {
115
+ const groups = new Map();
116
+ for (const { subject, credentialHash } of bindings) {
117
+ const identity = JSON.stringify([subject.harness, subject.credential_route, credentialHash]);
118
+ const group = groups.get(identity) ?? [];
119
+ group.push(subject);
120
+ groups.set(identity, group);
121
+ }
122
+ this.credentialAliases = new Map();
123
+ for (const group of groups.values()) {
124
+ const floor = group.reduce((current, subject) => {
125
+ const candidate = this.subjectFloor(subject);
126
+ return candidate.until > current.until ? candidate : current;
127
+ }, { until: 0, observed: 0 });
128
+ for (const subject of group) {
129
+ this.credentialAliases.set(subjectKey(subject), group);
130
+ this.saveSubjectFloor(subject, floor.until, floor.observed);
131
+ }
132
+ }
133
+ }
134
+ subjectFloor(subject) {
135
+ const key = subjectKey(subject);
136
+ let floor = this.subjectFloors.get(key);
137
+ if (floor === undefined) {
138
+ let until = 0;
139
+ try {
140
+ until = this.store?.loadSubject?.(subject) ?? 0;
141
+ }
142
+ catch {
143
+ /* fail-open */
144
+ }
145
+ floor = { until, observed: 0 };
146
+ this.subjectFloors.set(key, floor);
147
+ }
148
+ return floor;
149
+ }
150
+ saveSubjectFloor(subject, until, observed) {
151
+ if (until <= this.subjectFloor(subject).until)
152
+ return;
153
+ this.subjectFloors.set(subjectKey(subject), { until, observed });
154
+ try {
155
+ this.store?.saveSubject?.(subject, until);
156
+ }
157
+ catch {
158
+ /* fail-open */
159
+ }
114
160
  }
115
161
  /** Stable observation stamp for the ACTIVE floor's derived gap rows: the
116
162
  * instant the floor was observed. A store-loaded floor (daemon restart)
117
163
  * has no recorded observation, so the first read anchors it — stability of
118
164
  * the projection signature matters more than the exact historical instant,
119
165
  * and the anchor is honest ("known paused since at least then"). */
120
- rateLimitObservedAt(now) {
166
+ rateLimitObservedAt(now, subject) {
167
+ if (subject !== undefined && this.subjectFloor(subject).until > this.rateLimitedNotBefore) {
168
+ const floor = this.subjectFloor(subject);
169
+ if (floor.observed === 0)
170
+ floor.observed = now;
171
+ return floor.observed;
172
+ }
121
173
  if (this.rateLimitedSince === 0)
122
174
  this.rateLimitedSince = now;
123
175
  return this.rateLimitedSince;
@@ -141,25 +193,25 @@ export class QuotaPollPacer {
141
193
  notePollFailure(completedAt, renewalNotBefore = null) {
142
194
  this.armBackoff(completedAt, renewalNotBefore);
143
195
  }
144
- /** A cycle observed a typed `rate_limited` absence for this vendor: arm the
145
- * floor at max(exponential ladder, vendor Retry-After), monotonic, and
146
- * persist it (a null Retry-After — Anthropic does not always send one —
147
- * still arms the exponential-derived floor). */
148
- noteRateLimited(observedAt, retryAfterMs) {
149
- const exponential = Math.min(POLL_BACKOFF_MS * 2 ** Math.max(this.failures - 1, 0), MAX_POLL_BACKOFF_MS);
196
+ /** A primary observation resets this subject's missing-header retry ladder.
197
+ * Its already-recorded Retry-After floor is still honored until its deadline. */
198
+ noteSubjectSuccess(subject) {
199
+ for (const alias of this.credentialAliases.get(subjectKey(subject)) ?? [subject]) {
200
+ this.subjectFailures.delete(subjectKey(alias));
201
+ }
202
+ }
203
+ /** A 429 paces only its subject and known token-identical aliases. The
204
+ * no-header exponential ladder belongs to that identity too: a healthy
205
+ * sibling's successful renewal must neither reset it nor acquire it. */
206
+ noteRateLimited(observedAt, retryAfterMs, subject) {
207
+ const prior = this.subjectFailures.get(subjectKey(subject));
208
+ const count = prior?.observedAt === observedAt ? prior.count : (prior?.count ?? 0) + 1;
209
+ const exponential = Math.min(POLL_BACKOFF_MS * 2 ** (count - 1), MAX_POLL_BACKOFF_MS);
150
210
  const floor = Math.max(exponential, Math.min(retryAfterMs ?? 0, MAX_RATE_LIMIT_FLOOR_MS));
151
211
  const until = observedAt + floor;
152
- if (until <= this.rateLimitedNotBefore)
153
- return;
154
- this.rateLimitedNotBefore = until;
155
- this.rateLimitedSince = observedAt;
156
- if (this.vendor !== null) {
157
- try {
158
- this.store?.save(this.vendor, until);
159
- }
160
- catch {
161
- /* fail-open: losing the durable floor only re-allows polling */
162
- }
212
+ for (const alias of this.credentialAliases.get(subjectKey(subject)) ?? [subject]) {
213
+ this.subjectFailures.set(subjectKey(alias), { observedAt, count });
214
+ this.saveSubjectFloor(alias, until, observedAt);
163
215
  }
164
216
  }
165
217
  armBackoff(completedAt, renewalNotBefore) {
@@ -1 +1 @@
1
- {"version":3,"file":"quota-poll-pacer.js","sourceRoot":"","sources":["../src/quota-poll-pacer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"quota-poll-pacer.js","sourceRoot":"","sources":["../src/quota-poll-pacer.ts"],"names":[],"mappings":"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"}
@@ -15,11 +15,85 @@ export declare function reactiveCooldownSnapshot(input: {
15
15
  source: QuotaSource;
16
16
  existing: QuotaSnapshot | undefined;
17
17
  }, now: Date): QuotaSnapshot;
18
+ /** Freshness-annotated snapshots with expired (>24h) observations pruned.
19
+ * An old observation whose constraint still EXTENDS into the future (a
20
+ * weekly cooldown/reset seen once) is kept and stale-marked: pruning it
21
+ * would hide a live cap from both the footer and the router's ledger. */
22
+ export declare function activeQuotaSnapshots(snapshots: readonly QuotaSnapshot[], now: number): QuotaSnapshot[];
18
23
  export declare const QUOTA_FRESHNESS_TTL_MS: number;
19
24
  /** Same quota EVIDENCE: everything but the observation time (a freshness flip
20
25
  * is evidence). A poll that only re-observed unchanged evidence keeps the fresh
21
26
  * `observed_at` in memory without a journal frame. */
22
27
  export declare function sameQuotaEvidence(a: QuotaSnapshot, b: QuotaSnapshot): boolean;
28
+ /** Retire only restrictions superseded by a later authenticated primary read.
29
+ * The registry must journal a changed result even when the primary read only
30
+ * changed its timestamp. Keep the old observation's time and unrelated facts. */
31
+ export declare function withoutSupersededQuotaConstraints(existing: QuotaSnapshot, observation: QuotaSnapshot, now: Date): QuotaSnapshot;
32
+ /** One reconciliation over the current raw snapshots; the registry commits all
33
+ * returned facts together before updating any in-memory projection. */
34
+ export declare function reconcileQuotaSnapshot(value: QuotaSnapshot, existing: readonly QuotaSnapshot[], now: Date): {
35
+ snapshot: {
36
+ subject: {
37
+ harness: string;
38
+ credential_route: "local" | "managed_api_key" | "vendor_native";
39
+ plan_label: string | null;
40
+ subject_id: string | null;
41
+ };
42
+ constraints: {
43
+ id: string;
44
+ label: string;
45
+ applies_to_models?: string[] | null | undefined;
46
+ applies_to_model_prefixes?: string[] | undefined;
47
+ applies_to_unspecified_model?: boolean | undefined;
48
+ used_ratio: number | null;
49
+ window_seconds: number | null;
50
+ resets_at: string | null;
51
+ cooldown_until: string | null;
52
+ }[];
53
+ source: "agy_command_usage" | "claude_api_retry" | "claude_oauth_usage" | "claude_rate_limit_event" | "claude_statusline" | "codex_app_server" | "codex_app_server_event" | "codex_rollout" | "cursor_rate_limit";
54
+ observed_at: string;
55
+ freshness: "fresh" | "stale" | "unknown";
56
+ };
57
+ updates: {
58
+ subject: {
59
+ harness: string;
60
+ credential_route: "local" | "managed_api_key" | "vendor_native";
61
+ plan_label: string | null;
62
+ subject_id: string | null;
63
+ };
64
+ constraints: {
65
+ id: string;
66
+ label: string;
67
+ applies_to_models?: string[] | null | undefined;
68
+ applies_to_model_prefixes?: string[] | undefined;
69
+ applies_to_unspecified_model?: boolean | undefined;
70
+ used_ratio: number | null;
71
+ window_seconds: number | null;
72
+ resets_at: string | null;
73
+ cooldown_until: string | null;
74
+ }[];
75
+ source: "agy_command_usage" | "claude_api_retry" | "claude_oauth_usage" | "claude_rate_limit_event" | "claude_statusline" | "codex_app_server" | "codex_app_server_event" | "codex_rollout" | "cursor_rate_limit";
76
+ observed_at: string;
77
+ freshness: "fresh" | "stale" | "unknown";
78
+ }[];
79
+ supersessions: {
80
+ subject: {
81
+ harness: string;
82
+ credential_route: "local" | "managed_api_key" | "vendor_native";
83
+ plan_label: string | null;
84
+ subject_id: string | null;
85
+ };
86
+ observed_at: string;
87
+ version: 1;
88
+ snapshot_id: string;
89
+ }[];
90
+ reconciled: boolean;
91
+ };
92
+ /** Existing prepare/upsert wire shape, reusable for an atomic reconciliation. */
93
+ export declare function quotaSnapshotRecords(snapshot: QuotaSnapshot): Array<{
94
+ type: string;
95
+ payload: unknown;
96
+ }>;
23
97
  export declare function snapshotKey(snapshot: QuotaSnapshot): string;
24
98
  /** Exact durable payload accepted by the strict v3.2.0 quota schemas. Keep an
25
99
  * explicit allowlist at every nested level so a future additive field cannot
@@ -1 +1 @@
1
- {"version":3,"file":"quota-registry-support.d.ts","sourceRoot":"","sources":["../src/quota-registry-support.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAG3B;;2EAE2E;AAC3E,wBAAgB,wBAAwB,CACtC,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,eAAe,CAAC;IACjC,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC;CACrC,EACD,GAAG,EAAE,IAAI,GACR,aAAa,CAyCf;AAED,eAAO,MAAM,sBAAsB,QAAa,CAAC;AAEjD;;sDAEsD;AACtD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAE7E;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAE3D;AAED;;6DAE6D;AAC7D,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,GAAG,aAAa,CAoBzE;AAED,wBAAgB,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAM3E;AAED;;;;oDAIoD;AACpD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAQlE;AAED;;+EAE+E;AAC/E,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzF;AAOD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC/B,UAAU,EAAE,eAAe,EAC3B,GAAG,EAAE,MAAM,GACV,OAAO,CAQT;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,MAAM,GACV,aAAa,GAAG,IAAI,CAMtB"}
1
+ {"version":3,"file":"quota-registry-support.d.ts","sourceRoot":"","sources":["../src/quota-registry-support.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EASL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAG3B;;2EAE2E;AAC3E,wBAAgB,wBAAwB,CACtC,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,eAAe,CAAC;IACjC,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC;CACrC,EACD,GAAG,EAAE,IAAI,GACR,aAAa,CAyCf;AAMD;;;yEAGyE;AACzE,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,aAAa,EAAE,EACnC,GAAG,EAAE,MAAM,GACV,aAAa,EAAE,CAgBjB;AAED,eAAO,MAAM,sBAAsB,QAAa,CAAC;AAEjD;;sDAEsD;AACtD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAE7E;AAED;;iFAEiF;AACjF,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,aAAa,EACvB,WAAW,EAAE,aAAa,EAC1B,GAAG,EAAE,IAAI,GACR,aAAa,CAqCf;AAyDD;uEACuE;AACvE,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,GAAG,EAAE,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBV;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,aAAa,GACtB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB3C;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAE3D;AAED;;6DAE6D;AAC7D,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,GAAG,aAAa,CAoBzE;AAED,wBAAgB,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAM3E;AAED;;;;oDAIoD;AACpD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAQlE;AAED;;+EAE+E;AAC/E,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzF;AAOD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC/B,UAAU,EAAE,eAAe,EAC3B,GAAG,EAAE,MAAM,GACV,OAAO,CAQT;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,MAAM,GACV,aAAa,GAAG,IAAI,CAMtB"}