{"version":3,"file":"index.mjs","names":[],"sources":["../src/streaming.ts","../src/tools.ts","../src/messages.ts","../src/agent.ts"],"sourcesContent":["import {\n  EventType,\n  TextMessageChunkEvent,\n  ToolCallStartEvent,\n  ToolCallArgsEvent,\n  ToolCallEndEvent,\n  ToolCallResultEvent,\n  BaseEvent,\n} from \"@ag-ui/client\";\nimport { TokenUsage, tokenUsageFromLangChainMetadata } from \"@ag-ui/core\";\nimport {\n  AIMessage,\n  AIMessageChunk,\n  BaseMessageChunk,\n} from \"@langchain/core/messages\";\nimport { IterableReadableStream } from \"@langchain/core/utils/stream\";\nimport { randomUUID } from \"crypto\";\n\n/**\n * LangChain response types that can be returned from chainFn\n */\nexport type LangChainResponse =\n  | string\n  | AIMessage\n  | AIMessageChunk\n  | BaseMessageChunk\n  | IterableReadableStream<BaseMessageChunk>\n  | IterableReadableStream<AIMessageChunk>;\n\n/**\n * Helper type guards.\n *\n * These read LangChain's own message type tag (`_getType()`, or `type` on\n * newer cores) and duck-type chunks by their `concat` method, rather than\n * comparing `constructor.name`: a minifying bundler (e.g. a Next.js production\n * server build) renames the classes, which silently dropped every streamed\n * tool call.\n */\nfunction messageType(obj: any): string | undefined {\n  if (typeof obj?._getType === \"function\") return obj._getType();\n  return typeof obj?.type === \"string\" ? obj.type : undefined;\n}\n\nfunction isMessageChunk(obj: any): boolean {\n  return messageType(obj) !== undefined && typeof obj.concat === \"function\";\n}\n\nfunction isAIMessage(obj: any): obj is AIMessage {\n  return messageType(obj) === \"ai\" && !isMessageChunk(obj);\n}\n\nfunction isAIMessageChunk(obj: any): obj is AIMessageChunk {\n  return messageType(obj) === \"ai\" && isMessageChunk(obj);\n}\n\nfunction isBaseMessageChunk(obj: any): obj is BaseMessageChunk {\n  return isMessageChunk(obj) && messageType(obj) !== \"ai\";\n}\n\nfunction isStream(obj: any): obj is IterableReadableStream<any> {\n  return obj && typeof obj.getReader === \"function\";\n}\n\n/**\n * Converts LangChain response to AG-UI events\n */\nexport async function* streamLangChainResponse(\n  response: LangChainResponse,\n  usageSink?: (usage: TokenUsage) => void,\n): AsyncGenerator<BaseEvent> {\n  // Report provider-reported numeric token usage from a message/chunk to the\n  // optional sink. Reads only `usage_metadata` (+ model_name label) — never\n  // content. No-op when usage is absent or no sink was provided.\n  const reportUsage = (msg: any): void => {\n    if (!usageSink) return;\n    const usage = tokenUsageFromLangChainMetadata(msg?.usage_metadata, {\n      model: msg?.response_metadata?.model_name,\n    });\n    if (usage) usageSink(usage);\n  };\n\n  // 1. Handle string response\n  if (typeof response === \"string\") {\n    const messageId = randomUUID();\n    yield {\n      type: EventType.TEXT_MESSAGE_CHUNK,\n      role: \"assistant\",\n      messageId,\n      delta: response,\n    } as TextMessageChunkEvent;\n    return;\n  }\n\n  // 2. Handle AIMessage (complete message with content and tool calls)\n  if (isAIMessage(response)) {\n    reportUsage(response);\n    const messageId = randomUUID();\n\n    // Emit text content if present\n    if (response.content) {\n      yield {\n        type: EventType.TEXT_MESSAGE_CHUNK,\n        role: \"assistant\",\n        messageId,\n        delta: String(response.content),\n      } as TextMessageChunkEvent;\n    }\n\n    // Emit tool calls if present\n    if (response.tool_calls && response.tool_calls.length > 0) {\n      for (const toolCall of response.tool_calls) {\n        const toolCallId = toolCall.id || randomUUID();\n\n        yield {\n          type: EventType.TOOL_CALL_START,\n          parentMessageId: messageId,\n          toolCallId,\n          toolCallName: toolCall.name,\n        } as ToolCallStartEvent;\n\n        yield {\n          type: EventType.TOOL_CALL_ARGS,\n          toolCallId,\n          delta: JSON.stringify(toolCall.args),\n        } as ToolCallArgsEvent;\n\n        yield {\n          type: EventType.TOOL_CALL_END,\n          toolCallId,\n        } as ToolCallEndEvent;\n      }\n    }\n    return;\n  }\n\n  // 3. Handle BaseMessageChunk (single chunk)\n  if (isBaseMessageChunk(response)) {\n    if (response.content) {\n      const messageId = randomUUID();\n      yield {\n        type: EventType.TEXT_MESSAGE_CHUNK,\n        role: \"assistant\",\n        messageId,\n        delta: String(response.content),\n      } as TextMessageChunkEvent;\n    }\n    return;\n  }\n\n  // 4. Handle streaming responses\n  if (isStream(response)) {\n    const reader = response.getReader();\n    let mode: \"text\" | \"tool\" | null = null;\n    let currentMessageId = randomUUID();\n    let currentToolCallId: string | undefined;\n    let currentToolCallName: string | undefined;\n\n    // Tool call tracking\n    const toolCallState = {\n      id: null as string | null,\n      name: null as string | null,\n      index: null as number | null,\n      prevIndex: null as number | null,\n    };\n\n    try {\n      while (true) {\n        const { done, value } = await reader.read();\n        if (done) break;\n\n        // LangChain attaches usage_metadata to the final streamed chunk; report\n        // it as it arrives (no-op on chunks without it).\n        reportUsage(value);\n\n        let hasToolCall = false;\n        let toolCallId: string | undefined;\n        let toolCallName: string | undefined;\n        let toolCallArgs: string | undefined;\n        let textContent = \"\";\n\n        // Extract content from the chunk\n        if (value && value.content) {\n          textContent = Array.isArray(value.content)\n            ? ((value.content[0] as any)?.text ?? \"\")\n            : String(value.content);\n        }\n\n        // Check for tool calls in AIMessageChunk\n        if (isAIMessageChunk(value)) {\n          const chunk = value.tool_call_chunks?.[0];\n          if (chunk) {\n            hasToolCall = true;\n            toolCallArgs = chunk.args;\n            if (chunk.name) toolCallState.name = chunk.name;\n            if (chunk.index != null) {\n              toolCallState.index = chunk.index;\n              if (toolCallState.prevIndex == null) {\n                toolCallState.prevIndex = chunk.index;\n              }\n            }\n            if (chunk.id) {\n              toolCallState.id =\n                chunk.index != null ? `${chunk.id}-idx-${chunk.index}` : chunk.id;\n            }\n            toolCallName = toolCallState.name || undefined;\n            toolCallId = toolCallState.id || undefined;\n          }\n        }\n        // Check for tool calls in BaseMessageChunk\n        else if (isBaseMessageChunk(value)) {\n          const chunk = (value as any).additional_kwargs?.tool_calls?.[0];\n          if (chunk?.function) {\n            hasToolCall = true;\n            toolCallName = chunk.function.name;\n            toolCallId = chunk.id;\n            toolCallArgs = chunk.function.arguments;\n          }\n        }\n\n        // Mode transitions\n        if (mode === \"text\" && (hasToolCall || done)) {\n          mode = null;\n        } else if (mode === \"tool\" && (!hasToolCall || done)) {\n          if (currentToolCallId) {\n            yield {\n              type: EventType.TOOL_CALL_END,\n              toolCallId: currentToolCallId,\n            } as ToolCallEndEvent;\n          }\n          mode = null;\n        }\n\n        // Start new mode\n        if (mode === null) {\n          if (hasToolCall && toolCallId && toolCallName) {\n            mode = \"tool\";\n            currentToolCallId = toolCallId;\n            currentToolCallName = toolCallName;\n\n            yield {\n              type: EventType.TOOL_CALL_START,\n              parentMessageId: currentMessageId,\n              toolCallId,\n              toolCallName,\n            } as ToolCallStartEvent;\n          } else if (textContent) {\n            mode = \"text\";\n            // Text chunks don't need explicit start event in AG-UI\n          }\n        }\n\n        // Emit content\n        if (mode === \"text\" && textContent) {\n          yield {\n            type: EventType.TEXT_MESSAGE_CHUNK,\n            role: \"assistant\",\n            messageId: currentMessageId,\n            delta: textContent,\n          } as TextMessageChunkEvent;\n        } else if (mode === \"tool\" && toolCallArgs) {\n          // Handle multiple tool calls with different indices\n          if (\n            toolCallState.index !== toolCallState.prevIndex &&\n            currentToolCallId\n          ) {\n            yield {\n              type: EventType.TOOL_CALL_END,\n              toolCallId: currentToolCallId,\n            } as ToolCallEndEvent;\n\n            currentToolCallId = toolCallId;\n            yield {\n              type: EventType.TOOL_CALL_START,\n              parentMessageId: currentMessageId,\n              toolCallId: currentToolCallId!,\n              toolCallName: currentToolCallName!,\n            } as ToolCallStartEvent;\n\n            toolCallState.prevIndex = toolCallState.index;\n          }\n\n          yield {\n            type: EventType.TOOL_CALL_ARGS,\n            toolCallId: currentToolCallId!,\n            delta: toolCallArgs,\n          } as ToolCallArgsEvent;\n        }\n      }\n\n      // Final cleanup\n      if (mode === \"tool\" && currentToolCallId) {\n        yield {\n          type: EventType.TOOL_CALL_END,\n          toolCallId: currentToolCallId,\n        } as ToolCallEndEvent;\n      }\n    } finally {\n      reader.releaseLock();\n    }\n    return;\n  }\n\n  // Unsupported type - throw error\n  throw new Error(\n    `Unsupported LangChain response type: ${typeof response}`\n  );\n}\n","import { DynamicStructuredTool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\n\n/**\n * JSON Schema type definition\n */\ninterface JsonSchema {\n  type: \"object\" | \"string\" | \"number\" | \"boolean\" | \"array\";\n  description?: string;\n  properties?: Record<string, JsonSchema>;\n  required?: string[];\n  items?: JsonSchema;\n}\n\n/**\n * AG-UI Tool definition\n */\ninterface AGUITool {\n  name: string;\n  description: string;\n  parameters: JsonSchema;\n}\n\n/**\n * Converts JSON Schema to Zod schema\n */\nfunction convertJsonSchemaToZod(jsonSchema: JsonSchema, required: boolean): z.ZodSchema {\n  if (jsonSchema.type === \"object\") {\n    const spec: { [key: string]: z.ZodSchema } = {};\n\n    if (!jsonSchema.properties || !Object.keys(jsonSchema.properties).length) {\n      return !required ? z.object(spec).optional() : z.object(spec);\n    }\n\n    for (const [key, value] of Object.entries(jsonSchema.properties)) {\n      spec[key] = convertJsonSchemaToZod(\n        value,\n        jsonSchema.required ? jsonSchema.required.includes(key) : false\n      );\n    }\n    let schema = z.object(spec).describe(jsonSchema.description ?? \"\");\n    return required ? schema : schema.optional();\n  } else if (jsonSchema.type === \"string\") {\n    let schema = z.string().describe(jsonSchema.description ?? \"\");\n    return required ? schema : schema.optional();\n  } else if (jsonSchema.type === \"number\") {\n    let schema = z.number().describe(jsonSchema.description ?? \"\");\n    return required ? schema : schema.optional();\n  } else if (jsonSchema.type === \"boolean\") {\n    let schema = z.boolean().describe(jsonSchema.description ?? \"\");\n    return required ? schema : schema.optional();\n  } else if (jsonSchema.type === \"array\") {\n    if (!jsonSchema.items) {\n      throw new Error(\"Array type must have items property\");\n    }\n    let itemSchema = convertJsonSchemaToZod(jsonSchema.items, true);\n    let schema = z.array(itemSchema).describe(jsonSchema.description ?? \"\");\n    return required ? schema : schema.optional();\n  }\n  throw new Error(\"Invalid JSON schema\");\n}\n\nexport type LangChainToolWithName = {\n  type: \"function\";\n  name?: string;\n  function: {\n    name: string;\n    description: string;\n    parameters: any;\n  },\n}\n\n/**\n * Converts AG-UI Tool to LangChain DynamicStructuredTool\n */\nexport function convertAGUIToolToLangChain(tool: AGUITool): DynamicStructuredTool {\n  const schema = convertJsonSchemaToZod(tool.parameters, true) as z.ZodTypeAny;\n\n  // Use explicit type annotation to avoid TS2589 \"Type instantiation is excessively deep\"\n  // caused by DynamicStructuredTool's complex generic inference\n  const toolInstance: DynamicStructuredTool = new (DynamicStructuredTool as any)({\n    name: tool.name,\n    description: tool.description,\n    schema: schema,\n    func: async () => {\n      return \"\";\n    },\n  });\n  return toolInstance;\n}\n\n/**\n * Converts array of AG-UI Tools to LangChain DynamicStructuredTools\n */\nexport function convertAGUIToolsToLangChain(tools: AGUITool[]): DynamicStructuredTool[] {\n  return tools.map(convertAGUIToolToLangChain);\n}\n","import { Message } from \"@ag-ui/client\";\nimport { contentHasMedia, contentToText } from \"@ag-ui/core\";\nimport type { ContentPart, ImagePart } from \"@ag-ui/core\";\nimport {\n  BaseMessage,\n  HumanMessage,\n  AIMessage,\n  SystemMessage,\n  ToolMessage,\n} from \"@langchain/core/messages\";\n\n/**\n * A LangChain content block this bridge produces for a user message: text, or\n * an image in the OpenAI-style `image_url` shape that LangChain's chat model\n * integrations understand (and that both @langchain/core 0.3 and 1.x accept\n * as message content).\n */\ntype LangChainUserContentBlock =\n  | { type: \"text\"; text: string }\n  | { type: \"image_url\"; image_url: { url: string } };\n\n/**\n * Warns about AG-UI content this bridge cannot hand to LangChain. Honours\n * `SUPPRESS_TRANSFORMATION_WARNINGS`, the switch the rest of AG-UI's\n * transformation warnings sit behind.\n */\nfunction warnDropped(message: string): void {\n  if (\n    typeof process !== \"undefined\" &&\n    typeof process.env !== \"undefined\" &&\n    Boolean(process.env.SUPPRESS_TRANSFORMATION_WARNINGS)\n  ) {\n    return;\n  }\n  console.warn(message);\n}\n\n/**\n * The URL a LangChain `image_url` block can carry for an AG-UI image part:\n * the URL itself, or a data URL for inline bytes. A provider file handle has\n * no portable LangChain shape, so it yields undefined and is dropped.\n */\nfunction imagePartUrl(part: ImagePart): string | undefined {\n  switch (part.source.type) {\n    case \"url\":\n      return part.source.value;\n    case \"data\":\n      return `data:${part.source.mimeType};base64,${part.source.value}`;\n    default:\n      return undefined;\n  }\n}\n\n/**\n * Maps user-message content parts to LangChain content. Text parts become\n * text blocks and URL/inline image parts become `image_url` blocks; anything\n * else (audio, video, documents, provider file handles) has no portable\n * LangChain shape and is dropped with a warning. Text-only content stays a\n * plain string, the shape every model accepts.\n */\nfunction convertUserContent(\n  messageId: string,\n  parts: ContentPart[],\n): string | LangChainUserContentBlock[] {\n  const blocks: LangChainUserContentBlock[] = [];\n  const dropped: string[] = [];\n\n  for (const part of parts) {\n    if (part.type === \"text\") {\n      blocks.push({ type: \"text\", text: part.text });\n    } else if (part.type === \"image\") {\n      const url = imagePartUrl(part);\n      if (url !== undefined) {\n        blocks.push({ type: \"image_url\", image_url: { url } });\n      } else {\n        dropped.push(`image (${part.source.type} source)`);\n      }\n    } else {\n      dropped.push(part.type);\n    }\n  }\n\n  if (dropped.length > 0) {\n    warnDropped(\n      `[ag-ui][langchain] User message '${messageId}' carries content parts this bridge cannot pass to LangChain; dropping: ${dropped.join(\", \")}.`,\n    );\n  }\n\n  if (blocks.every((block) => block.type === \"text\")) {\n    return contentToText(parts);\n  }\n  return blocks;\n}\n\n/**\n * The content of a LangChain ToolMessage for an AG-UI tool result. AG-UI\n * content parts are not LangChain content blocks, and provider support for\n * media in tool results varies, so the result is flattened to its text; any\n * media it carried is dropped with a warning.\n */\nfunction convertToolContent(\n  toolCallId: string,\n  content: string | ContentPart[],\n): string {\n  if (contentHasMedia(content)) {\n    warnDropped(\n      `[ag-ui][langchain] The result of tool call '${toolCallId}' carries media content parts; only its text parts are passed to LangChain and the rest is dropped.`,\n    );\n  }\n  return contentToText(content);\n}\n\n/**\n * Converts AG-UI Message to LangChain BaseMessage\n */\nexport function convertAGUIMessageToLangChain(message: Message): BaseMessage {\n  // User message\n  if (message.role === \"user\") {\n    // Handle string content\n    if (typeof message.content === \"string\") {\n      return new HumanMessage(message.content);\n    }\n    // Handle array content: text and image parts map to LangChain blocks\n    if (Array.isArray(message.content)) {\n      return new HumanMessage({\n        content: convertUserContent(message.id, message.content),\n      });\n    }\n    return new HumanMessage(\"\");\n  }\n\n  // Assistant message\n  if (message.role === \"assistant\") {\n    const toolCalls = message.toolCalls?.map((tc) => ({\n      id: tc.id,\n      name: tc.function.name,\n      args: JSON.parse(tc.function.arguments),\n    })) || [];\n\n    return new AIMessage({\n      content: message.content || \"\",\n      tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n    });\n  }\n\n  // Tool/Function result message\n  if (message.role === \"tool\") {\n    return new ToolMessage({\n      content: convertToolContent(message.toolCallId, message.content),\n      tool_call_id: message.toolCallId,\n      // Carry the AG-UI failure signal onto LangChain's tool-result status, so a\n      // client-reported tool failure is not delivered to the model as a success.\n      status: message.error ? \"error\" : \"success\",\n    });\n  }\n\n  // System message\n  if (message.role === \"system\") {\n    return new SystemMessage(message.content as string);\n  }\n\n  // Fallback - treat as human message\n  return new HumanMessage(String(message.content || \"\"));\n}\n\n/**\n * Converts array of AG-UI Messages to LangChain BaseMessages\n */\nexport function convertAGUIMessagesToLangChain(messages: Message[]): BaseMessage[] {\n  return messages.map(convertAGUIMessageToLangChain);\n}\n","import {\n  AbstractAgent,\n  BaseEvent,\n  RunAgentInput,\n  EventType,\n  RunStartedEvent,\n  RunFinishedEvent,\n  RunErrorEvent,\n} from \"@ag-ui/client\";\nimport { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { BaseMessage } from \"@langchain/core/messages\";\nimport { DynamicStructuredTool } from \"@langchain/core/tools\";\nimport { PROTOCOL_VERSION, TokenUsage, aggregateTokenUsage } from \"@ag-ui/core\";\nimport { LangChainResponse, streamLangChainResponse } from \"./streaming\";\nimport { convertAGUIToolsToLangChain } from \"./tools\";\nimport { Observable } from \"rxjs\";\nimport { convertAGUIMessagesToLangChain } from \"./messages\";\n\n/**\n * Best-effort provider/model identity from a LangChain chat model instance.\n * `_llmType()` is the stable provider tag (e.g. \"openai\", \"anthropic\"); the\n * model name lives on `.model` (newer) or `.modelName` (older). Used to label\n * token usage that the streamed chunks left unlabeled.\n */\nfunction getModelIdentity(model: any): { provider?: string; model?: string } {\n  const provider = typeof model?._llmType === \"function\" ? model._llmType() : undefined;\n  const modelName = model?.model ?? model?.modelName;\n  return {\n    provider: typeof provider === \"string\" ? provider : undefined,\n    model: typeof modelName === \"string\" ? modelName : undefined,\n  };\n}\n\n/**\n * Parameters passed to chainFn callback\n */\nexport interface ChainFnParams {\n  /**\n   * Converted LangChain messages\n   */\n  messages: BaseMessage[];\n  /**\n   * Converted LangChain tools\n   */\n  tools: DynamicStructuredTool[];\n  /**\n   * Application state (can be edited via state tools)\n   */\n  state?: any;\n  /**\n   * Application context\n   */\n  context: Array<{ description: string; value: string }>;\n  /**\n   * Thread ID\n   */\n  threadId: string;\n  /**\n   * Run ID\n   */\n  runId: string;\n  /**\n   * Per-request HTTP headers forwarded from the runtime (e.g. x-aimock-context).\n   * Undefined when no headers are set on the agent instance.\n   */\n  headers?: Record<string, string>;\n}\n\n/**\n * Configuration for advanced usage with custom LangChain logic\n */\nexport interface LangChainAgentChainFnConfig {\n  /**\n   * Custom function that handles LangChain execution\n   * This allows full control over chains, graphs, and custom logic\n   */\n  chainFn: (\n    params: ChainFnParams,\n  ) => Promise<LangChainResponse> | LangChainResponse;\n}\n\n/**\n * Configuration for simple usage with direct model\n */\nexport interface LangChainAgentModelConfig {\n  /**\n   * LangChain chat model instance\n   */\n  model: BaseChatModel;\n  /**\n   * Optional system prompt\n   */\n  prompt?: string;\n  /**\n   * Options to pass to bindTools()\n   */\n  bindToolsOptions?: Record<string, any>;\n}\n\n/**\n * Dual configuration: either chainFn OR model\n */\nexport type LangChainAgentConfig =\n  | LangChainAgentChainFnConfig\n  | LangChainAgentModelConfig;\n\n/**\n * Type guard to check if config uses chainFn pattern\n */\nfunction isChainFnConfig(\n  config: LangChainAgentConfig,\n): config is LangChainAgentChainFnConfig {\n  return \"chainFn\" in config;\n}\n\n/**\n * LangChain Agent - bridges AG-UI and LangChain ecosystems\n *\n * Supports two usage patterns:\n *\n * 1. Advanced (chainFn): Full control over LangChain execution\n * ```typescript\n * new LangChainAgent({\n *   chainFn: async ({ messages, tools }) => {\n *     const model = new ChatOpenAI({ model: \"gpt-4\" });\n *     return model.bindTools(tools).stream(messages);\n *   }\n * })\n * ```\n *\n * 2. Simple (model): Direct model usage with automatic plumbing\n * ```typescript\n * new LangChainAgent({\n *   model: new ChatOpenAI({ model: \"gpt-4\" }),\n *   prompt: \"You are a helpful assistant\"\n * })\n * ```\n */\nexport class LangChainAgent extends AbstractAgent {\n  private abortController?: AbortController;\n\n  /**\n   * Per-request HTTP headers to forward to the underlying LLM provider.\n   * Set by CopilotKit Runtime via configureAgentForRequest() to propagate\n   * x-aimock-context, x-aimock-strict, x-test-id, and other forwarded headers.\n   */\n  public headers?: Record<string, string>;\n\n  constructor(private config: LangChainAgentConfig) {\n    super();\n  }\n\n  public run(input: RunAgentInput): Observable<BaseEvent> {\n    return new Observable<BaseEvent>((subscriber) => {\n      // Emit RUN_STARTED\n      const startEvent: RunStartedEvent = {\n        type: EventType.RUN_STARTED,\n        threadId: input.threadId,\n        runId: input.runId,\n        protocolVersion: PROTOCOL_VERSION,\n      };\n      subscriber.next(startEvent);\n\n      // Set up abort controller\n      const abortController = new AbortController();\n      this.abortController = abortController;\n\n      // Execute async logic\n      (async () => {\n        try {\n          // Convert AG-UI messages to LangChain messages\n          const langchainMessages = convertAGUIMessagesToLangChain(\n            input.messages,\n          );\n\n          // Add system message if using model config with prompt\n          if (!isChainFnConfig(this.config) && this.config.prompt) {\n            const systemPrompt = this.buildSystemPrompt(\n              this.config.prompt,\n              input.context,\n              input.state,\n            );\n            langchainMessages.unshift({\n              content: systemPrompt,\n              getType: () => \"system\",\n            } as BaseMessage);\n          }\n\n          // Convert AG-UI tools to LangChain tools\n          const langchainTools = convertAGUIToolsToLangChain(\n            input.tools as any[],\n          );\n\n          let response: LangChainResponse;\n\n          // Provider/model labels for usage entries whose chunks didn't carry\n          // them. Known only in the direct-model pattern (we hold the model).\n          let modelIdentity: { provider?: string; model?: string } = {};\n\n          // Execute based on configuration pattern\n          if (isChainFnConfig(this.config)) {\n            // Pattern A: User-provided chainFn — expose headers so user code can forward them\n            response = await this.config.chainFn({\n              messages: langchainMessages,\n              tools: langchainTools,\n              state: input.state,\n              context: input.context,\n              threadId: input.threadId,\n              runId: input.runId,\n              headers: this.headers,\n            });\n          } else {\n            // Pattern B: Direct model usage\n            const modelConfig = this.config as LangChainAgentModelConfig;\n            modelIdentity = getModelIdentity(modelConfig.model);\n            const boundModel = modelConfig.bindToolsOptions\n              ? modelConfig.model.bindTools?.(\n                  langchainTools,\n                  modelConfig.bindToolsOptions,\n                )\n              : modelConfig.model.bindTools?.(langchainTools);\n\n            // `options.headers` is a provider-specific extension supported by @langchain/openai\n            // and @langchain/anthropic via their underlying SDK's RequestOptions. It is NOT\n            // part of BaseChatModel's typed surface — hence the explicit shape annotation.\n            const callOptions: {\n              signal: AbortSignal;\n              options?: { headers: Record<string, string> };\n            } = {\n              signal: abortController.signal,\n            };\n\n            if (this.headers && Object.keys(this.headers).length > 0) {\n              callOptions.options = { headers: this.headers };\n            }\n\n            const model = boundModel || modelConfig.model;\n            response = await model.stream(\n              langchainMessages,\n              callOptions as Parameters<typeof model.stream>[1],\n            );\n          }\n\n          // Collect provider-reported token usage as the response streams, then\n          // attach the per-(provider, model) aggregate to the terminal event.\n          const usageEntries: TokenUsage[] = [];\n\n          // Stream the response and emit AG-UI events\n          for await (const event of streamLangChainResponse(response, (u) =>\n            usageEntries.push(u),\n          )) {\n            if (abortController.signal.aborted) {\n              break;\n            }\n            subscriber.next(event);\n          }\n\n          // Emit RUN_FINISHED if not aborted\n          if (!abortController.signal.aborted) {\n            // Fill provider/model from the configured model when the streamed\n            // chunks didn't include them (LangChain-JS streaming often omits\n            // model_name), so downstream can compare usage per provider/model.\n            const labeled = usageEntries.map((u) => ({\n              ...u,\n              ...(u.provider == null && modelIdentity.provider != null\n                ? { provider: modelIdentity.provider }\n                : {}),\n              ...(u.model == null && modelIdentity.model != null\n                ? { model: modelIdentity.model }\n                : {}),\n            }));\n            const usage = aggregateTokenUsage(labeled);\n            const finishedEvent: RunFinishedEvent = {\n              type: EventType.RUN_FINISHED,\n              threadId: input.threadId,\n              runId: input.runId,\n              ...(usage.length > 0 ? { usage } : {}),\n            };\n            subscriber.next(finishedEvent);\n          }\n\n          subscriber.complete();\n        } catch (error) {\n          if (!abortController.signal.aborted) {\n            const errorEvent: RunErrorEvent = {\n              type: EventType.RUN_ERROR,\n              message: error instanceof Error ? error.message : String(error),\n            };\n            subscriber.next(errorEvent);\n            subscriber.error(error);\n          } else {\n            subscriber.complete();\n          }\n        } finally {\n          this.abortController = undefined;\n        }\n      })();\n\n      // Cleanup function\n      return () => {\n        abortController.abort();\n      };\n    });\n  }\n\n  /**\n   * Build system prompt with context and state\n   */\n  private buildSystemPrompt(\n    prompt: string,\n    context: Array<{ description: string; value: string }>,\n    state?: any,\n  ): string {\n    const parts: string[] = [prompt];\n\n    // Add context if present\n    if (context && context.length > 0) {\n      parts.push(\"\\n## Context from the application\\n\");\n      for (const ctx of context) {\n        parts.push(`${ctx.description}:\\n${ctx.value}\\n`);\n      }\n    }\n\n    // Add state if present\n    if (state !== undefined && state !== null) {\n      const hasState =\n        typeof state !== \"object\" || Object.keys(state).length > 0;\n      if (hasState) {\n        parts.push(\n          \"\\n## Application State\\n\" +\n            \"This is state from the application.\\n\" +\n            `\\`\\`\\`json\\n${JSON.stringify(state, null, 2)}\\n\\`\\`\\`\\n`,\n        );\n      }\n    }\n\n    return parts.join(\"\");\n  }\n\n  clone(): LangChainAgent {\n    const cloned = new LangChainAgent(this.config);\n    if (this.headers) {\n      cloned.headers = { ...this.headers };\n    }\n    return cloned;\n  }\n\n  abortRun(): void {\n    this.abortController?.abort();\n  }\n}\n"],"mappings":"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"}