import { createDebugLogger } from "../utils/debugLog.js";
import { ApprovalType as ApprovalTypeEnum, ApprovalStatus, LLMErrorCode } from "@dexto/core";
import { generateMessageId } from "../utils/idGenerator.js";
import { checkForSplit } from "../utils/streamSplitter.js";
import { formatToolHeader, shouldHideTool } from "../utils/messageFormatting.js";
import { isAutoApprovableInEditMode } from "../utils/toolUtils.js";
import { captureAnalytics } from "../host/index.js";
import chalk from "chalk";
function buildErrorContent(error, prefix) {
  const errorMessage = error instanceof Error ? error.message : String(error);
  let errorContent = `${prefix}${errorMessage}`;
  if (error instanceof Error && "recovery" in error && error.recovery) {
    const recoveryMessages = Array.isArray(error.recovery) ? error.recovery : [error.recovery];
    errorContent += "\n\n" + recoveryMessages.map((msg) => `\u{1F4A1} ${msg}`).join("\n");
  }
  return errorContent;
}
function getErrorCode(error) {
  if (typeof error !== "object" || error === null) {
    return null;
  }
  const code = Reflect.get(error, "code");
  return typeof code === "string" && code.length > 0 ? code : null;
}
function extractInsufficientCreditsBalance(error) {
  if (typeof error === "object" && error !== null) {
    const context = Reflect.get(error, "context");
    if (typeof context === "object" && context !== null) {
      const balance = Reflect.get(context, "balance");
      if (typeof balance === "number" && Number.isFinite(balance)) {
        return balance;
      }
      if (typeof balance === "string") {
        const parsed2 = Number.parseFloat(balance);
        if (Number.isFinite(parsed2)) {
          return parsed2;
        }
      }
    }
  }
  const message = error instanceof Error ? error.message : String(error);
  const match = message.match(/Balance:\s*\$?(-?[\d.]+)/i);
  if (!match) {
    return null;
  }
  const parsed = Number.parseFloat(match[1] ?? "");
  return Number.isFinite(parsed) ? parsed : null;
}
function buildInsufficientCreditsContent(balance) {
  if (balance === null) {
    return "Out of Dexto Nova credits.\nUse the billing prompt to top up and retry your request.";
  }
  return `Out of Dexto Nova credits. Current balance: $${balance.toFixed(2)}.
Use the billing prompt to top up and retry your request.`;
}
function hasMeaningfulTokenUsageForAnalytics(tokenUsage, estimatedCost) {
  if (estimatedCost !== void 0) {
    return true;
  }
  return (tokenUsage.inputTokens ?? 0) > 0 || (tokenUsage.outputTokens ?? 0) > 0 || (tokenUsage.reasoningTokens ?? 0) > 0 || (tokenUsage.cacheReadTokens ?? 0) > 0 || (tokenUsage.cacheWriteTokens ?? 0) > 0 || (tokenUsage.totalTokens ?? 0) > 0;
}
async function processStream(iterator, setters, options) {
  const {
    setMessages,
    setPendingMessages,
    setDequeuedBuffer,
    setUi,
    setSession: _setSession,
    setSteerMessages,
    setQueuedMessages,
    setApproval,
    setApprovalQueue
  } = setters;
  const useStreaming = options?.useStreaming ?? true;
  const approvalIdToToolCallId = /* @__PURE__ */ new Map();
  const state = {
    messageId: null,
    content: "",
    reasoning: "",
    reasoningFinalized: false,
    lastInputTokens: 0,
    cumulativeOutputTokens: 0,
    finalizedContent: "",
    splitCounter: 0,
    textFinalizedBeforeTool: false,
    nonStreamingAccumulatedText: "",
    nonStreamingAccumulatedReasoning: ""
  };
  let localPending = [];
  const extractTextContent = (content) => {
    return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
  };
  const formatQueuedMessagesForDisplay = (messages) => {
    const userMessages = messages.filter((message) => message.kind !== "background");
    if (userMessages.length === 0) {
      return "";
    }
    if (userMessages.length === 1) {
      return extractTextContent(userMessages[0]?.content ?? []) || "[attachment]";
    }
    return userMessages.map((message, index) => {
      const prefix = userMessages.length === 2 ? index === 0 ? "First" : "Also" : `[${index + 1}]`;
      const content = extractTextContent(message.content) || "[attachment]";
      return `${prefix}: ${content}`;
    }).join("\n\n");
  };
  const removeDequeuedMessagesFromState = (event) => {
    const dequeuedIds = new Set(
      event.ids ?? event.messages?.map((message) => message.id) ?? []
    );
    if (dequeuedIds.size === 0) {
      if (event.queue === "steer") {
        setSteerMessages([]);
      } else {
        setQueuedMessages([]);
      }
      return;
    }
    if (event.queue === "steer") {
      setSteerMessages((prev) => prev.filter((message) => !dequeuedIds.has(message.id)));
    } else {
      setQueuedMessages((prev) => prev.filter((message) => !dequeuedIds.has(message.id)));
    }
  };
  const finalizeMessage = (messageId, updates = {}) => {
    const msg = localPending.find((m) => m.id === messageId);
    if (msg) {
      setMessages((prev) => [...prev, { ...msg, ...updates }]);
    }
    localPending = localPending.filter((m) => m.id !== messageId);
    setPendingMessages(localPending);
  };
  const finalizeAllPending = () => {
    if (localPending.length > 0) {
      const toFinalize = [...localPending];
      setMessages((prev) => [...prev, ...toFinalize]);
    }
    localPending = [];
    setPendingMessages([]);
  };
  const flushDequeuedBuffer = () => {
    setDequeuedBuffer((buffer) => {
      if (buffer.length > 0) {
        setMessages((prev) => [...prev, ...buffer]);
      }
      return [];
    });
  };
  const addToPending = (msg) => {
    localPending = [...localPending, msg];
    setPendingMessages(localPending);
  };
  const updatePending = (messageId, updates) => {
    localPending = localPending.map((m) => m.id === messageId ? { ...m, ...updates } : m);
    setPendingMessages(localPending);
  };
  const removeFromPending = (messageId) => {
    localPending = localPending.filter((m) => m.id !== messageId);
    setPendingMessages(localPending);
  };
  const clearPending = () => {
    localPending = [];
    setPendingMessages([]);
  };
  const updatePendingStatus = (messageId, status) => {
    localPending = localPending.map(
      (msg) => msg.id === messageId ? { ...msg, toolStatus: status } : msg
    );
    setPendingMessages(localPending);
  };
  const progressiveFinalize = (content) => {
    const splitResult = checkForSplit(content);
    if (splitResult.shouldSplit && splitResult.before && splitResult.after !== void 0) {
      state.splitCounter++;
      const splitId = `${state.messageId}-split-${state.splitCounter}`;
      const beforeContent = splitResult.before;
      const afterContent = splitResult.after;
      const isFirstSplit = state.splitCounter === 1;
      if (state.messageId) {
        localPending = localPending.map(
          (m) => m.id === state.messageId ? { ...m, content: "", isContinuation: true } : m
        );
        setPendingMessages(localPending);
      }
      const splitReasoning = !state.reasoningFinalized && state.reasoning ? state.reasoning : void 0;
      setMessages((prev) => [
        ...prev,
        {
          id: splitId,
          role: "assistant",
          content: beforeContent,
          ...splitReasoning ? { reasoning: splitReasoning } : {},
          timestamp: /* @__PURE__ */ new Date(),
          isStreaming: false,
          // First split shows the indicator, subsequent splits are continuations
          isContinuation: !isFirstSplit
        }
      ]);
      if (splitReasoning) {
        state.reasoningFinalized = true;
        state.reasoning = "";
        if (state.messageId) {
          localPending = localPending.map(
            (m) => m.id === state.messageId ? { ...m, reasoning: void 0 } : m
          );
          setPendingMessages(localPending);
        }
      }
      if (state.messageId) {
        localPending = localPending.map(
          (m) => m.id === state.messageId ? { ...m, content: afterContent } : m
        );
        setPendingMessages(localPending);
      }
      state.finalizedContent += beforeContent;
      return afterContent;
    }
    return content;
  };
  const resolveFinalReasoning = (eventReasoning) => {
    if (state.reasoningFinalized) {
      return void 0;
    }
    if (!useStreaming && state.nonStreamingAccumulatedReasoning) {
      return state.nonStreamingAccumulatedReasoning;
    }
    return eventReasoning;
  };
  const debug = createDebugLogger("stream");
  debug.reset();
  debug.log("CONFIG", { useStreaming });
  try {
    for await (const event of iterator) {
      debug.log(`EVENT: ${event.name}`, {
        ...event.name === "llm:chunk" && "chunkType" in event && {
          chunkType: event.chunkType,
          contentLen: event.content?.length
        },
        ...event.name === "llm:tool-call" && "toolName" in event && {
          toolName: event.toolName
        }
      });
      switch (event.name) {
        case "llm:thinking": {
          debug.log("THINKING: resetting state", {
            prevMessageId: state.messageId,
            prevContentLen: state.content.length
          });
          flushDequeuedBuffer();
          setUi((prev) => ({ ...prev, isThinking: true }));
          state.messageId = null;
          state.content = "";
          state.reasoning = "";
          state.reasoningFinalized = false;
          state.lastInputTokens = 0;
          state.cumulativeOutputTokens = 0;
          state.finalizedContent = "";
          state.splitCounter = 0;
          state.textFinalizedBeforeTool = false;
          state.nonStreamingAccumulatedText = "";
          state.nonStreamingAccumulatedReasoning = "";
          break;
        }
        case "llm:chunk": {
          if (!useStreaming) {
            if (event.chunkType === "text") {
              state.nonStreamingAccumulatedText += event.content;
              debug.log("CHUNK (non-stream): accumulated", {
                chunkLen: event.content?.length,
                totalLen: state.nonStreamingAccumulatedText.length,
                preview: state.nonStreamingAccumulatedText.slice(0, 50)
              });
            } else if (event.chunkType === "reasoning") {
              state.nonStreamingAccumulatedReasoning += event.content;
            }
            break;
          }
          setUi((prev) => ({ ...prev, isThinking: false }));
          if (event.chunkType === "reasoning") {
            if (state.reasoningFinalized) break;
            if (!state.messageId) {
              const newId = generateMessageId("assistant");
              state.messageId = newId;
              state.reasoning = event.content;
              state.content = "";
              state.finalizedContent = "";
              state.splitCounter = 0;
              addToPending({
                id: newId,
                role: "assistant",
                content: "",
                reasoning: event.content,
                timestamp: /* @__PURE__ */ new Date(),
                isStreaming: true
              });
            } else {
              state.reasoning += event.content;
              updatePending(state.messageId, { reasoning: state.reasoning });
            }
            break;
          }
          if (event.chunkType === "text") {
            debug.log("CHUNK (stream): text", {
              hasMessageId: !!state.messageId,
              chunkLen: event.content?.length,
              currentContentLen: state.content.length,
              preview: event.content?.slice(0, 30)
            });
            if (!state.messageId) {
              const newId = generateMessageId("assistant");
              state.messageId = newId;
              state.content = event.content;
              state.reasoning = "";
              state.reasoningFinalized = false;
              state.finalizedContent = "";
              state.splitCounter = 0;
              addToPending({
                id: newId,
                role: "assistant",
                content: event.content,
                timestamp: /* @__PURE__ */ new Date(),
                isStreaming: true
              });
            } else {
              state.content += event.content;
              const pendingContent = progressiveFinalize(state.content);
              const splitOccurred = pendingContent !== state.content;
              state.content = pendingContent;
              if (!splitOccurred) {
                const messageId = state.messageId;
                const isContinuation = state.splitCounter > 0;
                updatePending(messageId, {
                  content: pendingContent,
                  isContinuation
                });
              }
            }
          }
          break;
        }
        case "llm:response": {
          if (!useStreaming) {
            setUi((prev) => ({ ...prev, isThinking: false }));
          }
          const rawInputTokens = event.tokenUsage.inputTokens ?? 0;
          const cacheWriteTokens = event.tokenUsage.cacheWriteTokens ?? 0;
          const inputTokens = Math.max(0, rawInputTokens - cacheWriteTokens);
          if (inputTokens > 0) {
            state.lastInputTokens = inputTokens;
          }
          if (event.tokenUsage.outputTokens) {
            state.cumulativeOutputTokens += event.tokenUsage.outputTokens;
          }
          if (hasMeaningfulTokenUsageForAnalytics(event.tokenUsage, event.estimatedCost)) {
            let estimateAccuracyPercent;
            const actualInputTokens = event.tokenUsage.inputTokens;
            if (event.estimatedInputTokens !== void 0 && actualInputTokens) {
              const diff = event.estimatedInputTokens - actualInputTokens;
              estimateAccuracyPercent = Math.round(diff / actualInputTokens * 100);
            }
            captureAnalytics("dexto_llm_tokens_consumed", {
              source: "cli",
              sessionId: event.sessionId,
              provider: event.provider,
              model: event.model,
              reasoningVariant: event.reasoningVariant ?? void 0,
              reasoningBudgetTokens: event.reasoningBudgetTokens ?? void 0,
              inputTokens: event.tokenUsage.inputTokens,
              outputTokens: event.tokenUsage.outputTokens,
              reasoningTokens: event.tokenUsage.reasoningTokens,
              totalTokens: event.tokenUsage.totalTokens,
              cacheReadTokens: event.tokenUsage.cacheReadTokens,
              cacheWriteTokens: event.tokenUsage.cacheWriteTokens,
              estimatedCostUsd: event.estimatedCost,
              inputCostUsd: event.costBreakdown?.inputUsd,
              outputCostUsd: event.costBreakdown?.outputUsd,
              reasoningCostUsd: event.costBreakdown?.reasoningUsd,
              cacheReadCostUsd: event.costBreakdown?.cacheReadUsd,
              cacheWriteCostUsd: event.costBreakdown?.cacheWriteUsd,
              estimatedInputTokens: event.estimatedInputTokens,
              estimateAccuracyPercent
            });
          }
          const finalContent = event.content || "";
          if (!state.reasoningFinalized && !state.reasoning && event.reasoning) {
            state.reasoning = event.reasoning;
          }
          if (state.messageId) {
            const messageId = state.messageId;
            const content = state.content || finalContent;
            const reasoning = !state.reasoningFinalized && state.reasoning ? state.reasoning : void 0;
            finalizeMessage(messageId, {
              content,
              ...reasoning ? { reasoning } : {},
              isStreaming: false
            });
            state.messageId = null;
            state.content = "";
            state.reasoning = "";
            state.reasoningFinalized = false;
          } else if (finalContent && !state.textFinalizedBeforeTool) {
            const reasoning = resolveFinalReasoning(event.reasoning);
            setMessages((prev) => [
              ...prev,
              {
                id: generateMessageId("assistant"),
                role: "assistant",
                content: finalContent,
                ...reasoning ? { reasoning } : {},
                timestamp: /* @__PURE__ */ new Date(),
                isStreaming: false
              }
            ]);
          }
          if (!useStreaming) {
            state.nonStreamingAccumulatedText = "";
            state.nonStreamingAccumulatedReasoning = "";
          }
          state.textFinalizedBeforeTool = false;
          break;
        }
        case "interaction:blocked": {
          setUi((prev) => ({ ...prev, isThinking: false }));
          setMessages((prev) => [
            ...prev,
            {
              id: event.messageId,
              role: "assistant",
              content: event.content,
              timestamp: /* @__PURE__ */ new Date(),
              isStreaming: false
            }
          ]);
          break;
        }
        case "llm:tool-call": {
          if (shouldHideTool(event.toolName)) {
            break;
          }
          debug.log("TOOL-CALL: state check", {
            toolName: event.toolName,
            hasMessageId: !!state.messageId,
            contentLen: state.content.length,
            nonStreamAccumLen: state.nonStreamingAccumulatedText.length,
            contentPreview: state.content.slice(0, 50),
            nonStreamPreview: state.nonStreamingAccumulatedText.slice(0, 50),
            useStreaming
          });
          if (state.messageId) {
            if (state.content) {
              const messageId = state.messageId;
              const content = state.content;
              const isContinuation = state.splitCounter > 0;
              debug.log("TOOL-CALL: finalizing pending message", {
                messageId,
                contentLen: content.length
              });
              finalizeMessage(messageId, {
                content,
                isStreaming: false,
                isContinuation
              });
              state.textFinalizedBeforeTool = true;
              const hadReasoning = state.reasoningFinalized || !!state.reasoning;
              state.reasoning = "";
              state.reasoningFinalized = hadReasoning;
            } else {
              const hasReasoning = !state.reasoningFinalized && !!state.reasoning;
              if (hasReasoning) {
                finalizeMessage(state.messageId, {
                  content: "",
                  reasoning: state.reasoning,
                  isStreaming: false
                });
                state.reasoning = "";
                state.reasoningFinalized = true;
              } else {
                debug.log("TOOL-CALL: removing empty pending message", {
                  messageId: state.messageId
                });
                removeFromPending(state.messageId);
              }
            }
            state.messageId = null;
            state.content = "";
          } else {
            debug.log("TOOL-CALL: no pending message to finalize");
          }
          if (!useStreaming && (state.nonStreamingAccumulatedText || state.nonStreamingAccumulatedReasoning)) {
            const content = state.nonStreamingAccumulatedText;
            const reasoning = state.nonStreamingAccumulatedReasoning || void 0;
            debug.log("TOOL-CALL: adding non-stream accumulated content", {
              contentLen: content.length,
              reasoningLen: reasoning?.length ?? 0
            });
            setMessages((prev) => [
              ...prev,
              {
                id: generateMessageId("assistant"),
                role: "assistant",
                content,
                ...reasoning ? { reasoning } : {},
                timestamp: /* @__PURE__ */ new Date(),
                isStreaming: false
              }
            ]);
            const hadText = !!content;
            const hadReasoning = !!reasoning;
            state.nonStreamingAccumulatedText = "";
            state.nonStreamingAccumulatedReasoning = "";
            if (hadText) {
              state.textFinalizedBeforeTool = true;
            }
            if (hadReasoning) {
              state.reasoningFinalized = true;
            }
          }
          const toolMessageId = event.callId ? `tool-${event.callId}` : generateMessageId("tool");
          const { header: toolContent } = formatToolHeader({
            toolName: event.toolName,
            args: event.args || {},
            ...event.presentationSnapshot !== void 0 && {
              presentationSnapshot: event.presentationSnapshot
            }
          });
          let finalToolContent = toolContent;
          const callDescription = event.callDescription;
          if (typeof callDescription === "string" && callDescription.trim().length > 0) {
            finalToolContent += `
${chalk.dim(callDescription)}`;
          }
          addToPending({
            id: toolMessageId,
            role: "tool",
            content: finalToolContent,
            timestamp: /* @__PURE__ */ new Date(),
            toolStatus: "pending"
          });
          captureAnalytics("dexto_tool_called", {
            source: "cli",
            sessionId: event.sessionId,
            toolName: event.toolName
          });
          break;
        }
        case "llm:tool-result": {
          if (shouldHideTool(event.toolName)) {
            break;
          }
          const sanitized = event.sanitized;
          const toolDisplayData = sanitized?.meta?.display;
          const toolContent = sanitized?.content;
          let resultPreview = "";
          try {
            const result = event.sanitized || event.rawResult;
            if (result) {
              let resultStr = "";
              if (typeof result === "string") {
                resultStr = result;
              } else if (result && typeof result === "object") {
                const resultObj = result;
                if (Array.isArray(resultObj.content)) {
                  resultStr = resultObj.content.filter(
                    (item) => typeof item === "object" && item !== null && "type" in item && item.type === "text"
                  ).map((item) => item.text || "").join("\n");
                } else if (resultObj.text) {
                  resultStr = resultObj.text;
                } else {
                  resultStr = JSON.stringify(result, null, 2);
                }
              }
              const maxChars = 400;
              if (resultStr.length > maxChars) {
                resultPreview = resultStr.slice(0, maxChars) + "\n...";
              } else {
                resultPreview = resultStr;
              }
            }
          } catch {
            resultPreview = "";
          }
          if (event.callId) {
            const toolMessageId = `tool-${event.callId}`;
            finalizeMessage(toolMessageId, {
              toolResult: resultPreview,
              toolStatus: "finished",
              isError: !event.success,
              ...toolDisplayData && { toolDisplayData },
              ...toolContent && { toolContent }
            });
          }
          if (event.toolName === "plan_review" && event.success !== false) {
            try {
              const planReviewResult = event.rawResult;
              if (planReviewResult?.approved) {
                setUi((prev) => ({
                  ...prev,
                  planModeActive: false,
                  planModeInitialized: false
                }));
              }
            } catch {
            }
          }
          captureAnalytics("dexto_tool_result", {
            source: "cli",
            sessionId: event.sessionId,
            toolName: event.toolName || "unknown",
            success: event.success !== false
          });
          break;
        }
        case "llm:error": {
          const insufficientCredits = getErrorCode(event.error) === LLMErrorCode.INSUFFICIENT_CREDITS;
          const insufficientCreditsBalance = insufficientCredits ? extractInsufficientCreditsBalance(event.error) : null;
          const errorContent = insufficientCredits ? buildInsufficientCreditsContent(insufficientCreditsBalance) : buildErrorContent(event.error, "Error: ");
          setMessages((prev) => [
            ...prev,
            {
              id: generateMessageId("error"),
              role: "system",
              content: errorContent,
              timestamp: /* @__PURE__ */ new Date()
            }
          ]);
          if (event.recoverable !== true) {
            if (state.messageId) {
              removeFromPending(state.messageId);
              state.messageId = null;
              state.content = "";
            }
            clearPending();
            setUi((prev) => ({
              ...prev,
              isProcessing: false,
              isCancelling: false,
              isThinking: false,
              ...insufficientCredits ? {
                activeOverlay: "insufficient-credits",
                insufficientCredits: {
                  balanceUsd: insufficientCreditsBalance
                }
              } : {}
            }));
          }
          break;
        }
        case "llm:unsupported-input": {
          const warningContent = "\u26A0\uFE0F  " + event.errors.join("\n\u26A0\uFE0F  ");
          setMessages((prev) => [
            ...prev,
            {
              id: generateMessageId("warning"),
              role: "system",
              content: warningContent,
              timestamp: /* @__PURE__ */ new Date()
            }
          ]);
          break;
        }
        case "run:complete": {
          const { durationMs } = event;
          const totalTokens = state.lastInputTokens + state.cumulativeOutputTokens;
          finalizeAllPending();
          if (durationMs > 0 || totalTokens > 0) {
            const summaryMessage = {
              id: generateMessageId("summary"),
              role: "system",
              content: "",
              // Content rendered via styledType
              timestamp: /* @__PURE__ */ new Date(),
              styledType: "run-summary",
              styledData: {
                durationMs,
                totalTokens
              }
            };
            setMessages((prev) => [...prev, summaryMessage]);
          }
          setUi((prev) => ({
            ...prev,
            isProcessing: false,
            isCancelling: false,
            isThinking: false,
            isCompacting: false
          }));
          options.soundService?.playCompleteSound();
          break;
        }
        case "message:dequeued": {
          finalizeAllPending();
          if (event.messages?.some((message) => message.kind === "background")) {
            const userText = event.messages ? formatQueuedMessagesForDisplay(event.messages) : "";
            if (userText) {
              setMessages((prev) => [
                ...prev,
                {
                  id: generateMessageId("user"),
                  role: "user",
                  content: userText,
                  timestamp: /* @__PURE__ */ new Date()
                }
              ]);
            }
            removeDequeuedMessagesFromState(event);
            setUi((prev) => ({ ...prev, isProcessing: true }));
            break;
          }
          const textContent = extractTextContent(event.content);
          if (textContent || event.content.length > 0) {
            setMessages((prev) => [
              ...prev,
              {
                id: generateMessageId("user"),
                role: "user",
                content: textContent || "[attachment]",
                timestamp: /* @__PURE__ */ new Date()
              }
            ]);
          }
          removeDequeuedMessagesFromState(event);
          setUi((prev) => ({ ...prev, isProcessing: true }));
          break;
        }
        case "tool:running": {
          const runningToolId = `tool-${event.toolCallId}`;
          updatePendingStatus(runningToolId, "running");
          break;
        }
        // Note: context:compacting and context:compacted are handled in useAgentEvents.ts
        // as the single source of truth for both manual /compact and auto-compaction
        case "approval:request": {
          const bypassPermissions = options.bypassPermissionsRef.current;
          const autoApproveEdits = options.autoApproveEditsRef.current;
          const { eventBus } = options;
          if (bypassPermissions && (event.type === ApprovalTypeEnum.TOOL_APPROVAL || event.type === ApprovalTypeEnum.COMMAND_APPROVAL)) {
            if (event.type === ApprovalTypeEnum.TOOL_APPROVAL) {
              const { toolName } = event.metadata;
              if (toolName === "plan_create" || toolName === "plan_review") {
                setUi((prev) => ({
                  ...prev,
                  planModeActive: false,
                  planModeInitialized: false
                }));
              }
            }
            eventBus.emit("approval:response", {
              approvalId: event.approvalId,
              status: ApprovalStatus.APPROVED,
              sessionId: event.sessionId,
              hostRuntime: event.hostRuntime,
              data: {}
            });
            break;
          }
          if (autoApproveEdits && event.type === ApprovalTypeEnum.TOOL_APPROVAL) {
            const { toolName, approvalKey } = event.metadata;
            if (approvalKey === void 0 && isAutoApprovableInEditMode(toolName)) {
              eventBus.emit("approval:response", {
                approvalId: event.approvalId,
                status: ApprovalStatus.APPROVED,
                sessionId: event.sessionId,
                hostRuntime: event.hostRuntime,
                data: {}
              });
              break;
            }
          }
          const toolCallId = event.type === ApprovalTypeEnum.TOOL_APPROVAL ? event.metadata.toolCallId : void 0;
          if (toolCallId) {
            approvalIdToToolCallId.set(event.approvalId, toolCallId);
            updatePendingStatus(`tool-${toolCallId}`, "pending_approval");
          }
          if (event.type === ApprovalTypeEnum.TOOL_APPROVAL || event.type === ApprovalTypeEnum.COMMAND_APPROVAL || event.type === ApprovalTypeEnum.ELICITATION) {
            const newApproval = {
              approvalId: event.approvalId,
              type: event.type,
              timestamp: event.timestamp,
              metadata: event.metadata
            };
            if (event.sessionId !== void 0) {
              newApproval.sessionId = event.sessionId;
            }
            if (event.hostRuntime !== void 0) {
              newApproval.hostRuntime = event.hostRuntime;
            }
            if (event.timeout !== void 0) {
              newApproval.timeout = event.timeout;
            }
            setApproval((current) => {
              if (current !== null) {
                setApprovalQueue((queue) => [...queue, newApproval]);
                return current;
              }
              setUi((prev) => ({ ...prev, activeOverlay: "approval" }));
              return newApproval;
            });
            options.soundService?.playApprovalSound();
          }
          break;
        }
        case "approval:response": {
          const { approvalId } = event;
          const toolCallId = approvalIdToToolCallId.get(approvalId);
          if (toolCallId) {
            approvalIdToToolCallId.delete(approvalId);
            if (event.status !== ApprovalStatus.APPROVED) {
              finalizeMessage(`tool-${toolCallId}`, {
                toolStatus: "finished",
                toolResult: "Cancelled",
                isError: true
              });
            }
          }
          setApprovalQueue((queue) => queue.filter((a) => a.approvalId !== approvalId));
          setApproval((currentApproval) => {
            if (currentApproval?.approvalId !== approvalId) {
              return currentApproval;
            }
            setApprovalQueue((queue) => {
              if (queue.length > 0) {
                const [next, ...rest] = queue;
                if (next) {
                  setApproval(next);
                  setUi((prev) => ({ ...prev, activeOverlay: "approval" }));
                  return rest;
                }
              }
              setUi((prev) => ({ ...prev, activeOverlay: "none" }));
              return [];
            });
            return null;
          });
          break;
        }
        case "service:event": {
          debug.log("SERVICE-EVENT received", {
            service: event.service,
            eventType: event.event,
            toolCallId: event.toolCallId,
            sessionId: event.sessionId
          });
          if (event.service === "agent-spawner" && event.event === "progress") {
            const { toolCallId, data } = event;
            if (toolCallId && data && typeof data === "object") {
              const toolMessageId = `tool-${toolCallId}`;
              const progressData = data;
              debug.log("SERVICE-EVENT updating progress", {
                toolMessageId,
                toolsCalled: progressData.toolsCalled,
                currentTool: progressData.currentTool,
                tokenUsage: progressData.tokenUsage
              });
              updatePending(toolMessageId, {
                subAgentProgress: {
                  task: progressData.task,
                  agentId: progressData.agentId,
                  ...progressData.runtimeAgentId !== void 0 && {
                    runtimeAgentId: progressData.runtimeAgentId
                  },
                  ...progressData.subAgentLogFilePath !== void 0 && {
                    subAgentLogFilePath: progressData.subAgentLogFilePath
                  },
                  toolsCalled: progressData.toolsCalled,
                  currentTool: progressData.currentTool,
                  ...progressData.currentArgs && {
                    currentArgs: progressData.currentArgs
                  },
                  ...progressData.tokenUsage && {
                    tokenUsage: progressData.tokenUsage
                  }
                }
              });
            }
          }
          if (event.service === "todo" && event.event === "updated") {
            const { data, sessionId } = event;
            if (data && typeof data === "object" && sessionId) {
              const todoData = data;
              if (!Array.isArray(todoData.todos)) {
                debug.log("SERVICE-EVENT todo updated: invalid payload", {
                  sessionId
                });
                break;
              }
              debug.log("SERVICE-EVENT todo updated", {
                sessionId,
                todoCount: todoData.todos.length,
                stats: todoData.stats
              });
              if (options.setTodos) {
                options.setTodos(todoData.todos);
              }
            }
          }
          break;
        }
        // Ignore other events
        default:
          break;
      }
    }
  } catch (error) {
    if (error instanceof Error && error.name === "AbortError") {
      clearPending();
      setUi((prev) => ({
        ...prev,
        isProcessing: false,
        isCancelling: false,
        isThinking: false
      }));
    } else {
      clearPending();
      const errorContent = buildErrorContent(error, "Stream error: ");
      setMessages((prev) => [
        ...prev,
        {
          id: generateMessageId("error"),
          role: "system",
          content: errorContent,
          timestamp: /* @__PURE__ */ new Date()
        }
      ]);
      setUi((prev) => ({
        ...prev,
        isProcessing: false,
        isCancelling: false,
        isThinking: false
      }));
    }
  }
}
export {
  processStream
};
