@hyperdrive.bot/paseo-protocol 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +12 -0
  2. package/dist/agent-attention-notification.d.ts +41 -0
  3. package/dist/agent-attention-notification.js +140 -0
  4. package/dist/agent-labels.d.ts +9 -0
  5. package/dist/agent-labels.js +23 -0
  6. package/dist/agent-lifecycle.d.ts +3 -0
  7. package/dist/agent-lifecycle.js +8 -0
  8. package/dist/agent-state-bucket.d.ts +24 -0
  9. package/dist/agent-state-bucket.js +45 -0
  10. package/dist/agent-title-limits.d.ts +2 -0
  11. package/dist/agent-title-limits.js +2 -0
  12. package/dist/agent-types.d.ts +461 -0
  13. package/dist/agent-types.js +22 -0
  14. package/dist/binary-frames/chunk-size.d.ts +2 -0
  15. package/dist/binary-frames/chunk-size.js +7 -0
  16. package/dist/binary-frames/demux.d.ts +11 -0
  17. package/dist/binary-frames/demux.js +23 -0
  18. package/dist/binary-frames/file-transfer.d.ts +50 -0
  19. package/dist/binary-frames/file-transfer.js +114 -0
  20. package/dist/binary-frames/index.d.ts +4 -0
  21. package/dist/binary-frames/index.js +4 -0
  22. package/dist/binary-frames/terminal.d.ts +31 -0
  23. package/dist/binary-frames/terminal.js +99 -0
  24. package/dist/branch-slug.d.ts +14 -0
  25. package/dist/branch-slug.js +49 -0
  26. package/dist/browser-automation/capabilities.d.ts +7 -0
  27. package/dist/browser-automation/capabilities.js +31 -0
  28. package/dist/browser-automation/rpc-schemas.d.ts +1112 -0
  29. package/dist/browser-automation/rpc-schemas.js +462 -0
  30. package/dist/chat/rpc-schemas.d.ts +158 -0
  31. package/dist/chat/rpc-schemas.js +103 -0
  32. package/dist/chat/types.d.ts +30 -0
  33. package/dist/chat/types.js +22 -0
  34. package/dist/client-capabilities.d.ts +8 -0
  35. package/dist/client-capabilities.js +16 -0
  36. package/dist/connection-offer.d.ts +36 -0
  37. package/dist/connection-offer.js +53 -0
  38. package/dist/daemon-endpoints.d.ts +47 -0
  39. package/dist/daemon-endpoints.js +201 -0
  40. package/dist/error-utils.d.ts +11 -0
  41. package/dist/error-utils.js +27 -0
  42. package/dist/generated/validation/ws-outbound.aot.d.ts +2 -0
  43. package/dist/generated/validation/ws-outbound.aot.js +46936 -0
  44. package/dist/git-remote.d.ts +16 -0
  45. package/dist/git-remote.js +72 -0
  46. package/dist/host-connection-schema.d.ts +11 -0
  47. package/dist/host-connection-schema.js +9 -0
  48. package/dist/literal-union.d.ts +2 -0
  49. package/dist/literal-union.js +2 -0
  50. package/dist/loop/rpc-schemas.d.ts +636 -0
  51. package/dist/loop/rpc-schemas.js +163 -0
  52. package/dist/messages.d.ts +36565 -0
  53. package/dist/messages.js +4703 -0
  54. package/dist/paseo-config-schema.d.ts +117 -0
  55. package/dist/paseo-config-schema.js +79 -0
  56. package/dist/path-utils.d.ts +2 -0
  57. package/dist/path-utils.js +16 -0
  58. package/dist/provider-config.d.ts +136 -0
  59. package/dist/provider-config.js +119 -0
  60. package/dist/provider-icon-names.d.ts +5 -0
  61. package/dist/provider-icon-names.js +52 -0
  62. package/dist/provider-manifest.d.ts +114 -0
  63. package/dist/provider-manifest.js +344 -0
  64. package/dist/schedule/cron-expression.d.ts +13 -0
  65. package/dist/schedule/cron-expression.js +97 -0
  66. package/dist/schedule/rpc-schemas.d.ts +574 -0
  67. package/dist/schedule/rpc-schemas.js +151 -0
  68. package/dist/schedule/types.d.ts +202 -0
  69. package/dist/schedule/types.js +75 -0
  70. package/dist/terminal-activity.d.ts +21 -0
  71. package/dist/terminal-activity.js +25 -0
  72. package/dist/terminal-input-mode.d.ts +26 -0
  73. package/dist/terminal-input-mode.js +151 -0
  74. package/dist/terminal-key-input.d.ts +13 -0
  75. package/dist/terminal-key-input.js +201 -0
  76. package/dist/terminal-profiles.d.ts +6 -0
  77. package/dist/terminal-profiles.js +28 -0
  78. package/dist/terminal-snapshot.d.ts +3 -0
  79. package/dist/terminal-snapshot.js +178 -0
  80. package/dist/terminal-stream-protocol.d.ts +2 -0
  81. package/dist/terminal-stream-protocol.js +2 -0
  82. package/dist/terminal-subscription-key.d.ts +2 -0
  83. package/dist/terminal-subscription-key.js +9 -0
  84. package/dist/tool-call-display.d.ts +11 -0
  85. package/dist/tool-call-display.js +135 -0
  86. package/dist/tool-name-normalization.d.ts +9 -0
  87. package/dist/tool-name-normalization.js +82 -0
  88. package/dist/validation/ws-outbound-schema-metadata.d.ts +6828 -0
  89. package/dist/validation/ws-outbound-schema-metadata.js +3 -0
  90. package/dist/validation/ws-outbound.d.ts +12 -0
  91. package/dist/validation/ws-outbound.js +8 -0
  92. package/package.json +42 -0
@@ -0,0 +1,4703 @@
1
+ import { z } from "zod";
2
+ import { TerminalActivitySchema } from "./terminal-activity.js";
3
+ import { CLIENT_CAPS } from "./client-capabilities.js";
4
+ import { AGENT_LIFECYCLE_STATUSES } from "./agent-lifecycle.js";
5
+ import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from "./agent-title-limits.js";
6
+ import { AgentProviderSchema } from "./provider-manifest.js";
7
+ import { TOOL_CALL_ICON_NAMES } from "./agent-types.js";
8
+ import { FileBeginMetadataSchema } from "./binary-frames/file-transfer.js";
9
+ import { ChatCreateRequestSchema, ChatListRequestSchema, ChatInspectRequestSchema, ChatDeleteRequestSchema, ChatPostRequestSchema, ChatReadRequestSchema, ChatWaitRequestSchema, ChatCreateResponseSchema, ChatListResponseSchema, ChatInspectResponseSchema, ChatDeleteResponseSchema, ChatPostResponseSchema, ChatReadResponseSchema, ChatWaitResponseSchema, } from "./chat/rpc-schemas.js";
10
+ import { ScheduleCreateRequestSchema, ScheduleListRequestSchema, ScheduleInspectRequestSchema, ScheduleLogsRequestSchema, SchedulePauseRequestSchema, ScheduleResumeRequestSchema, ScheduleDeleteRequestSchema, ScheduleRunOnceRequestSchema, ScheduleUpdateRequestSchema, ScheduleCreateResponseSchema, ScheduleListResponseSchema, ScheduleInspectResponseSchema, ScheduleLogsResponseSchema, SchedulePauseResponseSchema, ScheduleResumeResponseSchema, ScheduleDeleteResponseSchema, ScheduleRunOnceResponseSchema, ScheduleUpdateResponseSchema, } from "./schedule/rpc-schemas.js";
11
+ import { LoopRunRequestSchema, LoopListRequestSchema, LoopInspectRequestSchema, LoopLogsRequestSchema, LoopStopRequestSchema, LoopRunResponseSchema, LoopListResponseSchema, LoopInspectResponseSchema, LoopLogsResponseSchema, LoopStopResponseSchema, } from "./loop/rpc-schemas.js";
12
+ import { BrowserAutomationExecuteRequestSchema, BrowserAutomationExecuteResponseSchema, } from "./browser-automation/rpc-schemas.js";
13
+ import { BrowserAutomationHostCapabilitySchema } from "./browser-automation/capabilities.js";
14
+ import { PaseoConfigRawSchema, PaseoLifecycleCommandRawSchema, PaseoMetadataGenerationEntrySchema, PaseoMetadataGenerationSchema, PaseoScriptEntryRawSchema, PaseoWorktreeConfigRawSchema, PaseoConfigRevisionSchema, ProjectConfigRpcErrorSchema, } from "./paseo-config-schema.js";
15
+ export { PaseoConfigRawSchema, PaseoLifecycleCommandRawSchema, PaseoMetadataGenerationEntrySchema, PaseoMetadataGenerationSchema, PaseoScriptEntryRawSchema, PaseoWorktreeConfigRawSchema, };
16
+ // ---------------------------------------------------------------------------
17
+ // Mutable daemon config schemas (shared between server store and client)
18
+ // ---------------------------------------------------------------------------
19
+ const MutableDaemonProviderModelSchema = z
20
+ .object({
21
+ id: z.string().min(1),
22
+ label: z.string().min(1),
23
+ description: z.string().optional(),
24
+ isDefault: z.boolean().optional(),
25
+ })
26
+ .passthrough();
27
+ const MutableDaemonProviderConfigSchema = z
28
+ .object({
29
+ enabled: z.boolean().optional(),
30
+ additionalModels: z.array(MutableDaemonProviderModelSchema).optional(),
31
+ })
32
+ .passthrough();
33
+ const MutableStructuredGenerationProviderSchema = z
34
+ .object({
35
+ provider: z.string().min(1),
36
+ model: z.string().min(1).optional(),
37
+ thinkingOptionId: z.string().min(1).optional(),
38
+ })
39
+ .passthrough();
40
+ const MutableMetadataGenerationConfigSchema = z
41
+ .object({
42
+ providers: z.array(MutableStructuredGenerationProviderSchema).default([]),
43
+ })
44
+ .passthrough();
45
+ export const TerminalProfileSchema = z
46
+ .object({
47
+ id: z.string(),
48
+ name: z.string(),
49
+ command: z.string(),
50
+ args: z.array(z.string()).optional(),
51
+ icon: z.string().optional(),
52
+ })
53
+ .passthrough();
54
+ const MutableBrowserToolsConfigSchema = z
55
+ .object({
56
+ enabled: z.boolean().default(false),
57
+ })
58
+ .passthrough();
59
+ export const MutableDaemonConfigSchema = z
60
+ .object({
61
+ mcp: z
62
+ .object({
63
+ injectIntoAgents: z.boolean(),
64
+ })
65
+ .passthrough(),
66
+ browserTools: MutableBrowserToolsConfigSchema.default({ enabled: false }),
67
+ providers: z.record(z.string(), MutableDaemonProviderConfigSchema).default({}),
68
+ metadataGeneration: MutableMetadataGenerationConfigSchema.default({ providers: [] }),
69
+ autoArchiveAfterMerge: z.boolean().default(false),
70
+ enableTerminalAgentHooks: z.boolean().default(false),
71
+ appendSystemPrompt: z.string().default(""),
72
+ terminalProfiles: z.array(TerminalProfileSchema).optional(),
73
+ })
74
+ .passthrough();
75
+ export const MutableDaemonConfigPatchSchema = z
76
+ .object({
77
+ mcp: MutableDaemonConfigSchema.shape.mcp.partial().optional(),
78
+ browserTools: MutableBrowserToolsConfigSchema.partial().optional(),
79
+ providers: z
80
+ .record(z.string(), MutableDaemonProviderConfigSchema.partial().passthrough())
81
+ .optional(),
82
+ metadataGeneration: MutableMetadataGenerationConfigSchema.partial().optional(),
83
+ autoArchiveAfterMerge: z.boolean().optional(),
84
+ enableTerminalAgentHooks: z.boolean().optional(),
85
+ appendSystemPrompt: z.string().optional(),
86
+ terminalProfiles: z.array(TerminalProfileSchema).optional(),
87
+ })
88
+ .partial()
89
+ .passthrough();
90
+ export const AgentStatusSchema = z.enum(AGENT_LIFECYCLE_STATUSES);
91
+ // Workflow (first-class entity) — Epic 1, Story 1.1.
92
+ // `running` and `cancelled` are load-bearing for later stories (kill-proof smoke +
93
+ // workflow.cancel). This enum is concrete and final-for-now; extend only additively.
94
+ export const WORKFLOW_STATUSES = [
95
+ "pending",
96
+ "running",
97
+ "completed",
98
+ "failed",
99
+ "cancelled",
100
+ ];
101
+ export const WorkflowStatusSchema = z.enum(WORKFLOW_STATUSES);
102
+ // Minimal phase shape. Story 1.2 ports the full phase/story task-graph and will ADD
103
+ // fields additively — keep every non-essential field optional so 1.2 never has to
104
+ // break the protocol contract.
105
+ export const WorkflowPhaseSchema = z.object({
106
+ id: z.string(),
107
+ title: z.string(),
108
+ status: WorkflowStatusSchema,
109
+ storyCount: z.number().int().nonnegative().optional(),
110
+ completedStoryCount: z.number().int().nonnegative().optional(),
111
+ });
112
+ // Live/wire snapshot of a workflow. New fields must be additive (.optional() with a
113
+ // sensible default) to preserve back-compat — a 6-month-old client must still parse this.
114
+ export const WorkflowSnapshotSchema = z.object({
115
+ id: z.string(),
116
+ cwd: z.string(),
117
+ status: WorkflowStatusSchema,
118
+ title: z.string().nullable().optional(),
119
+ labels: z.record(z.string(), z.string()).default({}),
120
+ phases: z.array(WorkflowPhaseSchema).default([]),
121
+ childAgentIds: z.array(z.string()).default([]),
122
+ totalStoryCount: z.number().int().nonnegative().optional(),
123
+ // Defaults to 0 because the epic asserts this count increases over a workflow's life.
124
+ completedStoryCount: z.number().int().nonnegative().default(0),
125
+ createdAt: z.string(),
126
+ updatedAt: z.string(),
127
+ startedAt: z.string().nullable().optional(),
128
+ completedAt: z.string().nullable().optional(),
129
+ archivedAt: z.string().nullable().optional(),
130
+ });
131
+ // Durable persisted record. For Story 1.1 it is identical to the wire snapshot (no
132
+ // manager projection exists yet — that arrives in Story 1.2). Aliasing guarantees every
133
+ // loaded STORED_WORKFLOW_SCHEMA record also satisfies WorkflowSnapshotSchema.
134
+ export const STORED_WORKFLOW_SCHEMA = WorkflowSnapshotSchema;
135
+ // ============================================================================
136
+ // Workflow RPC — Epic 1, Story 1.3 (`workflow.list/get/cancel/events`)
137
+ // ============================================================================
138
+ // Dotted namespace + direction suffix (docs/rpc-namespacing.md): params at the
139
+ // top level on requests, correlated data under `payload` on responses, and a
140
+ // `requestId` correlation key in BOTH directions. Every member here is additive
141
+ // — a 6-month-old client never sends these and still parses `server_info`; a
142
+ // 6-month-old daemon never advertises the feature and is never sent them.
143
+ // A recorded workflow lifecycle event, DERIVED from a snapshot's timestamps
144
+ // (created/started/completed/archived). `workflow.events.response` returns the
145
+ // current list only — continuous server-push of new events is Epic 2.
146
+ export const WORKFLOW_EVENT_TYPES = [
147
+ "created",
148
+ "started",
149
+ "completed",
150
+ "failed",
151
+ "cancelled",
152
+ "archived",
153
+ ];
154
+ export const WorkflowEventTypeSchema = z.enum(WORKFLOW_EVENT_TYPES);
155
+ export const WorkflowLifecycleEventSchema = z.object({
156
+ workflowId: z.string(),
157
+ type: WorkflowEventTypeSchema,
158
+ status: WorkflowStatusSchema,
159
+ at: z.string(),
160
+ });
161
+ // workflow.list — every known workflow snapshot.
162
+ export const WorkflowListRequestSchema = z.object({
163
+ type: z.literal("workflow.list.request"),
164
+ requestId: z.string(),
165
+ });
166
+ export const WorkflowListResponseSchema = z.object({
167
+ type: z.literal("workflow.list.response"),
168
+ payload: z.object({
169
+ requestId: z.string(),
170
+ workflows: z.array(WorkflowSnapshotSchema),
171
+ }),
172
+ });
173
+ // workflow.get — `payload.workflow` is null for an unknown id. Absence is a
174
+ // valid, parseable response, NOT an error.
175
+ export const WorkflowGetRequestSchema = z.object({
176
+ type: z.literal("workflow.get.request"),
177
+ requestId: z.string(),
178
+ workflowId: z.string(),
179
+ });
180
+ export const WorkflowGetResponseSchema = z.object({
181
+ type: z.literal("workflow.get.response"),
182
+ payload: z.object({
183
+ requestId: z.string(),
184
+ workflow: WorkflowSnapshotSchema.nullable(),
185
+ }),
186
+ });
187
+ // workflow.cancel — `payload.workflow` is the post-cancel snapshot. Cancelling
188
+ // an unknown or already-terminal workflow resolves via `rpc_error`, not here.
189
+ export const WorkflowCancelRequestSchema = z.object({
190
+ type: z.literal("workflow.cancel.request"),
191
+ requestId: z.string(),
192
+ workflowId: z.string(),
193
+ });
194
+ export const WorkflowCancelResponseSchema = z.object({
195
+ type: z.literal("workflow.cancel.response"),
196
+ payload: z.object({
197
+ requestId: z.string(),
198
+ workflow: WorkflowSnapshotSchema,
199
+ }),
200
+ });
201
+ // workflow.events — one-shot read of the recorded lifecycle-event list.
202
+ // `events` is a read operation; the noun-segment name is fixed by Epic 1's
203
+ // success criterion 5.
204
+ export const WorkflowEventsRequestSchema = z.object({
205
+ type: z.literal("workflow.events.request"),
206
+ requestId: z.string(),
207
+ workflowId: z.string(),
208
+ });
209
+ export const WorkflowEventsResponseSchema = z.object({
210
+ type: z.literal("workflow.events.response"),
211
+ payload: z.object({
212
+ requestId: z.string(),
213
+ workflowId: z.string(),
214
+ events: z.array(WorkflowLifecycleEventSchema),
215
+ }),
216
+ });
217
+ // ============================================================================
218
+ // Workflow task-graph + start RPC — Epic 3, Story 3.3 (`workflow.start`)
219
+ // ============================================================================
220
+ // COMPAT(workflows): added in v0.1.X, remove gate after 2026-12-17.
221
+ // The task-graph schema is the SINGLE parse boundary for the structure the daemon
222
+ // already consumes (`TaskGraph`/`TaskNode` in `workflow-manager.ts`). It is reused
223
+ // by BOTH this client-facing `workflow.start` RPC and the `workflow_start` MCP
224
+ // tool (no second divergent copy). The CLI's `child_process` / `claude -p`
225
+ // spawning is deliberately NOT modelled — `agentManager.createAgent` replaces it.
226
+ /** One task node — becomes one child agent when the workflow starts. */
227
+ export const WorkflowTaskNodeSchema = z.object({
228
+ id: z.string(),
229
+ dependencies: z.array(z.string()),
230
+ description: z.string(),
231
+ prompt: z.string(),
232
+ title: z.string(),
233
+ outputFile: z.string(),
234
+ parallelizable: z.boolean(),
235
+ estimatedMinutes: z.number(),
236
+ agentType: z.string().optional(),
237
+ targetFiles: z.array(z.string()).optional(),
238
+ });
239
+ /** Decomposed task graph driving child-agent spawning. */
240
+ export const WorkflowTaskGraphSchema = z.object({
241
+ goal: z.string(),
242
+ masterPrompt: z.string(),
243
+ metadata: z.object({
244
+ executionLayers: z.array(z.array(z.string())),
245
+ maxParallelism: z.number(),
246
+ totalTasks: z.number(),
247
+ estimatedDuration: z.number(),
248
+ perFileMode: z.boolean(),
249
+ storyFormat: z.boolean().optional(),
250
+ totalFiles: z.number().optional(),
251
+ }),
252
+ session: z.object({
253
+ id: z.string(),
254
+ createdAt: z.string(),
255
+ outputDirectory: z.string(),
256
+ }),
257
+ tasks: z.array(WorkflowTaskNodeSchema),
258
+ });
259
+ // workflow.start — the client-facing twin of the `workflow_start` MCP tool. The
260
+ // daemon spawns one child agent per task node through the normal agent lifecycle
261
+ // and returns the new workflow id + spawned child agent ids. Sequencing/retry
262
+ // stays in WorkflowManager — this message is only the launch boundary, so a CLI
263
+ // or app client (which speaks RPC, not MCP) can launch a daemon-owned, kill-proof
264
+ // run. `cwd` is required: an RPC client has no caller-agent cwd to default from.
265
+ export const WorkflowStartRequestSchema = z.object({
266
+ type: z.literal("workflow.start.request"),
267
+ requestId: z.string(),
268
+ graph: WorkflowTaskGraphSchema,
269
+ provider: z.string(),
270
+ cwd: z.string(),
271
+ model: z.string().optional(),
272
+ title: z.string().optional(),
273
+ labels: z.record(z.string(), z.string()).optional(),
274
+ });
275
+ export const WorkflowStartResponseSchema = z.object({
276
+ type: z.literal("workflow.start.response"),
277
+ payload: z.object({
278
+ requestId: z.string(),
279
+ workflowId: z.string(),
280
+ childAgentIds: z.array(z.string()),
281
+ status: WorkflowStatusSchema,
282
+ }),
283
+ });
284
+ const AgentModeSchema = z.object({
285
+ id: z.string(),
286
+ label: z.string(),
287
+ description: z.string().optional(),
288
+ icon: z.string().optional(),
289
+ colorTier: z.string().optional(),
290
+ });
291
+ const ProviderStatusSchema = z.enum([
292
+ "ready",
293
+ "loading",
294
+ "error",
295
+ "unavailable",
296
+ ]);
297
+ const AgentSelectOptionSchema = z.object({
298
+ id: z.string(),
299
+ label: z.string(),
300
+ description: z.string().optional(),
301
+ isDefault: z.boolean().optional(),
302
+ metadata: z.record(z.string(), z.unknown()).optional(),
303
+ });
304
+ const AgentProviderNoticeSchema = z.discriminatedUnion("type", [
305
+ z.object({ type: z.literal("info"), message: z.string() }),
306
+ z.object({ type: z.literal("warning"), message: z.string() }),
307
+ z.object({ type: z.literal("error"), message: z.string() }),
308
+ ]);
309
+ export const AgentFeatureToggleSchema = z.object({
310
+ type: z.literal("toggle"),
311
+ id: z.string(),
312
+ label: z.string(),
313
+ description: z.string().optional(),
314
+ tooltip: z.string().optional(),
315
+ icon: z.string().optional(),
316
+ value: z.boolean(),
317
+ });
318
+ export const AgentFeatureSelectSchema = z.object({
319
+ type: z.literal("select"),
320
+ id: z.string(),
321
+ label: z.string(),
322
+ description: z.string().optional(),
323
+ tooltip: z.string().optional(),
324
+ icon: z.string().optional(),
325
+ value: z.string().nullable(),
326
+ options: z.array(AgentSelectOptionSchema),
327
+ });
328
+ export const AgentFeatureSchema = z.discriminatedUnion("type", [
329
+ AgentFeatureToggleSchema,
330
+ AgentFeatureSelectSchema,
331
+ ]);
332
+ const AgentModelDefinitionSchema = z.object({
333
+ provider: AgentProviderSchema,
334
+ id: z.string(),
335
+ label: z.string(),
336
+ description: z.string().optional(),
337
+ isDefault: z.boolean().optional(),
338
+ metadata: z.record(z.string(), z.unknown()).optional(),
339
+ contextWindowMaxTokens: z.number().optional(),
340
+ thinkingOptions: z.array(AgentSelectOptionSchema).optional(),
341
+ defaultThinkingOptionId: z.string().optional(),
342
+ });
343
+ export const ProviderSnapshotEntrySchema = z.object({
344
+ provider: AgentProviderSchema,
345
+ status: ProviderStatusSchema,
346
+ enabled: z.boolean().optional().default(true),
347
+ error: z.string().optional(),
348
+ models: z.array(AgentModelDefinitionSchema).optional(),
349
+ modes: z.array(AgentModeSchema).optional(),
350
+ fetchedAt: z.string().optional(),
351
+ label: z.string().optional(),
352
+ description: z.string().optional(),
353
+ defaultModeId: z.string().nullable().optional(),
354
+ // Hot-swap fields (added 2026-05). Both optional for backward compatibility —
355
+ // old daemons did not emit them; old clients tolerate them on inbound.
356
+ wireFamily: z.enum(["anthropic-messages", "openai-compatible", "generic-acp"]).optional(),
357
+ compatibleProviders: z.array(z.string()).optional(),
358
+ });
359
+ const AgentCapabilityFlagsSchema = z
360
+ .object({
361
+ supportsStreaming: z.boolean(),
362
+ supportsSessionPersistence: z.boolean(),
363
+ supportsSessionListing: z.boolean().optional(),
364
+ supportsDynamicModes: z.boolean(),
365
+ supportsMcpServers: z.boolean(),
366
+ supportsReasoningStream: z.boolean(),
367
+ supportsToolInvocations: z.boolean(),
368
+ // COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X.
369
+ supportsRewindConversation: z.boolean().optional().default(false),
370
+ // COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X.
371
+ supportsRewindFiles: z.boolean().optional().default(false),
372
+ // COMPAT(rewind): added in v0.1.X, drop when floor >= v0.1.X.
373
+ supportsRewindBoth: z.boolean().optional().default(false),
374
+ })
375
+ .catchall(z.boolean());
376
+ const AgentUsageSchema = z.object({
377
+ inputTokens: z.number().optional(),
378
+ cachedInputTokens: z.number().optional(),
379
+ outputTokens: z.number().optional(),
380
+ totalCostUsd: z.number().optional(),
381
+ contextWindowMaxTokens: z.number().optional(),
382
+ contextWindowUsedTokens: z.number().optional(),
383
+ });
384
+ const McpStdioServerConfigSchema = z.object({
385
+ type: z.literal("stdio"),
386
+ command: z.string(),
387
+ args: z.array(z.string()).optional(),
388
+ env: z.record(z.string(), z.string()).optional(),
389
+ alwaysLoad: z.boolean().optional(),
390
+ });
391
+ const McpHttpServerConfigSchema = z.object({
392
+ type: z.literal("http"),
393
+ url: z.string(),
394
+ headers: z.record(z.string(), z.string()).optional(),
395
+ alwaysLoad: z.boolean().optional(),
396
+ });
397
+ const McpSseServerConfigSchema = z.object({
398
+ type: z.literal("sse"),
399
+ url: z.string(),
400
+ headers: z.record(z.string(), z.string()).optional(),
401
+ alwaysLoad: z.boolean().optional(),
402
+ });
403
+ const McpServerConfigSchema = z.discriminatedUnion("type", [
404
+ McpStdioServerConfigSchema,
405
+ McpHttpServerConfigSchema,
406
+ McpSseServerConfigSchema,
407
+ ]);
408
+ const AgentSessionConfigSchema = z.object({
409
+ provider: AgentProviderSchema,
410
+ cwd: z.string(),
411
+ modeId: z.string().optional(),
412
+ model: z.string().optional(),
413
+ thinkingOptionId: z.string().optional(),
414
+ featureValues: z.record(z.string(), z.unknown()).optional(),
415
+ title: z.string().trim().min(1).max(MAX_EXPLICIT_AGENT_TITLE_CHARS).optional().nullable(),
416
+ approvalPolicy: z.string().optional(),
417
+ sandboxMode: z.string().optional(),
418
+ networkAccess: z.boolean().optional(),
419
+ webSearch: z.boolean().optional(),
420
+ extra: z
421
+ .object({
422
+ codex: z.record(z.string(), z.unknown()).optional(),
423
+ claude: z.record(z.string(), z.unknown()).optional(),
424
+ })
425
+ .partial()
426
+ .optional(),
427
+ systemPrompt: z.string().optional(),
428
+ mcpServers: z.record(z.string(), McpServerConfigSchema).optional(),
429
+ });
430
+ const AgentPermissionUpdateSchema = z.record(z.string(), z.unknown());
431
+ const AgentPermissionActionSchema = z.object({
432
+ id: z.string(),
433
+ label: z.string(),
434
+ behavior: z.enum(["allow", "deny"]),
435
+ variant: z.enum(["primary", "secondary", "danger"]).optional(),
436
+ intent: z.enum(["implement", "implement_resume", "dismiss"]).optional(),
437
+ });
438
+ export const AgentPermissionResponseSchema = z.discriminatedUnion("behavior", [
439
+ z.object({
440
+ behavior: z.literal("allow"),
441
+ selectedActionId: z.string().optional(),
442
+ updatedInput: z.record(z.string(), z.unknown()).optional(),
443
+ updatedPermissions: z.array(AgentPermissionUpdateSchema).optional(),
444
+ }),
445
+ z.object({
446
+ behavior: z.literal("deny"),
447
+ selectedActionId: z.string().optional(),
448
+ message: z.string().optional(),
449
+ interrupt: z.boolean().optional(),
450
+ }),
451
+ ]);
452
+ export const AgentPermissionRequestPayloadSchema = z.object({
453
+ id: z.string(),
454
+ provider: AgentProviderSchema,
455
+ name: z.string(),
456
+ kind: z.enum(["tool", "plan", "question", "mode", "other"]),
457
+ title: z.string().optional(),
458
+ description: z.string().optional(),
459
+ input: z.record(z.string(), z.unknown()).optional(),
460
+ detail: z.lazy(() => ToolCallDetailPayloadSchema).optional(),
461
+ suggestions: z.array(AgentPermissionUpdateSchema).optional(),
462
+ actions: z.array(AgentPermissionActionSchema).optional(),
463
+ metadata: z.record(z.string(), z.unknown()).optional(),
464
+ });
465
+ const UnknownValueSchema = z.union([
466
+ z.null(),
467
+ z.boolean(),
468
+ z.number(),
469
+ z.string(),
470
+ z.array(z.unknown()),
471
+ z.object({}).passthrough(),
472
+ ]);
473
+ const NonNullUnknownSchema = z.union([
474
+ z.boolean(),
475
+ z.number(),
476
+ z.string(),
477
+ z.array(z.unknown()),
478
+ z.object({}).passthrough(),
479
+ ]);
480
+ const WorktreeSetupCommandSnapshotSchema = z.object({
481
+ index: z.number().int().positive(),
482
+ command: z.string(),
483
+ cwd: z.string(),
484
+ log: z.string().optional().default(""),
485
+ status: z.enum(["running", "completed", "failed"]),
486
+ exitCode: z.number().nullable(),
487
+ durationMs: z.number().nonnegative().optional(),
488
+ });
489
+ const WorktreeSetupDetailPayloadSchema = z.object({
490
+ type: z.literal("worktree_setup"),
491
+ worktreePath: z.string(),
492
+ branchName: z.string(),
493
+ log: z.string(),
494
+ commands: z.array(WorktreeSetupCommandSnapshotSchema),
495
+ truncated: z.boolean().optional(),
496
+ });
497
+ const ToolCallDetailPayloadSchema = z.discriminatedUnion("type", [
498
+ WorktreeSetupDetailPayloadSchema,
499
+ z.object({
500
+ type: z.literal("shell"),
501
+ command: z.string(),
502
+ cwd: z.string().optional(),
503
+ output: z.string().optional(),
504
+ exitCode: z.number().nullable().optional(),
505
+ }),
506
+ z.object({
507
+ type: z.literal("read"),
508
+ filePath: z.string(),
509
+ content: z.string().optional(),
510
+ offset: z.number().optional(),
511
+ limit: z.number().optional(),
512
+ }),
513
+ z.object({
514
+ type: z.literal("edit"),
515
+ filePath: z.string(),
516
+ oldString: z.string().optional(),
517
+ newString: z.string().optional(),
518
+ unifiedDiff: z.string().optional(),
519
+ }),
520
+ z.object({
521
+ type: z.literal("write"),
522
+ filePath: z.string(),
523
+ content: z.string().optional(),
524
+ }),
525
+ z.object({
526
+ type: z.literal("search"),
527
+ query: z.string(),
528
+ toolName: z.enum(["search", "grep", "glob", "web_search"]).optional(),
529
+ content: z.string().optional(),
530
+ filePaths: z.array(z.string()).optional(),
531
+ webResults: z
532
+ .array(z.object({
533
+ title: z.string(),
534
+ url: z.string(),
535
+ }))
536
+ .optional(),
537
+ annotations: z.array(z.string()).optional(),
538
+ numFiles: z.number().optional(),
539
+ numMatches: z.number().optional(),
540
+ durationMs: z.number().optional(),
541
+ durationSeconds: z.number().optional(),
542
+ truncated: z.boolean().optional(),
543
+ mode: z.enum(["content", "files_with_matches", "count"]).optional(),
544
+ }),
545
+ z.object({
546
+ type: z.literal("fetch"),
547
+ url: z.string(),
548
+ prompt: z.string().optional(),
549
+ result: z.string().optional(),
550
+ code: z.number().optional(),
551
+ codeText: z.string().optional(),
552
+ bytes: z.number().optional(),
553
+ durationMs: z.number().optional(),
554
+ }),
555
+ z.object({
556
+ type: z.literal("sub_agent"),
557
+ subAgentType: z.string().optional(),
558
+ description: z.string().optional(),
559
+ childSessionId: z.string().optional(),
560
+ log: z.string(),
561
+ // Compat cruft for clients <= 0.1.65-beta.3 that required this field. Producers still
562
+ // emit `[]`; nothing reads it. Drop the field (and the `[]` emissions) once those
563
+ // clients are no longer in the field.
564
+ actions: z
565
+ .array(z.object({
566
+ index: z.number().int().positive(),
567
+ toolName: z.string(),
568
+ summary: z.string().optional(),
569
+ }))
570
+ .optional(),
571
+ }),
572
+ z.object({
573
+ type: z.literal("plain_text"),
574
+ label: z.string().optional(),
575
+ text: z.string().optional(),
576
+ icon: z.enum(TOOL_CALL_ICON_NAMES).optional(),
577
+ }),
578
+ z.object({
579
+ type: z.literal("plan"),
580
+ text: z.string(),
581
+ }),
582
+ z.object({
583
+ type: z.literal("unknown"),
584
+ input: UnknownValueSchema,
585
+ output: UnknownValueSchema,
586
+ }),
587
+ ]);
588
+ const ToolCallBasePayloadSchema = z.object({
589
+ type: z.literal("tool_call"),
590
+ callId: z.string(),
591
+ name: z.string(),
592
+ detail: ToolCallDetailPayloadSchema,
593
+ metadata: z.record(z.string(), z.unknown()).optional(),
594
+ });
595
+ const ToolCallRunningPayloadSchema = ToolCallBasePayloadSchema.extend({
596
+ status: z.literal("running"),
597
+ error: z.null(),
598
+ });
599
+ const ToolCallCompletedPayloadSchema = ToolCallBasePayloadSchema.extend({
600
+ status: z.literal("completed"),
601
+ error: z.null(),
602
+ });
603
+ const ToolCallFailedPayloadSchema = ToolCallBasePayloadSchema.extend({
604
+ status: z.literal("failed"),
605
+ error: NonNullUnknownSchema,
606
+ });
607
+ const ToolCallCanceledPayloadSchema = ToolCallBasePayloadSchema.extend({
608
+ status: z.literal("canceled"),
609
+ error: z.null(),
610
+ });
611
+ const ToolCallTimelineItemPayloadSchema = z.discriminatedUnion("status", [
612
+ ToolCallRunningPayloadSchema,
613
+ ToolCallCompletedPayloadSchema,
614
+ ToolCallFailedPayloadSchema,
615
+ ToolCallCanceledPayloadSchema,
616
+ ]);
617
+ // zod-aot 0.20.4 miscompiles this as a nested discriminated union by omitting
618
+ // the inner tool_call branch from the generated outer dispatch.
619
+ export const AgentTimelineItemPayloadSchema = z.union([
620
+ z.object({
621
+ type: z.literal("user_message"),
622
+ text: z.string(),
623
+ messageId: z.string().optional(),
624
+ }),
625
+ z.object({
626
+ type: z.literal("assistant_message"),
627
+ text: z.string(),
628
+ messageId: z.string().optional(),
629
+ }),
630
+ z.object({
631
+ type: z.literal("reasoning"),
632
+ text: z.string(),
633
+ }),
634
+ ToolCallTimelineItemPayloadSchema,
635
+ z.object({
636
+ type: z.literal("todo"),
637
+ items: z.array(z.object({
638
+ text: z.string(),
639
+ completed: z.boolean(),
640
+ })),
641
+ }),
642
+ z.object({
643
+ type: z.literal("error"),
644
+ message: z.string(),
645
+ }),
646
+ z.object({
647
+ type: z.literal("compaction"),
648
+ status: z.enum(["loading", "completed"]),
649
+ trigger: z.enum(["auto", "manual"]).optional(),
650
+ preTokens: z.number().optional(),
651
+ }),
652
+ ]);
653
+ export const AgentStreamEventPayloadSchema = z.discriminatedUnion("type", [
654
+ z.object({
655
+ type: z.literal("thread_started"),
656
+ sessionId: z.string(),
657
+ provider: AgentProviderSchema,
658
+ }),
659
+ z.object({
660
+ type: z.literal("turn_started"),
661
+ provider: AgentProviderSchema,
662
+ }),
663
+ z.object({
664
+ type: z.literal("turn_completed"),
665
+ provider: AgentProviderSchema,
666
+ usage: AgentUsageSchema.optional(),
667
+ }),
668
+ z.object({
669
+ type: z.literal("turn_failed"),
670
+ provider: AgentProviderSchema,
671
+ error: z.string(),
672
+ code: z.string().optional(),
673
+ diagnostic: z.string().optional(),
674
+ }),
675
+ z.object({
676
+ type: z.literal("turn_canceled"),
677
+ provider: AgentProviderSchema,
678
+ reason: z.string(),
679
+ }),
680
+ z.object({
681
+ type: z.literal("timeline"),
682
+ provider: AgentProviderSchema,
683
+ item: AgentTimelineItemPayloadSchema,
684
+ }),
685
+ z.object({
686
+ type: z.literal("permission_requested"),
687
+ provider: AgentProviderSchema,
688
+ request: AgentPermissionRequestPayloadSchema,
689
+ }),
690
+ z.object({
691
+ type: z.literal("permission_resolved"),
692
+ provider: AgentProviderSchema,
693
+ requestId: z.string(),
694
+ resolution: AgentPermissionResponseSchema,
695
+ }),
696
+ z.object({
697
+ type: z.literal("attention_required"),
698
+ provider: AgentProviderSchema,
699
+ reason: z.enum(["finished", "error", "permission"]),
700
+ timestamp: z.string(),
701
+ shouldNotify: z.boolean(),
702
+ notification: z
703
+ .object({
704
+ title: z.string(),
705
+ body: z.string(),
706
+ data: z.object({
707
+ serverId: z.string(),
708
+ agentId: z.string(),
709
+ reason: z.enum(["finished", "error", "permission"]),
710
+ }),
711
+ })
712
+ .optional(),
713
+ }),
714
+ ]);
715
+ const AgentPersistenceHandleSchema = z
716
+ .object({
717
+ provider: AgentProviderSchema,
718
+ sessionId: z.string(),
719
+ nativeHandle: z.string().optional(),
720
+ metadata: z.record(z.string(), z.unknown()).optional(),
721
+ })
722
+ .nullable();
723
+ const AgentRuntimeInfoSchema = z.object({
724
+ provider: AgentProviderSchema,
725
+ sessionId: z.string().nullable(),
726
+ model: z.string().nullable().optional(),
727
+ thinkingOptionId: z.string().nullable().optional(),
728
+ modeId: z.string().nullable().optional(),
729
+ extra: z.record(z.string(), z.unknown()).optional(),
730
+ });
731
+ export const SessionDigestLinkSchema = z.object({
732
+ kind: z.enum(["pr", "mr", "issue", "deploy", "preview", "doc", "other"]),
733
+ url: z.string(),
734
+ label: z.string().optional(),
735
+ });
736
+ /**
737
+ * Session digest — structured, machine-readable per-session metadata so an
738
+ * orchestrator can triage a fleet without replaying transcripts. See
739
+ * docs/session-digest.md. Three writers (all fields optional, backward-compatible):
740
+ * - self-reported by the agent (set_session_digest): goal, nextStep, blockers, links
741
+ * - daemon projection (deterministic): keyFiles
742
+ * - daemon LLM on running→idle: summary, accomplishments
743
+ */
744
+ export const SessionDigestSchema = z.object({
745
+ goal: z.string().optional(),
746
+ nextStep: z.string().optional(),
747
+ blockers: z.array(z.string()).optional(),
748
+ links: z.array(SessionDigestLinkSchema).optional(),
749
+ keyFiles: z.array(z.string()).optional(),
750
+ summary: z.string().optional(),
751
+ accomplishments: z.array(z.string()).optional(),
752
+ selfReportedAt: z.string().optional(),
753
+ generatedAt: z.string().optional(),
754
+ });
755
+ export const AgentSnapshotPayloadSchema = z.object({
756
+ id: z.string(),
757
+ provider: AgentProviderSchema,
758
+ cwd: z.string(),
759
+ workspaceId: z.string().optional(),
760
+ model: z.string().nullable(),
761
+ features: z.array(AgentFeatureSchema).optional(),
762
+ thinkingOptionId: z.string().nullable().optional(),
763
+ effectiveThinkingOptionId: z.string().nullable().optional(),
764
+ createdAt: z.string(),
765
+ updatedAt: z.string(),
766
+ lastUserMessageAt: z.string().nullable(),
767
+ status: AgentStatusSchema,
768
+ capabilities: AgentCapabilityFlagsSchema,
769
+ currentModeId: z.string().nullable(),
770
+ availableModes: z.array(AgentModeSchema),
771
+ pendingPermissions: z.array(AgentPermissionRequestPayloadSchema),
772
+ persistence: AgentPersistenceHandleSchema.nullable(),
773
+ runtimeInfo: AgentRuntimeInfoSchema.optional(),
774
+ lastUsage: AgentUsageSchema.optional(),
775
+ lastError: z.string().optional(),
776
+ title: z.string().nullable(),
777
+ labels: z.record(z.string(), z.string()).default({}),
778
+ digest: SessionDigestSchema.optional(),
779
+ requiresAttention: z.boolean().optional(),
780
+ attentionReason: z.enum(["finished", "error", "permission"]).nullable().optional(),
781
+ attentionTimestamp: z.string().nullable().optional(),
782
+ archivedAt: z.string().nullable().optional(),
783
+ /**
784
+ * Count of still-alive Claude Code background shells (`Bash` run_in_background)
785
+ * the agent owns. Keeps the agent surfacing as "Running" past the turn until
786
+ * they finish or are dismissed. Omitted/0 for providers that don't model them.
787
+ */
788
+ activeBackgroundTaskCount: z.number().int().nonnegative().optional(),
789
+ providerUnavailable: z.boolean().optional(),
790
+ });
791
+ export const AgentListItemPayloadSchema = z.object({
792
+ id: z.string(),
793
+ shortId: z.string(),
794
+ title: z.string().nullable(),
795
+ provider: AgentProviderSchema,
796
+ model: z.string().nullable(),
797
+ thinkingOptionId: z.string().nullable().optional(),
798
+ effectiveThinkingOptionId: z.string().nullable().optional(),
799
+ status: AgentStatusSchema,
800
+ cwd: z.string(),
801
+ createdAt: z.string(),
802
+ updatedAt: z.string(),
803
+ lastUserMessageAt: z.string().nullable(),
804
+ archivedAt: z.string().nullable().optional(),
805
+ requiresAttention: z.boolean().optional(),
806
+ attentionReason: z.enum(["finished", "error", "permission"]).nullable().optional(),
807
+ attentionTimestamp: z.string().nullable().optional(),
808
+ labels: z.record(z.string(), z.string()).default({}),
809
+ digest: SessionDigestSchema.optional(),
810
+ activeBackgroundTaskCount: z.number().int().nonnegative().optional(),
811
+ providerUnavailable: z.boolean().optional(),
812
+ });
813
+ export const RecentProviderSessionDescriptorPayloadSchema = z.object({
814
+ providerId: z.string(),
815
+ providerLabel: z.string(),
816
+ providerHandleId: z.string(),
817
+ cwd: z.string(),
818
+ title: z.string().nullable(),
819
+ firstPromptPreview: z.string().nullable(),
820
+ lastPromptPreview: z.string().nullable(),
821
+ lastActivityAt: z.string(),
822
+ });
823
+ // ============================================================================
824
+ // Session Inbound Messages (Session receives these)
825
+ // ============================================================================
826
+ export const VoiceAudioChunkMessageSchema = z.object({
827
+ type: z.literal("voice_audio_chunk"),
828
+ audio: z.string(), // base64 encoded
829
+ format: z.string(),
830
+ isLast: z.boolean(),
831
+ });
832
+ export const AbortRequestMessageSchema = z.object({
833
+ type: z.literal("abort_request"),
834
+ });
835
+ export const AudioPlayedMessageSchema = z.object({
836
+ type: z.literal("audio_played"),
837
+ id: z.string(),
838
+ });
839
+ const AgentDirectoryFilterSchema = z.object({
840
+ labels: z.record(z.string(), z.string()).optional(),
841
+ projectKeys: z.array(z.string()).optional(),
842
+ statuses: z.array(AgentStatusSchema).optional(),
843
+ includeArchived: z.boolean().optional(),
844
+ requiresAttention: z.boolean().optional(),
845
+ thinkingOptionId: z.string().nullable().optional(),
846
+ });
847
+ export const DeleteAgentRequestMessageSchema = z.object({
848
+ type: z.literal("delete_agent_request"),
849
+ agentId: z.string(),
850
+ requestId: z.string(),
851
+ });
852
+ export const ArchiveAgentRequestMessageSchema = z.object({
853
+ type: z.literal("archive_agent_request"),
854
+ agentId: z.string(),
855
+ requestId: z.string(),
856
+ });
857
+ /**
858
+ * Dismiss an agent's live background shell(s). For Claude Code agents (PTY)
859
+ * this drives the CLI's own `ctrl+x ctrl+k` kill chord — killing the shell
860
+ * inside Claude Code. `taskId` names the shell the user dismissed (for
861
+ * symmetry/logging); the kill is the CLI's global stop-background-work action.
862
+ */
863
+ export const DismissBackgroundTaskRequestMessageSchema = z.object({
864
+ type: z.literal("dismiss_background_task_request"),
865
+ agentId: z.string(),
866
+ taskId: z.string(),
867
+ requestId: z.string(),
868
+ });
869
+ export const BackgroundTaskDismissedMessageSchema = z.object({
870
+ type: z.literal("background_task_dismissed"),
871
+ payload: z.object({
872
+ agentId: z.string(),
873
+ taskId: z.string(),
874
+ dismissed: z.boolean(),
875
+ requestId: z.string(),
876
+ }),
877
+ });
878
+ export const CloseItemsRequestMessageSchema = z.object({
879
+ type: z.literal("close_items_request"),
880
+ agentIds: z.array(z.string()).default([]),
881
+ terminalIds: z.array(z.string()).default([]),
882
+ requestId: z.string(),
883
+ });
884
+ export const UpdateAgentRequestMessageSchema = z.object({
885
+ type: z.literal("update_agent_request"),
886
+ agentId: z.string(),
887
+ name: z.string().optional(),
888
+ labels: z.record(z.string(), z.string()).optional(),
889
+ requestId: z.string(),
890
+ });
891
+ export const ProjectRenameRequestSchema = z.object({
892
+ type: z.literal("project.rename.request"),
893
+ projectId: z.string(),
894
+ // Null or empty string clears the override and reverts to the derived name.
895
+ customName: z.string().nullable(),
896
+ requestId: z.string(),
897
+ });
898
+ export const ProjectRemoveRequestSchema = z.object({
899
+ type: z.literal("project.remove.request"),
900
+ projectId: z.string(),
901
+ requestId: z.string(),
902
+ });
903
+ export const WorkspaceTitleSetRequestSchema = z.object({
904
+ type: z.literal("workspace.title.set.request"),
905
+ workspaceId: z.string(),
906
+ // Null or empty string clears the title and reverts to the derived name.
907
+ title: z.string().nullable(),
908
+ requestId: z.string(),
909
+ });
910
+ export const SetVoiceModeMessageSchema = z.object({
911
+ type: z.literal("set_voice_mode"),
912
+ enabled: z.boolean(),
913
+ agentId: z.string().optional(),
914
+ requestId: z.string().optional(),
915
+ });
916
+ export const GitHubPrAttachmentSchema = z.object({
917
+ type: z.literal("github_pr"),
918
+ mimeType: z.literal("application/github-pr"),
919
+ number: z.number().int().positive(),
920
+ title: z.string(),
921
+ url: z.string(),
922
+ body: z.string().nullable().optional(),
923
+ baseRefName: z.string().nullable().optional(),
924
+ headRefName: z.string().nullable().optional(),
925
+ });
926
+ export const GitHubIssueAttachmentSchema = z.object({
927
+ type: z.literal("github_issue"),
928
+ mimeType: z.literal("application/github-issue"),
929
+ number: z.number().int().positive(),
930
+ title: z.string(),
931
+ url: z.string(),
932
+ body: z.string().nullable().optional(),
933
+ });
934
+ export const TextAttachmentSchema = z
935
+ .object({
936
+ type: z.literal("text"),
937
+ mimeType: z.literal("text/plain"),
938
+ contextKind: z.string().optional(),
939
+ title: z.string().nullable().optional(),
940
+ text: z.string(),
941
+ })
942
+ .transform(({ contextKind, ...attachment }) => ({
943
+ ...attachment,
944
+ ...(contextKind === "chat_history" ? { contextKind } : {}),
945
+ }));
946
+ export const ReviewAttachmentContextLineSchema = z.object({
947
+ oldLineNumber: z.number().int().positive().nullable(),
948
+ newLineNumber: z.number().int().positive().nullable(),
949
+ type: z.enum(["add", "remove", "context"]),
950
+ content: z.string(),
951
+ });
952
+ export const ReviewAttachmentCommentSchema = z.object({
953
+ filePath: z.string(),
954
+ side: z.enum(["old", "new"]),
955
+ lineNumber: z.number().int().positive(),
956
+ body: z.string(),
957
+ context: z.object({
958
+ hunkHeader: z.string(),
959
+ targetLine: ReviewAttachmentContextLineSchema,
960
+ lines: z.array(ReviewAttachmentContextLineSchema),
961
+ }),
962
+ });
963
+ export const ReviewAttachmentSchema = z.object({
964
+ type: z.literal("review"),
965
+ mimeType: z.literal("application/paseo-review"),
966
+ cwd: z.string(),
967
+ mode: z.enum(["uncommitted", "base"]),
968
+ baseRef: z.string().nullable().optional(),
969
+ comments: z.array(ReviewAttachmentCommentSchema),
970
+ });
971
+ export const FileAttachmentSchema = z.object({
972
+ type: z.literal("file"),
973
+ name: z.string(),
974
+ mimeType: z.string(),
975
+ size: z.number().int().nonnegative(),
976
+ fileId: z.string(),
977
+ path: z.string(),
978
+ });
979
+ export const UploadedFileAttachmentSchema = z.object({
980
+ type: z.literal("uploaded_file"),
981
+ id: z.string(),
982
+ fileName: z.string(),
983
+ mimeType: z.string(),
984
+ size: z.number().int().nonnegative(),
985
+ path: z.string(),
986
+ });
987
+ export const AgentAttachmentSchema = z.discriminatedUnion("type", [
988
+ GitHubPrAttachmentSchema,
989
+ GitHubIssueAttachmentSchema,
990
+ TextAttachmentSchema,
991
+ ReviewAttachmentSchema,
992
+ FileAttachmentSchema,
993
+ UploadedFileAttachmentSchema,
994
+ ]);
995
+ function normalizeAgentAttachments(input) {
996
+ if (!Array.isArray(input)) {
997
+ return [];
998
+ }
999
+ const normalized = [];
1000
+ for (const item of input) {
1001
+ const parsed = AgentAttachmentSchema.safeParse(item);
1002
+ if (parsed.success) {
1003
+ normalized.push(parsed.data);
1004
+ }
1005
+ }
1006
+ return normalized;
1007
+ }
1008
+ const AgentAttachmentsSchema = z.unknown().transform(normalizeAgentAttachments).optional();
1009
+ const ImageAttachmentSchema = z.object({
1010
+ data: z.string(), // base64 encoded image
1011
+ mimeType: z.string(), // e.g., "image/jpeg", "image/png"
1012
+ });
1013
+ export const SendAgentMessageSchema = z.object({
1014
+ type: z.literal("send_agent_message"),
1015
+ agentId: z.string(),
1016
+ text: z.string(),
1017
+ messageId: z.string().optional(), // Client-provided ID for deduplication
1018
+ images: z.array(ImageAttachmentSchema).optional(),
1019
+ attachments: AgentAttachmentsSchema,
1020
+ });
1021
+ // ============================================================================
1022
+ // Agent RPCs (requestId-correlated)
1023
+ // ============================================================================
1024
+ export const FetchAgentsRequestMessageSchema = z.object({
1025
+ type: z.literal("fetch_agents_request"),
1026
+ requestId: z.string(),
1027
+ scope: z.enum(["active"]).optional(),
1028
+ filter: AgentDirectoryFilterSchema.optional(),
1029
+ sort: z
1030
+ .array(z.object({
1031
+ key: z.enum(["status_priority", "created_at", "updated_at", "title"]),
1032
+ direction: z.enum(["asc", "desc"]),
1033
+ }))
1034
+ .optional(),
1035
+ page: z
1036
+ .object({
1037
+ limit: z.number().int().positive().max(200),
1038
+ cursor: z.string().min(1).optional(),
1039
+ })
1040
+ .optional(),
1041
+ subscribe: z
1042
+ .object({
1043
+ subscriptionId: z.string().optional(),
1044
+ })
1045
+ .optional(),
1046
+ });
1047
+ const WorkspaceStateBucketSchema = z.enum([
1048
+ "needs_input",
1049
+ "failed",
1050
+ "running",
1051
+ "attention",
1052
+ "done",
1053
+ ]);
1054
+ export const FetchWorkspacesRequestMessageSchema = z.object({
1055
+ type: z.literal("fetch_workspaces_request"),
1056
+ requestId: z.string(),
1057
+ filter: z
1058
+ .object({
1059
+ query: z.string().optional(),
1060
+ projectId: z.string().optional(),
1061
+ // Unused: accepted so older clients still parse, but the server does not filter on it.
1062
+ idPrefix: z.string().optional(),
1063
+ })
1064
+ .optional(),
1065
+ sort: z
1066
+ .array(z.object({
1067
+ key: z.enum(["status_priority", "activity_at", "name", "project_id"]),
1068
+ direction: z.enum(["asc", "desc"]),
1069
+ }))
1070
+ .optional(),
1071
+ page: z
1072
+ .object({
1073
+ limit: z.number().int().positive().max(200),
1074
+ cursor: z.string().min(1).optional(),
1075
+ })
1076
+ .optional(),
1077
+ subscribe: z
1078
+ .object({
1079
+ subscriptionId: z.string().optional(),
1080
+ })
1081
+ .optional(),
1082
+ });
1083
+ export const FetchAgentHistoryRequestMessageSchema = z.object({
1084
+ type: z.literal("fetch_agent_history_request"),
1085
+ requestId: z.string(),
1086
+ filter: AgentDirectoryFilterSchema.optional(),
1087
+ sort: z
1088
+ .array(z.object({
1089
+ key: z.enum(["status_priority", "created_at", "updated_at", "title"]),
1090
+ direction: z.enum(["asc", "desc"]),
1091
+ }))
1092
+ .optional(),
1093
+ page: z
1094
+ .object({
1095
+ limit: z.number().int().positive().max(200),
1096
+ cursor: z.string().min(1).optional(),
1097
+ })
1098
+ .optional(),
1099
+ });
1100
+ export const FetchRecentProviderSessionsRequestMessageSchema = z.object({
1101
+ type: z.literal("fetch_recent_provider_sessions_request"),
1102
+ requestId: z.string(),
1103
+ cwd: z.string().optional(),
1104
+ providers: z.array(z.string()).optional(),
1105
+ since: z.string().optional(),
1106
+ limit: z.number().int().positive().max(200).optional(),
1107
+ });
1108
+ export const FetchAgentRequestMessageSchema = z.object({
1109
+ type: z.literal("fetch_agent_request"),
1110
+ requestId: z.string(),
1111
+ /** Accepts full ID, unique prefix, or exact full title (server resolves). */
1112
+ agentId: z.string(),
1113
+ });
1114
+ export const SendAgentMessageRequestSchema = z.object({
1115
+ type: z.literal("send_agent_message_request"),
1116
+ requestId: z.string(),
1117
+ /** Accepts full ID, unique prefix, or exact full title (server resolves). */
1118
+ agentId: z.string(),
1119
+ text: z.string(),
1120
+ messageId: z.string().optional(), // Client-provided ID for deduplication
1121
+ images: z.array(ImageAttachmentSchema).optional(),
1122
+ attachments: AgentAttachmentsSchema,
1123
+ });
1124
+ export const WaitForFinishRequestSchema = z.object({
1125
+ type: z.literal("wait_for_finish_request"),
1126
+ requestId: z.string(),
1127
+ /** Accepts full ID, unique prefix, or exact full title (server resolves). */
1128
+ agentId: z.string(),
1129
+ timeoutMs: z.number().int().positive().optional(),
1130
+ });
1131
+ export const DaemonGetStatusRequestSchema = z.object({
1132
+ type: z.literal("daemon.get_status.request"),
1133
+ requestId: z.string(),
1134
+ });
1135
+ export const DaemonGetPairingOfferRequestSchema = z.object({
1136
+ type: z.literal("daemon.get_pairing_offer.request"),
1137
+ requestId: z.string(),
1138
+ });
1139
+ export const DiagnosticsRequestSchema = z.object({
1140
+ type: z.literal("diagnostics.request"),
1141
+ requestId: z.string(),
1142
+ });
1143
+ export const GetDaemonConfigRequestMessageSchema = z.object({
1144
+ type: z.literal("get_daemon_config_request"),
1145
+ requestId: z.string(),
1146
+ });
1147
+ export const SetDaemonConfigRequestMessageSchema = z.object({
1148
+ type: z.literal("set_daemon_config_request"),
1149
+ requestId: z.string(),
1150
+ config: MutableDaemonConfigPatchSchema,
1151
+ });
1152
+ export const ReadProjectConfigRequestMessageSchema = z.object({
1153
+ type: z.literal("read_project_config_request"),
1154
+ requestId: z.string(),
1155
+ repoRoot: z.string(),
1156
+ });
1157
+ export const WriteProjectConfigRequestMessageSchema = z.object({
1158
+ type: z.literal("write_project_config_request"),
1159
+ requestId: z.string(),
1160
+ repoRoot: z.string(),
1161
+ config: PaseoConfigRawSchema,
1162
+ expectedRevision: PaseoConfigRevisionSchema.nullable(),
1163
+ });
1164
+ // ============================================================================
1165
+ // Dictation Streaming (lossless, resumable)
1166
+ // ============================================================================
1167
+ export const DictationStreamStartMessageSchema = z.object({
1168
+ type: z.literal("dictation_stream_start"),
1169
+ dictationId: z.string(),
1170
+ format: z.string(), // e.g. "audio/pcm;rate=16000;bits=16"
1171
+ });
1172
+ export const DictationStreamChunkMessageSchema = z.object({
1173
+ type: z.literal("dictation_stream_chunk"),
1174
+ dictationId: z.string(),
1175
+ seq: z.number().int().nonnegative(),
1176
+ audio: z.string(), // base64 encoded chunk
1177
+ format: z.string(), // e.g. "audio/pcm;rate=16000;bits=16"
1178
+ });
1179
+ export const DictationStreamFinishMessageSchema = z.object({
1180
+ type: z.literal("dictation_stream_finish"),
1181
+ dictationId: z.string(),
1182
+ finalSeq: z.number().int().nonnegative(),
1183
+ });
1184
+ export const DictationStreamCancelMessageSchema = z.object({
1185
+ type: z.literal("dictation_stream_cancel"),
1186
+ dictationId: z.string(),
1187
+ });
1188
+ const GitSetupOptionsSchema = z.object({
1189
+ baseBranch: z.string().optional(),
1190
+ createNewBranch: z.boolean().optional(),
1191
+ newBranchName: z.string().optional(),
1192
+ createWorktree: z.boolean().optional(),
1193
+ worktreeSlug: z.string().optional(),
1194
+ refName: z.string().min(1).optional(),
1195
+ action: z.enum(["branch-off", "checkout"]).optional(),
1196
+ githubPrNumber: z.number().int().positive().optional(),
1197
+ });
1198
+ export const CreateAgentWorktreeTargetSchema = z.discriminatedUnion("mode", [
1199
+ z.object({
1200
+ mode: z.literal("branch-off"),
1201
+ newBranch: z.string().min(1),
1202
+ base: z.string().min(1).optional(),
1203
+ }),
1204
+ z.object({
1205
+ mode: z.literal("checkout-branch"),
1206
+ branch: z.string().min(1),
1207
+ }),
1208
+ z.object({
1209
+ mode: z.literal("checkout-pr"),
1210
+ prNumber: z.number().int().positive(),
1211
+ }),
1212
+ ]);
1213
+ export const CreateAgentRequestMessageSchema = z.object({
1214
+ type: z.literal("create_agent_request"),
1215
+ config: AgentSessionConfigSchema,
1216
+ env: z.record(z.string(), z.string()).optional(),
1217
+ workspaceId: z.string().optional(),
1218
+ worktreeName: z.string().optional(),
1219
+ initialPrompt: z.string().optional(),
1220
+ clientMessageId: z.string().optional(),
1221
+ outputSchema: z.record(z.string(), z.unknown()).optional(),
1222
+ images: z.array(ImageAttachmentSchema).optional(),
1223
+ attachments: AgentAttachmentsSchema,
1224
+ git: GitSetupOptionsSchema.optional(),
1225
+ worktree: CreateAgentWorktreeTargetSchema.optional(),
1226
+ autoArchive: z.boolean().optional(),
1227
+ // upstream zod-v4 2-arg record + our Story 3.6 resumeSessionId (fork-past-session).
1228
+ labels: z.record(z.string(), z.string()).default({}),
1229
+ // Story 3.6 — Fork a past session by seeding the new agent with `--resume <sid>`.
1230
+ // When set, the spawned Claude process reads the source JSONL (read-only) to
1231
+ // hydrate turn history and writes to a NEW JSONL with a new SID. The source
1232
+ // file MUST remain byte-identical (AC4). Backward-compatible: old clients
1233
+ // omit this field; old daemons ignore it.
1234
+ resumeSessionId: z.string().min(1).optional(),
1235
+ requestId: z.string(),
1236
+ });
1237
+ export const ListProviderModelsRequestMessageSchema = z.object({
1238
+ type: z.literal("list_provider_models_request"),
1239
+ provider: AgentProviderSchema,
1240
+ cwd: z.string().optional(),
1241
+ requestId: z.string(),
1242
+ });
1243
+ export const ListProviderModesRequestMessageSchema = z.object({
1244
+ type: z.literal("list_provider_modes_request"),
1245
+ provider: AgentProviderSchema,
1246
+ cwd: z.string().optional(),
1247
+ requestId: z.string(),
1248
+ });
1249
+ export const ListAvailableProvidersRequestMessageSchema = z.object({
1250
+ type: z.literal("list_available_providers_request"),
1251
+ requestId: z.string(),
1252
+ });
1253
+ export const GetProvidersSnapshotRequestMessageSchema = z.object({
1254
+ type: z.literal("get_providers_snapshot_request"),
1255
+ cwd: z.string().optional(),
1256
+ requestId: z.string(),
1257
+ });
1258
+ export const RefreshProvidersSnapshotRequestMessageSchema = z.object({
1259
+ type: z.literal("refresh_providers_snapshot_request"),
1260
+ cwd: z.string().optional(),
1261
+ providers: z.array(AgentProviderSchema).optional(),
1262
+ requestId: z.string(),
1263
+ });
1264
+ export const ProviderDiagnosticRequestMessageSchema = z.object({
1265
+ type: z.literal("provider_diagnostic_request"),
1266
+ provider: AgentProviderSchema,
1267
+ requestId: z.string(),
1268
+ });
1269
+ export const ProviderUsageListRequestMessageSchema = z.object({
1270
+ type: z.literal("provider.usage.list.request"),
1271
+ requestId: z.string(),
1272
+ });
1273
+ export const ResumeAgentRequestMessageSchema = z.object({
1274
+ type: z.literal("resume_agent_request"),
1275
+ handle: AgentPersistenceHandleSchema,
1276
+ overrides: AgentSessionConfigSchema.partial().optional(),
1277
+ requestId: z.string(),
1278
+ });
1279
+ export const ImportAgentRequestMessageSchema = z.object({
1280
+ type: z.literal("import_agent_request"),
1281
+ provider: AgentProviderSchema.optional(),
1282
+ providerId: z.string().optional(),
1283
+ sessionId: z.string().optional(),
1284
+ providerHandleId: z.string().optional(),
1285
+ cwd: z.string().optional(),
1286
+ labels: z.record(z.string(), z.string()).optional(),
1287
+ requestId: z.string(),
1288
+ });
1289
+ export const RefreshAgentRequestMessageSchema = z.object({
1290
+ type: z.literal("refresh_agent_request"),
1291
+ agentId: z.string(),
1292
+ requestId: z.string(),
1293
+ });
1294
+ export const CancelAgentRequestMessageSchema = z.object({
1295
+ type: z.literal("cancel_agent_request"),
1296
+ agentId: z.string(),
1297
+ requestId: z.string().optional(),
1298
+ });
1299
+ export const RestartServerRequestMessageSchema = z.object({
1300
+ type: z.literal("restart_server_request"),
1301
+ reason: z.string().optional(),
1302
+ requestId: z.string(),
1303
+ });
1304
+ export const ShutdownServerRequestMessageSchema = z.object({
1305
+ type: z.literal("shutdown_server_request"),
1306
+ requestId: z.string(),
1307
+ });
1308
+ export const DaemonUpdateRequestMessageSchema = z.object({
1309
+ type: z.literal("daemon.update.request"),
1310
+ requestId: z.string(),
1311
+ });
1312
+ export const AgentTimelineCursorSchema = z.object({
1313
+ epoch: z.string(),
1314
+ seq: z.number().int().nonnegative(),
1315
+ });
1316
+ export const FetchAgentTimelineRequestMessageSchema = z.object({
1317
+ type: z.literal("fetch_agent_timeline_request"),
1318
+ agentId: z.string(),
1319
+ requestId: z.string(),
1320
+ direction: z.enum(["tail", "before", "after"]).optional(),
1321
+ cursor: AgentTimelineCursorSchema.optional(),
1322
+ // 0 means "all matching rows for this query window".
1323
+ limit: z.number().int().nonnegative().optional(),
1324
+ // Default should be projected for app timeline loading.
1325
+ projection: z.enum(["projected", "canonical"]).optional(),
1326
+ });
1327
+ export const AgentForkContextRequestMessageSchema = z.object({
1328
+ type: z.literal("agent.fork_context.request"),
1329
+ agentId: z.string(),
1330
+ boundaryMessageId: z.string().optional(),
1331
+ requestId: z.string(),
1332
+ });
1333
+ export const SetAgentModeRequestMessageSchema = z.object({
1334
+ type: z.literal("set_agent_mode_request"),
1335
+ agentId: z.string(),
1336
+ modeId: z.string(),
1337
+ requestId: z.string(),
1338
+ });
1339
+ const AgentActionResponsePayloadSchema = z.object({
1340
+ requestId: z.string(),
1341
+ agentId: z.string(),
1342
+ accepted: z.boolean(),
1343
+ error: z.string().nullable(),
1344
+ notice: AgentProviderNoticeSchema.nullable().optional(),
1345
+ });
1346
+ export const SetAgentModeResponseMessageSchema = z.object({
1347
+ type: z.literal("set_agent_mode_response"),
1348
+ payload: AgentActionResponsePayloadSchema,
1349
+ });
1350
+ export const SetAgentModelRequestMessageSchema = z.object({
1351
+ type: z.literal("set_agent_model_request"),
1352
+ agentId: z.string(),
1353
+ modelId: z.string().nullable(),
1354
+ requestId: z.string(),
1355
+ });
1356
+ export const SetAgentModelResponseMessageSchema = z.object({
1357
+ type: z.literal("set_agent_model_response"),
1358
+ payload: AgentActionResponsePayloadSchema,
1359
+ });
1360
+ /**
1361
+ * Hot-swap provider request — added 2026-05.
1362
+ *
1363
+ * Backward-compatible: old daemons reject this message type (no parse path)
1364
+ * and the client receives a generic error. Old clients never emit it.
1365
+ * All `overrides` fields are optional.
1366
+ */
1367
+ export const SwapAgentProviderRequestMessageSchema = z.object({
1368
+ type: z.literal("swap_agent_provider_request"),
1369
+ requestId: z.string(),
1370
+ agentId: z.string(),
1371
+ newProviderId: z.string(),
1372
+ overrides: z
1373
+ .object({
1374
+ model: z.string().nullable().optional(),
1375
+ modeId: z.string().optional(),
1376
+ })
1377
+ .optional(),
1378
+ });
1379
+ export const SwapAgentProviderResponseMessageSchema = z.object({
1380
+ type: z.literal("swap_agent_provider_response"),
1381
+ payload: z.object({
1382
+ requestId: z.string(),
1383
+ agentId: z.string(),
1384
+ accepted: z.boolean(),
1385
+ error: z.string().nullable(),
1386
+ /** Optional context for clients on success — what we swapped from/to. */
1387
+ oldProvider: z.string().optional(),
1388
+ newProvider: z.string().optional(),
1389
+ }),
1390
+ });
1391
+ /**
1392
+ * Broadcast event when a swap completes. Sent in addition to the standard
1393
+ * `agent_state` update so clients can drive UI affordances (toast,
1394
+ * transient highlight) without diffing the full agent record.
1395
+ *
1396
+ * Old clients ignore unknown event types — safe to emit unconditionally.
1397
+ */
1398
+ export const AgentProviderSwappedEventSchema = z.object({
1399
+ type: z.literal("agent_provider_swapped"),
1400
+ payload: z.object({
1401
+ agentId: z.string(),
1402
+ oldProvider: z.string(),
1403
+ newProvider: z.string(),
1404
+ timestamp: z.string(),
1405
+ }),
1406
+ });
1407
+ export const SetAgentThinkingRequestMessageSchema = z.object({
1408
+ type: z.literal("set_agent_thinking_request"),
1409
+ agentId: z.string(),
1410
+ thinkingOptionId: z.string().nullable(),
1411
+ requestId: z.string(),
1412
+ });
1413
+ export const SetAgentThinkingResponseMessageSchema = z.object({
1414
+ type: z.literal("set_agent_thinking_response"),
1415
+ payload: AgentActionResponsePayloadSchema,
1416
+ });
1417
+ export const SetAgentFeatureRequestMessageSchema = z.object({
1418
+ type: z.literal("set_agent_feature_request"),
1419
+ agentId: z.string(),
1420
+ featureId: z.string(),
1421
+ value: z.unknown(),
1422
+ requestId: z.string(),
1423
+ });
1424
+ export const SetAgentFeatureResponseMessageSchema = z.object({
1425
+ type: z.literal("set_agent_feature_response"),
1426
+ payload: AgentActionResponsePayloadSchema,
1427
+ });
1428
+ export const AgentDetachRequestMessageSchema = z.object({
1429
+ type: z.literal("agent.detach.request"),
1430
+ agentId: z.string(),
1431
+ requestId: z.string(),
1432
+ });
1433
+ export const AgentDetachResponseMessageSchema = z.object({
1434
+ type: z.literal("agent.detach.response"),
1435
+ payload: AgentActionResponsePayloadSchema,
1436
+ });
1437
+ export const AgentRewindModeSchema = z.enum(["conversation", "files", "both"]);
1438
+ export const AgentRewindRequestMessageSchema = z.object({
1439
+ type: z.literal("agent.rewind.request"),
1440
+ agentId: z.string(),
1441
+ messageId: z.string(),
1442
+ mode: AgentRewindModeSchema,
1443
+ requestId: z.string(),
1444
+ });
1445
+ export const AgentRewindResponseMessageSchema = z.object({
1446
+ type: z.literal("agent.rewind.response"),
1447
+ payload: z.object({
1448
+ requestId: z.string(),
1449
+ agentId: z.string(),
1450
+ ok: z.boolean(),
1451
+ error: z.string().nullable(),
1452
+ }),
1453
+ });
1454
+ export const UpdateAgentResponseMessageSchema = z.object({
1455
+ type: z.literal("update_agent_response"),
1456
+ payload: AgentActionResponsePayloadSchema,
1457
+ });
1458
+ export const ProjectRenameResponsePayloadSchema = z.object({
1459
+ requestId: z.string(),
1460
+ projectId: z.string(),
1461
+ accepted: z.boolean(),
1462
+ customName: z.string().nullable(),
1463
+ error: z.string().nullable(),
1464
+ });
1465
+ export const ProjectRenameResponseSchema = z.object({
1466
+ type: z.literal("project.rename.response"),
1467
+ payload: ProjectRenameResponsePayloadSchema,
1468
+ });
1469
+ export const ProjectRemoveResponsePayloadSchema = z.object({
1470
+ requestId: z.string(),
1471
+ projectId: z.string(),
1472
+ accepted: z.boolean(),
1473
+ removedWorkspaceIds: z.array(z.string()).default([]),
1474
+ error: z.string().nullable(),
1475
+ });
1476
+ export const ProjectRemoveResponseSchema = z.object({
1477
+ type: z.literal("project.remove.response"),
1478
+ payload: ProjectRemoveResponsePayloadSchema,
1479
+ });
1480
+ export const WorkspaceTitleSetResponsePayloadSchema = z.object({
1481
+ requestId: z.string(),
1482
+ workspaceId: z.string(),
1483
+ accepted: z.boolean(),
1484
+ title: z.string().nullable(),
1485
+ error: z.string().nullable(),
1486
+ });
1487
+ export const WorkspaceTitleSetResponseSchema = z.object({
1488
+ type: z.literal("workspace.title.set.response"),
1489
+ payload: WorkspaceTitleSetResponsePayloadSchema,
1490
+ });
1491
+ export const SetVoiceModeResponseMessageSchema = z.object({
1492
+ type: z.literal("set_voice_mode_response"),
1493
+ payload: z.object({
1494
+ requestId: z.string(),
1495
+ enabled: z.boolean(),
1496
+ agentId: z.string().nullable(),
1497
+ accepted: z.boolean(),
1498
+ error: z.string().nullable(),
1499
+ reasonCode: z.string().optional(),
1500
+ retryable: z.boolean().optional(),
1501
+ missingModelIds: z.array(z.string()).optional(),
1502
+ }),
1503
+ });
1504
+ export const AgentPermissionResponseMessageSchema = z.object({
1505
+ type: z.literal("agent_permission_response"),
1506
+ agentId: z.string(),
1507
+ requestId: z.string(),
1508
+ response: AgentPermissionResponseSchema,
1509
+ });
1510
+ // --- Watch companion: inbound (watch/client -> daemon) ---
1511
+ export const WatchNotificationActionMessageSchema = z.object({
1512
+ type: z.literal("watch.notificationAction"),
1513
+ notificationId: z.string(),
1514
+ action: z.enum(["approve", "deny", "reply"]),
1515
+ replyText: z.string().optional(),
1516
+ });
1517
+ export const WatchSessionSearchMessageSchema = z.object({
1518
+ type: z.literal("watch.sessionSearch"),
1519
+ requestId: z.string(),
1520
+ query: z.string(),
1521
+ });
1522
+ export const WatchComposerDictateMessageSchema = z.object({
1523
+ type: z.literal("watch.composerDictate"),
1524
+ sessionId: z.string(),
1525
+ text: z.string(),
1526
+ });
1527
+ const CheckoutErrorCodeSchema = z.enum([
1528
+ "NOT_GIT_REPO",
1529
+ "NOT_ALLOWED",
1530
+ "MERGE_CONFLICT",
1531
+ "UNKNOWN",
1532
+ ]);
1533
+ const CheckoutErrorSchema = z.object({
1534
+ code: CheckoutErrorCodeSchema,
1535
+ message: z.string(),
1536
+ });
1537
+ const CheckoutDiffCompareSchema = z.object({
1538
+ mode: z.enum(["uncommitted", "base"]),
1539
+ baseRef: z.string().optional(),
1540
+ ignoreWhitespace: z.boolean().optional(),
1541
+ });
1542
+ export const CheckoutStatusRequestSchema = z.object({
1543
+ type: z.literal("checkout_status_request"),
1544
+ cwd: z.string(),
1545
+ requestId: z.string(),
1546
+ });
1547
+ export const SubscribeCheckoutDiffRequestSchema = z.object({
1548
+ type: z.literal("subscribe_checkout_diff_request"),
1549
+ subscriptionId: z.string(),
1550
+ cwd: z.string(),
1551
+ compare: CheckoutDiffCompareSchema,
1552
+ requestId: z.string(),
1553
+ });
1554
+ export const UnsubscribeCheckoutDiffRequestSchema = z.object({
1555
+ type: z.literal("unsubscribe_checkout_diff_request"),
1556
+ subscriptionId: z.string(),
1557
+ });
1558
+ export const CheckoutCommitRequestSchema = z.object({
1559
+ type: z.literal("checkout_commit_request"),
1560
+ cwd: z.string(),
1561
+ message: z.string().optional(),
1562
+ addAll: z.boolean().optional(),
1563
+ requestId: z.string(),
1564
+ });
1565
+ export const CheckoutMergeRequestSchema = z.object({
1566
+ type: z.literal("checkout_merge_request"),
1567
+ cwd: z.string(),
1568
+ baseRef: z.string().optional(),
1569
+ strategy: z.enum(["merge", "squash"]).optional(),
1570
+ requireCleanTarget: z.boolean().optional(),
1571
+ requestId: z.string(),
1572
+ });
1573
+ export const CheckoutMergeFromBaseRequestSchema = z.object({
1574
+ type: z.literal("checkout_merge_from_base_request"),
1575
+ cwd: z.string(),
1576
+ baseRef: z.string().optional(),
1577
+ requireCleanTarget: z.boolean().optional(),
1578
+ requestId: z.string(),
1579
+ });
1580
+ export const CheckoutPullRequestSchema = z.object({
1581
+ type: z.literal("checkout_pull_request"),
1582
+ cwd: z.string(),
1583
+ requestId: z.string(),
1584
+ });
1585
+ export const CheckoutPushRequestSchema = z.object({
1586
+ type: z.literal("checkout_push_request"),
1587
+ cwd: z.string(),
1588
+ requestId: z.string(),
1589
+ });
1590
+ export const CheckoutRefreshRequestSchema = z.object({
1591
+ type: z.literal("checkout.refresh.request"),
1592
+ cwd: z.string(),
1593
+ requestId: z.string(),
1594
+ });
1595
+ export const CheckoutPrCreateRequestSchema = z.object({
1596
+ type: z.literal("checkout_pr_create_request"),
1597
+ cwd: z.string(),
1598
+ title: z.string().optional(),
1599
+ body: z.string().optional(),
1600
+ baseRef: z.string().optional(),
1601
+ requestId: z.string(),
1602
+ });
1603
+ export const CheckoutPrMergeRequestSchema = z.object({
1604
+ type: z.literal("checkout_pr_merge_request"),
1605
+ cwd: z.string(),
1606
+ mergeMethod: z.enum(["merge", "squash", "rebase"]),
1607
+ requestId: z.string(),
1608
+ });
1609
+ export const CheckoutGithubSetAutoMergeRequestSchema = z.object({
1610
+ type: z.literal("checkout.github.set_auto_merge.request"),
1611
+ cwd: z.string(),
1612
+ enabled: z.boolean(),
1613
+ mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(),
1614
+ requestId: z.string(),
1615
+ });
1616
+ // ============================================================================
1617
+ // File upload RPCs (requestId-correlated; the chunk stream is keyed by uploadId)
1618
+ // ============================================================================
1619
+ // Shared upload error codes — reused by begin.response, complete.response, and
1620
+ // the files.upload.error message so the set is defined once, before the RPCs.
1621
+ export const FileUploadErrorCodeSchema = z.enum([
1622
+ "too_large",
1623
+ "checksum_mismatch",
1624
+ "aborted",
1625
+ "write_failed",
1626
+ "unsupported",
1627
+ ]);
1628
+ export const FilesUploadBeginRequestSchema = z.object({
1629
+ type: z.literal("files.upload.begin.request"),
1630
+ requestId: z.string(),
1631
+ agentId: z.string(),
1632
+ uploadId: z.string(),
1633
+ // Reuses FileBeginMetadataSchema (binary-frames/file-transfer.ts) so the
1634
+ // declared size enables early reject downstream — not re-declared inline.
1635
+ // Wrapped in z.lazy because file-transfer.ts -> terminal.ts -> messages.ts is
1636
+ // an import cycle: a direct reference here is undefined at module-eval time
1637
+ // when file-transfer is the cycle entry. z.lazy defers the lookup to parse
1638
+ // time, by which point the live binding is populated.
1639
+ metadata: z.lazy(() => FileBeginMetadataSchema),
1640
+ });
1641
+ export const FilesUploadCompleteRequestSchema = z.object({
1642
+ type: z.literal("files.upload.complete.request"),
1643
+ requestId: z.string(),
1644
+ uploadId: z.string(),
1645
+ totalChunks: z.number().int().nonnegative(),
1646
+ });
1647
+ // Abort/error for an in-flight upload (either side may emit it). Declared here — before
1648
+ // the inbound and outbound unions — because it is registered in BOTH (bidirectional).
1649
+ export const FilesUploadErrorSchema = z.object({
1650
+ type: z.literal("files.upload.error"),
1651
+ payload: z.object({
1652
+ requestId: z.string(),
1653
+ uploadId: z.string(),
1654
+ code: FileUploadErrorCodeSchema,
1655
+ message: z.string().optional(),
1656
+ }),
1657
+ });
1658
+ const GitHubRepoSegmentSchema = z.string().regex(/^[A-Za-z0-9._-]+$/);
1659
+ export const CheckoutGithubGetCheckDetailsRequestSchema = z.object({
1660
+ type: z.literal("checkout.github.get_check_details.request"),
1661
+ cwd: z.string(),
1662
+ repoOwner: GitHubRepoSegmentSchema,
1663
+ repoName: GitHubRepoSegmentSchema,
1664
+ checkRunId: z.number().int().positive(),
1665
+ workflowRunId: z.number().int().positive().optional(),
1666
+ requestId: z.string(),
1667
+ });
1668
+ export const CheckoutPrStatusRequestSchema = z.object({
1669
+ type: z.literal("checkout_pr_status_request"),
1670
+ cwd: z.string(),
1671
+ requestId: z.string(),
1672
+ });
1673
+ export const PullRequestTimelineRequestSchema = z.object({
1674
+ type: z.literal("pull_request_timeline_request"),
1675
+ cwd: z.string(),
1676
+ prNumber: z.number(),
1677
+ repoOwner: z.string(),
1678
+ repoName: z.string(),
1679
+ requestId: z.string(),
1680
+ });
1681
+ export const ValidateBranchRequestSchema = z.object({
1682
+ type: z.literal("validate_branch_request"),
1683
+ cwd: z.string(),
1684
+ branchName: z.string(),
1685
+ requestId: z.string(),
1686
+ });
1687
+ export const CheckoutSwitchBranchRequestSchema = z.object({
1688
+ type: z.literal("checkout_switch_branch_request"),
1689
+ cwd: z.string(),
1690
+ branch: z.string(),
1691
+ requestId: z.string(),
1692
+ });
1693
+ export const CheckoutRenameBranchRequestSchema = z.object({
1694
+ type: z.literal("checkout.rename_branch.request"),
1695
+ cwd: z.string(),
1696
+ branch: z.string(),
1697
+ requestId: z.string(),
1698
+ });
1699
+ export const StashSaveRequestSchema = z.object({
1700
+ type: z.literal("stash_save_request"),
1701
+ cwd: z.string(),
1702
+ /** Branch name to tag the stash with for later identification. */
1703
+ branch: z.string().optional(),
1704
+ requestId: z.string(),
1705
+ });
1706
+ export const StashPopRequestSchema = z.object({
1707
+ type: z.literal("stash_pop_request"),
1708
+ cwd: z.string(),
1709
+ /** Zero-based index from stash_list_response. */
1710
+ stashIndex: z.number().int().min(0),
1711
+ requestId: z.string(),
1712
+ });
1713
+ export const StashListRequestSchema = z.object({
1714
+ type: z.literal("stash_list_request"),
1715
+ cwd: z.string(),
1716
+ /** If true, only return paseo-created stashes. Default true. */
1717
+ paseoOnly: z.boolean().optional(),
1718
+ requestId: z.string(),
1719
+ });
1720
+ export const BranchSuggestionsRequestSchema = z.object({
1721
+ type: z.literal("branch_suggestions_request"),
1722
+ cwd: z.string(),
1723
+ query: z.string().optional(),
1724
+ limit: z.number().int().min(1).max(200).optional(),
1725
+ requestId: z.string(),
1726
+ });
1727
+ export const GitHubSearchItemSchema = z.object({
1728
+ kind: z.enum(["issue", "pr"]),
1729
+ number: z.number(),
1730
+ title: z.string(),
1731
+ url: z.string(),
1732
+ state: z.string(),
1733
+ body: z.string().nullable(),
1734
+ labels: z.array(z.string()),
1735
+ baseRefName: z.string().nullable().optional(),
1736
+ headRefName: z.string().nullable().optional(),
1737
+ updatedAt: z.string().optional(),
1738
+ });
1739
+ export const GitHubSearchKindSchema = z.enum(["github-issue", "github-pr"]);
1740
+ export const GitHubSearchRequestSchema = z.object({
1741
+ type: z.literal("github_search_request"),
1742
+ cwd: z.string(),
1743
+ query: z.string(),
1744
+ limit: z.number().int().min(1).max(50).optional(),
1745
+ kinds: z.array(GitHubSearchKindSchema).optional(),
1746
+ requestId: z.string(),
1747
+ });
1748
+ export const DirectorySuggestionsRequestSchema = z.object({
1749
+ type: z.literal("directory_suggestions_request"),
1750
+ query: z.string(),
1751
+ cwd: z.string().optional(),
1752
+ includeFiles: z.boolean().optional(),
1753
+ includeDirectories: z.boolean().optional(),
1754
+ matchMode: z.enum(["fuzzy", "suffix"]).optional(),
1755
+ limit: z.number().int().min(1).max(100).optional(),
1756
+ requestId: z.string(),
1757
+ });
1758
+ export const PaseoWorktreeListRequestSchema = z.object({
1759
+ type: z.literal("paseo_worktree_list_request"),
1760
+ cwd: z.string().optional(),
1761
+ repoRoot: z.string().optional(),
1762
+ requestId: z.string(),
1763
+ });
1764
+ export const PaseoWorktreeArchiveRequestSchema = z.object({
1765
+ type: z.literal("paseo_worktree_archive_request"),
1766
+ worktreePath: z.string().optional(),
1767
+ repoRoot: z.string().optional(),
1768
+ branchName: z.string().optional(),
1769
+ // COMPAT(worktreeArchiveWorkspaceId): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
1770
+ // Explicit workspace record to archive. A directory can back multiple workspaces
1771
+ // (Model B), so resolving the target by cwd alone picks the wrong record. When
1772
+ // present the daemon archives this exact workspace; when absent it falls back to
1773
+ // resolving by worktreePath, preferring the worktree-kind record on a cwd tie.
1774
+ workspaceId: z.string().optional(),
1775
+ // COMPAT(worktreeArchiveScope): added in v0.1.97, drop the gate when floor >= v0.1.97.
1776
+ // Scope of the archive operation. "workspace" archives a single workspace record
1777
+ // (today's default UI behavior). "worktree" archives every active workspace whose
1778
+ // cwd resolves to the target directory, then removes the directory if it is
1779
+ // Paseo-owned. Omitted/unknown values default to "workspace" for old-client safety.
1780
+ scope: z.enum(["workspace", "worktree"]).optional().default("workspace"),
1781
+ // COMPAT(worktreeDiskDeletion): added in v0.1.97, ignored as of v0.1.97
1782
+ // (disk removal derived from scope + last-reference + ownership); field
1783
+ // retained for wire parse-compat, drop when floor >= v0.1.97.
1784
+ deleteWorktreeFromDisk: z.boolean().optional().default(false),
1785
+ requestId: z.string(),
1786
+ });
1787
+ export const FirstAgentContextSchema = z.object({
1788
+ prompt: z.string().optional(),
1789
+ attachments: AgentAttachmentsSchema,
1790
+ });
1791
+ export const CreatePaseoWorktreeRequestSchema = z.object({
1792
+ type: z.literal("create_paseo_worktree_request"),
1793
+ cwd: z.string(),
1794
+ projectId: z.string().optional(),
1795
+ worktreeSlug: z.string().optional(),
1796
+ nameContext: z.string().optional(),
1797
+ attachments: AgentAttachmentsSchema.optional(),
1798
+ firstAgentContext: FirstAgentContextSchema.optional(),
1799
+ refName: z.string().min(1).optional(),
1800
+ action: z.enum(["branch-off", "checkout"]).optional(),
1801
+ githubPrNumber: z.number().int().positive().optional(),
1802
+ requestId: z.string(),
1803
+ });
1804
+ export const WorkspaceSetupStatusRequestSchema = z.object({
1805
+ type: z.literal("workspace_setup_status_request"),
1806
+ workspaceId: z.string(),
1807
+ requestId: z.string(),
1808
+ });
1809
+ // Editor target descriptors (Hyperpaseo open-in-editor feature).
1810
+ export const LEGACY_EDITOR_TARGET_IDS = [
1811
+ "cursor",
1812
+ "vscode",
1813
+ "zed",
1814
+ "finder",
1815
+ "explorer",
1816
+ "file-manager",
1817
+ ];
1818
+ export const KNOWN_EDITOR_TARGET_IDS = [...LEGACY_EDITOR_TARGET_IDS, "webstorm"];
1819
+ export const KnownEditorTargetIdSchema = z.enum(KNOWN_EDITOR_TARGET_IDS);
1820
+ export const LegacyEditorTargetIdSchema = z.enum(LEGACY_EDITOR_TARGET_IDS);
1821
+ export const EditorTargetIdSchema = z.string().trim().min(1);
1822
+ const KNOWN_EDITOR_TARGET_ID_SET = new Set(KNOWN_EDITOR_TARGET_IDS);
1823
+ const LEGACY_EDITOR_TARGET_ID_SET = new Set(LEGACY_EDITOR_TARGET_IDS);
1824
+ export function isKnownEditorTargetId(value) {
1825
+ return KNOWN_EDITOR_TARGET_ID_SET.has(value);
1826
+ }
1827
+ export function isLegacyEditorTargetId(value) {
1828
+ return LEGACY_EDITOR_TARGET_ID_SET.has(value);
1829
+ }
1830
+ export const EditorTargetDescriptorPayloadSchema = z.object({
1831
+ id: EditorTargetIdSchema,
1832
+ label: z.string(),
1833
+ });
1834
+ // COMPAT(desktopEditorBridge): added in v0.1.88, remove after 2026-12-03 once old clients no longer call daemon editor RPCs.
1835
+ export const LegacyListAvailableEditorsRequestSchema = z.object({
1836
+ type: z.literal("list_available_editors_request"),
1837
+ requestId: z.string(),
1838
+ });
1839
+ export const LegacyOpenInEditorRequestSchema = z.object({
1840
+ type: z.literal("open_in_editor_request"),
1841
+ path: z.string(),
1842
+ editorId: z.string().trim().min(1),
1843
+ mode: z.enum(["open", "reveal"]).optional(),
1844
+ cwd: z.string().optional(),
1845
+ requestId: z.string(),
1846
+ });
1847
+ export const OpenProjectRequestSchema = z.object({
1848
+ type: z.literal("open_project_request"),
1849
+ // Path used only for workspace lookup/creation. Use the returned workspace.id for all subsequent references.
1850
+ cwd: z.string(),
1851
+ requestId: z.string(),
1852
+ });
1853
+ export const ProjectAddRequestSchema = z.object({
1854
+ type: z.literal("project.add.request"),
1855
+ cwd: z.string(),
1856
+ requestId: z.string(),
1857
+ });
1858
+ export const ArchiveWorkspaceRequestSchema = z.object({
1859
+ type: z.literal("archive_workspace_request"),
1860
+ workspaceId: z.string(),
1861
+ requestId: z.string(),
1862
+ });
1863
+ // Create a new workspace record. Unlike open_project, this never deduplicates by
1864
+ // directory: it always produces a fresh workspace. The source discriminates
1865
+ // between an existing local directory and a newly created paseo worktree.
1866
+ export const WorkspaceCreateRequestSchema = z.object({
1867
+ type: z.literal("workspace.create.request"),
1868
+ requestId: z.string(),
1869
+ // Optional user-set title applied to the created workspace.
1870
+ title: z.string().optional(),
1871
+ // Optional prompt context for workspace-level name/branch generation.
1872
+ firstAgentContext: FirstAgentContextSchema.optional(),
1873
+ source: z.discriminatedUnion("kind", [
1874
+ z.object({
1875
+ kind: z.literal("directory"),
1876
+ // Path of the existing checkout/directory to back the workspace.
1877
+ path: z.string(),
1878
+ projectId: z.string().optional(),
1879
+ }),
1880
+ z.object({
1881
+ kind: z.literal("worktree"),
1882
+ // The project whose repo the worktree is cut from.
1883
+ cwd: z.string().optional(),
1884
+ projectId: z.string().optional(),
1885
+ action: z.enum(["branch-off", "checkout"]).optional(),
1886
+ // Target branch name for checkout, or new branch name for branch-off.
1887
+ refName: z.string().min(1).optional(),
1888
+ baseBranch: z.string().optional(),
1889
+ githubPrNumber: z.number().int().positive().optional(),
1890
+ worktreeSlug: z.string().optional(),
1891
+ }),
1892
+ ]),
1893
+ });
1894
+ export const WorkspaceClearAttentionRequestSchema = z.object({
1895
+ type: z.literal("workspace.clear_attention.request"),
1896
+ workspaceId: z.union([z.string(), z.array(z.string())]),
1897
+ requestId: z.string(),
1898
+ });
1899
+ // Highlighted diff token schema
1900
+ // Note: style can be a compound class name (e.g., "heading meta") from the syntax highlighter
1901
+ const HighlightTokenSchema = z.object({
1902
+ text: z.string(),
1903
+ style: z.string().nullable(),
1904
+ });
1905
+ const DiffLineSchema = z.object({
1906
+ type: z.enum(["add", "remove", "context", "header"]),
1907
+ content: z.string(),
1908
+ tokens: z.array(HighlightTokenSchema).optional(),
1909
+ });
1910
+ const DiffHunkSchema = z.object({
1911
+ oldStart: z.number(),
1912
+ oldCount: z.number(),
1913
+ newStart: z.number(),
1914
+ newCount: z.number(),
1915
+ lines: z.array(DiffLineSchema),
1916
+ });
1917
+ const ParsedDiffFileSchema = z.object({
1918
+ path: z.string(),
1919
+ isNew: z.boolean(),
1920
+ isDeleted: z.boolean(),
1921
+ additions: z.number(),
1922
+ deletions: z.number(),
1923
+ hunks: z.array(DiffHunkSchema),
1924
+ status: z.enum(["ok", "too_large", "binary"]).optional(),
1925
+ });
1926
+ const FileExplorerEntrySchema = z.object({
1927
+ name: z.string(),
1928
+ path: z.string(),
1929
+ kind: z.enum(["file", "directory"]),
1930
+ size: z.number(),
1931
+ modifiedAt: z.string(),
1932
+ });
1933
+ const FileExplorerFileSchema = z.object({
1934
+ path: z.string(),
1935
+ kind: z.enum(["text", "image", "binary"]),
1936
+ encoding: z.enum(["utf-8", "base64", "none"]),
1937
+ content: z.string().optional(),
1938
+ mimeType: z.string().optional(),
1939
+ size: z.number(),
1940
+ modifiedAt: z.string(),
1941
+ });
1942
+ const FileExplorerDirectorySchema = z.object({
1943
+ path: z.string(),
1944
+ entries: z.array(FileExplorerEntrySchema),
1945
+ });
1946
+ export const FileExplorerRequestSchema = z.object({
1947
+ type: z.literal("file_explorer_request"),
1948
+ cwd: z.string(),
1949
+ path: z.string().optional(),
1950
+ mode: z.enum(["list", "file"]),
1951
+ requestId: z.string(),
1952
+ acceptBinary: z.boolean().optional(),
1953
+ });
1954
+ export const ProjectIconRequestSchema = z.object({
1955
+ type: z.literal("project_icon_request"),
1956
+ cwd: z.string(),
1957
+ requestId: z.string(),
1958
+ });
1959
+ export const FileDownloadTokenRequestSchema = z.object({
1960
+ type: z.literal("file_download_token_request"),
1961
+ cwd: z.string(),
1962
+ path: z.string(),
1963
+ requestId: z.string(),
1964
+ });
1965
+ export const FileUploadRequestSchema = z.object({
1966
+ type: z.literal("file.upload.request"),
1967
+ fileName: z.string().min(1),
1968
+ mimeType: z.string().min(1),
1969
+ size: z.number().int().nonnegative(),
1970
+ modifiedAt: z.string(),
1971
+ requestId: z.string(),
1972
+ });
1973
+ export const ClearAgentAttentionMessageSchema = z.object({
1974
+ type: z.literal("clear_agent_attention"),
1975
+ agentId: z.union([z.string(), z.array(z.string())]),
1976
+ requestId: z.string().optional(),
1977
+ });
1978
+ export const ClientHeartbeatMessageSchema = z.object({
1979
+ type: z.literal("client_heartbeat"),
1980
+ deviceType: z.enum(["web", "mobile"]),
1981
+ focusedAgentId: z.string().nullable(),
1982
+ // COMPAT(terminalFocusHeartbeat): added in v0.1.97, remove optional default after 2026-12-13 once old clients no longer send heartbeats without terminal focus.
1983
+ focusedTerminalId: z.string().nullable().optional().default(null),
1984
+ lastActivityAt: z.string(),
1985
+ appVisible: z.boolean(),
1986
+ appVisibilityChangedAt: z.string().optional(),
1987
+ });
1988
+ export const PingMessageSchema = z.object({
1989
+ type: z.literal("ping"),
1990
+ requestId: z.string(),
1991
+ clientSentAt: z.number().int().optional(),
1992
+ });
1993
+ const ListCommandsDraftConfigSchema = z.object({
1994
+ provider: AgentProviderSchema,
1995
+ cwd: z.string(),
1996
+ modeId: z.string().optional(),
1997
+ model: z.string().optional(),
1998
+ thinkingOptionId: z.string().optional(),
1999
+ featureValues: z.record(z.string(), z.unknown()).optional(),
2000
+ });
2001
+ export const ListProviderFeaturesRequestMessageSchema = z.object({
2002
+ type: z.literal("list_provider_features_request"),
2003
+ draftConfig: ListCommandsDraftConfigSchema,
2004
+ requestId: z.string(),
2005
+ });
2006
+ export const ListCommandsRequestSchema = z.object({
2007
+ type: z.literal("list_commands_request"),
2008
+ agentId: z.string(),
2009
+ draftConfig: ListCommandsDraftConfigSchema.optional(),
2010
+ requestId: z.string(),
2011
+ });
2012
+ export const RegisterPushTokenMessageSchema = z.object({
2013
+ type: z.literal("register_push_token"),
2014
+ token: z.string(),
2015
+ });
2016
+ // ============================================================================
2017
+ // Chat-actions WebSocket bridges (relay-safe replacements for HTTP routes)
2018
+ // Added 2026-05. Old clients still use HTTP; new clients use these messages so
2019
+ // relay-connected sessions work. All schemas are additive — old daemons reject
2020
+ // unknown types with a generic rpc_error; old clients never emit them.
2021
+ // ============================================================================
2022
+ export const RewindSessionRequestMessageSchema = z.object({
2023
+ type: z.literal("rewind_session_request"),
2024
+ requestId: z.string(),
2025
+ agentId: z.string(),
2026
+ turnId: z.string(),
2027
+ });
2028
+ export const RewindSessionResponseMessageSchema = z.object({
2029
+ type: z.literal("rewind_session_response"),
2030
+ payload: z.object({
2031
+ requestId: z.string(),
2032
+ agentId: z.string(),
2033
+ newSessionId: z.string().optional(),
2034
+ linesCopied: z.number().int().nonnegative().optional(),
2035
+ error: z.string().optional(),
2036
+ errorCode: z.string().optional(),
2037
+ }),
2038
+ });
2039
+ export const EditMessageRequestMessageSchema = z.object({
2040
+ type: z.literal("edit_message_request"),
2041
+ requestId: z.string(),
2042
+ agentId: z.string(),
2043
+ turnId: z.string(),
2044
+ newContent: z.string().optional(),
2045
+ });
2046
+ export const EditMessageResponseMessageSchema = z.object({
2047
+ type: z.literal("edit_message_response"),
2048
+ payload: z.object({
2049
+ requestId: z.string(),
2050
+ agentId: z.string(),
2051
+ newSessionId: z.string().optional(),
2052
+ linesCopied: z.number().int().nonnegative().optional(),
2053
+ error: z.string().optional(),
2054
+ errorCode: z.string().optional(),
2055
+ }),
2056
+ });
2057
+ export const ListWorkspaceSessionsRequestMessageSchema = z.object({
2058
+ type: z.literal("list_workspace_sessions_request"),
2059
+ requestId: z.string(),
2060
+ workspaceId: z.string(),
2061
+ });
2062
+ const WorkspaceSessionEntrySchema = z.object({
2063
+ sid: z.string(),
2064
+ title: z.string().nullable(),
2065
+ lastActivity: z.string(),
2066
+ cwd: z.string(),
2067
+ agentName: z.string().nullable(),
2068
+ });
2069
+ export const ListWorkspaceSessionsResponseMessageSchema = z.object({
2070
+ type: z.literal("list_workspace_sessions_response"),
2071
+ payload: z.object({
2072
+ requestId: z.string(),
2073
+ workspaceId: z.string(),
2074
+ sessions: z.array(WorkspaceSessionEntrySchema),
2075
+ error: z.string().optional(),
2076
+ errorCode: z.string().optional(),
2077
+ }),
2078
+ });
2079
+ // COMPAT(sessionsListRpc): added in v0.1.X, remove gate after 2026-12-13.
2080
+ // WS transport for the GLOBAL session list + recall FTS so the command
2081
+ // palette works over the relay (HTTP routes throw over relay transport).
2082
+ // Mirrors list_workspace_sessions_request/_response above.
2083
+ export const ListAllSessionsRequestMessageSchema = z.object({
2084
+ type: z.literal("list_all_sessions_request"),
2085
+ requestId: z.string(),
2086
+ });
2087
+ const AllSessionsEntrySchema = z.object({
2088
+ sid: z.string(),
2089
+ title: z.string().nullable(),
2090
+ lastActivity: z.string(),
2091
+ cwd: z.string(),
2092
+ agentName: z.string().nullable(),
2093
+ });
2094
+ export const ListAllSessionsResponseMessageSchema = z.object({
2095
+ type: z.literal("list_all_sessions_response"),
2096
+ payload: z.object({
2097
+ requestId: z.string(),
2098
+ sessions: z.array(AllSessionsEntrySchema),
2099
+ error: z.string().optional(),
2100
+ errorCode: z.string().optional(),
2101
+ }),
2102
+ });
2103
+ // COMPAT(extensions): added in v0.1.X, remove gate after 2026-12-17.
2104
+ // Plugin platform RPCs. The daemon reports installed-extension contributions so
2105
+ // the client populates its sidebar/command registries, and routes plugin
2106
+ // command execution to the extension-host. Dotted RPC names per
2107
+ // docs/rpc-namespacing.md. The contribution shapes mirror @hyperpaseo/extension-sdk
2108
+ // (kept local — protocol must not depend on the SDK).
2109
+ const ExtensionCommandContributionSchema = z.object({
2110
+ id: z.string(),
2111
+ title: z.string(),
2112
+ icon: z.string().optional(),
2113
+ });
2114
+ const ExtensionSidebarItemContributionSchema = z.object({
2115
+ id: z.string(),
2116
+ title: z.string(),
2117
+ icon: z.string().optional(),
2118
+ command: z.string().optional(),
2119
+ view: z.string().optional(),
2120
+ order: z.number().optional(),
2121
+ });
2122
+ const ExtensionViewContributionSchema = z.object({
2123
+ id: z.string(),
2124
+ title: z.string(),
2125
+ location: z.enum(["sidebar", "panel"]).optional(),
2126
+ ui: z.string(),
2127
+ // COMPAT(extensions): absolute file:// URL to the view's HTML, resolved
2128
+ // daemon-side from `ui` + the extension dir. Optional + rides the existing
2129
+ // features.extensions capability — old daemons omit it; only consumed by the
2130
+ // desktop webview view-host (remote/non-desktop clients ignore it).
2131
+ uiUrl: z.string().optional(),
2132
+ });
2133
+ const InstalledExtensionSchema = z.object({
2134
+ id: z.string(),
2135
+ name: z.string(),
2136
+ version: z.string(),
2137
+ commands: z.array(ExtensionCommandContributionSchema),
2138
+ sidebarItems: z.array(ExtensionSidebarItemContributionSchema),
2139
+ views: z.array(ExtensionViewContributionSchema),
2140
+ });
2141
+ export const ListExtensionsRequestMessageSchema = z.object({
2142
+ type: z.literal("extensions.list.request"),
2143
+ requestId: z.string(),
2144
+ });
2145
+ export const ListExtensionsResponseMessageSchema = z.object({
2146
+ type: z.literal("extensions.list.response"),
2147
+ payload: z.object({
2148
+ requestId: z.string(),
2149
+ extensions: z.array(InstalledExtensionSchema),
2150
+ error: z.string().optional(),
2151
+ errorCode: z.string().optional(),
2152
+ }),
2153
+ });
2154
+ export const ExecuteExtensionCommandRequestMessageSchema = z.object({
2155
+ type: z.literal("extensions.command.execute.request"),
2156
+ requestId: z.string(),
2157
+ commandId: z.string(),
2158
+ args: z.array(z.unknown()).optional(),
2159
+ });
2160
+ export const ExecuteExtensionCommandResponseMessageSchema = z.object({
2161
+ type: z.literal("extensions.command.execute.response"),
2162
+ payload: z.object({
2163
+ requestId: z.string(),
2164
+ ok: z.boolean(),
2165
+ result: z.unknown().optional(),
2166
+ error: z.string().optional(),
2167
+ }),
2168
+ });
2169
+ export const SearchSessionsRequestMessageSchema = z.object({
2170
+ type: z.literal("search_sessions_request"),
2171
+ requestId: z.string(),
2172
+ query: z.string(),
2173
+ limit: z.number().int().positive().optional(),
2174
+ sources: z.array(z.string()).optional(),
2175
+ });
2176
+ const SearchSessionEntrySchema = z.object({
2177
+ sid: z.string(),
2178
+ source: z.string(),
2179
+ cwd: z.string(),
2180
+ timestamp: z.string(),
2181
+ snippet: z.string(),
2182
+ resumeCommand: z.string(),
2183
+ });
2184
+ export const SearchSessionsResponseMessageSchema = z.object({
2185
+ type: z.literal("search_sessions_response"),
2186
+ payload: z.object({
2187
+ requestId: z.string(),
2188
+ query: z.string(),
2189
+ results: z.array(SearchSessionEntrySchema),
2190
+ error: z.string().optional(),
2191
+ errorCode: z.string().optional(),
2192
+ }),
2193
+ });
2194
+ const WebPushSubscriptionJSONSchema = z.object({
2195
+ endpoint: z.string(),
2196
+ keys: z.object({
2197
+ p256dh: z.string(),
2198
+ auth: z.string(),
2199
+ }),
2200
+ expirationTime: z.union([z.number(), z.null()]).optional(),
2201
+ });
2202
+ const PushSubscriptionPayloadSchema = z.discriminatedUnion("type", [
2203
+ z.object({
2204
+ type: z.literal("expo"),
2205
+ token: z.string().min(1),
2206
+ }),
2207
+ z.object({
2208
+ type: z.literal("web"),
2209
+ subscription: WebPushSubscriptionJSONSchema,
2210
+ }),
2211
+ ]);
2212
+ export const SubscribePushRequestMessageSchema = z.object({
2213
+ type: z.literal("subscribe_push_request"),
2214
+ requestId: z.string(),
2215
+ subscription: PushSubscriptionPayloadSchema,
2216
+ });
2217
+ export const SubscribePushResponseMessageSchema = z.object({
2218
+ type: z.literal("subscribe_push_response"),
2219
+ payload: z.object({
2220
+ requestId: z.string(),
2221
+ ok: z.boolean(),
2222
+ error: z.string().optional(),
2223
+ errorCode: z.string().optional(),
2224
+ vapidPublicKey: z.string().optional(),
2225
+ }),
2226
+ });
2227
+ export const GetVapidPublicKeyRequestMessageSchema = z.object({
2228
+ type: z.literal("get_vapid_public_key_request"),
2229
+ requestId: z.string(),
2230
+ });
2231
+ export const GetVapidPublicKeyResponseMessageSchema = z.object({
2232
+ type: z.literal("get_vapid_public_key_response"),
2233
+ payload: z.object({
2234
+ requestId: z.string(),
2235
+ publicKey: z.string().nullable(),
2236
+ error: z.string().optional(),
2237
+ }),
2238
+ });
2239
+ // ============================================================================
2240
+ // Terminal Messages
2241
+ // ============================================================================
2242
+ export const ListTerminalsRequestSchema = z.object({
2243
+ type: z.literal("list_terminals_request"),
2244
+ cwd: z.string().optional(),
2245
+ workspaceId: z.string().optional(),
2246
+ requestId: z.string(),
2247
+ });
2248
+ export const SubscribeTerminalsRequestSchema = z.object({
2249
+ type: z.literal("subscribe_terminals_request"),
2250
+ cwd: z.string(),
2251
+ workspaceId: z.string().optional(),
2252
+ });
2253
+ export const UnsubscribeTerminalsRequestSchema = z.object({
2254
+ type: z.literal("unsubscribe_terminals_request"),
2255
+ cwd: z.string(),
2256
+ workspaceId: z.string().optional(),
2257
+ });
2258
+ export const CreateTerminalRequestSchema = z.object({
2259
+ type: z.literal("create_terminal_request"),
2260
+ cwd: z.string(),
2261
+ workspaceId: z.string().optional(),
2262
+ name: z.string().optional(),
2263
+ agentId: z.string().optional(),
2264
+ command: z.string().optional(),
2265
+ args: z.array(z.string()).optional(),
2266
+ requestId: z.string(),
2267
+ });
2268
+ export const RenameTerminalRequestSchema = z.object({
2269
+ type: z.literal("terminal.rename.request"),
2270
+ terminalId: z.string(),
2271
+ title: z.string(),
2272
+ requestId: z.string(),
2273
+ });
2274
+ export const StartWorkspaceScriptRequestSchema = z.object({
2275
+ type: z.literal("start_workspace_script_request"),
2276
+ workspaceId: z.string(),
2277
+ scriptName: z.string(),
2278
+ requestId: z.string(),
2279
+ });
2280
+ export const SubscribeTerminalRequestSchema = z.object({
2281
+ type: z.literal("subscribe_terminal_request"),
2282
+ terminalId: z.string(),
2283
+ requestId: z.string(),
2284
+ restore: z
2285
+ .object({
2286
+ mode: z.enum(["live", "visible-snapshot", "full-snapshot"]),
2287
+ scrollbackLines: z.number().int().nonnegative().optional(),
2288
+ size: z
2289
+ .object({
2290
+ rows: z.number().int().positive(),
2291
+ cols: z.number().int().positive(),
2292
+ })
2293
+ .optional(),
2294
+ })
2295
+ .optional(),
2296
+ });
2297
+ export const UnsubscribeTerminalRequestSchema = z.object({
2298
+ type: z.literal("unsubscribe_terminal_request"),
2299
+ terminalId: z.string(),
2300
+ });
2301
+ const TerminalClientMessageSchema = z.discriminatedUnion("type", [
2302
+ z.object({ type: z.literal("input"), data: z.string() }),
2303
+ z.object({ type: z.literal("resize"), rows: z.number(), cols: z.number() }),
2304
+ z.object({
2305
+ type: z.literal("mouse"),
2306
+ row: z.number(),
2307
+ col: z.number(),
2308
+ button: z.number(),
2309
+ action: z.enum(["down", "up", "move"]),
2310
+ }),
2311
+ ]);
2312
+ export const TerminalInputSchema = z.object({
2313
+ type: z.literal("terminal_input"),
2314
+ terminalId: z.string(),
2315
+ message: TerminalClientMessageSchema,
2316
+ });
2317
+ export const KillTerminalRequestSchema = z.object({
2318
+ type: z.literal("kill_terminal_request"),
2319
+ terminalId: z.string(),
2320
+ requestId: z.string(),
2321
+ });
2322
+ export const CaptureTerminalRequestSchema = z.object({
2323
+ type: z.literal("capture_terminal_request"),
2324
+ terminalId: z.string(),
2325
+ start: z.number().int().optional(),
2326
+ end: z.number().int().optional(),
2327
+ stripAnsi: z.boolean().default(true),
2328
+ requestId: z.string(),
2329
+ });
2330
+ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
2331
+ BrowserAutomationExecuteResponseSchema,
2332
+ VoiceAudioChunkMessageSchema,
2333
+ AbortRequestMessageSchema,
2334
+ AudioPlayedMessageSchema,
2335
+ FetchAgentsRequestMessageSchema,
2336
+ FetchAgentHistoryRequestMessageSchema,
2337
+ FetchRecentProviderSessionsRequestMessageSchema,
2338
+ FetchWorkspacesRequestMessageSchema,
2339
+ FetchAgentRequestMessageSchema,
2340
+ DeleteAgentRequestMessageSchema,
2341
+ ArchiveAgentRequestMessageSchema,
2342
+ DismissBackgroundTaskRequestMessageSchema,
2343
+ CloseItemsRequestMessageSchema,
2344
+ UpdateAgentRequestMessageSchema,
2345
+ ProjectRenameRequestSchema,
2346
+ ProjectRemoveRequestSchema,
2347
+ WorkspaceTitleSetRequestSchema,
2348
+ SetVoiceModeMessageSchema,
2349
+ SendAgentMessageRequestSchema,
2350
+ WaitForFinishRequestSchema,
2351
+ DaemonGetStatusRequestSchema,
2352
+ DaemonGetPairingOfferRequestSchema,
2353
+ WorkflowListRequestSchema,
2354
+ WorkflowGetRequestSchema,
2355
+ WorkflowCancelRequestSchema,
2356
+ WorkflowEventsRequestSchema,
2357
+ // COMPAT(workflows): added in v0.1.X, remove gate after 2026-12-17.
2358
+ WorkflowStartRequestSchema,
2359
+ DiagnosticsRequestSchema,
2360
+ GetDaemonConfigRequestMessageSchema,
2361
+ SetDaemonConfigRequestMessageSchema,
2362
+ ReadProjectConfigRequestMessageSchema,
2363
+ WriteProjectConfigRequestMessageSchema,
2364
+ DictationStreamStartMessageSchema,
2365
+ DictationStreamChunkMessageSchema,
2366
+ DictationStreamFinishMessageSchema,
2367
+ DictationStreamCancelMessageSchema,
2368
+ CreateAgentRequestMessageSchema,
2369
+ ListProviderModelsRequestMessageSchema,
2370
+ ListProviderModesRequestMessageSchema,
2371
+ ListProviderFeaturesRequestMessageSchema,
2372
+ ListAvailableProvidersRequestMessageSchema,
2373
+ GetProvidersSnapshotRequestMessageSchema,
2374
+ RefreshProvidersSnapshotRequestMessageSchema,
2375
+ ProviderDiagnosticRequestMessageSchema,
2376
+ ProviderUsageListRequestMessageSchema,
2377
+ ResumeAgentRequestMessageSchema,
2378
+ ImportAgentRequestMessageSchema,
2379
+ RefreshAgentRequestMessageSchema,
2380
+ CancelAgentRequestMessageSchema,
2381
+ ShutdownServerRequestMessageSchema,
2382
+ RestartServerRequestMessageSchema,
2383
+ DaemonUpdateRequestMessageSchema,
2384
+ FetchAgentTimelineRequestMessageSchema,
2385
+ AgentForkContextRequestMessageSchema,
2386
+ SetAgentModeRequestMessageSchema,
2387
+ SetAgentModelRequestMessageSchema,
2388
+ SwapAgentProviderRequestMessageSchema,
2389
+ SetAgentThinkingRequestMessageSchema,
2390
+ SetAgentFeatureRequestMessageSchema,
2391
+ AgentDetachRequestMessageSchema,
2392
+ AgentRewindRequestMessageSchema,
2393
+ AgentPermissionResponseMessageSchema,
2394
+ CheckoutStatusRequestSchema,
2395
+ SubscribeCheckoutDiffRequestSchema,
2396
+ UnsubscribeCheckoutDiffRequestSchema,
2397
+ CheckoutCommitRequestSchema,
2398
+ CheckoutMergeRequestSchema,
2399
+ CheckoutMergeFromBaseRequestSchema,
2400
+ CheckoutPullRequestSchema,
2401
+ CheckoutPushRequestSchema,
2402
+ CheckoutRefreshRequestSchema,
2403
+ CheckoutPrCreateRequestSchema,
2404
+ CheckoutPrMergeRequestSchema,
2405
+ CheckoutGithubSetAutoMergeRequestSchema,
2406
+ FilesUploadBeginRequestSchema,
2407
+ FilesUploadCompleteRequestSchema,
2408
+ // files.upload.error is bidirectional (either side may abort an in-flight upload).
2409
+ // Story 1.2 registered it outbound-only; story 2.1's inbound abort handler
2410
+ // (Session.handleFileUploadErrorMessage) needs it parseable as an inbound message too.
2411
+ FilesUploadErrorSchema,
2412
+ CheckoutGithubGetCheckDetailsRequestSchema,
2413
+ CheckoutPrStatusRequestSchema,
2414
+ PullRequestTimelineRequestSchema,
2415
+ CheckoutSwitchBranchRequestSchema,
2416
+ CheckoutRenameBranchRequestSchema,
2417
+ StashSaveRequestSchema,
2418
+ StashPopRequestSchema,
2419
+ StashListRequestSchema,
2420
+ ValidateBranchRequestSchema,
2421
+ BranchSuggestionsRequestSchema,
2422
+ GitHubSearchRequestSchema,
2423
+ DirectorySuggestionsRequestSchema,
2424
+ PaseoWorktreeListRequestSchema,
2425
+ PaseoWorktreeArchiveRequestSchema,
2426
+ CreatePaseoWorktreeRequestSchema,
2427
+ WorkspaceSetupStatusRequestSchema,
2428
+ LegacyListAvailableEditorsRequestSchema,
2429
+ LegacyOpenInEditorRequestSchema,
2430
+ OpenProjectRequestSchema,
2431
+ ProjectAddRequestSchema,
2432
+ ArchiveWorkspaceRequestSchema,
2433
+ WorkspaceCreateRequestSchema,
2434
+ WorkspaceClearAttentionRequestSchema,
2435
+ FileExplorerRequestSchema,
2436
+ ProjectIconRequestSchema,
2437
+ FileDownloadTokenRequestSchema,
2438
+ FileUploadRequestSchema,
2439
+ ClearAgentAttentionMessageSchema,
2440
+ ClientHeartbeatMessageSchema,
2441
+ PingMessageSchema,
2442
+ ListCommandsRequestSchema,
2443
+ RegisterPushTokenMessageSchema,
2444
+ RewindSessionRequestMessageSchema,
2445
+ EditMessageRequestMessageSchema,
2446
+ ListWorkspaceSessionsRequestMessageSchema,
2447
+ ListAllSessionsRequestMessageSchema,
2448
+ ListExtensionsRequestMessageSchema,
2449
+ ExecuteExtensionCommandRequestMessageSchema,
2450
+ SearchSessionsRequestMessageSchema,
2451
+ SubscribePushRequestMessageSchema,
2452
+ GetVapidPublicKeyRequestMessageSchema,
2453
+ ListTerminalsRequestSchema,
2454
+ SubscribeTerminalsRequestSchema,
2455
+ UnsubscribeTerminalsRequestSchema,
2456
+ CreateTerminalRequestSchema,
2457
+ RenameTerminalRequestSchema,
2458
+ StartWorkspaceScriptRequestSchema,
2459
+ SubscribeTerminalRequestSchema,
2460
+ UnsubscribeTerminalRequestSchema,
2461
+ TerminalInputSchema,
2462
+ KillTerminalRequestSchema,
2463
+ CaptureTerminalRequestSchema,
2464
+ ChatCreateRequestSchema,
2465
+ ChatListRequestSchema,
2466
+ ChatInspectRequestSchema,
2467
+ ChatDeleteRequestSchema,
2468
+ ChatPostRequestSchema,
2469
+ ChatReadRequestSchema,
2470
+ ChatWaitRequestSchema,
2471
+ ScheduleCreateRequestSchema,
2472
+ ScheduleListRequestSchema,
2473
+ ScheduleInspectRequestSchema,
2474
+ ScheduleLogsRequestSchema,
2475
+ SchedulePauseRequestSchema,
2476
+ ScheduleResumeRequestSchema,
2477
+ ScheduleDeleteRequestSchema,
2478
+ ScheduleRunOnceRequestSchema,
2479
+ ScheduleUpdateRequestSchema,
2480
+ LoopRunRequestSchema,
2481
+ LoopListRequestSchema,
2482
+ LoopInspectRequestSchema,
2483
+ LoopLogsRequestSchema,
2484
+ LoopStopRequestSchema,
2485
+ WatchNotificationActionMessageSchema,
2486
+ WatchSessionSearchMessageSchema,
2487
+ WatchComposerDictateMessageSchema,
2488
+ ]);
2489
+ // ============================================================================
2490
+ // Session Outbound Messages (Session emits these)
2491
+ // ============================================================================
2492
+ export const ActivityLogPayloadSchema = z.object({
2493
+ id: z.string(),
2494
+ timestamp: z.coerce.date(),
2495
+ type: z.enum(["transcript", "assistant", "tool_call", "tool_result", "error", "system"]),
2496
+ content: z.string(),
2497
+ metadata: z.record(z.string(), z.unknown()).optional(),
2498
+ });
2499
+ export const ActivityLogMessageSchema = z.object({
2500
+ type: z.literal("activity_log"),
2501
+ payload: ActivityLogPayloadSchema,
2502
+ });
2503
+ export const AssistantChunkMessageSchema = z.object({
2504
+ type: z.literal("assistant_chunk"),
2505
+ payload: z.object({
2506
+ chunk: z.string(),
2507
+ }),
2508
+ });
2509
+ export const AudioOutputMessageSchema = z.object({
2510
+ type: z.literal("audio_output"),
2511
+ payload: z.object({
2512
+ audio: z.string(), // base64 encoded
2513
+ format: z.string(),
2514
+ id: z.string(),
2515
+ isVoiceMode: z.boolean(), // Mode when audio was generated (for drift protection)
2516
+ groupId: z.string().optional(), // Logical utterance id
2517
+ chunkIndex: z.number().int().nonnegative().optional(),
2518
+ isLastChunk: z.boolean().optional(),
2519
+ }),
2520
+ });
2521
+ export const TranscriptionResultMessageSchema = z.object({
2522
+ type: z.literal("transcription_result"),
2523
+ payload: z.object({
2524
+ text: z.string(),
2525
+ language: z.string().optional(),
2526
+ duration: z.number().optional(),
2527
+ requestId: z.string(), // Echoed back from request for tracking
2528
+ avgLogprob: z.number().optional(),
2529
+ isLowConfidence: z.boolean().optional(),
2530
+ byteLength: z.number().optional(),
2531
+ format: z.string().optional(),
2532
+ debugRecordingPath: z.string().optional(),
2533
+ }),
2534
+ });
2535
+ export const VoiceInputStateMessageSchema = z.object({
2536
+ type: z.literal("voice_input_state"),
2537
+ payload: z.object({
2538
+ isSpeaking: z.boolean(),
2539
+ }),
2540
+ });
2541
+ export const DictationStreamAckMessageSchema = z.object({
2542
+ type: z.literal("dictation_stream_ack"),
2543
+ payload: z.object({
2544
+ dictationId: z.string(),
2545
+ ackSeq: z.number().int(),
2546
+ }),
2547
+ });
2548
+ export const DictationStreamFinishAcceptedMessageSchema = z.object({
2549
+ type: z.literal("dictation_stream_finish_accepted"),
2550
+ payload: z.object({
2551
+ dictationId: z.string(),
2552
+ timeoutMs: z.number().int().positive(),
2553
+ }),
2554
+ });
2555
+ export const DictationStreamPartialMessageSchema = z.object({
2556
+ type: z.literal("dictation_stream_partial"),
2557
+ payload: z.object({
2558
+ dictationId: z.string(),
2559
+ text: z.string(),
2560
+ }),
2561
+ });
2562
+ export const DictationStreamFinalMessageSchema = z.object({
2563
+ type: z.literal("dictation_stream_final"),
2564
+ payload: z.object({
2565
+ dictationId: z.string(),
2566
+ text: z.string(),
2567
+ debugRecordingPath: z.string().optional(),
2568
+ }),
2569
+ });
2570
+ export const DictationStreamErrorMessageSchema = z.object({
2571
+ type: z.literal("dictation_stream_error"),
2572
+ payload: z.object({
2573
+ dictationId: z.string(),
2574
+ error: z.string(),
2575
+ retryable: z.boolean(),
2576
+ reasonCode: z.string().optional(),
2577
+ missingModelIds: z.array(z.string()).optional(),
2578
+ debugRecordingPath: z.string().optional(),
2579
+ }),
2580
+ });
2581
+ export const ServerCapabilityStateSchema = z.object({
2582
+ enabled: z.boolean(),
2583
+ reason: z.string(),
2584
+ });
2585
+ export const ServerVoiceCapabilitiesSchema = z.object({
2586
+ dictation: ServerCapabilityStateSchema,
2587
+ voice: ServerCapabilityStateSchema,
2588
+ });
2589
+ export const ServerCapabilitiesSchema = z
2590
+ .object({
2591
+ voice: ServerVoiceCapabilitiesSchema.optional(),
2592
+ })
2593
+ .passthrough();
2594
+ const ServerInfoHostnameSchema = z.unknown().transform((value) => {
2595
+ if (typeof value !== "string") {
2596
+ return null;
2597
+ }
2598
+ const trimmed = value.trim();
2599
+ return trimmed.length > 0 ? trimmed : null;
2600
+ });
2601
+ const ServerInfoVersionSchema = z.unknown().transform((value) => {
2602
+ if (typeof value !== "string") {
2603
+ return null;
2604
+ }
2605
+ const trimmed = value.trim();
2606
+ return trimmed.length > 0 ? trimmed : null;
2607
+ });
2608
+ const ServerCapabilitiesFromUnknownSchema = z
2609
+ .unknown()
2610
+ .optional()
2611
+ .transform((value) => {
2612
+ if (value === undefined) {
2613
+ return undefined;
2614
+ }
2615
+ const parsed = ServerCapabilitiesSchema.safeParse(value);
2616
+ if (!parsed.success) {
2617
+ return undefined;
2618
+ }
2619
+ return parsed.data;
2620
+ });
2621
+ export const ServerInfoStatusPayloadSchema = z
2622
+ .object({
2623
+ status: z.literal("server_info"),
2624
+ serverId: z.string().trim().min(1),
2625
+ hostname: ServerInfoHostnameSchema.optional(),
2626
+ version: ServerInfoVersionSchema.optional(),
2627
+ capabilities: ServerCapabilitiesFromUnknownSchema.optional(),
2628
+ // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
2629
+ features: z
2630
+ .object({
2631
+ providersSnapshot: z.boolean().optional(),
2632
+ checkoutGithubSetAutoMerge: z.boolean().optional(),
2633
+ // COMPAT(githubCheckDetails): added in v0.1.92, remove gate after 2026-12-08.
2634
+ githubCheckDetails: z.boolean().optional(),
2635
+ // COMPAT(daemonStatusRpc): added in v0.1.76, remove gate after 2026-11-18.
2636
+ daemonStatusRpc: z.boolean().optional(),
2637
+ // COMPAT(terminalRestoreModes): added in v0.1.81, remove gate after 2026-11-23.
2638
+ "terminal-restore-modes": z.boolean().optional(),
2639
+ // COMPAT(rewind): added in v0.1.X, drop the gate when floor >= v0.1.X.
2640
+ rewind: z.boolean().optional(),
2641
+ // COMPAT(checkoutRefresh): added in v0.1.86, remove gate after 2026-11-29.
2642
+ checkoutRefresh: z.boolean().optional(),
2643
+ // COMPAT(recallSearch): added in v0.1.X, remove gate after 2026-12-03.
2644
+ recallSearch: z.boolean().optional(),
2645
+ // COMPAT(fileUploads): added in v0.1.88, drop the gate when floor >= v0.1.88.
2646
+ fileUploads: z.boolean().optional(),
2647
+ // COMPAT(sessionsListRpc): added in v0.1.X, remove gate after 2026-12-13.
2648
+ // Daemon serves the global session list + recall FTS over WS (works
2649
+ // over the relay); client falls back to HTTP when this flag is absent.
2650
+ sessionsListRpc: z.boolean().optional(),
2651
+ // COMPAT(workflows): added in v0.1.X, remove gate after 2026-12-17.
2652
+ // Daemon exposes `workflow.list/get/cancel/events` over WS. Absent on
2653
+ // older daemons; new clients then hide the workflows feature.
2654
+ workflows: z.boolean().optional(),
2655
+ // COMPAT(extensions): added in v0.1.X, remove gate after 2026-12-17.
2656
+ // Plugin platform — daemon serves installed-extension contributions +
2657
+ // routes plugin command execution over WS; client populates the
2658
+ // sidebar/command registries when present (absent = no plugin platform).
2659
+ extensions: z.boolean().optional(),
2660
+ // COMPAT(sessionDigest): added in v0.1.X, remove gate after 2026-12-27.
2661
+ // Daemon writes/serves per-session digests (set/get_session_digest +
2662
+ // list_agents includeDigest) and refreshes summary/keyFiles on idle.
2663
+ // Absent on older daemons → client hides the digest UI.
2664
+ sessionDigest: z.boolean().optional(),
2665
+ // COMPAT(workspaceMultiplicity): added in v0.1.97, drop the gate when floor >= v0.1.97
2666
+ workspaceMultiplicity: z.boolean().optional(),
2667
+ // COMPAT(projectRemove): added in v0.1.97, drop the gate when floor >= v0.1.97.
2668
+ projectRemove: z.boolean().optional(),
2669
+ // COMPAT(projectAdd): added in v0.1.97, drop the gate when floor >= v0.1.97.
2670
+ projectAdd: z.boolean().optional(),
2671
+ // COMPAT(worktreeRestore): added in v0.1.97, drop the gate when floor >= v0.1.97
2672
+ worktreeRestore: z.boolean().optional(),
2673
+ // COMPAT(providerUsageList): added in v0.1.98, drop the gate when daemon floor >= v0.1.98.
2674
+ providerUsageList: z.boolean().optional(),
2675
+ // COMPAT(agentDetach): added in v0.1.98, remove gate after 2026-12-19 once daemon floor >= v0.1.98.
2676
+ agentDetach: z.boolean().optional(),
2677
+ // COMPAT(daemonDiagnostics): added in v0.1.100, remove gate after 2026-12-25 once daemon floor >= v0.1.100.
2678
+ daemonDiagnostics: z.boolean().optional(),
2679
+ // COMPAT(daemonSelfUpdate): added in v0.1.93, remove gate after 2026-12-13.
2680
+ daemonSelfUpdate: z.boolean().optional(),
2681
+ // COMPAT(agentForkContext): added in v0.1.102, remove gate after 2026-12-28.
2682
+ agentForkContext: z.boolean().optional(),
2683
+ // COMPAT(meetingsLiveCapture): added in v0.2.1, drop the gate when floor >= v0.2.1.
2684
+ meetingsLiveCapture: z.boolean().optional(),
2685
+ })
2686
+ .optional(),
2687
+ })
2688
+ .passthrough()
2689
+ .transform((payload) => ({
2690
+ ...payload,
2691
+ hostname: payload.hostname ?? null,
2692
+ version: payload.version ?? null,
2693
+ }));
2694
+ export const StatusMessageSchema = z.object({
2695
+ type: z.literal("status"),
2696
+ payload: z
2697
+ .object({
2698
+ status: z.string(),
2699
+ })
2700
+ .passthrough(), // Allow additional fields
2701
+ });
2702
+ export const PongMessageSchema = z.object({
2703
+ type: z.literal("pong"),
2704
+ payload: z.object({
2705
+ requestId: z.string(),
2706
+ clientSentAt: z.number().int().optional(),
2707
+ serverReceivedAt: z.number().int(),
2708
+ serverSentAt: z.number().int(),
2709
+ }),
2710
+ });
2711
+ export const RpcErrorMessageSchema = z.object({
2712
+ type: z.literal("rpc_error"),
2713
+ payload: z.object({
2714
+ requestId: z.string(),
2715
+ requestType: z.string().optional(),
2716
+ error: z.string(),
2717
+ code: z.string().optional(),
2718
+ }),
2719
+ });
2720
+ const AgentStatusWithRequestSchema = z.object({
2721
+ agentId: z.string(),
2722
+ requestId: z.string(),
2723
+ });
2724
+ const AgentStatusWithTimelineSchema = AgentStatusWithRequestSchema.extend({
2725
+ timelineSize: z.number().optional(),
2726
+ });
2727
+ export const AgentCreatedStatusPayloadSchema = z
2728
+ .object({
2729
+ status: z.literal("agent_created"),
2730
+ agent: AgentSnapshotPayloadSchema,
2731
+ })
2732
+ .extend(AgentStatusWithRequestSchema.shape);
2733
+ export const AgentCreateFailedStatusPayloadSchema = z.object({
2734
+ status: z.literal("agent_create_failed"),
2735
+ requestId: z.string(),
2736
+ error: z.string(),
2737
+ errorCode: z.string().optional(),
2738
+ });
2739
+ export const AgentResumedStatusPayloadSchema = z
2740
+ .object({
2741
+ status: z.literal("agent_resumed"),
2742
+ agent: AgentSnapshotPayloadSchema,
2743
+ })
2744
+ .extend(AgentStatusWithTimelineSchema.shape);
2745
+ export const AgentRefreshedStatusPayloadSchema = z
2746
+ .object({
2747
+ status: z.literal("agent_refreshed"),
2748
+ })
2749
+ .extend(AgentStatusWithTimelineSchema.shape);
2750
+ export const RestartRequestedStatusPayloadSchema = z.object({
2751
+ status: z.literal("restart_requested"),
2752
+ clientId: z.string(),
2753
+ reason: z.string().optional(),
2754
+ requestId: z.string(),
2755
+ });
2756
+ export const ShutdownRequestedStatusPayloadSchema = z.object({
2757
+ status: z.literal("shutdown_requested"),
2758
+ clientId: z.string(),
2759
+ requestId: z.string(),
2760
+ });
2761
+ export const DaemonConfigChangedStatusPayloadSchema = z
2762
+ .object({
2763
+ status: z.literal("daemon_config_changed"),
2764
+ config: MutableDaemonConfigSchema,
2765
+ })
2766
+ .passthrough();
2767
+ export const KnownStatusPayloadSchema = z.discriminatedUnion("status", [
2768
+ AgentCreatedStatusPayloadSchema,
2769
+ AgentCreateFailedStatusPayloadSchema,
2770
+ AgentResumedStatusPayloadSchema,
2771
+ AgentRefreshedStatusPayloadSchema,
2772
+ ShutdownRequestedStatusPayloadSchema,
2773
+ RestartRequestedStatusPayloadSchema,
2774
+ DaemonConfigChangedStatusPayloadSchema,
2775
+ ]);
2776
+ export const ArtifactMessageSchema = z.object({
2777
+ type: z.literal("artifact"),
2778
+ payload: z.object({
2779
+ type: z.enum(["markdown", "diff", "image", "code"]),
2780
+ id: z.string(),
2781
+ title: z.string(),
2782
+ content: z.string(),
2783
+ isBase64: z.boolean(),
2784
+ }),
2785
+ });
2786
+ export const ProjectCheckoutLiteNotGitPayloadSchema = z
2787
+ .object({
2788
+ cwd: z.string(),
2789
+ isGit: z.literal(false),
2790
+ currentBranch: z.null(),
2791
+ remoteUrl: z.null(),
2792
+ worktreeRoot: z.null().optional(),
2793
+ isPaseoOwnedWorktree: z.literal(false),
2794
+ mainRepoRoot: z.null(),
2795
+ })
2796
+ .transform((value) => ({
2797
+ ...value,
2798
+ worktreeRoot: null,
2799
+ }));
2800
+ export const ProjectCheckoutLiteGitNonPaseoPayloadSchema = z
2801
+ .object({
2802
+ cwd: z.string(),
2803
+ isGit: z.literal(true),
2804
+ currentBranch: z.string().nullable(),
2805
+ remoteUrl: z.string().nullable(),
2806
+ worktreeRoot: z.string().optional(),
2807
+ isPaseoOwnedWorktree: z.literal(false),
2808
+ mainRepoRoot: z.string().nullable().optional().default(null),
2809
+ })
2810
+ .transform((value) => ({
2811
+ ...value,
2812
+ worktreeRoot: value.worktreeRoot ?? value.cwd,
2813
+ }));
2814
+ export const ProjectCheckoutLiteGitPaseoPayloadSchema = z
2815
+ .object({
2816
+ cwd: z.string(),
2817
+ isGit: z.literal(true),
2818
+ currentBranch: z.string().nullable(),
2819
+ remoteUrl: z.string().nullable(),
2820
+ worktreeRoot: z.string().optional(),
2821
+ isPaseoOwnedWorktree: z.literal(true),
2822
+ mainRepoRoot: z.string(),
2823
+ })
2824
+ .transform((value) => ({
2825
+ ...value,
2826
+ worktreeRoot: value.worktreeRoot ?? value.cwd,
2827
+ }));
2828
+ export const ProjectCheckoutLitePayloadSchema = z.union([
2829
+ ProjectCheckoutLiteNotGitPayloadSchema,
2830
+ ProjectCheckoutLiteGitNonPaseoPayloadSchema,
2831
+ ProjectCheckoutLiteGitPaseoPayloadSchema,
2832
+ ]);
2833
+ export const ProjectPlacementPayloadSchema = z.object({
2834
+ projectKey: z.string(),
2835
+ projectName: z.string(),
2836
+ workspaceName: z.string().nullable().optional(),
2837
+ checkout: ProjectCheckoutLitePayloadSchema,
2838
+ });
2839
+ export const WorkspaceScriptLifecycleSchema = z.enum(["running", "stopped"]);
2840
+ export const WorkspaceScriptHealthSchema = z.enum(["healthy", "unhealthy"]);
2841
+ export const WorkspaceScriptPayloadSchema = z.object({
2842
+ scriptName: z.string(),
2843
+ type: z.enum(["script", "service"]).optional().default("service"),
2844
+ hostname: z.string(),
2845
+ port: z.number().int().positive().nullable(),
2846
+ localProxyUrl: z.string().nullable().optional(),
2847
+ publicProxyUrl: z.string().nullable().optional(),
2848
+ proxyUrl: z.string().nullable().optional().default(null),
2849
+ lifecycle: WorkspaceScriptLifecycleSchema,
2850
+ health: WorkspaceScriptHealthSchema.nullable(),
2851
+ exitCode: z.number().nullable().optional().default(null),
2852
+ terminalId: z.string().nullable().optional().default(null),
2853
+ });
2854
+ const WorkspaceGitRuntimePayloadSchema = z
2855
+ .object({
2856
+ currentBranch: z.string().nullable().optional(),
2857
+ remoteUrl: z.string().nullable().optional(),
2858
+ isPaseoOwnedWorktree: z.boolean().optional(),
2859
+ isDirty: z.boolean().nullable().optional(),
2860
+ aheadBehind: z
2861
+ .object({
2862
+ ahead: z.number(),
2863
+ behind: z.number(),
2864
+ })
2865
+ .nullable()
2866
+ .optional(),
2867
+ aheadOfOrigin: z.number().nullable().optional(),
2868
+ behindOfOrigin: z.number().nullable().optional(),
2869
+ })
2870
+ .optional()
2871
+ .nullable();
2872
+ const WorkspaceGitHubRuntimePayloadSchema = z
2873
+ .object({
2874
+ featuresEnabled: z.boolean().optional(),
2875
+ pullRequest: z
2876
+ .object({
2877
+ number: z.number().optional(),
2878
+ url: z.string(),
2879
+ title: z.string(),
2880
+ state: z.string(),
2881
+ baseRefName: z.string(),
2882
+ headRefName: z.string(),
2883
+ isMerged: z.boolean(),
2884
+ isDraft: z.boolean().optional(),
2885
+ mergeable: z.enum(["MERGEABLE", "CONFLICTING", "UNKNOWN"]).catch("UNKNOWN").optional(),
2886
+ checks: z
2887
+ .array(z.object({
2888
+ name: z.string(),
2889
+ status: z.enum(["success", "failure", "pending", "skipped", "cancelled"]),
2890
+ url: z.string().nullable(),
2891
+ workflow: z.string().optional(),
2892
+ duration: z.string().optional(),
2893
+ }))
2894
+ .optional(),
2895
+ checksStatus: z.enum(["none", "pending", "success", "failure"]).optional(),
2896
+ reviewDecision: z.enum(["approved", "changes_requested", "pending"]).nullable().optional(),
2897
+ repoOwner: z.string().optional(),
2898
+ repoName: z.string().optional(),
2899
+ github: z.unknown().optional(),
2900
+ })
2901
+ .nullable()
2902
+ .optional(),
2903
+ error: z
2904
+ .object({
2905
+ message: z.string(),
2906
+ })
2907
+ .nullable()
2908
+ .optional(),
2909
+ refreshedAt: z.string().nullable().optional(),
2910
+ })
2911
+ .optional()
2912
+ .nullable();
2913
+ export const WorkspaceDescriptorPayloadSchema = z
2914
+ .object({
2915
+ id: z.string(),
2916
+ projectId: z.string(),
2917
+ projectDisplayName: z.string(),
2918
+ // COMPAT(projectCustomName): added in v0.1.76, drop the optional gate when floor >= v0.1.76.
2919
+ // When the user has renamed a project, projectDisplayName carries the resolved
2920
+ // value (customName) and projectCustomName mirrors the raw override so the
2921
+ // settings UI can prefill its input and offer a "reset" action.
2922
+ projectCustomName: z.string().nullable().optional(),
2923
+ projectRootPath: z.string(),
2924
+ workspaceDirectory: z.string().optional(),
2925
+ projectKind: z.enum(["git", "non_git", "directory"]),
2926
+ // COMPAT(workspaces): keep legacy directory workspace kind parseable.
2927
+ workspaceKind: z.enum(["directory", "local_checkout", "checkout", "worktree"]),
2928
+ name: z.string(),
2929
+ // COMPAT(workspaceTitles): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
2930
+ // When the user has titled a workspace, `name` carries the resolved value
2931
+ // (title) and `title` mirrors the raw override so the rename UI can prefill
2932
+ // its input and offer a "reset to branch name" action. Null means the name
2933
+ // is derived from the branch/directory.
2934
+ title: z.string().nullable().optional(),
2935
+ archivingAt: z.string().nullable().optional().default(null),
2936
+ status: WorkspaceStateBucketSchema,
2937
+ // Best-effort workspace status entry timestamp. Old daemons omit the
2938
+ // field; old clients treat missing and null equivalently. The transform
2939
+ // coerces a missing field to `null` so downstream code never has to
2940
+ // handle `undefined`.
2941
+ statusEnteredAt: z
2942
+ .string()
2943
+ .nullish()
2944
+ .transform((value) => value ?? null),
2945
+ activityAt: z.string().nullable(),
2946
+ diffStat: z
2947
+ .object({
2948
+ additions: z.number(),
2949
+ deletions: z.number(),
2950
+ })
2951
+ .nullable()
2952
+ .optional(),
2953
+ scripts: z.array(WorkspaceScriptPayloadSchema).default([]),
2954
+ gitRuntime: WorkspaceGitRuntimePayloadSchema,
2955
+ githubRuntime: WorkspaceGitHubRuntimePayloadSchema,
2956
+ project: ProjectPlacementPayloadSchema.optional(),
2957
+ })
2958
+ .transform((workspace) => ({
2959
+ ...workspace,
2960
+ workspaceDirectory: workspace.workspaceDirectory ?? workspace.projectRootPath,
2961
+ }));
2962
+ export const AgentUpdateMessageSchema = z.object({
2963
+ type: z.literal("agent_update"),
2964
+ payload: z.discriminatedUnion("kind", [
2965
+ z.object({
2966
+ kind: z.literal("upsert"),
2967
+ agent: AgentSnapshotPayloadSchema,
2968
+ project: ProjectPlacementPayloadSchema.nullable().optional(),
2969
+ }),
2970
+ z.object({
2971
+ kind: z.literal("remove"),
2972
+ agentId: z.string(),
2973
+ }),
2974
+ ]),
2975
+ });
2976
+ /**
2977
+ * open_url (API-389): server→client push asking the paired client(s) to open a
2978
+ * URL in the user's OWN browser (phone/desktop/web), not the host machine.
2979
+ * Purely additive: an old client drops the unknown type during union parse
2980
+ * (no crash), and an old daemon simply never sends it. https is enforced
2981
+ * daemon-side and re-checked client-side.
2982
+ */
2983
+ export const OpenUrlMessageSchema = z.object({
2984
+ type: z.literal("open_url"),
2985
+ payload: z.object({
2986
+ url: z.string(),
2987
+ // The requesting agent, when known. Optional/additive for back-compat.
2988
+ agentId: z.string().optional(),
2989
+ }),
2990
+ });
2991
+ export const AgentStreamMessageSchema = z.object({
2992
+ type: z.literal("agent_stream"),
2993
+ payload: z.object({
2994
+ agentId: z.string(),
2995
+ event: AgentStreamEventPayloadSchema,
2996
+ timestamp: z.string(),
2997
+ // Present for timeline events. Maps 1:1 to canonical in-memory timeline rows.
2998
+ seq: z.number().int().nonnegative().optional(),
2999
+ epoch: z.string().optional(),
3000
+ }),
3001
+ });
3002
+ export const AgentStatusMessageSchema = z.object({
3003
+ type: z.literal("agent_status"),
3004
+ payload: z.object({
3005
+ agentId: z.string(),
3006
+ status: z.string(),
3007
+ info: AgentSnapshotPayloadSchema,
3008
+ }),
3009
+ });
3010
+ export const AgentListMessageSchema = z.object({
3011
+ type: z.literal("agent_list"),
3012
+ payload: z.object({
3013
+ agents: z.array(AgentSnapshotPayloadSchema),
3014
+ }),
3015
+ });
3016
+ const AgentDirectoryResponseEntrySchema = z.object({
3017
+ agent: AgentSnapshotPayloadSchema,
3018
+ project: ProjectPlacementPayloadSchema,
3019
+ });
3020
+ const AgentDirectoryPageInfoSchema = z.object({
3021
+ nextCursor: z.string().nullable(),
3022
+ prevCursor: z.string().nullable(),
3023
+ hasMore: z.boolean(),
3024
+ });
3025
+ export const FetchAgentsResponseMessageSchema = z.object({
3026
+ type: z.literal("fetch_agents_response"),
3027
+ payload: z.object({
3028
+ requestId: z.string(),
3029
+ subscriptionId: z.string().nullable().optional(),
3030
+ entries: z.array(AgentDirectoryResponseEntrySchema),
3031
+ pageInfo: AgentDirectoryPageInfoSchema,
3032
+ }),
3033
+ });
3034
+ export const FetchAgentHistoryResponseMessageSchema = z.object({
3035
+ type: z.literal("fetch_agent_history_response"),
3036
+ payload: z.object({
3037
+ requestId: z.string(),
3038
+ entries: z.array(AgentDirectoryResponseEntrySchema),
3039
+ pageInfo: AgentDirectoryPageInfoSchema,
3040
+ }),
3041
+ });
3042
+ export const FetchRecentProviderSessionsResponseMessageSchema = z.object({
3043
+ type: z.literal("fetch_recent_provider_sessions_response"),
3044
+ payload: z.object({
3045
+ requestId: z.string(),
3046
+ entries: z.array(RecentProviderSessionDescriptorPayloadSchema),
3047
+ filteredAlreadyImportedCount: z.number().int().nonnegative().optional(),
3048
+ }),
3049
+ });
3050
+ // COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
3051
+ // A project parent that has zero active workspaces. The sidebar renders the
3052
+ // project row with a new-workspace child so projects persist after their last
3053
+ // workspace is archived.
3054
+ export const WorkspaceProjectDescriptorPayloadSchema = z.object({
3055
+ projectId: z.string(),
3056
+ projectDisplayName: z.string(),
3057
+ projectCustomName: z.string().nullable().optional(),
3058
+ projectRootPath: z.string(),
3059
+ projectKind: z.enum(["git", "non_git", "directory"]),
3060
+ });
3061
+ export const FetchWorkspacesResponseMessageSchema = z.object({
3062
+ type: z.literal("fetch_workspaces_response"),
3063
+ payload: z.object({
3064
+ requestId: z.string(),
3065
+ subscriptionId: z.string().nullable().optional(),
3066
+ entries: z.array(WorkspaceDescriptorPayloadSchema),
3067
+ // COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
3068
+ // Project parents with no active workspaces. Old daemons omit it; old clients
3069
+ // ignore it. Only populated on the first page (no cursor).
3070
+ emptyProjects: z.array(WorkspaceProjectDescriptorPayloadSchema).optional().default([]),
3071
+ pageInfo: z.object({
3072
+ nextCursor: z.string().nullable(),
3073
+ prevCursor: z.string().nullable(),
3074
+ hasMore: z.boolean(),
3075
+ }),
3076
+ }),
3077
+ });
3078
+ export const WorkspaceUpdateMessageSchema = z.object({
3079
+ type: z.literal("workspace_update"),
3080
+ payload: z.discriminatedUnion("kind", [
3081
+ z.object({
3082
+ kind: z.literal("upsert"),
3083
+ workspace: WorkspaceDescriptorPayloadSchema,
3084
+ }),
3085
+ z.object({
3086
+ kind: z.literal("remove"),
3087
+ id: z.string(),
3088
+ // COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
3089
+ // When archiving this workspace leaves its project with no active
3090
+ // workspaces, the daemon includes the project parent so the sidebar keeps
3091
+ // rendering it without waiting for a full re-hydration. Old daemons omit
3092
+ // it; old clients ignore it and surface the project on their next
3093
+ // workspace fetch instead.
3094
+ emptyProject: WorkspaceProjectDescriptorPayloadSchema.optional(),
3095
+ // Project removal is represented on the existing workspace update channel
3096
+ // so old clients can still parse the message and ignore the extra field.
3097
+ removedProjectId: z.string().optional(),
3098
+ }),
3099
+ ]),
3100
+ });
3101
+ export const ScriptStatusUpdateMessageSchema = z.object({
3102
+ type: z.literal("script_status_update"),
3103
+ payload: z.object({
3104
+ workspaceId: z.string(),
3105
+ scripts: z.array(WorkspaceScriptPayloadSchema),
3106
+ }),
3107
+ });
3108
+ export const WorkspaceSetupProgressMessageSchema = z.object({
3109
+ type: z.literal("workspace_setup_progress"),
3110
+ payload: z.object({
3111
+ workspaceId: z.string(),
3112
+ status: z.enum(["running", "completed", "failed"]),
3113
+ detail: WorktreeSetupDetailPayloadSchema,
3114
+ error: z.string().nullable(),
3115
+ }),
3116
+ });
3117
+ export const WorkspaceSetupSnapshotSchema = z.object({
3118
+ status: z.enum(["running", "completed", "failed"]),
3119
+ detail: WorktreeSetupDetailPayloadSchema,
3120
+ error: z.string().nullable(),
3121
+ });
3122
+ export const WorkspaceSetupStatusResponseMessageSchema = z.object({
3123
+ type: z.literal("workspace_setup_status_response"),
3124
+ payload: z.object({
3125
+ requestId: z.string(),
3126
+ workspaceId: z.string(),
3127
+ snapshot: WorkspaceSetupSnapshotSchema.nullable(),
3128
+ }),
3129
+ });
3130
+ export const OpenProjectResponseMessageSchema = z.object({
3131
+ type: z.literal("open_project_response"),
3132
+ payload: z.object({
3133
+ requestId: z.string(),
3134
+ workspace: WorkspaceDescriptorPayloadSchema.nullable(),
3135
+ error: z.string().nullable(),
3136
+ // Unknown codes from newer daemons degrade to null; clients fall back to `error`.
3137
+ errorCode: z.enum(["directory_not_found"]).nullish().catch(null),
3138
+ }),
3139
+ });
3140
+ export const ProjectAddResponseSchema = z.object({
3141
+ type: z.literal("project.add.response"),
3142
+ payload: z.object({
3143
+ requestId: z.string(),
3144
+ project: WorkspaceProjectDescriptorPayloadSchema.nullable(),
3145
+ error: z.string().nullable(),
3146
+ errorCode: z.enum(["directory_not_found"]).nullish().catch(null),
3147
+ }),
3148
+ });
3149
+ export const StartWorkspaceScriptResponseMessageSchema = z.object({
3150
+ type: z.literal("start_workspace_script_response"),
3151
+ payload: z.object({
3152
+ requestId: z.string(),
3153
+ workspaceId: z.string(),
3154
+ scriptName: z.string(),
3155
+ terminalId: z.string().nullable(),
3156
+ error: z.string().nullable(),
3157
+ }),
3158
+ });
3159
+ // COMPAT(desktopEditorBridge): added in v0.1.88, remove after 2026-12-03 once old clients no longer parse daemon editor RPC responses.
3160
+ export const LegacyListAvailableEditorsResponseMessageSchema = z.object({
3161
+ type: z.literal("list_available_editors_response"),
3162
+ payload: z.object({
3163
+ requestId: z.string(),
3164
+ editors: z.array(z.object({
3165
+ id: z.string().trim().min(1),
3166
+ label: z.string(),
3167
+ })),
3168
+ error: z.string().nullable(),
3169
+ }),
3170
+ });
3171
+ export const LegacyOpenInEditorResponseMessageSchema = z.object({
3172
+ type: z.literal("open_in_editor_response"),
3173
+ payload: z.object({
3174
+ requestId: z.string(),
3175
+ error: z.string().nullable(),
3176
+ }),
3177
+ });
3178
+ export const ArchiveWorkspaceResponseMessageSchema = z.object({
3179
+ type: z.literal("archive_workspace_response"),
3180
+ payload: z.object({
3181
+ requestId: z.string(),
3182
+ workspaceId: z.string(),
3183
+ archivedAt: z.string().nullable(),
3184
+ error: z.string().nullable(),
3185
+ }),
3186
+ });
3187
+ export const FetchAgentResponseMessageSchema = z.object({
3188
+ type: z.literal("fetch_agent_response"),
3189
+ payload: z.object({
3190
+ requestId: z.string(),
3191
+ agent: AgentSnapshotPayloadSchema.nullable(),
3192
+ project: ProjectPlacementPayloadSchema.nullable().optional(),
3193
+ error: z.string().nullable(),
3194
+ }),
3195
+ });
3196
+ const AgentTimelineSeqRangeSchema = z.object({
3197
+ startSeq: z.number().int().nonnegative(),
3198
+ endSeq: z.number().int().nonnegative(),
3199
+ });
3200
+ export const AgentTimelineEntryPayloadSchema = z.object({
3201
+ provider: AgentProviderSchema,
3202
+ item: AgentTimelineItemPayloadSchema,
3203
+ timestamp: z.string(),
3204
+ seqStart: z.number().int().nonnegative(),
3205
+ seqEnd: z.number().int().nonnegative(),
3206
+ sourceSeqRanges: z.array(AgentTimelineSeqRangeSchema),
3207
+ collapsed: z.array(z.enum(["assistant_merge", "reasoning_merge", "tool_lifecycle"])),
3208
+ });
3209
+ export const FetchAgentTimelineResponseMessageSchema = z.object({
3210
+ type: z.literal("fetch_agent_timeline_response"),
3211
+ payload: z.object({
3212
+ requestId: z.string(),
3213
+ agentId: z.string(),
3214
+ agent: AgentSnapshotPayloadSchema.nullable(),
3215
+ direction: z.enum(["tail", "before", "after"]),
3216
+ projection: z.enum(["projected", "canonical"]),
3217
+ epoch: z.string(),
3218
+ reset: z.boolean(),
3219
+ staleCursor: z.boolean(),
3220
+ gap: z.boolean(),
3221
+ window: z.object({
3222
+ minSeq: z.number().int().nonnegative(),
3223
+ maxSeq: z.number().int().nonnegative(),
3224
+ nextSeq: z.number().int().nonnegative(),
3225
+ }),
3226
+ startCursor: AgentTimelineCursorSchema.nullable(),
3227
+ endCursor: AgentTimelineCursorSchema.nullable(),
3228
+ hasOlder: z.boolean(),
3229
+ hasNewer: z.boolean(),
3230
+ entries: z.array(AgentTimelineEntryPayloadSchema),
3231
+ error: z.string().nullable(),
3232
+ }),
3233
+ });
3234
+ export const AgentForkContextResponseMessageSchema = z.object({
3235
+ type: z.literal("agent.fork_context.response"),
3236
+ payload: z.object({
3237
+ requestId: z.string(),
3238
+ agentId: z.string(),
3239
+ attachment: TextAttachmentSchema.nullable(),
3240
+ itemCount: z.number().int().nonnegative(),
3241
+ boundaryMessageId: z.string().nullable(),
3242
+ error: z.string().nullable(),
3243
+ }),
3244
+ });
3245
+ export const CancelAgentResponseMessageSchema = z.object({
3246
+ type: z.literal("cancel_agent_response"),
3247
+ payload: z.object({
3248
+ requestId: z.string(),
3249
+ agentId: z.string(),
3250
+ agent: AgentSnapshotPayloadSchema.nullable(),
3251
+ }),
3252
+ });
3253
+ export const ClearAgentAttentionResponseMessageSchema = z.object({
3254
+ type: z.literal("clear_agent_attention_response"),
3255
+ payload: z.object({
3256
+ requestId: z.string(),
3257
+ agentId: z.string().or(z.array(z.string())),
3258
+ agents: z.array(AgentSnapshotPayloadSchema),
3259
+ }),
3260
+ });
3261
+ export const WorkspaceCreateResponseSchema = z.object({
3262
+ type: z.literal("workspace.create.response"),
3263
+ payload: z.object({
3264
+ workspace: WorkspaceDescriptorPayloadSchema.nullable(),
3265
+ setupTerminalId: z.string().nullable(),
3266
+ error: z.string().nullable(),
3267
+ errorCode: z.string().optional(),
3268
+ requestId: z.string(),
3269
+ }),
3270
+ });
3271
+ export const WorkspaceClearAttentionResponseSchema = z.object({
3272
+ type: z.literal("workspace.clear_attention.response"),
3273
+ payload: z.object({
3274
+ requestId: z.string(),
3275
+ workspaceId: z.union([z.string(), z.array(z.string())]),
3276
+ clearedAgentIds: z.array(z.string()),
3277
+ results: z.array(z.object({
3278
+ workspaceId: z.string(),
3279
+ clearedAgentIds: z.array(z.string()),
3280
+ success: z.boolean(),
3281
+ error: z.string().nullable(),
3282
+ })),
3283
+ success: z.boolean(),
3284
+ error: z.string().nullable(),
3285
+ }),
3286
+ });
3287
+ export const SendAgentMessageResponseMessageSchema = z.object({
3288
+ type: z.literal("send_agent_message_response"),
3289
+ payload: z.object({
3290
+ requestId: z.string(),
3291
+ agentId: z.string(),
3292
+ accepted: z.boolean(),
3293
+ error: z.string().nullable(),
3294
+ }),
3295
+ });
3296
+ export const WaitForFinishResponseMessageSchema = z.object({
3297
+ type: z.literal("wait_for_finish_response"),
3298
+ payload: z.object({
3299
+ requestId: z.string(),
3300
+ status: z.enum(["idle", "error", "permission", "timeout"]),
3301
+ final: AgentSnapshotPayloadSchema.nullable(),
3302
+ error: z.string().nullable(),
3303
+ lastMessage: z.string().nullable(),
3304
+ }),
3305
+ });
3306
+ export const GetDaemonConfigResponseMessageSchema = z.object({
3307
+ type: z.literal("get_daemon_config_response"),
3308
+ payload: z
3309
+ .object({
3310
+ requestId: z.string(),
3311
+ config: MutableDaemonConfigSchema,
3312
+ })
3313
+ .passthrough(),
3314
+ });
3315
+ export const DaemonGetStatusResponseSchema = z.object({
3316
+ type: z.literal("daemon.get_status.response"),
3317
+ payload: z
3318
+ .object({
3319
+ requestId: z.string(),
3320
+ serverId: z.string(),
3321
+ version: z.string().nullable().optional(),
3322
+ pid: z.number(),
3323
+ nodePath: z.string(),
3324
+ startedAt: z.string().nullable().optional(),
3325
+ listen: z.string().nullable(),
3326
+ relay: z
3327
+ .object({
3328
+ enabled: z.boolean(),
3329
+ endpoint: z.string(),
3330
+ publicEndpoint: z.string(),
3331
+ useTls: z.boolean(),
3332
+ publicUseTls: z.boolean(),
3333
+ })
3334
+ .nullable()
3335
+ .optional(),
3336
+ providers: z.array(z.object({
3337
+ provider: z.string(),
3338
+ available: z.boolean(),
3339
+ error: z.string().nullable().optional(),
3340
+ })),
3341
+ })
3342
+ .passthrough(),
3343
+ });
3344
+ export const DaemonGetPairingOfferResponseSchema = z.object({
3345
+ type: z.literal("daemon.get_pairing_offer.response"),
3346
+ payload: z
3347
+ .object({
3348
+ requestId: z.string(),
3349
+ url: z.string(),
3350
+ qr: z.string().nullable().optional(),
3351
+ relayEnabled: z.boolean(),
3352
+ })
3353
+ .passthrough(),
3354
+ });
3355
+ export const DiagnosticsResponseSchema = z.object({
3356
+ type: z.literal("diagnostics.response"),
3357
+ payload: z
3358
+ .object({
3359
+ requestId: z.string(),
3360
+ diagnostic: z.string(),
3361
+ })
3362
+ .passthrough(),
3363
+ });
3364
+ export const SetDaemonConfigResponseMessageSchema = z.object({
3365
+ type: z.literal("set_daemon_config_response"),
3366
+ payload: z
3367
+ .object({
3368
+ requestId: z.string(),
3369
+ config: MutableDaemonConfigSchema,
3370
+ })
3371
+ .passthrough(),
3372
+ });
3373
+ export const ReadProjectConfigResponseMessageSchema = z.object({
3374
+ type: z.literal("read_project_config_response"),
3375
+ // zod-aot 0.2.0 miscompiles boolean discriminators as string options
3376
+ // (`"true"`/`"false"`), so keep this sequential until upstream fixes it.
3377
+ payload: z.union([
3378
+ z.object({
3379
+ requestId: z.string(),
3380
+ repoRoot: z.string(),
3381
+ ok: z.literal(true),
3382
+ config: PaseoConfigRawSchema.nullable(),
3383
+ revision: PaseoConfigRevisionSchema.nullable(),
3384
+ }),
3385
+ z.object({
3386
+ requestId: z.string(),
3387
+ repoRoot: z.string(),
3388
+ ok: z.literal(false),
3389
+ error: ProjectConfigRpcErrorSchema,
3390
+ }),
3391
+ ]),
3392
+ });
3393
+ export const WriteProjectConfigResponseMessageSchema = z.object({
3394
+ type: z.literal("write_project_config_response"),
3395
+ // zod-aot 0.2.0 miscompiles boolean discriminators as string options
3396
+ // (`"true"`/`"false"`), so keep this sequential until upstream fixes it.
3397
+ payload: z.union([
3398
+ z.object({
3399
+ requestId: z.string(),
3400
+ repoRoot: z.string(),
3401
+ ok: z.literal(true),
3402
+ config: PaseoConfigRawSchema,
3403
+ revision: PaseoConfigRevisionSchema,
3404
+ }),
3405
+ z.object({
3406
+ requestId: z.string(),
3407
+ repoRoot: z.string(),
3408
+ ok: z.literal(false),
3409
+ error: ProjectConfigRpcErrorSchema,
3410
+ }),
3411
+ ]),
3412
+ });
3413
+ export const AgentPermissionRequestMessageSchema = z.object({
3414
+ type: z.literal("agent_permission_request"),
3415
+ payload: z.object({
3416
+ agentId: z.string(),
3417
+ request: AgentPermissionRequestPayloadSchema,
3418
+ }),
3419
+ });
3420
+ // --- Watch companion: outbound (daemon -> watch/client) ---
3421
+ // Simplified action vocabulary the watch card renders; matches the
3422
+ // approve/deny/reply enum the inbound `watch.notificationAction` accepts.
3423
+ export const WatchActionSchema = z.object({
3424
+ id: z.string(),
3425
+ label: z.string(),
3426
+ action: z.enum(["approve", "deny", "reply"]),
3427
+ });
3428
+ export const WatchActionablePushMessageSchema = z.object({
3429
+ type: z.literal("watch.actionablePush"),
3430
+ notificationId: z.string(),
3431
+ sessionId: z.string(),
3432
+ kind: z.string(),
3433
+ summary: z.string(),
3434
+ actions: z.array(WatchActionSchema),
3435
+ });
3436
+ export const WatchActionableResolvedMessageSchema = z.object({
3437
+ type: z.literal("watch.actionableResolved"),
3438
+ notificationId: z.string(),
3439
+ });
3440
+ export const WatchSessionMatchSchema = z.object({
3441
+ id: z.string(),
3442
+ title: z.string(),
3443
+ lastActivity: z.string(),
3444
+ snippet: z.string(),
3445
+ });
3446
+ export const WatchSessionSearchResultMessageSchema = z.object({
3447
+ type: z.literal("watch.sessionSearchResult"),
3448
+ requestId: z.string(),
3449
+ results: z.array(WatchSessionMatchSchema),
3450
+ error: z.string().optional(),
3451
+ });
3452
+ export const AgentPermissionResolvedMessageSchema = z.object({
3453
+ type: z.literal("agent_permission_resolved"),
3454
+ payload: z.object({
3455
+ agentId: z.string(),
3456
+ requestId: z.string(),
3457
+ resolution: AgentPermissionResponseSchema,
3458
+ }),
3459
+ });
3460
+ export const AgentDeletedMessageSchema = z.object({
3461
+ type: z.literal("agent_deleted"),
3462
+ payload: z.object({
3463
+ agentId: z.string(),
3464
+ requestId: z.string(),
3465
+ }),
3466
+ });
3467
+ export const AgentArchivedMessageSchema = z.object({
3468
+ type: z.literal("agent_archived"),
3469
+ payload: z.object({
3470
+ agentId: z.string(),
3471
+ archivedAt: z.string(),
3472
+ requestId: z.string(),
3473
+ }),
3474
+ });
3475
+ const CloseItemsAgentResultSchema = z.object({
3476
+ agentId: z.string(),
3477
+ archivedAt: z.string(),
3478
+ });
3479
+ const CloseItemsTerminalResultSchema = z.object({
3480
+ terminalId: z.string(),
3481
+ success: z.boolean(),
3482
+ });
3483
+ export const CloseItemsResponseSchema = z.object({
3484
+ type: z.literal("close_items_response"),
3485
+ payload: z.object({
3486
+ agents: z.array(CloseItemsAgentResultSchema),
3487
+ terminals: z.array(CloseItemsTerminalResultSchema),
3488
+ requestId: z.string(),
3489
+ }),
3490
+ });
3491
+ const AheadBehindSchema = z.object({
3492
+ ahead: z.number(),
3493
+ behind: z.number(),
3494
+ });
3495
+ const CheckoutStatusCommonSchema = z.object({
3496
+ cwd: z.string(),
3497
+ error: CheckoutErrorSchema.nullable(),
3498
+ requestId: z.string(),
3499
+ });
3500
+ const CheckoutStatusNotGitSchema = CheckoutStatusCommonSchema.extend({
3501
+ isGit: z.literal(false),
3502
+ isPaseoOwnedWorktree: z.literal(false),
3503
+ repoRoot: z.null(),
3504
+ currentBranch: z.null(),
3505
+ isDirty: z.null(),
3506
+ baseRef: z.null(),
3507
+ aheadBehind: z.null(),
3508
+ aheadOfOrigin: z.null(),
3509
+ behindOfOrigin: z.null(),
3510
+ hasRemote: z.boolean(),
3511
+ remoteUrl: z.null(),
3512
+ });
3513
+ const CheckoutStatusGitNonPaseoSchema = CheckoutStatusCommonSchema.extend({
3514
+ isGit: z.literal(true),
3515
+ isPaseoOwnedWorktree: z.literal(false),
3516
+ repoRoot: z.string(),
3517
+ mainRepoRoot: z.string().nullable().optional().default(null),
3518
+ currentBranch: z.string().nullable(),
3519
+ isDirty: z.boolean(),
3520
+ baseRef: z.string().nullable(),
3521
+ aheadBehind: AheadBehindSchema.nullable(),
3522
+ aheadOfOrigin: z.number().nullable(),
3523
+ behindOfOrigin: z.number().nullable(),
3524
+ hasRemote: z.boolean(),
3525
+ remoteUrl: z.string().nullable(),
3526
+ });
3527
+ const CheckoutStatusGitPaseoSchema = CheckoutStatusCommonSchema.extend({
3528
+ isGit: z.literal(true),
3529
+ isPaseoOwnedWorktree: z.literal(true),
3530
+ repoRoot: z.string(),
3531
+ mainRepoRoot: z.string(),
3532
+ currentBranch: z.string().nullable(),
3533
+ isDirty: z.boolean(),
3534
+ baseRef: z.string(),
3535
+ aheadBehind: AheadBehindSchema.nullable(),
3536
+ aheadOfOrigin: z.number().nullable(),
3537
+ behindOfOrigin: z.number().nullable(),
3538
+ hasRemote: z.boolean(),
3539
+ remoteUrl: z.string().nullable(),
3540
+ });
3541
+ export const CheckoutStatusResponseSchema = z.object({
3542
+ type: z.literal("checkout_status_response"),
3543
+ payload: z.union([
3544
+ CheckoutStatusNotGitSchema,
3545
+ CheckoutStatusGitNonPaseoSchema,
3546
+ CheckoutStatusGitPaseoSchema,
3547
+ ]),
3548
+ });
3549
+ const CheckoutPrGithubAutoMergeRequestSchema = z
3550
+ .object({
3551
+ enabledAt: z.string().nullable().optional().default(null),
3552
+ mergeMethod: z.string().nullable().optional().default(null),
3553
+ enabledBy: z.string().nullable().optional().default(null),
3554
+ })
3555
+ .nullable()
3556
+ .optional()
3557
+ .default(null);
3558
+ const CheckoutPrGithubRepositoryPolicySchema = z
3559
+ .object({
3560
+ autoMergeAllowed: z.boolean().optional().default(false),
3561
+ mergeCommitAllowed: z.boolean().optional().default(false),
3562
+ squashMergeAllowed: z.boolean().optional().default(false),
3563
+ rebaseMergeAllowed: z.boolean().optional().default(false),
3564
+ viewerDefaultMergeMethod: z.string().nullable().optional().default(null),
3565
+ })
3566
+ .optional()
3567
+ .default({
3568
+ autoMergeAllowed: false,
3569
+ mergeCommitAllowed: false,
3570
+ squashMergeAllowed: false,
3571
+ rebaseMergeAllowed: false,
3572
+ viewerDefaultMergeMethod: null,
3573
+ });
3574
+ const CheckoutPrGithubStatusSchema = z
3575
+ .object({
3576
+ mergeStateStatus: z.string().nullable().optional().default(null),
3577
+ autoMergeRequest: CheckoutPrGithubAutoMergeRequestSchema,
3578
+ viewerCanEnableAutoMerge: z.boolean().optional().default(false),
3579
+ viewerCanDisableAutoMerge: z.boolean().optional().default(false),
3580
+ viewerCanMergeAsAdmin: z.boolean().optional().default(false),
3581
+ viewerCanUpdateBranch: z.boolean().optional().default(false),
3582
+ repository: CheckoutPrGithubRepositoryPolicySchema,
3583
+ isMergeQueueEnabled: z.boolean().optional().default(false),
3584
+ isInMergeQueue: z.boolean().optional().default(false),
3585
+ })
3586
+ .optional();
3587
+ export const CheckoutPrStatusSchema = z.object({
3588
+ number: z.number().optional(),
3589
+ url: z.string(),
3590
+ title: z.string(),
3591
+ state: z.string(),
3592
+ baseRefName: z.string(),
3593
+ headRefName: z.string(),
3594
+ isMerged: z.boolean(),
3595
+ isDraft: z.boolean().optional().default(false),
3596
+ mergeable: z
3597
+ .enum(["MERGEABLE", "CONFLICTING", "UNKNOWN"])
3598
+ .catch("UNKNOWN")
3599
+ .optional()
3600
+ .default("UNKNOWN"),
3601
+ checks: z
3602
+ .array(z.object({
3603
+ name: z.string(),
3604
+ status: z.string(),
3605
+ url: z.string().nullable(),
3606
+ workflow: z.string().optional(),
3607
+ duration: z.string().optional(),
3608
+ checkRunId: z.number().optional(),
3609
+ workflowRunId: z.number().optional(),
3610
+ }))
3611
+ .optional()
3612
+ .default([]),
3613
+ checksStatus: z.string().optional(),
3614
+ reviewDecision: z.string().nullable().optional(),
3615
+ repoOwner: z.string().optional(),
3616
+ repoName: z.string().optional(),
3617
+ github: CheckoutPrGithubStatusSchema,
3618
+ });
3619
+ const CheckoutPrStatusPayloadSchema = z.object({
3620
+ cwd: z.string(),
3621
+ status: CheckoutPrStatusSchema.nullable(),
3622
+ githubFeaturesEnabled: z.boolean(),
3623
+ error: CheckoutErrorSchema.nullable(),
3624
+ requestId: z.string(),
3625
+ });
3626
+ const CheckoutStatusUpdateMetadataSchema = z.object({
3627
+ prStatus: CheckoutPrStatusPayloadSchema.optional(),
3628
+ });
3629
+ export const CheckoutStatusUpdateSchema = z.object({
3630
+ type: z.literal("checkout_status_update"),
3631
+ payload: z
3632
+ .union([
3633
+ CheckoutStatusNotGitSchema,
3634
+ CheckoutStatusGitNonPaseoSchema,
3635
+ CheckoutStatusGitPaseoSchema,
3636
+ ])
3637
+ .and(CheckoutStatusUpdateMetadataSchema),
3638
+ });
3639
+ const CheckoutDiffSubscriptionPayloadSchema = z.object({
3640
+ subscriptionId: z.string(),
3641
+ cwd: z.string(),
3642
+ files: z.array(ParsedDiffFileSchema),
3643
+ error: CheckoutErrorSchema.nullable(),
3644
+ });
3645
+ export const SubscribeCheckoutDiffResponseSchema = z.object({
3646
+ type: z.literal("subscribe_checkout_diff_response"),
3647
+ payload: CheckoutDiffSubscriptionPayloadSchema.extend({
3648
+ requestId: z.string(),
3649
+ }),
3650
+ });
3651
+ export const CheckoutDiffUpdateSchema = z.object({
3652
+ type: z.literal("checkout_diff_update"),
3653
+ payload: CheckoutDiffSubscriptionPayloadSchema,
3654
+ });
3655
+ export const CheckoutCommitResponseSchema = z.object({
3656
+ type: z.literal("checkout_commit_response"),
3657
+ payload: z.object({
3658
+ cwd: z.string(),
3659
+ success: z.boolean(),
3660
+ error: CheckoutErrorSchema.nullable(),
3661
+ requestId: z.string(),
3662
+ }),
3663
+ });
3664
+ export const CheckoutMergeResponseSchema = z.object({
3665
+ type: z.literal("checkout_merge_response"),
3666
+ payload: z.object({
3667
+ cwd: z.string(),
3668
+ success: z.boolean(),
3669
+ error: CheckoutErrorSchema.nullable(),
3670
+ requestId: z.string(),
3671
+ }),
3672
+ });
3673
+ export const CheckoutMergeFromBaseResponseSchema = z.object({
3674
+ type: z.literal("checkout_merge_from_base_response"),
3675
+ payload: z.object({
3676
+ cwd: z.string(),
3677
+ success: z.boolean(),
3678
+ error: CheckoutErrorSchema.nullable(),
3679
+ requestId: z.string(),
3680
+ }),
3681
+ });
3682
+ export const CheckoutPullResponseSchema = z.object({
3683
+ type: z.literal("checkout_pull_response"),
3684
+ payload: z.object({
3685
+ cwd: z.string(),
3686
+ success: z.boolean(),
3687
+ error: CheckoutErrorSchema.nullable(),
3688
+ requestId: z.string(),
3689
+ }),
3690
+ });
3691
+ export const CheckoutPushResponseSchema = z.object({
3692
+ type: z.literal("checkout_push_response"),
3693
+ payload: z.object({
3694
+ cwd: z.string(),
3695
+ success: z.boolean(),
3696
+ error: CheckoutErrorSchema.nullable(),
3697
+ requestId: z.string(),
3698
+ }),
3699
+ });
3700
+ export const CheckoutRefreshResponseSchema = z.object({
3701
+ type: z.literal("checkout.refresh.response"),
3702
+ payload: z.object({
3703
+ cwd: z.string(),
3704
+ success: z.boolean(),
3705
+ error: CheckoutErrorSchema.nullable(),
3706
+ requestId: z.string(),
3707
+ }),
3708
+ });
3709
+ export const CheckoutPrCreateResponseSchema = z.object({
3710
+ type: z.literal("checkout_pr_create_response"),
3711
+ payload: z.object({
3712
+ cwd: z.string(),
3713
+ url: z.string().nullable(),
3714
+ number: z.number().nullable(),
3715
+ error: CheckoutErrorSchema.nullable(),
3716
+ requestId: z.string(),
3717
+ }),
3718
+ });
3719
+ export const CheckoutPrMergeResponseSchema = z.object({
3720
+ type: z.literal("checkout_pr_merge_response"),
3721
+ payload: z.object({
3722
+ cwd: z.string(),
3723
+ success: z.boolean(),
3724
+ error: CheckoutErrorSchema.nullable(),
3725
+ requestId: z.string(),
3726
+ }),
3727
+ });
3728
+ export const CheckoutGithubSetAutoMergeResponseSchema = z.object({
3729
+ type: z.literal("checkout.github.set_auto_merge.response"),
3730
+ payload: z.object({
3731
+ cwd: z.string(),
3732
+ enabled: z.boolean(),
3733
+ success: z.boolean(),
3734
+ error: CheckoutErrorSchema.nullable(),
3735
+ requestId: z.string(),
3736
+ }),
3737
+ });
3738
+ export const FilesUploadBeginResponseSchema = z.object({
3739
+ type: z.literal("files.upload.begin.response"),
3740
+ payload: z.object({
3741
+ requestId: z.string(),
3742
+ uploadId: z.string(),
3743
+ accepted: z.boolean(),
3744
+ chunkSize: z.number().int().positive(),
3745
+ error: FileUploadErrorCodeSchema.nullable(),
3746
+ }),
3747
+ });
3748
+ // Non-1:1 response: a daemon→client per-chunk ack with NO matching `.request`.
3749
+ // Per docs/rpc-namespacing.md, requests without a one-to-one response are called
3750
+ // out near the schema — this ack streams once per received chunk.
3751
+ export const FilesUploadChunkAckSchema = z.object({
3752
+ type: z.literal("files.upload.chunk.ack"),
3753
+ payload: z.object({
3754
+ requestId: z.string(),
3755
+ uploadId: z.string(),
3756
+ chunkIndex: z.number().int().nonnegative(),
3757
+ bytesReceived: z.number().int().nonnegative(),
3758
+ }),
3759
+ });
3760
+ export const FilesUploadCompleteResponseSchema = z.object({
3761
+ type: z.literal("files.upload.complete.response"),
3762
+ payload: z.object({
3763
+ requestId: z.string(),
3764
+ uploadId: z.string(),
3765
+ fileId: z.string(),
3766
+ path: z.string(),
3767
+ size: z.number().int().nonnegative(),
3768
+ mimeType: z.string(),
3769
+ error: FileUploadErrorCodeSchema.nullable(),
3770
+ }),
3771
+ });
3772
+ const CheckoutGithubCheckAnnotationSchema = z.object({
3773
+ path: z.string().optional(),
3774
+ startLine: z.number().optional(),
3775
+ endLine: z.number().optional(),
3776
+ annotationLevel: z.string().optional(),
3777
+ message: z.string().optional(),
3778
+ title: z.string().optional(),
3779
+ rawDetails: z.string().optional(),
3780
+ });
3781
+ const CheckoutGithubCheckJobSchema = z.object({
3782
+ jobId: z.number(),
3783
+ name: z.string(),
3784
+ status: z.string().nullable().optional(),
3785
+ conclusion: z.string().nullable().optional(),
3786
+ url: z.string().nullable().optional(),
3787
+ logTail: z.string().optional(),
3788
+ logTruncated: z.boolean().optional(),
3789
+ });
3790
+ export const CheckoutGithubCheckDetailsSchema = z.object({
3791
+ checkRunId: z.number(),
3792
+ workflowRunId: z.number().nullable().optional(),
3793
+ name: z.string(),
3794
+ status: z.string().nullable().optional(),
3795
+ conclusion: z.string().nullable().optional(),
3796
+ url: z.string().nullable().optional(),
3797
+ detailsUrl: z.string().nullable().optional(),
3798
+ output: z
3799
+ .object({
3800
+ title: z.string().nullable().optional(),
3801
+ summary: z.string().nullable().optional(),
3802
+ text: z.string().nullable().optional(),
3803
+ })
3804
+ .nullable()
3805
+ .optional(),
3806
+ annotations: z.array(CheckoutGithubCheckAnnotationSchema).optional().default([]),
3807
+ failedJobs: z.array(CheckoutGithubCheckJobSchema).optional().default([]),
3808
+ truncated: z.boolean().optional().default(false),
3809
+ });
3810
+ export const CheckoutGithubGetCheckDetailsResponseSchema = z.object({
3811
+ type: z.literal("checkout.github.get_check_details.response"),
3812
+ payload: z.object({
3813
+ cwd: z.string(),
3814
+ success: z.boolean(),
3815
+ details: CheckoutGithubCheckDetailsSchema.nullable().optional().default(null),
3816
+ error: CheckoutErrorSchema.nullable(),
3817
+ requestId: z.string(),
3818
+ }),
3819
+ });
3820
+ export const CheckoutPrStatusResponseSchema = z.object({
3821
+ type: z.literal("checkout_pr_status_response"),
3822
+ payload: CheckoutPrStatusPayloadSchema,
3823
+ });
3824
+ const PullRequestTimelineKnownErrorSchema = z.discriminatedUnion("kind", [
3825
+ z.object({
3826
+ kind: z.literal("not_found"),
3827
+ message: z.string().optional().default(""),
3828
+ }),
3829
+ z.object({
3830
+ kind: z.literal("forbidden"),
3831
+ message: z.string().optional().default(""),
3832
+ }),
3833
+ z.object({
3834
+ kind: z.literal("unknown"),
3835
+ message: z.string().optional().default(""),
3836
+ }),
3837
+ ]);
3838
+ const PullRequestTimelineErrorSchema = z.preprocess((value) => {
3839
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
3840
+ return { kind: "unknown", message: "" };
3841
+ }
3842
+ const error = value;
3843
+ if (error.kind === "not_found" || error.kind === "forbidden" || error.kind === "unknown") {
3844
+ return error;
3845
+ }
3846
+ return { ...error, kind: "unknown" };
3847
+ }, PullRequestTimelineKnownErrorSchema);
3848
+ const PullRequestTimelineReviewItemSchema = z.object({
3849
+ id: z.string().optional().default(""),
3850
+ kind: z.literal("review"),
3851
+ author: z.string().optional().default("unknown"),
3852
+ authorUrl: z.string().nullable().optional(),
3853
+ avatarUrl: z.string().nullable().optional(),
3854
+ body: z.string().optional().default(""),
3855
+ createdAt: z.number().optional().default(0),
3856
+ url: z.string().optional().default(""),
3857
+ reviewState: z
3858
+ .enum(["approved", "changes_requested", "commented"])
3859
+ .optional()
3860
+ .default("commented"),
3861
+ });
3862
+ const PullRequestTimelineCommentItemSchema = z.object({
3863
+ id: z.string().optional().default(""),
3864
+ kind: z.literal("comment"),
3865
+ author: z.string().optional().default("unknown"),
3866
+ authorUrl: z.string().nullable().optional(),
3867
+ avatarUrl: z.string().nullable().optional(),
3868
+ body: z.string().optional().default(""),
3869
+ createdAt: z.number().optional().default(0),
3870
+ url: z.string().optional().default(""),
3871
+ // GitHub review id this inline comment belongs to; lets clients nest review
3872
+ // threads under their parent review. Absent on issue comments and on
3873
+ // timelines from daemons that predate the field.
3874
+ reviewId: z.string().optional(),
3875
+ location: z
3876
+ .object({
3877
+ path: z.string(),
3878
+ line: z.number().optional(),
3879
+ startLine: z.number().optional(),
3880
+ threadId: z.string().optional(),
3881
+ isResolved: z.boolean().optional(),
3882
+ isOutdated: z.boolean().optional(),
3883
+ })
3884
+ .optional(),
3885
+ });
3886
+ export const PullRequestTimelineItemSchema = z.preprocess((value) => {
3887
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
3888
+ return value;
3889
+ }
3890
+ const item = value;
3891
+ if (item.kind === "review" || item.kind === "comment") {
3892
+ return item;
3893
+ }
3894
+ return { ...item, kind: "comment" };
3895
+ }, z.discriminatedUnion("kind", [
3896
+ PullRequestTimelineReviewItemSchema,
3897
+ PullRequestTimelineCommentItemSchema,
3898
+ ]));
3899
+ export const PullRequestTimelineResponseSchema = z.object({
3900
+ type: z.literal("pull_request_timeline_response"),
3901
+ payload: z
3902
+ .object({
3903
+ cwd: z.string().optional().default(""),
3904
+ prNumber: z.number().nullable().optional().default(null),
3905
+ items: z.array(PullRequestTimelineItemSchema).optional().default([]),
3906
+ truncated: z.boolean().optional().default(false),
3907
+ error: PullRequestTimelineErrorSchema.nullable().optional().default(null),
3908
+ requestId: z.string().optional().default(""),
3909
+ githubFeaturesEnabled: z.boolean().optional().default(true),
3910
+ })
3911
+ .optional()
3912
+ .prefault({}),
3913
+ });
3914
+ export const CheckoutSwitchBranchResponseSchema = z.object({
3915
+ type: z.literal("checkout_switch_branch_response"),
3916
+ payload: z.object({
3917
+ cwd: z.string(),
3918
+ success: z.boolean(),
3919
+ branch: z.string(),
3920
+ source: z.enum(["local", "remote"]).optional(),
3921
+ error: CheckoutErrorSchema.nullable(),
3922
+ requestId: z.string(),
3923
+ }),
3924
+ });
3925
+ export const CheckoutRenameBranchResponseSchema = z.object({
3926
+ type: z.literal("checkout.rename_branch.response"),
3927
+ payload: z.object({
3928
+ requestId: z.string(),
3929
+ success: z.boolean(),
3930
+ cwd: z.string(),
3931
+ currentBranch: z.string().nullable(),
3932
+ error: CheckoutErrorSchema.nullable(),
3933
+ }),
3934
+ });
3935
+ const StashEntrySchema = z.object({
3936
+ index: z.number().int().min(0),
3937
+ message: z.string(),
3938
+ branch: z.string().nullable(),
3939
+ isPaseo: z.boolean(),
3940
+ });
3941
+ export const StashSaveResponseSchema = z.object({
3942
+ type: z.literal("stash_save_response"),
3943
+ payload: z.object({
3944
+ cwd: z.string(),
3945
+ success: z.boolean(),
3946
+ error: CheckoutErrorSchema.nullable(),
3947
+ requestId: z.string(),
3948
+ }),
3949
+ });
3950
+ export const StashPopResponseSchema = z.object({
3951
+ type: z.literal("stash_pop_response"),
3952
+ payload: z.object({
3953
+ cwd: z.string(),
3954
+ success: z.boolean(),
3955
+ error: CheckoutErrorSchema.nullable(),
3956
+ requestId: z.string(),
3957
+ }),
3958
+ });
3959
+ export const StashListResponseSchema = z.object({
3960
+ type: z.literal("stash_list_response"),
3961
+ payload: z.object({
3962
+ cwd: z.string(),
3963
+ entries: z.array(StashEntrySchema),
3964
+ error: CheckoutErrorSchema.nullable(),
3965
+ requestId: z.string(),
3966
+ }),
3967
+ });
3968
+ export const ValidateBranchResponseSchema = z.object({
3969
+ type: z.literal("validate_branch_response"),
3970
+ payload: z.object({
3971
+ exists: z.boolean(),
3972
+ resolvedRef: z.string().nullable(),
3973
+ isRemote: z.boolean(),
3974
+ error: z.string().nullable(),
3975
+ requestId: z.string(),
3976
+ }),
3977
+ });
3978
+ export const BranchSuggestionsResponseSchema = z.object({
3979
+ type: z.literal("branch_suggestions_response"),
3980
+ payload: z.object({
3981
+ branches: z.array(z.string()),
3982
+ branchDetails: z
3983
+ .array(z.object({
3984
+ name: z.string(),
3985
+ committerDate: z.number(),
3986
+ hasLocal: z.boolean().optional(),
3987
+ hasRemote: z.boolean().optional(),
3988
+ }))
3989
+ .optional(),
3990
+ error: z.string().nullable(),
3991
+ requestId: z.string(),
3992
+ }),
3993
+ });
3994
+ export const GitHubSearchResponseSchema = z.object({
3995
+ type: z.literal("github_search_response"),
3996
+ payload: z.object({
3997
+ items: z.array(GitHubSearchItemSchema),
3998
+ githubFeaturesEnabled: z.boolean(),
3999
+ error: z.string().nullable(),
4000
+ requestId: z.string(),
4001
+ }),
4002
+ });
4003
+ export const DirectorySuggestionsResponseSchema = z.object({
4004
+ type: z.literal("directory_suggestions_response"),
4005
+ payload: z.object({
4006
+ directories: z.array(z.string()),
4007
+ entries: z
4008
+ .array(z.object({
4009
+ path: z.string(),
4010
+ kind: z.enum(["file", "directory"]),
4011
+ }))
4012
+ .optional()
4013
+ .default([]),
4014
+ error: z.string().nullable(),
4015
+ requestId: z.string(),
4016
+ }),
4017
+ });
4018
+ const PaseoWorktreeSchema = z.object({
4019
+ worktreePath: z.string(),
4020
+ createdAt: z.string(),
4021
+ branchName: z.string().nullable().optional(),
4022
+ head: z.string().nullable().optional(),
4023
+ });
4024
+ export const PaseoWorktreeListResponseSchema = z.object({
4025
+ type: z.literal("paseo_worktree_list_response"),
4026
+ payload: z.object({
4027
+ worktrees: z.array(PaseoWorktreeSchema),
4028
+ error: CheckoutErrorSchema.nullable(),
4029
+ requestId: z.string(),
4030
+ }),
4031
+ });
4032
+ export const PaseoWorktreeArchiveResponseSchema = z.object({
4033
+ type: z.literal("paseo_worktree_archive_response"),
4034
+ payload: z.object({
4035
+ success: z.boolean(),
4036
+ removedAgents: z.array(z.string()).optional(),
4037
+ error: CheckoutErrorSchema.nullable(),
4038
+ requestId: z.string(),
4039
+ }),
4040
+ });
4041
+ export const CreatePaseoWorktreeResponseSchema = z.object({
4042
+ type: z.literal("create_paseo_worktree_response"),
4043
+ payload: z.object({
4044
+ workspace: WorkspaceDescriptorPayloadSchema.nullable(),
4045
+ error: z.string().nullable(),
4046
+ errorCode: z.string().optional(),
4047
+ setupTerminalId: z.string().nullable(),
4048
+ requestId: z.string(),
4049
+ }),
4050
+ });
4051
+ export const FileExplorerResponseSchema = z.object({
4052
+ type: z.literal("file_explorer_response"),
4053
+ payload: z.object({
4054
+ cwd: z.string(),
4055
+ path: z.string(),
4056
+ mode: z.enum(["list", "file"]),
4057
+ directory: FileExplorerDirectorySchema.nullable(),
4058
+ file: FileExplorerFileSchema.nullable(),
4059
+ error: z.string().nullable(),
4060
+ requestId: z.string(),
4061
+ }),
4062
+ });
4063
+ const ProjectIconSchema = z.object({
4064
+ data: z.string(),
4065
+ mimeType: z.string(),
4066
+ });
4067
+ export const ProjectIconResponseSchema = z.object({
4068
+ type: z.literal("project_icon_response"),
4069
+ payload: z.object({
4070
+ cwd: z.string(),
4071
+ icon: ProjectIconSchema.nullable(),
4072
+ error: z.string().nullable(),
4073
+ requestId: z.string(),
4074
+ }),
4075
+ });
4076
+ export const FileDownloadTokenResponseSchema = z.object({
4077
+ type: z.literal("file_download_token_response"),
4078
+ payload: z.object({
4079
+ cwd: z.string(),
4080
+ path: z.string(),
4081
+ token: z.string().nullable(),
4082
+ fileName: z.string().nullable(),
4083
+ mimeType: z.string().nullable(),
4084
+ size: z.number().nullable(),
4085
+ error: z.string().nullable(),
4086
+ requestId: z.string(),
4087
+ }),
4088
+ });
4089
+ export const FileUploadResponseSchema = z.object({
4090
+ type: z.literal("file.upload.response"),
4091
+ payload: z.object({
4092
+ requestId: z.string(),
4093
+ file: UploadedFileAttachmentSchema.nullable(),
4094
+ error: z.string().nullable(),
4095
+ }),
4096
+ });
4097
+ export const ListProviderModelsResponseMessageSchema = z.object({
4098
+ type: z.literal("list_provider_models_response"),
4099
+ payload: z.object({
4100
+ provider: AgentProviderSchema,
4101
+ models: z.array(AgentModelDefinitionSchema).optional(),
4102
+ error: z.string().nullable().optional(),
4103
+ fetchedAt: z.string(),
4104
+ requestId: z.string(),
4105
+ }),
4106
+ });
4107
+ export const ListProviderModesResponseMessageSchema = z.object({
4108
+ type: z.literal("list_provider_modes_response"),
4109
+ payload: z.object({
4110
+ provider: AgentProviderSchema,
4111
+ modes: z.array(AgentModeSchema).optional(),
4112
+ error: z.string().nullable().optional(),
4113
+ fetchedAt: z.string(),
4114
+ requestId: z.string(),
4115
+ }),
4116
+ });
4117
+ export const ListProviderFeaturesResponseMessageSchema = z.object({
4118
+ type: z.literal("list_provider_features_response"),
4119
+ payload: z.object({
4120
+ provider: AgentProviderSchema,
4121
+ features: z.array(AgentFeatureSchema).optional(),
4122
+ error: z.string().nullable().optional(),
4123
+ fetchedAt: z.string(),
4124
+ requestId: z.string(),
4125
+ }),
4126
+ });
4127
+ const ProviderAvailabilitySchema = z.object({
4128
+ provider: AgentProviderSchema,
4129
+ available: z.boolean(),
4130
+ error: z.string().nullable().optional(),
4131
+ });
4132
+ export const ListAvailableProvidersResponseSchema = z.object({
4133
+ type: z.literal("list_available_providers_response"),
4134
+ payload: z.object({
4135
+ providers: z.array(ProviderAvailabilitySchema),
4136
+ error: z.string().nullable().optional(),
4137
+ fetchedAt: z.string(),
4138
+ requestId: z.string(),
4139
+ }),
4140
+ });
4141
+ // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
4142
+ export const GetProvidersSnapshotResponseMessageSchema = z.object({
4143
+ type: z.literal("get_providers_snapshot_response"),
4144
+ payload: z.object({
4145
+ entries: z.array(ProviderSnapshotEntrySchema),
4146
+ generatedAt: z.string(),
4147
+ requestId: z.string(),
4148
+ }),
4149
+ });
4150
+ // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
4151
+ export const ProvidersSnapshotUpdateMessageSchema = z.object({
4152
+ type: z.literal("providers_snapshot_update"),
4153
+ payload: z.object({
4154
+ cwd: z.string().optional(),
4155
+ entries: z.array(ProviderSnapshotEntrySchema),
4156
+ generatedAt: z.string(),
4157
+ }),
4158
+ });
4159
+ // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
4160
+ export const RefreshProvidersSnapshotResponseMessageSchema = z.object({
4161
+ type: z.literal("refresh_providers_snapshot_response"),
4162
+ payload: z.object({
4163
+ requestId: z.string(),
4164
+ acknowledged: z.boolean(),
4165
+ }),
4166
+ });
4167
+ // COMPAT(providersSnapshot): added in v0.1.48, remove gating when all clients use snapshot
4168
+ export const ProviderDiagnosticResponseMessageSchema = z.object({
4169
+ type: z.literal("provider_diagnostic_response"),
4170
+ payload: z.object({
4171
+ provider: AgentProviderSchema,
4172
+ diagnostic: z.string(),
4173
+ requestId: z.string(),
4174
+ }),
4175
+ });
4176
+ export const ProviderUsageToneSchema = z.enum(["default", "ok", "warning", "danger"]);
4177
+ export const ProviderUsageStatusSchema = z.enum(["available", "unavailable", "error"]);
4178
+ export const ProviderUsageWindowSchema = z.object({
4179
+ id: z.string(),
4180
+ label: z.string(),
4181
+ usedPct: z.number().nullable().optional(),
4182
+ remainingPct: z.number().nullable().optional(),
4183
+ resetsAt: z.string().nullable().optional(),
4184
+ runsOutAt: z.string().nullable().optional(),
4185
+ shortfallPct: z.number().nullable().optional(),
4186
+ tone: ProviderUsageToneSchema.optional(),
4187
+ });
4188
+ export const ProviderUsageBalanceSchema = z.object({
4189
+ id: z.string(),
4190
+ label: z.string(),
4191
+ used: z.number().nullable().optional(),
4192
+ remaining: z.number().nullable().optional(),
4193
+ limit: z.number().nullable().optional(),
4194
+ unit: z.enum(["usd", "credits", "requests", "tokens"]),
4195
+ resetsAt: z.string().nullable().optional(),
4196
+ tone: ProviderUsageToneSchema.optional(),
4197
+ });
4198
+ export const ProviderUsageDetailSchema = z.object({
4199
+ id: z.string(),
4200
+ label: z.string(),
4201
+ value: z.string(),
4202
+ tone: ProviderUsageToneSchema.optional(),
4203
+ });
4204
+ export const ProviderUsageSchema = z.object({
4205
+ providerId: z.string(),
4206
+ displayName: z.string(),
4207
+ status: ProviderUsageStatusSchema,
4208
+ planLabel: z.string().nullable(),
4209
+ sourceLabel: z.string().nullable().optional(),
4210
+ fetchedAt: z.string().nullable().optional(),
4211
+ nextRefreshAt: z.string().nullable().optional(),
4212
+ windows: z.array(ProviderUsageWindowSchema),
4213
+ balances: z.array(ProviderUsageBalanceSchema).optional(),
4214
+ details: z.array(ProviderUsageDetailSchema).optional(),
4215
+ error: z.string().nullable().optional(),
4216
+ });
4217
+ export const ProviderUsageListResponseMessageSchema = z.object({
4218
+ type: z.literal("provider.usage.list.response"),
4219
+ payload: z.object({
4220
+ requestId: z.string(),
4221
+ fetchedAt: z.string(),
4222
+ providers: z.array(ProviderUsageSchema),
4223
+ }),
4224
+ });
4225
+ const AgentSlashCommandSchema = z.object({
4226
+ name: z.string(),
4227
+ description: z.string(),
4228
+ argumentHint: z.string(),
4229
+ kind: z.enum(["command", "skill"]).optional().catch("command"),
4230
+ });
4231
+ export const ListCommandsResponseSchema = z.object({
4232
+ type: z.literal("list_commands_response"),
4233
+ payload: z.object({
4234
+ agentId: z.string(),
4235
+ commands: z.array(AgentSlashCommandSchema),
4236
+ error: z.string().nullable(),
4237
+ requestId: z.string(),
4238
+ }),
4239
+ });
4240
+ // ============================================================================
4241
+ // Terminal Outbound Messages
4242
+ // ============================================================================
4243
+ const TerminalInfoSchema = z.object({
4244
+ id: z.string(),
4245
+ name: z.string(),
4246
+ cwd: z.string(),
4247
+ workspaceId: z.string().optional(),
4248
+ title: z.string().optional(),
4249
+ activity: TerminalActivitySchema.nullable().optional(),
4250
+ });
4251
+ export const TerminalCellSchema = z.object({
4252
+ char: z.string(),
4253
+ fg: z.number().optional(),
4254
+ bg: z.number().optional(),
4255
+ fgMode: z.number().optional(),
4256
+ bgMode: z.number().optional(),
4257
+ bold: z.boolean().optional(),
4258
+ italic: z.boolean().optional(),
4259
+ underline: z.boolean().optional(),
4260
+ dim: z.boolean().optional(),
4261
+ inverse: z.boolean().optional(),
4262
+ strikethrough: z.boolean().optional(),
4263
+ });
4264
+ export const TerminalCursorStyleSchema = z.enum(["block", "underline", "bar"]);
4265
+ export const TerminalCursorSchema = z.object({
4266
+ row: z.number(),
4267
+ col: z.number(),
4268
+ hidden: z.boolean().optional(),
4269
+ style: TerminalCursorStyleSchema.optional(),
4270
+ blink: z.boolean().optional(),
4271
+ });
4272
+ export const TerminalStateSchema = z.object({
4273
+ rows: z.number(),
4274
+ cols: z.number(),
4275
+ grid: z.array(z.array(TerminalCellSchema)),
4276
+ scrollback: z.array(z.array(TerminalCellSchema)),
4277
+ cursor: TerminalCursorSchema,
4278
+ title: z.string().optional(),
4279
+ // Per-row soft-wrap flags aligned 1:1 with `grid` / `scrollback`. `true` means
4280
+ // the row continued onto the next row (xterm's GRID_LINE_WRAPPED equivalent),
4281
+ // so the client can re-wrap the logical line on resize instead of freezing it
4282
+ // at the snapshot width. Optional: only sent to clients that advertise the
4283
+ // `terminalReflowableSnapshot` capability, so old daemons/clients are unaffected.
4284
+ gridWrapped: z.array(z.boolean()).optional(),
4285
+ scrollbackWrapped: z.array(z.boolean()).optional(),
4286
+ });
4287
+ export const ListTerminalsResponseSchema = z.object({
4288
+ type: z.literal("list_terminals_response"),
4289
+ payload: z.object({
4290
+ cwd: z.string().optional(),
4291
+ terminals: z.array(TerminalInfoSchema.omit({ cwd: true })),
4292
+ requestId: z.string(),
4293
+ }),
4294
+ });
4295
+ export const TerminalsChangedSchema = z.object({
4296
+ type: z.literal("terminals_changed"),
4297
+ payload: z.object({
4298
+ cwd: z.string(),
4299
+ terminals: z.array(TerminalInfoSchema.omit({ cwd: true })),
4300
+ }),
4301
+ });
4302
+ export const CreateTerminalResponseSchema = z.object({
4303
+ type: z.literal("create_terminal_response"),
4304
+ payload: z.object({
4305
+ terminal: TerminalInfoSchema.nullable(),
4306
+ error: z.string().nullable(),
4307
+ requestId: z.string(),
4308
+ }),
4309
+ });
4310
+ export const RenameTerminalResponseSchema = z.object({
4311
+ type: z.literal("terminal.rename.response"),
4312
+ payload: z.object({
4313
+ requestId: z.string(),
4314
+ success: z.boolean(),
4315
+ error: z.string().nullable(),
4316
+ }),
4317
+ });
4318
+ export const SubscribeTerminalResponseSchema = z.object({
4319
+ type: z.literal("subscribe_terminal_response"),
4320
+ payload: z.union([
4321
+ z.object({
4322
+ terminalId: z.string(),
4323
+ slot: z.number().int().min(0).max(255),
4324
+ error: z.null(),
4325
+ requestId: z.string(),
4326
+ }),
4327
+ z.object({
4328
+ terminalId: z.string(),
4329
+ error: z.string(),
4330
+ requestId: z.string(),
4331
+ }),
4332
+ ]),
4333
+ });
4334
+ export const KillTerminalResponseSchema = z.object({
4335
+ type: z.literal("kill_terminal_response"),
4336
+ payload: z.object({
4337
+ terminalId: z.string(),
4338
+ success: z.boolean(),
4339
+ requestId: z.string(),
4340
+ }),
4341
+ });
4342
+ export const CaptureTerminalResponseSchema = z.object({
4343
+ type: z.literal("capture_terminal_response"),
4344
+ payload: z.object({
4345
+ terminalId: z.string(),
4346
+ lines: z.array(z.string()),
4347
+ totalLines: z.number().int().nonnegative(),
4348
+ requestId: z.string(),
4349
+ }),
4350
+ });
4351
+ export const TerminalStreamExitSchema = z.object({
4352
+ type: z.literal("terminal_stream_exit"),
4353
+ payload: z.object({
4354
+ terminalId: z.string(),
4355
+ }),
4356
+ });
4357
+ export const TerminalAttentionRequiredSchema = z.object({
4358
+ type: z.literal("terminal_attention_required"),
4359
+ payload: z.object({
4360
+ serverId: z.string().optional(),
4361
+ terminalId: z.string(),
4362
+ cwd: z.string(),
4363
+ workspaceId: z.string().optional(),
4364
+ reason: z.enum(["finished", "needs_input"]),
4365
+ title: z.string(),
4366
+ body: z.string(),
4367
+ shouldNotify: z.boolean(),
4368
+ }),
4369
+ });
4370
+ export const DaemonUpdateResponseSchema = z.object({
4371
+ type: z.literal("daemon.update.response"),
4372
+ payload: z.object({
4373
+ requestId: z.string(),
4374
+ success: z.boolean(),
4375
+ error: z.string().nullable(),
4376
+ previousVersion: z.string().nullable(),
4377
+ newVersion: z.string().nullable(),
4378
+ }),
4379
+ });
4380
+ export const DaemonUpdateProgressMessageSchema = z.object({
4381
+ type: z.literal("daemon.update.progress"),
4382
+ payload: z.object({
4383
+ requestId: z.string(),
4384
+ phase: z.enum(["starting", "downloading", "installing", "complete"]),
4385
+ }),
4386
+ });
4387
+ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
4388
+ BrowserAutomationExecuteRequestSchema,
4389
+ ActivityLogMessageSchema,
4390
+ AssistantChunkMessageSchema,
4391
+ AudioOutputMessageSchema,
4392
+ TranscriptionResultMessageSchema,
4393
+ VoiceInputStateMessageSchema,
4394
+ DictationStreamAckMessageSchema,
4395
+ DictationStreamFinishAcceptedMessageSchema,
4396
+ DictationStreamPartialMessageSchema,
4397
+ DictationStreamFinalMessageSchema,
4398
+ DictationStreamErrorMessageSchema,
4399
+ StatusMessageSchema,
4400
+ PongMessageSchema,
4401
+ RpcErrorMessageSchema,
4402
+ ArtifactMessageSchema,
4403
+ AgentUpdateMessageSchema,
4404
+ OpenUrlMessageSchema,
4405
+ WorkspaceUpdateMessageSchema,
4406
+ ScriptStatusUpdateMessageSchema,
4407
+ WorkspaceSetupProgressMessageSchema,
4408
+ WorkspaceSetupStatusResponseMessageSchema,
4409
+ AgentStreamMessageSchema,
4410
+ AgentStatusMessageSchema,
4411
+ FetchAgentsResponseMessageSchema,
4412
+ FetchAgentHistoryResponseMessageSchema,
4413
+ FetchRecentProviderSessionsResponseMessageSchema,
4414
+ FetchWorkspacesResponseMessageSchema,
4415
+ ProjectAddResponseSchema,
4416
+ OpenProjectResponseMessageSchema,
4417
+ StartWorkspaceScriptResponseMessageSchema,
4418
+ LegacyListAvailableEditorsResponseMessageSchema,
4419
+ LegacyOpenInEditorResponseMessageSchema,
4420
+ ArchiveWorkspaceResponseMessageSchema,
4421
+ FetchAgentResponseMessageSchema,
4422
+ FetchAgentTimelineResponseMessageSchema,
4423
+ AgentForkContextResponseMessageSchema,
4424
+ CancelAgentResponseMessageSchema,
4425
+ ClearAgentAttentionResponseMessageSchema,
4426
+ WorkspaceCreateResponseSchema,
4427
+ WorkspaceClearAttentionResponseSchema,
4428
+ SendAgentMessageResponseMessageSchema,
4429
+ SetVoiceModeResponseMessageSchema,
4430
+ DaemonGetStatusResponseSchema,
4431
+ DaemonGetPairingOfferResponseSchema,
4432
+ WorkflowListResponseSchema,
4433
+ WorkflowGetResponseSchema,
4434
+ WorkflowCancelResponseSchema,
4435
+ WorkflowEventsResponseSchema,
4436
+ // COMPAT(workflows): added in v0.1.X, remove gate after 2026-12-17.
4437
+ WorkflowStartResponseSchema,
4438
+ DiagnosticsResponseSchema,
4439
+ GetDaemonConfigResponseMessageSchema,
4440
+ SetDaemonConfigResponseMessageSchema,
4441
+ ReadProjectConfigResponseMessageSchema,
4442
+ WriteProjectConfigResponseMessageSchema,
4443
+ SetAgentModeResponseMessageSchema,
4444
+ SetAgentModelResponseMessageSchema,
4445
+ SwapAgentProviderResponseMessageSchema,
4446
+ AgentProviderSwappedEventSchema,
4447
+ SetAgentThinkingResponseMessageSchema,
4448
+ SetAgentFeatureResponseMessageSchema,
4449
+ AgentDetachResponseMessageSchema,
4450
+ AgentRewindResponseMessageSchema,
4451
+ UpdateAgentResponseMessageSchema,
4452
+ ProjectRenameResponseSchema,
4453
+ ProjectRemoveResponseSchema,
4454
+ WorkspaceTitleSetResponseSchema,
4455
+ WaitForFinishResponseMessageSchema,
4456
+ AgentPermissionRequestMessageSchema,
4457
+ AgentPermissionResolvedMessageSchema,
4458
+ AgentDeletedMessageSchema,
4459
+ AgentArchivedMessageSchema,
4460
+ BackgroundTaskDismissedMessageSchema,
4461
+ CloseItemsResponseSchema,
4462
+ CheckoutStatusResponseSchema,
4463
+ CheckoutStatusUpdateSchema,
4464
+ SubscribeCheckoutDiffResponseSchema,
4465
+ CheckoutDiffUpdateSchema,
4466
+ CheckoutCommitResponseSchema,
4467
+ CheckoutMergeResponseSchema,
4468
+ CheckoutMergeFromBaseResponseSchema,
4469
+ CheckoutPullResponseSchema,
4470
+ CheckoutPushResponseSchema,
4471
+ CheckoutRefreshResponseSchema,
4472
+ CheckoutPrCreateResponseSchema,
4473
+ CheckoutPrMergeResponseSchema,
4474
+ CheckoutGithubSetAutoMergeResponseSchema,
4475
+ FilesUploadBeginResponseSchema,
4476
+ FilesUploadChunkAckSchema,
4477
+ FilesUploadCompleteResponseSchema,
4478
+ FilesUploadErrorSchema,
4479
+ CheckoutGithubGetCheckDetailsResponseSchema,
4480
+ CheckoutPrStatusResponseSchema,
4481
+ PullRequestTimelineResponseSchema,
4482
+ CheckoutSwitchBranchResponseSchema,
4483
+ CheckoutRenameBranchResponseSchema,
4484
+ StashSaveResponseSchema,
4485
+ StashPopResponseSchema,
4486
+ StashListResponseSchema,
4487
+ ValidateBranchResponseSchema,
4488
+ BranchSuggestionsResponseSchema,
4489
+ GitHubSearchResponseSchema,
4490
+ DirectorySuggestionsResponseSchema,
4491
+ PaseoWorktreeListResponseSchema,
4492
+ PaseoWorktreeArchiveResponseSchema,
4493
+ CreatePaseoWorktreeResponseSchema,
4494
+ FileExplorerResponseSchema,
4495
+ ProjectIconResponseSchema,
4496
+ FileDownloadTokenResponseSchema,
4497
+ FileUploadResponseSchema,
4498
+ ListProviderModelsResponseMessageSchema,
4499
+ ListProviderModesResponseMessageSchema,
4500
+ ListProviderFeaturesResponseMessageSchema,
4501
+ ListAvailableProvidersResponseSchema,
4502
+ GetProvidersSnapshotResponseMessageSchema,
4503
+ ProvidersSnapshotUpdateMessageSchema,
4504
+ RefreshProvidersSnapshotResponseMessageSchema,
4505
+ ProviderDiagnosticResponseMessageSchema,
4506
+ ProviderUsageListResponseMessageSchema,
4507
+ ListCommandsResponseSchema,
4508
+ ListTerminalsResponseSchema,
4509
+ TerminalsChangedSchema,
4510
+ CreateTerminalResponseSchema,
4511
+ RenameTerminalResponseSchema,
4512
+ SubscribeTerminalResponseSchema,
4513
+ KillTerminalResponseSchema,
4514
+ CaptureTerminalResponseSchema,
4515
+ TerminalStreamExitSchema,
4516
+ TerminalAttentionRequiredSchema,
4517
+ ChatCreateResponseSchema,
4518
+ ChatListResponseSchema,
4519
+ ChatInspectResponseSchema,
4520
+ ChatDeleteResponseSchema,
4521
+ ChatPostResponseSchema,
4522
+ ChatReadResponseSchema,
4523
+ ChatWaitResponseSchema,
4524
+ ScheduleCreateResponseSchema,
4525
+ ScheduleListResponseSchema,
4526
+ ScheduleInspectResponseSchema,
4527
+ ScheduleLogsResponseSchema,
4528
+ SchedulePauseResponseSchema,
4529
+ ScheduleResumeResponseSchema,
4530
+ ScheduleDeleteResponseSchema,
4531
+ ScheduleRunOnceResponseSchema,
4532
+ ScheduleUpdateResponseSchema,
4533
+ LoopRunResponseSchema,
4534
+ LoopListResponseSchema,
4535
+ LoopInspectResponseSchema,
4536
+ LoopLogsResponseSchema,
4537
+ LoopStopResponseSchema,
4538
+ RewindSessionResponseMessageSchema,
4539
+ EditMessageResponseMessageSchema,
4540
+ ListWorkspaceSessionsResponseMessageSchema,
4541
+ ListAllSessionsResponseMessageSchema,
4542
+ ListExtensionsResponseMessageSchema,
4543
+ ExecuteExtensionCommandResponseMessageSchema,
4544
+ SearchSessionsResponseMessageSchema,
4545
+ SubscribePushResponseMessageSchema,
4546
+ GetVapidPublicKeyResponseMessageSchema,
4547
+ WatchActionablePushMessageSchema,
4548
+ WatchActionableResolvedMessageSchema,
4549
+ WatchSessionSearchResultMessageSchema,
4550
+ DaemonUpdateProgressMessageSchema,
4551
+ DaemonUpdateResponseSchema,
4552
+ ]);
4553
+ // ============================================================================
4554
+ // WebSocket Level Messages (wraps session messages)
4555
+ // ============================================================================
4556
+ // WebSocket-only messages (not session messages)
4557
+ export const WSPingMessageSchema = z.object({
4558
+ type: z.literal("ping"),
4559
+ });
4560
+ export const WSPongMessageSchema = z.object({
4561
+ type: z.literal("pong"),
4562
+ });
4563
+ export const WSHelloMessageSchema = z.object({
4564
+ type: z.literal("hello"),
4565
+ clientId: z.string().min(1),
4566
+ clientType: z.enum(["mobile", "browser", "cli", "mcp"]),
4567
+ protocolVersion: z.number().int(),
4568
+ appVersion: z.string().optional(),
4569
+ capabilities: z
4570
+ .object({
4571
+ voice: z.boolean().optional(),
4572
+ pushNotifications: z.boolean().optional(),
4573
+ [CLIENT_CAPS.reasoningMergeEnum]: z.boolean().optional(),
4574
+ [CLIENT_CAPS.customModeIcons]: z.boolean().optional(),
4575
+ [CLIENT_CAPS.terminalReflowableSnapshot]: z.boolean().optional(),
4576
+ [CLIENT_CAPS.browserHost]: BrowserAutomationHostCapabilitySchema.optional(),
4577
+ })
4578
+ .passthrough()
4579
+ .optional(),
4580
+ });
4581
+ export const WSRecordingStateMessageSchema = z.object({
4582
+ type: z.literal("recording_state"),
4583
+ isRecording: z.boolean(),
4584
+ });
4585
+ // Wrapped session message
4586
+ export const WSSessionInboundSchema = z.object({
4587
+ type: z.literal("session"),
4588
+ message: SessionInboundMessageSchema,
4589
+ });
4590
+ export const WSSessionOutboundSchema = z.object({
4591
+ type: z.literal("session"),
4592
+ message: SessionOutboundMessageSchema,
4593
+ });
4594
+ // Complete WebSocket message schemas
4595
+ export const WSInboundMessageSchema = z.discriminatedUnion("type", [
4596
+ WSPingMessageSchema,
4597
+ WSHelloMessageSchema,
4598
+ WSRecordingStateMessageSchema,
4599
+ WSSessionInboundSchema,
4600
+ ]);
4601
+ export const WSOutboundMessageSchema = z.discriminatedUnion("type", [
4602
+ WSPongMessageSchema,
4603
+ WSSessionOutboundSchema,
4604
+ ]);
4605
+ // ============================================================================
4606
+ // Helper functions for message conversion
4607
+ // ============================================================================
4608
+ /**
4609
+ * Extract session message from WebSocket message
4610
+ * Returns null if message should be handled at WS level only
4611
+ */
4612
+ export function extractSessionMessage(wsMsg) {
4613
+ if (wsMsg.type === "session") {
4614
+ return wsMsg.message;
4615
+ }
4616
+ // Ping and recording_state are WS-level only
4617
+ return null;
4618
+ }
4619
+ /**
4620
+ * Wrap session message in WebSocket envelope
4621
+ */
4622
+ export function wrapSessionMessage(sessionMsg) {
4623
+ return {
4624
+ type: "session",
4625
+ message: sessionMsg,
4626
+ };
4627
+ }
4628
+ export function parseServerInfoStatusPayload(payload) {
4629
+ const parsed = ServerInfoStatusPayloadSchema.safeParse(payload);
4630
+ if (!parsed.success) {
4631
+ return null;
4632
+ }
4633
+ return parsed.data;
4634
+ }
4635
+ // ============================================================================
4636
+ // HookEvent types — daemon-internal (Unix socket boundary only, NOT WebSocket)
4637
+ // ============================================================================
4638
+ export const PermissionHookEventSchema = z.object({
4639
+ kind: z.literal("permission"),
4640
+ tool_name: z.string(),
4641
+ tool_input: z.unknown(),
4642
+ agent_id: z.string(),
4643
+ });
4644
+ export const PreToolHookEventSchema = z.object({
4645
+ kind: z.literal("pre-tool"),
4646
+ tool_name: z.string(),
4647
+ tool_input: z.unknown(),
4648
+ agent_id: z.string(),
4649
+ });
4650
+ export const PostToolHookEventSchema = z.object({
4651
+ kind: z.literal("post-tool"),
4652
+ tool_name: z.string(),
4653
+ tool_input: z.unknown(),
4654
+ tool_response: z.unknown(),
4655
+ agent_id: z.string(),
4656
+ });
4657
+ export const StatusLineHookEventSchema = z.object({
4658
+ kind: z.literal("status-line"),
4659
+ statusText: z.string(),
4660
+ agent_id: z.string(),
4661
+ });
4662
+ export const SessionStartHookEventSchema = z.object({
4663
+ kind: z.literal("session-start"),
4664
+ session_id: z.string(),
4665
+ transcript_path: z.string(),
4666
+ agent_id: z.string(),
4667
+ });
4668
+ export const SessionEndHookEventSchema = z.object({
4669
+ kind: z.literal("session-end"),
4670
+ session_id: z.string(),
4671
+ transcript_path: z.string(),
4672
+ agent_id: z.string(),
4673
+ });
4674
+ // Transcript-derived events (sourced from the JSONL transcript tail, not from the
4675
+ // claude hook subprocess). Spike Issue 1: assistant text deltas and per-request
4676
+ // cache token detail are not delivered through any hook; the tailer reads them
4677
+ // from ~/.claude/projects/<cwd-slug>/<session-id>.jsonl and emits them on the
4678
+ // same channel for pump uniformity.
4679
+ export const AssistantMessageDeltaHookEventSchema = z.object({
4680
+ kind: z.literal("assistant_message_delta"),
4681
+ delta: z.object({ text: z.string() }),
4682
+ });
4683
+ export const UsageUpdateHookEventSchema = z.object({
4684
+ kind: z.literal("usage_update"),
4685
+ usage: z.object({
4686
+ input_tokens: z.number().optional(),
4687
+ output_tokens: z.number().optional(),
4688
+ cache_creation_input_tokens: z.number().optional(),
4689
+ cache_read_input_tokens: z.number().optional(),
4690
+ total_cost_usd: z.number().optional(),
4691
+ }),
4692
+ });
4693
+ export const HookEventSchema = z.discriminatedUnion("kind", [
4694
+ PermissionHookEventSchema,
4695
+ PreToolHookEventSchema,
4696
+ PostToolHookEventSchema,
4697
+ StatusLineHookEventSchema,
4698
+ SessionStartHookEventSchema,
4699
+ SessionEndHookEventSchema,
4700
+ AssistantMessageDeltaHookEventSchema,
4701
+ UsageUpdateHookEventSchema,
4702
+ ]);
4703
+ //# sourceMappingURL=messages.js.map