@copilotkit/aimock 1.27.2 → 1.28.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 (49) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +19 -0
  4. package/README.md +1 -1
  5. package/dist/config-loader.d.ts.map +1 -1
  6. package/dist/gemini.cjs +18 -8
  7. package/dist/gemini.cjs.map +1 -1
  8. package/dist/gemini.d.cts.map +1 -1
  9. package/dist/gemini.d.ts.map +1 -1
  10. package/dist/gemini.js +18 -8
  11. package/dist/gemini.js.map +1 -1
  12. package/dist/harmony.cjs +419 -0
  13. package/dist/harmony.cjs.map +1 -0
  14. package/dist/harmony.js +417 -0
  15. package/dist/harmony.js.map +1 -0
  16. package/dist/images.cjs +3 -3
  17. package/dist/images.cjs.map +1 -1
  18. package/dist/images.d.cts +1 -1
  19. package/dist/images.d.ts +1 -1
  20. package/dist/images.js +3 -3
  21. package/dist/images.js.map +1 -1
  22. package/dist/recorder.cjs +57 -13
  23. package/dist/recorder.cjs.map +1 -1
  24. package/dist/recorder.d.cts +6 -1
  25. package/dist/recorder.d.cts.map +1 -1
  26. package/dist/recorder.d.ts +6 -1
  27. package/dist/recorder.d.ts.map +1 -1
  28. package/dist/recorder.js +57 -13
  29. package/dist/recorder.js.map +1 -1
  30. package/dist/server.cjs +4 -3
  31. package/dist/server.cjs.map +1 -1
  32. package/dist/server.d.cts.map +1 -1
  33. package/dist/server.d.ts.map +1 -1
  34. package/dist/server.js +4 -3
  35. package/dist/server.js.map +1 -1
  36. package/dist/stream-collapse.cjs +219 -57
  37. package/dist/stream-collapse.cjs.map +1 -1
  38. package/dist/stream-collapse.d.cts +16 -0
  39. package/dist/stream-collapse.d.cts.map +1 -1
  40. package/dist/stream-collapse.d.ts +16 -0
  41. package/dist/stream-collapse.d.ts.map +1 -1
  42. package/dist/stream-collapse.js +219 -57
  43. package/dist/stream-collapse.js.map +1 -1
  44. package/dist/types.d.cts +9 -0
  45. package/dist/types.d.cts.map +1 -1
  46. package/dist/types.d.ts +9 -0
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/vector-types.d.cts.map +1 -1
  49. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
+ const require_harmony = require('./harmony.cjs');
2
3
  let node_zlib = require("node:zlib");
3
4
 
4
5
  //#region src/stream-collapse.ts
@@ -11,6 +12,60 @@ let node_zlib = require("node:zlib");
11
12
  * text followed by tool calls.
12
13
  */
13
14
  /**
15
+ * Slice the first `max` UTF-16 code units of `s` for a diagnostic sample,
16
+ * trimming a trailing lone high-surrogate so the resulting sample never ends on
17
+ * a lone high surrogate (i.e. never mid-surrogate-pair).
18
+ */
19
+ function surrogateSafeSlice(s, max) {
20
+ let out = s.slice(0, max);
21
+ if (out.length > 0) {
22
+ const last = out.charCodeAt(out.length - 1);
23
+ if (last >= 55296 && last <= 56319) out = out.slice(0, -1);
24
+ }
25
+ return out;
26
+ }
27
+ /**
28
+ * Split a raw SSE body into per-event blocks.
29
+ *
30
+ * Events are delimited by a blank line. Real HTTP/SSE transports use CRLF
31
+ * (`\r\n`) line endings, so the inter-event delimiter is `\r\n\r\n` (which
32
+ * contains no `\n\n` substring) and each line ends with a trailing `\r`.
33
+ * Splitting on `/\r?\n\r?\n/` handles LF, CRLF, and mixed streams; per-line
34
+ * `\r` trimming happens in {@link splitSSELines}. Blank blocks are dropped.
35
+ */
36
+ function splitSSEEvents(body) {
37
+ return body.split(/\r?\n\r?\n/).filter((block) => block.trim().length > 0);
38
+ }
39
+ /**
40
+ * Split a single SSE event block into its lines, trimming a trailing `\r` so
41
+ * CRLF streams parse identically to LF streams.
42
+ */
43
+ function splitSSELines(block) {
44
+ return block.split("\n").map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
45
+ }
46
+ /**
47
+ * Extract the SSE `data` field from a single event block's lines.
48
+ *
49
+ * Per the SSE spec a single event may carry MULTIPLE `data:` lines; the field
50
+ * value is every data line's content joined with "\n". Collecting only the
51
+ * first `data:` line (e.g. via `.find`) corrupts payloads that a server split
52
+ * across lines. Callers MUST pass lines produced by {@link splitSSELines} so
53
+ * any trailing `\r` is already stripped. Returns the joined payload (with the
54
+ * leading "data:" prefix and one optional leading space stripped per line), or
55
+ * `undefined` when the block contains no `data:` line.
56
+ */
57
+ function extractSSEData(lines) {
58
+ const dataParts = [];
59
+ for (const line of lines) {
60
+ if (!line.startsWith("data:")) continue;
61
+ let part = line.slice(5);
62
+ if (part.startsWith(" ")) part = part.slice(1);
63
+ dataParts.push(part);
64
+ }
65
+ if (dataParts.length === 0) return void 0;
66
+ return dataParts.join("\n");
67
+ }
68
+ /**
14
69
  * Collapse OpenAI Chat Completions SSE stream into a single response.
15
70
  *
16
71
  * Format:
@@ -18,24 +73,28 @@ let node_zlib = require("node:zlib");
18
73
  * data: [DONE]\n\n
19
74
  */
20
75
  function collapseOpenAISSE(body) {
21
- const lines = body.split("\n\n").filter((l) => l.trim().length > 0);
76
+ const lines = splitSSEEvents(body);
22
77
  let content = "";
23
78
  let reasoning = "";
24
79
  const webSearchQueries = [];
25
80
  let droppedChunks = 0;
26
81
  let firstDroppedSample;
82
+ let harmonyUnparsed = false;
83
+ let harmonyNote;
27
84
  const toolCallMap = /* @__PURE__ */ new Map();
85
+ let nextSyntheticIndex = 1e6;
86
+ const idKeyMap = /* @__PURE__ */ new Map();
28
87
  for (const line of lines) {
29
- const dataLine = line.split("\n").find((l) => l.startsWith("data:"));
30
- if (!dataLine) continue;
31
- const payload = dataLine.slice(5).trim();
88
+ const data = extractSSEData(splitSSELines(line));
89
+ if (data === void 0) continue;
90
+ const payload = data.trim();
32
91
  if (payload === "[DONE]") continue;
33
92
  let parsed;
34
93
  try {
35
94
  parsed = JSON.parse(payload);
36
95
  } catch (err) {
37
96
  droppedChunks++;
38
- if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload.slice(0, 200)}`;
97
+ if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
39
98
  continue;
40
99
  }
41
100
  if (parsed.type === "response.reasoning_summary_text.delta" && typeof parsed.delta === "string") {
@@ -65,10 +124,20 @@ function collapseOpenAISSE(body) {
65
124
  if (typeof delta.content === "string") content += delta.content;
66
125
  const toolCalls = delta.tool_calls;
67
126
  if (toolCalls) for (const tc of toolCalls) {
68
- const index = tc.index;
69
127
  const fn = tc.function;
128
+ const rawId = typeof tc.id === "string" ? tc.id : void 0;
129
+ let index;
130
+ if (typeof tc.index === "number") index = tc.index;
131
+ else if (rawId !== void 0) {
132
+ const existing = idKeyMap.get(rawId);
133
+ if (existing !== void 0) index = existing;
134
+ else {
135
+ index = nextSyntheticIndex++;
136
+ idKeyMap.set(rawId, index);
137
+ }
138
+ } else index = nextSyntheticIndex++;
70
139
  if (!toolCallMap.has(index)) toolCallMap.set(index, {
71
- id: tc.id ?? "",
140
+ id: rawId ?? "",
72
141
  name: fn?.name ?? "",
73
142
  arguments: ""
74
143
  });
@@ -78,17 +147,33 @@ function collapseOpenAISSE(body) {
78
147
  if (fn?.arguments && typeof fn.arguments === "string") entry.arguments += fn.arguments;
79
148
  }
80
149
  }
81
- if (toolCallMap.size > 0) {
150
+ const harmonyToolCalls = [];
151
+ if (toolCallMap.size === 0 && require_harmony.isHarmonyContent(content)) {
152
+ const parsed = require_harmony.parseHarmonyContent(content);
153
+ if (parsed.failed) {
154
+ harmonyUnparsed = true;
155
+ harmonyNote = `harmony tokens present but unparseable; content preserved verbatim: ${surrogateSafeSlice(content, 200)}`;
156
+ } else {
157
+ content = parsed.content;
158
+ if (parsed.reasoning) reasoning += parsed.reasoning;
159
+ harmonyToolCalls.push(...parsed.toolCalls);
160
+ }
161
+ }
162
+ if (toolCallMap.size > 0 || harmonyToolCalls.length > 0) {
82
163
  const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);
83
164
  return {
84
165
  ...content ? { content } : {},
85
- toolCalls: sorted.map(([, tc]) => ({
166
+ toolCalls: [...sorted.map(([, tc]) => ({
86
167
  name: tc.name,
87
168
  arguments: tc.arguments,
88
169
  ...tc.id ? { id: tc.id } : {}
89
- })),
170
+ })), ...harmonyToolCalls],
171
+ ...reasoning ? { reasoning } : {},
172
+ ...webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {},
90
173
  ...droppedChunks > 0 ? { droppedChunks } : {},
91
- ...firstDroppedSample ? { firstDroppedSample } : {}
174
+ ...firstDroppedSample ? { firstDroppedSample } : {},
175
+ ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
176
+ ...harmonyNote ? { harmonyNote } : {}
92
177
  };
93
178
  }
94
179
  return {
@@ -96,7 +181,9 @@ function collapseOpenAISSE(body) {
96
181
  ...reasoning ? { reasoning } : {},
97
182
  ...webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {},
98
183
  ...droppedChunks > 0 ? { droppedChunks } : {},
99
- ...firstDroppedSample ? { firstDroppedSample } : {}
184
+ ...firstDroppedSample ? { firstDroppedSample } : {},
185
+ ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
186
+ ...harmonyNote ? { harmonyNote } : {}
100
187
  };
101
188
  }
102
189
  /**
@@ -107,45 +194,56 @@ function collapseOpenAISSE(body) {
107
194
  * event: content_block_delta\ndata: {"delta":{"type":"text_delta","text":"Hello"}}\n\n
108
195
  */
109
196
  function collapseAnthropicSSE(body) {
110
- const blocks = body.split("\n\n").filter((b) => b.trim().length > 0);
197
+ const blocks = splitSSEEvents(body);
111
198
  let content = "";
112
199
  let reasoning = "";
113
200
  let droppedChunks = 0;
114
201
  let firstDroppedSample;
115
202
  const toolCallMap = /* @__PURE__ */ new Map();
203
+ let nextSyntheticIndex = 1e6;
204
+ let lastSyntheticIndex;
116
205
  for (const block of blocks) {
117
- const lines = block.split("\n");
206
+ const lines = splitSSELines(block);
118
207
  const eventLine = lines.find((l) => l.startsWith("event:"));
119
- const dataLine = lines.find((l) => l.startsWith("data:"));
120
- if (!dataLine) continue;
208
+ const data = extractSSEData(lines);
209
+ if (data === void 0) continue;
121
210
  const eventType = eventLine ? eventLine.slice(6).trim() : "";
122
- const payload = dataLine.slice(5).trim();
211
+ const payload = data.trim();
123
212
  let parsed;
124
213
  try {
125
214
  parsed = JSON.parse(payload);
126
215
  } catch (err) {
127
216
  droppedChunks++;
128
- if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload.slice(0, 200)}`;
217
+ if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
129
218
  continue;
130
219
  }
131
220
  if (eventType === "content_block_start") {
132
- const index = parsed.index;
133
221
  const contentBlock = parsed.content_block;
134
- if (contentBlock?.type === "tool_use") toolCallMap.set(index, {
135
- id: contentBlock.id ?? "",
136
- name: contentBlock.name ?? "",
137
- arguments: ""
138
- });
222
+ if (contentBlock?.type === "tool_use") {
223
+ let index;
224
+ if (typeof parsed.index === "number") index = parsed.index;
225
+ else index = nextSyntheticIndex++;
226
+ lastSyntheticIndex = index;
227
+ toolCallMap.set(index, {
228
+ id: contentBlock.id ?? "",
229
+ name: contentBlock.name ?? "",
230
+ arguments: ""
231
+ });
232
+ }
139
233
  }
140
234
  if (eventType === "content_block_delta") {
141
- const index = parsed.index;
142
235
  const delta = parsed.delta;
143
236
  if (!delta) continue;
144
237
  if (delta.type === "text_delta" && typeof delta.text === "string") content += delta.text;
145
238
  if (delta.type === "thinking_delta" && typeof delta.thinking === "string") reasoning += delta.thinking;
146
239
  if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
147
- const entry = toolCallMap.get(index);
240
+ const index = typeof parsed.index === "number" ? parsed.index : lastSyntheticIndex;
241
+ const entry = index !== void 0 ? toolCallMap.get(index) : void 0;
148
242
  if (entry) entry.arguments += delta.partial_json;
243
+ else {
244
+ droppedChunks++;
245
+ if (droppedChunks === 1) firstDroppedSample = `input_json_delta with no correlating tool_use start: ${surrogateSafeSlice(payload, 200)}`;
246
+ }
149
247
  }
150
248
  }
151
249
  }
@@ -177,7 +275,7 @@ function collapseAnthropicSSE(body) {
177
275
  * data: {"candidates":[{"content":{"parts":[{"text":"Hello"}]}}]}\n\n
178
276
  */
179
277
  function collapseGeminiSSE(body) {
180
- const lines = body.split("\n\n").filter((l) => l.trim().length > 0);
278
+ const lines = splitSSEEvents(body);
181
279
  let content = "";
182
280
  let reasoning = "";
183
281
  let droppedChunks = 0;
@@ -186,15 +284,15 @@ function collapseGeminiSSE(body) {
186
284
  let audioMimeType;
187
285
  const toolCalls = [];
188
286
  for (const line of lines) {
189
- const dataLine = line.split("\n").find((l) => l.startsWith("data:"));
190
- if (!dataLine) continue;
191
- const payload = dataLine.slice(5).trim();
287
+ const data = extractSSEData(splitSSELines(line));
288
+ if (data === void 0) continue;
289
+ const payload = data.trim();
192
290
  let parsed;
193
291
  try {
194
292
  parsed = JSON.parse(payload);
195
293
  } catch (err) {
196
294
  droppedChunks++;
197
- if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload.slice(0, 200)}`;
295
+ if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
198
296
  continue;
199
297
  }
200
298
  const candidates = parsed.candidates;
@@ -207,7 +305,7 @@ function collapseGeminiSSE(body) {
207
305
  const fc = part.functionCall;
208
306
  toolCalls.push({
209
307
  name: String(fc.name ?? ""),
210
- arguments: typeof fc.args === "string" ? fc.args : JSON.stringify(fc.args)
308
+ arguments: typeof fc.args === "string" ? fc.args : JSON.stringify(fc.args ?? {})
211
309
  });
212
310
  } else if (part.inlineData && typeof part.inlineData.mimeType === "string" && part.inlineData.mimeType.startsWith("audio/")) {
213
311
  const inlineData = part.inlineData;
@@ -219,6 +317,9 @@ function collapseGeminiSSE(body) {
219
317
  if (audioB64) return {
220
318
  audioB64,
221
319
  audioMimeType,
320
+ ...content ? { content } : {},
321
+ ...reasoning ? { reasoning } : {},
322
+ ...toolCalls.length > 0 ? { toolCalls } : {},
222
323
  ...droppedChunks > 0 ? { droppedChunks } : {},
223
324
  ...firstDroppedSample ? { firstDroppedSample } : {}
224
325
  };
@@ -244,12 +345,20 @@ function collapseGeminiSSE(body) {
244
345
  *
245
346
  * /api/generate format:
246
347
  * {"model":"llama3","response":"Hello","done":false}\n
348
+ *
349
+ * Open-weight gpt-oss served via Ollama streams harmony channel tokens inside
350
+ * `message.content` (just like the OpenAI SSE path), so after accumulation the
351
+ * content is run through the same fail-safe {@link parseHarmonyContent} gate to
352
+ * capture structured tool calls / reasoning instead of leaking raw tokens.
247
353
  */
248
354
  function collapseOllamaNDJSON(body) {
249
355
  const lines = body.split("\n").filter((l) => l.trim().length > 0);
250
356
  let content = "";
357
+ let reasoning = "";
251
358
  let droppedChunks = 0;
252
359
  let firstDroppedSample;
360
+ let harmonyUnparsed = false;
361
+ let harmonyNote;
253
362
  const toolCalls = [];
254
363
  for (const line of lines) {
255
364
  let parsed;
@@ -257,7 +366,7 @@ function collapseOllamaNDJSON(body) {
257
366
  parsed = JSON.parse(line.trim());
258
367
  } catch (err) {
259
368
  droppedChunks++;
260
- if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${line.trim().slice(0, 200)}`;
369
+ if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(line.trim(), 200)}`;
261
370
  continue;
262
371
  }
263
372
  const message = parsed.message;
@@ -267,21 +376,38 @@ function collapseOllamaNDJSON(body) {
267
376
  const fn = tc.function;
268
377
  if (fn) toolCalls.push({
269
378
  name: String(fn.name ?? ""),
270
- arguments: typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments)
379
+ arguments: typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments ?? {})
271
380
  });
272
381
  }
273
382
  } else if (typeof parsed.response === "string") content += parsed.response;
274
383
  }
384
+ if (toolCalls.length === 0 && require_harmony.isHarmonyContent(content)) {
385
+ const parsedHarmony = require_harmony.parseHarmonyContent(content);
386
+ if (parsedHarmony.failed) {
387
+ harmonyUnparsed = true;
388
+ harmonyNote = `harmony tokens present but unparseable; content preserved verbatim: ${surrogateSafeSlice(content, 200)}`;
389
+ } else {
390
+ content = parsedHarmony.content;
391
+ if (parsedHarmony.reasoning) reasoning += parsedHarmony.reasoning;
392
+ toolCalls.push(...parsedHarmony.toolCalls);
393
+ }
394
+ }
275
395
  if (toolCalls.length > 0) return {
276
396
  ...content ? { content } : {},
277
397
  toolCalls,
398
+ ...reasoning ? { reasoning } : {},
278
399
  ...droppedChunks > 0 ? { droppedChunks } : {},
279
- ...firstDroppedSample ? { firstDroppedSample } : {}
400
+ ...firstDroppedSample ? { firstDroppedSample } : {},
401
+ ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
402
+ ...harmonyNote ? { harmonyNote } : {}
280
403
  };
281
404
  return {
282
405
  content,
406
+ ...reasoning ? { reasoning } : {},
283
407
  ...droppedChunks > 0 ? { droppedChunks } : {},
284
- ...firstDroppedSample ? { firstDroppedSample } : {}
408
+ ...firstDroppedSample ? { firstDroppedSample } : {},
409
+ ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
410
+ ...harmonyNote ? { harmonyNote } : {}
285
411
  };
286
412
  }
287
413
  /**
@@ -291,24 +417,26 @@ function collapseOllamaNDJSON(body) {
291
417
  * event: content-delta\ndata: {"type":"content-delta","delta":{"message":{"content":{"text":"Hello"}}}}\n\n
292
418
  */
293
419
  function collapseCohereSSE(body) {
294
- const blocks = body.split("\n\n").filter((b) => b.trim().length > 0);
420
+ const blocks = splitSSEEvents(body);
295
421
  let content = "";
296
422
  let droppedChunks = 0;
297
423
  let firstDroppedSample;
298
424
  const toolCallMap = /* @__PURE__ */ new Map();
425
+ let nextSyntheticIndex = 1e6;
426
+ let lastStartKey;
299
427
  for (const block of blocks) {
300
- const lines = block.split("\n");
428
+ const lines = splitSSELines(block);
301
429
  const eventLine = lines.find((l) => l.startsWith("event:"));
302
- const dataLine = lines.find((l) => l.startsWith("data:"));
303
- if (!dataLine) continue;
430
+ const data = extractSSEData(lines);
431
+ if (data === void 0) continue;
304
432
  const eventType = eventLine ? eventLine.slice(6).trim() : "";
305
- const payload = dataLine.slice(5).trim();
433
+ const payload = data.trim();
306
434
  let parsed;
307
435
  try {
308
436
  parsed = JSON.parse(payload);
309
437
  } catch (err) {
310
438
  droppedChunks++;
311
- if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload.slice(0, 200)}`;
439
+ if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
312
440
  continue;
313
441
  }
314
442
  if (eventType === "content-delta") {
@@ -316,7 +444,10 @@ function collapseCohereSSE(body) {
316
444
  if (contentObj && typeof contentObj.text === "string") content += contentObj.text;
317
445
  }
318
446
  if (eventType === "tool-call-start") {
319
- const index = parsed.index;
447
+ let index;
448
+ if (typeof parsed.index === "number") index = parsed.index;
449
+ else index = nextSyntheticIndex++;
450
+ lastStartKey = index;
320
451
  const toolCalls = (parsed.delta?.message)?.tool_calls;
321
452
  if (toolCalls) {
322
453
  const fn = toolCalls.function;
@@ -328,13 +459,17 @@ function collapseCohereSSE(body) {
328
459
  }
329
460
  }
330
461
  if (eventType === "tool-call-delta") {
331
- const index = parsed.index;
462
+ const index = typeof parsed.index === "number" ? parsed.index : lastStartKey;
332
463
  const toolCalls = (parsed.delta?.message)?.tool_calls;
333
464
  if (toolCalls) {
334
465
  const fn = toolCalls.function;
335
466
  if (fn && typeof fn.arguments === "string") {
336
- const entry = toolCallMap.get(index);
467
+ const entry = index !== void 0 ? toolCallMap.get(index) : void 0;
337
468
  if (entry) entry.arguments += fn.arguments;
469
+ else {
470
+ droppedChunks++;
471
+ if (droppedChunks === 1) firstDroppedSample = `tool-call-delta with no correlating start: ${surrogateSafeSlice(payload, 200)}`;
472
+ }
338
473
  }
339
474
  }
340
475
  }
@@ -388,22 +523,43 @@ function decodeEventStreamFrames(buf) {
388
523
  };
389
524
  const headersStart = offset + 12;
390
525
  const headersEnd = headersStart + headersLength;
526
+ const payloadEnd = offset + totalLength - 4;
527
+ if (headersEnd > payloadEnd || headersEnd > buf.length) return {
528
+ frames,
529
+ truncated: true
530
+ };
391
531
  const headers = {};
392
532
  let hOffset = headersStart;
533
+ let headerOverrun = false;
393
534
  while (hOffset < headersEnd) {
535
+ if (hOffset + 1 > headersEnd) {
536
+ headerOverrun = true;
537
+ break;
538
+ }
394
539
  const nameLen = buf.readUInt8(hOffset);
395
540
  hOffset += 1;
541
+ if (hOffset + nameLen + 1 + 2 > headersEnd) {
542
+ headerOverrun = true;
543
+ break;
544
+ }
396
545
  const name = buf.subarray(hOffset, hOffset + nameLen).toString("utf8");
397
546
  hOffset += nameLen;
398
547
  hOffset += 1;
399
548
  const valueLen = buf.readUInt16BE(hOffset);
400
549
  hOffset += 2;
550
+ if (hOffset + valueLen > headersEnd) {
551
+ headerOverrun = true;
552
+ break;
553
+ }
401
554
  const value = buf.subarray(hOffset, hOffset + valueLen).toString("utf8");
402
555
  hOffset += valueLen;
403
556
  headers[name] = value;
404
557
  }
558
+ if (headerOverrun) return {
559
+ frames,
560
+ truncated: true
561
+ };
405
562
  const payloadStart = headersEnd;
406
- const payloadEnd = offset + totalLength - 4;
407
563
  const payload = buf.subarray(payloadStart, payloadEnd);
408
564
  const messageCrc = buf.readUInt32BE(offset + totalLength - 4);
409
565
  const computedMessageCrc = (0, node_zlib.crc32)(buf.subarray(offset, offset + totalLength - 4));
@@ -441,7 +597,7 @@ function collapseBedrockEventStream(body) {
441
597
  parsed = JSON.parse(frameStr);
442
598
  } catch (err) {
443
599
  droppedChunks++;
444
- if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${frameStr.slice(0, 200)}`;
600
+ if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(frameStr, 200)}`;
445
601
  continue;
446
602
  }
447
603
  if (parsed.type === "content_block_delta") {
@@ -449,9 +605,11 @@ function collapseBedrockEventStream(body) {
449
605
  if (delta?.type === "text_delta" && typeof delta.text === "string") content += delta.text;
450
606
  if (delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
451
607
  const index = parsed.index;
452
- if (index !== void 0) {
453
- const entry = toolCallMap.get(index);
454
- if (entry) entry.arguments += delta.partial_json;
608
+ const entry = index !== void 0 ? toolCallMap.get(index) : void 0;
609
+ if (entry) entry.arguments += delta.partial_json;
610
+ else {
611
+ droppedChunks++;
612
+ if (droppedChunks === 1) firstDroppedSample = `input_json_delta with no correlating tool_use start: ${surrogateSafeSlice(frameStr, 200)}`;
455
613
  }
456
614
  }
457
615
  continue;
@@ -487,9 +645,13 @@ function collapseBedrockEventStream(body) {
487
645
  if (typeof delta.text === "string") content += delta.text;
488
646
  if (typeof delta.toolUse === "object" && delta.toolUse !== null) {
489
647
  const toolUseDelta = delta.toolUse;
490
- if (typeof toolUseDelta.input === "string" && index !== void 0) {
491
- const entry = toolCallMap.get(index);
648
+ if (typeof toolUseDelta.input === "string") {
649
+ const entry = index !== void 0 ? toolCallMap.get(index) : void 0;
492
650
  if (entry) entry.arguments += toolUseDelta.input;
651
+ else {
652
+ droppedChunks++;
653
+ if (droppedChunks === 1) firstDroppedSample = `toolUse.input delta with no correlating tool_use start: ${surrogateSafeSlice(frameStr, 200)}`;
654
+ }
493
655
  }
494
656
  }
495
657
  }
@@ -519,22 +681,22 @@ function collapseBedrockEventStream(body) {
519
681
  * data: {"event_type":"interaction.complete","interaction":{"id":"...","usage":{...}}}\n\n
520
682
  */
521
683
  function collapseGeminiInteractionsSSE(body) {
522
- const lines = body.split("\n\n").filter((l) => l.trim().length > 0);
684
+ const lines = splitSSEEvents(body);
523
685
  let content = "";
524
686
  let reasoning = "";
525
687
  let droppedChunks = 0;
526
688
  let firstDroppedSample;
527
689
  const toolCalls = [];
528
690
  for (const line of lines) {
529
- const dataLine = line.split("\n").find((l) => l.startsWith("data:"));
530
- if (!dataLine) continue;
531
- const payload = dataLine.slice(5).trim();
691
+ const data = extractSSEData(splitSSELines(line));
692
+ if (data === void 0) continue;
693
+ const payload = data.trim();
532
694
  let parsed;
533
695
  try {
534
696
  parsed = JSON.parse(payload);
535
697
  } catch (err) {
536
698
  droppedChunks++;
537
- if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${payload.slice(0, 200)}`;
699
+ if (droppedChunks === 1) firstDroppedSample = `parse failed (${err instanceof Error ? err.message : "unknown"}): ${surrogateSafeSlice(payload, 200)}`;
538
700
  continue;
539
701
  }
540
702
  const eventType = parsed.event_type;