assistant-stream 0.3.20 → 0.3.22

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 (36) hide show
  1. package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -1
  2. package/dist/core/accumulators/assistant-message-accumulator.js +5 -0
  3. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  4. package/dist/core/converters/toGenericMessages.js.map +1 -1
  5. package/dist/core/object/ObjectStreamResponse.js.map +1 -1
  6. package/dist/core/object/types.d.ts +1 -0
  7. package/dist/core/object/types.d.ts.map +1 -1
  8. package/dist/core/serialization/PlainText.js.map +1 -1
  9. package/dist/core/serialization/assistant-transport/AssistantTransport.js.map +1 -1
  10. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  11. package/dist/core/serialization/ui-message-stream/UIMessageStream.js.map +1 -1
  12. package/dist/core/tool/ToolCallReader.d.ts +1 -0
  13. package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
  14. package/dist/core/tool/ToolCallReader.js.map +1 -1
  15. package/dist/core/tool/ToolExecutionStream.js.map +1 -1
  16. package/dist/core/tool/schema-utils.js.map +1 -1
  17. package/dist/core/tool/tool-types.d.ts.map +1 -1
  18. package/dist/core/tool/toolResultStream.js.map +1 -1
  19. package/dist/core/utils/stream/AssistantTransformStream.js.map +1 -1
  20. package/dist/core/utils/stream/SSE.js.map +1 -1
  21. package/dist/core/utils/stream/merge.js.map +1 -1
  22. package/dist/core/utils/types.d.ts +12 -1
  23. package/dist/core/utils/types.d.ts.map +1 -1
  24. package/dist/index.d.ts +2 -2
  25. package/dist/resumable/ResumableStreamContext.js.map +1 -1
  26. package/dist/resumable/createResumableAssistantStreamResponse.js.map +1 -1
  27. package/dist/resumable/stores/InMemoryResumableStreamStore.js.map +1 -1
  28. package/dist/resumable/stores/ioredis.js.map +1 -1
  29. package/dist/utils/json/fix-json.js.map +1 -1
  30. package/dist/utils/json/is-json.js.map +1 -1
  31. package/package.json +4 -4
  32. package/src/core/accumulators/assistant-message-accumulator.test.ts +51 -0
  33. package/src/core/accumulators/assistant-message-accumulator.ts +9 -0
  34. package/src/core/utils/types.ts +14 -0
  35. package/src/index.ts +1 -0
  36. package/src/resumable/__tests__/integration.test.ts +12 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ToolExecutionStream.js","names":[],"sources":["../../../src/core/tool/ToolExecutionStream.ts"],"sourcesContent":["import sjson from \"secure-json-parse\";\nimport type { AssistantStreamChunk } from \"../AssistantStreamChunk\";\nimport {\n type AssistantMetaStreamChunk,\n AssistantMetaTransformStream,\n} from \"../utils/stream/AssistantMetaTransformStream\";\nimport { PipeableTransformStream } from \"../utils/stream/PipeableTransformStream\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils/json/json-value\";\nimport { ToolResponse } from \"./ToolResponse\";\nimport { withPromiseOrValue } from \"../utils/withPromiseOrValue\";\nimport { ToolCallReaderImpl } from \"./ToolCallReader\";\nimport type { ToolCallReader } from \"./tool-types\";\n\ntype ToolCallback = (toolCall: {\n toolCallId: string;\n toolName: string;\n args: ReadonlyJSONObject;\n}) =>\n | Promise<ToolResponse<ReadonlyJSONValue>>\n | ToolResponse<ReadonlyJSONValue>\n | undefined;\n\ntype ToolStreamCallback = <\n TArgs extends ReadonlyJSONObject = ReadonlyJSONObject,\n TResult extends ReadonlyJSONValue = ReadonlyJSONValue,\n>(toolCall: {\n reader: ToolCallReader<TArgs, TResult>;\n toolCallId: string;\n toolName: string;\n}) => void;\n\ntype ToolExecutionOptions = {\n execute: ToolCallback;\n streamCall: ToolStreamCallback;\n onExecutionStart?:\n | ((toolCallId: string, toolName: string) => void)\n | undefined;\n onExecutionEnd?: ((toolCallId: string, toolName: string) => void) | undefined;\n};\n\nexport class ToolExecutionStream extends PipeableTransformStream<\n AssistantStreamChunk,\n AssistantStreamChunk\n> {\n constructor(options: ToolExecutionOptions) {\n const toolCallPromises = new Map<string, PromiseLike<void>>();\n const toolCallControllers = new Map<\n string,\n ToolCallReaderImpl<ReadonlyJSONObject, ReadonlyJSONValue>\n >();\n\n super((readable) => {\n const transform = new TransformStream<\n AssistantMetaStreamChunk,\n AssistantStreamChunk\n >({\n async transform(chunk, controller) {\n // forward everything\n if (chunk.type !== \"part-finish\" || chunk.meta.type !== \"tool-call\") {\n controller.enqueue(chunk);\n }\n\n const type = chunk.type;\n\n switch (type) {\n case \"part-start\":\n if (chunk.part.type === \"tool-call\") {\n const reader = new ToolCallReaderImpl<\n ReadonlyJSONObject,\n ReadonlyJSONValue\n >();\n toolCallControllers.set(chunk.part.toolCallId, reader);\n\n options.streamCall({\n reader,\n toolCallId: chunk.part.toolCallId,\n toolName: chunk.part.toolName,\n });\n }\n break;\n case \"text-delta\": {\n if (chunk.meta.type === \"tool-call\") {\n const toolCallId = chunk.meta.toolCallId;\n\n const controller = toolCallControllers.get(toolCallId);\n if (!controller)\n throw new Error(\"No controller found for tool call\");\n // Awaited so the writer lock is released (and argsText updated)\n // before the next chunk acquires the writer.\n await controller.appendArgsTextDelta(chunk.textDelta);\n }\n break;\n }\n case \"result\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId } = chunk.meta;\n const controller = toolCallControllers.get(toolCallId);\n if (!controller)\n throw new Error(\"No controller found for tool call\");\n controller.setResponse(\n new ToolResponse({\n result: chunk.result,\n artifact: chunk.artifact,\n isError: chunk.isError,\n modelContent: chunk.modelContent,\n }),\n );\n break;\n }\n case \"tool-call-args-text-finish\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId, toolName } = chunk.meta;\n const streamController = toolCallControllers.get(toolCallId)!;\n if (!streamController)\n throw new Error(\"No controller found for tool call\");\n\n // Args fully streamed: close the reader so awaited absent fields\n // resolve. Awaited so the close settles before the writer is reused.\n await streamController.finishArgsText();\n\n let isExecuting = false;\n const promise = withPromiseOrValue(\n () => {\n let args: ReadonlyJSONObject;\n try {\n args = sjson.parse(\n streamController.argsText,\n ) as ReadonlyJSONObject;\n } catch (e) {\n throw new Error(\n `Function parameter parsing failed. ${JSON.stringify((e as Error).message)}`,\n );\n }\n\n const executeResult = options.execute({\n toolCallId,\n toolName,\n args,\n });\n\n // Only mark as executing if the tool has frontend execution\n if (executeResult !== undefined) {\n isExecuting = true;\n options.onExecutionStart?.(toolCallId, toolName);\n }\n\n return executeResult;\n },\n (c) => {\n if (isExecuting) {\n options.onExecutionEnd?.(toolCallId, toolName);\n }\n\n if (c === undefined) return;\n\n // TODO how to handle new ToolResult({ result: undefined })?\n const result = new ToolResponse({\n artifact: c.artifact,\n result: c.result,\n isError: c.isError,\n messages: c.messages,\n modelContent: c.modelContent,\n });\n streamController.setResponse(result);\n controller.enqueue({\n type: \"result\",\n path: chunk.path,\n ...result,\n });\n },\n (e) => {\n if (isExecuting) {\n options.onExecutionEnd?.(toolCallId, toolName);\n }\n\n const result = new ToolResponse({\n result: String(e),\n isError: true,\n });\n\n streamController.setResponse(result);\n controller.enqueue({\n type: \"result\",\n path: chunk.path,\n ...result,\n });\n },\n );\n if (promise) {\n toolCallPromises.set(toolCallId, promise);\n }\n break;\n }\n\n case \"part-finish\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId } = chunk.meta;\n const toolCallPromise = toolCallPromises.get(toolCallId);\n if (toolCallPromise) {\n toolCallPromise.then(() => {\n toolCallPromises.delete(toolCallId);\n toolCallControllers.delete(toolCallId);\n\n controller.enqueue(chunk);\n });\n } else {\n controller.enqueue(chunk);\n }\n }\n }\n },\n async flush() {\n await Promise.all(toolCallPromises.values());\n },\n });\n\n return readable\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeThrough(transform);\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ToolExecutionStream.js","names":[],"sources":["../../../src/core/tool/ToolExecutionStream.ts"],"sourcesContent":["import sjson from \"secure-json-parse\";\nimport type { AssistantStreamChunk } from \"../AssistantStreamChunk\";\nimport {\n type AssistantMetaStreamChunk,\n AssistantMetaTransformStream,\n} from \"../utils/stream/AssistantMetaTransformStream\";\nimport { PipeableTransformStream } from \"../utils/stream/PipeableTransformStream\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils/json/json-value\";\nimport { ToolResponse } from \"./ToolResponse\";\nimport { withPromiseOrValue } from \"../utils/withPromiseOrValue\";\nimport { ToolCallReaderImpl } from \"./ToolCallReader\";\nimport type { ToolCallReader } from \"./tool-types\";\n\ntype ToolCallback = (toolCall: {\n toolCallId: string;\n toolName: string;\n args: ReadonlyJSONObject;\n}) =>\n | Promise<ToolResponse<ReadonlyJSONValue>>\n | ToolResponse<ReadonlyJSONValue>\n | undefined;\n\ntype ToolStreamCallback = <\n TArgs extends ReadonlyJSONObject = ReadonlyJSONObject,\n TResult extends ReadonlyJSONValue = ReadonlyJSONValue,\n>(toolCall: {\n reader: ToolCallReader<TArgs, TResult>;\n toolCallId: string;\n toolName: string;\n}) => void;\n\ntype ToolExecutionOptions = {\n execute: ToolCallback;\n streamCall: ToolStreamCallback;\n onExecutionStart?:\n | ((toolCallId: string, toolName: string) => void)\n | undefined;\n onExecutionEnd?: ((toolCallId: string, toolName: string) => void) | undefined;\n};\n\nexport class ToolExecutionStream extends PipeableTransformStream<\n AssistantStreamChunk,\n AssistantStreamChunk\n> {\n constructor(options: ToolExecutionOptions) {\n const toolCallPromises = new Map<string, PromiseLike<void>>();\n const toolCallControllers = new Map<\n string,\n ToolCallReaderImpl<ReadonlyJSONObject, ReadonlyJSONValue>\n >();\n\n super((readable) => {\n const transform = new TransformStream<\n AssistantMetaStreamChunk,\n AssistantStreamChunk\n >({\n async transform(chunk, controller) {\n // forward everything\n if (chunk.type !== \"part-finish\" || chunk.meta.type !== \"tool-call\") {\n controller.enqueue(chunk);\n }\n\n const type = chunk.type;\n\n switch (type) {\n case \"part-start\":\n if (chunk.part.type === \"tool-call\") {\n const reader = new ToolCallReaderImpl<\n ReadonlyJSONObject,\n ReadonlyJSONValue\n >();\n toolCallControllers.set(chunk.part.toolCallId, reader);\n\n options.streamCall({\n reader,\n toolCallId: chunk.part.toolCallId,\n toolName: chunk.part.toolName,\n });\n }\n break;\n case \"text-delta\": {\n if (chunk.meta.type === \"tool-call\") {\n const toolCallId = chunk.meta.toolCallId;\n\n const controller = toolCallControllers.get(toolCallId);\n if (!controller)\n throw new Error(\"No controller found for tool call\");\n // Awaited so the writer lock is released (and argsText updated)\n // before the next chunk acquires the writer.\n await controller.appendArgsTextDelta(chunk.textDelta);\n }\n break;\n }\n case \"result\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId } = chunk.meta;\n const controller = toolCallControllers.get(toolCallId);\n if (!controller)\n throw new Error(\"No controller found for tool call\");\n controller.setResponse(\n new ToolResponse({\n result: chunk.result,\n artifact: chunk.artifact,\n isError: chunk.isError,\n modelContent: chunk.modelContent,\n }),\n );\n break;\n }\n case \"tool-call-args-text-finish\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId, toolName } = chunk.meta;\n const streamController = toolCallControllers.get(toolCallId)!;\n if (!streamController)\n throw new Error(\"No controller found for tool call\");\n\n // Args fully streamed: close the reader so awaited absent fields\n // resolve. Awaited so the close settles before the writer is reused.\n await streamController.finishArgsText();\n\n let isExecuting = false;\n const promise = withPromiseOrValue(\n () => {\n let args: ReadonlyJSONObject;\n try {\n args = sjson.parse(\n streamController.argsText,\n ) as ReadonlyJSONObject;\n } catch (e) {\n throw new Error(\n `Function parameter parsing failed. ${JSON.stringify((e as Error).message)}`,\n );\n }\n\n const executeResult = options.execute({\n toolCallId,\n toolName,\n args,\n });\n\n // Only mark as executing if the tool has frontend execution\n if (executeResult !== undefined) {\n isExecuting = true;\n options.onExecutionStart?.(toolCallId, toolName);\n }\n\n return executeResult;\n },\n (c) => {\n if (isExecuting) {\n options.onExecutionEnd?.(toolCallId, toolName);\n }\n\n if (c === undefined) return;\n\n // TODO how to handle new ToolResult({ result: undefined })?\n const result = new ToolResponse({\n artifact: c.artifact,\n result: c.result,\n isError: c.isError,\n messages: c.messages,\n modelContent: c.modelContent,\n });\n streamController.setResponse(result);\n controller.enqueue({\n type: \"result\",\n path: chunk.path,\n ...result,\n });\n },\n (e) => {\n if (isExecuting) {\n options.onExecutionEnd?.(toolCallId, toolName);\n }\n\n const result = new ToolResponse({\n result: String(e),\n isError: true,\n });\n\n streamController.setResponse(result);\n controller.enqueue({\n type: \"result\",\n path: chunk.path,\n ...result,\n });\n },\n );\n if (promise) {\n toolCallPromises.set(toolCallId, promise);\n }\n break;\n }\n\n case \"part-finish\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId } = chunk.meta;\n const toolCallPromise = toolCallPromises.get(toolCallId);\n if (toolCallPromise) {\n toolCallPromise.then(() => {\n toolCallPromises.delete(toolCallId);\n toolCallControllers.delete(toolCallId);\n\n controller.enqueue(chunk);\n });\n } else {\n controller.enqueue(chunk);\n }\n }\n }\n },\n async flush() {\n await Promise.all(toolCallPromises.values());\n },\n });\n\n return readable\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeThrough(transform);\n });\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema-utils.js","names":[],"sources":["../../../src/core/tool/schema-utils.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ProviderOptions, Tool } from \"./tool-types\";\n\n/**\n * Type for a tool definition with JSON Schema parameters.\n */\nexport type ToolJSONSchema = {\n description?: string;\n parameters: JSONSchema7;\n providerOptions?: ProviderOptions;\n};\n\nexport type ToToolsJSONSchemaOptions = {\n /**\n * Filter to determine which tools to include.\n * Defaults to excluding disabled tools and backend tools.\n *\n * Tools with backend-default parameters are always excluded.\n */\n filter?: (name: string, tool: Tool) => boolean;\n};\n\nfunction isStandardSchema(schema: unknown): schema is StandardSchemaV1 & {\n \"~standard\": StandardSchemaV1[\"~standard\"] & {\n toJSONSchema?: () => unknown;\n jsonSchema?: { input?: () => unknown; output?: () => unknown };\n };\n} {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema &&\n typeof (schema as StandardSchemaV1)[\"~standard\"] === \"object\"\n );\n}\n\nfunction hasToJSONSchemaMethod(\n schema: unknown,\n): schema is { toJSONSchema: () => unknown } {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"toJSONSchema\" in schema &&\n typeof (schema as { toJSONSchema: unknown }).toJSONSchema === \"function\"\n );\n}\n\nfunction hasToJSONMethod(schema: unknown): schema is { toJSON: () => unknown } {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"toJSON\" in schema &&\n typeof (schema as { toJSON: unknown }).toJSON === \"function\"\n );\n}\n\n/**\n * Converts a schema to JSONSchema7.\n * Supports:\n * - StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n * - StandardSchemaV1 with ~standard.jsonSchema.input() (e.g., Zod v4)\n * - Objects with toJSONSchema() method (e.g., Zod v4)\n * - Objects with toJSON() method\n * - Plain JSONSchema7 objects (must have a \"type\" property)\n */\nexport function toJSONSchema(\n schema: StandardSchemaV1 | JSONSchema7,\n): JSONSchema7 {\n // StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n if (isStandardSchema(schema)) {\n const toJSONSchemaMethod = schema[\"~standard\"].toJSONSchema;\n if (typeof toJSONSchemaMethod === \"function\") {\n return toJSONSchemaMethod() as JSONSchema7;\n }\n\n // StandardSchemaV1 with ~standard.jsonSchema.input()\n const jsonSchema = schema[\"~standard\"].jsonSchema;\n if (\n typeof jsonSchema === \"object\" &&\n jsonSchema !== null &&\n typeof jsonSchema.input === \"function\"\n ) {\n return jsonSchema.input() as JSONSchema7;\n }\n }\n\n // toJSONSchema method on the schema itself\n if (hasToJSONSchemaMethod(schema)) {\n return schema.toJSONSchema() as JSONSchema7;\n }\n\n // toJSON method on the schema\n if (hasToJSONMethod(schema)) {\n return schema.toJSON() as JSONSchema7;\n }\n\n // If it's a Standard Schema that we couldn't convert, throw a helpful error\n if (isStandardSchema(schema)) {\n throw new Error(\n \"Could not convert schema to JSON Schema. \" +\n \"The schema implements Standard Schema but does not support JSON Schema conversion. \" +\n \"If you are using Zod, please upgrade to Zod v4 (npm install zod@latest). \" +\n \"Alternatively, pass a plain JSON Schema object instead.\",\n );\n }\n\n // Already a plain JSONSchema7\n return schema as JSONSchema7;\n}\n\n/**\n * Returns a copy of the JSON Schema with `required` removed recursively,\n * making every property optional. Array item schemas are left unchanged.\n */\nexport function toPartialJSONSchema(schema: JSONSchema7): JSONSchema7 {\n const { required: _, ...result } = schema;\n\n if (result.properties) {\n result.properties = Object.fromEntries(\n Object.entries(result.properties).map(([key, prop]) => {\n if (typeof prop === \"object\" && prop !== null && !Array.isArray(prop)) {\n const p = prop as JSONSchema7;\n return [key, p.properties != null ? toPartialJSONSchema(p) : prop];\n }\n return [key, prop];\n }),\n );\n }\n\n return result;\n}\n\nfunction defaultToolFilter(_name: string, tool: Tool): boolean {\n return (\n !tool.disabled &&\n tool.type !== \"backend\" &&\n (tool.type !== \"frontend\" || tool.execute !== undefined)\n );\n}\n\nfunction toolHasUploadableParameters(\n tool: Tool,\n): tool is Tool & { parameters: NonNullable<Tool[\"parameters\"]> } {\n return (\n tool.parameters !== undefined && !tool.unstable_backendDefault?.parameters\n );\n}\n\n/**\n * Converts a record of tools to a record of tool definitions with JSON Schema parameters.\n * By default, filters out disabled tools and backend tools.\n *\n * Entries are emitted in alphabetical order so the resulting request body is\n * byte-identical regardless of the order in which tools were registered. This\n * keeps provider prompt caches stable across renders that mount tools in\n * different orders.\n */\nexport function toToolsJSONSchema(\n tools: Record<string, Tool> | undefined,\n options: ToToolsJSONSchemaOptions = {},\n): Record<string, ToolJSONSchema> {\n if (!tools) return {};\n\n const filter = options.filter ?? defaultToolFilter;\n\n return Object.fromEntries(\n Object.entries(tools)\n .filter(([name, tool]) => filter(name, tool))\n .filter(\n (\n entry,\n ): entry is [\n string,\n Tool & { parameters: NonNullable<Tool[\"parameters\"]> },\n ] => toolHasUploadableParameters(entry[1]),\n )\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([name, tool]) => [\n name,\n {\n ...(tool.description && { description: tool.description }),\n parameters: toJSONSchema(tool.parameters),\n ...(tool.providerOptions && {\n providerOptions: tool.providerOptions,\n }),\n },\n ]),\n );\n}\n"],"mappings":";AAuBA,SAAS,iBAAiB,QAKxB;CACA,OACE,OAAO,WAAW,YAClB,WAAW,QACX,eAAe,UACf,OAAQ,OAA4B,iBAAiB;AAEzD;AAEA,SAAS,sBACP,QAC2C;CAC3C,OACE,OAAO,WAAW,YAClB,WAAW,QACX,kBAAkB,UAClB,OAAQ,OAAqC,iBAAiB;AAElE;AAEA,SAAS,gBAAgB,QAAsD;CAC7E,OACE,OAAO,WAAW,YAClB,WAAW,QACX,YAAY,UACZ,OAAQ,OAA+B,WAAW;AAEtD;;;;;;;;;;AAWA,SAAgB,aACd,QACa;CAEb,IAAI,iBAAiB,MAAM,GAAG;EAC5B,MAAM,qBAAqB,OAAO,aAAa;EAC/C,IAAI,OAAO,uBAAuB,YAChC,OAAO,mBAAmB;EAI5B,MAAM,aAAa,OAAO,aAAa;EACvC,IACE,OAAO,eAAe,YACtB,eAAe,QACf,OAAO,WAAW,UAAU,YAE5B,OAAO,WAAW,MAAM;CAE5B;CAGA,IAAI,sBAAsB,MAAM,GAC9B,OAAO,OAAO,aAAa;CAI7B,IAAI,gBAAgB,MAAM,GACxB,OAAO,OAAO,OAAO;CAIvB,IAAI,iBAAiB,MAAM,GACzB,MAAM,IAAI,MACR,8PAIF;CAIF,OAAO;AACT;;;;;AAMA,SAAgB,oBAAoB,QAAkC;CACpE,MAAM,EAAE,UAAU,GAAG,GAAG,WAAW;CAEnC,IAAI,OAAO,YACT,OAAO,aAAa,OAAO,YACzB,OAAO,QAAQ,OAAO,UAAU,EAAE,KAAK,CAAC,KAAK,UAAU;EACrD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,GAAG;GACrE,MAAM,IAAI;GACV,OAAO,CAAC,KAAK,EAAE,cAAc,OAAO,oBAAoB,CAAC,IAAI,IAAI;EACnE;EACA,OAAO,CAAC,KAAK,IAAI;CACnB,CAAC,CACH;CAGF,OAAO;AACT;AAEA,SAAS,kBAAkB,OAAe,MAAqB;CAC7D,OACE,CAAC,KAAK,YACN,KAAK,SAAS,cACb,KAAK,SAAS,cAAc,KAAK,YAAY,KAAA;AAElD;AAEA,SAAS,4BACP,MACgE;CAChE,OACE,KAAK,eAAe,KAAA,KAAa,CAAC,KAAK,yBAAyB;AAEpE;;;;;;;;;;AAWA,SAAgB,kBACd,OACA,UAAoC,CAAC,GACL;CAChC,IAAI,CAAC,OAAO,OAAO,CAAC;CAEpB,MAAM,SAAS,QAAQ,UAAU;CAEjC,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,EACjB,QAAQ,CAAC,MAAM,UAAU,OAAO,MAAM,IAAI,CAAC,EAC3C,QAEG,UAIG,4BAA4B,MAAM,EAAE,CAC3C,EACC,MAAM,CAAC,IAAI,CAAC,OAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAE,EAC/C,KAAK,CAAC,MAAM,UAAU,CACrB,MACA;EACE,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;EACxD,YAAY,aAAa,KAAK,UAAU;EACxC,GAAI,KAAK,mBAAmB,EAC1B,iBAAiB,KAAK,gBACxB;CACF,CACF,CAAC,CACL;AACF"}
1
+ {"version":3,"file":"schema-utils.js","names":[],"sources":["../../../src/core/tool/schema-utils.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ProviderOptions, Tool } from \"./tool-types\";\n\n/**\n * Type for a tool definition with JSON Schema parameters.\n */\nexport type ToolJSONSchema = {\n description?: string;\n parameters: JSONSchema7;\n providerOptions?: ProviderOptions;\n};\n\nexport type ToToolsJSONSchemaOptions = {\n /**\n * Filter to determine which tools to include.\n * Defaults to excluding disabled tools and backend tools.\n *\n * Tools with backend-default parameters are always excluded.\n */\n filter?: (name: string, tool: Tool) => boolean;\n};\n\nfunction isStandardSchema(schema: unknown): schema is StandardSchemaV1 & {\n \"~standard\": StandardSchemaV1[\"~standard\"] & {\n toJSONSchema?: () => unknown;\n jsonSchema?: { input?: () => unknown; output?: () => unknown };\n };\n} {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema &&\n typeof (schema as StandardSchemaV1)[\"~standard\"] === \"object\"\n );\n}\n\nfunction hasToJSONSchemaMethod(\n schema: unknown,\n): schema is { toJSONSchema: () => unknown } {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"toJSONSchema\" in schema &&\n typeof (schema as { toJSONSchema: unknown }).toJSONSchema === \"function\"\n );\n}\n\nfunction hasToJSONMethod(schema: unknown): schema is { toJSON: () => unknown } {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"toJSON\" in schema &&\n typeof (schema as { toJSON: unknown }).toJSON === \"function\"\n );\n}\n\n/**\n * Converts a schema to JSONSchema7.\n * Supports:\n * - StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n * - StandardSchemaV1 with ~standard.jsonSchema.input() (e.g., Zod v4)\n * - Objects with toJSONSchema() method (e.g., Zod v4)\n * - Objects with toJSON() method\n * - Plain JSONSchema7 objects (must have a \"type\" property)\n */\nexport function toJSONSchema(\n schema: StandardSchemaV1 | JSONSchema7,\n): JSONSchema7 {\n // StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n if (isStandardSchema(schema)) {\n const toJSONSchemaMethod = schema[\"~standard\"].toJSONSchema;\n if (typeof toJSONSchemaMethod === \"function\") {\n return toJSONSchemaMethod() as JSONSchema7;\n }\n\n // StandardSchemaV1 with ~standard.jsonSchema.input()\n const jsonSchema = schema[\"~standard\"].jsonSchema;\n if (\n typeof jsonSchema === \"object\" &&\n jsonSchema !== null &&\n typeof jsonSchema.input === \"function\"\n ) {\n return jsonSchema.input() as JSONSchema7;\n }\n }\n\n // toJSONSchema method on the schema itself\n if (hasToJSONSchemaMethod(schema)) {\n return schema.toJSONSchema() as JSONSchema7;\n }\n\n // toJSON method on the schema\n if (hasToJSONMethod(schema)) {\n return schema.toJSON() as JSONSchema7;\n }\n\n // If it's a Standard Schema that we couldn't convert, throw a helpful error\n if (isStandardSchema(schema)) {\n throw new Error(\n \"Could not convert schema to JSON Schema. \" +\n \"The schema implements Standard Schema but does not support JSON Schema conversion. \" +\n \"If you are using Zod, please upgrade to Zod v4 (npm install zod@latest). \" +\n \"Alternatively, pass a plain JSON Schema object instead.\",\n );\n }\n\n // Already a plain JSONSchema7\n return schema as JSONSchema7;\n}\n\n/**\n * Returns a copy of the JSON Schema with `required` removed recursively,\n * making every property optional. Array item schemas are left unchanged.\n */\nexport function toPartialJSONSchema(schema: JSONSchema7): JSONSchema7 {\n const { required: _, ...result } = schema;\n\n if (result.properties) {\n result.properties = Object.fromEntries(\n Object.entries(result.properties).map(([key, prop]) => {\n if (typeof prop === \"object\" && prop !== null && !Array.isArray(prop)) {\n const p = prop as JSONSchema7;\n return [key, p.properties != null ? toPartialJSONSchema(p) : prop];\n }\n return [key, prop];\n }),\n );\n }\n\n return result;\n}\n\nfunction defaultToolFilter(_name: string, tool: Tool): boolean {\n return (\n !tool.disabled &&\n tool.type !== \"backend\" &&\n (tool.type !== \"frontend\" || tool.execute !== undefined)\n );\n}\n\nfunction toolHasUploadableParameters(\n tool: Tool,\n): tool is Tool & { parameters: NonNullable<Tool[\"parameters\"]> } {\n return (\n tool.parameters !== undefined && !tool.unstable_backendDefault?.parameters\n );\n}\n\n/**\n * Converts a record of tools to a record of tool definitions with JSON Schema parameters.\n * By default, filters out disabled tools and backend tools.\n *\n * Entries are emitted in alphabetical order so the resulting request body is\n * byte-identical regardless of the order in which tools were registered. This\n * keeps provider prompt caches stable across renders that mount tools in\n * different orders.\n */\nexport function toToolsJSONSchema(\n tools: Record<string, Tool> | undefined,\n options: ToToolsJSONSchemaOptions = {},\n): Record<string, ToolJSONSchema> {\n if (!tools) return {};\n\n const filter = options.filter ?? defaultToolFilter;\n\n return Object.fromEntries(\n Object.entries(tools)\n .filter(([name, tool]) => filter(name, tool))\n .filter(\n (\n entry,\n ): entry is [\n string,\n Tool & { parameters: NonNullable<Tool[\"parameters\"]> },\n ] => toolHasUploadableParameters(entry[1]),\n )\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([name, tool]) => [\n name,\n {\n ...(tool.description && { description: tool.description }),\n parameters: toJSONSchema(tool.parameters),\n ...(tool.providerOptions && {\n providerOptions: tool.providerOptions,\n }),\n },\n ]),\n );\n}\n"],"mappings":";AAuBA,SAAS,iBAAiB,QAKxB;CACA,OACE,OAAO,WAAW,YAClB,WAAW,QACX,eAAe,UACf,OAAQ,OAA4B,iBAAiB;AAEzD;AAEA,SAAS,sBACP,QAC2C;CAC3C,OACE,OAAO,WAAW,YAClB,WAAW,QACX,kBAAkB,UAClB,OAAQ,OAAqC,iBAAiB;AAElE;AAEA,SAAS,gBAAgB,QAAsD;CAC7E,OACE,OAAO,WAAW,YAClB,WAAW,QACX,YAAY,UACZ,OAAQ,OAA+B,WAAW;AAEtD;;;;;;;;;;AAWA,SAAgB,aACd,QACa;CAEb,IAAI,iBAAiB,MAAM,GAAG;EAC5B,MAAM,qBAAqB,OAAO,YAAY,CAAC;EAC/C,IAAI,OAAO,uBAAuB,YAChC,OAAO,mBAAmB;EAI5B,MAAM,aAAa,OAAO,YAAY,CAAC;EACvC,IACE,OAAO,eAAe,YACtB,eAAe,QACf,OAAO,WAAW,UAAU,YAE5B,OAAO,WAAW,MAAM;CAE5B;CAGA,IAAI,sBAAsB,MAAM,GAC9B,OAAO,OAAO,aAAa;CAI7B,IAAI,gBAAgB,MAAM,GACxB,OAAO,OAAO,OAAO;CAIvB,IAAI,iBAAiB,MAAM,GACzB,MAAM,IAAI,MACR,8PAIF;CAIF,OAAO;AACT;;;;;AAMA,SAAgB,oBAAoB,QAAkC;CACpE,MAAM,EAAE,UAAU,GAAG,GAAG,WAAW;CAEnC,IAAI,OAAO,YACT,OAAO,aAAa,OAAO,YACzB,OAAO,QAAQ,OAAO,UAAU,CAAC,CAAC,KAAK,CAAC,KAAK,UAAU;EACrD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,GAAG;GACrE,MAAM,IAAI;GACV,OAAO,CAAC,KAAK,EAAE,cAAc,OAAO,oBAAoB,CAAC,IAAI,IAAI;EACnE;EACA,OAAO,CAAC,KAAK,IAAI;CACnB,CAAC,CACH;CAGF,OAAO;AACT;AAEA,SAAS,kBAAkB,OAAe,MAAqB;CAC7D,OACE,CAAC,KAAK,YACN,KAAK,SAAS,cACb,KAAK,SAAS,cAAc,KAAK,YAAY,KAAA;AAElD;AAEA,SAAS,4BACP,MACgE;CAChE,OACE,KAAK,eAAe,KAAA,KAAa,CAAC,KAAK,yBAAyB;AAEpE;;;;;;;;;;AAWA,SAAgB,kBACd,OACA,UAAoC,CAAC,GACL;CAChC,IAAI,CAAC,OAAO,OAAO,CAAC;CAEpB,MAAM,SAAS,QAAQ,UAAU;CAEjC,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAClB,QAAQ,CAAC,MAAM,UAAU,OAAO,MAAM,IAAI,CAAC,CAAC,CAC5C,QAEG,UAIG,4BAA4B,MAAM,EAAE,CAC3C,CAAC,CACA,MAAM,CAAC,IAAI,CAAC,OAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAE,CAAC,CAChD,KAAK,CAAC,MAAM,UAAU,CACrB,MACA;EACE,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;EACxD,YAAY,aAAa,KAAK,UAAU;EACxC,GAAI,KAAK,mBAAmB,EAC1B,iBAAiB,KAAK,gBACxB;CACF,CACF,CAAC,CACL;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-types.d.ts","names":[],"sources":["../../../src/core/tool/tool-types.ts"],"mappings":"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"}
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":"toolResultStream.js","names":[],"sources":["../../../src/core/tool/toolResultStream.ts"],"sourcesContent":["import type { Tool, ToolCallReader, ToolExecuteFunction } from \"./tool-types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { ToolResponse } from \"./ToolResponse\";\nimport { ToolExecutionStream } from \"./ToolExecutionStream\";\nimport type { AssistantMessage } from \"../utils/types\";\nimport type { ReadonlyJSONObject, ReadonlyJSONValue } from \"../../utils\";\n\nconst isStandardSchemaV1 = (\n schema: unknown,\n): schema is StandardSchemaV1<unknown> => {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema &&\n (schema as StandardSchemaV1<unknown>)[\"~standard\"].version === 1\n );\n};\n\nfunction getToolResponse(\n tools: Record<string, Tool> | undefined,\n abortSignal: AbortSignal,\n toolCall: {\n toolCallId: string;\n toolName: string;\n args: ReadonlyJSONObject;\n },\n human: (toolCallId: string, payload: unknown) => Promise<unknown>,\n) {\n const tool = tools?.[toolCall.toolName];\n if (!tool?.execute) return undefined;\n\n const getResult = async (\n toolExecute: ToolExecuteFunction<ReadonlyJSONObject, unknown>,\n ): Promise<ToolResponse<ReadonlyJSONValue>> => {\n // Check if already aborted before starting\n if (abortSignal.aborted) {\n return new ToolResponse({\n result: \"Tool execution was cancelled.\",\n isError: true,\n });\n }\n\n let executeFn = toolExecute;\n\n if (isStandardSchemaV1(tool.parameters)) {\n let result = tool.parameters[\"~standard\"].validate(toolCall.args);\n if (result instanceof Promise) result = await result;\n\n if (result.issues) {\n executeFn =\n tool.experimental_onSchemaValidationError ??\n (() => {\n throw new Error(\n `Function parameter validation failed. ${JSON.stringify(result.issues)}`,\n );\n });\n }\n }\n\n // Create abort promise that resolves after 2 microtasks\n // This gives tools that handle abort a chance to win the race\n const abortPromise = new Promise<ToolResponse<ReadonlyJSONValue>>(\n (resolve) => {\n const onAbort = () => {\n queueMicrotask(() => {\n queueMicrotask(() => {\n resolve(\n new ToolResponse({\n result: \"Tool execution was cancelled.\",\n isError: true,\n }),\n );\n });\n });\n };\n if (abortSignal.aborted) {\n onAbort();\n } else {\n abortSignal.addEventListener(\"abort\", onAbort, { once: true });\n }\n },\n );\n\n const executePromise = (async () => {\n const result = (await executeFn(toolCall.args, {\n toolCallId: toolCall.toolCallId,\n abortSignal,\n human: (payload: unknown) => human(toolCall.toolCallId, payload),\n })) as unknown as ReadonlyJSONValue;\n const response = ToolResponse.toResponse(result);\n if (\n tool.toModelOutput &&\n !response.isError &&\n response.modelContent === undefined\n ) {\n try {\n const modelContent = await tool.toModelOutput({\n toolCallId: toolCall.toolCallId,\n input: toolCall.args,\n output: response.result,\n });\n return new ToolResponse({\n result: response.result,\n artifact: response.artifact,\n isError: response.isError,\n messages: response.messages,\n modelContent,\n });\n } catch (e) {\n console.warn(\n `[assistant-stream] tool \"${toolCall.toolName}\" toModelOutput threw; falling back to default projection.`,\n e,\n );\n }\n }\n return response;\n })();\n\n return Promise.race([executePromise, abortPromise]);\n };\n\n return getResult(tool.execute);\n}\n\nfunction getToolStreamResponse(\n tools: Record<string, Tool> | undefined,\n abortSignal: AbortSignal,\n reader: ToolCallReader<any, ReadonlyJSONValue>,\n context: {\n toolCallId: string;\n toolName: string;\n },\n human: (toolCallId: string, payload: unknown) => Promise<unknown>,\n) {\n tools?.[context.toolName]?.streamCall?.(reader, {\n toolCallId: context.toolCallId,\n abortSignal,\n human: (payload: unknown) => human(context.toolCallId, payload),\n });\n}\n\nexport async function unstable_runPendingTools(\n message: AssistantMessage,\n tools: Record<string, Tool> | undefined,\n abortSignal: AbortSignal,\n human: (toolCallId: string, payload: unknown) => Promise<unknown>,\n) {\n const toolCallPromises = message.parts\n .filter((part) => part.type === \"tool-call\")\n .map(async (part) => {\n const promiseOrUndefined = getToolResponse(\n tools,\n abortSignal,\n part,\n human ??\n (async () => {\n throw new Error(\n \"Tool human input is not supported in this context\",\n );\n }),\n );\n if (promiseOrUndefined) {\n const result = await promiseOrUndefined;\n return {\n toolCallId: part.toolCallId,\n result,\n };\n }\n return null;\n });\n\n const toolCallResults = (await Promise.all(toolCallPromises)).filter(\n (result) => result !== null,\n ) as { toolCallId: string; result: ToolResponse<ReadonlyJSONValue> }[];\n\n if (toolCallResults.length === 0) {\n return message;\n }\n\n const toolCallResultsById = toolCallResults.reduce(\n (acc, { toolCallId, result }) => {\n acc[toolCallId] = result;\n return acc;\n },\n {} as Record<string, ToolResponse<ReadonlyJSONValue>>,\n );\n\n const updatedParts = message.parts.map((p) => {\n if (p.type === \"tool-call\") {\n const toolResponse = toolCallResultsById[p.toolCallId];\n if (toolResponse) {\n return {\n ...p,\n state: \"result\" as const,\n ...(toolResponse.artifact !== undefined\n ? { artifact: toolResponse.artifact }\n : {}),\n ...(toolResponse.modelContent !== undefined\n ? { modelContent: toolResponse.modelContent }\n : {}),\n result: toolResponse.result as ReadonlyJSONValue,\n isError: toolResponse.isError,\n };\n }\n }\n return p;\n });\n\n return {\n ...message,\n parts: updatedParts,\n content: updatedParts,\n };\n}\n\nexport type ToolResultStreamOptions = {\n /** Called immediately before a frontend tool's `execute` function runs. */\n onExecutionStart?: (toolCallId: string, toolName: string) => void;\n /** Called after frontend tool execution finishes or fails. */\n onExecutionEnd?: (toolCallId: string, toolName: string) => void;\n};\n\n/**\n * Transform stream that executes frontend tools and appends tool results.\n *\n * The transform watches streamed tool-call arguments, runs the matching\n * frontend tool once its arguments are complete, and emits a result chunk for\n * the tool call. Backend and human tools pass through according to their tool\n * definition.\n *\n * @param tools Tool registry or function returning the current registry.\n * @param abortSignal Signal, or signal getter, used for the current run.\n * @param human Callback used to resolve human-tool requests from UI input.\n * @param options Optional execution lifecycle callbacks.\n */\nexport function toolResultStream(\n tools:\n | Record<string, Tool>\n | (() => Record<string, Tool> | undefined)\n | undefined,\n abortSignal: AbortSignal | (() => AbortSignal),\n human: (toolCallId: string, payload: unknown) => Promise<unknown>,\n options?: ToolResultStreamOptions,\n) {\n const toolsFn = typeof tools === \"function\" ? tools : () => tools;\n const abortSignalFn =\n typeof abortSignal === \"function\" ? abortSignal : () => abortSignal;\n return new ToolExecutionStream({\n execute: (toolCall) =>\n getToolResponse(toolsFn(), abortSignalFn(), toolCall, human),\n streamCall: ({ reader, ...context }) =>\n getToolStreamResponse(toolsFn(), abortSignalFn(), reader, context, human),\n onExecutionStart: options?.onExecutionStart,\n onExecutionEnd: options?.onExecutionEnd,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"toolResultStream.js","names":[],"sources":["../../../src/core/tool/toolResultStream.ts"],"sourcesContent":["import type { Tool, ToolCallReader, ToolExecuteFunction } from \"./tool-types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { ToolResponse } from \"./ToolResponse\";\nimport { ToolExecutionStream } from \"./ToolExecutionStream\";\nimport type { AssistantMessage } from \"../utils/types\";\nimport type { ReadonlyJSONObject, ReadonlyJSONValue } from \"../../utils\";\n\nconst isStandardSchemaV1 = (\n schema: unknown,\n): schema is StandardSchemaV1<unknown> => {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema &&\n (schema as StandardSchemaV1<unknown>)[\"~standard\"].version === 1\n );\n};\n\nfunction getToolResponse(\n tools: Record<string, Tool> | undefined,\n abortSignal: AbortSignal,\n toolCall: {\n toolCallId: string;\n toolName: string;\n args: ReadonlyJSONObject;\n },\n human: (toolCallId: string, payload: unknown) => Promise<unknown>,\n) {\n const tool = tools?.[toolCall.toolName];\n if (!tool?.execute) return undefined;\n\n const getResult = async (\n toolExecute: ToolExecuteFunction<ReadonlyJSONObject, unknown>,\n ): Promise<ToolResponse<ReadonlyJSONValue>> => {\n // Check if already aborted before starting\n if (abortSignal.aborted) {\n return new ToolResponse({\n result: \"Tool execution was cancelled.\",\n isError: true,\n });\n }\n\n let executeFn = toolExecute;\n\n if (isStandardSchemaV1(tool.parameters)) {\n let result = tool.parameters[\"~standard\"].validate(toolCall.args);\n if (result instanceof Promise) result = await result;\n\n if (result.issues) {\n executeFn =\n tool.experimental_onSchemaValidationError ??\n (() => {\n throw new Error(\n `Function parameter validation failed. ${JSON.stringify(result.issues)}`,\n );\n });\n }\n }\n\n // Create abort promise that resolves after 2 microtasks\n // This gives tools that handle abort a chance to win the race\n const abortPromise = new Promise<ToolResponse<ReadonlyJSONValue>>(\n (resolve) => {\n const onAbort = () => {\n queueMicrotask(() => {\n queueMicrotask(() => {\n resolve(\n new ToolResponse({\n result: \"Tool execution was cancelled.\",\n isError: true,\n }),\n );\n });\n });\n };\n if (abortSignal.aborted) {\n onAbort();\n } else {\n abortSignal.addEventListener(\"abort\", onAbort, { once: true });\n }\n },\n );\n\n const executePromise = (async () => {\n const result = (await executeFn(toolCall.args, {\n toolCallId: toolCall.toolCallId,\n abortSignal,\n human: (payload: unknown) => human(toolCall.toolCallId, payload),\n })) as unknown as ReadonlyJSONValue;\n const response = ToolResponse.toResponse(result);\n if (\n tool.toModelOutput &&\n !response.isError &&\n response.modelContent === undefined\n ) {\n try {\n const modelContent = await tool.toModelOutput({\n toolCallId: toolCall.toolCallId,\n input: toolCall.args,\n output: response.result,\n });\n return new ToolResponse({\n result: response.result,\n artifact: response.artifact,\n isError: response.isError,\n messages: response.messages,\n modelContent,\n });\n } catch (e) {\n console.warn(\n `[assistant-stream] tool \"${toolCall.toolName}\" toModelOutput threw; falling back to default projection.`,\n e,\n );\n }\n }\n return response;\n })();\n\n return Promise.race([executePromise, abortPromise]);\n };\n\n return getResult(tool.execute);\n}\n\nfunction getToolStreamResponse(\n tools: Record<string, Tool> | undefined,\n abortSignal: AbortSignal,\n reader: ToolCallReader<any, ReadonlyJSONValue>,\n context: {\n toolCallId: string;\n toolName: string;\n },\n human: (toolCallId: string, payload: unknown) => Promise<unknown>,\n) {\n tools?.[context.toolName]?.streamCall?.(reader, {\n toolCallId: context.toolCallId,\n abortSignal,\n human: (payload: unknown) => human(context.toolCallId, payload),\n });\n}\n\nexport async function unstable_runPendingTools(\n message: AssistantMessage,\n tools: Record<string, Tool> | undefined,\n abortSignal: AbortSignal,\n human: (toolCallId: string, payload: unknown) => Promise<unknown>,\n) {\n const toolCallPromises = message.parts\n .filter((part) => part.type === \"tool-call\")\n .map(async (part) => {\n const promiseOrUndefined = getToolResponse(\n tools,\n abortSignal,\n part,\n human ??\n (async () => {\n throw new Error(\n \"Tool human input is not supported in this context\",\n );\n }),\n );\n if (promiseOrUndefined) {\n const result = await promiseOrUndefined;\n return {\n toolCallId: part.toolCallId,\n result,\n };\n }\n return null;\n });\n\n const toolCallResults = (await Promise.all(toolCallPromises)).filter(\n (result) => result !== null,\n ) as { toolCallId: string; result: ToolResponse<ReadonlyJSONValue> }[];\n\n if (toolCallResults.length === 0) {\n return message;\n }\n\n const toolCallResultsById = toolCallResults.reduce(\n (acc, { toolCallId, result }) => {\n acc[toolCallId] = result;\n return acc;\n },\n {} as Record<string, ToolResponse<ReadonlyJSONValue>>,\n );\n\n const updatedParts = message.parts.map((p) => {\n if (p.type === \"tool-call\") {\n const toolResponse = toolCallResultsById[p.toolCallId];\n if (toolResponse) {\n return {\n ...p,\n state: \"result\" as const,\n ...(toolResponse.artifact !== undefined\n ? { artifact: toolResponse.artifact }\n : {}),\n ...(toolResponse.modelContent !== undefined\n ? { modelContent: toolResponse.modelContent }\n : {}),\n result: toolResponse.result as ReadonlyJSONValue,\n isError: toolResponse.isError,\n };\n }\n }\n return p;\n });\n\n return {\n ...message,\n parts: updatedParts,\n content: updatedParts,\n };\n}\n\nexport type ToolResultStreamOptions = {\n /** Called immediately before a frontend tool's `execute` function runs. */\n onExecutionStart?: (toolCallId: string, toolName: string) => void;\n /** Called after frontend tool execution finishes or fails. */\n onExecutionEnd?: (toolCallId: string, toolName: string) => void;\n};\n\n/**\n * Transform stream that executes frontend tools and appends tool results.\n *\n * The transform watches streamed tool-call arguments, runs the matching\n * frontend tool once its arguments are complete, and emits a result chunk for\n * the tool call. Backend and human tools pass through according to their tool\n * definition.\n *\n * @param tools Tool registry or function returning the current registry.\n * @param abortSignal Signal, or signal getter, used for the current run.\n * @param human Callback used to resolve human-tool requests from UI input.\n * @param options Optional execution lifecycle callbacks.\n */\nexport function toolResultStream(\n tools:\n | Record<string, Tool>\n | (() => Record<string, Tool> | undefined)\n | undefined,\n abortSignal: AbortSignal | (() => AbortSignal),\n human: (toolCallId: string, payload: unknown) => Promise<unknown>,\n options?: ToolResultStreamOptions,\n) {\n const toolsFn = typeof tools === \"function\" ? tools : () => tools;\n const abortSignalFn =\n typeof abortSignal === \"function\" ? abortSignal : () => abortSignal;\n return new ToolExecutionStream({\n execute: (toolCall) =>\n getToolResponse(toolsFn(), abortSignalFn(), toolCall, human),\n streamCall: ({ reader, ...context }) =>\n getToolStreamResponse(toolsFn(), abortSignalFn(), reader, context, human),\n onExecutionStart: options?.onExecutionStart,\n onExecutionEnd: options?.onExecutionEnd,\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantTransformStream.js","names":[],"sources":["../../../../src/core/utils/stream/AssistantTransformStream.ts"],"sourcesContent":["import type { AssistantStreamChunk } from \"../../AssistantStreamChunk\";\nimport {\n type AssistantStreamController,\n createAssistantStreamController,\n} from \"../../modules/assistant-stream\";\n\ntype AssistantTransformerFlushCallback = (\n controller: AssistantStreamController,\n) => void | PromiseLike<void>;\n\ntype AssistantTransformerStartCallback = (\n controller: AssistantStreamController,\n) => void | PromiseLike<void>;\n\ntype AssistantTransformerTransformCallback<I> = (\n chunk: I,\n controller: AssistantStreamController,\n) => void | PromiseLike<void>;\n\ntype AssistantTransformer<I> = {\n flush?: AssistantTransformerFlushCallback;\n start?: AssistantTransformerStartCallback;\n transform?: AssistantTransformerTransformCallback<I>;\n};\n\nexport class AssistantTransformStream<I> extends TransformStream<\n I,\n AssistantStreamChunk\n> {\n constructor(\n transformer: AssistantTransformer<I>,\n writableStrategy?: QueuingStrategy<I>,\n readableStrategy?: QueuingStrategy<AssistantStreamChunk>,\n ) {\n const [stream, runController] = createAssistantStreamController();\n\n let runPipeTask: Promise<void>;\n super(\n {\n start(controller) {\n runPipeTask = stream\n .pipeTo(\n new WritableStream({\n write(chunk) {\n controller.enqueue(chunk);\n },\n abort(reason?: any) {\n controller.error(reason);\n },\n close() {\n controller.terminate();\n },\n }),\n )\n .catch((error) => {\n controller.error(error);\n });\n\n return transformer.start?.(runController);\n },\n transform(chunk) {\n return transformer.transform?.(chunk, runController);\n },\n async flush() {\n await transformer.flush?.(runController);\n runController.close();\n await runPipeTask;\n },\n },\n writableStrategy,\n readableStrategy,\n );\n }\n}\n"],"mappings":";;AAyBA,IAAa,2BAAb,cAAiD,gBAG/C;CACA,YACE,aACA,kBACA,kBACA;EACA,MAAM,CAAC,QAAQ,iBAAiB,gCAAgC;EAEhE,IAAI;EACJ,MACE;GACE,MAAM,YAAY;IAChB,cAAc,OACX,OACC,IAAI,eAAe;KACjB,MAAM,OAAO;MACX,WAAW,QAAQ,KAAK;KAC1B;KACA,MAAM,QAAc;MAClB,WAAW,MAAM,MAAM;KACzB;KACA,QAAQ;MACN,WAAW,UAAU;KACvB;IACF,CAAC,CACH,EACC,OAAO,UAAU;KAChB,WAAW,MAAM,KAAK;IACxB,CAAC;IAEH,OAAO,YAAY,QAAQ,aAAa;GAC1C;GACA,UAAU,OAAO;IACf,OAAO,YAAY,YAAY,OAAO,aAAa;GACrD;GACA,MAAM,QAAQ;IACZ,MAAM,YAAY,QAAQ,aAAa;IACvC,cAAc,MAAM;IACpB,MAAM;GACR;EACF,GACA,kBACA,gBACF;CACF;AACF"}
1
+ {"version":3,"file":"AssistantTransformStream.js","names":[],"sources":["../../../../src/core/utils/stream/AssistantTransformStream.ts"],"sourcesContent":["import type { AssistantStreamChunk } from \"../../AssistantStreamChunk\";\nimport {\n type AssistantStreamController,\n createAssistantStreamController,\n} from \"../../modules/assistant-stream\";\n\ntype AssistantTransformerFlushCallback = (\n controller: AssistantStreamController,\n) => void | PromiseLike<void>;\n\ntype AssistantTransformerStartCallback = (\n controller: AssistantStreamController,\n) => void | PromiseLike<void>;\n\ntype AssistantTransformerTransformCallback<I> = (\n chunk: I,\n controller: AssistantStreamController,\n) => void | PromiseLike<void>;\n\ntype AssistantTransformer<I> = {\n flush?: AssistantTransformerFlushCallback;\n start?: AssistantTransformerStartCallback;\n transform?: AssistantTransformerTransformCallback<I>;\n};\n\nexport class AssistantTransformStream<I> extends TransformStream<\n I,\n AssistantStreamChunk\n> {\n constructor(\n transformer: AssistantTransformer<I>,\n writableStrategy?: QueuingStrategy<I>,\n readableStrategy?: QueuingStrategy<AssistantStreamChunk>,\n ) {\n const [stream, runController] = createAssistantStreamController();\n\n let runPipeTask: Promise<void>;\n super(\n {\n start(controller) {\n runPipeTask = stream\n .pipeTo(\n new WritableStream({\n write(chunk) {\n controller.enqueue(chunk);\n },\n abort(reason?: any) {\n controller.error(reason);\n },\n close() {\n controller.terminate();\n },\n }),\n )\n .catch((error) => {\n controller.error(error);\n });\n\n return transformer.start?.(runController);\n },\n transform(chunk) {\n return transformer.transform?.(chunk, runController);\n },\n async flush() {\n await transformer.flush?.(runController);\n runController.close();\n await runPipeTask;\n },\n },\n writableStrategy,\n readableStrategy,\n );\n }\n}\n"],"mappings":";;AAyBA,IAAa,2BAAb,cAAiD,gBAG/C;CACA,YACE,aACA,kBACA,kBACA;EACA,MAAM,CAAC,QAAQ,iBAAiB,gCAAgC;EAEhE,IAAI;EACJ,MACE;GACE,MAAM,YAAY;IAChB,cAAc,OACX,OACC,IAAI,eAAe;KACjB,MAAM,OAAO;MACX,WAAW,QAAQ,KAAK;KAC1B;KACA,MAAM,QAAc;MAClB,WAAW,MAAM,MAAM;KACzB;KACA,QAAQ;MACN,WAAW,UAAU;KACvB;IACF,CAAC,CACH,CAAC,CACA,OAAO,UAAU;KAChB,WAAW,MAAM,KAAK;IACxB,CAAC;IAEH,OAAO,YAAY,QAAQ,aAAa;GAC1C;GACA,UAAU,OAAO;IACf,OAAO,YAAY,YAAY,OAAO,aAAa;GACrD;GACA,MAAM,QAAQ;IACZ,MAAM,YAAY,QAAQ,aAAa;IACvC,cAAc,MAAM;IACpB,MAAM;GACR;EACF,GACA,kBACA,gBACF;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"SSE.js","names":[],"sources":["../../../../src/core/utils/stream/SSE.ts"],"sourcesContent":["import { PipeableTransformStream } from \"./PipeableTransformStream\";\nimport { LineDecoderStream } from \"./LineDecoderStream\";\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\ntype SSEEvent = {\n event: string;\n data: string;\n id?: string | undefined;\n retry?: number | undefined;\n};\n\nclass SSEEventStream extends TransformStream<string, SSEEvent> {\n constructor() {\n let eventBuffer: Partial<SSEEvent> = {};\n let dataLines: string[] = [];\n\n super({\n start() {\n eventBuffer = {};\n dataLines = [];\n },\n transform(line, controller) {\n if (line.startsWith(\":\")) return; // Ignore comments\n\n if (line === \"\") {\n if (dataLines.length > 0) {\n controller.enqueue({\n event: eventBuffer.event || \"message\",\n data: dataLines.join(\"\\n\"),\n id: eventBuffer.id,\n retry: eventBuffer.retry,\n });\n }\n eventBuffer = {};\n dataLines = [];\n return;\n }\n\n const [field, ...rest] = line.split(\":\");\n const value = rest.join(\":\").trimStart();\n\n switch (field) {\n case \"event\":\n eventBuffer.event = value;\n break;\n case \"data\":\n dataLines.push(value);\n break;\n case \"id\":\n eventBuffer.id = value;\n break;\n case \"retry\":\n eventBuffer.retry = Number(value);\n break;\n }\n },\n flush(controller) {\n if (dataLines.length > 0) {\n controller.enqueue({\n event: eventBuffer.event || \"message\",\n data: dataLines.join(\"\\n\"),\n id: eventBuffer.id,\n retry: eventBuffer.retry,\n });\n }\n },\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 LineDecoderStream())\n .pipeThrough(new SSEEventStream())\n .pipeThrough(\n new TransformStream<SSEEvent, 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":";;;AAGA,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,EACC,YAAY,IAAI,kBAAkB,CAAC,CACxC;CACF;AACF;AASA,IAAM,iBAAN,cAA6B,gBAAkC;CAC7D,cAAc;EACZ,IAAI,cAAiC,CAAC;EACtC,IAAI,YAAsB,CAAC;EAE3B,MAAM;GACJ,QAAQ;IACN,cAAc,CAAC;IACf,YAAY,CAAC;GACf;GACA,UAAU,MAAM,YAAY;IAC1B,IAAI,KAAK,WAAW,GAAG,GAAG;IAE1B,IAAI,SAAS,IAAI;KACf,IAAI,UAAU,SAAS,GACrB,WAAW,QAAQ;MACjB,OAAO,YAAY,SAAS;MAC5B,MAAM,UAAU,KAAK,IAAI;MACzB,IAAI,YAAY;MAChB,OAAO,YAAY;KACrB,CAAC;KAEH,cAAc,CAAC;KACf,YAAY,CAAC;KACb;IACF;IAEA,MAAM,CAAC,OAAO,GAAG,QAAQ,KAAK,MAAM,GAAG;IACvC,MAAM,QAAQ,KAAK,KAAK,GAAG,EAAE,UAAU;IAEvC,QAAQ,OAAR;KACE,KAAK;MACH,YAAY,QAAQ;MACpB;KACF,KAAK;MACH,UAAU,KAAK,KAAK;MACpB;KACF,KAAK;MACH,YAAY,KAAK;MACjB;KACF,KAAK;MACH,YAAY,QAAQ,OAAO,KAAK;MAChC;IACJ;GACF;GACA,MAAM,YAAY;IAChB,IAAI,UAAU,SAAS,GACrB,WAAW,QAAQ;KACjB,OAAO,YAAY,SAAS;KAC5B,MAAM,UAAU,KAAK,IAAI;KACzB,IAAI,YAAY;KAChB,OAAO,YAAY;IACrB,CAAC;GAEL;EACF,CAAC;CACH;AACF;AAEA,IAAa,aAAb,cAAmC,wBAGjC;CACA,cAAc;EACZ,OAAO,aACL,SACG,YAAY,IAAI,kBAAkB,CAAC,EACnC,YAAY,IAAI,kBAAkB,CAAC,EACnC,YAAY,IAAI,eAAe,CAAC,EAChC,YACC,IAAI,gBAA6B,EAC/B,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 { PipeableTransformStream } from \"./PipeableTransformStream\";\nimport { LineDecoderStream } from \"./LineDecoderStream\";\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\ntype SSEEvent = {\n event: string;\n data: string;\n id?: string | undefined;\n retry?: number | undefined;\n};\n\nclass SSEEventStream extends TransformStream<string, SSEEvent> {\n constructor() {\n let eventBuffer: Partial<SSEEvent> = {};\n let dataLines: string[] = [];\n\n super({\n start() {\n eventBuffer = {};\n dataLines = [];\n },\n transform(line, controller) {\n if (line.startsWith(\":\")) return; // Ignore comments\n\n if (line === \"\") {\n if (dataLines.length > 0) {\n controller.enqueue({\n event: eventBuffer.event || \"message\",\n data: dataLines.join(\"\\n\"),\n id: eventBuffer.id,\n retry: eventBuffer.retry,\n });\n }\n eventBuffer = {};\n dataLines = [];\n return;\n }\n\n const [field, ...rest] = line.split(\":\");\n const value = rest.join(\":\").trimStart();\n\n switch (field) {\n case \"event\":\n eventBuffer.event = value;\n break;\n case \"data\":\n dataLines.push(value);\n break;\n case \"id\":\n eventBuffer.id = value;\n break;\n case \"retry\":\n eventBuffer.retry = Number(value);\n break;\n }\n },\n flush(controller) {\n if (dataLines.length > 0) {\n controller.enqueue({\n event: eventBuffer.event || \"message\",\n data: dataLines.join(\"\\n\"),\n id: eventBuffer.id,\n retry: eventBuffer.retry,\n });\n }\n },\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 LineDecoderStream())\n .pipeThrough(new SSEEventStream())\n .pipeThrough(\n new TransformStream<SSEEvent, 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":";;;AAGA,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;AASA,IAAM,iBAAN,cAA6B,gBAAkC;CAC7D,cAAc;EACZ,IAAI,cAAiC,CAAC;EACtC,IAAI,YAAsB,CAAC;EAE3B,MAAM;GACJ,QAAQ;IACN,cAAc,CAAC;IACf,YAAY,CAAC;GACf;GACA,UAAU,MAAM,YAAY;IAC1B,IAAI,KAAK,WAAW,GAAG,GAAG;IAE1B,IAAI,SAAS,IAAI;KACf,IAAI,UAAU,SAAS,GACrB,WAAW,QAAQ;MACjB,OAAO,YAAY,SAAS;MAC5B,MAAM,UAAU,KAAK,IAAI;MACzB,IAAI,YAAY;MAChB,OAAO,YAAY;KACrB,CAAC;KAEH,cAAc,CAAC;KACf,YAAY,CAAC;KACb;IACF;IAEA,MAAM,CAAC,OAAO,GAAG,QAAQ,KAAK,MAAM,GAAG;IACvC,MAAM,QAAQ,KAAK,KAAK,GAAG,CAAC,CAAC,UAAU;IAEvC,QAAQ,OAAR;KACE,KAAK;MACH,YAAY,QAAQ;MACpB;KACF,KAAK;MACH,UAAU,KAAK,KAAK;MACpB;KACF,KAAK;MACH,YAAY,KAAK;MACjB;KACF,KAAK;MACH,YAAY,QAAQ,OAAO,KAAK;MAChC;IACJ;GACF;GACA,MAAM,YAAY;IAChB,IAAI,UAAU,SAAS,GACrB,WAAW,QAAQ;KACjB,OAAO,YAAY,SAAS;KAC5B,MAAM,UAAU,KAAK,IAAI;KACzB,IAAI,YAAY;KAChB,OAAO,YAAY;IACrB,CAAC;GAEL;EACF,CAAC;CACH;AACF;AAEA,IAAa,aAAb,cAAmC,wBAGjC;CACA,cAAc;EACZ,OAAO,aACL,SACG,YAAY,IAAI,kBAAkB,CAAC,CAAC,CACpC,YAAY,IAAI,kBAAkB,CAAC,CAAC,CACpC,YAAY,IAAI,eAAe,CAAC,CAAC,CACjC,YACC,IAAI,gBAA6B,EAC/B,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 +1 @@
1
- {"version":3,"file":"merge.js","names":[],"sources":["../../../../src/core/utils/stream/merge.ts"],"sourcesContent":["import type { AssistantStreamChunk } from \"../../AssistantStreamChunk\";\nimport { promiseWithResolvers } from \"../../../utils/promiseWithResolvers\";\n\ntype MergeStreamItem = {\n reader: ReadableStreamDefaultReader<AssistantStreamChunk>;\n promise?: Promise<unknown> | undefined;\n};\n\nexport const createMergeStream = () => {\n const list: MergeStreamItem[] = [];\n let sealed = false;\n let controller: ReadableStreamDefaultController<AssistantStreamChunk>;\n let currentPull: ReturnType<typeof promiseWithResolvers<void>> | undefined;\n\n const handlePull = (item: MergeStreamItem) => {\n if (!item.promise) {\n // TODO for most streams, we can directly pipeTo to avoid the microTask queue\n // add an option to eagerly pipe the stream to the merge stream\n // ideally, using assistant-stream w sync run method + piping to a sync WritableStream runs in the same microtask\n // this is useful because we often use AssistantStreams internally as a serialization utility, e. g. AssistantTransformStream\n // idea: avoid reader.read() by instead using a WritableStream & if (!hasPendingPull) await waitForPull()?\n item.promise = item.reader\n .read()\n .then(({ done, value }) => {\n item.promise = undefined;\n if (done) {\n list.splice(list.indexOf(item), 1);\n if (sealed && list.length === 0) {\n controller.close();\n }\n } else {\n controller.enqueue(value);\n }\n\n currentPull?.resolve();\n currentPull = undefined;\n })\n .catch((e) => {\n console.error(e);\n\n list.forEach((item) => {\n item.reader.cancel();\n });\n list.length = 0;\n\n controller.error(e);\n\n currentPull?.reject(e);\n currentPull = undefined;\n });\n }\n };\n\n const readable = new ReadableStream<AssistantStreamChunk>({\n start(c) {\n controller = c;\n },\n pull() {\n currentPull = promiseWithResolvers();\n list.forEach((item) => {\n handlePull(item);\n });\n\n return currentPull.promise;\n },\n cancel() {\n list.forEach((item) => {\n item.reader.cancel();\n });\n list.length = 0;\n },\n });\n\n return {\n readable,\n isSealed() {\n return sealed;\n },\n seal() {\n sealed = true;\n if (list.length === 0) controller.close();\n },\n addStream(stream: ReadableStream<AssistantStreamChunk>) {\n if (sealed)\n throw new Error(\n \"Cannot add streams after the run callback has settled.\",\n );\n\n const item = { reader: stream.getReader() };\n list.push(item);\n handlePull(item);\n },\n enqueue(chunk: AssistantStreamChunk) {\n this.addStream(\n new ReadableStream({\n start(c) {\n c.enqueue(chunk);\n c.close();\n },\n }),\n );\n },\n };\n};\n\n// TODO\n// export class SpanContainerMerger {\n// public get isSealed() {\n// return this.mergeStream.isSealed();\n// }\n\n// public get readable() {\n// return this.mergeStream.readable;\n// }\n\n// private subAllocator = new Counter();\n// private mergeStream = createMergeStream();\n\n// constructor() {\n// // id 0 is auto allocated\n// this.subAllocator.up();\n// }\n\n// add(stream: ReadableStream<AssistantStreamChunk>) {\n// this.mergeStream.addStream(\n// stream.pipeThrough(new SpanParentEncoder(this.subAllocator)),\n// );\n// }\n\n// enqueue(chunk: AssistantStreamChunk & { parentId: 0 }) {\n// this.mergeStream.addStream(\n// new ReadableStream({\n// start(c) {\n// c.enqueue(chunk);\n// c.close();\n// },\n// }),\n// );\n// }\n\n// seal() {\n// this.mergeStream.seal();\n// }\n// }\n\n// export class SpanContainerSplitter {\n// public writable;\n\n// private isSealed = false;\n// private writers = new Map<\n// number,\n// WritableStreamDefaultWriter<AssistantStreamChunk>\n// >();\n\n// private closeTasks: Promise<void>[] = [];\n\n// private allocator = new Counter();\n// private subAllocator = new Counter();\n\n// constructor() {\n// // id 0 is auto-allocated\n// this.allocator.up();\n\n// this.writable = new WritableStream({\n// write: (chunk) => {\n// const { type, parentId } = chunk;\n\n// const writer = this.writers.get(parentId);\n// if (writer === undefined) throw new Error(\"Parent id not found\");\n\n// writer.write(chunk);\n\n// if (type === \"span\") {\n// // allocate a new span id\n// this.writers.set(this.allocator.up(), writer);\n// }\n// if (type === \"finish\") {\n// this.writers.delete(parentId);\n// writer.close();\n\n// if (this.writers.size === 0) {\n// const closeTask = this.writable.close();\n// this.closeTasks.push(closeTask);\n// closeTask.then(() => {\n// this.closeTasks.splice(this.closeTasks.indexOf(closeTask), 1);\n// });\n// }\n// }\n// },\n// close: async () => {\n// if (this.writers.size > 0) throw new Error(\"Not all writers closed\");\n\n// // await and throw on any errors\n// await Promise.all(this.closeTasks);\n// },\n// });\n// }\n\n// add(stream: WritableStream<AssistantStreamChunk>) {\n// if (this.isSealed) throw new Error(\"Cannot add streams after sealing\");\n\n// const decoder = new SpanParentDecoder(this.subAllocator);\n// decoder.readable.pipeTo(stream);\n\n// this.writers.set(this.allocator.up(), decoder.writable.getWriter());\n// }\n\n// seal() {\n// this.isSealed = true;\n// if (this.writers.size === 0) this.writable.close();\n// }\n// }\n"],"mappings":";;AAQA,MAAa,0BAA0B;CACrC,MAAM,OAA0B,CAAC;CACjC,IAAI,SAAS;CACb,IAAI;CACJ,IAAI;CAEJ,MAAM,cAAc,SAA0B;EAC5C,IAAI,CAAC,KAAK,SAMR,KAAK,UAAU,KAAK,OACjB,KAAK,EACL,MAAM,EAAE,MAAM,YAAY;GACzB,KAAK,UAAU,KAAA;GACf,IAAI,MAAM;IACR,KAAK,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC;IACjC,IAAI,UAAU,KAAK,WAAW,GAC5B,WAAW,MAAM;GAErB,OACE,WAAW,QAAQ,KAAK;GAG1B,aAAa,QAAQ;GACrB,cAAc,KAAA;EAChB,CAAC,EACA,OAAO,MAAM;GACZ,QAAQ,MAAM,CAAC;GAEf,KAAK,SAAS,SAAS;IACrB,KAAK,OAAO,OAAO;GACrB,CAAC;GACD,KAAK,SAAS;GAEd,WAAW,MAAM,CAAC;GAElB,aAAa,OAAO,CAAC;GACrB,cAAc,KAAA;EAChB,CAAC;CAEP;CAsBA,OAAO;EACL,UAAA,IArBmB,eAAqC;GACxD,MAAM,GAAG;IACP,aAAa;GACf;GACA,OAAO;IACL,cAAc,qBAAqB;IACnC,KAAK,SAAS,SAAS;KACrB,WAAW,IAAI;IACjB,CAAC;IAED,OAAO,YAAY;GACrB;GACA,SAAS;IACP,KAAK,SAAS,SAAS;KACrB,KAAK,OAAO,OAAO;IACrB,CAAC;IACD,KAAK,SAAS;GAChB;EACF,CAGS;EACP,WAAW;GACT,OAAO;EACT;EACA,OAAO;GACL,SAAS;GACT,IAAI,KAAK,WAAW,GAAG,WAAW,MAAM;EAC1C;EACA,UAAU,QAA8C;GACtD,IAAI,QACF,MAAM,IAAI,MACR,wDACF;GAEF,MAAM,OAAO,EAAE,QAAQ,OAAO,UAAU,EAAE;GAC1C,KAAK,KAAK,IAAI;GACd,WAAW,IAAI;EACjB;EACA,QAAQ,OAA6B;GACnC,KAAK,UACH,IAAI,eAAe,EACjB,MAAM,GAAG;IACP,EAAE,QAAQ,KAAK;IACf,EAAE,MAAM;GACV,EACF,CAAC,CACH;EACF;CACF;AACF"}
1
+ {"version":3,"file":"merge.js","names":[],"sources":["../../../../src/core/utils/stream/merge.ts"],"sourcesContent":["import type { AssistantStreamChunk } from \"../../AssistantStreamChunk\";\nimport { promiseWithResolvers } from \"../../../utils/promiseWithResolvers\";\n\ntype MergeStreamItem = {\n reader: ReadableStreamDefaultReader<AssistantStreamChunk>;\n promise?: Promise<unknown> | undefined;\n};\n\nexport const createMergeStream = () => {\n const list: MergeStreamItem[] = [];\n let sealed = false;\n let controller: ReadableStreamDefaultController<AssistantStreamChunk>;\n let currentPull: ReturnType<typeof promiseWithResolvers<void>> | undefined;\n\n const handlePull = (item: MergeStreamItem) => {\n if (!item.promise) {\n // TODO for most streams, we can directly pipeTo to avoid the microTask queue\n // add an option to eagerly pipe the stream to the merge stream\n // ideally, using assistant-stream w sync run method + piping to a sync WritableStream runs in the same microtask\n // this is useful because we often use AssistantStreams internally as a serialization utility, e. g. AssistantTransformStream\n // idea: avoid reader.read() by instead using a WritableStream & if (!hasPendingPull) await waitForPull()?\n item.promise = item.reader\n .read()\n .then(({ done, value }) => {\n item.promise = undefined;\n if (done) {\n list.splice(list.indexOf(item), 1);\n if (sealed && list.length === 0) {\n controller.close();\n }\n } else {\n controller.enqueue(value);\n }\n\n currentPull?.resolve();\n currentPull = undefined;\n })\n .catch((e) => {\n console.error(e);\n\n list.forEach((item) => {\n item.reader.cancel();\n });\n list.length = 0;\n\n controller.error(e);\n\n currentPull?.reject(e);\n currentPull = undefined;\n });\n }\n };\n\n const readable = new ReadableStream<AssistantStreamChunk>({\n start(c) {\n controller = c;\n },\n pull() {\n currentPull = promiseWithResolvers();\n list.forEach((item) => {\n handlePull(item);\n });\n\n return currentPull.promise;\n },\n cancel() {\n list.forEach((item) => {\n item.reader.cancel();\n });\n list.length = 0;\n },\n });\n\n return {\n readable,\n isSealed() {\n return sealed;\n },\n seal() {\n sealed = true;\n if (list.length === 0) controller.close();\n },\n addStream(stream: ReadableStream<AssistantStreamChunk>) {\n if (sealed)\n throw new Error(\n \"Cannot add streams after the run callback has settled.\",\n );\n\n const item = { reader: stream.getReader() };\n list.push(item);\n handlePull(item);\n },\n enqueue(chunk: AssistantStreamChunk) {\n this.addStream(\n new ReadableStream({\n start(c) {\n c.enqueue(chunk);\n c.close();\n },\n }),\n );\n },\n };\n};\n\n// TODO\n// export class SpanContainerMerger {\n// public get isSealed() {\n// return this.mergeStream.isSealed();\n// }\n\n// public get readable() {\n// return this.mergeStream.readable;\n// }\n\n// private subAllocator = new Counter();\n// private mergeStream = createMergeStream();\n\n// constructor() {\n// // id 0 is auto allocated\n// this.subAllocator.up();\n// }\n\n// add(stream: ReadableStream<AssistantStreamChunk>) {\n// this.mergeStream.addStream(\n// stream.pipeThrough(new SpanParentEncoder(this.subAllocator)),\n// );\n// }\n\n// enqueue(chunk: AssistantStreamChunk & { parentId: 0 }) {\n// this.mergeStream.addStream(\n// new ReadableStream({\n// start(c) {\n// c.enqueue(chunk);\n// c.close();\n// },\n// }),\n// );\n// }\n\n// seal() {\n// this.mergeStream.seal();\n// }\n// }\n\n// export class SpanContainerSplitter {\n// public writable;\n\n// private isSealed = false;\n// private writers = new Map<\n// number,\n// WritableStreamDefaultWriter<AssistantStreamChunk>\n// >();\n\n// private closeTasks: Promise<void>[] = [];\n\n// private allocator = new Counter();\n// private subAllocator = new Counter();\n\n// constructor() {\n// // id 0 is auto-allocated\n// this.allocator.up();\n\n// this.writable = new WritableStream({\n// write: (chunk) => {\n// const { type, parentId } = chunk;\n\n// const writer = this.writers.get(parentId);\n// if (writer === undefined) throw new Error(\"Parent id not found\");\n\n// writer.write(chunk);\n\n// if (type === \"span\") {\n// // allocate a new span id\n// this.writers.set(this.allocator.up(), writer);\n// }\n// if (type === \"finish\") {\n// this.writers.delete(parentId);\n// writer.close();\n\n// if (this.writers.size === 0) {\n// const closeTask = this.writable.close();\n// this.closeTasks.push(closeTask);\n// closeTask.then(() => {\n// this.closeTasks.splice(this.closeTasks.indexOf(closeTask), 1);\n// });\n// }\n// }\n// },\n// close: async () => {\n// if (this.writers.size > 0) throw new Error(\"Not all writers closed\");\n\n// // await and throw on any errors\n// await Promise.all(this.closeTasks);\n// },\n// });\n// }\n\n// add(stream: WritableStream<AssistantStreamChunk>) {\n// if (this.isSealed) throw new Error(\"Cannot add streams after sealing\");\n\n// const decoder = new SpanParentDecoder(this.subAllocator);\n// decoder.readable.pipeTo(stream);\n\n// this.writers.set(this.allocator.up(), decoder.writable.getWriter());\n// }\n\n// seal() {\n// this.isSealed = true;\n// if (this.writers.size === 0) this.writable.close();\n// }\n// }\n"],"mappings":";;AAQA,MAAa,0BAA0B;CACrC,MAAM,OAA0B,CAAC;CACjC,IAAI,SAAS;CACb,IAAI;CACJ,IAAI;CAEJ,MAAM,cAAc,SAA0B;EAC5C,IAAI,CAAC,KAAK,SAMR,KAAK,UAAU,KAAK,OACjB,KAAK,CAAC,CACN,MAAM,EAAE,MAAM,YAAY;GACzB,KAAK,UAAU,KAAA;GACf,IAAI,MAAM;IACR,KAAK,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC;IACjC,IAAI,UAAU,KAAK,WAAW,GAC5B,WAAW,MAAM;GAErB,OACE,WAAW,QAAQ,KAAK;GAG1B,aAAa,QAAQ;GACrB,cAAc,KAAA;EAChB,CAAC,CAAC,CACD,OAAO,MAAM;GACZ,QAAQ,MAAM,CAAC;GAEf,KAAK,SAAS,SAAS;IACrB,KAAK,OAAO,OAAO;GACrB,CAAC;GACD,KAAK,SAAS;GAEd,WAAW,MAAM,CAAC;GAElB,aAAa,OAAO,CAAC;GACrB,cAAc,KAAA;EAChB,CAAC;CAEP;CAsBA,OAAO;EACL,UAAA,IArBmB,eAAqC;GACxD,MAAM,GAAG;IACP,aAAa;GACf;GACA,OAAO;IACL,cAAc,qBAAqB;IACnC,KAAK,SAAS,SAAS;KACrB,WAAW,IAAI;IACjB,CAAC;IAED,OAAO,YAAY;GACrB;GACA,SAAS;IACP,KAAK,SAAS,SAAS;KACrB,KAAK,OAAO,OAAO;IACrB,CAAC;IACD,KAAK,SAAS;GAChB;EACF,CAGS;EACP,WAAW;GACT,OAAO;EACT;EACA,OAAO;GACL,SAAS;GACT,IAAI,KAAK,WAAW,GAAG,WAAW,MAAM;EAC1C;EACA,UAAU,QAA8C;GACtD,IAAI,QACF,MAAM,IAAI,MACR,wDACF;GAEF,MAAM,OAAO,EAAE,QAAQ,OAAO,UAAU,EAAE;GAC1C,KAAK,KAAK,IAAI;GACd,WAAW,IAAI;EACjB;EACA,QAAQ,OAA6B;GACnC,KAAK,UACH,IAAI,eAAe,EACjB,MAAM,GAAG;IACP,EAAE,QAAQ,KAAK;IACf,EAAE,MAAM;GACV,EACF,CAAC,CACH;EACF;CACF;AACF"}
@@ -36,6 +36,16 @@ type ToolCallStatus = {
36
36
  type: "incomplete";
37
37
  reason: "cancelled" | "length" | "content-filter" | "other";
38
38
  };
39
+ /**
40
+ * Wall-clock timing of a tool call. Accumulator-populated timings are
41
+ * measured by the consuming accumulator, so resumed or replayed streams
42
+ * re-measure them; hosts that need authoritative timings supply the field
43
+ * themselves.
44
+ */
45
+ type ToolCallTiming = {
46
+ /** Epoch milliseconds when the tool call started streaming or executing. */readonly startedAt: number; /** Epoch milliseconds when the result landed. Absent while the call runs. */
47
+ readonly completedAt?: number;
48
+ };
39
49
  type ToolCallPartBase = {
40
50
  type: "tool-call";
41
51
  status: ToolCallStatus;
@@ -43,6 +53,7 @@ type ToolCallPartBase = {
43
53
  toolName: string;
44
54
  argsText: string;
45
55
  args: ReadonlyJSONObject;
56
+ timing?: ToolCallTiming;
46
57
  artifact?: ReadonlyJSONValue;
47
58
  result?: ReadonlyJSONValue;
48
59
  modelContent?: readonly ToolModelContentPart[];
@@ -137,5 +148,5 @@ type AssistantMessage = {
137
148
  };
138
149
  };
139
150
  //#endregion
140
- export { AssistantMessage, AssistantMessagePart, AssistantMessageStatus, AssistantMessageTiming, DataPart, FilePart, ReasoningPart, SourcePart, TextPart, ToolCallPart };
151
+ export { AssistantMessage, AssistantMessagePart, AssistantMessageStatus, AssistantMessageTiming, DataPart, FilePart, ReasoningPart, SourcePart, TextPart, ToolCallPart, ToolCallTiming };
141
152
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/utils/types.ts"],"mappings":";;;;KAMK,UAAA;EAEC,IAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;AAAA,KAOM,QAAA;EACV,IAAA;EACA,IAAA;EACA,MAAA,EAAQ,UAAU;EAClB,QAAA;AAAA;AAAA,KAGU,aAAA;EACV,IAAA;EACA,IAAA;EACA,MAAA,EAAQ,UAAU;EAClB,QAAA;AAAA;AAAA,KAGG,cAAA;EAEC,IAAA;EACA,cAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;AAAA,KAGD,gBAAA;EACH,IAAA;EACA,MAAA,EAAQ,cAAA;EACR,UAAA;EACA,QAAA;EACA,QAAA;EACA,IAAA,EAAM,kBAAA;EACN,QAAA,GAAW,iBAAA;EACX,MAAA,GAAS,iBAAA;EACT,YAAA,YAAwB,oBAAA;EACxB,OAAA;EACA,QAAA;AAAA;AAAA,KAGG,yBAAA,GAA4B,gBAAgB;EAC/C,KAAA;EACA,MAAA;EACA,YAAA;AAAA;AAAA,KAGG,sBAAA,GAAyB,gBAAA;EAC5B,KAAA;EACA,MAAA,EAAQ,iBAAA;EACR,QAAA,GAAW,iBAAA;EACX,YAAA,YAAwB,oBAAA;EACxB,OAAA;AAAA;AAAA,KAGU,YAAA,GAAe,yBAAA,GAA4B,sBAAsB;AAAA,KAEjE,UAAA;EACV,IAAA;EACA,UAAA;EACA,EAAA;EACA,GAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,KAGU,QAAA;EACV,IAAA;EACA,IAAA;EACA,QAAA;EACA,QAAA;AAAA;AAAA,KAGU,QAAA;EACV,IAAA;EACA,IAAA;EACA,IAAA,EAAM,iBAAiB;EACvB,QAAA;AAAA;AAAA,KAGU,oBAAA,GACR,QAAA,GACA,aAAA,GACA,YAAA,GACA,UAAA,GACA,QAAA,GACA,QAAA;AAAA,KAEC,yBAAA;EACH,WAAA;EACA,YAAY;AAAA;AAAA,KAGT,4BAAA;EAEC,KAAA;EACA,SAAA;AAAA;EAGA,KAAA;EACA,SAAA;EACA,YAAA;EAQA,KAAA,GAAQ,yBAAyB;EACjC,WAAA;AAAA;AAAA,KAGM,sBAAA;EAEN,IAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;EAOA,KAAA,GAAQ,iBAAiB;AAAA;AAAA,KAGnB,sBAAA;2DAEV,eAAA,UApF2E;EAsF3E,cAAA,WApFoB;EAsFpB,eAAA,WAtFoB;EAwFpB,UAAA,WAtFA;EAwFA,eAAA,WAtFA;EAwFA,WAAA,UAtFA;EAwFA,aAAA;AAAA;AAAA,KAGU,gBAAA;EACV,IAAA;EACA,MAAA,EAAQ,sBAAA;EACR,KAAA,EAAO,oBAAA;EA1FP;;;EA8FA,OAAA,EAAS,oBAAA;EAET,QAAA;IACE,cAAA,EAAgB,iBAAA;IAChB,aAAA,EAAe,iBAAA;IACf,oBAAA,EAAsB,iBAAA;IACtB,KAAA,EAAO,4BAAA;IACP,MAAA,EAAQ,MAAA;IACR,MAAA,GAAS,sBAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/utils/types.ts"],"mappings":";;;;KAMK,UAAA;EAEC,IAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;AAAA,KAOM,QAAA;EACV,IAAA;EACA,IAAA;EACA,MAAA,EAAQ,UAAU;EAClB,QAAA;AAAA;AAAA,KAGU,aAAA;EACV,IAAA;EACA,IAAA;EACA,MAAA,EAAQ,UAAU;EAClB,QAAA;AAAA;AAAA,KAGG,cAAA;EAEC,IAAA;EACA,cAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;AAlBI;AACR;;;;;AADQ,KA2BE,cAAA;EAlBN,qFAoBK,SAAA,UAhBL;EAAA,SAkBK,WAAW;AAAA;AAAA,KAGjB,gBAAA;EACH,IAAA;EACA,MAAA,EAAQ,cAAA;EACR,UAAA;EACA,QAAA;EACA,QAAA;EACA,IAAA,EAAM,kBAAA;EACN,MAAA,GAAS,cAAA;EACT,QAAA,GAAW,iBAAA;EACX,MAAA,GAAS,iBAAA;EACT,YAAA,YAAwB,oBAAA;EACxB,OAAA;EACA,QAAA;AAAA;AAAA,KAGG,yBAAA,GAA4B,gBAAgB;EAC/C,KAAA;EACA,MAAA;EACA,YAAA;AAAA;AAAA,KAGG,sBAAA,GAAyB,gBAAA;EAC5B,KAAA;EACA,MAAA,EAAQ,iBAAA;EACR,QAAA,GAAW,iBAAA;EACX,YAAA,YAAwB,oBAAA;EACxB,OAAA;AAAA;AAAA,KAGU,YAAA,GAAe,yBAAA,GAA4B,sBAAsB;AAAA,KAEjE,UAAA;EACV,IAAA;EACA,UAAA;EACA,EAAA;EACA,GAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,KAGU,QAAA;EACV,IAAA;EACA,IAAA;EACA,QAAA;EACA,QAAA;AAAA;AAAA,KAGU,QAAA;EACV,IAAA;EACA,IAAA;EACA,IAAA,EAAM,iBAAiB;EACvB,QAAA;AAAA;AAAA,KAGU,oBAAA,GACR,QAAA,GACA,aAAA,GACA,YAAA,GACA,UAAA,GACA,QAAA,GACA,QAAA;AAAA,KAEC,yBAAA;EACH,WAAA;EACA,YAAY;AAAA;AAAA,KAGT,4BAAA;EAEC,KAAA;EACA,SAAA;AAAA;EAGA,KAAA;EACA,SAAA;EACA,YAAA;EAQA,KAAA,GAAQ,yBAAyB;EACjC,WAAA;AAAA;AAAA,KAGM,sBAAA;EAEN,IAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;AAAA;EAGA,IAAA;EACA,MAAA;EAOA,KAAA,GAAQ,iBAAiB;AAAA;AAAA,KAGnB,sBAAA;2DAEV,eAAA,UAjFA;EAmFA,cAAA,WAjFA;EAmFA,eAAA,WAjFA;EAmFA,UAAA,WAlFQ;EAoFR,eAAA,WAjFU;EAmFV,WAAA;EAEA,aAAA;AAAA;AAAA,KAGU,gBAAA;EACV,IAAA;EACA,MAAA,EAAQ,sBAAA;EACR,KAAA,EAAO,oBAAA;EAvFC;AAGV;;EAwFE,OAAA,EAAS,oBAAA;EAET,QAAA;IACE,cAAA,EAAgB,iBAAA;IAChB,aAAA,EAAe,iBAAA;IACf,oBAAA,EAAsB,iBAAA;IACtB,KAAA,EAAO,4BAAA;IACP,MAAA,EAAQ,MAAA;IACR,MAAA,GAAS,sBAAA;EAAA;AAAA"}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { TextStreamController } from "./core/modules/text.js";
2
2
  import { McpServerConfig, ProviderOptions, Tool, ToolCallReader, ToolDeclaration, ToolModelContentPart, ToolModelOutputFunction } from "./core/tool/tool-types.js";
3
3
  import { ToolResponse, ToolResponseLike } from "./core/tool/ToolResponse.js";
4
4
  import { ToolCallStreamController } from "./core/modules/tool-call.js";
5
- import { AssistantMessage, AssistantMessageTiming, DataPart } from "./core/utils/types.js";
5
+ import { AssistantMessage, AssistantMessageTiming, DataPart, ToolCallTiming } from "./core/utils/types.js";
6
6
  import { AssistantStreamController, createAssistantStream, createAssistantStreamController, createAssistantStreamResponse } from "./core/modules/assistant-stream.js";
7
7
  import { ObjectStreamChunk } from "./core/object/types.js";
8
8
  import { AssistantStreamChunk } from "./core/AssistantStreamChunk.js";
@@ -20,4 +20,4 @@ import { UIMessageStreamDecoder, UIMessageStreamDecoderOptions } from "./core/se
20
20
  import { ToolExecutionStream } from "./core/tool/ToolExecutionStream.js";
21
21
  import { ToToolsJSONSchemaOptions, ToolJSONSchema, toJSONSchema, toPartialJSONSchema, toToolsJSONSchema } from "./core/tool/schema-utils.js";
22
22
  import { ToolResultStreamOptions, toolResultStream, unstable_runPendingTools } from "./core/tool/toolResultStream.js";
23
- export { type AssistantMessage, AssistantMessageAccumulator, AssistantMessageStream, type AssistantMessageTiming, AssistantStream, type AssistantStreamChunk, type AssistantStreamController, AssistantTransportDecoder, AssistantTransportEncoder, type DataPart, DataStreamDecoder, DataStreamEncoder, type GenericAssistantMessage, type GenericFilePart, type GenericMessage, type GenericSystemMessage, type GenericTextPart, type GenericToolCallPart, type GenericToolMessage, type GenericToolResultPart, type GenericUserMessage, type McpServerConfig, type ObjectStreamChunk, ObjectStreamResponse, PlainTextDecoder, PlainTextEncoder, type ProviderOptions, type TextStreamController, type ToToolsJSONSchemaOptions, type Tool, type ToolCallReader, type ToolCallStreamController, type ToolDeclaration, ToolExecutionStream, type ToolJSONSchema, type ToolModelContentPart, type ToolModelOutputFunction, ToolResponse, type ToolResponseLike, type ToolResultStreamOptions, type UIMessageStreamChunk, type UIMessageStreamDataChunk, UIMessageStreamDecoder, type UIMessageStreamDecoderOptions, createAssistantStream, createAssistantStreamController, createAssistantStreamResponse, createObjectStream, fromObjectStreamResponse, toGenericMessages, toJSONSchema, toPartialJSONSchema, toToolsJSONSchema, createInitialMessage as unstable_createInitialMessage, unstable_runPendingTools, toolResultStream as unstable_toolResultStream };
23
+ export { type AssistantMessage, AssistantMessageAccumulator, AssistantMessageStream, type AssistantMessageTiming, AssistantStream, type AssistantStreamChunk, type AssistantStreamController, AssistantTransportDecoder, AssistantTransportEncoder, type DataPart, DataStreamDecoder, DataStreamEncoder, type GenericAssistantMessage, type GenericFilePart, type GenericMessage, type GenericSystemMessage, type GenericTextPart, type GenericToolCallPart, type GenericToolMessage, type GenericToolResultPart, type GenericUserMessage, type McpServerConfig, type ObjectStreamChunk, ObjectStreamResponse, PlainTextDecoder, PlainTextEncoder, type ProviderOptions, type TextStreamController, type ToToolsJSONSchemaOptions, type Tool, type ToolCallReader, type ToolCallStreamController, type ToolCallTiming, type ToolDeclaration, ToolExecutionStream, type ToolJSONSchema, type ToolModelContentPart, type ToolModelOutputFunction, ToolResponse, type ToolResponseLike, type ToolResultStreamOptions, type UIMessageStreamChunk, type UIMessageStreamDataChunk, UIMessageStreamDecoder, type UIMessageStreamDecoderOptions, createAssistantStream, createAssistantStreamController, createAssistantStreamResponse, createObjectStream, fromObjectStreamResponse, toGenericMessages, toJSONSchema, toPartialJSONSchema, toToolsJSONSchema, createInitialMessage as unstable_createInitialMessage, unstable_runPendingTools, toolResultStream as unstable_toolResultStream };
@@ -1 +1 @@
1
- {"version":3,"file":"ResumableStreamContext.js","names":[],"sources":["../../src/resumable/ResumableStreamContext.ts"],"sourcesContent":["import { ResumableStreamError } from \"./errors\";\nimport type {\n ResumableStreamRole,\n ResumableStreamStatus,\n ResumableStreamStore,\n} from \"./types\";\n\nexport type ResumableStreamContextOptions = {\n readonly store: ResumableStreamStore;\n readonly ttlMs?: number;\n /**\n * Required on serverless runtimes that terminate background work when the\n * request handler returns (Vercel, Cloudflare). Pass `after` from\n * `next/server` so the producer task outlives the response.\n */\n readonly waitUntil?: (promise: Promise<unknown>) => void;\n readonly onAcquire?: (streamId: string, role: ResumableStreamRole) => void;\n readonly onAppend?: (streamId: string, byteLength: number) => void;\n readonly onFinalize?: (\n streamId: string,\n status: \"done\" | \"error\",\n error?: string,\n ) => void;\n readonly onError?: (streamId: string, error: unknown) => void;\n};\n\nexport interface ResumableStreamContext {\n /** Producer or consumer entrypoint. Atomically elects the role. */\n run(\n streamId: string,\n makeStream: () => ReadableStream<Uint8Array>,\n ): Promise<ReadableStream<Uint8Array>>;\n\n /** Returns `null` when the stream does not exist. */\n resume(streamId: string): Promise<ReadableStream<Uint8Array> | null>;\n\n /** Throws `ResumableStreamError(\"missing\")` when the stream does not exist. */\n requireResume(streamId: string): Promise<ReadableStream<Uint8Array>>;\n\n status(streamId: string): Promise<ResumableStreamStatus>;\n\n delete(streamId: string): Promise<void>;\n}\n\nexport function createResumableStreamContext(\n options: ResumableStreamContextOptions,\n): ResumableStreamContext {\n const { store, waitUntil, onAcquire, onAppend, onFinalize, onError } =\n options;\n const acquireOptions =\n options.ttlMs !== undefined ? { ttlMs: options.ttlMs } : undefined;\n\n return {\n async run(streamId, makeStream) {\n const role = await store.acquire(streamId, acquireOptions);\n onAcquire?.(streamId, role);\n if (role === \"producer\") {\n startProducerTask(store, streamId, makeStream, {\n waitUntil,\n onAppend,\n onFinalize,\n onError,\n });\n }\n return readFromStore(store, streamId);\n },\n\n async resume(streamId) {\n const status = await store.status(streamId);\n if (status === \"missing\") return null;\n return readFromStore(store, streamId);\n },\n\n async requireResume(streamId) {\n const status = await store.status(streamId);\n if (status === \"missing\") {\n throw new ResumableStreamError(\n \"missing\",\n `resumable stream not found: ${streamId}`,\n );\n }\n return readFromStore(store, streamId);\n },\n\n async status(streamId) {\n return store.status(streamId);\n },\n\n async delete(streamId) {\n await store.delete(streamId);\n },\n };\n}\n\ntype ProducerHooks = {\n readonly waitUntil: ((promise: Promise<unknown>) => void) | undefined;\n readonly onAppend:\n | ((streamId: string, byteLength: number) => void)\n | undefined;\n readonly onFinalize:\n | ((streamId: string, status: \"done\" | \"error\", error?: string) => void)\n | undefined;\n readonly onError: ((streamId: string, error: unknown) => void) | undefined;\n};\n\nfunction startProducerTask(\n store: ResumableStreamStore,\n streamId: string,\n makeStream: () => ReadableStream<Uint8Array>,\n hooks: ProducerHooks,\n): void {\n const { waitUntil, onAppend, onFinalize, onError } = hooks;\n const task = (async () => {\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n let cancelled = false;\n try {\n reader = makeStream().getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n await store.append(streamId, value);\n onAppend?.(streamId, value.byteLength);\n }\n await store.finalize(streamId, \"done\");\n onFinalize?.(streamId, \"done\");\n } catch (err) {\n cancelled = true;\n onError?.(streamId, err);\n const message = err instanceof Error ? err.message : String(err);\n try {\n await reader?.cancel(err);\n } catch (cancelErr) {\n console.error(\"resumable stream reader cancel failed:\", cancelErr);\n }\n try {\n await store.finalize(streamId, \"error\", message);\n onFinalize?.(streamId, \"error\", message);\n } catch (finalizeErr) {\n console.error(\"resumable stream finalize failed:\", finalizeErr);\n }\n } finally {\n if (!cancelled) reader?.releaseLock();\n }\n })();\n\n if (waitUntil) waitUntil(task);\n task.catch((err) => {\n console.error(\"resumable producer task failed:\", err);\n });\n}\n\nfunction readFromStore(\n store: ResumableStreamStore,\n streamId: string,\n): ReadableStream<Uint8Array> {\n const ac = new AbortController();\n let iterator: AsyncIterator<{ chunk: Uint8Array }> | undefined;\n\n return new ReadableStream<Uint8Array>({\n start() {\n iterator = store.read(streamId, \"\", ac.signal)[Symbol.asyncIterator]();\n },\n async pull(controller) {\n try {\n if (!iterator) return;\n const { done, value } = await iterator.next();\n if (done) {\n controller.close();\n return;\n }\n controller.enqueue(value.chunk);\n } catch (err) {\n // the platform never calls cancel() on errored streams, so unwind\n // the store iterator and abort the signal explicitly.\n ac.abort();\n try {\n await iterator?.return?.();\n } catch {}\n controller.error(err);\n }\n },\n cancel() {\n ac.abort();\n iterator?.return?.().catch(() => {});\n },\n });\n}\n"],"mappings":";;AA4CA,SAAgB,6BACd,SACwB;CACxB,MAAM,EAAE,OAAO,WAAW,WAAW,UAAU,YAAY,YACzD;CACF,MAAM,iBACJ,QAAQ,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,KAAA;CAE3D,OAAO;EACL,MAAM,IAAI,UAAU,YAAY;GAC9B,MAAM,OAAO,MAAM,MAAM,QAAQ,UAAU,cAAc;GACzD,YAAY,UAAU,IAAI;GAC1B,IAAI,SAAS,YACX,kBAAkB,OAAO,UAAU,YAAY;IAC7C;IACA;IACA;IACA;GACF,CAAC;GAEH,OAAO,cAAc,OAAO,QAAQ;EACtC;EAEA,MAAM,OAAO,UAAU;GAErB,IAAI,MADiB,MAAM,OAAO,QAAQ,MAC3B,WAAW,OAAO;GACjC,OAAO,cAAc,OAAO,QAAQ;EACtC;EAEA,MAAM,cAAc,UAAU;GAE5B,IAAI,MADiB,MAAM,OAAO,QAAQ,MAC3B,WACb,MAAM,IAAI,qBACR,WACA,+BAA+B,UACjC;GAEF,OAAO,cAAc,OAAO,QAAQ;EACtC;EAEA,MAAM,OAAO,UAAU;GACrB,OAAO,MAAM,OAAO,QAAQ;EAC9B;EAEA,MAAM,OAAO,UAAU;GACrB,MAAM,MAAM,OAAO,QAAQ;EAC7B;CACF;AACF;AAaA,SAAS,kBACP,OACA,UACA,YACA,OACM;CACN,MAAM,EAAE,WAAW,UAAU,YAAY,YAAY;CACrD,MAAM,QAAQ,YAAY;EACxB,IAAI;EACJ,IAAI,YAAY;EAChB,IAAI;GACF,SAAS,WAAW,EAAE,UAAU;GAChC,OAAO,MAAM;IACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;IAC1C,IAAI,MAAM;IACV,MAAM,MAAM,OAAO,UAAU,KAAK;IAClC,WAAW,UAAU,MAAM,UAAU;GACvC;GACA,MAAM,MAAM,SAAS,UAAU,MAAM;GACrC,aAAa,UAAU,MAAM;EAC/B,SAAS,KAAK;GACZ,YAAY;GACZ,UAAU,UAAU,GAAG;GACvB,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAC/D,IAAI;IACF,MAAM,QAAQ,OAAO,GAAG;GAC1B,SAAS,WAAW;IAClB,QAAQ,MAAM,0CAA0C,SAAS;GACnE;GACA,IAAI;IACF,MAAM,MAAM,SAAS,UAAU,SAAS,OAAO;IAC/C,aAAa,UAAU,SAAS,OAAO;GACzC,SAAS,aAAa;IACpB,QAAQ,MAAM,qCAAqC,WAAW;GAChE;EACF,UAAU;GACR,IAAI,CAAC,WAAW,QAAQ,YAAY;EACtC;CACF,GAAG;CAEH,IAAI,WAAW,UAAU,IAAI;CAC7B,KAAK,OAAO,QAAQ;EAClB,QAAQ,MAAM,mCAAmC,GAAG;CACtD,CAAC;AACH;AAEA,SAAS,cACP,OACA,UAC4B;CAC5B,MAAM,KAAK,IAAI,gBAAgB;CAC/B,IAAI;CAEJ,OAAO,IAAI,eAA2B;EACpC,QAAQ;GACN,WAAW,MAAM,KAAK,UAAU,IAAI,GAAG,MAAM,EAAE,OAAO,eAAe;EACvE;EACA,MAAM,KAAK,YAAY;GACrB,IAAI;IACF,IAAI,CAAC,UAAU;IACf,MAAM,EAAE,MAAM,UAAU,MAAM,SAAS,KAAK;IAC5C,IAAI,MAAM;KACR,WAAW,MAAM;KACjB;IACF;IACA,WAAW,QAAQ,MAAM,KAAK;GAChC,SAAS,KAAK;IAGZ,GAAG,MAAM;IACT,IAAI;KACF,MAAM,UAAU,SAAS;IAC3B,QAAQ,CAAC;IACT,WAAW,MAAM,GAAG;GACtB;EACF;EACA,SAAS;GACP,GAAG,MAAM;GACT,UAAU,SAAS,EAAE,YAAY,CAAC,CAAC;EACrC;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"ResumableStreamContext.js","names":[],"sources":["../../src/resumable/ResumableStreamContext.ts"],"sourcesContent":["import { ResumableStreamError } from \"./errors\";\nimport type {\n ResumableStreamRole,\n ResumableStreamStatus,\n ResumableStreamStore,\n} from \"./types\";\n\nexport type ResumableStreamContextOptions = {\n readonly store: ResumableStreamStore;\n readonly ttlMs?: number;\n /**\n * Required on serverless runtimes that terminate background work when the\n * request handler returns (Vercel, Cloudflare). Pass `after` from\n * `next/server` so the producer task outlives the response.\n */\n readonly waitUntil?: (promise: Promise<unknown>) => void;\n readonly onAcquire?: (streamId: string, role: ResumableStreamRole) => void;\n readonly onAppend?: (streamId: string, byteLength: number) => void;\n readonly onFinalize?: (\n streamId: string,\n status: \"done\" | \"error\",\n error?: string,\n ) => void;\n readonly onError?: (streamId: string, error: unknown) => void;\n};\n\nexport interface ResumableStreamContext {\n /** Producer or consumer entrypoint. Atomically elects the role. */\n run(\n streamId: string,\n makeStream: () => ReadableStream<Uint8Array>,\n ): Promise<ReadableStream<Uint8Array>>;\n\n /** Returns `null` when the stream does not exist. */\n resume(streamId: string): Promise<ReadableStream<Uint8Array> | null>;\n\n /** Throws `ResumableStreamError(\"missing\")` when the stream does not exist. */\n requireResume(streamId: string): Promise<ReadableStream<Uint8Array>>;\n\n status(streamId: string): Promise<ResumableStreamStatus>;\n\n delete(streamId: string): Promise<void>;\n}\n\nexport function createResumableStreamContext(\n options: ResumableStreamContextOptions,\n): ResumableStreamContext {\n const { store, waitUntil, onAcquire, onAppend, onFinalize, onError } =\n options;\n const acquireOptions =\n options.ttlMs !== undefined ? { ttlMs: options.ttlMs } : undefined;\n\n return {\n async run(streamId, makeStream) {\n const role = await store.acquire(streamId, acquireOptions);\n onAcquire?.(streamId, role);\n if (role === \"producer\") {\n startProducerTask(store, streamId, makeStream, {\n waitUntil,\n onAppend,\n onFinalize,\n onError,\n });\n }\n return readFromStore(store, streamId);\n },\n\n async resume(streamId) {\n const status = await store.status(streamId);\n if (status === \"missing\") return null;\n return readFromStore(store, streamId);\n },\n\n async requireResume(streamId) {\n const status = await store.status(streamId);\n if (status === \"missing\") {\n throw new ResumableStreamError(\n \"missing\",\n `resumable stream not found: ${streamId}`,\n );\n }\n return readFromStore(store, streamId);\n },\n\n async status(streamId) {\n return store.status(streamId);\n },\n\n async delete(streamId) {\n await store.delete(streamId);\n },\n };\n}\n\ntype ProducerHooks = {\n readonly waitUntil: ((promise: Promise<unknown>) => void) | undefined;\n readonly onAppend:\n | ((streamId: string, byteLength: number) => void)\n | undefined;\n readonly onFinalize:\n | ((streamId: string, status: \"done\" | \"error\", error?: string) => void)\n | undefined;\n readonly onError: ((streamId: string, error: unknown) => void) | undefined;\n};\n\nfunction startProducerTask(\n store: ResumableStreamStore,\n streamId: string,\n makeStream: () => ReadableStream<Uint8Array>,\n hooks: ProducerHooks,\n): void {\n const { waitUntil, onAppend, onFinalize, onError } = hooks;\n const task = (async () => {\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n let cancelled = false;\n try {\n reader = makeStream().getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n await store.append(streamId, value);\n onAppend?.(streamId, value.byteLength);\n }\n await store.finalize(streamId, \"done\");\n onFinalize?.(streamId, \"done\");\n } catch (err) {\n cancelled = true;\n onError?.(streamId, err);\n const message = err instanceof Error ? err.message : String(err);\n try {\n await reader?.cancel(err);\n } catch (cancelErr) {\n console.error(\"resumable stream reader cancel failed:\", cancelErr);\n }\n try {\n await store.finalize(streamId, \"error\", message);\n onFinalize?.(streamId, \"error\", message);\n } catch (finalizeErr) {\n console.error(\"resumable stream finalize failed:\", finalizeErr);\n }\n } finally {\n if (!cancelled) reader?.releaseLock();\n }\n })();\n\n if (waitUntil) waitUntil(task);\n task.catch((err) => {\n console.error(\"resumable producer task failed:\", err);\n });\n}\n\nfunction readFromStore(\n store: ResumableStreamStore,\n streamId: string,\n): ReadableStream<Uint8Array> {\n const ac = new AbortController();\n let iterator: AsyncIterator<{ chunk: Uint8Array }> | undefined;\n\n return new ReadableStream<Uint8Array>({\n start() {\n iterator = store.read(streamId, \"\", ac.signal)[Symbol.asyncIterator]();\n },\n async pull(controller) {\n try {\n if (!iterator) return;\n const { done, value } = await iterator.next();\n if (done) {\n controller.close();\n return;\n }\n controller.enqueue(value.chunk);\n } catch (err) {\n // the platform never calls cancel() on errored streams, so unwind\n // the store iterator and abort the signal explicitly.\n ac.abort();\n try {\n await iterator?.return?.();\n } catch {}\n controller.error(err);\n }\n },\n cancel() {\n ac.abort();\n iterator?.return?.().catch(() => {});\n },\n });\n}\n"],"mappings":";;AA4CA,SAAgB,6BACd,SACwB;CACxB,MAAM,EAAE,OAAO,WAAW,WAAW,UAAU,YAAY,YACzD;CACF,MAAM,iBACJ,QAAQ,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,KAAA;CAE3D,OAAO;EACL,MAAM,IAAI,UAAU,YAAY;GAC9B,MAAM,OAAO,MAAM,MAAM,QAAQ,UAAU,cAAc;GACzD,YAAY,UAAU,IAAI;GAC1B,IAAI,SAAS,YACX,kBAAkB,OAAO,UAAU,YAAY;IAC7C;IACA;IACA;IACA;GACF,CAAC;GAEH,OAAO,cAAc,OAAO,QAAQ;EACtC;EAEA,MAAM,OAAO,UAAU;GAErB,IAAI,MADiB,MAAM,OAAO,QAAQ,MAC3B,WAAW,OAAO;GACjC,OAAO,cAAc,OAAO,QAAQ;EACtC;EAEA,MAAM,cAAc,UAAU;GAE5B,IAAI,MADiB,MAAM,OAAO,QAAQ,MAC3B,WACb,MAAM,IAAI,qBACR,WACA,+BAA+B,UACjC;GAEF,OAAO,cAAc,OAAO,QAAQ;EACtC;EAEA,MAAM,OAAO,UAAU;GACrB,OAAO,MAAM,OAAO,QAAQ;EAC9B;EAEA,MAAM,OAAO,UAAU;GACrB,MAAM,MAAM,OAAO,QAAQ;EAC7B;CACF;AACF;AAaA,SAAS,kBACP,OACA,UACA,YACA,OACM;CACN,MAAM,EAAE,WAAW,UAAU,YAAY,YAAY;CACrD,MAAM,QAAQ,YAAY;EACxB,IAAI;EACJ,IAAI,YAAY;EAChB,IAAI;GACF,SAAS,WAAW,CAAC,CAAC,UAAU;GAChC,OAAO,MAAM;IACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;IAC1C,IAAI,MAAM;IACV,MAAM,MAAM,OAAO,UAAU,KAAK;IAClC,WAAW,UAAU,MAAM,UAAU;GACvC;GACA,MAAM,MAAM,SAAS,UAAU,MAAM;GACrC,aAAa,UAAU,MAAM;EAC/B,SAAS,KAAK;GACZ,YAAY;GACZ,UAAU,UAAU,GAAG;GACvB,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAC/D,IAAI;IACF,MAAM,QAAQ,OAAO,GAAG;GAC1B,SAAS,WAAW;IAClB,QAAQ,MAAM,0CAA0C,SAAS;GACnE;GACA,IAAI;IACF,MAAM,MAAM,SAAS,UAAU,SAAS,OAAO;IAC/C,aAAa,UAAU,SAAS,OAAO;GACzC,SAAS,aAAa;IACpB,QAAQ,MAAM,qCAAqC,WAAW;GAChE;EACF,UAAU;GACR,IAAI,CAAC,WAAW,QAAQ,YAAY;EACtC;CACF,EAAA,CAAG;CAEH,IAAI,WAAW,UAAU,IAAI;CAC7B,KAAK,OAAO,QAAQ;EAClB,QAAQ,MAAM,mCAAmC,GAAG;CACtD,CAAC;AACH;AAEA,SAAS,cACP,OACA,UAC4B;CAC5B,MAAM,KAAK,IAAI,gBAAgB;CAC/B,IAAI;CAEJ,OAAO,IAAI,eAA2B;EACpC,QAAQ;GACN,WAAW,MAAM,KAAK,UAAU,IAAI,GAAG,MAAM,CAAC,CAAC,OAAO,cAAc,CAAC;EACvE;EACA,MAAM,KAAK,YAAY;GACrB,IAAI;IACF,IAAI,CAAC,UAAU;IACf,MAAM,EAAE,MAAM,UAAU,MAAM,SAAS,KAAK;IAC5C,IAAI,MAAM;KACR,WAAW,MAAM;KACjB;IACF;IACA,WAAW,QAAQ,MAAM,KAAK;GAChC,SAAS,KAAK;IAGZ,GAAG,MAAM;IACT,IAAI;KACF,MAAM,UAAU,SAAS;IAC3B,QAAQ,CAAC;IACT,WAAW,MAAM,GAAG;GACtB;EACF;EACA,SAAS;GACP,GAAG,MAAM;GACT,UAAU,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC;EACrC;CACF,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"createResumableAssistantStreamResponse.js","names":[],"sources":["../../src/resumable/createResumableAssistantStreamResponse.ts"],"sourcesContent":["import type { AssistantStreamEncoder } from \"../core/AssistantStream\";\nimport {\n createAssistantStream,\n type AssistantStreamController,\n} from \"../core/modules/assistant-stream\";\nimport { DataStreamEncoder } from \"../core/serialization/data-stream/DataStream\";\nimport type { ResumableStreamContext } from \"./ResumableStreamContext\";\n\nexport const RESUMABLE_STREAM_ID_HEADER = \"x-resumable-stream-id\";\n\nexport type CreateResumableAssistantStreamResponseOptions = {\n readonly context: ResumableStreamContext;\n readonly streamId: string;\n readonly callback: (\n controller: AssistantStreamController,\n ) => PromiseLike<void> | void;\n /** Defaults to `DataStreamEncoder`. Also consulted for response headers. */\n readonly encoder?: () => AssistantStreamEncoder;\n readonly headers?: HeadersInit;\n};\n\nexport async function createResumableAssistantStreamResponse(\n options: CreateResumableAssistantStreamResponseOptions,\n): Promise<Response> {\n const encoder = (options.encoder ?? (() => new DataStreamEncoder()))();\n\n const stream = await options.context.run(options.streamId, () => {\n const aStream = createAssistantStream(options.callback);\n return aStream.pipeThrough(encoder);\n });\n\n return new Response(stream, {\n headers: mergeHeaders(encoder.headers, options.headers, options.streamId),\n });\n}\n\nexport type CreateResumeAssistantStreamResponseOptions = {\n readonly context: ResumableStreamContext;\n readonly streamId: string;\n /** Read for `headers` only; the encoder transform is not invoked on resume. */\n readonly encoder?: () => AssistantStreamEncoder;\n readonly headers?: HeadersInit;\n /** Defaults to a 404 JSON response. */\n readonly missingResponse?: () => Response;\n};\n\nexport async function createResumeAssistantStreamResponse(\n options: CreateResumeAssistantStreamResponseOptions,\n): Promise<Response> {\n const stream = await options.context.resume(options.streamId);\n if (!stream) {\n return options.missingResponse?.() ?? defaultMissingResponse();\n }\n const encoder = (options.encoder ?? (() => new DataStreamEncoder()))();\n return new Response(stream, {\n headers: mergeHeaders(encoder.headers, options.headers, options.streamId),\n });\n}\n\nfunction defaultMissingResponse(): Response {\n return new Response(JSON.stringify({ error: \"stream not found\" }), {\n status: 404,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction mergeHeaders(\n encoderHeaders: Headers | undefined,\n extra: HeadersInit | undefined,\n streamId: string,\n): Headers {\n const merged = new Headers(encoderHeaders ?? {});\n if (extra) {\n for (const [key, value] of new Headers(extra)) {\n merged.set(key, value);\n }\n }\n merged.set(RESUMABLE_STREAM_ID_HEADER, streamId);\n return merged;\n}\n"],"mappings":";;;AAQA,MAAa,6BAA6B;AAa1C,eAAsB,uCACpB,SACmB;CACnB,MAAM,WAAW,QAAQ,kBAAkB,IAAI,kBAAkB,IAAI;CAErE,MAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI,QAAQ,gBAAgB;EAE/D,OADgB,sBAAsB,QAAQ,QACjC,EAAE,YAAY,OAAO;CACpC,CAAC;CAED,OAAO,IAAI,SAAS,QAAQ,EAC1B,SAAS,aAAa,QAAQ,SAAS,QAAQ,SAAS,QAAQ,QAAQ,EAC1E,CAAC;AACH;AAYA,eAAsB,oCACpB,SACmB;CACnB,MAAM,SAAS,MAAM,QAAQ,QAAQ,OAAO,QAAQ,QAAQ;CAC5D,IAAI,CAAC,QACH,OAAO,QAAQ,kBAAkB,KAAK,uBAAuB;CAE/D,MAAM,WAAW,QAAQ,kBAAkB,IAAI,kBAAkB,IAAI;CACrE,OAAO,IAAI,SAAS,QAAQ,EAC1B,SAAS,aAAa,QAAQ,SAAS,QAAQ,SAAS,QAAQ,QAAQ,EAC1E,CAAC;AACH;AAEA,SAAS,yBAAmC;CAC1C,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,mBAAmB,CAAC,GAAG;EACjE,QAAQ;EACR,SAAS,EAAE,gBAAgB,mBAAmB;CAChD,CAAC;AACH;AAEA,SAAS,aACP,gBACA,OACA,UACS;CACT,MAAM,SAAS,IAAI,QAAQ,kBAAkB,CAAC,CAAC;CAC/C,IAAI,OACF,KAAK,MAAM,CAAC,KAAK,UAAU,IAAI,QAAQ,KAAK,GAC1C,OAAO,IAAI,KAAK,KAAK;CAGzB,OAAO,IAAI,4BAA4B,QAAQ;CAC/C,OAAO;AACT"}
1
+ {"version":3,"file":"createResumableAssistantStreamResponse.js","names":[],"sources":["../../src/resumable/createResumableAssistantStreamResponse.ts"],"sourcesContent":["import type { AssistantStreamEncoder } from \"../core/AssistantStream\";\nimport {\n createAssistantStream,\n type AssistantStreamController,\n} from \"../core/modules/assistant-stream\";\nimport { DataStreamEncoder } from \"../core/serialization/data-stream/DataStream\";\nimport type { ResumableStreamContext } from \"./ResumableStreamContext\";\n\nexport const RESUMABLE_STREAM_ID_HEADER = \"x-resumable-stream-id\";\n\nexport type CreateResumableAssistantStreamResponseOptions = {\n readonly context: ResumableStreamContext;\n readonly streamId: string;\n readonly callback: (\n controller: AssistantStreamController,\n ) => PromiseLike<void> | void;\n /** Defaults to `DataStreamEncoder`. Also consulted for response headers. */\n readonly encoder?: () => AssistantStreamEncoder;\n readonly headers?: HeadersInit;\n};\n\nexport async function createResumableAssistantStreamResponse(\n options: CreateResumableAssistantStreamResponseOptions,\n): Promise<Response> {\n const encoder = (options.encoder ?? (() => new DataStreamEncoder()))();\n\n const stream = await options.context.run(options.streamId, () => {\n const aStream = createAssistantStream(options.callback);\n return aStream.pipeThrough(encoder);\n });\n\n return new Response(stream, {\n headers: mergeHeaders(encoder.headers, options.headers, options.streamId),\n });\n}\n\nexport type CreateResumeAssistantStreamResponseOptions = {\n readonly context: ResumableStreamContext;\n readonly streamId: string;\n /** Read for `headers` only; the encoder transform is not invoked on resume. */\n readonly encoder?: () => AssistantStreamEncoder;\n readonly headers?: HeadersInit;\n /** Defaults to a 404 JSON response. */\n readonly missingResponse?: () => Response;\n};\n\nexport async function createResumeAssistantStreamResponse(\n options: CreateResumeAssistantStreamResponseOptions,\n): Promise<Response> {\n const stream = await options.context.resume(options.streamId);\n if (!stream) {\n return options.missingResponse?.() ?? defaultMissingResponse();\n }\n const encoder = (options.encoder ?? (() => new DataStreamEncoder()))();\n return new Response(stream, {\n headers: mergeHeaders(encoder.headers, options.headers, options.streamId),\n });\n}\n\nfunction defaultMissingResponse(): Response {\n return new Response(JSON.stringify({ error: \"stream not found\" }), {\n status: 404,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction mergeHeaders(\n encoderHeaders: Headers | undefined,\n extra: HeadersInit | undefined,\n streamId: string,\n): Headers {\n const merged = new Headers(encoderHeaders ?? {});\n if (extra) {\n for (const [key, value] of new Headers(extra)) {\n merged.set(key, value);\n }\n }\n merged.set(RESUMABLE_STREAM_ID_HEADER, streamId);\n return merged;\n}\n"],"mappings":";;;AAQA,MAAa,6BAA6B;AAa1C,eAAsB,uCACpB,SACmB;CACnB,MAAM,WAAW,QAAQ,kBAAkB,IAAI,kBAAkB,GAAA,CAAI;CAErE,MAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI,QAAQ,gBAAgB;EAE/D,OADgB,sBAAsB,QAAQ,QACjC,CAAC,CAAC,YAAY,OAAO;CACpC,CAAC;CAED,OAAO,IAAI,SAAS,QAAQ,EAC1B,SAAS,aAAa,QAAQ,SAAS,QAAQ,SAAS,QAAQ,QAAQ,EAC1E,CAAC;AACH;AAYA,eAAsB,oCACpB,SACmB;CACnB,MAAM,SAAS,MAAM,QAAQ,QAAQ,OAAO,QAAQ,QAAQ;CAC5D,IAAI,CAAC,QACH,OAAO,QAAQ,kBAAkB,KAAK,uBAAuB;CAE/D,MAAM,WAAW,QAAQ,kBAAkB,IAAI,kBAAkB,GAAA,CAAI;CACrE,OAAO,IAAI,SAAS,QAAQ,EAC1B,SAAS,aAAa,QAAQ,SAAS,QAAQ,SAAS,QAAQ,QAAQ,EAC1E,CAAC;AACH;AAEA,SAAS,yBAAmC;CAC1C,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,mBAAmB,CAAC,GAAG;EACjE,QAAQ;EACR,SAAS,EAAE,gBAAgB,mBAAmB;CAChD,CAAC;AACH;AAEA,SAAS,aACP,gBACA,OACA,UACS;CACT,MAAM,SAAS,IAAI,QAAQ,kBAAkB,CAAC,CAAC;CAC/C,IAAI,OACF,KAAK,MAAM,CAAC,KAAK,UAAU,IAAI,QAAQ,KAAK,GAC1C,OAAO,IAAI,KAAK,KAAK;CAGzB,OAAO,IAAI,4BAA4B,QAAQ;CAC/C,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"InMemoryResumableStreamStore.js","names":[],"sources":["../../../src/resumable/stores/InMemoryResumableStreamStore.ts"],"sourcesContent":["import { DEFAULT_TTL_MS } from \"../constants\";\nimport { ResumableStreamError, validateStreamId } from \"../errors\";\nimport type {\n ResumableStreamEntry,\n ResumableStreamStatus,\n ResumableStreamStore,\n} from \"../types\";\n\ntype FinalizeMarker = { kind: \"done\" } | { kind: \"error\"; error: string };\n\ntype StreamState = {\n entries: ResumableStreamEntry[];\n nextSeq: number;\n expiresAt: number;\n ttlMs: number;\n final: FinalizeMarker | undefined;\n waiters: Array<() => void>;\n};\n\nconst cursorOf = (seq: number): string => seq.toString(36);\nconst seqFromCursor = (cursor: string): number => {\n if (cursor === \"\") return 0;\n const parsed = Number.parseInt(cursor, 36);\n return Number.isNaN(parsed) ? 0 : parsed;\n};\n\nexport type InMemoryResumableStreamStoreOptions = {\n readonly defaultTtlMs?: number;\n readonly now?: () => number;\n readonly maxChunkBytes?: number;\n readonly maxEntriesPerStream?: number;\n readonly maxStreams?: number;\n readonly gcIntervalMs?: number;\n};\n\nexport function createInMemoryResumableStreamStore(\n options: InMemoryResumableStreamStoreOptions = {},\n): ResumableStreamStore & { dispose: () => void } {\n const streams = new Map<string, StreamState>();\n const defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;\n const now = options.now ?? Date.now;\n const maxChunkBytes = options.maxChunkBytes;\n const maxEntriesPerStream = options.maxEntriesPerStream;\n const maxStreams = options.maxStreams;\n\n const evictExpired = (): void => {\n const t = now();\n for (const [id, state] of streams) {\n if (state.expiresAt > t) continue;\n streams.delete(id);\n state.final ??= { kind: \"error\", error: \"Stream expired\" };\n notify(state);\n }\n };\n\n const notify = (state: StreamState): void => {\n const waiters = state.waiters;\n state.waiters = [];\n for (const wake of waiters) wake();\n };\n\n const findStartIndex = (state: StreamState, cursor: string): number => {\n if (cursor === \"\") return 0;\n const after = seqFromCursor(cursor);\n let lo = 0;\n let hi = state.entries.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const seq = seqFromCursor(state.entries[mid]!.cursor);\n if (seq <= after) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n };\n\n const waitForUpdate = (\n state: StreamState,\n signal: AbortSignal,\n wakeBy?: number,\n ): Promise<void> =>\n new Promise<void>((resolve) => {\n let settled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const wake = () => {\n if (settled) return;\n settled = true;\n if (timer !== undefined) clearTimeout(timer);\n signal.removeEventListener(\"abort\", wake);\n const idx = state.waiters.indexOf(wake);\n if (idx !== -1) state.waiters.splice(idx, 1);\n resolve();\n };\n if (signal.aborted) {\n wake();\n return;\n }\n state.waiters.push(wake);\n signal.addEventListener(\"abort\", wake, { once: true });\n if (wakeBy !== undefined) {\n if (wakeBy > 0) {\n timer = setTimeout(wake, wakeBy);\n } else {\n // already past the deadline; resolve so the caller can re-check\n // expiration without waiting for an external notify.\n wake();\n }\n }\n });\n\n const requireActive = (streamId: string): StreamState => {\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n if (state.final) {\n throw new ResumableStreamError(\n \"finalized\",\n `Stream already finalized: ${streamId}`,\n );\n }\n return state;\n };\n\n const gcTimer =\n options.gcIntervalMs !== undefined\n ? setInterval(evictExpired, options.gcIntervalMs)\n : undefined;\n gcTimer?.unref?.();\n\n return {\n async acquire(streamId, acquireOptions) {\n validateStreamId(streamId);\n evictExpired();\n const existing = streams.get(streamId);\n if (existing) return \"consumer\";\n\n if (maxStreams !== undefined && streams.size >= maxStreams) {\n throw new Error(\"maxStreams exceeded\");\n }\n\n const ttlMs = acquireOptions?.ttlMs ?? defaultTtlMs;\n streams.set(streamId, {\n entries: [],\n nextSeq: 1,\n expiresAt: now() + ttlMs,\n ttlMs,\n final: undefined,\n waiters: [],\n });\n return \"producer\";\n },\n\n async append(streamId, chunk) {\n validateStreamId(streamId);\n if (maxChunkBytes !== undefined && chunk.byteLength > maxChunkBytes) {\n throw new Error(`Chunk exceeds maxChunkBytes: ${chunk.byteLength}`);\n }\n const state = requireActive(streamId);\n if (\n maxEntriesPerStream !== undefined &&\n state.entries.length >= maxEntriesPerStream\n ) {\n throw new Error(`Stream exceeded maxEntriesPerStream: ${streamId}`);\n }\n const seq = state.nextSeq;\n state.nextSeq += 1;\n state.entries.push({ cursor: cursorOf(seq), chunk });\n state.expiresAt = now() + state.ttlMs;\n notify(state);\n },\n\n async finalize(streamId, status, error) {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n if (state.final) return;\n state.final =\n status === \"done\"\n ? { kind: \"done\" }\n : { kind: \"error\", error: error ?? \"Stream errored\" };\n state.expiresAt = now() + state.ttlMs;\n notify(state);\n },\n\n async *read(streamId, cursor, signal) {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n\n let idx = findStartIndex(state, cursor);\n\n while (true) {\n if (signal.aborted) return;\n\n while (idx < state.entries.length) {\n if (signal.aborted) return;\n yield state.entries[idx]!;\n idx += 1;\n }\n\n if (state.final) {\n if (state.final.kind === \"error\") {\n throw new Error(state.final.error);\n }\n return;\n }\n\n const wakeBy = state.expiresAt - now();\n await waitForUpdate(state, signal, wakeBy);\n evictExpired();\n }\n },\n\n async status(streamId): Promise<ResumableStreamStatus> {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) return \"missing\";\n if (!state.final) return \"streaming\";\n return state.final.kind === \"error\" ? \"error\" : \"done\";\n },\n\n async delete(streamId) {\n validateStreamId(streamId);\n const state = streams.get(streamId);\n if (!state) return;\n streams.delete(streamId);\n state.final ??= { kind: \"done\" };\n notify(state);\n },\n\n dispose() {\n if (gcTimer !== undefined) clearInterval(gcTimer);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InMemoryResumableStreamStore.js","names":[],"sources":["../../../src/resumable/stores/InMemoryResumableStreamStore.ts"],"sourcesContent":["import { DEFAULT_TTL_MS } from \"../constants\";\nimport { ResumableStreamError, validateStreamId } from \"../errors\";\nimport type {\n ResumableStreamEntry,\n ResumableStreamStatus,\n ResumableStreamStore,\n} from \"../types\";\n\ntype FinalizeMarker = { kind: \"done\" } | { kind: \"error\"; error: string };\n\ntype StreamState = {\n entries: ResumableStreamEntry[];\n nextSeq: number;\n expiresAt: number;\n ttlMs: number;\n final: FinalizeMarker | undefined;\n waiters: Array<() => void>;\n};\n\nconst cursorOf = (seq: number): string => seq.toString(36);\nconst seqFromCursor = (cursor: string): number => {\n if (cursor === \"\") return 0;\n const parsed = Number.parseInt(cursor, 36);\n return Number.isNaN(parsed) ? 0 : parsed;\n};\n\nexport type InMemoryResumableStreamStoreOptions = {\n readonly defaultTtlMs?: number;\n readonly now?: () => number;\n readonly maxChunkBytes?: number;\n readonly maxEntriesPerStream?: number;\n readonly maxStreams?: number;\n readonly gcIntervalMs?: number;\n};\n\nexport function createInMemoryResumableStreamStore(\n options: InMemoryResumableStreamStoreOptions = {},\n): ResumableStreamStore & { dispose: () => void } {\n const streams = new Map<string, StreamState>();\n const defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;\n const now = options.now ?? Date.now;\n const maxChunkBytes = options.maxChunkBytes;\n const maxEntriesPerStream = options.maxEntriesPerStream;\n const maxStreams = options.maxStreams;\n\n const evictExpired = (): void => {\n const t = now();\n for (const [id, state] of streams) {\n if (state.expiresAt > t) continue;\n streams.delete(id);\n state.final ??= { kind: \"error\", error: \"Stream expired\" };\n notify(state);\n }\n };\n\n const notify = (state: StreamState): void => {\n const waiters = state.waiters;\n state.waiters = [];\n for (const wake of waiters) wake();\n };\n\n const findStartIndex = (state: StreamState, cursor: string): number => {\n if (cursor === \"\") return 0;\n const after = seqFromCursor(cursor);\n let lo = 0;\n let hi = state.entries.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const seq = seqFromCursor(state.entries[mid]!.cursor);\n if (seq <= after) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n };\n\n const waitForUpdate = (\n state: StreamState,\n signal: AbortSignal,\n wakeBy?: number,\n ): Promise<void> =>\n new Promise<void>((resolve) => {\n let settled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const wake = () => {\n if (settled) return;\n settled = true;\n if (timer !== undefined) clearTimeout(timer);\n signal.removeEventListener(\"abort\", wake);\n const idx = state.waiters.indexOf(wake);\n if (idx !== -1) state.waiters.splice(idx, 1);\n resolve();\n };\n if (signal.aborted) {\n wake();\n return;\n }\n state.waiters.push(wake);\n signal.addEventListener(\"abort\", wake, { once: true });\n if (wakeBy !== undefined) {\n if (wakeBy > 0) {\n timer = setTimeout(wake, wakeBy);\n } else {\n // already past the deadline; resolve so the caller can re-check\n // expiration without waiting for an external notify.\n wake();\n }\n }\n });\n\n const requireActive = (streamId: string): StreamState => {\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n if (state.final) {\n throw new ResumableStreamError(\n \"finalized\",\n `Stream already finalized: ${streamId}`,\n );\n }\n return state;\n };\n\n const gcTimer =\n options.gcIntervalMs !== undefined\n ? setInterval(evictExpired, options.gcIntervalMs)\n : undefined;\n gcTimer?.unref?.();\n\n return {\n async acquire(streamId, acquireOptions) {\n validateStreamId(streamId);\n evictExpired();\n const existing = streams.get(streamId);\n if (existing) return \"consumer\";\n\n if (maxStreams !== undefined && streams.size >= maxStreams) {\n throw new Error(\"maxStreams exceeded\");\n }\n\n const ttlMs = acquireOptions?.ttlMs ?? defaultTtlMs;\n streams.set(streamId, {\n entries: [],\n nextSeq: 1,\n expiresAt: now() + ttlMs,\n ttlMs,\n final: undefined,\n waiters: [],\n });\n return \"producer\";\n },\n\n async append(streamId, chunk) {\n validateStreamId(streamId);\n if (maxChunkBytes !== undefined && chunk.byteLength > maxChunkBytes) {\n throw new Error(`Chunk exceeds maxChunkBytes: ${chunk.byteLength}`);\n }\n const state = requireActive(streamId);\n if (\n maxEntriesPerStream !== undefined &&\n state.entries.length >= maxEntriesPerStream\n ) {\n throw new Error(`Stream exceeded maxEntriesPerStream: ${streamId}`);\n }\n const seq = state.nextSeq;\n state.nextSeq += 1;\n state.entries.push({ cursor: cursorOf(seq), chunk });\n state.expiresAt = now() + state.ttlMs;\n notify(state);\n },\n\n async finalize(streamId, status, error) {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n if (state.final) return;\n state.final =\n status === \"done\"\n ? { kind: \"done\" }\n : { kind: \"error\", error: error ?? \"Stream errored\" };\n state.expiresAt = now() + state.ttlMs;\n notify(state);\n },\n\n async *read(streamId, cursor, signal) {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n\n let idx = findStartIndex(state, cursor);\n\n while (true) {\n if (signal.aborted) return;\n\n while (idx < state.entries.length) {\n if (signal.aborted) return;\n yield state.entries[idx]!;\n idx += 1;\n }\n\n if (state.final) {\n if (state.final.kind === \"error\") {\n throw new Error(state.final.error);\n }\n return;\n }\n\n const wakeBy = state.expiresAt - now();\n await waitForUpdate(state, signal, wakeBy);\n evictExpired();\n }\n },\n\n async status(streamId): Promise<ResumableStreamStatus> {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) return \"missing\";\n if (!state.final) return \"streaming\";\n return state.final.kind === \"error\" ? \"error\" : \"done\";\n },\n\n async delete(streamId) {\n validateStreamId(streamId);\n const state = streams.get(streamId);\n if (!state) return;\n streams.delete(streamId);\n state.final ??= { kind: \"done\" };\n notify(state);\n },\n\n dispose() {\n if (gcTimer !== undefined) clearInterval(gcTimer);\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ioredis.js","names":[],"sources":["../../../src/resumable/stores/ioredis.ts"],"sourcesContent":["import type {\n ChainableCommander,\n Cluster as IoRedisCluster,\n Redis as IoRedis,\n} from \"ioredis\";\nimport {\n RedisResumableStreamStore,\n type PipelineCommand,\n type RedisLikeClient,\n type RedisResumableStreamStoreOptions,\n} from \"./redis-impl\";\nimport type { ResumableStreamStore } from \"../types\";\n\nexport type IoRedisLike = IoRedis | IoRedisCluster;\n\n/**\n * Resumable stream store backed by [`ioredis`](https://www.npmjs.com/package/ioredis)\n * v5. Accepts a `Redis` or `Cluster` instance.\n */\nexport function createIoredisResumableStreamStore(\n client: IoRedisLike,\n options?: RedisResumableStreamStoreOptions,\n): ResumableStreamStore {\n return new RedisResumableStreamStore(adapt(client), options);\n}\n\nfunction adapt(client: IoRedisLike): RedisLikeClient {\n return {\n async setNX(key, value, ttlSec) {\n const result = await client.set(key, value, \"EX\", ttlSec, \"NX\");\n return result === \"OK\";\n },\n async set(key, value, ttlSec) {\n await client.set(key, value, \"EX\", ttlSec);\n },\n async get(key) {\n return client.get(key);\n },\n async expire(key, ttlSec) {\n await client.expire(key, ttlSec);\n },\n async exists(key) {\n const result = await client.exists(key);\n return result > 0;\n },\n async del(keys) {\n if (keys.length === 0) return;\n await client.del(...keys);\n },\n async xAdd(key, fields) {\n const id = await client.xadd(key, \"*\", ...toFieldArgs(fields));\n return id ?? \"\";\n },\n async xRange(key, start, end) {\n const entries = await client.xrangeBuffer(key, start, end);\n return entries.map(([idBuf, fieldArray]) => ({\n id: idBuf.toString(\"utf8\"),\n fields: bufferFieldsToRecord(fieldArray),\n }));\n },\n async pipeline(commands) {\n if (commands.length === 0) return;\n const pipe = client.pipeline();\n for (const cmd of commands) {\n applyPipelineCommand(pipe, cmd);\n }\n const results = (await pipe.exec()) ?? [];\n for (const [err] of results) {\n if (err) throw err;\n }\n },\n };\n}\n\nfunction applyPipelineCommand(\n pipe: ChainableCommander,\n cmd: PipelineCommand,\n): void {\n switch (cmd.type) {\n case \"xAdd\":\n pipe.xadd(cmd.key, \"*\", ...toFieldArgs(cmd.fields));\n return;\n case \"expire\":\n pipe.expire(cmd.key, cmd.ttlSec);\n return;\n case \"set\":\n pipe.set(cmd.key, cmd.value, \"EX\", cmd.ttlSec);\n return;\n }\n}\n\nfunction toFieldArgs(\n fields: Record<string, string | Uint8Array>,\n): Array<string | Buffer> {\n const args: Array<string | Buffer> = [];\n for (const [k, v] of Object.entries(fields)) {\n args.push(k, typeof v === \"string\" ? v : toBuffer(v));\n }\n return args;\n}\n\nfunction toBuffer(bytes: Uint8Array): Buffer {\n if (Buffer.isBuffer(bytes)) return bytes;\n return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n}\n\nfunction bufferFieldsToRecord(\n fields: Buffer[],\n): Record<string, string | Uint8Array> {\n const out: Record<string, string | Uint8Array> = {};\n for (let i = 0; i + 1 < fields.length; i += 2) {\n const key = fields[i]?.toString(\"utf8\");\n const value = fields[i + 1];\n if (key !== undefined && value !== undefined) {\n out[key] = new Uint8Array(\n value.buffer,\n value.byteOffset,\n value.byteLength,\n );\n }\n }\n return out;\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,kCACd,QACA,SACsB;CACtB,OAAO,IAAI,0BAA0B,MAAM,MAAM,GAAG,OAAO;AAC7D;AAEA,SAAS,MAAM,QAAsC;CACnD,OAAO;EACL,MAAM,MAAM,KAAK,OAAO,QAAQ;GAE9B,OAAO,MADc,OAAO,IAAI,KAAK,OAAO,MAAM,QAAQ,IAAI,MAC5C;EACpB;EACA,MAAM,IAAI,KAAK,OAAO,QAAQ;GAC5B,MAAM,OAAO,IAAI,KAAK,OAAO,MAAM,MAAM;EAC3C;EACA,MAAM,IAAI,KAAK;GACb,OAAO,OAAO,IAAI,GAAG;EACvB;EACA,MAAM,OAAO,KAAK,QAAQ;GACxB,MAAM,OAAO,OAAO,KAAK,MAAM;EACjC;EACA,MAAM,OAAO,KAAK;GAEhB,OAAO,MADc,OAAO,OAAO,GAAG,IACtB;EAClB;EACA,MAAM,IAAI,MAAM;GACd,IAAI,KAAK,WAAW,GAAG;GACvB,MAAM,OAAO,IAAI,GAAG,IAAI;EAC1B;EACA,MAAM,KAAK,KAAK,QAAQ;GAEtB,OAAO,MADU,OAAO,KAAK,KAAK,KAAK,GAAG,YAAY,MAAM,CAAC,KAChD;EACf;EACA,MAAM,OAAO,KAAK,OAAO,KAAK;GAE5B,QAAO,MADe,OAAO,aAAa,KAAK,OAAO,GAAG,GAC1C,KAAK,CAAC,OAAO,iBAAiB;IAC3C,IAAI,MAAM,SAAS,MAAM;IACzB,QAAQ,qBAAqB,UAAU;GACzC,EAAE;EACJ;EACA,MAAM,SAAS,UAAU;GACvB,IAAI,SAAS,WAAW,GAAG;GAC3B,MAAM,OAAO,OAAO,SAAS;GAC7B,KAAK,MAAM,OAAO,UAChB,qBAAqB,MAAM,GAAG;GAEhC,MAAM,UAAW,MAAM,KAAK,KAAK,KAAM,CAAC;GACxC,KAAK,MAAM,CAAC,QAAQ,SAClB,IAAI,KAAK,MAAM;EAEnB;CACF;AACF;AAEA,SAAS,qBACP,MACA,KACM;CACN,QAAQ,IAAI,MAAZ;EACE,KAAK;GACH,KAAK,KAAK,IAAI,KAAK,KAAK,GAAG,YAAY,IAAI,MAAM,CAAC;GAClD;EACF,KAAK;GACH,KAAK,OAAO,IAAI,KAAK,IAAI,MAAM;GAC/B;EACF,KAAK;GACH,KAAK,IAAI,IAAI,KAAK,IAAI,OAAO,MAAM,IAAI,MAAM;GAC7C;CACJ;AACF;AAEA,SAAS,YACP,QACwB;CACxB,MAAM,OAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,GACxC,KAAK,KAAK,GAAG,OAAO,MAAM,WAAW,IAAI,SAAS,CAAC,CAAC;CAEtD,OAAO;AACT;AAEA,SAAS,SAAS,OAA2B;CAC3C,IAAI,OAAO,SAAS,KAAK,GAAG,OAAO;CACnC,OAAO,OAAO,KAAK,MAAM,QAAQ,MAAM,YAAY,MAAM,UAAU;AACrE;AAEA,SAAS,qBACP,QACqC;CACrC,MAAM,MAA2C,CAAC;CAClD,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,OAAO,QAAQ,KAAK,GAAG;EAC7C,MAAM,MAAM,OAAO,IAAI,SAAS,MAAM;EACtC,MAAM,QAAQ,OAAO,IAAI;EACzB,IAAI,QAAQ,KAAA,KAAa,UAAU,KAAA,GACjC,IAAI,OAAO,IAAI,WACb,MAAM,QACN,MAAM,YACN,MAAM,UACR;CAEJ;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"ioredis.js","names":[],"sources":["../../../src/resumable/stores/ioredis.ts"],"sourcesContent":["import type {\n ChainableCommander,\n Cluster as IoRedisCluster,\n Redis as IoRedis,\n} from \"ioredis\";\nimport {\n RedisResumableStreamStore,\n type PipelineCommand,\n type RedisLikeClient,\n type RedisResumableStreamStoreOptions,\n} from \"./redis-impl\";\nimport type { ResumableStreamStore } from \"../types\";\n\nexport type IoRedisLike = IoRedis | IoRedisCluster;\n\n/**\n * Resumable stream store backed by [`ioredis`](https://www.npmjs.com/package/ioredis)\n * v5. Accepts a `Redis` or `Cluster` instance.\n */\nexport function createIoredisResumableStreamStore(\n client: IoRedisLike,\n options?: RedisResumableStreamStoreOptions,\n): ResumableStreamStore {\n return new RedisResumableStreamStore(adapt(client), options);\n}\n\nfunction adapt(client: IoRedisLike): RedisLikeClient {\n return {\n async setNX(key, value, ttlSec) {\n const result = await client.set(key, value, \"EX\", ttlSec, \"NX\");\n return result === \"OK\";\n },\n async set(key, value, ttlSec) {\n await client.set(key, value, \"EX\", ttlSec);\n },\n async get(key) {\n return client.get(key);\n },\n async expire(key, ttlSec) {\n await client.expire(key, ttlSec);\n },\n async exists(key) {\n const result = await client.exists(key);\n return result > 0;\n },\n async del(keys) {\n if (keys.length === 0) return;\n await client.del(...keys);\n },\n async xAdd(key, fields) {\n const id = await client.xadd(key, \"*\", ...toFieldArgs(fields));\n return id ?? \"\";\n },\n async xRange(key, start, end) {\n const entries = await client.xrangeBuffer(key, start, end);\n return entries.map(([idBuf, fieldArray]) => ({\n id: idBuf.toString(\"utf8\"),\n fields: bufferFieldsToRecord(fieldArray),\n }));\n },\n async pipeline(commands) {\n if (commands.length === 0) return;\n const pipe = client.pipeline();\n for (const cmd of commands) {\n applyPipelineCommand(pipe, cmd);\n }\n const results = (await pipe.exec()) ?? [];\n for (const [err] of results) {\n if (err) throw err;\n }\n },\n };\n}\n\nfunction applyPipelineCommand(\n pipe: ChainableCommander,\n cmd: PipelineCommand,\n): void {\n switch (cmd.type) {\n case \"xAdd\":\n pipe.xadd(cmd.key, \"*\", ...toFieldArgs(cmd.fields));\n return;\n case \"expire\":\n pipe.expire(cmd.key, cmd.ttlSec);\n return;\n case \"set\":\n pipe.set(cmd.key, cmd.value, \"EX\", cmd.ttlSec);\n return;\n }\n}\n\nfunction toFieldArgs(\n fields: Record<string, string | Uint8Array>,\n): Array<string | Buffer> {\n const args: Array<string | Buffer> = [];\n for (const [k, v] of Object.entries(fields)) {\n args.push(k, typeof v === \"string\" ? v : toBuffer(v));\n }\n return args;\n}\n\nfunction toBuffer(bytes: Uint8Array): Buffer {\n if (Buffer.isBuffer(bytes)) return bytes;\n return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n}\n\nfunction bufferFieldsToRecord(\n fields: Buffer[],\n): Record<string, string | Uint8Array> {\n const out: Record<string, string | Uint8Array> = {};\n for (let i = 0; i + 1 < fields.length; i += 2) {\n const key = fields[i]?.toString(\"utf8\");\n const value = fields[i + 1];\n if (key !== undefined && value !== undefined) {\n out[key] = new Uint8Array(\n value.buffer,\n value.byteOffset,\n value.byteLength,\n );\n }\n }\n return out;\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,kCACd,QACA,SACsB;CACtB,OAAO,IAAI,0BAA0B,MAAM,MAAM,GAAG,OAAO;AAC7D;AAEA,SAAS,MAAM,QAAsC;CACnD,OAAO;EACL,MAAM,MAAM,KAAK,OAAO,QAAQ;GAE9B,OAAO,MADc,OAAO,IAAI,KAAK,OAAO,MAAM,QAAQ,IAAI,MAC5C;EACpB;EACA,MAAM,IAAI,KAAK,OAAO,QAAQ;GAC5B,MAAM,OAAO,IAAI,KAAK,OAAO,MAAM,MAAM;EAC3C;EACA,MAAM,IAAI,KAAK;GACb,OAAO,OAAO,IAAI,GAAG;EACvB;EACA,MAAM,OAAO,KAAK,QAAQ;GACxB,MAAM,OAAO,OAAO,KAAK,MAAM;EACjC;EACA,MAAM,OAAO,KAAK;GAEhB,OAAO,MADc,OAAO,OAAO,GAAG,IACtB;EAClB;EACA,MAAM,IAAI,MAAM;GACd,IAAI,KAAK,WAAW,GAAG;GACvB,MAAM,OAAO,IAAI,GAAG,IAAI;EAC1B;EACA,MAAM,KAAK,KAAK,QAAQ;GAEtB,OAAO,MADU,OAAO,KAAK,KAAK,KAAK,GAAG,YAAY,MAAM,CAAC,KAChD;EACf;EACA,MAAM,OAAO,KAAK,OAAO,KAAK;GAE5B,QAAO,MADe,OAAO,aAAa,KAAK,OAAO,GAAG,EAAA,CAC1C,KAAK,CAAC,OAAO,iBAAiB;IAC3C,IAAI,MAAM,SAAS,MAAM;IACzB,QAAQ,qBAAqB,UAAU;GACzC,EAAE;EACJ;EACA,MAAM,SAAS,UAAU;GACvB,IAAI,SAAS,WAAW,GAAG;GAC3B,MAAM,OAAO,OAAO,SAAS;GAC7B,KAAK,MAAM,OAAO,UAChB,qBAAqB,MAAM,GAAG;GAEhC,MAAM,UAAW,MAAM,KAAK,KAAK,KAAM,CAAC;GACxC,KAAK,MAAM,CAAC,QAAQ,SAClB,IAAI,KAAK,MAAM;EAEnB;CACF;AACF;AAEA,SAAS,qBACP,MACA,KACM;CACN,QAAQ,IAAI,MAAZ;EACE,KAAK;GACH,KAAK,KAAK,IAAI,KAAK,KAAK,GAAG,YAAY,IAAI,MAAM,CAAC;GAClD;EACF,KAAK;GACH,KAAK,OAAO,IAAI,KAAK,IAAI,MAAM;GAC/B;EACF,KAAK;GACH,KAAK,IAAI,IAAI,KAAK,IAAI,OAAO,MAAM,IAAI,MAAM;GAC7C;CACJ;AACF;AAEA,SAAS,YACP,QACwB;CACxB,MAAM,OAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,GACxC,KAAK,KAAK,GAAG,OAAO,MAAM,WAAW,IAAI,SAAS,CAAC,CAAC;CAEtD,OAAO;AACT;AAEA,SAAS,SAAS,OAA2B;CAC3C,IAAI,OAAO,SAAS,KAAK,GAAG,OAAO;CACnC,OAAO,OAAO,KAAK,MAAM,QAAQ,MAAM,YAAY,MAAM,UAAU;AACrE;AAEA,SAAS,qBACP,QACqC;CACrC,MAAM,MAA2C,CAAC;CAClD,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,OAAO,QAAQ,KAAK,GAAG;EAC7C,MAAM,MAAM,OAAO,EAAE,EAAE,SAAS,MAAM;EACtC,MAAM,QAAQ,OAAO,IAAI;EACzB,IAAI,QAAQ,KAAA,KAAa,UAAU,KAAA,GACjC,IAAI,OAAO,IAAI,WACb,MAAM,QACN,MAAM,YACN,MAAM,UACR;CAEJ;CACA,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"fix-json.js","names":[],"sources":["../../../src/utils/json/fix-json.ts"],"sourcesContent":["// LICENSE for this file only\n\n// MIT License\n\n// Copyright (c) 2025 AgentbaseAI Inc.\n// Copyright (c) 2023 Lars Grammel\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\ntype State =\n | \"ROOT\"\n | \"FINISH\"\n | \"INSIDE_STRING\"\n | \"INSIDE_STRING_ESCAPE\"\n | \"INSIDE_LITERAL\"\n | \"INSIDE_NUMBER\"\n | \"INSIDE_OBJECT_START\"\n | \"INSIDE_OBJECT_KEY\"\n | \"INSIDE_OBJECT_AFTER_KEY\"\n | \"INSIDE_OBJECT_BEFORE_VALUE\"\n | \"INSIDE_OBJECT_AFTER_VALUE\"\n | \"INSIDE_OBJECT_AFTER_COMMA\"\n | \"INSIDE_ARRAY_START\"\n | \"INSIDE_ARRAY_AFTER_VALUE\"\n | \"INSIDE_ARRAY_AFTER_COMMA\";\n\n// Implemented as a scanner with additional fixing\n// that performs a single linear time scan pass over the partial JSON.\n//\n// The states should ideally match relevant states from the JSON spec:\n// https://www.json.org/json-en.html\n//\n// Please note that invalid JSON is not considered/covered, because it\n// is assumed that the resulting JSON will be processed by a standard\n// JSON parser that will detect any invalid JSON.\n\n// Returns a tuple of [fixedJson, partialPath]\n// partialPath is an array of object/array keys that represent\n// the currently partial values. An object is considered partial\n// if through appending extra characters to the JSON string, its\n// value could change.\n\n// Example input: '{\"foo\":[{\"a\":f'\n// Example output: ['{\"foo\":[{\"a\":false}]}', ['foo', '0']]\n// Example input: '{\"foo\":'\n// Example output: ['{}', []]\n\nexport function fixJson(input: string): [string, string[]] {\n const stack: State[] = [\"ROOT\"];\n let lastValidIndex = -1;\n let literalStart: number | null = null;\n const path: string[] = [];\n let currentKey: string | undefined;\n\n function pushCurrentKeyToPath(): void {\n if (currentKey !== undefined) {\n path.push(JSON.parse(`\"${currentKey}\"`));\n currentKey = undefined;\n }\n }\n\n function processValueStart(char: string, i: number, swapState: State) {\n switch (char) {\n case '\"': {\n lastValidIndex = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_STRING\");\n\n pushCurrentKeyToPath();\n break;\n }\n\n case \"f\":\n case \"t\":\n case \"n\": {\n lastValidIndex = i;\n literalStart = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_LITERAL\");\n break;\n }\n\n case \"-\": {\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_NUMBER\");\n\n pushCurrentKeyToPath();\n break;\n }\n case \"0\":\n case \"1\":\n case \"2\":\n case \"3\":\n case \"4\":\n case \"5\":\n case \"6\":\n case \"7\":\n case \"8\":\n case \"9\": {\n lastValidIndex = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_NUMBER\");\n\n pushCurrentKeyToPath();\n break;\n }\n\n case \"{\": {\n lastValidIndex = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_OBJECT_START\");\n\n pushCurrentKeyToPath();\n break;\n }\n\n case \"[\": {\n lastValidIndex = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_ARRAY_START\");\n\n pushCurrentKeyToPath();\n break;\n }\n }\n }\n\n function processAfterObjectValue(char: string, i: number) {\n switch (char) {\n case \",\": {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_AFTER_COMMA\");\n break;\n }\n case \"}\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n }\n }\n\n function processAfterArrayValue(char: string, i: number) {\n switch (char) {\n case \",\": {\n stack.pop();\n stack.push(\"INSIDE_ARRAY_AFTER_COMMA\");\n currentKey = (Number(currentKey) + 1).toString();\n break;\n }\n case \"]\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n }\n }\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i]!;\n const currentState = stack[stack.length - 1];\n\n switch (currentState) {\n case \"ROOT\":\n processValueStart(char, i, \"FINISH\");\n break;\n\n case \"INSIDE_OBJECT_START\": {\n switch (char) {\n case '\"': {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_KEY\");\n currentKey = \"\";\n break;\n }\n case \"}\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_OBJECT_AFTER_COMMA\": {\n switch (char) {\n case '\"': {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_KEY\");\n currentKey = \"\";\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_OBJECT_KEY\": {\n switch (char) {\n case '\"': {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_AFTER_KEY\");\n break;\n }\n case \"\\\\\": {\n stack.push(\"INSIDE_STRING_ESCAPE\");\n currentKey += char;\n break;\n }\n default: {\n currentKey += char;\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_OBJECT_AFTER_KEY\": {\n switch (char) {\n case \":\": {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_BEFORE_VALUE\");\n\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_OBJECT_BEFORE_VALUE\": {\n processValueStart(char, i, \"INSIDE_OBJECT_AFTER_VALUE\");\n break;\n }\n\n case \"INSIDE_OBJECT_AFTER_VALUE\": {\n processAfterObjectValue(char, i);\n break;\n }\n\n case \"INSIDE_STRING\": {\n switch (char) {\n case '\"': {\n stack.pop();\n lastValidIndex = i;\n\n currentKey = path.pop();\n break;\n }\n\n case \"\\\\\": {\n stack.push(\"INSIDE_STRING_ESCAPE\");\n break;\n }\n\n default: {\n lastValidIndex = i;\n }\n }\n\n break;\n }\n\n case \"INSIDE_ARRAY_START\": {\n switch (char) {\n case \"]\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n\n default: {\n lastValidIndex = i;\n currentKey = \"0\";\n processValueStart(char, i, \"INSIDE_ARRAY_AFTER_VALUE\");\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_ARRAY_AFTER_VALUE\": {\n switch (char) {\n case \",\": {\n stack.pop();\n stack.push(\"INSIDE_ARRAY_AFTER_COMMA\");\n\n currentKey = (Number(currentKey) + 1).toString();\n break;\n }\n\n case \"]\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n\n default: {\n lastValidIndex = i;\n break;\n }\n }\n\n break;\n }\n\n case \"INSIDE_ARRAY_AFTER_COMMA\": {\n processValueStart(char, i, \"INSIDE_ARRAY_AFTER_VALUE\");\n break;\n }\n\n case \"INSIDE_STRING_ESCAPE\": {\n stack.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_STRING\") {\n lastValidIndex = i;\n } else if (stack[stack.length - 1] === \"INSIDE_OBJECT_KEY\") {\n currentKey += char;\n }\n\n break;\n }\n\n case \"INSIDE_NUMBER\": {\n switch (char) {\n case \"0\":\n case \"1\":\n case \"2\":\n case \"3\":\n case \"4\":\n case \"5\":\n case \"6\":\n case \"7\":\n case \"8\":\n case \"9\": {\n lastValidIndex = i;\n break;\n }\n\n case \"e\":\n case \"E\":\n case \"-\":\n case \".\": {\n break;\n }\n\n case \",\": {\n stack.pop();\n currentKey = path.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_ARRAY_AFTER_VALUE\") {\n processAfterArrayValue(char, i);\n }\n\n if (stack[stack.length - 1] === \"INSIDE_OBJECT_AFTER_VALUE\") {\n processAfterObjectValue(char, i);\n }\n\n break;\n }\n\n case \"}\": {\n stack.pop();\n currentKey = path.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_OBJECT_AFTER_VALUE\") {\n processAfterObjectValue(char, i);\n }\n\n break;\n }\n\n case \"]\": {\n stack.pop();\n currentKey = path.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_ARRAY_AFTER_VALUE\") {\n processAfterArrayValue(char, i);\n }\n\n break;\n }\n\n default: {\n stack.pop();\n currentKey = path.pop();\n break;\n }\n }\n\n break;\n }\n\n case \"INSIDE_LITERAL\": {\n const partialLiteral = input.substring(literalStart!, i + 1);\n\n if (\n !\"false\".startsWith(partialLiteral) &&\n !\"true\".startsWith(partialLiteral) &&\n !\"null\".startsWith(partialLiteral)\n ) {\n stack.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_OBJECT_AFTER_VALUE\") {\n processAfterObjectValue(char, i);\n } else if (stack[stack.length - 1] === \"INSIDE_ARRAY_AFTER_VALUE\") {\n processAfterArrayValue(char, i);\n }\n } else {\n lastValidIndex = i;\n }\n\n break;\n }\n }\n }\n\n let result = input.slice(0, lastValidIndex + 1);\n\n for (let i = stack.length - 1; i >= 0; i--) {\n const state = stack[i];\n\n switch (state) {\n case \"INSIDE_STRING\": {\n result += '\"';\n break;\n }\n\n case \"INSIDE_OBJECT_KEY\":\n case \"INSIDE_OBJECT_AFTER_KEY\":\n case \"INSIDE_OBJECT_AFTER_COMMA\":\n case \"INSIDE_OBJECT_START\":\n case \"INSIDE_OBJECT_BEFORE_VALUE\":\n case \"INSIDE_OBJECT_AFTER_VALUE\": {\n result += \"}\";\n break;\n }\n\n case \"INSIDE_ARRAY_START\":\n case \"INSIDE_ARRAY_AFTER_COMMA\":\n case \"INSIDE_ARRAY_AFTER_VALUE\": {\n result += \"]\";\n break;\n }\n\n case \"INSIDE_LITERAL\": {\n const partialLiteral = input.substring(literalStart!, input.length);\n\n if (\"true\".startsWith(partialLiteral)) {\n result += \"true\".slice(partialLiteral.length);\n } else if (\"false\".startsWith(partialLiteral)) {\n result += \"false\".slice(partialLiteral.length);\n } else if (\"null\".startsWith(partialLiteral)) {\n result += \"null\".slice(partialLiteral.length);\n }\n }\n }\n }\n\n return [result, path];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"fix-json.js","names":[],"sources":["../../../src/utils/json/fix-json.ts"],"sourcesContent":["// LICENSE for this file only\n\n// MIT License\n\n// Copyright (c) 2025 AgentbaseAI Inc.\n// Copyright (c) 2023 Lars Grammel\n\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\ntype State =\n | \"ROOT\"\n | \"FINISH\"\n | \"INSIDE_STRING\"\n | \"INSIDE_STRING_ESCAPE\"\n | \"INSIDE_LITERAL\"\n | \"INSIDE_NUMBER\"\n | \"INSIDE_OBJECT_START\"\n | \"INSIDE_OBJECT_KEY\"\n | \"INSIDE_OBJECT_AFTER_KEY\"\n | \"INSIDE_OBJECT_BEFORE_VALUE\"\n | \"INSIDE_OBJECT_AFTER_VALUE\"\n | \"INSIDE_OBJECT_AFTER_COMMA\"\n | \"INSIDE_ARRAY_START\"\n | \"INSIDE_ARRAY_AFTER_VALUE\"\n | \"INSIDE_ARRAY_AFTER_COMMA\";\n\n// Implemented as a scanner with additional fixing\n// that performs a single linear time scan pass over the partial JSON.\n//\n// The states should ideally match relevant states from the JSON spec:\n// https://www.json.org/json-en.html\n//\n// Please note that invalid JSON is not considered/covered, because it\n// is assumed that the resulting JSON will be processed by a standard\n// JSON parser that will detect any invalid JSON.\n\n// Returns a tuple of [fixedJson, partialPath]\n// partialPath is an array of object/array keys that represent\n// the currently partial values. An object is considered partial\n// if through appending extra characters to the JSON string, its\n// value could change.\n\n// Example input: '{\"foo\":[{\"a\":f'\n// Example output: ['{\"foo\":[{\"a\":false}]}', ['foo', '0']]\n// Example input: '{\"foo\":'\n// Example output: ['{}', []]\n\nexport function fixJson(input: string): [string, string[]] {\n const stack: State[] = [\"ROOT\"];\n let lastValidIndex = -1;\n let literalStart: number | null = null;\n const path: string[] = [];\n let currentKey: string | undefined;\n\n function pushCurrentKeyToPath(): void {\n if (currentKey !== undefined) {\n path.push(JSON.parse(`\"${currentKey}\"`));\n currentKey = undefined;\n }\n }\n\n function processValueStart(char: string, i: number, swapState: State) {\n switch (char) {\n case '\"': {\n lastValidIndex = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_STRING\");\n\n pushCurrentKeyToPath();\n break;\n }\n\n case \"f\":\n case \"t\":\n case \"n\": {\n lastValidIndex = i;\n literalStart = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_LITERAL\");\n break;\n }\n\n case \"-\": {\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_NUMBER\");\n\n pushCurrentKeyToPath();\n break;\n }\n case \"0\":\n case \"1\":\n case \"2\":\n case \"3\":\n case \"4\":\n case \"5\":\n case \"6\":\n case \"7\":\n case \"8\":\n case \"9\": {\n lastValidIndex = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_NUMBER\");\n\n pushCurrentKeyToPath();\n break;\n }\n\n case \"{\": {\n lastValidIndex = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_OBJECT_START\");\n\n pushCurrentKeyToPath();\n break;\n }\n\n case \"[\": {\n lastValidIndex = i;\n stack.pop();\n stack.push(swapState);\n stack.push(\"INSIDE_ARRAY_START\");\n\n pushCurrentKeyToPath();\n break;\n }\n }\n }\n\n function processAfterObjectValue(char: string, i: number) {\n switch (char) {\n case \",\": {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_AFTER_COMMA\");\n break;\n }\n case \"}\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n }\n }\n\n function processAfterArrayValue(char: string, i: number) {\n switch (char) {\n case \",\": {\n stack.pop();\n stack.push(\"INSIDE_ARRAY_AFTER_COMMA\");\n currentKey = (Number(currentKey) + 1).toString();\n break;\n }\n case \"]\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n }\n }\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i]!;\n const currentState = stack[stack.length - 1];\n\n switch (currentState) {\n case \"ROOT\":\n processValueStart(char, i, \"FINISH\");\n break;\n\n case \"INSIDE_OBJECT_START\": {\n switch (char) {\n case '\"': {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_KEY\");\n currentKey = \"\";\n break;\n }\n case \"}\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_OBJECT_AFTER_COMMA\": {\n switch (char) {\n case '\"': {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_KEY\");\n currentKey = \"\";\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_OBJECT_KEY\": {\n switch (char) {\n case '\"': {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_AFTER_KEY\");\n break;\n }\n case \"\\\\\": {\n stack.push(\"INSIDE_STRING_ESCAPE\");\n currentKey += char;\n break;\n }\n default: {\n currentKey += char;\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_OBJECT_AFTER_KEY\": {\n switch (char) {\n case \":\": {\n stack.pop();\n stack.push(\"INSIDE_OBJECT_BEFORE_VALUE\");\n\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_OBJECT_BEFORE_VALUE\": {\n processValueStart(char, i, \"INSIDE_OBJECT_AFTER_VALUE\");\n break;\n }\n\n case \"INSIDE_OBJECT_AFTER_VALUE\": {\n processAfterObjectValue(char, i);\n break;\n }\n\n case \"INSIDE_STRING\": {\n switch (char) {\n case '\"': {\n stack.pop();\n lastValidIndex = i;\n\n currentKey = path.pop();\n break;\n }\n\n case \"\\\\\": {\n stack.push(\"INSIDE_STRING_ESCAPE\");\n break;\n }\n\n default: {\n lastValidIndex = i;\n }\n }\n\n break;\n }\n\n case \"INSIDE_ARRAY_START\": {\n switch (char) {\n case \"]\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n\n default: {\n lastValidIndex = i;\n currentKey = \"0\";\n processValueStart(char, i, \"INSIDE_ARRAY_AFTER_VALUE\");\n break;\n }\n }\n break;\n }\n\n case \"INSIDE_ARRAY_AFTER_VALUE\": {\n switch (char) {\n case \",\": {\n stack.pop();\n stack.push(\"INSIDE_ARRAY_AFTER_COMMA\");\n\n currentKey = (Number(currentKey) + 1).toString();\n break;\n }\n\n case \"]\": {\n lastValidIndex = i;\n stack.pop();\n currentKey = path.pop();\n break;\n }\n\n default: {\n lastValidIndex = i;\n break;\n }\n }\n\n break;\n }\n\n case \"INSIDE_ARRAY_AFTER_COMMA\": {\n processValueStart(char, i, \"INSIDE_ARRAY_AFTER_VALUE\");\n break;\n }\n\n case \"INSIDE_STRING_ESCAPE\": {\n stack.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_STRING\") {\n lastValidIndex = i;\n } else if (stack[stack.length - 1] === \"INSIDE_OBJECT_KEY\") {\n currentKey += char;\n }\n\n break;\n }\n\n case \"INSIDE_NUMBER\": {\n switch (char) {\n case \"0\":\n case \"1\":\n case \"2\":\n case \"3\":\n case \"4\":\n case \"5\":\n case \"6\":\n case \"7\":\n case \"8\":\n case \"9\": {\n lastValidIndex = i;\n break;\n }\n\n case \"e\":\n case \"E\":\n case \"-\":\n case \".\": {\n break;\n }\n\n case \",\": {\n stack.pop();\n currentKey = path.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_ARRAY_AFTER_VALUE\") {\n processAfterArrayValue(char, i);\n }\n\n if (stack[stack.length - 1] === \"INSIDE_OBJECT_AFTER_VALUE\") {\n processAfterObjectValue(char, i);\n }\n\n break;\n }\n\n case \"}\": {\n stack.pop();\n currentKey = path.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_OBJECT_AFTER_VALUE\") {\n processAfterObjectValue(char, i);\n }\n\n break;\n }\n\n case \"]\": {\n stack.pop();\n currentKey = path.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_ARRAY_AFTER_VALUE\") {\n processAfterArrayValue(char, i);\n }\n\n break;\n }\n\n default: {\n stack.pop();\n currentKey = path.pop();\n break;\n }\n }\n\n break;\n }\n\n case \"INSIDE_LITERAL\": {\n const partialLiteral = input.substring(literalStart!, i + 1);\n\n if (\n !\"false\".startsWith(partialLiteral) &&\n !\"true\".startsWith(partialLiteral) &&\n !\"null\".startsWith(partialLiteral)\n ) {\n stack.pop();\n\n if (stack[stack.length - 1] === \"INSIDE_OBJECT_AFTER_VALUE\") {\n processAfterObjectValue(char, i);\n } else if (stack[stack.length - 1] === \"INSIDE_ARRAY_AFTER_VALUE\") {\n processAfterArrayValue(char, i);\n }\n } else {\n lastValidIndex = i;\n }\n\n break;\n }\n }\n }\n\n let result = input.slice(0, lastValidIndex + 1);\n\n for (let i = stack.length - 1; i >= 0; i--) {\n const state = stack[i];\n\n switch (state) {\n case \"INSIDE_STRING\": {\n result += '\"';\n break;\n }\n\n case \"INSIDE_OBJECT_KEY\":\n case \"INSIDE_OBJECT_AFTER_KEY\":\n case \"INSIDE_OBJECT_AFTER_COMMA\":\n case \"INSIDE_OBJECT_START\":\n case \"INSIDE_OBJECT_BEFORE_VALUE\":\n case \"INSIDE_OBJECT_AFTER_VALUE\": {\n result += \"}\";\n break;\n }\n\n case \"INSIDE_ARRAY_START\":\n case \"INSIDE_ARRAY_AFTER_COMMA\":\n case \"INSIDE_ARRAY_AFTER_VALUE\": {\n result += \"]\";\n break;\n }\n\n case \"INSIDE_LITERAL\": {\n const partialLiteral = input.substring(literalStart!, input.length);\n\n if (\"true\".startsWith(partialLiteral)) {\n result += \"true\".slice(partialLiteral.length);\n } else if (\"false\".startsWith(partialLiteral)) {\n result += \"false\".slice(partialLiteral.length);\n } else if (\"null\".startsWith(partialLiteral)) {\n result += \"null\".slice(partialLiteral.length);\n }\n }\n }\n }\n\n return [result, path];\n}\n"],"mappings":"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"}