@copilotkit/aimock 1.34.0 → 1.35.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 (151) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +22 -0
  4. package/README.md +2 -1
  5. package/dist/agui-types.d.cts.map +1 -1
  6. package/dist/bedrock-converse.cjs +110 -8
  7. package/dist/bedrock-converse.cjs.map +1 -1
  8. package/dist/bedrock-converse.d.cts.map +1 -1
  9. package/dist/bedrock-converse.d.ts.map +1 -1
  10. package/dist/bedrock-converse.js +111 -9
  11. package/dist/bedrock-converse.js.map +1 -1
  12. package/dist/bedrock.cjs +141 -4
  13. package/dist/bedrock.cjs.map +1 -1
  14. package/dist/bedrock.d.cts.map +1 -1
  15. package/dist/bedrock.d.ts.map +1 -1
  16. package/dist/bedrock.js +142 -5
  17. package/dist/bedrock.js.map +1 -1
  18. package/dist/cohere.cjs +89 -7
  19. package/dist/cohere.cjs.map +1 -1
  20. package/dist/cohere.d.cts.map +1 -1
  21. package/dist/cohere.d.ts.map +1 -1
  22. package/dist/cohere.js +90 -8
  23. package/dist/cohere.js.map +1 -1
  24. package/dist/config-loader.d.cts.map +1 -1
  25. package/dist/fixture-loader.cjs +121 -20
  26. package/dist/fixture-loader.cjs.map +1 -1
  27. package/dist/fixture-loader.d.cts.map +1 -1
  28. package/dist/fixture-loader.d.ts.map +1 -1
  29. package/dist/fixture-loader.js +121 -20
  30. package/dist/fixture-loader.js.map +1 -1
  31. package/dist/gemini-interactions.cjs +97 -60
  32. package/dist/gemini-interactions.cjs.map +1 -1
  33. package/dist/gemini-interactions.d.cts.map +1 -1
  34. package/dist/gemini-interactions.d.ts.map +1 -1
  35. package/dist/gemini-interactions.js +98 -61
  36. package/dist/gemini-interactions.js.map +1 -1
  37. package/dist/gemini.cjs +71 -6
  38. package/dist/gemini.cjs.map +1 -1
  39. package/dist/gemini.d.cts.map +1 -1
  40. package/dist/gemini.d.ts.map +1 -1
  41. package/dist/gemini.js +72 -7
  42. package/dist/gemini.js.map +1 -1
  43. package/dist/grok-video.cjs +834 -0
  44. package/dist/grok-video.cjs.map +1 -0
  45. package/dist/grok-video.d.cts +74 -0
  46. package/dist/grok-video.d.cts.map +1 -0
  47. package/dist/grok-video.d.ts +74 -0
  48. package/dist/grok-video.d.ts.map +1 -0
  49. package/dist/grok-video.js +829 -0
  50. package/dist/grok-video.js.map +1 -0
  51. package/dist/helpers.cjs +181 -2
  52. package/dist/helpers.cjs.map +1 -1
  53. package/dist/helpers.d.cts +33 -3
  54. package/dist/helpers.d.cts.map +1 -1
  55. package/dist/helpers.d.ts +33 -3
  56. package/dist/helpers.d.ts.map +1 -1
  57. package/dist/helpers.js +181 -3
  58. package/dist/helpers.js.map +1 -1
  59. package/dist/index.cjs +11 -0
  60. package/dist/index.d.cts +5 -3
  61. package/dist/index.d.ts +5 -3
  62. package/dist/index.js +4 -2
  63. package/dist/messages.cjs +99 -10
  64. package/dist/messages.cjs.map +1 -1
  65. package/dist/messages.d.cts.map +1 -1
  66. package/dist/messages.d.ts.map +1 -1
  67. package/dist/messages.js +100 -11
  68. package/dist/messages.js.map +1 -1
  69. package/dist/metrics.cjs +11 -1
  70. package/dist/metrics.cjs.map +1 -1
  71. package/dist/metrics.d.cts.map +1 -1
  72. package/dist/metrics.d.ts.map +1 -1
  73. package/dist/metrics.js +8 -1
  74. package/dist/metrics.js.map +1 -1
  75. package/dist/ollama.cjs +74 -4
  76. package/dist/ollama.cjs.map +1 -1
  77. package/dist/ollama.d.cts.map +1 -1
  78. package/dist/ollama.d.ts.map +1 -1
  79. package/dist/ollama.js +75 -5
  80. package/dist/ollama.js.map +1 -1
  81. package/dist/openrouter-video.cjs +19 -138
  82. package/dist/openrouter-video.cjs.map +1 -1
  83. package/dist/openrouter-video.d.cts.map +1 -1
  84. package/dist/openrouter-video.d.ts.map +1 -1
  85. package/dist/openrouter-video.js +1 -120
  86. package/dist/openrouter-video.js.map +1 -1
  87. package/dist/recorder.cjs +36 -20
  88. package/dist/recorder.cjs.map +1 -1
  89. package/dist/recorder.js +36 -20
  90. package/dist/recorder.js.map +1 -1
  91. package/dist/responses.cjs +136 -58
  92. package/dist/responses.cjs.map +1 -1
  93. package/dist/responses.d.cts +2 -2
  94. package/dist/responses.d.cts.map +1 -1
  95. package/dist/responses.d.ts +2 -2
  96. package/dist/responses.d.ts.map +1 -1
  97. package/dist/responses.js +137 -59
  98. package/dist/responses.js.map +1 -1
  99. package/dist/server.cjs +162 -20
  100. package/dist/server.cjs.map +1 -1
  101. package/dist/server.d.cts +4 -0
  102. package/dist/server.d.cts.map +1 -1
  103. package/dist/server.d.ts +4 -0
  104. package/dist/server.d.ts.map +1 -1
  105. package/dist/server.js +164 -22
  106. package/dist/server.js.map +1 -1
  107. package/dist/stream-collapse.cjs +300 -74
  108. package/dist/stream-collapse.cjs.map +1 -1
  109. package/dist/stream-collapse.d.cts +17 -1
  110. package/dist/stream-collapse.d.cts.map +1 -1
  111. package/dist/stream-collapse.d.ts +17 -1
  112. package/dist/stream-collapse.d.ts.map +1 -1
  113. package/dist/stream-collapse.js +300 -74
  114. package/dist/stream-collapse.js.map +1 -1
  115. package/dist/types.d.cts +104 -7
  116. package/dist/types.d.cts.map +1 -1
  117. package/dist/types.d.ts +104 -7
  118. package/dist/types.d.ts.map +1 -1
  119. package/dist/vector-types.d.cts.map +1 -1
  120. package/dist/vector-types.d.ts.map +1 -1
  121. package/dist/veo-video.cjs +794 -0
  122. package/dist/veo-video.cjs.map +1 -0
  123. package/dist/veo-video.d.cts +50 -0
  124. package/dist/veo-video.d.cts.map +1 -0
  125. package/dist/veo-video.d.ts +50 -0
  126. package/dist/veo-video.d.ts.map +1 -0
  127. package/dist/veo-video.js +789 -0
  128. package/dist/veo-video.js.map +1 -0
  129. package/dist/video-proxy-shared.cjs +145 -0
  130. package/dist/video-proxy-shared.cjs.map +1 -0
  131. package/dist/video-proxy-shared.js +140 -0
  132. package/dist/video-proxy-shared.js.map +1 -0
  133. package/dist/ws-gemini-live.cjs +96 -3
  134. package/dist/ws-gemini-live.cjs.map +1 -1
  135. package/dist/ws-gemini-live.d.cts.map +1 -1
  136. package/dist/ws-gemini-live.d.ts.map +1 -1
  137. package/dist/ws-gemini-live.js +97 -4
  138. package/dist/ws-gemini-live.js.map +1 -1
  139. package/dist/ws-realtime.cjs +281 -4
  140. package/dist/ws-realtime.cjs.map +1 -1
  141. package/dist/ws-realtime.d.cts.map +1 -1
  142. package/dist/ws-realtime.d.ts.map +1 -1
  143. package/dist/ws-realtime.js +282 -5
  144. package/dist/ws-realtime.js.map +1 -1
  145. package/dist/ws-responses.cjs +1 -1
  146. package/dist/ws-responses.cjs.map +1 -1
  147. package/dist/ws-responses.js +1 -1
  148. package/dist/ws-responses.js.map +1 -1
  149. package/fixtures/examples/llm/blocks-tool-first.json +13 -0
  150. package/package.json +1 -1
  151. package/skills/write-fixtures/SKILL.md +19 -1
@@ -11,6 +11,88 @@ import { crc32 } from "node:zlib";
11
11
  * text followed by tool calls.
12
12
  */
13
13
  /**
14
+ * Normalize a tool call's accumulated `arguments` into valid JSON exactly like
15
+ * the flat-`toolCalls` recorder path: an empty / whitespace-only / missing
16
+ * value becomes `"{}"`, never `""`. Mirrors `recorder.ts` `toToolCallArguments`
17
+ * so a `blocks[].arguments` value is always parseable JSON and never disagrees
18
+ * with the flat `toolCalls` entry for the same call.
19
+ */
20
+ function normalizeToolArguments(args) {
21
+ if (args === void 0 || args.trim() === "") return "{}";
22
+ return args;
23
+ }
24
+ /**
25
+ * Build a finalized {@link FixtureBlock.toolCall} from a tool-call accumulator,
26
+ * normalizing `arguments` so the block agrees byte-for-byte with the flat
27
+ * `toolCalls` entry built from the SAME accumulator object.
28
+ */
29
+ function toToolCallBlock(ref) {
30
+ return {
31
+ type: "toolCall",
32
+ name: ref.name,
33
+ arguments: normalizeToolArguments(ref.arguments),
34
+ ...ref.id ? { id: ref.id } : {}
35
+ };
36
+ }
37
+ /**
38
+ * Decide whether a recorded atom sequence is "interleaved" and, if so, build
39
+ * the ordered {@link FixtureBlock} list. Returns `undefined` when NOT
40
+ * interleaved (text-first, text-only, or tool-only) so callers leave
41
+ * `CollapseResult.blocks` unset and the recorder keeps the legacy shape.
42
+ *
43
+ * Interleaved ⇔ (a tool atom appears strictly before the first text atom) OR
44
+ * (a text atom appears after any tool atom). A stream with no tool atoms, or
45
+ * with no text atoms, is never interleaved. Text-first-then-tools is the common
46
+ * legacy case and is explicitly NOT interleaved.
47
+ *
48
+ * CONSISTENCY (#274): each toolCall block is derived from the SAME accumulator
49
+ * object referenced by its atom and normalized identically to the flat
50
+ * `toolCalls` path ({@link toToolCallBlock} / {@link normalizeToolArguments}).
51
+ * Because the atom `ref` is the very object the flat list is built from, the
52
+ * block and its flat counterpart describe the same call by identity — even when
53
+ * upstream tool-call indices do not match stream-arrival order. Empty/missing
54
+ * arguments normalize to `"{}"` in BOTH representations, never `""`.
55
+ */
56
+ function buildOrderedBlocks(atoms) {
57
+ let firstTextIndex = -1;
58
+ let firstToolIndex = -1;
59
+ let textAfterTool = false;
60
+ let sawTool = false;
61
+ let sawText = false;
62
+ for (let i = 0; i < atoms.length; i++) if (atoms[i].kind === "text") {
63
+ sawText = true;
64
+ if (firstTextIndex === -1) firstTextIndex = i;
65
+ if (sawTool) textAfterTool = true;
66
+ } else {
67
+ sawTool = true;
68
+ if (firstToolIndex === -1) firstToolIndex = i;
69
+ }
70
+ if (!sawTool || !sawText) return void 0;
71
+ if (!(firstToolIndex < firstTextIndex) && !textAfterTool) return void 0;
72
+ const blocks = [];
73
+ let pendingText = "";
74
+ let hasPendingText = false;
75
+ const flushText = () => {
76
+ if (hasPendingText) {
77
+ blocks.push({
78
+ type: "text",
79
+ text: pendingText
80
+ });
81
+ pendingText = "";
82
+ hasPendingText = false;
83
+ }
84
+ };
85
+ for (const a of atoms) if (a.kind === "text") {
86
+ pendingText += a.text;
87
+ hasPendingText = true;
88
+ } else {
89
+ flushText();
90
+ blocks.push(toToolCallBlock(a.ref));
91
+ }
92
+ flushText();
93
+ return blocks;
94
+ }
95
+ /**
14
96
  * The opaque `data` of a non-empty Anthropic `redacted_thinking` block, or
15
97
  * `undefined` if `block` is not a redacted_thinking block or carries empty/no
16
98
  * data. NON-EMPTY is required: the replay-side validator rejects a leading
@@ -95,6 +177,7 @@ function collapseOpenAISSE(body) {
95
177
  const toolCallMap = /* @__PURE__ */ new Map();
96
178
  let nextSyntheticIndex = 1e6;
97
179
  const idKeyMap = /* @__PURE__ */ new Map();
180
+ const orderAtoms = [];
98
181
  for (const line of lines) {
99
182
  const data = extractSSEData(splitSSELines(line));
100
183
  if (data === void 0) continue;
@@ -132,7 +215,13 @@ function collapseOpenAISSE(body) {
132
215
  const delta = choices[0].delta;
133
216
  if (!delta) continue;
134
217
  if (typeof delta.reasoning_content === "string") reasoning += delta.reasoning_content;
135
- if (typeof delta.content === "string") content += delta.content;
218
+ if (typeof delta.content === "string") {
219
+ content += delta.content;
220
+ if (delta.content.length > 0) orderAtoms.push({
221
+ kind: "text",
222
+ text: delta.content
223
+ });
224
+ }
136
225
  const toolCalls = delta.tool_calls;
137
226
  if (toolCalls) for (const tc of toolCalls) {
138
227
  const fn = tc.function;
@@ -147,11 +236,18 @@ function collapseOpenAISSE(body) {
147
236
  idKeyMap.set(rawId, index);
148
237
  }
149
238
  } else index = nextSyntheticIndex++;
150
- if (!toolCallMap.has(index)) toolCallMap.set(index, {
151
- id: rawId ?? "",
152
- name: fn?.name ?? "",
153
- arguments: ""
154
- });
239
+ if (!toolCallMap.has(index)) {
240
+ const created = {
241
+ id: rawId ?? "",
242
+ name: fn?.name ?? "",
243
+ arguments: ""
244
+ };
245
+ toolCallMap.set(index, created);
246
+ orderAtoms.push({
247
+ kind: "toolCall",
248
+ ref: created
249
+ });
250
+ }
155
251
  const entry = toolCallMap.get(index);
156
252
  if (fn?.name && typeof fn.name === "string" && !entry.name) entry.name = fn.name;
157
253
  if (tc.id && typeof tc.id === "string" && !entry.id) entry.id = tc.id;
@@ -171,14 +267,21 @@ function collapseOpenAISSE(body) {
171
267
  }
172
268
  }
173
269
  if (toolCallMap.size > 0 || harmonyToolCalls.length > 0) {
174
- const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);
270
+ const blocks = buildOrderedBlocks(orderAtoms);
271
+ const orderedToolCalls = orderAtoms.filter((a) => a.kind === "toolCall").map((a) => ({
272
+ name: a.ref.name,
273
+ arguments: normalizeToolArguments(a.ref.arguments),
274
+ ...a.ref.id ? { id: a.ref.id } : {}
275
+ }));
276
+ const indexSortedToolCalls = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b).map(([, tc]) => ({
277
+ name: tc.name,
278
+ arguments: normalizeToolArguments(tc.arguments),
279
+ ...tc.id ? { id: tc.id } : {}
280
+ }));
175
281
  return {
282
+ ...blocks ? { blocks } : {},
176
283
  ...content ? { content } : {},
177
- toolCalls: [...sorted.map(([, tc]) => ({
178
- name: tc.name,
179
- arguments: tc.arguments,
180
- ...tc.id ? { id: tc.id } : {}
181
- })), ...harmonyToolCalls],
284
+ toolCalls: [...blocks ? orderedToolCalls : indexSortedToolCalls, ...harmonyToolCalls],
182
285
  ...reasoning ? { reasoning } : {},
183
286
  ...webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {},
184
287
  ...droppedChunks > 0 ? { droppedChunks } : {},
@@ -215,6 +318,7 @@ function collapseAnthropicSSE(body) {
215
318
  const toolCallMap = /* @__PURE__ */ new Map();
216
319
  let nextSyntheticIndex = 1e6;
217
320
  let lastSyntheticIndex;
321
+ const orderAtoms = [];
218
322
  for (const block of blocks) {
219
323
  const lines = splitSSELines(block);
220
324
  const eventLine = lines.find((l) => l.startsWith("event:"));
@@ -239,17 +343,28 @@ function collapseAnthropicSSE(body) {
239
343
  if (typeof parsed.index === "number") index = parsed.index;
240
344
  else index = nextSyntheticIndex++;
241
345
  lastSyntheticIndex = index;
242
- toolCallMap.set(index, {
346
+ const created = {
243
347
  id: contentBlock.id ?? "",
244
348
  name: contentBlock.name ?? "",
245
349
  arguments: ""
350
+ };
351
+ toolCallMap.set(index, created);
352
+ orderAtoms.push({
353
+ kind: "toolCall",
354
+ ref: created
246
355
  });
247
356
  }
248
357
  }
249
358
  if (eventType === "content_block_delta") {
250
359
  const delta = parsed.delta;
251
360
  if (!delta) continue;
252
- if (delta.type === "text_delta" && typeof delta.text === "string") content += delta.text;
361
+ if (delta.type === "text_delta" && typeof delta.text === "string") {
362
+ content += delta.text;
363
+ if (delta.text.length > 0) orderAtoms.push({
364
+ kind: "text",
365
+ text: delta.text
366
+ });
367
+ }
253
368
  if (delta.type === "thinking_delta" && typeof delta.thinking === "string") reasoning += delta.thinking;
254
369
  if (delta.type === "signature_delta" && typeof delta.signature === "string") reasoningSignature = delta.signature;
255
370
  if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
@@ -264,14 +379,21 @@ function collapseAnthropicSSE(body) {
264
379
  }
265
380
  }
266
381
  if (toolCallMap.size > 0) {
267
- const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);
382
+ const orderedBlocks = buildOrderedBlocks(orderAtoms);
383
+ const orderedToolCalls = orderAtoms.filter((a) => a.kind === "toolCall").map((a) => ({
384
+ name: a.ref.name,
385
+ arguments: normalizeToolArguments(a.ref.arguments),
386
+ ...a.ref.id ? { id: a.ref.id } : {}
387
+ }));
388
+ const indexSortedToolCalls = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b).map(([, tc]) => ({
389
+ name: tc.name,
390
+ arguments: normalizeToolArguments(tc.arguments),
391
+ ...tc.id ? { id: tc.id } : {}
392
+ }));
268
393
  return {
394
+ ...orderedBlocks ? { blocks: orderedBlocks } : {},
269
395
  ...content ? { content } : {},
270
- toolCalls: sorted.map(([, tc]) => ({
271
- name: tc.name,
272
- arguments: tc.arguments,
273
- ...tc.id ? { id: tc.id } : {}
274
- })),
396
+ toolCalls: orderedBlocks ? orderedToolCalls : indexSortedToolCalls,
275
397
  ...reasoning ? { reasoning } : {},
276
398
  ...reasoningSignature ? { reasoningSignature } : {},
277
399
  ...redactedThinking.length > 0 ? { redactedThinking } : {},
@@ -303,6 +425,7 @@ function collapseGeminiSSE(body) {
303
425
  let audioB64 = "";
304
426
  let audioMimeType;
305
427
  const toolCalls = [];
428
+ const orderAtoms = [];
306
429
  for (const line of lines) {
307
430
  const data = extractSSEData(splitSSELines(line));
308
431
  if (data === void 0) continue;
@@ -323,33 +446,52 @@ function collapseGeminiSSE(body) {
323
446
  if (!parts || parts.length === 0) continue;
324
447
  for (const part of parts) if (part.functionCall) {
325
448
  const fc = part.functionCall;
326
- toolCalls.push({
449
+ const created = {
327
450
  name: String(fc.name ?? ""),
328
451
  arguments: typeof fc.args === "string" ? fc.args : JSON.stringify(fc.args ?? {})
452
+ };
453
+ toolCalls.push(created);
454
+ orderAtoms.push({
455
+ kind: "toolCall",
456
+ ref: created
329
457
  });
330
458
  } else if (part.inlineData && typeof part.inlineData.mimeType === "string" && part.inlineData.mimeType.startsWith("audio/")) {
331
459
  const inlineData = part.inlineData;
332
460
  if (!audioMimeType) audioMimeType = inlineData.mimeType;
333
461
  if (typeof inlineData.data === "string") audioB64 += inlineData.data;
334
462
  } else if (typeof part.text === "string") if (part.thought) reasoning += part.text;
335
- else content += part.text;
463
+ else {
464
+ content += part.text;
465
+ if (part.text.length > 0) orderAtoms.push({
466
+ kind: "text",
467
+ text: part.text
468
+ });
469
+ }
336
470
  }
471
+ const normalizedToolCalls = toolCalls.map((tc) => ({
472
+ ...tc,
473
+ arguments: normalizeToolArguments(tc.arguments)
474
+ }));
337
475
  if (audioB64) return {
338
476
  audioB64,
339
477
  audioMimeType,
340
478
  ...content ? { content } : {},
341
479
  ...reasoning ? { reasoning } : {},
342
- ...toolCalls.length > 0 ? { toolCalls } : {},
343
- ...droppedChunks > 0 ? { droppedChunks } : {},
344
- ...firstDroppedSample ? { firstDroppedSample } : {}
345
- };
346
- if (toolCalls.length > 0) return {
347
- ...content ? { content } : {},
348
- toolCalls,
349
- ...reasoning ? { reasoning } : {},
480
+ ...normalizedToolCalls.length > 0 ? { toolCalls: normalizedToolCalls } : {},
350
481
  ...droppedChunks > 0 ? { droppedChunks } : {},
351
482
  ...firstDroppedSample ? { firstDroppedSample } : {}
352
483
  };
484
+ if (toolCalls.length > 0) {
485
+ const blocks = buildOrderedBlocks(orderAtoms);
486
+ return {
487
+ ...blocks ? { blocks } : {},
488
+ ...content ? { content } : {},
489
+ toolCalls: normalizedToolCalls,
490
+ ...reasoning ? { reasoning } : {},
491
+ ...droppedChunks > 0 ? { droppedChunks } : {},
492
+ ...firstDroppedSample ? { firstDroppedSample } : {}
493
+ };
494
+ }
353
495
  return {
354
496
  content,
355
497
  ...reasoning ? { reasoning } : {},
@@ -380,6 +522,7 @@ function collapseOllamaNDJSON(body) {
380
522
  let harmonyUnparsed = false;
381
523
  let harmonyNote;
382
524
  const toolCalls = [];
525
+ const orderAtoms = [];
383
526
  for (const line of lines) {
384
527
  let parsed;
385
528
  try {
@@ -391,15 +534,34 @@ function collapseOllamaNDJSON(body) {
391
534
  }
392
535
  const message = parsed.message;
393
536
  if (message) {
394
- if (typeof message.content === "string") content += message.content;
537
+ if (typeof message.content === "string") {
538
+ content += message.content;
539
+ if (message.content.length > 0) orderAtoms.push({
540
+ kind: "text",
541
+ text: message.content
542
+ });
543
+ }
395
544
  if (Array.isArray(message.tool_calls)) for (const tc of message.tool_calls) {
396
545
  const fn = tc.function;
397
- if (fn) toolCalls.push({
398
- name: String(fn.name ?? ""),
399
- arguments: typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments ?? {})
400
- });
546
+ if (fn) {
547
+ const created = {
548
+ name: String(fn.name ?? ""),
549
+ arguments: typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments ?? {})
550
+ };
551
+ toolCalls.push(created);
552
+ orderAtoms.push({
553
+ kind: "toolCall",
554
+ ref: created
555
+ });
556
+ }
401
557
  }
402
- } else if (typeof parsed.response === "string") content += parsed.response;
558
+ } else if (typeof parsed.response === "string") {
559
+ content += parsed.response;
560
+ if (parsed.response.length > 0) orderAtoms.push({
561
+ kind: "text",
562
+ text: parsed.response
563
+ });
564
+ }
403
565
  }
404
566
  if (toolCalls.length === 0 && isHarmonyContent(content)) {
405
567
  const parsedHarmony = parseHarmonyContent(content);
@@ -412,15 +574,23 @@ function collapseOllamaNDJSON(body) {
412
574
  toolCalls.push(...parsedHarmony.toolCalls);
413
575
  }
414
576
  }
415
- if (toolCalls.length > 0) return {
416
- ...content ? { content } : {},
417
- toolCalls,
418
- ...reasoning ? { reasoning } : {},
419
- ...droppedChunks > 0 ? { droppedChunks } : {},
420
- ...firstDroppedSample ? { firstDroppedSample } : {},
421
- ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
422
- ...harmonyNote ? { harmonyNote } : {}
423
- };
577
+ if (toolCalls.length > 0) {
578
+ const blocks = buildOrderedBlocks(orderAtoms);
579
+ const normalizedToolCalls = toolCalls.map((tc) => ({
580
+ ...tc,
581
+ arguments: normalizeToolArguments(tc.arguments)
582
+ }));
583
+ return {
584
+ ...blocks ? { blocks } : {},
585
+ ...content ? { content } : {},
586
+ toolCalls: normalizedToolCalls,
587
+ ...reasoning ? { reasoning } : {},
588
+ ...droppedChunks > 0 ? { droppedChunks } : {},
589
+ ...firstDroppedSample ? { firstDroppedSample } : {},
590
+ ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
591
+ ...harmonyNote ? { harmonyNote } : {}
592
+ };
593
+ }
424
594
  return {
425
595
  content,
426
596
  ...reasoning ? { reasoning } : {},
@@ -445,6 +615,7 @@ function collapseCohereSSE(body) {
445
615
  const toolCallMap = /* @__PURE__ */ new Map();
446
616
  let nextSyntheticIndex = 1e6;
447
617
  let lastStartKey;
618
+ const orderAtoms = [];
448
619
  for (const block of blocks) {
449
620
  const lines = splitSSELines(block);
450
621
  const eventLine = lines.find((l) => l.startsWith("event:"));
@@ -463,7 +634,13 @@ function collapseCohereSSE(body) {
463
634
  if (eventType === "content-delta") {
464
635
  const contentObj = (parsed.delta?.message)?.content;
465
636
  if (contentObj && contentObj.type === "thinking" && typeof contentObj.thinking === "string") reasoning += contentObj.thinking;
466
- else if (contentObj && typeof contentObj.text === "string") content += contentObj.text;
637
+ else if (contentObj && typeof contentObj.text === "string") {
638
+ content += contentObj.text;
639
+ if (contentObj.text.length > 0) orderAtoms.push({
640
+ kind: "text",
641
+ text: contentObj.text
642
+ });
643
+ }
467
644
  }
468
645
  if (eventType === "tool-call-start") {
469
646
  let index;
@@ -473,10 +650,15 @@ function collapseCohereSSE(body) {
473
650
  const toolCalls = (parsed.delta?.message)?.tool_calls;
474
651
  if (toolCalls) {
475
652
  const fn = toolCalls.function;
476
- toolCallMap.set(index, {
653
+ const created = {
477
654
  id: toolCalls.id ?? "",
478
655
  name: fn?.name ?? "",
479
656
  arguments: ""
657
+ };
658
+ toolCallMap.set(index, created);
659
+ orderAtoms.push({
660
+ kind: "toolCall",
661
+ ref: created
480
662
  });
481
663
  }
482
664
  }
@@ -497,14 +679,21 @@ function collapseCohereSSE(body) {
497
679
  }
498
680
  }
499
681
  if (toolCallMap.size > 0) {
500
- const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);
682
+ const orderedBlocks = buildOrderedBlocks(orderAtoms);
683
+ const orderedToolCalls = orderAtoms.filter((a) => a.kind === "toolCall").map((a) => ({
684
+ name: a.ref.name,
685
+ arguments: normalizeToolArguments(a.ref.arguments),
686
+ ...a.ref.id ? { id: a.ref.id } : {}
687
+ }));
688
+ const indexSortedToolCalls = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b).map(([, tc]) => ({
689
+ name: tc.name,
690
+ arguments: normalizeToolArguments(tc.arguments),
691
+ ...tc.id ? { id: tc.id } : {}
692
+ }));
501
693
  return {
694
+ ...orderedBlocks ? { blocks: orderedBlocks } : {},
502
695
  ...content ? { content } : {},
503
- toolCalls: sorted.map(([, tc]) => ({
504
- name: tc.name,
505
- arguments: tc.arguments,
506
- ...tc.id ? { id: tc.id } : {}
507
- })),
696
+ toolCalls: orderedBlocks ? orderedToolCalls : indexSortedToolCalls,
508
697
  ...reasoning ? { reasoning } : {},
509
698
  ...droppedChunks > 0 ? { droppedChunks } : {},
510
699
  ...firstDroppedSample ? { firstDroppedSample } : {}
@@ -617,6 +806,7 @@ function collapseBedrockEventStream(body) {
617
806
  let droppedChunks = 0;
618
807
  let firstDroppedSample;
619
808
  const toolCallMap = /* @__PURE__ */ new Map();
809
+ const orderAtoms = [];
620
810
  for (const frame of frames) {
621
811
  const frameStr = frame.payload.toString("utf8");
622
812
  let parsed;
@@ -629,7 +819,13 @@ function collapseBedrockEventStream(body) {
629
819
  }
630
820
  if (parsed.type === "content_block_delta") {
631
821
  const delta = parsed.delta;
632
- if (delta?.type === "text_delta" && typeof delta.text === "string") content += delta.text;
822
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
823
+ content += delta.text;
824
+ if (delta.text.length > 0) orderAtoms.push({
825
+ kind: "text",
826
+ text: delta.text
827
+ });
828
+ }
633
829
  if (delta?.type === "thinking_delta" && typeof delta.thinking === "string") reasoning += delta.thinking;
634
830
  if (delta?.type === "signature_delta" && typeof delta.signature === "string") reasoningSignature = delta.signature;
635
831
  if (delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
@@ -648,11 +844,18 @@ function collapseBedrockEventStream(body) {
648
844
  const index = parsed.index;
649
845
  const redactedData = capturedRedactedData(block);
650
846
  if (redactedData !== void 0) redactedThinking.push(redactedData);
651
- if (block?.type === "tool_use" && index !== void 0) toolCallMap.set(index, {
652
- id: block.id ?? "",
653
- name: block.name ?? "",
654
- arguments: ""
655
- });
847
+ if (block?.type === "tool_use" && index !== void 0) {
848
+ const created = {
849
+ id: block.id ?? "",
850
+ name: block.name ?? "",
851
+ arguments: ""
852
+ };
853
+ toolCallMap.set(index, created);
854
+ orderAtoms.push({
855
+ kind: "toolCall",
856
+ ref: created
857
+ });
858
+ }
656
859
  continue;
657
860
  }
658
861
  if (parsed.contentBlockStart) {
@@ -661,10 +864,15 @@ function collapseBedrockEventStream(body) {
661
864
  const start = blockStart.start;
662
865
  if (start?.toolUse && index !== void 0) {
663
866
  const toolUse = start.toolUse;
664
- toolCallMap.set(index, {
867
+ const created = {
665
868
  id: toolUse.toolUseId ?? "",
666
869
  name: toolUse.name ?? "",
667
870
  arguments: ""
871
+ };
872
+ toolCallMap.set(index, created);
873
+ orderAtoms.push({
874
+ kind: "toolCall",
875
+ ref: created
668
876
  });
669
877
  }
670
878
  }
@@ -673,7 +881,13 @@ function collapseBedrockEventStream(body) {
673
881
  const index = parsed.contentBlockIndex ?? blockDelta.contentBlockIndex;
674
882
  const delta = blockDelta.delta;
675
883
  if (!delta) continue;
676
- if (typeof delta.text === "string") content += delta.text;
884
+ if (typeof delta.text === "string") {
885
+ content += delta.text;
886
+ if (delta.text.length > 0) orderAtoms.push({
887
+ kind: "text",
888
+ text: delta.text
889
+ });
890
+ }
677
891
  if (typeof delta.reasoningContent === "object" && delta.reasoningContent !== null) {
678
892
  const reasoningDelta = delta.reasoningContent;
679
893
  if (typeof reasoningDelta.text === "string") reasoning += reasoningDelta.text;
@@ -691,19 +905,30 @@ function collapseBedrockEventStream(body) {
691
905
  }
692
906
  }
693
907
  }
694
- if (toolCallMap.size > 0) return {
695
- toolCalls: Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b).map(([, tc]) => ({
908
+ if (toolCallMap.size > 0) {
909
+ const orderedBlocks = buildOrderedBlocks(orderAtoms);
910
+ const orderedToolCalls = orderAtoms.filter((a) => a.kind === "toolCall").map((a) => ({
911
+ name: a.ref.name,
912
+ arguments: normalizeToolArguments(a.ref.arguments),
913
+ ...a.ref.id ? { id: a.ref.id } : {}
914
+ }));
915
+ const indexSortedToolCalls = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b).map(([, tc]) => ({
696
916
  name: tc.name,
697
- arguments: tc.arguments,
917
+ arguments: normalizeToolArguments(tc.arguments),
698
918
  ...tc.id ? { id: tc.id } : {}
699
- })),
700
- ...reasoning ? { reasoning } : {},
701
- ...reasoningSignature ? { reasoningSignature } : {},
702
- ...redactedThinking.length > 0 ? { redactedThinking } : {},
703
- ...droppedChunks > 0 ? { droppedChunks } : {},
704
- ...firstDroppedSample ? { firstDroppedSample } : {},
705
- ...truncated ? { truncated } : {}
706
- };
919
+ }));
920
+ return {
921
+ ...orderedBlocks ? { blocks: orderedBlocks } : {},
922
+ ...content ? { content } : {},
923
+ toolCalls: orderedBlocks ? orderedToolCalls : indexSortedToolCalls,
924
+ ...reasoning ? { reasoning } : {},
925
+ ...reasoningSignature ? { reasoningSignature } : {},
926
+ ...redactedThinking.length > 0 ? { redactedThinking } : {},
927
+ ...droppedChunks > 0 ? { droppedChunks } : {},
928
+ ...firstDroppedSample ? { firstDroppedSample } : {},
929
+ ...truncated ? { truncated } : {}
930
+ };
931
+ }
707
932
  return {
708
933
  content,
709
934
  ...reasoning ? { reasoning } : {},
@@ -777,7 +1002,7 @@ function collapseGeminiInteractionsSSE(body) {
777
1002
  }
778
1003
  } else if (delta.type === "function_call") toolCalls.push({
779
1004
  name: String(delta.name ?? ""),
780
- arguments: typeof delta.arguments === "string" ? delta.arguments : JSON.stringify(delta.arguments ?? {}),
1005
+ arguments: typeof delta.arguments === "string" ? normalizeToolArguments(delta.arguments) : JSON.stringify(delta.arguments ?? {}),
781
1006
  ...delta.id ? { id: String(delta.id) } : {}
782
1007
  });
783
1008
  else if (delta.type === "thought_summary") {
@@ -796,11 +1021,12 @@ function collapseGeminiInteractionsSSE(body) {
796
1021
  } catch {
797
1022
  droppedChunks++;
798
1023
  if (droppedChunks === 1) firstDroppedSample = `assembled arguments_delta not valid JSON for "${tc.name}": ${surrogateSafeSlice(args, 200)}`;
1024
+ args = "{}";
799
1025
  }
800
1026
  } else args = typeof tc.argsObj === "string" ? tc.argsObj : JSON.stringify(tc.argsObj ?? {});
801
1027
  toolCalls.push({
802
1028
  name: tc.name,
803
- arguments: args,
1029
+ arguments: normalizeToolArguments(args),
804
1030
  ...tc.id ? { id: tc.id } : {}
805
1031
  });
806
1032
  }