import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ensureValidJson } from "../json-utils.js";
import { AgentFeatureSchema, AgentPermissionRequestPayloadSchema, AgentListItemPayloadSchema, AgentPermissionResponseSchema, AgentSnapshotPayloadSchema, } from "../messages.js";
import { buildStoredAgentPayload, toAgentListItemPayload, toAgentPayload, } from "./agent-projections.js";
import { curateAgentActivity } from "./activity-curator.js";
import { selectItemsByProjectedLimit } from "./timeline-projection.js";
import { ensureAgentLoaded } from "./agent-loading.js";
import { isStoredAgentProviderAvailable } from "../persistence-hooks.js";
import { killTerminalsUnderPath, } from "../paseo-worktree-archive-service.js";
import { WaitForAgentTracker } from "./wait-for-agent-tracker.js";
import { createAgentCommand } from "./create-agent/create.js";
import { expandUserPath, isSameOrDescendantPath, resolvePathFromBase } from "../path-utils.js";
import { ScheduleRunSchema, ScheduleSummarySchema, StoredScheduleSchema, } from "@kong-link/protocol/schedule/types";
import { resolveSnapshotCwd } from "./provider-snapshot-manager.js";
import { AgentModelSchema, AgentProviderEnum, AgentStatusEnum, ProviderModeSchema, ProviderSummarySchema, parseDurationString, resolveRequiredProviderModel, sanitizePermissionRequest, serializeSnapshotWithMetadata, toScheduleSummary, waitForAgentWithTimeout, } from "./mcp-shared.js";
import { sendPromptToAgent, setupFinishNotification } from "./agent-prompt.js";
import { respondToAgentPermission } from "./permission-response.js";
import { archiveAgentCommand, cancelAgentRunCommand, closeAgentCommand, setAgentModeCommand, updateAgentCommand, } from "./lifecycle-command.js";
import { WorktreeRequestError } from "../worktree-errors.js";
import { archivePaseoWorktreeCommand, createPaseoWorktreeCommand, listPaseoWorktreesCommand, } from "../worktree/commands.js";
function addModelVisibleStructuredContent(result) {
    if (result.structuredContent === undefined || result.content.length > 0) {
        return result;
    }
    return {
        ...result,
        content: [
            {
                type: "text",
                text: formatStructuredContentForModel(result.structuredContent),
            },
        ],
    };
}
function formatStructuredContentForModel(structuredContent) {
    if (!structuredContent ||
        typeof structuredContent !== "object" ||
        Array.isArray(structuredContent)) {
        return JSON.stringify(structuredContent, null, 2);
    }
    const record = structuredContent;
    const summary = [];
    for (const [key, value] of Object.entries(record)) {
        if (!Array.isArray(value)) {
            continue;
        }
        summary.push(`${key}_count=${value.length}`);
        const ids = value
            .map((item) => item && typeof item === "object" && !Array.isArray(item)
            ? item.id
            : null)
            .filter((id) => typeof id === "string" && id.length > 0);
        if (ids.length === value.length && ids.length > 0) {
            summary.push(`${key}_ids=${ids.join(",")}`);
        }
    }
    const json = JSON.stringify(structuredContent, null, 2);
    return summary.length > 0 ? `${summary.join("\n")}\n\n${json}` : json;
}
function isZodSchema(value) {
    return (typeof value === "object" && value !== null && "_def" in value && "safeParseAsync" in value);
}
function relaxMcpOutputSchema(outputSchema) {
    if (!outputSchema) {
        return outputSchema;
    }
    if (isZodSchema(outputSchema)) {
        return outputSchema instanceof z.ZodObject ? outputSchema.passthrough() : outputSchema;
    }
    return z.object(outputSchema).passthrough();
}
function relaxMcpToolOutputSchema(config) {
    if (config.outputSchema === undefined) {
        return config;
    }
    return {
        ...config,
        outputSchema: relaxMcpOutputSchema(config.outputSchema),
    };
}
function parseTimestamp(value) {
    if (!value) {
        return 0;
    }
    const parsed = Date.parse(value);
    return Number.isNaN(parsed) ? 0 : parsed;
}
function resolveAgentListActivityTime(agent) {
    return Math.max(parseTimestamp(agent.updatedAt), parseTimestamp(agent.lastUserMessageAt), parseTimestamp(agent.attentionTimestamp), parseTimestamp(agent.archivedAt), parseTimestamp(agent.createdAt));
}
function toProviderSummary(entry) {
    return {
        id: entry.provider,
        label: entry.label ?? entry.provider,
        description: entry.description ?? "",
        enabled: entry.enabled,
        modes: entry.modes ?? [],
        status: entry.status === "ready" ? "available" : entry.status,
        ...(entry.error ? { error: entry.error } : {}),
    };
}
function compareAgentListItems(a, b) {
    const attentionDelta = Number(b.requiresAttention ?? false) - Number(a.requiresAttention ?? false);
    if (attentionDelta !== 0) {
        return attentionDelta;
    }
    const statusOrder = {
        running: 0,
        initializing: 1,
        idle: 2,
        error: 3,
        closed: 4,
    };
    const statusDelta = (statusOrder[a.status] ?? 999) - (statusOrder[b.status] ?? 999);
    if (statusDelta !== 0) {
        return statusDelta;
    }
    return resolveAgentListActivityTime(b) - resolveAgentListActivityTime(a);
}
function resolveScheduleProviderAndModel(params) {
    const providerInput = params.provider?.trim() || params.defaultProvider;
    const slashIndex = providerInput.indexOf("/");
    if (slashIndex === -1) {
        return { provider: providerInput };
    }
    const provider = providerInput.slice(0, slashIndex).trim();
    const model = providerInput.slice(slashIndex + 1).trim();
    if (!provider || !model) {
        throw new Error("provider must be <provider> or <provider>/<model>");
    }
    return {
        provider: provider,
        model,
    };
}
function resolveScheduleUpdateProviderAndModel(params) {
    const providerInput = params.provider?.trim();
    const modelInput = typeof params.model === "string" ? params.model.trim() : params.model;
    if (params.model !== undefined && modelInput === "") {
        throw new Error("model cannot be empty");
    }
    if (!providerInput) {
        return params.model !== undefined ? { model: modelInput } : {};
    }
    const slashIndex = providerInput.indexOf("/");
    if (slashIndex === -1) {
        return {
            provider: providerInput,
            ...(params.model !== undefined ? { model: modelInput } : {}),
        };
    }
    const provider = providerInput.slice(0, slashIndex).trim();
    const modelFromProvider = providerInput.slice(slashIndex + 1).trim();
    if (!provider || !modelFromProvider) {
        throw new Error("provider must be <provider> or <provider>/<model>");
    }
    if (params.model === null) {
        throw new Error("provider specifies a model but model is null");
    }
    if (typeof modelInput === "string" && modelInput !== modelFromProvider) {
        throw new Error("Conflicting model values provided");
    }
    return {
        provider,
        model: modelInput ?? modelFromProvider,
    };
}
function normalizeScheduleCadenceArg(value) {
    if (value === undefined) {
        return undefined;
    }
    const trimmed = value.trim();
    if (!trimmed) {
        return undefined;
    }
    return trimmed;
}
function normalizeScheduleTimeZoneArg(value) {
    return normalizeScheduleCadenceArg(value);
}
function resolveScheduleUpdateCadence(input) {
    const every = normalizeScheduleCadenceArg(input.every);
    const cron = normalizeScheduleCadenceArg(input.cron);
    const timeZone = normalizeScheduleTimeZoneArg(input.timezone);
    if (every !== undefined && cron !== undefined) {
        throw new Error("Specify at most one of every or cron");
    }
    if (timeZone !== undefined && cron === undefined) {
        throw new Error("timezone can only be used with cron");
    }
    if (every !== undefined) {
        return { type: "every", everyMs: parseDurationString(every) };
    }
    if (cron !== undefined) {
        return {
            type: "cron",
            expression: cron,
            ...(timeZone !== undefined ? { timezone: timeZone } : {}),
        };
    }
    return undefined;
}
function resolveScheduleUpdateExpiresAt(input) {
    if (input.expiresIn !== undefined && input.clearExpires) {
        throw new Error("Specify at most one of expiresIn or clearExpires");
    }
    if (input.expiresIn !== undefined) {
        return new Date(Date.now() + parseDurationString(input.expiresIn)).toISOString();
    }
    if (input.clearExpires) {
        return null;
    }
    return undefined;
}
function buildScheduleUpdateInput(input) {
    const cadence = resolveScheduleUpdateCadence(input);
    const expiresAt = resolveScheduleUpdateExpiresAt(input);
    const providerModelPatch = resolveScheduleUpdateProviderAndModel({
        provider: input.provider,
        model: input.model,
    });
    const newAgentConfig = {
        ...(providerModelPatch.provider !== undefined ? { provider: providerModelPatch.provider } : {}),
        ...(providerModelPatch.model !== undefined ? { model: providerModelPatch.model } : {}),
        ...(input.mode !== undefined ? { modeId: input.mode } : {}),
        ...(input.cwd !== undefined ? { cwd: input.cwd } : {}),
    };
    return {
        id: input.id,
        ...(input.name !== undefined ? { name: input.name } : {}),
        ...(input.prompt !== undefined ? { prompt: input.prompt } : {}),
        ...(cadence !== undefined ? { cadence } : {}),
        ...(input.maxRuns !== undefined ? { maxRuns: input.maxRuns } : {}),
        ...(expiresAt !== undefined ? { expiresAt } : {}),
        ...(Object.keys(newAgentConfig).length > 0 ? { newAgentConfig } : {}),
    };
}
function resolveChildAgentCwd(params) {
    const lockedCwd = params.lockedCwd?.trim();
    if (lockedCwd) {
        return expandUserPath(lockedCwd);
    }
    const requestedCwd = params.requestedCwd?.trim();
    if (!requestedCwd || !params.allowCustomCwd) {
        return params.parentCwd;
    }
    return resolvePathFromBase(params.parentCwd, requestedCwd);
}
const TerminalSummarySchema = z.object({
    id: z.string(),
    name: z.string(),
    cwd: z.string(),
});
const WorktreeSummarySchema = z.object({
    path: z.string(),
    createdAt: z.string(),
    branchName: z.string().optional(),
    head: z.string().optional(),
});
function resolveTerminalKeyToken(key, literal) {
    if (literal) {
        return key;
    }
    switch (key) {
        case "Enter":
            return "\r";
        case "Tab":
            return "\t";
        case "Escape":
            return "\u001b";
        case "Space":
            return " ";
        case "BSpace":
            return "\u007f";
        case "C-c":
            return "\u0003";
        case "C-d":
            return "\u0004";
        case "C-z":
            return "\u001a";
        case "C-l":
            return "\u000c";
        case "C-a":
            return "\u0001";
        case "C-e":
            return "\u0005";
        default:
            return key;
    }
}
export async function createAgentMcpServer(options) {
    const { agentManager, agentStorage, terminalManager, scheduleService, providerSnapshotManager, callerAgentId, resolveSpeakHandler, resolveCallerContext, logger, } = options;
    const childLogger = logger.child({ module: "agent", component: "mcp-server" });
    const waitTracker = new WaitForAgentTracker(logger);
    const callerContext = callerAgentId ? (resolveCallerContext?.(callerAgentId) ?? null) : null;
    const server = new McpServer({
        name: "agent-mcp",
        version: "2.0.0",
    });
    const registerRawTool = server.registerTool.bind(server);
    const registerTool = (name, config, handler) => registerRawTool(name, relaxMcpToolOutputSchema(config), (async (args, extra) => addModelVisibleStructuredContent(await handler(args, extra))));
    const buildCronScheduleCadence = (input) => {
        const expression = input.cron?.trim() ?? "";
        if (!expression) {
            throw new Error("cron is required");
        }
        const timezone = normalizeScheduleTimeZoneArg(input.timezone);
        return {
            type: "cron",
            expression,
            ...(timezone !== undefined ? { timezone } : {}),
        };
    };
    const buildScheduleExpiry = (expiresIn) => {
        return expiresIn === undefined
            ? undefined
            : new Date(Date.now() + parseDurationString(expiresIn)).toISOString();
    };
    const resolveCallerAgent = () => {
        if (!callerAgentId) {
            return null;
        }
        const parentAgent = agentManager.getAgent(callerAgentId);
        if (!parentAgent) {
            throw new Error(`Parent agent ${callerAgentId} not found`);
        }
        return parentAgent;
    };
    const resolveScopedCwd = (requestedCwd, opts) => {
        const callerAgent = resolveCallerAgent();
        if (callerAgent) {
            return resolveChildAgentCwd({
                parentCwd: callerAgent.cwd,
                requestedCwd,
                lockedCwd: callerContext?.lockedCwd,
                allowCustomCwd: callerContext?.allowCustomCwd ?? true,
            });
        }
        const trimmedCwd = requestedCwd?.trim();
        if (!trimmedCwd) {
            if (opts?.required) {
                throw new Error("cwd is required");
            }
            throw new Error("cwd is required outside an agent-scoped session");
        }
        return expandUserPath(trimmedCwd);
    };
    const buildCallerAgentScheduleConfigExtras = (callerAgent) => {
        return {
            ...(callerAgent.config.thinkingOptionId
                ? { thinkingOptionId: callerAgent.config.thinkingOptionId }
                : {}),
            ...(callerAgent.config.approvalPolicy
                ? { approvalPolicy: callerAgent.config.approvalPolicy }
                : {}),
            ...(callerAgent.config.sandboxMode ? { sandboxMode: callerAgent.config.sandboxMode } : {}),
            ...(typeof callerAgent.config.networkAccess === "boolean"
                ? { networkAccess: callerAgent.config.networkAccess }
                : {}),
            ...(typeof callerAgent.config.webSearch === "boolean"
                ? { webSearch: callerAgent.config.webSearch }
                : {}),
            ...(callerAgent.config.title ? { title: callerAgent.config.title } : {}),
            ...(callerAgent.config.extra ? { extra: callerAgent.config.extra } : {}),
            ...(callerAgent.config.featureValues
                ? { featureValues: callerAgent.config.featureValues }
                : {}),
            ...(callerAgent.config.systemPrompt ? { systemPrompt: callerAgent.config.systemPrompt } : {}),
            ...(callerAgent.config.mcpServers ? { mcpServers: callerAgent.config.mcpServers } : {}),
        };
    };
    const buildCallerAgentScheduleConfig = (callerAgent, params) => {
        const hasProviderOverride = params?.provider !== undefined;
        const resolvedProviderModel = hasProviderOverride
            ? resolveScheduleProviderAndModel({
                provider: params?.provider,
                defaultProvider: callerAgent.provider,
            })
            : null;
        const resolvedProvider = resolvedProviderModel?.provider ?? callerAgent.provider;
        let resolvedModel;
        if (resolvedProviderModel?.model) {
            resolvedModel = resolvedProviderModel.model;
        }
        else if (!hasProviderOverride && callerAgent.config.model) {
            resolvedModel = callerAgent.config.model;
        }
        return {
            provider: resolvedProvider,
            cwd: params?.cwd?.trim() ? expandUserPath(params.cwd) : callerAgent.cwd,
            ...(callerAgent.currentModeId && callerAgent.provider === resolvedProvider
                ? {
                    modeId: callerAgent.currentModeId,
                }
                : {}),
            ...(resolvedModel ? { model: resolvedModel } : {}),
            ...buildCallerAgentScheduleConfigExtras(callerAgent),
        };
    };
    const resolveNewAgentScheduleTarget = (params) => {
        if (!params?.provider?.trim()) {
            throw new Error("provider is required when target is new-agent");
        }
        const callerAgent = resolveCallerAgent();
        if (callerAgent) {
            return {
                type: "new-agent",
                config: buildCallerAgentScheduleConfig(callerAgent, params),
            };
        }
        const resolvedProviderModel = resolveScheduleProviderAndModel({
            provider: params?.provider,
            defaultProvider: params.provider,
        });
        return {
            type: "new-agent",
            config: {
                provider: resolvedProviderModel.provider,
                cwd: params?.cwd?.trim() ? expandUserPath(params.cwd) : process.cwd(),
                ...(resolvedProviderModel.model ? { model: resolvedProviderModel.model } : {}),
            },
        };
    };
    const ProviderModelInputSchema = AgentProviderEnum.trim()
        .refine((value) => value.includes("/"), {
        message: "provider must be provider/model, for example codex/gpt-5.4",
    })
        .refine((value) => {
        try {
            resolveRequiredProviderModel(value);
            return true;
        }
        catch {
            return false;
        }
    }, { message: "provider must be provider/model, for example codex/gpt-5.4" });
    const ProviderOrProviderModelInputSchema = AgentProviderEnum.trim()
        .min(1, "provider is required")
        .refine((value) => {
        if (!value.includes("/")) {
            return true;
        }
        try {
            resolveRequiredProviderModel(value);
            return true;
        }
        catch {
            return false;
        }
    }, { message: "provider must be provider or provider/model, for example codex/gpt-5.4" });
    const CreateAgentSettingsInputSchema = z
        .object({
        modeId: z.string().optional().describe("Session mode to configure before the first run."),
        thinkingOptionId: z.string().optional().describe("Thinking option ID."),
        features: z
            .record(z.unknown())
            .optional()
            .describe("Provider-specific feature values, for example { fast_mode: true } for Codex."),
    })
        .strict();
    const UpdateAgentSettingsInputSchema = z
        .object({
        modeId: z.string().optional().describe("Session mode ID."),
        model: z.string().nullable().optional().describe("Model ID. Pass null to clear."),
        thinkingOptionId: z
            .string()
            .nullable()
            .optional()
            .describe("Thinking option ID. Pass null to clear."),
        features: z
            .record(z.unknown())
            .optional()
            .describe("Provider-specific feature values, for example { fast_mode: true } for Codex."),
    })
        .strict();
    const InspectProviderSettingsInputSchema = z
        .object({
        modeId: z.string().optional().describe("Draft session mode ID."),
        model: z.string().optional().describe("Draft model ID."),
        thinkingOptionId: z.string().optional().describe("Draft thinking option ID."),
        features: z.record(z.unknown()).optional().describe("Draft provider feature values."),
    })
        .strict();
    const agentToAgentInputSchema = {
        cwd: z
            .string()
            .optional()
            .describe("Optional working directory. Defaults to your current working directory."),
        title: z
            .string()
            .trim()
            .min(1, "Title is required")
            .max(60, "Title must be 60 characters or fewer")
            .describe("Short descriptive title (<= 60 chars) summarizing the agent's focus."),
        provider: ProviderModelInputSchema.describe("Required provider/model pair, for example codex/gpt-5.4."),
        labels: z.record(z.string(), z.string()).optional().describe("Labels to set on the agent"),
        settings: CreateAgentSettingsInputSchema.optional().describe("Initial runtime settings for the new agent."),
        initialPrompt: z
            .string()
            .trim()
            .min(1, "initialPrompt is required")
            .describe("Required first task to run immediately after creation."),
        detached: z
            .boolean()
            .optional()
            .default(false)
            .describe("If true, the created agent stands on its own: it does not appear in your subagent track and is not archived with you."),
        notifyOnFinish: z
            .boolean()
            .optional()
            .default(true)
            .describe("Get notified when the created agent finishes, errors, or needs permission. Set false only for truly fire-and-forget agents."),
    };
    const topLevelInputSchema = {
        cwd: z
            .string()
            .describe("Required working directory for the agent (absolute, relative, or ~)."),
        title: z
            .string()
            .trim()
            .min(1, "Title is required")
            .max(60, "Title must be 60 characters or fewer")
            .describe("Short descriptive title (<= 60 chars) summarizing the agent's focus."),
        provider: ProviderModelInputSchema.describe("Required provider/model pair, for example codex/gpt-5.4."),
        labels: z.record(z.string(), z.string()).optional().describe("Labels to set on the agent"),
        settings: CreateAgentSettingsInputSchema.optional().describe("Initial runtime settings for the new agent."),
        initialPrompt: z
            .string()
            .trim()
            .min(1, "initialPrompt is required")
            .describe("Required first task to run immediately after creation."),
        worktreeName: z
            .string()
            .optional()
            .describe("Optional git worktree branch name (lowercase alphanumerics + hyphen)."),
        baseBranch: z
            .string()
            .optional()
            .describe("Required when worktreeName is set: the base branch to diff/merge against."),
        refName: z.string().min(1).optional().describe("Optional source ref for worktree creation."),
        action: z
            .enum(["branch-off", "checkout"])
            .optional()
            .describe("Optional worktree creation action."),
        githubPrNumber: z
            .number()
            .int()
            .positive()
            .optional()
            .describe("Optional GitHub pull request number to checkout."),
        background: z
            .boolean()
            .optional()
            .default(false)
            .describe("Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."),
        notifyOnFinish: z
            .boolean()
            .optional()
            .default(false)
            .describe("Agent-scoped only: get notified when the created agent finishes, errors, or needs permission."),
    };
    const createAgentInputSchema = callerAgentId ? agentToAgentInputSchema : topLevelInputSchema;
    const agentToAgentCreateAgentArgsSchema = z.object(agentToAgentInputSchema).strict();
    const topLevelCreateAgentArgsSchema = z.object(topLevelInputSchema).strict();
    const inspectProviderInputSchema = {
        provider: ProviderOrProviderModelInputSchema.describe("Provider ID, optionally with a model ID (for example codex or codex/gpt-5.4)."),
        cwd: z
            .string()
            .optional()
            .describe("Working directory used to resolve provider feature availability."),
        settings: InspectProviderSettingsInputSchema.optional().describe("Draft provider settings used to compute available features."),
    };
    if (options.voiceOnly || options.enableVoiceTools || callerContext?.enableVoiceTools) {
        registerTool("speak", {
            title: "Speak",
            description: "Speak text to the user via daemon-managed voice output. Blocks until playback completes.",
            inputSchema: {
                text: z
                    .string()
                    .trim()
                    .min(1, "text is required")
                    .max(4000, "text must be 4000 characters or fewer"),
            },
            outputSchema: {
                ok: z.boolean(),
            },
        }, async (args, context) => {
            if (!callerAgentId) {
                throw new Error("speak is only available to agent-scoped MCP sessions");
            }
            const handler = resolveSpeakHandler?.(callerAgentId) ?? null;
            if (!handler) {
                throw new Error(`No speak handler registered for your session '${callerAgentId}'`);
            }
            await handler({
                text: args.text,
                callerAgentId,
                signal: context?.signal,
            });
            return {
                content: [],
                structuredContent: ensureValidJson({ ok: true }),
            };
        });
    }
    if (options.voiceOnly) {
        return server;
    }
    registerTool("create_agent", {
        title: "Create agent",
        description: "Create an agent tied to a working directory. Requires provider/model, for example codex/gpt-5.4. Do not guess; call list_providers and list_models first if uncertain. Optionally run an initial prompt immediately or create a git worktree for the agent.",
        inputSchema: createAgentInputSchema,
        outputSchema: {
            agentId: z.string(),
            type: AgentProviderEnum,
            status: AgentStatusEnum,
            cwd: z.string(),
            currentModeId: z.string().nullable(),
            availableModes: z.array(ProviderModeSchema),
            lastMessage: z.string().nullable().optional(),
            permission: AgentPermissionRequestPayloadSchema.nullable().optional(),
            guidance: z.string().optional(),
        },
    }, async (args) => {
        const resolvedArgs = resolveCreateAgentToolArgs(args);
        const { parsedArgs, worktree } = resolvedArgs;
        let requestedBackground;
        let notifyOnFinish;
        let detached;
        if (resolvedArgs.kind === "agent-scoped") {
            requestedBackground = true;
            notifyOnFinish = resolvedArgs.parsedArgs.notifyOnFinish;
            detached = resolvedArgs.parsedArgs.detached;
        }
        else {
            requestedBackground = resolvedArgs.parsedArgs.background;
            notifyOnFinish = resolvedArgs.parsedArgs.notifyOnFinish ?? false;
            detached = false;
        }
        const { snapshot, background: createdInBackground, initialPromptStarted, } = await createAgentCommand({
            agentManager,
            agentStorage,
            logger: childLogger,
            paseoHome: options.paseoHome,
            worktreesRoot: options.worktreesRoot,
            workspaceGitService: options.workspaceGitService,
            terminalManager,
            providerSnapshotManager,
            createPaseoWorktree: options.createPaseoWorktree,
        }, {
            kind: "mcp",
            provider: parsedArgs.provider,
            title: parsedArgs.title,
            initialPrompt: parsedArgs.initialPrompt,
            cwd: parsedArgs.cwd,
            thinking: parsedArgs.settings?.thinkingOptionId,
            features: parsedArgs.settings?.features,
            labels: parsedArgs.labels,
            mode: parsedArgs.settings?.modeId,
            background: requestedBackground,
            notifyOnFinish,
            detached,
            callerAgentId,
            callerContext,
            worktree,
        });
        try {
            if (!createdInBackground && initialPromptStarted) {
                const result = await waitForAgentWithTimeout(agentManager, snapshot.id, {
                    waitForActive: true,
                });
                const liveSnapshot = agentManager.getAgent(snapshot.id) ?? snapshot;
                const responseData = {
                    agentId: snapshot.id,
                    type: snapshot.provider,
                    status: result.status,
                    cwd: liveSnapshot.cwd,
                    currentModeId: liveSnapshot.currentModeId,
                    availableModes: liveSnapshot.availableModes,
                    lastMessage: result.lastMessage,
                    permission: sanitizePermissionRequest(result.permission),
                };
                const validJson = ensureValidJson(responseData);
                const response = {
                    content: [],
                    structuredContent: validJson,
                };
                return response;
            }
        }
        catch (error) {
            childLogger.error({ err: error, agentId: snapshot.id }, "Failed to run initial prompt");
            throw error;
        }
        // Return immediately for async creation.
        const currentSnapshot = agentManager.getAgent(snapshot.id) ?? snapshot;
        const guidance = callerAgentId && notifyOnFinish && initialPromptStarted
            ? "You will get notified when the created agent finishes, errors, or needs permission. Do not call wait_for_agent or poll for status; continue with other work until the notification arrives."
            : undefined;
        const response = {
            content: [],
            structuredContent: ensureValidJson({
                agentId: currentSnapshot.id,
                type: snapshot.provider,
                status: currentSnapshot.lifecycle,
                cwd: currentSnapshot.cwd,
                currentModeId: currentSnapshot.currentModeId,
                availableModes: currentSnapshot.availableModes,
                lastMessage: null,
                permission: null,
                ...(guidance ? { guidance } : {}),
            }),
        };
        return response;
    });
    function resolveCreateAgentToolArgs(args) {
        if (callerAgentId) {
            return {
                kind: "agent-scoped",
                parsedArgs: agentToAgentCreateAgentArgsSchema.parse(args),
                worktree: undefined,
            };
        }
        const parsedArgs = topLevelCreateAgentArgsSchema.parse(args);
        return {
            kind: "top-level",
            parsedArgs,
            worktree: resolveTopLevelCreateAgentWorktree(parsedArgs),
        };
    }
    function resolveTopLevelCreateAgentWorktree(args) {
        return {
            worktreeName: args.worktreeName,
            baseBranch: args.baseBranch,
            refName: args.refName,
            action: args.action,
            githubPrNumber: args.githubPrNumber,
        };
    }
    registerTool("wait_for_agent", {
        title: "Wait for agent",
        description: "Block until the agent requests permission or the current run completes. Returns the pending permission (if any) and recent activity summary.",
        inputSchema: {
            agentId: z.string().describe("Agent identifier returned by the create_agent tool"),
        },
        outputSchema: {
            agentId: z.string(),
            status: AgentStatusEnum,
            permission: AgentPermissionRequestPayloadSchema.nullable(),
            lastMessage: z.string().nullable(),
        },
    }, async ({ agentId }, { signal }) => {
        const abortController = new AbortController();
        const cleanupFns = [];
        const cleanup = () => {
            while (cleanupFns.length) {
                const fn = cleanupFns.pop();
                try {
                    fn?.();
                }
                catch {
                    // ignore cleanup errors
                }
            }
        };
        const forwardExternalAbort = () => {
            if (!abortController.signal.aborted) {
                const reason = signal?.reason ?? new Error("wait_for_agent aborted");
                abortController.abort(reason);
            }
        };
        if (signal) {
            if (signal.aborted) {
                forwardExternalAbort();
            }
            else {
                signal.addEventListener("abort", forwardExternalAbort, { once: true });
                cleanupFns.push(() => signal.removeEventListener("abort", forwardExternalAbort));
            }
        }
        const unregister = waitTracker.register(agentId, (reason) => {
            if (!abortController.signal.aborted) {
                abortController.abort(new Error(reason ?? "wait_for_agent cancelled"));
            }
        });
        cleanupFns.push(unregister);
        try {
            const result = await waitForAgentWithTimeout(agentManager, agentId, {
                signal: abortController.signal,
            });
            const validJson = ensureValidJson({
                agentId,
                status: result.status,
                permission: sanitizePermissionRequest(result.permission),
                lastMessage: result.lastMessage,
            });
            const response = {
                content: [],
                structuredContent: validJson,
            };
            return response;
        }
        finally {
            cleanup();
        }
    });
    registerTool("send_agent_prompt", {
        title: "Send agent prompt",
        description: "Send a task to a running agent. Returns immediately after the agent begins processing.",
        inputSchema: {
            agentId: z.string(),
            prompt: z.string(),
            sessionMode: z
                .string()
                .optional()
                .describe("Optional mode to set before running the prompt."),
            background: z
                .boolean()
                .optional()
                .default(false)
                .describe("Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."),
            notifyOnFinish: z
                .boolean()
                .optional()
                .default(false)
                .describe("Agent-scoped only: get notified when this run finishes, errors, or needs permission."),
        },
        outputSchema: {
            success: z.boolean(),
            status: AgentStatusEnum,
            lastMessage: z.string().nullable().optional(),
            permission: AgentPermissionRequestPayloadSchema.nullable().optional(),
        },
    }, async ({ agentId, prompt, sessionMode, background = false, notifyOnFinish = false }) => {
        if (agentManager.hasInFlightRun(agentId)) {
            waitTracker.cancel(agentId, "Agent run interrupted by new prompt");
        }
        await sendPromptToAgent({
            agentManager,
            agentStorage,
            agentId,
            prompt,
            sessionMode,
            logger: childLogger,
        });
        if (notifyOnFinish && callerAgentId) {
            setupFinishNotification({
                agentManager,
                agentStorage,
                childAgentId: agentId,
                callerAgentId,
                logger: childLogger,
            });
        }
        // If not running in background, wait for completion
        if (!background) {
            const result = await waitForAgentWithTimeout(agentManager, agentId, {
                waitForActive: true,
            });
            const responseData = {
                success: true,
                status: result.status,
                lastMessage: result.lastMessage,
                permission: sanitizePermissionRequest(result.permission),
            };
            const validJson = ensureValidJson(responseData);
            const response = {
                content: [],
                structuredContent: validJson,
            };
            return response;
        }
        // Return immediately if background=true
        // Re-fetch snapshot since the state may have changed
        const currentSnapshot = agentManager.getAgent(agentId);
        const responseData = {
            success: true,
            status: currentSnapshot?.lifecycle ?? "idle",
            lastMessage: null,
            permission: null,
        };
        const validJson = ensureValidJson(responseData);
        const response = {
            content: [],
            structuredContent: validJson,
        };
        return response;
    });
    registerTool("get_agent_status", {
        title: "Get agent status",
        description: "Return the latest snapshot for an agent, including lifecycle state, capabilities, and pending permissions.",
        inputSchema: {
            agentId: z.string(),
        },
        outputSchema: {
            status: AgentStatusEnum,
            snapshot: AgentSnapshotPayloadSchema,
        },
    }, async ({ agentId }) => {
        const snapshot = agentManager.getAgent(agentId);
        if (snapshot) {
            const structuredSnapshot = await serializeSnapshotWithMetadata(agentStorage, snapshot, childLogger);
            return {
                content: [],
                structuredContent: ensureValidJson({
                    status: snapshot.lifecycle,
                    snapshot: structuredSnapshot,
                }),
            };
        }
        const record = await agentStorage.get(agentId);
        if (!record || record.internal) {
            throw new Error(`Agent ${agentId} not found`);
        }
        const structuredSnapshot = buildStoredAgentPayload(record, providerSnapshotManager.listRegisteredProviderIds());
        return {
            content: [],
            structuredContent: ensureValidJson({
                status: structuredSnapshot.status,
                snapshot: structuredSnapshot,
            }),
        };
    });
    registerTool("list_agents", {
        title: "List agents",
        description: "List recent agents as compact metadata.",
        inputSchema: {
            includeArchived: z.boolean().optional().default(false),
            cwd: z.string().optional(),
            sinceHours: z
                .number()
                .int()
                .positive()
                .max(24 * 30)
                .optional()
                .default(48),
            statuses: z.array(AgentStatusEnum).optional(),
            limit: z.number().int().positive().max(200).optional().default(50),
        },
        outputSchema: {
            agents: z.array(AgentListItemPayloadSchema),
        },
    }, async ({ includeArchived = false, cwd, sinceHours = 48, statuses, limit = 50 }) => {
        const callerCwd = callerAgentId ? resolveCallerAgent()?.cwd : undefined;
        const requestedCwd = cwd?.trim() ? expandUserPath(cwd) : callerCwd;
        const statusFilter = statuses && statuses.length > 0 ? new Set(statuses) : null;
        const sinceMs = Date.now() - sinceHours * 60 * 60 * 1000;
        const liveSnapshots = agentManager.listAgents();
        const liveAgents = await Promise.all(liveSnapshots.map((snapshot) => serializeSnapshotWithMetadata(agentStorage, snapshot, childLogger)));
        const liveIds = new Set(liveSnapshots.map((snapshot) => snapshot.id));
        const storedRecords = await agentStorage.list();
        const registeredProviderIds = providerSnapshotManager.listRegisteredProviderIds();
        const storedAgents = storedRecords
            .filter((record) => !record.internal && !liveIds.has(record.id))
            .filter((record) => includeArchived || !record.archivedAt)
            .filter((record) => includeArchived || isStoredAgentProviderAvailable(record, registeredProviderIds))
            .map((record) => buildStoredAgentPayload(record, registeredProviderIds));
        const agents = [...liveAgents, ...storedAgents]
            .map(toAgentListItemPayload)
            .filter((agent) => !requestedCwd || isSameOrDescendantPath(requestedCwd, agent.cwd))
            .filter((agent) => !statusFilter || statusFilter.has(agent.status))
            .filter((agent) => !agent.archivedAt || resolveAgentListActivityTime(agent) >= sinceMs)
            .sort(compareAgentListItems)
            .slice(0, limit);
        return {
            content: [],
            structuredContent: ensureValidJson({ agents }),
        };
    });
    registerTool("cancel_agent", {
        title: "Cancel agent run",
        description: "Abort the agent's current run but keep the agent alive for future tasks.",
        inputSchema: {
            agentId: z.string(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ agentId }) => {
        const { cancelled } = await cancelAgentRunCommand({ agentManager, logger: childLogger }, agentId);
        if (cancelled) {
            waitTracker.cancel(agentId, "Agent run cancelled");
        }
        return {
            content: [],
            structuredContent: ensureValidJson({ success: cancelled }),
        };
    });
    registerTool("archive_agent", {
        title: "Archive agent",
        description: "Archive an agent (soft-delete). The agent is interrupted if running and removed from the active list.",
        inputSchema: {
            agentId: z.string(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ agentId }) => {
        await archiveAgentCommand({
            agentManager,
            agentStorage,
            logger: childLogger,
        }, agentId);
        waitTracker.cancel(agentId, "Agent archived");
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("kill_agent", {
        title: "Kill agent",
        description: "Terminate an agent session permanently.",
        inputSchema: {
            agentId: z.string(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ agentId }) => {
        await closeAgentCommand({ agentManager }, agentId);
        waitTracker.cancel(agentId, "Agent terminated");
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("update_agent", {
        title: "Update agent",
        description: "Update an agent name, labels, and/or runtime settings.",
        inputSchema: {
            agentId: z.string(),
            name: z.string().optional(),
            labels: z.record(z.string(), z.string()).optional().describe("Labels to set on the agent"),
            settings: UpdateAgentSettingsInputSchema.optional().describe("Runtime settings to apply to the agent."),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ agentId, name, labels, settings }) => {
        if (settings?.modeId !== undefined) {
            await agentManager.setAgentMode(agentId, settings.modeId);
        }
        if (settings?.model !== undefined) {
            await agentManager.setAgentModel(agentId, settings.model);
        }
        if (settings?.thinkingOptionId !== undefined) {
            await agentManager.setAgentThinkingOption(agentId, settings.thinkingOptionId);
        }
        if (settings?.features) {
            for (const [featureId, value] of Object.entries(settings.features)) {
                await agentManager.setAgentFeature(agentId, featureId, value);
            }
        }
        await updateAgentCommand({ agentManager }, { agentId, name, labels });
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("list_terminals", {
        title: "List terminals",
        description: "List terminals for a working directory or across all working directories.",
        inputSchema: {
            cwd: z
                .string()
                .optional()
                .describe("Optional working directory. Defaults to your current working directory."),
            all: z.boolean().optional().describe("List terminals across all working directories."),
        },
        outputSchema: {
            terminals: z.array(TerminalSummarySchema),
        },
    }, async ({ cwd, all }) => {
        if (!terminalManager) {
            throw new Error("Terminal manager is not configured");
        }
        const terminals = all
            ? (await Promise.all(terminalManager.listDirectories().map(async (directory) => (await terminalManager.getTerminals(directory)).map((terminal) => ({
                id: terminal.id,
                name: terminal.name,
                cwd: terminal.cwd,
            }))))).flat()
            : (await terminalManager.getTerminals(resolveScopedCwd(cwd, { required: true }))).map((terminal) => ({
                id: terminal.id,
                name: terminal.name,
                cwd: terminal.cwd,
            }));
        return {
            content: [],
            structuredContent: ensureValidJson({ terminals }),
        };
    });
    registerTool("create_terminal", {
        title: "Create terminal",
        description: "Create a terminal session for a working directory.",
        inputSchema: {
            cwd: z
                .string()
                .optional()
                .describe("Optional working directory. Defaults to your current working directory."),
            name: z.string().optional().describe("Optional terminal name."),
        },
        outputSchema: TerminalSummarySchema.shape,
    }, async ({ cwd, name }) => {
        if (!terminalManager) {
            throw new Error("Terminal manager is not configured");
        }
        const terminal = await terminalManager.createTerminal({
            cwd: resolveScopedCwd(cwd, { required: true }),
            ...(name?.trim() ? { name: name.trim() } : {}),
        });
        return {
            content: [],
            structuredContent: ensureValidJson({
                id: terminal.id,
                name: terminal.name,
                cwd: terminal.cwd,
            }),
        };
    });
    registerTool("kill_terminal", {
        title: "Kill terminal",
        description: "Kill an existing terminal session.",
        inputSchema: {
            terminalId: z.string(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ terminalId }) => {
        if (!terminalManager) {
            throw new Error("Terminal manager is not configured");
        }
        const terminal = terminalManager.getTerminal(terminalId);
        if (!terminal) {
            throw new Error(`Terminal ${terminalId} not found`);
        }
        terminal.kill();
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("capture_terminal", {
        title: "Capture terminal",
        description: "Capture plain-text terminal output lines from a terminal session.",
        inputSchema: {
            terminalId: z.string(),
            start: z.number().optional(),
            end: z.number().optional(),
            scrollback: z.boolean().optional(),
            stripAnsi: z.boolean().optional().default(true),
        },
        outputSchema: {
            terminalId: z.string(),
            lines: z.array(z.string()),
            totalLines: z.number().int().nonnegative(),
        },
    }, async ({ terminalId, start, end, scrollback, stripAnsi = true }) => {
        if (!terminalManager) {
            throw new Error("Terminal manager is not configured");
        }
        if (!terminalManager.getTerminal(terminalId)) {
            throw new Error(`Terminal ${terminalId} not found`);
        }
        const capture = await terminalManager.captureTerminal(terminalId, {
            start: scrollback ? 0 : start,
            end,
            stripAnsi,
        });
        return {
            content: [],
            structuredContent: ensureValidJson({
                terminalId,
                lines: capture.lines,
                totalLines: capture.totalLines,
            }),
        };
    });
    registerTool("send_terminal_keys", {
        title: "Send terminal keys",
        description: "Send literal text or special key tokens to a terminal session.",
        inputSchema: {
            terminalId: z.string(),
            keys: z.string(),
            literal: z.boolean().optional(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ terminalId, keys, literal = false }) => {
        if (!terminalManager) {
            throw new Error("Terminal manager is not configured");
        }
        const terminal = terminalManager.getTerminal(terminalId);
        if (!terminal) {
            throw new Error(`Terminal ${terminalId} not found`);
        }
        terminal.send({
            type: "input",
            data: resolveTerminalKeyToken(keys, literal),
        });
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("create_schedule", {
        title: "Create schedule",
        description: "Create a recurring schedule that starts a new agent on a cron cadence.",
        inputSchema: {
            prompt: z.string().trim().min(1, "prompt is required"),
            cron: z.string().trim().min(1, "cron is required"),
            timezone: z
                .string()
                .trim()
                .min(1)
                .optional()
                .describe("IANA time zone for the cron cadence. For example: America/New_York."),
            name: z.string().optional(),
            provider: AgentProviderEnum.describe("Provider, or provider/model (for example: codex or codex/gpt-5.4)."),
            cwd: z.string().optional(),
            maxRuns: z.number().int().positive().optional(),
            expiresIn: z.string().optional(),
        },
        outputSchema: ScheduleSummarySchema.shape,
    }, async ({ prompt, cron, timezone, name, provider, cwd, maxRuns, expiresIn }) => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        const expiresAt = buildScheduleExpiry(expiresIn);
        const schedule = await scheduleService.create({
            prompt: prompt.trim(),
            cadence: buildCronScheduleCadence({
                cron,
                ...(timezone !== undefined ? { timezone } : {}),
            }),
            target: resolveNewAgentScheduleTarget({ provider, cwd }),
            ...(name?.trim() ? { name: name.trim() } : {}),
            ...(maxRuns === undefined ? {} : { maxRuns }),
            ...(expiresAt === undefined ? {} : { expiresAt }),
        });
        return {
            content: [],
            structuredContent: ensureValidJson(toScheduleSummary(schedule)),
        };
    });
    registerTool("create_heartbeat", {
        title: "Create heartbeat",
        description: "Create a recurring heartbeat that sends you a prompt on a cron cadence.",
        inputSchema: {
            prompt: z.string().trim().min(1, "prompt is required"),
            cron: z.string().trim().min(1, "cron is required"),
            timezone: z
                .string()
                .trim()
                .min(1)
                .optional()
                .describe("IANA time zone for the cron cadence. For example: America/New_York."),
            name: z.string().optional(),
            maxRuns: z.number().int().positive().optional(),
            expiresIn: z.string().optional(),
        },
        outputSchema: ScheduleSummarySchema.shape,
    }, async ({ prompt, cron, timezone, name, maxRuns, expiresIn }) => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        if (!callerAgentId) {
            throw new Error("create_heartbeat requires an agent-scoped session");
        }
        resolveCallerAgent();
        const expiresAt = buildScheduleExpiry(expiresIn);
        const schedule = await scheduleService.create({
            prompt: prompt.trim(),
            cadence: buildCronScheduleCadence({
                cron,
                ...(timezone !== undefined ? { timezone } : {}),
            }),
            target: { type: "agent", agentId: callerAgentId },
            ...(name?.trim() ? { name: name.trim() } : {}),
            ...(maxRuns === undefined ? {} : { maxRuns }),
            ...(expiresAt === undefined ? {} : { expiresAt }),
        });
        return {
            content: [],
            structuredContent: ensureValidJson(toScheduleSummary(schedule)),
        };
    });
    registerTool("list_schedules", {
        title: "List schedules",
        description: "List all schedules managed by the daemon.",
        inputSchema: {},
        outputSchema: {
            schedules: z.array(ScheduleSummarySchema),
        },
    }, async () => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        const schedules = (await scheduleService.list()).map((schedule) => toScheduleSummary(schedule));
        return {
            content: [],
            structuredContent: ensureValidJson({ schedules }),
        };
    });
    registerTool("inspect_schedule", {
        title: "Inspect schedule",
        description: "Inspect a schedule and its run history.",
        inputSchema: {
            id: z.string(),
        },
        outputSchema: StoredScheduleSchema.shape,
    }, async ({ id }) => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        const schedule = await scheduleService.inspect(id);
        return {
            content: [],
            structuredContent: ensureValidJson(schedule),
        };
    });
    registerTool("pause_schedule", {
        title: "Pause schedule",
        description: "Pause an active schedule.",
        inputSchema: {
            id: z.string(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ id }) => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        await scheduleService.pause(id);
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("resume_schedule", {
        title: "Resume schedule",
        description: "Resume a paused schedule.",
        inputSchema: {
            id: z.string(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ id }) => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        await scheduleService.resume(id);
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("delete_schedule", {
        title: "Delete schedule",
        description: "Delete a schedule permanently.",
        inputSchema: {
            id: z.string(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ id }) => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        await scheduleService.delete(id);
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("update_schedule", {
        title: "Update schedule",
        description: "Update an existing schedule. Only provided fields are changed; omitted fields remain unchanged.",
        inputSchema: {
            id: z.string(),
            every: z.string().optional().describe("New interval duration string (e.g. 5m, 1h)."),
            cron: z.string().optional().describe("New cron expression."),
            timezone: z
                .string()
                .trim()
                .min(1)
                .optional()
                .describe("IANA time zone for cron cadence; requires cron. For example: America/New_York."),
            name: z.string().nullable().optional().describe("New name (null to clear)."),
            prompt: z.string().trim().min(1).optional().describe("New prompt text."),
            maxRuns: z
                .number()
                .int()
                .positive()
                .nullable()
                .optional()
                .describe("New max runs limit (null to clear)."),
            provider: z
                .string()
                .trim()
                .min(1)
                .optional()
                .describe("New provider for new-agent target."),
            model: z
                .string()
                .trim()
                .min(1)
                .nullable()
                .optional()
                .describe("New model for new-agent target (null to clear)."),
            mode: z
                .string()
                .trim()
                .min(1)
                .nullable()
                .optional()
                .describe("New mode for new-agent target (null to clear)."),
            cwd: z.string().trim().min(1).optional().describe("New cwd for new-agent target."),
            expiresIn: z
                .string()
                .optional()
                .describe("New relative expiry duration (for example: 1h, 2d)."),
            clearExpires: z.boolean().optional().describe("Clear any schedule expiry."),
        },
        outputSchema: StoredScheduleSchema.shape,
    }, async (input) => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        const schedule = await scheduleService.update(buildScheduleUpdateInput(input));
        return {
            content: [],
            structuredContent: ensureValidJson(schedule),
        };
    });
    registerTool("schedule_logs", {
        title: "Schedule logs",
        description: "Get the run history (logs) for a schedule.",
        inputSchema: {
            id: z.string(),
        },
        outputSchema: {
            runs: z.array(ScheduleRunSchema),
        },
    }, async ({ id }) => {
        if (!scheduleService) {
            throw new Error("Schedule service is not configured");
        }
        const runs = await scheduleService.logs(id);
        return {
            content: [],
            structuredContent: ensureValidJson({ runs }),
        };
    });
    registerTool("list_providers", {
        title: "List providers",
        description: "List configured agent providers, availability, and their modes.",
        inputSchema: {},
        outputSchema: {
            providers: z.array(ProviderSummarySchema),
        },
    }, async () => {
        const providers = (await providerSnapshotManager.listProviders({ wait: true })).map(toProviderSummary);
        return {
            content: [],
            structuredContent: ensureValidJson({ providers }),
        };
    });
    registerTool("list_models", {
        title: "List models",
        description: "List models for an agent provider.",
        inputSchema: {
            provider: AgentProviderEnum,
        },
        outputSchema: {
            provider: z.string(),
            models: z.array(AgentModelSchema),
        },
    }, async ({ provider }) => {
        const models = await providerSnapshotManager.listModels({
            cwd: resolveSnapshotCwd(),
            provider,
            wait: true,
        });
        return {
            content: [],
            structuredContent: ensureValidJson({
                provider,
                models,
            }),
        };
    });
    registerTool("inspect_provider", {
        title: "Inspect provider",
        description: "Inspect compact provider capabilities for orchestration, including modes and draft feature settings. Use list_models for the full model list.",
        inputSchema: inspectProviderInputSchema,
        outputSchema: {
            provider: AgentProviderEnum,
            label: z.string().nullable().optional(),
            description: z.string().nullable().optional(),
            enabled: z.boolean(),
            status: z.string(),
            modes: z.array(ProviderModeSchema).nullish(),
            selectedModel: z.string().nullable(),
            features: z.array(AgentFeatureSchema),
        },
    }, async ({ provider, cwd, settings }) => {
        const resolvedProviderModel = resolveScheduleProviderAndModel({
            provider,
            defaultProvider: provider,
        });
        const providerId = resolvedProviderModel.provider;
        const resolvedCwd = resolveScopedCwd(cwd, { required: true });
        const entry = await providerSnapshotManager.getProvider({
            cwd: resolvedCwd,
            provider: providerId,
            wait: true,
        });
        const summary = toProviderSummary(entry);
        if (!entry.enabled) {
            throw new Error(`Provider '${providerId}' is disabled`);
        }
        if (entry.status !== "ready") {
            throw new Error(entry.error ?? `Provider '${providerId}' is unavailable`);
        }
        const selectedModel = settings?.model ?? resolvedProviderModel.model;
        const features = await agentManager.listDraftFeatures({
            provider: providerId,
            cwd: resolvedCwd,
            ...(settings?.modeId ? { modeId: settings.modeId } : {}),
            ...(selectedModel ? { model: selectedModel } : {}),
            ...(settings?.thinkingOptionId ? { thinkingOptionId: settings.thinkingOptionId } : {}),
            ...(settings?.features ? { featureValues: settings.features } : {}),
        });
        return {
            content: [],
            structuredContent: ensureValidJson({
                provider: providerId,
                label: summary.label,
                description: summary.description,
                enabled: summary.enabled,
                status: summary.status,
                modes: summary.modes,
                selectedModel: selectedModel ?? null,
                features,
            }),
        };
    });
    registerTool("list_worktrees", {
        title: "List worktrees",
        description: "List Paseo-managed git worktrees for a repository.",
        inputSchema: {
            cwd: z
                .string()
                .optional()
                .describe("Optional repository cwd. Defaults to your current working directory."),
        },
        outputSchema: {
            worktrees: z.array(WorktreeSummarySchema),
        },
    }, async ({ cwd }) => {
        const resolvedCwd = resolveScopedCwd(cwd, { required: true });
        if (!options.workspaceGitService) {
            throw new Error("WorkspaceGitService is required to list worktrees");
        }
        const worktrees = await listPaseoWorktreesCommand({ workspaceGitService: options.workspaceGitService }, {
            cwd: resolvedCwd,
            reason: "mcp:list-worktrees",
        });
        return {
            content: [],
            structuredContent: ensureValidJson({ worktrees }),
        };
    });
    registerTool("create_worktree", {
        title: "Create worktree",
        description: "Create a Paseo-managed git worktree. Branch off a new branch, check out an existing branch, or check out a GitHub PR.",
        inputSchema: {
            cwd: z.string().optional().describe("Repository directory. Defaults to the agent's cwd."),
            target: z
                .discriminatedUnion("mode", [
                z
                    .object({
                    mode: z.literal("branch-off"),
                    newBranch: z.string().min(1).describe("Name for the new branch."),
                    base: z
                        .string()
                        .min(1)
                        .optional()
                        .describe("Base ref. Defaults to the repo's default branch."),
                })
                    .describe("Create a new branch off a base."),
                z
                    .object({
                    mode: z.literal("checkout-branch"),
                    branch: z.string().min(1).describe("Existing branch to check out."),
                })
                    .describe("Check out an existing branch."),
                z
                    .object({
                    mode: z.literal("checkout-pr"),
                    prNumber: z.number().int().positive().describe("Pull request number."),
                })
                    .describe("Check out a GitHub pull request."),
            ])
                .describe("What the worktree should contain."),
        },
        outputSchema: {
            branchName: z.string(),
            worktreePath: z.string(),
        },
    }, async ({ cwd, target }) => {
        const repoRoot = resolveScopedCwd(cwd, { required: true });
        const commandResult = await createPaseoWorktreeCommand({
            paseoHome: options.paseoHome,
            worktreesRoot: options.worktreesRoot,
            createPaseoWorktreeWorkflow: options.createPaseoWorktree,
        }, createMcpWorktreeCommandInput(repoRoot, target));
        if (!commandResult.ok) {
            throw new WorktreeRequestError(commandResult.error);
        }
        const { worktree } = commandResult.createdWorktree;
        await options.workspaceGitService?.listWorktrees?.(repoRoot, {
            force: true,
            reason: "mcp:create-worktree",
        });
        return {
            content: [],
            structuredContent: ensureValidJson({
                branchName: worktree.branchName,
                worktreePath: worktree.worktreePath,
            }),
        };
    });
    registerTool("archive_worktree", {
        title: "Archive worktree",
        description: "Delete a Paseo-managed git worktree.",
        inputSchema: {
            cwd: z
                .string()
                .optional()
                .describe("Optional repository cwd. Defaults to your current working directory."),
            worktreePath: z.string().optional(),
            worktreeSlug: z.string().optional(),
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ cwd, worktreePath, worktreeSlug }) => {
        const resolvedCwd = resolveScopedCwd(cwd, { required: true });
        if (!worktreePath && !worktreeSlug) {
            throw new Error("worktreePath or worktreeSlug is required");
        }
        if (!options.workspaceGitService) {
            throw new Error("WorkspaceGitService is required to archive worktrees");
        }
        const repoRoot = await options.workspaceGitService.resolveRepoRoot(resolvedCwd);
        const result = await archivePaseoWorktreeCommand(archiveWorktreeDependencies(options, {
            agentManager,
            agentStorage,
            terminalManager: terminalManager ?? null,
            logger: childLogger,
        }), {
            requestId: "mcp:archive_worktree",
            repoRoot,
            worktreePath,
            worktreeSlug,
        });
        if (!result.ok) {
            throw new Error(result.message);
        }
        await options.workspaceGitService.listWorktrees(repoRoot, {
            force: true,
            reason: "mcp:archive-worktree",
        });
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    registerTool("get_agent_activity", {
        title: "Get agent activity",
        description: "Return recent agent timeline entries as a curated summary.",
        inputSchema: {
            agentId: z.string(),
            limit: z
                .number()
                .optional()
                .describe("Optional limit for number of activities to include (most recent first)."),
        },
        outputSchema: {
            agentId: z.string(),
            updateCount: z.number(),
            currentModeId: z.string().nullable(),
            content: z.string(),
        },
    }, async ({ agentId, limit }) => {
        await ensureAgentLoaded(agentId, {
            agentManager,
            agentStorage,
            logger: childLogger,
        });
        const timeline = agentManager.getTimeline(agentId);
        const snapshot = agentManager.getAgent(agentId);
        const selection = selectItemsByProjectedLimit({
            items: timeline,
            direction: "tail",
            limit: limit ?? 0,
        });
        const curatedContent = curateAgentActivity(selection.items);
        const { totalProjected, shownProjected } = selection;
        const noun = totalProjected === 1 ? "activity" : "activities";
        const countHeader = limit && shownProjected < totalProjected
            ? `Showing ${shownProjected} of ${totalProjected} ${noun} (limited to ${limit})`
            : `Showing all ${totalProjected} ${noun}`;
        const contentWithCount = `${countHeader}\n\n${curatedContent}`;
        return {
            content: [],
            structuredContent: ensureValidJson({
                agentId,
                updateCount: timeline.length,
                currentModeId: snapshot?.currentModeId ?? null,
                content: contentWithCount,
            }),
        };
    });
    registerTool("set_agent_mode", {
        title: "Set agent session mode",
        description: "Switch the agent's session mode (plan, bypassPermissions, read-only, auto, etc.).",
        inputSchema: {
            agentId: z.string(),
            modeId: z.string(),
        },
        outputSchema: {
            success: z.boolean(),
            newMode: z.string(),
        },
    }, async ({ agentId, modeId }) => {
        const result = await setAgentModeCommand({ agentManager }, { agentId, modeId });
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true, newMode: result.modeId }),
        };
    });
    registerTool("list_pending_permissions", {
        title: "List pending permissions",
        description: "Return all pending permission requests across all agents with the normalized payloads.",
        inputSchema: {},
        outputSchema: {
            permissions: z.array(z.object({
                agentId: z.string(),
                status: AgentStatusEnum,
                request: AgentPermissionRequestPayloadSchema,
            })),
        },
    }, async () => {
        const permissions = agentManager.listAgents().flatMap((agent) => {
            const payload = toAgentPayload(agent);
            return payload.pendingPermissions.map((request) => ({
                agentId: agent.id,
                status: payload.status,
                request,
            }));
        });
        return {
            content: [],
            structuredContent: ensureValidJson({ permissions }),
        };
    });
    registerTool("respond_to_permission", {
        title: "Respond to permission",
        description: "Approve or deny a pending permission request with an AgentManager-compatible response payload.",
        inputSchema: {
            agentId: z.string(),
            requestId: z.string(),
            response: AgentPermissionResponseSchema,
        },
        outputSchema: {
            success: z.boolean(),
        },
    }, async ({ agentId, requestId, response }) => {
        await respondToAgentPermission({
            agentManager,
            agentId,
            requestId,
            response,
            logger: childLogger,
        });
        return {
            content: [],
            structuredContent: ensureValidJson({ success: true }),
        };
    });
    return server;
}
function archiveWorktreeDependencies(options, context) {
    if (!options.github) {
        throw new Error("GitHub service is required to archive worktrees");
    }
    if (!options.workspaceGitService) {
        throw new Error("WorkspaceGitService is required to archive worktrees");
    }
    if (!options.archiveWorkspaceRecord) {
        throw new Error("Workspace registry archiver is required to archive worktrees");
    }
    if (!options.emitWorkspaceUpdatesForWorkspaceIds) {
        throw new Error("Workspace update emitter is required to archive worktrees");
    }
    if (!options.markWorkspaceArchiving) {
        throw new Error("Workspace archiving marker is required to archive worktrees");
    }
    if (!options.clearWorkspaceArchiving) {
        throw new Error("Workspace archiving clearer is required to archive worktrees");
    }
    return {
        paseoHome: options.paseoHome,
        worktreesRoot: options.worktreesRoot,
        github: options.github,
        workspaceGitService: options.workspaceGitService,
        agentManager: context.agentManager,
        agentStorage: context.agentStorage,
        archiveWorkspaceRecord: options.archiveWorkspaceRecord,
        emitWorkspaceUpdatesForWorkspaceIds: options.emitWorkspaceUpdatesForWorkspaceIds,
        markWorkspaceArchiving: options.markWorkspaceArchiving,
        clearWorkspaceArchiving: options.clearWorkspaceArchiving,
        isPathWithinRoot: isSameOrDescendantPath,
        killTerminalsUnderPath: (rootPath) => killTerminalsUnderPath({
            terminalManager: context.terminalManager,
            isPathWithinRoot: isSameOrDescendantPath,
            killTrackedTerminal: () => { },
            sessionLogger: context.logger,
        }, rootPath),
        sessionLogger: context.logger,
    };
}
function createMcpWorktreeCommandInput(repoRoot, target) {
    const base = { cwd: repoRoot };
    switch (target.mode) {
        case "branch-off":
            return {
                ...base,
                worktreeSlug: target.newBranch,
                action: "branch-off",
                ...(target.base ? { refName: target.base } : {}),
            };
        case "checkout-branch":
            return { ...base, action: "checkout", refName: target.branch };
        case "checkout-pr":
            return { ...base, action: "checkout", githubPrNumber: target.prNumber };
        default:
            throw new Error("unreachable");
    }
}
//# sourceMappingURL=mcp-server.js.map