assistant-stream 0.3.27 → 0.3.28

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 (43) hide show
  1. package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -1
  2. package/dist/core/accumulators/AssistantMessageStream.js +0 -1
  3. package/dist/core/accumulators/AssistantMessageStream.js.map +1 -1
  4. package/dist/core/modules/tool-call.d.ts.map +1 -1
  5. package/dist/core/modules/tool-call.js +1 -1
  6. package/dist/core/modules/tool-call.js.map +1 -1
  7. package/dist/core/serialization/data-stream/DataStream.js +48 -48
  8. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  9. package/dist/core/serialization/data-stream/chunk-types.d.ts +23 -22
  10. package/dist/core/serialization/data-stream/chunk-types.d.ts.map +1 -1
  11. package/dist/core/serialization/data-stream/chunk-types.js +22 -23
  12. package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
  13. package/dist/core/serialization/data-stream/serialization.d.ts.map +1 -1
  14. package/dist/core/serialization/data-stream/serialization.js +14 -2
  15. package/dist/core/serialization/data-stream/serialization.js.map +1 -1
  16. package/dist/core/serialization/ui-message-stream/UIMessageStream.d.ts.map +1 -1
  17. package/dist/core/serialization/ui-message-stream/UIMessageStream.js +2 -1
  18. package/dist/core/serialization/ui-message-stream/UIMessageStream.js.map +1 -1
  19. package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
  20. package/dist/core/tool/ToolCallReader.js.map +1 -1
  21. package/dist/core/tool/tool-types.d.ts +6 -0
  22. package/dist/core/tool/tool-types.d.ts.map +1 -1
  23. package/dist/core/utils/stream/SSE.d.ts.map +1 -1
  24. package/dist/core/utils/stream/SSE.js +11 -2
  25. package/dist/core/utils/stream/SSE.js.map +1 -1
  26. package/package.json +2 -2
  27. package/src/core/accumulators/AssistantMessageStream.ts +3 -1
  28. package/src/core/converters/toGenericMessages.test.ts +3 -39
  29. package/src/core/gorp/GorpStreamDeltaTracker.test.ts +30 -0
  30. package/src/core/modules/tool-call.ts +4 -1
  31. package/src/core/serialization/data-stream/DataStream.ts +1 -1
  32. package/src/core/serialization/data-stream/chunk-types.ts +24 -22
  33. package/src/core/serialization/data-stream/serialization.test.ts +133 -0
  34. package/src/core/serialization/data-stream/serialization.ts +20 -2
  35. package/src/core/serialization/ui-message-stream/UIMessageStream.test.ts +24 -0
  36. package/src/core/serialization/ui-message-stream/UIMessageStream.ts +2 -1
  37. package/src/core/tool/ToolCallReader.ts +5 -1
  38. package/src/core/tool/tool-types.ts +7 -0
  39. package/src/core/utils/stream/SSE.test.ts +97 -0
  40. package/src/core/utils/stream/SSE.ts +13 -2
  41. package/src/gorp.test.ts +0 -10
  42. package/src/core/accumulators/TimingTracker.test.ts +0 -100
  43. package/src/core/gorp/changeTree.test.ts +0 -168
@@ -1,3 +1,4 @@
1
+ import sjson from "secure-json-parse";
1
2
  //#region src/core/serialization/data-stream/serialization.ts
2
3
  var DataStreamChunkEncoder = class extends TransformStream {
3
4
  constructor() {
@@ -10,10 +11,21 @@ var DataStreamChunkDecoder = class extends TransformStream {
10
11
  constructor() {
11
12
  super({ transform: (chunk, controller) => {
12
13
  const index = chunk.indexOf(":");
13
- if (index === -1) throw new Error("Invalid stream part");
14
+ if (index === -1) {
15
+ if (chunk.trim().length === 0) return;
16
+ console.warn(`Dropped invalid data-stream chunk: ${chunk.slice(0, 200)}`);
17
+ return;
18
+ }
19
+ let value;
20
+ try {
21
+ value = sjson.parse(chunk.slice(index + 1));
22
+ } catch {
23
+ console.warn(`Dropped invalid data-stream chunk: ${chunk.slice(0, 200)}`);
24
+ return;
25
+ }
14
26
  controller.enqueue({
15
27
  type: chunk.slice(0, index),
16
- value: JSON.parse(chunk.slice(index + 1))
28
+ value
17
29
  });
18
30
  } });
19
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"serialization.js","names":[],"sources":["../../../../src/core/serialization/data-stream/serialization.ts"],"sourcesContent":["import type { DataStreamChunk, DataStreamStreamChunkType } from \"./chunk-types\";\n\nexport class DataStreamChunkEncoder extends TransformStream<\n DataStreamChunk,\n string\n> {\n constructor() {\n super({\n transform: (chunk, controller) => {\n controller.enqueue(`${chunk.type}:${JSON.stringify(chunk.value)}\\n`);\n },\n });\n }\n}\n\nexport class DataStreamChunkDecoder extends TransformStream<\n string,\n DataStreamChunk\n> {\n constructor() {\n super({\n transform: (chunk, controller) => {\n const index = chunk.indexOf(\":\");\n if (index === -1) throw new Error(\"Invalid stream part\");\n controller.enqueue({\n type: chunk.slice(0, index) as DataStreamStreamChunkType,\n value: JSON.parse(chunk.slice(index + 1)),\n });\n },\n });\n }\n}\n"],"mappings":";AAEA,IAAa,yBAAb,cAA4C,gBAG1C;CACA,cAAc;EACZ,MAAM,EACJ,YAAY,OAAO,eAAe;GAChC,WAAW,QAAQ,GAAG,MAAM,KAAK,GAAG,KAAK,UAAU,MAAM,KAAK,EAAE,GAAG;EACrE,EACF,CAAC;CACH;AACF;AAEA,IAAa,yBAAb,cAA4C,gBAG1C;CACA,cAAc;EACZ,MAAM,EACJ,YAAY,OAAO,eAAe;GAChC,MAAM,QAAQ,MAAM,QAAQ,GAAG;GAC/B,IAAI,UAAU,IAAI,MAAM,IAAI,MAAM,qBAAqB;GACvD,WAAW,QAAQ;IACjB,MAAM,MAAM,MAAM,GAAG,KAAK;IAC1B,OAAO,KAAK,MAAM,MAAM,MAAM,QAAQ,CAAC,CAAC;GAC1C,CAAC;EACH,EACF,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"serialization.js","names":[],"sources":["../../../../src/core/serialization/data-stream/serialization.ts"],"sourcesContent":["import sjson from \"secure-json-parse\";\nimport type { DataStreamChunk, DataStreamStreamChunkType } from \"./chunk-types\";\n\nexport class DataStreamChunkEncoder extends TransformStream<\n DataStreamChunk,\n string\n> {\n constructor() {\n super({\n transform: (chunk, controller) => {\n controller.enqueue(`${chunk.type}:${JSON.stringify(chunk.value)}\\n`);\n },\n });\n }\n}\n\nexport class DataStreamChunkDecoder extends TransformStream<\n string,\n DataStreamChunk\n> {\n constructor() {\n super({\n transform: (chunk, controller) => {\n const index = chunk.indexOf(\":\");\n if (index === -1) {\n // Blank lines are benign data-stream framing (keepalive newlines,\n // replay-buffer separators emitted on resume/reconnect).\n if (chunk.trim().length === 0) return;\n console.warn(\n `Dropped invalid data-stream chunk: ${chunk.slice(0, 200)}`,\n );\n return;\n }\n let value;\n try {\n value = sjson.parse(chunk.slice(index + 1));\n } catch {\n console.warn(\n `Dropped invalid data-stream chunk: ${chunk.slice(0, 200)}`,\n );\n return;\n }\n controller.enqueue({\n type: chunk.slice(0, index) as DataStreamStreamChunkType,\n value,\n });\n },\n });\n }\n}\n"],"mappings":";;AAGA,IAAa,yBAAb,cAA4C,gBAG1C;CACA,cAAc;EACZ,MAAM,EACJ,YAAY,OAAO,eAAe;GAChC,WAAW,QAAQ,GAAG,MAAM,KAAK,GAAG,KAAK,UAAU,MAAM,KAAK,EAAE,GAAG;EACrE,EACF,CAAC;CACH;AACF;AAEA,IAAa,yBAAb,cAA4C,gBAG1C;CACA,cAAc;EACZ,MAAM,EACJ,YAAY,OAAO,eAAe;GAChC,MAAM,QAAQ,MAAM,QAAQ,GAAG;GAC/B,IAAI,UAAU,IAAI;IAGhB,IAAI,MAAM,KAAK,CAAC,CAAC,WAAW,GAAG;IAC/B,QAAQ,KACN,sCAAsC,MAAM,MAAM,GAAG,GAAG,GAC1D;IACA;GACF;GACA,IAAI;GACJ,IAAI;IACF,QAAQ,MAAM,MAAM,MAAM,MAAM,QAAQ,CAAC,CAAC;GAC5C,QAAQ;IACN,QAAQ,KACN,sCAAsC,MAAM,MAAM,GAAG,GAAG,GAC1D;IACA;GACF;GACA,WAAW,QAAQ;IACjB,MAAM,MAAM,MAAM,GAAG,KAAK;IAC1B;GACF,CAAC;EACH,EACF,CAAC;CACH;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"UIMessageStream.d.ts","names":[],"sources":["../../../../src/core/serialization/ui-message-stream/UIMessageStream.ts"],"mappings":";;;;KAiBY;EACV,UAAU;IACR;IACA;IACA;IACA;;;;;;cAWS,+BAA+B,wBAC1C,WAAW,cACX;EAEY,YAAA,UAAS"}
1
+ {"version":3,"file":"UIMessageStream.d.ts","names":[],"sources":["../../../../src/core/serialization/ui-message-stream/UIMessageStream.ts"],"mappings":";;;;KAkBY;EACV,UAAU;IACR;IACA;IACA;IACA;;;;;;cAWS,+BAA+B,wBAC1C,WAAW,cACX;EAEY,YAAA,UAAS"}
@@ -2,6 +2,7 @@ import { AssistantTransformStream } from "../../utils/stream/AssistantTransformS
2
2
  import { PipeableTransformStream } from "../../utils/stream/PipeableTransformStream.js";
3
3
  import { generateId } from "../../utils/generateId.js";
4
4
  import { SSEEventDecoderStream } from "../../utils/stream/SSEEventDecoderStream.js";
5
+ import sjson from "secure-json-parse";
5
6
  //#region src/core/serialization/ui-message-stream/UIMessageStream.ts
6
7
  const isDataChunk = (chunk) => chunk.type.startsWith("data-");
7
8
  /**
@@ -150,7 +151,7 @@ var UIMessageStreamDecoder = class extends PipeableTransformStream {
150
151
  }
151
152
  let chunk;
152
153
  try {
153
- chunk = JSON.parse(event.data);
154
+ chunk = sjson.parse(event.data);
154
155
  } catch {
155
156
  chunk = void 0;
156
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"UIMessageStream.js","names":[],"sources":["../../../../src/core/serialization/ui-message-stream/UIMessageStream.ts"],"sourcesContent":["import type { AssistantStreamChunk } from \"../../AssistantStreamChunk\";\nimport type { ToolCallStreamController } from \"../../modules/tool-call\";\nimport type { TextStreamController } from \"../../modules/text\";\nimport { AssistantTransformStream } from \"../../utils/stream/AssistantTransformStream\";\nimport { PipeableTransformStream } from \"../../utils/stream/PipeableTransformStream\";\nimport {\n SSEEventDecoderStream,\n type PipelineSSEEvent,\n} from \"../../utils/stream/SSEEventDecoderStream\";\nimport type {\n UIMessageStreamChunk,\n UIMessageStreamDataChunk,\n} from \"./chunk-types\";\nimport { generateId } from \"../../utils/generateId\";\n\nexport type { UIMessageStreamChunk, UIMessageStreamDataChunk };\n\nexport type UIMessageStreamDecoderOptions = {\n onData?: (data: {\n type: string;\n name: string;\n data: unknown;\n transient?: boolean;\n }) => void;\n};\n\nconst isDataChunk = (\n chunk: UIMessageStreamChunk,\n): chunk is UIMessageStreamDataChunk => chunk.type.startsWith(\"data-\");\n\n/**\n * Decodes AI SDK v6 UI Message Stream format into AssistantStreamChunks.\n */\nexport class UIMessageStreamDecoder extends PipeableTransformStream<\n Uint8Array<ArrayBuffer>,\n AssistantStreamChunk\n> {\n constructor(options: UIMessageStreamDecoderOptions = {}) {\n super((readable) => {\n const toolCallControllers = new Map<string, ToolCallStreamController>();\n let activeToolCallArgsText: TextStreamController | undefined;\n let currentMessageId: string | undefined;\n let receivedDone = false;\n\n const transform = new AssistantTransformStream<UIMessageStreamChunk>({\n transform(chunk, controller) {\n const type = chunk.type;\n\n if (isDataChunk(chunk)) {\n const name = chunk.type.slice(5);\n\n if (options.onData) {\n options.onData({\n type: chunk.type,\n name,\n data: chunk.data,\n ...(chunk.transient !== undefined && {\n transient: chunk.transient,\n }),\n });\n }\n\n if (!chunk.transient) {\n controller.enqueue({\n type: \"data\",\n path: [],\n data: [{ name, data: chunk.data }],\n });\n }\n return;\n }\n\n switch (type) {\n case \"start\":\n currentMessageId = chunk.messageId;\n controller.enqueue({\n type: \"step-start\",\n path: [],\n messageId: chunk.messageId,\n });\n break;\n\n case \"text-start\":\n case \"text-end\":\n case \"reasoning-start\":\n case \"reasoning-end\":\n break;\n\n case \"text-delta\":\n controller.appendText(chunk.textDelta);\n break;\n\n case \"reasoning-delta\":\n controller.appendReasoning(chunk.delta);\n break;\n\n case \"source\":\n controller.appendSource({\n type: \"source\",\n sourceType: chunk.source.sourceType,\n id: chunk.source.id,\n url: chunk.source.url,\n ...(chunk.source.title && { title: chunk.source.title }),\n });\n break;\n\n case \"file\":\n controller.appendFile({\n type: \"file\",\n mimeType: chunk.file.mimeType,\n data: chunk.file.data,\n });\n break;\n\n case \"tool-call-start\": {\n activeToolCallArgsText?.close();\n activeToolCallArgsText = undefined;\n\n if (toolCallControllers.has(chunk.toolCallId)) {\n throw new Error(\n `Encountered duplicate tool call id: ${chunk.toolCallId}`,\n );\n }\n\n const toolCallController = controller.addToolCallPart({\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n });\n toolCallControllers.set(chunk.toolCallId, toolCallController);\n activeToolCallArgsText = toolCallController.argsText;\n break;\n }\n\n case \"tool-call-delta\":\n activeToolCallArgsText?.append(chunk.argsText);\n break;\n\n case \"tool-call-end\":\n activeToolCallArgsText?.close();\n activeToolCallArgsText = undefined;\n break;\n\n case \"tool-result\": {\n const toolCallController = toolCallControllers.get(\n chunk.toolCallId,\n );\n if (!toolCallController) {\n throw new Error(\n `Encountered tool result with unknown id: ${chunk.toolCallId}`,\n );\n }\n if (toolCallController.argsText === activeToolCallArgsText) {\n activeToolCallArgsText = undefined;\n }\n toolCallController.setResponse({\n result: chunk.result,\n isError: chunk.isError ?? false,\n ...(chunk.messages !== undefined\n ? { messages: chunk.messages }\n : {}),\n });\n break;\n }\n\n case \"start-step\":\n controller.enqueue({\n type: \"step-start\",\n path: [],\n messageId: chunk.messageId ?? currentMessageId ?? generateId(),\n });\n break;\n\n case \"finish-step\":\n controller.enqueue({\n type: \"step-finish\",\n path: [],\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n isContinued: chunk.isContinued,\n });\n break;\n\n case \"finish\":\n controller.enqueue({\n type: \"message-finish\",\n path: [],\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n });\n break;\n\n case \"error\":\n controller.enqueue({\n type: \"error\",\n path: [],\n error: chunk.errorText,\n });\n break;\n\n default:\n // ignore unknown types for forward compatibility\n break;\n }\n },\n flush() {\n activeToolCallArgsText?.close();\n toolCallControllers.forEach((ctrl) => ctrl.close());\n toolCallControllers.clear();\n },\n });\n\n return readable\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new SSEEventDecoderStream())\n .pipeThrough(\n new TransformStream<PipelineSSEEvent, UIMessageStreamChunk>({\n transform(event, controller) {\n if (event.event !== \"message\") {\n throw new Error(`Unknown SSE event type: ${event.event}`);\n }\n\n if (event.data === \"[DONE]\") {\n receivedDone = true;\n controller.terminate();\n return;\n }\n\n let chunk;\n try {\n chunk = JSON.parse(event.data);\n } catch {\n chunk = undefined;\n }\n if (\n typeof chunk !== \"object\" ||\n chunk === null ||\n Array.isArray(chunk) ||\n typeof chunk.type !== \"string\"\n ) {\n console.warn(\n `Dropped invalid UIMessageStream chunk: ${event.data.slice(0, 200)}`,\n );\n return;\n }\n if (\n chunk.type === \"text-delta\" &&\n chunk.textDelta === undefined\n ) {\n const { delta, ...rest } = chunk;\n controller.enqueue({ ...rest, textDelta: delta ?? \"\" });\n return;\n }\n if (chunk.type === \"start\") {\n controller.enqueue({\n ...chunk,\n messageId: chunk.messageId ?? generateId(),\n });\n return;\n }\n if (chunk.type === \"source-url\") {\n controller.enqueue({\n type: \"source\",\n source: {\n sourceType: \"url\",\n id: chunk.sourceId,\n url: chunk.url,\n ...(chunk.title && { title: chunk.title }),\n },\n });\n return;\n }\n if (chunk.type === \"source\" && chunk.source == null) return;\n if (chunk.type === \"file\" && chunk.file == null) {\n if (chunk.url === undefined) return;\n controller.enqueue({\n type: \"file\",\n file: { mimeType: chunk.mediaType, data: chunk.url },\n });\n return;\n }\n if (chunk.type === \"finish-step\") {\n controller.enqueue({\n ...chunk,\n finishReason: chunk.finishReason ?? \"unknown\",\n usage: chunk.usage ?? { inputTokens: 0, outputTokens: 0 },\n isContinued: chunk.isContinued ?? false,\n });\n return;\n }\n if (chunk.type === \"finish\") {\n controller.enqueue({\n ...chunk,\n finishReason: chunk.finishReason ?? \"unknown\",\n usage: chunk.usage ?? { inputTokens: 0, outputTokens: 0 },\n });\n return;\n }\n\n controller.enqueue(chunk);\n },\n flush() {\n if (!receivedDone) {\n throw new Error(\n \"Stream ended abruptly without receiving [DONE] marker\",\n );\n }\n },\n }),\n )\n .pipeThrough(transform);\n });\n }\n}\n"],"mappings":";;;;;AA0BA,MAAM,eACJ,UACsC,MAAM,KAAK,WAAW,OAAO;;;;AAKrE,IAAa,yBAAb,cAA4C,wBAG1C;CACA,YAAY,UAAyC,CAAC,GAAG;EACvD,OAAO,aAAa;GAClB,MAAM,sCAAsB,IAAI,IAAsC;GACtE,IAAI;GACJ,IAAI;GACJ,IAAI,eAAe;GAEnB,MAAM,YAAY,IAAI,yBAA+C;IACnE,UAAU,OAAO,YAAY;KAC3B,MAAM,OAAO,MAAM;KAEnB,IAAI,YAAY,KAAK,GAAG;MACtB,MAAM,OAAO,MAAM,KAAK,MAAM,CAAC;MAE/B,IAAI,QAAQ,QACV,QAAQ,OAAO;OACb,MAAM,MAAM;OACZ;OACA,MAAM,MAAM;OACZ,GAAI,MAAM,cAAc,KAAA,KAAa,EACnC,WAAW,MAAM,UACnB;MACF,CAAC;MAGH,IAAI,CAAC,MAAM,WACT,WAAW,QAAQ;OACjB,MAAM;OACN,MAAM,CAAC;OACP,MAAM,CAAC;QAAE;QAAM,MAAM,MAAM;OAAK,CAAC;MACnC,CAAC;MAEH;KACF;KAEA,QAAQ,MAAR;MACE,KAAK;OACH,mBAAmB,MAAM;OACzB,WAAW,QAAQ;QACjB,MAAM;QACN,MAAM,CAAC;QACP,WAAW,MAAM;OACnB,CAAC;OACD;MAEF,KAAK;MACL,KAAK;MACL,KAAK;MACL,KAAK,iBACH;MAEF,KAAK;OACH,WAAW,WAAW,MAAM,SAAS;OACrC;MAEF,KAAK;OACH,WAAW,gBAAgB,MAAM,KAAK;OACtC;MAEF,KAAK;OACH,WAAW,aAAa;QACtB,MAAM;QACN,YAAY,MAAM,OAAO;QACzB,IAAI,MAAM,OAAO;QACjB,KAAK,MAAM,OAAO;QAClB,GAAI,MAAM,OAAO,SAAS,EAAE,OAAO,MAAM,OAAO,MAAM;OACxD,CAAC;OACD;MAEF,KAAK;OACH,WAAW,WAAW;QACpB,MAAM;QACN,UAAU,MAAM,KAAK;QACrB,MAAM,MAAM,KAAK;OACnB,CAAC;OACD;MAEF,KAAK,mBAAmB;OACtB,wBAAwB,MAAM;OAC9B,yBAAyB,KAAA;OAEzB,IAAI,oBAAoB,IAAI,MAAM,UAAU,GAC1C,MAAM,IAAI,MACR,uCAAuC,MAAM,YAC/C;OAGF,MAAM,qBAAqB,WAAW,gBAAgB;QACpD,YAAY,MAAM;QAClB,UAAU,MAAM;OAClB,CAAC;OACD,oBAAoB,IAAI,MAAM,YAAY,kBAAkB;OAC5D,yBAAyB,mBAAmB;OAC5C;MACF;MAEA,KAAK;OACH,wBAAwB,OAAO,MAAM,QAAQ;OAC7C;MAEF,KAAK;OACH,wBAAwB,MAAM;OAC9B,yBAAyB,KAAA;OACzB;MAEF,KAAK,eAAe;OAClB,MAAM,qBAAqB,oBAAoB,IAC7C,MAAM,UACR;OACA,IAAI,CAAC,oBACH,MAAM,IAAI,MACR,4CAA4C,MAAM,YACpD;OAEF,IAAI,mBAAmB,aAAa,wBAClC,yBAAyB,KAAA;OAE3B,mBAAmB,YAAY;QAC7B,QAAQ,MAAM;QACd,SAAS,MAAM,WAAW;QAC1B,GAAI,MAAM,aAAa,KAAA,IACnB,EAAE,UAAU,MAAM,SAAS,IAC3B,CAAC;OACP,CAAC;OACD;MACF;MAEA,KAAK;OACH,WAAW,QAAQ;QACjB,MAAM;QACN,MAAM,CAAC;QACP,WAAW,MAAM,aAAa,oBAAoB,WAAW;OAC/D,CAAC;OACD;MAEF,KAAK;OACH,WAAW,QAAQ;QACjB,MAAM;QACN,MAAM,CAAC;QACP,cAAc,MAAM;QACpB,OAAO,MAAM;QACb,aAAa,MAAM;OACrB,CAAC;OACD;MAEF,KAAK;OACH,WAAW,QAAQ;QACjB,MAAM;QACN,MAAM,CAAC;QACP,cAAc,MAAM;QACpB,OAAO,MAAM;OACf,CAAC;OACD;MAEF,KAAK;OACH,WAAW,QAAQ;QACjB,MAAM;QACN,MAAM,CAAC;QACP,OAAO,MAAM;OACf,CAAC;OACD;MAEF,SAEE;KACJ;IACF;IACA,QAAQ;KACN,wBAAwB,MAAM;KAC9B,oBAAoB,SAAS,SAAS,KAAK,MAAM,CAAC;KAClD,oBAAoB,MAAM;IAC5B;GACF,CAAC;GAED,OAAO,SACJ,YAAY,IAAI,kBAAkB,CAAC,CAAC,CACpC,YAAY,IAAI,sBAAsB,CAAC,CAAC,CACxC,YACC,IAAI,gBAAwD;IAC1D,UAAU,OAAO,YAAY;KAC3B,IAAI,MAAM,UAAU,WAClB,MAAM,IAAI,MAAM,2BAA2B,MAAM,OAAO;KAG1D,IAAI,MAAM,SAAS,UAAU;MAC3B,eAAe;MACf,WAAW,UAAU;MACrB;KACF;KAEA,IAAI;KACJ,IAAI;MACF,QAAQ,KAAK,MAAM,MAAM,IAAI;KAC/B,QAAQ;MACN,QAAQ,KAAA;KACV;KACA,IACE,OAAO,UAAU,YACjB,UAAU,QACV,MAAM,QAAQ,KAAK,KACnB,OAAO,MAAM,SAAS,UACtB;MACA,QAAQ,KACN,0CAA0C,MAAM,KAAK,MAAM,GAAG,GAAG,GACnE;MACA;KACF;KACA,IACE,MAAM,SAAS,gBACf,MAAM,cAAc,KAAA,GACpB;MACA,MAAM,EAAE,OAAO,GAAG,SAAS;MAC3B,WAAW,QAAQ;OAAE,GAAG;OAAM,WAAW,SAAS;MAAG,CAAC;MACtD;KACF;KACA,IAAI,MAAM,SAAS,SAAS;MAC1B,WAAW,QAAQ;OACjB,GAAG;OACH,WAAW,MAAM,aAAa,WAAW;MAC3C,CAAC;MACD;KACF;KACA,IAAI,MAAM,SAAS,cAAc;MAC/B,WAAW,QAAQ;OACjB,MAAM;OACN,QAAQ;QACN,YAAY;QACZ,IAAI,MAAM;QACV,KAAK,MAAM;QACX,GAAI,MAAM,SAAS,EAAE,OAAO,MAAM,MAAM;OAC1C;MACF,CAAC;MACD;KACF;KACA,IAAI,MAAM,SAAS,YAAY,MAAM,UAAU,MAAM;KACrD,IAAI,MAAM,SAAS,UAAU,MAAM,QAAQ,MAAM;MAC/C,IAAI,MAAM,QAAQ,KAAA,GAAW;MAC7B,WAAW,QAAQ;OACjB,MAAM;OACN,MAAM;QAAE,UAAU,MAAM;QAAW,MAAM,MAAM;OAAI;MACrD,CAAC;MACD;KACF;KACA,IAAI,MAAM,SAAS,eAAe;MAChC,WAAW,QAAQ;OACjB,GAAG;OACH,cAAc,MAAM,gBAAgB;OACpC,OAAO,MAAM,SAAS;QAAE,aAAa;QAAG,cAAc;OAAE;OACxD,aAAa,MAAM,eAAe;MACpC,CAAC;MACD;KACF;KACA,IAAI,MAAM,SAAS,UAAU;MAC3B,WAAW,QAAQ;OACjB,GAAG;OACH,cAAc,MAAM,gBAAgB;OACpC,OAAO,MAAM,SAAS;QAAE,aAAa;QAAG,cAAc;OAAE;MAC1D,CAAC;MACD;KACF;KAEA,WAAW,QAAQ,KAAK;IAC1B;IACA,QAAQ;KACN,IAAI,CAAC,cACH,MAAM,IAAI,MACR,uDACF;IAEJ;GACF,CAAC,CACH,CAAC,CACA,YAAY,SAAS;EAC1B,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"UIMessageStream.js","names":[],"sources":["../../../../src/core/serialization/ui-message-stream/UIMessageStream.ts"],"sourcesContent":["import sjson from \"secure-json-parse\";\nimport type { AssistantStreamChunk } from \"../../AssistantStreamChunk\";\nimport type { ToolCallStreamController } from \"../../modules/tool-call\";\nimport type { TextStreamController } from \"../../modules/text\";\nimport { AssistantTransformStream } from \"../../utils/stream/AssistantTransformStream\";\nimport { PipeableTransformStream } from \"../../utils/stream/PipeableTransformStream\";\nimport {\n SSEEventDecoderStream,\n type PipelineSSEEvent,\n} from \"../../utils/stream/SSEEventDecoderStream\";\nimport type {\n UIMessageStreamChunk,\n UIMessageStreamDataChunk,\n} from \"./chunk-types\";\nimport { generateId } from \"../../utils/generateId\";\n\nexport type { UIMessageStreamChunk, UIMessageStreamDataChunk };\n\nexport type UIMessageStreamDecoderOptions = {\n onData?: (data: {\n type: string;\n name: string;\n data: unknown;\n transient?: boolean;\n }) => void;\n};\n\nconst isDataChunk = (\n chunk: UIMessageStreamChunk,\n): chunk is UIMessageStreamDataChunk => chunk.type.startsWith(\"data-\");\n\n/**\n * Decodes AI SDK v6 UI Message Stream format into AssistantStreamChunks.\n */\nexport class UIMessageStreamDecoder extends PipeableTransformStream<\n Uint8Array<ArrayBuffer>,\n AssistantStreamChunk\n> {\n constructor(options: UIMessageStreamDecoderOptions = {}) {\n super((readable) => {\n const toolCallControllers = new Map<string, ToolCallStreamController>();\n let activeToolCallArgsText: TextStreamController | undefined;\n let currentMessageId: string | undefined;\n let receivedDone = false;\n\n const transform = new AssistantTransformStream<UIMessageStreamChunk>({\n transform(chunk, controller) {\n const type = chunk.type;\n\n if (isDataChunk(chunk)) {\n const name = chunk.type.slice(5);\n\n if (options.onData) {\n options.onData({\n type: chunk.type,\n name,\n data: chunk.data,\n ...(chunk.transient !== undefined && {\n transient: chunk.transient,\n }),\n });\n }\n\n if (!chunk.transient) {\n controller.enqueue({\n type: \"data\",\n path: [],\n data: [{ name, data: chunk.data }],\n });\n }\n return;\n }\n\n switch (type) {\n case \"start\":\n currentMessageId = chunk.messageId;\n controller.enqueue({\n type: \"step-start\",\n path: [],\n messageId: chunk.messageId,\n });\n break;\n\n case \"text-start\":\n case \"text-end\":\n case \"reasoning-start\":\n case \"reasoning-end\":\n break;\n\n case \"text-delta\":\n controller.appendText(chunk.textDelta);\n break;\n\n case \"reasoning-delta\":\n controller.appendReasoning(chunk.delta);\n break;\n\n case \"source\":\n controller.appendSource({\n type: \"source\",\n sourceType: chunk.source.sourceType,\n id: chunk.source.id,\n url: chunk.source.url,\n ...(chunk.source.title && { title: chunk.source.title }),\n });\n break;\n\n case \"file\":\n controller.appendFile({\n type: \"file\",\n mimeType: chunk.file.mimeType,\n data: chunk.file.data,\n });\n break;\n\n case \"tool-call-start\": {\n activeToolCallArgsText?.close();\n activeToolCallArgsText = undefined;\n\n if (toolCallControllers.has(chunk.toolCallId)) {\n throw new Error(\n `Encountered duplicate tool call id: ${chunk.toolCallId}`,\n );\n }\n\n const toolCallController = controller.addToolCallPart({\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n });\n toolCallControllers.set(chunk.toolCallId, toolCallController);\n activeToolCallArgsText = toolCallController.argsText;\n break;\n }\n\n case \"tool-call-delta\":\n activeToolCallArgsText?.append(chunk.argsText);\n break;\n\n case \"tool-call-end\":\n activeToolCallArgsText?.close();\n activeToolCallArgsText = undefined;\n break;\n\n case \"tool-result\": {\n const toolCallController = toolCallControllers.get(\n chunk.toolCallId,\n );\n if (!toolCallController) {\n throw new Error(\n `Encountered tool result with unknown id: ${chunk.toolCallId}`,\n );\n }\n if (toolCallController.argsText === activeToolCallArgsText) {\n activeToolCallArgsText = undefined;\n }\n toolCallController.setResponse({\n result: chunk.result,\n isError: chunk.isError ?? false,\n ...(chunk.messages !== undefined\n ? { messages: chunk.messages }\n : {}),\n });\n break;\n }\n\n case \"start-step\":\n controller.enqueue({\n type: \"step-start\",\n path: [],\n messageId: chunk.messageId ?? currentMessageId ?? generateId(),\n });\n break;\n\n case \"finish-step\":\n controller.enqueue({\n type: \"step-finish\",\n path: [],\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n isContinued: chunk.isContinued,\n });\n break;\n\n case \"finish\":\n controller.enqueue({\n type: \"message-finish\",\n path: [],\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n });\n break;\n\n case \"error\":\n controller.enqueue({\n type: \"error\",\n path: [],\n error: chunk.errorText,\n });\n break;\n\n default:\n // ignore unknown types for forward compatibility\n break;\n }\n },\n flush() {\n activeToolCallArgsText?.close();\n toolCallControllers.forEach((ctrl) => ctrl.close());\n toolCallControllers.clear();\n },\n });\n\n return readable\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new SSEEventDecoderStream())\n .pipeThrough(\n new TransformStream<PipelineSSEEvent, UIMessageStreamChunk>({\n transform(event, controller) {\n if (event.event !== \"message\") {\n throw new Error(`Unknown SSE event type: ${event.event}`);\n }\n\n if (event.data === \"[DONE]\") {\n receivedDone = true;\n controller.terminate();\n return;\n }\n\n let chunk;\n try {\n chunk = sjson.parse(event.data);\n } catch {\n chunk = undefined;\n }\n if (\n typeof chunk !== \"object\" ||\n chunk === null ||\n Array.isArray(chunk) ||\n typeof chunk.type !== \"string\"\n ) {\n console.warn(\n `Dropped invalid UIMessageStream chunk: ${event.data.slice(0, 200)}`,\n );\n return;\n }\n if (\n chunk.type === \"text-delta\" &&\n chunk.textDelta === undefined\n ) {\n const { delta, ...rest } = chunk;\n controller.enqueue({ ...rest, textDelta: delta ?? \"\" });\n return;\n }\n if (chunk.type === \"start\") {\n controller.enqueue({\n ...chunk,\n messageId: chunk.messageId ?? generateId(),\n });\n return;\n }\n if (chunk.type === \"source-url\") {\n controller.enqueue({\n type: \"source\",\n source: {\n sourceType: \"url\",\n id: chunk.sourceId,\n url: chunk.url,\n ...(chunk.title && { title: chunk.title }),\n },\n });\n return;\n }\n if (chunk.type === \"source\" && chunk.source == null) return;\n if (chunk.type === \"file\" && chunk.file == null) {\n if (chunk.url === undefined) return;\n controller.enqueue({\n type: \"file\",\n file: { mimeType: chunk.mediaType, data: chunk.url },\n });\n return;\n }\n if (chunk.type === \"finish-step\") {\n controller.enqueue({\n ...chunk,\n finishReason: chunk.finishReason ?? \"unknown\",\n usage: chunk.usage ?? { inputTokens: 0, outputTokens: 0 },\n isContinued: chunk.isContinued ?? false,\n });\n return;\n }\n if (chunk.type === \"finish\") {\n controller.enqueue({\n ...chunk,\n finishReason: chunk.finishReason ?? \"unknown\",\n usage: chunk.usage ?? { inputTokens: 0, outputTokens: 0 },\n });\n return;\n }\n\n controller.enqueue(chunk);\n },\n flush() {\n if (!receivedDone) {\n throw new Error(\n \"Stream ended abruptly without receiving [DONE] marker\",\n );\n }\n },\n }),\n )\n .pipeThrough(transform);\n });\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCallReader.d.ts","names":[],"sources":["../../../src/core/tool/ToolCallReader.ts"],"mappings":";;;;;;;cA0Qa,uBACX,UAAU,+BACC,mBAAmB;UACtB;UACA;UACA;UACA;EAEI,YAAA,eAAe;UAKb;EA+Bd,IAAI,cAAc,SAAS,OACtB,WAAW,QACb,QAAQ,WAAW,GAAG;EAiCzB,aAAa,cAAc,SAAS,OAC/B,WAAW,QACb,oBAAoB,YAAY,WAAW,GAAG;EA4BjD,WAAW,cAAc,SAAS,OAC7B,WAAW,QACb,WAAW,GAAG,+BAA+B,KAC5C,oBAAoB;EA6BxB,QAAQ,cAAc,SAAS,OAC1B,WAAW,QACb,YAAY,WAAW,GAAG,gBAAgB,YAAY,KACrD,oBAAoB;;cA8Bb,2BACX,gBAAgB,8BACL,uBAAuB;mBACL;EAAA,YAAA,SAAS,QAAQ,aAAa;EAEpD,OAAG,QAAA,aAAA;;cAKC,mBACX,cAAc,oBACd,gBAAgB,8BACL,eAAe,OAAO;WACjB,MAAM,uBAAuB;WAC7B,UAAU,2BAA2B;mBACpC;mBACA;EAEV;;EAYD,oBAAoB,eAAe;EAanC,kBAAkB;EAWxB,YAAY,OAAO,aAAa;EAIhC"}
1
+ {"version":3,"file":"ToolCallReader.d.ts","names":[],"sources":["../../../src/core/tool/ToolCallReader.ts"],"mappings":";;;;;;;cA0Qa,uBACX,UAAU,+BACC,mBAAmB;UACtB;UACA;UACA;UACA;EAEI,YAAA,eAAe;UAKb;EA+Bd,IAAI,cAAc,SAAS,OACtB,WAAW,QACb,QAAQ,WAAW,GAAG;EAiCzB,aAAa,cAAc,SAAS,OAC/B,WAAW,QACb,oBAAoB,YAAY,WAAW,GAAG;EA4BjD,WAAW,cAAc,SAAS,OAC7B,WAAW,QACb,WAAW,GAAG,+BAA+B,KAC5C,oBAAoB;EA6BxB,QAAQ,cAAc,SAAS,OAC1B,WAAW,QACb,YAAY,WAAW,GAAG,gBAAgB,YAAY,KACrD,oBAAoB;;cA8Bb,2BACX,gBAAgB,8BACL,uBAAuB;mBACjB;EAEL,YAAA,SAAS,QAAQ,aAAa;EAInC,OAAG,QAAA,aAAA;;cAKC,mBACX,cAAc,oBACd,gBAAgB,8BACL,eAAe,OAAO;WACjB,MAAM,uBAAuB;WAC7B,UAAU,2BAA2B;mBACpC;mBACA;EAEV;;EAYD,oBAAoB,eAAe;EAanC,kBAAkB;EAWxB,YAAY,OAAO,aAAa;EAIhC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCallReader.js","names":[],"sources":["../../../src/core/tool/ToolCallReader.ts"],"sourcesContent":["import { promiseWithResolvers } from \"../../utils/promiseWithResolvers\";\nimport {\n parsePartialJsonObject,\n getPartialJsonObjectFieldState,\n} from \"../../utils/json/parse-partial-json-object\";\nimport type {\n ToolCallArgsReader,\n ToolCallReader,\n ToolCallResponseReader,\n} from \"./tool-types\";\nimport type { DeepPartial, TypeAtPath, TypePath } from \"./type-path-utils\";\nimport type { ToolResponse } from \"./ToolResponse\";\nimport { asAsyncIterableStream } from \"../../utils/AsyncIterableStream\";\nimport type {\n AsyncIterableStream,\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils\";\n\n// TODO: remove dispose\n\nfunction getField<T>(obj: T, fieldPath: (string | number)[]): unknown {\n let current: unknown = obj;\n for (const key of fieldPath) {\n if (current === undefined || current === null) {\n return undefined;\n }\n current = current[key as keyof typeof current];\n }\n return current;\n}\n\ninterface Handle {\n update(args: unknown): void;\n end(args: unknown): void;\n dispose(): void;\n}\n\nclass GetHandle<T, TValue> implements Handle {\n private resolve: (value: TValue) => void;\n private reject: (reason: unknown) => void;\n private disposed = false;\n private fieldPath: (string | number)[];\n\n constructor(\n resolve: (value: TValue) => void,\n reject: (reason: unknown) => void,\n fieldPath: (string | number)[],\n ) {\n this.resolve = resolve;\n this.reject = reject;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n // Check if the field is complete\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n const value = getField(args as T, this.fieldPath);\n if (value !== undefined) {\n this.resolve(value as TValue);\n this.dispose();\n }\n }\n } catch (e) {\n this.reject(e);\n this.dispose();\n }\n }\n\n end(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n this.resolve(value as TValue);\n } catch (e) {\n this.reject(e);\n } finally {\n this.dispose();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass StreamValuesHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n\n if (value !== undefined) {\n this.controller.enqueue(value);\n }\n\n // Check if the field is complete, if so close the stream\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass StreamTextHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n private lastValue: string | undefined = undefined;\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n\n if (value !== undefined && typeof value === \"string\") {\n const delta = value.substring(this.lastValue?.length || 0);\n this.lastValue = value;\n this.controller.enqueue(delta);\n }\n\n // Check if the field is complete, if so close the stream\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass ForEachHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n private processedIndexes = new Set<number>();\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const array = getField(args as T, this.fieldPath);\n\n if (!Array.isArray(array)) {\n return;\n }\n\n // Check each array element and emit completed ones that haven't been processed\n for (let i = 0; i < array.length; i++) {\n if (!this.processedIndexes.has(i)) {\n const elementPath = [...this.fieldPath, i];\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n elementPath,\n ) === \"complete\"\n ) {\n this.controller.enqueue(array[i]);\n this.processedIndexes.add(i);\n }\n }\n }\n\n // Check if the entire array is complete\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\n// Implementation of ToolCallReader that uses stream of partial JSON\nexport class ToolCallArgsReaderImpl<\n T extends ReadonlyJSONObject,\n> implements ToolCallArgsReader<T> {\n private argTextDeltas: ReadableStream<string>;\n private handles: Set<Handle> = new Set();\n private args: unknown = parsePartialJsonObject(\"\");\n private finished = false;\n\n constructor(argTextDeltas: ReadableStream<string>) {\n this.argTextDeltas = argTextDeltas;\n this.processStream();\n }\n\n private async processStream(): Promise<void> {\n try {\n let accumulatedText = \"\";\n const reader = this.argTextDeltas.getReader();\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n accumulatedText += value;\n const parsedArgs = parsePartialJsonObject(accumulatedText);\n\n if (parsedArgs !== undefined) {\n this.args = parsedArgs;\n // Notify all handles of the updated args\n for (const handle of this.handles) {\n handle.update(parsedArgs);\n }\n }\n }\n } catch (error) {\n console.error(\"Error processing argument stream:\", error);\n } finally {\n this.finished = true;\n for (const handle of this.handles) {\n handle.end(this.args);\n }\n this.handles.clear();\n }\n }\n\n get<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): Promise<TypeAtPath<T, PathT>> {\n return new Promise<TypeAtPath<T, PathT>>((resolve, reject) => {\n const handle = new GetHandle<T, TypeAtPath<T, PathT>>(\n resolve,\n reject,\n fieldPath,\n );\n\n // Check if the field is already complete in current args\n if (\n this.args &&\n getPartialJsonObjectFieldState(\n this.args as Record<string, unknown>,\n fieldPath,\n ) === \"complete\"\n ) {\n const value = getField(this.args as T, fieldPath);\n if (value !== undefined) {\n resolve(value as TypeAtPath<T, PathT>);\n return;\n }\n }\n\n if (this.finished) {\n handle.end(this.args);\n return;\n }\n\n this.handles.add(handle);\n handle.update(this.args);\n });\n }\n\n streamValues<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): AsyncIterableStream<DeepPartial<TypeAtPath<T, PathT>>> {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: StreamValuesHandle<T> | undefined;\n const stream = new ReadableStream<DeepPartial<TypeAtPath<T, PathT>>>({\n start: (controller) => {\n handle = new StreamValuesHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent streamValues()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n\n streamText<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): TypeAtPath<T, PathT> extends string & (infer U)\n ? AsyncIterableStream<U>\n : never {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: StreamTextHandle<T> | undefined;\n const stream = new ReadableStream<unknown>({\n start: (controller) => {\n handle = new StreamTextHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent streamText()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n\n forEach<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): NonNullable<TypeAtPath<T, PathT>> extends Array<infer U>\n ? AsyncIterableStream<U>\n : never {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: ForEachHandle<T> | undefined;\n const stream = new ReadableStream<unknown>({\n start: (controller) => {\n handle = new ForEachHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent forEach()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n}\n\nexport class ToolCallResponseReaderImpl<\n TResult extends ReadonlyJSONValue,\n> implements ToolCallResponseReader<TResult> {\n constructor(private readonly promise: Promise<ToolResponse<TResult>>) {}\n\n public get() {\n return this.promise;\n }\n}\n\nexport class ToolCallReaderImpl<\n TArgs extends ReadonlyJSONObject,\n TResult extends ReadonlyJSONValue,\n> implements ToolCallReader<TArgs, TResult> {\n public readonly args: ToolCallArgsReaderImpl<TArgs>;\n public readonly response: ToolCallResponseReaderImpl<TResult>;\n private readonly writable: WritableStream<string>;\n private readonly resolve: (value: ToolResponse<TResult>) => void;\n\n public argsText: string = \"\";\n\n constructor() {\n const stream = new TransformStream<string, string>();\n this.writable = stream.writable;\n this.args = new ToolCallArgsReaderImpl<TArgs>(stream.readable);\n\n const { promise, resolve } = promiseWithResolvers<ToolResponse<TResult>>();\n this.resolve = resolve;\n this.response = new ToolCallResponseReaderImpl<TResult>(promise);\n }\n\n async appendArgsTextDelta(text: string): Promise<void> {\n const writer = this.writable.getWriter();\n try {\n await writer.write(text);\n } catch (err) {\n console.warn(err);\n } finally {\n writer.releaseLock();\n }\n\n this.argsText += text;\n }\n\n async finishArgsText(): Promise<void> {\n const writer = this.writable.getWriter();\n try {\n await writer.close();\n } catch (err) {\n console.warn(err);\n } finally {\n writer.releaseLock();\n }\n }\n\n setResponse(value: ToolResponse<TResult>): void {\n this.resolve(value);\n }\n\n result = {\n get: async () => {\n const response = await this.response.get();\n return response.result;\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ToolCallReader.js","names":[],"sources":["../../../src/core/tool/ToolCallReader.ts"],"sourcesContent":["import { promiseWithResolvers } from \"../../utils/promiseWithResolvers\";\nimport {\n parsePartialJsonObject,\n getPartialJsonObjectFieldState,\n} from \"../../utils/json/parse-partial-json-object\";\nimport type {\n ToolCallArgsReader,\n ToolCallReader,\n ToolCallResponseReader,\n} from \"./tool-types\";\nimport type { DeepPartial, TypeAtPath, TypePath } from \"./type-path-utils\";\nimport type { ToolResponse } from \"./ToolResponse\";\nimport { asAsyncIterableStream } from \"../../utils/AsyncIterableStream\";\nimport type {\n AsyncIterableStream,\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils\";\n\n// TODO: remove dispose\n\nfunction getField<T>(obj: T, fieldPath: (string | number)[]): unknown {\n let current: unknown = obj;\n for (const key of fieldPath) {\n if (current === undefined || current === null) {\n return undefined;\n }\n current = current[key as keyof typeof current];\n }\n return current;\n}\n\ninterface Handle {\n update(args: unknown): void;\n end(args: unknown): void;\n dispose(): void;\n}\n\nclass GetHandle<T, TValue> implements Handle {\n private resolve: (value: TValue) => void;\n private reject: (reason: unknown) => void;\n private disposed = false;\n private fieldPath: (string | number)[];\n\n constructor(\n resolve: (value: TValue) => void,\n reject: (reason: unknown) => void,\n fieldPath: (string | number)[],\n ) {\n this.resolve = resolve;\n this.reject = reject;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n // Check if the field is complete\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n const value = getField(args as T, this.fieldPath);\n if (value !== undefined) {\n this.resolve(value as TValue);\n this.dispose();\n }\n }\n } catch (e) {\n this.reject(e);\n this.dispose();\n }\n }\n\n end(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n this.resolve(value as TValue);\n } catch (e) {\n this.reject(e);\n } finally {\n this.dispose();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass StreamValuesHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n\n if (value !== undefined) {\n this.controller.enqueue(value);\n }\n\n // Check if the field is complete, if so close the stream\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass StreamTextHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n private lastValue: string | undefined = undefined;\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n\n if (value !== undefined && typeof value === \"string\") {\n const delta = value.substring(this.lastValue?.length || 0);\n this.lastValue = value;\n this.controller.enqueue(delta);\n }\n\n // Check if the field is complete, if so close the stream\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass ForEachHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n private processedIndexes = new Set<number>();\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const array = getField(args as T, this.fieldPath);\n\n if (!Array.isArray(array)) {\n return;\n }\n\n // Check each array element and emit completed ones that haven't been processed\n for (let i = 0; i < array.length; i++) {\n if (!this.processedIndexes.has(i)) {\n const elementPath = [...this.fieldPath, i];\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n elementPath,\n ) === \"complete\"\n ) {\n this.controller.enqueue(array[i]);\n this.processedIndexes.add(i);\n }\n }\n }\n\n // Check if the entire array is complete\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\n// Implementation of ToolCallReader that uses stream of partial JSON\nexport class ToolCallArgsReaderImpl<\n T extends ReadonlyJSONObject,\n> implements ToolCallArgsReader<T> {\n private argTextDeltas: ReadableStream<string>;\n private handles: Set<Handle> = new Set();\n private args: unknown = parsePartialJsonObject(\"\");\n private finished = false;\n\n constructor(argTextDeltas: ReadableStream<string>) {\n this.argTextDeltas = argTextDeltas;\n this.processStream();\n }\n\n private async processStream(): Promise<void> {\n try {\n let accumulatedText = \"\";\n const reader = this.argTextDeltas.getReader();\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n accumulatedText += value;\n const parsedArgs = parsePartialJsonObject(accumulatedText);\n\n if (parsedArgs !== undefined) {\n this.args = parsedArgs;\n // Notify all handles of the updated args\n for (const handle of this.handles) {\n handle.update(parsedArgs);\n }\n }\n }\n } catch (error) {\n console.error(\"Error processing argument stream:\", error);\n } finally {\n this.finished = true;\n for (const handle of this.handles) {\n handle.end(this.args);\n }\n this.handles.clear();\n }\n }\n\n get<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): Promise<TypeAtPath<T, PathT>> {\n return new Promise<TypeAtPath<T, PathT>>((resolve, reject) => {\n const handle = new GetHandle<T, TypeAtPath<T, PathT>>(\n resolve,\n reject,\n fieldPath,\n );\n\n // Check if the field is already complete in current args\n if (\n this.args &&\n getPartialJsonObjectFieldState(\n this.args as Record<string, unknown>,\n fieldPath,\n ) === \"complete\"\n ) {\n const value = getField(this.args as T, fieldPath);\n if (value !== undefined) {\n resolve(value as TypeAtPath<T, PathT>);\n return;\n }\n }\n\n if (this.finished) {\n handle.end(this.args);\n return;\n }\n\n this.handles.add(handle);\n handle.update(this.args);\n });\n }\n\n streamValues<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): AsyncIterableStream<DeepPartial<TypeAtPath<T, PathT>>> {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: StreamValuesHandle<T> | undefined;\n const stream = new ReadableStream<DeepPartial<TypeAtPath<T, PathT>>>({\n start: (controller) => {\n handle = new StreamValuesHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent streamValues()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n\n streamText<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): TypeAtPath<T, PathT> extends string & (infer U)\n ? AsyncIterableStream<U>\n : never {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: StreamTextHandle<T> | undefined;\n const stream = new ReadableStream<unknown>({\n start: (controller) => {\n handle = new StreamTextHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent streamText()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n\n forEach<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): NonNullable<TypeAtPath<T, PathT>> extends Array<infer U>\n ? AsyncIterableStream<U>\n : never {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: ForEachHandle<T> | undefined;\n const stream = new ReadableStream<unknown>({\n start: (controller) => {\n handle = new ForEachHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent forEach()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n}\n\nexport class ToolCallResponseReaderImpl<\n TResult extends ReadonlyJSONValue,\n> implements ToolCallResponseReader<TResult> {\n private readonly promise: Promise<ToolResponse<TResult>>;\n\n constructor(promise: Promise<ToolResponse<TResult>>) {\n this.promise = promise;\n }\n\n public get() {\n return this.promise;\n }\n}\n\nexport class ToolCallReaderImpl<\n TArgs extends ReadonlyJSONObject,\n TResult extends ReadonlyJSONValue,\n> implements ToolCallReader<TArgs, TResult> {\n public readonly args: ToolCallArgsReaderImpl<TArgs>;\n public readonly response: ToolCallResponseReaderImpl<TResult>;\n private readonly writable: WritableStream<string>;\n private readonly resolve: (value: ToolResponse<TResult>) => void;\n\n public argsText: string = \"\";\n\n constructor() {\n const stream = new TransformStream<string, string>();\n this.writable = stream.writable;\n this.args = new ToolCallArgsReaderImpl<TArgs>(stream.readable);\n\n const { promise, resolve } = promiseWithResolvers<ToolResponse<TResult>>();\n this.resolve = resolve;\n this.response = new ToolCallResponseReaderImpl<TResult>(promise);\n }\n\n async appendArgsTextDelta(text: string): Promise<void> {\n const writer = this.writable.getWriter();\n try {\n await writer.write(text);\n } catch (err) {\n console.warn(err);\n } finally {\n writer.releaseLock();\n }\n\n this.argsText += text;\n }\n\n async finishArgsText(): Promise<void> {\n const writer = this.writable.getWriter();\n try {\n await writer.close();\n } catch (err) {\n console.warn(err);\n } finally {\n writer.releaseLock();\n }\n }\n\n setResponse(value: ToolResponse<TResult>): void {\n this.resolve(value);\n }\n\n result = {\n get: async () => {\n const response = await this.response.get();\n return response.result;\n },\n };\n}\n"],"mappings":"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"}
@@ -167,6 +167,12 @@ type ToolBase<TArgs extends Record<string, unknown> = Record<string, unknown>, T
167
167
  * @see ToolDisplay
168
168
  */
169
169
  display?: ToolDisplay;
170
+ /**
171
+ * Replaces an already-registered same-priority tool with the same name
172
+ * instead of throwing. Discouraged escape hatch — overwriting a same-priority
173
+ * tool is usually a design smell; prefer distinct names or priorities.
174
+ */
175
+ overwrite?: boolean;
170
176
  };
171
177
  type BackendTool<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = ToolBase<TArgs, TResult> & {
172
178
  /** Tool that is executed by the backend rather than in the browser. */
@@ -1 +1 @@
1
- {"version":3,"file":"tool-types.d.ts","names":[],"sources":["../../../src/core/tool/tool-types.ts"],"mappings":";;;;;;;KAMY;;WAGG;;WAEA;;;WAIA;;;;;WAKA;;WAEA;;WAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4BH,wBAAwB,OAAO,YAAY;;EAErD;;EAEA,OAAO;;EAEP,QAAQ;eAEG,yBACT,iBAAiB;;;;;;;;;UAUJ,mBAAmB,cAAc;;;;;;;EAOhD,IAAI,cAAc,SAAS,WACtB,WAAW,QACb,QAAQ,WAAW,OAAO;;;;;;;EAQ7B,aAAa,cAAc,SAAS,WAC/B,WAAW,QACb,oBAAoB,YAAY,WAAW,OAAO;;;;;;;EAQrD,WAAW,cAAc,SAAS,WAC7B,WAAW,QACb,WAAW,OAAO,+BAA+B,KAChD,oBAAoB;;;;;;;EASxB,QAAQ,cAAc,SAAS,WAC1B,WAAW,QACb,YAAY,WAAW,OAAO,gBAAgB,YAAY,KACzD,oBAAoB;;UAIT,uBAAuB;EACtC,WAAW,QAAQ,aAAa;;UAGjB,eACf,cAAc,0BAA0B,yBACxC;EAEA,MAAM,mBAAmB;EACzB,UAAU,uBAAuB;;;;;EAMjC;IACE,WAAW,QAAQ;;;KAIX;;EAEV;;EAEA,aAAa;;;;;EAKb,QAAQ,qBAAqB;;;;;KAMnB,oBAAoB,OAAO,YACrC,MAAM,OACN,SAAS,yBACN,UAAU,QAAQ;KAEX,uBACV,cAAc,0BAA0B,yBACxC,sBAEA,QAAQ,eAAe,OAAO,UAC9B,SAAS;KAGN,gCAAgC,WAAW,6BAE9C;;;;;;;;;;;;;KAeU,kBAAkB,eAAe;;;;;;;;;;;;;;KAexC;KAEA,SACH,cAAc,0BAA0B,yBACxC;;;;EAKA,aAAa,uBAAuB,OAAO;;;;;;;;;;EAW3C,UAAU;;KAaP,YACH,cAAc,0BAA0B,yBACxC,qBACE,SAAS,OAAO;;EAElB;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;;;KAUG,uBACH,cAAc,0BAA0B,yBACxC,qBACE,SAAS,OAAO;EAClB;;EAGA;;EAEA,aAAa,iBAAiB,SAAS;;EAEvC;;EAEA,UAAU,oBAAoB,OAAO;;EAErC,gBAAgB,wBAAwB,OAAO;;EAE/C,uCAAuC,gCAAgC;EACvE,kBAAkB;;KAGf,aACH,cAAc,0BAA0B,yBACxC,qBACE,SAAS,OAAO;;EAElB;;EAGA;;EAEA,YAAY,iBAAiB,SAAS;;EAEtC;;EAEA,UAAU,oBAAoB,OAAO;;EAErC,gBAAgB,wBAAwB,OAAO;;EAE/C,uCAAuC,gCAAgC;EACvE,kBAAkB;;KAGf,UACH,cAAc,0BAA0B,yBACxC,qBACE,SAAS,OAAO;;EAElB;;EAGA;;EAEA,YAAY,iBAAiB,SAAS;;EAEtC;;EAEA;EACA;EACA;EACA;EACA,kBAAkB;;KAGf,aACH,cAAc,0BAA0B,yBACxC,qBACE,SAAS,OAAO;;EAElB;;EAGA;;EAEA,aAAa,iBAAiB,SAAS;;EAEvC,MAAM;;;;EAIN;EAEA;EACA;EACA;EACA;EACA;EACA,kBAAkB;;KAGR;;EAGN;EACA;EACA,UAAU;EACV;;EAEA;;;EAIA;EACA;EACA;EACA,MAAM;EACN;;EAEA;;KAGD,UAAU,SAAS;;EAEtB;EACA,QAAQ;EAER;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4CU,KACV,cAAc,0BAA0B,yBACxC,qBAEE,aAAa,OAAO,WACpB,YAAY,OAAO,WACnB,UAAU,OAAO,WACjB,aAAa,OAAO,WACpB,UACA,gBAAgB,OAAO;;;;;;;KAQf,gBACV,cAAc,0BAA0B,yBACxC,qBAEE,aAAa,OAAO,WACpB,uBAAuB,OAAO,WAC9B,UAAU,OAAO,WACjB,aAAa,OAAO,WACpB,UACA,gBAAgB,OAAO;;;;KAKf,gBACV,cAAc,0BAA0B,yBACxC,sBAEE,KAAK,aAAa,OAAO,oBACzB,KAAK,YAAY,OAAO,oBACxB,KAAK,UAAU,OAAO,oBACtB,KAAK,aAAa,OAAO;EACvB"}
1
+ {"version":3,"file":"tool-types.d.ts","names":[],"sources":["../../../src/core/tool/tool-types.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SSE.d.ts","names":[],"sources":["../../../../src/core/utils/stream/SSE.ts"],"mappings":";;cAMa,WAAW,WAAW,wBACjC,GACA,WAAW;kBAEK,SAAO;EAMvB,SAAO;;;cAiBI,WAAW,WAAW,wBACjC,WAAW,cACX"}
1
+ {"version":3,"file":"SSE.d.ts","names":[],"sources":["../../../../src/core/utils/stream/SSE.ts"],"mappings":";;cAOa,WAAW,WAAW,wBACjC,GACA,WAAW;kBAEK,SAAO;EAMvB,SAAO;;;cAiBI,WAAW,WAAW,wBACjC,WAAW,cACX"}
@@ -1,5 +1,6 @@
1
1
  import { PipeableTransformStream } from "./PipeableTransformStream.js";
2
2
  import { SSEEventDecoderStream } from "./SSEEventDecoderStream.js";
3
+ import sjson from "secure-json-parse";
3
4
  //#region src/core/utils/stream/SSE.ts
4
5
  var SSEEncoder = class SSEEncoder extends PipeableTransformStream {
5
6
  static headers = new Headers({
@@ -18,9 +19,17 @@ var SSEDecoder = class extends PipeableTransformStream {
18
19
  constructor() {
19
20
  super((readable) => readable.pipeThrough(new TextDecoderStream()).pipeThrough(new SSEEventDecoderStream()).pipeThrough(new TransformStream({ transform(event, controller) {
20
21
  switch (event.event) {
21
- case "message":
22
- controller.enqueue(JSON.parse(event.data));
22
+ case "message": {
23
+ let value;
24
+ try {
25
+ value = sjson.parse(event.data);
26
+ } catch {
27
+ console.warn(`Dropped invalid SSE message: ${event.data.slice(0, 200)}`);
28
+ break;
29
+ }
30
+ controller.enqueue(value);
23
31
  break;
32
+ }
24
33
  default: throw new Error(`Unknown SSE event type: ${event.event}`);
25
34
  }
26
35
  } })));
@@ -1 +1 @@
1
- {"version":3,"file":"SSE.js","names":[],"sources":["../../../../src/core/utils/stream/SSE.ts"],"sourcesContent":["import { PipeableTransformStream } from \"./PipeableTransformStream\";\nimport {\n SSEEventDecoderStream,\n type PipelineSSEEvent,\n} from \"./SSEEventDecoderStream\";\n\nexport class SSEEncoder<T> extends PipeableTransformStream<\n T,\n Uint8Array<ArrayBuffer>\n> {\n static readonly headers = new Headers({\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n\n headers = SSEEncoder.headers;\n\n constructor() {\n super((readable) =>\n readable\n .pipeThrough(\n new TransformStream<T, string>({\n transform(chunk, controller) {\n controller.enqueue(`data: ${JSON.stringify(chunk)}\\n\\n`);\n },\n }),\n )\n .pipeThrough(new TextEncoderStream()),\n );\n }\n}\n\nexport class SSEDecoder<T> extends PipeableTransformStream<\n Uint8Array<ArrayBuffer>,\n T\n> {\n constructor() {\n super((readable) =>\n readable\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new SSEEventDecoderStream())\n .pipeThrough(\n new TransformStream<PipelineSSEEvent, T>({\n transform(event, controller) {\n switch (event.event) {\n case \"message\":\n controller.enqueue(JSON.parse(event.data));\n break;\n default:\n throw new Error(`Unknown SSE event type: ${event.event}`);\n }\n },\n }),\n ),\n );\n }\n}\n"],"mappings":";;;AAMA,IAAa,aAAb,MAAa,mBAAsB,wBAGjC;CACA,OAAgB,UAAU,IAAI,QAAQ;EACpC,gBAAgB;EAChB,iBAAiB;EACjB,YAAY;CACd,CAAC;CAED,UAAU,WAAW;CAErB,cAAc;EACZ,OAAO,aACL,SACG,YACC,IAAI,gBAA2B,EAC7B,UAAU,OAAO,YAAY;GAC3B,WAAW,QAAQ,SAAS,KAAK,UAAU,KAAK,EAAE,KAAK;EACzD,EACF,CAAC,CACH,CAAC,CACA,YAAY,IAAI,kBAAkB,CAAC,CACxC;CACF;AACF;AAEA,IAAa,aAAb,cAAmC,wBAGjC;CACA,cAAc;EACZ,OAAO,aACL,SACG,YAAY,IAAI,kBAAkB,CAAC,CAAC,CACpC,YAAY,IAAI,sBAAsB,CAAC,CAAC,CACxC,YACC,IAAI,gBAAqC,EACvC,UAAU,OAAO,YAAY;GAC3B,QAAQ,MAAM,OAAd;IACE,KAAK;KACH,WAAW,QAAQ,KAAK,MAAM,MAAM,IAAI,CAAC;KACzC;IACF,SACE,MAAM,IAAI,MAAM,2BAA2B,MAAM,OAAO;GAC5D;EACF,EACF,CAAC,CACH,CACJ;CACF;AACF"}
1
+ {"version":3,"file":"SSE.js","names":[],"sources":["../../../../src/core/utils/stream/SSE.ts"],"sourcesContent":["import sjson from \"secure-json-parse\";\nimport { PipeableTransformStream } from \"./PipeableTransformStream\";\nimport {\n SSEEventDecoderStream,\n type PipelineSSEEvent,\n} from \"./SSEEventDecoderStream\";\n\nexport class SSEEncoder<T> extends PipeableTransformStream<\n T,\n Uint8Array<ArrayBuffer>\n> {\n static readonly headers = new Headers({\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n\n headers = SSEEncoder.headers;\n\n constructor() {\n super((readable) =>\n readable\n .pipeThrough(\n new TransformStream<T, string>({\n transform(chunk, controller) {\n controller.enqueue(`data: ${JSON.stringify(chunk)}\\n\\n`);\n },\n }),\n )\n .pipeThrough(new TextEncoderStream()),\n );\n }\n}\n\nexport class SSEDecoder<T> extends PipeableTransformStream<\n Uint8Array<ArrayBuffer>,\n T\n> {\n constructor() {\n super((readable) =>\n readable\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new SSEEventDecoderStream())\n .pipeThrough(\n new TransformStream<PipelineSSEEvent, T>({\n transform(event, controller) {\n switch (event.event) {\n case \"message\": {\n let value;\n try {\n value = sjson.parse(event.data);\n } catch {\n console.warn(\n `Dropped invalid SSE message: ${event.data.slice(0, 200)}`,\n );\n break;\n }\n controller.enqueue(value);\n break;\n }\n default:\n throw new Error(`Unknown SSE event type: ${event.event}`);\n }\n },\n }),\n ),\n );\n }\n}\n"],"mappings":";;;;AAOA,IAAa,aAAb,MAAa,mBAAsB,wBAGjC;CACA,OAAgB,UAAU,IAAI,QAAQ;EACpC,gBAAgB;EAChB,iBAAiB;EACjB,YAAY;CACd,CAAC;CAED,UAAU,WAAW;CAErB,cAAc;EACZ,OAAO,aACL,SACG,YACC,IAAI,gBAA2B,EAC7B,UAAU,OAAO,YAAY;GAC3B,WAAW,QAAQ,SAAS,KAAK,UAAU,KAAK,EAAE,KAAK;EACzD,EACF,CAAC,CACH,CAAC,CACA,YAAY,IAAI,kBAAkB,CAAC,CACxC;CACF;AACF;AAEA,IAAa,aAAb,cAAmC,wBAGjC;CACA,cAAc;EACZ,OAAO,aACL,SACG,YAAY,IAAI,kBAAkB,CAAC,CAAC,CACpC,YAAY,IAAI,sBAAsB,CAAC,CAAC,CACxC,YACC,IAAI,gBAAqC,EACvC,UAAU,OAAO,YAAY;GAC3B,QAAQ,MAAM,OAAd;IACE,KAAK,WAAW;KACd,IAAI;KACJ,IAAI;MACF,QAAQ,MAAM,MAAM,MAAM,IAAI;KAChC,QAAQ;MACN,QAAQ,KACN,gCAAgC,MAAM,KAAK,MAAM,GAAG,GAAG,GACzD;MACA;KACF;KACA,WAAW,QAAQ,KAAK;KACxB;IACF;IACA,SACE,MAAM,IAAI,MAAM,2BAA2B,MAAM,OAAO;GAC5D;EACF,EACF,CAAC,CACH,CACJ;CACF;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assistant-stream",
3
- "version": "0.3.27",
3
+ "version": "0.3.28",
4
4
  "description": "Streaming utilities for AI assistants",
5
5
  "keywords": [
6
6
  "ai",
@@ -67,7 +67,7 @@
67
67
  "ioredis": "^5.11.1",
68
68
  "redis": "^6.1.0",
69
69
  "vitest": "^4.1.10",
70
- "@assistant-ui/x-buildutils": "0.0.19"
70
+ "@assistant-ui/x-buildutils": "0.0.20"
71
71
  },
72
72
  "publishConfig": {
73
73
  "access": "public",
@@ -3,7 +3,9 @@ import type { AssistantMessage } from "../utils/types";
3
3
  import { AssistantMessageAccumulator } from "./assistant-message-accumulator";
4
4
 
5
5
  export class AssistantMessageStream {
6
- constructor(public readonly readable: ReadableStream<AssistantMessage>) {
6
+ public readonly readable: ReadableStream<AssistantMessage>;
7
+
8
+ constructor(readable: ReadableStream<AssistantMessage>) {
7
9
  this.readable = readable;
8
10
  }
9
11
 
@@ -563,66 +563,30 @@ describe("toGenericMessages", () => {
563
563
  expect(content[0]!.mediaType).toBe(expectedType);
564
564
  };
565
565
 
566
- it("handles jpg extension", () => {
566
+ it("maps known extensions to their media types", () => {
567
567
  testMediaType("https://example.com/photo.jpg", "image/jpeg");
568
- });
569
-
570
- it("handles jpeg extension", () => {
571
568
  testMediaType("https://example.com/photo.jpeg", "image/jpeg");
572
- });
573
-
574
- it("handles png extension", () => {
575
569
  testMediaType("https://example.com/photo.png", "image/png");
576
- });
577
-
578
- it("handles gif extension", () => {
579
570
  testMediaType("https://example.com/photo.gif", "image/gif");
580
- });
581
-
582
- it("handles webp extension", () => {
583
571
  testMediaType("https://example.com/photo.webp", "image/webp");
584
- });
585
-
586
- it("handles svg extension", () => {
587
572
  testMediaType("https://example.com/icon.svg", "image/svg+xml");
588
- });
589
-
590
- it("handles avif extension", () => {
591
573
  testMediaType("https://example.com/photo.avif", "image/avif");
592
- });
593
-
594
- it("handles bmp extension", () => {
595
574
  testMediaType("https://example.com/photo.bmp", "image/bmp");
596
- });
597
-
598
- it("handles ico extension", () => {
599
575
  testMediaType("https://example.com/favicon.ico", "image/x-icon");
600
- });
601
-
602
- it("handles tiff extension", () => {
603
576
  testMediaType("https://example.com/photo.tiff", "image/tiff");
604
- });
605
-
606
- it("handles tif extension", () => {
607
577
  testMediaType("https://example.com/photo.tif", "image/tiff");
608
578
  });
609
579
 
610
- it("defaults to image/png for unknown extension", () => {
580
+ it("defaults to image/png for unknown or missing extensions", () => {
611
581
  testMediaType("https://example.com/photo.unknown", "image/png");
612
- });
613
-
614
- it("defaults to image/png for no extension", () => {
615
582
  testMediaType("https://example.com/photo", "image/png");
616
583
  });
617
584
 
618
- it("handles query params in URL", () => {
585
+ it("ignores query params and casing in the extension", () => {
619
586
  testMediaType(
620
587
  "https://example.com/photo.jpg?size=large&quality=high",
621
588
  "image/jpeg",
622
589
  );
623
- });
624
-
625
- it("handles uppercase extensions", () => {
626
590
  testMediaType("https://example.com/photo.JPG", "image/jpeg");
627
591
  });
628
592
  });
@@ -59,6 +59,36 @@ describe("GorpStreamDeltaTracker isChangedAt", () => {
59
59
  expect(tracker.isChangedAt(["c"])).toBe(false);
60
60
  });
61
61
 
62
+ it("collapses a subtree to fully-changed when an ancestor is set in the same frame", () => {
63
+ const tracker = new GorpStreamDeltaTracker({ a: { b: 1, c: 2 } });
64
+ tracker.append([
65
+ { type: "set", path: ["a", "b"], value: 10 },
66
+ { type: "set", path: ["a"], value: { d: 3 } },
67
+ ]);
68
+ expect(tracker.isChangedAt(["a", "anything", "deep"])).toBe(true);
69
+ expect(tracker.getChangedKeys(["a"])).toEqual(["d", "c", "b"]);
70
+ });
71
+
72
+ it("keeps a subtree fully-changed when a descendant is set after its ancestor in the same frame", () => {
73
+ const tracker = new GorpStreamDeltaTracker({ a: { b: 1, c: 2 } });
74
+ tracker.append([
75
+ { type: "set", path: ["a"], value: { d: 3 } },
76
+ { type: "set", path: ["a", "d"], value: 4 },
77
+ ]);
78
+ expect(tracker.isChangedAt(["a", "anything", "deep"])).toBe(true);
79
+ expect(tracker.getChangedKeys(["a"])).toEqual(["d", "c", "b"]);
80
+ });
81
+
82
+ it("reports a leaf set twice in one frame as changed once", () => {
83
+ const tracker = new GorpStreamDeltaTracker({ a: { b: 1 } });
84
+ tracker.append([
85
+ { type: "set", path: ["a", "b"], value: 2 },
86
+ { type: "set", path: ["a", "b"], value: 3 },
87
+ ]);
88
+ expect(tracker.getChangedKeys(["a"])).toEqual(["b"]);
89
+ expect((tracker.state as ReadonlyJSONObject).a).toEqual({ b: 3 });
90
+ });
91
+
62
92
  it("marks append-text targets as changed", () => {
63
93
  const tracker = new GorpStreamDeltaTracker({ message: "Hello" });
64
94
  tracker.append([{ type: "append-text", path: ["message"], value: "!" }]);
@@ -16,9 +16,12 @@ class ToolCallStreamControllerImpl implements ToolCallStreamController {
16
16
  private _isClosed = false;
17
17
 
18
18
  private _mergeTask: Promise<void>;
19
+ private _controller: ReadableStreamDefaultController<AssistantStreamChunk>;
20
+
19
21
  constructor(
20
- private _controller: ReadableStreamDefaultController<AssistantStreamChunk>,
22
+ _controller: ReadableStreamDefaultController<AssistantStreamChunk>,
21
23
  ) {
24
+ this._controller = _controller;
22
25
  const stream = createTextStream({
23
26
  start: (c) => {
24
27
  this._argsTextController = c;
@@ -208,7 +208,7 @@ export class DataStreamEncoder
208
208
  }
209
209
  }
210
210
 
211
- const TOOL_CALL_ARGS_CLOSING_CHUNKS = [
211
+ const TOOL_CALL_ARGS_CLOSING_CHUNKS: DataStreamStreamChunkType[] = [
212
212
  DataStreamStreamChunkType.StartToolCall,
213
213
  DataStreamStreamChunkType.ToolCall,
214
214
  DataStreamStreamChunkType.TextDelta,
@@ -25,29 +25,31 @@ type LanguageModelV1Usage = {
25
25
  outputTokens: number;
26
26
  };
27
27
 
28
- export enum DataStreamStreamChunkType {
29
- TextDelta = "0",
30
- Data = "2",
31
- Error = "3",
32
- Annotation = "8",
33
- ToolCall = "9",
34
- ToolCallResult = "a",
35
- StartToolCall = "b",
36
- ToolCallArgsTextDelta = "c",
37
- FinishMessage = "d",
38
- FinishStep = "e",
39
- StartStep = "f",
40
- ReasoningDelta = "g",
41
- Source = "h",
42
- RedactedReasoning = "i",
43
- ReasoningSignature = "j",
44
- File = "k",
28
+ export const DataStreamStreamChunkType = {
29
+ TextDelta: "0",
30
+ Data: "2",
31
+ Error: "3",
32
+ Annotation: "8",
33
+ ToolCall: "9",
34
+ ToolCallResult: "a",
35
+ StartToolCall: "b",
36
+ ToolCallArgsTextDelta: "c",
37
+ FinishMessage: "d",
38
+ FinishStep: "e",
39
+ StartStep: "f",
40
+ ReasoningDelta: "g",
41
+ Source: "h",
42
+ RedactedReasoning: "i",
43
+ ReasoningSignature: "j",
44
+ File: "k",
45
45
 
46
- AuiUpdateStateOperations = "aui-state",
47
- AuiTextDelta = "aui-text-delta",
48
- AuiReasoningDelta = "aui-reasoning-delta",
49
- AuiDataPart = "aui-data",
50
- }
46
+ AuiUpdateStateOperations: "aui-state",
47
+ AuiTextDelta: "aui-text-delta",
48
+ AuiReasoningDelta: "aui-reasoning-delta",
49
+ AuiDataPart: "aui-data",
50
+ } as const;
51
+ export type DataStreamStreamChunkType =
52
+ (typeof DataStreamStreamChunkType)[keyof typeof DataStreamStreamChunkType];
51
53
  type DataStreamStreamChunkValue = {
52
54
  [DataStreamStreamChunkType.TextDelta]: string;
53
55
  [DataStreamStreamChunkType.Data]: ReadonlyJSONValue[];