import { jsx as _jsx } from "react/jsx-runtime";
import AssistantMessage from '../../../components/assistant-message.js';
import { ErrorMessage } from '../../../components/message-box.js';
import UserMessage from '../../../components/user-message.js';
import { parseToolCalls } from '../../../tool-calling/index.js';
import { getLogger } from '../../../utils/logging/index.js';
import { MessageBuilder } from '../../../utils/message-builder.js';
import { parseToolArguments } from '../../../utils/tool-args-parser.js';
import { displayToolResult } from '../../../utils/tool-result-display.js';
import { filterValidToolCalls } from '../utils/tool-filters.js';
import { executeToolsDirectly } from './tool-executor.js';
/**
 * Main conversation loop that processes assistant responses and handles tool calls.
 * This function orchestrates the entire conversation flow including:
 * - Streaming responses from the LLM
 * - Parsing and validating tool calls
 * - Executing or requesting confirmation for tools
 * - Handling errors and self-correction
 * - Managing the conversation state
 */
export const processAssistantResponse = async (params) => {
    const { systemMessage, messages, client, toolManager, abortController, setAbortController, setIsGenerating, setStreamingContent, setTokenCount, setMessages, addToChatQueue, getNextComponentKey, currentModel, developmentMode, nonInteractiveMode, conversationStateManager, onStartToolConfirmationFlow, onConversationComplete, } = params;
    // Ensure we have an abort controller for this request
    let controller = abortController;
    if (!controller) {
        controller = new AbortController();
        setAbortController(controller);
    }
    // Use streaming with callbacks
    setIsGenerating(true);
    setStreamingContent('');
    setTokenCount(0);
    const result = await client.chat([systemMessage, ...messages], toolManager?.getAllTools() || {}, {
        onToolExecuted: (toolCall, result) => {
            // Display formatter for auto-executed tools (after execution with results)
            void (async () => {
                const toolResult = {
                    tool_call_id: toolCall.id,
                    role: 'tool',
                    name: toolCall.function.name,
                    content: result,
                };
                await displayToolResult(toolCall, toolResult, toolManager, addToChatQueue, getNextComponentKey);
            })();
        },
        onFinish: () => {
            setIsGenerating(false);
        },
    }, controller.signal);
    if (!result || !result.choices || result.choices.length === 0) {
        throw new Error('No response received from model');
    }
    const message = result.choices[0].message;
    const toolCalls = message.tool_calls || null;
    const fullContent = message.content || '';
    // Parse any tool calls from content for non-tool-calling models
    const parseResult = parseToolCalls(fullContent);
    // Check for malformed tool calls and send error back to model for self-correction
    if (!parseResult.success) {
        const errorContent = `${parseResult.error}\n\n${parseResult.examples}`;
        // Display error to user
        addToChatQueue(_jsx(ErrorMessage, { message: errorContent, hideBox: true }, `malformed-tool-${Date.now()}`));
        // Create assistant message with the malformed content (so model knows what it said)
        const assistantMsgWithError = {
            role: 'assistant',
            content: fullContent,
        };
        // Create a user message with the error feedback for the model
        const errorFeedbackMessage = {
            role: 'user',
            content: `Your previous response contained a malformed tool call. ${errorContent}\n\nPlease try again using the correct format.`,
        };
        // Update messages and continue conversation loop for self-correction
        const malformedBuilder = new MessageBuilder(messages);
        malformedBuilder
            .addAssistantMessage(assistantMsgWithError)
            .addMessage(errorFeedbackMessage);
        const updatedMessagesWithError = malformedBuilder.build();
        setMessages(updatedMessagesWithError);
        // Clear streaming state before recursing
        setIsGenerating(false);
        setStreamingContent('');
        // Continue the main conversation loop with error message as context
        await processAssistantResponse({
            ...params,
            messages: updatedMessagesWithError,
        });
        return;
    }
    const parsedToolCalls = parseResult.toolCalls;
    const cleanedContent = parseResult.cleanedContent;
    // Display the assistant response (cleaned of any tool calls)
    if (cleanedContent.trim()) {
        addToChatQueue(_jsx(AssistantMessage, { message: cleanedContent, model: currentModel }, `assistant-${getNextComponentKey()}`));
    }
    // Merge structured tool calls from AI SDK with content-parsed tool calls
    const allToolCalls = [...(toolCalls || []), ...parsedToolCalls];
    const { validToolCalls, errorResults } = filterValidToolCalls(allToolCalls, toolManager);
    // Add assistant message to conversation history only if it has content or tool_calls
    // Empty assistant messages cause API errors: "Assistant message must have either content or tool_calls"
    const assistantMsg = {
        role: 'assistant',
        content: cleanedContent,
        tool_calls: validToolCalls.length > 0 ? validToolCalls : undefined,
    };
    const hasValidAssistantMessage = cleanedContent.trim() || validToolCalls.length > 0;
    // Build updated messages array using MessageBuilder
    const builder = new MessageBuilder(messages);
    // Add auto-executed messages (assistant + tool results) from AI SDK multi-step execution
    // This ensures they're counted in usage tracking and included in context
    if (result.autoExecutedMessages && result.autoExecutedMessages.length > 0) {
        builder.addAutoExecutedMessages(result.autoExecutedMessages);
    }
    // Add the final assistant message if it has content or tool calls
    if (hasValidAssistantMessage) {
        builder.addAssistantMessage(assistantMsg);
        // Update conversation state with assistant message
        conversationStateManager.current.updateAssistantMessage(assistantMsg);
    }
    // Build the final messages array
    const updatedMessages = builder.build();
    // Update messages state once with all changes
    if ((result.autoExecutedMessages && result.autoExecutedMessages.length > 0) ||
        hasValidAssistantMessage) {
        setMessages(updatedMessages);
    }
    // Clear streaming state after response is complete
    setIsGenerating(false);
    setStreamingContent('');
    // Handle error results for non-existent tools
    if (errorResults.length > 0) {
        // Display error messages to user
        for (const error of errorResults) {
            addToChatQueue(_jsx(ErrorMessage, { message: error.content, hideBox: true }, `unknown-tool-${error.tool_call_id}-${Date.now()}`));
        }
        // Send error results back to model for self-correction
        const errorBuilder = new MessageBuilder(updatedMessages);
        errorBuilder.addToolResults(errorResults);
        const updatedMessagesWithError = errorBuilder.build();
        setMessages(updatedMessagesWithError);
        // Continue the main conversation loop with error messages as context
        await processAssistantResponse({
            ...params,
            messages: updatedMessagesWithError,
        });
        return;
    }
    // Handle tool calls if present - this continues the loop
    if (validToolCalls && validToolCalls.length > 0) {
        // Note: Plan mode tool blocking was removed - the referenced tools
        // (create_file, delete_lines, insert_lines, replace_lines) no longer exist.
        // Plan mode restrictions are handled via needsApproval in tool definitions.
        // TODO: Implement registry-based blocking for plan mode (track as separate issue).
        // Separate tools that need confirmation vs those that don't
        // Check tool's needsApproval property to determine if confirmation is needed
        const toolsNeedingConfirmation = [];
        const toolsToExecuteDirectly = [];
        for (const toolCall of validToolCalls) {
            // Check if tool has a validator
            let validationFailed = false;
            // XML validation errors are treated as validation failures
            if (toolCall.function.name === '__xml_validation_error__') {
                validationFailed = true;
            }
            else if (toolManager) {
                const validator = toolManager.getToolValidator(toolCall.function.name);
                if (validator) {
                    try {
                        const parsedArgs = parseToolArguments(toolCall.function.arguments);
                        const validationResult = await validator(parsedArgs);
                        if (!validationResult.valid) {
                            validationFailed = true;
                        }
                    }
                    catch (error) {
                        const logger = getLogger();
                        logger.debug('Tool validation threw error', {
                            toolName: toolCall.function.name,
                            error,
                        });
                        validationFailed = true;
                    }
                }
            }
            // Check tool's needsApproval property from the tool definition
            let toolNeedsApproval = true; // Default to requiring approval for safety
            if (toolManager) {
                const toolEntry = toolManager.getToolEntry(toolCall.function.name);
                if (toolEntry?.tool) {
                    const needsApprovalProp = toolEntry.tool.needsApproval;
                    if (typeof needsApprovalProp === 'boolean') {
                        toolNeedsApproval = needsApprovalProp;
                    }
                    else if (typeof needsApprovalProp === 'function') {
                        // Evaluate function - our tools use getCurrentMode() internally
                        // and don't actually need the args parameter
                        try {
                            const parsedArgs = parseToolArguments(toolCall.function.arguments);
                            // Cast to any to handle AI SDK type signature mismatch
                            // Our tool implementations don't use the second parameter
                            toolNeedsApproval = await needsApprovalProp(parsedArgs);
                        }
                        catch (error) {
                            const logger = getLogger();
                            logger.debug('needsApproval evaluation failed, requiring approval', {
                                toolName: toolCall.function.name,
                                error,
                            });
                            toolNeedsApproval = true;
                        }
                    }
                }
            }
            // Execute directly if:
            // 1. Validation failed (need to send error back to model)
            // 2. Tool has needsApproval: false
            // 3. In auto-accept mode (except bash which always needs approval)
            const isBashTool = toolCall.function.name === 'execute_bash';
            if (validationFailed ||
                !toolNeedsApproval ||
                (developmentMode === 'auto-accept' && !isBashTool)) {
                toolsToExecuteDirectly.push(toolCall);
            }
            else {
                toolsNeedingConfirmation.push(toolCall);
            }
        }
        // Execute non-confirmation tools directly
        if (toolsToExecuteDirectly.length > 0) {
            const directResults = await executeToolsDirectly(toolsToExecuteDirectly, toolManager, conversationStateManager, addToChatQueue, getNextComponentKey);
            // If we have results, continue the conversation with them
            if (directResults.length > 0) {
                // Add tool results to messages
                const directBuilder = new MessageBuilder(updatedMessages);
                directBuilder.addToolResults(directResults);
                const updatedMessagesWithTools = directBuilder.build();
                setMessages(updatedMessagesWithTools);
                // Continue the main conversation loop with tool results as context
                await processAssistantResponse({
                    ...params,
                    messages: updatedMessagesWithTools,
                });
                return;
            }
        }
        // Start confirmation flow only for tools that need it
        if (toolsNeedingConfirmation.length > 0) {
            // In non-interactive mode, exit when tool approval is required
            if (nonInteractiveMode) {
                const toolNames = toolsNeedingConfirmation
                    .map(tc => tc.function.name)
                    .join(', ');
                const errorMsg = `Tool approval required for: ${toolNames}. Exiting non-interactive mode`;
                // Add error message to UI
                addToChatQueue(_jsx(ErrorMessage, { message: errorMsg, hideBox: true }, `tool-approval-required-${Date.now()}`));
                // Add error to messages array so exit detection can find it
                const errorMessage = {
                    role: 'assistant',
                    content: errorMsg,
                };
                // Use updatedMessages which already includes auto-executed tool results
                const errorBuilder = new MessageBuilder(updatedMessages);
                errorBuilder.addMessage(errorMessage);
                setMessages(errorBuilder.build());
                // Signal completion to trigger exit
                if (onConversationComplete) {
                    onConversationComplete();
                }
                return;
            }
            // Pass complete messages including assistant msg
            // useToolHandler will add tool results
            onStartToolConfirmationFlow(toolsNeedingConfirmation, updatedMessages, // Includes assistant message
            assistantMsg, systemMessage);
        }
    }
    // If no tool calls, the conversation naturally ends here
    // BUT: if there's ALSO no content, that's likely an error - the model should have said something
    // Auto-reprompt to help the model continue
    if (validToolCalls.length === 0 && !cleanedContent.trim()) {
        // Check if we just executed tools (updatedMessages should have tool results)
        const lastMessage = updatedMessages[updatedMessages.length - 1];
        const hasRecentToolResults = lastMessage?.role === 'tool';
        // Add a continuation message to help the model respond
        // For recent tool results, ask for a summary; otherwise, ask to continue
        const nudgeContent = hasRecentToolResults
            ? 'Please provide a summary or response based on the tool results above.'
            : 'Please continue with the task.';
        const nudgeMessage = {
            role: 'user',
            content: nudgeContent,
        };
        // Display a "continue" message in chat so user knows what happened
        addToChatQueue(_jsx(UserMessage, { message: "continue" }, `auto-continue-${getNextComponentKey()}`));
        // Don't include the empty assistantMsg - it would cause API error
        // "Assistant message must have either content or tool_calls"
        const nudgeBuilder = new MessageBuilder(updatedMessages);
        nudgeBuilder.addMessage(nudgeMessage);
        const updatedMessagesWithNudge = nudgeBuilder.build();
        setMessages(updatedMessagesWithNudge);
        // Continue the conversation loop with the nudge
        await processAssistantResponse({
            ...params,
            messages: updatedMessagesWithNudge,
        });
        return;
    }
    if (validToolCalls.length === 0 && cleanedContent.trim()) {
        onConversationComplete?.();
    }
};
//# sourceMappingURL=conversation-loop.js.map