@copilotkit/aimock 1.37.0 → 1.37.2

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 (51) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/agui-recorder.cjs +62 -0
  3. package/dist/agui-recorder.cjs.map +1 -1
  4. package/dist/agui-recorder.d.cts +23 -1
  5. package/dist/agui-recorder.d.cts.map +1 -1
  6. package/dist/agui-recorder.d.ts +23 -1
  7. package/dist/agui-recorder.d.ts.map +1 -1
  8. package/dist/agui-recorder.js +60 -1
  9. package/dist/agui-recorder.js.map +1 -1
  10. package/dist/agui-types.d.cts +13 -0
  11. package/dist/agui-types.d.ts +13 -0
  12. package/dist/config-loader.d.ts.map +1 -1
  13. package/dist/index.cjs +5 -0
  14. package/dist/index.d.cts +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +3 -3
  17. package/dist/journal.cjs +35 -1
  18. package/dist/journal.cjs.map +1 -1
  19. package/dist/journal.d.cts.map +1 -1
  20. package/dist/journal.d.ts.map +1 -1
  21. package/dist/journal.js +35 -1
  22. package/dist/journal.js.map +1 -1
  23. package/dist/recorder.cjs +12 -4
  24. package/dist/recorder.cjs.map +1 -1
  25. package/dist/recorder.d.cts.map +1 -1
  26. package/dist/recorder.d.ts.map +1 -1
  27. package/dist/recorder.js +12 -4
  28. package/dist/recorder.js.map +1 -1
  29. package/dist/router.cjs +53 -25
  30. package/dist/router.cjs.map +1 -1
  31. package/dist/router.d.cts +20 -0
  32. package/dist/router.d.cts.map +1 -1
  33. package/dist/router.d.ts +20 -0
  34. package/dist/router.d.ts.map +1 -1
  35. package/dist/router.js +53 -25
  36. package/dist/router.js.map +1 -1
  37. package/dist/server.cjs +4 -1
  38. package/dist/server.cjs.map +1 -1
  39. package/dist/server.js +4 -1
  40. package/dist/server.js.map +1 -1
  41. package/dist/stream-collapse.cjs +120 -27
  42. package/dist/stream-collapse.cjs.map +1 -1
  43. package/dist/stream-collapse.d.cts +31 -8
  44. package/dist/stream-collapse.d.cts.map +1 -1
  45. package/dist/stream-collapse.d.ts +31 -8
  46. package/dist/stream-collapse.d.ts.map +1 -1
  47. package/dist/stream-collapse.js +119 -28
  48. package/dist/stream-collapse.js.map +1 -1
  49. package/dist/ws-gemini-live.d.cts +2 -2
  50. package/dist/ws-realtime.d.cts +2 -2
  51. package/package.json +1 -1
@@ -12,6 +12,75 @@ let node_zlib = require("node:zlib");
12
12
  * text followed by tool calls.
13
13
  */
14
14
  /**
15
+ * Hard ceiling (UTF-16 code units) for any SINGLE string a collapser
16
+ * accumulates across deltas (`content`, `reasoning`, a tool call's
17
+ * `arguments`, `audioB64`, `argsStr`, and the coalesced text of `orderAtoms`).
18
+ *
19
+ * V8's maximum string length on 64-bit is 2^29 - 1 (~536.8M UTF-16 code
20
+ * units); appending past it throws `RangeError: Invalid string length`. The
21
+ * proxy path bounds the raw BYTE buffer under `PROXY_BUFFER_HARD_CEILING`
22
+ * (256 MiB) before collapse, but the collapser is ALSO reachable directly
23
+ * (exported from index.ts) and — even on the proxy path — a single accumulator
24
+ * can approach the byte budget in code units, so it needs its own guard rather
25
+ * than relying on the byte cap alone. 256Mi code units is comfortably below
26
+ * V8's limit while leaving huge headroom for any real completion (whose
27
+ * accumulated content can never exceed the total streamed body).
28
+ *
29
+ * When an append would cross the ceiling the accumulator keeps the ceiling's
30
+ * worth of characters, drops the overflow, and marks itself truncated — never
31
+ * throwing — mirroring the proxy buffer's "bound then mark truncated, never
32
+ * fail the client" discipline.
33
+ */
34
+ const MAX_COLLAPSE_STRING_LENGTH = 256 * 1024 * 1024;
35
+ /**
36
+ * Test-only override of {@link MAX_COLLAPSE_STRING_LENGTH}. Lets the cap suite
37
+ * exercise the truncation path with a tiny budget instead of building a
38
+ * multi-hundred-MB string. `undefined` (the default) uses the real ceiling.
39
+ * NEVER set from production code.
40
+ */
41
+ let collapseStringLimitOverride;
42
+ /** @internal test-only — see {@link collapseStringLimitOverride}. */
43
+ function setCollapseStringLimitForTests(value) {
44
+ collapseStringLimitOverride = value;
45
+ }
46
+ /** Effective accumulated-string ceiling, honoring any active test-only override. */
47
+ function effectiveCollapseStringLimit() {
48
+ return collapseStringLimitOverride ?? MAX_COLLAPSE_STRING_LENGTH;
49
+ }
50
+ /**
51
+ * Guard a raw collapse-input body against V8's max string length BEFORE any
52
+ * per-delta accumulation begins. Every collapser accumulates `content` /
53
+ * `reasoning` / a tool call's `arguments` / `audioB64` / `argsStr` (and the
54
+ * coalesced text of `orderAtoms`) from fragments of `body`; the sum of any one
55
+ * of those accumulators can never exceed the total input length, so bounding
56
+ * the INPUT under the ceiling bounds EVERY accumulator at once — no per-`+=`
57
+ * site can then build a string past the limit and throw
58
+ * `RangeError: Invalid string length` (the ~1/sec prod crash).
59
+ *
60
+ * Returns the body UNCHANGED when it is within the ceiling. When it exceeds the
61
+ * ceiling this returns a hard-truncated prefix (never throwing) — the collapse
62
+ * result the caller builds from it is marked incomplete via `truncated: true`
63
+ * (see {@link isCollapseInputTruncated}). This mirrors the proxy buffer's
64
+ * "bound then mark truncated, never fail the client" discipline: on the proxy
65
+ * path a body this large has already tripped the byte cap and skipped recording,
66
+ * so this is a defense-in-depth backstop that also covers the direct (exported)
67
+ * collapse callers where no byte cap ran.
68
+ */
69
+ function guardCollapseBody(body) {
70
+ const limit = effectiveCollapseStringLimit();
71
+ if (body.length <= limit) return body;
72
+ return body.slice(0, limit);
73
+ }
74
+ /**
75
+ * True when a body exceeded the accumulated-string ceiling and had to be
76
+ * truncated by {@link guardCollapseBody}. Collapsers stamp `truncated: true` on
77
+ * their result in this case so the recorder skips journaling a partial fixture,
78
+ * exactly like a transport-truncated stream.
79
+ */
80
+ function isCollapseInputTruncated(body) {
81
+ return body.length > effectiveCollapseStringLimit();
82
+ }
83
+ /**
15
84
  * Normalize a tool call's accumulated `arguments` into valid JSON exactly like
16
85
  * the flat-`toolCalls` recorder path: an empty / whitespace-only / missing
17
86
  * value becomes `"{}"`, never `""`. Mirrors `recorder.ts` `toToolCallArguments`
@@ -166,8 +235,9 @@ function extractSSEData(lines) {
166
235
  * data: {"id":"chatcmpl-123","choices":[{"delta":{"content":"Hello"}}]}\n\n
167
236
  * data: [DONE]\n\n
168
237
  */
169
- function collapseOpenAISSE(body) {
170
- const lines = splitSSEEvents(body);
238
+ function collapseOpenAISSE(rawBody) {
239
+ const inputTruncated = isCollapseInputTruncated(rawBody);
240
+ const lines = splitSSEEvents(guardCollapseBody(rawBody));
171
241
  let content = "";
172
242
  let reasoning = "";
173
243
  const webSearchQueries = [];
@@ -288,7 +358,8 @@ function collapseOpenAISSE(body) {
288
358
  ...droppedChunks > 0 ? { droppedChunks } : {},
289
359
  ...firstDroppedSample ? { firstDroppedSample } : {},
290
360
  ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
291
- ...harmonyNote ? { harmonyNote } : {}
361
+ ...harmonyNote ? { harmonyNote } : {},
362
+ ...inputTruncated ? { truncated: true } : {}
292
363
  };
293
364
  }
294
365
  return {
@@ -298,7 +369,8 @@ function collapseOpenAISSE(body) {
298
369
  ...droppedChunks > 0 ? { droppedChunks } : {},
299
370
  ...firstDroppedSample ? { firstDroppedSample } : {},
300
371
  ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
301
- ...harmonyNote ? { harmonyNote } : {}
372
+ ...harmonyNote ? { harmonyNote } : {},
373
+ ...inputTruncated ? { truncated: true } : {}
302
374
  };
303
375
  }
304
376
  /**
@@ -308,8 +380,9 @@ function collapseOpenAISSE(body) {
308
380
  * event: message_start\ndata: {...}\n\n
309
381
  * event: content_block_delta\ndata: {"delta":{"type":"text_delta","text":"Hello"}}\n\n
310
382
  */
311
- function collapseAnthropicSSE(body) {
312
- const blocks = splitSSEEvents(body);
383
+ function collapseAnthropicSSE(rawBody) {
384
+ const inputTruncated = isCollapseInputTruncated(rawBody);
385
+ const blocks = splitSSEEvents(guardCollapseBody(rawBody));
313
386
  let content = "";
314
387
  let reasoning = "";
315
388
  let reasoningSignature;
@@ -399,7 +472,8 @@ function collapseAnthropicSSE(body) {
399
472
  ...reasoningSignature ? { reasoningSignature } : {},
400
473
  ...redactedThinking.length > 0 ? { redactedThinking } : {},
401
474
  ...droppedChunks > 0 ? { droppedChunks } : {},
402
- ...firstDroppedSample ? { firstDroppedSample } : {}
475
+ ...firstDroppedSample ? { firstDroppedSample } : {},
476
+ ...inputTruncated ? { truncated: true } : {}
403
477
  };
404
478
  }
405
479
  return {
@@ -408,7 +482,8 @@ function collapseAnthropicSSE(body) {
408
482
  ...reasoningSignature ? { reasoningSignature } : {},
409
483
  ...redactedThinking.length > 0 ? { redactedThinking } : {},
410
484
  ...droppedChunks > 0 ? { droppedChunks } : {},
411
- ...firstDroppedSample ? { firstDroppedSample } : {}
485
+ ...firstDroppedSample ? { firstDroppedSample } : {},
486
+ ...inputTruncated ? { truncated: true } : {}
412
487
  };
413
488
  }
414
489
  /**
@@ -417,8 +492,9 @@ function collapseAnthropicSSE(body) {
417
492
  * Format (data-only, no event prefix, no [DONE]):
418
493
  * data: {"candidates":[{"content":{"parts":[{"text":"Hello"}]}}]}\n\n
419
494
  */
420
- function collapseGeminiSSE(body) {
421
- const lines = splitSSEEvents(body);
495
+ function collapseGeminiSSE(rawBody) {
496
+ const inputTruncated = isCollapseInputTruncated(rawBody);
497
+ const lines = splitSSEEvents(guardCollapseBody(rawBody));
422
498
  let content = "";
423
499
  let reasoning = "";
424
500
  let droppedChunks = 0;
@@ -480,7 +556,8 @@ function collapseGeminiSSE(body) {
480
556
  ...reasoning ? { reasoning } : {},
481
557
  ...normalizedToolCalls.length > 0 ? { toolCalls: normalizedToolCalls } : {},
482
558
  ...droppedChunks > 0 ? { droppedChunks } : {},
483
- ...firstDroppedSample ? { firstDroppedSample } : {}
559
+ ...firstDroppedSample ? { firstDroppedSample } : {},
560
+ ...inputTruncated ? { truncated: true } : {}
484
561
  };
485
562
  if (toolCalls.length > 0) {
486
563
  const blocks = buildOrderedBlocks(orderAtoms);
@@ -490,14 +567,16 @@ function collapseGeminiSSE(body) {
490
567
  toolCalls: normalizedToolCalls,
491
568
  ...reasoning ? { reasoning } : {},
492
569
  ...droppedChunks > 0 ? { droppedChunks } : {},
493
- ...firstDroppedSample ? { firstDroppedSample } : {}
570
+ ...firstDroppedSample ? { firstDroppedSample } : {},
571
+ ...inputTruncated ? { truncated: true } : {}
494
572
  };
495
573
  }
496
574
  return {
497
575
  content,
498
576
  ...reasoning ? { reasoning } : {},
499
577
  ...droppedChunks > 0 ? { droppedChunks } : {},
500
- ...firstDroppedSample ? { firstDroppedSample } : {}
578
+ ...firstDroppedSample ? { firstDroppedSample } : {},
579
+ ...inputTruncated ? { truncated: true } : {}
501
580
  };
502
581
  }
503
582
  /**
@@ -514,8 +593,9 @@ function collapseGeminiSSE(body) {
514
593
  * content is run through the same fail-safe {@link parseHarmonyContent} gate to
515
594
  * capture structured tool calls / reasoning instead of leaking raw tokens.
516
595
  */
517
- function collapseOllamaNDJSON(body) {
518
- const lines = body.split("\n").filter((l) => l.trim().length > 0);
596
+ function collapseOllamaNDJSON(rawBody) {
597
+ const inputTruncated = isCollapseInputTruncated(rawBody);
598
+ const lines = guardCollapseBody(rawBody).split("\n").filter((l) => l.trim().length > 0);
519
599
  let content = "";
520
600
  let reasoning = "";
521
601
  let droppedChunks = 0;
@@ -589,7 +669,8 @@ function collapseOllamaNDJSON(body) {
589
669
  ...droppedChunks > 0 ? { droppedChunks } : {},
590
670
  ...firstDroppedSample ? { firstDroppedSample } : {},
591
671
  ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
592
- ...harmonyNote ? { harmonyNote } : {}
672
+ ...harmonyNote ? { harmonyNote } : {},
673
+ ...inputTruncated ? { truncated: true } : {}
593
674
  };
594
675
  }
595
676
  return {
@@ -598,7 +679,8 @@ function collapseOllamaNDJSON(body) {
598
679
  ...droppedChunks > 0 ? { droppedChunks } : {},
599
680
  ...firstDroppedSample ? { firstDroppedSample } : {},
600
681
  ...harmonyUnparsed ? { harmonyUnparsed: true } : {},
601
- ...harmonyNote ? { harmonyNote } : {}
682
+ ...harmonyNote ? { harmonyNote } : {},
683
+ ...inputTruncated ? { truncated: true } : {}
602
684
  };
603
685
  }
604
686
  /**
@@ -607,8 +689,9 @@ function collapseOllamaNDJSON(body) {
607
689
  * Format:
608
690
  * event: content-delta\ndata: {"type":"content-delta","delta":{"message":{"content":{"text":"Hello"}}}}\n\n
609
691
  */
610
- function collapseCohereSSE(body) {
611
- const blocks = splitSSEEvents(body);
692
+ function collapseCohereSSE(rawBody) {
693
+ const inputTruncated = isCollapseInputTruncated(rawBody);
694
+ const blocks = splitSSEEvents(guardCollapseBody(rawBody));
612
695
  let content = "";
613
696
  let reasoning = "";
614
697
  let droppedChunks = 0;
@@ -697,14 +780,16 @@ function collapseCohereSSE(body) {
697
780
  toolCalls: orderedBlocks ? orderedToolCalls : indexSortedToolCalls,
698
781
  ...reasoning ? { reasoning } : {},
699
782
  ...droppedChunks > 0 ? { droppedChunks } : {},
700
- ...firstDroppedSample ? { firstDroppedSample } : {}
783
+ ...firstDroppedSample ? { firstDroppedSample } : {},
784
+ ...inputTruncated ? { truncated: true } : {}
701
785
  };
702
786
  }
703
787
  return {
704
788
  content,
705
789
  ...reasoning ? { reasoning } : {},
706
790
  ...droppedChunks > 0 ? { droppedChunks } : {},
707
- ...firstDroppedSample ? { firstDroppedSample } : {}
791
+ ...firstDroppedSample ? { firstDroppedSample } : {},
792
+ ...inputTruncated ? { truncated: true } : {}
708
793
  };
709
794
  }
710
795
  /**
@@ -798,8 +883,11 @@ function decodeEventStreamFrames(buf) {
798
883
  * Each frame contains a JSON payload with event types like:
799
884
  * contentBlockDelta, contentBlockStart, etc.
800
885
  */
801
- function collapseBedrockEventStream(body) {
802
- const { frames, truncated } = decodeEventStreamFrames(body);
886
+ function collapseBedrockEventStream(rawBody) {
887
+ const limit = effectiveCollapseStringLimit();
888
+ const inputTruncated = rawBody.byteLength > limit;
889
+ const { frames, truncated: frameTruncated } = decodeEventStreamFrames(inputTruncated ? rawBody.subarray(0, limit) : rawBody);
890
+ const truncated = frameTruncated || inputTruncated;
803
891
  let content = "";
804
892
  let reasoning = "";
805
893
  let reasoningSignature;
@@ -954,8 +1042,9 @@ function collapseBedrockEventStream(body) {
954
1042
  * delta) are still accepted for backward compatibility with previously
955
1043
  * recorded fixtures.
956
1044
  */
957
- function collapseGeminiInteractionsSSE(body) {
958
- const lines = splitSSEEvents(body);
1045
+ function collapseGeminiInteractionsSSE(rawBody) {
1046
+ const inputTruncated = isCollapseInputTruncated(rawBody);
1047
+ const lines = splitSSEEvents(guardCollapseBody(rawBody));
959
1048
  let content = "";
960
1049
  let reasoning = "";
961
1050
  let droppedChunks = 0;
@@ -1036,13 +1125,15 @@ function collapseGeminiInteractionsSSE(body) {
1036
1125
  toolCalls,
1037
1126
  ...reasoning ? { reasoning } : {},
1038
1127
  ...droppedChunks > 0 ? { droppedChunks } : {},
1039
- ...firstDroppedSample ? { firstDroppedSample } : {}
1128
+ ...firstDroppedSample ? { firstDroppedSample } : {},
1129
+ ...inputTruncated ? { truncated: true } : {}
1040
1130
  };
1041
1131
  return {
1042
1132
  content,
1043
1133
  ...reasoning ? { reasoning } : {},
1044
1134
  ...droppedChunks > 0 ? { droppedChunks } : {},
1045
- ...firstDroppedSample ? { firstDroppedSample } : {}
1135
+ ...firstDroppedSample ? { firstDroppedSample } : {},
1136
+ ...inputTruncated ? { truncated: true } : {}
1046
1137
  };
1047
1138
  }
1048
1139
  /**
@@ -1074,6 +1165,7 @@ function collapseStreamingResponse(contentType, providerKey, body, logger) {
1074
1165
  }
1075
1166
 
1076
1167
  //#endregion
1168
+ exports.MAX_COLLAPSE_STRING_LENGTH = MAX_COLLAPSE_STRING_LENGTH;
1077
1169
  exports.capturedRedactedData = capturedRedactedData;
1078
1170
  exports.collapseAnthropicSSE = collapseAnthropicSSE;
1079
1171
  exports.collapseBedrockEventStream = collapseBedrockEventStream;
@@ -1083,4 +1175,5 @@ exports.collapseGeminiSSE = collapseGeminiSSE;
1083
1175
  exports.collapseOllamaNDJSON = collapseOllamaNDJSON;
1084
1176
  exports.collapseOpenAISSE = collapseOpenAISSE;
1085
1177
  exports.collapseStreamingResponse = collapseStreamingResponse;
1178
+ exports.setCollapseStringLimitForTests = setCollapseStringLimitForTests;
1086
1179
  //# sourceMappingURL=stream-collapse.cjs.map