import { ensureAgentLoaded } from "./agent-loading.js";
export function startAgentRun(agentManager, agentId, prompt, logger, options) {
    const snapshot = agentManager.getAgent(agentId);
    logger.trace({
        agentId,
        provider: snapshot?.provider,
        providerSessionId: snapshot?.persistence?.sessionId ?? undefined,
        turnId: snapshot?.activeForegroundTurnId ?? undefined,
        promptType: typeof prompt === "string" ? "string" : "structured",
        hasRunOptions: Boolean(options?.runOptions),
        replaceRunning: Boolean(options?.replaceRunning),
    }, "agent.session.start_stream.request");
    // Out-of-band commands (e.g. /goal pause) must run WITHOUT canceling an
    // in-flight turn — replaceAgentRun would interrupt the running turn. The
    // intercept lives at this layer so it covers every prompt entrypoint.
    if (agentManager.tryRunOutOfBand(agentId, prompt)) {
        return { outOfBand: true };
    }
    const shouldReplace = Boolean(options?.replaceRunning && agentManager.hasInFlightRun(agentId));
    const runOptions = options?.runOptions;
    const iterator = shouldReplace
        ? agentManager.replaceAgentRun(agentId, prompt, runOptions)
        : agentManager.streamAgent(agentId, prompt, runOptions);
    logger.trace({
        agentId,
        provider: snapshot?.provider,
        providerSessionId: snapshot?.persistence?.sessionId ?? undefined,
        shouldReplace,
    }, "agent.session.start_stream.iterator_returned");
    void (async () => {
        try {
            for await (const _ of iterator) {
                // Events are broadcast via AgentManager subscribers.
            }
            logger.trace({
                agentId,
                provider: snapshot?.provider,
                providerSessionId: snapshot?.persistence?.sessionId ?? undefined,
            }, "agent.session.iterator.drained");
        }
        catch (error) {
            logger.trace({
                agentId,
                provider: snapshot?.provider,
                providerSessionId: snapshot?.persistence?.sessionId ?? undefined,
                err: error,
            }, "agent.session.iterator.error");
            logger.error({ err: error, agentId }, "Agent stream failed");
        }
    })();
    return { outOfBand: false };
}
/**
 * Clear the archived flag from a stored agent record.
 * Shared across Session (app/WS), MCP, and CLI so every surface that acts on
 * an archived agent unarchives it the same way.
 */
export async function unarchiveAgentState(agentStorage, agentManager, agentId) {
    const record = await agentStorage.get(agentId);
    if (!record || !record.archivedAt) {
        return false;
    }
    const updatedAt = new Date().toISOString();
    await agentStorage.upsert({
        ...record,
        archivedAt: null,
        updatedAt,
    });
    agentManager.notifyAgentState(agentId);
    return true;
}
/**
 * Wrap a body in <paseo-system>…</paseo-system> so the receiving agent
 * recognizes the prompt as system-injected context — not a user turn.
 * Used by chat mentions, schedule fires, and notify-on-finish.
 */
export function formatSystemNotificationPrompt(reason) {
    return `<paseo-system>\n${reason}\n</paseo-system>`;
}
const SYSTEM_ENVELOPE_PATTERN = /^<paseo-system>\n[\s\S]*\n<\/paseo-system>$/;
export function isSystemInjectedEnvelope(text) {
    return SYSTEM_ENVELOPE_PATTERN.test(text);
}
const AGENT_RUN_START_TIMEOUT_MS = 15000;
export async function waitForAgentRunStartWithTimeout(agentManager, agentId) {
    const startAbort = new AbortController();
    const startTimeout = setTimeout(() => startAbort.abort("timeout"), AGENT_RUN_START_TIMEOUT_MS);
    try {
        await agentManager.waitForAgentRunStart(agentId, { signal: startAbort.signal });
    }
    finally {
        clearTimeout(startTimeout);
    }
}
/**
 * Full send-prompt orchestration: (optional unarchive) → load → (optional
 * mode change) → start run.
 *
 * Every surface that sends a prompt to an agent (Session/WS, MCP, CLI-through-MCP,
 * chat mentions, notify-on-finish) MUST go through this so behavior can never
 * drift between them.
 *
 * When `unarchive` is false and the agent is archived, the call is a silent
 * no-op (returns `{ outOfBand: false }`) — the agent is not run.
 */
export async function sendPromptToAgent(params) {
    const unarchive = params.unarchive ?? true;
    const record = await params.agentStorage.get(params.agentId);
    if (record?.archivedAt) {
        if (!unarchive) {
            return { outOfBand: false };
        }
        await unarchiveAgentState(params.agentStorage, params.agentManager, params.agentId);
    }
    await ensureAgentLoaded(params.agentId, {
        agentManager: params.agentManager,
        agentStorage: params.agentStorage,
        logger: params.logger,
    });
    if (params.sessionMode) {
        await params.agentManager.setAgentMode(params.agentId, params.sessionMode);
    }
    const runOptions = params.messageId
        ? { ...params.runOptions, messageId: params.messageId }
        : params.runOptions;
    return startAgentRun(params.agentManager, params.agentId, params.prompt, params.logger, {
        replaceRunning: true,
        runOptions,
    });
}
export async function startCreatedAgentInitialPrompt(params) {
    const currentSnapshot = params.agentManager.getAgent(params.agentId) ?? params.snapshot ?? null;
    if (!currentSnapshot) {
        throw new Error(`Agent ${params.agentId} not found`);
    }
    if (params.prompt === null) {
        return currentSnapshot;
    }
    const dispatchResult = startAgentRun(params.agentManager, params.agentId, params.prompt, params.logger, {
        runOptions: params.runOptions,
    });
    if (!dispatchResult.outOfBand) {
        await waitForAgentRunStartWithTimeout(params.agentManager, params.agentId);
    }
    const refreshedSnapshot = params.agentManager.getAgent(params.agentId) ?? params.snapshot ?? null;
    if (!refreshedSnapshot) {
        throw new Error(`Agent ${params.agentId} not found`);
    }
    return refreshedSnapshot;
}
export function setupFinishNotification(params) {
    const { agentManager, agentStorage, childAgentId, callerAgentId, logger } = params;
    let hasSeenRunning = false;
    let fired = false;
    let unsubscribe = null;
    async function notify(reason) {
        if (fired) {
            return;
        }
        fired = true;
        unsubscribe?.();
        const record = await agentStorage.get(childAgentId);
        const title = record?.title ?? childAgentId;
        const body = `Agent ${childAgentId} (${title}) ${reason}.`;
        await sendPromptToAgent({
            agentManager,
            agentStorage,
            agentId: callerAgentId,
            prompt: formatSystemNotificationPrompt(body),
            unarchive: false,
            logger,
        });
    }
    unsubscribe = agentManager.subscribe((event) => {
        if (fired) {
            return;
        }
        if (event.type === "agent_state") {
            if (event.agent.lifecycle === "running") {
                hasSeenRunning = true;
                return;
            }
            if (event.agent.lifecycle === "error") {
                void notify("errored");
                return;
            }
            if (event.agent.lifecycle === "idle" && hasSeenRunning) {
                void notify("finished");
                return;
            }
            if (event.agent.lifecycle === "closed") {
                fired = true;
                unsubscribe?.();
                return;
            }
            return;
        }
        if (event.event.type === "permission_requested") {
            void notify("needs permission");
        }
    }, { agentId: childAgentId, replayState: false });
    // Check if the child is already running (catches the case where
    // the lifecycle flipped before our subscribe call was processed).
    // Do NOT treat an immediate "idle" as "finished" — the agent may
    // not have started yet (streamAgent sets a pending run before
    // transitioning to "running").
    const childSnapshot = agentManager.getAgent(childAgentId);
    if (!childSnapshot || childSnapshot.lifecycle === "closed") {
        unsubscribe();
        return;
    }
    if (childSnapshot.lifecycle === "running") {
        hasSeenRunning = true;
    }
    else if (childSnapshot.lifecycle === "error") {
        void notify("errored");
    }
}
//# sourceMappingURL=agent-prompt.js.map