@theokit/sdk 5.10.0 → 5.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/{agent-XTLAVVYC.js → agent-DFV52V6E.js} +8 -7
  3. package/dist/{agent-XTLAVVYC.js.map → agent-DFV52V6E.js.map} +1 -1
  4. package/dist/{agent-TLRURB7W.cjs → agent-OKP4LHG2.cjs} +9 -8
  5. package/dist/{agent-TLRURB7W.cjs.map → agent-OKP4LHG2.cjs.map} +1 -1
  6. package/dist/{chunk-CVEQ5366.cjs → chunk-35KBC4BX.cjs} +177 -119
  7. package/dist/chunk-35KBC4BX.cjs.map +1 -0
  8. package/dist/{chunk-FR6COQXZ.cjs → chunk-3JANYJYS.cjs} +3 -3
  9. package/dist/{chunk-FR6COQXZ.cjs.map → chunk-3JANYJYS.cjs.map} +1 -1
  10. package/dist/{chunk-523O5UYB.js → chunk-6G4IRQUD.js} +3 -3
  11. package/dist/{chunk-523O5UYB.js.map → chunk-6G4IRQUD.js.map} +1 -1
  12. package/dist/{chunk-EVH3OUMY.js → chunk-BBJ77S62.js} +190 -44
  13. package/dist/chunk-BBJ77S62.js.map +1 -0
  14. package/dist/{chunk-PCLRIQPX.cjs → chunk-EQ5NCH2Y.cjs} +4 -4
  15. package/dist/{chunk-PCLRIQPX.cjs.map → chunk-EQ5NCH2Y.cjs.map} +1 -1
  16. package/dist/{chunk-TE6XVTOA.js → chunk-JLIRFWUG.js} +47 -17
  17. package/dist/chunk-JLIRFWUG.js.map +1 -0
  18. package/dist/{chunk-YEHQ3SIC.cjs → chunk-JV7JA7BM.cjs} +5 -5
  19. package/dist/{chunk-YEHQ3SIC.cjs.map → chunk-JV7JA7BM.cjs.map} +1 -1
  20. package/dist/chunk-LZJXEPEQ.js +58 -0
  21. package/dist/chunk-LZJXEPEQ.js.map +1 -0
  22. package/dist/{chunk-RTE2DD4S.js → chunk-MHIBYKWA.js} +127 -69
  23. package/dist/chunk-MHIBYKWA.js.map +1 -0
  24. package/dist/{chunk-WPO3VCY4.cjs → chunk-N7KS3TIR.cjs} +47 -17
  25. package/dist/chunk-N7KS3TIR.cjs.map +1 -0
  26. package/dist/chunk-NWS2CS3Y.cjs +61 -0
  27. package/dist/chunk-NWS2CS3Y.cjs.map +1 -0
  28. package/dist/{chunk-QY3CIMUV.cjs → chunk-P5N3Q2NH.cjs} +210 -62
  29. package/dist/chunk-P5N3Q2NH.cjs.map +1 -0
  30. package/dist/{chunk-EXI7EHDN.js → chunk-Q34EBNYG.js} +3 -3
  31. package/dist/{chunk-EXI7EHDN.js.map → chunk-Q34EBNYG.js.map} +1 -1
  32. package/dist/{chunk-HI3W37FY.js → chunk-U27XHOEF.js} +3 -3
  33. package/dist/{chunk-HI3W37FY.js.map → chunk-U27XHOEF.js.map} +1 -1
  34. package/dist/{compact-session-PVAWTKKL.js → compact-session-4HAUP3HK.js} +4 -4
  35. package/dist/{compact-session-PVAWTKKL.js.map → compact-session-4HAUP3HK.js.map} +1 -1
  36. package/dist/{compact-session-4QHBBGEY.cjs → compact-session-Y56D3S57.cjs} +13 -13
  37. package/dist/{compact-session-4QHBBGEY.cjs.map → compact-session-Y56D3S57.cjs.map} +1 -1
  38. package/dist/cron.cjs +8 -7
  39. package/dist/cron.js +7 -6
  40. package/dist/eval.cjs +7 -6
  41. package/dist/eval.cjs.map +1 -1
  42. package/dist/eval.js +6 -5
  43. package/dist/eval.js.map +1 -1
  44. package/dist/index.cjs +40 -130
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.js +11 -106
  47. package/dist/index.js.map +1 -1
  48. package/dist/{inject-session-O24XGRR6.js → inject-session-LJX5JGV5.js} +3 -3
  49. package/dist/{inject-session-O24XGRR6.js.map → inject-session-LJX5JGV5.js.map} +1 -1
  50. package/dist/{inject-session-OMLFL2V3.cjs → inject-session-R73PRM7B.cjs} +4 -4
  51. package/dist/{inject-session-OMLFL2V3.cjs.map → inject-session-R73PRM7B.cjs.map} +1 -1
  52. package/dist/internal/budget/normalize-usage.d.ts +81 -0
  53. package/dist/internal/error-mappers/openai-compatible.d.ts +3 -1
  54. package/dist/internal/llm/anthropic-shared.d.ts +6 -0
  55. package/dist/internal/llm/openai.d.ts +3 -5
  56. package/dist/internal/llm/openrouter-system.d.ts +1 -0
  57. package/dist/internal/llm/types.d.ts +10 -5
  58. package/dist/internal/memory/adapters/index.cjs +3 -3
  59. package/dist/internal/memory/adapters/index.js +2 -2
  60. package/dist/models.cjs +5 -50
  61. package/dist/models.cjs.map +1 -1
  62. package/dist/models.js +3 -51
  63. package/dist/models.js.map +1 -1
  64. package/docs/error-codes.md +8 -4
  65. package/package.json +1 -1
  66. package/dist/chunk-CVEQ5366.cjs.map +0 -1
  67. package/dist/chunk-EVH3OUMY.js.map +0 -1
  68. package/dist/chunk-QY3CIMUV.cjs.map +0 -1
  69. package/dist/chunk-RTE2DD4S.js.map +0 -1
  70. package/dist/chunk-TE6XVTOA.js.map +0 -1
  71. package/dist/chunk-WPO3VCY4.cjs.map +0 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  var chunkCQYBJOB2_cjs = require('./chunk-CQYBJOB2.cjs');
4
4
  var chunkYJNYDJ2T_cjs = require('./chunk-YJNYDJ2T.cjs');
5
- var chunkWPO3VCY4_cjs = require('./chunk-WPO3VCY4.cjs');
5
+ var chunkN7KS3TIR_cjs = require('./chunk-N7KS3TIR.cjs');
6
6
  var chunkQOGNZMHD_cjs = require('./chunk-QOGNZMHD.cjs');
7
7
  var chunkYSTXFWQE_cjs = require('./chunk-YSTXFWQE.cjs');
8
8
  var chunkFRITRLZQ_cjs = require('./chunk-FRITRLZQ.cjs');
@@ -120,7 +120,7 @@ function mapAnthropicError(args) {
120
120
  const { status, body, headers, endpoint } = args;
121
121
  const code = mapAnthropicStatusToCode(status, body);
122
122
  const message = formatMessage(status, code);
123
- const metadata = chunkWPO3VCY4_cjs.buildErrorMetadata({
123
+ const metadata = chunkN7KS3TIR_cjs.buildErrorMetadata({
124
124
  provider: "anthropic",
125
125
  endpoint,
126
126
  code,
@@ -159,7 +159,7 @@ function mapAnthropicStatusToCode(status, body) {
159
159
  }
160
160
  return "invalid_request";
161
161
  }
162
- return chunkWPO3VCY4_cjs.httpStatusToErrorCode(status);
162
+ return chunkN7KS3TIR_cjs.httpStatusToErrorCode(status);
163
163
  }
164
164
  function formatMessage(status, code) {
165
165
  return `Anthropic API error: ${code} (HTTP ${status})`;
@@ -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" || record.event === "message_start") continue;
545
- let parsed;
546
- try {
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
- if (md.usage?.input_tokens !== void 0) this.inputTokens = md.usage.input_tokens;
649
- if (md.usage?.output_tokens !== void 0) this.outputTokens = md.usage.output_tokens;
650
- if (md.usage?.cache_creation_input_tokens !== void 0 && md.usage.cache_creation_input_tokens > 0) {
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() {
@@ -701,10 +741,10 @@ function classifyBedrockError(args, awsType, message) {
701
741
  return "invalid_request";
702
742
  }
703
743
  if (awsType.includes("Timeout")) return "timeout";
704
- return chunkWPO3VCY4_cjs.httpStatusToErrorCode(args.status);
744
+ return chunkN7KS3TIR_cjs.httpStatusToErrorCode(args.status);
705
745
  }
706
746
  function buildBedrockMetadata(args, code) {
707
- return chunkWPO3VCY4_cjs.buildErrorMetadata({
747
+ return chunkN7KS3TIR_cjs.buildErrorMetadata({
708
748
  provider: "bedrock",
709
749
  endpoint: args.endpoint,
710
750
  code,
@@ -714,7 +754,7 @@ function buildBedrockMetadata(args, code) {
714
754
  });
715
755
  }
716
756
  function mapBedrockError(args) {
717
- const parsed = chunkWPO3VCY4_cjs.parseErrorBody(args.body);
757
+ const parsed = chunkN7KS3TIR_cjs.parseErrorBody(args.body);
718
758
  const awsType = parsed.__type ?? "";
719
759
  const message = parsed.message ?? parsed.Message ?? "Bedrock request failed";
720
760
  const code = classifyBedrockError(args, awsType, message);
@@ -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-QY3CIMUV.cjs', document.baseURI).href)));
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-P5N3Q2NH.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");
@@ -840,7 +880,7 @@ var FaultInjectingLlmClient = class {
840
880
  return yield* this.inner.stream(request, signal);
841
881
  }
842
882
  if (parsed.status >= 400) {
843
- throw chunkWPO3VCY4_cjs.mapOpenAICompatibleError({
883
+ throw chunkN7KS3TIR_cjs.mapOpenAICompatibleError({
844
884
  providerId: this.name,
845
885
  status: parsed.status,
846
886
  body: parsed.body,
@@ -915,7 +955,7 @@ function mapOllamaTransportError(args) {
915
955
  if (args.providerId !== "ollama") return void 0;
916
956
  const causeCode = extractCauseCode(args.cause);
917
957
  if (causeCode !== "ECONNREFUSED" && causeCode !== "ENOTFOUND") return void 0;
918
- const metadata = chunkWPO3VCY4_cjs.buildErrorMetadata({
958
+ const metadata = chunkN7KS3TIR_cjs.buildErrorMetadata({
919
959
  provider: "ollama",
920
960
  endpoint: args.endpoint,
921
961
  code: "network",
@@ -933,7 +973,7 @@ function mapOllamaHttpError(args) {
933
973
  const errString = extractErrorString(args.body).toLowerCase();
934
974
  if (args.status === 404 && errString.includes("not found") && errString.includes("pull")) {
935
975
  const modelHint = extractModelName(errString);
936
- const metadata = chunkWPO3VCY4_cjs.buildErrorMetadata({
976
+ const metadata = chunkN7KS3TIR_cjs.buildErrorMetadata({
937
977
  provider: "ollama",
938
978
  endpoint: args.endpoint,
939
979
  code: "model_unavailable",
@@ -948,7 +988,7 @@ function mapOllamaHttpError(args) {
948
988
  );
949
989
  }
950
990
  if (args.status === 503 && errString.includes("model is loading")) {
951
- const metadata = chunkWPO3VCY4_cjs.buildErrorMetadata({
991
+ const metadata = chunkN7KS3TIR_cjs.buildErrorMetadata({
952
992
  provider: "ollama",
953
993
  endpoint: args.endpoint,
954
994
  code: "server_error",
@@ -1035,7 +1075,7 @@ var OllamaNativeClient = class {
1035
1075
  endpoint: "/api/chat"
1036
1076
  });
1037
1077
  if (mapped !== void 0) throw mapped;
1038
- throw chunkWPO3VCY4_cjs.mapOpenAICompatibleError({
1078
+ throw chunkN7KS3TIR_cjs.mapOpenAICompatibleError({
1039
1079
  providerId: "ollama",
1040
1080
  status: response.status,
1041
1081
  body: bodyJson,
@@ -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 {
@@ -1573,7 +1734,7 @@ var OpenAIClient = class {
1573
1734
  endpoint: "/v1/chat/completions"
1574
1735
  });
1575
1736
  if (ollamaMapped !== void 0) throw ollamaMapped;
1576
- throw chunkWPO3VCY4_cjs.mapOpenAICompatibleError({
1737
+ throw chunkN7KS3TIR_cjs.mapOpenAICompatibleError({
1577
1738
  providerId,
1578
1739
  status: response.status,
1579
1740
  body,
@@ -1609,7 +1770,7 @@ var OpenAIClient = class {
1609
1770
  }
1610
1771
  if (chunk.error !== void 0 && chunk.error !== null) {
1611
1772
  const status = typeof chunk.error.code === "number" ? chunk.error.code : 502;
1612
- throw chunkWPO3VCY4_cjs.mapOpenAICompatibleError({
1773
+ throw chunkN7KS3TIR_cjs.mapOpenAICompatibleError({
1613
1774
  providerId,
1614
1775
  status,
1615
1776
  body: chunk,
@@ -1649,11 +1810,8 @@ var OpenAIStreamAccumulator = class {
1649
1810
  allowedToolNames;
1650
1811
  text = "";
1651
1812
  stopReason = "end_turn";
1652
- inputTokens;
1653
- outputTokens;
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?.prompt_tokens !== void 0) this.inputTokens = usage.prompt_tokens;
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
- inputTokens: this.inputTokens,
1773
- outputTokens: this.outputTokens,
1774
- cacheReadTokens: this.cacheReadTokens,
1775
- cacheWriteTokens: this.cacheWriteTokens,
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 (systemText.length > 0) {
1799
- messages.push({ role: "system", content: systemText });
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
  }
@@ -2308,7 +2454,7 @@ var ResponsesApiClient = class {
2308
2454
  body = JSON.parse(text);
2309
2455
  } catch {
2310
2456
  }
2311
- return chunkWPO3VCY4_cjs.mapOpenAICompatibleError({
2457
+ return chunkN7KS3TIR_cjs.mapOpenAICompatibleError({
2312
2458
  providerId,
2313
2459
  status: response.status,
2314
2460
  body,
@@ -2344,7 +2490,7 @@ var ResponsesApiClient = class {
2344
2490
  const failure = ResponsesStreamAccumulator.failureMessage(event);
2345
2491
  if (failure !== void 0) {
2346
2492
  yield { type: "error", message: failure };
2347
- throw chunkWPO3VCY4_cjs.mapOpenAICompatibleError({
2493
+ throw chunkN7KS3TIR_cjs.mapOpenAICompatibleError({
2348
2494
  providerId,
2349
2495
  status: 502,
2350
2496
  body: { error: { message: failure } },
@@ -2458,11 +2604,11 @@ function classifyVertexError(args, errStatus) {
2458
2604
  for (const rule of VERTEX_RULES) {
2459
2605
  if (rule.test(args.status, errStatus)) return rule.code;
2460
2606
  }
2461
- const shared = chunkWPO3VCY4_cjs.httpStatusToErrorCode(args.status);
2607
+ const shared = chunkN7KS3TIR_cjs.httpStatusToErrorCode(args.status);
2462
2608
  return shared === "auth_failed" ? "auth_permission" : shared;
2463
2609
  }
2464
2610
  function vertexMetadata(args, code) {
2465
- return chunkWPO3VCY4_cjs.buildErrorMetadata({
2611
+ return chunkN7KS3TIR_cjs.buildErrorMetadata({
2466
2612
  provider: "vertex",
2467
2613
  endpoint: args.endpoint,
2468
2614
  code,
@@ -2494,7 +2640,7 @@ var VERTEX_ERROR_BUILDERS = {
2494
2640
  unknown: (args, msg) => new chunkUNFBHV46_cjs.UnknownAgentError(`Vertex unknown: ${msg}`, { metadata: vertexMetadata(args, "unknown") })
2495
2641
  };
2496
2642
  function mapVertexError(args) {
2497
- const parsed = chunkWPO3VCY4_cjs.parseErrorBody(args.body);
2643
+ const parsed = chunkN7KS3TIR_cjs.parseErrorBody(args.body);
2498
2644
  const errStatus = parsed.error?.status ?? "";
2499
2645
  const message = parsed.error?.message ?? "Vertex request failed";
2500
2646
  const code = classifyVertexError(args, errStatus);
@@ -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-QY3CIMUV.cjs', document.baseURI).href)));
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-P5N3Q2NH.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-QY3CIMUV.cjs.map
3327
- //# sourceMappingURL=chunk-QY3CIMUV.cjs.map
3474
+ //# sourceMappingURL=chunk-P5N3Q2NH.cjs.map
3475
+ //# sourceMappingURL=chunk-P5N3Q2NH.cjs.map