/**
 * AgentController class that uses the agent-loop directly.
 * No transport abstraction - calls streamSimple via the loop.
 */
import { AgentEngine } from './agent-engine.js';
import { AgentMessageQueue } from './agent-message-queue.js';
import { AgentRunState } from './agent-run-state.js';
export class AgentController {
    _state;
    listeners = new Set();
    _messageQueue;
    _runState = new AgentRunState();
    _runtime;
    constructor(opts = {}) {
        const initialState = opts.initialState ?? {};
        this._state = {
            systemPrompt: initialState.systemPrompt ?? '',
            model: initialState.model,
            thinkingLevel: initialState.thinkingLevel ?? 'off',
            tools: initialState.tools ?? [],
            messages: initialState.messages ?? [],
            isStreaming: initialState.isStreaming ?? false,
            streamMessage: initialState.streamMessage ?? null,
            pendingToolCalls: initialState.pendingToolCalls ?? new Set(),
            error: initialState.error,
            errorKind: initialState.errorKind,
            inFlight: initialState.inFlight ?? []
        };
        this._messageQueue = new AgentMessageQueue({
            steeringMode: opts.steeringMode,
            followUpMode: opts.followUpMode
        });
        this._runtime =
            opts.runtime instanceof AgentEngine ? opts.runtime : new AgentEngine(opts.runtime);
    }
    get sessionId() {
        return this._runtime.sessionId;
    }
    set sessionId(value) {
        this._runtime.sessionId = value;
    }
    get transport() {
        return this._runtime.transport;
    }
    setTransport(value) {
        this._runtime.transport = value;
    }
    get maxRetryDelayMs() {
        return this._runtime.maxRetryDelayMs;
    }
    set maxRetryDelayMs(value) {
        this._runtime.maxRetryDelayMs = value;
    }
    get streamFn() {
        return this._runtime.streamFn;
    }
    set streamFn(value) {
        this._runtime.streamFn = value;
    }
    /**
     * Per-call speed mode. Set by the embedded runtime's submit() from
     * `AgentSubmitOptions.speed`; cleared after the run. Forwarded into
     * `AgentLoopConfig.speed` and read by the ShortcutXL stream impl as
     * the `speed` field on the proxy request body. See spec.md §7-A.
     */
    get speed() {
        return this._runtime.speed;
    }
    set speed(value) {
        this._runtime.speed = value;
    }
    get state() {
        return this._state;
    }
    subscribe(fn) {
        this.listeners.add(fn);
        return () => this.listeners.delete(fn);
    }
    // State mutators
    setSystemPrompt(v) {
        this._state.systemPrompt = v;
    }
    setModel(m) {
        this._state.model = m;
    }
    setThinkingLevel(l) {
        this._state.thinkingLevel = l;
    }
    setSteeringMode(mode) {
        this._messageQueue.setSteeringMode(mode);
    }
    getSteeringMode() {
        return this._messageQueue.getSteeringMode();
    }
    setFollowUpMode(mode) {
        this._messageQueue.setFollowUpMode(mode);
    }
    getFollowUpMode() {
        return this._messageQueue.getFollowUpMode();
    }
    setTools(t) {
        this._state.tools = t;
    }
    replaceMessages(ms) {
        this._state.messages = ms.slice();
    }
    appendMessage(m) {
        this._state.messages = [...this._state.messages, m];
    }
    /**
     * Queue a steering message to interrupt the agent mid-run.
     * Delivered after current tool execution, skips remaining tools.
     */
    steer(m) {
        this._messageQueue.steer(m);
    }
    /**
     * Queue a follow-up message to be processed after the agent finishes.
     * Delivered only when agent has no more tool calls or steering messages.
     */
    followUp(m) {
        this._messageQueue.followUp(m);
    }
    clearSteeringQueue() {
        this._messageQueue.clearSteeringQueue();
    }
    clearFollowUpQueue() {
        this._messageQueue.clearFollowUpQueue();
    }
    clearAllQueues() {
        this._messageQueue.clearAllQueues();
    }
    hasQueuedMessages() {
        return this._messageQueue.hasQueuedMessages();
    }
    clearMessages() {
        this._state.messages = [];
    }
    abort() {
        this._runState.abort();
    }
    waitForIdle() {
        return this._runState.waitForIdle();
    }
    reset() {
        this._state.messages = [];
        this._state.isStreaming = false;
        this._state.streamMessage = null;
        this._state.pendingToolCalls = new Set();
        this._state.error = undefined;
        this._state.errorKind = undefined;
        this._state.inFlight = [];
        this._messageQueue.reset();
    }
    async prompt(input, images) {
        if (this._state.isStreaming) {
            throw new Error('AgentController is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.');
        }
        const model = this._state.model;
        if (!model)
            throw new Error('No model configured');
        let msgs;
        if (Array.isArray(input)) {
            msgs = input;
        }
        else if (typeof input === 'string') {
            const content = [{ type: 'text', text: input }];
            if (images && images.length > 0) {
                content.push(...images);
            }
            msgs = [
                {
                    role: 'user',
                    content,
                    timestamp: Date.now()
                }
            ];
        }
        else {
            msgs = [input];
        }
        await this._executeTurn(msgs);
    }
    /**
     * Continue from current context (used for retries and resuming queued messages).
     */
    async continue() {
        if (this._state.isStreaming) {
            throw new Error('AgentController is already processing. Wait for completion before continuing.');
        }
        const messages = this._state.messages;
        if (messages.length === 0) {
            throw new Error('No messages to continue from');
        }
        if (messages[messages.length - 1].role === 'assistant') {
            const queuedSteering = this._messageQueue.dequeueSteeringMessages();
            if (queuedSteering.length > 0) {
                await this._executeTurn(queuedSteering, { skipInitialSteeringPoll: true });
                return;
            }
            const queuedFollowUp = this._messageQueue.dequeueFollowUpMessages();
            if (queuedFollowUp.length > 0) {
                await this._executeTurn(queuedFollowUp);
                return;
            }
            throw new Error('Cannot continue from message role: assistant');
        }
        await this._executeTurn(undefined);
    }
    async _executeTurn(messages, options) {
        const model = this._state.model;
        if (!model)
            throw new Error('No model configured');
        const signal = this._runState.start();
        this._state.isStreaming = true;
        this._state.streamMessage = null;
        this._state.error = undefined;
        this._state.errorKind = undefined;
        try {
            await this._runtime.runTurn({
                state: this._state,
                messageQueue: this._messageQueue,
                signal,
                messages,
                skipInitialSteeringPoll: options?.skipInitialSteeringPoll,
                onEvent: (event) => this.emit(event)
            });
        }
        finally {
            this._state.isStreaming = false;
            this._state.streamMessage = null;
            this._state.pendingToolCalls = new Set();
            this._state.inFlight = [];
            this._runState.finish();
        }
    }
    emit(e) {
        for (const listener of this.listeners) {
            listener(e);
        }
    }
}
//# sourceMappingURL=agent-controller.js.map