import type { MessageCost, TurnMessage } from '../api/chat/types'; // ── Envelope ── export type SidecarMessage = SidecarCommand | SidecarEvent; // ── Commands (API server → sidecar) ── export type SidecarCommand = | { type: 'ping'; id: string } | { type: 'state:sync'; id: string } // Proxy | { type: 'proxy:secret'; id: string } // Claude Code | { type: 'claude:spawn'; id: string; params: ClaudeSpawnParams } | { type: 'claude:spawn-streaming'; id: string; params: ClaudeSpawnStreamingParams } | { type: 'claude:kill'; id: string; sessionKey: string } | { type: 'claude:interrupt'; id: string; sessionKey: string } | { type: 'claude:clear-session'; id: string; sessionKey: string } // OpenCode — drive a turn via `opencode run … --format json` (tools re-anchored to cwd via --dir) | { type: 'opencode:run-streaming'; id: string; params: OpenCodeRunParams } | { type: 'opencode:kill'; id: string; sessionKey: string } // VNC | { type: 'vnc:start'; id: string; params: VncStartParams } // Provision the VNC password without starting a server — the UI needs it before it can connect | { type: 'vnc:ensure-password'; id: string; email: string } | { type: 'vnc:stop'; id: string; email: string } | { type: 'vnc:status'; id: string; email: string }; // ── Responses/Events (sidecar → API server) ── export type SidecarEvent = | { type: 'pong'; id: string } | { type: 'state:sync'; id: string; state: ClaudeState } | { type: 'proxy:secret'; id: string; secret: string } // Claude Code | { type: 'claude:spawned'; id: string; sessionKey: string } // A finished, browser-facing turn message. The agent has already committed it to chat_session_events // and `seq` is its cursor id there; officer relays it verbatim. No `seq` means it is not durable — // an `assistant:delta` (superseded by the text that follows) or a message whose write failed. | { type: 'claude:message'; sessionKey: string; msg: TurnMessage; seq?: number } | { type: 'claude:result'; id: string; result: ClaudeCodeResult } | { type: 'claude:error'; id: string; error: string } | { type: 'claude:killed'; id: string } | { type: 'claude:interrupted'; id: string } | { type: 'claude:session-cleared'; id: string } // VNC | { type: 'vnc:started'; id: string; port: number; display: number } | { type: 'vnc:password'; id: string; password: string } | { type: 'vnc:stopped'; id: string } | { type: 'vnc:status'; id: string; session: VncSessionInfo | null } | { type: 'vnc:error'; id: string; error: string } // Email | { type: 'email:new'; userEmail: string } // OpenCode — the sidecar reports where its `opencode serve` is listening (random port) on connect | { type: 'opencode:server'; port: number } // OpenCode turn streaming (analog of claude:*): spawned ack, per-message stream, session id report | { type: 'opencode:spawned'; id: string; sessionKey: string } // Same contract as `claude:message`: a finished turn message the sidecar has already committed to // chat_session_events, plus the cursor id it landed on. Officer relays it and folds it into its // in-memory transcript; it does not translate or persist. | { type: 'opencode:message'; sessionKey: string; msg: TurnMessage; seq?: number } | { type: 'opencode:session'; sessionKey: string; sessionId: string } | { type: 'opencode:error'; id: string; error: string } // Music — the sidecar reports where its audio-streaming HTTP server is listening (random port) on connect | { type: 'music:server'; port: number } // Vault — the sidecar reports where its Vaultwarden reverse-proxy HTTP/WS server is listening on connect | { type: 'vault:server'; port: number } // slskd — the sidecar reports where its slskd reverse-proxy HTTP server is listening (random port) on connect | { type: 'slskd:server'; port: number } // Generic | { type: 'error'; id?: string; error: string }; // ── Claude sidecar state ── export type ClaudeState = { proxySecret: string; claudeSessions: Record; // sessionKey → Claude Code session_id }; // ── Param types ── export type ClaudeSpawnParams = { userId: number; email: string; username: string; prompt: string; sessionKey: string; model?: string; cwd?: string; }; export type ClaudeSpawnStreamingParams = { userId: number; email: string; username: string; prompt: string; sessionKey: string; cwd?: string; model?: string; resumeSessionId?: string; // resume this Claude session uuid (from the /chat session list) // Whether turn output should be committed to chat_session_events (default true). A chat session wants // it — that is what survives an officer restart. A pipeline step does not: its sessionKey is a throwaway // uuid no browser will ever replay, and the job's own event log is its record. durable?: boolean; }; export type ClaudeCodeResult = { text: string; sessionId: string; model: string; cost: MessageCost; }; // ── OpenCode turn params ── export type OpenCodeRunParams = { sessionKey: string; prompt: string; cwd?: string; // passed to `opencode run --dir` — hard-re-anchors tools to this directory model?: string; // `providerID/modelID` (e.g. opencode/claude-haiku-4-5); passed to --model verbatim resumeSessionId?: string; // OpenCode `ses_…` id to continue (`--session`) durable?: boolean; // commit turn output to chat_session_events (default true) — see ClaudeSpawnStreamingParams }; // ── VNC types ── export type VncStartParams = { email: string; username: string; resolution?: string; }; export type VncSessionInfo = { email: string; display: number; port: number; pid: number; alive: boolean; }; // ── PTY types ── export type PtyInitConfig = { sessionId: string; shell?: { command: string; args?: string[] }; cwd?: string; homeDir?: string; userLabel?: string; host?: boolean; cols?: number; rows?: number; env?: Record; }; // PTY commands (API → PTY sidecar) export type PtyCommand = | { type: 'pty:init'; id: string; sessionId: string; config: PtyInitConfig } | { type: 'pty:input'; id: string; sessionId: string; data: string } | { type: 'pty:resize'; id: string; sessionId: string; cols: number; rows: number } | { type: 'pty:close'; id: string; sessionId: string }; // PTY events (PTY sidecar → API) export type PtyEvent = | { type: 'pty:ready'; id: string; sessionId: string } | { type: 'pty:output'; sessionId: string; data: string } | { type: 'pty:exit'; sessionId: string; exitCode: number; signal?: number };