// Minimal typed subset vendored from `codex app-server generate-ts`. // Keep names and field shapes aligned with the official app-server protocol. import type { CodexReasoningEffort, ServiceTier } from "../shared/types" export type CodexRequestId = string | number export interface JsonRpcResponse { id: CodexRequestId result?: TResult error?: { code?: number message?: string } } export interface InitializeParams { clientInfo: { name: string title: string version: string } capabilities: { experimentalApi: boolean } } export interface ThreadStartParams { model?: string | null cwd?: string | null serviceTier?: ServiceTier | null approvalPolicy?: "never" | "on-request" | "on-failure" | "untrusted" | null sandbox?: "read-only" | "workspace-write" | "danger-full-access" | null experimentalRawEvents: boolean persistExtendedHistory: boolean } export interface ThreadResumeParams { threadId: string model?: string | null cwd?: string | null serviceTier?: ServiceTier | null approvalPolicy?: "never" | "on-request" | "on-failure" | "untrusted" | null sandbox?: "read-only" | "workspace-write" | "danger-full-access" | null persistExtendedHistory: boolean } export interface ThreadForkParams { threadId: string model?: string | null cwd?: string | null serviceTier?: ServiceTier | null approvalPolicy?: "never" | "on-request" | "on-failure" | "untrusted" | null sandbox?: "read-only" | "workspace-write" | "danger-full-access" | null ephemeral?: boolean persistExtendedHistory: boolean } export interface TextUserInput { type: "text" text: string text_elements: [] } /** * Structured skill invocation: the server resolves `path` by exact match * against its own discovered+enabled skills and injects the SKILL.md contents * into the turn. Unmatched paths are silently skipped by codex, which is why * Kanna also appends a `` failsafe to the text item. */ export interface SkillUserInput { type: "skill" name: string path: string } export type CodexUserInput = TextUserInput | SkillUserInput export interface CollaborationMode { mode: "default" | "plan" settings: { model: string | null reasoning_effort: ReasoningEffort | null developer_instructions: string | null } } export type ReasoningEffort = CodexReasoningEffort export interface TurnStartParams { threadId: string input: CodexUserInput[] approvalPolicy?: "never" | "on-request" | "on-failure" | "untrusted" | null model?: string | null effort?: ReasoningEffort | null serviceTier?: ServiceTier | null collaborationMode?: CollaborationMode | null } export interface TurnInterruptParams { threadId: string turnId: string } /** `skills/list` (codex >= 0.73). Older servers answer with "method not found". */ export interface SkillsListParams { cwds: string[] forceReload?: boolean } export interface CodexSkillInterfaceMetadata { displayName?: string shortDescription?: string defaultPrompt?: string } export interface CodexSkillMetadata { name: string description: string shortDescription?: string interface?: CodexSkillInterfaceMetadata | null /** Absolute path to the SKILL.md file itself — the resolution key for SkillUserInput. */ path: string scope: "user" | "repo" | "system" | "admin" enabled: boolean } export interface SkillsListResponse { data: { cwd: string skills: CodexSkillMetadata[] errors?: { path: string; message: string }[] }[] } export interface ThreadSummary { id: string } export interface ThreadStartResponse { thread: ThreadSummary model: string reasoningEffort: ReasoningEffort | null } export type ThreadResumeResponse = ThreadStartResponse export type ThreadForkResponse = ThreadStartResponse export interface TurnSummary { id: string status: "inProgress" | "completed" | "failed" | "interrupted" error: { message?: string } | null } export interface TurnStartResponse { turn: TurnSummary } export interface ThreadStartedNotification { thread: ThreadSummary } export interface TurnStartedNotification { threadId: string turn: TurnSummary } export interface TurnCompletedNotification { threadId: string turn: TurnSummary } export interface TurnPlanStep { step: string status: "pending" | "inProgress" | "completed" } export interface TurnPlanUpdatedNotification { threadId: string turnId: string explanation: string | null plan: TurnPlanStep[] } export interface PlanDeltaNotification { threadId: string turnId: string itemId: string delta: string } export interface ContextCompactedNotification { threadId: string turnId: string } export interface TokenUsageCounter { input_tokens?: number inputTokens?: number cached_input_tokens?: number cachedInputTokens?: number output_tokens?: number outputTokens?: number reasoning_output_tokens?: number reasoningOutputTokens?: number total_tokens?: number totalTokens?: number } export interface ThreadTokenUsageUpdatedNotification { threadId: string turnId: string tokenUsage: { total_token_usage?: TokenUsageCounter total?: TokenUsageCounter last_token_usage?: TokenUsageCounter last?: TokenUsageCounter model_context_window?: number modelContextWindow?: number } } export interface ToolRequestUserInputOption { label: string description?: string | null } export interface ToolRequestUserInputQuestion { id: string header: string question: string isOther: boolean isSecret: boolean options: ToolRequestUserInputOption[] | null } export interface ToolRequestUserInputParams { threadId: string turnId: string itemId: string questions: ToolRequestUserInputQuestion[] } export interface ToolRequestUserInputResponse { answers: Record } export interface CommandExecutionRequestApprovalParams { threadId: string turnId: string itemId: string approvalId?: string | null reason?: string | null command?: string | null cwd?: string | null } export interface FileChangeRequestApprovalParams { threadId: string turnId: string itemId: string reason?: string | null grantRoot?: string | null } export type CommandExecutionApprovalDecision = | "accept" | "acceptForSession" | "decline" | "cancel" export type FileChangeApprovalDecision = | "accept" | "acceptForSession" | "decline" | "cancel" export interface CommandExecutionRequestApprovalResponse { decision: CommandExecutionApprovalDecision } export interface FileChangeRequestApprovalResponse { decision: FileChangeApprovalDecision } export interface ToolRequestUserInputRequest { id: CodexRequestId method: "item/tool/requestUserInput" params: ToolRequestUserInputParams } export interface DynamicToolCallParams { threadId: string turnId: string callId: string tool: string arguments: Record | unknown[] | string | number | boolean | null } export interface DynamicToolCallOutputContentItem { type: "inputText" | "inputImage" text?: string imageUrl?: string } export interface DynamicToolCallResponse { contentItems: DynamicToolCallOutputContentItem[] success: boolean } export interface DynamicToolCallRequest { id: CodexRequestId method: "item/tool/call" params: DynamicToolCallParams } export interface CommandExecutionRequestApprovalRequest { id: CodexRequestId method: "item/commandExecution/requestApproval" params: CommandExecutionRequestApprovalParams } export interface FileChangeRequestApprovalRequest { id: CodexRequestId method: "item/fileChange/requestApproval" params: FileChangeRequestApprovalParams } export type ServerRequest = | ToolRequestUserInputRequest | DynamicToolCallRequest | CommandExecutionRequestApprovalRequest | FileChangeRequestApprovalRequest export interface UserMessageItem { type: "userMessage" id: string content: Array<{ type: "text" text: string text_elements: [] }> } export interface ReasoningItem { type: "reasoning" id: string summary: unknown[] content: unknown[] } export interface AgentMessageItem { type: "agentMessage" id: string text: string phase?: string } export interface PlanItem { type: "plan" id: string text: string } export interface CommandExecutionItem { type: "commandExecution" id: string command: string cwd?: string processId?: string status: "inProgress" | "completed" | "failed" | "declined" aggregatedOutput?: string | null exitCode?: number | null durationMs?: number | null } export interface McpToolCallItem { type: "mcpToolCall" id: string server: string tool: string arguments?: Record | null result?: { content?: unknown[] structuredContent?: unknown } | null error?: { message?: string } | null status: "inProgress" | "completed" | "failed" } export interface DynamicToolCallItem { type: "dynamicToolCall" id: string tool: string arguments?: Record | unknown[] | string | number | boolean | null status: "inProgress" | "completed" | "failed" contentItems?: DynamicToolCallOutputContentItem[] | null success?: boolean | null durationMs?: number | null } export interface CollabAgentToolCallItem { type: "collabAgentToolCall" id: string tool: "spawnAgent" | "sendInput" | "resumeAgent" | "wait" | "closeAgent" status: "inProgress" | "completed" | "failed" senderThreadId: string receiverThreadIds: string[] prompt?: string | null agentsStates?: Record | null } export interface WebSearchItem { type: "webSearch" id: string query: string action?: { type?: string query?: string queries?: string[] } | null } export interface FileChangeItem { type: "fileChange" id: string changes: Array<{ path: string kind: | "add" | "delete" | "update" | { type: "add" | "delete" | "update" move_path?: string | null } diff?: string | null }> status: "inProgress" | "completed" | "failed" | "declined" } export interface ErrorItem { type: "error" id: string message: string } export type ThreadItem = | UserMessageItem | ReasoningItem | AgentMessageItem | PlanItem | CommandExecutionItem | McpToolCallItem | DynamicToolCallItem | CollabAgentToolCallItem | WebSearchItem | FileChangeItem | ErrorItem export interface ItemStartedNotification { item: ThreadItem threadId: string turnId: string } export interface ItemCompletedNotification { item: ThreadItem threadId: string turnId: string } export interface ErrorNotification { error: { message: string codexErrorInfo?: string additionalDetails?: unknown } willRetry: boolean threadId?: string turnId?: string } export interface CodexRateLimitWindow { usedPercent?: number windowDurationMins?: number | null resetsAt?: number | null } export interface CodexRateLimitSnapshot { limitId?: string | null limitName?: string | null primary?: CodexRateLimitWindow | null secondary?: CodexRateLimitWindow | null credits?: { hasCredits?: boolean; unlimited?: boolean; balance?: string | null } | null planType?: string | null } export interface AccountRateLimitsUpdatedNotification { rateLimits: CodexRateLimitSnapshot } export interface GetAccountRateLimitsResponse { rateLimits: CodexRateLimitSnapshot rateLimitsByLimitId?: Record | null } export type ServerNotification = | { method: "thread/started"; params: ThreadStartedNotification } | { method: "thread/tokenUsage/updated"; params: ThreadTokenUsageUpdatedNotification } | { method: "turn/started"; params: TurnStartedNotification } | { method: "turn/completed"; params: TurnCompletedNotification } | { method: "turn/plan/updated"; params: TurnPlanUpdatedNotification } | { method: "item/started"; params: ItemStartedNotification } | { method: "item/completed"; params: ItemCompletedNotification } | { method: "item/plan/delta"; params: PlanDeltaNotification } | { method: "thread/compacted"; params: ContextCompactedNotification } | { method: "account/rateLimits/updated"; params: AccountRateLimitsUpdatedNotification } | { method: "error"; params: ErrorNotification } export function isJsonRpcResponse(value: unknown): value is JsonRpcResponse { return Boolean(value) && typeof value === "object" && "id" in (value as Record) && ("result" in (value as Record) || "error" in (value as Record)) && !("method" in (value as Record)) } export function isServerRequest(value: unknown): value is ServerRequest { if (!value || typeof value !== "object") return false const candidate = value as Record if (typeof candidate.method !== "string" || !("id" in candidate)) return false return candidate.method === "item/tool/requestUserInput" || candidate.method === "item/tool/call" || candidate.method === "item/commandExecution/requestApproval" || candidate.method === "item/fileChange/requestApproval" } export function isServerNotification(value: unknown): value is ServerNotification { if (!value || typeof value !== "object") return false const candidate = value as Record if (typeof candidate.method !== "string" || "id" in candidate) return false return candidate.method === "thread/started" || candidate.method === "thread/tokenUsage/updated" || candidate.method === "turn/started" || candidate.method === "turn/completed" || candidate.method === "turn/plan/updated" || candidate.method === "item/started" || candidate.method === "item/completed" || candidate.method === "item/plan/delta" || candidate.method === "thread/compacted" || candidate.method === "account/rateLimits/updated" || candidate.method === "error" }