export function buildAgentTerminalRunDiagnosticsSnapshot(input) {
    const lastEvent = input.recentEvents[input.recentEvents.length - 1];
    const observedMaxSeq = input.currentRunReceivedSeqs[input.currentRunReceivedSeqs.length - 1] ?? 0;
    return {
        agentId: input.agentId,
        isSubmitting: input.isSubmitting,
        isConnecting: input.isConnecting,
        isWaitingForResponse: input.isWaitingForResponse,
        isAgentThinking: input.isAgentThinking,
        isExecuting: input.isExecuting,
        lastStatusReason: input.lastStatusReason,
        lastStatusMessage: input.lastStatusMessage,
        hasActiveStreaming: input.hasActiveStreaming,
        isSubscribed: input.isSubscribed,
        lastSeq: Math.max(input.lastSeq, observedMaxSeq),
        lastEventType: lastEvent?.type ?? null,
        lastEventAt: lastEvent?.timestamp ?? null,
        recentEvents: input.recentEvents,
        currentRunReceivedSeqs: input.currentRunReceivedSeqs,
        currentRunMissingSeqs: input.currentRunMissingSeqs,
        currentRunTotalEventCount: input.currentRunTotalEventCount,
        assistantMessageCount: input.assistantMessageCount,
        assistantGroupCount: input.assistantGroupCount,
        assistantGroupsWithRenderableToolCalls: input.assistantGroupsWithRenderableToolCalls,
        messagesWithToolCalls: input.messagesWithToolCalls,
        totalToolCallCount: input.totalToolCallCount,
        incompleteToolCallCount: input.incompleteToolCallCount,
        recentToolUiEvents: input.recentToolUiEvents,
    };
}
const ACTIVE_SERVER_STATUSES = new Set([
    "queued",
    "running",
    "waitingforapproval",
    "waitingforinput",
]);
function normalizeStatus(value) {
    return (value || "").replace(/\s+/g, "").toLowerCase();
}
function isWaitingForCapacity(localSnapshot, serverDiagnostics) {
    if (normalizeStatus(localSnapshot.lastStatusReason) === "machinecapacity") {
        return true;
    }
    if (normalizeStatus(serverDiagnostics?.transport?.lastStatusState) ===
        "waitingforcapacity") {
        return true;
    }
    return (normalizeStatus(serverDiagnostics?.execution?.status) === "running" &&
        (serverDiagnostics?.persistentLogs || []).some((log) => normalizeStatus(log.stage) === "waiting-for-capacity"));
}
function toTimestamp(value) {
    if (!value)
        return null;
    const parsed = Date.parse(value);
    return Number.isFinite(parsed) ? parsed : null;
}
function getMostRecentTimestamp(values) {
    let latest = null;
    for (const value of values) {
        const parsed = toTimestamp(value);
        if (parsed == null)
            continue;
        if (latest == null || parsed > latest) {
            latest = parsed;
        }
    }
    return latest;
}
export function isServerExecutionActive(diagnostics) {
    return ACTIVE_SERVER_STATUSES.has(normalizeStatus(diagnostics?.execution?.status ?? null));
}
export function getBackendLatestSeq(diagnostics) {
    const candidates = [
        diagnostics?.currentSession?.lastDelivery?.lastSeq,
        diagnostics?.transport?.lastSeq,
    ].filter((value) => typeof value === "number" && Number.isFinite(value));
    if (candidates.length === 0)
        return null;
    return Math.max(...candidates);
}
export function interpretAgentRunDiagnostics(params) {
    const { socketDiagnostics, localSnapshot, serverDiagnostics, now = new Date(), stalledAfterMs = 30_000, } = params;
    const serverActive = isServerExecutionActive(serverDiagnostics);
    const serverFailed = normalizeStatus(serverDiagnostics?.execution?.status) === "error" ||
        serverDiagnostics?.transport?.lastLifecycleEventType === "agent:run:error";
    const socketOpen = socketDiagnostics.readyStateLabel === "open";
    const sessionSubscribed = serverDiagnostics?.currentSession?.isSubscribed ??
        localSnapshot.isSubscribed;
    const sessionConnected = serverDiagnostics?.currentSession?.isConnected ?? socketOpen;
    const serverLastSeq = serverDiagnostics?.currentSession?.lastDelivery?.lastSeq ??
        serverDiagnostics?.transport?.lastSeq ??
        null;
    const serverLastDeliveryAt = getMostRecentTimestamp([
        serverDiagnostics?.currentSession?.lastDelivery?.lastSuccessAt,
        serverDiagnostics?.transport?.lastBroadcastAt,
        serverDiagnostics?.transport?.lastProducedAt,
    ]);
    const latestProgressAt = getMostRecentTimestamp([
        localSnapshot.lastEventAt,
        serverDiagnostics?.transport?.lastBroadcastAt,
        serverDiagnostics?.transport?.lastProducedAt,
    ]);
    const clientLagging = serverActive &&
        socketOpen &&
        sessionConnected &&
        sessionSubscribed &&
        typeof serverLastSeq === "number" &&
        serverLastSeq > localSnapshot.lastSeq &&
        serverLastDeliveryAt != null &&
        now.getTime() - serverLastDeliveryAt > 8_000;
    const stalled = serverActive &&
        socketOpen &&
        latestProgressAt != null &&
        now.getTime() - latestProgressAt > stalledAfterMs;
    const waitingForCapacity = isWaitingForCapacity(localSnapshot, serverDiagnostics);
    if (serverFailed) {
        return {
            code: "server-failed",
            title: "Server failed",
            detail: serverDiagnostics?.execution?.error ||
                serverDiagnostics?.transport?.lastFailure ||
                "The backend reported an agent error.",
        };
    }
    if (serverActive && !socketOpen) {
        return {
            code: "client-disconnected",
            title: "Client disconnected",
            detail: "The backend still reports the run as active, but this editor WebSocket is not open.",
        };
    }
    if (serverActive && sessionConnected && !sessionSubscribed) {
        return {
            code: "subscription-lost",
            title: "Subscription lost",
            detail: "This tab is connected, but the current session is no longer subscribed to the agent stream.",
        };
    }
    if (waitingForCapacity) {
        return {
            code: "waiting-for-capacity",
            title: "Waiting for capacity",
            detail: localSnapshot.lastStatusMessage ||
                "This agent is queued and will start automatically when a machine slot becomes available.",
        };
    }
    if (clientLagging) {
        return {
            code: "client-lagging",
            title: "Client stream lagging",
            detail: "The backend has delivered newer stream events than this terminal has applied. The terminal will reconnect and resubscribe to recover.",
        };
    }
    if (stalled) {
        return {
            code: "stream-stalled",
            title: "Stream stalled",
            detail: "The run still looks active, but no non-heartbeat status or delta progress has arrived recently.",
        };
    }
    if (serverActive && socketOpen && sessionSubscribed) {
        return {
            code: "healthy",
            title: "Stream healthy",
            detail: "The socket is open, this session is subscribed, and the backend still reports the run as active.",
        };
    }
    if (normalizeStatus(serverDiagnostics?.execution?.status) === "completed" ||
        normalizeStatus(serverDiagnostics?.execution?.status) === "cancelled" ||
        normalizeStatus(serverDiagnostics?.execution?.status) === "idle") {
        return {
            code: "run-finished",
            title: "Run finished",
            detail: "The backend no longer reports an active execution for this run.",
        };
    }
    if (localSnapshot.isExecuting || localSnapshot.isSubmitting) {
        return {
            code: "waiting",
            title: "Waiting for activity",
            detail: "The client still believes the run is active, but the backend has not reported a definitive state yet.",
        };
    }
    return {
        code: "run-finished",
        title: "Run idle",
        detail: "No active execution or recent stream activity was detected.",
    };
}
export function formatSeqRanges(seqs) {
    const sorted = [...new Set(seqs)].sort((a, b) => a - b);
    const first = sorted[0];
    if (first === undefined)
        return "";
    const ranges = [];
    let start = first;
    let end = first;
    for (let i = 1; i < sorted.length; i++) {
        const value = sorted[i];
        if (value === undefined)
            continue;
        if (value === end + 1) {
            end = value;
            continue;
        }
        ranges.push(start === end ? `${start}` : `${start}-${end}`);
        start = value;
        end = value;
    }
    ranges.push(start === end ? `${start}` : `${start}-${end}`);
    return ranges.join(", ");
}
export const STREAM_STALLED_AFTER_MS = 15_000;
/**
 * Watchdog rule: while the terminal locally believes the run is active and is visible,
 * if no non-heartbeat run update has arrived for `stalledAfterMs`, treat the stream as
 * stalled and ask the backend whether it has advanced beyond what we've applied.
 *
 * Suppressed while a stop is pending or the run is queued for machine capacity — those
 * states can legitimately be quiet.
 */
export function isStreamStalled(params) {
    const { isExecuting, isVisible, isStopping, isWaitingForCapacity, lastUpdateAt, now, stalledAfterMs = STREAM_STALLED_AFTER_MS, } = params;
    if (!isExecuting || !isVisible || isStopping || isWaitingForCapacity) {
        return false;
    }
    if (lastUpdateAt <= 0)
        return false;
    return now - lastUpdateAt > stalledAfterMs;
}
/**
 * Reload only when the backend has produced sequence numbers we have not applied. If
 * we don't have a backend seq, or the backend is at-or-behind the client, do nothing —
 * the silence is something else (idle run, server-side lag, etc.) and a reload would
 * just be churn.
 */
export function shouldReloadAfterDiagnostics(params) {
    const { localSeq, backendSeq } = params;
    if (backendSeq == null)
        return false;
    return backendSeq > localSeq;
}
export function formatAgentDiagnosticsEvent(event) {
    const direction = "direction" in event && event.direction ? `${event.direction} ` : "";
    const eventLabel = "type" in event ? `${direction}${event.type}` : event.messageType;
    const parts = [event.timestamp, eventLabel].filter(Boolean);
    const detail = "detail" in event ? event.detail : undefined;
    if (detail) {
        parts.push(String(detail));
    }
    if (event.seq != null) {
        parts.push(`seq ${event.seq}`);
    }
    return parts.join(" | ");
}
//# sourceMappingURL=agentDiagnostics.js.map