instar 1.3.121 → 1.3.123
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/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +16 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +10 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +34 -2
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/TopicResumeMap.d.ts.map +1 -1
- package/dist/core/TopicResumeMap.js +18 -2
- package/dist/core/TopicResumeMap.js.map +1 -1
- package/dist/messaging/relayContentDedup.d.ts +51 -0
- package/dist/messaging/relayContentDedup.d.ts.map +1 -0
- package/dist/messaging/relayContentDedup.js +0 -0
- package/dist/messaging/relayContentDedup.js.map +1 -0
- package/dist/monitoring/frameworkActivitySignals.d.ts.map +1 -1
- package/dist/monitoring/frameworkActivitySignals.js +12 -1
- package/dist/monitoring/frameworkActivitySignals.js.map +1 -1
- package/dist/providers/adapters/openai-codex/observability/codexModelSwapPolicy.d.ts +83 -0
- package/dist/providers/adapters/openai-codex/observability/codexModelSwapPolicy.d.ts.map +1 -0
- package/dist/providers/adapters/openai-codex/observability/codexModelSwapPolicy.js +93 -0
- package/dist/providers/adapters/openai-codex/observability/codexModelSwapPolicy.js.map +1 -0
- package/dist/providers/adapters/openai-codex/observability/codexRateLimitReader.d.ts +95 -0
- package/dist/providers/adapters/openai-codex/observability/codexRateLimitReader.d.ts.map +1 -0
- package/dist/providers/adapters/openai-codex/observability/codexRateLimitReader.js +170 -0
- package/dist/providers/adapters/openai-codex/observability/codexRateLimitReader.js.map +1 -0
- package/dist/providers/adapters/openai-codex/observability/sessionPaths.d.ts +10 -0
- package/dist/providers/adapters/openai-codex/observability/sessionPaths.d.ts.map +1 -1
- package/dist/providers/adapters/openai-codex/observability/sessionPaths.js +44 -1
- package/dist/providers/adapters/openai-codex/observability/sessionPaths.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +5 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +11 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +58 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/ThreadResumeMap.d.ts.map +1 -1
- package/dist/threadline/ThreadResumeMap.js +19 -7
- package/dist/threadline/ThreadResumeMap.js.map +1 -1
- package/package.json +1 -1
- package/scripts/instar-dev-precommit.js +10 -1
- package/src/data/builtin-manifest.json +63 -63
- package/src/scaffold/templates.ts +5 -0
- package/upgrades/1.3.122.md +86 -0
- package/upgrades/1.3.123.md +105 -0
- package/upgrades/side-effects/codex-exec-json-silence-signal.md +64 -0
- package/upgrades/side-effects/codex-model-swap.md +134 -0
- package/upgrades/side-effects/codex-resume-jsonl-exists.md +68 -0
- package/upgrades/side-effects/codex-usage-visibility.md +107 -0
- package/upgrades/side-effects/ship-gate-sha-mismatch-error.md +54 -0
- package/upgrades/side-effects/threadline-relay-content-dedup.md +93 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* relayContentDedup — content-hash dedup for the agent-to-agent `relay-agent`
|
|
3
|
+
* ingress path.
|
|
4
|
+
*
|
|
5
|
+
* The id-based dedup in MessageRouter.relay keys on `message.id`. That misses a
|
|
6
|
+
* specific failure mode observed 2026-05-30: a SENDER whose HTTP call blocks on
|
|
7
|
+
* the RECEIVER's session spawn (slow on a loaded box) times out, then RETRIES
|
|
8
|
+
* the same message with a FRESH `message.id`. The retry slips past the id-based
|
|
9
|
+
* dedup → the receiver spawns/resumes and replies twice (the duplicate-reply
|
|
10
|
+
* bug — saw 6 doubled replies in one evening).
|
|
11
|
+
*
|
|
12
|
+
* This layer recognizes the retry by the STABLE triple
|
|
13
|
+
* (senderAgent, threadId, normalized content) within a short window, regardless
|
|
14
|
+
* of the message id. It is a bounded, in-memory, fixed-window dedup:
|
|
15
|
+
* - fresh (sender, thread, content) → process + record
|
|
16
|
+
* - identical within `ttlMs` of first-seen → drop (duplicate retry)
|
|
17
|
+
* - identical `ttlMs`+ after first-seen → process (plausibly a real re-send)
|
|
18
|
+
*
|
|
19
|
+
* Memory is bounded by `maxEntries` (oldest-evicted) plus a lazy sweep of
|
|
20
|
+
* expired keys on each call. The window is intentionally short so it only ever
|
|
21
|
+
* collapses true rapid retries, never two genuinely-distinct sends.
|
|
22
|
+
*/
|
|
23
|
+
export interface RelayContentDedupOpts {
|
|
24
|
+
/** Window from first-seen during which an identical inbound is a duplicate. */
|
|
25
|
+
ttlMs?: number;
|
|
26
|
+
/** Hard cap on retained keys (oldest evicted past this). */
|
|
27
|
+
maxEntries?: number;
|
|
28
|
+
/** Injectable clock for deterministic tests. */
|
|
29
|
+
now?: () => number;
|
|
30
|
+
}
|
|
31
|
+
export declare class RelayContentDedup {
|
|
32
|
+
private readonly ttlMs;
|
|
33
|
+
private readonly maxEntries;
|
|
34
|
+
private readonly now;
|
|
35
|
+
/** key -> firstSeen epoch ms (Map preserves insertion order for eviction). */
|
|
36
|
+
private readonly seen;
|
|
37
|
+
constructor(opts?: RelayContentDedupOpts);
|
|
38
|
+
/** Stable dedup key for an inbound relay message. */
|
|
39
|
+
static keyFor(senderAgent: string, threadId: string, content: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Returns true if this inbound should be processed (fresh, or the prior
|
|
42
|
+
* sighting has aged past the window); false if it is a duplicate retry seen
|
|
43
|
+
* within the window. Fresh keys are recorded. Safe to call on every inbound.
|
|
44
|
+
*/
|
|
45
|
+
shouldProcess(senderAgent: string, threadId: string, content: string): boolean;
|
|
46
|
+
/** Number of retained keys (test/observability aid). */
|
|
47
|
+
size(): number;
|
|
48
|
+
private sweep;
|
|
49
|
+
private enforceCap;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=relayContentDedup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relayContentDedup.d.ts","sourceRoot":"","sources":["../../src/messaging/relayContentDedup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,8EAA8E;IAC9E,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;gBAEtC,IAAI,GAAE,qBAA0B;IAM5C,qDAAqD;IACrD,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAU7E;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAgB9E,wDAAwD;IACxD,IAAI,IAAI,MAAM;IAId,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,UAAU;CASnB"}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relayContentDedup.js","sourceRoot":"","sources":["../../src/messaging/relayContentDedup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AAWjC,MAAM,OAAO,iBAAiB;IACX,KAAK,CAAS;IACd,UAAU,CAAS;IACnB,GAAG,CAAe;IACnC,8EAA8E;IAC7D,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IAElD,YAAY,OAA8B,EAAE;QAC1C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC;QAClC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;QAC1C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IAClC,CAAC;IAED,qDAAqD;IACrD,MAAM,CAAC,MAAM,CAAC,WAAmB,EAAE,QAAgB,EAAE,OAAe;QAClE,oEAAoE;QACpE,8CAA8C;QAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACvD,OAAO,MAAM;aACV,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC,GAAG,WAAW,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;aAClD,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,WAAmB,EAAE,QAAgB,EAAE,OAAe;QAClE,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACd,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,4EAA4E;QAC5E,4EAA4E;QAC5E,wEAAwE;QACxE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wDAAwD;IACxD,IAAI;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAEO,KAAK,CAAC,CAAS;QACrB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACjC,KAAK,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,KAAK;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAEO,UAAU;QAChB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QAClD,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACjC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,EAAE,CAAC,IAAI,QAAQ;gBAAE,MAAM;QAC7B,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frameworkActivitySignals.d.ts","sourceRoot":"","sources":["../../src/monitoring/frameworkActivitySignals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAEpF,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC;
|
|
1
|
+
{"version":3,"file":"frameworkActivitySignals.d.ts","sourceRoot":"","sources":["../../src/monitoring/frameworkActivitySignals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAEpF,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC;AAsDD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,qBAAqB,GAAG,SAAS,GAAG,IAAI,GAAG,uBAAuB,CAU9G;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,CAAC;IAAE,SAAS,EAAE,qBAAqB,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,CAAC,CAK1H"}
|
|
@@ -43,7 +43,18 @@ const CODEX_CLI_SIGNAL = {
|
|
|
43
43
|
// proxy default to "still working" forever (the 2026-05-23 stuck-session
|
|
44
44
|
// incident). The placeholder prompt "Find and fix a bug in @filename" is
|
|
45
45
|
// likewise an IDLE indicator, not work.
|
|
46
|
-
|
|
46
|
+
//
|
|
47
|
+
// codex EXEC --json mode (jobs, autonomous spawns) emits a JSON EVENT STREAM,
|
|
48
|
+
// not the TUI status line: {"type":"thread.started"}, {"type":"turn.started"},
|
|
49
|
+
// {"type":"item.started"|"item.completed"|"item.updated"}, etc. None of the
|
|
50
|
+
// TUI patterns above match that, so a working `codex exec --json` session read
|
|
51
|
+
// as NOT active → marked paused → skipped by ActiveWorkSilenceSentinel. That
|
|
52
|
+
// hid a genuinely-wedged exec-json job (frozen mid-turn ~8.5h) from the
|
|
53
|
+
// silence watchdog. Match the event-stream namespaces so an exec-json session
|
|
54
|
+
// that streamed events THEN froze is silence-eligible (the OutputActivity-
|
|
55
|
+
// Tracker's observed-change requirement still gates "frozen before we watched").
|
|
56
|
+
// These are structured JSON markers, not idle status text.
|
|
57
|
+
toolCallOrSpinner: /Working\s*\(\d+\s*s|•\s*Ran\b|exec\(|shell\(|apply_patch\(|⠋|⠙|⠹|⠸|⠼|⠴|⠦|⠧|⠇|⠏|\bgenerating\b|"type":\s*"(thread|turn|item)\./i,
|
|
47
58
|
// Codex shows a BARE "esc to interrupt" (no "press"/"hit" prefix) inside
|
|
48
59
|
// its working status line, so the Claude-style prefixed pattern never
|
|
49
60
|
// matched a real Codex pane. Match the bare form.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frameworkActivitySignals.js","sourceRoot":"","sources":["../../src/monitoring/frameworkActivitySignals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA+BH,MAAM,kBAAkB,GAA4B;IAClD,WAAW,EAAE,aAAa;IAC1B,kEAAkE;IAClE,wCAAwC;IACxC,iBAAiB,EAAE,uEAAuE;IAC1F,iBAAiB,EAAE,mBAAmB;IACtC,gBAAgB,EAAE,cAAc;IAChC,oBAAoB,EAClB,+JAA+J;CAClK,CAAC;AAEF,MAAM,gBAAgB,GAA4B;IAChD,WAAW,EAAE,WAAW;IACxB,0EAA0E;IAC1E,sEAAsE;IACtE,uEAAuE;IACvE,oDAAoD;IACpD,EAAE;IACF,+DAA+D;IAC/D,gEAAgE;IAChE,uEAAuE;IACvE,yEAAyE;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,yEAAyE;IACzE,wCAAwC;IACxC,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"frameworkActivitySignals.js","sourceRoot":"","sources":["../../src/monitoring/frameworkActivitySignals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA+BH,MAAM,kBAAkB,GAA4B;IAClD,WAAW,EAAE,aAAa;IAC1B,kEAAkE;IAClE,wCAAwC;IACxC,iBAAiB,EAAE,uEAAuE;IAC1F,iBAAiB,EAAE,mBAAmB;IACtC,gBAAgB,EAAE,cAAc;IAChC,oBAAoB,EAClB,+JAA+J;CAClK,CAAC;AAEF,MAAM,gBAAgB,GAA4B;IAChD,WAAW,EAAE,WAAW;IACxB,0EAA0E;IAC1E,sEAAsE;IACtE,uEAAuE;IACvE,oDAAoD;IACpD,EAAE;IACF,+DAA+D;IAC/D,gEAAgE;IAChE,uEAAuE;IACvE,yEAAyE;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,yEAAyE;IACzE,wCAAwC;IACxC,EAAE;IACF,8EAA8E;IAC9E,+EAA+E;IAC/E,4EAA4E;IAC5E,+EAA+E;IAC/E,6EAA6E;IAC7E,wEAAwE;IACxE,8EAA8E;IAC9E,2EAA2E;IAC3E,iFAAiF;IACjF,2DAA2D;IAC3D,iBAAiB,EAAE,gIAAgI;IACnJ,yEAAyE;IACzE,sEAAsE;IACtE,kDAAkD;IAClD,iBAAiB,EAAE,mBAAmB;IACtC,gBAAgB,EAAE,gEAAgE;IAClF,oBAAoB,EAClB,kRAAkR;CACrR,CAAC;AAEF,MAAM,gBAAgB,GAA2D;IAC/E,aAAa,EAAE,kBAAkB;IACjC,WAAW,EAAE,gBAAgB;CAC9B,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAmD;IACnF,IAAI,CAAC,SAAS;QAAE,OAAO,kBAAkB,CAAC;IAC1C,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC3C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,kEAAkE;IAClE,gEAAgE;IAChE,kEAAkE;IAClE,kEAAkE;IAClE,6CAA6C;IAC7C,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAQ,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAA6B,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAClF,SAAS;QACT,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC;KACpC,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex rate-limit model-swap policy.
|
|
3
|
+
*
|
|
4
|
+
* Justin's ask (2026-05-30): "swap to GPT-5.3-Codex-Spark when the other usage
|
|
5
|
+
* hits the limit." Codex's primary model (e.g. gpt-5.5) and a fallback like
|
|
6
|
+
* Codex-Spark draw on SEPARATE account quota buckets, so when the main model's
|
|
7
|
+
* weekly window is exhausted, launching the next session on the fallback keeps
|
|
8
|
+
* the agent working instead of stalling on a depleted window.
|
|
9
|
+
*
|
|
10
|
+
* This module is the DECISION; it holds no authority of its own. The pure
|
|
11
|
+
* `resolveCodexLaunchModel` takes a usage snapshot (from `readLatestCodexUsage`,
|
|
12
|
+
* the authoritative on-disk rate-limit reader) and the operator config and
|
|
13
|
+
* returns which model the NEXT codex session should launch with. A running
|
|
14
|
+
* session can't change model mid-turn, so the swap naturally applies at launch.
|
|
15
|
+
*
|
|
16
|
+
* Safe-by-default: ships DARK. With `enabled` false (the default) or no
|
|
17
|
+
* `fallbackModel` set, it is a no-op and adds zero overhead to the spawn path.
|
|
18
|
+
* The fallback model id is OPERATOR CONFIG — never hardcoded — because the exact
|
|
19
|
+
* Codex-Spark `--model` string + its subscription availability is the account
|
|
20
|
+
* owner's to confirm (it is not in instar's probed model list).
|
|
21
|
+
*
|
|
22
|
+
* RULE 3.1 RATIONALE
|
|
23
|
+
* Criticality: medium (cost-routing; never blocks a launch)
|
|
24
|
+
* Frequency: per codex session launch, only when enabled
|
|
25
|
+
* Stability: stable (pure decision over a structured snapshot)
|
|
26
|
+
* Fallback: no swap (launch with the requested model) on any uncertainty
|
|
27
|
+
* Verdict: pure policy + best-effort usage read; unit-tested both sides
|
|
28
|
+
*/
|
|
29
|
+
import type { CodexUsageSnapshot } from './codexRateLimitReader.js';
|
|
30
|
+
/** Operator config for the swap, read from `.instar/config.json` → codex.rateLimitModelSwap. */
|
|
31
|
+
export interface CodexModelSwapConfig {
|
|
32
|
+
/** Master switch. Default false — the feature ships dark. */
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The model to launch on when the main model's window is exhausted (e.g. the
|
|
36
|
+
* Codex-Spark model id). REQUIRED to swap — no default, because the exact id +
|
|
37
|
+
* subscription availability is the account owner's to confirm.
|
|
38
|
+
*/
|
|
39
|
+
fallbackModel?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Swap when the weekly (secondary) window's remaining percent is at or below
|
|
42
|
+
* this. Default 10. The authoritative `rate_limit_reached_type` flag also
|
|
43
|
+
* triggers a swap regardless of this threshold.
|
|
44
|
+
*/
|
|
45
|
+
weeklyRemainingThreshold?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface CodexModelSwapDecision {
|
|
48
|
+
/** The model the session should launch with (the request, unchanged, or the fallback). */
|
|
49
|
+
model: string | undefined;
|
|
50
|
+
/** True only when the policy actually substituted the fallback. */
|
|
51
|
+
swapped: boolean;
|
|
52
|
+
/** Human-readable reason when swapped, else null. */
|
|
53
|
+
reason: string | null;
|
|
54
|
+
}
|
|
55
|
+
export declare const DEFAULT_WEEKLY_REMAINING_THRESHOLD = 10;
|
|
56
|
+
/**
|
|
57
|
+
* Pure decision: given the framework, the requested model, the operator config,
|
|
58
|
+
* and the current codex usage snapshot, return which model to launch with.
|
|
59
|
+
* Never throws. Returns the requested model unchanged on any reason not to swap.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveCodexLaunchModel(params: {
|
|
62
|
+
framework: string;
|
|
63
|
+
requestedModel: string | undefined;
|
|
64
|
+
config: CodexModelSwapConfig | undefined;
|
|
65
|
+
usage: CodexUsageSnapshot | null;
|
|
66
|
+
}): CodexModelSwapDecision;
|
|
67
|
+
/**
|
|
68
|
+
* Best-effort async wrapper used on the spawn path. Reads the current codex
|
|
69
|
+
* usage (only when the feature is enabled — disabled = zero disk I/O) and
|
|
70
|
+
* applies {@link resolveCodexLaunchModel}. NEVER throws and NEVER blocks a
|
|
71
|
+
* launch: any read failure resolves to "no swap" (launch with the requested
|
|
72
|
+
* model). `readUsage` is injectable for tests.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveCodexLaunchModelWithUsage(params: {
|
|
75
|
+
framework: string;
|
|
76
|
+
requestedModel: string | undefined;
|
|
77
|
+
config: CodexModelSwapConfig | undefined;
|
|
78
|
+
codexHome?: string;
|
|
79
|
+
readUsage?: (opts: {
|
|
80
|
+
codexHome?: string;
|
|
81
|
+
}) => Promise<CodexUsageSnapshot | null>;
|
|
82
|
+
}): Promise<CodexModelSwapDecision>;
|
|
83
|
+
//# sourceMappingURL=codexModelSwapPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codexModelSwapPolicy.d.ts","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/codexModelSwapPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGpE,gGAAgG;AAChG,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACrC,0FAA0F;IAC1F,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,mEAAmE;IACnE,OAAO,EAAE,OAAO,CAAC;IACjB,qDAAqD;IACrD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,eAAO,MAAM,kCAAkC,KAAK,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,MAAM,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACzC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAClC,GAAG,sBAAsB,CA2BzB;AAED;;;;;;GAMG;AACH,wBAAsB,gCAAgC,CAAC,MAAM,EAAE;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,MAAM,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;CAClF,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAiBlC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex rate-limit model-swap policy.
|
|
3
|
+
*
|
|
4
|
+
* Justin's ask (2026-05-30): "swap to GPT-5.3-Codex-Spark when the other usage
|
|
5
|
+
* hits the limit." Codex's primary model (e.g. gpt-5.5) and a fallback like
|
|
6
|
+
* Codex-Spark draw on SEPARATE account quota buckets, so when the main model's
|
|
7
|
+
* weekly window is exhausted, launching the next session on the fallback keeps
|
|
8
|
+
* the agent working instead of stalling on a depleted window.
|
|
9
|
+
*
|
|
10
|
+
* This module is the DECISION; it holds no authority of its own. The pure
|
|
11
|
+
* `resolveCodexLaunchModel` takes a usage snapshot (from `readLatestCodexUsage`,
|
|
12
|
+
* the authoritative on-disk rate-limit reader) and the operator config and
|
|
13
|
+
* returns which model the NEXT codex session should launch with. A running
|
|
14
|
+
* session can't change model mid-turn, so the swap naturally applies at launch.
|
|
15
|
+
*
|
|
16
|
+
* Safe-by-default: ships DARK. With `enabled` false (the default) or no
|
|
17
|
+
* `fallbackModel` set, it is a no-op and adds zero overhead to the spawn path.
|
|
18
|
+
* The fallback model id is OPERATOR CONFIG — never hardcoded — because the exact
|
|
19
|
+
* Codex-Spark `--model` string + its subscription availability is the account
|
|
20
|
+
* owner's to confirm (it is not in instar's probed model list).
|
|
21
|
+
*
|
|
22
|
+
* RULE 3.1 RATIONALE
|
|
23
|
+
* Criticality: medium (cost-routing; never blocks a launch)
|
|
24
|
+
* Frequency: per codex session launch, only when enabled
|
|
25
|
+
* Stability: stable (pure decision over a structured snapshot)
|
|
26
|
+
* Fallback: no swap (launch with the requested model) on any uncertainty
|
|
27
|
+
* Verdict: pure policy + best-effort usage read; unit-tested both sides
|
|
28
|
+
*/
|
|
29
|
+
import { readLatestCodexUsage } from './codexRateLimitReader.js';
|
|
30
|
+
export const DEFAULT_WEEKLY_REMAINING_THRESHOLD = 10;
|
|
31
|
+
/**
|
|
32
|
+
* Pure decision: given the framework, the requested model, the operator config,
|
|
33
|
+
* and the current codex usage snapshot, return which model to launch with.
|
|
34
|
+
* Never throws. Returns the requested model unchanged on any reason not to swap.
|
|
35
|
+
*/
|
|
36
|
+
export function resolveCodexLaunchModel(params) {
|
|
37
|
+
const { framework, requestedModel, config, usage } = params;
|
|
38
|
+
const noSwap = { model: requestedModel, swapped: false, reason: null };
|
|
39
|
+
if (framework !== 'codex-cli')
|
|
40
|
+
return noSwap;
|
|
41
|
+
if (!config?.enabled)
|
|
42
|
+
return noSwap;
|
|
43
|
+
const fallback = config.fallbackModel;
|
|
44
|
+
if (!fallback)
|
|
45
|
+
return noSwap; // nothing to swap to — operator hasn't set the id
|
|
46
|
+
// Already on the fallback → don't re-swap (no-op / avoids a confusing log).
|
|
47
|
+
if (requestedModel && requestedModel === fallback)
|
|
48
|
+
return noSwap;
|
|
49
|
+
if (!usage)
|
|
50
|
+
return noSwap; // couldn't read usage → conservative: launch as requested
|
|
51
|
+
const threshold = config.weeklyRemainingThreshold ?? DEFAULT_WEEKLY_REMAINING_THRESHOLD;
|
|
52
|
+
const reached = usage.rateLimitReachedType; // authoritative "a window is hit NOW"
|
|
53
|
+
const weeklyRemaining = usage.secondary?.remainingPercent;
|
|
54
|
+
const reachedHit = reached !== null && reached !== undefined;
|
|
55
|
+
const weeklyLow = typeof weeklyRemaining === 'number' && weeklyRemaining <= threshold;
|
|
56
|
+
if (!reachedHit && !weeklyLow)
|
|
57
|
+
return noSwap;
|
|
58
|
+
return {
|
|
59
|
+
model: fallback,
|
|
60
|
+
swapped: true,
|
|
61
|
+
reason: reachedHit
|
|
62
|
+
? `codex rate_limit_reached_type=${reached}; swapping launch model to ${fallback}`
|
|
63
|
+
: `codex weekly window at ${weeklyRemaining}% remaining (<= ${threshold}%); swapping launch model to ${fallback}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Best-effort async wrapper used on the spawn path. Reads the current codex
|
|
68
|
+
* usage (only when the feature is enabled — disabled = zero disk I/O) and
|
|
69
|
+
* applies {@link resolveCodexLaunchModel}. NEVER throws and NEVER blocks a
|
|
70
|
+
* launch: any read failure resolves to "no swap" (launch with the requested
|
|
71
|
+
* model). `readUsage` is injectable for tests.
|
|
72
|
+
*/
|
|
73
|
+
export async function resolveCodexLaunchModelWithUsage(params) {
|
|
74
|
+
const { framework, requestedModel, config, codexHome } = params;
|
|
75
|
+
const noSwap = { model: requestedModel, swapped: false, reason: null };
|
|
76
|
+
// Fast paths that avoid any disk read when the feature can't act.
|
|
77
|
+
if (framework !== 'codex-cli')
|
|
78
|
+
return noSwap;
|
|
79
|
+
if (!config?.enabled || !config.fallbackModel)
|
|
80
|
+
return noSwap;
|
|
81
|
+
if (requestedModel && requestedModel === config.fallbackModel)
|
|
82
|
+
return noSwap;
|
|
83
|
+
const read = params.readUsage ?? readLatestCodexUsage;
|
|
84
|
+
let usage = null;
|
|
85
|
+
try {
|
|
86
|
+
usage = await read({ codexHome });
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return noSwap; // best-effort: never block a launch on a usage read
|
|
90
|
+
}
|
|
91
|
+
return resolveCodexLaunchModel({ framework, requestedModel, config, usage });
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=codexModelSwapPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codexModelSwapPolicy.js","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/codexModelSwapPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AA6BjE,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,CAAC;AAErD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAKvC;IACC,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAC5D,MAAM,MAAM,GAA2B,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAE/F,IAAI,SAAS,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO,MAAM,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC;IACtC,IAAI,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC,CAAC,kDAAkD;IAChF,4EAA4E;IAC5E,IAAI,cAAc,IAAI,cAAc,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC,CAAC,0DAA0D;IAErF,MAAM,SAAS,GAAG,MAAM,CAAC,wBAAwB,IAAI,kCAAkC,CAAC;IACxF,MAAM,OAAO,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC,sCAAsC;IAClF,MAAM,eAAe,GAAG,KAAK,CAAC,SAAS,EAAE,gBAAgB,CAAC;IAE1D,MAAM,UAAU,GAAG,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,CAAC;IAC7D,MAAM,SAAS,GAAG,OAAO,eAAe,KAAK,QAAQ,IAAI,eAAe,IAAI,SAAS,CAAC;IACtF,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC;IAE7C,OAAO;QACL,KAAK,EAAE,QAAQ;QACf,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,UAAU;YAChB,CAAC,CAAC,iCAAiC,OAAO,8BAA8B,QAAQ,EAAE;YAClF,CAAC,CAAC,0BAA0B,eAAe,mBAAmB,SAAS,gCAAgC,QAAQ,EAAE;KACpH,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CAAC,MAMtD;IACC,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAChE,MAAM,MAAM,GAA2B,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAE/F,kEAAkE;IAClE,IAAI,SAAS,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa;QAAE,OAAO,MAAM,CAAC;IAC7D,IAAI,cAAc,IAAI,cAAc,KAAK,MAAM,CAAC,aAAa;QAAE,OAAO,MAAM,CAAC;IAE7E,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,IAAI,oBAAoB,CAAC;IACtD,IAAI,KAAK,GAA8B,IAAI,CAAC;IAC5C,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,CAAC,oDAAoD;IACrE,CAAC;IACD,OAAO,uBAAuB,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the codex CLI's `/status`-equivalent rate-limit usage from the
|
|
3
|
+
* on-disk rollout JSONL stream — WITHOUT the interactive TUI.
|
|
4
|
+
*
|
|
5
|
+
* Codex has no public usage endpoint (the UsageMeterProvider falls back to
|
|
6
|
+
* local token accounting and reports isAuthoritative()=false). But the codex
|
|
7
|
+
* CLI DOES persist the authoritative account rate-limit windows it gets back
|
|
8
|
+
* from OpenAI: every turn it appends a `token_count` event to the session
|
|
9
|
+
* rollout whose `payload.rate_limits` carries the same primary (5h) /
|
|
10
|
+
* secondary (weekly) windows the TUI's `/status` shows:
|
|
11
|
+
*
|
|
12
|
+
* { "type": "event_msg",
|
|
13
|
+
* "payload": {
|
|
14
|
+
* "type": "token_count",
|
|
15
|
+
* "rate_limits": {
|
|
16
|
+
* "limit_id": "codex",
|
|
17
|
+
* "primary": { "used_percent": 13, "window_minutes": 300, "resets_at": 1780171524 },
|
|
18
|
+
* "secondary": { "used_percent": 93, "window_minutes": 10080, "resets_at": 1780174809 },
|
|
19
|
+
* "plan_type": "prolite",
|
|
20
|
+
* "rate_limit_reached_type": null } } }
|
|
21
|
+
*
|
|
22
|
+
* This reader finds the newest rollout(s), reads only the tail (rate-limit
|
|
23
|
+
* events are appended per-turn, so the freshest is near the end), and returns
|
|
24
|
+
* the most recent snapshot. It is read-only and never mutates any session
|
|
25
|
+
* state — the same discipline as the rest of the codex observability layer.
|
|
26
|
+
*
|
|
27
|
+
* RULE 3.1 RATIONALE
|
|
28
|
+
* Criticality: high (cost-routing / model-swap input)
|
|
29
|
+
* Frequency: per-poll / on-demand (route hit)
|
|
30
|
+
* Stability: semi-stable (codex rollout layout changes occasionally —
|
|
31
|
+
* shared with sessionPaths, covered by codexSessionLayoutCanary)
|
|
32
|
+
* Fallback: return null when no rollout / no token_count event is found
|
|
33
|
+
* Verdict: deterministic tail-scan; canary covers the layout
|
|
34
|
+
*/
|
|
35
|
+
/** A single rate-limit window (5h primary or weekly secondary). */
|
|
36
|
+
export interface CodexRateWindow {
|
|
37
|
+
/** Percent of the window's budget consumed (0-100). */
|
|
38
|
+
usedPercent: number;
|
|
39
|
+
/** Convenience: 100 - usedPercent, clamped to [0, 100]. */
|
|
40
|
+
remainingPercent: number;
|
|
41
|
+
/** Window length in minutes (300 = 5h, 10080 = weekly). */
|
|
42
|
+
windowMinutes: number;
|
|
43
|
+
/** Absolute reset time, unix epoch seconds (as codex reports it). */
|
|
44
|
+
resetsAt: number;
|
|
45
|
+
/** Derived ISO-8601 of `resetsAt`, or null if `resetsAt` is missing/invalid. */
|
|
46
|
+
resetsAtIso: string | null;
|
|
47
|
+
/** Seconds until reset relative to the provided clock, or null when no clock. */
|
|
48
|
+
resetsInSeconds: number | null;
|
|
49
|
+
}
|
|
50
|
+
/** A point-in-time snapshot of codex account rate-limit usage. */
|
|
51
|
+
export interface CodexUsageSnapshot {
|
|
52
|
+
source: 'codex-rollout';
|
|
53
|
+
/** The rollout file the snapshot was read from. */
|
|
54
|
+
rolloutPath: string;
|
|
55
|
+
/** Thread UUID parsed from the rollout filename, or null. */
|
|
56
|
+
threadId: string | null;
|
|
57
|
+
/** Timestamp of the token_count event the snapshot came from (ISO), or null. */
|
|
58
|
+
capturedAt: string | null;
|
|
59
|
+
/** Model in effect at capture (best-effort from the latest turn_context), or null. */
|
|
60
|
+
model: string | null;
|
|
61
|
+
/** Plan tier codex reports (e.g. "plus", "prolite"), or null. */
|
|
62
|
+
planType: string | null;
|
|
63
|
+
/**
|
|
64
|
+
* Which window (if any) is currently exhausted. codex sets this to e.g.
|
|
65
|
+
* "primary" / "secondary" when a limit is hit; null when usage is fine.
|
|
66
|
+
* This is the authoritative "we are rate-limited" signal.
|
|
67
|
+
*/
|
|
68
|
+
rateLimitReachedType: string | null;
|
|
69
|
+
/** The 5h rolling window (window_minutes 300), or null if absent. */
|
|
70
|
+
primary: CodexRateWindow | null;
|
|
71
|
+
/** The weekly rolling window (window_minutes 10080), or null if absent. */
|
|
72
|
+
secondary: CodexRateWindow | null;
|
|
73
|
+
}
|
|
74
|
+
export interface ReadCodexUsageOptions {
|
|
75
|
+
/** Override $CODEX_HOME (defaults to ~/.codex). */
|
|
76
|
+
codexHome?: string;
|
|
77
|
+
/** Clock for deriving `resetsInSeconds` (defaults to Date.now()). */
|
|
78
|
+
nowMs?: number;
|
|
79
|
+
/** How many newest rollouts to scan for a token_count event (default 8). */
|
|
80
|
+
maxRolloutsScanned?: number;
|
|
81
|
+
/** Tail size to read from each rollout (default 512 KiB). */
|
|
82
|
+
tailBytes?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Read the freshest codex rate-limit snapshot from disk. Returns null when
|
|
86
|
+
* there is no codex rollout with a rate-limit-bearing token_count event
|
|
87
|
+
* (e.g. a pure-Claude agent, or a session that has not completed a turn yet).
|
|
88
|
+
*/
|
|
89
|
+
export declare function readLatestCodexUsage(opts?: ReadCodexUsageOptions): Promise<CodexUsageSnapshot | null>;
|
|
90
|
+
/**
|
|
91
|
+
* Parse the freshest rate-limit snapshot out of a rollout tail. Exported for
|
|
92
|
+
* unit tests so they can exercise the parser without touching disk.
|
|
93
|
+
*/
|
|
94
|
+
export declare function parseUsageFromTail(tail: string, rolloutPath: string, nowMs: number): CodexUsageSnapshot | null;
|
|
95
|
+
//# sourceMappingURL=codexRateLimitReader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codexRateLimitReader.d.ts","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/codexRateLimitReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAKH,mEAAmE;AACnE,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iFAAiF;IACjF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,kEAAkE;AAClE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,eAAe,CAAC;IACxB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,gFAAgF;IAChF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,sFAAsF;IACtF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;OAIG;IACH,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,qEAAqE;IACrE,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,2EAA2E;IAC3E,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAiBpC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,kBAAkB,GAAG,IAAI,CA8C3B"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the codex CLI's `/status`-equivalent rate-limit usage from the
|
|
3
|
+
* on-disk rollout JSONL stream — WITHOUT the interactive TUI.
|
|
4
|
+
*
|
|
5
|
+
* Codex has no public usage endpoint (the UsageMeterProvider falls back to
|
|
6
|
+
* local token accounting and reports isAuthoritative()=false). But the codex
|
|
7
|
+
* CLI DOES persist the authoritative account rate-limit windows it gets back
|
|
8
|
+
* from OpenAI: every turn it appends a `token_count` event to the session
|
|
9
|
+
* rollout whose `payload.rate_limits` carries the same primary (5h) /
|
|
10
|
+
* secondary (weekly) windows the TUI's `/status` shows:
|
|
11
|
+
*
|
|
12
|
+
* { "type": "event_msg",
|
|
13
|
+
* "payload": {
|
|
14
|
+
* "type": "token_count",
|
|
15
|
+
* "rate_limits": {
|
|
16
|
+
* "limit_id": "codex",
|
|
17
|
+
* "primary": { "used_percent": 13, "window_minutes": 300, "resets_at": 1780171524 },
|
|
18
|
+
* "secondary": { "used_percent": 93, "window_minutes": 10080, "resets_at": 1780174809 },
|
|
19
|
+
* "plan_type": "prolite",
|
|
20
|
+
* "rate_limit_reached_type": null } } }
|
|
21
|
+
*
|
|
22
|
+
* This reader finds the newest rollout(s), reads only the tail (rate-limit
|
|
23
|
+
* events are appended per-turn, so the freshest is near the end), and returns
|
|
24
|
+
* the most recent snapshot. It is read-only and never mutates any session
|
|
25
|
+
* state — the same discipline as the rest of the codex observability layer.
|
|
26
|
+
*
|
|
27
|
+
* RULE 3.1 RATIONALE
|
|
28
|
+
* Criticality: high (cost-routing / model-swap input)
|
|
29
|
+
* Frequency: per-poll / on-demand (route hit)
|
|
30
|
+
* Stability: semi-stable (codex rollout layout changes occasionally —
|
|
31
|
+
* shared with sessionPaths, covered by codexSessionLayoutCanary)
|
|
32
|
+
* Fallback: return null when no rollout / no token_count event is found
|
|
33
|
+
* Verdict: deterministic tail-scan; canary covers the layout
|
|
34
|
+
*/
|
|
35
|
+
import { promises as fs } from 'node:fs';
|
|
36
|
+
import { listAllRollouts } from './sessionPaths.js';
|
|
37
|
+
const DEFAULT_TAIL_BYTES = 512 * 1024;
|
|
38
|
+
const DEFAULT_MAX_ROLLOUTS = 8;
|
|
39
|
+
const UUID_RE = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
|
|
40
|
+
/**
|
|
41
|
+
* Read the freshest codex rate-limit snapshot from disk. Returns null when
|
|
42
|
+
* there is no codex rollout with a rate-limit-bearing token_count event
|
|
43
|
+
* (e.g. a pure-Claude agent, or a session that has not completed a turn yet).
|
|
44
|
+
*/
|
|
45
|
+
export async function readLatestCodexUsage(opts = {}) {
|
|
46
|
+
const nowMs = opts.nowMs ?? Date.now();
|
|
47
|
+
const tailBytes = opts.tailBytes ?? DEFAULT_TAIL_BYTES;
|
|
48
|
+
const maxRollouts = opts.maxRolloutsScanned ?? DEFAULT_MAX_ROLLOUTS;
|
|
49
|
+
const rollouts = await listAllRollouts(opts.codexHome, maxRollouts);
|
|
50
|
+
for (const { path: rolloutPath } of rollouts) {
|
|
51
|
+
let tail;
|
|
52
|
+
try {
|
|
53
|
+
tail = await readTail(rolloutPath, tailBytes);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const snapshot = parseUsageFromTail(tail, rolloutPath, nowMs);
|
|
59
|
+
if (snapshot)
|
|
60
|
+
return snapshot;
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Parse the freshest rate-limit snapshot out of a rollout tail. Exported for
|
|
66
|
+
* unit tests so they can exercise the parser without touching disk.
|
|
67
|
+
*/
|
|
68
|
+
export function parseUsageFromTail(tail, rolloutPath, nowMs) {
|
|
69
|
+
const lines = tail.split('\n');
|
|
70
|
+
let rateLimits = null;
|
|
71
|
+
let capturedAt = null;
|
|
72
|
+
let model = null;
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
if (!line || line[0] !== '{')
|
|
75
|
+
continue;
|
|
76
|
+
// Cheap pre-filter before the JSON.parse cost.
|
|
77
|
+
const hasRate = line.includes('"rate_limits"');
|
|
78
|
+
const hasModel = line.includes('"turn_context"') && line.includes('"model"');
|
|
79
|
+
if (!hasRate && !hasModel)
|
|
80
|
+
continue;
|
|
81
|
+
let obj;
|
|
82
|
+
try {
|
|
83
|
+
obj = JSON.parse(line);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const payload = obj.payload;
|
|
89
|
+
if (!payload)
|
|
90
|
+
continue;
|
|
91
|
+
if (payload.type === 'token_count' && payload.rate_limits) {
|
|
92
|
+
rateLimits = payload.rate_limits;
|
|
93
|
+
capturedAt = typeof obj.timestamp === 'string' ? obj.timestamp : capturedAt;
|
|
94
|
+
}
|
|
95
|
+
else if (payload.type === undefined && obj.type === 'turn_context') {
|
|
96
|
+
// turn_context nests its fields directly under payload.
|
|
97
|
+
const m = payload.model;
|
|
98
|
+
if (typeof m === 'string' && m)
|
|
99
|
+
model = m;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (!rateLimits)
|
|
103
|
+
return null;
|
|
104
|
+
return {
|
|
105
|
+
source: 'codex-rollout',
|
|
106
|
+
rolloutPath,
|
|
107
|
+
threadId: parseThreadId(rolloutPath),
|
|
108
|
+
capturedAt,
|
|
109
|
+
model,
|
|
110
|
+
planType: typeof rateLimits.plan_type === 'string' ? rateLimits.plan_type : null,
|
|
111
|
+
rateLimitReachedType: typeof rateLimits.rate_limit_reached_type === 'string'
|
|
112
|
+
? rateLimits.rate_limit_reached_type
|
|
113
|
+
: null,
|
|
114
|
+
primary: parseWindow(rateLimits.primary, nowMs),
|
|
115
|
+
secondary: parseWindow(rateLimits.secondary, nowMs),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function parseWindow(raw, nowMs) {
|
|
119
|
+
if (!raw || typeof raw !== 'object')
|
|
120
|
+
return null;
|
|
121
|
+
const w = raw;
|
|
122
|
+
const usedPercent = typeof w.used_percent === 'number' ? w.used_percent : null;
|
|
123
|
+
const windowMinutes = typeof w.window_minutes === 'number' ? w.window_minutes : null;
|
|
124
|
+
const resetsAt = typeof w.resets_at === 'number' ? w.resets_at : null;
|
|
125
|
+
if (usedPercent === null || windowMinutes === null || resetsAt === null)
|
|
126
|
+
return null;
|
|
127
|
+
const validReset = Number.isFinite(resetsAt) && resetsAt > 0;
|
|
128
|
+
return {
|
|
129
|
+
usedPercent,
|
|
130
|
+
remainingPercent: clampPercent(100 - usedPercent),
|
|
131
|
+
windowMinutes,
|
|
132
|
+
resetsAt,
|
|
133
|
+
resetsAtIso: validReset ? new Date(resetsAt * 1000).toISOString() : null,
|
|
134
|
+
resetsInSeconds: validReset ? Math.round((resetsAt * 1000 - nowMs) / 1000) : null,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function clampPercent(n) {
|
|
138
|
+
if (n < 0)
|
|
139
|
+
return 0;
|
|
140
|
+
if (n > 100)
|
|
141
|
+
return 100;
|
|
142
|
+
return n;
|
|
143
|
+
}
|
|
144
|
+
function parseThreadId(rolloutPath) {
|
|
145
|
+
const m = UUID_RE.exec(rolloutPath);
|
|
146
|
+
return m ? m[1] : null;
|
|
147
|
+
}
|
|
148
|
+
async function readTail(filePath, maxBytes) {
|
|
149
|
+
const handle = await fs.open(filePath, 'r');
|
|
150
|
+
try {
|
|
151
|
+
const { size } = await handle.stat();
|
|
152
|
+
const start = size > maxBytes ? size - maxBytes : 0;
|
|
153
|
+
const length = size - start;
|
|
154
|
+
if (length <= 0)
|
|
155
|
+
return '';
|
|
156
|
+
const buf = Buffer.alloc(length);
|
|
157
|
+
await handle.read(buf, 0, length, start);
|
|
158
|
+
let text = buf.toString('utf8');
|
|
159
|
+
if (start > 0) {
|
|
160
|
+
// The first line is likely partial — drop up to the first newline.
|
|
161
|
+
const nl = text.indexOf('\n');
|
|
162
|
+
text = nl >= 0 ? text.slice(nl + 1) : '';
|
|
163
|
+
}
|
|
164
|
+
return text;
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
await handle.close();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=codexRateLimitReader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codexRateLimitReader.js","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/codexRateLimitReader.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,16 @@
|
|
|
15
15
|
export declare function codexHomeFromConfig(codexHome?: string): string;
|
|
16
16
|
/** Find a rollout JSONL file by Codex thread UUID. Returns null on miss. */
|
|
17
17
|
export declare function findRolloutFile(threadId: string, codexHome?: string): Promise<string | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Synchronous variant of {@link findRolloutFile}. Needed by callers that run on
|
|
20
|
+
* a sync code path and cannot await — notably the resume-map `jsonlExists`
|
|
21
|
+
* guards (ThreadResumeMap, TopicResumeMap), which previously checked only the
|
|
22
|
+
* Claude flat layout and so returned false for EVERY codex session (the
|
|
23
|
+
* codex-compat resume bug: every codex thread looked expired/missing). Returns
|
|
24
|
+
* the rollout path or null. A missing `$CODEX_HOME/sessions` (e.g. a pure
|
|
25
|
+
* Claude agent) returns null fast — `readdirSync` throws and is caught.
|
|
26
|
+
*/
|
|
27
|
+
export declare function findRolloutFileSync(threadId: string, codexHome?: string): string | null;
|
|
18
28
|
/** List all rollout files under $CODEX_HOME/sessions, newest first. */
|
|
19
29
|
export declare function listAllRollouts(codexHome?: string, limit?: number): Promise<ReadonlyArray<{
|
|
20
30
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionPaths.d.ts","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/sessionPaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;
|
|
1
|
+
{"version":3,"file":"sessionPaths.d.ts","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/sessionPaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIvF;AAoDD,uEAAuE;AACvE,wBAAsB,eAAe,CACnC,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,SAAM,GACV,OAAO,CAAC,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAMzD"}
|