{"version":3,"file":"aisdk.mjs","names":[],"sources":["../../../src/agent/converters/aisdk.ts"],"sourcesContent":["import {\n  BaseEvent,\n  EventType,\n  ReasoningEndEvent,\n  ReasoningMessageContentEvent,\n  ReasoningMessageEndEvent,\n  ReasoningMessageStartEvent,\n  ReasoningStartEvent,\n  TextMessageChunkEvent,\n  ToolCallArgsEvent,\n  ToolCallEndEvent,\n  ToolCallStartEvent,\n  ToolCallResultEvent,\n  StateSnapshotEvent,\n  StateDeltaEvent,\n} from \"@ag-ui/client\";\nimport { randomUUID } from \"@copilotkit/shared\";\n\n/**\n * Converts an AI SDK `fullStream` into AG-UI `BaseEvent` objects.\n *\n * This is a pure converter — it does NOT emit lifecycle events\n * (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class)\n * is responsible for those.\n *\n * Terminal stream events (finish, error, abort) cause the generator to\n * return so the caller can handle lifecycle appropriately.\n */\nexport async function* convertAISDKStream(\n  fullStream: AsyncIterable<unknown>,\n  abortSignal: AbortSignal,\n): AsyncGenerator<BaseEvent> {\n  let messageId = randomUUID();\n  let reasoningMessageId = randomUUID();\n  let isInReasoning = false;\n\n  const toolCallStates = new Map<\n    string,\n    {\n      started: boolean;\n      hasArgsDelta: boolean;\n      ended: boolean;\n      toolName?: string;\n    }\n  >();\n\n  const ensureToolCallState = (toolCallId: string) => {\n    let state = toolCallStates.get(toolCallId);\n    if (!state) {\n      state = { started: false, hasArgsDelta: false, ended: false };\n      toolCallStates.set(toolCallId, state);\n    }\n    return state;\n  };\n\n  /**\n   * Auto-close an open reasoning lifecycle.\n   * Some AI SDK providers (notably @ai-sdk/anthropic) never emit \"reasoning-end\",\n   * which leaves downstream state machines stuck. This helper emits the\n   * missing REASONING_MESSAGE_END + REASONING_END events so the stream\n   * can transition to text, tool-call, or finish phases.\n   */\n  function* closeReasoningIfOpen(): Generator<BaseEvent> {\n    if (!isInReasoning) return;\n    isInReasoning = false;\n    const reasoningMsgEnd: ReasoningMessageEndEvent = {\n      type: EventType.REASONING_MESSAGE_END,\n      messageId: reasoningMessageId,\n    };\n    yield reasoningMsgEnd;\n    const reasoningEnd: ReasoningEndEvent = {\n      type: EventType.REASONING_END,\n      messageId: reasoningMessageId,\n    };\n    yield reasoningEnd;\n  }\n\n  try {\n    for await (const part of fullStream) {\n      const p = part as Record<string, unknown>;\n\n      // Close any open reasoning lifecycle on every event except\n      // reasoning-delta, which arrives mid-block and must not interrupt it.\n      if (p.type !== \"reasoning-delta\") {\n        yield* closeReasoningIfOpen();\n      }\n\n      switch (p.type) {\n        case \"abort\": {\n          // Terminal — let the caller handle lifecycle\n          return;\n        }\n\n        case \"reasoning-start\": {\n          // Use SDK-provided id, or generate a fresh UUID if id is falsy/\"0\"\n          // to prevent consecutive reasoning blocks from sharing a messageId\n          const providedId = \"id\" in p ? p.id : undefined;\n          reasoningMessageId =\n            providedId && providedId !== \"0\"\n              ? (providedId as string)\n              : randomUUID();\n          const reasoningStartEvent: ReasoningStartEvent = {\n            type: EventType.REASONING_START,\n            messageId: reasoningMessageId,\n          };\n          yield reasoningStartEvent;\n          const reasoningMessageStart: ReasoningMessageStartEvent = {\n            type: EventType.REASONING_MESSAGE_START,\n            messageId: reasoningMessageId,\n            role: \"reasoning\",\n          };\n          yield reasoningMessageStart;\n          isInReasoning = true;\n          break;\n        }\n\n        case \"reasoning-delta\": {\n          const delta = (p.text as string) ?? \"\";\n          if (!delta) break; // skip — @ag-ui/core schema requires delta to be non-empty\n          const reasoningDeltaEvent: ReasoningMessageContentEvent = {\n            type: EventType.REASONING_MESSAGE_CONTENT,\n            messageId: reasoningMessageId,\n            delta,\n          };\n          yield reasoningDeltaEvent;\n          break;\n        }\n\n        case \"reasoning-end\": {\n          // closeReasoningIfOpen() already called before the switch — no-op here\n          // if the SDK never emits this event (e.g. @ai-sdk/anthropic).\n          break;\n        }\n\n        case \"tool-input-start\": {\n          const toolCallId = p.id as string;\n          const state = ensureToolCallState(toolCallId);\n          state.toolName = p.toolName as string;\n          if (!state.started) {\n            state.started = true;\n            const startEvent: ToolCallStartEvent = {\n              type: EventType.TOOL_CALL_START,\n              parentMessageId: messageId,\n              toolCallId,\n              toolCallName: p.toolName as string,\n            };\n            yield startEvent;\n          }\n          break;\n        }\n\n        case \"tool-input-delta\": {\n          const toolCallId = p.id as string;\n          const state = ensureToolCallState(toolCallId);\n          state.hasArgsDelta = true;\n          const argsEvent: ToolCallArgsEvent = {\n            type: EventType.TOOL_CALL_ARGS,\n            toolCallId,\n            delta: p.delta as string,\n          };\n          yield argsEvent;\n          break;\n        }\n\n        case \"tool-input-end\": {\n          // No direct event – the subsequent \"tool-call\" part marks completion.\n          break;\n        }\n\n        case \"text-start\": {\n          // New text message starting - use the SDK-provided id\n          // Use randomUUID() if part.id is falsy or \"0\" to prevent message merging issues\n          const providedId = \"id\" in p ? p.id : undefined;\n          messageId =\n            providedId && providedId !== \"0\"\n              ? (providedId as string)\n              : randomUUID();\n          break;\n        }\n\n        case \"text-delta\": {\n          // AI SDK text-delta events use 'text' (not 'delta')\n          const textDelta = \"text\" in p ? (p.text as string) : \"\";\n          const textEvent: TextMessageChunkEvent = {\n            type: EventType.TEXT_MESSAGE_CHUNK,\n            role: \"assistant\",\n            messageId,\n            delta: textDelta,\n          };\n          yield textEvent;\n          break;\n        }\n\n        case \"tool-call\": {\n          const toolCallId = p.toolCallId as string;\n          const state = ensureToolCallState(toolCallId);\n          state.toolName = (p.toolName as string) ?? state.toolName;\n\n          if (!state.started) {\n            state.started = true;\n            const startEvent: ToolCallStartEvent = {\n              type: EventType.TOOL_CALL_START,\n              parentMessageId: messageId,\n              toolCallId,\n              toolCallName: p.toolName as string,\n            };\n            yield startEvent;\n          }\n\n          if (!state.hasArgsDelta && \"input\" in p && p.input !== undefined) {\n            let serializedInput = \"\";\n            if (typeof p.input === \"string\") {\n              serializedInput = p.input;\n            } else {\n              try {\n                serializedInput = JSON.stringify(p.input);\n              } catch {\n                serializedInput = String(p.input);\n              }\n            }\n\n            if (serializedInput.length > 0) {\n              const argsEvent: ToolCallArgsEvent = {\n                type: EventType.TOOL_CALL_ARGS,\n                toolCallId,\n                delta: serializedInput,\n              };\n              yield argsEvent;\n              state.hasArgsDelta = true;\n            }\n          }\n\n          if (!state.ended) {\n            state.ended = true;\n            const endEvent: ToolCallEndEvent = {\n              type: EventType.TOOL_CALL_END,\n              toolCallId,\n            };\n            yield endEvent;\n          }\n          break;\n        }\n\n        case \"tool-result\": {\n          // AI SDK tool-result uses \"output\"; older versions used \"result\" — check both\n          const toolResult =\n            \"output\" in p ? p.output : \"result\" in p ? p.result : null;\n          const toolName = \"toolName\" in p ? (p.toolName as string) : \"\";\n          toolCallStates.delete(p.toolCallId as string);\n\n          // Check if this is a state update tool\n          if (\n            toolName === \"AGUISendStateSnapshot\" &&\n            toolResult &&\n            typeof toolResult === \"object\"\n          ) {\n            const snapshot = (toolResult as Record<string, unknown>).snapshot;\n            if (snapshot !== undefined) {\n              const stateSnapshotEvent: StateSnapshotEvent = {\n                type: EventType.STATE_SNAPSHOT,\n                snapshot,\n              };\n              yield stateSnapshotEvent;\n            }\n          } else if (\n            toolName === \"AGUISendStateDelta\" &&\n            toolResult &&\n            typeof toolResult === \"object\"\n          ) {\n            const delta = (toolResult as Record<string, unknown>).delta;\n            if (delta !== undefined) {\n              const stateDeltaEvent: StateDeltaEvent = {\n                type: EventType.STATE_DELTA,\n                delta,\n              };\n              yield stateDeltaEvent;\n            }\n          }\n\n          // Always emit the tool result event for the LLM\n          let serializedResult: string;\n          try {\n            serializedResult = JSON.stringify(toolResult);\n          } catch {\n            serializedResult = `[Unserializable tool result from ${toolName || \"unknown tool\"}]`;\n          }\n          const resultEvent: ToolCallResultEvent = {\n            type: EventType.TOOL_CALL_RESULT,\n            role: \"tool\",\n            messageId: randomUUID(),\n            toolCallId: p.toolCallId as string,\n            content: serializedResult,\n          };\n          yield resultEvent;\n          break;\n        }\n\n        case \"finish\": {\n          // Terminal — let the caller handle lifecycle\n          return;\n        }\n\n        case \"error\": {\n          if (abortSignal.aborted) {\n            return;\n          }\n          // Re-throw so the caller can emit RUN_ERROR\n          const err = p.error ?? p.message ?? p.cause;\n          if (err instanceof Error) throw err;\n          throw new Error(\n            typeof err === \"string\"\n              ? err\n              : `AI SDK stream error: ${JSON.stringify(p)}`,\n          );\n        }\n\n        default:\n          // Unknown event types are silently ignored\n          break;\n      }\n    }\n  } finally {\n    // Always close reasoning on exit (normal or exceptional)\n    yield* closeReasoningIfOpen();\n  }\n}\n"],"mappings":"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"}