/** * Server ↔ Browser WebSocket protocol messages. */ import type { PluginIntentsMessage, PluginActionMessage, } from "./dashboard-plugin/intent-types.js"; import type { DashboardSession, DashboardEvent, CommandInfo, FlowInfo, ImageContent, FileEntry, OpenSpecData, OpenSpecGroup, ModelInfo, PiSessionInfo, ExtensionUiModule, DecoratorDescriptor, } from "./types.js"; import type { TerminalSession } from "./terminal-types.js"; import type { EditorInstanceStatus } from "./editor-types.js"; // ── Server → Browser ──────────────────────────────────────────────── export interface SessionAddedMessage { type: "session_added"; session: DashboardSession; /** * Echoed `requestId` from the originating browser `spawn_session` / * `resume_session` (when known). Lets the client auto-select / dismiss * placeholder by exact correlation, replacing the cwd-FIFO heuristic. * Absent for server-initiated spawns (auto-resume, headless reload). * See change: spawn-correlation-token. */ spawnRequestId?: string; } export interface SessionUpdatedMessage { type: "session_updated"; sessionId: string; updates: Partial; } export interface SessionRemovedMessage { type: "session_removed"; sessionId: string; } export interface EventMessage { type: "event"; sessionId: string; seq: number; event: DashboardEvent; } export interface EventReplayMessage { type: "event_replay"; sessionId: string; events: Array<{ seq: number; event: DashboardEvent }>; isLast: boolean; } export interface BrowserCommandsListMessage { type: "commands_list"; sessionId: string; commands: CommandInfo[]; } export interface BrowserFlowsListMessage { type: "flows_list"; sessionId: string; flows: FlowInfo[]; } export interface BrowserExtensionUiRequestMessage { type: "extension_ui_request"; sessionId: string; requestId: string; method: string; params: Record; } export interface BrowserUiDismissMessage { type: "ui_dismiss"; sessionId: string; requestId: string; } export interface BrowserFilesListMessage { type: "files_list"; sessionId: string; query: string; files: FileEntry[]; } export interface BrowserOpenSpecUpdateMessage { type: "openspec_update"; cwd: string; data: OpenSpecData; } /** * Per-repo OpenSpec change-grouping update. Broadcast after every successful * write to `/openspec/groups/groups.json`, debounced 100 ms per cwd. * Full payload (no incremental delta) so client logic stays simple. * See change: add-openspec-change-grouping. */ export interface BrowserOpenSpecGroupsUpdateMessage { type: "openspec_groups_update"; cwd: string; groups: OpenSpecGroup[]; assignments: Record; } export interface BrowserModelsListMessage { type: "models_list"; sessionId: string; models: ModelInfo[]; } export interface ModelsRefreshedMessage { type: "models_refreshed"; } export interface BrowserRolesListMessage { type: "roles_list"; sessionId: string; roles: Record; presets: Array<{ name: string; roles: Record }>; activePreset: string | null; } export interface SessionsListBrowserMessage { type: "sessions_list"; sessionId: string; cwd: string; sessions: PiSessionInfo[]; } export interface ResumeResultBrowserMessage { type: "resume_result"; sessionId: string; success: boolean; message: string; /** Echoed from input `resume_session.requestId` when provided. */ requestId?: string; /** * For `mode: "fork"` only — populated once the new fork's bridge has * registered and been correlated. Absent for `mode: "continue"` (the * sessionId is unchanged across the respawn). * See change: spawn-correlation-token. */ newSessionId?: string; /** * Optional structured failure classifier. Known values: * - `"FORK_EMPTY_SESSION"`: fork attempted on a session whose * `sessionFile` does not exist on disk yet (e.g., freshly spawned, * no messages persisted). * Old clients that don't read this field still get the human-readable * `message`. See change: fix-fork-empty-session-silent-timeout. */ code?: string; } export interface SpawnResultBrowserMessage { type: "spawn_result"; cwd: string; success: boolean; message: string; /** Echoed from input `spawn_session.requestId` when provided. */ requestId?: string; /** Spawned process PID when known (headless strategies); informational. */ pid?: number; } /** * Failure classification codes for spawn errors. * Set on every `{ success: false }` path inside process-manager and the * session-action-handler. Additive — clients that do not know a code fall * back to the `message` string. * See change: spawn-failure-diagnostics. */ export type SpawnFailureCode = | "DIR_MISSING" | "PI_NOT_FOUND" | "WIN_PI_CMD_ONLY" | "WT_MISSING" | "TMUX_MISSING" | "PI_CRASHED" | "SPAWN_ERRNO" | "PREFLIGHT_FAILED" | "REGISTER_TIMEOUT"; /** * A single reason from the synchronous spawn preflight check. * See change: spawn-failure-diagnostics. */ export interface PreflightReason { code: string; message: string; } /** * Emitted when a session spawn fails — either because `spawnPiSession` threw, * returned `{ success: false }`, or the spawned child crashed immediately. * Carries enough context for the UI to render a retryable error banner * instead of leaving the user staring at a silent empty state. */ export interface SpawnErrorMessage { type: "spawn_error"; cwd: string; strategy: string; message: string; /** Up to ~2 KB tail of stderr captured from the failed child, if any. */ stderr?: string; /** Structured failure classifier. Additive — old clients ignore this field. See change: spawn-failure-diagnostics. */ code?: SpawnFailureCode; /** Preflight failure reasons. Only set when code === "PREFLIGHT_FAILED". See change: spawn-failure-diagnostics. */ reasons?: PreflightReason[]; } /** * Emitted when a spawned pi session never sends `session_register` within * the configured `spawnRegisterTimeoutMs` window. * See change: spawn-failure-diagnostics. */ export interface SpawnRegisterTimeoutMessage { type: "spawn_register_timeout"; cwd: string; /** Present for headless spawns; absent for tmux/wt/wsl-tmux. */ pid?: number; /** Last 4 KB of the per-session stderr log, if available. */ stderrTail?: string; /** The effective watchdog timeout in ms — so the UI can render e.g. "30s". See change: spawn-failure-diagnostics (fix W2). */ timeoutMs?: number; } /** * Emitted when pi finally registers AFTER the watchdog already fired. * The UI uses it to auto-clear the timeout banner for the given cwd. * See change: spawn-failure-diagnostics. */ export interface SpawnRegisterRecoveredMessage { type: "spawn_register_recovered"; cwd: string; pid?: number; } export interface SessionsReorderedMessage { type: "sessions_reordered"; cwd: string; sessionIds: string[]; } /** * Atomic on-connect snapshot of the server's full session registry and * per-cwd ordering. Replaces the legacy per-session `session_added` loop * + per-cwd `sessions_reordered` loop that the gateway used to emit on * each browser WS connect. Client SHALL replace its `sessions` Map and * `sessionOrderMap` with this payload (no merging) so stale ids from a * previous server lifetime are dropped atomically. * * Live updates after the snapshot continue using the incremental * `session_added` / `session_updated` / `session_removed` / * `sessions_reordered` messages. * * See change: fix-stale-sessions-on-reconnect. */ export interface SessionsSnapshotMessage { type: "sessions_snapshot"; /** Every session known to the server at construction time, alive AND ended. */ sessions: DashboardSession[]; /** cwd → ordered session ids. Only non-empty arrays are included. */ orders: Record; } export interface PinnedDirsUpdatedMessage { type: "pinned_dirs_updated"; paths: string[]; } // ── Workspaces (folder-workspaces) ─────────────────────────────────── /** * Named, server-persisted, collapsible container grouping one or more * folders. Membership is authoritative and orthogonal to pinning — a * folder may be in `folders[]` and `pinnedDirectories` independently. * Single-membership: a folder belongs to ≤1 workspace. * See change: folder-workspaces. */ export interface Workspace { id: string; name: string; collapsed: boolean; folders: string[]; } /** Server → browser: full workspace list snapshot (sent on subscribe + every mutation). */ export interface WorkspacesUpdatedMessage { type: "workspaces_updated"; workspaces: Workspace[]; } export interface TerminalAddedMessage { type: "terminal_added"; terminal: TerminalSession; } export interface TerminalRemovedMessage { type: "terminal_removed"; terminalId: string; } export interface TerminalUpdatedMessage { type: "terminal_updated"; terminalId: string; updates: Partial; } /** Tells the browser to reset accumulated state for a session (bridge reconnected). */ export interface SessionStateResetMessage { type: "session_state_reset"; sessionId: string; } /** Notifies browsers of editor instance status changes. */ export interface EditorStatusMessage { type: "editor_status"; cwd: string; id: string; status: EditorInstanceStatus; } // ── PromptBus protocol (Server → Browser) ─────────────────────────── export interface BrowserPromptRequestMessage { type: "prompt_request"; sessionId: string; promptId: string; prompt: { question: string; type: string; options?: string[]; defaultValue?: string; pipeline?: string; metadata?: Record; }; component: { type: string; props: Record; }; placement: string; } export interface BrowserPromptDismissMessage { type: "prompt_dismiss"; sessionId: string; promptId: string; } export interface BrowserPromptCancelMessage { type: "prompt_cancel"; sessionId: string; promptId: string; } /** Progress event streamed during a package install/remove/update/move operation. * * `moveId` is set when this progress event is part of a move operation * (which composes install + remove). Clients group events by `moveId` * to display a single composite progress affordance instead of two * separate operations. Consumers that ignore the field continue to * render install + remove independently — graceful degradation. * * See change: unify-package-management-ui. */ export interface PackageProgressMessage { type: "package_progress"; operationId: string; /** Optional move grouping id when emitted as part of a move. */ moveId?: string; event: { type: "start" | "progress" | "complete" | "error"; action: "install" | "remove" | "update" | "clone" | "pull" | "move"; source: string; message?: string; }; } /** Progress event streamed during a pi core package update. */ export interface PiCoreUpdateProgressMessage { type: "pi_core_update_progress"; name: string; phase: "start" | "output" | "complete" | "error"; message?: string; } /** Sent when a full pi core update batch finishes. */ export interface PiCoreUpdateCompleteMessage { type: "pi_core_update_complete"; results: Array<{ name: string; success: boolean; error?: string }>; sessionsReloaded: number; } /** * Bootstrap state snapshot. Mirrors `BootstrapState` in * `packages/server/src/bootstrap-state.ts` but kept as a structural * subset here so the shared package doesn't take a runtime dependency * on the server package. * * See change: unified-bootstrap-install. */ export interface BootstrapStateSnapshot { status: "ready" | "installing" | "failed"; progress?: { step: string; pct?: number; output?: string }; error?: { message: string; stack?: string }; version?: { pi?: string; openspec?: string; tsx?: string }; compatibility?: { minimum: string; recommended: string; maximum: string | null; current?: string; upgradeRecommended?: boolean; upgradeDashboard?: boolean; }; bridgeRegistrationError?: string; /** * Legacy `@mariozechner/pi-coding-agent` installs detected on disk. * Surfaced by the client as a one-click cleanup banner. Empty array * means no legacy installs found. Pi was renamed to * `@earendil-works/pi-coding-agent` at v0.74 — the legacy scope can * collide with the new scope's `bin/pi` symlink. */ legacyPiInstalls?: Array<{ scope: "npm-global" | "npx-cache" | "managed"; path: string; version: string | null; }>; } /** * Broadcast on every bootstrap-state transition. Browsers use this to * render the first-run install banner, the upgrade-pi progress display, * and version-skew hints. */ export interface BootstrapStatusUpdateMessage { type: "bootstrap_status_update"; state: BootstrapStateSnapshot; } /** * Broadcast when a queued pi-dependent operation (e.g. a session-spawn * request accepted with 202 during "installing") finishes running after * the bootstrap transitioned to "ready". Clients that stored a ticketId * from the 202 response can correlate the outcome via this message. * * `success` is true when the queued handler resolved without throwing; * `error` carries the thrown message string when `success` is false. * * See change: unified-bootstrap-install. */ export interface BootstrapTicketCompleteMessage { type: "bootstrap_ticket_complete"; ticketId: string; success: boolean; error?: string; } /** Sent when a package operation finishes (success or failure). */ export interface PackageOperationCompleteMessage { type: "package_operation_complete"; operationId: string; /** Optional move grouping id; set on every event of a composite move op. */ moveId?: string; action: "install" | "remove" | "update" | "move"; source: string; scope: "global" | "local"; success: boolean; error?: string; /** Number of sessions reloaded (only on success). */ sessionsReloaded?: number; /** Set on a move op when install succeeded but remove failed. * Indicates the package now exists in BOTH scopes; UI should surface * a recovery action (POST /api/packages/remove against fromScope). */ partialSuccess?: { installed: boolean; removed: boolean; removeError?: string; }; } // ── Extension UI System (Phase 1: management-modal slot) ─────────── // See change: add-extension-ui-modal. /** Server → browser: cached extension-declared UI modules for a session. */ export interface BrowserUiModulesListMessage { type: "ui_modules_list"; sessionId: string; modules: ExtensionUiModule[]; } /** Server → browser: row data for a `view.dataEvent`. */ export interface BrowserUiDataListMessage { type: "ui_data_list"; sessionId: string; event: string; items: unknown[]; } // ── Extension UI System (Phase 2: live in-page decorations) ── // See change: add-extension-ui-decorations. /** * Server → browser: a live decorator descriptor (forwarded verbatim from the * extension). `removed: true` instructs the client to unmount the matching * descriptor. */ export interface BrowserExtUiDecoratorMessage { type: "ext_ui_decorator"; sessionId: string; descriptor: DecoratorDescriptor; removed?: boolean; } /** * Server → browser: register a base64-encoded image asset under a content * hash for the given session. Forwarded verbatim from the bridge's * `asset_register` message and replayed to reconnecting browsers (in * chronological position relative to its referencing `message_update` / * `message_end`). The client populates a per-session `Map` * consumed by the `MarkdownContent` `pi-asset:` resolver. * See change: chat-markdown-local-images-and-math. */ export interface BrowserAssetRegisterMessage { type: "asset_register"; sessionId: string; hash: string; mimeType: string; data: string; } /** Sent when a plugin's config changes; carries only that plugin's namespace. */ export interface PluginConfigUpdateMessage { type: "plugin_config_update"; /** Plugin id that was updated. */ id: string; /** * Only this plugin's namespace config (plugins..*). * Never contains other plugins' configs. */ config: unknown; } export type ServerToBrowserMessage = | PluginConfigUpdateMessage | SessionAddedMessage | SessionUpdatedMessage | SessionRemovedMessage | EventMessage | EventReplayMessage | BrowserCommandsListMessage | BrowserFlowsListMessage | BrowserExtensionUiRequestMessage | BrowserUiDismissMessage | BrowserFilesListMessage | BrowserOpenSpecUpdateMessage | BrowserOpenSpecGroupsUpdateMessage | BrowserModelsListMessage | SessionsListBrowserMessage | ResumeResultBrowserMessage | SpawnResultBrowserMessage | SpawnErrorMessage | SpawnRegisterTimeoutMessage | SpawnRegisterRecoveredMessage | SessionsReorderedMessage | SessionsSnapshotMessage | PinnedDirsUpdatedMessage | WorkspacesUpdatedMessage | TerminalAddedMessage | TerminalRemovedMessage | TerminalUpdatedMessage | SessionStateResetMessage | PackageProgressMessage | PackageOperationCompleteMessage | PiCoreUpdateProgressMessage | PiCoreUpdateCompleteMessage | EditorStatusMessage | ForceKillResultMessage | BrowserRolesListMessage | ProcessListUpdateMessage | ServersDiscoveredMessage | ServersUpdatedMessage | BrowserPromptRequestMessage | BrowserPromptDismissMessage | BrowserPromptCancelMessage | ModelsRefreshedMessage | BootstrapStatusUpdateMessage | BootstrapTicketCompleteMessage | BrowserUiModulesListMessage | BrowserUiDataListMessage | BrowserExtUiDecoratorMessage | BrowserAssetRegisterMessage | PluginIntentsMessage | QueueUpdateToBrowserMessage; // ── Browser → Server ──────────────────────────────────────────────── export interface SubscribeMessage { type: "subscribe"; sessionId: string; lastSeq?: number; } export interface UnsubscribeMessage { type: "unsubscribe"; sessionId: string; } export interface SendPromptToBrowserMessage { type: "send_prompt"; sessionId: string; text: string; images?: ImageContent[]; /** Delivery mode: "steer" (after current turn) or "followUp" (after agent finishes). Defaults to "followUp" when absent. See change: add-steering-message. */ delivery?: "steer" | "followUp"; } export interface AbortToBrowserMessage { type: "abort"; sessionId: string; } /** * Browser -> server: clear pi's steering queue for the given session. * Server forwards as `clear_steering_queue`; bridge calls `pi.clearSteeringQueue()`. * Pi emits a fresh `queue_update` reflecting the empty array, which the server * caches + broadcasts. Idempotent on empty queue. * See change: add-followup-edit-and-steer-cancel. */ export interface ClearSteeringQueueFromBrowserMessage { type: "clear_steering_queue"; sessionId: string; } /** * Browser -> server: clear pi's follow-up slot for the given session. * Server forwards as `clear_followup_slot`; bridge calls `pi.clearFollowUpQueue()`. * Idempotent on empty slot. * See change: add-followup-edit-and-steer-cancel. */ export interface ClearFollowupSlotFromBrowserMessage { type: "clear_followup_slot"; sessionId: string; } /** * Browser -> server (v1, deprecated): replace pi's follow-up slot atomically. * v2 prefers `edit_followup_entry { index: 0 }`. Bridge still accepts both * for backward compatibility. * See change: add-followup-edit-and-steer-cancel. */ export interface EditFollowupSlotFromBrowserMessage { type: "edit_followup_slot"; sessionId: string; text: string; images?: ImageContent[]; } /** * Browser -> server (v2): move follow-up entry at `index` to position 0. * See change: add-followup-edit-and-steer-cancel. */ export interface PromoteFollowupEntryFromBrowserMessage { type: "promote_followup_entry"; sessionId: string; index: number; } /** * Browser -> server (v2): remove follow-up entry at `index`. * See change: add-followup-edit-and-steer-cancel. */ export interface RemoveFollowupEntryFromBrowserMessage { type: "remove_followup_entry"; sessionId: string; index: number; } /** * Browser -> server (v2): replace follow-up entry at `index`. * See change: add-followup-edit-and-steer-cancel. */ export interface EditFollowupEntryFromBrowserMessage { type: "edit_followup_entry"; sessionId: string; index: number; text: string; images?: ImageContent[]; } /** * Server -> browser: broadcast pi's queue mirror after a `queue_update` * arrives from the bridge. Drives `Session.pendingQueues`. * See change: add-followup-edit-and-steer-cancel. */ export interface QueueUpdateToBrowserMessage { type: "queue_update"; sessionId: string; steering: string[]; followUp: string[]; } export interface RequestCommandsToBrowserMessage { type: "request_commands"; sessionId: string; } export interface FetchContentMessage { type: "fetch_content"; sessionId: string; seq: number; } export interface ListFilesToBrowserMessage { type: "list_files"; sessionId: string; query: string; } export interface OpenSpecRefreshBrowserMessage { type: "openspec_refresh"; cwd: string; } export interface RenameSessionBrowserMessage { type: "rename_session"; sessionId: string; name: string; } export interface RequestModelsBrowserMessage { type: "request_models"; sessionId: string; } /** * Browser asks the server to forward `request_providers` to the bridge. * See change: replace-hardcoded-provider-lists. */ export interface RequestProvidersBrowserMessage { type: "request_providers"; sessionId: string; } export interface SetThinkingLevelBrowserMessage { type: "set_thinking_level"; sessionId: string; level: string; } export interface SetModelBrowserMessage { type: "set_model"; sessionId: string; provider: string; modelId: string; } export interface ShutdownBrowserMessage { type: "shutdown"; sessionId: string; } export interface ForceKillBrowserMessage { type: "force_kill"; sessionId: string; } export interface ForceKillResultMessage { type: "force_kill_result"; sessionId: string; success: boolean; message?: string; } export interface ProcessListUpdateMessage { type: "process_list_update"; sessionId: string; processes: Array<{ pid: number; pgid: number; command: string; elapsedMs: number }>; } export interface ServersDiscoveredMessage { type: "servers_discovered"; servers: Array<{ host: string; port: number; piPort: number; version: string; pid: number; isLocal: boolean; source: "mdns" | "fallback"; }>; } export interface ServersUpdatedMessage { type: "servers_updated"; servers: Array<{ host: string; port: number; piPort: number; version: string; pid: number; isLocal: boolean; source: "mdns" | "fallback"; }>; } export interface KillProcessBrowserMessage { type: "kill_process"; sessionId: string; pgid: number; } export interface ListSessionsBrowserMessage { type: "list_sessions"; cwd: string; } export interface ResumeSessionBrowserMessage { type: "resume_session"; sessionId: string; mode: "continue" | "fork"; /** When forking, optionally fork from a specific session entry instead of the latest */ entryId?: string; /** * Client-minted UUIDv4 used to correlate `resume_result` and (for fork mode) * the eventual `session_added` for the new session. Optional for back-compat. * See change: spawn-correlation-token. */ requestId?: string; /** * Placement intent for the resumed session in the cwd's sessionOrder: * - "front" (default): move to top of alive tier (Resume button, REST, * prompt-auto-resume). * - "keep": leave order alone (drag-to-resume — drop position was already * persisted by an earlier `reorder_sessions` message). * Server defaults to "front" when omitted, preserving prior behavior. * See change: differentiate-resume-intent-by-trigger. */ placement?: "front" | "keep"; } export interface HideSessionBrowserMessage { type: "hide_session"; sessionId: string; } export interface UnhideSessionBrowserMessage { type: "unhide_session"; sessionId: string; } export interface SpawnSessionBrowserMessage { type: "spawn_session"; cwd: string; /** * Optional kebab-case OpenSpec change name to attach to the spawned session * once it registers. The server queues the intent in `pendingAttachByCwd` * and consumes it on the next matching `session_register`. * Old servers that ignore unknown fields produce a bare spawn (degraded but * recoverable: the user attaches manually). See change: * add-folder-task-checker-and-spawn-attach. */ attachProposal?: string; /** * Client-minted UUIDv4 used to correlate `spawn_result` and the eventual * `session_added` (which echoes it as `spawnRequestId`). Optional for * back-compat with older clients. * See change: spawn-correlation-token. */ requestId?: string; } export interface AttachProposalBrowserMessage { type: "attach_proposal"; sessionId: string; changeName: string; } export interface DetachProposalBrowserMessage { type: "detach_proposal"; sessionId: string; } export interface ReorderSessionsBrowserMessage { type: "reorder_sessions"; cwd: string; sessionIds: string[]; } export interface PinDirectoryMessage { type: "pin_directory"; path: string; } export interface UnpinDirectoryMessage { type: "unpin_directory"; path: string; } export interface ReorderPinnedDirsMessage { type: "reorder_pinned_dirs"; paths: string[]; } // ── Workspace mutation messages (browser → server) ─────────────────── // See change: folder-workspaces. Verb-first to match pin_directory family. export interface CreateWorkspaceMessage { type: "create_workspace"; name: string; } export interface RenameWorkspaceMessage { type: "rename_workspace"; id: string; name: string; } export interface DeleteWorkspaceMessage { type: "delete_workspace"; id: string; } export interface SetWorkspaceCollapsedMessage { type: "set_workspace_collapsed"; id: string; collapsed: boolean; } export interface AddFolderToWorkspaceMessage { type: "add_folder_to_workspace"; id: string; path: string; } export interface RemoveFolderFromWorkspaceMessage { type: "remove_folder_from_workspace"; id: string; path: string; } export interface ReorderWorkspaceFoldersMessage { type: "reorder_workspace_folders"; id: string; paths: string[]; } export interface ReorderWorkspacesMessage { type: "reorder_workspaces"; ids: string[]; } export interface OpenSpecBulkArchiveBrowserMessage { type: "openspec_bulk_archive"; cwd: string; } export interface CreateTerminalBrowserMessage { type: "create_terminal"; cwd: string; } export interface KillTerminalBrowserMessage { type: "kill_terminal"; terminalId: string; } export interface RenameTerminalBrowserMessage { type: "rename_terminal"; terminalId: string; title: string; } export interface BrowserExtensionUiResponseMessage { type: "extension_ui_response"; sessionId: string; requestId: string; result?: unknown; cancelled?: boolean; } export interface FlowControlBrowserMessage { type: "flow_control"; sessionId: string; action: "abort" | "toggle_autonomous" | "dismiss_summary"; } export interface RequestInstalledPackagesBrowserMessage { type: "request_installed_packages"; scope: "global" | "local"; cwd?: string; } export interface FlowManagementBrowserMessage { type: "flow_management"; sessionId: string; action: "run" | "new" | "edit" | "delete"; flowName?: string; task?: string; description?: string; } export interface ArchitectPromptResponseBrowserMessage { type: "architect_prompt_response"; sessionId: string; promptId: string; answer?: string; cancelled?: boolean; } export interface PromptResponseBrowserMessage { type: "prompt_response"; sessionId: string; promptId: string; answer?: string; cancelled?: boolean; source: string; } export interface RoleSetBrowserMessage { type: "role_set"; sessionId: string; role: string; modelId: string; } export interface RolePresetLoadBrowserMessage { type: "role_preset_load"; sessionId: string; presetName: string; } export interface RolePresetSaveBrowserMessage { type: "role_preset_save"; sessionId: string; presetName: string; } export interface RolePresetDeleteBrowserMessage { type: "role_preset_delete"; sessionId: string; presetName: string; } export interface RequestRolesBrowserMessage { type: "request_roles"; sessionId: string; } /** * Browser → server: the user invoked a Phase-1 module action / requested * row data. Server forwards via `piGateway.sendToSession` to the bridge, * which re-emits as `pi.events.emit(event, { ...params, action, _reply })`. * See change: add-extension-ui-modal. */ /** * Browser → server: declares which session a browser is currently displaying * (typically when the URL is `/session/:id`). The server uses this to gate * unread-trigger evaluation and to clear the unread bit when an unread session * is opened. Browsers SHALL re-send `session_view` for the currently-displayed * session on every WebSocket reconnect so server-side state stays coherent. * See change: session-card-unread-stripes. */ export interface SessionViewBrowserMessage { type: "session_view"; sessionId: string; } /** * Browser → server: declares the browser is no longer displaying the session * (e.g. user navigated away from `/session/:id`). * See change: session-card-unread-stripes. */ export interface SessionUnviewBrowserMessage { type: "session_unview"; sessionId: string; } export interface UiManagementBrowserMessage { type: "ui_management"; sessionId: string; action: string; event: string; params?: Record; } export type BrowserToServerMessage = | SubscribeMessage | UnsubscribeMessage | BrowserExtensionUiResponseMessage | SendPromptToBrowserMessage | AbortToBrowserMessage | RequestCommandsToBrowserMessage | FetchContentMessage | ListFilesToBrowserMessage | OpenSpecRefreshBrowserMessage | RenameSessionBrowserMessage | RequestModelsBrowserMessage | RequestProvidersBrowserMessage | SetThinkingLevelBrowserMessage | SetModelBrowserMessage | ShutdownBrowserMessage | ListSessionsBrowserMessage | ResumeSessionBrowserMessage | HideSessionBrowserMessage | UnhideSessionBrowserMessage | SpawnSessionBrowserMessage | AttachProposalBrowserMessage | DetachProposalBrowserMessage | ReorderSessionsBrowserMessage | PinDirectoryMessage | UnpinDirectoryMessage | ReorderPinnedDirsMessage | CreateWorkspaceMessage | RenameWorkspaceMessage | DeleteWorkspaceMessage | SetWorkspaceCollapsedMessage | AddFolderToWorkspaceMessage | RemoveFolderFromWorkspaceMessage | ReorderWorkspaceFoldersMessage | ReorderWorkspacesMessage | OpenSpecBulkArchiveBrowserMessage | CreateTerminalBrowserMessage | KillTerminalBrowserMessage | RenameTerminalBrowserMessage | FlowControlBrowserMessage | ForceKillBrowserMessage | FlowManagementBrowserMessage | ArchitectPromptResponseBrowserMessage | PromptResponseBrowserMessage | RoleSetBrowserMessage | RolePresetLoadBrowserMessage | RolePresetSaveBrowserMessage | RolePresetDeleteBrowserMessage | RequestRolesBrowserMessage | UiManagementBrowserMessage | SessionViewBrowserMessage | SessionUnviewBrowserMessage | KillProcessBrowserMessage | ClearSteeringQueueFromBrowserMessage | ClearFollowupSlotFromBrowserMessage | EditFollowupSlotFromBrowserMessage | PromoteFollowupEntryFromBrowserMessage | RemoveFollowupEntryFromBrowserMessage | EditFollowupEntryFromBrowserMessage | PluginActionMessage;