/**
 * Adapt the legacy `AgentSession` to the embedded-runtime contract
 * (`EmbeddedAgentRuntime`) so the CLI app can be driven through the
 * same RuntimeApi surface as web/worker hosts. This is the integration
 * seam between `app/` (legacy stack) and `core/` + `contracts/` (the new
 * embedded runtime). All state remains owned by `AgentSession`; this file
 * only exposes a subset of methods and resolves model/thinking-level
 * defaults consistently with how the CLI bootstrapped sessions previously.
 */
import { completeSimple, streamSimple } from '../ai/stream.js';
import { AgentStopReason } from '../contracts/agent-snapshot.js';
import { createAgentSnapshotFromAgentMessages } from '../core/agent-snapshot-builder.js';
import { createEmbeddedAgentRuntime } from '../core/embedded-agent-facade.js';
import { AgentController, AgentEngine, DEFAULT_THINKING_LEVEL } from '../core/index.js';
import { createPendingHostToolRequests } from '../core/pending-host-tool-requests.js';
import { getHostToolTimeoutMs } from '../embedded-agent/host-tools/timeouts.js';
import { AgentSession, LoginRequiredError } from './agent-session.js';
import { SessionAutoApproveState } from './approvals/session-auto-approve.js';
import { convertToLlm } from './messages.js';
import { findInitialModel } from './providers/model-resolver.js';
function createAgentSessionCore(session, pendingHostToolRequests) {
    // AgentSession itself doesn't carry a "the last turn was cancelled" flag —
    // cancellation just leaves the message stream truncated. Track it locally
    // so the next snapshot reflects AgentStopReason.Cancelled. Cleared on
    // any new submit/resume.
    let stopReasonOverride;
    return createEmbeddedAgentRuntime({
        async abort() {
            // Snapshot subscribers may fire while abort is unwinding. Mark the turn
            // cancelled before aborting so a stale completed snapshot cannot win.
            stopReasonOverride = AgentStopReason.Cancelled;
            await session.abort();
        },
        async configure(configuration) {
            session.replaceToolsByName(configuration.toolNames);
            session.agent.setSystemPrompt(configuration.systemPrompt);
        },
        dispose() {
            session.dispose();
        },
        getSnapshot() {
            return createAgentSnapshotFromAgentMessages({
                sessionId: session.sessionId,
                messages: session.messages,
                isStreaming: session.isStreaming,
                stopReasonOverride,
                errorKind: session.agent.state.errorKind,
                inFlight: session.agent.state.inFlight
            });
        },
        getSessionContext() {
            return session.sessionManager.buildSessionContext();
        },
        async resumeWithHostToolResult(result) {
            if (stopReasonOverride !== AgentStopReason.Cancelled) {
                stopReasonOverride = undefined;
            }
            pendingHostToolRequests.applyResult(result);
        },
        async submit(prompt) {
            stopReasonOverride = undefined;
            await session.prompt(prompt);
        },
        subscribeSnapshotSource(listener) {
            return session.subscribe(listener);
        }
    }, pendingHostToolRequests);
}
export async function createAgentSessionRuntimeAdapter(options) {
    const { approvalState = new SessionAutoApproveState(options.settingsManager), cwd, modelRegistry, resourceLoader, sessionManager, settingsManager } = options;
    const existingSession = sessionManager.buildSessionContext();
    const hasExistingSession = existingSession.messages.length > 0;
    const hasThinkingEntry = sessionManager
        .getBranch()
        .some((entry) => entry.type === 'thinking_level_change');
    let model = options.model;
    let modelFallbackMessage;
    if (!model && hasExistingSession && existingSession.model) {
        const restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);
        if (restoredModel && (await modelRegistry.getApiKey(restoredModel))) {
            model = restoredModel;
        }
        if (!model) {
            modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;
        }
    }
    if (!model) {
        const resolved = await findInitialModel({
            isContinuing: hasExistingSession,
            defaultProvider: settingsManager.getDefaultProvider(),
            defaultModelId: settingsManager.getDefaultModel(),
            defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),
            modelRegistry
        });
        model = resolved.model;
        if (!model) {
            modelFallbackMessage = 'No models available. Configure credentials or provide a model.';
        }
        else if (modelFallbackMessage) {
            modelFallbackMessage += `. Using ${model.provider}/${model.id}`;
        }
    }
    let thinkingLevel = options.thinkingLevel;
    if (thinkingLevel === undefined && hasExistingSession) {
        thinkingLevel = hasThinkingEntry
            ? existingSession.thinkingLevel
            : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
    }
    if (thinkingLevel === undefined) {
        thinkingLevel = settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
    }
    if (!model || !model.reasoning) {
        thinkingLevel = 'off';
    }
    // Mutable holder so the AgentEngine constructor's `getApiKey` can resolve
    // off the agent that we'll build below. Lets us construct the runtime (and
    // pending host-tool requests that depend on its heartbeat) before the
    // AgentController itself.
    const agentRef = {};
    const extensionRunnerRef = {};
    const agentRuntime = new AgentEngine({
        convertToLlm,
        streamFn: streamSimple,
        sessionId: sessionManager.getSessionId(),
        transformContext: async (messages) => {
            const runner = extensionRunnerRef.current;
            if (!runner)
                return messages;
            return runner.emitContext(messages);
        },
        transport: settingsManager.getTransport(),
        maxRetryDelayMs: settingsManager.getRetrySettings().maxDelayMs,
        getApiKey: async (provider) => {
            const resolvedProvider = provider || agentRef.agent?.state.model?.provider;
            if (!resolvedProvider) {
                throw new LoginRequiredError();
            }
            const apiKey = await modelRegistry.getApiKeyForProvider(resolvedProvider);
            if (!apiKey) {
                throw new LoginRequiredError();
            }
            return apiKey;
        },
        getToolContext: () => extensionRunnerRef.current?.createContext()
    });
    const pendingHostToolRequests = createPendingHostToolRequests({
        getTimeoutMs: getHostToolTimeoutMs,
        getHeartbeatWrap: () => agentRuntime.getHeartbeatWrap()
    });
    const sessionPolicy = {
        getApprovalContext() {
            return { autoApprove: approvalState.getAutoApprove() };
        }
    };
    const perKindLlmOptions = options.hostToolLlmOptions ?? {};
    const hostTools = (options.hostToolKinds ?? []).map((kind) => {
        if (!options.hostToolRegistry) {
            throw new Error(`hostToolKinds was provided but hostToolRegistry is not set (kind: ${kind})`);
        }
        const contract = options.hostToolRegistry.get(kind);
        if (!contract) {
            throw new Error(`Unknown host tool kind: ${kind}`);
        }
        const perToolOptions = perKindLlmOptions[kind] ?? {};
        const llmOptions = {
            pendingHostToolRequests,
            sessionPolicy,
            ...perToolOptions
        };
        return contract.createLlmTool(llmOptions);
    });
    const runtimeTools = [...hostTools, ...(options.tools ?? [])];
    const toolNames = options.toolNames ?? runtimeTools.map((tool) => tool.name);
    if (new Set(toolNames).size !== toolNames.length) {
        throw new Error('Embedded runtime tool names must be unique');
    }
    const agent = new AgentController({
        initialState: {
            model,
            systemPrompt: '',
            thinkingLevel,
            tools: []
        },
        steeringMode: settingsManager.getSteeringMode(),
        followUpMode: settingsManager.getFollowUpMode(),
        runtime: agentRuntime
    });
    agentRef.agent = agent;
    if (hasExistingSession) {
        agent.replaceMessages(existingSession.messages);
        if (!hasThinkingEntry) {
            sessionManager.appendThinkingLevelChange(thinkingLevel);
        }
    }
    else {
        if (model) {
            sessionManager.appendModelChange(model.provider, model.id);
        }
        sessionManager.appendThinkingLevelChange(thinkingLevel);
    }
    const session = new AgentSession({
        agent,
        sessionManager,
        settingsManager,
        approvalState,
        cwd,
        resourceLoader,
        tools: runtimeTools,
        reloadTools: options.reloadTools,
        initialToolNames: toolNames,
        initialModeName: options.modeName,
        initialPermissionPolicy: options.permissionPolicy,
        modelRegistry,
        fastModeAllowed: options.fastModeAllowed,
        fastModeDisabledReason: options.fastModeDisabledReason,
        extensionRunnerRef,
        prePromptContext: options.prePromptContext,
        budgetLimits: options.budgetLimits,
        enableToolSummaries: options.enableToolSummaries,
        observability: options.observability,
        compactionComplete: completeSimple
    });
    return {
        session,
        runtime: createAgentSessionCore(session, pendingHostToolRequests),
        extensionsResult: resourceLoader.getExtensions(),
        modelFallbackMessage
    };
}
//# sourceMappingURL=agent-session-runtime-adapter.js.map