
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0dd4aa06-42da-5e5e-98aa-5c317b586a20")}catch(e){}}();
import { WebSocket, WebSocketServer } from "ws";
import { createWorkspaceMetadataMonitor } from "./workspace-metadata-monitor.js";
const TASK_SESSION_STREAM_BATCH_MS = 150;
export function createRuntimeStateHub(deps) {
    const terminalSummaryUnsubscribeByWorkspaceId = new Map();
    const clineSummaryUnsubscribeByWorkspaceId = new Map();
    const clineMessageUnsubscribeByWorkspaceId = new Map();
    const clinePreviousSummaryByWorkspaceId = new Map();
    const pendingTaskSessionSummariesByWorkspaceId = new Map();
    const taskSessionBroadcastTimersByWorkspaceId = new Map();
    const runtimeStateClientsByWorkspaceId = new Map();
    const runtimeStateClients = new Set();
    const runtimeStateWorkspaceIdByClient = new Map();
    let clineSessionContextVersion = 0;
    const runtimeStateWebSocketServer = new WebSocketServer({ noServer: true });
    const workspaceMetadataMonitor = createWorkspaceMetadataMonitor({
        onMetadataUpdated: (workspaceId, workspaceMetadata) => {
            const clients = runtimeStateClientsByWorkspaceId.get(workspaceId);
            if (!clients || clients.size === 0) {
                return;
            }
            const payload = {
                type: "workspace_metadata_updated",
                workspaceId,
                workspaceMetadata,
            };
            for (const client of clients) {
                sendRuntimeStateMessage(client, payload);
            }
        },
    });
    const sendRuntimeStateMessage = (client, payload) => {
        if (client.readyState !== WebSocket.OPEN) {
            return;
        }
        try {
            client.send(JSON.stringify(payload));
        }
        catch {
            // Ignore websocket write errors; close handlers clean up disconnected sockets.
        }
    };
    const broadcastRuntimeProjectsUpdated = async (preferredCurrentProjectId) => {
        if (runtimeStateClients.size === 0) {
            return;
        }
        try {
            const payload = await deps.workspaceRegistry.buildProjectsPayload(preferredCurrentProjectId);
            for (const client of runtimeStateClients) {
                sendRuntimeStateMessage(client, {
                    type: "projects_updated",
                    currentProjectId: payload.currentProjectId,
                    projects: payload.projects,
                });
            }
        }
        catch {
            // Ignore transient project summary failures; next update will resync.
        }
    };
    const broadcastClineMcpAuthStatusesUpdated = (statuses) => {
        if (runtimeStateClients.size === 0) {
            return;
        }
        const payload = {
            type: "mcp_auth_updated",
            statuses,
        };
        for (const client of runtimeStateClients) {
            sendRuntimeStateMessage(client, payload);
        }
    };
    const bumpClineSessionContextVersion = () => {
        clineSessionContextVersion += 1;
        if (runtimeStateClients.size === 0) {
            return;
        }
        const payload = {
            type: "cline_session_context_updated",
            version: clineSessionContextVersion,
        };
        for (const client of runtimeStateClients) {
            sendRuntimeStateMessage(client, payload);
        }
    };
    const flushTaskSessionSummaries = (workspaceId) => {
        const pending = pendingTaskSessionSummariesByWorkspaceId.get(workspaceId);
        if (!pending || pending.size === 0) {
            return;
        }
        pendingTaskSessionSummariesByWorkspaceId.delete(workspaceId);
        const summaries = Array.from(pending.values());
        const runtimeClients = runtimeStateClientsByWorkspaceId.get(workspaceId);
        if (runtimeClients && runtimeClients.size > 0) {
            const payload = {
                type: "task_sessions_updated",
                workspaceId,
                summaries,
            };
            for (const client of runtimeClients) {
                sendRuntimeStateMessage(client, payload);
            }
        }
        void broadcastRuntimeProjectsUpdated(workspaceId);
    };
    const queueTaskSessionSummaryBroadcast = (workspaceId, summary) => {
        const pending = pendingTaskSessionSummariesByWorkspaceId.get(workspaceId) ?? new Map();
        pending.set(summary.taskId, summary);
        pendingTaskSessionSummariesByWorkspaceId.set(workspaceId, pending);
        if (taskSessionBroadcastTimersByWorkspaceId.has(workspaceId)) {
            return;
        }
        const timer = setTimeout(() => {
            taskSessionBroadcastTimersByWorkspaceId.delete(workspaceId);
            flushTaskSessionSummaries(workspaceId);
        }, TASK_SESSION_STREAM_BATCH_MS);
        timer.unref();
        taskSessionBroadcastTimersByWorkspaceId.set(workspaceId, timer);
    };
    const broadcastTaskChatMessage = (workspaceId, taskId, message) => {
        const runtimeClients = runtimeStateClientsByWorkspaceId.get(workspaceId);
        if (!runtimeClients || runtimeClients.size === 0) {
            return;
        }
        const payload = {
            type: "task_chat_message",
            workspaceId,
            taskId,
            message,
        };
        for (const client of runtimeClients) {
            sendRuntimeStateMessage(client, payload);
        }
    };
    const disposeTaskSessionSummaryBroadcast = (workspaceId) => {
        const timer = taskSessionBroadcastTimersByWorkspaceId.get(workspaceId);
        if (timer) {
            clearTimeout(timer);
        }
        taskSessionBroadcastTimersByWorkspaceId.delete(workspaceId);
        pendingTaskSessionSummariesByWorkspaceId.delete(workspaceId);
    };
    const cleanupRuntimeStateClient = (client) => {
        const workspaceId = runtimeStateWorkspaceIdByClient.get(client);
        if (workspaceId) {
            workspaceMetadataMonitor.disconnectWorkspace(workspaceId);
            const clients = runtimeStateClientsByWorkspaceId.get(workspaceId);
            if (clients) {
                clients.delete(client);
                if (clients.size === 0) {
                    runtimeStateClientsByWorkspaceId.delete(workspaceId);
                }
            }
        }
        runtimeStateWorkspaceIdByClient.delete(client);
        runtimeStateClients.delete(client);
    };
    const disposeWorkspace = (workspaceId, options) => {
        const unsubscribeSummary = terminalSummaryUnsubscribeByWorkspaceId.get(workspaceId);
        if (unsubscribeSummary) {
            try {
                unsubscribeSummary();
            }
            catch {
                // Ignore listener cleanup errors during project removal.
            }
        }
        terminalSummaryUnsubscribeByWorkspaceId.delete(workspaceId);
        const unsubscribeClineSummary = clineSummaryUnsubscribeByWorkspaceId.get(workspaceId);
        if (unsubscribeClineSummary) {
            try {
                unsubscribeClineSummary();
            }
            catch {
                // Ignore listener cleanup errors during project removal.
            }
        }
        clineSummaryUnsubscribeByWorkspaceId.delete(workspaceId);
        clinePreviousSummaryByWorkspaceId.delete(workspaceId);
        const unsubscribeClineMessage = clineMessageUnsubscribeByWorkspaceId.get(workspaceId);
        if (unsubscribeClineMessage) {
            try {
                unsubscribeClineMessage();
            }
            catch {
                // Ignore listener cleanup errors during project removal.
            }
        }
        clineMessageUnsubscribeByWorkspaceId.delete(workspaceId);
        disposeTaskSessionSummaryBroadcast(workspaceId);
        workspaceMetadataMonitor.disposeWorkspace(workspaceId);
        if (!options?.disconnectClients) {
            return;
        }
        const runtimeClients = runtimeStateClientsByWorkspaceId.get(workspaceId);
        if (!runtimeClients || runtimeClients.size === 0) {
            runtimeStateClientsByWorkspaceId.delete(workspaceId);
            return;
        }
        for (const runtimeClient of runtimeClients) {
            if (options.closeClientErrorMessage) {
                sendRuntimeStateMessage(runtimeClient, {
                    type: "error",
                    message: options.closeClientErrorMessage,
                });
            }
            try {
                runtimeClient.close();
            }
            catch {
                // Ignore close failures while disposing removed workspace clients.
            }
            cleanupRuntimeStateClient(runtimeClient);
        }
        runtimeStateClientsByWorkspaceId.delete(workspaceId);
    };
    const broadcastRuntimeWorkspaceStateUpdated = async (workspaceId, workspacePath) => {
        const clients = runtimeStateClientsByWorkspaceId.get(workspaceId);
        if (!clients || clients.size === 0) {
            return;
        }
        try {
            const workspaceState = await deps.workspaceRegistry.buildWorkspaceStateSnapshot(workspaceId, workspacePath);
            const payload = {
                type: "workspace_state_updated",
                workspaceId,
                workspaceState,
            };
            for (const client of clients) {
                sendRuntimeStateMessage(client, payload);
            }
            await workspaceMetadataMonitor.updateWorkspaceState({
                workspaceId,
                workspacePath,
                board: workspaceState.board,
            });
        }
        catch {
            // Ignore transient state read failures; next update will resync.
        }
    };
    const broadcastTaskReadyForReview = (workspaceId, taskId) => {
        const runtimeClients = runtimeStateClientsByWorkspaceId.get(workspaceId);
        if (!runtimeClients || runtimeClients.size === 0) {
            return;
        }
        const payload = {
            type: "task_ready_for_review",
            workspaceId,
            taskId,
            triggeredAt: Date.now(),
        };
        for (const client of runtimeClients) {
            sendRuntimeStateMessage(client, payload);
        }
    };
    runtimeStateWebSocketServer.on("connection", async (client, context) => {
        client.on("close", () => {
            cleanupRuntimeStateClient(client);
        });
        try {
            const requestedWorkspaceId = typeof context === "object" &&
                context !== null &&
                "requestedWorkspaceId" in context &&
                typeof context.requestedWorkspaceId === "string"
                ? context.requestedWorkspaceId || null
                : null;
            const workspace = await deps.workspaceRegistry.resolveWorkspaceForStream(requestedWorkspaceId, {
                onRemovedWorkspace: ({ workspaceId, message }) => {
                    disposeWorkspace(workspaceId, {
                        disconnectClients: true,
                        closeClientErrorMessage: message,
                    });
                },
            });
            if (client.readyState !== WebSocket.OPEN) {
                cleanupRuntimeStateClient(client);
                return;
            }
            /*
                Connection setup for workspace-scoped runtime streams is intentionally split into two phases.

                We need the initial snapshot to already contain the first workspace metadata payload, but we do not want
                the client to receive a separate "workspace_metadata_updated" event before that snapshot arrives.

                That race can happen if we register the websocket in runtimeStateClientsByWorkspaceId first and then call
                workspaceMetadataMonitor.connectWorkspace(...). connectWorkspace() performs an immediate refresh, and that
                refresh may broadcast "workspace_metadata_updated" to every currently registered workspace client. In that
                old ordering, a newly connected client could observe:

                1. workspace_metadata_updated
                2. snapshot

                which makes the initial load look wrong and forces the UI to process the same logical data twice in the
                opposite order from what readers expect.

                To avoid that, we:

                1. add the socket only to the global runtimeStateClients set so project-wide broadcasts still work
                2. build workspace state and connect the metadata monitor to get the initial metadata snapshot
                3. send the combined "snapshot" message
                4. only then register the socket in runtimeStateClientsByWorkspaceId so future incremental
                   workspace_metadata_updated events can flow normally

                The extra readyState checks and monitor cleanup below are paired with this delayed registration. If the
                socket closes while we are still assembling or sending the initial snapshot, we must disconnect the
                temporary metadata monitor subscription before returning, otherwise we would leave behind subscriber count
                state for a client that never finished the handshake.
            */
            runtimeStateClients.add(client);
            let monitorWorkspaceId = null;
            let didConnectWorkspaceMonitor = false;
            try {
                let projectsPayload;
                let workspaceState;
                let workspaceMetadata;
                if (workspace.workspaceId && workspace.workspacePath) {
                    monitorWorkspaceId = workspace.workspaceId;
                    [projectsPayload, workspaceState] = await Promise.all([
                        deps.workspaceRegistry.buildProjectsPayload(workspace.workspaceId),
                        deps.workspaceRegistry.buildWorkspaceStateSnapshot(workspace.workspaceId, workspace.workspacePath),
                    ]);
                    workspaceMetadata = await workspaceMetadataMonitor.connectWorkspace({
                        workspaceId: workspace.workspaceId,
                        workspacePath: workspace.workspacePath,
                        board: workspaceState.board,
                    });
                    didConnectWorkspaceMonitor = true;
                }
                else {
                    projectsPayload = await deps.workspaceRegistry.buildProjectsPayload(null);
                    workspaceState = null;
                    workspaceMetadata = null;
                }
                if (client.readyState !== WebSocket.OPEN) {
                    if (monitorWorkspaceId) {
                        workspaceMetadataMonitor.disconnectWorkspace(monitorWorkspaceId);
                    }
                    cleanupRuntimeStateClient(client);
                    return;
                }
                sendRuntimeStateMessage(client, {
                    type: "snapshot",
                    currentProjectId: projectsPayload.currentProjectId,
                    projects: projectsPayload.projects,
                    workspaceState,
                    workspaceMetadata,
                    clineSessionContextVersion,
                });
                if (client.readyState !== WebSocket.OPEN) {
                    if (monitorWorkspaceId) {
                        workspaceMetadataMonitor.disconnectWorkspace(monitorWorkspaceId);
                    }
                    cleanupRuntimeStateClient(client);
                    return;
                }
                if (monitorWorkspaceId) {
                    const workspaceClients = runtimeStateClientsByWorkspaceId.get(monitorWorkspaceId) ?? new Set();
                    workspaceClients.add(client);
                    runtimeStateClientsByWorkspaceId.set(monitorWorkspaceId, workspaceClients);
                    runtimeStateWorkspaceIdByClient.set(client, monitorWorkspaceId);
                    const clineSummaries = Array.from(clinePreviousSummaryByWorkspaceId.get(monitorWorkspaceId)?.values() ?? []);
                    if (clineSummaries.length > 0) {
                        sendRuntimeStateMessage(client, {
                            type: "task_sessions_updated",
                            workspaceId: monitorWorkspaceId,
                            summaries: clineSummaries,
                        });
                    }
                }
                if (workspace.removedRequestedWorkspacePath) {
                    sendRuntimeStateMessage(client, {
                        type: "error",
                        message: `Project no longer exists on disk and was removed: ${workspace.removedRequestedWorkspacePath}`,
                    });
                }
                if (workspace.didPruneProjects) {
                    void broadcastRuntimeProjectsUpdated(workspace.workspaceId);
                }
            }
            catch (error) {
                if (didConnectWorkspaceMonitor && monitorWorkspaceId) {
                    workspaceMetadataMonitor.disconnectWorkspace(monitorWorkspaceId);
                }
                const message = error instanceof Error ? error.message : String(error);
                sendRuntimeStateMessage(client, {
                    type: "error",
                    message,
                });
            }
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            sendRuntimeStateMessage(client, {
                type: "error",
                message,
            });
            client.close();
        }
    });
    return {
        trackTerminalManager: (workspaceId, manager) => {
            if (terminalSummaryUnsubscribeByWorkspaceId.has(workspaceId)) {
                return;
            }
            const unsubscribe = manager.onSummary((summary) => {
                queueTaskSessionSummaryBroadcast(workspaceId, summary);
            });
            terminalSummaryUnsubscribeByWorkspaceId.set(workspaceId, unsubscribe);
        },
        trackClineTaskSessionService: (workspaceId, workspacePath, service) => {
            if (clineSummaryUnsubscribeByWorkspaceId.has(workspaceId)) {
                return;
            }
            const previousSummariesByTaskId = new Map();
            clinePreviousSummaryByWorkspaceId.set(workspaceId, previousSummariesByTaskId);
            for (const summary of service.listSummaries()) {
                previousSummariesByTaskId.set(summary.taskId, summary);
                queueTaskSessionSummaryBroadcast(workspaceId, summary);
            }
            const unsubscribe = service.onSummary((summary) => {
                const previousSummary = previousSummariesByTaskId.get(summary.taskId);
                previousSummariesByTaskId.set(summary.taskId, summary);
                queueTaskSessionSummaryBroadcast(workspaceId, summary);
                const didCheckpointChange = previousSummary?.latestTurnCheckpoint?.commit !== summary.latestTurnCheckpoint?.commit ||
                    previousSummary?.previousTurnCheckpoint?.commit !== summary.previousTurnCheckpoint?.commit;
                if (didCheckpointChange) {
                    void broadcastRuntimeWorkspaceStateUpdated(workspaceId, workspacePath);
                }
                if (previousSummary &&
                    previousSummary.state !== "awaiting_review" &&
                    summary.state === "awaiting_review" &&
                    (summary.reviewReason === "hook" ||
                        summary.reviewReason === "attention" ||
                        summary.reviewReason === "error")) {
                    broadcastTaskReadyForReview(workspaceId, summary.taskId);
                }
            });
            clineSummaryUnsubscribeByWorkspaceId.set(workspaceId, unsubscribe);
            const unsubscribeMessage = service.onMessage((taskId, message) => {
                broadcastTaskChatMessage(workspaceId, taskId, message);
            });
            clineMessageUnsubscribeByWorkspaceId.set(workspaceId, unsubscribeMessage);
        },
        broadcastTaskChatMessage,
        handleUpgrade: (request, socket, head, context) => {
            runtimeStateWebSocketServer.handleUpgrade(request, socket, head, (ws) => {
                runtimeStateWebSocketServer.emit("connection", ws, context);
            });
        },
        disposeWorkspace,
        broadcastRuntimeWorkspaceStateUpdated,
        broadcastRuntimeProjectsUpdated,
        broadcastClineMcpAuthStatusesUpdated,
        bumpClineSessionContextVersion,
        broadcastTaskReadyForReview,
        close: async () => {
            for (const timer of taskSessionBroadcastTimersByWorkspaceId.values()) {
                clearTimeout(timer);
            }
            taskSessionBroadcastTimersByWorkspaceId.clear();
            pendingTaskSessionSummariesByWorkspaceId.clear();
            for (const unsubscribe of terminalSummaryUnsubscribeByWorkspaceId.values()) {
                try {
                    unsubscribe();
                }
                catch {
                    // Ignore listener cleanup errors during shutdown.
                }
            }
            terminalSummaryUnsubscribeByWorkspaceId.clear();
            for (const unsubscribe of clineSummaryUnsubscribeByWorkspaceId.values()) {
                try {
                    unsubscribe();
                }
                catch {
                    // Ignore listener cleanup errors during shutdown.
                }
            }
            clineSummaryUnsubscribeByWorkspaceId.clear();
            clinePreviousSummaryByWorkspaceId.clear();
            for (const unsubscribe of clineMessageUnsubscribeByWorkspaceId.values()) {
                try {
                    unsubscribe();
                }
                catch {
                    // Ignore listener cleanup errors during shutdown.
                }
            }
            clineMessageUnsubscribeByWorkspaceId.clear();
            workspaceMetadataMonitor.close();
            for (const client of runtimeStateClients) {
                try {
                    client.terminate();
                }
                catch {
                    // Ignore websocket termination errors during shutdown.
                }
            }
            runtimeStateClients.clear();
            runtimeStateClientsByWorkspaceId.clear();
            runtimeStateWorkspaceIdByClient.clear();
            await new Promise((resolveCloseWebSockets) => {
                runtimeStateWebSocketServer.close(() => {
                    resolveCloseWebSockets();
                });
            });
        },
    };
}
//# sourceMappingURL=runtime-state-hub.js.map
//# debugId=0dd4aa06-42da-5e5e-98aa-5c317b586a20
