/**
 * Nested subagent runner used by the `task` contract.
 * Constructs a child `AgentController` in-process with isolated message history,
 * caller-signal + timeout abort, and contained errors.
 */
import { AgentController } from '../../../core/agent-controller.js';
import { AgentEngine } from '../../../core/agent-engine.js';
/** Concatenate the text parts of an assistant message; ignore thinking/tool parts. */
function assistantMessageText(message) {
    if (!message)
        return '';
    return message.content
        .filter((part) => part.type === 'text')
        .map((part) => part.text ?? '')
        .join('');
}
export async function runSubagent(options) {
    const { definition, query, childTools, streamFn, model, signal, onChildAgentEvent } = options;
    const thinkingLevel = options.thinkingLevel ?? definition.thinkingLevel;
    const resolvedSystemPrompt = options.systemPrompt ??
        (typeof definition.systemPrompt === 'string' ? definition.systemPrompt : undefined);
    if (resolvedSystemPrompt === undefined) {
        return {
            error: `Subagent '${definition.name}' uses a system-prompt builder but no resolved ` +
                'systemPrompt was passed to runSubagent. The task contract resolves prompts ' +
                'before invoking the runner — direct callers must do the same.'
        };
    }
    const forwardEvent = (event) => {
        if (!onChildAgentEvent)
            return;
        try {
            onChildAgentEvent(event);
        }
        catch (err) {
            // Forwarding errors must not break the run.
            console.warn('[runSubagent] onChildAgentEvent handler threw:', err);
        }
    };
    if (signal.aborted) {
        return {
            error: `Subagent '${definition.name}' cancelled before start.`,
            cancelled: true
        };
    }
    // Compose caller signal and definition timeout into one abort source.
    const childAbort = new AbortController();
    const onCallerAbort = () => childAbort.abort();
    signal.addEventListener('abort', onCallerAbort, { once: true });
    let timedOut = false;
    const timeoutHandle = setTimeout(() => {
        timedOut = true;
        childAbort.abort();
    }, definition.timeoutSeconds * 1000);
    const child = new AgentController({
        initialState: {
            model,
            systemPrompt: resolvedSystemPrompt,
            thinkingLevel,
            tools: [...childTools],
            // INVARIANT: fresh array — child never sees the caller's messages.
            messages: []
        },
        runtime: new AgentEngine({
            streamFn,
            getApiKey: options.getApiKey
        })
    });
    child.speed = options.speed;
    const abortListener = () => child.abort();
    childAbort.signal.addEventListener('abort', abortListener, { once: true });
    // Preserve the whole final assistant message so terminal-error stop
    // reasons can be distinguished from successful completions.
    let finalMessage;
    let childError;
    const unsubscribe = child.subscribe((event) => {
        forwardEvent(event);
        if (event.type === 'agent_end') {
            for (let i = event.messages.length - 1; i >= 0; i -= 1) {
                const msg = event.messages[i];
                if (msg?.role === 'assistant') {
                    finalMessage = msg;
                    break;
                }
            }
        }
        else if (event.type === 'tool_execution_end' && event.isError) {
            const result = event.result;
            const msg = result?.content?.[0]?.text;
            if (typeof msg === 'string' && msg.length > 0) {
                childError = msg;
            }
        }
    });
    try {
        await child.prompt(query);
        await child.waitForIdle();
        if (timedOut) {
            return {
                error: `Subagent '${definition.name}' timed out after ${definition.timeoutSeconds}s.`,
                cancelled: true
            };
        }
        if (signal.aborted) {
            return {
                error: `Subagent '${definition.name}' cancelled.`,
                cancelled: true
            };
        }
        // Terminal-error detection: the agent loop injects "[Request failed]"
        // into the assistant message when stopReason === 'error'. Without this
        // guard the runner would treat the injected text as success.
        if (finalMessage?.stopReason === 'error' && finalMessage.errorMessage) {
            return {
                error: `Subagent '${definition.name}' failed: ${finalMessage.errorMessage}`
            };
        }
        const finalText = assistantMessageText(finalMessage);
        if (finalText.length > 0) {
            return { finalContent: finalText };
        }
        if (childError) {
            return { error: childError };
        }
        return { error: `Subagent '${definition.name}' produced no final message.` };
    }
    catch (error) {
        return {
            error: `Subagent '${definition.name}' failed: ${error instanceof Error ? error.message : String(error)}`
        };
    }
    finally {
        unsubscribe();
        clearTimeout(timeoutHandle);
        signal.removeEventListener('abort', onCallerAbort);
    }
}
//# sourceMappingURL=run-subagent.js.map