/**
 * Jait Provider — wraps Jait's own runAgentLoop as a provider adapter.
 *
 * This is the default provider that uses the existing OpenAI-compatible
 * agent loop with Jait's full tool registry, consent, and memory.
 * It doesn't spawn a child process — it runs in-process.
 */
import { EventEmitter } from "node:events";
import { uuidv7 } from "../db/uuidv7.js";
import { resolveJaitLlmConfig } from "../services/jait-llm.js";
import { buildSystemPrompt, buildTieredToolSchemas, runAgentLoop, } from "../tools/index.js";
import { NO_PROVIDER_AUTH, unsupportedLogin, unsupportedLogout } from "./provider-auth.js";
export class JaitProvider {
    deps;
    id = "jait";
    info = {
        id: "jait",
        name: "Jait (Built-in)",
        description: "Jait's native agent loop using OpenAI-compatible APIs with full tool access",
        available: true, // Always available — it's the built-in provider
        modes: ["full-access", "supervised"],
        auth: NO_PROVIDER_AUTH,
    };
    emitter = new EventEmitter();
    sessions = new Map();
    constructor(deps) {
        this.deps = deps;
    }
    async checkAvailability() {
        this.info.available = true;
        return true;
    }
    async listModels() {
        return JAIT_MODELS;
    }
    async getAuthStatus() {
        return {
            ...NO_PROVIDER_AUTH,
            authenticated: null,
            detail: "Jait uses API keys configured in Jait settings rather than a CLI login.",
        };
    }
    async startLogin() {
        return unsupportedLogin(this.id, "Jait uses API keys configured in Jait settings rather than a CLI login.");
    }
    async logout() {
        return unsupportedLogout(this.id, "Jait uses API keys configured in Jait settings rather than a CLI login.");
    }
    async startSession(options) {
        const thread = this.deps.threadService.getById(options.threadId);
        const userId = thread?.userId ?? undefined;
        const llm = this.buildLlmConfig(userId, options.model);
        const prompt = buildSystemPrompt("agent", { model: llm.openaiModel, baseUrl: llm.openaiBaseUrl }, { workspaceRoot: options.workingDirectory });
        const session = {
            id: uuidv7(),
            providerId: "jait",
            threadId: options.threadId,
            status: "running",
            runtimeMode: options.mode,
            startedAt: new Date().toISOString(),
        };
        this.sessions.set(session.id, {
            session,
            threadId: options.threadId,
            workingDirectory: options.workingDirectory,
            model: options.model,
            userId,
            history: [{ role: "system", content: prompt }],
        });
        this.emit({ type: "session.started", sessionId: session.id });
        return session;
    }
    async sendTurn(sessionId, message) {
        const state = this.sessions.get(sessionId);
        if (!state) {
            throw new Error(`Unknown Jait session: ${sessionId}`);
        }
        if (state.currentTurn) {
            throw new Error("A turn is already in progress for this Jait session.");
        }
        const abort = new AbortController();
        state.currentTurnAbort = abort;
        state.contextRounds = [];
        state.history.push({ role: "user", content: message });
        this.emit({ type: "turn.started", sessionId });
        const turnPromise = (async () => {
            try {
                const userSettings = state.userId ? this.deps.userService?.getSettings(state.userId) : undefined;
                const disabledTools = userSettings?.disabledTools?.length
                    ? new Set(userSettings.disabledTools)
                    : undefined;
                const toolSchemas = this.deps.toolRegistry
                    ? buildTieredToolSchemas(this.deps.toolRegistry, disabledTools)
                    : [];
                const llm = this.buildLlmConfig(state.userId, state.model);
                const result = await runAgentLoop({
                    llm,
                    history: state.history,
                    toolSchemas,
                    hasTools: toolSchemas.length > 0,
                    sessionId,
                    auth: {
                        userId: state.userId,
                        apiKeys: userSettings?.apiKeys ?? {},
                        providerId: "jait",
                        model: llm.openaiModel,
                        runtimeMode: state.session.runtimeMode,
                    },
                    abort,
                    maxRounds: 15,
                    parallel: true,
                    toolRegistry: this.deps.toolRegistry,
                    disabledTools,
                    mode: "agent",
                    onEvent: (event) => this.forwardAgentLoopEvent(sessionId, event),
                    onContext: (round) => this.forwardContextRound(sessionId, state, round),
                }, (toolName, input, sid, auth, onOutputChunk, signal) => this.executeTool(toolName, input, sid, auth, onOutputChunk, signal, state.workingDirectory));
                if (result.content) {
                    this.emit({ type: "message", sessionId, role: "assistant", content: result.content });
                }
                const session = this.sessions.get(sessionId)?.session;
                if (session) {
                    session.status = abort.signal.aborted ? "interrupted" : "running";
                }
                this.emit({ type: "turn.completed", sessionId });
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                const session = this.sessions.get(sessionId)?.session;
                if (session) {
                    session.status = "error";
                    session.error = message;
                }
                this.emit({ type: "session.error", sessionId, error: message });
            }
            finally {
                const current = this.sessions.get(sessionId);
                if (current) {
                    current.currentTurn = undefined;
                    current.currentTurnAbort = undefined;
                }
            }
        })();
        state.currentTurn = turnPromise;
        await turnPromise;
    }
    async interruptTurn(sessionId) {
        const state = this.sessions.get(sessionId);
        if (!state)
            return;
        state.currentTurnAbort?.abort();
        state.session.status = "interrupted";
    }
    async respondToApproval(_sessionId, _requestId, _approved) {
        // Jait uses its own ConsentManager — this is a no-op
    }
    async stopSession(sessionId) {
        const state = this.sessions.get(sessionId);
        if (state) {
            state.currentTurnAbort?.abort();
            state.session.status = "completed";
            state.session.completedAt = new Date().toISOString();
            if (state.currentTurn) {
                await state.currentTurn.catch(() => { });
            }
            this.emit({ type: "session.completed", sessionId });
        }
        this.sessions.delete(sessionId);
    }
    onEvent(handler) {
        this.emitter.on("event", handler);
        return () => this.emitter.off("event", handler);
    }
    emit(event) {
        this.emitter.emit("event", event);
    }
    buildLlmConfig(userId, requestedModel) {
        const userSettings = userId ? this.deps.userService?.getSettings(userId) : undefined;
        return resolveJaitLlmConfig({
            config: this.deps.config,
            apiKeys: userSettings?.apiKeys,
            requestedModel,
            jaitBackend: userSettings?.jaitBackend,
        });
    }
    async executeTool(toolName, input, sessionId, auth, onOutputChunk, signal, workspaceRoot) {
        if (!this.deps.toolRegistry) {
            return { ok: false, message: "Tool registry not available" };
        }
        const context = {
            sessionId,
            actionId: uuidv7(),
            workspaceRoot,
            requestedBy: "agent",
            userId: auth?.userId,
            apiKeys: auth?.apiKeys,
            providerId: auth?.providerId,
            model: auth?.model,
            runtimeMode: auth?.runtimeMode,
            onOutputChunk,
            signal,
        };
        try {
            return this.deps.toolExecutor
                ? await this.deps.toolExecutor(toolName, input, context)
                : await this.deps.toolRegistry.execute(toolName, input, context);
        }
        catch (error) {
            return { ok: false, message: error instanceof Error ? error.message : String(error) };
        }
    }
    forwardContextRound(sessionId, state, round) {
        // Accumulate rounds so we can emit the full flow each time
        if (!state.contextRounds)
            state.contextRounds = [];
        state.contextRounds.push(round);
        const llm = this.buildLlmConfig(state.userId, state.model);
        this.emit({
            type: "activity",
            sessionId,
            kind: "context_flow",
            summary: `Round ${round.round} context`,
            payload: {
                provider: "jait",
                model: llm.openaiModel,
                rounds: state.contextRounds,
            },
        });
    }
    forwardAgentLoopEvent(sessionId, event) {
        switch (event.type) {
            case "token":
                this.emit({ type: "token", sessionId, content: event.content });
                break;
            case "tool_start":
                this.emit({ type: "tool.start", sessionId, tool: event.tool, args: event.args, callId: event.call_id });
                break;
            case "tool_output":
                this.emit({ type: "tool.output", sessionId, callId: event.call_id, content: event.content });
                break;
            case "tool_result":
                this.emit({
                    type: "tool.result",
                    sessionId,
                    tool: event.tool,
                    ok: event.ok,
                    message: event.message,
                    callId: event.call_id,
                    data: event.data,
                });
                break;
            case "error":
                this.emit({ type: "activity", sessionId, kind: "error", summary: event.message });
                break;
            default:
                break;
        }
    }
}
const JAIT_MODELS = [
    { id: "gpt-4o", name: "GPT-4o", description: "OpenAI's flagship multimodal model", isDefault: true },
    { id: "gpt-4o-mini", name: "GPT-4o Mini", description: "Fast and affordable" },
    { id: "gpt-4.1", name: "GPT-4.1", description: "Latest GPT-4 series" },
    { id: "gpt-4.1-mini", name: "GPT-4.1 Mini", description: "Fast GPT-4.1" },
    { id: "gpt-4.1-nano", name: "GPT-4.1 Nano", description: "Ultra-fast, lightweight" },
    { id: "o4-mini", name: "o4 Mini", description: "Reasoning model" },
    { id: "o3", name: "o3", description: "Advanced reasoning" },
    { id: "o3-mini", name: "o3 Mini", description: "Fast reasoning" },
    { id: "claude-sonnet-4-6-20250318", name: "Claude Sonnet 4.6", description: "Via OpenRouter/compatible API" },
    { id: "deepseek-chat", name: "DeepSeek V3", description: "DeepSeek's latest" },
    { id: "deepseek-reasoner", name: "DeepSeek R1", description: "DeepSeek reasoning model" },
];
/** Models available when using OpenRouter as the base URL or API key. */
export const OPENROUTER_MODELS = [
    { id: "anthropic/claude-sonnet-4-20250514", name: "Claude Sonnet 4", description: "Anthropic's latest Sonnet" },
    { id: "anthropic/claude-3.5-sonnet", name: "Claude 3.5 Sonnet", description: "Fast and capable" },
    { id: "anthropic/claude-3-opus", name: "Claude 3 Opus", description: "Most powerful Claude" },
    { id: "anthropic/claude-3.5-haiku", name: "Claude 3.5 Haiku", description: "Fast and affordable" },
    { id: "google/gemini-2.5-pro-preview", name: "Gemini 2.5 Pro", description: "Google's latest" },
    { id: "google/gemini-2.5-flash-preview", name: "Gemini 2.5 Flash", description: "Fast Google model" },
    { id: "deepseek/deepseek-chat-v3-0324", name: "DeepSeek V3", description: "DeepSeek's latest via OpenRouter" },
    { id: "deepseek/deepseek-r1", name: "DeepSeek R1", description: "DeepSeek reasoning via OpenRouter" },
    { id: "xiaomi/mimo-v2-pro", name: "MiMo-V2-Pro", description: "Xiaomi's flagship agent model" },
    { id: "xiaomi/mimo-v2-flash", name: "MiMo-V2-Flash", description: "Fast Xiaomi reasoning and coding model" },
    { id: "xiaomi/mimo-v2-omni", name: "MiMo-V2-Omni", description: "Multimodal Xiaomi agent model" },
    { id: "meta-llama/llama-4-maverick", name: "Llama 4 Maverick", description: "Meta's latest open model" },
    { id: "meta-llama/llama-4-scout", name: "Llama 4 Scout", description: "Meta's efficient open model" },
    { id: "qwen/qwen3-235b-a22b", name: "Qwen3 235B", description: "Alibaba's largest model" },
    { id: "mistralai/mistral-large-2411", name: "Mistral Large", description: "Mistral's flagship" },
    { id: "x-ai/grok-3-mini-beta", name: "Grok 3 Mini", description: "xAI's reasoning model" },
];
//# sourceMappingURL=jait-provider.js.map