import { AgentLoopEventType } from './core-types.js';
import { classifyAgentError, getAgentErrorMessage } from './error-classification.js';
/** Apply one event; returns updated partial assistant message or null. */
export function reduceAgentLoopEvent(state, event, partial) {
    switch (event.type) {
        case AgentLoopEventType.MessageStart:
            state.streamMessage = event.message;
            return event.message;
        case AgentLoopEventType.MessageUpdate:
            state.streamMessage = event.message;
            return event.message;
        case AgentLoopEventType.MessageEnd:
            state.streamMessage = null;
            state.messages = [...state.messages, event.message];
            return null;
        case AgentLoopEventType.ToolExecutionStart: {
            const pending = new Set(state.pendingToolCalls);
            pending.add(event.toolCallId);
            state.pendingToolCalls = pending;
            return partial;
        }
        case AgentLoopEventType.ToolExecutionEnd: {
            const pending = new Set(state.pendingToolCalls);
            pending.delete(event.toolCallId);
            state.pendingToolCalls = pending;
            return partial;
        }
        case AgentLoopEventType.OpStart:
            state.inFlight = [
                ...state.inFlight,
                {
                    opId: event.opId,
                    kind: event.kind,
                    ...event.meta,
                    startedAt: event.startedAt,
                    elapsedMs: 0
                }
            ];
            return partial;
        case AgentLoopEventType.HeartbeatTick: {
            const idx = state.inFlight.findIndex((entry) => entry.opId === event.opId);
            if (idx === -1)
                return partial;
            const next = state.inFlight.slice();
            next[idx] = { ...next[idx], elapsedMs: event.elapsedMs };
            state.inFlight = next;
            return partial;
        }
        case AgentLoopEventType.OpEnd:
            state.inFlight = state.inFlight.filter((entry) => entry.opId !== event.opId);
            return partial;
        case AgentLoopEventType.TurnEnd:
            if (event.message.role === 'assistant' && 'errorMessage' in event.message) {
                const errorMessage = event.message.errorMessage;
                state.error = errorMessage;
                // The streamed-error path (provider returned a final message with
                // stopReason='error') needs the same classification as the thrown
                // path that AgentEngine.runTurn handles. Without this, errorKind
                // is set only for synchronous throws — which never happen on the
                // real provider stream — and the UI loses its categorical signal.
                if (event.message.stopReason === 'error') {
                    state.errorKind = classifyAgentError(new Error(errorMessage ?? ''));
                }
            }
            return partial;
        case AgentLoopEventType.AgentEnd:
            state.isStreaming = false;
            state.streamMessage = null;
            return partial;
        case AgentLoopEventType.AgentStart:
        case AgentLoopEventType.TurnStart:
        case AgentLoopEventType.ToolExecutionUpdate:
            return partial;
    }
}
/** Finalize partial assistant message at turn end; null if empty; throws if aborted-empty. */
export function finalizePartialAssistantMessage(partial, signal) {
    if (!partial || partial.role !== 'assistant' || partial.content.length === 0) {
        return null;
    }
    const hasMeaningfulContent = partial.content.some((content) => (content.type === 'thinking' && content.thinking.trim().length > 0) ||
        (content.type === 'text' && content.text.trim().length > 0) ||
        (content.type === 'toolCall' && content.name.trim().length > 0));
    if (hasMeaningfulContent) {
        return partial;
    }
    if (signal?.aborted) {
        throw new Error('Request was aborted');
    }
    return null;
}
/** Build a synthetic assistant message representing a terminal error (unrecoverable failure or abort). */
export function createTerminalErrorMessage(model, error, signal) {
    return {
        role: 'assistant',
        content: [{ type: 'text', text: '' }],
        api: model.api,
        provider: model.provider,
        model: model.id,
        usage: {
            input: 0,
            output: 0,
            cacheRead: 0,
            cacheWrite: 0,
            totalTokens: 0,
            cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
        },
        stopReason: signal?.aborted ? 'aborted' : 'error',
        errorMessage: getAgentErrorMessage(error),
        timestamp: Date.now()
    };
}
//# sourceMappingURL=agent-state-reducer.js.map