@copilotkit/aimock 1.39.0 → 1.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +30 -0
  4. package/README.md +1 -0
  5. package/dist/agui-handler.d.cts.map +1 -1
  6. package/dist/agui-stub.d.cts +2 -2
  7. package/dist/agui-stub.d.ts +2 -2
  8. package/dist/agui-types.d.cts +66 -3
  9. package/dist/agui-types.d.cts.map +1 -1
  10. package/dist/agui-types.d.ts +66 -3
  11. package/dist/agui-types.d.ts.map +1 -1
  12. package/dist/config-loader.d.cts.map +1 -1
  13. package/dist/config-loader.d.ts.map +1 -1
  14. package/dist/helpers.cjs +8 -4
  15. package/dist/helpers.cjs.map +1 -1
  16. package/dist/helpers.d.cts.map +1 -1
  17. package/dist/helpers.d.ts.map +1 -1
  18. package/dist/helpers.js +8 -4
  19. package/dist/helpers.js.map +1 -1
  20. package/dist/index.d.cts +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/openrouter-chat.cjs +41 -0
  23. package/dist/openrouter-chat.cjs.map +1 -1
  24. package/dist/openrouter-chat.js +41 -0
  25. package/dist/openrouter-chat.js.map +1 -1
  26. package/dist/recorder.cjs +77 -8
  27. package/dist/recorder.cjs.map +1 -1
  28. package/dist/recorder.d.cts.map +1 -1
  29. package/dist/recorder.d.ts.map +1 -1
  30. package/dist/recorder.js +77 -8
  31. package/dist/recorder.js.map +1 -1
  32. package/dist/server.cjs +46 -0
  33. package/dist/server.cjs.map +1 -1
  34. package/dist/server.d.cts.map +1 -1
  35. package/dist/server.d.ts.map +1 -1
  36. package/dist/server.js +46 -0
  37. package/dist/server.js.map +1 -1
  38. package/dist/stream-collapse.cjs +20 -6
  39. package/dist/stream-collapse.cjs.map +1 -1
  40. package/dist/stream-collapse.d.cts +16 -0
  41. package/dist/stream-collapse.d.cts.map +1 -1
  42. package/dist/stream-collapse.d.ts +16 -0
  43. package/dist/stream-collapse.d.ts.map +1 -1
  44. package/dist/stream-collapse.js +20 -6
  45. package/dist/stream-collapse.js.map +1 -1
  46. package/dist/types.d.cts +22 -3
  47. package/dist/types.d.cts.map +1 -1
  48. package/dist/types.d.ts +22 -3
  49. package/dist/types.d.ts.map +1 -1
  50. package/package.json +3 -2
@@ -55,6 +55,22 @@ interface CollapseResult {
55
55
  */
56
56
  redactedThinking?: string[];
57
57
  webSearches?: string[];
58
+ /**
59
+ * The provider-reported `usage` object from the stream's final usage frame,
60
+ * captured VERBATIM (#368). OpenAI-compatible streams (including OpenRouter)
61
+ * close with a `chat.completion.chunk` whose `choices` is empty and whose
62
+ * `usage` carries the real token counts — plus, on OpenRouter,
63
+ * `cost` / `cost_details` / `native_tokens_*`. Collapsing dropped that frame,
64
+ * so a recorded fixture could only ever replay estimated (`ceil(len/4)`) token
65
+ * counts and never a provider-reported cost. Last-usage-wins: a stream that
66
+ * emits several usage frames keeps the final one. Absent when the upstream
67
+ * stream carried no usage (e.g. `stream_options.include_usage` unset).
68
+ *
69
+ * Kept as a loose record because the field set is provider-specific and
70
+ * forward-extending; the recorder sanitizes it into the fixture
71
+ * `response.usage` override shape before persisting.
72
+ */
73
+ usage?: Record<string, unknown>;
58
74
  toolCalls?: ToolCall[];
59
75
  droppedChunks?: number;
60
76
  firstDroppedSample?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.d.ts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;;;;AA2WA;AAmXA;AAkMA;AAyJA;AAmIA;AAyRA;;;;;AA8PA;AAwLA;;;;;;AAKiB,cA12DJ,0BA02DI,EAAA,MAAA;;iBA/1DD,8BAAA;UAgDC,cAAA;;;;gBAID;;;YACJ;;;;;;;;;;;;;;;;;;;;;;cAsBE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAgCH;;;;;;;;;;;;;;;;;;;iBA+MK,iBAAA,mBAAoC;;;;;;;;iBAmXpC,oBAAA,mBAAuC;;;;;;;iBAkMvC,iBAAA,mBAAoC;;;;;;;;;;;;;;;iBAyJpC,oBAAA,mBAAuC;;;;;;;iBAmIvC,iBAAA,mBAAoC;;;;;;;iBAyRpC,0BAAA,UAAoC,SAAS;;;;;;;;;;;;;;;iBA8P7C,6BAAA,mBAAgD;;;;;;iBAwLhD,yBAAA,mCAED,kCACE,iBACN,SACR"}
1
+ {"version":3,"file":"stream-collapse.d.ts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AA2XA;AAiaA;AAkMA;AAyJA;AAmIA;AA6RA;;;;;AA0RA;AAwLA;;;;;AAKG,cAx8DU,0BAw8DV,EAAA,MAAA;;iBA77Da,8BAAA;UAgDC,cAAA;;;;gBAID;;;YACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAqCF;cACI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAgCH;;;;;;;;;;;;;;;;;;;iBA+MK,iBAAA,mBAAoC;;;;;;;;iBAiapC,oBAAA,mBAAuC;;;;;;;iBAkMvC,iBAAA,mBAAoC;;;;;;;;;;;;;;;iBAyJpC,oBAAA,mBAAuC;;;;;;;iBAmIvC,iBAAA,mBAAoC;;;;;;;iBA6RpC,0BAAA,UAAoC,SAAS;;;;;;;;;;;;;;;iBA0R7C,6BAAA,mBAAgD;;;;;;iBAwLhD,yBAAA,mCAED,kCACE,iBACN,SACR"}
@@ -244,6 +244,7 @@ function collapseOpenAISSE(rawBody) {
244
244
  let transcriptUsage;
245
245
  let reasoning = "";
246
246
  const webSearchQueries = [];
247
+ let usage;
247
248
  let droppedChunks = 0;
248
249
  let firstDroppedSample;
249
250
  let harmonyUnparsed = false;
@@ -251,6 +252,7 @@ function collapseOpenAISSE(rawBody) {
251
252
  const toolCallMap = /* @__PURE__ */ new Map();
252
253
  let nextSyntheticIndex = 1e6;
253
254
  const idKeyMap = /* @__PURE__ */ new Map();
255
+ let lastToolCallKey;
254
256
  const orderAtoms = [];
255
257
  for (const line of lines) {
256
258
  const data = extractSSEData(splitSSELines(line));
@@ -274,7 +276,7 @@ function collapseOpenAISSE(rawBody) {
274
276
  transcript = parsed.text;
275
277
  transcriptSeen = true;
276
278
  if (Array.isArray(parsed.languages)) transcriptLanguages = parsed.languages.filter((language) => typeof language === "object" && language !== null && typeof language.code === "string").map((language) => ({ code: language.code }));
277
- if (parsed.usage && typeof parsed.usage === "object") transcriptUsage = parsed.usage;
279
+ if (parsed.usage && typeof parsed.usage === "object" && !Array.isArray(parsed.usage) && Object.keys(parsed.usage).length > 0) transcriptUsage = parsed.usage;
278
280
  continue;
279
281
  }
280
282
  if (parsed.type === "response.reasoning_summary_text.delta" && typeof parsed.delta === "string") {
@@ -373,6 +375,7 @@ function collapseOpenAISSE(rawBody) {
373
375
  continue;
374
376
  }
375
377
  if (typeof parsed.type === "string" && parsed.type.startsWith("response.")) continue;
378
+ if (parsed.usage && typeof parsed.usage === "object" && !Array.isArray(parsed.usage) && Object.keys(parsed.usage).length > 0) usage = parsed.usage;
376
379
  const choices = parsed.choices;
377
380
  if (!choices || choices.length === 0) continue;
378
381
  const delta = choices[0].delta;
@@ -398,7 +401,9 @@ function collapseOpenAISSE(rawBody) {
398
401
  index = nextSyntheticIndex++;
399
402
  idKeyMap.set(rawId, index);
400
403
  }
401
- } else index = nextSyntheticIndex++;
404
+ } else if (lastToolCallKey !== void 0) index = lastToolCallKey;
405
+ else index = nextSyntheticIndex++;
406
+ lastToolCallKey = index;
402
407
  if (!toolCallMap.has(index)) {
403
408
  const created = {
404
409
  id: rawId ?? "",
@@ -452,6 +457,7 @@ function collapseOpenAISSE(rawBody) {
452
457
  toolCalls: [...blocks ? orderedToolCalls : indexSortedToolCalls, ...harmonyToolCalls],
453
458
  ...reasoning ? { reasoning } : {},
454
459
  ...webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {},
460
+ ...usage ? { usage } : {},
455
461
  ...droppedChunks > 0 ? { droppedChunks } : {},
456
462
  ...firstDroppedSample ? { firstDroppedSample } : {},
457
463
  ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
@@ -468,6 +474,7 @@ function collapseOpenAISSE(rawBody) {
468
474
  content,
469
475
  ...reasoning ? { reasoning } : {},
470
476
  ...webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {},
477
+ ...usage ? { usage } : {},
471
478
  ...droppedChunks > 0 ? { droppedChunks } : {},
472
479
  ...firstDroppedSample ? { firstDroppedSample } : {},
473
480
  ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
@@ -832,7 +839,6 @@ function collapseCohereSSE(rawBody) {
832
839
  let index;
833
840
  if (typeof parsed.index === "number") index = parsed.index;
834
841
  else index = nextSyntheticIndex++;
835
- lastStartKey = index;
836
842
  const toolCalls = (parsed.delta?.message)?.tool_calls;
837
843
  if (toolCalls) {
838
844
  const fn = toolCalls.function;
@@ -842,6 +848,7 @@ function collapseCohereSSE(rawBody) {
842
848
  arguments: ""
843
849
  };
844
850
  toolCallMap.set(index, created);
851
+ lastStartKey = index;
845
852
  orderAtoms.push({
846
853
  kind: "toolCall",
847
854
  ref: created
@@ -1035,7 +1042,7 @@ function collapseBedrockEventStream(rawBody) {
1035
1042
  const index = parsed.index;
1036
1043
  const redactedData = capturedRedactedData(block);
1037
1044
  if (redactedData !== void 0) redactedThinking.push(redactedData);
1038
- if (block?.type === "tool_use" && index !== void 0) {
1045
+ if (block?.type === "tool_use") if (index !== void 0) {
1039
1046
  const created = {
1040
1047
  id: block.id ?? "",
1041
1048
  name: block.name ?? "",
@@ -1046,6 +1053,9 @@ function collapseBedrockEventStream(rawBody) {
1046
1053
  kind: "toolCall",
1047
1054
  ref: created
1048
1055
  });
1056
+ } else {
1057
+ droppedChunks++;
1058
+ if (droppedChunks === 1) firstDroppedSample = `tool_use content_block_start with no index — tool call identity lost: ${surrogateSafeSlice(frameStr, 200)}`;
1049
1059
  }
1050
1060
  continue;
1051
1061
  }
@@ -1053,7 +1063,7 @@ function collapseBedrockEventStream(rawBody) {
1053
1063
  const blockStart = parsed.contentBlockStart;
1054
1064
  const index = parsed.contentBlockIndex ?? blockStart.contentBlockIndex;
1055
1065
  const start = blockStart.start;
1056
- if (start?.toolUse && index !== void 0) {
1066
+ if (start?.toolUse) if (index !== void 0) {
1057
1067
  const toolUse = start.toolUse;
1058
1068
  const created = {
1059
1069
  id: toolUse.toolUseId ?? "",
@@ -1065,6 +1075,9 @@ function collapseBedrockEventStream(rawBody) {
1065
1075
  kind: "toolCall",
1066
1076
  ref: created
1067
1077
  });
1078
+ } else {
1079
+ droppedChunks++;
1080
+ if (droppedChunks === 1) firstDroppedSample = `contentBlockStart toolUse with no index — tool call identity lost: ${surrogateSafeSlice(frameStr, 200)}`;
1068
1081
  }
1069
1082
  }
1070
1083
  if (parsed.contentBlockDelta) {
@@ -1251,7 +1264,8 @@ function collapseStreamingResponse(contentType, providerKey, body, logger) {
1251
1264
  const str = typeof body === "string" ? body : body.toString("utf8");
1252
1265
  switch (providerKey) {
1253
1266
  case "openai":
1254
- case "azure": return collapseOpenAISSE(str);
1267
+ case "azure":
1268
+ case "openrouter": return collapseOpenAISSE(str);
1255
1269
  case "anthropic": return collapseAnthropicSSE(str);
1256
1270
  case "gemini":
1257
1271
  case "vertexai": return collapseGeminiSSE(str);