import { z } from "zod"; import { TerminalActivitySchema } from "./terminal-activity.js"; import { CLIENT_CAPS } from "./client-capabilities.js"; import { AGENT_LIFECYCLE_STATUSES } from "./agent-lifecycle.js"; import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from "./agent-title-limits.js"; import { AgentProviderSchema } from "./provider-manifest.js"; import { TOOL_CALL_ICON_NAMES } from "./agent-types.js"; import { WORKSPACE_LABEL_COLORS } from "./workspace-labels.js"; import { ChatCreateRequestSchema, ChatListRequestSchema, ChatInspectRequestSchema, ChatDeleteRequestSchema, ChatPostRequestSchema, ChatReadRequestSchema, ChatWaitRequestSchema, ChatCreateResponseSchema, ChatListResponseSchema, ChatInspectResponseSchema, ChatDeleteResponseSchema, ChatPostResponseSchema, ChatReadResponseSchema, ChatWaitResponseSchema, } from "./chat/rpc-schemas.js"; import { ScheduleCreateRequestSchema, ScheduleListRequestSchema, ScheduleInspectRequestSchema, ScheduleLogsRequestSchema, SchedulePauseRequestSchema, ScheduleResumeRequestSchema, ScheduleDeleteRequestSchema, ScheduleRunOnceRequestSchema, ScheduleUpdateRequestSchema, ScheduleCreateResponseSchema, ScheduleListResponseSchema, ScheduleInspectResponseSchema, ScheduleLogsResponseSchema, SchedulePauseResponseSchema, ScheduleResumeResponseSchema, ScheduleDeleteResponseSchema, ScheduleRunOnceResponseSchema, ScheduleUpdateResponseSchema, } from "./schedule/rpc-schemas.js"; import { LoopRunRequestSchema, LoopListRequestSchema, LoopInspectRequestSchema, LoopLogsRequestSchema, LoopStopRequestSchema, LoopRunResponseSchema, LoopListResponseSchema, LoopInspectResponseSchema, LoopLogsResponseSchema, LoopStopResponseSchema, } from "./loop/rpc-schemas.js"; import { BrowserAutomationExecuteRequestSchema, BrowserAutomationExecuteResponseSchema, } from "./browser-automation/rpc-schemas.js"; import { BrowserAutomationHostCapabilitySchema } from "./browser-automation/capabilities.js"; import { PaseoConfigRawSchema, PaseoLifecycleCommandRawSchema, PaseoMetadataGenerationEntrySchema, PaseoMetadataGenerationSchema, PaseoScriptEntryRawSchema, PaseoWorktreeConfigRawSchema, PaseoConfigRevisionSchema, ProjectConfigRpcErrorSchema, type PaseoConfigRaw, type PaseoConfigRevision, type PaseoMetadataGeneration, type PaseoMetadataGenerationEntry, type PaseoScriptEntryRaw, type ProjectConfigRpcError, } from "./paseo-config-schema.js"; export { PaseoConfigRawSchema, PaseoLifecycleCommandRawSchema, PaseoMetadataGenerationEntrySchema, PaseoMetadataGenerationSchema, PaseoScriptEntryRawSchema, PaseoWorktreeConfigRawSchema, type PaseoConfigRaw, type PaseoConfigRevision, type PaseoMetadataGeneration, type PaseoMetadataGenerationEntry, type PaseoScriptEntryRaw, type ProjectConfigRpcError, }; // --------------------------------------------------------------------------- // Mutable daemon config schemas (shared between server store and client) // --------------------------------------------------------------------------- const MutableDaemonProviderModelSchema = z .object({ id: z.string().min(1), label: z.string().min(1), description: z.string().optional(), isDefault: z.boolean().optional(), }) .passthrough(); const MutableDaemonProviderConfigSchema = z .object({ enabled: z.boolean().optional(), additionalModels: z.array(MutableDaemonProviderModelSchema).optional(), }) .passthrough(); const MutableStructuredGenerationProviderSchema = z .object({ provider: z.string().min(1), model: z.string().min(1).optional(), thinkingOptionId: z.string().min(1).optional(), }) .passthrough(); const MutableMetadataGenerationConfigSchema = z .object({ providers: z.array(MutableStructuredGenerationProviderSchema).default([]), }) .passthrough(); export const TerminalProfileSchema = z .object({ id: z.string(), name: z.string(), command: z.string(), args: z.array(z.string()).optional(), icon: z.string().optional(), }) .passthrough(); export type TerminalProfile = z.infer; /** * A named launch bundle: a provider plus the agent-config values a client would * otherwise set one control at a time. Field names mirror `AgentSessionConfig` * so applying a profile is a copy rather than a translation table. * * There is deliberately no system prompt here. `AgentSessionConfig.systemPrompt` * is creation-only, so a profile carrying one would apply when starting a new * agent and silently do nothing when applied to a running one. */ export const AgentProfileSchema = z .object({ id: z.string(), name: z.string(), /** A key into the client's icon registry, not a glyph. Unknown keys draw the default. */ icon: z.string().optional(), /** An identity colour name shared with host badges. Unknown values draw unthemed. */ color: z.string().optional(), provider: z.string(), model: z.string().optional(), modeId: z.string().optional(), thinkingOptionId: z.string().optional(), featureValues: z.record(z.string(), z.unknown()).optional(), /** Free text, surfaced to orchestrating agents by the `list_profiles` MCP tool. */ notes: z.string().optional(), }) .passthrough(); export type AgentProfile = z.infer; const MutableBrowserToolsConfigSchema = z .object({ enabled: z.boolean().default(false), }) .passthrough(); const MutableRelayConfigSchema = z .object({ enabled: z.boolean(), }) .passthrough(); export const PluginIdSchema = z.string().regex(/^[a-z][a-z0-9-]*$/); export const DirectoryPluginSourceSchema = z .object({ source: z.literal("directory"), path: z.string().min(1), enabled: z.boolean().optional(), }) .strict(); export const PluginSourceSchema = z.discriminatedUnion("source", [DirectoryPluginSourceSchema]); export type PluginSource = z.infer; export const AgentSkillSelectionSchema = z.discriminatedUnion("mode", [ z.object({ mode: z.literal("all") }).strict(), z.object({ mode: z.literal("custom"), skills: z.array(z.string()) }).strict(), ]); export type AgentSkillSelection = z.infer; export const MutableDaemonConfigSchema = z .object({ // COMPAT(relayConfig): added in v0.2.6, remove after 2027-01-31 when old daemons are unsupported. relay: MutableRelayConfigSchema.optional(), mcp: z .object({ enabled: z.boolean().optional(), injectIntoAgents: z.boolean(), }) .passthrough(), hostnames: z.union([z.literal(true), z.array(z.string())]).optional(), cors: z .object({ allowedOrigins: z.array(z.string()), }) .passthrough() .optional(), trustedProxies: z.union([z.literal(true), z.array(z.string())]).optional(), git: z .object({ maxProcessesPerSecond: z.number().int().positive(), maxProcessConcurrency: z.number().int().positive(), }) .optional(), app: z.object({ baseUrl: z.string() }).optional(), catalogRefreshTimeoutMs: z.number().int().positive().optional(), browserTools: MutableBrowserToolsConfigSchema.default({ enabled: false }), providers: z.record(z.string(), MutableDaemonProviderConfigSchema).default({}), metadataGeneration: MutableMetadataGenerationConfigSchema.default({ providers: [] }), autoArchiveAfterMerge: z.boolean().default(false), enableTerminalAgentHooks: z.boolean().default(false), appendSystemPrompt: z.string().default(""), terminalProfiles: z.array(TerminalProfileSchema).optional(), agentProfiles: z.array(AgentProfileSchema).optional(), skills: z.object({ selection: AgentSkillSelectionSchema.optional() }).strict().optional(), pluginsEnabled: z.boolean().optional(), plugins: z.record(PluginIdSchema, PluginSourceSchema).optional(), }) .passthrough(); export const MutableDaemonConfigPatchSchema = z .object({ relay: MutableRelayConfigSchema.partial().optional(), mcp: z.object({ injectIntoAgents: z.boolean().optional() }).passthrough().optional(), browserTools: MutableBrowserToolsConfigSchema.partial().optional(), providers: z .record(z.string(), MutableDaemonProviderConfigSchema.partial().passthrough()) .optional(), removeProviders: z.array(z.string().min(1)).optional(), metadataGeneration: MutableMetadataGenerationConfigSchema.partial().optional(), autoArchiveAfterMerge: z.boolean().optional(), enableTerminalAgentHooks: z.boolean().optional(), appendSystemPrompt: z.string().optional(), terminalProfiles: z.array(TerminalProfileSchema).optional(), agentProfiles: z.array(AgentProfileSchema).optional(), pluginsEnabled: z.boolean().optional(), plugins: z.record(PluginIdSchema, PluginSourceSchema).optional(), }) .partial() .passthrough(); export type MutableDaemonConfig = z.infer; export type MutableDaemonConfigPatch = z.infer; import type { AgentCapabilityFlags, AgentModelDefinition, AgentMode, AgentPermissionRequest, AgentPermissionResponse, AgentPersistenceHandle, ProviderStatus, AgentRuntimeInfo, AgentTimelineItem, AgentProviderNotice, ToolCallDetail, ToolCallTimelineItem, AgentUsage, } from "./agent-types.js"; export const AgentStatusSchema = z.enum(AGENT_LIFECYCLE_STATUSES); const AgentModeSchema: z.ZodType = z.object({ id: z.string(), label: z.string(), description: z.string().optional(), icon: z.string().optional(), colorTier: z.string().optional(), }); const ProviderStatusSchema: z.ZodType = z.enum([ "ready", "loading", "error", "unavailable", ]); const AgentSelectOptionSchema = z.object({ id: z.string(), label: z.string(), description: z.string().optional(), isDefault: z.boolean().optional(), metadata: z.record(z.string(), z.unknown()).optional(), }); const AgentProviderNoticeSchema: z.ZodType = z.discriminatedUnion("type", [ z.object({ type: z.literal("info"), message: z.string() }), z.object({ type: z.literal("warning"), message: z.string() }), z.object({ type: z.literal("error"), message: z.string() }), ]); export const AgentFeatureToggleSchema = z.object({ type: z.literal("toggle"), id: z.string(), label: z.string(), description: z.string().optional(), tooltip: z.string().optional(), icon: z.string().optional(), value: z.boolean(), }); export const AgentFeatureSelectSchema = z.object({ type: z.literal("select"), id: z.string(), label: z.string(), description: z.string().optional(), tooltip: z.string().optional(), icon: z.string().optional(), value: z.string().nullable(), options: z.array(AgentSelectOptionSchema), }); export const AgentFeatureSchema = z.discriminatedUnion("type", [ AgentFeatureToggleSchema, AgentFeatureSelectSchema, ]); const AgentModelDefinitionSchema = z.object({ provider: AgentProviderSchema, id: z.string(), aliases: z.array(z.string()).optional(), isSelectable: z.boolean().optional(), label: z.string(), description: z.string().optional(), isDefault: z.boolean().optional(), metadata: z.record(z.string(), z.unknown()).optional(), contextWindowMaxTokens: z.number().optional(), thinkingOptions: z.array(AgentSelectOptionSchema).optional(), defaultThinkingOptionId: z.string().optional(), }) satisfies z.ZodType; export const ProviderSnapshotEntrySchema = z.object({ provider: AgentProviderSchema, status: ProviderStatusSchema, enabled: z.boolean().optional().default(true), source: z.enum(["builtin", "custom"]).optional(), error: z.string().optional(), models: z.array(AgentModelDefinitionSchema).optional(), modes: z.array(AgentModeSchema).optional(), fetchedAt: z.string().optional(), label: z.string().optional(), description: z.string().optional(), defaultModeId: z.string().nullable().optional(), }); export const CompactProviderSnapshotModelSchema = AgentModelDefinitionSchema.omit({ provider: true, thinkingOptions: true, }).extend({ thinkingSet: z.number().int().nonnegative().optional(), }); export const ProviderSnapshotThinkingSetSchema = z.object({ options: z.array(AgentSelectOptionSchema), defaultOptionId: z.string().optional(), }); export const CompactProviderSnapshotEntrySchema = ProviderSnapshotEntrySchema.omit({ models: true, }).extend({ models: z.array(CompactProviderSnapshotModelSchema).optional(), }); export const CompactProviderSnapshotSchema = z.object({ entries: z.array(CompactProviderSnapshotEntrySchema), thinkingSets: z.array(ProviderSnapshotThinkingSetSchema), }); const AgentCapabilityFlagsSchema: z.ZodType = z .object({ supportsStreaming: z.boolean(), supportsSessionPersistence: z.boolean(), supportsSessionListing: z.boolean().optional(), supportsDynamicModes: z.boolean(), supportsMcpServers: z.boolean(), supportsReasoningStream: z.boolean(), supportsToolInvocations: z.boolean(), // COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X. supportsRewindConversation: z.boolean().optional().default(false), // COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X. supportsRewindFiles: z.boolean().optional().default(false), // COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X. supportsRewindBoth: z.boolean().optional().default(false), }) .catchall(z.boolean()); const AgentUsageSchema: z.ZodType = z.object({ inputTokens: z.number().optional(), cachedInputTokens: z.number().optional(), outputTokens: z.number().optional(), totalCostUsd: z.number().optional(), contextWindowMaxTokens: z.number().optional(), contextWindowUsedTokens: z.number().optional(), }); const McpStdioServerConfigSchema = z.object({ type: z.literal("stdio"), command: z.string(), args: z.array(z.string()).optional(), env: z.record(z.string(), z.string()).optional(), alwaysLoad: z.boolean().optional(), }); const McpHttpServerConfigSchema = z.object({ type: z.literal("http"), url: z.string(), headers: z.record(z.string(), z.string()).optional(), alwaysLoad: z.boolean().optional(), }); const McpSseServerConfigSchema = z.object({ type: z.literal("sse"), url: z.string(), headers: z.record(z.string(), z.string()).optional(), alwaysLoad: z.boolean().optional(), }); const McpServerConfigSchema = z.discriminatedUnion("type", [ McpStdioServerConfigSchema, McpHttpServerConfigSchema, McpSseServerConfigSchema, ]); const ProviderOptionsSchema = z.record(z.string(), z.json()); const McpToolRefSchema = z .object({ kind: z.literal("mcp"), server: z.string().trim().min(1), tool: z.string().trim().min(1), }) .strict(); const ToolPolicySchema = z .object({ preapproved: z.array(McpToolRefSchema), }) .strict(); const AgentSessionConfigSchema = z.object({ provider: AgentProviderSchema, cwd: z.string(), modeId: z.string().optional(), model: z.string().optional(), thinkingOptionId: z.string().optional(), featureValues: z.record(z.string(), z.unknown()).optional(), title: z.string().trim().min(1).max(MAX_EXPLICIT_AGENT_TITLE_CHARS).optional().nullable(), providerOptions: ProviderOptionsSchema.optional(), toolPolicy: ToolPolicySchema.optional(), systemPrompt: z.string().optional(), mcpServers: z.record(z.string(), McpServerConfigSchema).optional(), }); const AgentPermissionUpdateSchema = z.record(z.string(), z.unknown()); const AgentPermissionActionSchema = z.object({ id: z.string(), label: z.string(), behavior: z.enum(["allow", "deny"]), variant: z.enum(["primary", "secondary", "danger"]).optional(), intent: z.enum(["implement", "implement_resume", "dismiss"]).optional(), }); export const AgentPermissionResponseSchema: z.ZodType = z.discriminatedUnion("behavior", [ z.object({ behavior: z.literal("allow"), selectedActionId: z.string().optional(), updatedInput: z.record(z.string(), z.unknown()).optional(), updatedPermissions: z.array(AgentPermissionUpdateSchema).optional(), }), z.object({ behavior: z.literal("deny"), selectedActionId: z.string().optional(), message: z.string().optional(), interrupt: z.boolean().optional(), }), ]); export const AgentPermissionRequestPayloadSchema: z.ZodType = z.object({ id: z.string(), provider: AgentProviderSchema, name: z.string(), kind: z.enum(["tool", "plan", "question", "mode", "other"]), title: z.string().optional(), description: z.string().optional(), input: z.record(z.string(), z.unknown()).optional(), detail: z.lazy(() => ToolCallDetailPayloadSchema).optional(), suggestions: z.array(AgentPermissionUpdateSchema).optional(), actions: z.array(AgentPermissionActionSchema).optional(), metadata: z.record(z.string(), z.unknown()).optional(), }); const UnknownValueSchema = z.union([ z.null(), z.boolean(), z.number(), z.string(), z.array(z.unknown()), z.object({}).passthrough(), ]); const NonNullUnknownSchema = z.union([ z.boolean(), z.number(), z.string(), z.array(z.unknown()), z.object({}).passthrough(), ]); const WorktreeSetupCommandSnapshotSchema = z.object({ index: z.number().int().positive(), command: z.string(), cwd: z.string(), log: z.string().optional().default(""), status: z.enum(["running", "completed", "failed"]), exitCode: z.number().nullable(), durationMs: z.number().nonnegative().optional(), }); const WorktreeSetupDetailPayloadSchema = z.object({ type: z.literal("worktree_setup"), worktreePath: z.string(), branchName: z.string(), log: z.string(), commands: z.array(WorktreeSetupCommandSnapshotSchema), truncated: z.boolean().optional(), }); const ToolCallDetailPayloadSchema: z.ZodType = z.discriminatedUnion( "type", [ WorktreeSetupDetailPayloadSchema, z.object({ type: z.literal("shell"), command: z.string(), cwd: z.string().optional(), output: z.string().optional(), exitCode: z.number().nullable().optional(), }), z.object({ type: z.literal("read"), filePath: z.string(), content: z.string().optional(), offset: z.number().optional(), limit: z.number().optional(), }), z.object({ type: z.literal("edit"), filePath: z.string(), oldString: z.string().optional(), newString: z.string().optional(), unifiedDiff: z.string().optional(), }), z.object({ type: z.literal("write"), filePath: z.string(), content: z.string().optional(), }), z.object({ type: z.literal("search"), query: z.string(), toolName: z.enum(["search", "grep", "glob", "web_search"]).optional(), content: z.string().optional(), filePaths: z.array(z.string()).optional(), webResults: z .array( z.object({ title: z.string(), url: z.string(), }), ) .optional(), annotations: z.array(z.string()).optional(), numFiles: z.number().optional(), numMatches: z.number().optional(), durationMs: z.number().optional(), durationSeconds: z.number().optional(), truncated: z.boolean().optional(), mode: z.enum(["content", "files_with_matches", "count"]).optional(), }), z.object({ type: z.literal("fetch"), url: z.string(), prompt: z.string().optional(), result: z.string().optional(), code: z.number().optional(), codeText: z.string().optional(), bytes: z.number().optional(), durationMs: z.number().optional(), }), z.object({ type: z.literal("sub_agent"), subAgentType: z.string().optional(), description: z.string().optional(), childSessionId: z.string().optional(), log: z.string(), // Compat cruft for clients <= 0.1.65-beta.3 that required this field. Producers still // emit `[]`; nothing reads it. Drop the field (and the `[]` emissions) once those // clients are no longer in the field. actions: z .array( z.object({ index: z.number().int().positive(), toolName: z.string(), summary: z.string().optional(), }), ) .optional(), }), z.object({ type: z.literal("plain_text"), label: z.string().optional(), text: z.string().optional(), icon: z.enum(TOOL_CALL_ICON_NAMES).optional(), }), z.object({ type: z.literal("plan"), text: z.string(), }), z.object({ type: z.literal("unknown"), input: UnknownValueSchema, output: UnknownValueSchema, }), ], ); const ToolCallBasePayloadSchema = z.object({ type: z.literal("tool_call"), callId: z.string(), name: z.string(), detail: ToolCallDetailPayloadSchema, metadata: z.record(z.string(), z.unknown()).optional(), }); const ToolCallRunningPayloadSchema = ToolCallBasePayloadSchema.extend({ status: z.literal("running"), error: z.null(), }); const ToolCallCompletedPayloadSchema = ToolCallBasePayloadSchema.extend({ status: z.literal("completed"), error: z.null(), }); const ToolCallFailedPayloadSchema = ToolCallBasePayloadSchema.extend({ status: z.literal("failed"), error: NonNullUnknownSchema, }); const ToolCallCanceledPayloadSchema = ToolCallBasePayloadSchema.extend({ status: z.literal("canceled"), error: z.null(), }); const ToolCallTimelineItemPayloadSchema: z.ZodType = z.discriminatedUnion("status", [ ToolCallRunningPayloadSchema, ToolCallCompletedPayloadSchema, ToolCallFailedPayloadSchema, ToolCallCanceledPayloadSchema, ]); // zod-aot 0.20.4 miscompiles this as a nested discriminated union by omitting // the inner tool_call branch from the generated outer dispatch. export const AgentTimelineItemPayloadSchema: z.ZodType = z.union([ z.object({ type: z.literal("user_message"), text: z.string(), messageId: z.string().optional(), clientMessageId: z.string().optional(), }), z.object({ type: z.literal("assistant_message"), text: z.string(), messageId: z.string().optional(), }), z.object({ type: z.literal("reasoning"), text: z.string(), }), ToolCallTimelineItemPayloadSchema, z.object({ type: z.literal("todo"), items: z.array( z.object({ text: z.string(), completed: z.boolean(), id: z.string().optional(), status: z.enum(["pending", "in_progress", "completed"]).optional(), activeForm: z.string().optional(), }), ), }), z.object({ type: z.literal("error"), message: z.string(), }), z.object({ type: z.literal("compaction"), status: z.enum(["loading", "completed"]), trigger: z.enum(["auto", "manual"]).optional(), preTokens: z.number().optional(), }), ]); export const AgentStreamEventPayloadSchema = z.discriminatedUnion("type", [ z.object({ type: z.literal("thread_started"), sessionId: z.string(), provider: AgentProviderSchema, }), z.object({ type: z.literal("turn_started"), provider: AgentProviderSchema, turnId: z.string().optional(), }), z.object({ type: z.literal("turn_completed"), provider: AgentProviderSchema, turnId: z.string().optional(), usage: AgentUsageSchema.optional(), }), z.object({ type: z.literal("turn_failed"), provider: AgentProviderSchema, turnId: z.string().optional(), error: z.string(), code: z.string().optional(), diagnostic: z.string().optional(), }), z.object({ type: z.literal("turn_canceled"), provider: AgentProviderSchema, turnId: z.string().optional(), reason: z.string(), }), z.object({ type: z.literal("timeline"), provider: AgentProviderSchema, item: AgentTimelineItemPayloadSchema, turnId: z.string().optional(), }), z.object({ type: z.literal("permission_requested"), provider: AgentProviderSchema, request: AgentPermissionRequestPayloadSchema, }), z.object({ type: z.literal("permission_resolved"), provider: AgentProviderSchema, requestId: z.string(), resolution: AgentPermissionResponseSchema, }), z.object({ type: z.literal("attention_required"), provider: AgentProviderSchema, reason: z.enum(["finished", "error", "permission"]), timestamp: z.string(), shouldNotify: z.boolean(), notification: z .object({ title: z.string(), body: z.string(), data: z.object({ serverId: z.string(), workspaceId: z.string().optional(), agentId: z.string(), reason: z.enum(["finished", "error", "permission"]), }), }) .optional(), }), ]); const AgentPersistenceHandleSchema: z.ZodType = z .object({ provider: AgentProviderSchema, sessionId: z.string(), nativeHandle: z.string().optional(), metadata: z.record(z.string(), z.unknown()).optional(), }) .nullable(); const AgentRuntimeInfoSchema: z.ZodType = z.object({ provider: AgentProviderSchema, sessionId: z.string().nullable(), model: z.string().nullable().optional(), thinkingOptionId: z.string().nullable().optional(), modeId: z.string().nullable().optional(), extra: z.record(z.string(), z.unknown()).optional(), }); const AgentActiveTurnPayloadSchema = z.object({ turnId: z.string(), startedAt: z.string().nullable(), }); export const AgentSnapshotPayloadSchema = z.object({ id: z.string(), provider: AgentProviderSchema, cwd: z.string(), workspaceId: z.string().optional(), model: z.string().nullable(), features: z.array(AgentFeatureSchema).optional(), thinkingOptionId: z.string().nullable().optional(), effectiveThinkingOptionId: z.string().nullable().optional(), createdAt: z.string(), updatedAt: z.string(), lastUserMessageAt: z.string().nullable(), status: AgentStatusSchema, activeTurn: AgentActiveTurnPayloadSchema.nullable().optional(), capabilities: AgentCapabilityFlagsSchema, currentModeId: z.string().nullable(), availableModes: z.array(AgentModeSchema), pendingPermissions: z.array(AgentPermissionRequestPayloadSchema), persistence: AgentPersistenceHandleSchema.nullable(), runtimeInfo: AgentRuntimeInfoSchema.optional(), lastUsage: AgentUsageSchema.optional(), lastError: z.string().optional(), title: z.string().nullable(), labels: z.record(z.string(), z.string()).default({}), requiresAttention: z.boolean().optional(), attentionReason: z.enum(["finished", "error", "permission"]).nullable().optional(), attentionTimestamp: z.string().nullable().optional(), archivedAt: z.string().nullable().optional(), providerUnavailable: z.boolean().optional(), }); export type AgentSnapshotPayload = z.infer; export const AgentListItemPayloadSchema = z.object({ id: z.string(), shortId: z.string(), title: z.string().nullable(), provider: AgentProviderSchema, model: z.string().nullable(), thinkingOptionId: z.string().nullable().optional(), effectiveThinkingOptionId: z.string().nullable().optional(), status: AgentStatusSchema, cwd: z.string(), createdAt: z.string(), updatedAt: z.string(), lastUserMessageAt: z.string().nullable(), archivedAt: z.string().nullable().optional(), requiresAttention: z.boolean().optional(), attentionReason: z.enum(["finished", "error", "permission"]).nullable().optional(), attentionTimestamp: z.string().nullable().optional(), labels: z.record(z.string(), z.string()).default({}), providerUnavailable: z.boolean().optional(), }); export type AgentListItemPayload = z.infer; export type AgentStreamEventPayload = z.infer; export const RecentProviderSessionDescriptorPayloadSchema = z.object({ providerId: z.string(), providerLabel: z.string(), providerHandleId: z.string(), cwd: z.string(), title: z.string().nullable(), firstPromptPreview: z.string().nullable(), lastPromptPreview: z.string().nullable(), lastActivityAt: z.string(), }); export type RecentProviderSessionDescriptorPayload = z.infer< typeof RecentProviderSessionDescriptorPayloadSchema >; // ============================================================================ // Session Inbound Messages (Session receives these) // ============================================================================ export const VoiceAudioChunkMessageSchema = z.object({ type: z.literal("voice_audio_chunk"), audio: z.string(), // base64 encoded format: z.string(), isLast: z.boolean(), }); export const AbortRequestMessageSchema = z.object({ type: z.literal("abort_request"), }); export const AudioPlayedMessageSchema = z.object({ type: z.literal("audio_played"), id: z.string(), }); const AgentDirectoryFilterSchema = z.object({ labels: z.record(z.string(), z.string()).optional(), projectKeys: z.array(z.string()).optional(), statuses: z.array(AgentStatusSchema).optional(), includeArchived: z.boolean().optional(), requiresAttention: z.boolean().optional(), thinkingOptionId: z.string().nullable().optional(), }); export const DeleteAgentRequestMessageSchema = z.object({ type: z.literal("delete_agent_request"), agentId: z.string(), requestId: z.string(), }); export const ArchiveAgentRequestMessageSchema = z.object({ type: z.literal("archive_agent_request"), agentId: z.string(), requestId: z.string(), }); export const CloseItemsRequestMessageSchema = z.object({ type: z.literal("close_items_request"), agentIds: z.array(z.string()).default([]), terminalIds: z.array(z.string()).default([]), requestId: z.string(), }); export const UpdateAgentRequestMessageSchema = z.object({ type: z.literal("update_agent_request"), agentId: z.string(), name: z.string().optional(), labels: z.record(z.string(), z.string()).optional(), requestId: z.string(), }); // The daemon accepts only image bytes chosen or acquired by the client. It must // never fetch a user-provided URL on the host's network. export const ProjectIconSourceSchema = z.discriminatedUnion("type", [ z.object({ type: z.literal("automatic") }), z.object({ type: z.literal("upload"), data: z.string() }), ]); export const ProjectRenameRequestSchema = z.object({ type: z.literal("project.rename.request"), projectId: z.string(), // Null or empty string clears the override and reverts to the derived name. customName: z.string().nullable(), requestId: z.string(), }); export const ProjectIconSetRequestSchema = z.object({ type: z.literal("project.icon.set.request"), projectId: z.string(), source: ProjectIconSourceSchema, requestId: z.string(), }); export const ProjectRemoveRequestSchema = z.object({ type: z.literal("project.remove.request"), projectId: z.string(), requestId: z.string(), }); export const WorkspaceTitleSetRequestSchema = z.object({ type: z.literal("workspace.title.set.request"), workspaceId: z.string(), // Null or empty string clears the title and reverts to the derived name. title: z.string().nullable(), requestId: z.string(), }); export const WorkspacePinSetRequestSchema = z.object({ type: z.literal("workspace.pin.set.request"), workspaceId: z.string(), pinned: z.boolean(), requestId: z.string(), }); export const WorkspaceLabelColorSchema = z.enum(WORKSPACE_LABEL_COLORS); export const WorkspaceLabelDefinitionSchema = z.object({ name: z.string(), color: WorkspaceLabelColorSchema, }); const WorkspaceLabelSyncCursorSchema = z.object({ generation: z.string(), afterSeq: z.number().int().nonnegative(), }); export const WorkspaceLabelListRequestSchema = z.object({ type: z.literal("workspace.label.list.request"), requestId: z.string(), subscribe: z.object({ subscriptionId: z.string() }), sync: WorkspaceLabelSyncCursorSchema.optional(), }); export const WorkspaceLabelAssignmentSetRequestSchema = z.object({ type: z.literal("workspace.label.assignment.set.request"), requestId: z.string(), workspaceId: z.string(), label: WorkspaceLabelDefinitionSchema, assigned: z.boolean(), }); /** * Editing a label is one operation. A name and a colour are two fields of one thing, and two * RPCs can land half-applied — leaving the catalog in a state the user never asked for and the * UI with nothing true to say. Both fields are optional; omitting one leaves it alone. */ export const WorkspaceLabelUpdateRequestSchema = z.object({ type: z.literal("workspace.label.update.request"), requestId: z.string(), name: z.string(), newName: z.string().optional(), color: WorkspaceLabelColorSchema.optional(), }); export const WorkspaceLabelDeleteRequestSchema = z.object({ type: z.literal("workspace.label.delete.request"), requestId: z.string(), name: z.string(), }); export const WorkspaceLabelDeleteInspectRequestSchema = z.object({ type: z.literal("workspace.label.delete.inspect.request"), requestId: z.string(), name: z.string(), }); export const WorkspaceRecoveryInspectRequestSchema = z.object({ type: z.literal("workspace.recovery.inspect.request"), workspaceId: z.string(), requestId: z.string(), }); export const WorkspaceRecoveryRestoreRequestSchema = z.object({ type: z.literal("workspace.recovery.restore.request"), workspaceId: z.string(), requestId: z.string(), }); export const SetVoiceModeMessageSchema = z.object({ type: z.literal("set_voice_mode"), enabled: z.boolean(), agentId: z.string().optional(), requestId: z.string().optional(), }); export const GitHubPrAttachmentSchema = z.object({ type: z.literal("github_pr"), mimeType: z.literal("application/github-pr"), number: z.number().int().positive(), title: z.string(), url: z.string(), body: z.string().nullable().optional(), baseRefName: z.string().nullable().optional(), headRefName: z.string().nullable().optional(), }); export const ForgeChangeRequestAttachmentSchema = z.object({ type: z.literal("forge_change_request"), mimeType: z.literal("application/paseo-forge-change-request"), forge: z.string().optional().default("github"), number: z.number().int().positive(), title: z.string(), url: z.string(), body: z.string().nullable().optional(), projectPath: z.string().optional(), baseRefName: z.string().nullable().optional(), headRefName: z.string().nullable().optional(), }); export const GitHubIssueAttachmentSchema = z.object({ type: z.literal("github_issue"), mimeType: z.literal("application/github-issue"), number: z.number().int().positive(), title: z.string(), url: z.string(), body: z.string().nullable().optional(), }); export const ForgeIssueAttachmentSchema = z.object({ type: z.literal("forge_issue"), mimeType: z.literal("application/paseo-forge-issue"), forge: z.string().optional().default("github"), number: z.number().int().positive(), title: z.string(), url: z.string(), body: z.string().nullable().optional(), projectPath: z.string().optional(), }); export const ExternalResourceAttachmentMetadataSchema = z.object({ provider: z.string(), providerLabel: z.string(), resourceType: z.string(), id: z.string(), identifier: z.string(), title: z.string(), url: z.string(), }); export const TextAttachmentSchema = z .object({ type: z.literal("text"), mimeType: z.literal("text/plain"), contextKind: z.string().optional(), title: z.string().nullable().optional(), text: z.string(), externalResource: ExternalResourceAttachmentMetadataSchema.optional(), }) .transform(({ contextKind, ...attachment }) => ({ ...attachment, ...(contextKind === "chat_history" ? { contextKind } : {}), })); export const ReviewAttachmentContextLineSchema = z.object({ oldLineNumber: z.number().int().positive().nullable(), newLineNumber: z.number().int().positive().nullable(), type: z.enum(["add", "remove", "context"]), content: z.string(), }); export const ReviewAttachmentCommentSchema = z.object({ filePath: z.string(), side: z.enum(["old", "new"]), lineNumber: z.number().int().positive(), body: z.string(), context: z.object({ hunkHeader: z.string(), targetLine: ReviewAttachmentContextLineSchema, lines: z.array(ReviewAttachmentContextLineSchema), }), }); export const ReviewAttachmentSchema = z.object({ type: z.literal("review"), mimeType: z.literal("application/paseo-review"), cwd: z.string(), mode: z.enum(["uncommitted", "base"]), baseRef: z.string().nullable().optional(), comments: z.array(ReviewAttachmentCommentSchema), }); export const UploadedFileAttachmentSchema = z.object({ type: z.literal("uploaded_file"), id: z.string(), fileName: z.string(), mimeType: z.string(), size: z.number().int().nonnegative(), path: z.string(), }); export const AgentAttachmentSchema = z.discriminatedUnion("type", [ ForgeChangeRequestAttachmentSchema, ForgeIssueAttachmentSchema, GitHubPrAttachmentSchema, GitHubIssueAttachmentSchema, TextAttachmentSchema, ReviewAttachmentSchema, UploadedFileAttachmentSchema, ]); function normalizeAgentAttachments(input: unknown): AgentAttachment[] { if (!Array.isArray(input)) { return []; } const normalized: AgentAttachment[] = []; for (const item of input) { const parsed = AgentAttachmentSchema.safeParse(item); if (parsed.success) { normalized.push(parsed.data); } } return normalized; } const AgentAttachmentsSchema = z.unknown().transform(normalizeAgentAttachments).optional(); export const ChangeRequestCheckoutSourceSchema = z.object({ kind: z.literal("change_request"), forge: z.string().optional(), number: z.number().int().positive(), projectPath: z.string().optional(), }); const ImageAttachmentSchema = z.object({ data: z.string(), // base64 encoded image mimeType: z.string(), // e.g., "image/jpeg", "image/png" }); export const ActiveTurnBehaviorSchema = z.enum(["interrupt", "steer"]); export type ActiveTurnBehavior = z.infer; export const SendAgentMessageSchema = z.object({ type: z.literal("send_agent_message"), agentId: z.string(), text: z.string(), messageId: z.string().optional(), // Client-provided ID for deduplication activeTurnBehavior: ActiveTurnBehaviorSchema.optional(), images: z.array(ImageAttachmentSchema).optional(), attachments: AgentAttachmentsSchema, }); // ============================================================================ // Agent RPCs (requestId-correlated) // ============================================================================ const DirectorySyncRequestSchema = z.object({ generation: z.string().optional(), afterSeq: z.number().int().nonnegative().optional(), }); const DirectorySyncRemovalSchema = z.object({ id: z.string(), seq: z.number().int().positive(), }); const DirectorySyncMetadataSchema = z.object({ generation: z.string(), headSeq: z.number().int().nonnegative(), mode: z.enum(["snapshot", "changes"]), reason: z .enum(["no_cursor", "generation_changed", "cursor_expired", "changes_too_large"]) .optional(), removals: z.array(DirectorySyncRemovalSchema), }); export const FetchAgentsRequestMessageSchema = z.object({ type: z.literal("fetch_agents_request"), requestId: z.string(), scope: z.enum(["active"]).optional(), filter: AgentDirectoryFilterSchema.optional(), sort: z .array( z.object({ key: z.enum(["status_priority", "created_at", "updated_at", "title"]), direction: z.enum(["asc", "desc"]), }), ) .optional(), page: z .object({ limit: z.number().int().positive().max(200), cursor: z.string().min(1).optional(), }) .optional(), subscribe: z .object({ subscriptionId: z.string().optional(), }) .optional(), // COMPAT(directorySync): added in v0.3.x, remove optional after 2027-02-12. sync: DirectorySyncRequestSchema.optional(), }); const WorkspaceStateBucketSchema = z.enum([ "needs_input", "failed", "running", "attention", "done", ]); export const FetchWorkspacesRequestMessageSchema = z.object({ type: z.literal("fetch_workspaces_request"), requestId: z.string(), filter: z .object({ query: z.string().optional(), projectId: z.string().optional(), // Unused: accepted so older clients still parse, but the server does not filter on it. idPrefix: z.string().optional(), }) .optional(), sort: z .array( z.object({ key: z.enum(["status_priority", "activity_at", "name", "project_id"]), direction: z.enum(["asc", "desc"]), }), ) .optional(), page: z .object({ limit: z.number().int().positive().max(200), cursor: z.string().min(1).optional(), }) .optional(), subscribe: z .object({ subscriptionId: z.string().optional(), }) .optional(), // COMPAT(directorySync): added in v0.3.x, remove optional after 2027-02-12. sync: DirectorySyncRequestSchema.optional(), }); export const ProjectListRequestMessageSchema = z.object({ type: z.literal("project.list.request"), requestId: z.string(), // COMPAT(directorySync): added in v0.3.x, remove optional after 2027-02-12. sync: DirectorySyncRequestSchema.optional(), }); export const FetchAgentHistoryRequestMessageSchema = z.object({ type: z.literal("fetch_agent_history_request"), requestId: z.string(), filter: AgentDirectoryFilterSchema.optional(), // A ranked free-text query over agent title, workspace name, branch, and // project name. Present only on history: agent subscriptions filter on // structure, not on relevance. Ranking replaces `sort` when it is set. search: z.string().optional(), sort: z .array( z.object({ key: z.enum(["status_priority", "created_at", "updated_at", "title"]), direction: z.enum(["asc", "desc"]), }), ) .optional(), page: z .object({ limit: z.number().int().positive().max(200), cursor: z.string().min(1).optional(), }) .optional(), }); export const FetchRecentProviderSessionsRequestMessageSchema = z.object({ type: z.literal("fetch_recent_provider_sessions_request"), requestId: z.string(), cwd: z.string().optional(), providers: z.array(z.string()).optional(), since: z.string().optional(), limit: z.number().int().positive().max(200).optional(), }); export const FetchAgentRequestMessageSchema = z.object({ type: z.literal("fetch_agent_request"), requestId: z.string(), /** Accepts full ID, unique prefix, or exact full title (server resolves). */ agentId: z.string(), }); export const SendAgentMessageRequestSchema = z.object({ type: z.literal("send_agent_message_request"), requestId: z.string(), /** Accepts full ID, unique prefix, or exact full title (server resolves). */ agentId: z.string(), text: z.string(), messageId: z.string().optional(), // Client-provided ID for deduplication activeTurnBehavior: ActiveTurnBehaviorSchema.optional(), images: z.array(ImageAttachmentSchema).optional(), attachments: AgentAttachmentsSchema, }); export const WaitForFinishRequestSchema = z.object({ type: z.literal("wait_for_finish_request"), requestId: z.string(), /** Accepts full ID, unique prefix, or exact full title (server resolves). */ agentId: z.string(), timeoutMs: z.number().int().positive().optional(), }); export const DaemonGetStatusRequestSchema = z.object({ type: z.literal("daemon.get_status.request"), requestId: z.string(), }); export const DaemonGetPairingOfferRequestSchema = z.object({ type: z.literal("daemon.get_pairing_offer.request"), requestId: z.string(), }); export const DaemonConfigReloadRequestSchema = z.object({ type: z.literal("daemon.config.reload.request"), requestId: z.string(), }); export const HubManagementDaemonConnectRequestSchema = z.object({ type: z.literal("hub.management.daemon.connect.request"), requestId: z.string(), hubUrl: z.string(), token: z.string(), }); export const HubManagementDaemonGetStatusRequestSchema = z.object({ type: z.literal("hub.management.daemon.get_status.request"), requestId: z.string(), }); export const HubManagementDaemonDisconnectRequestSchema = z.object({ type: z.literal("hub.management.daemon.disconnect.request"), requestId: z.string(), force: z.boolean().optional(), }); export const DiagnosticsRequestSchema = z.object({ type: z.literal("diagnostics.request"), requestId: z.string(), }); export const PluginCatalogGetRequestSchema = z.object({ type: z.literal("plugin.catalog.get.request"), requestId: z.string(), }); export const PluginListRequestSchema = z.object({ type: z.literal("plugin.list.request"), requestId: z.string(), }); export const PluginLogsGetRequestSchema = z.object({ type: z.literal("plugin.logs.get.request"), requestId: z.string(), pluginId: PluginIdSchema, }); export const PluginDirectoryInstallRequestSchema = z.object({ type: z.literal("plugin.directory.install.request"), requestId: z.string(), path: z.string().min(1), id: PluginIdSchema.optional(), }); export const PluginDirectoryInspectRequestSchema = z.object({ type: z.literal("plugin.directory.inspect.request"), requestId: z.string(), path: z.string().min(1), }); function pluginIdRequest(type: Type) { return z.object({ type: z.literal(type), requestId: z.string(), pluginId: PluginIdSchema }); } export const PluginReloadRequestSchema = pluginIdRequest("plugin.reload.request"); export const PluginEnableRequestSchema = pluginIdRequest("plugin.enable.request"); export const PluginDisableRequestSchema = pluginIdRequest("plugin.disable.request"); export const PluginRemoveRequestSchema = pluginIdRequest("plugin.remove.request"); export const PluginRpcInvokeRequestSchema = z.object({ type: z.literal("plugin.rpc.invoke.request"), requestId: z.string(), pluginId: PluginIdSchema, method: z.string().min(1), input: z.unknown(), }); export const AgentSkillOperationSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("add"), name: z.string() }).strict(), z.object({ kind: z.literal("update"), name: z.string() }).strict(), z.object({ kind: z.literal("delete"), name: z.string() }).strict(), ]); export type AgentSkillOperation = z.infer; export const AgentSkillsStatusSchema = z.object({ state: z.enum(["not-installed", "up-to-date", "drift"]), ops: z.array(AgentSkillOperationSchema), available: z.array(z.string()), installed: z.array(z.string()), selection: AgentSkillSelectionSchema, }); export type AgentSkillsStatus = z.infer; export const AgentSkillsConfirmationSchema = z.object({ removals: z.array(z.string()) }).strict(); export type AgentSkillsConfirmation = z.infer; export const AgentSkillsSaveResultSchema = AgentSkillsStatusSchema.extend({ confirmationRequired: AgentSkillsConfirmationSchema.nullable(), }); export type AgentSkillsSaveResult = z.infer; function agentSkillsRequest(type: Type) { return z.object({ type: z.literal(type), requestId: z.string() }).strict(); } export const AgentSkillsGetStatusRequestSchema = agentSkillsRequest( "agent.skills.get_status.request", ); export const AgentSkillsReconcileRequestSchema = agentSkillsRequest( "agent.skills.reconcile.request", ); export const AgentSkillsUninstallRequestSchema = agentSkillsRequest( "agent.skills.uninstall.request", ); export const AgentSkillsSaveSelectionRequestSchema = z .object({ type: z.literal("agent.skills.save_selection.request"), requestId: z.string(), selection: AgentSkillSelectionSchema, confirmedRemovals: z.array(z.string()).optional(), }) .strict(); export const AgentSkillsImportLegacySelectionRequestSchema = z .object({ type: z.literal("agent.skills.import_legacy_selection.request"), requestId: z.string(), selection: AgentSkillSelectionSchema, }) .strict(); export const GetDaemonConfigRequestMessageSchema = z.object({ type: z.literal("get_daemon_config_request"), requestId: z.string(), }); export const SetDaemonConfigRequestMessageSchema = z.object({ type: z.literal("set_daemon_config_request"), requestId: z.string(), config: MutableDaemonConfigPatchSchema, }); export const ReadProjectConfigRequestMessageSchema = z.object({ type: z.literal("read_project_config_request"), requestId: z.string(), repoRoot: z.string(), }); export const WriteProjectConfigRequestMessageSchema = z.object({ type: z.literal("write_project_config_request"), requestId: z.string(), repoRoot: z.string(), config: PaseoConfigRawSchema, expectedRevision: PaseoConfigRevisionSchema.nullable(), }); // ============================================================================ // Dictation Streaming (lossless, resumable) // ============================================================================ export const DictationStreamStartMessageSchema = z.object({ type: z.literal("dictation_stream_start"), dictationId: z.string(), format: z.string(), // e.g. "audio/pcm;rate=16000;bits=16" }); export const DictationStreamChunkMessageSchema = z.object({ type: z.literal("dictation_stream_chunk"), dictationId: z.string(), seq: z.number().int().nonnegative(), audio: z.string(), // base64 encoded chunk format: z.string(), // e.g. "audio/pcm;rate=16000;bits=16" }); export const DictationStreamFinishMessageSchema = z.object({ type: z.literal("dictation_stream_finish"), dictationId: z.string(), finalSeq: z.number().int().nonnegative(), }); export const DictationStreamCancelMessageSchema = z.object({ type: z.literal("dictation_stream_cancel"), dictationId: z.string(), }); const GitSetupOptionsSchema = z.object({ baseBranch: z.string().optional(), createNewBranch: z.boolean().optional(), newBranchName: z.string().optional(), createWorktree: z.boolean().optional(), worktreeSlug: z.string().optional(), refName: z.string().min(1).optional(), action: z.enum(["branch-off", "checkout"]).optional(), checkoutSource: ChangeRequestCheckoutSourceSchema.optional(), // COMPAT(githubPrNumber): added in v0.1.106, remove after 2026-12-28 once // clients send checkoutSource. githubPrNumber: z.number().int().positive().optional(), }); export type GitSetupOptions = z.infer; export const CreateAgentWorktreeTargetSchema = z.discriminatedUnion("mode", [ z.object({ mode: z.literal("branch-off"), newBranch: z.string().min(1), base: z.string().min(1).optional(), }), z.object({ mode: z.literal("checkout-branch"), branch: z.string().min(1), }), z.object({ mode: z.literal("checkout-pr"), prNumber: z.number().int().positive(), }), ]); export type CreateAgentWorktreeTarget = z.infer; export const CreateAgentRequestMessageSchema = z.object({ type: z.literal("create_agent_request"), config: AgentSessionConfigSchema, env: z.record(z.string(), z.string()).optional(), workspaceId: z.string().optional(), // Optional caller context lets managed CLI invocations use the same daemon-owned // workspace and parentage policy as agent-scoped MCP creation. callerAgentId: z.string().optional(), worktreeName: z.string().optional(), initialPrompt: z.string().optional(), clientMessageId: z.string().optional(), outputSchema: z.record(z.string(), z.unknown()).optional(), images: z.array(ImageAttachmentSchema).optional(), attachments: AgentAttachmentsSchema, git: GitSetupOptionsSchema.optional(), worktree: CreateAgentWorktreeTargetSchema.optional(), autoArchive: z.boolean().optional(), labels: z.record(z.string(), z.string()).default({}), requestId: z.string(), }); export const ListProviderModelsRequestMessageSchema = z.object({ type: z.literal("list_provider_models_request"), provider: AgentProviderSchema, cwd: z.string().optional(), requestId: z.string(), }); export const ListProviderModesRequestMessageSchema = z.object({ type: z.literal("list_provider_modes_request"), provider: AgentProviderSchema, cwd: z.string().optional(), requestId: z.string(), }); export const ListAvailableProvidersRequestMessageSchema = z.object({ type: z.literal("list_available_providers_request"), requestId: z.string(), }); export const GetProvidersSnapshotRequestMessageSchema = z.object({ type: z.literal("get_providers_snapshot_request"), cwd: z.string().optional(), // COMPAT(compactProviderSnapshots): old daemons ignore this field and return a full snapshot. ifNoneMatch: z.string().optional(), requestId: z.string(), }); export const RefreshProvidersSnapshotRequestMessageSchema = z.object({ type: z.literal("refresh_providers_snapshot_request"), cwd: z.string().optional(), providers: z.array(AgentProviderSchema).optional(), requestId: z.string(), }); export const ProviderDiagnosticRequestMessageSchema = z.object({ type: z.literal("provider_diagnostic_request"), provider: AgentProviderSchema, requestId: z.string(), }); export const ProviderUsageListRequestMessageSchema = z.object({ type: z.literal("provider.usage.list.request"), requestId: z.string(), }); export const ResumeAgentRequestMessageSchema = z.object({ type: z.literal("resume_agent_request"), handle: AgentPersistenceHandleSchema, overrides: AgentSessionConfigSchema.partial().optional(), requestId: z.string(), }); export const ImportAgentRequestMessageSchema = z.object({ type: z.literal("import_agent_request"), provider: AgentProviderSchema.optional(), providerId: z.string().optional(), sessionId: z.string().optional(), providerHandleId: z.string().optional(), cwd: z.string().optional(), workspaceId: z.string().optional(), labels: z.record(z.string(), z.string()).optional(), requestId: z.string(), }); export const RefreshAgentRequestMessageSchema = z.object({ type: z.literal("refresh_agent_request"), agentId: z.string(), requestId: z.string(), }); export const CancelAgentRequestMessageSchema = z.object({ type: z.literal("cancel_agent_request"), agentId: z.string(), requestId: z.string().optional(), }); export const RestartServerRequestMessageSchema = z.object({ type: z.literal("restart_server_request"), reason: z.string().optional(), requestId: z.string(), }); export const ShutdownServerRequestMessageSchema = z.object({ type: z.literal("shutdown_server_request"), requestId: z.string(), }); export const DaemonUpdateRequestMessageSchema = z.object({ type: z.literal("daemon.update.request"), requestId: z.string(), }); export const AgentTimelineCursorSchema = z.object({ epoch: z.string(), seq: z.number().int().nonnegative(), }); export const FetchAgentTimelineRequestMessageSchema = z.object({ type: z.literal("fetch_agent_timeline_request"), agentId: z.string(), requestId: z.string(), direction: z.enum(["tail", "before", "after"]).optional(), cursor: AgentTimelineCursorSchema.optional(), // 0 means "all matching rows for this query window". limit: z.number().int().nonnegative().optional(), // Default should be projected for app timeline loading. projection: z.enum(["projected", "canonical"]).optional(), // Allow the client to merge this bounded page outside its contiguous loaded range. mergeWindow: z.boolean().optional(), }); export const AgentTimelineListPromptsRequestMessageSchema = z.object({ type: z.literal("agent.timeline.list_prompts.request"), agentId: z.string(), requestId: z.string(), }); export const ProviderSubagentListRequestMessageSchema = z.object({ type: z.literal("agent.provider_subagents.list.request"), parentAgentId: z.string(), requestId: z.string(), }); export const ProviderSubagentTimelineRequestMessageSchema = z.object({ type: z.literal("agent.provider_subagents.timeline.get.request"), parentAgentId: z.string(), subagentId: z.string(), requestId: z.string(), direction: z.enum(["tail", "before", "after"]).optional(), cursor: AgentTimelineCursorSchema.optional(), limit: z.number().int().nonnegative().optional(), }); export const SetAgentTimelineSubscriptionRequestMessageSchema = z.object({ type: z.literal("agent.timeline.set_subscription.request"), agentIds: z.array(z.string()), requestId: z.string(), }); export const AgentForkContextRequestMessageSchema = z.object({ type: z.literal("agent.fork_context.request"), agentId: z.string(), boundaryCursor: AgentTimelineCursorSchema.optional(), boundaryMessageId: z.string().optional(), requestId: z.string(), }); export const SetAgentModeRequestMessageSchema = z.object({ type: z.literal("set_agent_mode_request"), agentId: z.string(), modeId: z.string(), requestId: z.string(), }); const AgentActionResponsePayloadSchema = z.object({ requestId: z.string(), agentId: z.string(), accepted: z.boolean(), error: z.string().nullable(), notice: AgentProviderNoticeSchema.nullable().optional(), }); export const SetAgentModeResponseMessageSchema = z.object({ type: z.literal("set_agent_mode_response"), payload: AgentActionResponsePayloadSchema, }); export const SetAgentModelRequestMessageSchema = z.object({ type: z.literal("set_agent_model_request"), agentId: z.string(), modelId: z.string().nullable(), requestId: z.string(), }); export const SetAgentModelResponseMessageSchema = z.object({ type: z.literal("set_agent_model_response"), payload: AgentActionResponsePayloadSchema, }); export const SetAgentThinkingRequestMessageSchema = z.object({ type: z.literal("set_agent_thinking_request"), agentId: z.string(), thinkingOptionId: z.string().nullable(), requestId: z.string(), }); export const SetAgentThinkingResponseMessageSchema = z.object({ type: z.literal("set_agent_thinking_response"), payload: AgentActionResponsePayloadSchema, }); export const SetAgentFeatureRequestMessageSchema = z.object({ type: z.literal("set_agent_feature_request"), agentId: z.string(), featureId: z.string(), value: z.unknown(), requestId: z.string(), }); export const SetAgentFeatureResponseMessageSchema = z.object({ type: z.literal("set_agent_feature_response"), payload: AgentActionResponsePayloadSchema, }); /** * Every agent-config value a client can change in one shot. Each field is * optional and an omitted field is left alone; `null` on model and thinking * clears them, matching the single-field RPCs above. */ export const AgentConfigApplySchema = z.object({ modelId: z.string().nullable().optional(), modeId: z.string().optional(), thinkingOptionId: z.string().nullable().optional(), featureValues: z.record(z.string(), z.unknown()).optional(), }); export type AgentConfigApply = z.infer; /** * Applies a whole config bundle to one agent. The four single-field RPCs above * stay for individual control edits. One request prevents client interruption * and other mutations from interleaving between bundle steps; provider-level * rejection can still leave earlier steps applied. */ export const AgentConfigApplyRequestMessageSchema = z.object({ type: z.literal("agent.config.apply.request"), agentId: z.string(), config: AgentConfigApplySchema, requestId: z.string(), }); export const AgentConfigApplyResponseMessageSchema = z.object({ type: z.literal("agent.config.apply.response"), payload: AgentActionResponsePayloadSchema, }); export const AgentDetachRequestMessageSchema = z.object({ type: z.literal("agent.detach.request"), agentId: z.string(), requestId: z.string(), }); export const AgentDetachResponseMessageSchema = z.object({ type: z.literal("agent.detach.response"), payload: AgentActionResponsePayloadSchema, }); export const AgentRewindModeSchema = z.enum(["conversation", "files", "both"]); export const AgentRewindRequestMessageSchema = z.object({ type: z.literal("agent.rewind.request"), agentId: z.string(), messageId: z.string(), mode: AgentRewindModeSchema, requestId: z.string(), }); export const AgentRewindResponseMessageSchema = z.object({ type: z.literal("agent.rewind.response"), payload: z.object({ requestId: z.string(), agentId: z.string(), ok: z.boolean(), error: z.string().nullable(), }), }); export const UpdateAgentResponseMessageSchema = z.object({ type: z.literal("update_agent_response"), payload: AgentActionResponsePayloadSchema, }); export const ProjectRenameResponsePayloadSchema = z.object({ requestId: z.string(), projectId: z.string(), accepted: z.boolean(), customName: z.string().nullable(), error: z.string().nullable(), }); export const ProjectRenameResponseSchema = z.object({ type: z.literal("project.rename.response"), payload: ProjectRenameResponsePayloadSchema, }); export const ProjectIconSetResponseSchema = z.object({ type: z.literal("project.icon.set.response"), payload: z.object({ requestId: z.string(), projectId: z.string(), accepted: z.boolean(), error: z.string().nullable(), }), }); export const ProjectRemoveResponsePayloadSchema = z.object({ requestId: z.string(), projectId: z.string(), accepted: z.boolean(), removedWorkspaceIds: z.array(z.string()).default([]), error: z.string().nullable(), }); export const ProjectRemoveResponseSchema = z.object({ type: z.literal("project.remove.response"), payload: ProjectRemoveResponsePayloadSchema, }); export const WorkspaceTitleSetResponsePayloadSchema = z.object({ requestId: z.string(), workspaceId: z.string(), accepted: z.boolean(), title: z.string().nullable(), error: z.string().nullable(), }); export const WorkspaceTitleSetResponseSchema = z.object({ type: z.literal("workspace.title.set.response"), payload: WorkspaceTitleSetResponsePayloadSchema, }); export const WorkspacePinSetResponsePayloadSchema = z.object({ requestId: z.string(), workspaceId: z.string(), accepted: z.boolean(), pinnedAt: z.string().nullable(), error: z.string().nullable(), }); export const WorkspacePinSetResponseSchema = z.object({ type: z.literal("workspace.pin.set.response"), payload: WorkspacePinSetResponsePayloadSchema, }); export const WorkspaceRecoveryStateSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("recoverable"), workspaceId: z.string(), workspaceName: z.string(), action: z.string(), branch: z.string().nullable(), }), z.object({ kind: z.literal("unavailable"), workspaceId: z.string(), reason: z.string(), message: z.string(), }), ]); export const WorkspaceRecoveryInspectResponseSchema = z.object({ type: z.literal("workspace.recovery.inspect.response"), payload: z.object({ requestId: z.string(), state: WorkspaceRecoveryStateSchema, }), }); export const WorkspaceRecoveryRestoreResponseSchema = z.object({ type: z.literal("workspace.recovery.restore.response"), payload: z.object({ requestId: z.string(), workspaceId: z.string(), accepted: z.boolean(), error: z.string().nullable(), }), }); export const SetVoiceModeResponseMessageSchema = z.object({ type: z.literal("set_voice_mode_response"), payload: z.object({ requestId: z.string(), enabled: z.boolean(), agentId: z.string().nullable(), accepted: z.boolean(), error: z.string().nullable(), reasonCode: z.string().optional(), retryable: z.boolean().optional(), missingModelIds: z.array(z.string()).optional(), }), }); export const AgentPermissionResponseMessageSchema = z.object({ type: z.literal("agent_permission_response"), agentId: z.string(), requestId: z.string(), response: AgentPermissionResponseSchema, }); const CheckoutErrorCodeSchema = z.enum([ "NOT_GIT_REPO", "NOT_ALLOWED", "MERGE_CONFLICT", "UNKNOWN", ]); const CheckoutErrorSchema = z.object({ code: CheckoutErrorCodeSchema, message: z.string(), }); const CheckoutDiffCompareSchema = z.object({ mode: z.enum(["uncommitted", "base"]), baseRef: z.string().optional(), ignoreWhitespace: z.boolean().optional(), }); export const CheckoutStatusRequestSchema = z.object({ type: z.literal("checkout_status_request"), cwd: z.string(), requestId: z.string(), }); export const SubscribeCheckoutDiffRequestSchema = z.object({ type: z.literal("subscribe_checkout_diff_request"), subscriptionId: z.string(), cwd: z.string(), compare: CheckoutDiffCompareSchema, requestId: z.string(), }); export const UnsubscribeCheckoutDiffRequestSchema = z.object({ type: z.literal("unsubscribe_checkout_diff_request"), subscriptionId: z.string(), }); export const CheckoutCommitRequestSchema = z.object({ type: z.literal("checkout_commit_request"), cwd: z.string(), message: z.string().optional(), addAll: z.boolean().optional(), requestId: z.string(), }); export const CheckoutMergeRequestSchema = z.object({ type: z.literal("checkout_merge_request"), cwd: z.string(), baseRef: z.string().optional(), strategy: z.enum(["merge", "squash"]).optional(), requireCleanTarget: z.boolean().optional(), requestId: z.string(), }); export const CheckoutMergeFromBaseRequestSchema = z.object({ type: z.literal("checkout_merge_from_base_request"), cwd: z.string(), baseRef: z.string().optional(), requireCleanTarget: z.boolean().optional(), requestId: z.string(), }); export const CheckoutPullRequestSchema = z.object({ type: z.literal("checkout_pull_request"), cwd: z.string(), requestId: z.string(), }); export const CheckoutPushRequestSchema = z.object({ type: z.literal("checkout_push_request"), cwd: z.string(), requestId: z.string(), }); export const CheckoutRefreshRequestSchema = z.object({ type: z.literal("checkout.refresh.request"), cwd: z.string(), requestId: z.string(), }); export const CheckoutDiscardChangesRequestSchema = z.object({ type: z.literal("checkout.discard_changes.request"), cwd: z.string(), paths: z.array(z.string()).min(1), requestId: z.string(), }); export const CheckoutPrCreateRequestSchema = z.object({ type: z.literal("checkout_pr_create_request"), cwd: z.string(), title: z.string().optional(), body: z.string().optional(), baseRef: z.string().optional(), requestId: z.string(), }); export const CheckoutPrMergeRequestSchema = z.object({ type: z.literal("checkout_pr_merge_request"), cwd: z.string(), mergeMethod: z.enum(["merge", "squash", "rebase"]), requestId: z.string(), }); export const CheckoutForgeSetAutoMergeRequestSchema = z.object({ type: z.literal("checkout.forge.set_auto_merge.request"), cwd: z.string(), enabled: z.boolean(), mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(), requestId: z.string(), }); // COMPAT(githubAutoMergeRpc): added in v0.1.106, remove after 2026-12-28 once // all supported clients use checkout.forge.set_auto_merge.*. export const CheckoutGithubSetAutoMergeRequestSchema = z.object({ type: z.literal("checkout.github.set_auto_merge.request"), cwd: z.string(), enabled: z.boolean(), mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(), requestId: z.string(), }); const CheckoutCommitFileSchema = z.object({ path: z.string(), additions: z.number(), deletions: z.number(), status: z.enum(["added", "modified", "deleted", "renamed"]).optional(), }); const CheckoutCommitSchema = z.object({ sha: z.string(), shortSha: z.string(), subject: z.string(), authorName: z.string(), authorDate: z.string(), // ISO 8601 isOnRemote: z.boolean(), // false = local-only (unpushed) // COMPAT(commitBaseClassification): added in v0.2.0, remove optional after 2027-01-23. isOnBase: z.boolean().optional(), files: z.array(CheckoutCommitFileSchema), }); export const CheckoutCommitsListRequestSchema = z.object({ type: z.literal("checkout.commits.list.request"), cwd: z.string(), requestId: z.string(), }); export const CheckoutCommitFileDiffRequestSchema = z.object({ type: z.literal("checkout.commits.file_diff.request"), cwd: z.string(), sha: z.string(), path: z.string(), requestId: z.string(), }); const GitHubRepoSegmentSchema = z.string().regex(/^[A-Za-z0-9._-]+$/); const CheckoutCheckDetailsRequestPayloadSchema = z.object({ cwd: z.string(), // GitHub addresses check runs by owner/name. GitLab resolves the project from // cwd and omits these GitHub-only single-segment fields. repoOwner: GitHubRepoSegmentSchema.optional(), repoName: GitHubRepoSegmentSchema.optional(), // Permanently optional: a check addressed only by workflowRunId (Gitea // Actions runs carry no check-run id) is fetchable. Callers send at least one // of checkRunId/workflowRunId; the gated forge RPC only reaches daemons that // understand this. checkRunId: z.number().int().positive().optional(), workflowRunId: z.number().int().positive().optional(), // Permanent forge-routing field, optional because only some forges need it: // GitLab routes check details to the MR's head pipeline; Gitea-family adapters // resolve the PR head SHA by number, including after merge/close. GitHub // ignores it. changeRequestNumber: z.number().int().positive().optional(), requestId: z.string(), }); export const CheckoutForgeGetCheckDetailsRequestSchema = CheckoutCheckDetailsRequestPayloadSchema.extend({ type: z.literal("checkout.forge.get_check_details.request"), }); // COMPAT(githubCheckDetailsRpc): added in v0.1.106, remove after 2026-12-28 once // all supported clients use checkout.forge.get_check_details.*. export const CheckoutGithubGetCheckDetailsRequestSchema = CheckoutCheckDetailsRequestPayloadSchema.extend({ type: z.literal("checkout.github.get_check_details.request"), }); export const CheckoutPrStatusRequestSchema = z.object({ type: z.literal("checkout_pr_status_request"), cwd: z.string(), requestId: z.string(), }); export const PullRequestTimelineRequestSchema = z.object({ type: z.literal("pull_request_timeline_request"), cwd: z.string(), prNumber: z.number(), repoOwner: z.string(), repoName: z.string(), requestId: z.string(), }); export const ValidateBranchRequestSchema = z.object({ type: z.literal("validate_branch_request"), cwd: z.string(), branchName: z.string(), requestId: z.string(), }); export const CheckoutSwitchBranchRequestSchema = z.object({ type: z.literal("checkout_switch_branch_request"), cwd: z.string(), branch: z.string(), requestId: z.string(), }); export const CheckoutRenameBranchRequestSchema = z.object({ type: z.literal("checkout.rename_branch.request"), cwd: z.string(), branch: z.string(), requestId: z.string(), }); export const StashSaveRequestSchema = z.object({ type: z.literal("stash_save_request"), cwd: z.string(), /** Branch name to tag the stash with for later identification. */ branch: z.string().optional(), requestId: z.string(), }); export const StashPopRequestSchema = z.object({ type: z.literal("stash_pop_request"), cwd: z.string(), /** Zero-based index from stash_list_response. */ stashIndex: z.number().int().min(0), requestId: z.string(), }); export const StashListRequestSchema = z.object({ type: z.literal("stash_list_request"), cwd: z.string(), /** If true, only return paseo-created stashes. Default true. */ paseoOnly: z.boolean().optional(), requestId: z.string(), }); export const BranchSuggestionsRequestSchema = z.object({ type: z.literal("branch_suggestions_request"), cwd: z.string(), query: z.string().optional(), limit: z.number().int().min(1).max(200).optional(), requestId: z.string(), }); export const GitHubSearchItemSchema = z.object({ kind: z.enum(["issue", "pr"]), forge: z.string().optional(), number: z.number(), title: z.string(), url: z.string(), state: z.string(), body: z.string().nullable(), labels: z.array(z.string()), projectPath: z.string().optional(), baseRefName: z.string().nullable().optional(), headRefName: z.string().nullable().optional(), updatedAt: z.string().optional(), }); export const ForgeSearchItemSchema = GitHubSearchItemSchema.extend({ kind: z.enum(["issue", "change_request"]), }); // COMPAT(githubSearchKind): added in v0.1.106, remove with the legacy // github_search_request RPC after 2026-12-28. export const ForgeSearchKindSchema = z.enum([ "issue", "change_request", "github-issue", "github-pr", "pr", ]); export const GitHubSearchKindSchema = ForgeSearchKindSchema; export const ForgeSearchRequestSchema = z.object({ type: z.literal("forge.search.request"), cwd: z.string(), query: z.string(), limit: z.number().int().min(1).max(50).optional(), kinds: z.array(ForgeSearchKindSchema).optional(), requestId: z.string(), }); // COMPAT(githubSearchRpc): added in v0.1.106, remove after 2026-12-28 once // clients use forge.search.*. export const GitHubSearchRequestSchema = z.object({ type: z.literal("github_search_request"), cwd: z.string(), query: z.string(), limit: z.number().int().min(1).max(50).optional(), kinds: z.array(GitHubSearchKindSchema).optional(), requestId: z.string(), }); export const DirectorySuggestionsRequestSchema = z.object({ type: z.literal("directory_suggestions_request"), query: z.string(), cwd: z.string().optional(), includeFiles: z.boolean().optional(), includeDirectories: z.boolean().optional(), matchMode: z.enum(["fuzzy", "suffix"]).optional(), limit: z.number().int().min(1).max(100).optional(), requestId: z.string(), }); export const PaseoWorktreeListRequestSchema = z.object({ type: z.literal("paseo_worktree_list_request"), cwd: z.string().optional(), repoRoot: z.string().optional(), requestId: z.string(), }); export const PaseoWorktreeArchiveRequestSchema = z.object({ type: z.literal("paseo_worktree_archive_request"), worktreePath: z.string().optional(), repoRoot: z.string().optional(), branchName: z.string().optional(), // COMPAT(worktreeArchiveWorkspaceId): added in v0.1.97, drop the optional gate when floor >= v0.1.97. // Explicit workspace record to archive. A directory can back multiple workspaces // (Model B), so resolving the target by cwd alone picks the wrong record. When // present the daemon archives this exact workspace; when absent it falls back to // resolving by worktreePath, preferring the worktree-kind record on a cwd tie. workspaceId: z.string().optional(), // COMPAT(worktreeArchiveScope): added in v0.1.97, drop the gate when floor >= v0.1.97. // Scope of the archive operation. "workspace" archives a single workspace record // (today's default UI behavior). "worktree" archives every active workspace whose // cwd resolves to the target directory, then removes the directory if it is // Paseo-owned. Omitted/unknown values default to "workspace" for old-client safety. scope: z.enum(["workspace", "worktree"]).optional().default("workspace"), // COMPAT(worktreeDiskDeletion): added in v0.1.97, ignored as of v0.1.97 // (disk removal derived from scope + last-reference + ownership); field // retained for wire parse-compat, drop when floor >= v0.1.97. deleteWorktreeFromDisk: z.boolean().optional().default(false), requestId: z.string(), }); export const FirstAgentContextSchema = z.object({ prompt: z.string().optional(), attachments: AgentAttachmentsSchema, }); export const CreatePaseoWorktreeRequestSchema = z.object({ type: z.literal("create_paseo_worktree_request"), cwd: z.string(), projectId: z.string().optional(), worktreeSlug: z.string().optional(), nameContext: z.string().optional(), attachments: AgentAttachmentsSchema.optional(), firstAgentContext: FirstAgentContextSchema.optional(), refName: z.string().min(1).optional(), action: z.enum(["branch-off", "checkout"]).optional(), checkoutSource: ChangeRequestCheckoutSourceSchema.optional(), // COMPAT(githubPrNumber): added in v0.1.106, remove after 2026-12-28 once // clients send checkoutSource: { kind: "change_request", forge, number }. githubPrNumber: z.number().int().positive().optional(), requestId: z.string(), }); export const WorkspaceSetupStatusRequestSchema = z.object({ type: z.literal("workspace_setup_status_request"), workspaceId: z.string(), requestId: z.string(), }); // COMPAT(desktopEditorBridge): added in v0.1.88, remove after 2026-12-03 once old clients no longer call daemon editor RPCs. export const LegacyListAvailableEditorsRequestSchema = z.object({ type: z.literal("list_available_editors_request"), requestId: z.string(), }); export const LegacyOpenInEditorRequestSchema = z.object({ type: z.literal("open_in_editor_request"), path: z.string(), editorId: z.string().trim().min(1), mode: z.enum(["open", "reveal"]).optional(), cwd: z.string().optional(), requestId: z.string(), }); export const OpenProjectRequestSchema = z.object({ type: z.literal("open_project_request"), // Path used only for workspace lookup/creation. Use the returned workspace.id for all subsequent references. cwd: z.string(), requestId: z.string(), }); // Smallest shorthand repo path is "a/b": owner, slash, repository. const MIN_REPOSITORY_PATH_LENGTH = 3; export const ProjectAddRequestSchema = z.object({ type: z.literal("project.add.request"), cwd: z.string(), requestId: z.string(), }); export const ProjectCreateDirectoryRequestSchema = z.object({ type: z.literal("project.create_directory.request"), parentPath: z.string(), name: z.string(), requestId: z.string(), }); export const GithubRepositorySchema = z.object({ id: z.string().min(1), name: z.string().min(1), nameWithOwner: z.string().min(MIN_REPOSITORY_PATH_LENGTH), description: z.string().nullable(), visibility: z.enum(["public", "private", "internal"]), updatedAt: z.string(), cloneUrl: z.string().min(MIN_REPOSITORY_PATH_LENGTH), }); export const WorkspaceGithubSearchRepositoriesRequestSchema = z.object({ type: z.literal("workspace.github.search_repositories.request"), query: z.string(), limit: z.number().int().min(1).max(50).optional(), requestId: z.string(), }); export const ProjectGithubCloneProtocolSchema = z.enum(["https", "ssh"]); export const ProjectGithubCloneRequestSchema = z.object({ type: z.literal("project.github.clone.request"), repo: z.string().trim().min(MIN_REPOSITORY_PATH_LENGTH), cloneProtocol: ProjectGithubCloneProtocolSchema.optional(), targetDirectory: z.string().trim().min(1), requestId: z.string(), }); export const ArchiveWorkspaceRequestSchema = z.object({ type: z.literal("archive_workspace_request"), workspaceId: z.string(), requestId: z.string(), }); // Create a new workspace record. Unlike open_project, this never deduplicates by // directory: it always produces a fresh workspace. The source discriminates // between an existing local directory and a newly created paseo worktree. export const WorkspaceCreateRequestSchema = z.object({ type: z.literal("workspace.create.request"), requestId: z.string(), // Optional user-set title applied to the created workspace. title: z.string().optional(), // Optional prompt context for workspace-level name/branch generation. firstAgentContext: FirstAgentContextSchema.optional(), source: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("directory"), // Path of the existing checkout/directory to back the workspace. path: z.string(), projectId: z.string().optional(), }), z.object({ kind: z.literal("worktree"), // The project whose repo the worktree is cut from. cwd: z.string().optional(), projectId: z.string().optional(), action: z.enum(["branch-off", "checkout"]).optional(), // Target branch for checkout, or base ref for branch-off. refName: z.string().min(1).optional(), baseBranch: z.string().optional(), // New branch name for branch-off. The worktree path may use a different slug. branchName: z.string().min(1).optional(), checkoutSource: ChangeRequestCheckoutSourceSchema.optional(), // COMPAT(githubPrNumber): added in v0.1.106, remove after 2026-12-28 once // clients send checkoutSource. githubPrNumber: z.number().int().positive().optional(), worktreeSlug: z.string().optional(), }), ]), }); export const WorkspaceClearAttentionRequestSchema = z.object({ type: z.literal("workspace.clear_attention.request"), workspaceId: z.union([z.string(), z.array(z.string())]), requestId: z.string(), }); // Highlighted diff token schema // Note: style can be a compound class name (e.g., "heading meta") from the syntax highlighter const HighlightTokenSchema = z.object({ text: z.string(), style: z.string().nullable(), }); const DiffLineSchema = z.object({ type: z.enum(["add", "remove", "context", "header"]), content: z.string(), tokens: z.array(HighlightTokenSchema).optional(), }); const DiffHunkSchema = z.object({ oldStart: z.number(), oldCount: z.number(), newStart: z.number(), newCount: z.number(), lines: z.array(DiffLineSchema), }); const ParsedDiffFileSchema = z.object({ path: z.string(), // COMPAT(diffOldPath): added in v0.3.0, remove gate after 2027-02-09. oldPath: z.string().optional(), isNew: z.boolean(), isDeleted: z.boolean(), additions: z.number(), deletions: z.number(), hunks: z.array(DiffHunkSchema), status: z.enum(["ok", "too_large", "binary"]).optional(), }); const FileExplorerEntrySchema = z.object({ name: z.string(), path: z.string(), kind: z.enum(["file", "directory"]), size: z.number(), modifiedAt: z.string(), }); const FileExplorerFileSchema = z.object({ path: z.string(), kind: z.enum(["text", "image", "binary"]), encoding: z.enum(["utf-8", "base64", "none"]), content: z.string().optional(), mimeType: z.string().optional(), size: z.number(), modifiedAt: z.string(), revision: z.string().optional(), }); const FileExplorerDirectorySchema = z.object({ path: z.string(), entries: z.array(FileExplorerEntrySchema), }); export const FileExplorerRequestSchema = z.object({ type: z.literal("file_explorer_request"), cwd: z.string(), path: z.string().optional(), mode: z.enum(["list", "file"]), requestId: z.string(), acceptBinary: z.boolean().optional(), }); export const FileVersionSchema = z.discriminatedUnion("status", [ z.object({ status: z.literal("ready"), cwd: z.string(), path: z.string(), size: z.number().int().nonnegative(), modifiedAt: z.string(), revision: z.string().optional(), }), z.object({ status: z.literal("missing"), cwd: z.string(), path: z.string(), }), z.object({ status: z.literal("error"), cwd: z.string(), path: z.string(), error: z.string(), }), ]); export const FileSubscribeRequestSchema = z.object({ type: z.literal("fs.file.subscribe.request"), cwd: z.string(), path: z.string(), subscriptionId: z.string(), requestId: z.string(), }); export const FileUnsubscribeRequestSchema = z.object({ type: z.literal("fs.file.unsubscribe.request"), subscriptionId: z.string(), requestId: z.string(), }); export const FileWriteRequestSchema = z.object({ type: z.literal("fs.file.write.request"), cwd: z.string(), path: z.string(), content: z.string(), expectedModifiedAt: z.string(), expectedRevision: z.string().optional(), requestId: z.string(), }); export const FileEntryCreateRequestSchema = z.object({ type: z.literal("fs.entry.create.request"), cwd: z.string(), parentPath: z.string(), name: z.string(), kind: z.enum(["file", "directory"]), requestId: z.string(), }); export const FileEntryRenameRequestSchema = z.object({ type: z.literal("fs.entry.rename.request"), cwd: z.string(), path: z.string(), name: z.string(), requestId: z.string(), }); export const FileEntryDuplicateRequestSchema = z.object({ type: z.literal("fs.entry.duplicate.request"), cwd: z.string(), path: z.string(), requestId: z.string(), }); export const FileEntryDeleteRequestSchema = z.object({ type: z.literal("fs.entry.delete.request"), cwd: z.string(), path: z.string(), requestId: z.string(), }); export const ProjectIconRequestSchema = z.object({ type: z.literal("project_icon_request"), cwd: z.string(), requestId: z.string(), }); export const ProjectIconGetRequestSchema = z.object({ type: z.literal("project.icon.get.request"), projectId: z.string(), requestId: z.string(), }); export const FileDownloadTokenRequestSchema = z.object({ type: z.literal("file_download_token_request"), cwd: z.string(), path: z.string(), requestId: z.string(), }); export const FileUploadRequestSchema = z.object({ type: z.literal("file.upload.request"), fileName: z.string().min(1), mimeType: z.string().min(1), size: z.number().int().nonnegative(), modifiedAt: z.string(), requestId: z.string(), }); export const ClearAgentAttentionMessageSchema = z.object({ type: z.literal("clear_agent_attention"), agentId: z.union([z.string(), z.array(z.string())]), requestId: z.string().optional(), }); export const ClientHeartbeatMessageSchema = z.object({ type: z.literal("client_heartbeat"), deviceType: z.enum(["web", "mobile"]), focusedAgentId: z.string().nullable(), // COMPAT(terminalFocusHeartbeat): added in v0.1.97, remove optional default after 2026-12-13 once old clients no longer send heartbeats without terminal focus. focusedTerminalId: z.string().nullable().optional().default(null), lastActivityAt: z.string(), appVisible: z.boolean(), appVisibilityChangedAt: z.string().optional(), }); export const PingMessageSchema = z.object({ type: z.literal("ping"), requestId: z.string(), clientSentAt: z.number().int().optional(), }); const ListCommandsDraftConfigSchema = z.object({ provider: AgentProviderSchema, cwd: z.string(), modeId: z.string().optional(), model: z.string().optional(), thinkingOptionId: z.string().optional(), featureValues: z.record(z.string(), z.unknown()).optional(), }); export const ListProviderFeaturesRequestMessageSchema = z.object({ type: z.literal("list_provider_features_request"), draftConfig: ListCommandsDraftConfigSchema, requestId: z.string(), }); export const ListCommandsRequestSchema = z.object({ type: z.literal("list_commands_request"), agentId: z.string(), draftConfig: ListCommandsDraftConfigSchema.optional(), requestId: z.string(), }); export const RegisterPushTokenMessageSchema = z.object({ type: z.literal("register_push_token"), token: z.string(), }); export const PushUnregisterRequestSchema = z.object({ type: z.literal("push.unregister.request"), token: z.string(), requestId: z.string(), }); export const PushUnregisterResponseSchema = z.object({ type: z.literal("push.unregister.response"), payload: z.object({ requestId: z.string(), }), }); // ============================================================================ // Terminal Messages // ============================================================================ export const ListTerminalsRequestSchema = z.object({ type: z.literal("list_terminals_request"), cwd: z.string().optional(), workspaceId: z.string().optional(), requestId: z.string(), }); export const SubscribeTerminalsRequestSchema = z.object({ type: z.literal("subscribe_terminals_request"), cwd: z.string(), workspaceId: z.string().optional(), }); export const UnsubscribeTerminalsRequestSchema = z.object({ type: z.literal("unsubscribe_terminals_request"), cwd: z.string(), workspaceId: z.string().optional(), }); export const CreateTerminalRequestSchema = z.object({ type: z.literal("create_terminal_request"), cwd: z.string(), workspaceId: z.string().optional(), name: z.string().optional(), agentId: z.string().optional(), command: z.string().optional(), args: z.array(z.string()).optional(), // Initial PTY size. Added in v0.1.107; the app no longer sends it (the estimate cache that fed // it was removed — the pane-focus resize claim sizes the PTY instead). Kept and honored // permanently: released v0.1.107 clients still send it, and programmatic callers may pass an // exact size. Daemons without it start at 80x24 and the first resize corrects that. size: z .object({ rows: z.number().int().positive(), cols: z.number().int().positive(), }) .optional(), requestId: z.string(), }); export const RenameTerminalRequestSchema = z.object({ type: z.literal("terminal.rename.request"), terminalId: z.string(), title: z.string(), requestId: z.string(), }); export const StartWorkspaceScriptRequestSchema = z.object({ type: z.literal("start_workspace_script_request"), workspaceId: z.string(), scriptName: z.string(), requestId: z.string(), }); export const WorkspaceScriptListRequestSchema = z.object({ type: z.literal("workspace.script.list.request"), workspaceId: z.string(), requestId: z.string(), }); export const WorkspaceScriptStartRequestSchema = z.object({ type: z.literal("workspace.script.start.request"), workspaceId: z.string(), scriptName: z.string(), requestId: z.string(), }); export const WorkspaceScriptStopRequestSchema = z.object({ type: z.literal("workspace.script.stop.request"), workspaceId: z.string(), scriptName: z.string(), requestId: z.string(), }); export const SubscribeTerminalRequestSchema = z.object({ type: z.literal("subscribe_terminal_request"), terminalId: z.string(), requestId: z.string(), restore: z .object({ mode: z.enum(["live", "visible-snapshot", "full-snapshot"]), scrollbackLines: z.number().int().nonnegative().optional(), size: z .object({ rows: z.number().int().positive(), cols: z.number().int().positive(), }) .optional(), }) .optional(), }); export const UnsubscribeTerminalRequestSchema = z.object({ type: z.literal("unsubscribe_terminal_request"), terminalId: z.string(), }); const TerminalClientMessageSchema = z.discriminatedUnion("type", [ z.object({ type: z.literal("input"), data: z.string() }), z.object({ type: z.literal("resize"), rows: z.number(), cols: z.number(), intent: z.enum(["claim", "update"]).optional(), }), z.object({ type: z.literal("mouse"), row: z.number(), col: z.number(), button: z.number(), action: z.enum(["down", "up", "move"]), }), ]); export const TerminalInputSchema = z.object({ type: z.literal("terminal_input"), terminalId: z.string(), message: TerminalClientMessageSchema, }); export const KillTerminalRequestSchema = z.object({ type: z.literal("kill_terminal_request"), terminalId: z.string(), requestId: z.string(), }); export const CaptureTerminalRequestSchema = z.object({ type: z.literal("capture_terminal_request"), terminalId: z.string(), start: z.number().int().optional(), end: z.number().int().optional(), stripAnsi: z.boolean().default(true), requestId: z.string(), }); export const HubExecutionAgentCreateRequestSchema = z.object({ type: z.literal("hub.execution.agent.create.request"), requestId: z.string(), executionId: z.string(), provider: z.string(), cwd: z.string(), prompt: z.string(), // COMPAT(hubExecutionWorkspaceSelection): semantics retired in v0.3.1; remove after 2027-08-08 once the Hub floor no longer sends it. workspaceId: z.string().optional(), model: z.string().optional(), modeId: z.string().optional(), thinkingOptionId: z.string().optional(), featureValues: z.record(z.string(), z.unknown()).optional(), providerOptions: ProviderOptionsSchema.optional(), toolPolicy: ToolPolicySchema.optional(), env: z.record(z.string(), z.string()).optional(), mcpServers: z.record(z.string(), McpServerConfigSchema).optional(), worktree: CreateAgentWorktreeTargetSchema.optional(), }); export type HubExecutionAgentCreateRequest = z.infer; export const HubExecutionAgentValidateRequestSchema = z.object({ type: z.literal("hub.execution.agent.validate.request"), requestId: z.string(), provider: z.string(), model: z.string().optional(), modeId: z.string().optional(), thinkingOptionId: z.string().optional(), providerOptions: ProviderOptionsSchema.optional(), }); export type HubExecutionAgentValidateRequest = z.infer< typeof HubExecutionAgentValidateRequestSchema >; const HubExecutionAgentCreateErrorSchema = z.discriminatedUnion("code", [ z.object({ code: z.literal("provider_options_invalid"), provider: z.string(), issues: z.array( z.object({ path: z.array(z.union([z.string(), z.number()])), message: z.string(), }), ), message: z.string(), }), z.object({ code: z.literal("tool_policy_unsupported"), provider: z.string(), message: z.string(), }), z.object({ code: z.literal("create_failed"), message: z.string(), }), ]); export type HubExecutionAgentCreateError = z.infer; export const HubExecutionControlActionSchema = z.enum(["interrupt", "archive"]); export type HubExecutionControlAction = z.infer; export const HubExecutionControlRequestSchema = z.object({ type: z.literal("hub.execution.control.request"), requestId: z.string(), executionId: z.string(), action: HubExecutionControlActionSchema, }); export type HubExecutionControlRequest = z.infer; export const SessionInboundMessageSchema = z.discriminatedUnion("type", [ HubExecutionAgentCreateRequestSchema, HubExecutionAgentValidateRequestSchema, HubExecutionControlRequestSchema, BrowserAutomationExecuteResponseSchema, VoiceAudioChunkMessageSchema, AbortRequestMessageSchema, AudioPlayedMessageSchema, FetchAgentsRequestMessageSchema, FetchAgentHistoryRequestMessageSchema, FetchRecentProviderSessionsRequestMessageSchema, FetchWorkspacesRequestMessageSchema, ProjectListRequestMessageSchema, FetchAgentRequestMessageSchema, DeleteAgentRequestMessageSchema, ArchiveAgentRequestMessageSchema, CloseItemsRequestMessageSchema, UpdateAgentRequestMessageSchema, ProjectRenameRequestSchema, ProjectIconSetRequestSchema, ProjectRemoveRequestSchema, WorkspaceTitleSetRequestSchema, WorkspacePinSetRequestSchema, WorkspaceLabelListRequestSchema, WorkspaceLabelAssignmentSetRequestSchema, WorkspaceLabelUpdateRequestSchema, WorkspaceLabelDeleteRequestSchema, WorkspaceLabelDeleteInspectRequestSchema, WorkspaceRecoveryInspectRequestSchema, WorkspaceRecoveryRestoreRequestSchema, SetVoiceModeMessageSchema, SendAgentMessageRequestSchema, WaitForFinishRequestSchema, DaemonGetStatusRequestSchema, DaemonGetPairingOfferRequestSchema, DaemonConfigReloadRequestSchema, HubManagementDaemonConnectRequestSchema, HubManagementDaemonGetStatusRequestSchema, HubManagementDaemonDisconnectRequestSchema, DiagnosticsRequestSchema, PluginCatalogGetRequestSchema, PluginListRequestSchema, PluginLogsGetRequestSchema, PluginDirectoryInstallRequestSchema, PluginDirectoryInspectRequestSchema, PluginReloadRequestSchema, PluginEnableRequestSchema, PluginDisableRequestSchema, PluginRemoveRequestSchema, PluginRpcInvokeRequestSchema, AgentSkillsGetStatusRequestSchema, AgentSkillsReconcileRequestSchema, AgentSkillsUninstallRequestSchema, AgentSkillsSaveSelectionRequestSchema, AgentSkillsImportLegacySelectionRequestSchema, GetDaemonConfigRequestMessageSchema, SetDaemonConfigRequestMessageSchema, ReadProjectConfigRequestMessageSchema, WriteProjectConfigRequestMessageSchema, DictationStreamStartMessageSchema, DictationStreamChunkMessageSchema, DictationStreamFinishMessageSchema, DictationStreamCancelMessageSchema, CreateAgentRequestMessageSchema, ListProviderModelsRequestMessageSchema, ListProviderModesRequestMessageSchema, ListProviderFeaturesRequestMessageSchema, ListAvailableProvidersRequestMessageSchema, GetProvidersSnapshotRequestMessageSchema, RefreshProvidersSnapshotRequestMessageSchema, ProviderDiagnosticRequestMessageSchema, ProviderUsageListRequestMessageSchema, ResumeAgentRequestMessageSchema, ImportAgentRequestMessageSchema, RefreshAgentRequestMessageSchema, CancelAgentRequestMessageSchema, ShutdownServerRequestMessageSchema, RestartServerRequestMessageSchema, DaemonUpdateRequestMessageSchema, FetchAgentTimelineRequestMessageSchema, AgentTimelineListPromptsRequestMessageSchema, ProviderSubagentListRequestMessageSchema, ProviderSubagentTimelineRequestMessageSchema, SetAgentTimelineSubscriptionRequestMessageSchema, AgentForkContextRequestMessageSchema, SetAgentModeRequestMessageSchema, SetAgentModelRequestMessageSchema, SetAgentThinkingRequestMessageSchema, SetAgentFeatureRequestMessageSchema, AgentConfigApplyRequestMessageSchema, AgentDetachRequestMessageSchema, AgentRewindRequestMessageSchema, AgentPermissionResponseMessageSchema, CheckoutStatusRequestSchema, SubscribeCheckoutDiffRequestSchema, UnsubscribeCheckoutDiffRequestSchema, CheckoutCommitRequestSchema, CheckoutMergeRequestSchema, CheckoutMergeFromBaseRequestSchema, CheckoutPullRequestSchema, CheckoutPushRequestSchema, CheckoutRefreshRequestSchema, CheckoutDiscardChangesRequestSchema, CheckoutPrCreateRequestSchema, CheckoutPrMergeRequestSchema, CheckoutForgeSetAutoMergeRequestSchema, CheckoutGithubSetAutoMergeRequestSchema, CheckoutCommitsListRequestSchema, CheckoutCommitFileDiffRequestSchema, CheckoutForgeGetCheckDetailsRequestSchema, CheckoutGithubGetCheckDetailsRequestSchema, CheckoutPrStatusRequestSchema, PullRequestTimelineRequestSchema, CheckoutSwitchBranchRequestSchema, CheckoutRenameBranchRequestSchema, StashSaveRequestSchema, StashPopRequestSchema, StashListRequestSchema, ValidateBranchRequestSchema, BranchSuggestionsRequestSchema, ForgeSearchRequestSchema, GitHubSearchRequestSchema, DirectorySuggestionsRequestSchema, PaseoWorktreeListRequestSchema, PaseoWorktreeArchiveRequestSchema, CreatePaseoWorktreeRequestSchema, WorkspaceSetupStatusRequestSchema, LegacyListAvailableEditorsRequestSchema, LegacyOpenInEditorRequestSchema, OpenProjectRequestSchema, ProjectAddRequestSchema, ProjectCreateDirectoryRequestSchema, WorkspaceGithubSearchRepositoriesRequestSchema, ProjectGithubCloneRequestSchema, ArchiveWorkspaceRequestSchema, WorkspaceCreateRequestSchema, WorkspaceClearAttentionRequestSchema, FileExplorerRequestSchema, FileSubscribeRequestSchema, FileUnsubscribeRequestSchema, FileWriteRequestSchema, FileEntryCreateRequestSchema, FileEntryRenameRequestSchema, FileEntryDuplicateRequestSchema, FileEntryDeleteRequestSchema, ProjectIconRequestSchema, ProjectIconGetRequestSchema, FileDownloadTokenRequestSchema, FileUploadRequestSchema, ClearAgentAttentionMessageSchema, ClientHeartbeatMessageSchema, PingMessageSchema, ListCommandsRequestSchema, RegisterPushTokenMessageSchema, PushUnregisterRequestSchema, ListTerminalsRequestSchema, SubscribeTerminalsRequestSchema, UnsubscribeTerminalsRequestSchema, CreateTerminalRequestSchema, RenameTerminalRequestSchema, StartWorkspaceScriptRequestSchema, WorkspaceScriptListRequestSchema, WorkspaceScriptStartRequestSchema, WorkspaceScriptStopRequestSchema, SubscribeTerminalRequestSchema, UnsubscribeTerminalRequestSchema, TerminalInputSchema, KillTerminalRequestSchema, CaptureTerminalRequestSchema, ChatCreateRequestSchema, ChatListRequestSchema, ChatInspectRequestSchema, ChatDeleteRequestSchema, ChatPostRequestSchema, ChatReadRequestSchema, ChatWaitRequestSchema, ScheduleCreateRequestSchema, ScheduleListRequestSchema, ScheduleInspectRequestSchema, ScheduleLogsRequestSchema, SchedulePauseRequestSchema, ScheduleResumeRequestSchema, ScheduleDeleteRequestSchema, ScheduleRunOnceRequestSchema, ScheduleUpdateRequestSchema, LoopRunRequestSchema, LoopListRequestSchema, LoopInspectRequestSchema, LoopLogsRequestSchema, LoopStopRequestSchema, ]); export type SessionInboundMessage = z.infer; // ============================================================================ // Session Outbound Messages (Session emits these) // ============================================================================ export const ActivityLogPayloadSchema = z.object({ id: z.string(), timestamp: z.coerce.date(), type: z.enum(["transcript", "assistant", "tool_call", "tool_result", "error", "system"]), content: z.string(), metadata: z.record(z.string(), z.unknown()).optional(), }); export const ActivityLogMessageSchema = z.object({ type: z.literal("activity_log"), payload: ActivityLogPayloadSchema, }); export const AssistantChunkMessageSchema = z.object({ type: z.literal("assistant_chunk"), payload: z.object({ chunk: z.string(), }), }); export const AudioOutputMessageSchema = z.object({ type: z.literal("audio_output"), payload: z.object({ audio: z.string(), // base64 encoded format: z.string(), id: z.string(), isVoiceMode: z.boolean(), // Mode when audio was generated (for drift protection) groupId: z.string().optional(), // Logical utterance id chunkIndex: z.number().int().nonnegative().optional(), isLastChunk: z.boolean().optional(), }), }); export const TranscriptionResultMessageSchema = z.object({ type: z.literal("transcription_result"), payload: z.object({ text: z.string(), language: z.string().optional(), duration: z.number().optional(), requestId: z.string(), // Echoed back from request for tracking avgLogprob: z.number().optional(), isLowConfidence: z.boolean().optional(), byteLength: z.number().optional(), format: z.string().optional(), debugRecordingPath: z.string().optional(), }), }); export const VoiceInputStateMessageSchema = z.object({ type: z.literal("voice_input_state"), payload: z.object({ isSpeaking: z.boolean(), }), }); export const DictationStreamAckMessageSchema = z.object({ type: z.literal("dictation_stream_ack"), payload: z.object({ dictationId: z.string(), ackSeq: z.number().int(), }), }); export const DictationStreamFinishAcceptedMessageSchema = z.object({ type: z.literal("dictation_stream_finish_accepted"), payload: z.object({ dictationId: z.string(), timeoutMs: z.number().int().positive(), }), }); export const DictationStreamPartialMessageSchema = z.object({ type: z.literal("dictation_stream_partial"), payload: z.object({ dictationId: z.string(), text: z.string(), }), }); export const DictationStreamFinalMessageSchema = z.object({ type: z.literal("dictation_stream_final"), payload: z.object({ dictationId: z.string(), text: z.string(), debugRecordingPath: z.string().optional(), }), }); export const DictationStreamErrorMessageSchema = z.object({ type: z.literal("dictation_stream_error"), payload: z.object({ dictationId: z.string(), error: z.string(), retryable: z.boolean(), reasonCode: z.string().optional(), missingModelIds: z.array(z.string()).optional(), debugRecordingPath: z.string().optional(), }), }); export const ServerCapabilityStateSchema = z.object({ enabled: z.boolean(), reason: z.string(), }); export const ServerVoiceCapabilitiesSchema = z.object({ dictation: ServerCapabilityStateSchema, voice: ServerCapabilityStateSchema, }); export const ServerCapabilitiesSchema = z .object({ voice: ServerVoiceCapabilitiesSchema.optional(), }) .passthrough(); const ServerInfoHostnameSchema = z.unknown().transform((value): string | null => { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; }); const ServerInfoVersionSchema = z.unknown().transform((value): string | null => { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; }); const ServerCapabilitiesFromUnknownSchema = z .unknown() .optional() .transform((value): z.infer | undefined => { if (value === undefined) { return undefined; } const parsed = ServerCapabilitiesSchema.safeParse(value); if (!parsed.success) { return undefined; } return parsed.data; }); export const ServerInfoStatusPayloadSchema = z .object({ status: z.literal("server_info"), serverId: z.string().trim().min(1), hostname: ServerInfoHostnameSchema.optional(), version: ServerInfoVersionSchema.optional(), // COMPAT(desktopManaged): added in v0.1.X, remove optional parsing after 2027-01-16. desktopManaged: z.boolean().optional(), capabilities: ServerCapabilitiesFromUnknownSchema.optional(), // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot features: z .object({ providersSnapshot: z.boolean().optional(), // COMPAT(providersSnapshotCwd): added in v0.3.2, remove gate after 2027-02-10. providersSnapshotCwd: z.boolean().optional(), // COMPAT(directorySync): added in v0.3.x, remove gate after 2027-02-12. directorySync: z.boolean().optional(), // COMPAT(workspaceLabels): added in v0.5.0, remove after 2027-08-14. workspaceLabels: z.boolean().optional(), // COMPAT(checkoutForgeSetAutoMerge): added in v0.1.106, remove old // checkoutGithubSetAutoMerge fallback after 2026-12-28. checkoutForgeSetAutoMerge: z.boolean().optional(), checkoutGithubSetAutoMerge: z.boolean().optional(), // COMPAT(githubCheckDetails): added in v0.1.92, remove gate after 2026-12-08. githubCheckDetails: z.boolean().optional(), // COMPAT(forgeCheckDetails): added in v0.1.106, remove githubCheckDetails fallback after 2026-12-28. forgeCheckDetails: z.boolean().optional(), // COMPAT(forgeSearch): added in v0.1.106, remove github_search fallback after 2026-12-28. forgeSearch: z.boolean().optional(), // COMPAT(daemonStatusRpc): added in v0.1.76, remove gate after 2026-11-18. daemonStatusRpc: z.boolean().optional(), // COMPAT(daemonConfigReload): added in v0.4.0, remove gate after 2027-02-14. daemonConfigReload: z.boolean().optional(), // COMPAT(relayConfig): added in v0.2.6, remove gate after 2027-01-31. relayConfig: z.boolean().optional(), // COMPAT(pushTokenRevocation): added in v0.3.2, remove gate after 2027-02-10. pushTokenRevocation: z.boolean().optional(), // COMPAT(plugins): added in v0.3.0, remove gate after 2027-08-07. plugins: z.boolean().optional(), // COMPAT(pluginManagement): added in v0.4.0, remove gate after 2027-08-14. pluginManagement: z.boolean().optional(), // COMPAT(pluginLogs): added in v0.4.0, remove gate after 2027-08-16. pluginLogs: z.boolean().optional(), // COMPAT(pluginThemes): added in v0.5.0, remove gate after 2027-08-20. // A daemon that predates this flag keeps `addTheme` in the server bundle it compiles, // so a theme plugin cannot start there at all. pluginThemes: z.boolean().optional(), // COMPAT(skillManagement): added in v0.4.0, remove gate after 2027-08-16. skillManagement: z.boolean().optional(), // COMPAT(terminalRestoreModes): added in v0.1.81, remove gate after 2026-11-23. "terminal-restore-modes": z.boolean().optional(), // COMPAT(terminalInputModeReplay): added in v0.2.6, remove gate after 2027-02-02. "terminal-input-mode-replay": z.boolean().optional(), // COMPAT(terminalSizeOwnership): added in v0.2.6, remove gate after 2027-02-02. "terminal-size-ownership": z.boolean().optional(), // COMPAT(rewind): added in v0.1.X, drop the gate when floor >= v0.1.X. rewind: z.boolean().optional(), // COMPAT(agentTimelinePromptIndex): added in v0.2.X, drop the gate when floor >= v0.2.X. agentTimelinePromptIndex: z.boolean().optional(), // COMPAT(agentHistorySearch): added in v0.3.0, remove gate after 2027-02-07. agentHistorySearch: z.boolean().optional(), // COMPAT(checkoutRefresh): added in v0.1.86, remove gate after 2026-11-29. checkoutRefresh: z.boolean().optional(), // COMPAT(workspaceMultiplicity): added in v0.1.97, drop the gate when floor >= v0.1.97 workspaceMultiplicity: z.boolean().optional(), // COMPAT(projectRemove): added in v0.1.97, drop the gate when floor >= v0.1.97. projectRemove: z.boolean().optional(), // COMPAT(projectAdd): added in v0.1.97, drop the gate when floor >= v0.1.97. projectAdd: z.boolean().optional(), // COMPAT(worktreeRestore): added in v0.1.97, drop the gate when floor >= v0.1.97 worktreeRestore: z.boolean().optional(), // COMPAT(workspaceRecovery): added in v0.1.105, remove after 2027-01-11 once daemon floor >= v0.1.105. workspaceRecovery: z.boolean().optional(), // COMPAT(workspaceFileEditing): added in v0.2.0, remove after 2027-01-18 once daemon floor >= v0.2.0. workspaceFileEditing: z.boolean().optional(), // COMPAT(providerUsageList): added in v0.1.98, drop the gate when daemon floor >= v0.1.98. providerUsageList: z.boolean().optional(), // COMPAT(agentDetach): added in v0.1.98, remove gate after 2026-12-19 once daemon floor >= v0.1.98. agentDetach: z.boolean().optional(), // COMPAT(agentThinkingUpdate): added in v0.2.4, remove gate after 2027-01-28. agentThinkingUpdate: z.boolean().optional(), // COMPAT(daemonDiagnostics): added in v0.1.100, remove gate after 2026-12-25 once daemon floor >= v0.1.100. daemonDiagnostics: z.boolean().optional(), // COMPAT(daemonSelfUpdate): added in v0.1.93, remove gate after 2026-12-13. daemonSelfUpdate: z.boolean().optional(), // COMPAT(agentForkContext): added in v0.1.102, remove gate after 2026-12-28. agentForkContext: z.boolean().optional(), // COMPAT(agentForkContextCursor): added in v0.1.108, remove gate after 2027-01-14. agentForkContextCursor: z.boolean().optional(), // COMPAT(providerSubagents): added in v0.1.107, remove gate after 2027-01-12. providerSubagents: z.boolean().optional(), // COMPAT(workspacePinning): added in v0.1.107, remove gate after 2027-01-12. workspacePinning: z.boolean().optional(), // COMPAT(hubRelationship): added in v0.1.X, drop the gate when floor >= v0.1.X. hubRelationship: z.boolean().optional(), // COMPAT(projectGithubClone): added in v0.1.108, remove gate after 2027-01-15. projectGithubClone: z.boolean().optional(), // COMPAT(workspaceGithubRepositorySearch): added in v0.1.108, remove gate after 2027-01-15. workspaceGithubRepositorySearch: z.boolean().optional(), // COMPAT(projectCreateDirectory): added in v0.1.108, remove gate after 2027-01-15. projectCreateDirectory: z.boolean().optional(), // COMPAT(projectList): added in v0.2.4, drop the gate when floor >= v0.2.4. projectList: z.boolean().optional(), // COMPAT(commitsList): added in v0.1.110, remove gate after 2027-01-16. commitsList: z.boolean().optional(), // COMPAT(commitBaseClassification): added in v0.2.0, remove gate after 2027-01-23. commitBaseClassification: z.boolean().optional(), // COMPAT(providerRemoval): added in v0.1.105, drop the gate when floor >= v0.1.105. providerRemoval: z.boolean().optional(), // COMPAT(importSessionWorkspaceTarget): added in v0.1.110, remove gate after 2027-01-16. importSessionWorkspaceTarget: z.boolean().optional(), // COMPAT(forgeProviders): added in v0.1.106, drop the gate when daemon floor >= v0.1.106. // Daemon advertises pluggable non-GitHub forge support (the forge registry); // the client gates non-GitHub setup UI on it. forgeProviders: z.boolean().optional(), // COMPAT(selectiveAgentTimeline): added in v0.1.106, remove after 2027-01-12. selectiveAgentTimeline: z.boolean().optional(), // COMPAT(canonicalSubmittedPrompts): added in v0.2.6, remove gate after 2027-01-30. canonicalSubmittedPrompts: z.boolean().optional(), // COMPAT(agentTurnIdentity): accept peers that observed pre-release v0.2.6 through 2027-01-31. agentTurnIdentity: z.boolean().optional(), // COMPAT(stableProjectIdentity): added in v0.1.109, remove gate after 2027-01-15. stableProjectIdentity: z.boolean().optional(), // COMPAT(workspaceScriptManagement): added in v0.1.105, remove gate after 2027-01-10. workspaceScriptManagement: z.boolean().optional(), // COMPAT(projectCustomIcon): added in v0.2.0, remove after 2027-01-20. projectCustomIcon: z.boolean().optional(), // COMPAT(fsEntryOps): added in v0.3.0, remove gate after 2027-02-08. fsEntryOps: z.boolean().optional(), // COMPAT(fsEntryDuplicate): added in v0.3.0, remove gate after 2027-02-09. fsEntryDuplicate: z.boolean().optional(), // COMPAT(checkoutDiscardChanges): added in v0.3.0, remove gate after 2027-02-08. checkoutDiscardChanges: z.boolean().optional(), // COMPAT(agentProfiles): added in v0.3.2, remove gate after 2027-02-11. // An older daemon parses its persisted config strictly, so writing // agentProfiles to one is silently dropped. The client hides the feature // rather than letting a save appear to succeed. agentProfiles: z.boolean().optional(), // COMPAT(agentConfigApply): added in v0.3.2, remove gate after 2027-02-11. agentConfigApply: z.boolean().optional(), }) .optional(), }) .passthrough() .transform((payload) => ({ ...payload, hostname: payload.hostname ?? null, version: payload.version ?? null, })); export const StatusMessageSchema = z.object({ type: z.literal("status"), payload: z .object({ status: z.string(), }) .passthrough(), // Allow additional fields }); export const PongMessageSchema = z.object({ type: z.literal("pong"), payload: z.object({ requestId: z.string(), clientSentAt: z.number().int().optional(), serverReceivedAt: z.number().int(), serverSentAt: z.number().int(), }), }); export const RpcErrorMessageSchema = z.object({ type: z.literal("rpc_error"), payload: z.object({ requestId: z.string(), requestType: z.string().optional(), error: z.string(), code: z.string().optional(), }), }); const AgentStatusWithRequestSchema = z.object({ agentId: z.string(), requestId: z.string(), }); const AgentStatusWithTimelineSchema = AgentStatusWithRequestSchema.extend({ timelineSize: z.number().optional(), }); export const AgentCreatedStatusPayloadSchema = z .object({ status: z.literal("agent_created"), agent: AgentSnapshotPayloadSchema, }) .extend(AgentStatusWithRequestSchema.shape); export const AgentCreateFailedStatusPayloadSchema = z.object({ status: z.literal("agent_create_failed"), requestId: z.string(), error: z.string(), errorCode: z.string().optional(), }); export const AgentResumedStatusPayloadSchema = z .object({ status: z.literal("agent_resumed"), agent: AgentSnapshotPayloadSchema, }) .extend(AgentStatusWithTimelineSchema.shape); export const AgentRefreshedStatusPayloadSchema = z .object({ status: z.literal("agent_refreshed"), }) .extend(AgentStatusWithTimelineSchema.shape); export const RestartRequestedStatusPayloadSchema = z.object({ status: z.literal("restart_requested"), clientId: z.string(), reason: z.string().optional(), requestId: z.string(), }); export const ShutdownRequestedStatusPayloadSchema = z.object({ status: z.literal("shutdown_requested"), clientId: z.string(), requestId: z.string(), }); export const DaemonConfigChangedStatusPayloadSchema = z .object({ status: z.literal("daemon_config_changed"), config: MutableDaemonConfigSchema, }) .passthrough(); export const PluginCatalogChangedStatusPayloadSchema = z.object({ status: z.literal("plugin_catalog_changed"), pluginId: PluginIdSchema, }); export const KnownStatusPayloadSchema = z.discriminatedUnion("status", [ AgentCreatedStatusPayloadSchema, AgentCreateFailedStatusPayloadSchema, AgentResumedStatusPayloadSchema, AgentRefreshedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, RestartRequestedStatusPayloadSchema, DaemonConfigChangedStatusPayloadSchema, PluginCatalogChangedStatusPayloadSchema, ]); export type KnownStatusPayload = z.infer; export const ArtifactMessageSchema = z.object({ type: z.literal("artifact"), payload: z.object({ type: z.enum(["markdown", "diff", "image", "code"]), id: z.string(), title: z.string(), content: z.string(), isBase64: z.boolean(), }), }); export const ProjectCheckoutLiteNotGitPayloadSchema = z .object({ cwd: z.string(), isGit: z.literal(false), currentBranch: z.null(), remoteUrl: z.null(), worktreeRoot: z.null().optional(), isPaseoOwnedWorktree: z.literal(false), mainRepoRoot: z.null(), }) .transform((value) => ({ ...value, worktreeRoot: null, })); export const ProjectCheckoutLiteGitNonPaseoPayloadSchema = z .object({ cwd: z.string(), isGit: z.literal(true), currentBranch: z.string().nullable(), remoteUrl: z.string().nullable(), worktreeRoot: z.string().optional(), isPaseoOwnedWorktree: z.literal(false), mainRepoRoot: z.string().nullable().optional().default(null), }) .transform((value) => ({ ...value, worktreeRoot: value.worktreeRoot ?? value.cwd, })); export const ProjectCheckoutLiteGitPaseoPayloadSchema = z .object({ cwd: z.string(), isGit: z.literal(true), currentBranch: z.string().nullable(), remoteUrl: z.string().nullable(), worktreeRoot: z.string().optional(), isPaseoOwnedWorktree: z.literal(true), mainRepoRoot: z.string(), }) .transform((value) => ({ ...value, worktreeRoot: value.worktreeRoot ?? value.cwd, })); export const ProjectCheckoutLitePayloadSchema = z.union([ ProjectCheckoutLiteNotGitPayloadSchema, ProjectCheckoutLiteGitNonPaseoPayloadSchema, ProjectCheckoutLiteGitPaseoPayloadSchema, ]); export const ProjectPlacementPayloadSchema = z.object({ projectKey: z.string(), projectName: z.string(), workspaceName: z.string().nullable().optional(), checkout: ProjectCheckoutLitePayloadSchema, }); export const WorkspaceScriptLifecycleSchema = z.enum(["running", "stopped"]); export const WorkspaceScriptHealthSchema = z.enum(["healthy", "unhealthy"]); export const WorkspaceScriptPayloadSchema = z.object({ scriptName: z.string(), type: z.enum(["script", "service"]).optional().default("service"), hostname: z.string(), port: z.number().int().positive().nullable(), localProxyUrl: z.string().nullable().optional(), publicProxyUrl: z.string().nullable().optional(), proxyUrl: z.string().nullable().optional().default(null), lifecycle: WorkspaceScriptLifecycleSchema, health: WorkspaceScriptHealthSchema.nullable(), exitCode: z.number().nullable().optional().default(null), terminalId: z.string().nullable().optional().default(null), }); const WorkspaceGitRuntimePayloadSchema = z .object({ currentBranch: z.string().nullable().optional(), remoteUrl: z.string().nullable().optional(), isPaseoOwnedWorktree: z.boolean().optional(), isDirty: z.boolean().nullable().optional(), aheadBehind: z .object({ ahead: z.number(), behind: z.number(), }) .nullable() .optional(), aheadOfOrigin: z.number().nullable().optional(), behindOfOrigin: z.number().nullable().optional(), }) .optional() .nullable(); export const WorkspaceGitHubRuntimePayloadSchema = z .object({ featuresEnabled: z.boolean().optional(), pullRequest: z .object({ number: z.number().optional(), url: z.string(), title: z.string(), state: z.string(), baseRefName: z.string(), headRefName: z.string(), isMerged: z.boolean(), isDraft: z.boolean().optional(), mergeable: z.enum(["MERGEABLE", "CONFLICTING", "UNKNOWN"]).catch("UNKNOWN").optional(), checks: z .array( z.object({ name: z.string(), status: z.enum(["success", "failure", "pending", "skipped", "cancelled"]), url: z.string().nullable(), workflow: z.string().optional(), duration: z.string().optional(), }), ) .optional(), checksStatus: z.enum(["none", "pending", "success", "failure"]).optional(), reviewDecision: z.enum(["approved", "changes_requested", "pending"]).nullable().optional(), repoOwner: z.string().optional(), repoName: z.string().optional(), github: z.unknown().optional(), }) .nullable() .optional(), error: z .object({ message: z.string(), }) .nullable() .optional(), refreshedAt: z.string().nullable().optional(), }) .optional() .nullable(); export const WorkspaceDescriptorPayloadSchema = z .object({ id: z.string(), projectId: z.string(), projectDisplayName: z.string(), // COMPAT(projectCustomName): added in v0.1.76, drop the optional gate when floor >= v0.1.76. // When the user has renamed a project, projectDisplayName carries the resolved // value (customName) and projectCustomName mirrors the raw override so the // settings UI can prefill its input and offer a "reset" action. projectCustomName: z.string().nullable().optional(), // Identifies the project's stored custom icon; null means automatic. // COMPAT(projectCustomIcon): added in v0.2.0, remove after 2027-01-20. projectCustomIconRevision: z.string().nullable().optional(), projectRootPath: z.string(), workspaceDirectory: z.string().optional(), // COMPAT(worktreeSlug): added in v0.2.6, remove optional after 2027-01-31. // Present only for Paseo-owned worktrees; this is the basename of their root directory. worktreeSlug: z.string().optional(), projectKind: z.enum(["git", "non_git", "directory"]), // COMPAT(workspaces): keep legacy directory workspace kind parseable. workspaceKind: z.enum(["directory", "local_checkout", "checkout", "worktree"]), name: z.string(), // COMPAT(workspaceTitles): added in v0.1.97, drop the optional gate when floor >= v0.1.97. // When the user has titled a workspace, `name` carries the resolved value // (title) and `title` mirrors the raw override so the rename UI can prefill // its input and offer a "reset to branch name" action. Null means the name // is derived from the branch/directory. title: z.string().nullable().optional(), // COMPAT(workspacePinning): added in v0.1.107, remove optional after 2027-01-12. pinnedAt: z.string().nullable().optional(), // COMPAT(workspaceLabels): added in v0.5.0, remove optional after 2027-08-14. labels: z.array(z.string()).optional(), archivingAt: z.string().nullable().optional().default(null), status: WorkspaceStateBucketSchema, // Best-effort workspace status entry timestamp. Old daemons omit the // field; old clients treat missing and null equivalently. The transform // coerces a missing field to `null` so downstream code never has to // handle `undefined`. statusEnteredAt: z .string() .nullish() .transform((value) => value ?? null), activityAt: z.string().nullable(), diffStat: z .object({ additions: z.number(), deletions: z.number(), }) .nullable() .optional(), scripts: z.array(WorkspaceScriptPayloadSchema).default([]), gitRuntime: WorkspaceGitRuntimePayloadSchema, githubRuntime: WorkspaceGitHubRuntimePayloadSchema, // COMPAT(forge): added in v0.1.106, remove after 2026-12-27. The forge resolved // for this workspace, so the sidebar/hover-card render the right brand mark. // Old daemons omit it; absent means the client falls back to GitHub. forge: z.string().optional(), project: ProjectPlacementPayloadSchema.optional(), // COMPAT(directorySync): sequence of this latest directory projection. syncSeq: z.number().int().positive().optional(), }) .transform((workspace) => ({ ...workspace, workspaceDirectory: workspace.workspaceDirectory ?? workspace.projectRootPath, })); export const AgentUpdateMessageSchema = z.object({ type: z.literal("agent_update"), payload: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("upsert"), agent: AgentSnapshotPayloadSchema, project: ProjectPlacementPayloadSchema.nullable().optional(), generation: z.string().optional(), seq: z.number().int().positive().optional(), }), z.object({ kind: z.literal("remove"), agentId: z.string(), generation: z.string().optional(), seq: z.number().int().positive().optional(), }), ]), }); export const AgentStreamMessageSchema = z.object({ type: z.literal("agent_stream"), payload: z.object({ agentId: z.string(), event: AgentStreamEventPayloadSchema, timestamp: z.string(), // Present for timeline events. Maps 1:1 to canonical in-memory timeline rows. seq: z.number().int().nonnegative().optional(), epoch: z.string().optional(), }), }); export const AgentStatusMessageSchema = z.object({ type: z.literal("agent_status"), payload: z.object({ agentId: z.string(), status: z.string(), info: AgentSnapshotPayloadSchema, }), }); export const AgentListMessageSchema = z.object({ type: z.literal("agent_list"), payload: z.object({ agents: z.array(AgentSnapshotPayloadSchema), }), }); export const AgentSearchMatchFieldSchema = z.enum(["workspace", "title", "branch", "project"]); export const AgentSearchMatchSchema = z.object({ field: AgentSearchMatchFieldSchema, ranges: z.array( z.object({ start: z.number().int().nonnegative(), length: z.number().int().positive(), }), ), }); export type AgentSearchMatch = z.infer; const AgentDirectoryResponseEntrySchema = z.object({ agent: AgentSnapshotPayloadSchema, project: ProjectPlacementPayloadSchema, // Relevance of this entry to the request's `search`, lower being better. // Set only when the request carried a query; a client merging results from // several hosts needs it to interleave their separately ranked pages. searchScore: z.number().optional(), // Where the query matched, so the row can mark it. The ranker computes this // anyway; sending it keeps the client from re-deriving a second opinion that // could disagree with the ranking it is explaining. searchMatches: z.array(AgentSearchMatchSchema).optional(), // COMPAT(directorySync): sequence of this latest directory projection. syncSeq: z.number().int().positive().optional(), }); const AgentDirectoryPageInfoSchema = z.object({ nextCursor: z.string().nullable(), prevCursor: z.string().nullable(), hasMore: z.boolean(), }); export const FetchAgentsResponseMessageSchema = z.object({ type: z.literal("fetch_agents_response"), payload: z.object({ requestId: z.string(), subscriptionId: z.string().nullable().optional(), entries: z.array(AgentDirectoryResponseEntrySchema), pageInfo: AgentDirectoryPageInfoSchema, sync: DirectorySyncMetadataSchema.optional(), }), }); export const FetchAgentHistoryResponseMessageSchema = z.object({ type: z.literal("fetch_agent_history_response"), payload: z.object({ requestId: z.string(), entries: z.array(AgentDirectoryResponseEntrySchema), pageInfo: AgentDirectoryPageInfoSchema, // More sessions matched the request's `search` than the page could hold. // Distinct from `pageInfo.hasMore`, which promises a fetchable next page — // a ranked result set has none, and the way on is a narrower query. searchTruncated: z.boolean().optional(), }), }); export const FetchRecentProviderSessionsResponseMessageSchema = z.object({ type: z.literal("fetch_recent_provider_sessions_response"), payload: z.object({ requestId: z.string(), entries: z.array(RecentProviderSessionDescriptorPayloadSchema), filteredAlreadyImportedCount: z.number().int().nonnegative().optional(), }), }); // COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97. // A project parent that has zero active workspaces. The sidebar renders the // project row with a new-workspace child so projects persist after their last // workspace is archived. export const WorkspaceProjectDescriptorPayloadSchema = z.object({ projectId: z.string(), // COMPAT(projectKey): added in v0.2.4 on 2026-07-28; remove optional after 2027-01-28. projectKey: z.string().optional(), projectDisplayName: z.string(), projectCustomName: z.string().nullable().optional(), // COMPAT(projectCustomIcon): added in v0.2.0, remove after 2027-01-20. projectCustomIconRevision: z.string().nullable().optional(), // Fingerprints the effective icon, including automatic discovery and the // absence of an icon. Clients may persist icon results against this value. // COMPAT(projectIconCache): added in v0.2.7, remove optional after 2027-02-12. projectIconRevision: z.string().optional(), projectRootPath: z.string(), projectKind: z.enum(["git", "non_git", "directory"]), // COMPAT(directorySync): sequence of this latest directory projection. syncSeq: z.number().int().positive().optional(), }); export const FetchWorkspacesResponseMessageSchema = z.object({ type: z.literal("fetch_workspaces_response"), payload: z.object({ requestId: z.string(), subscriptionId: z.string().nullable().optional(), entries: z.array(WorkspaceDescriptorPayloadSchema), // COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97. // Project parents with no active workspaces. Old daemons omit it; old clients // ignore it. Only populated on the first page (no cursor). emptyProjects: z.array(WorkspaceProjectDescriptorPayloadSchema).optional().default([]), pageInfo: z.object({ nextCursor: z.string().nullable(), prevCursor: z.string().nullable(), hasMore: z.boolean(), }), sync: DirectorySyncMetadataSchema.optional(), }), }); export const WorkspaceUpdateMessageSchema = z.object({ type: z.literal("workspace_update"), payload: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("upsert"), workspace: WorkspaceDescriptorPayloadSchema, generation: z.string().optional(), seq: z.number().int().positive().optional(), }), z.object({ kind: z.literal("remove"), id: z.string(), // COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97. // When archiving this workspace leaves its project with no active // workspaces, the daemon includes the project parent so the sidebar keeps // rendering it without waiting for a full re-hydration. Old daemons omit // it; old clients ignore it and surface the project on their next // workspace fetch instead. emptyProject: WorkspaceProjectDescriptorPayloadSchema.optional(), // Project removal is represented on the existing workspace update channel // so old clients can still parse the message and ignore the extra field. removedProjectId: z.string().optional(), generation: z.string().optional(), seq: z.number().int().positive().optional(), }), ]), }); const WorkspaceLabelSyncMetadataSchema = z.object({ mode: z.enum(["snapshot", "changes"]), generation: z.string(), headSeq: z.number().int().nonnegative(), removals: z.array(z.object({ name: z.string(), seq: z.number().int().positive() })), }); export const WorkspaceLabelListResponseSchema = z.object({ type: z.literal("workspace.label.list.response"), payload: z.object({ requestId: z.string(), labels: z.array(WorkspaceLabelDefinitionSchema), sync: WorkspaceLabelSyncMetadataSchema, }), }); export const WorkspaceLabelUpdateSchema = z.object({ type: z.literal("workspace.label.update"), payload: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("upsert"), label: WorkspaceLabelDefinitionSchema, previousName: z.string().optional(), generation: z.string(), seq: z.number().int().positive(), }), z.object({ kind: z.literal("remove"), name: z.string(), generation: z.string(), seq: z.number().int().positive(), }), ]), }); export const WorkspaceLabelAssignmentSetResponseSchema = z.object({ type: z.literal("workspace.label.assignment.set.response"), payload: z.object({ requestId: z.string(), label: WorkspaceLabelDefinitionSchema, workspaceLabels: z.array(z.string()), }), }); export const WorkspaceLabelUpdateResponseSchema = z.object({ type: z.literal("workspace.label.update.response"), payload: z.object({ requestId: z.string(), label: WorkspaceLabelDefinitionSchema, affectedWorkspaceCount: z.number().int().nonnegative(), }), }); export const WorkspaceLabelDeleteResponseSchema = z.object({ type: z.literal("workspace.label.delete.response"), payload: z.object({ requestId: z.string(), affectedWorkspaceCount: z.number().int().nonnegative(), }), }); export const WorkspaceLabelDeleteInspectResponseSchema = z.object({ type: z.literal("workspace.label.delete.inspect.response"), payload: z.object({ requestId: z.string(), affectedWorkspaceCount: z.number().int().nonnegative(), }), }); export const ProjectUpdateMessageSchema = z.object({ type: z.literal("project.update"), payload: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("upsert"), project: WorkspaceProjectDescriptorPayloadSchema, generation: z.string().optional(), seq: z.number().int().positive().optional(), }), z.object({ kind: z.literal("remove"), projectId: z.string(), generation: z.string().optional(), seq: z.number().int().positive().optional(), }), ]), }); export const ProjectListResponseMessageSchema = z.object({ type: z.literal("project.list.response"), payload: z.object({ requestId: z.string(), projects: z.array(WorkspaceProjectDescriptorPayloadSchema), sync: DirectorySyncMetadataSchema.optional(), }), }); export const ScriptStatusUpdateMessageSchema = z.object({ type: z.literal("script_status_update"), payload: z.object({ workspaceId: z.string(), scripts: z.array(WorkspaceScriptPayloadSchema), }), }); export const WorkspaceSetupProgressMessageSchema = z.object({ type: z.literal("workspace_setup_progress"), payload: z.object({ workspaceId: z.string(), status: z.enum(["running", "completed", "failed"]), detail: WorktreeSetupDetailPayloadSchema, error: z.string().nullable(), }), }); export const WorkspaceSetupSnapshotSchema = z.object({ status: z.enum(["running", "completed", "failed"]), detail: WorktreeSetupDetailPayloadSchema, error: z.string().nullable(), }); export const WorkspaceSetupStatusResponseMessageSchema = z.object({ type: z.literal("workspace_setup_status_response"), payload: z.object({ requestId: z.string(), workspaceId: z.string(), snapshot: WorkspaceSetupSnapshotSchema.nullable(), }), }); export const OpenProjectResponseMessageSchema = z.object({ type: z.literal("open_project_response"), payload: z.object({ requestId: z.string(), workspace: WorkspaceDescriptorPayloadSchema.nullable(), error: z.string().nullable(), // Unknown codes from newer daemons degrade to null; clients fall back to `error`. errorCode: z.enum(["directory_not_found"]).nullish().catch(null), }), }); export const ProjectAddResponseSchema = z.object({ type: z.literal("project.add.response"), payload: z.object({ requestId: z.string(), project: WorkspaceProjectDescriptorPayloadSchema.nullable(), error: z.string().nullable(), errorCode: z.enum(["directory_not_found"]).nullish().catch(null), }), }); export const ProjectCreateDirectoryErrorCodeSchema = z.enum([ "invalid_name", "parent_directory_not_found", "directory_exists", "permission_denied", "registration_failed", "filesystem_error", ]); export const ProjectCreateDirectoryResponseSchema = z.object({ type: z.literal("project.create_directory.response"), payload: z.object({ requestId: z.string(), directoryPath: z.string().nullable(), project: WorkspaceProjectDescriptorPayloadSchema.nullable(), error: z.string().nullable(), // Error codes are open-ended on the wire so older clients can still parse // responses after a newer daemon learns another failure reason. errorCode: z.string().nullable(), }), }); export const WorkspaceGithubSearchRepositoriesResponseSchema = z.object({ type: z.literal("workspace.github.search_repositories.response"), payload: z.discriminatedUnion("status", [ z.object({ status: z.literal("success"), requestId: z.string(), repositories: z.array(GithubRepositorySchema), available: z.literal(true), error: z.null(), }), z.object({ status: z.literal("unavailable"), requestId: z.string(), repositories: z.array(GithubRepositorySchema), reason: z.literal("gh_missing"), available: z.literal(false), error: z.string(), }), z.object({ status: z.literal("unauthenticated"), requestId: z.string(), repositories: z.array(GithubRepositorySchema), available: z.literal(false), error: z.string(), }), z.object({ status: z.literal("error"), requestId: z.string(), repositories: z.array(GithubRepositorySchema), available: z.literal(true), error: z.string(), }), ]), }); export const ProjectGithubCloneResponseSchema = z.object({ type: z.literal("project.github.clone.response"), payload: z.object({ requestId: z.string(), repo: z.string().trim().min(MIN_REPOSITORY_PATH_LENGTH), checkoutPath: z.string().nullable(), project: WorkspaceProjectDescriptorPayloadSchema.nullable(), error: z.string().nullable(), }), }); export const StartWorkspaceScriptResponseMessageSchema = z.object({ type: z.literal("start_workspace_script_response"), payload: z.object({ requestId: z.string(), workspaceId: z.string(), scriptName: z.string(), terminalId: z.string().nullable(), error: z.string().nullable(), }), }); const WorkspaceScriptOperationPayloadSchema = z.object({ requestId: z.string(), workspaceId: z.string(), scriptName: z.string().optional(), script: WorkspaceScriptPayloadSchema.nullable().optional(), scripts: z.array(WorkspaceScriptPayloadSchema).optional(), error: z.string().nullable(), }); export const WorkspaceScriptListResponseMessageSchema = z.object({ type: z.literal("workspace.script.list.response"), payload: WorkspaceScriptOperationPayloadSchema, }); export const WorkspaceScriptStartResponseMessageSchema = z.object({ type: z.literal("workspace.script.start.response"), payload: WorkspaceScriptOperationPayloadSchema, }); export const WorkspaceScriptStopResponseMessageSchema = z.object({ type: z.literal("workspace.script.stop.response"), payload: WorkspaceScriptOperationPayloadSchema, }); // COMPAT(desktopEditorBridge): added in v0.1.88, remove after 2026-12-03 once old clients no longer parse daemon editor RPC responses. export const LegacyListAvailableEditorsResponseMessageSchema = z.object({ type: z.literal("list_available_editors_response"), payload: z.object({ requestId: z.string(), editors: z.array( z.object({ id: z.string().trim().min(1), label: z.string(), }), ), error: z.string().nullable(), }), }); export const LegacyOpenInEditorResponseMessageSchema = z.object({ type: z.literal("open_in_editor_response"), payload: z.object({ requestId: z.string(), error: z.string().nullable(), }), }); export const ArchiveWorkspaceResponseMessageSchema = z.object({ type: z.literal("archive_workspace_response"), payload: z.object({ requestId: z.string(), workspaceId: z.string(), archivedAt: z.string().nullable(), error: z.string().nullable(), }), }); export const FetchAgentResponseMessageSchema = z.object({ type: z.literal("fetch_agent_response"), payload: z.object({ requestId: z.string(), agent: AgentSnapshotPayloadSchema.nullable(), project: ProjectPlacementPayloadSchema.nullable().optional(), error: z.string().nullable(), }), }); const AgentTimelineSeqRangeSchema = z.object({ startSeq: z.number().int().nonnegative(), endSeq: z.number().int().nonnegative(), }); export const AgentTimelineEntryPayloadSchema = z.object({ provider: AgentProviderSchema, item: AgentTimelineItemPayloadSchema, turnId: z.string().optional(), timestamp: z.string(), seqStart: z.number().int().nonnegative(), seqEnd: z.number().int().nonnegative(), sourceSeqRanges: z.array(AgentTimelineSeqRangeSchema), collapsed: z.array(z.enum(["assistant_merge", "reasoning_merge", "tool_lifecycle"])), }); export const FetchAgentTimelineResponseMessageSchema = z.object({ type: z.literal("fetch_agent_timeline_response"), payload: z.object({ requestId: z.string(), agentId: z.string(), agent: AgentSnapshotPayloadSchema.nullable(), direction: z.enum(["tail", "before", "after"]), projection: z.enum(["projected", "canonical"]), epoch: z.string(), reset: z.boolean(), staleCursor: z.boolean(), gap: z.boolean(), window: z.object({ minSeq: z.number().int().nonnegative(), maxSeq: z.number().int().nonnegative(), nextSeq: z.number().int().nonnegative(), }), startCursor: AgentTimelineCursorSchema.nullable(), endCursor: AgentTimelineCursorSchema.nullable(), hasOlder: z.boolean(), hasNewer: z.boolean(), mergeWindow: z.boolean().optional(), entries: z.array(AgentTimelineEntryPayloadSchema), error: z.string().nullable(), }), }); export const AgentTimelineListPromptsResponseMessageSchema = z.object({ type: z.literal("agent.timeline.list_prompts.response"), payload: z.object({ requestId: z.string(), agentId: z.string(), epoch: z.string(), prompts: z.array( z.object({ seq: z.number().int().nonnegative(), timestamp: z.string(), preview: z.string(), }), ), error: z.string().nullable(), }), }); export const ProviderSubagentDescriptorPayloadSchema = z.object({ id: z.string(), parentAgentId: z.string(), provider: AgentProviderSchema, title: z.string().nullable(), description: z.string().nullable(), status: z.enum(["running", "completed", "failed", "canceled"]), createdAt: z.string(), updatedAt: z.string(), toolCallId: z.string().nullable(), cwd: z.string().nullable().optional(), // Compact provider-owned context for the shared track. Providers choose what belongs here and // format it for display; clients must not parse provider-specific facts out of this string. subtitle: z.string().nullable().optional(), }); export type ProviderSubagentDescriptorPayload = z.infer< typeof ProviderSubagentDescriptorPayloadSchema >; export const ProviderSubagentListResponseMessageSchema = z.object({ type: z.literal("agent.provider_subagents.list.response"), payload: z.object({ requestId: z.string(), parentAgentId: z.string(), subagents: z.array(ProviderSubagentDescriptorPayloadSchema), error: z.string().nullable(), }), }); export const ProviderSubagentTimelineResponseMessageSchema = z.object({ type: z.literal("agent.provider_subagents.timeline.get.response"), payload: z.object({ requestId: z.string(), parentAgentId: z.string(), subagentId: z.string(), provider: AgentProviderSchema.nullable(), direction: z.enum(["tail", "before", "after"]), epoch: z.string(), reset: z.boolean(), staleCursor: z.boolean(), gap: z.boolean(), window: z.object({ minSeq: z.number().int().nonnegative(), maxSeq: z.number().int().nonnegative(), nextSeq: z.number().int().nonnegative(), }), hasOlder: z.boolean(), hasNewer: z.boolean(), rows: z.array( z.object({ item: AgentTimelineItemPayloadSchema, timestamp: z.string(), seq: z.number().int().nonnegative(), }), ), error: z.string().nullable(), }), }); export const ProviderSubagentUpdateMessageSchema = z.object({ type: z.literal("agent.provider_subagents.update"), payload: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("upsert"), subagent: ProviderSubagentDescriptorPayloadSchema, }), z.object({ kind: z.literal("timeline"), parentAgentId: z.string(), subagentId: z.string(), provider: AgentProviderSchema, item: AgentTimelineItemPayloadSchema, timestamp: z.string(), seq: z.number().int().nonnegative(), epoch: z.string(), }), z.object({ kind: z.literal("remove"), parentAgentId: z.string(), subagentId: z.string(), }), ]), }); export const SetAgentTimelineSubscriptionResponseMessageSchema = z.object({ type: z.literal("agent.timeline.set_subscription.response"), payload: z.object({ agentIds: z.array(z.string()), requestId: z.string(), }), }); export const AgentAttentionRequiredMessageSchema = z.object({ type: z.literal("agent_attention_required"), payload: z.object({ agentId: z.string(), reason: z.enum(["finished", "error", "permission"]), timestamp: z.string(), shouldNotify: z.boolean(), notification: z .object({ title: z.string(), body: z.string(), data: z.object({ serverId: z.string(), workspaceId: z.string().optional(), agentId: z.string(), reason: z.enum(["finished", "error", "permission"]), }), }) .optional(), }), }); export const AgentForkContextResponseMessageSchema = z.object({ type: z.literal("agent.fork_context.response"), payload: z.object({ requestId: z.string(), agentId: z.string(), attachment: TextAttachmentSchema.nullable(), itemCount: z.number().int().nonnegative(), boundaryMessageId: z.string().nullable(), boundaryCursor: AgentTimelineCursorSchema.nullable().optional(), error: z.string().nullable(), }), }); export const CancelAgentResponseMessageSchema = z.object({ type: z.literal("cancel_agent_response"), payload: z.object({ requestId: z.string(), agentId: z.string(), agent: AgentSnapshotPayloadSchema.nullable(), error: z.string().nullable().optional(), }), }); export const ClearAgentAttentionResponseMessageSchema = z.object({ type: z.literal("clear_agent_attention_response"), payload: z.object({ requestId: z.string(), agentId: z.string().or(z.array(z.string())), agents: z.array(AgentSnapshotPayloadSchema), }), }); export const WorkspaceCreateResponseSchema = z.object({ type: z.literal("workspace.create.response"), payload: z.object({ workspace: WorkspaceDescriptorPayloadSchema.nullable(), setupTerminalId: z.string().nullable(), error: z.string().nullable(), errorCode: z.string().optional(), requestId: z.string(), }), }); export const WorkspaceClearAttentionResponseSchema = z.object({ type: z.literal("workspace.clear_attention.response"), payload: z.object({ requestId: z.string(), workspaceId: z.union([z.string(), z.array(z.string())]), clearedAgentIds: z.array(z.string()), results: z.array( z.object({ workspaceId: z.string(), clearedAgentIds: z.array(z.string()), success: z.boolean(), error: z.string().nullable(), }), ), success: z.boolean(), error: z.string().nullable(), }), }); export const SendAgentMessageResponseMessageSchema = z.object({ type: z.literal("send_agent_message_response"), payload: z.object({ requestId: z.string(), agentId: z.string(), accepted: z.boolean(), error: z.string().nullable(), }), }); export const WaitForFinishResponseMessageSchema = z.object({ type: z.literal("wait_for_finish_response"), payload: z.object({ requestId: z.string(), status: z.enum(["idle", "error", "permission", "timeout"]), final: AgentSnapshotPayloadSchema.nullable(), error: z.string().nullable(), lastMessage: z.string().nullable(), }), }); export const GetDaemonConfigResponseMessageSchema = z.object({ type: z.literal("get_daemon_config_response"), payload: z .object({ requestId: z.string(), config: MutableDaemonConfigSchema, }) .passthrough(), }); export const DaemonGetStatusResponseSchema = z.object({ type: z.literal("daemon.get_status.response"), payload: z .object({ requestId: z.string(), serverId: z.string(), version: z.string().nullable().optional(), pid: z.number(), nodePath: z.string(), startedAt: z.string().nullable().optional(), listen: z.string().nullable(), relay: z .object({ enabled: z.boolean(), endpoint: z.string(), publicEndpoint: z.string(), useTls: z.boolean(), publicUseTls: z.boolean(), }) .nullable() .optional(), providers: z.array( z.object({ provider: z.string(), available: z.boolean(), error: z.string().nullable().optional(), }), ), }) .passthrough(), }); export const HubRelationshipStatusSchema = z.object({ state: z.enum([ "not_connected", "connecting", "connected", "reconnecting", "disconnecting", "revoked", ]), daemonId: z.string().nullable(), hubOrigin: z.string().nullable(), scopes: z.array(z.string()), connectedAt: z.string().nullable(), lastError: z.string().nullable(), }); export const HubManagementDaemonConnectResponseSchema = z.object({ type: z.literal("hub.management.daemon.connect.response"), payload: z.object({ requestId: z.string(), status: HubRelationshipStatusSchema }), }); export const HubManagementDaemonGetStatusResponseSchema = z.object({ type: z.literal("hub.management.daemon.get_status.response"), payload: z.object({ requestId: z.string(), status: HubRelationshipStatusSchema }), }); export const HubManagementDaemonDisconnectResponseSchema = z.object({ type: z.literal("hub.management.daemon.disconnect.response"), payload: z.object({ requestId: z.string(), status: HubRelationshipStatusSchema, warning: z.string().optional(), }), }); export const DaemonGetPairingOfferResponseSchema = z.object({ type: z.literal("daemon.get_pairing_offer.response"), payload: z .object({ requestId: z.string(), url: z.string(), qr: z.string().nullable().optional(), relayEnabled: z.boolean(), }) .passthrough(), }); export const DaemonConfigReloadResponseSchema = z.object({ type: z.literal("daemon.config.reload.response"), payload: z .object({ requestId: z.string(), appliedPaths: z.array(z.string()), restartRequiredPaths: z.array(z.string()), overrideControlledPaths: z.array(z.string()), }) .passthrough(), }); export const DiagnosticsResponseSchema = z.object({ type: z.literal("diagnostics.response"), payload: z .object({ requestId: z.string(), diagnostic: z.string(), }) .passthrough(), }); export const SetDaemonConfigResponseMessageSchema = z.object({ type: z.literal("set_daemon_config_response"), payload: z .object({ requestId: z.string(), config: MutableDaemonConfigSchema, }) .passthrough(), }); export const ReadProjectConfigResponseMessageSchema = z.object({ type: z.literal("read_project_config_response"), // zod-aot 0.2.0 miscompiles boolean discriminators as string options // (`"true"`/`"false"`), so keep this sequential until upstream fixes it. payload: z.union([ z.object({ requestId: z.string(), repoRoot: z.string(), ok: z.literal(true), config: PaseoConfigRawSchema.nullable(), revision: PaseoConfigRevisionSchema.nullable(), hasUncommittedWorktreeSetupChanges: z.boolean().optional(), }), z.object({ requestId: z.string(), repoRoot: z.string(), ok: z.literal(false), error: ProjectConfigRpcErrorSchema, }), ]), }); export const WriteProjectConfigResponseMessageSchema = z.object({ type: z.literal("write_project_config_response"), // zod-aot 0.2.0 miscompiles boolean discriminators as string options // (`"true"`/`"false"`), so keep this sequential until upstream fixes it. payload: z.union([ z.object({ requestId: z.string(), repoRoot: z.string(), ok: z.literal(true), config: PaseoConfigRawSchema, revision: PaseoConfigRevisionSchema, hasUncommittedWorktreeSetupChanges: z.boolean().optional(), }), z.object({ requestId: z.string(), repoRoot: z.string(), ok: z.literal(false), error: ProjectConfigRpcErrorSchema, }), ]), }); export const AgentPermissionRequestMessageSchema = z.object({ type: z.literal("agent_permission_request"), payload: z.object({ agentId: z.string(), request: AgentPermissionRequestPayloadSchema, }), }); export const AgentPermissionResolvedMessageSchema = z.object({ type: z.literal("agent_permission_resolved"), payload: z.object({ agentId: z.string(), requestId: z.string(), resolution: AgentPermissionResponseSchema, }), }); export const AgentDeletedMessageSchema = z.object({ type: z.literal("agent_deleted"), payload: z.object({ agentId: z.string(), requestId: z.string(), }), }); export const AgentArchivedMessageSchema = z.object({ type: z.literal("agent_archived"), payload: z.object({ agentId: z.string(), archivedAt: z.string(), requestId: z.string(), }), }); const CloseItemsAgentResultSchema = z.object({ agentId: z.string(), archivedAt: z.string(), }); const CloseItemsTerminalResultSchema = z.object({ terminalId: z.string(), success: z.boolean(), }); export const CloseItemsResponseSchema = z.object({ type: z.literal("close_items_response"), payload: z.object({ agents: z.array(CloseItemsAgentResultSchema), terminals: z.array(CloseItemsTerminalResultSchema), requestId: z.string(), }), }); const AheadBehindSchema = z.object({ ahead: z.number(), behind: z.number(), }); const CheckoutStatusCommonSchema = z.object({ cwd: z.string(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), // The full ref currentBranch tracks, as git resolves `@{upstream}`: // "refs/remotes/origin/main", "refs/remotes/upstream/main" on a fork, or a // "refs/heads/..." ref for a branch tracking a local branch. Null when there is no // upstream. Clients use it verbatim — the remote is not necessarily origin and the // upstream branch name is not necessarily currentBranch, so composing one is wrong. // aheadOfOrigin/behindOfOrigin are measured against exactly this ref. upstreamRef: z.string().nullable().optional(), }); const CheckoutStatusNotGitSchema = CheckoutStatusCommonSchema.extend({ isGit: z.literal(false), isPaseoOwnedWorktree: z.literal(false), repoRoot: z.null(), currentBranch: z.null(), isDirty: z.null(), baseRef: z.null(), aheadBehind: z.null(), aheadOfOrigin: z.null(), behindOfOrigin: z.null(), hasRemote: z.boolean(), remoteUrl: z.null(), }); const CheckoutStatusGitNonPaseoSchema = CheckoutStatusCommonSchema.extend({ isGit: z.literal(true), isPaseoOwnedWorktree: z.literal(false), repoRoot: z.string(), mainRepoRoot: z.string().nullable().optional().default(null), currentBranch: z.string().nullable(), isDirty: z.boolean(), baseRef: z.string().nullable(), aheadBehind: AheadBehindSchema.nullable(), aheadOfOrigin: z.number().nullable(), behindOfOrigin: z.number().nullable(), hasRemote: z.boolean(), remoteUrl: z.string().nullable(), }); const CheckoutStatusGitPaseoSchema = CheckoutStatusCommonSchema.extend({ isGit: z.literal(true), isPaseoOwnedWorktree: z.literal(true), repoRoot: z.string(), mainRepoRoot: z.string(), currentBranch: z.string().nullable(), isDirty: z.boolean(), baseRef: z.string(), aheadBehind: AheadBehindSchema.nullable(), aheadOfOrigin: z.number().nullable(), behindOfOrigin: z.number().nullable(), hasRemote: z.boolean(), remoteUrl: z.string().nullable(), }); export const CheckoutStatusResponseSchema = z.object({ type: z.literal("checkout_status_response"), payload: z.union([ CheckoutStatusNotGitSchema, CheckoutStatusGitNonPaseoSchema, CheckoutStatusGitPaseoSchema, ]), }); const CheckoutPrGithubAutoMergeRequestSchema = z .object({ enabledAt: z.string().nullable().optional().default(null), mergeMethod: z.string().nullable().optional().default(null), enabledBy: z.string().nullable().optional().default(null), }) .nullable() .optional() .default(null); const CheckoutPrGithubRepositoryPolicySchema = z .object({ autoMergeAllowed: z.boolean().optional().default(false), mergeCommitAllowed: z.boolean().optional().default(false), squashMergeAllowed: z.boolean().optional().default(false), rebaseMergeAllowed: z.boolean().optional().default(false), viewerDefaultMergeMethod: z.string().nullable().optional().default(null), }) .optional() .default({ autoMergeAllowed: false, mergeCommitAllowed: false, squashMergeAllowed: false, rebaseMergeAllowed: false, viewerDefaultMergeMethod: null, }); const CheckoutPrGithubStatusObjectSchema = z.object({ mergeStateStatus: z.string().nullable().optional().default(null), autoMergeRequest: CheckoutPrGithubAutoMergeRequestSchema, viewerCanEnableAutoMerge: z.boolean().optional().default(false), viewerCanDisableAutoMerge: z.boolean().optional().default(false), viewerCanMergeAsAdmin: z.boolean().optional().default(false), viewerCanUpdateBranch: z.boolean().optional().default(false), repository: CheckoutPrGithubRepositoryPolicySchema, isMergeQueueEnabled: z.boolean().optional().default(false), isInMergeQueue: z.boolean().optional().default(false), }); const CheckoutPrGithubStatusSchema = CheckoutPrGithubStatusObjectSchema.optional(); // The open facts envelope for forge-specific PR facts. Permanent — non-GitHub // forges deliver their native facts through it. The transitional piece is the // `github` mirror above, which stays populated for clients predating this // envelope; see COMPAT(forgeSpecific) in status-projection.ts for the shim. // // NOTE: `forgeSpecific.forge` is a FACTS-FAMILY tag, not the workspace brand id. // The whole Gitea family (gitea, forgejo, codeberg) emits `forge: "gitea"` here // because they share one facts shape, while the top-level `forge` above carries // the specific brand. Validation of family-specific payloads happens at runtime // in the consumer that knows that forge family. const CheckoutPrForgeSpecificSchema = z.unknown().optional(); export const CheckoutPrStatusSchema = z.object({ // COMPAT(forge): added in v0.1.106, remove the default after 2026-12-27 once daemon floor >= v0.1.106. forge: z.string().optional().default("github"), projectPath: z.string().optional(), number: z.number().optional(), url: z.string(), title: z.string(), state: z.string(), baseRefName: z.string(), headRefName: z.string(), isMerged: z.boolean(), isDraft: z.boolean().optional().default(false), mergeable: z .enum(["MERGEABLE", "CONFLICTING", "UNKNOWN"]) .catch("UNKNOWN") .optional() .default("UNKNOWN"), checks: z .array( z.object({ name: z.string(), status: z.string(), url: z.string().nullable(), workflow: z.string().optional(), /** * Formatted by the forge adapter: how long a finished check took, or how long a * running one has been going. Raw timestamps stay off the wire. */ duration: z.string().optional(), checkRunId: z.number().optional(), workflowRunId: z.number().optional(), }), ) .optional() .default([]), checksStatus: z.string().optional(), reviewDecision: z.string().nullable().optional(), repoOwner: z.string().optional(), repoName: z.string().optional(), github: CheckoutPrGithubStatusSchema, forgeSpecific: CheckoutPrForgeSpecificSchema, }); // Why a forge's PR/MR features are (un)available, so the client can offer the // precise next step instead of a generic dead-end. Kept open on the wire so // feature consumers can ignore values introduced by newer daemons. export type ForgeAuthState = | "authenticated" | "unauthenticated" | "cli_missing" | "no_remote" | "error"; export const ForgeAuthStateSchema = z.unknown().optional(); const CheckoutPrStatusPayloadSchema = z.object({ cwd: z.string(), status: CheckoutPrStatusSchema.nullable(), githubFeaturesEnabled: z.boolean(), // COMPAT(forgeAuthState): added in v0.1.106, remove after 2026-12-27. Optional richer // signal that supersedes githubFeaturesEnabled. The legacy boolean stays for old clients // and may remain true for non-auth error payloads so old clients still show the error. // Drop the boolean once the daemon floor >= v0.1.106. authState: ForgeAuthStateSchema, // COMPAT(forge): added in v0.1.106, remove the default after 2026-12-27 once daemon floor >= v0.1.106. forge: z.string().optional().default("github"), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }); const CheckoutStatusUpdateMetadataSchema = z.object({ prStatus: CheckoutPrStatusPayloadSchema.optional(), }); export const CheckoutStatusUpdateSchema = z.object({ type: z.literal("checkout_status_update"), payload: z .union([ CheckoutStatusNotGitSchema, CheckoutStatusGitNonPaseoSchema, CheckoutStatusGitPaseoSchema, ]) .and(CheckoutStatusUpdateMetadataSchema), }); const CheckoutDiffSubscriptionPayloadSchema = z.object({ subscriptionId: z.string(), cwd: z.string(), files: z.array(ParsedDiffFileSchema), error: CheckoutErrorSchema.nullable(), // COMPAT(diffTooLarge): added in v0.2.4, keep optional until the daemon floor is v0.2.4. diffTooLarge: z.boolean().optional(), }); export const SubscribeCheckoutDiffResponseSchema = z.object({ type: z.literal("subscribe_checkout_diff_response"), payload: CheckoutDiffSubscriptionPayloadSchema.extend({ requestId: z.string(), }), }); export const CheckoutDiffUpdateSchema = z.object({ type: z.literal("checkout_diff_update"), payload: CheckoutDiffSubscriptionPayloadSchema, }); export const CheckoutCommitResponseSchema = z.object({ type: z.literal("checkout_commit_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutMergeResponseSchema = z.object({ type: z.literal("checkout_merge_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutMergeFromBaseResponseSchema = z.object({ type: z.literal("checkout_merge_from_base_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutPullResponseSchema = z.object({ type: z.literal("checkout_pull_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutPushResponseSchema = z.object({ type: z.literal("checkout_push_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutRefreshResponseSchema = z.object({ type: z.literal("checkout.refresh.response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutPrCreateResponseSchema = z.object({ type: z.literal("checkout_pr_create_response"), payload: z.object({ cwd: z.string(), url: z.string().nullable(), number: z.number().nullable(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutPrMergeResponseSchema = z.object({ type: z.literal("checkout_pr_merge_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutForgeSetAutoMergeResponseSchema = z.object({ type: z.literal("checkout.forge.set_auto_merge.response"), payload: z.object({ cwd: z.string(), enabled: z.boolean(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); // COMPAT(githubAutoMergeRpc): added in v0.1.106, remove after 2026-12-28 once // all supported clients use checkout.forge.set_auto_merge.*. export const CheckoutGithubSetAutoMergeResponseSchema = z.object({ type: z.literal("checkout.github.set_auto_merge.response"), payload: z.object({ cwd: z.string(), enabled: z.boolean(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutDiscardChangesResponseSchema = z.object({ type: z.literal("checkout.discard_changes.response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutCommitsListResponseSchema = z.object({ type: z.literal("checkout.commits.list.response"), payload: z.object({ cwd: z.string(), baseRef: z.string().nullable(), commits: z.array(CheckoutCommitSchema), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutCommitFileDiffResponseSchema = z.object({ type: z.literal("checkout.commits.file_diff.response"), payload: z.object({ cwd: z.string(), sha: z.string(), path: z.string(), // null when the file is absent from the commit or carries no textual diff // (e.g. binary-only changes). file: ParsedDiffFileSchema.nullable(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); const CheckoutGithubCheckAnnotationSchema = z.object({ path: z.string().optional(), startLine: z.number().optional(), endLine: z.number().optional(), annotationLevel: z.string().optional(), message: z.string().optional(), title: z.string().optional(), rawDetails: z.string().optional(), }); const CheckoutGithubCheckJobSchema = z.object({ jobId: z.number(), name: z.string(), status: z.string().nullable().optional(), conclusion: z.string().nullable().optional(), url: z.string().nullable().optional(), logTail: z.string().optional(), logTruncated: z.boolean().optional(), }); // Statuses stay open strings so future forge values cannot break parsing. const CheckoutPipelineJobSchema = z.object({ id: z.number(), name: z.string(), stage: z.string(), status: z.string(), rawStatus: z.string(), url: z.string().nullable().optional().default(null), allowFailure: z.boolean().optional().default(false), durationSeconds: z.number().nullable().optional().default(null), }); const CheckoutPipelineStageSchema = z.object({ name: z.string(), status: z.string(), jobs: z.array(CheckoutPipelineJobSchema).optional().default([]), }); const CheckoutPipelineSchema = z.object({ id: z.number(), status: z.string(), rawStatus: z.string(), url: z.string().nullable().optional().default(null), ref: z.string().nullable().optional().default(null), sha: z.string().nullable().optional().default(null), stages: z.array(CheckoutPipelineStageSchema).optional().default([]), }); export const CheckoutGithubCheckDetailsSchema = z.object({ checkRunId: z.number(), workflowRunId: z.number().nullable().optional(), name: z.string(), status: z.string().nullable().optional(), conclusion: z.string().nullable().optional(), url: z.string().nullable().optional(), detailsUrl: z.string().nullable().optional(), output: z .object({ title: z.string().nullable().optional(), summary: z.string().nullable().optional(), text: z.string().nullable().optional(), }) .nullable() .optional(), annotations: z.array(CheckoutGithubCheckAnnotationSchema).optional().default([]), failedJobs: z.array(CheckoutGithubCheckJobSchema).optional().default([]), truncated: z.boolean().optional().default(false), // No default: server CheckDetails keeps this optional and GitHub leaves it absent. pipeline: CheckoutPipelineSchema.nullable().optional(), }); export const CheckoutCheckDetailsSchema = CheckoutGithubCheckDetailsSchema; export const CheckoutForgeGetCheckDetailsResponseSchema = z.object({ type: z.literal("checkout.forge.get_check_details.response"), payload: z.object({ cwd: z.string(), success: z.boolean(), details: CheckoutCheckDetailsSchema.nullable().optional().default(null), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); // COMPAT(githubCheckDetailsRpc): added in v0.1.106, remove after 2026-12-28 once // all supported clients use checkout.forge.get_check_details.*. export const CheckoutGithubGetCheckDetailsResponseSchema = z.object({ type: z.literal("checkout.github.get_check_details.response"), payload: z.object({ cwd: z.string(), success: z.boolean(), details: CheckoutCheckDetailsSchema.nullable().optional().default(null), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutPrStatusResponseSchema = z.object({ type: z.literal("checkout_pr_status_response"), payload: CheckoutPrStatusPayloadSchema, }); const PullRequestTimelineKnownErrorSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("not_found"), message: z.string().optional().default(""), }), z.object({ kind: z.literal("forbidden"), message: z.string().optional().default(""), }), z.object({ kind: z.literal("unknown"), message: z.string().optional().default(""), }), ]); const PullRequestTimelineErrorSchema = z.preprocess((value) => { if (!value || typeof value !== "object" || Array.isArray(value)) { return { kind: "unknown", message: "" }; } const error = value as Record; if (error.kind === "not_found" || error.kind === "forbidden" || error.kind === "unknown") { return error; } return { ...error, kind: "unknown" }; }, PullRequestTimelineKnownErrorSchema); const PullRequestTimelineReviewItemSchema = z.object({ id: z.string().optional().default(""), kind: z.literal("review"), author: z.string().optional().default("unknown"), authorUrl: z.string().nullable().optional(), avatarUrl: z.string().nullable().optional(), body: z.string().optional().default(""), createdAt: z.number().optional().default(0), url: z.string().optional().default(""), reviewState: z .enum(["approved", "changes_requested", "commented"]) .optional() .default("commented"), }); const PullRequestTimelineCommentItemSchema = z.object({ id: z.string().optional().default(""), kind: z.literal("comment"), author: z.string().optional().default("unknown"), authorUrl: z.string().nullable().optional(), avatarUrl: z.string().nullable().optional(), body: z.string().optional().default(""), createdAt: z.number().optional().default(0), url: z.string().optional().default(""), // GitHub review id this inline comment belongs to; lets clients nest review // threads under their parent review. Absent on issue comments and on // timelines from daemons that predate the field. reviewId: z.string().optional(), // Forge-neutral discussion/thread id this comment belongs to, independent of a // file position. GitLab maps its discussion id here so general (non-file) // reply chains group into one thread; file-position threads also carry it. // Absent on standalone comments and on timelines from daemons that predate it. threadId: z.string().optional(), // Forge-neutral resolution state for a thread that has no file position, e.g. a // GitLab general (non-file) discussion that is resolvable. File-position threads // carry their resolution under `location.isResolved` instead. Absent on ordinary // comments, on forges that expose no thread resolution, and on older timelines. threadIsResolved: z.boolean().optional(), location: z .object({ path: z.string(), line: z.number().optional(), startLine: z.number().optional(), threadId: z.string().optional(), isResolved: z.boolean().optional(), isOutdated: z.boolean().optional(), }) .optional(), }); export const PullRequestTimelineItemSchema = z.preprocess( (value) => { if (!value || typeof value !== "object" || Array.isArray(value)) { return value; } const item = value as Record; if (item.kind === "review" || item.kind === "comment") { return item; } return { ...item, kind: "comment" }; }, z.discriminatedUnion("kind", [ PullRequestTimelineReviewItemSchema, PullRequestTimelineCommentItemSchema, ]), ); export const PullRequestTimelineResponseSchema = z.object({ type: z.literal("pull_request_timeline_response"), payload: z .object({ cwd: z.string().optional().default(""), prNumber: z.number().nullable().optional().default(null), items: z.array(PullRequestTimelineItemSchema).optional().default([]), truncated: z.boolean().optional().default(false), error: PullRequestTimelineErrorSchema.nullable().optional().default(null), requestId: z.string().optional().default(""), githubFeaturesEnabled: z.boolean().optional().default(true), // COMPAT(forgeAuthState): added in v0.1.106, remove after 2026-12-27. Optional richer // signal that supersedes githubFeaturesEnabled, mirroring CheckoutPrStatusPayloadSchema. // Drop the boolean once the daemon floor >= v0.1.106. authState: ForgeAuthStateSchema, }) .optional() .prefault({}), }); export const CheckoutSwitchBranchResponseSchema = z.object({ type: z.literal("checkout_switch_branch_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), branch: z.string(), source: z.enum(["local", "remote"]).optional(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CheckoutRenameBranchResponseSchema = z.object({ type: z.literal("checkout.rename_branch.response"), payload: z.object({ requestId: z.string(), success: z.boolean(), cwd: z.string(), currentBranch: z.string().nullable(), error: CheckoutErrorSchema.nullable(), }), }); const StashEntrySchema = z.object({ index: z.number().int().min(0), message: z.string(), branch: z.string().nullable(), isPaseo: z.boolean(), }); export const StashSaveResponseSchema = z.object({ type: z.literal("stash_save_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const StashPopResponseSchema = z.object({ type: z.literal("stash_pop_response"), payload: z.object({ cwd: z.string(), success: z.boolean(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const StashListResponseSchema = z.object({ type: z.literal("stash_list_response"), payload: z.object({ cwd: z.string(), entries: z.array(StashEntrySchema), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const ValidateBranchResponseSchema = z.object({ type: z.literal("validate_branch_response"), payload: z.object({ exists: z.boolean(), resolvedRef: z.string().nullable(), isRemote: z.boolean(), error: z.string().nullable(), requestId: z.string(), }), }); export const BranchSuggestionsResponseSchema = z.object({ type: z.literal("branch_suggestions_response"), payload: z.object({ branches: z.array(z.string()), branchDetails: z .array( z.object({ name: z.string(), committerDate: z.number(), hasLocal: z.boolean().optional(), hasRemote: z.boolean().optional(), localAhead: z.number().int().nonnegative().optional(), localBehind: z.number().int().nonnegative().optional(), }), ) .optional(), error: z.string().nullable(), requestId: z.string(), }), }); const ForgeSearchResponsePayloadSchema = z.object({ items: z.array(z.unknown()), authState: z.unknown().optional(), error: z.string().nullable(), requestId: z.string(), }); const GitHubSearchResponsePayloadSchema = z.object({ items: z.array(z.unknown()), featuresEnabled: z.boolean().optional(), authState: z.unknown().optional(), githubFeaturesEnabled: z.boolean().optional(), error: z.string().nullable(), requestId: z.string(), }); export const ForgeSearchResponseSchema = z.object({ type: z.literal("forge.search.response"), payload: ForgeSearchResponsePayloadSchema, }); // COMPAT(githubSearchRpc): added in v0.1.106, remove after 2026-12-28 once // clients use forge.search.*. export const GitHubSearchResponseSchema = z.object({ type: z.literal("github_search_response"), payload: GitHubSearchResponsePayloadSchema, }); export const DirectorySuggestionsResponseSchema = z.object({ type: z.literal("directory_suggestions_response"), payload: z.object({ directories: z.array(z.string()), entries: z .array( z.object({ path: z.string(), kind: z.enum(["file", "directory"]), }), ) .optional() .default([]), error: z.string().nullable(), requestId: z.string(), }), }); const PaseoWorktreeSchema = z.object({ worktreePath: z.string(), createdAt: z.string(), branchName: z.string().nullable().optional(), head: z.string().nullable().optional(), }); export const PaseoWorktreeListResponseSchema = z.object({ type: z.literal("paseo_worktree_list_response"), payload: z.object({ worktrees: z.array(PaseoWorktreeSchema), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const PaseoWorktreeArchiveResponseSchema = z.object({ type: z.literal("paseo_worktree_archive_response"), payload: z.object({ success: z.boolean(), removedAgents: z.array(z.string()).optional(), error: CheckoutErrorSchema.nullable(), requestId: z.string(), }), }); export const CreatePaseoWorktreeResponseSchema = z.object({ type: z.literal("create_paseo_worktree_response"), payload: z.object({ workspace: WorkspaceDescriptorPayloadSchema.nullable(), error: z.string().nullable(), errorCode: z.string().optional(), setupTerminalId: z.string().nullable(), requestId: z.string(), }), }); export const FileExplorerResponseSchema = z.object({ type: z.literal("file_explorer_response"), payload: z.object({ cwd: z.string(), path: z.string(), mode: z.enum(["list", "file"]), directory: FileExplorerDirectorySchema.nullable(), file: FileExplorerFileSchema.nullable(), error: z.string().nullable(), requestId: z.string(), }), }); export const FileSubscribeResponseSchema = z.object({ type: z.literal("fs.file.subscribe.response"), payload: z.object({ subscriptionId: z.string(), initial: FileVersionSchema, requestId: z.string(), }), }); export const FileUnsubscribeResponseSchema = z.object({ type: z.literal("fs.file.unsubscribe.response"), payload: z.object({ subscriptionId: z.string(), requestId: z.string(), }), }); export const FileWriteResultSchema = z.discriminatedUnion("status", [ z.object({ status: z.literal("written"), modifiedAt: z.string(), size: z.number(), revision: z.string().optional(), }), z.object({ status: z.literal("conflict"), version: FileVersionSchema }), z.object({ status: z.literal("error"), error: z.string() }), ]); export const FileWriteResponseSchema = z.object({ type: z.literal("fs.file.write.response"), payload: z.object({ result: FileWriteResultSchema, requestId: z.string(), }), }); export const FileEntryCreateResponseSchema = z.object({ type: z.literal("fs.entry.create.response"), payload: z.object({ cwd: z.string(), parentPath: z.string(), path: z.string().nullable(), success: z.boolean(), error: z.string().nullable(), requestId: z.string(), }), }); export const FileEntryRenameResponseSchema = z.object({ type: z.literal("fs.entry.rename.response"), payload: z.object({ cwd: z.string(), path: z.string(), renamedPath: z.string().nullable(), success: z.boolean(), error: z.string().nullable(), requestId: z.string(), }), }); export const FileEntryDuplicateResponseSchema = z.object({ type: z.literal("fs.entry.duplicate.response"), payload: z.object({ cwd: z.string(), path: z.string(), duplicatedPath: z.string().nullable(), success: z.boolean(), error: z.string().nullable(), requestId: z.string(), }), }); export const FileEntryDeleteResponseSchema = z.object({ type: z.literal("fs.entry.delete.response"), payload: z.object({ cwd: z.string(), path: z.string(), success: z.boolean(), error: z.string().nullable(), requestId: z.string(), }), }); export const FileUpdateSchema = z.object({ type: z.literal("fs.file.update"), payload: z.object({ subscriptionId: z.string(), version: FileVersionSchema, }), }); const ProjectIconSchema = z.object({ data: z.string(), mimeType: z.string(), }); export const ProjectIconResponseSchema = z.object({ type: z.literal("project_icon_response"), payload: z.object({ cwd: z.string(), icon: ProjectIconSchema.nullable(), error: z.string().nullable(), requestId: z.string(), }), }); export const ProjectIconGetResponseSchema = z.object({ type: z.literal("project.icon.get.response"), payload: z.object({ projectId: z.string(), icon: ProjectIconSchema.nullable(), error: z.string().nullable(), requestId: z.string(), }), }); export const FileDownloadTokenResponseSchema = z.object({ type: z.literal("file_download_token_response"), payload: z.object({ cwd: z.string(), path: z.string(), token: z.string().nullable(), fileName: z.string().nullable(), mimeType: z.string().nullable(), size: z.number().nullable(), error: z.string().nullable(), requestId: z.string(), }), }); export const FileUploadResponseSchema = z.object({ type: z.literal("file.upload.response"), payload: z.object({ requestId: z.string(), file: UploadedFileAttachmentSchema.nullable(), error: z.string().nullable(), }), }); export const ListProviderModelsResponseMessageSchema = z.object({ type: z.literal("list_provider_models_response"), payload: z.object({ provider: AgentProviderSchema, models: z.array(AgentModelDefinitionSchema).optional(), error: z.string().nullable().optional(), fetchedAt: z.string(), requestId: z.string(), }), }); export const ListProviderModesResponseMessageSchema = z.object({ type: z.literal("list_provider_modes_response"), payload: z.object({ provider: AgentProviderSchema, modes: z.array(AgentModeSchema).optional(), error: z.string().nullable().optional(), fetchedAt: z.string(), requestId: z.string(), }), }); export const ListProviderFeaturesResponseMessageSchema = z.object({ type: z.literal("list_provider_features_response"), payload: z.object({ provider: AgentProviderSchema, features: z.array(AgentFeatureSchema).optional(), error: z.string().nullable().optional(), fetchedAt: z.string(), requestId: z.string(), }), }); const ProviderAvailabilitySchema = z.object({ provider: AgentProviderSchema, available: z.boolean(), error: z.string().nullable().optional(), }); export const ListAvailableProvidersResponseSchema = z.object({ type: z.literal("list_available_providers_response"), payload: z.object({ providers: z.array(ProviderAvailabilitySchema), error: z.string().nullable().optional(), fetchedAt: z.string(), requestId: z.string(), }), }); // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot export const GetProvidersSnapshotResponseMessageSchema = z.object({ type: z.literal("get_providers_snapshot_response"), payload: z.object({ cwd: z.string().optional(), entries: z.array(ProviderSnapshotEntrySchema), compactSnapshot: CompactProviderSnapshotSchema.optional(), snapshotHash: z.string().optional(), notModified: z.boolean().optional(), generatedAt: z.string(), requestId: z.string(), }), }); // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot export const ProvidersSnapshotUpdateMessageSchema = z.object({ type: z.literal("providers_snapshot_update"), payload: z.object({ cwd: z.string().optional(), entries: z.array(ProviderSnapshotEntrySchema), compactSnapshot: CompactProviderSnapshotSchema.optional(), snapshotHash: z.string().optional(), generatedAt: z.string(), }), }); // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot export const RefreshProvidersSnapshotResponseMessageSchema = z.object({ type: z.literal("refresh_providers_snapshot_response"), payload: z.object({ requestId: z.string(), acknowledged: z.boolean(), }), }); // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot export const ProviderDiagnosticResponseMessageSchema = z.object({ type: z.literal("provider_diagnostic_response"), payload: z.object({ provider: AgentProviderSchema, diagnostic: z.string(), requestId: z.string(), }), }); export const ProviderUsageToneSchema = z.enum(["default", "ok", "warning", "danger"]); export const ProviderUsageStatusSchema = z.enum(["available", "unavailable", "error"]); export const ProviderUsageWindowSchema = z.object({ id: z.string(), label: z.string(), usedPct: z.number().nullable().optional(), remainingPct: z.number().nullable().optional(), resetsAt: z.string().nullable().optional(), runsOutAt: z.string().nullable().optional(), shortfallPct: z.number().nullable().optional(), tone: ProviderUsageToneSchema.optional(), }); export const ProviderUsageBalanceSchema = z.object({ id: z.string(), label: z.string(), used: z.number().nullable().optional(), remaining: z.number().nullable().optional(), limit: z.number().nullable().optional(), unit: z.enum(["usd", "credits", "requests", "tokens"]), resetsAt: z.string().nullable().optional(), tone: ProviderUsageToneSchema.optional(), }); export const ProviderUsageDetailSchema = z.object({ id: z.string(), label: z.string(), value: z.string(), tone: ProviderUsageToneSchema.optional(), }); export const ProviderUsageSchema = z.object({ providerId: z.string(), displayName: z.string(), status: ProviderUsageStatusSchema, planLabel: z.string().nullable(), sourceLabel: z.string().nullable().optional(), fetchedAt: z.string().nullable().optional(), nextRefreshAt: z.string().nullable().optional(), windows: z.array(ProviderUsageWindowSchema), balances: z.array(ProviderUsageBalanceSchema).optional(), details: z.array(ProviderUsageDetailSchema).optional(), error: z.string().nullable().optional(), }); export const ProviderUsageListResponseMessageSchema = z.object({ type: z.literal("provider.usage.list.response"), payload: z.object({ requestId: z.string(), fetchedAt: z.string(), providers: z.array(ProviderUsageSchema), }), }); const AgentSlashCommandSchema = z.object({ name: z.string(), description: z.string(), argumentHint: z.string(), kind: z.enum(["command", "skill"]).optional().catch("command"), }); export const ListCommandsResponseSchema = z.object({ type: z.literal("list_commands_response"), payload: z.object({ agentId: z.string(), commands: z.array(AgentSlashCommandSchema), error: z.string().nullable(), requestId: z.string(), }), }); // ============================================================================ // Terminal Outbound Messages // ============================================================================ const TerminalInfoSchema = z.object({ id: z.string(), name: z.string(), cwd: z.string(), workspaceId: z.string().optional(), title: z.string().optional(), activity: TerminalActivitySchema.nullable().optional(), }); export const TerminalCellSchema = z.object({ char: z.string(), fg: z.number().optional(), bg: z.number().optional(), fgMode: z.number().optional(), bgMode: z.number().optional(), bold: z.boolean().optional(), italic: z.boolean().optional(), underline: z.boolean().optional(), dim: z.boolean().optional(), inverse: z.boolean().optional(), strikethrough: z.boolean().optional(), }); export const TerminalCursorStyleSchema = z.enum(["block", "underline", "bar"]); export const TerminalCursorSchema = z.object({ row: z.number(), col: z.number(), hidden: z.boolean().optional(), style: TerminalCursorStyleSchema.optional(), blink: z.boolean().optional(), }); export const TerminalStateSchema = z.object({ rows: z.number(), cols: z.number(), grid: z.array(z.array(TerminalCellSchema)), scrollback: z.array(z.array(TerminalCellSchema)), cursor: TerminalCursorSchema, title: z.string().optional(), // Per-row soft-wrap flags aligned 1:1 with `grid` / `scrollback`. `true` means // the row continued onto the next row (xterm's GRID_LINE_WRAPPED equivalent), // so the client can re-wrap the logical line on resize instead of freezing it // at the snapshot width. Optional: only sent to clients that advertise the // `terminalReflowableSnapshot` capability, so old daemons/clients are unaffected. gridWrapped: z.array(z.boolean()).optional(), scrollbackWrapped: z.array(z.boolean()).optional(), }); export const ListTerminalsResponseSchema = z.object({ type: z.literal("list_terminals_response"), payload: z.object({ cwd: z.string().optional(), terminals: z.array(TerminalInfoSchema.omit({ cwd: true })), requestId: z.string(), }), }); export const TerminalsChangedSchema = z.object({ type: z.literal("terminals_changed"), payload: z.object({ cwd: z.string(), terminals: z.array(TerminalInfoSchema.omit({ cwd: true })), }), }); export const CreateTerminalResponseSchema = z.object({ type: z.literal("create_terminal_response"), payload: z.object({ terminal: TerminalInfoSchema.nullable(), error: z.string().nullable(), requestId: z.string(), }), }); export const RenameTerminalResponseSchema = z.object({ type: z.literal("terminal.rename.response"), payload: z.object({ requestId: z.string(), success: z.boolean(), error: z.string().nullable(), }), }); export const SubscribeTerminalResponseSchema = z.object({ type: z.literal("subscribe_terminal_response"), payload: z.union([ z.object({ terminalId: z.string(), slot: z.number().int().min(0).max(255), error: z.null(), requestId: z.string(), }), z.object({ terminalId: z.string(), error: z.string(), requestId: z.string(), }), ]), }); export const KillTerminalResponseSchema = z.object({ type: z.literal("kill_terminal_response"), payload: z.object({ terminalId: z.string(), success: z.boolean(), requestId: z.string(), }), }); export const CaptureTerminalResponseSchema = z.object({ type: z.literal("capture_terminal_response"), payload: z.object({ terminalId: z.string(), lines: z.array(z.string()), totalLines: z.number().int().nonnegative(), requestId: z.string(), }), }); export const TerminalStreamExitSchema = z.object({ type: z.literal("terminal_stream_exit"), payload: z.object({ terminalId: z.string(), }), }); export const TerminalAttentionRequiredSchema = z.object({ type: z.literal("terminal_attention_required"), payload: z.object({ serverId: z.string().optional(), terminalId: z.string(), cwd: z.string(), workspaceId: z.string().optional(), reason: z.enum(["finished", "needs_input"]), title: z.string(), body: z.string(), shouldNotify: z.boolean(), }), }); export const DaemonUpdateResponseSchema = z.object({ type: z.literal("daemon.update.response"), payload: z.object({ requestId: z.string(), success: z.boolean(), error: z.string().nullable(), previousVersion: z.string().nullable(), newVersion: z.string().nullable(), }), }); export type DaemonUpdateResponse = z.infer; export const DaemonUpdateProgressMessageSchema = z.object({ type: z.literal("daemon.update.progress"), payload: z.object({ requestId: z.string(), phase: z.enum(["starting", "downloading", "installing", "complete"]), }), }); export const HubExecutionAgentCreateResponseSchema = z.object({ type: z.literal("hub.execution.agent.create.response"), payload: z.object({ requestId: z.string(), executionId: z.string(), agentId: z.string().nullable(), agent: AgentSnapshotPayloadSchema.nullable(), success: z.boolean(), toolPolicyApplied: z.literal(true).optional(), error: HubExecutionAgentCreateErrorSchema.nullable(), }), }); export const HubExecutionAgentValidationIssueSchema = z.object({ path: z.array(z.union([z.string(), z.number()])), message: z.string(), }); export type HubExecutionAgentValidationIssue = z.infer< typeof HubExecutionAgentValidationIssueSchema >; export const HubExecutionAgentValidateResponseSchema = z.object({ type: z.literal("hub.execution.agent.validate.response"), payload: z.object({ requestId: z.string(), valid: z.boolean(), issues: z.array(HubExecutionAgentValidationIssueSchema), error: z.string().nullable(), }), }); export const HubExecutionControlResponseSchema = z.object({ type: z.literal("hub.execution.control.response"), payload: z.object({ requestId: z.string(), executionId: z.string(), action: HubExecutionControlActionSchema, success: z.boolean(), error: z.string().nullable(), }), }); export const HubExecutionAgentUpdateSchema = z.object({ type: z.literal("hub.execution.agent.update"), payload: z.object({ executionId: z.string(), agentId: z.string(), agent: AgentSnapshotPayloadSchema, }), }); export const HubExecutionAgentStreamSchema = z.object({ type: z.literal("hub.execution.agent.stream"), payload: z.object({ executionId: z.string(), agentId: z.string(), event: AgentStreamEventPayloadSchema, }), }); export type HubExecutionAgentCreateResponse = z.infer; export type HubExecutionAgentValidateResponse = z.infer< typeof HubExecutionAgentValidateResponseSchema >; export type HubExecutionControlResponse = z.infer; export type HubExecutionAgentUpdate = z.infer; export type HubExecutionAgentStream = z.infer; export const HubExecutionOutboundMessageSchema = z.discriminatedUnion("type", [ HubExecutionAgentCreateResponseSchema, HubExecutionAgentValidateResponseSchema, HubExecutionControlResponseSchema, HubExecutionAgentUpdateSchema, HubExecutionAgentStreamSchema, ]); export type HubExecutionOutboundMessage = z.infer; export class HubMessageCorrelationError extends Error { constructor(messageType: HubExecutionOutboundMessage["type"]) { super(`Hub message ${messageType} has mismatched agent correlation`); this.name = "HubMessageCorrelationError"; } } export function parseHubExecutionOutboundMessage(value: unknown): HubExecutionOutboundMessage { const message = HubExecutionOutboundMessageSchema.parse(value); const payload = message.payload; if ( "agent" in payload && payload.agent !== null && "agentId" in payload && payload.agentId !== null && payload.agent.id !== payload.agentId ) { throw new HubMessageCorrelationError(message.type); } return message; } export type DaemonUpdateProgressMessage = z.infer; export const PluginCatalogGetResponseSchema = z.object({ type: z.literal("plugin.catalog.get.response"), payload: z.object({ requestId: z.string(), plugins: z.array( z.object({ id: PluginIdSchema, clientBundle: z.string(), }), ), }), }); export const PluginStatusSchema = z.enum(["running", "disabled", "failed"]); export type PluginStatus = z.infer; export const PluginListItemSchema = z.object({ id: PluginIdSchema, path: z.string(), enabled: z.boolean(), status: PluginStatusSchema, error: z.string().optional(), }); export type PluginListItem = z.infer; export const PluginLogEntrySchema = z.object({ sequence: z.number().int().nonnegative(), timestamp: z.string().datetime(), stream: z.enum(["stdout", "stderr"]), message: z.string(), }); export type PluginLogEntry = z.infer; export const PluginListResponseSchema = z.object({ type: z.literal("plugin.list.response"), payload: z.object({ requestId: z.string(), plugins: z.array(PluginListItemSchema) }), }); export const PluginLogsGetResponseSchema = z.object({ type: z.literal("plugin.logs.get.response"), payload: z.object({ requestId: z.string(), pluginId: PluginIdSchema, entries: z.array(PluginLogEntrySchema), }), }); export const PluginDirectoryInstallResponseSchema = z.object({ type: z.literal("plugin.directory.install.response"), payload: z.object({ requestId: z.string(), plugin: PluginListItemSchema }), }); export const PluginDirectoryInspectResponseSchema = z.object({ type: z.literal("plugin.directory.inspect.response"), payload: z.object({ requestId: z.string(), id: PluginIdSchema }), }); function pluginActionResponse(type: Type) { return z.object({ type: z.literal(type), payload: z.object({ requestId: z.string(), plugin: PluginListItemSchema }), }); } export const PluginReloadResponseSchema = pluginActionResponse("plugin.reload.response"); export const PluginEnableResponseSchema = pluginActionResponse("plugin.enable.response"); export const PluginDisableResponseSchema = pluginActionResponse("plugin.disable.response"); export const PluginRemoveResponseSchema = z.object({ type: z.literal("plugin.remove.response"), payload: z.object({ requestId: z.string() }).strict(), }); export const PluginRpcInvokeResponseSchema = z.object({ type: z.literal("plugin.rpc.invoke.response"), payload: z.object({ requestId: z.string(), output: z.unknown(), }), }); function agentSkillsStatusResponse(type: Type) { return z.object({ type: z.literal(type), payload: AgentSkillsStatusSchema.extend({ requestId: z.string() }), }); } export const AgentSkillsGetStatusResponseSchema = agentSkillsStatusResponse( "agent.skills.get_status.response", ); export const AgentSkillsReconcileResponseSchema = agentSkillsStatusResponse( "agent.skills.reconcile.response", ); export const AgentSkillsUninstallResponseSchema = agentSkillsStatusResponse( "agent.skills.uninstall.response", ); export const AgentSkillsSaveSelectionResponseSchema = z.object({ type: z.literal("agent.skills.save_selection.response"), payload: AgentSkillsSaveResultSchema.extend({ requestId: z.string() }), }); export const AgentSkillsImportLegacySelectionResponseSchema = z.object({ type: z.literal("agent.skills.import_legacy_selection.response"), payload: z.object({ requestId: z.string(), imported: z.boolean(), selection: AgentSkillSelectionSchema, }), }); export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [ HubExecutionAgentCreateResponseSchema, HubExecutionAgentValidateResponseSchema, HubExecutionControlResponseSchema, HubExecutionAgentUpdateSchema, HubExecutionAgentStreamSchema, BrowserAutomationExecuteRequestSchema, PluginCatalogGetResponseSchema, PluginListResponseSchema, PluginLogsGetResponseSchema, PluginDirectoryInstallResponseSchema, PluginDirectoryInspectResponseSchema, PluginReloadResponseSchema, PluginEnableResponseSchema, PluginDisableResponseSchema, PluginRemoveResponseSchema, PluginRpcInvokeResponseSchema, AgentSkillsGetStatusResponseSchema, AgentSkillsReconcileResponseSchema, AgentSkillsUninstallResponseSchema, AgentSkillsSaveSelectionResponseSchema, AgentSkillsImportLegacySelectionResponseSchema, ActivityLogMessageSchema, AssistantChunkMessageSchema, AudioOutputMessageSchema, TranscriptionResultMessageSchema, VoiceInputStateMessageSchema, DictationStreamAckMessageSchema, DictationStreamFinishAcceptedMessageSchema, DictationStreamPartialMessageSchema, DictationStreamFinalMessageSchema, DictationStreamErrorMessageSchema, StatusMessageSchema, PongMessageSchema, PushUnregisterResponseSchema, RpcErrorMessageSchema, ArtifactMessageSchema, AgentUpdateMessageSchema, WorkspaceUpdateMessageSchema, WorkspaceLabelListResponseSchema, WorkspaceLabelUpdateSchema, WorkspaceLabelAssignmentSetResponseSchema, WorkspaceLabelUpdateResponseSchema, WorkspaceLabelDeleteResponseSchema, WorkspaceLabelDeleteInspectResponseSchema, ProjectUpdateMessageSchema, ProjectListResponseMessageSchema, ScriptStatusUpdateMessageSchema, WorkspaceSetupProgressMessageSchema, WorkspaceSetupStatusResponseMessageSchema, AgentStreamMessageSchema, AgentStatusMessageSchema, FetchAgentsResponseMessageSchema, FetchAgentHistoryResponseMessageSchema, FetchRecentProviderSessionsResponseMessageSchema, FetchWorkspacesResponseMessageSchema, ProjectAddResponseSchema, ProjectCreateDirectoryResponseSchema, OpenProjectResponseMessageSchema, WorkspaceGithubSearchRepositoriesResponseSchema, ProjectGithubCloneResponseSchema, StartWorkspaceScriptResponseMessageSchema, WorkspaceScriptListResponseMessageSchema, WorkspaceScriptStartResponseMessageSchema, WorkspaceScriptStopResponseMessageSchema, LegacyListAvailableEditorsResponseMessageSchema, LegacyOpenInEditorResponseMessageSchema, ArchiveWorkspaceResponseMessageSchema, FetchAgentResponseMessageSchema, FetchAgentTimelineResponseMessageSchema, AgentTimelineListPromptsResponseMessageSchema, ProviderSubagentListResponseMessageSchema, ProviderSubagentTimelineResponseMessageSchema, ProviderSubagentUpdateMessageSchema, SetAgentTimelineSubscriptionResponseMessageSchema, AgentAttentionRequiredMessageSchema, AgentForkContextResponseMessageSchema, CancelAgentResponseMessageSchema, ClearAgentAttentionResponseMessageSchema, WorkspaceCreateResponseSchema, WorkspaceClearAttentionResponseSchema, SendAgentMessageResponseMessageSchema, SetVoiceModeResponseMessageSchema, DaemonGetStatusResponseSchema, DaemonGetPairingOfferResponseSchema, DaemonConfigReloadResponseSchema, HubManagementDaemonConnectResponseSchema, HubManagementDaemonGetStatusResponseSchema, HubManagementDaemonDisconnectResponseSchema, DiagnosticsResponseSchema, GetDaemonConfigResponseMessageSchema, SetDaemonConfigResponseMessageSchema, ReadProjectConfigResponseMessageSchema, WriteProjectConfigResponseMessageSchema, SetAgentModeResponseMessageSchema, SetAgentModelResponseMessageSchema, SetAgentThinkingResponseMessageSchema, SetAgentFeatureResponseMessageSchema, AgentConfigApplyResponseMessageSchema, AgentDetachResponseMessageSchema, AgentRewindResponseMessageSchema, UpdateAgentResponseMessageSchema, ProjectRenameResponseSchema, ProjectIconSetResponseSchema, ProjectRemoveResponseSchema, WorkspaceTitleSetResponseSchema, WorkspacePinSetResponseSchema, WorkspaceRecoveryInspectResponseSchema, WorkspaceRecoveryRestoreResponseSchema, WaitForFinishResponseMessageSchema, AgentPermissionRequestMessageSchema, AgentPermissionResolvedMessageSchema, AgentDeletedMessageSchema, AgentArchivedMessageSchema, CloseItemsResponseSchema, CheckoutStatusResponseSchema, CheckoutStatusUpdateSchema, SubscribeCheckoutDiffResponseSchema, CheckoutDiffUpdateSchema, CheckoutCommitResponseSchema, CheckoutMergeResponseSchema, CheckoutMergeFromBaseResponseSchema, CheckoutPullResponseSchema, CheckoutPushResponseSchema, CheckoutRefreshResponseSchema, CheckoutDiscardChangesResponseSchema, CheckoutPrCreateResponseSchema, CheckoutPrMergeResponseSchema, CheckoutForgeSetAutoMergeResponseSchema, CheckoutGithubSetAutoMergeResponseSchema, CheckoutCommitsListResponseSchema, CheckoutCommitFileDiffResponseSchema, CheckoutForgeGetCheckDetailsResponseSchema, CheckoutGithubGetCheckDetailsResponseSchema, CheckoutPrStatusResponseSchema, PullRequestTimelineResponseSchema, CheckoutSwitchBranchResponseSchema, CheckoutRenameBranchResponseSchema, StashSaveResponseSchema, StashPopResponseSchema, StashListResponseSchema, ValidateBranchResponseSchema, BranchSuggestionsResponseSchema, ForgeSearchResponseSchema, GitHubSearchResponseSchema, DirectorySuggestionsResponseSchema, PaseoWorktreeListResponseSchema, PaseoWorktreeArchiveResponseSchema, CreatePaseoWorktreeResponseSchema, FileExplorerResponseSchema, FileSubscribeResponseSchema, FileUnsubscribeResponseSchema, FileWriteResponseSchema, FileEntryCreateResponseSchema, FileEntryRenameResponseSchema, FileEntryDuplicateResponseSchema, FileEntryDeleteResponseSchema, FileUpdateSchema, ProjectIconResponseSchema, ProjectIconGetResponseSchema, FileDownloadTokenResponseSchema, FileUploadResponseSchema, ListProviderModelsResponseMessageSchema, ListProviderModesResponseMessageSchema, ListProviderFeaturesResponseMessageSchema, ListAvailableProvidersResponseSchema, GetProvidersSnapshotResponseMessageSchema, ProvidersSnapshotUpdateMessageSchema, RefreshProvidersSnapshotResponseMessageSchema, ProviderDiagnosticResponseMessageSchema, ProviderUsageListResponseMessageSchema, ListCommandsResponseSchema, ListTerminalsResponseSchema, TerminalsChangedSchema, CreateTerminalResponseSchema, RenameTerminalResponseSchema, SubscribeTerminalResponseSchema, KillTerminalResponseSchema, CaptureTerminalResponseSchema, TerminalStreamExitSchema, TerminalAttentionRequiredSchema, ChatCreateResponseSchema, ChatListResponseSchema, ChatInspectResponseSchema, ChatDeleteResponseSchema, ChatPostResponseSchema, ChatReadResponseSchema, ChatWaitResponseSchema, ScheduleCreateResponseSchema, ScheduleListResponseSchema, ScheduleInspectResponseSchema, ScheduleLogsResponseSchema, SchedulePauseResponseSchema, ScheduleResumeResponseSchema, ScheduleDeleteResponseSchema, ScheduleRunOnceResponseSchema, ScheduleUpdateResponseSchema, LoopRunResponseSchema, LoopListResponseSchema, LoopInspectResponseSchema, LoopLogsResponseSchema, LoopStopResponseSchema, DaemonUpdateProgressMessageSchema, DaemonUpdateResponseSchema, ]); export type SessionOutboundMessage = z.infer; // Type exports for individual message types export type ActivityLogMessage = z.infer; export type AssistantChunkMessage = z.infer; export type AudioOutputMessage = z.infer; export type TranscriptionResultMessage = z.infer; export type StatusMessage = z.infer; export type ServerCapabilityState = z.infer; export type ServerVoiceCapabilities = z.infer; export type ServerCapabilities = z.infer; export type ServerInfoStatusPayload = z.infer; export type RpcErrorMessage = z.infer; export type ArtifactMessage = z.infer; export type AgentUpdateMessage = z.infer; export type WorkspaceSetupProgressMessage = z.infer; export type WorkspaceSetupSnapshot = z.infer; export type WorkspaceSetupStatusResponseMessage = z.infer< typeof WorkspaceSetupStatusResponseMessageSchema >; export type AgentStreamMessage = z.infer; export type AgentStatusMessage = z.infer; export type ProjectCheckoutLitePayload = z.infer; export type ProjectPlacementPayload = z.infer; export type WorkspaceStateBucket = z.infer; export type WorkspaceDescriptorPayload = z.infer; export type WorkspaceProjectDescriptorPayload = z.infer< typeof WorkspaceProjectDescriptorPayloadSchema >; export type ProjectListResponseMessage = z.infer; export type WorkspaceScriptLifecycle = z.infer; export type WorkspaceScriptHealth = z.infer; export type WorkspaceScriptPayload = z.infer; export type FetchAgentsResponseMessage = z.infer; export type FetchAgentHistoryResponseMessage = z.infer< typeof FetchAgentHistoryResponseMessageSchema >; export type FetchRecentProviderSessionsResponseMessage = z.infer< typeof FetchRecentProviderSessionsResponseMessageSchema >; export type FetchWorkspacesResponseMessage = z.infer; export type ProjectAddResponse = z.infer; export type ProjectCreateDirectoryResponse = z.infer; export type ScriptStatusUpdateMessage = z.infer; export type OpenProjectResponseMessage = z.infer; export type WorkspaceGithubSearchRepositoriesResponse = z.infer< typeof WorkspaceGithubSearchRepositoriesResponseSchema >; export type GithubRepository = z.infer; export type ProjectGithubCloneResponse = z.infer; export type StartWorkspaceScriptResponseMessage = z.infer< typeof StartWorkspaceScriptResponseMessageSchema >; export type WorkspaceScriptListRequest = z.infer; export type WorkspaceScriptStartRequest = z.infer; export type WorkspaceScriptStopRequest = z.infer; export type WorkspaceScriptListResponseMessage = z.infer< typeof WorkspaceScriptListResponseMessageSchema >; export type WorkspaceScriptStartResponseMessage = z.infer< typeof WorkspaceScriptStartResponseMessageSchema >; export type WorkspaceScriptStopResponseMessage = z.infer< typeof WorkspaceScriptStopResponseMessageSchema >; export type LegacyListAvailableEditorsResponseMessage = z.infer< typeof LegacyListAvailableEditorsResponseMessageSchema >; export type LegacyOpenInEditorResponseMessage = z.infer< typeof LegacyOpenInEditorResponseMessageSchema >; export type ArchiveWorkspaceResponseMessage = z.infer; export type FetchAgentResponseMessage = z.infer; export type FetchAgentTimelineResponseMessage = z.infer< typeof FetchAgentTimelineResponseMessageSchema >; export type AgentTimelineListPromptsResponseMessage = z.infer< typeof AgentTimelineListPromptsResponseMessageSchema >; export type AgentForkContextResponseMessage = z.infer; export type CancelAgentResponseMessage = z.infer; export type SendAgentMessageResponseMessage = z.infer; export type SetVoiceModeResponseMessage = z.infer; export type SetAgentModeResponseMessage = z.infer; export type SetAgentModelResponseMessage = z.infer; export type SetAgentThinkingResponseMessage = z.infer; export type SetAgentFeatureResponseMessage = z.infer; export type AgentConfigApplyResponseMessage = z.infer; export type AgentDetachResponseMessage = z.infer; export type AgentRewindResponseMessage = z.infer; export type UpdateAgentResponseMessage = z.infer; export type ProjectRenameResponse = z.infer; export type ProjectIconSetResponse = z.infer; export type ProjectRemoveResponse = z.infer; export type WorkspaceTitleSetResponse = z.infer; export type WorkspaceTitleSetResponsePayload = z.infer< typeof WorkspaceTitleSetResponsePayloadSchema >; export type WorkspacePinSetResponse = z.infer; export type WorkspacePinSetResponsePayload = z.infer; export type WorkspaceRecoveryState = z.infer; export type WorkspaceRecoveryInspectResponse = z.infer< typeof WorkspaceRecoveryInspectResponseSchema >; export type WorkspaceRecoveryRestoreResponse = z.infer< typeof WorkspaceRecoveryRestoreResponseSchema >; export type WorkspaceCreateRequest = z.infer; export type WorkspaceCreateResponse = z.infer; export type ProjectRenameResponsePayload = z.infer; export type ProjectRemoveResponsePayload = z.infer; export type WaitForFinishResponseMessage = z.infer; export type AgentPermissionRequestMessage = z.infer; export type AgentPermissionResolvedMessage = z.infer; export type AgentDeletedMessage = z.infer; export type ListProviderModelsResponseMessage = z.infer< typeof ListProviderModelsResponseMessageSchema >; export type ListProviderModesResponseMessage = z.infer< typeof ListProviderModesResponseMessageSchema >; export type ListProviderFeaturesResponseMessage = z.infer< typeof ListProviderFeaturesResponseMessageSchema >; export type ListAvailableProvidersResponse = z.infer; export type DaemonGetStatusResponse = z.infer; export type DaemonGetPairingOfferResponse = z.infer; export type DaemonConfigReloadResponse = z.infer; export type DiagnosticsResponse = z.infer; export type GetProvidersSnapshotResponseMessage = z.infer< typeof GetProvidersSnapshotResponseMessageSchema >; export type ProvidersSnapshotUpdateMessage = z.infer; export type RefreshProvidersSnapshotResponseMessage = z.infer< typeof RefreshProvidersSnapshotResponseMessageSchema >; export type ProviderDiagnosticResponseMessage = z.infer< typeof ProviderDiagnosticResponseMessageSchema >; export type ProviderUsageTone = z.infer; export type ProviderUsageStatus = z.infer; export type ProviderUsage = z.infer; export type ProviderUsageWindow = z.infer; export type ProviderUsageBalance = z.infer; export type ProviderUsageDetail = z.infer; export type ProviderUsageListResponseMessage = z.infer< typeof ProviderUsageListResponseMessageSchema >; export type ChatCreateResponse = z.infer; export type ChatListResponse = z.infer; export type ChatInspectResponse = z.infer; export type ChatDeleteResponse = z.infer; export type ChatPostResponse = z.infer; export type ChatReadResponse = z.infer; export type ChatWaitResponse = z.infer; export type ScheduleCreateResponse = z.infer; export type ScheduleListResponse = z.infer; export type ScheduleInspectResponse = z.infer; export type ScheduleLogsResponse = z.infer; export type SchedulePauseResponse = z.infer; export type ScheduleResumeResponse = z.infer; export type ScheduleDeleteResponse = z.infer; export type ScheduleRunOnceResponse = z.infer; export type ScheduleUpdateResponse = z.infer; export type LoopRunResponse = z.infer; export type LoopListResponse = z.infer; export type LoopInspectResponse = z.infer; export type LoopLogsResponse = z.infer; export type LoopStopResponse = z.infer; // Type exports for payload types export type ActivityLogPayload = z.infer; // Type exports for inbound message types export type VoiceAudioChunkMessage = z.infer; export type FetchAgentsRequestMessage = z.infer; export type FetchAgentHistoryRequestMessage = z.infer; export type FetchRecentProviderSessionsRequestMessage = z.infer< typeof FetchRecentProviderSessionsRequestMessageSchema >; export type FetchWorkspacesRequestMessage = z.infer; export type ProjectListRequestMessage = z.infer; export type FetchAgentRequestMessage = z.infer; export type AgentForkContextRequestMessage = z.infer; export type SendAgentMessageRequest = z.infer; export type WaitForFinishRequest = z.infer; export type DictationStreamStartMessage = z.infer; export type DictationStreamChunkMessage = z.infer; export type DictationStreamFinishMessage = z.infer; export type DictationStreamCancelMessage = z.infer; export type CreateAgentRequestMessage = z.infer; export type AgentAttachment = z.infer; export type ForgeChangeRequestAttachment = z.infer; export type ForgeIssueAttachment = z.infer; export type UploadedFileAttachment = z.infer; export type FirstAgentContext = z.infer; export type ReviewAttachment = z.infer; export type ListProviderModelsRequestMessage = z.infer< typeof ListProviderModelsRequestMessageSchema >; export type ListProviderModesRequestMessage = z.infer; export type ListProviderFeaturesRequestMessage = z.infer< typeof ListProviderFeaturesRequestMessageSchema >; export type ListAvailableProvidersRequestMessage = z.infer< typeof ListAvailableProvidersRequestMessageSchema >; export type GetProvidersSnapshotRequestMessage = z.infer< typeof GetProvidersSnapshotRequestMessageSchema >; export type RefreshProvidersSnapshotRequestMessage = z.infer< typeof RefreshProvidersSnapshotRequestMessageSchema >; export type ProviderDiagnosticRequestMessage = z.infer< typeof ProviderDiagnosticRequestMessageSchema >; export type ChatCreateRequest = z.infer; export type ChatListRequest = z.infer; export type ChatInspectRequest = z.infer; export type ChatDeleteRequest = z.infer; export type ChatPostRequest = z.infer; export type ChatReadRequest = z.infer; export type ChatWaitRequest = z.infer; export type ScheduleCreateRequest = z.infer; export type ScheduleListRequest = z.infer; export type ScheduleInspectRequest = z.infer; export type ScheduleLogsRequest = z.infer; export type SchedulePauseRequest = z.infer; export type ScheduleResumeRequest = z.infer; export type ScheduleDeleteRequest = z.infer; export type ScheduleRunOnceRequest = z.infer; export type ScheduleUpdateRequest = z.infer; export type LoopRunRequest = z.infer; export type LoopListRequest = z.infer; export type LoopInspectRequest = z.infer; export type LoopLogsRequest = z.infer; export type LoopStopRequest = z.infer; export type ResumeAgentRequestMessage = z.infer; export type DeleteAgentRequestMessage = z.infer; export type UpdateAgentRequestMessage = z.infer; export type ProjectIconSource = z.infer; export type ProjectRenameRequest = z.infer; export type ProjectIconSetRequest = z.infer; export type ProjectRemoveRequest = z.infer; export type WorkspaceTitleSetRequest = z.infer; export type WorkspacePinSetRequest = z.infer; export type WorkspaceRecoveryInspectRequest = z.infer; export type WorkspaceRecoveryRestoreRequest = z.infer; export type SetAgentModeRequestMessage = z.infer; export type SetAgentModelRequestMessage = z.infer; export type SetAgentThinkingRequestMessage = z.infer; export type SetAgentFeatureRequestMessage = z.infer; export type AgentConfigApplyRequestMessage = z.infer; export type AgentDetachRequestMessage = z.infer; export type AgentPermissionResponseMessage = z.infer; export type CheckoutStatusRequest = z.infer; export type CheckoutStatusResponse = z.infer; export type CheckoutStatusUpdate = z.infer; export type SubscribeCheckoutDiffRequest = z.infer; export type UnsubscribeCheckoutDiffRequest = z.infer; export type SubscribeCheckoutDiffResponse = z.infer; export type CheckoutDiffUpdate = z.infer; export type CheckoutCommitRequest = z.infer; export type CheckoutCommitResponse = z.infer; export type CheckoutMergeRequest = z.infer; export type CheckoutMergeResponse = z.infer; export type CheckoutMergeFromBaseRequest = z.infer; export type CheckoutMergeFromBaseResponse = z.infer; export type CheckoutPullRequest = z.infer; export type CheckoutPullResponse = z.infer; export type CheckoutPushRequest = z.infer; export type CheckoutPushResponse = z.infer; export type CheckoutRefreshRequest = z.infer; export type CheckoutRefreshResponse = z.infer; export type CheckoutDiscardChangesRequest = z.infer; export type CheckoutDiscardChangesResponse = z.infer; export type CheckoutCommitFile = z.infer; export type CheckoutCommit = z.infer; export type CheckoutCommitsListRequest = z.infer; export type CheckoutCommitsListResponse = z.infer; export type CheckoutCommitFileDiffRequest = z.infer; export type CheckoutCommitFileDiffResponse = z.infer; export type ParsedDiffFile = z.infer; export type CheckoutPrCreateRequest = z.infer; export type CheckoutPrCreateResponse = z.infer; export type CheckoutPrMergeRequest = z.infer; export type CheckoutPrMergeResponse = z.infer; export type CheckoutPrMergeMethod = z.infer["mergeMethod"]; export type CheckoutForgeSetAutoMergeRequest = z.infer< typeof CheckoutForgeSetAutoMergeRequestSchema >; export type CheckoutForgeSetAutoMergeResponse = z.infer< typeof CheckoutForgeSetAutoMergeResponseSchema >; export type CheckoutGithubSetAutoMergeRequest = z.infer< typeof CheckoutGithubSetAutoMergeRequestSchema >; export type CheckoutGithubSetAutoMergeResponse = z.infer< typeof CheckoutGithubSetAutoMergeResponseSchema >; export type CheckoutForgeGetCheckDetailsRequest = z.infer< typeof CheckoutForgeGetCheckDetailsRequestSchema >; export type CheckoutGithubGetCheckDetailsRequest = z.infer< typeof CheckoutGithubGetCheckDetailsRequestSchema >; export type CheckoutCheckDetails = z.infer; export type CheckoutGithubCheckDetails = z.infer; export type CheckoutPipeline = z.infer; export type CheckoutPipelineStage = z.infer; export type CheckoutPipelineJob = z.infer; export type CheckoutForgeGetCheckDetailsResponse = z.infer< typeof CheckoutForgeGetCheckDetailsResponseSchema >; export type CheckoutGithubGetCheckDetailsResponse = z.infer< typeof CheckoutGithubGetCheckDetailsResponseSchema >; export type PullRequestMergeable = z.infer["mergeable"]; export type CheckoutPrStatusRequest = z.infer; export type CheckoutPrStatusResponse = z.infer; export type PullRequestTimelineRequest = z.infer; export type PullRequestTimelineItem = z.infer; export type PullRequestTimelineResponse = z.infer; export type CheckoutSwitchBranchRequest = z.infer; export type CheckoutSwitchBranchResponse = z.infer; export type CheckoutRenameBranchRequest = z.infer; export type CheckoutRenameBranchResponse = z.infer; export type StashSaveRequest = z.infer; export type StashSaveResponse = z.infer; export type StashPopRequest = z.infer; export type StashPopResponse = z.infer; export type StashListRequest = z.infer; export type StashListResponse = z.infer; export type StashEntry = z.infer; export type ValidateBranchRequest = z.infer; export type ValidateBranchResponse = z.infer; export type BranchSuggestionsRequest = z.infer; export type BranchSuggestionsResponse = z.infer; export type ForgeSearchItem = z.infer; export type ForgeSearchKind = "issue" | "change_request"; export type ForgeSearchRequest = z.infer; export type ForgeSearchResponse = z.infer; export type GitHubSearchItem = z.infer; export type GitHubSearchKind = z.infer; export type GitHubSearchRequest = z.infer; export type GitHubSearchResponse = z.infer; export type ChangeRequestCheckoutSource = z.infer; export type CreatePaseoWorktreeRequest = z.infer; export type DirectorySuggestionsRequest = z.infer; export type DirectorySuggestionsResponse = z.infer; export type PaseoWorktreeListRequest = z.infer; export type PaseoWorktreeListResponse = z.infer; export type PaseoWorktreeArchiveRequest = z.infer; export type PaseoWorktreeArchiveResponse = z.infer; export type WorkspaceSetupStatusRequest = z.infer; export type LegacyListAvailableEditorsRequest = z.infer< typeof LegacyListAvailableEditorsRequestSchema >; export type LegacyOpenInEditorRequest = z.infer; export type OpenProjectRequest = z.infer; export type ProjectAddRequest = z.infer; export type ProjectCreateDirectoryRequest = z.infer; export type ProjectCreateDirectoryErrorCode = z.infer; export type WorkspaceGithubSearchRepositoriesRequest = z.infer< typeof WorkspaceGithubSearchRepositoriesRequestSchema >; export type ProjectGithubCloneRequest = z.infer; export type ProjectGithubCloneProtocol = z.infer; export type ArchiveWorkspaceRequest = z.infer; export type WorkspaceClearAttentionRequest = z.infer; export type FileExplorerRequest = z.infer; export type FileExplorerResponse = z.infer; export type FileVersion = z.infer; export type FileSubscribeRequest = z.infer; export type FileSubscribeResponse = z.infer; export type FileUnsubscribeRequest = z.infer; export type FileUnsubscribeResponse = z.infer; export type FileWriteRequest = z.infer; export type FileWriteResponse = z.infer; export type FileEntryCreateRequest = z.infer; export type FileEntryCreateResponse = z.infer; export type FileEntryRenameRequest = z.infer; export type FileEntryRenameResponse = z.infer; export type FileEntryDuplicateRequest = z.infer; export type FileEntryDuplicateResponse = z.infer; export type FileEntryDeleteRequest = z.infer; export type FileEntryDeleteResponse = z.infer; export type FileWriteResult = z.infer; export type FileUpdate = z.infer; export type ProjectIconRequest = z.infer; export type ProjectIconResponse = z.infer; export type ProjectIconGetRequest = z.infer; export type ProjectIconGetResponse = z.infer; export type ProjectIcon = z.infer; export type FileDownloadTokenRequest = z.infer; export type FileDownloadTokenResponse = z.infer; export type FileUploadRequest = z.infer; export type FileUploadResponse = z.infer; export type RestartServerRequestMessage = z.infer; export type ShutdownServerRequestMessage = z.infer; export type ClearAgentAttentionMessage = z.infer; export type ClearAgentAttentionResponseMessage = z.infer< typeof ClearAgentAttentionResponseMessageSchema >; export type ClientHeartbeatMessage = z.infer; export type ListCommandsRequest = z.infer; export type ListCommandsResponse = z.infer; export type RegisterPushTokenMessage = z.infer; export type PushUnregisterRequest = z.infer; export type PushUnregisterResponse = z.infer; // Terminal message types export type ListTerminalsRequest = z.infer; export type ListTerminalsResponse = z.infer; export type SubscribeTerminalsRequest = z.infer; export type UnsubscribeTerminalsRequest = z.infer; export type TerminalsChanged = z.infer; export type CreateTerminalRequest = z.infer; export type CreateTerminalResponse = z.infer; export type RenameTerminalRequest = z.infer; export type RenameTerminalResponse = z.infer; export type StartWorkspaceScriptRequest = z.infer; export type StartWorkspaceScriptResponse = z.infer< typeof StartWorkspaceScriptResponseMessageSchema >; export type SubscribeTerminalRequest = z.infer; export type SubscribeTerminalResponse = z.infer; export type UnsubscribeTerminalRequest = z.infer; export type TerminalInput = z.infer; export type TerminalCell = z.infer; export type TerminalCursorStyle = z.infer; export type TerminalCursor = z.infer; export type TerminalState = z.infer; export type CloseItemsRequest = z.infer; export type CloseItemsResponse = z.infer; export type KillTerminalRequest = z.infer; export type KillTerminalResponse = z.infer; export type CaptureTerminalRequest = z.infer; export type CaptureTerminalResponse = z.infer; export type TerminalStreamExit = z.infer; // ============================================================================ // WebSocket Level Messages (wraps session messages) // ============================================================================ // WebSocket-only messages (not session messages) export const WSPingMessageSchema = z.object({ type: z.literal("ping"), }); export const WSPongMessageSchema = z.object({ type: z.literal("pong"), }); export const WSHelloMessageSchema = z.object({ type: z.literal("hello"), clientId: z.string().min(1), clientType: z.enum(["mobile", "browser", "cli", "mcp"]), protocolVersion: z.number().int(), appVersion: z.string().optional(), capabilities: z .object({ voice: z.boolean().optional(), pushNotifications: z.boolean().optional(), [CLIENT_CAPS.reasoningMergeEnum]: z.boolean().optional(), [CLIENT_CAPS.selectiveAgentTimeline]: z.boolean().optional(), [CLIENT_CAPS.customModeIcons]: z.boolean().optional(), [CLIENT_CAPS.terminalReflowableSnapshot]: z.boolean().optional(), [CLIENT_CAPS.providerSubagents]: z.boolean().optional(), [CLIENT_CAPS.projectUpdates]: z.boolean().optional(), [CLIENT_CAPS.compactProviderSnapshots]: z.boolean().optional(), [CLIENT_CAPS.browserHost]: BrowserAutomationHostCapabilitySchema.optional(), }) .passthrough() .optional(), }); export const WSRecordingStateMessageSchema = z.object({ type: z.literal("recording_state"), isRecording: z.boolean(), }); // Wrapped session message export const WSSessionInboundSchema = z.object({ type: z.literal("session"), message: SessionInboundMessageSchema, }); export const WSSessionOutboundSchema = z.object({ type: z.literal("session"), message: SessionOutboundMessageSchema, }); // Complete WebSocket message schemas export const WSInboundMessageSchema = z.discriminatedUnion("type", [ WSPingMessageSchema, WSHelloMessageSchema, WSRecordingStateMessageSchema, WSSessionInboundSchema, ]); export const WSOutboundMessageSchema = z.discriminatedUnion("type", [ WSPongMessageSchema, WSSessionOutboundSchema, ]); export type WSInboundMessage = z.infer; export type WSOutboundMessage = z.infer; export type WSHelloMessage = z.infer; // ============================================================================ // Helper functions for message conversion // ============================================================================ /** * Extract session message from WebSocket message * Returns null if message should be handled at WS level only */ export function extractSessionMessage(wsMsg: WSInboundMessage): SessionInboundMessage | null { if (wsMsg.type === "session") { return wsMsg.message; } // Ping and recording_state are WS-level only return null; } /** * Wrap session message in WebSocket envelope */ export function wrapSessionMessage(sessionMsg: SessionOutboundMessage): WSOutboundMessage { return { type: "session", message: sessionMsg, }; } export function parseServerInfoStatusPayload(payload: unknown): ServerInfoStatusPayload | null { const parsed = ServerInfoStatusPayloadSchema.safeParse(payload); if (!parsed.success) { return null; } return parsed.data; }