@tanstack/ai 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +1 -0
  2. package/dist/esm/activities/chat/index.js +264 -186
  3. package/dist/esm/activities/chat/index.js.map +1 -1
  4. package/dist/esm/activities/chat/messages.js +22 -0
  5. package/dist/esm/activities/chat/messages.js.map +1 -1
  6. package/dist/esm/activities/chat/stream/index.d.ts +1 -1
  7. package/dist/esm/activities/chat/stream/processor.d.ts +22 -43
  8. package/dist/esm/activities/chat/stream/processor.js +93 -157
  9. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  10. package/dist/esm/activities/chat/tools/tool-calls.d.ts +22 -21
  11. package/dist/esm/activities/chat/tools/tool-calls.js +40 -27
  12. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  13. package/dist/esm/activities/generateImage/index.js +41 -1
  14. package/dist/esm/activities/generateImage/index.js.map +1 -1
  15. package/dist/esm/activities/generateSpeech/index.js +33 -1
  16. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  17. package/dist/esm/activities/generateTranscription/index.js +29 -1
  18. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  19. package/dist/esm/activities/generateVideo/index.js +50 -0
  20. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  21. package/dist/esm/activities/summarize/index.js +9 -8
  22. package/dist/esm/activities/summarize/index.js.map +1 -1
  23. package/dist/esm/event-client.d.ts +378 -254
  24. package/dist/esm/event-client.js.map +1 -1
  25. package/dist/esm/index.d.ts +2 -2
  26. package/dist/esm/stream-to-response.d.ts +1 -1
  27. package/dist/esm/stream-to-response.js +5 -3
  28. package/dist/esm/stream-to-response.js.map +1 -1
  29. package/dist/esm/types.d.ts +162 -46
  30. package/package.json +1 -1
  31. package/src/activities/chat/index.ts +362 -216
  32. package/src/activities/chat/messages.ts +40 -1
  33. package/src/activities/chat/stream/index.ts +1 -5
  34. package/src/activities/chat/stream/processor.ts +142 -270
  35. package/src/activities/chat/tools/tool-calls.ts +55 -49
  36. package/src/activities/generateImage/index.ts +47 -2
  37. package/src/activities/generateSpeech/index.ts +37 -1
  38. package/src/activities/generateTranscription/index.ts +33 -1
  39. package/src/activities/generateVideo/index.ts +54 -0
  40. package/src/activities/summarize/index.ts +10 -9
  41. package/src/event-client.ts +460 -256
  42. package/src/index.ts +2 -3
  43. package/src/stream-to-response.ts +8 -6
  44. package/src/types.ts +209 -65
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/activities/chat/index.ts"],"sourcesContent":["/**\n * Text Activity\n *\n * Handles agentic text generation, one-shot text generation, and agentic structured output.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport { streamToText } from '../../stream-to-response.js'\nimport { ToolCallManager, executeToolCalls } from './tools/tool-calls'\nimport {\n convertSchemaToJsonSchema,\n isStandardSchema,\n parseWithStandardSchema,\n} from './tools/schema-converter'\nimport { maxIterations as maxIterationsStrategy } from './agent-loop-strategies'\nimport type {\n ApprovalRequest,\n ClientToolRequest,\n ToolResult,\n} from './tools/tool-calls'\nimport type { AnyTextAdapter } from './adapter'\nimport type {\n AgentLoopStrategy,\n ConstrainedModelMessage,\n DoneStreamChunk,\n InferSchemaType,\n ModelMessage,\n SchemaInput,\n StreamChunk,\n TextOptions,\n Tool,\n ToolCall,\n} from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'text' as const\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the text activity.\n * Types are extracted directly from the adapter (which has pre-resolved generics).\n *\n * @template TAdapter - The text adapter type (created by a provider function)\n * @template TSchema - Optional Standard Schema for structured output\n * @template TStream - Whether to stream the output (default: true)\n */\nexport interface TextActivityOptions<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined,\n TStream extends boolean,\n> {\n /** The text adapter to use (created by a provider function like openaiText('gpt-4o')) */\n adapter: TAdapter\n /** Conversation messages - content types are constrained by the adapter's input modalities and metadata */\n messages?: Array<\n ConstrainedModelMessage<{\n inputModalities: TAdapter['~types']['inputModalities']\n messageMetadataByModality: TAdapter['~types']['messageMetadataByModality']\n }>\n >\n /** System prompts to prepend to the conversation */\n systemPrompts?: TextOptions['systemPrompts']\n /** Tools for function calling (auto-executed when called) */\n tools?: TextOptions['tools']\n /** Controls the randomness of the output. Higher values make output more random. Range: [0.0, 2.0] */\n temperature?: TextOptions['temperature']\n /** Nucleus sampling parameter. The model considers tokens with topP probability mass. */\n topP?: TextOptions['topP']\n /** The maximum number of tokens to generate in the response. */\n maxTokens?: TextOptions['maxTokens']\n /** Additional metadata to attach to the request. */\n metadata?: TextOptions['metadata']\n /** Model-specific provider options (type comes from adapter) */\n modelOptions?: TAdapter['~types']['providerOptions']\n /** AbortController for cancellation */\n abortController?: TextOptions['abortController']\n /** Strategy for controlling the agent loop */\n agentLoopStrategy?: TextOptions['agentLoopStrategy']\n /** Unique conversation identifier for tracking */\n conversationId?: TextOptions['conversationId']\n /**\n * Optional Standard Schema for structured output.\n * When provided, the activity will:\n * 1. Run the full agentic loop (executing tools as needed)\n * 2. Once complete, return a Promise with the parsed output matching the schema\n *\n * Supports any Standard Schema compliant library (Zod v4+, ArkType, Valibot, etc.)\n *\n * @example\n * ```ts\n * const result = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Generate a person' }],\n * outputSchema: z.object({ name: z.string(), age: z.number() })\n * })\n * // result is { name: string, age: number }\n * ```\n */\n outputSchema?: TSchema\n /**\n * Whether to stream the text result.\n * When true (default), returns an AsyncIterable<StreamChunk> for streaming output.\n * When false, returns a Promise<string> with the collected text content.\n *\n * Note: If outputSchema is provided, this option is ignored and the result\n * is always a Promise<InferSchemaType<TSchema>>.\n *\n * @default true\n *\n * @example Non-streaming text\n * ```ts\n * const text = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }],\n * stream: false\n * })\n * // text is a string with the full response\n * ```\n */\n stream?: TStream\n}\n\n// ===========================\n// Chat Options Helper\n// ===========================\n\n/**\n * Create typed options for the chat() function without executing.\n * This is useful for pre-defining configurations with full type inference.\n *\n * @example\n * ```ts\n * const chatOptions = createChatOptions({\n * adapter: anthropicText('claude-sonnet-4-5'),\n * })\n *\n * const stream = chat({ ...chatOptions, messages })\n * ```\n */\nexport function createChatOptions<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined = undefined,\n TStream extends boolean = true,\n>(\n options: TextActivityOptions<TAdapter, TSchema, TStream>,\n): TextActivityOptions<TAdapter, TSchema, TStream> {\n return options\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the text activity.\n * - If outputSchema is provided: Promise<InferSchemaType<TSchema>>\n * - If stream is false: Promise<string>\n * - Otherwise (stream is true, default): AsyncIterable<StreamChunk>\n */\nexport type TextActivityResult<\n TSchema extends SchemaInput | undefined,\n TStream extends boolean = true,\n> = TSchema extends SchemaInput\n ? Promise<InferSchemaType<TSchema>>\n : TStream extends false\n ? Promise<string>\n : AsyncIterable<StreamChunk>\n\n// ===========================\n// ChatEngine Implementation\n// ===========================\n\ninterface TextEngineConfig<\n TAdapter extends AnyTextAdapter,\n TParams extends TextOptions<any, any> = TextOptions<any>,\n> {\n adapter: TAdapter\n systemPrompts?: Array<string>\n params: TParams\n}\n\ntype ToolPhaseResult = 'continue' | 'stop' | 'wait'\ntype CyclePhase = 'processText' | 'executeToolCalls'\n\nclass TextEngine<\n TAdapter extends AnyTextAdapter,\n TParams extends TextOptions<any, any> = TextOptions<any>,\n> {\n private readonly adapter: TAdapter\n private readonly params: TParams\n private readonly systemPrompts: Array<string>\n private readonly tools: ReadonlyArray<Tool>\n private readonly loopStrategy: AgentLoopStrategy\n private readonly toolCallManager: ToolCallManager\n private readonly initialMessageCount: number\n private readonly requestId: string\n private readonly streamId: string\n private readonly effectiveRequest?: Request | RequestInit\n private readonly effectiveSignal?: AbortSignal\n\n private messages: Array<ModelMessage>\n private iterationCount = 0\n private lastFinishReason: string | null = null\n private streamStartTime = 0\n private totalChunkCount = 0\n private currentMessageId: string | null = null\n private accumulatedContent = ''\n private doneChunk: DoneStreamChunk | null = null\n private shouldEmitStreamEnd = true\n private earlyTermination = false\n private toolPhase: ToolPhaseResult = 'continue'\n private cyclePhase: CyclePhase = 'processText'\n\n constructor(config: TextEngineConfig<TAdapter, TParams>) {\n this.adapter = config.adapter\n this.params = config.params\n this.systemPrompts = config.params.systemPrompts || []\n this.tools = config.params.tools || []\n this.loopStrategy =\n config.params.agentLoopStrategy || maxIterationsStrategy(5)\n this.toolCallManager = new ToolCallManager(this.tools)\n this.initialMessageCount = config.params.messages.length\n this.messages = config.params.messages\n this.requestId = this.createId('chat')\n this.streamId = this.createId('stream')\n this.effectiveRequest = config.params.abortController\n ? { signal: config.params.abortController.signal }\n : undefined\n this.effectiveSignal = config.params.abortController?.signal\n }\n\n /** Get the accumulated content after the chat loop completes */\n getAccumulatedContent(): string {\n return this.accumulatedContent\n }\n\n /** Get the final messages array after the chat loop completes */\n getMessages(): Array<ModelMessage> {\n return this.messages\n }\n\n async *run(): AsyncGenerator<StreamChunk> {\n this.beforeRun()\n\n try {\n const pendingPhase = yield* this.checkForPendingToolCalls()\n if (pendingPhase === 'wait') {\n return\n }\n\n do {\n if (this.earlyTermination || this.isAborted()) {\n return\n }\n\n this.beginCycle()\n\n if (this.cyclePhase === 'processText') {\n yield* this.streamModelResponse()\n } else {\n yield* this.processToolCalls()\n }\n\n this.endCycle()\n } while (this.shouldContinue())\n } finally {\n this.afterRun()\n }\n }\n\n private beforeRun(): void {\n this.streamStartTime = Date.now()\n const {\n model,\n tools,\n temperature,\n topP,\n maxTokens,\n metadata,\n modelOptions,\n conversationId,\n } = this.params\n\n // Gather flattened options into an object for event emission\n const options: Record<string, unknown> = {}\n if (temperature !== undefined) options.temperature = temperature\n if (topP !== undefined) options.topP = topP\n if (maxTokens !== undefined) options.maxTokens = maxTokens\n if (metadata !== undefined) options.metadata = metadata\n\n aiEventClient.emit('text:started', {\n requestId: this.requestId,\n streamId: this.streamId,\n model: model,\n provider: this.adapter.name,\n messageCount: this.initialMessageCount,\n hasTools: !!tools && tools.length > 0,\n streaming: true,\n timestamp: Date.now(),\n clientId: conversationId,\n toolNames: tools?.map((t) => t.name),\n options: Object.keys(options).length > 0 ? options : undefined,\n modelOptions: modelOptions as Record<string, unknown> | undefined,\n })\n\n aiEventClient.emit('stream:started', {\n streamId: this.streamId,\n model,\n provider: this.adapter.name,\n timestamp: Date.now(),\n })\n }\n\n private afterRun(): void {\n if (!this.shouldEmitStreamEnd) {\n return\n }\n\n const now = Date.now()\n\n // Emit text:completed with final state\n aiEventClient.emit('text:completed', {\n requestId: this.requestId,\n streamId: this.streamId,\n model: this.params.model,\n content: this.accumulatedContent,\n messageId: this.currentMessageId || undefined,\n finishReason: this.lastFinishReason || undefined,\n usage: this.doneChunk?.usage,\n timestamp: now,\n })\n\n aiEventClient.emit('stream:ended', {\n requestId: this.requestId,\n streamId: this.streamId,\n totalChunks: this.totalChunkCount,\n duration: now - this.streamStartTime,\n timestamp: now,\n })\n }\n\n private beginCycle(): void {\n if (this.cyclePhase === 'processText') {\n this.beginIteration()\n }\n }\n\n private endCycle(): void {\n if (this.cyclePhase === 'processText') {\n this.cyclePhase = 'executeToolCalls'\n return\n }\n\n this.cyclePhase = 'processText'\n this.iterationCount++\n }\n\n private beginIteration(): void {\n this.currentMessageId = this.createId('msg')\n this.accumulatedContent = ''\n this.doneChunk = null\n }\n\n private async *streamModelResponse(): AsyncGenerator<StreamChunk> {\n const { temperature, topP, maxTokens, metadata, modelOptions } = this.params\n const tools = this.params.tools\n\n // Convert tool schemas to JSON Schema before passing to adapter\n const toolsWithJsonSchemas = tools?.map((tool) => ({\n ...tool,\n inputSchema: tool.inputSchema\n ? convertSchemaToJsonSchema(tool.inputSchema)\n : undefined,\n outputSchema: tool.outputSchema\n ? convertSchemaToJsonSchema(tool.outputSchema)\n : undefined,\n }))\n\n for await (const chunk of this.adapter.chatStream({\n model: this.params.model,\n messages: this.messages,\n tools: toolsWithJsonSchemas,\n temperature,\n topP,\n maxTokens,\n metadata,\n request: this.effectiveRequest,\n modelOptions,\n systemPrompts: this.systemPrompts,\n })) {\n if (this.isAborted()) {\n break\n }\n\n this.totalChunkCount++\n\n yield chunk\n this.handleStreamChunk(chunk)\n\n if (this.earlyTermination) {\n break\n }\n }\n }\n\n private handleStreamChunk(chunk: StreamChunk): void {\n switch (chunk.type) {\n case 'content':\n this.handleContentChunk(chunk)\n break\n case 'tool_call':\n this.handleToolCallChunk(chunk)\n break\n case 'tool_result':\n this.handleToolResultChunk(chunk)\n break\n case 'done':\n this.handleDoneChunk(chunk)\n break\n case 'error':\n this.handleErrorChunk(chunk)\n break\n case 'thinking':\n this.handleThinkingChunk(chunk)\n break\n default:\n break\n }\n }\n\n private handleContentChunk(chunk: Extract<StreamChunk, { type: 'content' }>) {\n this.accumulatedContent = chunk.content\n aiEventClient.emit('stream:chunk:content', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n content: chunk.content,\n delta: chunk.delta,\n timestamp: Date.now(),\n })\n }\n\n private handleToolCallChunk(\n chunk: Extract<StreamChunk, { type: 'tool_call' }>,\n ): void {\n this.toolCallManager.addToolCallChunk(chunk)\n aiEventClient.emit('stream:chunk:tool-call', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: chunk.toolCall.id,\n toolName: chunk.toolCall.function.name,\n index: chunk.index,\n arguments: chunk.toolCall.function.arguments,\n timestamp: Date.now(),\n })\n }\n\n private handleToolResultChunk(\n chunk: Extract<StreamChunk, { type: 'tool_result' }>,\n ): void {\n aiEventClient.emit('stream:chunk:tool-result', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: chunk.toolCallId,\n result: chunk.content,\n timestamp: Date.now(),\n })\n }\n\n private handleDoneChunk(chunk: DoneStreamChunk): void {\n // Don't overwrite a tool_calls finishReason with a stop finishReason\n // This can happen when adapters send multiple done chunks\n if (\n this.doneChunk?.finishReason === 'tool_calls' &&\n chunk.finishReason === 'stop'\n ) {\n // Still emit the event and update lastFinishReason, but don't overwrite doneChunk\n this.lastFinishReason = chunk.finishReason\n aiEventClient.emit('stream:chunk:done', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n\n if (chunk.usage) {\n aiEventClient.emit('usage:tokens', {\n requestId: this.requestId,\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n model: this.params.model,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n }\n return\n }\n\n this.doneChunk = chunk\n this.lastFinishReason = chunk.finishReason\n aiEventClient.emit('stream:chunk:done', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n\n if (chunk.usage) {\n aiEventClient.emit('usage:tokens', {\n requestId: this.requestId,\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n model: this.params.model,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n }\n }\n\n private handleErrorChunk(\n chunk: Extract<StreamChunk, { type: 'error' }>,\n ): void {\n aiEventClient.emit('stream:chunk:error', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n error: chunk.error.message,\n timestamp: Date.now(),\n })\n this.earlyTermination = true\n this.shouldEmitStreamEnd = false\n }\n\n private handleThinkingChunk(\n chunk: Extract<StreamChunk, { type: 'thinking' }>,\n ): void {\n aiEventClient.emit('stream:chunk:thinking', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n content: chunk.content,\n delta: chunk.delta,\n timestamp: Date.now(),\n })\n }\n\n private async *checkForPendingToolCalls(): AsyncGenerator<\n StreamChunk,\n ToolPhaseResult,\n void\n > {\n const pendingToolCalls = this.getPendingToolCallsFromMessages()\n if (pendingToolCalls.length === 0) {\n return 'continue'\n }\n\n const doneChunk = this.createSyntheticDoneChunk()\n\n aiEventClient.emit('text:iteration', {\n requestId: this.requestId,\n streamId: this.streamId,\n iterationNumber: this.iterationCount + 1,\n messageCount: this.messages.length,\n toolCallCount: pendingToolCalls.length,\n timestamp: Date.now(),\n })\n\n const { approvals, clientToolResults } = this.collectClientState()\n\n const executionResult = await executeToolCalls(\n pendingToolCalls,\n this.tools,\n approvals,\n clientToolResults,\n )\n\n if (\n executionResult.needsApproval.length > 0 ||\n executionResult.needsClientExecution.length > 0\n ) {\n for (const chunk of this.emitApprovalRequests(\n executionResult.needsApproval,\n doneChunk,\n )) {\n yield chunk\n }\n\n for (const chunk of this.emitClientToolInputs(\n executionResult.needsClientExecution,\n doneChunk,\n )) {\n yield chunk\n }\n\n this.shouldEmitStreamEnd = false\n return 'wait'\n }\n\n const toolResultChunks = this.emitToolResults(\n executionResult.results,\n doneChunk,\n )\n\n for (const chunk of toolResultChunks) {\n yield chunk\n }\n\n return 'continue'\n }\n\n private async *processToolCalls(): AsyncGenerator<StreamChunk, void, void> {\n if (!this.shouldExecuteToolPhase()) {\n this.setToolPhase('stop')\n return\n }\n\n const toolCalls = this.toolCallManager.getToolCalls()\n const doneChunk = this.doneChunk\n\n if (!doneChunk || toolCalls.length === 0) {\n this.setToolPhase('stop')\n return\n }\n\n aiEventClient.emit('text:iteration', {\n requestId: this.requestId,\n streamId: this.streamId,\n iterationNumber: this.iterationCount + 1,\n messageCount: this.messages.length,\n toolCallCount: toolCalls.length,\n timestamp: Date.now(),\n })\n\n this.addAssistantToolCallMessage(toolCalls)\n\n const { approvals, clientToolResults } = this.collectClientState()\n\n const executionResult = await executeToolCalls(\n toolCalls,\n this.tools,\n approvals,\n clientToolResults,\n )\n\n if (\n executionResult.needsApproval.length > 0 ||\n executionResult.needsClientExecution.length > 0\n ) {\n for (const chunk of this.emitApprovalRequests(\n executionResult.needsApproval,\n doneChunk,\n )) {\n yield chunk\n }\n\n for (const chunk of this.emitClientToolInputs(\n executionResult.needsClientExecution,\n doneChunk,\n )) {\n yield chunk\n }\n\n this.setToolPhase('wait')\n return\n }\n\n const toolResultChunks = this.emitToolResults(\n executionResult.results,\n doneChunk,\n )\n\n for (const chunk of toolResultChunks) {\n yield chunk\n }\n\n this.toolCallManager.clear()\n\n this.setToolPhase('continue')\n }\n\n private shouldExecuteToolPhase(): boolean {\n return (\n this.doneChunk?.finishReason === 'tool_calls' &&\n this.tools.length > 0 &&\n this.toolCallManager.hasToolCalls()\n )\n }\n\n private addAssistantToolCallMessage(toolCalls: Array<ToolCall>): void {\n this.messages = [\n ...this.messages,\n {\n role: 'assistant',\n content: this.accumulatedContent || null,\n toolCalls,\n },\n ]\n }\n\n private collectClientState(): {\n approvals: Map<string, boolean>\n clientToolResults: Map<string, any>\n } {\n const approvals = new Map<string, boolean>()\n const clientToolResults = new Map<string, any>()\n\n for (const message of this.messages) {\n // todo remove any and fix this\n if (message.role === 'assistant' && (message as any).parts) {\n const parts = (message as any).parts\n for (const part of parts) {\n if (\n part.type === 'tool-call' &&\n part.state === 'approval-responded' &&\n part.approval\n ) {\n approvals.set(part.approval.id, part.approval.approved)\n }\n\n if (\n part.type === 'tool-call' &&\n part.output !== undefined &&\n !part.approval\n ) {\n clientToolResults.set(part.id, part.output)\n }\n }\n }\n }\n\n return { approvals, clientToolResults }\n }\n\n private emitApprovalRequests(\n approvals: Array<ApprovalRequest>,\n doneChunk: DoneStreamChunk,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const approval of approvals) {\n aiEventClient.emit('stream:approval-requested', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: approval.toolCallId,\n toolName: approval.toolName,\n input: approval.input,\n approvalId: approval.approvalId,\n timestamp: Date.now(),\n })\n\n chunks.push({\n type: 'approval-requested',\n id: doneChunk.id,\n model: doneChunk.model,\n timestamp: Date.now(),\n toolCallId: approval.toolCallId,\n toolName: approval.toolName,\n input: approval.input,\n approval: {\n id: approval.approvalId,\n needsApproval: true,\n },\n })\n }\n\n return chunks\n }\n\n private emitClientToolInputs(\n clientRequests: Array<ClientToolRequest>,\n doneChunk: DoneStreamChunk,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const clientTool of clientRequests) {\n aiEventClient.emit('stream:tool-input-available', {\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: clientTool.toolCallId,\n toolName: clientTool.toolName,\n input: clientTool.input,\n timestamp: Date.now(),\n })\n\n chunks.push({\n type: 'tool-input-available',\n id: doneChunk.id,\n model: doneChunk.model,\n timestamp: Date.now(),\n toolCallId: clientTool.toolCallId,\n toolName: clientTool.toolName,\n input: clientTool.input,\n })\n }\n\n return chunks\n }\n\n private emitToolResults(\n results: Array<ToolResult>,\n doneChunk: DoneStreamChunk,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const result of results) {\n aiEventClient.emit('tool:call-completed', {\n requestId: this.requestId,\n streamId: this.streamId,\n messageId: this.currentMessageId || undefined,\n toolCallId: result.toolCallId,\n toolName: result.toolName,\n result: result.result,\n duration: result.duration ?? 0,\n timestamp: Date.now(),\n })\n\n const content = JSON.stringify(result.result)\n const chunk: Extract<StreamChunk, { type: 'tool_result' }> = {\n type: 'tool_result',\n id: doneChunk.id,\n model: doneChunk.model,\n timestamp: Date.now(),\n toolCallId: result.toolCallId,\n content,\n }\n\n chunks.push(chunk)\n\n this.messages = [\n ...this.messages,\n {\n role: 'tool',\n content,\n toolCallId: result.toolCallId,\n },\n ]\n }\n\n return chunks\n }\n\n private getPendingToolCallsFromMessages(): Array<ToolCall> {\n const completedToolIds = new Set(\n this.messages\n .filter((message) => message.role === 'tool' && message.toolCallId)\n .map((message) => message.toolCallId!), // toolCallId exists due to filter\n )\n\n const pending: Array<ToolCall> = []\n\n for (const message of this.messages) {\n if (message.role === 'assistant' && message.toolCalls) {\n for (const toolCall of message.toolCalls) {\n if (!completedToolIds.has(toolCall.id)) {\n pending.push(toolCall)\n }\n }\n }\n }\n\n return pending\n }\n\n private createSyntheticDoneChunk(): DoneStreamChunk {\n return {\n type: 'done',\n id: this.createId('pending'),\n model: this.params.model,\n timestamp: Date.now(),\n finishReason: 'tool_calls',\n }\n }\n\n private shouldContinue(): boolean {\n if (this.cyclePhase === 'executeToolCalls') {\n return true\n }\n\n return (\n this.loopStrategy({\n iterationCount: this.iterationCount,\n messages: this.messages,\n finishReason: this.lastFinishReason,\n }) && this.toolPhase === 'continue'\n )\n }\n\n private isAborted(): boolean {\n return !!this.effectiveSignal?.aborted\n }\n\n private setToolPhase(phase: ToolPhaseResult): void {\n this.toolPhase = phase\n if (phase === 'wait') {\n this.shouldEmitStreamEnd = false\n }\n }\n\n private createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n }\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Text activity - handles agentic text generation, one-shot text generation, and agentic structured output.\n *\n * This activity supports four modes:\n * 1. **Streaming agentic text**: Stream responses with automatic tool execution\n * 2. **Streaming one-shot text**: Simple streaming request/response without tools\n * 3. **Non-streaming text**: Returns collected text as a string (stream: false)\n * 4. **Agentic structured output**: Run tools, then return structured data\n *\n * @example Full agentic text (streaming with tools)\n * ```ts\n * import { chat } from '@tanstack/ai'\n * import { openaiText } from '@tanstack/ai-openai'\n *\n * for await (const chunk of chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'What is the weather?' }],\n * tools: [weatherTool]\n * })) {\n * if (chunk.type === 'content') {\n * console.log(chunk.delta)\n * }\n * }\n * ```\n *\n * @example One-shot text (streaming without tools)\n * ```ts\n * for await (const chunk of chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }]\n * })) {\n * console.log(chunk)\n * }\n * ```\n *\n * @example Non-streaming text (stream: false)\n * ```ts\n * const text = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }],\n * stream: false\n * })\n * // text is a string with the full response\n * ```\n *\n * @example Agentic structured output (tools + structured response)\n * ```ts\n * import { z } from 'zod'\n *\n * const result = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Research and summarize the topic' }],\n * tools: [researchTool, analyzeTool],\n * outputSchema: z.object({\n * summary: z.string(),\n * keyPoints: z.array(z.string())\n * })\n * })\n * // result is { summary: string, keyPoints: string[] }\n * ```\n */\nexport function chat<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined = undefined,\n TStream extends boolean = true,\n>(\n options: TextActivityOptions<TAdapter, TSchema, TStream>,\n): TextActivityResult<TSchema, TStream> {\n const { outputSchema, stream } = options\n\n // If outputSchema is provided, run agentic structured output\n if (outputSchema) {\n return runAgenticStructuredOutput(\n options as unknown as TextActivityOptions<\n AnyTextAdapter,\n SchemaInput,\n boolean\n >,\n ) as TextActivityResult<TSchema, TStream>\n }\n\n // If stream is explicitly false, run non-streaming text\n if (stream === false) {\n return runNonStreamingText(\n options as unknown as TextActivityOptions<\n AnyTextAdapter,\n undefined,\n false\n >,\n ) as TextActivityResult<TSchema, TStream>\n }\n\n // Otherwise, run streaming text (default)\n return runStreamingText(\n options as unknown as TextActivityOptions<AnyTextAdapter, undefined, true>,\n ) as TextActivityResult<TSchema, TStream>\n}\n\n/**\n * Run streaming text (agentic or one-shot depending on tools)\n */\nasync function* runStreamingText(\n options: TextActivityOptions<AnyTextAdapter, undefined, true>,\n): AsyncIterable<StreamChunk> {\n const { adapter, ...textOptions } = options\n const model = adapter.model\n\n const engine = new TextEngine({\n adapter,\n params: { ...textOptions, model } as TextOptions<\n Record<string, any>,\n Record<string, any>\n >,\n })\n\n for await (const chunk of engine.run()) {\n yield chunk\n }\n}\n\n/**\n * Run non-streaming text - collects all content and returns as a string.\n * Runs the full agentic loop (if tools are provided) but returns collected text.\n */\nfunction runNonStreamingText(\n options: TextActivityOptions<AnyTextAdapter, undefined, false>,\n): Promise<string> {\n // Run the streaming text and collect all text using streamToText\n const stream = runStreamingText(\n options as unknown as TextActivityOptions<AnyTextAdapter, undefined, true>,\n )\n\n return streamToText(stream)\n}\n\n/**\n * Run agentic structured output:\n * 1. Execute the full agentic loop (with tools)\n * 2. Once complete, call adapter.structuredOutput with the conversation context\n * 3. Validate and return the structured result\n */\nasync function runAgenticStructuredOutput<TSchema extends SchemaInput>(\n options: TextActivityOptions<AnyTextAdapter, TSchema, boolean>,\n): Promise<InferSchemaType<TSchema>> {\n const { adapter, outputSchema, ...textOptions } = options\n const model = adapter.model\n\n if (!outputSchema) {\n throw new Error('outputSchema is required for structured output')\n }\n\n // Create the engine and run the agentic loop\n const engine = new TextEngine({\n adapter,\n params: { ...textOptions, model } as TextOptions<\n Record<string, unknown>,\n Record<string, unknown>\n >,\n })\n\n // Consume the stream to run the agentic loop\n for await (const _chunk of engine.run()) {\n // Just consume the stream to execute the agentic loop\n }\n\n // Get the final messages from the engine (includes tool results)\n const finalMessages = engine.getMessages()\n\n // Build text options for structured output, excluding tools since\n // the agentic loop is complete and we only need the final response\n const {\n tools: _tools,\n agentLoopStrategy: _als,\n ...structuredTextOptions\n } = textOptions\n\n // Convert the schema to JSON Schema before passing to the adapter\n const jsonSchema = convertSchemaToJsonSchema(outputSchema)\n if (!jsonSchema) {\n throw new Error('Failed to convert output schema to JSON Schema')\n }\n\n // Call the adapter's structured output method with the conversation context\n // The adapter receives JSON Schema and can apply vendor-specific patches\n const result = await adapter.structuredOutput({\n chatOptions: {\n ...structuredTextOptions,\n model,\n messages: finalMessages,\n },\n outputSchema: jsonSchema,\n })\n\n // Validate the result against the schema if it's a Standard Schema\n if (isStandardSchema(outputSchema)) {\n return parseWithStandardSchema<InferSchemaType<TSchema>>(\n outputSchema,\n result.data,\n )\n }\n\n // For plain JSON Schema, return the data as-is\n return result.data as InferSchemaType<TSchema>\n}\n\n// Re-export adapter types\nexport type {\n TextAdapter,\n TextAdapterConfig,\n StructuredOutputOptions,\n StructuredOutputResult,\n} from './adapter'\nexport { BaseTextAdapter } from './adapter'\n"],"names":["maxIterationsStrategy"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/activities/chat/index.ts"],"sourcesContent":["/**\n * Text Activity\n *\n * Handles agentic text generation, one-shot text generation, and agentic structured output.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '../../event-client.js'\nimport { streamToText } from '../../stream-to-response.js'\nimport { ToolCallManager, executeToolCalls } from './tools/tool-calls'\nimport {\n convertSchemaToJsonSchema,\n isStandardSchema,\n parseWithStandardSchema,\n} from './tools/schema-converter'\nimport { maxIterations as maxIterationsStrategy } from './agent-loop-strategies'\nimport { convertMessagesToModelMessages } from './messages'\nimport type {\n ApprovalRequest,\n ClientToolRequest,\n ToolResult,\n} from './tools/tool-calls'\nimport type { AnyTextAdapter } from './adapter'\nimport type {\n AgentLoopStrategy,\n ConstrainedModelMessage,\n InferSchemaType,\n ModelMessage,\n RunFinishedEvent,\n SchemaInput,\n StreamChunk,\n TextMessageContentEvent,\n TextOptions,\n Tool,\n ToolCall,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n} from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'text' as const\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the text activity.\n * Types are extracted directly from the adapter (which has pre-resolved generics).\n *\n * @template TAdapter - The text adapter type (created by a provider function)\n * @template TSchema - Optional Standard Schema for structured output\n * @template TStream - Whether to stream the output (default: true)\n */\nexport interface TextActivityOptions<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined,\n TStream extends boolean,\n> {\n /** The text adapter to use (created by a provider function like openaiText('gpt-4o')) */\n adapter: TAdapter\n /** Conversation messages - content types are constrained by the adapter's input modalities and metadata */\n messages?: Array<\n ConstrainedModelMessage<{\n inputModalities: TAdapter['~types']['inputModalities']\n messageMetadataByModality: TAdapter['~types']['messageMetadataByModality']\n }>\n >\n /** System prompts to prepend to the conversation */\n systemPrompts?: TextOptions['systemPrompts']\n /** Tools for function calling (auto-executed when called) */\n tools?: TextOptions['tools']\n /** Controls the randomness of the output. Higher values make output more random. Range: [0.0, 2.0] */\n temperature?: TextOptions['temperature']\n /** Nucleus sampling parameter. The model considers tokens with topP probability mass. */\n topP?: TextOptions['topP']\n /** The maximum number of tokens to generate in the response. */\n maxTokens?: TextOptions['maxTokens']\n /** Additional metadata to attach to the request. */\n metadata?: TextOptions['metadata']\n /** Model-specific provider options (type comes from adapter) */\n modelOptions?: TAdapter['~types']['providerOptions']\n /** AbortController for cancellation */\n abortController?: TextOptions['abortController']\n /** Strategy for controlling the agent loop */\n agentLoopStrategy?: TextOptions['agentLoopStrategy']\n /** Unique conversation identifier for tracking */\n conversationId?: TextOptions['conversationId']\n /**\n * Optional Standard Schema for structured output.\n * When provided, the activity will:\n * 1. Run the full agentic loop (executing tools as needed)\n * 2. Once complete, return a Promise with the parsed output matching the schema\n *\n * Supports any Standard Schema compliant library (Zod v4+, ArkType, Valibot, etc.)\n *\n * @example\n * ```ts\n * const result = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Generate a person' }],\n * outputSchema: z.object({ name: z.string(), age: z.number() })\n * })\n * // result is { name: string, age: number }\n * ```\n */\n outputSchema?: TSchema\n /**\n * Whether to stream the text result.\n * When true (default), returns an AsyncIterable<StreamChunk> for streaming output.\n * When false, returns a Promise<string> with the collected text content.\n *\n * Note: If outputSchema is provided, this option is ignored and the result\n * is always a Promise<InferSchemaType<TSchema>>.\n *\n * @default true\n *\n * @example Non-streaming text\n * ```ts\n * const text = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }],\n * stream: false\n * })\n * // text is a string with the full response\n * ```\n */\n stream?: TStream\n}\n\n// ===========================\n// Chat Options Helper\n// ===========================\n\n/**\n * Create typed options for the chat() function without executing.\n * This is useful for pre-defining configurations with full type inference.\n *\n * @example\n * ```ts\n * const chatOptions = createChatOptions({\n * adapter: anthropicText('claude-sonnet-4-5'),\n * })\n *\n * const stream = chat({ ...chatOptions, messages })\n * ```\n */\nexport function createChatOptions<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined = undefined,\n TStream extends boolean = true,\n>(\n options: TextActivityOptions<TAdapter, TSchema, TStream>,\n): TextActivityOptions<TAdapter, TSchema, TStream> {\n return options\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the text activity.\n * - If outputSchema is provided: Promise<InferSchemaType<TSchema>>\n * - If stream is false: Promise<string>\n * - Otherwise (stream is true, default): AsyncIterable<StreamChunk>\n */\nexport type TextActivityResult<\n TSchema extends SchemaInput | undefined,\n TStream extends boolean = true,\n> = TSchema extends SchemaInput\n ? Promise<InferSchemaType<TSchema>>\n : TStream extends false\n ? Promise<string>\n : AsyncIterable<StreamChunk>\n\n// ===========================\n// ChatEngine Implementation\n// ===========================\n\ninterface TextEngineConfig<\n TAdapter extends AnyTextAdapter,\n TParams extends TextOptions<any, any> = TextOptions<any>,\n> {\n adapter: TAdapter\n systemPrompts?: Array<string>\n params: TParams\n}\n\ntype ToolPhaseResult = 'continue' | 'stop' | 'wait'\ntype CyclePhase = 'processText' | 'executeToolCalls'\n\nclass TextEngine<\n TAdapter extends AnyTextAdapter,\n TParams extends TextOptions<any, any> = TextOptions<any>,\n> {\n private readonly adapter: TAdapter\n private readonly params: TParams\n private readonly systemPrompts: Array<string>\n private readonly tools: ReadonlyArray<Tool>\n private readonly loopStrategy: AgentLoopStrategy\n private readonly toolCallManager: ToolCallManager\n private readonly initialMessageCount: number\n private readonly requestId: string\n private readonly streamId: string\n private readonly effectiveRequest?: Request | RequestInit\n private readonly effectiveSignal?: AbortSignal\n\n private messages: Array<ModelMessage>\n private iterationCount = 0\n private lastFinishReason: string | null = null\n private streamStartTime = 0\n private totalChunkCount = 0\n private currentMessageId: string | null = null\n private accumulatedContent = ''\n private eventOptions?: Record<string, unknown>\n private eventToolNames?: Array<string>\n private finishedEvent: RunFinishedEvent | null = null\n private shouldEmitStreamEnd = true\n private earlyTermination = false\n private toolPhase: ToolPhaseResult = 'continue'\n private cyclePhase: CyclePhase = 'processText'\n // Client state extracted from initial messages (before conversion to ModelMessage)\n private readonly initialApprovals: Map<string, boolean>\n private readonly initialClientToolResults: Map<string, any>\n\n constructor(config: TextEngineConfig<TAdapter, TParams>) {\n this.adapter = config.adapter\n this.params = config.params\n this.systemPrompts = config.params.systemPrompts || []\n this.tools = config.params.tools || []\n this.loopStrategy =\n config.params.agentLoopStrategy || maxIterationsStrategy(5)\n this.toolCallManager = new ToolCallManager(this.tools)\n this.initialMessageCount = config.params.messages.length\n\n // Extract client state (approvals, client tool results) from original messages BEFORE conversion\n // This preserves UIMessage parts data that would be lost during conversion to ModelMessage\n const { approvals, clientToolResults } =\n this.extractClientStateFromOriginalMessages(\n config.params.messages as Array<any>,\n )\n this.initialApprovals = approvals\n this.initialClientToolResults = clientToolResults\n\n // Convert messages to ModelMessage format (handles both UIMessage and ModelMessage input)\n // This ensures consistent internal format regardless of what the client sends\n this.messages = convertMessagesToModelMessages(\n config.params.messages as Array<any>,\n )\n this.requestId = this.createId('chat')\n this.streamId = this.createId('stream')\n this.effectiveRequest = config.params.abortController\n ? { signal: config.params.abortController.signal }\n : undefined\n this.effectiveSignal = config.params.abortController?.signal\n }\n\n /** Get the accumulated content after the chat loop completes */\n getAccumulatedContent(): string {\n return this.accumulatedContent\n }\n\n /** Get the final messages array after the chat loop completes */\n getMessages(): Array<ModelMessage> {\n return this.messages\n }\n\n async *run(): AsyncGenerator<StreamChunk> {\n this.beforeRun()\n\n try {\n const pendingPhase = yield* this.checkForPendingToolCalls()\n if (pendingPhase === 'wait') {\n return\n }\n\n do {\n if (this.earlyTermination || this.isAborted()) {\n return\n }\n\n this.beginCycle()\n\n if (this.cyclePhase === 'processText') {\n yield* this.streamModelResponse()\n } else {\n yield* this.processToolCalls()\n }\n\n this.endCycle()\n } while (this.shouldContinue())\n } finally {\n this.afterRun()\n }\n }\n\n private beforeRun(): void {\n this.streamStartTime = Date.now()\n const { tools, temperature, topP, maxTokens, metadata } = this.params\n\n // Gather flattened options into an object for event emission\n const options: Record<string, unknown> = {}\n if (temperature !== undefined) options.temperature = temperature\n if (topP !== undefined) options.topP = topP\n if (maxTokens !== undefined) options.maxTokens = maxTokens\n if (metadata !== undefined) options.metadata = metadata\n\n this.eventOptions = Object.keys(options).length > 0 ? options : undefined\n this.eventToolNames = tools?.map((t) => t.name)\n\n aiEventClient.emit('text:request:started', {\n ...this.buildTextEventContext(),\n timestamp: Date.now(),\n })\n\n // Always emit messages for tracking:\n // - For existing conversations (with conversationId): only emit the latest user message\n // - For new conversations (without conversationId): emit all messages for reconstruction\n const messagesToEmit = this.params.conversationId\n ? this.messages.slice(-1).filter((m) => m.role === 'user')\n : this.messages\n\n messagesToEmit.forEach((message, index) => {\n const messageIndex = this.params.conversationId\n ? this.messages.length - 1\n : index\n const messageId = this.createId('msg')\n const baseContext = this.buildTextEventContext()\n const content = this.getContentString(message.content)\n\n aiEventClient.emit('text:message:created', {\n ...baseContext,\n messageId,\n role: message.role,\n content,\n toolCalls: message.toolCalls,\n messageIndex,\n timestamp: Date.now(),\n })\n\n if (message.role === 'user') {\n aiEventClient.emit('text:message:user', {\n ...baseContext,\n messageId,\n role: 'user',\n content,\n messageIndex,\n timestamp: Date.now(),\n })\n }\n })\n }\n\n private afterRun(): void {\n if (!this.shouldEmitStreamEnd) {\n return\n }\n\n const now = Date.now()\n // Emit text:request:completed with final state\n aiEventClient.emit('text:request:completed', {\n ...this.buildTextEventContext(),\n content: this.accumulatedContent,\n messageId: this.currentMessageId || undefined,\n finishReason: this.lastFinishReason || undefined,\n usage: this.finishedEvent?.usage,\n duration: now - this.streamStartTime,\n timestamp: now,\n })\n }\n\n private beginCycle(): void {\n if (this.cyclePhase === 'processText') {\n this.beginIteration()\n }\n }\n\n private endCycle(): void {\n if (this.cyclePhase === 'processText') {\n this.cyclePhase = 'executeToolCalls'\n return\n }\n\n this.cyclePhase = 'processText'\n this.iterationCount++\n }\n\n private beginIteration(): void {\n this.currentMessageId = this.createId('msg')\n this.accumulatedContent = ''\n this.finishedEvent = null\n\n const baseContext = this.buildTextEventContext()\n aiEventClient.emit('text:message:created', {\n ...baseContext,\n messageId: this.currentMessageId,\n role: 'assistant',\n content: '',\n timestamp: Date.now(),\n })\n }\n\n private async *streamModelResponse(): AsyncGenerator<StreamChunk> {\n const { temperature, topP, maxTokens, metadata, modelOptions } = this.params\n const tools = this.params.tools\n\n // Convert tool schemas to JSON Schema before passing to adapter\n const toolsWithJsonSchemas = tools?.map((tool) => ({\n ...tool,\n inputSchema: tool.inputSchema\n ? convertSchemaToJsonSchema(tool.inputSchema)\n : undefined,\n outputSchema: tool.outputSchema\n ? convertSchemaToJsonSchema(tool.outputSchema)\n : undefined,\n }))\n\n for await (const chunk of this.adapter.chatStream({\n model: this.params.model,\n messages: this.messages,\n tools: toolsWithJsonSchemas,\n temperature,\n topP,\n maxTokens,\n metadata,\n request: this.effectiveRequest,\n modelOptions,\n systemPrompts: this.systemPrompts,\n })) {\n if (this.isAborted()) {\n break\n }\n\n this.totalChunkCount++\n\n yield chunk\n this.handleStreamChunk(chunk)\n\n if (this.earlyTermination) {\n break\n }\n }\n }\n\n private handleStreamChunk(chunk: StreamChunk): void {\n switch (chunk.type) {\n // AG-UI Events\n case 'TEXT_MESSAGE_CONTENT':\n this.handleTextMessageContentEvent(chunk)\n break\n case 'TOOL_CALL_START':\n this.handleToolCallStartEvent(chunk)\n break\n case 'TOOL_CALL_ARGS':\n this.handleToolCallArgsEvent(chunk)\n break\n case 'TOOL_CALL_END':\n this.handleToolCallEndEvent(chunk)\n break\n case 'RUN_FINISHED':\n this.handleRunFinishedEvent(chunk)\n break\n case 'RUN_ERROR':\n this.handleRunErrorEvent(chunk)\n break\n case 'STEP_FINISHED':\n this.handleStepFinishedEvent(chunk)\n break\n\n default:\n // RUN_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_END, STEP_STARTED,\n // STATE_SNAPSHOT, STATE_DELTA, CUSTOM\n // - no special handling needed in chat activity\n break\n }\n }\n\n // ===========================\n // AG-UI Event Handlers\n // ===========================\n\n private handleTextMessageContentEvent(chunk: TextMessageContentEvent): void {\n if (chunk.content) {\n this.accumulatedContent = chunk.content\n } else {\n this.accumulatedContent += chunk.delta\n }\n aiEventClient.emit('text:chunk:content', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n content: this.accumulatedContent,\n delta: chunk.delta,\n timestamp: Date.now(),\n })\n }\n\n private handleToolCallStartEvent(chunk: ToolCallStartEvent): void {\n this.toolCallManager.addToolCallStartEvent(chunk)\n aiEventClient.emit('text:chunk:tool-call', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n index: chunk.index ?? 0,\n arguments: '',\n timestamp: Date.now(),\n })\n }\n\n private handleToolCallArgsEvent(chunk: ToolCallArgsEvent): void {\n this.toolCallManager.addToolCallArgsEvent(chunk)\n aiEventClient.emit('text:chunk:tool-call', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n toolCallId: chunk.toolCallId,\n toolName: '',\n index: 0,\n arguments: chunk.delta,\n timestamp: Date.now(),\n })\n }\n\n private handleToolCallEndEvent(chunk: ToolCallEndEvent): void {\n this.toolCallManager.completeToolCall(chunk)\n aiEventClient.emit('text:chunk:tool-result', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n toolCallId: chunk.toolCallId,\n result: chunk.result || '',\n timestamp: Date.now(),\n })\n }\n\n private handleRunFinishedEvent(chunk: RunFinishedEvent): void {\n aiEventClient.emit('text:chunk:done', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n finishReason: chunk.finishReason,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n\n if (chunk.usage) {\n aiEventClient.emit('text:usage', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n usage: chunk.usage,\n timestamp: Date.now(),\n })\n }\n\n // Don't overwrite a tool_calls finishReason with a stop finishReason\n if (\n this.finishedEvent?.finishReason === 'tool_calls' &&\n chunk.finishReason === 'stop'\n ) {\n this.lastFinishReason = chunk.finishReason\n return\n }\n\n this.finishedEvent = chunk\n this.lastFinishReason = chunk.finishReason\n }\n\n private handleRunErrorEvent(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n ): void {\n aiEventClient.emit('text:chunk:error', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n error: chunk.error.message,\n timestamp: Date.now(),\n })\n this.earlyTermination = true\n this.shouldEmitStreamEnd = false\n }\n\n private handleStepFinishedEvent(\n chunk: Extract<StreamChunk, { type: 'STEP_FINISHED' }>,\n ): void {\n // Handle thinking/reasoning content from STEP_FINISHED events\n if (chunk.content || chunk.delta) {\n aiEventClient.emit('text:chunk:thinking', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n content: chunk.content || '',\n delta: chunk.delta,\n timestamp: Date.now(),\n })\n }\n }\n\n private async *checkForPendingToolCalls(): AsyncGenerator<\n StreamChunk,\n ToolPhaseResult,\n void\n > {\n const pendingToolCalls = this.getPendingToolCallsFromMessages()\n if (pendingToolCalls.length === 0) {\n return 'continue'\n }\n\n const finishEvent = this.createSyntheticFinishedEvent()\n\n const { approvals, clientToolResults } = this.collectClientState()\n\n const executionResult = await executeToolCalls(\n pendingToolCalls,\n this.tools,\n approvals,\n clientToolResults,\n )\n\n if (\n executionResult.needsApproval.length > 0 ||\n executionResult.needsClientExecution.length > 0\n ) {\n for (const chunk of this.emitApprovalRequests(\n executionResult.needsApproval,\n finishEvent,\n )) {\n yield chunk\n }\n\n for (const chunk of this.emitClientToolInputs(\n executionResult.needsClientExecution,\n finishEvent,\n )) {\n yield chunk\n }\n\n this.shouldEmitStreamEnd = false\n return 'wait'\n }\n\n const toolResultChunks = this.emitToolResults(\n executionResult.results,\n finishEvent,\n )\n\n for (const chunk of toolResultChunks) {\n yield chunk\n }\n\n return 'continue'\n }\n\n private async *processToolCalls(): AsyncGenerator<StreamChunk, void, void> {\n if (!this.shouldExecuteToolPhase()) {\n this.setToolPhase('stop')\n return\n }\n\n const toolCalls = this.toolCallManager.getToolCalls()\n const finishEvent = this.finishedEvent\n\n if (!finishEvent || toolCalls.length === 0) {\n this.setToolPhase('stop')\n return\n }\n\n this.addAssistantToolCallMessage(toolCalls)\n\n const { approvals, clientToolResults } = this.collectClientState()\n\n const executionResult = await executeToolCalls(\n toolCalls,\n this.tools,\n approvals,\n clientToolResults,\n )\n\n if (\n executionResult.needsApproval.length > 0 ||\n executionResult.needsClientExecution.length > 0\n ) {\n for (const chunk of this.emitApprovalRequests(\n executionResult.needsApproval,\n finishEvent,\n )) {\n yield chunk\n }\n\n for (const chunk of this.emitClientToolInputs(\n executionResult.needsClientExecution,\n finishEvent,\n )) {\n yield chunk\n }\n\n this.setToolPhase('wait')\n return\n }\n\n const toolResultChunks = this.emitToolResults(\n executionResult.results,\n finishEvent,\n )\n\n for (const chunk of toolResultChunks) {\n yield chunk\n }\n\n this.toolCallManager.clear()\n\n this.setToolPhase('continue')\n }\n\n private shouldExecuteToolPhase(): boolean {\n return (\n this.finishedEvent?.finishReason === 'tool_calls' &&\n this.tools.length > 0 &&\n this.toolCallManager.hasToolCalls()\n )\n }\n\n private addAssistantToolCallMessage(toolCalls: Array<ToolCall>): void {\n const messageId = this.currentMessageId ?? this.createId('msg')\n this.messages = [\n ...this.messages,\n {\n role: 'assistant',\n content: this.accumulatedContent || null,\n toolCalls,\n },\n ]\n\n aiEventClient.emit('text:message:created', {\n ...this.buildTextEventContext(),\n messageId,\n role: 'assistant',\n content: this.accumulatedContent || '',\n toolCalls,\n timestamp: Date.now(),\n })\n }\n\n /**\n * Extract client state (approvals and client tool results) from original messages.\n * This is called in the constructor BEFORE converting to ModelMessage format,\n * because the parts array (which contains approval state) is lost during conversion.\n */\n private extractClientStateFromOriginalMessages(\n originalMessages: Array<any>,\n ): {\n approvals: Map<string, boolean>\n clientToolResults: Map<string, any>\n } {\n const approvals = new Map<string, boolean>()\n const clientToolResults = new Map<string, any>()\n\n for (const message of originalMessages) {\n // Check for UIMessage format (parts array) - extract client tool results and approvals\n if (message.role === 'assistant' && message.parts) {\n for (const part of message.parts) {\n if (part.type === 'tool-call') {\n // Extract client tool results (tools without approval that have output)\n if (part.output !== undefined && !part.approval) {\n clientToolResults.set(part.id, part.output)\n }\n // Extract approval responses from UIMessage format parts\n if (\n part.approval?.id &&\n part.approval?.approved !== undefined &&\n part.state === 'approval-responded'\n ) {\n approvals.set(part.approval.id, part.approval.approved)\n }\n }\n }\n }\n }\n\n return { approvals, clientToolResults }\n }\n\n private collectClientState(): {\n approvals: Map<string, boolean>\n clientToolResults: Map<string, any>\n } {\n // Start with the initial client state extracted from original messages\n const approvals = new Map(this.initialApprovals)\n const clientToolResults = new Map(this.initialClientToolResults)\n\n // Also check current messages for any additional tool results (from server tools)\n for (const message of this.messages) {\n // Check for ModelMessage format (role: 'tool' messages contain tool results)\n // This handles results sent back from the client after executing client-side tools\n if (message.role === 'tool' && message.toolCallId) {\n // Parse content back to original output (was stringified by uiMessageToModelMessages)\n let output: unknown\n try {\n output = JSON.parse(message.content as string)\n } catch {\n output = message.content\n }\n clientToolResults.set(message.toolCallId, output)\n }\n }\n\n return { approvals, clientToolResults }\n }\n\n private emitApprovalRequests(\n approvals: Array<ApprovalRequest>,\n finishEvent: RunFinishedEvent,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const approval of approvals) {\n aiEventClient.emit('tools:approval:requested', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n toolCallId: approval.toolCallId,\n toolName: approval.toolName,\n input: approval.input,\n approvalId: approval.approvalId,\n timestamp: Date.now(),\n })\n\n // Emit a CUSTOM event for approval requests\n chunks.push({\n type: 'CUSTOM',\n timestamp: Date.now(),\n model: finishEvent.model,\n name: 'approval-requested',\n data: {\n toolCallId: approval.toolCallId,\n toolName: approval.toolName,\n input: approval.input,\n approval: {\n id: approval.approvalId,\n needsApproval: true,\n },\n },\n })\n }\n\n return chunks\n }\n\n private emitClientToolInputs(\n clientRequests: Array<ClientToolRequest>,\n finishEvent: RunFinishedEvent,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const clientTool of clientRequests) {\n aiEventClient.emit('tools:input:available', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n toolCallId: clientTool.toolCallId,\n toolName: clientTool.toolName,\n input: clientTool.input,\n timestamp: Date.now(),\n })\n\n // Emit a CUSTOM event for client tool inputs\n chunks.push({\n type: 'CUSTOM',\n timestamp: Date.now(),\n model: finishEvent.model,\n name: 'tool-input-available',\n data: {\n toolCallId: clientTool.toolCallId,\n toolName: clientTool.toolName,\n input: clientTool.input,\n },\n })\n }\n\n return chunks\n }\n\n private emitToolResults(\n results: Array<ToolResult>,\n finishEvent: RunFinishedEvent,\n ): Array<StreamChunk> {\n const chunks: Array<StreamChunk> = []\n\n for (const result of results) {\n aiEventClient.emit('tools:call:completed', {\n ...this.buildTextEventContext(),\n messageId: this.currentMessageId || undefined,\n toolCallId: result.toolCallId,\n toolName: result.toolName,\n result: result.result,\n duration: result.duration ?? 0,\n timestamp: Date.now(),\n })\n\n const content = JSON.stringify(result.result)\n\n // Emit TOOL_CALL_END event\n chunks.push({\n type: 'TOOL_CALL_END',\n timestamp: Date.now(),\n model: finishEvent.model,\n toolCallId: result.toolCallId,\n toolName: result.toolName,\n result: content,\n })\n\n this.messages = [\n ...this.messages,\n {\n role: 'tool',\n content,\n toolCallId: result.toolCallId,\n },\n ]\n\n aiEventClient.emit('text:message:created', {\n ...this.buildTextEventContext(),\n messageId: this.createId('msg'),\n role: 'tool',\n content,\n timestamp: Date.now(),\n })\n }\n\n return chunks\n }\n\n private getPendingToolCallsFromMessages(): Array<ToolCall> {\n // Build a set of completed tool IDs, but exclude tools with pendingExecution marker\n // (these are approved tools that still need to execute)\n const completedToolIds = new Set<string>()\n\n for (const message of this.messages) {\n if (message.role === 'tool' && message.toolCallId) {\n // Check if this is an approval response with pendingExecution marker\n let hasPendingExecution = false\n if (typeof message.content === 'string') {\n try {\n const parsed = JSON.parse(message.content)\n if (parsed.pendingExecution === true) {\n hasPendingExecution = true\n }\n } catch {\n // Not JSON, treat as regular tool result\n }\n }\n\n // Only mark as complete if NOT pending execution\n if (!hasPendingExecution) {\n completedToolIds.add(message.toolCallId)\n }\n }\n }\n\n const pending: Array<ToolCall> = []\n\n for (const message of this.messages) {\n if (message.role === 'assistant' && message.toolCalls) {\n for (const toolCall of message.toolCalls) {\n if (!completedToolIds.has(toolCall.id)) {\n pending.push(toolCall)\n }\n }\n }\n }\n\n return pending\n }\n\n private createSyntheticFinishedEvent(): RunFinishedEvent {\n return {\n type: 'RUN_FINISHED',\n runId: this.createId('pending'),\n model: this.params.model,\n timestamp: Date.now(),\n finishReason: 'tool_calls',\n }\n }\n\n private shouldContinue(): boolean {\n if (this.cyclePhase === 'executeToolCalls') {\n return true\n }\n\n return (\n this.loopStrategy({\n iterationCount: this.iterationCount,\n messages: this.messages,\n finishReason: this.lastFinishReason,\n }) && this.toolPhase === 'continue'\n )\n }\n\n private isAborted(): boolean {\n return !!this.effectiveSignal?.aborted\n }\n\n private buildTextEventContext(): {\n requestId: string\n streamId: string\n provider: string\n model: string\n clientId?: string\n source?: 'client' | 'server'\n systemPrompts?: Array<string>\n toolNames?: Array<string>\n options?: Record<string, unknown>\n modelOptions?: Record<string, unknown>\n messageCount: number\n hasTools: boolean\n streaming: boolean\n } {\n return {\n requestId: this.requestId,\n streamId: this.streamId,\n provider: this.adapter.name,\n model: this.params.model,\n clientId: this.params.conversationId,\n source: 'server',\n systemPrompts:\n this.systemPrompts.length > 0 ? this.systemPrompts : undefined,\n toolNames: this.eventToolNames,\n options: this.eventOptions,\n modelOptions: this.params.modelOptions as\n | Record<string, unknown>\n | undefined,\n messageCount: this.initialMessageCount,\n hasTools: this.tools.length > 0,\n streaming: true,\n }\n }\n\n private getContentString(content: ModelMessage['content']): string {\n if (typeof content === 'string') return content\n const text =\n content\n ?.map((part) => (part.type === 'text' ? part.content : ''))\n .join('') || ''\n return text\n }\n\n private setToolPhase(phase: ToolPhaseResult): void {\n this.toolPhase = phase\n if (phase === 'wait') {\n this.shouldEmitStreamEnd = false\n }\n }\n\n private createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n }\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Text activity - handles agentic text generation, one-shot text generation, and agentic structured output.\n *\n * This activity supports four modes:\n * 1. **Streaming agentic text**: Stream responses with automatic tool execution\n * 2. **Streaming one-shot text**: Simple streaming request/response without tools\n * 3. **Non-streaming text**: Returns collected text as a string (stream: false)\n * 4. **Agentic structured output**: Run tools, then return structured data\n *\n * @example Full agentic text (streaming with tools)\n * ```ts\n * import { chat } from '@tanstack/ai'\n * import { openaiText } from '@tanstack/ai-openai'\n *\n * for await (const chunk of chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'What is the weather?' }],\n * tools: [weatherTool]\n * })) {\n * if (chunk.type === 'content') {\n * console.log(chunk.delta)\n * }\n * }\n * ```\n *\n * @example One-shot text (streaming without tools)\n * ```ts\n * for await (const chunk of chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }]\n * })) {\n * console.log(chunk)\n * }\n * ```\n *\n * @example Non-streaming text (stream: false)\n * ```ts\n * const text = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Hello!' }],\n * stream: false\n * })\n * // text is a string with the full response\n * ```\n *\n * @example Agentic structured output (tools + structured response)\n * ```ts\n * import { z } from 'zod'\n *\n * const result = await chat({\n * adapter: openaiText('gpt-4o'),\n * messages: [{ role: 'user', content: 'Research and summarize the topic' }],\n * tools: [researchTool, analyzeTool],\n * outputSchema: z.object({\n * summary: z.string(),\n * keyPoints: z.array(z.string())\n * })\n * })\n * // result is { summary: string, keyPoints: string[] }\n * ```\n */\nexport function chat<\n TAdapter extends AnyTextAdapter,\n TSchema extends SchemaInput | undefined = undefined,\n TStream extends boolean = true,\n>(\n options: TextActivityOptions<TAdapter, TSchema, TStream>,\n): TextActivityResult<TSchema, TStream> {\n const { outputSchema, stream } = options\n\n // If outputSchema is provided, run agentic structured output\n if (outputSchema) {\n return runAgenticStructuredOutput(\n options as unknown as TextActivityOptions<\n AnyTextAdapter,\n SchemaInput,\n boolean\n >,\n ) as TextActivityResult<TSchema, TStream>\n }\n\n // If stream is explicitly false, run non-streaming text\n if (stream === false) {\n return runNonStreamingText(\n options as unknown as TextActivityOptions<\n AnyTextAdapter,\n undefined,\n false\n >,\n ) as TextActivityResult<TSchema, TStream>\n }\n\n // Otherwise, run streaming text (default)\n return runStreamingText(\n options as unknown as TextActivityOptions<AnyTextAdapter, undefined, true>,\n ) as TextActivityResult<TSchema, TStream>\n}\n\n/**\n * Run streaming text (agentic or one-shot depending on tools)\n */\nasync function* runStreamingText(\n options: TextActivityOptions<AnyTextAdapter, undefined, true>,\n): AsyncIterable<StreamChunk> {\n const { adapter, ...textOptions } = options\n const model = adapter.model\n\n const engine = new TextEngine({\n adapter,\n params: { ...textOptions, model } as TextOptions<\n Record<string, any>,\n Record<string, any>\n >,\n })\n\n for await (const chunk of engine.run()) {\n yield chunk\n }\n}\n\n/**\n * Run non-streaming text - collects all content and returns as a string.\n * Runs the full agentic loop (if tools are provided) but returns collected text.\n */\nfunction runNonStreamingText(\n options: TextActivityOptions<AnyTextAdapter, undefined, false>,\n): Promise<string> {\n // Run the streaming text and collect all text using streamToText\n const stream = runStreamingText(\n options as unknown as TextActivityOptions<AnyTextAdapter, undefined, true>,\n )\n\n return streamToText(stream)\n}\n\n/**\n * Run agentic structured output:\n * 1. Execute the full agentic loop (with tools)\n * 2. Once complete, call adapter.structuredOutput with the conversation context\n * 3. Validate and return the structured result\n */\nasync function runAgenticStructuredOutput<TSchema extends SchemaInput>(\n options: TextActivityOptions<AnyTextAdapter, TSchema, boolean>,\n): Promise<InferSchemaType<TSchema>> {\n const { adapter, outputSchema, ...textOptions } = options\n const model = adapter.model\n\n if (!outputSchema) {\n throw new Error('outputSchema is required for structured output')\n }\n\n // Create the engine and run the agentic loop\n const engine = new TextEngine({\n adapter,\n params: { ...textOptions, model } as TextOptions<\n Record<string, unknown>,\n Record<string, unknown>\n >,\n })\n\n // Consume the stream to run the agentic loop\n for await (const _chunk of engine.run()) {\n // Just consume the stream to execute the agentic loop\n }\n\n // Get the final messages from the engine (includes tool results)\n const finalMessages = engine.getMessages()\n\n // Build text options for structured output, excluding tools since\n // the agentic loop is complete and we only need the final response\n const {\n tools: _tools,\n agentLoopStrategy: _als,\n ...structuredTextOptions\n } = textOptions\n\n // Convert the schema to JSON Schema before passing to the adapter\n const jsonSchema = convertSchemaToJsonSchema(outputSchema)\n if (!jsonSchema) {\n throw new Error('Failed to convert output schema to JSON Schema')\n }\n\n // Call the adapter's structured output method with the conversation context\n // The adapter receives JSON Schema and can apply vendor-specific patches\n const result = await adapter.structuredOutput({\n chatOptions: {\n ...structuredTextOptions,\n model,\n messages: finalMessages,\n },\n outputSchema: jsonSchema,\n })\n\n // Validate the result against the schema if it's a Standard Schema\n if (isStandardSchema(outputSchema)) {\n return parseWithStandardSchema<InferSchemaType<TSchema>>(\n outputSchema,\n result.data,\n )\n }\n\n // For plain JSON Schema, return the data as-is\n return result.data as InferSchemaType<TSchema>\n}\n\n// Re-export adapter types\nexport type {\n TextAdapter,\n TextAdapterConfig,\n StructuredOutputOptions,\n StructuredOutputResult,\n} from './adapter'\nexport { BaseTextAdapter } from './adapter'\n"],"names":["maxIterationsStrategy"],"mappings":"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"}
@@ -69,6 +69,28 @@ function uiMessageToModelMessages(uiMessage) {
69
69
  });
70
70
  }
71
71
  }
72
+ for (const toolCallPart of toolCallParts) {
73
+ if (toolCallPart.output !== void 0 && !toolCallPart.approval) {
74
+ messageList.push({
75
+ role: "tool",
76
+ content: JSON.stringify(toolCallPart.output),
77
+ toolCallId: toolCallPart.id
78
+ });
79
+ }
80
+ if (toolCallPart.state === "approval-responded" && toolCallPart.approval?.approved !== void 0) {
81
+ const approved = toolCallPart.approval.approved;
82
+ messageList.push({
83
+ role: "tool",
84
+ content: JSON.stringify({
85
+ approved,
86
+ // Mark approved tools as pending execution - they still need to run
87
+ ...approved && { pendingExecution: true },
88
+ message: approved ? "User approved this action" : "User denied this action"
89
+ }),
90
+ toolCallId: toolCallPart.id
91
+ });
92
+ }
93
+ }
72
94
  return messageList;
73
95
  }
74
96
  function modelMessageToUIMessage(modelMessage, id) {
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sources":["../../../../src/activities/chat/messages.ts"],"sourcesContent":["import type {\n ContentPart,\n MessagePart,\n ModelMessage,\n TextPart,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n} from '../../types'\n// ===========================\n// Message Converters\n// ===========================\n\n/**\n * Helper to extract text content from string or ContentPart array\n * For multimodal content, this extracts only the text parts\n */\nfunction getTextContent(content: string | null | Array<ContentPart>): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // Extract text from ContentPart array\n return content\n .filter((part) => part.type === 'text')\n .map((part) => part.content)\n .join('')\n}\n\n/**\n * Convert UIMessages or ModelMessages to ModelMessages\n */\nexport function convertMessagesToModelMessages(\n messages: Array<UIMessage | ModelMessage>,\n): Array<ModelMessage> {\n const modelMessages: Array<ModelMessage> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n // UIMessage - convert to ModelMessages\n modelMessages.push(...uiMessageToModelMessages(msg))\n } else {\n // Already ModelMessage\n modelMessages.push(msg)\n }\n }\n return modelMessages\n}\n\n/**\n * Convert a UIMessage to ModelMessage(s)\n *\n * This conversion handles the parts-based structure:\n * - Text parts → content field\n * - ToolCall parts → toolCalls array\n * - ToolResult parts → separate role=\"tool\" messages\n *\n * @param uiMessage - The UIMessage to convert\n * @returns An array of ModelMessages (may be multiple if tool results are present)\n */\nexport function uiMessageToModelMessages(\n uiMessage: UIMessage,\n): Array<ModelMessage> {\n const messageList: Array<ModelMessage> = []\n\n // Skip system messages - they're handled via systemPrompts, not ModelMessages\n if (uiMessage.role === 'system') {\n return messageList\n }\n\n // Separate parts by type\n // Note: thinking parts are UI-only and not included in ModelMessages\n const textParts: Array<TextPart> = []\n const toolCallParts: Array<ToolCallPart> = []\n const toolResultParts: Array<ToolResultPart> = []\n\n for (const part of uiMessage.parts) {\n if (part.type === 'text') {\n textParts.push(part)\n } else if (part.type === 'tool-call') {\n toolCallParts.push(part)\n } else if (part.type === 'tool-result') {\n toolResultParts.push(part)\n }\n // thinking parts are skipped - they're UI-only\n }\n\n // Build the main message (user or assistant)\n const content = textParts.map((p) => p.content).join('') || null\n const toolCalls =\n toolCallParts.length > 0\n ? toolCallParts\n .filter(\n (p) =>\n p.state === 'input-complete' ||\n p.state === 'approval-responded' ||\n p.output !== undefined, // Include if has output (client tool result)\n )\n .map((p) => ({\n id: p.id,\n type: 'function' as const,\n function: {\n name: p.name,\n arguments: p.arguments,\n },\n }))\n : undefined\n\n // Create the main message\n if (uiMessage.role !== 'assistant' || content || !toolCalls) {\n messageList.push({\n role: uiMessage.role,\n content,\n ...(toolCalls && toolCalls.length > 0 && { toolCalls }),\n })\n } else if (toolCalls.length > 0) {\n // Assistant message with only tool calls\n messageList.push({\n role: 'assistant',\n content,\n toolCalls,\n })\n }\n\n // Add tool result messages (only completed ones)\n for (const toolResultPart of toolResultParts) {\n if (\n toolResultPart.state === 'complete' ||\n toolResultPart.state === 'error'\n ) {\n messageList.push({\n role: 'tool',\n content: toolResultPart.content,\n toolCallId: toolResultPart.toolCallId,\n })\n }\n }\n\n return messageList\n}\n\n/**\n * Convert a ModelMessage to UIMessage\n *\n * This conversion creates a parts-based structure:\n * - content field → TextPart\n * - toolCalls array → ToolCallPart[]\n * - role=\"tool\" messages should be converted separately and merged\n *\n * @param modelMessage - The ModelMessage to convert\n * @param id - Optional ID for the UIMessage (generated if not provided)\n * @returns A UIMessage with parts\n */\nexport function modelMessageToUIMessage(\n modelMessage: ModelMessage,\n id?: string,\n): UIMessage {\n const parts: Array<MessagePart> = []\n\n // Handle content (convert multimodal content to text for UI)\n const textContent = getTextContent(modelMessage.content)\n if (textContent) {\n parts.push({\n type: 'text',\n content: textContent,\n })\n }\n\n // Handle tool calls\n if (modelMessage.toolCalls && modelMessage.toolCalls.length > 0) {\n for (const toolCall of modelMessage.toolCalls) {\n parts.push({\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n state: 'input-complete', // Model messages have complete arguments\n })\n }\n }\n\n // Handle tool results (when role is \"tool\")\n if (modelMessage.role === 'tool' && modelMessage.toolCallId) {\n parts.push({\n type: 'tool-result',\n toolCallId: modelMessage.toolCallId,\n content: getTextContent(modelMessage.content),\n state: 'complete',\n })\n }\n\n return {\n id: id || generateMessageId(),\n role: modelMessage.role === 'tool' ? 'assistant' : modelMessage.role,\n parts,\n }\n}\n\n/**\n * Convert an array of ModelMessages to UIMessages\n *\n * This handles merging tool result messages with their corresponding assistant messages\n *\n * @param modelMessages - Array of ModelMessages to convert\n * @returns Array of UIMessages\n */\nexport function modelMessagesToUIMessages(\n modelMessages: Array<ModelMessage>,\n): Array<UIMessage> {\n const uiMessages: Array<UIMessage> = []\n let currentAssistantMessage: UIMessage | null = null\n\n for (const msg of modelMessages) {\n if (msg.role === 'tool') {\n // Tool result - merge into the last assistant message if possible\n if (\n currentAssistantMessage &&\n currentAssistantMessage.role === 'assistant'\n ) {\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId!,\n content: getTextContent(msg.content),\n state: 'complete',\n })\n } else {\n // No assistant message to merge into, create a standalone one\n const toolResultUIMessage = modelMessageToUIMessage(msg)\n uiMessages.push(toolResultUIMessage)\n }\n } else {\n // Regular message\n const uiMessage = modelMessageToUIMessage(msg)\n uiMessages.push(uiMessage)\n\n // Track assistant messages for potential tool result merging\n if (msg.role === 'assistant') {\n currentAssistantMessage = uiMessage\n } else {\n currentAssistantMessage = null\n }\n }\n }\n\n return uiMessages\n}\n\n/**\n * Normalize a message (UIMessage or ModelMessage) to a UIMessage\n * Ensures the message has an ID and createdAt timestamp\n *\n * @param message - Either a UIMessage or ModelMessage\n * @param generateId - Function to generate a message ID if needed\n * @returns A UIMessage with guaranteed id and createdAt\n */\nexport function normalizeToUIMessage(\n message: UIMessage | ModelMessage,\n generateId: () => string,\n): UIMessage {\n if ('parts' in message) {\n // Already a UIMessage\n return {\n ...message,\n id: message.id || generateId(),\n createdAt: message.createdAt || new Date(),\n }\n } else {\n // ModelMessage - convert to UIMessage\n return {\n ...modelMessageToUIMessage(message, generateId()),\n createdAt: new Date(),\n }\n }\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `msg-${Date.now()}-${Math.random().toString(36).substring(7)}`\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","sources":["../../../../src/activities/chat/messages.ts"],"sourcesContent":["import type {\n ContentPart,\n MessagePart,\n ModelMessage,\n TextPart,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n} from '../../types'\n// ===========================\n// Message Converters\n// ===========================\n\n/**\n * Helper to extract text content from string or ContentPart array\n * For multimodal content, this extracts only the text parts\n */\nfunction getTextContent(content: string | null | Array<ContentPart>): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // Extract text from ContentPart array\n return content\n .filter((part) => part.type === 'text')\n .map((part) => part.content)\n .join('')\n}\n\n/**\n * Convert UIMessages or ModelMessages to ModelMessages\n */\nexport function convertMessagesToModelMessages(\n messages: Array<UIMessage | ModelMessage>,\n): Array<ModelMessage> {\n const modelMessages: Array<ModelMessage> = []\n for (const msg of messages) {\n if ('parts' in msg) {\n // UIMessage - convert to ModelMessages\n modelMessages.push(...uiMessageToModelMessages(msg))\n } else {\n // Already ModelMessage\n modelMessages.push(msg)\n }\n }\n return modelMessages\n}\n\n/**\n * Convert a UIMessage to ModelMessage(s)\n *\n * This conversion handles the parts-based structure:\n * - Text parts → content field\n * - ToolCall parts → toolCalls array\n * - ToolResult parts → separate role=\"tool\" messages\n *\n * @param uiMessage - The UIMessage to convert\n * @returns An array of ModelMessages (may be multiple if tool results are present)\n */\nexport function uiMessageToModelMessages(\n uiMessage: UIMessage,\n): Array<ModelMessage> {\n const messageList: Array<ModelMessage> = []\n\n // Skip system messages - they're handled via systemPrompts, not ModelMessages\n if (uiMessage.role === 'system') {\n return messageList\n }\n\n // Separate parts by type\n // Note: thinking parts are UI-only and not included in ModelMessages\n const textParts: Array<TextPart> = []\n const toolCallParts: Array<ToolCallPart> = []\n const toolResultParts: Array<ToolResultPart> = []\n\n for (const part of uiMessage.parts) {\n if (part.type === 'text') {\n textParts.push(part)\n } else if (part.type === 'tool-call') {\n toolCallParts.push(part)\n } else if (part.type === 'tool-result') {\n toolResultParts.push(part)\n }\n // thinking parts are skipped - they're UI-only\n }\n\n // Build the main message (user or assistant)\n const content = textParts.map((p) => p.content).join('') || null\n const toolCalls =\n toolCallParts.length > 0\n ? toolCallParts\n .filter(\n (p) =>\n p.state === 'input-complete' ||\n p.state === 'approval-responded' ||\n p.output !== undefined, // Include if has output (client tool result)\n )\n .map((p) => ({\n id: p.id,\n type: 'function' as const,\n function: {\n name: p.name,\n arguments: p.arguments,\n },\n }))\n : undefined\n\n // Create the main message\n if (uiMessage.role !== 'assistant' || content || !toolCalls) {\n messageList.push({\n role: uiMessage.role,\n content,\n ...(toolCalls && toolCalls.length > 0 && { toolCalls }),\n })\n } else if (toolCalls.length > 0) {\n // Assistant message with only tool calls\n messageList.push({\n role: 'assistant',\n content,\n toolCalls,\n })\n }\n\n // Add tool result messages for completed tool calls\n // This includes:\n // 1. Explicit tool-result parts (from server tools)\n // 2. Client tool calls with output set\n // 3. Approval-responded tool calls (approval result)\n for (const toolResultPart of toolResultParts) {\n if (\n toolResultPart.state === 'complete' ||\n toolResultPart.state === 'error'\n ) {\n messageList.push({\n role: 'tool',\n content: toolResultPart.content,\n toolCallId: toolResultPart.toolCallId,\n })\n }\n }\n\n // Add tool result messages for client tool results (tools with output)\n // and approval responses (so iteration tracking works correctly)\n for (const toolCallPart of toolCallParts) {\n // Client tool with output - add as tool result\n if (toolCallPart.output !== undefined && !toolCallPart.approval) {\n messageList.push({\n role: 'tool',\n content: JSON.stringify(toolCallPart.output),\n toolCallId: toolCallPart.id,\n })\n }\n\n // Approval response - add as tool result for iteration tracking\n // For APPROVED: includes pendingExecution marker so the tool still executes\n // For DENIED: just marks the tool as complete (no execution needed)\n if (\n toolCallPart.state === 'approval-responded' &&\n toolCallPart.approval?.approved !== undefined\n ) {\n const approved = toolCallPart.approval.approved\n messageList.push({\n role: 'tool',\n content: JSON.stringify({\n approved,\n // Mark approved tools as pending execution - they still need to run\n ...(approved && { pendingExecution: true }),\n message: approved\n ? 'User approved this action'\n : 'User denied this action',\n }),\n toolCallId: toolCallPart.id,\n })\n }\n }\n\n return messageList\n}\n\n/**\n * Convert a ModelMessage to UIMessage\n *\n * This conversion creates a parts-based structure:\n * - content field → TextPart\n * - toolCalls array → ToolCallPart[]\n * - role=\"tool\" messages should be converted separately and merged\n *\n * @param modelMessage - The ModelMessage to convert\n * @param id - Optional ID for the UIMessage (generated if not provided)\n * @returns A UIMessage with parts\n */\nexport function modelMessageToUIMessage(\n modelMessage: ModelMessage,\n id?: string,\n): UIMessage {\n const parts: Array<MessagePart> = []\n\n // Handle content (convert multimodal content to text for UI)\n const textContent = getTextContent(modelMessage.content)\n if (textContent) {\n parts.push({\n type: 'text',\n content: textContent,\n })\n }\n\n // Handle tool calls\n if (modelMessage.toolCalls && modelMessage.toolCalls.length > 0) {\n for (const toolCall of modelMessage.toolCalls) {\n parts.push({\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n state: 'input-complete', // Model messages have complete arguments\n })\n }\n }\n\n // Handle tool results (when role is \"tool\")\n if (modelMessage.role === 'tool' && modelMessage.toolCallId) {\n parts.push({\n type: 'tool-result',\n toolCallId: modelMessage.toolCallId,\n content: getTextContent(modelMessage.content),\n state: 'complete',\n })\n }\n\n return {\n id: id || generateMessageId(),\n role: modelMessage.role === 'tool' ? 'assistant' : modelMessage.role,\n parts,\n }\n}\n\n/**\n * Convert an array of ModelMessages to UIMessages\n *\n * This handles merging tool result messages with their corresponding assistant messages\n *\n * @param modelMessages - Array of ModelMessages to convert\n * @returns Array of UIMessages\n */\nexport function modelMessagesToUIMessages(\n modelMessages: Array<ModelMessage>,\n): Array<UIMessage> {\n const uiMessages: Array<UIMessage> = []\n let currentAssistantMessage: UIMessage | null = null\n\n for (const msg of modelMessages) {\n if (msg.role === 'tool') {\n // Tool result - merge into the last assistant message if possible\n if (\n currentAssistantMessage &&\n currentAssistantMessage.role === 'assistant'\n ) {\n currentAssistantMessage.parts.push({\n type: 'tool-result',\n toolCallId: msg.toolCallId!,\n content: getTextContent(msg.content),\n state: 'complete',\n })\n } else {\n // No assistant message to merge into, create a standalone one\n const toolResultUIMessage = modelMessageToUIMessage(msg)\n uiMessages.push(toolResultUIMessage)\n }\n } else {\n // Regular message\n const uiMessage = modelMessageToUIMessage(msg)\n uiMessages.push(uiMessage)\n\n // Track assistant messages for potential tool result merging\n if (msg.role === 'assistant') {\n currentAssistantMessage = uiMessage\n } else {\n currentAssistantMessage = null\n }\n }\n }\n\n return uiMessages\n}\n\n/**\n * Normalize a message (UIMessage or ModelMessage) to a UIMessage\n * Ensures the message has an ID and createdAt timestamp\n *\n * @param message - Either a UIMessage or ModelMessage\n * @param generateId - Function to generate a message ID if needed\n * @returns A UIMessage with guaranteed id and createdAt\n */\nexport function normalizeToUIMessage(\n message: UIMessage | ModelMessage,\n generateId: () => string,\n): UIMessage {\n if ('parts' in message) {\n // Already a UIMessage\n return {\n ...message,\n id: message.id || generateId(),\n createdAt: message.createdAt || new Date(),\n }\n } else {\n // ModelMessage - convert to UIMessage\n return {\n ...modelMessageToUIMessage(message, generateId()),\n createdAt: new Date(),\n }\n }\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `msg-${Date.now()}-${Math.random().toString(36).substring(7)}`\n}\n"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
  * Unified stream processing for both server and client.
5
5
  */
6
6
  export { StreamProcessor, createReplayStream } from './processor.js';
7
- export type { StreamProcessorEvents, StreamProcessorHandlers, StreamProcessorOptions, } from './processor.js';
7
+ export type { StreamProcessorEvents, StreamProcessorOptions } from './processor.js';
8
8
  export { ImmediateStrategy, PunctuationStrategy, BatchStrategy, WordBoundaryStrategy, CompositeStrategy, } from './strategies.js';
9
9
  export { PartialJSONParser, defaultJSONParser, parsePartialJSON, } from './json-parser.js';
10
10
  export type { JSONParser } from './json-parser.js';
@@ -1,5 +1,5 @@
1
- import { ChunkRecording, ChunkStrategy, ProcessorResult, ProcessorState, ToolCallState, ToolResultState } from './types.js';
2
- import { ModelMessage, StreamChunk, ToolCall, UIMessage } from '../../../types.js';
1
+ import { ChunkRecording, ChunkStrategy, ProcessorResult, ProcessorState, ToolCallState } from './types.js';
2
+ import { ModelMessage, StreamChunk, UIMessage } from '../../../types.js';
3
3
  /**
4
4
  * Events emitted by the StreamProcessor
5
5
  */
@@ -23,35 +23,13 @@ export interface StreamProcessorEvents {
23
23
  onToolCallStateChange?: (messageId: string, toolCallId: string, state: ToolCallState, args: string) => void;
24
24
  onThinkingUpdate?: (messageId: string, content: string) => void;
25
25
  }
26
- /**
27
- * Legacy handlers for backward compatibility
28
- * These are the old callback-style handlers
29
- */
30
- export interface StreamProcessorHandlers {
31
- onTextUpdate?: (content: string) => void;
32
- onThinkingUpdate?: (content: string) => void;
33
- onToolCallStart?: (index: number, id: string, name: string) => void;
34
- onToolCallDelta?: (index: number, args: string) => void;
35
- onToolCallComplete?: (index: number, id: string, name: string, args: string) => void;
36
- onToolCallStateChange?: (index: number, id: string, name: string, state: ToolCallState, args: string, parsedArgs?: any) => void;
37
- onToolResultStateChange?: (toolCallId: string, content: string, state: ToolResultState, error?: string) => void;
38
- onApprovalRequested?: (toolCallId: string, toolName: string, input: any, approvalId: string) => void;
39
- onToolInputAvailable?: (toolCallId: string, toolName: string, input: any) => void;
40
- onStreamEnd?: (content: string, toolCalls?: Array<ToolCall>) => void;
41
- onError?: (error: {
42
- message: string;
43
- code?: string;
44
- }) => void;
45
- }
46
26
  /**
47
27
  * Options for StreamProcessor
48
28
  */
49
29
  export interface StreamProcessorOptions {
50
30
  chunkStrategy?: ChunkStrategy;
51
- /** New event-driven handlers */
31
+ /** Event-driven handlers */
52
32
  events?: StreamProcessorEvents;
53
- /** Legacy callback handlers (for backward compatibility) */
54
- handlers?: StreamProcessorHandlers;
55
33
  jsonParser?: {
56
34
  parse: (jsonString: string) => any;
57
35
  };
@@ -80,7 +58,6 @@ export interface StreamProcessorOptions {
80
58
  export declare class StreamProcessor {
81
59
  private chunkStrategy;
82
60
  private events;
83
- private handlers;
84
61
  private jsonParser;
85
62
  private recordingEnabled;
86
63
  private messages;
@@ -148,37 +125,39 @@ export declare class StreamProcessor {
148
125
  */
149
126
  processChunk(chunk: StreamChunk): void;
150
127
  /**
151
- * Handle a content chunk
128
+ * Handle TEXT_MESSAGE_CONTENT event
152
129
  */
153
- private handleContentChunk;
130
+ private handleTextMessageContentEvent;
154
131
  /**
155
- * Handle a tool call chunk
132
+ * Handle TOOL_CALL_START event
156
133
  */
157
- private handleToolCallChunk;
134
+ private handleToolCallStartEvent;
158
135
  /**
159
- * Handle a tool result chunk
136
+ * Handle TOOL_CALL_ARGS event
160
137
  */
161
- private handleToolResultChunk;
138
+ private handleToolCallArgsEvent;
162
139
  /**
163
- * Handle a done chunk
140
+ * Handle TOOL_CALL_END event
164
141
  */
165
- private handleDoneChunk;
142
+ private handleToolCallEndEvent;
166
143
  /**
167
- * Handle an error chunk
144
+ * Handle RUN_FINISHED event
168
145
  */
169
- private handleErrorChunk;
146
+ private handleRunFinishedEvent;
170
147
  /**
171
- * Handle a thinking chunk
148
+ * Handle RUN_ERROR event
172
149
  */
173
- private handleThinkingChunk;
150
+ private handleRunErrorEvent;
174
151
  /**
175
- * Handle an approval-requested chunk
152
+ * Handle STEP_FINISHED event (for thinking/reasoning content)
176
153
  */
177
- private handleApprovalRequestedChunk;
154
+ private handleStepFinishedEvent;
178
155
  /**
179
- * Handle a tool-input-available chunk
156
+ * Handle CUSTOM event
157
+ * Handles special custom events like 'tool-input-available' for client-side tool execution
158
+ * and 'approval-requested' for tool approval flows
180
159
  */
181
- private handleToolInputAvailableChunk;
160
+ private handleCustomEvent;
182
161
  /**
183
162
  * Detect if an incoming content chunk represents a NEW text segment
184
163
  */
@@ -212,7 +191,7 @@ export declare class StreamProcessor {
212
191
  */
213
192
  private getResult;
214
193
  /**
215
- * Get current processor state (legacy)
194
+ * Get current processor state
216
195
  */
217
196
  getState(): ProcessorState;
218
197
  /**