@claudexor/daemon 3.20.1 → 3.22.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.
- package/dist/command-retention.d.ts +1 -1
- package/dist/command-retention.d.ts.map +1 -1
- package/dist/command-retention.js +20 -4
- package/dist/command-retention.js.map +1 -1
- package/dist/continuation-admission.d.ts +11 -0
- package/dist/continuation-admission.d.ts.map +1 -0
- package/dist/continuation-admission.js +16 -0
- package/dist/continuation-admission.js.map +1 -0
- package/dist/credential-unusable-ledger.d.ts +3 -8
- package/dist/credential-unusable-ledger.d.ts.map +1 -1
- package/dist/credential-unusable-ledger.js +5 -8
- package/dist/credential-unusable-ledger.js.map +1 -1
- package/dist/journal-fold-policy.d.ts.map +1 -1
- package/dist/journal-fold-policy.js +12 -1
- package/dist/journal-fold-policy.js.map +1 -1
- package/dist/quota-poll-lanes.d.ts +25 -7
- package/dist/quota-poll-lanes.d.ts.map +1 -1
- package/dist/quota-poll-lanes.js +90 -16
- package/dist/quota-poll-lanes.js.map +1 -1
- package/dist/quota-poll-pacer.d.ts +33 -36
- package/dist/quota-poll-pacer.d.ts.map +1 -1
- package/dist/quota-poll-pacer.js +115 -63
- package/dist/quota-poll-pacer.js.map +1 -1
- package/dist/quota-registry-support.d.ts +74 -0
- package/dist/quota-registry-support.d.ts.map +1 -1
- package/dist/quota-registry-support.js +153 -1
- package/dist/quota-registry-support.js.map +1 -1
- package/dist/quota-registry.d.ts +26 -4
- package/dist/quota-registry.d.ts.map +1 -1
- package/dist/quota-registry.js +60 -76
- package/dist/quota-registry.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/terminal-authority.d.ts +1 -0
- package/dist/terminal-authority.d.ts.map +1 -1
- package/package.json +6 -5
|
@@ -1,42 +1,30 @@
|
|
|
1
|
+
import type { QuotaSubject } from "@claudexor/schema";
|
|
1
2
|
/**
|
|
2
|
-
* Daemon-private persistence for
|
|
3
|
-
*
|
|
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
|
-
/**
|
|
10
|
+
/** Legacy vendor floor, retained for upgrades; 0 = none. */
|
|
10
11
|
load(vendor: string): number;
|
|
11
|
-
|
|
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
|
-
*
|
|
19
|
-
*
|
|
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
|
|
51
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
81
|
-
* floor
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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":"
|
|
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"}
|
package/dist/quota-poll-pacer.js
CHANGED
|
@@ -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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*
|
|
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
|
|
89
|
-
*
|
|
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
|
-
/**
|
|
112
|
-
rateLimitCooldownUntil(now) {
|
|
113
|
-
|
|
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
|
|
145
|
-
* floor
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
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,
|
|
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"}
|