@theokit/sdk 5.10.0 → 5.11.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/CHANGELOG.md +59 -0
- package/dist/{agent-TLRURB7W.cjs → agent-5DOR5T3E.cjs} +7 -6
- package/dist/{agent-TLRURB7W.cjs.map → agent-5DOR5T3E.cjs.map} +1 -1
- package/dist/{agent-XTLAVVYC.js → agent-GWKRARAL.js} +6 -5
- package/dist/{agent-XTLAVVYC.js.map → agent-GWKRARAL.js.map} +1 -1
- package/dist/{chunk-RTE2DD4S.js → chunk-4PHSVR2R.js} +50 -32
- package/dist/chunk-4PHSVR2R.js.map +1 -0
- package/dist/{chunk-PCLRIQPX.cjs → chunk-5H2GJXGC.cjs} +4 -4
- package/dist/{chunk-PCLRIQPX.cjs.map → chunk-5H2GJXGC.cjs.map} +1 -1
- package/dist/{chunk-CVEQ5366.cjs → chunk-5OHB5HJF.cjs} +90 -72
- package/dist/chunk-5OHB5HJF.cjs.map +1 -0
- package/dist/{chunk-EVH3OUMY.js → chunk-74OA5DIM.js} +189 -43
- package/dist/chunk-74OA5DIM.js.map +1 -0
- package/dist/{chunk-EXI7EHDN.js → chunk-7Y23B7XX.js} +3 -3
- package/dist/{chunk-EXI7EHDN.js.map → chunk-7Y23B7XX.js.map} +1 -1
- package/dist/{chunk-QY3CIMUV.cjs → chunk-BZKF7TXX.cjs} +192 -44
- package/dist/chunk-BZKF7TXX.cjs.map +1 -0
- package/dist/chunk-LZJXEPEQ.js +58 -0
- package/dist/chunk-LZJXEPEQ.js.map +1 -0
- package/dist/{chunk-FR6COQXZ.cjs → chunk-N7ZIPW2N.cjs} +3 -3
- package/dist/{chunk-FR6COQXZ.cjs.map → chunk-N7ZIPW2N.cjs.map} +1 -1
- package/dist/chunk-NWS2CS3Y.cjs +61 -0
- package/dist/chunk-NWS2CS3Y.cjs.map +1 -0
- package/dist/{chunk-523O5UYB.js → chunk-Z5BAFHPU.js} +3 -3
- package/dist/{chunk-523O5UYB.js.map → chunk-Z5BAFHPU.js.map} +1 -1
- package/dist/{compact-session-4QHBBGEY.cjs → compact-session-2AETONOS.cjs} +12 -12
- package/dist/{compact-session-4QHBBGEY.cjs.map → compact-session-2AETONOS.cjs.map} +1 -1
- package/dist/{compact-session-PVAWTKKL.js → compact-session-F2UDGZCN.js} +3 -3
- package/dist/{compact-session-PVAWTKKL.js.map → compact-session-F2UDGZCN.js.map} +1 -1
- package/dist/cron.cjs +6 -5
- package/dist/cron.js +5 -4
- package/dist/eval.cjs +5 -4
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +4 -3
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +38 -128
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +9 -104
- package/dist/index.js.map +1 -1
- package/dist/{inject-session-O24XGRR6.js → inject-session-E5UDFVLI.js} +3 -3
- package/dist/{inject-session-O24XGRR6.js.map → inject-session-E5UDFVLI.js.map} +1 -1
- package/dist/{inject-session-OMLFL2V3.cjs → inject-session-W4UKACYJ.cjs} +4 -4
- package/dist/{inject-session-OMLFL2V3.cjs.map → inject-session-W4UKACYJ.cjs.map} +1 -1
- package/dist/internal/budget/normalize-usage.d.ts +81 -0
- package/dist/internal/llm/anthropic-shared.d.ts +6 -0
- package/dist/internal/llm/openai.d.ts +3 -5
- package/dist/internal/llm/openrouter-system.d.ts +1 -0
- package/dist/internal/llm/types.d.ts +10 -5
- package/dist/models.cjs +5 -50
- package/dist/models.cjs.map +1 -1
- package/dist/models.js +3 -51
- package/dist/models.js.map +1 -1
- package/docs/error-codes.md +3 -2
- package/package.json +1 -1
- package/dist/chunk-CVEQ5366.cjs.map +0 -1
- package/dist/chunk-EVH3OUMY.js.map +0 -1
- package/dist/chunk-QY3CIMUV.cjs.map +0 -1
- package/dist/chunk-RTE2DD4S.js.map +0 -1
|
@@ -46,7 +46,7 @@ function persistTurnToTranscript(store, loc, sessionId, turn, onCompact) {
|
|
|
46
46
|
const count = (recordCounts.get(key) ?? 0) + 1;
|
|
47
47
|
recordCounts.set(key, count);
|
|
48
48
|
if (turn.autoCompact !== void 0) {
|
|
49
|
-
const { autoCompactIfNeeded } = await import('./compact-session-
|
|
49
|
+
const { autoCompactIfNeeded } = await import('./compact-session-F2UDGZCN.js');
|
|
50
50
|
const fired = await autoCompactIfNeeded({
|
|
51
51
|
store,
|
|
52
52
|
loc,
|
|
@@ -105,5 +105,5 @@ function enqueueSessionWrite(cwd, agentId, fn) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
export { appendSessionMessage, clearSession, discardSession, enqueueSessionWrite, flushSessionWrites, getSessionMessages, hydrateSession, persistTurnToTranscript };
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-7Y23B7XX.js.map
|
|
109
|
+
//# sourceMappingURL=chunk-7Y23B7XX.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/session/agent-session.ts"],"names":[],"mappings":";;;;;AAyCA,IAAM,aAAA,uBAAoB,GAAA,EAA2B;AACrD,IAAM,YAAA,uBAAmB,GAAA,EAAoB;AAStC,SAAS,oBAAA,CAAqB,SAAiB,OAAA,EAA+B;AACnF,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,GAAA,CAAI,OAAO,KAAK,EAAC;AAC3C,EAAA,QAAA,CAAS,KAAK,OAAO,CAAA;AAIrB,EAAA,QAAA,CAAS,OAAO,OAAO,CAAA;AACvB,EAAA,QAAA,CAAS,GAAA,CAAI,SAAS,QAAQ,CAAA;AAC9B,EAAA,cAAA,EAAe;AACjB;AAWO,IAAM,mBAAA,GAAsB,EAAA;AAEnC,SAAS,cAAA,GAAuB;AAC9B,EAAA,OAAO,QAAA,CAAS,OAAO,mBAAA,EAAqB;AAC1C,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,IAAA,EAAK,CAAE,MAAK,CAAE,KAAA;AACtC,IAAA,IAAI,WAAW,MAAA,EAAW;AAC1B,IAAA,QAAA,CAAS,OAAO,MAAM,CAAA;AACtB,IAAA,iBAAA,CAAkB,MAAM,CAAA;AAAA,EAC1B;AACF;AAeA,SAAS,kBAAkB,OAAA,EAAuB;AAChD,EAAA,MAAM,MAAA,GAAS,cAAc,EAAA,EAAI,OAAO,EAAE,KAAA,CAAM,CAAA,GAAI,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AACtE,EAAA,KAAA,MAAW,CAAA,IAAK,CAAC,GAAG,YAAY,CAAA,EAAG,IAAI,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAG,YAAA,CAAa,MAAA,CAAO,CAAC,CAAA;AAChF,EAAA,KAAA,MAAW,CAAA,IAAK,CAAC,GAAG,aAAA,CAAc,MAAM,CAAA,EAAG,IAAI,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAG,aAAA,CAAc,OAAO,CAAC,CAAA;AACzF,EAAA,KAAA,MAAW,CAAA,IAAK,CAAC,GAAG,YAAA,CAAa,MAAM,CAAA,EAAG,IAAI,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAG,YAAA,CAAa,OAAO,CAAC,CAAA;AACzF;AAaO,SAAS,cAAA,CAAe,KAAa,OAAA,EAAyB;AACnE,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACtC,EAAA,IAAI,OAAA,GAAU,CAAA;AAUd,EAAA,IAAI,YAAA,CAAa,MAAA,CAAO,GAAG,CAAA,EAAG,OAAA,EAAA;AAC9B,EAAA,IAAI,aAAA,CAAc,MAAA,CAAO,GAAG,CAAA,EAAG,OAAA,EAAA;AAC/B,EAAA,IAAI,YAAA,CAAa,MAAA,CAAO,GAAG,CAAA,EAAG,OAAA,EAAA;AAC9B,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,mBAAmB,OAAA,EAAmC;AACpE,EAAA,OAAO,QAAA,CAAS,GAAA,CAAI,OAAO,CAAA,IAAK,EAAC;AACnC;AAWO,SAAS,uBAAA,CACd,KAAA,EACA,GAAA,EACA,SAAA,EACA,MACA,SAAA,EACM;AACN,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,GAAA,CAAI,GAAA,EAAK,IAAI,OAAO,CAAA;AAM9C,EAAA,MAAM,OAAA,GAAA,CAAW,cAAc,GAAA,CAAI,GAAG,KAAK,OAAA,CAAQ,OAAA,EAAQ,EAAG,IAAA,CAAK,YAAY;AAC7E,IAAA,IAAI;AACF,MAAA,MAAM,WAAA,CAAY,KAAA,EAAO,GAAA,EAAK,SAAA,EAAW,IAAI,CAAA;AAC7C,MAAA,MAAM,KAAA,GAAA,CAAS,YAAA,CAAa,GAAA,CAAI,GAAG,KAAK,CAAA,IAAK,CAAA;AAC7C,MAAA,YAAA,CAAa,GAAA,CAAI,KAAK,KAAK,CAAA;AAI3B,MAAA,IAAI,IAAA,CAAK,gBAAgB,KAAA,CAAA,EAAW;AAClC,QAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,MAAM,OAAO,+BAAsB,CAAA;AACnE,QAAA,MAAM,KAAA,GAAQ,MAAM,mBAAA,CAAoB;AAAA,UACtC,KAAA;AAAA,UACA,GAAA;AAAA,UACA,SAAA;AAAA,UACA,UAAA,EAAY,KAAK,WAAA,CAAY,UAAA;AAAA,UAC7B,aAAA,EAAe,KAAK,WAAA,CAAY,aAAA;AAAA,UAChC,SAAA,EAAW,KAAA;AAAA,UACX,SAAA,EAAW,KAAK,WAAA,CAAY;AAAA,SAC7B,CAAA;AACD,QAAA,IAAI,OAAO,SAAA,IAAY;AAAA,MACzB;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,MAAM,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AACjE,MAAA,IAAA,CAAK,CAAA,+CAAA,EAAkD,GAAA,CAAI,OAAO,CAAA,GAAA,EAAM,GAAG;AAAA,CAAI,CAAA;AAAA,IACjF;AAAA,EACF,CAAC,CAAA;AACD,EAAA,aAAA,CAAc,GAAA;AAAA,IACZ,GAAA;AAAA,IACA,OAAA,CAAQ,IAAA;AAAA,MACN,MAAM,MAAA;AAAA,MACN,MAAM;AAAA;AACR,GACF;AACF;AAQA,eAAsB,cAAA,CACpB,SACA,GAAA,EACe;AACf,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,GAAA,CAAI,GAAA,EAAK,OAAO,CAAA;AAC1C,EAAA,IAAI,YAAA,CAAa,GAAA,CAAI,GAAG,CAAA,EAAG;AAC3B,EAAA,YAAA,CAAa,IAAI,GAAG,CAAA;AAEpB,EAAA,MAAM,SAAA,GAAY,MAAM,mBAAA,CAAoB,GAAA,CAAI,OAAO,OAAO,CAAA;AAC9D,EAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAM5B,EAAA,QAAA,CAAS,GAAA,CAAI,SAAS,SAAS,CAAA;AACjC;AAQA,eAAsB,kBAAA,GAAoC;AACxD,EAAA,OAAO,aAAA,CAAc,OAAO,CAAA,EAAG;AAC7B,IAAA,MAAM,GAAA,GAAM,KAAA,CAAM,IAAA,CAAK,aAAA,CAAc,QAAQ,CAAA;AAC7C,IAAA,aAAA,CAAc,KAAA,EAAM;AACpB,IAAA,MAAM,OAAA,CAAQ,IAAI,GAAG,CAAA;AAAA,EACvB;AACF;AAkBO,SAAS,aAAa,OAAA,EAAuB;AAClD,EAAA,QAAA,CAAS,OAAO,OAAO,CAAA;AACzB;AAOO,SAAS,mBAAA,CACd,GAAA,EACA,OAAA,EACA,EAAA,EACY;AACZ,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACtC,EAAA,MAAM,QAAQ,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA,IAAK,QAAQ,OAAA,EAAQ;AACxD,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA;AAC5B,EAAA,aAAA,CAAc,GAAA;AAAA,IACZ,GAAA;AAAA,IACA,MAAA,CAAO,IAAA;AAAA,MACL,MAAM,MAAA;AAAA,MACN,MAAM;AAAA;AACR,GACF;AACA,EAAA,OAAO,MAAA;AACT","file":"chunk-EXI7EHDN.js","sourcesContent":["import type { SessionStore } from \"../../types/session-store.js\";\nimport {\n type PersistTurnInput,\n persistTurn,\n readSessionMessages,\n type TranscriptLocation,\n} from \"./agent-session-store.js\";\n\n/**\n * Per-agent conversation history kept across runs (and across `Agent.resume()`\n * within the same process). Lets the fixture responder recall prior facts when\n * the user asks a follow-up question.\n *\n * SE40 — disk persistence IS the native Claude-shaped `.jsonl` transcript. The\n * in-memory cache holds the narrowed `SessionMessage[]` (user/assistant text) so\n * a send can read `priorMessages` synchronously; the whole rich turn (user +\n * assistant + tool blocks) lands on disk once per send via {@link persistTurnToTranscript}\n * (fed by `run.conversation()`). Hydration on resume reconstructs the session by\n * walking the transcript DAG (`readSessionMessages`).\n *\n * @internal\n */\n\n// `SessionMessage` lives in `./session-types.ts` (leaf types file). Re-exported\n// for back-compat with downstream importers that pulled it from here.\nexport type { SessionMessage } from \"./types.js\";\n\nimport type { SessionMessage } from \"./types.js\";\n\n// M75 — the cache lives in a leaf; see session-cache.ts for the reason (cycle broken by extraction).\nexport {\n hydratedKeys,\n invalidateSessionCache,\n sessions,\n transcriptKey,\n} from \"./session-cache.js\";\n\nimport { diag } from \"../diagnostics.js\";\nimport { globalSingleton } from \"../global-singleton.js\";\nimport { hydratedKeys, sessions, transcriptKey } from \"./session-cache.js\";\n\nconst pendingWrites = new Map<string, Promise<void>>();\nconst recordCounts = new Map<string, number>();\n\n/**\n * Append a session message to the in-memory cache only. Disk persistence for the\n * whole turn happens once per send via {@link persistTurnToTranscript}; the cache\n * feeds `priorMessages` / `onBeforeSend.previousMessageCount` synchronously.\n *\n * @internal\n */\nexport function appendSessionMessage(agentId: string, message: SessionMessage): void {\n const existing = sessions.get(agentId) ?? [];\n existing.push(message);\n // `delete` + `set` reinserts at the END: a JS `Map` preserves insertion order, so the first key\n // is always the least recently touched. That is the entire LRU, with no new structure (parsimony\n // rungs 2/5 — the ordering we need is already a language guarantee).\n sessions.delete(agentId);\n sessions.set(agentId, existing);\n enforceCeiling();\n}\n\n/**\n * Ceiling on sessions kept in memory.\n *\n * The runtime only reads the **active** session; the rest is pure cache, rebuildable from the\n * on-disk transcript. 32 is deliberately generous — the primary removal path is the explicit\n * `discardSession()` at the end of the agent's life, and this ceiling is a safety net against a\n * long-lived process running hundreds of sessions (plan risk #2: a tight ceiling could evict a\n * session still referenced by an in-flight async path).\n */\nexport const MAX_CACHED_SESSIONS = 32;\n\nfunction enforceCeiling(): void {\n while (sessions.size > MAX_CACHED_SESSIONS) {\n const oldest = sessions.keys().next().value;\n if (oldest === undefined) return;\n sessions.delete(oldest);\n forgetBookkeeping(oldest);\n }\n}\n\n/**\n * Erases the bookkeeping for an `agentId` across EVERY `cwd` it appears in.\n *\n * The three maps are keyed by `transcriptKey(cwd, agentId)`; `sessions` is the only one keyed by the\n * raw `agentId`. The ceiling's first version deleted from all three using `sessions`' key — that is,\n * deleted nothing — and left `hydratedKeys` **orphaned**. Since `hydrateSession` returns early when\n * the marker is present, an evicted session came back **empty** instead of rehydrating from disk:\n * silent amnesia, and a regression new to M95, because before it nothing evicted.\n *\n * The ceiling only knows the `agentId`, not the `cwd`, so it scans by suffix — which is the format\n * `transcriptKey` produces. A scan rather than an index because these maps hold tens of entries, not\n * thousands: a reverse index here would be new structure for a problem that does not exist.\n */\nfunction forgetBookkeeping(agentId: string): void {\n const suffix = transcriptKey(\"\", agentId).slice(0 - agentId.length - 2);\n for (const k of [...hydratedKeys]) if (k.endsWith(suffix)) hydratedKeys.delete(k);\n for (const k of [...pendingWrites.keys()]) if (k.endsWith(suffix)) pendingWrites.delete(k);\n for (const k of [...recordCounts.keys()]) if (k.endsWith(suffix)) recordCounts.delete(k);\n}\n\n/**\n * Erases the agent's module bookkeeping and returns how many entries were removed.\n *\n * M95 — `invalidateSessionCache` used to clear **two** of the four maps (`sessions`, `hydratedKeys`);\n * `pendingWrites` and `recordCounts` were never touched by id, so they grew for the life of the\n * process. Neither is large per entry — the leak is in count, not volume — but a cache with no owner\n * for removal is a cache that only grows.\n *\n * Returns the count so the caller can prove the removal; a second discard returns 0, which is what\n * makes the idempotency test possible without exposing the maps.\n */\nexport function discardSession(cwd: string, agentId: string): number {\n const key = transcriptKey(cwd, agentId);\n let removed = 0;\n // `sessions` is NOT erased here — and the distinction is measured, not aesthetic. It holds the\n // readable conversation, and there is a legitimate reader AFTER dispose: the golden\n // `two-concurrent-sends-serialize` calls `getSessionMessages(agentId)` after `agent.dispose()`.\n // Erasing it here returned an empty list and broke two goldens. What bounds it is the LRU ceiling\n // above; the three below are pure bookkeeping, with no post-dispose reader.\n //\n // The key is `transcriptKey(cwd, agentId)` in ALL THREE — not the raw `agentId`. The first version\n // erased two of them by `agentId` and therefore **never erased anything**; the test did not catch\n // it because it only asserted that the SECOND call returns 0, which is true either way.\n if (hydratedKeys.delete(key)) removed++;\n if (pendingWrites.delete(key)) removed++;\n if (recordCounts.delete(key)) removed++;\n return removed;\n}\n\nexport function getSessionMessages(agentId: string): SessionMessage[] {\n return sessions.get(agentId) ?? [];\n}\n\n/**\n * Persist a full conversation turn (user + assistant + tool blocks) to the native\n * transcript. Chained per-(agent, transcript) so on-disk order matches send order,\n * and fire-and-forget so `send()` is not blocked by disk I/O. Every\n * M50 — when the caller supplies `turn.autoCompact`, size-driven auto-compaction (usage real vs\n * the model's context window) runs in this same chain, surfaced via the optional `onCompact` observer.\n *\n * @internal\n */\nexport function persistTurnToTranscript(\n store: SessionStore,\n loc: TranscriptLocation,\n sessionId: string,\n turn: PersistTurnInput,\n onCompact?: () => void,\n): void {\n const key = transcriptKey(loc.cwd, loc.agentId);\n // PRE-EXISTING debt, exposed when M75 fixed the Biome config that used to abort before\n // sweeping these files (a nested root under refactor/). It is not new code and was not touched\n // by M75; refactoring SDK internals without review would trade a visible problem for a diff\n // risky. Tracked in usetheodev/theokit-sdk#151.\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: see the reason just above\n const chained = (pendingWrites.get(key) ?? Promise.resolve()).then(async () => {\n try {\n await persistTurn(store, loc, sessionId, turn);\n const count = (recordCounts.get(key) ?? 0) + 1;\n recordCounts.set(key, count);\n // M50 — the 50-turn no-summary boundary stub is GONE (it silently amnesia'd the session).\n // Auto-compaction is now size-driven with a real summary: see `maybeAutoCompact` below,\n // invoked in this same write chain by the post-run lifecycle when usage is known.\n if (turn.autoCompact !== undefined) {\n const { autoCompactIfNeeded } = await import(\"./compact-session.js\");\n const fired = await autoCompactIfNeeded({\n store,\n loc,\n sessionId,\n usageTotal: turn.autoCompact.usageTotal,\n contextWindow: turn.autoCompact.contextWindow,\n turnCount: count,\n summarize: turn.autoCompact.summarize,\n });\n if (fired) onCompact?.();\n }\n } catch (cause) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n diag(`[theokit-sdk] session transcript write failed (${loc.agentId}): ${msg}\\n`);\n }\n });\n pendingWrites.set(\n key,\n chained.then(\n () => undefined,\n () => undefined,\n ),\n );\n}\n\n/**\n * Load the persisted transcript into the in-memory cache. Idempotent per\n * (baseDir, cwd, agentId). Call once per agent lifecycle before the first read.\n *\n * @internal\n */\nexport async function hydrateSession(\n agentId: string,\n loc: { store: SessionStore; cwd: string },\n): Promise<void> {\n const key = transcriptKey(loc.cwd, agentId);\n if (hydratedKeys.has(key)) return;\n hydratedKeys.add(key);\n\n const persisted = await readSessionMessages(loc.store, agentId);\n if (persisted.length === 0) return;\n // M51 review F4 — the DISK is the source of truth at hydration time: after an invalidation\n // (compact/inject), an in-flight turn may have repopulated the cache with a SINGLE message before\n // this hydrate ran; the old \"skip when non-empty\" guard then pinned the parent to a 1-message\n // context (history + injected pair lost until restart). The persist chain serializes writes, so\n // the disk already contains that in-flight turn — replacing is always correct.\n sessions.set(agentId, persisted);\n}\n\n/**\n * Wait for all pending transcript writes to settle. Used by tests and by the\n * agent dispose path so on-disk state matches in-memory before the caller proceeds.\n *\n * @internal\n */\nexport async function flushSessionWrites(): Promise<void> {\n while (pendingWrites.size > 0) {\n const all = Array.from(pendingWrites.values());\n pendingWrites.clear();\n await Promise.all(all);\n }\n}\n\n/**\n * Drop the in-memory message cache for `agentId` and NOTHING else — the hydration marker in\n * `hydratedKeys` deliberately stays.\n *\n * That asymmetry is the feature, not an oversight, and it is what separates this from\n * `invalidateSessionCache`, which drops both. The single caller is the personality switch's\n * `reset: true` path (`internal/personality/switch.ts`): leaving the marker set makes\n * `hydrateSession` return early, so the next send starts from an EMPTY context instead of\n * replaying the pre-reset transcript off disk. Clearing the marker here would re-hydrate the\n * conversation the reset was asked to discard.\n *\n * The same shape read as a bug one function up: `forgetBookkeeping` documents an evicted session\n * coming back empty as \"silent amnesia\". The difference is intent — eviction wants the transcript\n * back, a reset does not. Anyone reaching for this to invalidate a cache wants\n * `invalidateSessionCache(cwd, agentId)` instead.\n */\nexport function clearSession(agentId: string): void {\n sessions.delete(agentId);\n}\n\n/**\n * M50 review F5 — run `fn` serialized on the SAME per-(cwd,agentId) write chain the per-turn\n * persistence uses, so a manual `Agent.compact` can never interleave with an in-flight turn's\n * writes (boundary landing mid-turn would orphan the turn from the replay).\n */\nexport function enqueueSessionWrite<T>(\n cwd: string,\n agentId: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const key = transcriptKey(cwd, agentId);\n const prior = pendingWrites.get(key) ?? Promise.resolve();\n const result = prior.then(fn);\n pendingWrites.set(\n key,\n result.then(\n () => undefined,\n () => undefined,\n ),\n );\n return result;\n}\n\n/** Test-only: drop every cached session and hydration marker. @internal */\nexport function clearAllSessions(): void {\n sessions.clear();\n hydratedKeys.clear();\n recordCounts.clear();\n // M50 review F11 — the auto-compact attempt marks live on globalThis; tests reset them here.\n // Through the helper, with the SAME key compact-session.ts uses: two files hand-rolling one slot is\n // how two copies of a mechanism drift apart.\n globalSingleton(\n \"theokit-sdk.session.auto-compact-attempts\",\n () => new Map<string, number>(),\n ).clear();\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/session/agent-session.ts"],"names":[],"mappings":"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","file":"chunk-7Y23B7XX.js","sourcesContent":["import type { SessionStore } from \"../../types/session-store.js\";\nimport {\n type PersistTurnInput,\n persistTurn,\n readSessionMessages,\n type TranscriptLocation,\n} from \"./agent-session-store.js\";\n\n/**\n * Per-agent conversation history kept across runs (and across `Agent.resume()`\n * within the same process). Lets the fixture responder recall prior facts when\n * the user asks a follow-up question.\n *\n * SE40 — disk persistence IS the native Claude-shaped `.jsonl` transcript. The\n * in-memory cache holds the narrowed `SessionMessage[]` (user/assistant text) so\n * a send can read `priorMessages` synchronously; the whole rich turn (user +\n * assistant + tool blocks) lands on disk once per send via {@link persistTurnToTranscript}\n * (fed by `run.conversation()`). Hydration on resume reconstructs the session by\n * walking the transcript DAG (`readSessionMessages`).\n *\n * @internal\n */\n\n// `SessionMessage` lives in `./session-types.ts` (leaf types file). Re-exported\n// for back-compat with downstream importers that pulled it from here.\nexport type { SessionMessage } from \"./types.js\";\n\nimport type { SessionMessage } from \"./types.js\";\n\n// M75 — the cache lives in a leaf; see session-cache.ts for the reason (cycle broken by extraction).\nexport {\n hydratedKeys,\n invalidateSessionCache,\n sessions,\n transcriptKey,\n} from \"./session-cache.js\";\n\nimport { diag } from \"../diagnostics.js\";\nimport { globalSingleton } from \"../global-singleton.js\";\nimport { hydratedKeys, sessions, transcriptKey } from \"./session-cache.js\";\n\nconst pendingWrites = new Map<string, Promise<void>>();\nconst recordCounts = new Map<string, number>();\n\n/**\n * Append a session message to the in-memory cache only. Disk persistence for the\n * whole turn happens once per send via {@link persistTurnToTranscript}; the cache\n * feeds `priorMessages` / `onBeforeSend.previousMessageCount` synchronously.\n *\n * @internal\n */\nexport function appendSessionMessage(agentId: string, message: SessionMessage): void {\n const existing = sessions.get(agentId) ?? [];\n existing.push(message);\n // `delete` + `set` reinserts at the END: a JS `Map` preserves insertion order, so the first key\n // is always the least recently touched. That is the entire LRU, with no new structure (parsimony\n // rungs 2/5 — the ordering we need is already a language guarantee).\n sessions.delete(agentId);\n sessions.set(agentId, existing);\n enforceCeiling();\n}\n\n/**\n * Ceiling on sessions kept in memory.\n *\n * The runtime only reads the **active** session; the rest is pure cache, rebuildable from the\n * on-disk transcript. 32 is deliberately generous — the primary removal path is the explicit\n * `discardSession()` at the end of the agent's life, and this ceiling is a safety net against a\n * long-lived process running hundreds of sessions (plan risk #2: a tight ceiling could evict a\n * session still referenced by an in-flight async path).\n */\nexport const MAX_CACHED_SESSIONS = 32;\n\nfunction enforceCeiling(): void {\n while (sessions.size > MAX_CACHED_SESSIONS) {\n const oldest = sessions.keys().next().value;\n if (oldest === undefined) return;\n sessions.delete(oldest);\n forgetBookkeeping(oldest);\n }\n}\n\n/**\n * Erases the bookkeeping for an `agentId` across EVERY `cwd` it appears in.\n *\n * The three maps are keyed by `transcriptKey(cwd, agentId)`; `sessions` is the only one keyed by the\n * raw `agentId`. The ceiling's first version deleted from all three using `sessions`' key — that is,\n * deleted nothing — and left `hydratedKeys` **orphaned**. Since `hydrateSession` returns early when\n * the marker is present, an evicted session came back **empty** instead of rehydrating from disk:\n * silent amnesia, and a regression new to M95, because before it nothing evicted.\n *\n * The ceiling only knows the `agentId`, not the `cwd`, so it scans by suffix — which is the format\n * `transcriptKey` produces. A scan rather than an index because these maps hold tens of entries, not\n * thousands: a reverse index here would be new structure for a problem that does not exist.\n */\nfunction forgetBookkeeping(agentId: string): void {\n const suffix = transcriptKey(\"\", agentId).slice(0 - agentId.length - 2);\n for (const k of [...hydratedKeys]) if (k.endsWith(suffix)) hydratedKeys.delete(k);\n for (const k of [...pendingWrites.keys()]) if (k.endsWith(suffix)) pendingWrites.delete(k);\n for (const k of [...recordCounts.keys()]) if (k.endsWith(suffix)) recordCounts.delete(k);\n}\n\n/**\n * Erases the agent's module bookkeeping and returns how many entries were removed.\n *\n * M95 — `invalidateSessionCache` used to clear **two** of the four maps (`sessions`, `hydratedKeys`);\n * `pendingWrites` and `recordCounts` were never touched by id, so they grew for the life of the\n * process. Neither is large per entry — the leak is in count, not volume — but a cache with no owner\n * for removal is a cache that only grows.\n *\n * Returns the count so the caller can prove the removal; a second discard returns 0, which is what\n * makes the idempotency test possible without exposing the maps.\n */\nexport function discardSession(cwd: string, agentId: string): number {\n const key = transcriptKey(cwd, agentId);\n let removed = 0;\n // `sessions` is NOT erased here — and the distinction is measured, not aesthetic. It holds the\n // readable conversation, and there is a legitimate reader AFTER dispose: the golden\n // `two-concurrent-sends-serialize` calls `getSessionMessages(agentId)` after `agent.dispose()`.\n // Erasing it here returned an empty list and broke two goldens. What bounds it is the LRU ceiling\n // above; the three below are pure bookkeeping, with no post-dispose reader.\n //\n // The key is `transcriptKey(cwd, agentId)` in ALL THREE — not the raw `agentId`. The first version\n // erased two of them by `agentId` and therefore **never erased anything**; the test did not catch\n // it because it only asserted that the SECOND call returns 0, which is true either way.\n if (hydratedKeys.delete(key)) removed++;\n if (pendingWrites.delete(key)) removed++;\n if (recordCounts.delete(key)) removed++;\n return removed;\n}\n\nexport function getSessionMessages(agentId: string): SessionMessage[] {\n return sessions.get(agentId) ?? [];\n}\n\n/**\n * Persist a full conversation turn (user + assistant + tool blocks) to the native\n * transcript. Chained per-(agent, transcript) so on-disk order matches send order,\n * and fire-and-forget so `send()` is not blocked by disk I/O. Every\n * M50 — when the caller supplies `turn.autoCompact`, size-driven auto-compaction (usage real vs\n * the model's context window) runs in this same chain, surfaced via the optional `onCompact` observer.\n *\n * @internal\n */\nexport function persistTurnToTranscript(\n store: SessionStore,\n loc: TranscriptLocation,\n sessionId: string,\n turn: PersistTurnInput,\n onCompact?: () => void,\n): void {\n const key = transcriptKey(loc.cwd, loc.agentId);\n // PRE-EXISTING debt, exposed when M75 fixed the Biome config that used to abort before\n // sweeping these files (a nested root under refactor/). It is not new code and was not touched\n // by M75; refactoring SDK internals without review would trade a visible problem for a diff\n // risky. Tracked in usetheodev/theokit-sdk#151.\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: see the reason just above\n const chained = (pendingWrites.get(key) ?? Promise.resolve()).then(async () => {\n try {\n await persistTurn(store, loc, sessionId, turn);\n const count = (recordCounts.get(key) ?? 0) + 1;\n recordCounts.set(key, count);\n // M50 — the 50-turn no-summary boundary stub is GONE (it silently amnesia'd the session).\n // Auto-compaction is now size-driven with a real summary: see `maybeAutoCompact` below,\n // invoked in this same write chain by the post-run lifecycle when usage is known.\n if (turn.autoCompact !== undefined) {\n const { autoCompactIfNeeded } = await import(\"./compact-session.js\");\n const fired = await autoCompactIfNeeded({\n store,\n loc,\n sessionId,\n usageTotal: turn.autoCompact.usageTotal,\n contextWindow: turn.autoCompact.contextWindow,\n turnCount: count,\n summarize: turn.autoCompact.summarize,\n });\n if (fired) onCompact?.();\n }\n } catch (cause) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n diag(`[theokit-sdk] session transcript write failed (${loc.agentId}): ${msg}\\n`);\n }\n });\n pendingWrites.set(\n key,\n chained.then(\n () => undefined,\n () => undefined,\n ),\n );\n}\n\n/**\n * Load the persisted transcript into the in-memory cache. Idempotent per\n * (baseDir, cwd, agentId). Call once per agent lifecycle before the first read.\n *\n * @internal\n */\nexport async function hydrateSession(\n agentId: string,\n loc: { store: SessionStore; cwd: string },\n): Promise<void> {\n const key = transcriptKey(loc.cwd, agentId);\n if (hydratedKeys.has(key)) return;\n hydratedKeys.add(key);\n\n const persisted = await readSessionMessages(loc.store, agentId);\n if (persisted.length === 0) return;\n // M51 review F4 — the DISK is the source of truth at hydration time: after an invalidation\n // (compact/inject), an in-flight turn may have repopulated the cache with a SINGLE message before\n // this hydrate ran; the old \"skip when non-empty\" guard then pinned the parent to a 1-message\n // context (history + injected pair lost until restart). The persist chain serializes writes, so\n // the disk already contains that in-flight turn — replacing is always correct.\n sessions.set(agentId, persisted);\n}\n\n/**\n * Wait for all pending transcript writes to settle. Used by tests and by the\n * agent dispose path so on-disk state matches in-memory before the caller proceeds.\n *\n * @internal\n */\nexport async function flushSessionWrites(): Promise<void> {\n while (pendingWrites.size > 0) {\n const all = Array.from(pendingWrites.values());\n pendingWrites.clear();\n await Promise.all(all);\n }\n}\n\n/**\n * Drop the in-memory message cache for `agentId` and NOTHING else — the hydration marker in\n * `hydratedKeys` deliberately stays.\n *\n * That asymmetry is the feature, not an oversight, and it is what separates this from\n * `invalidateSessionCache`, which drops both. The single caller is the personality switch's\n * `reset: true` path (`internal/personality/switch.ts`): leaving the marker set makes\n * `hydrateSession` return early, so the next send starts from an EMPTY context instead of\n * replaying the pre-reset transcript off disk. Clearing the marker here would re-hydrate the\n * conversation the reset was asked to discard.\n *\n * The same shape read as a bug one function up: `forgetBookkeeping` documents an evicted session\n * coming back empty as \"silent amnesia\". The difference is intent — eviction wants the transcript\n * back, a reset does not. Anyone reaching for this to invalidate a cache wants\n * `invalidateSessionCache(cwd, agentId)` instead.\n */\nexport function clearSession(agentId: string): void {\n sessions.delete(agentId);\n}\n\n/**\n * M50 review F5 — run `fn` serialized on the SAME per-(cwd,agentId) write chain the per-turn\n * persistence uses, so a manual `Agent.compact` can never interleave with an in-flight turn's\n * writes (boundary landing mid-turn would orphan the turn from the replay).\n */\nexport function enqueueSessionWrite<T>(\n cwd: string,\n agentId: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const key = transcriptKey(cwd, agentId);\n const prior = pendingWrites.get(key) ?? Promise.resolve();\n const result = prior.then(fn);\n pendingWrites.set(\n key,\n result.then(\n () => undefined,\n () => undefined,\n ),\n );\n return result;\n}\n\n/** Test-only: drop every cached session and hydration marker. @internal */\nexport function clearAllSessions(): void {\n sessions.clear();\n hydratedKeys.clear();\n recordCounts.clear();\n // M50 review F11 — the auto-compact attempt marks live on globalThis; tests reset them here.\n // Through the helper, with the SAME key compact-session.ts uses: two files hand-rolling one slot is\n // how two copies of a mechanism drift apart.\n globalSingleton(\n \"theokit-sdk.session.auto-compact-attempts\",\n () => new Map<string, number>(),\n ).clear();\n}\n"]}
|
|
@@ -328,7 +328,10 @@ async function consumeAnthropicResponse(response) {
|
|
|
328
328
|
toolCalls: extractAnthropicToolCalls(data),
|
|
329
329
|
stopReason: mapAnthropicStopReason(data.stop_reason ?? null),
|
|
330
330
|
inputTokens: data.usage?.input_tokens,
|
|
331
|
-
outputTokens: data.usage?.output_tokens
|
|
331
|
+
outputTokens: data.usage?.output_tokens,
|
|
332
|
+
// B-412 — the counters the Bedrock and Vertex Anthropic clients used to drop.
|
|
333
|
+
cacheReadTokens: data.usage?.cache_read_input_tokens,
|
|
334
|
+
cacheWriteTokens: data.usage?.cache_creation_input_tokens
|
|
332
335
|
};
|
|
333
336
|
}
|
|
334
337
|
async function parseHttpErrorBody(response) {
|
|
@@ -541,13 +544,9 @@ var AnthropicClient = class {
|
|
|
541
544
|
}
|
|
542
545
|
const accumulator = new AnthropicStreamAccumulator();
|
|
543
546
|
for await (const record of parseSseStream(response.body, signal)) {
|
|
544
|
-
if (record.event === "ping"
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
parsed = JSON.parse(record.data);
|
|
548
|
-
} catch {
|
|
549
|
-
continue;
|
|
550
|
-
}
|
|
547
|
+
if (record.event === "ping") continue;
|
|
548
|
+
const parsed = parseAnthropicFrame(record);
|
|
549
|
+
if (parsed === void 0) continue;
|
|
551
550
|
const events = accumulator.consume(parsed);
|
|
552
551
|
for (const event of events) yield event;
|
|
553
552
|
}
|
|
@@ -559,6 +558,30 @@ var AnthropicClient = class {
|
|
|
559
558
|
return accumulator.finish();
|
|
560
559
|
}
|
|
561
560
|
};
|
|
561
|
+
var CONTENT_FRAMES = /* @__PURE__ */ new Set([
|
|
562
|
+
"content_block_start",
|
|
563
|
+
"content_block_delta",
|
|
564
|
+
"message_delta"
|
|
565
|
+
]);
|
|
566
|
+
function parseAnthropicFrame(record) {
|
|
567
|
+
try {
|
|
568
|
+
return JSON.parse(record.data);
|
|
569
|
+
} catch (parseErr) {
|
|
570
|
+
const event = record.event;
|
|
571
|
+
const size = `${record.data.length} chars`;
|
|
572
|
+
if (CONTENT_FRAMES.has(event)) {
|
|
573
|
+
throw new chunkUNFBHV46_cjs.NetworkError(
|
|
574
|
+
`Anthropic SSE ${event} frame (${size}) is not valid JSON; the turn would lose content`,
|
|
575
|
+
{ code: "stream_malformed_frame", cause: parseErr }
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
chunk6LHQPOMI_cjs.diag(
|
|
579
|
+
`[theokit-sdk] skipped an Anthropic SSE ${event} frame (${size}) that is not valid JSON
|
|
580
|
+
`
|
|
581
|
+
);
|
|
582
|
+
return void 0;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
562
585
|
var AnthropicStreamAccumulator = class {
|
|
563
586
|
text = "";
|
|
564
587
|
stopReason = "end_turn";
|
|
@@ -593,6 +616,11 @@ var AnthropicStreamAccumulator = class {
|
|
|
593
616
|
if (event.type === "message_delta") {
|
|
594
617
|
this.handleMessageDelta(event);
|
|
595
618
|
}
|
|
619
|
+
if (event.type === "message_start") {
|
|
620
|
+
const usage = event.message?.usage;
|
|
621
|
+
if (typeof usage?.input_tokens === "number") this.inputTokens = usage.input_tokens;
|
|
622
|
+
this.readCacheCounters(usage);
|
|
623
|
+
}
|
|
596
624
|
return [];
|
|
597
625
|
}
|
|
598
626
|
handleToolStart(start) {
|
|
@@ -645,14 +673,26 @@ var AnthropicStreamAccumulator = class {
|
|
|
645
673
|
this.sawStopReason = true;
|
|
646
674
|
}
|
|
647
675
|
this.stopReason = mapAnthropicStopReason(md.delta.stop_reason);
|
|
648
|
-
|
|
649
|
-
if (
|
|
650
|
-
|
|
651
|
-
this.cacheWriteTokens = md.usage.cache_creation_input_tokens;
|
|
652
|
-
}
|
|
653
|
-
if (md.usage?.cache_read_input_tokens !== void 0 && md.usage.cache_read_input_tokens > 0) {
|
|
654
|
-
this.cacheReadTokens = md.usage.cache_read_input_tokens;
|
|
676
|
+
const input = md.usage?.input_tokens;
|
|
677
|
+
if (typeof input === "number" && (input > 0 || this.inputTokens === void 0)) {
|
|
678
|
+
this.inputTokens = input;
|
|
655
679
|
}
|
|
680
|
+
if (md.usage?.output_tokens !== void 0) this.outputTokens = md.usage.output_tokens;
|
|
681
|
+
this.readCacheCounters(md.usage);
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* T3.8 / B-412 / B-475 — the cache counters, from `message_start` and then `message_delta`. A
|
|
685
|
+
* value above 0 replaces the earlier one. A zero, a null or an absent field leaves it in place,
|
|
686
|
+
* so a `message_delta` that zero-fills its usage does not erase what `message_start` reported;
|
|
687
|
+
* a counter no event reports above 0 stays unreported (usage-accumulator strips zero entries).
|
|
688
|
+
* The `typeof` check is there because the wire is JSON: a null arrives although the type
|
|
689
|
+
* declares none.
|
|
690
|
+
*/
|
|
691
|
+
readCacheCounters(usage) {
|
|
692
|
+
const write = usage?.cache_creation_input_tokens;
|
|
693
|
+
if (typeof write === "number" && write > 0) this.cacheWriteTokens = write;
|
|
694
|
+
const read = usage?.cache_read_input_tokens;
|
|
695
|
+
if (typeof read === "number" && read > 0) this.cacheReadTokens = read;
|
|
656
696
|
}
|
|
657
697
|
/** M2 #61 — whether the terminal `message_delta` (stop_reason) was seen. */
|
|
658
698
|
get finishReasonSeen() {
|
|
@@ -748,7 +788,7 @@ async function resolveBedrockToken(region) {
|
|
|
748
788
|
return cachedToken.value;
|
|
749
789
|
}
|
|
750
790
|
try {
|
|
751
|
-
const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-
|
|
791
|
+
const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-BZKF7TXX.cjs', document.baseURI).href)));
|
|
752
792
|
const gen = r("@aws/bedrock-token-generator");
|
|
753
793
|
if (typeof gen.getToken !== "function") return void 0;
|
|
754
794
|
const providers = r("@aws-sdk/credential-providers");
|
|
@@ -1231,6 +1271,117 @@ function toOllamaTools(tools) {
|
|
|
1231
1271
|
}));
|
|
1232
1272
|
}
|
|
1233
1273
|
|
|
1274
|
+
// src/internal/budget/normalize-usage.ts
|
|
1275
|
+
function int(v) {
|
|
1276
|
+
if (typeof v === "number") return Number.isFinite(v) ? Math.max(0, Math.trunc(v)) : 0;
|
|
1277
|
+
if (typeof v === "string") {
|
|
1278
|
+
const n = Number.parseInt(v, 10);
|
|
1279
|
+
return Number.isFinite(n) ? Math.max(0, n) : 0;
|
|
1280
|
+
}
|
|
1281
|
+
return 0;
|
|
1282
|
+
}
|
|
1283
|
+
function buildTotal(buckets) {
|
|
1284
|
+
return buckets.inputTokens + buckets.outputTokens + buckets.cacheReadTokens + buckets.cacheWriteTokens;
|
|
1285
|
+
}
|
|
1286
|
+
function omitUndefined(usage) {
|
|
1287
|
+
return {
|
|
1288
|
+
inputTokens: usage.inputTokens,
|
|
1289
|
+
outputTokens: usage.outputTokens,
|
|
1290
|
+
...usage.cacheReadTokens > 0 ? { cacheReadTokens: usage.cacheReadTokens } : {},
|
|
1291
|
+
...usage.cacheWriteTokens > 0 ? { cacheWriteTokens: usage.cacheWriteTokens } : {},
|
|
1292
|
+
...usage.reasoningTokens > 0 ? { reasoningTokens: usage.reasoningTokens } : {},
|
|
1293
|
+
totalTokens: usage.totalTokens
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
function inferApiMode(provider) {
|
|
1297
|
+
const p = provider.toLowerCase();
|
|
1298
|
+
if (p === "anthropic" || p === "claude" || p === "bedrock_anthropic") {
|
|
1299
|
+
return "anthropic_messages";
|
|
1300
|
+
}
|
|
1301
|
+
if (p === "openai-codex" || p === "codex") return "openai_responses";
|
|
1302
|
+
return "openai_chat_completions";
|
|
1303
|
+
}
|
|
1304
|
+
function normalizeUsage(rawUsage, opts) {
|
|
1305
|
+
if (rawUsage === null || rawUsage === void 0) {
|
|
1306
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1307
|
+
}
|
|
1308
|
+
if (typeof rawUsage !== "object") {
|
|
1309
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
1310
|
+
}
|
|
1311
|
+
const raw = rawUsage;
|
|
1312
|
+
const mode = opts.apiMode ?? inferApiMode(opts.provider);
|
|
1313
|
+
if (mode === "anthropic_messages") return normalizeAnthropic(raw);
|
|
1314
|
+
if (mode === "openai_responses") return normalizeOpenAIResponses(raw);
|
|
1315
|
+
return normalizeOpenAIChat(raw);
|
|
1316
|
+
}
|
|
1317
|
+
function normalizeAnthropic(raw) {
|
|
1318
|
+
const inputTokens = int(raw.input_tokens);
|
|
1319
|
+
const outputTokens = int(raw.output_tokens);
|
|
1320
|
+
const cacheReadTokens = int(raw.cache_read_input_tokens);
|
|
1321
|
+
const cacheWriteTokens = int(raw.cache_creation_input_tokens);
|
|
1322
|
+
const usage = {
|
|
1323
|
+
inputTokens,
|
|
1324
|
+
outputTokens,
|
|
1325
|
+
cacheReadTokens,
|
|
1326
|
+
cacheWriteTokens,
|
|
1327
|
+
reasoningTokens: 0,
|
|
1328
|
+
totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens })
|
|
1329
|
+
};
|
|
1330
|
+
return omitUndefined(usage);
|
|
1331
|
+
}
|
|
1332
|
+
function normalizeOpenAIResponses(raw) {
|
|
1333
|
+
const inputTotal = int(raw.input_tokens);
|
|
1334
|
+
const outputTokens = int(raw.output_tokens);
|
|
1335
|
+
const inputDetails = raw.input_tokens_details ?? {};
|
|
1336
|
+
const outputDetails = raw.output_tokens_details ?? {};
|
|
1337
|
+
const cacheReadTokens = int(inputDetails.cached_tokens);
|
|
1338
|
+
const cacheWriteTokens = int(inputDetails.cache_creation_tokens);
|
|
1339
|
+
const reasoningTokens = int(outputDetails.reasoning_tokens);
|
|
1340
|
+
const inputTokens = Math.max(0, inputTotal - cacheReadTokens - cacheWriteTokens);
|
|
1341
|
+
const usage = {
|
|
1342
|
+
inputTokens,
|
|
1343
|
+
outputTokens,
|
|
1344
|
+
cacheReadTokens,
|
|
1345
|
+
cacheWriteTokens,
|
|
1346
|
+
reasoningTokens,
|
|
1347
|
+
totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens })
|
|
1348
|
+
};
|
|
1349
|
+
return omitUndefined(usage);
|
|
1350
|
+
}
|
|
1351
|
+
function sent(v) {
|
|
1352
|
+
return v !== void 0 && v !== null;
|
|
1353
|
+
}
|
|
1354
|
+
function splitChatCompletionsPrompt(raw) {
|
|
1355
|
+
const details = raw.prompt_tokens_details ?? {};
|
|
1356
|
+
const bucket = (nested, topLevel) => sent(nested) || sent(topLevel) ? int(nested) || int(topLevel) : void 0;
|
|
1357
|
+
const cacheReadTokens = bucket(details.cached_tokens, raw.cache_read_input_tokens);
|
|
1358
|
+
const cacheWriteTokens = bucket(details.cache_write_tokens, raw.cache_creation_input_tokens);
|
|
1359
|
+
const cached = (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0);
|
|
1360
|
+
return {
|
|
1361
|
+
...sent(raw.prompt_tokens) ? { inputTokens: Math.max(0, int(raw.prompt_tokens) - cached) } : {},
|
|
1362
|
+
...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
|
|
1363
|
+
...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
function normalizeOpenAIChat(raw) {
|
|
1367
|
+
const outputTokens = int(raw.completion_tokens);
|
|
1368
|
+
const completionDetails = raw.completion_tokens_details ?? {};
|
|
1369
|
+
const split = splitChatCompletionsPrompt(raw);
|
|
1370
|
+
const inputTokens = split.inputTokens ?? 0;
|
|
1371
|
+
const cacheReadTokens = split.cacheReadTokens ?? 0;
|
|
1372
|
+
const cacheWriteTokens = split.cacheWriteTokens ?? 0;
|
|
1373
|
+
const reasoningTokens = int(completionDetails.reasoning_tokens);
|
|
1374
|
+
const usage = {
|
|
1375
|
+
inputTokens,
|
|
1376
|
+
outputTokens,
|
|
1377
|
+
cacheReadTokens,
|
|
1378
|
+
cacheWriteTokens,
|
|
1379
|
+
reasoningTokens,
|
|
1380
|
+
totalTokens: buildTotal({ inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens })
|
|
1381
|
+
};
|
|
1382
|
+
return omitUndefined(usage);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1234
1385
|
// src/internal/base-url.ts
|
|
1235
1386
|
function getConfiguredBaseUrl() {
|
|
1236
1387
|
const value = process.env.THEOKIT_API_BASE_URL;
|
|
@@ -1507,6 +1658,16 @@ function assistantMessage(message) {
|
|
|
1507
1658
|
return result;
|
|
1508
1659
|
}
|
|
1509
1660
|
|
|
1661
|
+
// src/internal/llm/openrouter-system.ts
|
|
1662
|
+
function encodeOpenRouterSystemContent(system) {
|
|
1663
|
+
if (!Array.isArray(system) || !system.some((block) => block.cacheable === true)) {
|
|
1664
|
+
return void 0;
|
|
1665
|
+
}
|
|
1666
|
+
return system.map(
|
|
1667
|
+
(block) => block.cacheable === true ? { type: "text", text: block.text, cache_control: { type: "ephemeral" } } : { type: "text", text: block.text }
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1510
1671
|
// src/internal/llm/openai.ts
|
|
1511
1672
|
function deriveChatPath(baseUrl) {
|
|
1512
1673
|
try {
|
|
@@ -1649,11 +1810,8 @@ var OpenAIStreamAccumulator = class {
|
|
|
1649
1810
|
allowedToolNames;
|
|
1650
1811
|
text = "";
|
|
1651
1812
|
stopReason = "end_turn";
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
cacheReadTokens;
|
|
1655
|
-
cacheWriteTokens;
|
|
1656
|
-
reasoningTokens;
|
|
1813
|
+
/** The stream's `usage` object, a later chunk replacing an earlier one per top-level field. */
|
|
1814
|
+
usage = {};
|
|
1657
1815
|
toolCalls = /* @__PURE__ */ new Map();
|
|
1658
1816
|
/** R7: present only when recovery is enabled AND the request declares tools — holds suspected
|
|
1659
1817
|
* leaked-dialect content back from the `text_delta` stream. `undefined` ⇒ stream immediately. */
|
|
@@ -1687,18 +1845,7 @@ var OpenAIStreamAccumulator = class {
|
|
|
1687
1845
|
return { type: "reasoning_delta", text: reasoning };
|
|
1688
1846
|
}
|
|
1689
1847
|
applyUsage(usage) {
|
|
1690
|
-
if (usage
|
|
1691
|
-
if (usage?.completion_tokens !== void 0) this.outputTokens = usage.completion_tokens;
|
|
1692
|
-
const cachedDetail = usage?.prompt_tokens_details?.cached_tokens;
|
|
1693
|
-
if (cachedDetail !== void 0) this.cacheReadTokens = cachedDetail;
|
|
1694
|
-
const reasoningDetail = usage?.completion_tokens_details?.reasoning_tokens;
|
|
1695
|
-
if (reasoningDetail !== void 0) this.reasoningTokens = reasoningDetail;
|
|
1696
|
-
if (usage?.cache_read_input_tokens !== void 0) {
|
|
1697
|
-
this.cacheReadTokens = usage.cache_read_input_tokens;
|
|
1698
|
-
}
|
|
1699
|
-
if (usage?.cache_creation_input_tokens !== void 0) {
|
|
1700
|
-
this.cacheWriteTokens = usage.cache_creation_input_tokens;
|
|
1701
|
-
}
|
|
1848
|
+
if (usage !== void 0) this.usage = { ...this.usage, ...usage };
|
|
1702
1849
|
}
|
|
1703
1850
|
applyContentDelta(content) {
|
|
1704
1851
|
if (typeof content !== "string" || content.length === 0) return void 0;
|
|
@@ -1769,11 +1916,10 @@ var OpenAIStreamAccumulator = class {
|
|
|
1769
1916
|
stopReason,
|
|
1770
1917
|
text,
|
|
1771
1918
|
toolCalls,
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
reasoningTokens: this.reasoningTokens
|
|
1919
|
+
// D376: the prompt split (cache buckets out of inputTokens) has one owner, normalize-usage.ts.
|
|
1920
|
+
...splitChatCompletionsPrompt(this.usage),
|
|
1921
|
+
outputTokens: this.usage.completion_tokens,
|
|
1922
|
+
reasoningTokens: this.usage.completion_tokens_details?.reasoning_tokens
|
|
1777
1923
|
});
|
|
1778
1924
|
}
|
|
1779
1925
|
};
|
|
@@ -1794,10 +1940,10 @@ function applyToolsRequest(body, request) {
|
|
|
1794
1940
|
}
|
|
1795
1941
|
function buildOpenAIBody(request, providerName) {
|
|
1796
1942
|
const messages = [];
|
|
1943
|
+
const systemParts = providerName === "openrouter" ? encodeOpenRouterSystemContent(request.system) : void 0;
|
|
1797
1944
|
const systemText = openAISystemText(request.system);
|
|
1798
|
-
if (
|
|
1799
|
-
|
|
1800
|
-
}
|
|
1945
|
+
if (systemParts !== void 0) messages.push({ role: "system", content: systemParts });
|
|
1946
|
+
else if (systemText.length > 0) messages.push({ role: "system", content: systemText });
|
|
1801
1947
|
for (const message of request.messages) {
|
|
1802
1948
|
for (const out of toOpenAIMessages(message)) messages.push(out);
|
|
1803
1949
|
}
|
|
@@ -2516,7 +2662,7 @@ async function resolveVertexAccessToken() {
|
|
|
2516
2662
|
async function getAuthClientOrThrow() {
|
|
2517
2663
|
let mod;
|
|
2518
2664
|
try {
|
|
2519
|
-
const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-
|
|
2665
|
+
const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-BZKF7TXX.cjs', document.baseURI).href)));
|
|
2520
2666
|
mod = r("google-auth-library");
|
|
2521
2667
|
} catch (err) {
|
|
2522
2668
|
const isModuleMissing = err instanceof Error && (err.message.includes("Cannot find module") || err.code === "MODULE_NOT_FOUND");
|
|
@@ -3305,11 +3451,13 @@ exports.compactSessionTranscript = compactSessionTranscript;
|
|
|
3305
3451
|
exports.discoverProviderPlugins = discoverProviderPlugins;
|
|
3306
3452
|
exports.getConfiguredBaseUrl = getConfiguredBaseUrl;
|
|
3307
3453
|
exports.httpRequest = httpRequest;
|
|
3454
|
+
exports.inferApiMode = inferApiMode;
|
|
3308
3455
|
exports.isCompactSummary = isCompactSummary;
|
|
3309
3456
|
exports.isFixtureApiKey = isFixtureApiKey;
|
|
3310
3457
|
exports.mapOllamaHttpError = mapOllamaHttpError;
|
|
3311
3458
|
exports.mapOllamaTransportError = mapOllamaTransportError;
|
|
3312
3459
|
exports.mergeExplicitApiKey = mergeExplicitApiKey;
|
|
3460
|
+
exports.normalizeUsage = normalizeUsage;
|
|
3313
3461
|
exports.parseSseStream = parseSseStream;
|
|
3314
3462
|
exports.presentProviderCredentialEnvVars = presentProviderCredentialEnvVars;
|
|
3315
3463
|
exports.readErrorResponseBody = readErrorResponseBody;
|
|
@@ -3323,5 +3471,5 @@ exports.shouldUseFixtureMode = shouldUseFixtureMode;
|
|
|
3323
3471
|
exports.shouldUseRealLocalRuntime = shouldUseRealLocalRuntime;
|
|
3324
3472
|
exports.tryFirstEvent = tryFirstEvent;
|
|
3325
3473
|
exports.validateApiKeyShape = validateApiKeyShape;
|
|
3326
|
-
//# sourceMappingURL=chunk-
|
|
3327
|
-
//# sourceMappingURL=chunk-
|
|
3474
|
+
//# sourceMappingURL=chunk-BZKF7TXX.cjs.map
|
|
3475
|
+
//# sourceMappingURL=chunk-BZKF7TXX.cjs.map
|