/** * RPC protocol types for headless operation. * * Commands are sent as JSON lines on stdin. * Responses and events are emitted as JSON lines on stdout. */ import type { AgentMessage, AgentToolResult, ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction"; import type { Effort, ImageContent, Model } from "@oh-my-pi/pi-ai"; import type { BashResult } from "../../exec/bash-executor"; import type { ContextUsage } from "../../extensibility/extensions/types"; import type { SessionStats } from "../../session/agent-session"; import type { TodoPhase } from "../../tools/todo-write"; // ============================================================================ // RPC Commands (stdin) // ============================================================================ export type RpcCommand = // Prompting | { id?: string; type: "prompt"; message: string; images?: ImageContent[]; streamingBehavior?: "steer" | "followUp" } | { id?: string; type: "steer"; message: string; images?: ImageContent[] } | { id?: string; type: "follow_up"; message: string; images?: ImageContent[] } | { id?: string; type: "abort" } | { id?: string; type: "abort_and_prompt"; message: string; images?: ImageContent[] } | { id?: string; type: "new_session"; parentSession?: string } // State | { id?: string; type: "get_state" } | { id?: string; type: "set_todos"; phases: TodoPhase[] } | { id?: string; type: "set_host_tools"; tools: RpcHostToolDefinition[] } | { id?: string; type: "set_host_uri_schemes"; schemes: RpcHostUriSchemeDefinition[] } // Model | { id?: string; type: "set_model"; provider: string; modelId: string } | { id?: string; type: "cycle_model" } | { id?: string; type: "get_available_models" } // Thinking | { id?: string; type: "set_thinking_level"; level: ThinkingLevel } | { id?: string; type: "cycle_thinking_level" } // Queue modes | { id?: string; type: "set_steering_mode"; mode: "all" | "one-at-a-time" } | { id?: string; type: "set_follow_up_mode"; mode: "all" | "one-at-a-time" } | { id?: string; type: "set_interrupt_mode"; mode: "immediate" | "wait" } // Compaction | { id?: string; type: "compact"; customInstructions?: string } | { id?: string; type: "set_auto_compaction"; enabled: boolean } // Retry | { id?: string; type: "set_auto_retry"; enabled: boolean } | { id?: string; type: "abort_retry" } // Bash | { id?: string; type: "bash"; command: string } | { id?: string; type: "abort_bash" } // Session | { id?: string; type: "get_session_stats" } | { id?: string; type: "export_html"; outputPath?: string } | { id?: string; type: "switch_session"; sessionPath: string } | { id?: string; type: "branch"; entryId: string } | { id?: string; type: "get_branch_messages" } | { id?: string; type: "get_last_assistant_text" } | { id?: string; type: "set_session_name"; name: string } | { id?: string; type: "handoff"; customInstructions?: string } // Messages | { id?: string; type: "get_messages" } // Login | { id?: string; type: "get_login_providers" } | { id?: string; type: "login"; providerId: string }; // ============================================================================ // RPC State // ============================================================================ export interface RpcSessionState { model?: Model; thinkingLevel: ThinkingLevel | undefined; isStreaming: boolean; isCompacting: boolean; steeringMode: "all" | "one-at-a-time"; followUpMode: "all" | "one-at-a-time"; interruptMode: "immediate" | "wait"; sessionFile?: string; sessionId: string; sessionName?: string; autoCompactionEnabled: boolean; messageCount: number; queuedMessageCount: number; todoPhases: TodoPhase[]; /** For session dump / export (plain-text parity with /dump). */ systemPrompt?: string[]; dumpTools?: Array<{ name: string; description: string; parameters: unknown }>; /** Current context window usage. Null tokens/percent when unknown (e.g. right after compaction). */ contextUsage?: ContextUsage; } export interface RpcHandoffResult { savedPath?: string; } // ============================================================================ // RPC Responses (stdout) // ============================================================================ // Success responses with data export type RpcResponse = // Prompting (async - events follow) | { id?: string; type: "response"; command: "prompt"; success: true } | { id?: string; type: "response"; command: "steer"; success: true } | { id?: string; type: "response"; command: "follow_up"; success: true } | { id?: string; type: "response"; command: "abort"; success: true } | { id?: string; type: "response"; command: "abort_and_prompt"; success: true } | { id?: string; type: "response"; command: "new_session"; success: true; data: { cancelled: boolean } } // State | { id?: string; type: "response"; command: "get_state"; success: true; data: RpcSessionState } | { id?: string; type: "response"; command: "set_todos"; success: true; data: { todoPhases: TodoPhase[] } } | { id?: string; type: "response"; command: "set_host_tools"; success: true; data: { toolNames: string[] } } | { id?: string; type: "response"; command: "set_host_uri_schemes"; success: true; data: { schemes: string[] } } // Model | { id?: string; type: "response"; command: "set_model"; success: true; data: Model; } | { id?: string; type: "response"; command: "cycle_model"; success: true; data: { model: Model; thinkingLevel: ThinkingLevel | undefined; isScoped: boolean } | null; } | { id?: string; type: "response"; command: "get_available_models"; success: true; data: { models: Model[] }; } // Thinking | { id?: string; type: "response"; command: "set_thinking_level"; success: true } | { id?: string; type: "response"; command: "cycle_thinking_level"; success: true; data: { level: Effort } | null; } // Queue modes | { id?: string; type: "response"; command: "set_steering_mode"; success: true } | { id?: string; type: "response"; command: "set_follow_up_mode"; success: true } | { id?: string; type: "response"; command: "set_interrupt_mode"; success: true } // Compaction | { id?: string; type: "response"; command: "compact"; success: true; data: CompactionResult } | { id?: string; type: "response"; command: "set_auto_compaction"; success: true } // Retry | { id?: string; type: "response"; command: "set_auto_retry"; success: true } | { id?: string; type: "response"; command: "abort_retry"; success: true } // Bash | { id?: string; type: "response"; command: "bash"; success: true; data: BashResult } | { id?: string; type: "response"; command: "abort_bash"; success: true } // Session | { id?: string; type: "response"; command: "get_session_stats"; success: true; data: SessionStats } | { id?: string; type: "response"; command: "export_html"; success: true; data: { path: string } } | { id?: string; type: "response"; command: "switch_session"; success: true; data: { cancelled: boolean } } | { id?: string; type: "response"; command: "branch"; success: true; data: { text: string; cancelled: boolean } } | { id?: string; type: "response"; command: "get_branch_messages"; success: true; data: { messages: Array<{ entryId: string; text: string }> }; } | { id?: string; type: "response"; command: "get_last_assistant_text"; success: true; data: { text: string | null }; } | { id?: string; type: "response"; command: "set_session_name"; success: true } | { id?: string; type: "response"; command: "handoff"; success: true; data: RpcHandoffResult | null } // Messages | { id?: string; type: "response"; command: "get_messages"; success: true; data: { messages: AgentMessage[] } } // Login | { id?: string; type: "response"; command: "get_login_providers"; success: true; data: { providers: Array<{ id: string; name: string; available: boolean; authenticated: boolean }> }; } | { id?: string; type: "response"; command: "login"; success: true; data: { providerId: string } } // Error response (any command can fail) | { id?: string; type: "response"; command: string; success: false; error: string }; // ============================================================================ // Extension UI Events (stdout) // ============================================================================ /** Emitted when an extension needs user input */ export type RpcExtensionUIRequest = | { type: "extension_ui_request"; id: string; method: "select"; title: string; options: string[]; timeout?: number } | { type: "extension_ui_request"; id: string; method: "confirm"; title: string; message: string; timeout?: number } | { type: "extension_ui_request"; id: string; method: "input"; title: string; placeholder?: string; timeout?: number; } | { type: "extension_ui_request"; id: string; method: "editor"; title: string; prefill?: string; promptStyle?: boolean; } | { type: "extension_ui_request"; id: string; method: "cancel"; targetId: string } | { type: "extension_ui_request"; id: string; method: "notify"; message: string; notifyType?: "info" | "warning" | "error"; } | { type: "extension_ui_request"; id: string; method: "setStatus"; statusKey: string; statusText: string | undefined; } | { type: "extension_ui_request"; id: string; method: "setWidget"; widgetKey: string; widgetLines: string[] | undefined; widgetPlacement?: "aboveEditor" | "belowEditor"; } | { type: "extension_ui_request"; id: string; method: "setTitle"; title: string } | { type: "extension_ui_request"; id: string; method: "set_editor_text"; text: string } | { type: "extension_ui_request"; id: string; method: "open_url"; url: string; instructions?: string }; // ============================================================================ // Host Tool Frames (bidirectional) // ============================================================================ export interface RpcHostToolDefinition { name: string; label?: string; description: string; parameters: Record; hidden?: boolean; } /** Emitted by the RPC server when it needs the host to execute a registered tool. */ export interface RpcHostToolCallRequest { type: "host_tool_call"; id: string; toolCallId: string; toolName: string; arguments: Record; } /** Emitted by the RPC server when a pending host tool call should be aborted. */ export interface RpcHostToolCancelRequest { type: "host_tool_cancel"; id: string; targetId: string; } /** Sent by the host to stream partial tool updates back to the RPC server. */ export interface RpcHostToolUpdate { type: "host_tool_update"; id: string; partialResult: AgentToolResult; } /** Sent by the host to complete a pending tool call. */ export interface RpcHostToolResult { type: "host_tool_result"; id: string; result: AgentToolResult; isError?: boolean; } // ============================================================================ // Host URI Frames (bidirectional) // ============================================================================ export interface RpcHostUriSchemeDefinition { /** URL scheme without trailing `://` (e.g. `db`, `notion`). */ scheme: string; /** Optional human-readable description for logs/diagnostics. */ description?: string; /** When true, the write tool is allowed to dispatch writes to this scheme. */ writable?: boolean; /** When true, downstream callers suppress hashline anchors for resolved content. */ immutable?: boolean; } export type RpcHostUriOperation = "read" | "write"; /** Emitted by the RPC server when it needs the host to satisfy a URI operation. */ export interface RpcHostUriRequest { type: "host_uri_request"; id: string; operation: RpcHostUriOperation; url: string; /** Present for write operations. */ content?: string; } /** Emitted by the RPC server when a pending URI request should be aborted. */ export interface RpcHostUriCancelRequest { type: "host_uri_cancel"; id: string; targetId: string; } /** Sent by the host to complete a pending URI request. */ export interface RpcHostUriResult { type: "host_uri_result"; id: string; /** * Required for successful `read` results. Ignored for `write` success. * Set on errors when a textual explanation accompanies `isError`. */ content?: string; /** Defaults to `text/plain` when omitted. */ contentType?: "text/markdown" | "application/json" | "text/plain"; /** Optional resolution notes propagated to the read tool. */ notes?: string[]; /** Overrides the scheme-level `immutable` flag for this single resolution. */ immutable?: boolean; /** When true, surface the result content as an error to the caller. */ isError?: boolean; /** Optional error message; preferred over `content` for error surfacing. */ error?: string; } // ============================================================================ // Extension UI Commands (stdin) // ============================================================================ /** Response to an extension UI request */ export type RpcExtensionUIResponse = | { type: "extension_ui_response"; id: string; value: string } | { type: "extension_ui_response"; id: string; confirmed: boolean } | { type: "extension_ui_response"; id: string; cancelled: true; timedOut?: boolean }; // ============================================================================ // Helper type for extracting command types // ============================================================================ export type RpcCommandType = RpcCommand["type"];