import { z } from "zod";
import { curateAgentActivity } from "./activity-curator.js";
import { AGENT_PROVIDER_DEFINITIONS } from "./provider-registry.js";
import { serializeAgentSnapshot } from "../messages.js";
export const AgentProviderEnum = z.enum(AGENT_PROVIDER_DEFINITIONS.map((definition) => definition.id));
export const AgentStatusEnum = z.enum(["initializing", "idle", "running", "error", "closed"]);
export const ProviderModeSchema = z.object({
    id: z.string(),
    label: z.string(),
    description: z.string().optional(),
});
export const ProviderSummarySchema = z.object({
    id: z.string(),
    label: z.string(),
    modes: z.array(ProviderModeSchema),
});
export const AgentSelectOptionSchema = z.object({
    id: z.string(),
    label: z.string(),
    description: z.string().optional(),
    isDefault: z.boolean().optional(),
    metadata: z.record(z.unknown()).optional(),
});
export const AgentModelSchema = z.object({
    provider: z.string(),
    id: z.string(),
    label: z.string(),
    description: z.string().optional(),
    isDefault: z.boolean().optional(),
    metadata: z.record(z.unknown()).optional(),
    thinkingOptions: z.array(AgentSelectOptionSchema).optional(),
    defaultThinkingOptionId: z.string().optional(),
});
// 30 seconds - surface friendly message before SDK tool timeout (~60s)
export const AGENT_WAIT_TIMEOUT_MS = 30000;
/**
 * Wraps agentManager.waitForAgentEvent with a self-imposed timeout.
 * Returns a friendly message when timeout occurs, rather than letting
 * the SDK tool timeout trigger a generic "tool failed" error.
 */
export async function waitForAgentWithTimeout(agentManager, agentId, options) {
    const timeoutController = new AbortController();
    const combinedController = new AbortController();
    const timeoutId = setTimeout(() => {
        timeoutController.abort(new Error("wait timeout"));
    }, AGENT_WAIT_TIMEOUT_MS);
    const forwardAbort = (reason) => {
        if (!combinedController.signal.aborted) {
            combinedController.abort(reason);
        }
    };
    if (options?.signal) {
        if (options.signal.aborted) {
            forwardAbort(options.signal.reason);
        }
        else {
            options.signal.addEventListener("abort", () => forwardAbort(options.signal.reason), {
                once: true,
            });
        }
    }
    timeoutController.signal.addEventListener("abort", () => forwardAbort(timeoutController.signal.reason), { once: true });
    try {
        const result = await agentManager.waitForAgentEvent(agentId, {
            signal: combinedController.signal,
            waitForActive: options?.waitForActive,
        });
        return result;
    }
    catch (error) {
        if (error instanceof Error && error.message === "wait timeout") {
            const snapshot = agentManager.getAgent(agentId);
            const timeline = agentManager.getTimeline(agentId);
            const recentActivity = curateAgentActivity(timeline.slice(-5));
            const waitedSeconds = Math.round(AGENT_WAIT_TIMEOUT_MS / 1000);
            const message = `Awaiting the agent timed out after ${waitedSeconds}s. This does not mean the agent failed - call wait_for_agent again to continue waiting.\n\nRecent activity:\n${recentActivity}`;
            return {
                status: snapshot?.lifecycle ?? "idle",
                permission: null,
                lastMessage: message,
            };
        }
        throw error;
    }
    finally {
        clearTimeout(timeoutId);
    }
}
export function startAgentRun(agentManager, agentId, prompt, logger, options) {
    const shouldReplace = Boolean(options?.replaceRunning && agentManager.hasInFlightRun(agentId));
    const iterator = shouldReplace
        ? agentManager.replaceAgentRun(agentId, prompt)
        : agentManager.streamAgent(agentId, prompt);
    void (async () => {
        try {
            for await (const _ of iterator) {
                // Events are broadcast via AgentManager subscribers.
            }
        }
        catch (error) {
            logger.error({ err: error, agentId }, "Agent stream failed");
        }
    })();
}
export function setupFinishNotification(params) {
    const { agentManager, childAgentId, callerAgentId, logger } = params;
    let hasSeenRunning = false;
    let fired = false;
    let unsubscribe = null;
    function notify(reason) {
        if (fired) {
            return;
        }
        fired = true;
        unsubscribe?.();
        if (!agentManager.getAgent(callerAgentId)) {
            return;
        }
        const title = agentManager.getAgent(childAgentId)?.config?.title ?? childAgentId;
        const prompt = `<seawork-system>\nAgent ${childAgentId} (${title}) ${reason}.\n</seawork-system>`;
        startAgentRun(agentManager, callerAgentId, prompt, logger, {
            replaceRunning: true,
        });
    }
    unsubscribe = agentManager.subscribe((event) => {
        if (fired) {
            return;
        }
        if (event.type === "agent_state") {
            if (event.agent.lifecycle === "running") {
                hasSeenRunning = true;
                return;
            }
            if (event.agent.lifecycle === "error") {
                notify("errored");
                return;
            }
            if (event.agent.lifecycle === "idle" && hasSeenRunning) {
                notify("finished");
                return;
            }
            if (event.agent.lifecycle === "closed") {
                fired = true;
                unsubscribe?.();
                return;
            }
            return;
        }
        if (event.event.type === "permission_requested") {
            notify("needs permission");
        }
    }, { agentId: childAgentId, replayState: false });
    // Check if the child is already running (catches the case where
    // the lifecycle flipped before our subscribe call was processed).
    // Do NOT treat an immediate "idle" as "finished" — the agent may
    // not have started yet (streamAgent sets a pending run before
    // transitioning to "running").
    const childSnapshot = agentManager.getAgent(childAgentId);
    if (!childSnapshot || childSnapshot.lifecycle === "closed") {
        unsubscribe();
        return;
    }
    if (childSnapshot.lifecycle === "running") {
        hasSeenRunning = true;
    }
    else if (childSnapshot.lifecycle === "error") {
        notify("errored");
    }
}
export function sanitizePermissionRequest(permission) {
    if (!permission) {
        return null;
    }
    const sanitized = { ...permission };
    if (sanitized.title === undefined) {
        delete sanitized.title;
    }
    if (sanitized.description === undefined) {
        delete sanitized.description;
    }
    if (sanitized.input === undefined) {
        delete sanitized.input;
    }
    if (sanitized.suggestions === undefined) {
        delete sanitized.suggestions;
    }
    if (sanitized.actions === undefined) {
        delete sanitized.actions;
    }
    if (sanitized.metadata === undefined) {
        delete sanitized.metadata;
    }
    return sanitized;
}
export async function resolveAgentTitle(agentStorage, agentId, logger) {
    try {
        const record = await agentStorage.get(agentId);
        return record?.title ?? null;
    }
    catch (error) {
        logger.error({ err: error, agentId }, "Failed to load agent title");
        return null;
    }
}
export async function serializeSnapshotWithMetadata(agentStorage, snapshot, logger) {
    const title = await resolveAgentTitle(agentStorage, snapshot.id, logger);
    return serializeAgentSnapshot(snapshot, { title });
}
export function parseDurationString(input) {
    const trimmed = input.trim();
    if (/^\d+$/.test(trimmed)) {
        return Number.parseInt(trimmed, 10) * 1000;
    }
    let totalMs = 0;
    let hasMatch = false;
    const regex = /(\d+)([smh])/g;
    let match;
    while ((match = regex.exec(trimmed)) !== null) {
        hasMatch = true;
        const value = Number.parseInt(match[1], 10);
        switch (match[2]) {
            case "s":
                totalMs += value * 1000;
                break;
            case "m":
                totalMs += value * 60 * 1000;
                break;
            case "h":
                totalMs += value * 60 * 60 * 1000;
                break;
        }
    }
    if (!hasMatch) {
        throw new Error(`Invalid duration format: ${input}. Use formats like: 5m, 30s, 1h, 2h30m`);
    }
    return totalMs;
}
export function toScheduleSummary(schedule) {
    const { runs: _runs, ...summary } = schedule;
    return summary;
}
//# sourceMappingURL=mcp-shared.js.map