import { getAgentEngine } from "./engines/engine-factory.js";
import { getConfigDir, getPlaywrightProfileDir, getScreenshotsDir, loadDynamicMcpServers } from "../config/index.js";
import { getCdpEndpoint } from "./browser-launcher.js";
import { resolvePhonecallApiKey } from "../phonecall-credentials.js";
import { resolveFinancialToolApiKey } from "../financial-tool-credentials.js";
import { resolveSerpApiKey } from "../serpapi-credentials.js";
import { ensurePlaywriterExtension, isPlaywriterExtensionInChromeProfiles, isPlaywriterExtensionJsonInstalled, } from "../onboarding/playwriter-extension.js";
import { ensurePlaywriterRelay, cleanupOrphanRelay, getRelayCdpEndpoint, } from "../utils/playwriter-relay.js";
import { killPlaywrightProfileCdpChrome, launchPlaywriterProfileChrome, } from "./engines/claude/session-browser-policy.js";
import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";
import { logger } from "../logger.js";
async function detectBrowserBackend(config) {
    if (process.platform !== "darwin")
        return;
    const host = config.playwriterRelayHost ?? "127.0.0.1";
    const port = config.playwriterRelayPort ?? 19988;
    ensurePlaywriterExtension();
    if (config.browserBackend === "playwriter") {
        try {
            killPlaywrightProfileCdpChrome();
            launchPlaywriterProfileChrome();
            const status = await ensurePlaywriterRelay(host, port);
            if (status.extensionConnected) {
                logger.system("Browser backend: Playwriter (explicitly configured, relay healthy)");
            }
            else {
                config.browserBackend = "playwright";
                logger.system(`Browser backend: Playwright CDP (Playwriter relay started but extension not connected at ${host}:${port}, falling back)`);
            }
        }
        catch (err) {
            config.browserBackend = "playwright";
            logger.system(`Browser backend: Playwright CDP (Playwriter relay startup failed: ${err instanceof Error ? err.message : String(err)})`);
        }
        return;
    }
    try {
        if (isPlaywriterExtensionJsonInstalled()) {
            const chromeProfiles = isPlaywriterExtensionInChromeProfiles();
            try {
                killPlaywrightProfileCdpChrome();
                launchPlaywriterProfileChrome();
                const status = await ensurePlaywriterRelay(host, port);
                if (status.extensionConnected) {
                    config.browserBackend = "playwriter";
                    const profileInfo = chromeProfiles.installed
                        ? ` (extension in ${chromeProfiles.profiles.join(", ")})`
                        : "";
                    logger.system(`Browser backend: Playwriter (relay healthy${profileInfo})`);
                }
                else {
                    logger.system(`Browser backend: Playwright CDP (relay started but extension not connected at ${host}:${port})`);
                }
            }
            catch (err) {
                logger.system(`Browser backend: Playwright CDP (relay startup failed: ${err instanceof Error ? err.message : String(err)})`);
            }
        }
        else {
            logger.system("Browser backend: Playwright CDP (Playwriter extension JSON not found)");
            cleanupOrphanRelay();
        }
    }
    catch {
        logger.system("Browser backend: Playwright CDP (detection failed, using default)");
    }
}
export async function buildRuntimeSurface(config) {
    await detectBrowserBackend(config);
    const dynamicExternalMcpServers = loadDynamicMcpServers();
    const hasSerpApi = Boolean(resolveSerpApiKey(config));
    const agentEngine = getAgentEngine(config);
    return {
        runtimeSurface: {
            nativeTools: buildNativeToolSurface(config, { hasSerpApi }),
            disallowedTools: [
                // Claude Code CLI tools that are meaningless for a headless autonomous agent
                "AskUserQuestion",
                "CronCreate",
                "CronDelete",
                "CronList",
                "EnterPlanMode",
                "ExitPlanMode",
                "EnterWorktree",
                "ExitWorktree",
                "PushNotification",
                "RemoteTrigger",
                "ScheduleWakeup",
                ...(hasSerpApi ? ["WebSearch"] : []),
            ],
            visionClawToolTransport: {
                transport: agentEngine === "pi-coding" ? "pi-native" : "claude-mcp",
                supportsSwitchSessionTool: true,
            },
            externalMcpServers: {
                ...buildBuiltinExternalMcpServers(config),
                ...dynamicExternalMcpServers,
            },
        },
        startupDynamicMcpServerCount: Object.keys(dynamicExternalMcpServers).length,
    };
}
function buildNativeToolSurface(_config, _opts) {
    return { kind: "claude-default" };
}
function buildPlaywrightMcpServer() {
    const screenshotsDir = getScreenshotsDir();
    const cdpEndpoint = getCdpEndpoint();
    const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
    const binDir = path.join(packageRoot, "node_modules", ".bin");
    const playwrightConfig = {
        outputDir: screenshotsDir,
        browser: { cdpEndpoint, cdpTimeout: 30_000 },
    };
    const configPath = path.join(getConfigDir(), "playwright-mcp-config.json");
    fs.writeFileSync(configPath, JSON.stringify(playwrightConfig, null, 2));
    return {
        playwright: {
            command: "npx",
            args: ["@playwright/mcp", "--config", configPath],
            env: {
                PATH: `${binDir}:${process.env.PATH ?? ""}`,
            },
            // SDK 0.3.142+ connects MCP servers in the background by default, so
            // deferred tools may be missing on turn 1. The browser must be usable
            // immediately, so force eager load (startup waits up to ~5s).
            alwaysLoad: true,
        },
    };
}
function buildPlaywriterRelayMcpServer(config) {
    const screenshotsDir = getScreenshotsDir();
    const host = config.playwriterRelayHost ?? "127.0.0.1";
    const port = config.playwriterRelayPort ?? 19988;
    const cdpEndpoint = getRelayCdpEndpoint(host, port);
    const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
    const binDir = path.join(packageRoot, "node_modules", ".bin");
    const playwrightConfig = {
        outputDir: screenshotsDir,
        browser: { cdpEndpoint, cdpTimeout: 30_000, userDataDir: getPlaywrightProfileDir() },
    };
    const configPath = path.join(getConfigDir(), "playwright-mcp-relay-config.json");
    fs.writeFileSync(configPath, JSON.stringify(playwrightConfig, null, 2));
    return {
        playwright: {
            command: "npx",
            args: ["@playwright/mcp", "--config", configPath],
            env: {
                PATH: `${binDir}:${process.env.PATH ?? ""}`,
            },
            // See buildPlaywrightMcpServer: browser tools must be ready on turn 1.
            alwaysLoad: true,
        },
    };
}
function buildBuiltinExternalMcpServers(config) {
    const browserServer = config.browserBackend === "playwriter"
        ? buildPlaywriterRelayMcpServer(config)
        : buildPlaywrightMcpServer();
    const externalMcpServers = {
        ...browserServer,
    };
    const serpApiKey = resolveSerpApiKey(config);
    if (serpApiKey) {
        externalMcpServers.serpapi = {
            type: "http",
            url: buildSerpApiMcpUrl(serpApiKey),
        };
    }
    const phonecallApiKey = resolvePhonecallApiKey(config);
    if (phonecallApiKey) {
        externalMcpServers.phonecall = {
            type: "http",
            url: `https://phone-mcp.visionclaw.dev/mcp/${phonecallApiKey}`,
        };
    }
    const financialToolApiKey = resolveFinancialToolApiKey(config);
    if (financialToolApiKey) {
        externalMcpServers["financial-tool"] = {
            type: "http",
            url: `https://financial-tool.visionclaw.dev/mcp/${financialToolApiKey}`,
        };
    }
    return externalMcpServers;
}
/**
 * Pick the SerpAPI MCP endpoint based on the shape of the key.
 *
 * VisionClaw mints two distinct kinds of "SerpAPI keys":
 *
 *   1. Per-agent keys provisioned through Azure APIM
 *      (`POST /api/agents/[name]/credentials/serpapi`). These are APIM
 *      subscription primary keys — exactly 32 lowercase hex chars — and
 *      must be presented to the APIM-fronted MCP at
 *      `https://visionclaw-serpapi.azure-api.net/<key>/mcp`.
 *
 *   2. Direct SerpAPI keys (legacy / shared / BYO). These come from
 *      serpapi.com itself and are 64 mixed-case hex chars. They go to
 *      `https://mcp.serpapi.com/<key>/mcp`.
 *
 * The two services don't accept each other's keys, so we route by
 * sniffing the key shape. This is a deliberate heuristic — the
 * "principled" alternative is to tag the credential with a `provider`
 * field at the source (heartbeat / config), which is what we'd do if
 * the shapes ever collide. Today they don't, so the regex wins on
 * simplicity.
 */
function buildSerpApiMcpUrl(apiKey) {
    const trimmed = apiKey.trim();
    if (/^[0-9a-f]{32}$/.test(trimmed)) {
        return `https://visionclaw-serpapi.azure-api.net/${trimmed}/mcp`;
    }
    return `https://mcp.serpapi.com/${trimmed}/mcp`;
}
//# sourceMappingURL=runtime-surface.js.map