/** * RPC protocol — typed operation envelope for the new WebSocket surface. * * Pattern adapted from t3code's orchestration RPC. Replaces the engine's * current ~50 hand-coded `if (req.method === ... && req.url === ...)` * branches in src/studio/server.ts with a typed operation discriminator * + per-operation request/response shapes. * * This commit ships the protocol + an in-memory handler so consumers can * use the new surface today via the in-process API. Mounting on the * WebSocket server.ts upgrade path is a separate follow-up commit * (the existing HTTP router has many entanglements with workspace state * that aren't worth breaking on the same diff). The protocol is locked * here so the mount is mechanical when it lands. */ import { z } from "zod"; import type { SessionId, ThreadId, TurnId } from "../contracts/ids.js"; import type { ProviderRuntimeEvent } from "../contracts/provider-runtime.js"; export type RpcOperation = "dispatchCommand" | "subscribeThread" | "replayEvents" | "subscribeShell" | "getTurnDiff"; export interface DispatchCommandRequest { readonly op: "dispatchCommand"; readonly requestId: string; readonly sessionId: SessionId; readonly threadId?: ThreadId; readonly command: "start" | "sendTurn" | "interrupt" | "shutdown"; /** Per-command payload. For sendTurn: { turnId, prompt, attachments? }. */ readonly payload?: Record; } export interface SubscribeThreadRequest { readonly op: "subscribeThread"; readonly requestId: string; readonly sessionId: SessionId; readonly threadId?: ThreadId; /** Optional cursor — start from this seq onward. Default: live tail only. */ readonly fromSeq?: number; } export interface ReplayEventsRequest { readonly op: "replayEvents"; readonly requestId: string; readonly sessionId: SessionId; readonly fromSeq?: number; } export interface SubscribeShellRequest { readonly op: "subscribeShell"; readonly requestId: string; readonly sessionId: SessionId; readonly turnId?: TurnId; } export interface GetTurnDiffRequest { readonly op: "getTurnDiff"; readonly requestId: string; readonly sessionId: SessionId; readonly turnId: TurnId; } export type RpcRequest = DispatchCommandRequest | SubscribeThreadRequest | ReplayEventsRequest | SubscribeShellRequest | GetTurnDiffRequest; export interface RpcAck { readonly kind: "ack"; readonly requestId: string; } export interface RpcEvent { readonly kind: "event"; readonly requestId: string; readonly event: ProviderRuntimeEvent; } export interface RpcEnd { readonly kind: "end"; readonly requestId: string; readonly reason?: string; } export interface RpcError { readonly kind: "error"; readonly requestId: string; readonly error: string; readonly errorTag?: string; } export interface RpcResult { readonly kind: "result"; readonly requestId: string; readonly result: unknown; } export type RpcResponse = RpcAck | RpcEvent | RpcEnd | RpcError | RpcResult; export declare const dispatchCommandSchema: z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"dispatchCommand">; threadId: z.ZodOptional; command: z.ZodEnum<["start", "sendTurn", "interrupt", "shutdown"]>; payload: z.ZodOptional>; }, "strip", z.ZodTypeAny, { command: "start" | "interrupt" | "shutdown" | "sendTurn"; sessionId: string; requestId: string; op: "dispatchCommand"; payload?: Record | undefined; threadId?: string | undefined; }, { command: "start" | "interrupt" | "shutdown" | "sendTurn"; sessionId: string; requestId: string; op: "dispatchCommand"; payload?: Record | undefined; threadId?: string | undefined; }>; export declare const subscribeThreadSchema: z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"subscribeThread">; threadId: z.ZodOptional; fromSeq: z.ZodOptional; }, "strip", z.ZodTypeAny, { sessionId: string; requestId: string; op: "subscribeThread"; threadId?: string | undefined; fromSeq?: number | undefined; }, { sessionId: string; requestId: string; op: "subscribeThread"; threadId?: string | undefined; fromSeq?: number | undefined; }>; export declare const replayEventsSchema: z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"replayEvents">; fromSeq: z.ZodOptional; }, "strip", z.ZodTypeAny, { sessionId: string; requestId: string; op: "replayEvents"; fromSeq?: number | undefined; }, { sessionId: string; requestId: string; op: "replayEvents"; fromSeq?: number | undefined; }>; export declare const subscribeShellSchema: z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"subscribeShell">; turnId: z.ZodOptional; }, "strip", z.ZodTypeAny, { sessionId: string; requestId: string; op: "subscribeShell"; turnId?: string | undefined; }, { sessionId: string; requestId: string; op: "subscribeShell"; turnId?: string | undefined; }>; export declare const getTurnDiffSchema: z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"getTurnDiff">; turnId: z.ZodString; }, "strip", z.ZodTypeAny, { sessionId: string; turnId: string; requestId: string; op: "getTurnDiff"; }, { sessionId: string; turnId: string; requestId: string; op: "getTurnDiff"; }>; export declare const rpcRequestSchema: z.ZodDiscriminatedUnion<"op", [z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"dispatchCommand">; threadId: z.ZodOptional; command: z.ZodEnum<["start", "sendTurn", "interrupt", "shutdown"]>; payload: z.ZodOptional>; }, "strip", z.ZodTypeAny, { command: "start" | "interrupt" | "shutdown" | "sendTurn"; sessionId: string; requestId: string; op: "dispatchCommand"; payload?: Record | undefined; threadId?: string | undefined; }, { command: "start" | "interrupt" | "shutdown" | "sendTurn"; sessionId: string; requestId: string; op: "dispatchCommand"; payload?: Record | undefined; threadId?: string | undefined; }>, z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"subscribeThread">; threadId: z.ZodOptional; fromSeq: z.ZodOptional; }, "strip", z.ZodTypeAny, { sessionId: string; requestId: string; op: "subscribeThread"; threadId?: string | undefined; fromSeq?: number | undefined; }, { sessionId: string; requestId: string; op: "subscribeThread"; threadId?: string | undefined; fromSeq?: number | undefined; }>, z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"replayEvents">; fromSeq: z.ZodOptional; }, "strip", z.ZodTypeAny, { sessionId: string; requestId: string; op: "replayEvents"; fromSeq?: number | undefined; }, { sessionId: string; requestId: string; op: "replayEvents"; fromSeq?: number | undefined; }>, z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"subscribeShell">; turnId: z.ZodOptional; }, "strip", z.ZodTypeAny, { sessionId: string; requestId: string; op: "subscribeShell"; turnId?: string | undefined; }, { sessionId: string; requestId: string; op: "subscribeShell"; turnId?: string | undefined; }>, z.ZodObject<{ requestId: z.ZodString; sessionId: z.ZodString; } & { op: z.ZodLiteral<"getTurnDiff">; turnId: z.ZodString; }, "strip", z.ZodTypeAny, { sessionId: string; turnId: string; requestId: string; op: "getTurnDiff"; }, { sessionId: string; turnId: string; requestId: string; op: "getTurnDiff"; }>]>; export declare function parseRpcRequest(raw: unknown): RpcRequest; export declare function safeParseRpcRequest(raw: unknown): { ok: true; req: RpcRequest; } | { ok: false; error: string; };