import { jsx as _jsx } from "react/jsx-runtime";
import AgentProgress, { MultiAgentProgress } from '../../../components/agent-progress.js';
import BashProgress from '../../../components/bash-progress.js';
import { ErrorMessage } from '../../../components/message-box.js';
import { clearAllSubagentProgress, getSubagentProgress, resetSubagentProgressById, } from '../../../services/subagent-events.js';
import { generateKey } from '../../../session/key-generator.js';
import { MAX_CONCURRENT_AGENTS } from '../../../subagents/subagent-executor.js';
import { startAgentExecution } from '../../../tools/agent-tool.js';
import { formatError } from '../../../utils/error-formatter.js';
import { runStreamingBashTool, } from '../../../utils/streaming-bash-tool.js';
import { parseToolArguments } from '../../../utils/tool-args-parser.js';
import { ALWAYS_EXPANDED_TOOLS, displayToolResult, LIVE_TASK_TOOLS, } from '../../../utils/tool-result-display.js';
/**
 * Validates and executes a single tool call.
 * Returns the tool call paired with its result for sequential post-processing.
 */
const executeOne = async (toolCall, processToolUse) => {
    try {
        // Validation runs inside the validated registry handler that
        // processToolUse invokes, so invalid args come back here as an error
        // tool-result (the handler never executes).
        const result = await processToolUse(toolCall);
        return { toolCall, result };
    }
    catch (error) {
        return {
            toolCall,
            result: {
                tool_call_id: toolCall.id,
                role: 'tool',
                name: toolCall.function.name,
                content: `Error: ${formatError(error)}`,
            },
        };
    }
};
/**
 * Execute an execute_bash tool call through the shared streaming runner,
 * mounting a live BashProgress so streamed output shows while the command runs.
 * Returns the captured BashExecutionState so the caller can render a completed
 * BashProgress (expanded mode) instead of the command-only formatter.
 */
const executeBashStreaming = (toolCall, toolManager, setLiveComponent, signal) => runStreamingBashTool(toolCall, toolManager, setLiveComponent, 'direct-bash', signal);
/**
 * Execute a single already-approved tool call. execute_bash streams through the
 * live BashProgress when a live area is available; everything else runs through
 * the validated registry handler. The single per-tool execution primitive
 * shared by the auto-execute batch and the (post-approval) confirmation path.
 */
export const executeApprovedTool = (toolCall, toolManager, processToolUse, setLiveComponent, signal) => {
    if (toolCall.function.name === 'execute_bash' && setLiveComponent) {
        return executeBashStreaming(toolCall, toolManager, setLiveComponent, signal);
    }
    return executeOne(toolCall, processToolUse);
};
/**
 * Render one executed tool's result and fold it into conversation state. The
 * single display primitive shared by both execution paths, so compact-tally,
 * expanded-bash, live-task, and non-interactive rendering behave identically
 * regardless of whether the tool was auto-executed or user-approved.
 */
export const displayExecutedTool = async (execution, toolManager, addToChatQueue, conversationStateManager, options) => {
    const { toolCall, result, bashState } = execution;
    conversationStateManager.current.updateAfterToolExecution(toolCall, result.content);
    if (LIVE_TASK_TOOLS.has(result.name) &&
        !result.content.startsWith('Error: ')) {
        // Task tools render in the live area (updating in-place)
        options?.onLiveTaskUpdate?.();
    }
    else if (options?.compactDisplay &&
        !ALWAYS_EXPANDED_TOOLS.has(result.name)) {
        // In compact mode, signal the count callback for live display
        // (skip for tools that should always show expanded output).
        //
        // Non-interactive mode has no live tally renderer, so push
        // per-tool one-liners straight to the static queue to keep
        // tool activity in chronological order.
        //
        // Failures (generic "Error: …" or the streaming bash path's
        // "⚒ Validation failed: …") don't fold into the count tally; they
        // render as a condensed red one-liner ("⚒ write_file failed")
        // instead of the full error. The model still receives the full
        // error in conversation history — mirror displayToolResult's detection.
        const isError = result.content.startsWith('Error: ') ||
            result.content.startsWith('⚒ Validation failed');
        if (isError) {
            // Condense failures to a short red one-liner in compact mode.
            await displayToolResult(toolCall, result, toolManager, addToChatQueue, true);
        }
        else if (options.nonInteractiveMode) {
            await displayToolResult(toolCall, result, toolManager, addToChatQueue, true);
        }
        else {
            options.onCompactToolCount?.(result.name);
        }
    }
    else if (result.name === 'execute_bash' && bashState) {
        // Expanded mode: render the completed BashProgress (command +
        // status + tokens), matching the confirmation path's completed view.
        addToChatQueue(_jsx(BashProgress, { executionId: bashState.executionId, command: bashState.command, completedState: bashState }, generateKey(`direct-bash-complete-${toolCall.id}`)));
    }
    else {
        // Full display mode
        await displayToolResult(toolCall, result, toolManager, addToChatQueue);
    }
};
/**
 * Classify a tool call for grouping purposes.
 * Read-only tools and agent tools can be parallelized within their groups.
 */
const classifyTool = (toolCall, toolManager) => {
    if (toolCall.function.name === 'agent')
        return 'agent';
    if (toolManager?.isReadOnly(toolCall.function.name))
        return 'readOnly';
    return 'other';
};
/**
 * Groups consecutive parallelizable tools for parallel execution.
 * Consecutive read-only tools and consecutive agent tools form parallel groups.
 * Other tools form single-item groups to preserve ordering.
 *
 * Example: [read, read, agent, agent, write, read] →
 *          [[read, read], [agent, agent], [write], [read]]
 */
const groupForParallelExecution = (tools, toolManager) => {
    const groups = [];
    let currentGroup = [];
    let currentType = null;
    for (const toolCall of tools) {
        const type = classifyTool(toolCall, toolManager);
        const isParallelizable = type === 'readOnly' || type === 'agent';
        if (isParallelizable && type === currentType) {
            // Continue the current parallelizable group
            currentGroup.push(toolCall);
        }
        else {
            // Start a new group
            if (currentGroup.length > 0 && currentType !== null) {
                groups.push({ group: currentGroup, type: currentType });
            }
            currentGroup = [toolCall];
            currentType = type;
        }
    }
    if (currentGroup.length > 0 && currentType !== null) {
        groups.push({ group: currentGroup, type: currentType });
    }
    return groups;
};
/**
 * Execute a batch of agent tool calls in parallel.
 * Returns tool results for all agents.
 */
const executeAgentBatch = async (agentToolCalls, toolManager, addToChatQueue, compactDisplay, setLiveComponent, onCompactToolCount, nonInteractiveMode, signal) => {
    // Enforce concurrency limit — return error results for excess agents
    const excessResults = [];
    let toExecute = agentToolCalls;
    if (agentToolCalls.length > MAX_CONCURRENT_AGENTS) {
        const excess = agentToolCalls.slice(MAX_CONCURRENT_AGENTS);
        toExecute = agentToolCalls.slice(0, MAX_CONCURRENT_AGENTS);
        for (const toolCall of excess) {
            excessResults.push({
                toolCall,
                result: {
                    tool_call_id: toolCall.id,
                    role: 'tool',
                    name: toolCall.function.name,
                    content: `Error: Maximum concurrent agent limit (${MAX_CONCURRENT_AGENTS}) exceeded. Please retry this agent call separately.`,
                },
            });
        }
    }
    // Start all agents
    const agentExecutions = toExecute.map(toolCall => {
        const parsedArgs = parseToolArguments(toolCall.function.arguments);
        // Coerce, don't assert: a weak model can emit these as objects/numbers,
        // and a non-string flowing into the progress UI crashes the renderer.
        const agentName = typeof parsedArgs.subagent_type === 'string'
            ? parsedArgs.subagent_type
            : 'agent';
        const agentDesc = typeof parsedArgs.description === 'string' ? parsedArgs.description : '';
        const { agentId, promise } = startAgentExecution(parsedArgs, signal);
        resetSubagentProgressById(agentId);
        return { toolCall, agentId, agentName, agentDesc, promise };
    });
    // Show live progress
    if (setLiveComponent && agentExecutions.length > 0) {
        const agentInfos = agentExecutions.map(e => ({
            agentId: e.agentId,
            subagentName: e.agentName,
            description: e.agentDesc,
        }));
        if (agentExecutions.length === 1) {
            const e = agentExecutions[0];
            setLiveComponent(_jsx(AgentProgress, { subagentName: e.agentName, description: e.agentDesc, agentId: e.agentId, isLive: true }, generateKey(`agent-live-direct-${e.toolCall.id}`)));
        }
        else {
            setLiveComponent(_jsx(MultiAgentProgress, { agents: agentInfos, isLive: true }, generateKey('multi-agent-live-direct')));
        }
    }
    // Await all results
    const settledResults = await Promise.allSettled(agentExecutions.map(e => e.promise));
    // Clear live progress
    setLiveComponent?.(null);
    // Build results
    const results = [];
    for (let i = 0; i < agentExecutions.length; i++) {
        const e = agentExecutions[i];
        const settled = settledResults[i];
        const agentResult = settled.status === 'fulfilled'
            ? settled.value
            : {
                content: '',
                success: false,
                error: settled.reason instanceof Error
                    ? settled.reason.message
                    : String(settled.reason),
            };
        const progress = getSubagentProgress(e.agentId);
        const result = {
            tool_call_id: e.toolCall.id,
            role: 'tool',
            name: e.toolCall.function.name,
            content: agentResult.success
                ? agentResult.content
                : `Error: ${agentResult.error || 'Subagent execution failed'}`,
        };
        results.push({ toolCall: e.toolCall, result });
        // Compact: feed into the shared count accumulator so delegated-task
        // summaries group with other tool counts. Errors are still shown in
        // full. Non-compact: render the rich AgentProgress card.
        //
        // Non-interactive mode bypasses the live accumulator (nothing renders
        // it) and pushes a one-liner directly into the static queue so tool
        // activity appears in chronological order in stdout.
        if (compactDisplay) {
            const isError = result.content.startsWith('Error: ');
            if (isError) {
                await displayToolResult(e.toolCall, result, toolManager, addToChatQueue);
            }
            else if (nonInteractiveMode) {
                await displayToolResult(e.toolCall, result, toolManager, addToChatQueue, true);
            }
            else {
                onCompactToolCount?.(result.name);
            }
        }
        else {
            addToChatQueue(_jsx(AgentProgress, { subagentName: e.agentName, description: e.agentDesc, agentId: e.agentId, completedState: {
                    toolCallCount: progress.toolCallCount,
                    tokenCount: progress.tokenCount,
                    success: agentResult.success,
                    toolHistory: [...progress.toolHistory],
                } }, generateKey(`agent-complete-${e.toolCall.id}`)));
        }
    }
    clearAllSubagentProgress();
    // Append error results for excess agents that were rejected
    for (const excess of excessResults) {
        results.push(excess);
        addToChatQueue(_jsx(ErrorMessage, { message: excess.result.content, hideBox: true }, generateKey(`agent-excess-${excess.toolCall.id}`)));
    }
    return results;
};
/**
 * Executes tools directly without confirmation.
 * Read-only tools and agent tools in consecutive groups are executed in parallel.
 * Other tools are executed sequentially to preserve ordering.
 * Results are displayed in the original input order.
 *
 * @returns Array of tool results from executed tools
 */
export const executeToolsDirectly = async (toolsToExecuteDirectly, toolManager, conversationStateManager, addToChatQueue, options) => {
    // Import processToolUse here to avoid circular dependencies
    const { processToolUse } = await import('../../../message-handler.js');
    // Group consecutive parallelizable tools
    const groups = groupForParallelExecution(toolsToExecuteDirectly, toolManager);
    const directResults = [];
    for (const { group, type } of groups) {
        let executions;
        if (type === 'agent' && group.length > 0) {
            // Parallel execution for consecutive agent tools
            const agentResults = await executeAgentBatch(group, toolManager, addToChatQueue, options?.compactDisplay, options?.setLiveComponent, options?.onCompactToolCount, options?.nonInteractiveMode, options?.signal);
            // Agent results are already displayed by executeAgentBatch
            for (const { toolCall, result } of agentResults) {
                directResults.push(result);
                conversationStateManager.current.updateAfterToolExecution(toolCall, result.content);
            }
            continue;
        }
        if (type === 'readOnly' && group.length > 1) {
            // Parallel execution for consecutive read-only tools
            executions = await Promise.all(group.map(toolCall => executeOne(toolCall, processToolUse)));
        }
        else {
            // Sequential execution for non-parallelizable tools (or single-item groups)
            executions = [];
            for (const toolCall of group) {
                executions.push(await executeApprovedTool(toolCall, toolManager, processToolUse, options?.setLiveComponent, options?.signal));
            }
        }
        // Display results in order
        for (const execution of executions) {
            directResults.push(execution.result);
            await displayExecutedTool(execution, toolManager, addToChatQueue, conversationStateManager, options);
        }
    }
    return directResults;
};
//# sourceMappingURL=tool-executor.js.map