@otto-code/protocol 0.6.7 → 0.7.1

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.
package/dist/messages.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { TerminalActivitySchema } from "./terminal-activity.js";
3
- import { OrchestrationGraphSchema, RunSchema } from "./orchestration.js";
3
+ import { OrchestrationGraphSchema, PromptTemplateSchema, RunSchema } from "./orchestration.js";
4
4
  import { ArtifactMetadataSchema } from "./artifacts/types.js";
5
5
  import { ArtifactListRequestSchema, ArtifactCreateRequestSchema, ArtifactUpdateRequestSchema, ArtifactRegenerateRequestSchema, ArtifactCancelRequestSchema, ArtifactDeleteRequestSchema, ArtifactStarRequestSchema, ArtifactGetContentRequestSchema, ArtifactListResponseSchema, ArtifactCreateResponseSchema, ArtifactUpdateResponseSchema, ArtifactRegenerateResponseSchema, ArtifactCancelResponseSchema, ArtifactDeleteResponseSchema, ArtifactStarResponseSchema, ArtifactGetContentResponseSchema, ArtifactCreatedNotificationSchema, ArtifactUpdatedNotificationSchema, ArtifactDeletedNotificationSchema, } from "./artifacts/rpc-schemas.js";
6
6
  import { CLIENT_CAPS } from "./client-capabilities.js";
@@ -84,6 +84,53 @@ const MutableBrowserToolsConfigSchema = z
84
84
  enabled: z.boolean().default(false),
85
85
  })
86
86
  .passthrough();
87
+ /**
88
+ * Language-server code intelligence, host-scoped because the servers are processes
89
+ * on the daemon's machine — they follow the host, not the client.
90
+ *
91
+ * `enabled` defaults **on** and that is safe: nothing spawns until a
92
+ * code-intelligence action needs a language in a workspace, so an unused language
93
+ * costs nothing. What the switch guarantees is that off means off — no server
94
+ * spawns for any workspace, and the ctags index still serves the outline and the
95
+ * fuzzy finder.
96
+ *
97
+ * `languages` keys are registry row ids (`typescript`, `python`, `csharp`, …). An
98
+ * absent key means "use the row's own default", so a new row ships with its
99
+ * intended default rather than reading as disabled.
100
+ */
101
+ const MutableLspConfigSchema = z
102
+ .object({
103
+ enabled: z.boolean().default(true),
104
+ languages: z.record(z.string(), z.boolean()).default({}),
105
+ /** Hard LRU cap on simultaneously running servers, across all workspaces. */
106
+ maxRunningServers: z.number().int().positive().default(6),
107
+ idleMinutes: z.number().int().positive().default(10),
108
+ /** Shorter allowance for workspaces the user is not currently looking at. */
109
+ backgroundIdleMinutes: z.number().int().positive().default(2),
110
+ })
111
+ .passthrough();
112
+ /**
113
+ * "Microsoft .NET Solution Management" — the Solution view's own switch.
114
+ *
115
+ * **A sibling of `lsp`, not a member of it.** Turning C# code intelligence off does not turn
116
+ * this off and vice versa: they are independent capabilities that happen to share a language,
117
+ * and nesting this inside the LSP settings object would imply exactly the coupling that
118
+ * decision rejects. (It would also be wrong on the facts — LSP has no project-structure
119
+ * request, so nothing here rides on a language server.)
120
+ *
121
+ * Defaults **off**: the feature spawns a process and evaluates MSBuild. Disabled is genuinely
122
+ * off, not merely hidden — no discovery walk, no `.sln` read, no `.csproj` parse, no sidecar,
123
+ * no cache, no watcher, and no view switcher. The daemon reads this before scheduling any work,
124
+ * so a disabled feature costs exactly one boolean check.
125
+ */
126
+ const MutableDotnetSolutionConfigSchema = z
127
+ .object({
128
+ enabled: z.boolean().default(false),
129
+ /** Hard cap on simultaneously running sidecars, across all workspaces. */
130
+ maxRunningProbes: z.number().int().positive().default(2),
131
+ idleMinutes: z.number().int().positive().default(10),
132
+ })
133
+ .passthrough();
87
134
  // Speech engine ids and model ids stay plain strings on the wire so adding an
88
135
  // engine or model never breaks an older peer; the daemon validates values
89
136
  // against its own catalog when applying a patch.
@@ -244,6 +291,13 @@ export const AgentPersonalitySchema = z
244
291
  spinner: AgentPersonalitySpinnerSchema.optional(),
245
292
  voice: AgentPersonalityVoiceSchema.optional(),
246
293
  voiceCues: AgentPersonalityVoiceCuesSchema.optional(),
294
+ // Whether this personality accrues lessons across sessions (personality
295
+ // memory). ABSENT MEANS ON: a personality with no lessons injects nothing
296
+ // and costs nothing, so an off-by-default switch would only mean the feature
297
+ // never starts working for anyone who did not go looking for it. The switch
298
+ // exists to stop a personality accruing, not to start it.
299
+ // See docs/agent-personalities.md § Memory.
300
+ memoryEnabled: z.boolean().optional(),
247
301
  })
248
302
  .passthrough();
249
303
  const MutableAgentPersonalitiesConfigSchema = z
@@ -378,6 +432,13 @@ export const MutableDaemonConfigSchema = z
378
432
  // Defaults false so a new client parsing an old daemon's config keeps the
379
433
  // action visible. Gated by server_info features.hideMergeIntoBaseSetting.
380
434
  hideMergeIntoBaseAction: z.boolean().default(false),
435
+ // Retention for the images agents produce (docs/attachment-lifecycle.md).
436
+ // Host-level, because the store they govern is the daemon's. Defaults match
437
+ // the constants the daemon shipped with, so a client parsing an old daemon's
438
+ // config sees the policy actually in force rather than zeros. 0 on either
439
+ // disables that lever. Gated by server_info features.attachmentStorage.
440
+ attachmentImageMaxAgeDays: z.number().int().min(0).default(30),
441
+ attachmentImageMaxTotalMb: z.number().int().min(0).default(512),
381
442
  enableTerminalAgentHooks: z.boolean().default(false),
382
443
  appendSystemPrompt: z.string().default(""),
383
444
  terminalProfiles: z.array(TerminalProfileSchema).optional(),
@@ -402,6 +463,24 @@ export const MutableDaemonConfigSchema = z
402
463
  // empty so a new client parsing an old daemon's config still sees a
403
464
  // well-formed array.
404
465
  savedProviderEndpoints: z.array(SavedProviderEndpointSchema).default([]),
466
+ // Language-server code intelligence. Gated by server_info features.lsp; the
467
+ // default section is well-formed so a new client parsing an old daemon's config
468
+ // still renders the screen.
469
+ lsp: MutableLspConfigSchema.default({
470
+ enabled: true,
471
+ languages: {},
472
+ maxRunningServers: 6,
473
+ idleMinutes: 10,
474
+ backgroundIdleMinutes: 2,
475
+ }),
476
+ // The Solution view's switch. Gated by server_info features.solutionView; the default
477
+ // section is well-formed and OFF, so a new client parsing an old daemon's config renders
478
+ // the row without ever implying the feature is running.
479
+ dotnetSolutionManagement: MutableDotnetSolutionConfigSchema.default({
480
+ enabled: false,
481
+ maxRunningProbes: 2,
482
+ idleMinutes: 10,
483
+ }),
405
484
  })
406
485
  .passthrough();
407
486
  export const MutableDaemonConfigPatchSchema = z
@@ -420,6 +499,9 @@ export const MutableDaemonConfigPatchSchema = z
420
499
  autoArchiveAfterMerge: z.boolean().optional(),
421
500
  // Gated by server_info features.hideMergeIntoBaseSetting.
422
501
  hideMergeIntoBaseAction: z.boolean().optional(),
502
+ // Gated by server_info features.attachmentStorage.
503
+ attachmentImageMaxAgeDays: z.number().int().min(0).optional(),
504
+ attachmentImageMaxTotalMb: z.number().int().min(0).optional(),
423
505
  enableTerminalAgentHooks: z.boolean().optional(),
424
506
  appendSystemPrompt: z.string().optional(),
425
507
  terminalProfiles: z.array(TerminalProfileSchema).optional(),
@@ -442,6 +524,11 @@ export const MutableDaemonConfigPatchSchema = z
442
524
  // Gated by server_info features.savedProviderEndpoints. Replaces the full
443
525
  // array (read-modify-write), so forgetting an endpoint drops it from disk.
444
526
  savedProviderEndpoints: z.array(SavedProviderEndpointSchema).optional(),
527
+ // Gated by server_info features.lsp; patches deep-merge, so a `languages`
528
+ // patch replaces only the keys it names.
529
+ lsp: MutableLspConfigSchema.partial().optional(),
530
+ // Gated by server_info features.solutionView; patches deep-merge.
531
+ dotnetSolutionManagement: MutableDotnetSolutionConfigSchema.partial().optional(),
445
532
  })
446
533
  .partial()
447
534
  .passthrough();
@@ -563,6 +650,14 @@ const ContextCompositionSchema = z.object({
563
650
  reasoning: z.number().optional(),
564
651
  subagentResults: z.number().optional(),
565
652
  });
653
+ // Declared above AgentUsageSchema on purpose: a schema referenced before its
654
+ // declaration is a build-time ReferenceError in the zod-aot output.
655
+ const AgentContextCategorySchema = z.object({
656
+ /** Provider-supplied display label, not translated and not an enum. */
657
+ name: z.string(),
658
+ tokens: z.number(),
659
+ isDeferred: z.boolean().optional(),
660
+ });
566
661
  const AgentUsageSchema = z.object({
567
662
  inputTokens: z.number().optional(),
568
663
  cachedInputTokens: z.number().optional(),
@@ -575,6 +670,10 @@ const AgentUsageSchema = z.object({
575
670
  // Provider-graded context breakdown for the visualizer ring/bar; absent ⇒
576
671
  // occupancy only (pre-composition behavior). See ContextComposition.
577
672
  contextComposition: ContextCompositionSchema.optional(),
673
+ // The provider's own labelled split — same accounting as
674
+ // `agent.context.get_usage`, pushed on the snapshot. Preferred over
675
+ // contextComposition; absent ⇒ fall back to the estimate, then to occupancy.
676
+ contextCategories: z.array(AgentContextCategorySchema).optional(),
578
677
  });
579
678
  const AgentSessionConfigSchema = z.object({
580
679
  provider: AgentProviderSchema,
@@ -919,6 +1018,52 @@ const AgentRuntimeInfoSchema = z.object({
919
1018
  modeId: z.string().nullable().optional(),
920
1019
  extra: z.record(z.string(), z.unknown()).optional(),
921
1020
  });
1021
+ /**
1022
+ * One message parked for delivery as an agent's NEXT turn (`delivery: "queue"`).
1023
+ * The daemon owns the queue; this is the read-only projection the Queue track
1024
+ * renders. Declared above AgentSnapshotPayloadSchema — zod-aot emits schemas in
1025
+ * source order, so a forward reference is a build-time ReferenceError.
1026
+ */
1027
+ export const QueuedAgentMessagePayloadSchema = z.object({
1028
+ id: z.string(),
1029
+ /** Leading text of the message, truncated for display. */
1030
+ preview: z.string(),
1031
+ enqueuedAt: z.string(),
1032
+ attachmentCount: z.number().int().nonnegative().optional(),
1033
+ });
1034
+ /**
1035
+ * An agent's LIFETIME SPEND, kept as the real token split plus the provider's
1036
+ * own cost — the raw material for "what did this chat cost".
1037
+ *
1038
+ * Deliberately distinct from context-window occupancy (`agent.context.get_usage`
1039
+ * and `lastUsage.contextWindow*`), which answers "how full am I" and shares no
1040
+ * units with this. Conflating the two is why the numbers used to feel wrong; the
1041
+ * UI must never mix them. See docs/glossary.md.
1042
+ *
1043
+ * `costUsd` is only ever a provider's OWN reported cost, already de-inflated so
1044
+ * a parent never carries what its sub-agents reported. It is NEVER derived from
1045
+ * a $/M rate table — a rate keyed off a model id misprices a gateway serving
1046
+ * that model at its own prices (docs/subagent-accounting.md, pricing invariant).
1047
+ * `costCoverage` says how far it can be trusted, so a surface can show a floor
1048
+ * or an honest blank rather than a confident wrong figure.
1049
+ *
1050
+ * Declared above AgentSnapshotPayloadSchema — zod-aot emits schemas in source
1051
+ * order, so a forward reference is a build-time ReferenceError.
1052
+ */
1053
+ export const AgentCumulativeUsageSchema = z.object({
1054
+ inputTokens: z.number().optional(),
1055
+ cachedInputTokens: z.number().optional(),
1056
+ cacheCreationInputTokens: z.number().optional(),
1057
+ outputTokens: z.number().optional(),
1058
+ /** Provider-reported cost booked so far. Absent ⇒ nothing was priceable. */
1059
+ costUsd: z.number().optional(),
1060
+ /**
1061
+ * `complete` — every token-bearing turn was priced; `costUsd` is the total.
1062
+ * `partial` — some turns were unpriced; `costUsd` is a FLOOR, present it as
1063
+ * one. `none` — nothing priceable; show tokens and a blank, never an estimate.
1064
+ */
1065
+ costCoverage: z.enum(["complete", "partial", "none"]).optional(),
1066
+ });
922
1067
  export const AgentSnapshotPayloadSchema = z.object({
923
1068
  id: z.string(),
924
1069
  provider: AgentProviderSchema,
@@ -946,6 +1091,30 @@ export const AgentSnapshotPayloadSchema = z.object({
946
1091
  // additive; absent ⇒ no readout. Old clients ignore it.
947
1092
  // See docs/agent-lifecycle.md (Item 3).
948
1093
  cumulativeTokens: z.number().optional(),
1094
+ // The same lifetime spend as `cumulativeTokens`, but as the REAL split plus
1095
+ // the provider's own cost, so a chat total can be priced honestly instead of
1096
+ // flattened to one scalar and multiplied by a guessed rate. Its token leaves
1097
+ // sum to `cumulativeTokens`; a client that ignores this loses only the split
1098
+ // and the cost. Absent ⇒ the daemon predates the field or reported nothing.
1099
+ // COMPAT(cumulativeUsage): added in v0.7.0; gated on
1100
+ // server_info.features.cumulativeUsage, drop the gate when floor >= v0.7.0.
1101
+ // See docs/subagent-accounting.md (Chat totals).
1102
+ cumulativeUsage: AgentCumulativeUsageSchema.optional(),
1103
+ // Liveness signals for the sub-agents track: how much work this agent has done
1104
+ // (`toolUseCount`, cumulative and monotonic) and what it is doing right now
1105
+ // (`currentTool`, the latest tool name, cleared once the agent is terminal —
1106
+ // a finished agent isn't "running Bash"). Both are purely additive optional
1107
+ // leaves: a provider that can't report them leaves them absent and the row
1108
+ // omits the readout rather than showing a wrong value.
1109
+ // COMPAT(subagentLiveness): added in v0.6.7; old clients ignore both.
1110
+ // See docs/chat-lifecycle.md (the subagents track).
1111
+ toolUseCount: z.number().optional(),
1112
+ currentTool: z.string().optional(),
1113
+ // Messages parked for delivery as this agent's NEXT turn (delivery: "queue").
1114
+ // Daemon-owned and ephemeral — the composer's Queue track renders straight
1115
+ // from this. Absent/empty ⇒ nothing queued; old clients ignore it.
1116
+ // COMPAT(steerQueue): added in v0.6.8, drop the gate when floor >= v0.6.8.
1117
+ queuedMessages: z.array(QueuedAgentMessagePayloadSchema).optional(),
949
1118
  lastError: z.string().optional(),
950
1119
  title: z.string().nullable(),
951
1120
  labels: z.record(z.string(), z.string()).default({}),
@@ -1046,6 +1215,45 @@ export const CloseItemsRequestMessageSchema = z.object({
1046
1215
  terminalIds: z.array(z.string()).default([]),
1047
1216
  requestId: z.string(),
1048
1217
  });
1218
+ // ── History management ──────────────────────────────────────────────────────
1219
+ // Bulk counterpart to the existing flat `delete_agent_request`: hard-delete
1220
+ // every archived chat record at or past a cutoff in one server-side pass. It has
1221
+ // to be server-side because the history list is cursor-paginated across hosts,
1222
+ // so the client never holds the whole archived set.
1223
+ //
1224
+ // Deleting a chat removes OTTO's record only. The provider's own on-disk
1225
+ // transcript (Claude's `~/.claude/projects/**/<sessionId>.jsonl`, Codex threads,
1226
+ // OpenCode sessions) is deliberately left in place: Otto never created it,
1227
+ // another tool still reads it, and silently deleting another tool's state is not
1228
+ // ours to do. There is intentionally **no** opt-in flag for provider data on the
1229
+ // wire — the UI discloses what stays behind instead. See docs/chat-lifecycle.md.
1230
+ // Gated by server_info.features.historyDelete.
1231
+ export const HistoryAgentsClearArchivedRequestSchema = z.object({
1232
+ type: z.literal("history.agents.clear_archived.request"),
1233
+ // 0 = every archived chat. N = only chats archived at least N days ago.
1234
+ olderThanDays: z.number().int().min(0).default(0),
1235
+ // Safe by default: a request that omits the flag previews instead of deleting.
1236
+ // The client always sends it explicitly.
1237
+ dryRun: z.boolean().default(true),
1238
+ requestId: z.string(),
1239
+ });
1240
+ export const HistoryAgentsClearArchivedResponseSchema = z.object({
1241
+ type: z.literal("history.agents.clear_archived.response"),
1242
+ payload: z.object({
1243
+ // How many archived records the cutoff selected — the number the confirm
1244
+ // dialog quotes back after a dry run.
1245
+ matched: z.number().int().nonnegative(),
1246
+ deleted: z.number().int().nonnegative(),
1247
+ failed: z.number().int().nonnegative(),
1248
+ // Ids actually deleted, so the client drops exactly those rows from its
1249
+ // caches. Empty on a dry run. Unlike close_items_response, a destructive
1250
+ // batch reports per-item outcome rather than silently omitting failures.
1251
+ agentIds: z.array(z.string()),
1252
+ dryRun: z.boolean(),
1253
+ error: z.string().nullable(),
1254
+ requestId: z.string(),
1255
+ }),
1256
+ });
1049
1257
  export const UpdateAgentRequestMessageSchema = z.object({
1050
1258
  type: z.literal("update_agent_request"),
1051
1259
  agentId: z.string(),
@@ -1053,6 +1261,58 @@ export const UpdateAgentRequestMessageSchema = z.object({
1053
1261
  labels: z.record(z.string(), z.string()).optional(),
1054
1262
  requestId: z.string(),
1055
1263
  });
1264
+ // ── Attachment storage ──────────────────────────────────────────────────────
1265
+ // Agents produce image bytes continuously (browser screenshots above all), and
1266
+ // the daemon materializes each one to $OTTO_HOME/attachments so the timeline has
1267
+ // a file to point at. These two RPCs are the user's window into that store: how
1268
+ // much is there, and give it back. See docs/attachment-lifecycle.md.
1269
+ //
1270
+ // Scope is deliberately global, not per-chat or per-workspace. Filenames are a
1271
+ // content hash, so the same bytes may be referenced from several transcripts and
1272
+ // "this workspace's images" is a fiction we would have to invent and maintain.
1273
+ // Gated by server_info.features.attachmentStorage.
1274
+ export const AttachmentsImagesStatsRequestSchema = z.object({
1275
+ type: z.literal("attachments.images.get_stats.request"),
1276
+ requestId: z.string(),
1277
+ });
1278
+ export const AttachmentsImagesStatsResponseSchema = z.object({
1279
+ type: z.literal("attachments.images.get_stats.response"),
1280
+ payload: z.object({
1281
+ fileCount: z.number().int().nonnegative(),
1282
+ totalBytes: z.number().nonnegative(),
1283
+ // ISO timestamp of the oldest image, or null when the store is empty. The
1284
+ // readout quotes it so "512 MB" comes with "since March".
1285
+ oldestAt: z.string().nullable(),
1286
+ // The policy currently in force, so the settings row shows real numbers
1287
+ // rather than the client's idea of the defaults.
1288
+ maxAgeDays: z.number().int().nonnegative(),
1289
+ maxTotalMb: z.number().int().nonnegative(),
1290
+ error: z.string().nullable(),
1291
+ requestId: z.string(),
1292
+ }),
1293
+ });
1294
+ export const AttachmentsImagesClearRequestSchema = z.object({
1295
+ type: z.literal("attachments.images.clear.request"),
1296
+ // 0 = every stored image. N = only images untouched for at least N days.
1297
+ olderThanDays: z.number().int().min(0).default(0),
1298
+ // Safe by default: a request that omits the flag previews instead of deleting.
1299
+ // The client always sends it explicitly. Same contract as
1300
+ // history.agents.clear_archived, and for the same reason — the client cannot
1301
+ // enumerate the set, and there is no undo.
1302
+ dryRun: z.boolean().default(true),
1303
+ requestId: z.string(),
1304
+ });
1305
+ export const AttachmentsImagesClearResponseSchema = z.object({
1306
+ type: z.literal("attachments.images.clear.response"),
1307
+ payload: z.object({
1308
+ matched: z.number().int().nonnegative(),
1309
+ deleted: z.number().int().nonnegative(),
1310
+ freedBytes: z.number().nonnegative(),
1311
+ dryRun: z.boolean(),
1312
+ error: z.string().nullable(),
1313
+ requestId: z.string(),
1314
+ }),
1315
+ });
1056
1316
  export const ProjectRenameRequestSchema = z.object({
1057
1317
  type: z.literal("project.rename.request"),
1058
1318
  projectId: z.string(),
@@ -1326,6 +1586,47 @@ export const SendAgentMessageRequestSchema = z.object({
1326
1586
  messageId: z.string().optional(), // Client-provided ID for deduplication
1327
1587
  images: z.array(ImageAttachmentSchema).optional(),
1328
1588
  attachments: AgentAttachmentsSchema,
1589
+ // How to reach a BUSY agent. "interrupt" (default, and what every older
1590
+ // client means by omitting this) cancels the in-flight turn and runs this
1591
+ // instead; "queue" lets the turn finish and runs this as the next one.
1592
+ // Against an idle agent both run immediately.
1593
+ // COMPAT(steerQueue): added in v0.6.8, drop the gate when floor >= v0.6.8.
1594
+ delivery: z.enum(["interrupt", "queue"]).optional(),
1595
+ });
1596
+ /** Pull one message back out of an agent's queue (Queue-track edit / send now). */
1597
+ export const AgentQueueRemoveRequestMessageSchema = z.object({
1598
+ type: z.literal("agent.queue.remove.request"),
1599
+ requestId: z.string(),
1600
+ /** Accepts full ID, unique prefix, or exact full title (server resolves). */
1601
+ agentId: z.string(),
1602
+ /** The queued message's `id` from `AgentSnapshotPayload.queuedMessages`. */
1603
+ messageId: z.string(),
1604
+ });
1605
+ /**
1606
+ * Move one queued message to a different position in an agent's queue.
1607
+ *
1608
+ * Order is what the queue means, so this is the edit that changes the next
1609
+ * turn's content without changing the queue's membership. The daemon resolves
1610
+ * `messageId` fresh and clamps `toIndex`, so a client acting on a snapshot that
1611
+ * is one drain stale reorders what is actually there or reports `moved: false`.
1612
+ * COMPAT(steerQueueReorder): added in v0.6.9, drop the gate when floor >= v0.6.9.
1613
+ */
1614
+ export const AgentQueueReorderRequestMessageSchema = z.object({
1615
+ type: z.literal("agent.queue.reorder.request"),
1616
+ requestId: z.string(),
1617
+ /** Accepts full ID, unique prefix, or exact full title (server resolves). */
1618
+ agentId: z.string(),
1619
+ /** The queued message's `id` from `AgentSnapshotPayload.queuedMessages`. */
1620
+ messageId: z.string(),
1621
+ /** Zero-based destination, clamped to the queue's current length. */
1622
+ toIndex: z.number().int().nonnegative(),
1623
+ });
1624
+ /** Drop every message queued behind an agent's current turn. */
1625
+ export const AgentQueueClearRequestMessageSchema = z.object({
1626
+ type: z.literal("agent.queue.clear.request"),
1627
+ requestId: z.string(),
1628
+ /** Accepts full ID, unique prefix, or exact full title (server resolves). */
1629
+ agentId: z.string(),
1329
1630
  });
1330
1631
  export const WaitForFinishRequestSchema = z.object({
1331
1632
  type: z.literal("wait_for_finish_request"),
@@ -1731,6 +2032,13 @@ export const ImportAgentRequestMessageSchema = z.object({
1731
2032
  sessionId: z.string().optional(),
1732
2033
  providerHandleId: z.string().optional(),
1733
2034
  cwd: z.string().optional(),
2035
+ // The workspace the import was requested from. Present when the client has a
2036
+ // workspace context (a chat tab); absent from the home screen, where the
2037
+ // daemon resolves a workspace for the cwd instead.
2038
+ // COMPAT(importAgentWorkspaceId): added in v0.7.1, drop the optionality when
2039
+ // the floor is >= v0.7.1. An older client omits it and keeps the
2040
+ // resolve-by-directory behaviour.
2041
+ workspaceId: z.string().optional(),
1734
2042
  labels: z.record(z.string(), z.string()).optional(),
1735
2043
  requestId: z.string(),
1736
2044
  });
@@ -1926,7 +2234,7 @@ export const SuggestedTasksChangedSchema = z.object({
1926
2234
  }),
1927
2235
  });
1928
2236
  // Context Management — the daemon's accounting of everything a provider sends
1929
- // before the user types (see projects/context-management/context-management.md).
2237
+ // before the user types (see docs/context-management.md).
1930
2238
  //
1931
2239
  // Two distinctions carry the whole feature and must not be collapsed on the
1932
2240
  // wire: an `import` edge is inlined into the request while a `reference` edge
@@ -1957,6 +2265,16 @@ export const ContextCategorySchema = z.enum([
1957
2265
  export const ContextCostClassSchema = z.enum(["fixed", "conditional", "referenced"]);
1958
2266
  export const ContextSeveritySchema = z.enum(["ok", "notice", "warn", "critical"]);
1959
2267
  export const ContextConfidenceSchema = z.enum(["exact", "convention", "unverified"]);
2268
+ // Per-category disclosure of how well the daemon can see a provider's payload.
2269
+ // `not_visible` is the reason this exists: a CLI-backed provider composes its
2270
+ // own preset and hands MCP servers to a subprocess, so those categories are
2271
+ // unmeasurable rather than empty, and the row has to be able to say which.
2272
+ export const ContextCategoryVisibilitySchema = z.enum([
2273
+ "exact",
2274
+ "convention",
2275
+ "unverified",
2276
+ "not_visible",
2277
+ ]);
1960
2278
  export const ContextFindingKindSchema = z.enum([
1961
2279
  "dead_import",
1962
2280
  "dead_reference",
@@ -1977,8 +2295,8 @@ export const ContextFindingSchema = z.object({
1977
2295
  relatedNodeIds: z.array(z.string()).optional(),
1978
2296
  // The node this finding is about. Redundant while the finding sits on its
1979
2297
  // node, load-bearing once the report flattens them all into one list — that
1980
- // list is the "Worth fixing" tab, and without this a row cannot say where it
1981
- // came from or take you there.
2298
+ // list is the "Issues" tab, and without this a row cannot say where it came
2299
+ // from or take you there.
1982
2300
  nodeId: z.string().optional(),
1983
2301
  // 1-based line of `range.start` in that node's file, so the fix list can jump
1984
2302
  // the editor without the client re-reading bytes to count newlines.
@@ -1986,6 +2304,16 @@ export const ContextFindingSchema = z.object({
1986
2304
  // Last line of the range, so the client can select the whole offending span
1987
2305
  // rather than dropping a cursor at the top of it.
1988
2306
  lineEnd: z.number().optional(),
2307
+ // True for kinds a mechanical delete can resolve on its own (dead links, a
2308
+ // duplicate block) — false/absent for kinds that need judgment (which side
2309
+ // of an import cycle to cut, how to split an oversized entry). Computed
2310
+ // server-side, once, in `locateFinding` — the only place that knows the kind
2311
+ // vocabulary, so the fix-all button never has to guess.
2312
+ fixable: z.boolean().optional(),
2313
+ // The exact text at `range` when the file was scanned. `context.findings.fix`
2314
+ // verifies this still matches before deleting, the same staleness guard
2315
+ // `context.edge.convert` uses for `rawTarget`.
2316
+ snippet: z.string().optional(),
1989
2317
  });
1990
2318
  export const ContextNodeSchema = z.object({
1991
2319
  id: z.string(),
@@ -2016,6 +2344,10 @@ export const ContextCategoryTotalSchema = z.object({
2016
2344
  estTokens: z.number(),
2017
2345
  sharePercent: z.number(),
2018
2346
  severity: ContextSeveritySchema,
2347
+ // COMPAT(contextCategoryVisibility): added in v0.7.1, drop the optionality
2348
+ // when the floor is >= v0.7.1. An older client ignores the field and still
2349
+ // gets correct totals; a newer client seeing it absent renders no badge.
2350
+ visibility: ContextCategoryVisibilitySchema.optional(),
2019
2351
  });
2020
2352
  export const ContextReportSchema = z.object({
2021
2353
  workspaceId: z.string(),
@@ -2036,6 +2368,16 @@ export const ContextReportSchema = z.object({
2036
2368
  workingRoom: z.number(),
2037
2369
  aggregateSeverity: ContextSeveritySchema,
2038
2370
  findings: z.array(ContextFindingSchema),
2371
+ // Which personality this report was evaluated FOR. Context became
2372
+ // personality-specific once personalities accrue memory, so a report is only
2373
+ // interpretable alongside the identity it was measured against.
2374
+ // COMPAT(personalityMemory): additive; absent = the pre-memory, personality-agnostic report.
2375
+ personalityId: z.string().optional(),
2376
+ // That personality's injected memory brief, in tokens. Folded into the
2377
+ // `otto_injected` category total rather than a new category: ContextCategory
2378
+ // is a z.enum travelling daemon->client, so a new member would make a new
2379
+ // daemon's report unparseable by an older client.
2380
+ personalityMemoryTokens: z.number().optional(),
2039
2381
  });
2040
2382
  // Pushed with the full current report whenever a watched context file changes.
2041
2383
  // Full-report reconciliation, same idiom as suggested_tasks_changed.
@@ -2054,6 +2396,10 @@ export const ContextReportGetRequestMessageSchema = z.object({
2054
2396
  workspaceId: z.string(),
2055
2397
  provider: z.string().optional(),
2056
2398
  windowTokens: z.number().optional(),
2399
+ // "Evaluate as if this personality were running here": folds that
2400
+ // personality's injected memory brief into the report's fixed weight. Omitted
2401
+ // means the personality-agnostic report.
2402
+ personalityId: z.string().optional(),
2057
2403
  });
2058
2404
  export const ContextReportGetResponseMessageSchema = z.object({
2059
2405
  type: z.literal("context.report.get.response"),
@@ -2062,6 +2408,43 @@ export const ContextReportGetResponseMessageSchema = z.object({
2062
2408
  report: ContextReportSchema.nullable(),
2063
2409
  }),
2064
2410
  });
2411
+ // One readable block of the assembled prompt. `text` is absent exactly when
2412
+ // `visibility` is "not_visible" — the provider composes that part internally and
2413
+ // Otto has nothing to show, which the section states rather than hides.
2414
+ export const ContextPromptSectionSchema = z.object({
2415
+ category: ContextCategorySchema,
2416
+ label: z.string(),
2417
+ visibility: ContextCategoryVisibilitySchema,
2418
+ text: z.string().optional(),
2419
+ estTokens: z.number(),
2420
+ });
2421
+ export const ContextPromptPreviewSchema = z.object({
2422
+ sections: z.array(ContextPromptSectionSchema),
2423
+ estTokens: z.number(),
2424
+ });
2425
+ // Read-only by design: there is no matching write RPC. Editing happens per file
2426
+ // through the existing file pane, against the real file rather than a
2427
+ // concatenation of several.
2428
+ export const ContextPromptPreviewGetRequestMessageSchema = z.object({
2429
+ type: z.literal("context.prompt.preview.get.request"),
2430
+ requestId: z.string(),
2431
+ workspaceId: z.string(),
2432
+ provider: z.string().optional(),
2433
+ windowTokens: z.number().optional(),
2434
+ personalityId: z.string().optional(),
2435
+ // Assemble only this category. The tab reads one section at a time — the user
2436
+ // clicked a row in the tree — and assembling the rest would re-read every
2437
+ // context file on disk to build text nobody asked to see. Omitted means all,
2438
+ // which is what an older client sends.
2439
+ category: ContextCategorySchema.optional(),
2440
+ });
2441
+ export const ContextPromptPreviewGetResponseMessageSchema = z.object({
2442
+ type: z.literal("context.prompt.preview.get.response"),
2443
+ payload: z.object({
2444
+ requestId: z.string(),
2445
+ preview: ContextPromptPreviewSchema.nullable(),
2446
+ }),
2447
+ });
2065
2448
  // Converts one edge between "always loaded" and "link only". Server-side
2066
2449
  // because the parent file may live outside the workspace root.
2067
2450
  export const ContextEdgeConvertRequestMessageSchema = z.object({
@@ -2083,6 +2466,154 @@ export const ContextEdgeConvertResponseMessageSchema = z.object({
2083
2466
  error: z.string().optional(),
2084
2467
  }),
2085
2468
  });
2469
+ // Deletes every mechanically-fixable finding's range in one pass — the
2470
+ // "Fix all" button in the Issues tab. Each item names the file, the range the
2471
+ // scan flagged, and the snippet expected there; a file that changed since the
2472
+ // scan is skipped rather than corrupted (charter §7.5).
2473
+ export const ContextFindingsFixRequestMessageSchema = z.object({
2474
+ type: z.literal("context.findings.fix.request"),
2475
+ requestId: z.string(),
2476
+ workspaceId: z.string(),
2477
+ findings: z.array(z.object({
2478
+ filePath: z.string(),
2479
+ range: ContextRangeSchema,
2480
+ snippet: z.string(),
2481
+ })),
2482
+ });
2483
+ export const ContextFindingsFixResponseMessageSchema = z.object({
2484
+ type: z.literal("context.findings.fix.response"),
2485
+ payload: z.object({
2486
+ requestId: z.string(),
2487
+ fixedCount: z.number(),
2488
+ failedCount: z.number(),
2489
+ errors: z.array(z.string()),
2490
+ }),
2491
+ });
2492
+ // ---------------------------------------------------------------------------
2493
+ // Personality memory — the lessons a named personality accrues across sessions.
2494
+ // See docs/agent-personalities.md § Memory.
2495
+ // ---------------------------------------------------------------------------
2496
+ // Plain strings on the wire, like personality roles and effort levels, so the
2497
+ // daemon can grow the vocabulary without breaking old peers. Logical values:
2498
+ // scope "project" | "global"; source "agent" | "user" | "review" | "transfer".
2499
+ export const PersonalityMemoryEntrySchema = z
2500
+ .object({
2501
+ id: z.string(),
2502
+ text: z.string(),
2503
+ scope: z.string(),
2504
+ // Absolute, daemon-side. Present only on project-scoped entries.
2505
+ projectRoot: z.string().optional(),
2506
+ createdAt: z.string(),
2507
+ updatedAt: z.string(),
2508
+ source: z.string(),
2509
+ // How many times the lesson has been restated. Drives injection order and
2510
+ // is shown in the brief, because a repeatedly-relearned gotcha is stronger
2511
+ // evidence than a one-off observation.
2512
+ reinforcedCount: z.number().optional(),
2513
+ transferredFrom: z.string().optional(),
2514
+ })
2515
+ .passthrough();
2516
+ export const PersonalityMemoryListRequestMessageSchema = z.object({
2517
+ type: z.literal("personality.memory.list.request"),
2518
+ requestId: z.string(),
2519
+ personalityId: z.string(),
2520
+ // Which project's lessons count as in-scope for the returned brief. Prefer
2521
+ // `workspaceId` and let the daemon resolve the root: a client computing repo
2522
+ // roots would disagree with the daemon the moment a worktree is involved.
2523
+ workspaceId: z.string().optional(),
2524
+ // Explicit root, for callers with no workspace. Ignored when `workspaceId`
2525
+ // resolves. Omitted (with no workspace) means global lessons only.
2526
+ projectRoot: z.string().optional(),
2527
+ });
2528
+ export const PersonalityMemoryListResponseMessageSchema = z.object({
2529
+ type: z.literal("personality.memory.list.response"),
2530
+ payload: z.object({
2531
+ requestId: z.string(),
2532
+ personalityId: z.string(),
2533
+ personalityName: z.string(),
2534
+ /** Whether this personality is accruing (the `memoryEnabled` switch). */
2535
+ enabled: z.boolean(),
2536
+ /** Every stored entry, including other projects' — the UI shows them all. */
2537
+ entries: z.array(PersonalityMemoryEntrySchema),
2538
+ // The EXACT text the daemon would inject for `projectRoot`, not a
2539
+ // reconstruction. Memory is only trustworthy if it is inspectable, and the
2540
+ // only way the shown text cannot drift from the injected text is for both
2541
+ // to come from one composer.
2542
+ brief: z.string(),
2543
+ briefTokens: z.number(),
2544
+ /** Entries the injection budget cut, so the UI can say so. */
2545
+ briefOmittedCount: z.number().optional(),
2546
+ // The root the brief was composed for, so the UI can tell a project-scoped
2547
+ // entry that applies here from one belonging to another project. Without it
2548
+ // every project entry looks the same and an empty brief next to a list of
2549
+ // lessons reads as a bug. Absent when the request named no workspace.
2550
+ projectRoot: z.string().optional(),
2551
+ }),
2552
+ });
2553
+ // One write RPC covers add / edit / delete: no `entryId` = add a new lesson,
2554
+ // `drop: true` = forget one. The user-facing editing path from Context
2555
+ // Management (charter §2.4).
2556
+ export const PersonalityMemoryUpdateRequestMessageSchema = z.object({
2557
+ type: z.literal("personality.memory.update.request"),
2558
+ requestId: z.string(),
2559
+ personalityId: z.string(),
2560
+ entryId: z.string().optional(),
2561
+ text: z.string().optional(),
2562
+ scope: z.string().optional(),
2563
+ // Which project a `scope: "project"` write binds to. Same rule as the list
2564
+ // request: prefer `workspaceId` and let the daemon resolve the root, because a
2565
+ // project-scoped entry whose root does not match the daemon's resolution is
2566
+ // filtered out of every brief and is therefore stored but never sent.
2567
+ workspaceId: z.string().optional(),
2568
+ // Explicit root, for callers with no workspace. Ignored when `workspaceId`
2569
+ // resolves.
2570
+ projectRoot: z.string().optional(),
2571
+ drop: z.boolean().optional(),
2572
+ });
2573
+ export const PersonalityMemoryUpdateResponseMessageSchema = z.object({
2574
+ type: z.literal("personality.memory.update.response"),
2575
+ payload: z.object({
2576
+ requestId: z.string(),
2577
+ ok: z.boolean(),
2578
+ error: z.string().optional(),
2579
+ }),
2580
+ });
2581
+ // Deleting a personality must never silently destroy what it learned, so the
2582
+ // delete flow resolves here first: `mode: "transfer"` moves the lessons to
2583
+ // `toPersonalityId` (merging near-duplicates), `mode: "delete"` discards them.
2584
+ export const PersonalityMemoryTransferRequestMessageSchema = z.object({
2585
+ type: z.literal("personality.memory.transfer.request"),
2586
+ requestId: z.string(),
2587
+ fromPersonalityId: z.string(),
2588
+ toPersonalityId: z.string().optional(),
2589
+ mode: z.string(),
2590
+ });
2591
+ export const PersonalityMemoryTransferResponseMessageSchema = z.object({
2592
+ type: z.literal("personality.memory.transfer.response"),
2593
+ payload: z.object({
2594
+ requestId: z.string(),
2595
+ ok: z.boolean(),
2596
+ /** Entries that landed as new rows in the destination. */
2597
+ transferred: z.number().optional(),
2598
+ /** Entries that merged into a lesson the destination already knew. */
2599
+ merged: z.number().optional(),
2600
+ error: z.string().optional(),
2601
+ }),
2602
+ });
2603
+ // Per-personality lesson counts. Its own RPC over its own file, mirroring
2604
+ // agentPersonalities.get_stats — counts must not ride the daemon-config
2605
+ // broadcast, or every recorded lesson would fan a config change to every client.
2606
+ export const PersonalityMemoryStatsRequestMessageSchema = z.object({
2607
+ type: z.literal("personality.memory.stats.request"),
2608
+ requestId: z.string(),
2609
+ });
2610
+ export const PersonalityMemoryStatsResponseMessageSchema = z.object({
2611
+ type: z.literal("personality.memory.stats.response"),
2612
+ payload: z.object({
2613
+ requestId: z.string(),
2614
+ counts: z.record(z.string(), z.number()),
2615
+ }),
2616
+ });
2086
2617
  // Aggregate outcome for a start/dismiss over one or more tasks. `succeeded`/
2087
2618
  // `failed` count the tasks acted on so the client can report "Started 3 tasks";
2088
2619
  // `error` collects any per-task failure messages (the failed tasks' chips stay).
@@ -2397,8 +2928,27 @@ export const RunsClearResponseSchema = z.object({
2397
2928
  requestId: z.string(),
2398
2929
  }),
2399
2930
  });
2931
+ // Delete one run by id. Terminal (done/failed/canceled) and draft runs only —
2932
+ // deleting an active run is refused so a cleanup click can't silently orphan
2933
+ // running agents; cancel it first. Gated by server_info.features.runsDelete.
2934
+ export const RunsDeleteRequestSchema = z.object({
2935
+ type: z.literal("runs.delete.request"),
2936
+ requestId: z.string(),
2937
+ runId: z.string(),
2938
+ });
2939
+ export const RunsDeleteResponseSchema = z.object({
2940
+ type: z.literal("runs.delete.response"),
2941
+ payload: z.object({
2942
+ // The deleted id, or absent when nothing was deleted (unknown or still
2943
+ // active) — `error` then carries why.
2944
+ runId: z.string().optional(),
2945
+ error: z.string().optional(),
2946
+ requestId: z.string(),
2947
+ }),
2948
+ });
2400
2949
  // Broadcast to every connected client (including the requester) so all
2401
2950
  // caches drop the same runs, mirroring runs.updated.notification's upsert.
2951
+ // Serves both runs.clear (many ids) and runs.delete (one).
2402
2952
  export const RunsClearedNotificationSchema = z.object({
2403
2953
  type: z.literal("runs.cleared.notification"),
2404
2954
  payload: z.object({
@@ -2457,12 +3007,60 @@ export const RunsGraphsChangedNotificationSchema = z.object({
2457
3007
  graphs: z.array(OrchestrationGraphSchema),
2458
3008
  }),
2459
3009
  });
3010
+ // ── Prompt templates ────────────────────────────────────────────────────────
3011
+ // Host-level reusable prompts and snippets a graph node can bind to. Same shape
3012
+ // as the graph trio above, for the same reason: one store, list/save/delete,
3013
+ // plus a full-list push so every client converges.
3014
+ export const RunsTemplatesListRequestSchema = z.object({
3015
+ type: z.literal("runs.templates.list.request"),
3016
+ requestId: z.string(),
3017
+ });
3018
+ export const RunsTemplatesListResponseSchema = z.object({
3019
+ type: z.literal("runs.templates.list.response"),
3020
+ payload: z.object({
3021
+ templates: z.array(PromptTemplateSchema),
3022
+ requestId: z.string(),
3023
+ }),
3024
+ });
3025
+ export const RunsTemplatesSaveRequestSchema = z.object({
3026
+ type: z.literal("runs.templates.save.request"),
3027
+ template: PromptTemplateSchema,
3028
+ requestId: z.string(),
3029
+ });
3030
+ export const RunsTemplatesSaveResponseSchema = z.object({
3031
+ type: z.literal("runs.templates.save.response"),
3032
+ payload: z.object({
3033
+ template: PromptTemplateSchema.optional(),
3034
+ error: z.string().optional(),
3035
+ requestId: z.string(),
3036
+ }),
3037
+ });
3038
+ export const RunsTemplatesDeleteRequestSchema = z.object({
3039
+ type: z.literal("runs.templates.delete.request"),
3040
+ templateId: z.string(),
3041
+ requestId: z.string(),
3042
+ });
3043
+ export const RunsTemplatesDeleteResponseSchema = z.object({
3044
+ type: z.literal("runs.templates.delete.response"),
3045
+ payload: z.object({
3046
+ deleted: z.boolean(),
3047
+ error: z.string().optional(),
3048
+ requestId: z.string(),
3049
+ }),
3050
+ });
3051
+ export const RunsTemplatesChangedNotificationSchema = z.object({
3052
+ type: z.literal("runs.templates.changed.notification"),
3053
+ payload: z.object({
3054
+ templates: z.array(PromptTemplateSchema),
3055
+ }),
3056
+ });
2460
3057
  // Start (or draft) a user-initiated orchestration from the New Orchestration
2461
3058
  // dialog. `flavor` is an open vocabulary: "ai" (prompt-and-go — the daemon
2462
3059
  // spawns an orchestrator agent that declares its own plan via start_run) or
2463
3060
  // "graph" (deterministic — the daemon executes `graphId` with `graphInputs`).
2464
3061
  // `draft: true` creates the record without executing (the designer flow);
2465
- // `runId` executes an existing draft in place.
3062
+ // `runId` executes an existing draft in place — or, with `draft: true`, re-saves
3063
+ // that draft in place (Edit Orchestration).
2466
3064
  export const RunsStartRequestSchema = z.object({
2467
3065
  type: z.literal("runs.start.request"),
2468
3066
  flavor: z.string(),
@@ -2857,6 +3455,96 @@ export const ProjectAddRequestSchema = z.object({
2857
3455
  cwd: z.string(),
2858
3456
  requestId: z.string(),
2859
3457
  });
3458
+ // ── New project scaffolding ──────────────────────────────────────────────
3459
+ // project.add takes a directory that already exists. Scaffolding is the other
3460
+ // half: create the directory, optionally give it a git repo (fresh, or cloned
3461
+ // from a remote), optionally create that remote on a connected hosting
3462
+ // provider, then register the result as a project. One RPC rather than a
3463
+ // client-driven sequence so a half-finished project can never be left behind by
3464
+ // a dropped socket — the daemon owns the whole transaction.
3465
+ // COMPAT(projectScaffold): added in v0.6.9. Gated by server_info.features.projectScaffold.
3466
+ // Built-in .gitignore starters. Deliberately a short list of the ecosystems Otto
3467
+ // itself works in — this is a convenience, not a mirror of github/gitignore. The
3468
+ // wire field stays an open string so a newer daemon can add one without an older
3469
+ // client's validator rejecting the message.
3470
+ export const PROJECT_SCAFFOLD_GITIGNORE_TEMPLATE_IDS = [
3471
+ "node",
3472
+ "python",
3473
+ "go",
3474
+ "rust",
3475
+ "java",
3476
+ "dotnet",
3477
+ ];
3478
+ // Where the working tree comes from.
3479
+ export const ProjectScaffoldGitSchema = z.discriminatedUnion("kind", [
3480
+ // A plain directory. No repository is created.
3481
+ z.object({ kind: z.literal("none") }),
3482
+ z.object({
3483
+ kind: z.literal("init"),
3484
+ // Branch the fresh repo starts on. Absent means the daemon's git default.
3485
+ initialBranch: z.string().optional(),
3486
+ // Starter files written before the first commit.
3487
+ addReadme: z.boolean().optional(),
3488
+ // Identifier of a built-in .gitignore template (see PROJECT_SCAFFOLD_GITIGNORE_TEMPLATE_IDS).
3489
+ gitignoreTemplate: z.string().optional(),
3490
+ // Commit whatever starter files were written. Required for a push.
3491
+ initialCommit: z.boolean().optional(),
3492
+ // Create the repository on a hosting provider, wire it as `origin`, and push.
3493
+ // Requires the provider's createRepository capability.
3494
+ remote: z
3495
+ .object({
3496
+ providerId: GitHostingProviderIdWireSchema,
3497
+ // Account/organization/workspace to create under. Null means the
3498
+ // provider's default for the authenticated identity.
3499
+ owner: z.string().nullable(),
3500
+ name: z.string(),
3501
+ description: z.string().optional(),
3502
+ visibility: z.enum(["private", "public"]),
3503
+ })
3504
+ .optional(),
3505
+ }),
3506
+ z.object({
3507
+ kind: z.literal("clone"),
3508
+ // Any URL `git clone` accepts (https, ssh, scp-style).
3509
+ url: z.string(),
3510
+ }),
3511
+ ]);
3512
+ export const ProjectScaffoldRequestSchema = z.object({
3513
+ type: z.literal("project.scaffold.request"),
3514
+ requestId: z.string(),
3515
+ // Existing directory that will contain the new project folder.
3516
+ parentDirectory: z.string(),
3517
+ // Single path segment created inside parentDirectory. For a clone this may be
3518
+ // omitted, in which case the daemon derives it from the repository URL.
3519
+ folderName: z.string().optional(),
3520
+ git: ProjectScaffoldGitSchema,
3521
+ });
3522
+ // Steps are reported in the order the daemon runs them. New daemons may add
3523
+ // steps, so the wire form stays an open string and clients label unknown ids
3524
+ // generically instead of dropping the progress message.
3525
+ export const ProjectScaffoldStepIdSchema = z.string();
3526
+ export const ProjectScaffoldStepStatusSchema = z.enum(["running", "done", "skipped", "failed"]);
3527
+ export const ProjectScaffoldStepSchema = z.object({
3528
+ id: ProjectScaffoldStepIdSchema,
3529
+ status: ProjectScaffoldStepStatusSchema,
3530
+ detail: z.string().nullable(),
3531
+ });
3532
+ // Repository enumeration for the clone picker, and owner enumeration for the
3533
+ // "create a new remote" form. Both are host-level (no repo cwd exists yet), so
3534
+ // they address a provider directly instead of resolving one from a checkout.
3535
+ export const HostingListRepositoriesRequestSchema = z.object({
3536
+ type: z.literal("hosting.list_repositories.request"),
3537
+ provider: GitHostingProviderIdWireSchema,
3538
+ // Substring filter applied by the provider where it supports one.
3539
+ query: z.string().optional(),
3540
+ limit: z.number().int().min(1).max(100).optional(),
3541
+ requestId: z.string(),
3542
+ });
3543
+ export const HostingListOwnersRequestSchema = z.object({
3544
+ type: z.literal("hosting.list_owners.request"),
3545
+ provider: GitHostingProviderIdWireSchema,
3546
+ requestId: z.string(),
3547
+ });
2860
3548
  export const ArchiveWorkspaceRequestSchema = z.object({
2861
3549
  type: z.literal("archive_workspace_request"),
2862
3550
  workspaceId: z.string(),
@@ -2879,6 +3567,18 @@ export const WorkspaceArchivePreflightRequestSchema = z.object({
2879
3567
  requestId: z.string(),
2880
3568
  workspaceId: z.string(),
2881
3569
  });
3570
+ // Repoint a worktree-backed workspace's base branch — what the Changes view diffs
3571
+ // against, and what merge-into-base and PR creation target. On a stacked branch the
3572
+ // useful base is the parent branch, not the repo default, the same way a forge PR
3573
+ // carries an explicit base. A null baseRef resets to the repository default branch.
3574
+ // COMPAT(worktreeDiffBase): added in v0.6.8.
3575
+ export const WorktreeBaseRefSetRequestSchema = z.object({
3576
+ type: z.literal("worktree.baseRef.set.request"),
3577
+ requestId: z.string(),
3578
+ workspaceId: z.string(),
3579
+ // Branch name, with or without an `origin/` prefix; null resets to the default branch.
3580
+ baseRef: z.string().nullable(),
3581
+ });
2882
3582
  // Create a new workspace record. Unlike open_project, this never deduplicates by
2883
3583
  // directory: it always produces a fresh workspace. The source discriminates
2884
3584
  // between an existing local directory and a newly created otto worktree.
@@ -2980,6 +3680,8 @@ const FileExplorerEntrySchema = z.object({
2980
3680
  modifiedAt: z.string(),
2981
3681
  });
2982
3682
  export const FileEolSchema = z.enum(["lf", "crlf"]);
3683
+ /** What a directory entry is. Shared by the file-mutation RPCs below. */
3684
+ export const FileEntryKindSchema = z.enum(["file", "directory"]);
2983
3685
  const FileExplorerFileSchema = z.object({
2984
3686
  path: z.string(),
2985
3687
  kind: z.enum(["text", "image", "binary"]),
@@ -3045,6 +3747,96 @@ export const FileWriteRequestSchema = z.object({
3045
3747
  eol: FileEolSchema.optional(),
3046
3748
  requestId: z.string(),
3047
3749
  });
3750
+ /**
3751
+ * The general file-mutation surface: create, delete, rename/move.
3752
+ *
3753
+ * Deliberately separate from `file.write.request`, which is the text editor's
3754
+ * conditional *content* save. These three change what exists in the directory
3755
+ * rather than what is inside a file, and unlike `file.write` they are
3756
+ * **workspace-bounded**: the daemon refuses a `cwd` outside every known Otto
3757
+ * workspace, the way directory listing already does. Editing a stray file the
3758
+ * user opened by path is one thing; unlinking one is another.
3759
+ *
3760
+ * `path` is workspace-relative throughout, matching every other file RPC.
3761
+ */
3762
+ export const FileCreateRequestSchema = z.object({
3763
+ type: z.literal("file.create.request"),
3764
+ cwd: z.string(),
3765
+ path: z.string(),
3766
+ kind: FileEntryKindSchema,
3767
+ requestId: z.string(),
3768
+ });
3769
+ /**
3770
+ * Permanent delete — an unlink, not a move to the OS trash. The daemon may be
3771
+ * headless, remote, or inside WSL, where there is no reliable trash to move to;
3772
+ * a "deleted" file that silently stayed on disk in one environment and vanished
3773
+ * in another would be worse than either. The client's confirmation says so.
3774
+ */
3775
+ export const FileDeleteRequestSchema = z.object({
3776
+ type: z.literal("file.delete.request"),
3777
+ cwd: z.string(),
3778
+ path: z.string(),
3779
+ // Required to delete a directory that has children. Absent (the default) a
3780
+ // non-empty directory comes back as `not_empty` and nothing is removed, so a
3781
+ // client that never asks can never recursively wipe a tree by accident.
3782
+ recursive: z.boolean().optional(),
3783
+ requestId: z.string(),
3784
+ });
3785
+ /**
3786
+ * Rename and move are the same operation — a move is a rename whose new path
3787
+ * has a different parent. Never clobbers: an occupied destination comes back as
3788
+ * `exists` and nothing moves. There is no overwrite flag on purpose, so this
3789
+ * RPC cannot destroy a file the user did not name.
3790
+ */
3791
+ export const FileRenameRequestSchema = z.object({
3792
+ type: z.literal("file.rename.request"),
3793
+ cwd: z.string(),
3794
+ path: z.string(),
3795
+ newPath: z.string(),
3796
+ requestId: z.string(),
3797
+ });
3798
+ /**
3799
+ * Refine — an AI rewrite the user reviews as a diff before anything is written.
3800
+ * This RPC only *proposes*: it reads nothing from disk and writes nothing. The
3801
+ * accepted result goes back through `file.write.request` like any other save,
3802
+ * so the conditional-write precondition still guards it.
3803
+ *
3804
+ * `base` travels from the client rather than being re-read on the daemon, so
3805
+ * the model rewrites exactly the document the user is looking at and the diff
3806
+ * they review is the diff of what they saw.
3807
+ */
3808
+ /**
3809
+ * One document in a refine request. `id` is opaque and client-minted; the model
3810
+ * never sees it and the daemon only echoes it back. That is deliberate: the
3811
+ * client maps id -> absolute path itself, so a model that mangles or invents a
3812
+ * filename cannot misroute a write. `label` is the only path-ish thing the
3813
+ * model sees, and it exists purely so the prompt can say which file is which.
3814
+ */
3815
+ export const FileRefineDocumentSchema = z.object({
3816
+ id: z.string().min(1),
3817
+ label: z.string(),
3818
+ content: z.string(),
3819
+ });
3820
+ /** A file the model may read for context but must never rewrite. */
3821
+ export const FileRefineReferenceSchema = z.object({
3822
+ label: z.string(),
3823
+ content: z.string(),
3824
+ });
3825
+ export const FileRefineRequestSchema = z.object({
3826
+ type: z.literal("file.refine.request"),
3827
+ // Provider resolution only — which workspace's mini-task chain runs this.
3828
+ // Documents are NOT read from disk here; they travel on the wire.
3829
+ cwd: z.string(),
3830
+ // What the model may rewrite. The blast radius of the whole request: a file
3831
+ // absent from this list cannot be changed, whatever the model returns.
3832
+ documents: z.array(FileRefineDocumentSchema).min(1),
3833
+ // What it may read to understand the first list. Optional so an old client
3834
+ // that only sends documents still parses.
3835
+ references: z.array(FileRefineReferenceSchema).optional(),
3836
+ // The user's plain-language instruction, possibly seeded from a preset.
3837
+ instruction: z.string(),
3838
+ requestId: z.string(),
3839
+ });
3048
3840
  // Subscriptions exist only for paths open in tabs; the daemon cleans them up
3049
3841
  // when the session ends.
3050
3842
  export const FileWatchSubscribeRequestSchema = z.object({
@@ -3079,48 +3871,217 @@ export const CodeOutlineRequestSchema = z.object({
3079
3871
  requestId: z.string(),
3080
3872
  });
3081
3873
  /**
3082
- * Project-wide search ("Find in Files" semantics: explicit search, not
3083
- * per-keystroke). Results stream as file.search.result events correlated by
3084
- * searchId (= this requestId); a new search from the same session supersedes
3085
- * any in-flight one.
3874
+ * LSP-backed code intelligence (projects/lsp-code-intelligence). Distinct from the
3875
+ * ctags `code.symbols` RPC above in the only way that matters: it carries a
3876
+ * **position**, so the daemon can resolve the reference under the cursor instead of
3877
+ * matching a name.
3878
+ *
3879
+ * Line and column are **1-based** here, matching `CodeSymbolLocation` and the rest of
3880
+ * Otto. LSP itself is 0-based; that conversion is the daemon's business and does not
3881
+ * reach the wire.
3086
3882
  */
3087
- export const FileSearchRequestSchema = z.object({
3088
- type: z.literal("file.search.request"),
3883
+ export const CodeDefinitionRequestSchema = z.object({
3884
+ type: z.literal("code.definition.request"),
3089
3885
  cwd: z.string(),
3090
- query: z.string(),
3091
- caseSensitive: z.boolean().optional(),
3092
- wholeWord: z.boolean().optional(),
3093
- regexp: z.boolean().optional(),
3094
- include: z.string().optional(),
3095
- exclude: z.string().optional(),
3096
- requestId: z.string(),
3097
- });
3098
- const FileReplaceMatchSchema = z.object({
3099
- /** 1-based line number. */
3886
+ path: z.string(),
3100
3887
  line: z.number().int().positive(),
3101
- /** 1-based character column of the match start. */
3102
3888
  column: z.number().int().positive(),
3103
- /** Match length in characters. */
3104
- length: z.number().int().nonnegative(),
3889
+ requestId: z.string(),
3105
3890
  });
3106
3891
  /**
3107
- * Preview-first project replace. Each file carries the hash the preview was
3108
- * built against files changed since are skipped and reported, never
3109
- * corrupted. The replacement string is literal (no capture references in v1).
3892
+ * The editor's current buffer text, so definitions resolve against unsaved edits
3893
+ * rather than stale disk content. Sent debounced, not per keystroke.
3110
3894
  */
3111
- export const FileReplaceRequestSchema = z.object({
3112
- type: z.literal("file.replace.request"),
3895
+ export const CodeDocumentSyncRequestSchema = z.object({
3896
+ type: z.literal("code.document.sync.request"),
3113
3897
  cwd: z.string(),
3114
- replacement: z.string(),
3115
- files: z.array(z.object({
3116
- path: z.string(),
3117
- expectedHash: z.string(),
3118
- matches: z.array(FileReplaceMatchSchema),
3119
- })),
3898
+ path: z.string(),
3899
+ text: z.string(),
3120
3900
  requestId: z.string(),
3121
3901
  });
3122
- export const ClearAgentAttentionMessageSchema = z.object({
3123
- type: z.literal("clear_agent_attention"),
3902
+ export const CodeDocumentCloseRequestSchema = z.object({
3903
+ type: z.literal("code.document.close.request"),
3904
+ cwd: z.string(),
3905
+ path: z.string(),
3906
+ requestId: z.string(),
3907
+ });
3908
+ /**
3909
+ * The rest of the position-based code-intelligence family. All three carry a 1-based
3910
+ * position like `code.definition`, and all three are answered against the mirrored
3911
+ * buffer rather than the file on disk.
3912
+ */
3913
+ export const CodeHoverRequestSchema = z.object({
3914
+ type: z.literal("code.hover.request"),
3915
+ cwd: z.string(),
3916
+ path: z.string(),
3917
+ line: z.number().int().positive(),
3918
+ column: z.number().int().positive(),
3919
+ requestId: z.string(),
3920
+ });
3921
+ export const CodeReferencesRequestSchema = z.object({
3922
+ type: z.literal("code.references.request"),
3923
+ cwd: z.string(),
3924
+ path: z.string(),
3925
+ line: z.number().int().positive(),
3926
+ column: z.number().int().positive(),
3927
+ requestId: z.string(),
3928
+ });
3929
+ /**
3930
+ * A rename **dry run**. Deliberately not "do the rename": the daemon computes every edit
3931
+ * and returns them for the user to audit, because a rename's blast radius is the whole
3932
+ * project. Nothing is written by this request.
3933
+ */
3934
+ export const CodeRenamePreviewRequestSchema = z.object({
3935
+ type: z.literal("code.rename.preview.request"),
3936
+ cwd: z.string(),
3937
+ path: z.string(),
3938
+ line: z.number().int().positive(),
3939
+ column: z.number().int().positive(),
3940
+ newName: z.string().min(1),
3941
+ requestId: z.string(),
3942
+ });
3943
+ /**
3944
+ * Execute a rename the user has audited. **The edits are deliberately NOT on this request.**
3945
+ *
3946
+ * The client sends back only the `planId` it was shown; the daemon recomputes the plan and
3947
+ * refuses unless the identity matches. A request that carried its own edit list would be a
3948
+ * remote arbitrary-write primitive wearing a rename's name — any client could post any text
3949
+ * at any path. This shape makes the daemon's own language server the sole author of what
3950
+ * gets written, and the plan id the proof that the user saw it.
3951
+ */
3952
+ export const CodeRenameApplyRequestSchema = z.object({
3953
+ type: z.literal("code.rename.apply.request"),
3954
+ cwd: z.string(),
3955
+ path: z.string(),
3956
+ line: z.number().int().positive(),
3957
+ column: z.number().int().positive(),
3958
+ newName: z.string().min(1),
3959
+ /** From the preview response. Identity of the exact plan the user approved. */
3960
+ planId: z.string().min(1),
3961
+ requestId: z.string(),
3962
+ });
3963
+ /**
3964
+ * Undo a run. Carries only the run's id — the daemon holds the before-images.
3965
+ *
3966
+ * Declared here, with the other inbound rename schemas, rather than beside its response
3967
+ * further down: `SessionInboundMessageSchema` is a top-level const, so a schema it names
3968
+ * must already be initialized when that line runs. Below the union it is a
3969
+ * ReferenceError at import time, not a type error.
3970
+ */
3971
+ export const CodeRenameUndoRequestSchema = z.object({
3972
+ type: z.literal("code.rename.undo.request"),
3973
+ cwd: z.string(),
3974
+ runId: z.string().min(1),
3975
+ requestId: z.string(),
3976
+ });
3977
+ /**
3978
+ * Live language-server state for the Daemon → Code screen. Separate from the daemon
3979
+ * config RPCs because none of it is configuration: which servers this machine can
3980
+ * actually supply, and which are running right now.
3981
+ *
3982
+ * `cwd` scopes the availability check — a server can be present in one workspace's
3983
+ * `node_modules` and absent in another's.
3984
+ */
3985
+ export const LspServersListRequestSchema = z.object({
3986
+ type: z.literal("lsp.servers.list.request"),
3987
+ cwd: z.string(),
3988
+ requestId: z.string(),
3989
+ });
3990
+ /** Stop one running server, so a user who suspects it of hogging memory can kill it. */
3991
+ export const LspServerStopRequestSchema = z.object({
3992
+ type: z.literal("lsp.server.stop.request"),
3993
+ rootPath: z.string(),
3994
+ serverId: z.string(),
3995
+ requestId: z.string(),
3996
+ });
3997
+ /**
3998
+ * The Solution view (projects/solution-view). A second lens on the Files module showing the tree
3999
+ * as the build system sees it rather than as the filesystem lays it out.
4000
+ *
4001
+ * **Independent of the LSP family above, despite sharing the `code.` domain.** There is no
4002
+ * project-structure request in the Language Server Protocol — not one Otto has yet to wire, one
4003
+ * that does not exist — so this subsystem builds its own model through Microsoft's solution
4004
+ * libraries. Turning C# code intelligence off does not turn this off, and vice versa.
4005
+ *
4006
+ * Discovery is separate from loading on purpose: `list` decides whether the switcher appears at
4007
+ * all, so it runs for every workspace and must stay cheap (a directory walk, no process). Only
4008
+ * `get_tree` reaches the .NET sidecar.
4009
+ *
4010
+ * COMPAT(solutionView): added in v0.6.8; gate lives in features.solutionView.
4011
+ */
4012
+ export const CodeSolutionListRequestSchema = z.object({
4013
+ type: z.literal("code.solution.list.request"),
4014
+ cwd: z.string(),
4015
+ requestId: z.string(),
4016
+ });
4017
+ /**
4018
+ * One solution's organisation: folders, the projects inside them, and the configurations. No file
4019
+ * membership — that is `load_project`, paid per project on expand, because evaluating fifty
4020
+ * projects to render a collapsed tree is the cost this design exists to avoid.
4021
+ */
4022
+ export const CodeSolutionGetTreeRequestSchema = z.object({
4023
+ type: z.literal("code.solution.get_tree.request"),
4024
+ cwd: z.string(),
4025
+ /** Workspace-relative, as reported by `list`. */
4026
+ solutionPath: z.string(),
4027
+ requestId: z.string(),
4028
+ });
4029
+ /**
4030
+ * One project's evaluated file membership. `solutionPath` scopes the sidecar instance so two
4031
+ * solutions in one repo never share a warm `ProjectCollection` — and so Phase 4 has the selection
4032
+ * it needs for `--solution`.
4033
+ */
4034
+ export const CodeSolutionLoadProjectRequestSchema = z.object({
4035
+ type: z.literal("code.solution.load_project.request"),
4036
+ cwd: z.string(),
4037
+ solutionPath: z.string(),
4038
+ /** Workspace-relative, or absolute when the solution names a project outside the workspace. */
4039
+ projectPath: z.string(),
4040
+ requestId: z.string(),
4041
+ });
4042
+ /**
4043
+ * Project-wide search ("Find in Files" semantics: explicit search, not
4044
+ * per-keystroke). Results stream as file.search.result events correlated by
4045
+ * searchId (= this requestId); a new search from the same session supersedes
4046
+ * any in-flight one.
4047
+ */
4048
+ export const FileSearchRequestSchema = z.object({
4049
+ type: z.literal("file.search.request"),
4050
+ cwd: z.string(),
4051
+ query: z.string(),
4052
+ caseSensitive: z.boolean().optional(),
4053
+ wholeWord: z.boolean().optional(),
4054
+ regexp: z.boolean().optional(),
4055
+ include: z.string().optional(),
4056
+ exclude: z.string().optional(),
4057
+ requestId: z.string(),
4058
+ });
4059
+ const FileReplaceMatchSchema = z.object({
4060
+ /** 1-based line number. */
4061
+ line: z.number().int().positive(),
4062
+ /** 1-based character column of the match start. */
4063
+ column: z.number().int().positive(),
4064
+ /** Match length in characters. */
4065
+ length: z.number().int().nonnegative(),
4066
+ });
4067
+ /**
4068
+ * Preview-first project replace. Each file carries the hash the preview was
4069
+ * built against — files changed since are skipped and reported, never
4070
+ * corrupted. The replacement string is literal (no capture references in v1).
4071
+ */
4072
+ export const FileReplaceRequestSchema = z.object({
4073
+ type: z.literal("file.replace.request"),
4074
+ cwd: z.string(),
4075
+ replacement: z.string(),
4076
+ files: z.array(z.object({
4077
+ path: z.string(),
4078
+ expectedHash: z.string(),
4079
+ matches: z.array(FileReplaceMatchSchema),
4080
+ })),
4081
+ requestId: z.string(),
4082
+ });
4083
+ export const ClearAgentAttentionMessageSchema = z.object({
4084
+ type: z.literal("clear_agent_attention"),
3124
4085
  agentId: z.union([z.string(), z.array(z.string())]),
3125
4086
  requestId: z.string().optional(),
3126
4087
  });
@@ -3266,6 +4227,9 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
3266
4227
  DeleteAgentRequestMessageSchema,
3267
4228
  ArchiveAgentRequestMessageSchema,
3268
4229
  CloseItemsRequestMessageSchema,
4230
+ HistoryAgentsClearArchivedRequestSchema,
4231
+ AttachmentsImagesStatsRequestSchema,
4232
+ AttachmentsImagesClearRequestSchema,
3269
4233
  UpdateAgentRequestMessageSchema,
3270
4234
  ProjectRenameRequestSchema,
3271
4235
  ProjectRemoveRequestSchema,
@@ -3304,7 +4268,13 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
3304
4268
  ProviderUsageListRequestMessageSchema,
3305
4269
  StatsActivityGetRequestMessageSchema,
3306
4270
  ContextReportGetRequestMessageSchema,
4271
+ ContextPromptPreviewGetRequestMessageSchema,
3307
4272
  ContextEdgeConvertRequestMessageSchema,
4273
+ ContextFindingsFixRequestMessageSchema,
4274
+ PersonalityMemoryListRequestMessageSchema,
4275
+ PersonalityMemoryUpdateRequestMessageSchema,
4276
+ PersonalityMemoryTransferRequestMessageSchema,
4277
+ PersonalityMemoryStatsRequestMessageSchema,
3308
4278
  StatsActivityResetRequestMessageSchema,
3309
4279
  UsageLogGetRequestMessageSchema,
3310
4280
  AgentContextGetUsageRequestMessageSchema,
@@ -3329,6 +4299,9 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
3329
4299
  TasksSuggestedDismissRequestMessageSchema,
3330
4300
  AgentPersonalitySetRequestMessageSchema,
3331
4301
  AgentRewindRequestMessageSchema,
4302
+ AgentQueueRemoveRequestMessageSchema,
4303
+ AgentQueueReorderRequestMessageSchema,
4304
+ AgentQueueClearRequestMessageSchema,
3332
4305
  AgentPermissionResponseMessageSchema,
3333
4306
  CheckoutStatusRequestSchema,
3334
4307
  SubscribeCheckoutDiffRequestSchema,
@@ -3346,9 +4319,13 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
3346
4319
  RunsGateRespondRequestSchema,
3347
4320
  RunsCancelRequestSchema,
3348
4321
  RunsClearRequestSchema,
4322
+ RunsDeleteRequestSchema,
3349
4323
  RunsGraphsListRequestSchema,
3350
4324
  RunsGraphsSaveRequestSchema,
3351
4325
  RunsGraphsDeleteRequestSchema,
4326
+ RunsTemplatesListRequestSchema,
4327
+ RunsTemplatesSaveRequestSchema,
4328
+ RunsTemplatesDeleteRequestSchema,
3352
4329
  RunsStartRequestSchema,
3353
4330
  CheckoutMergeRequestSchema,
3354
4331
  CheckoutMergeFromBaseRequestSchema,
@@ -3384,8 +4361,12 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
3384
4361
  LegacyOpenInEditorRequestSchema,
3385
4362
  OpenProjectRequestSchema,
3386
4363
  ProjectAddRequestSchema,
4364
+ ProjectScaffoldRequestSchema,
4365
+ HostingListRepositoriesRequestSchema,
4366
+ HostingListOwnersRequestSchema,
3387
4367
  ArchiveWorkspaceRequestSchema,
3388
4368
  WorkspaceArchivePreflightRequestSchema,
4369
+ WorktreeBaseRefSetRequestSchema,
3389
4370
  WorktreeReattachListRequestSchema,
3390
4371
  WorktreeReattachRequestSchema,
3391
4372
  WorkspaceCreateRequestSchema,
@@ -3395,6 +4376,10 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
3395
4376
  FileDownloadTokenRequestSchema,
3396
4377
  FileUploadRequestSchema,
3397
4378
  FileWriteRequestSchema,
4379
+ FileCreateRequestSchema,
4380
+ FileDeleteRequestSchema,
4381
+ FileRenameRequestSchema,
4382
+ FileRefineRequestSchema,
3398
4383
  FileWatchSubscribeRequestSchema,
3399
4384
  FileWatchUnsubscribeRequestSchema,
3400
4385
  FileSearchRequestSchema,
@@ -3402,6 +4387,19 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
3402
4387
  CodeListFilesRequestSchema,
3403
4388
  CodeSymbolsRequestSchema,
3404
4389
  CodeOutlineRequestSchema,
4390
+ CodeDefinitionRequestSchema,
4391
+ CodeDocumentSyncRequestSchema,
4392
+ CodeDocumentCloseRequestSchema,
4393
+ CodeHoverRequestSchema,
4394
+ CodeReferencesRequestSchema,
4395
+ CodeRenamePreviewRequestSchema,
4396
+ CodeRenameApplyRequestSchema,
4397
+ CodeRenameUndoRequestSchema,
4398
+ LspServersListRequestSchema,
4399
+ LspServerStopRequestSchema,
4400
+ CodeSolutionListRequestSchema,
4401
+ CodeSolutionGetTreeRequestSchema,
4402
+ CodeSolutionLoadProjectRequestSchema,
3405
4403
  ClearAgentAttentionMessageSchema,
3406
4404
  ClientHeartbeatMessageSchema,
3407
4405
  PingMessageSchema,
@@ -3609,6 +4607,12 @@ export const ServerInfoStatusPayloadSchema = z
3609
4607
  projectRemove: z.boolean().optional(),
3610
4608
  // COMPAT(projectAdd): added in v0.1.97, drop the gate when floor >= v0.1.97.
3611
4609
  projectAdd: z.boolean().optional(),
4610
+ // COMPAT(projectScaffold): added in v0.6.9, drop the gate when floor >= v0.6.9.
4611
+ // The daemon can create a project directory from scratch (mkdir, git
4612
+ // init/clone, optional remote creation) instead of only adopting one
4613
+ // that already exists. Without it the New project page offers the
4614
+ // open-an-existing-folder path only.
4615
+ projectScaffold: z.boolean().optional(),
3612
4616
  // COMPAT(worktreeRestore): added in v0.1.97, drop the gate when floor >= v0.1.97
3613
4617
  worktreeRestore: z.boolean().optional(),
3614
4618
  // COMPAT(providerUsageList): added in v0.1.98, drop the gate when daemon floor >= v0.1.98.
@@ -3637,6 +4641,25 @@ export const ServerInfoStatusPayloadSchema = z
3637
4641
  retainedTranscripts: z.boolean().optional(),
3638
4642
  // COMPAT(suggestedTasks): added in v0.5.6, drop the gate when daemon floor >= v0.5.6.
3639
4643
  suggestedTasks: z.boolean().optional(),
4644
+ // COMPAT(steerQueue): added in v0.6.8, drop the gate when daemon floor >= v0.6.8.
4645
+ // Daemon owns a per-agent queue of steering messages, accepts
4646
+ // `delivery: "queue"` on send, reports `queuedMessages` on the agent
4647
+ // snapshot, and serves agent.queue.remove/clear. Without it the
4648
+ // composer keeps its own local queue — that is the pre-existing
4649
+ // behavior, not a degraded build of this feature.
4650
+ steerQueue: z.boolean().optional(),
4651
+ // Daemon serves agent.queue.reorder. Separate from `steerQueue`
4652
+ // because a daemon on 0.6.8 owns a queue but cannot re-order it;
4653
+ // without this the move controls are absent (the queue still works).
4654
+ // COMPAT(steerQueueReorder): added in v0.6.9, drop the gate when daemon floor >= v0.6.9.
4655
+ steerQueueReorder: z.boolean().optional(),
4656
+ // Daemon reports `cumulativeUsage` (the lifetime in/cached/out split
4657
+ // plus its own booked cost) on every agent snapshot, so a chat's total
4658
+ // spend can be summed and priced honestly. Without it the client shows
4659
+ // token totals only and no cost — NOT an estimated cost, which is the
4660
+ // behavior this feature exists to remove.
4661
+ // COMPAT(cumulativeUsage): added in v0.7.0, drop the gate when daemon floor >= v0.7.0.
4662
+ cumulativeUsage: z.boolean().optional(),
3640
4663
  // Daemon can resolve and evaluate the provider's context graph, serve
3641
4664
  // context.report.* and push context_report_changed. Without it the
3642
4665
  // client hides both the Context Management tab and the composer
@@ -3646,10 +4669,38 @@ export const ServerInfoStatusPayloadSchema = z
3646
4669
  contextManagement: z.boolean().optional(),
3647
4670
  // COMPAT(textEditor): added in v0.4.4, drop the gate when daemon floor >= v0.4.4.
3648
4671
  textEditor: z.boolean().optional(),
4672
+ // Refine — the daemon can turn a pinned document plus an instruction
4673
+ // into a proposed rewrite (`file.refine.*`). Without it the Refine
4674
+ // entry is absent: there is no client-side substitute for a model, and
4675
+ // a degraded "open a chat instead" path is exactly the unreviewed edit
4676
+ // Refine exists to replace.
4677
+ // COMPAT(refine): added in v0.6.9, drop the gate when daemon floor >= v0.6.9.
4678
+ refine: z.boolean().optional(),
4679
+ // Personality memory — the daemon stores per-personality lessons, injects
4680
+ // them at spawn, and serves personality.memory.*. Without it the client
4681
+ // hides the Memory tab, the accrual indicator and the transfer-on-delete
4682
+ // choice: storage is daemon-side by definition, so there is nothing a
4683
+ // client-side fallback could read.
4684
+ // COMPAT(personalityMemory): added in v0.7.0, drop the gate when daemon floor >= v0.7.0.
4685
+ personalityMemory: z.boolean().optional(),
3649
4686
  // COMPAT(projectSearch): added in v0.4.4, drop the gate when daemon floor >= v0.4.4.
3650
4687
  projectSearch: z.boolean().optional(),
3651
4688
  // COMPAT(codeIndex): added in v0.4.4, drop the gate when daemon floor >= v0.4.4.
3652
4689
  codeIndex: z.boolean().optional(),
4690
+ // Language-server-backed definitions (`code.definition`, `code.document.*`).
4691
+ // Separate from `codeIndex`: that gate covers the ctags path, which stays as
4692
+ // the outline/fuzzy-finder source and the no-server fallback.
4693
+ // COMPAT(lsp): added in v0.6.8, drop the gate when daemon floor >= v0.6.8.
4694
+ lsp: z.boolean().optional(),
4695
+ // The Solution view — the daemon can discover solutions and serve
4696
+ // `code.solution.*`. Deliberately NOT implied by `lsp`: there is no
4697
+ // project-structure request in LSP, so this subsystem is independent of
4698
+ // language servers and of the C# row's on/off state. Without the flag the
4699
+ // client never shows the view switcher and never asks — there is no
4700
+ // client-side substitute for reading a solution, and a hand-parsed
4701
+ // half-tree is exactly the mistake this design exists to avoid.
4702
+ // COMPAT(solutionView): added in v0.6.8, drop the gate when daemon floor >= v0.6.8.
4703
+ solutionView: z.boolean().optional(),
3653
4704
  // COMPAT(artifactsToolGroup): added in v0.4.5, drop the gate when daemon floor >= v0.4.5.
3654
4705
  artifactsToolGroup: z.boolean().optional(),
3655
4706
  // COMPAT(speechSettings): added in v0.4.5, drop the gate when daemon floor >= v0.4.5.
@@ -3690,6 +4741,12 @@ export const ServerInfoStatusPayloadSchema = z
3690
4741
  worktreeArchiveBranchCleanup: z.boolean().optional(),
3691
4742
  // COMPAT(worktreeReattach): added in v0.6.7, drop the gate when daemon floor >= v0.6.7.
3692
4743
  worktreeReattach: z.boolean().optional(),
4744
+ // Set when the daemon can repoint a worktree's stored base branch
4745
+ // (worktree.baseRef.set.*). Without it the client renders the base as a
4746
+ // read-only "vs <base>" label — there is no client-side override, since only
4747
+ // the daemon can write the worktree's metadata.
4748
+ // COMPAT(worktreeDiffBase): added in v0.6.8, drop the gate when daemon floor >= v0.6.8.
4749
+ worktreeDiffBase: z.boolean().optional(),
3693
4750
  // Set when the daemon persists the host-level hideMergeIntoBaseAction
3694
4751
  // workspace policy (read/written via the daemon config RPCs). Without
3695
4752
  // it the client hides the Workspaces toggle, since patching the field
@@ -3708,6 +4765,12 @@ export const ServerInfoStatusPayloadSchema = z
3708
4765
  activityStats: z.boolean().optional(),
3709
4766
  // COMPAT(runsClear): added in v0.5.3, drop the gate when daemon floor >= v0.5.3.
3710
4767
  runsClear: z.boolean().optional(),
4768
+ // COMPAT(runsDelete): added in v0.6.8, drop the gate when daemon floor >= v0.6.8.
4769
+ runsDelete: z.boolean().optional(),
4770
+ // COMPAT(runsDraftEdit): added in v0.6.8, drop the gate when daemon floor >= v0.6.8.
4771
+ // The daemon accepts `runs.start` with `draft: true` AND a `runId`,
4772
+ // re-saving that draft in place instead of minting a second one.
4773
+ runsDraftEdit: z.boolean().optional(),
3711
4774
  // COMPAT(orchestrationGraphs): added in v0.6.7, drop the gate when daemon floor >= v0.6.7.
3712
4775
  orchestrationGraphs: z.boolean().optional(),
3713
4776
  // COMPAT(projectLinks): added in v0.5.6, drop the gate when daemon floor >= v0.5.6.
@@ -3765,6 +4828,30 @@ export const ServerInfoStatusPayloadSchema = z
3765
4828
  // counters + the itemized ledger). The client gates the Metrics screen's
3766
4829
  // "Reset" button on this; an old daemon simply doesn't offer it.
3767
4830
  statsReset: z.boolean().optional(),
4831
+ // COMPAT(historyDelete): added in v0.7.0, drop the gate when daemon floor >= v0.7.0.
4832
+ // Set when the daemon offers hard delete for chat records: per-row via
4833
+ // the existing `delete_agent_request`, and in bulk via
4834
+ // `history.agents.clear_archived`. Archive has always been a soft delete
4835
+ // with no counterpart; this is the counterpart. Deleting removes Otto's
4836
+ // record only — provider transcripts are never touched (see the
4837
+ // clear_archived schema). The client hides both affordances when this is
4838
+ // absent rather than shipping a degraded path.
4839
+ historyDelete: z.boolean().optional(),
4840
+ // COMPAT(fileMutations): added in v0.7.0, drop the gate when daemon floor >= v0.7.0.
4841
+ // Set when the daemon serves `file.create`, `file.delete` and
4842
+ // `file.rename` — creating, removing and moving entries, as opposed to
4843
+ // `file.write`, which only changes what is inside an existing file.
4844
+ // There is no client-side substitute (the client never touches the
4845
+ // filesystem), so an old daemon simply does not get the menu items.
4846
+ fileMutations: z.boolean().optional(),
4847
+ // COMPAT(attachmentStorage): added in v0.7.1, drop the gate when daemon floor >= v0.7.1.
4848
+ // Set when the daemon serves `attachments.images.get_stats` and
4849
+ // `attachments.images.clear` — the readout and reclaim for the images it
4850
+ // materializes on the agent's behalf. The client has no way to size or
4851
+ // clear a directory on the host, so an old daemon simply does not get
4852
+ // the daemon half of the Storage section; the app-side preview cache row
4853
+ // is local and always shown.
4854
+ attachmentStorage: z.boolean().optional(),
3768
4855
  })
3769
4856
  .optional(),
3770
4857
  })
@@ -3847,7 +4934,70 @@ export const DaemonConfigChangedStatusPayloadSchema = z
3847
4934
  config: MutableDaemonConfigSchema,
3848
4935
  })
3849
4936
  .passthrough();
4937
+ /**
4938
+ * Which workspaces currently have a language server starting up or indexing. Sent as
4939
+ * the whole busy set rather than per-workspace transitions: the only consumer is a
4940
+ * spinner, so an idempotent snapshot cannot drift out of sync the way a missed
4941
+ * transition would.
4942
+ *
4943
+ * Separate from the workspace status bucket on purpose — indexing is not the workspace
4944
+ * "working", and folding it in would mislabel a quiet workspace as busy with agent work.
4945
+ */
4946
+ export const LspActivityChangedStatusPayloadSchema = z
4947
+ .object({
4948
+ status: z.literal("lsp_activity_changed"),
4949
+ /** Absolute workspace roots with language-server work in flight. */
4950
+ busyRoots: z.array(z.string()),
4951
+ })
4952
+ .passthrough();
4953
+ /**
4954
+ * Compiler severity, named rather than numbered. LSP uses 1–4; a magic number on the
4955
+ * wire would have every consumer re-deriving which one is a warning.
4956
+ */
4957
+ export const CodeDiagnosticSeveritySchema = z.enum(["error", "warning", "info", "hint"]);
4958
+ /** One problem the language server reported, 1-based like every other position. */
4959
+ export const CodeDiagnosticSchema = z.object({
4960
+ line: z.number().int().positive(),
4961
+ column: z.number().int().positive(),
4962
+ endLine: z.number().int().positive(),
4963
+ endColumn: z.number().int().positive(),
4964
+ severity: CodeDiagnosticSeveritySchema,
4965
+ message: z.string(),
4966
+ /** Who says so — `ts`, `pyright`, a linter behind the server. */
4967
+ source: z.string().optional(),
4968
+ /** The server's own code for the rule or error, e.g. TypeScript's `2345`. */
4969
+ code: z.string().optional(),
4970
+ /** Documentation for that rule, when the server offers one — oxlint does. */
4971
+ codeHref: z.string().optional(),
4972
+ /** Which registry row published it, so two servers on one file stay attributable. */
4973
+ serverId: z.string().optional(),
4974
+ });
4975
+ /**
4976
+ * Diagnostics for one open document, pushed unsolicited.
4977
+ *
4978
+ * This is the one part of code intelligence that is not request/response:
4979
+ * `textDocument/publishDiagnostics` arrives whenever the server has recomputed, which is
4980
+ * whenever it feels like it. So it is a status broadcast, and the payload is the document's
4981
+ * **whole** current set — never a delta. A missed delta would leave a stale squiggle on a
4982
+ * line the user already fixed, and an idempotent snapshot cannot drift.
4983
+ *
4984
+ * Only documents a client has synced produce these. A server may know about every file in
4985
+ * the project; pushing all of it would be unbounded, and nothing can render a marker in a
4986
+ * file that is not open.
4987
+ */
4988
+ export const LspDiagnosticsChangedStatusPayloadSchema = z
4989
+ .object({
4990
+ status: z.literal("lsp_diagnostics_changed"),
4991
+ /** Workspace root the document belongs to. */
4992
+ cwd: z.string(),
4993
+ /** Absolute path of the document these describe. */
4994
+ path: z.string(),
4995
+ diagnostics: z.array(CodeDiagnosticSchema),
4996
+ })
4997
+ .passthrough();
3850
4998
  export const KnownStatusPayloadSchema = z.discriminatedUnion("status", [
4999
+ LspActivityChangedStatusPayloadSchema,
5000
+ LspDiagnosticsChangedStatusPayloadSchema,
3851
5001
  AgentCreatedStatusPayloadSchema,
3852
5002
  AgentCreateFailedStatusPayloadSchema,
3853
5003
  AgentResumedStatusPayloadSchema,
@@ -4172,6 +5322,22 @@ export const WorkspaceUpdateMessageSchema = z.object({
4172
5322
  }),
4173
5323
  ]),
4174
5324
  });
5325
+ // A project's own metadata changed (today: the user renamed it). The workspace
5326
+ // channel can only carry a project's name inside its workspaces' descriptors, so
5327
+ // a project with no active workspaces had no live channel at all — its name only
5328
+ // refreshed on the next full workspace fetch. This is the project-level channel:
5329
+ // it fires whether or not the project currently has workspaces, and the daemon
5330
+ // fans it out to every connected session because project metadata is host-global.
5331
+ export const ProjectUpdatedNotificationSchema = z.object({
5332
+ type: z.literal("project.updated.notification"),
5333
+ payload: z.object({
5334
+ project: WorkspaceProjectDescriptorPayloadSchema,
5335
+ // False means the project has no active workspaces right now, so the client
5336
+ // keeps it in the empty-project bucket instead of expecting a workspace
5337
+ // descriptor to carry its name.
5338
+ hasActiveWorkspaces: z.boolean(),
5339
+ }),
5340
+ });
4175
5341
  export const ScriptStatusUpdateMessageSchema = z.object({
4176
5342
  type: z.literal("script_status_update"),
4177
5343
  payload: z.object({
@@ -4220,6 +5386,89 @@ export const ProjectAddResponseSchema = z.object({
4220
5386
  errorCode: z.enum(["directory_not_found"]).nullish().catch(null),
4221
5387
  }),
4222
5388
  });
5389
+ // COMPAT(projectScaffold): added in v0.6.9.
5390
+ export const ProjectScaffoldResponseSchema = z.object({
5391
+ type: z.literal("project.scaffold.response"),
5392
+ payload: z.object({
5393
+ requestId: z.string(),
5394
+ // Registered project on success. Null whenever any step failed — the
5395
+ // daemon does not register a half-built directory.
5396
+ project: WorkspaceProjectDescriptorPayloadSchema.nullable(),
5397
+ // Absolute path of the created directory. Non-null even on a late failure
5398
+ // (e.g. push rejected) so the UI can tell the user what is on disk.
5399
+ path: z.string().nullable(),
5400
+ // Remote the repo was wired to, when one was created or cloned from.
5401
+ remoteUrl: z.string().nullable(),
5402
+ error: z.string().nullable(),
5403
+ // Unknown codes from newer daemons degrade to null; clients fall back to `error`.
5404
+ errorCode: z
5405
+ .enum([
5406
+ "parent_not_found",
5407
+ "invalid_name",
5408
+ "already_exists",
5409
+ "git_unavailable",
5410
+ "git_failed",
5411
+ "provider_unavailable",
5412
+ "remote_failed",
5413
+ "clone_failed",
5414
+ "register_failed",
5415
+ ])
5416
+ .nullish()
5417
+ .catch(null),
5418
+ // Terminal state of every step the daemon ran, in run order.
5419
+ steps: z.array(ProjectScaffoldStepSchema),
5420
+ }),
5421
+ });
5422
+ // Uncorrelated progress stream for an in-flight scaffold, keyed by the request's
5423
+ // requestId. Purely advisory: the response carries the authoritative step list,
5424
+ // so a client that ignores these still gets a correct result.
5425
+ export const ProjectScaffoldProgressSchema = z.object({
5426
+ type: z.literal("project.scaffold.progress"),
5427
+ payload: z.object({
5428
+ requestId: z.string(),
5429
+ step: ProjectScaffoldStepIdSchema,
5430
+ status: ProjectScaffoldStepStatusSchema,
5431
+ detail: z.string().nullable(),
5432
+ }),
5433
+ });
5434
+ export const HostingRepositorySummarySchema = z.object({
5435
+ // Provider-unique identifier, e.g. "owner/name" or "workspace/slug".
5436
+ fullName: z.string(),
5437
+ name: z.string(),
5438
+ owner: z.string(),
5439
+ cloneUrl: z.string(),
5440
+ isPrivate: z.boolean(),
5441
+ description: z.string().nullable(),
5442
+ // ISO-8601. Clients sort most-recent-first when present.
5443
+ updatedAt: z.string().nullable(),
5444
+ });
5445
+ export const HostingOwnerSummarySchema = z.object({
5446
+ // Value to send back as `owner` when creating a repository.
5447
+ id: z.string(),
5448
+ label: z.string(),
5449
+ // Open string: providers name this differently (org, workspace, team).
5450
+ kind: z.string(),
5451
+ });
5452
+ // COMPAT(projectScaffold): added in v0.6.9.
5453
+ export const HostingListRepositoriesResponseSchema = z.object({
5454
+ type: z.literal("hosting.list_repositories.response"),
5455
+ payload: z.object({
5456
+ requestId: z.string(),
5457
+ provider: GitHostingProviderIdWireSchema,
5458
+ repositories: z.array(HostingRepositorySummarySchema),
5459
+ error: z.string().nullable(),
5460
+ }),
5461
+ });
5462
+ // COMPAT(projectScaffold): added in v0.6.9.
5463
+ export const HostingListOwnersResponseSchema = z.object({
5464
+ type: z.literal("hosting.list_owners.response"),
5465
+ payload: z.object({
5466
+ requestId: z.string(),
5467
+ provider: GitHostingProviderIdWireSchema,
5468
+ owners: z.array(HostingOwnerSummarySchema),
5469
+ error: z.string().nullable(),
5470
+ }),
5471
+ });
4223
5472
  export const StartWorkspaceScriptResponseMessageSchema = z.object({
4224
5473
  type: z.literal("start_workspace_script_response"),
4225
5474
  payload: z.object({
@@ -4297,6 +5546,19 @@ export const WorkspaceArchivePreflightResponseSchema = z.object({
4297
5546
  error: z.string().nullable(),
4298
5547
  }),
4299
5548
  });
5549
+ // COMPAT(worktreeDiffBase): added in v0.6.8.
5550
+ export const WorktreeBaseRefSetResponseSchema = z.object({
5551
+ type: z.literal("worktree.baseRef.set.response"),
5552
+ payload: z.object({
5553
+ requestId: z.string(),
5554
+ workspaceId: z.string(),
5555
+ // The stored base branch after the write; null when the write failed.
5556
+ baseRef: z.string().nullable(),
5557
+ // The stored base is the repository default branch (no stacked-branch override).
5558
+ isDefault: z.boolean(),
5559
+ error: z.string().nullable(),
5560
+ }),
5561
+ });
4300
5562
  // A re-attachable Otto worktree surfaced by worktree.reattach.list.
4301
5563
  // COMPAT(worktreeReattach): added in v0.6.7.
4302
5564
  export const WorktreeReattachCandidateSchema = z.object({
@@ -4389,6 +5651,52 @@ export const AgentForkContextResponseMessageSchema = z.object({
4389
5651
  error: z.string().nullable(),
4390
5652
  }),
4391
5653
  });
5654
+ export const AgentQueueRemoveResponseMessageSchema = z.object({
5655
+ type: z.literal("agent.queue.remove.response"),
5656
+ payload: z.object({
5657
+ requestId: z.string(),
5658
+ agentId: z.string(),
5659
+ /**
5660
+ * The removed message's text, handed back so the composer can put it back
5661
+ * in the box for editing or re-send it right away. Null when the id was
5662
+ * already gone — the turn drained it while the tap was in flight.
5663
+ * Attachments are not echoed: the client that queued the message keeps its
5664
+ * own local copy keyed by `id` (see the composer's queued-attachment
5665
+ * sidecar), and a client that never queued it has nothing to restore.
5666
+ */
5667
+ removed: z
5668
+ .object({
5669
+ id: z.string(),
5670
+ text: z.string(),
5671
+ })
5672
+ .nullable(),
5673
+ error: z.string().nullable(),
5674
+ }),
5675
+ });
5676
+ export const AgentQueueReorderResponseMessageSchema = z.object({
5677
+ type: z.literal("agent.queue.reorder.response"),
5678
+ payload: z.object({
5679
+ requestId: z.string(),
5680
+ agentId: z.string(),
5681
+ /**
5682
+ * False when the id was already gone (the turn drained it while the tap was
5683
+ * in flight) or the entry was already at that position. The authoritative
5684
+ * order arrives on the agent snapshot either way, so a client only needs
5685
+ * this to decide whether to surface an error.
5686
+ */
5687
+ moved: z.boolean(),
5688
+ error: z.string().nullable(),
5689
+ }),
5690
+ });
5691
+ export const AgentQueueClearResponseMessageSchema = z.object({
5692
+ type: z.literal("agent.queue.clear.response"),
5693
+ payload: z.object({
5694
+ requestId: z.string(),
5695
+ agentId: z.string(),
5696
+ clearedCount: z.number().int().nonnegative(),
5697
+ error: z.string().nullable(),
5698
+ }),
5699
+ });
4392
5700
  export const CancelAgentResponseMessageSchema = z.object({
4393
5701
  type: z.literal("cancel_agent_response"),
4394
5702
  payload: z.object({
@@ -4443,6 +5751,13 @@ export const SendAgentMessageResponseMessageSchema = z.object({
4443
5751
  agentId: z.string(),
4444
5752
  accepted: z.boolean(),
4445
5753
  error: z.string().nullable(),
5754
+ // Set when the message was parked for the next turn rather than dispatched
5755
+ // (`delivery: "queue"` against a busy agent). `queuedMessageId` is the
5756
+ // entry's id in `AgentSnapshotPayload.queuedMessages` — the key the sender
5757
+ // uses to find its own entry again. Absent ⇒ dispatched now (or old daemon).
5758
+ // COMPAT(steerQueue): added in v0.6.8, drop the gate when floor >= v0.6.8.
5759
+ queued: z.boolean().optional(),
5760
+ queuedMessageId: z.string().optional(),
4446
5761
  }),
4447
5762
  });
4448
5763
  export const WaitForFinishResponseMessageSchema = z.object({
@@ -4711,6 +6026,12 @@ const CheckoutStatusCommonSchema = z.object({
4711
6026
  cwd: z.string(),
4712
6027
  error: CheckoutErrorSchema.nullable(),
4713
6028
  requestId: z.string(),
6029
+ // Daemon clock (epoch ms) at which the git-tracking fields below were measured.
6030
+ // One writer stamps it, so clients can compare two payloads and drop an
6031
+ // out-of-order push instead of clobbering newer git state.
6032
+ // COMPAT(checkoutStatusGitStateAt): added in v0.6.8; absent from older daemons.
6033
+ // Drop the optional marker when floor >= v0.6.8 (target 2027-01-24).
6034
+ gitStateAt: z.number().optional(),
4714
6035
  });
4715
6036
  const CheckoutStatusNotGitSchema = CheckoutStatusCommonSchema.extend({
4716
6037
  isGit: z.literal(false),
@@ -4867,6 +6188,12 @@ const CheckoutPrStatusPayloadSchema = z.object({
4867
6188
  });
4868
6189
  const CheckoutStatusUpdateMetadataSchema = z.object({
4869
6190
  prStatus: CheckoutPrStatusPayloadSchema.optional(),
6191
+ // True when the push refreshed only PR/check state (the hosting PR-status poll).
6192
+ // The git block on such a payload is an unrefreshed echo of the last snapshot,
6193
+ // so clients must apply `prStatus` and leave their git-tracking state alone.
6194
+ // COMPAT(checkoutStatusPrStatusOnly): added in v0.6.8; absent from older daemons.
6195
+ // Drop the default when floor >= v0.6.8 (target 2027-01-24).
6196
+ prStatusOnly: z.boolean().optional().default(false),
4870
6197
  });
4871
6198
  export const CheckoutStatusUpdateSchema = z.object({
4872
6199
  type: z.literal("checkout_status_update"),
@@ -5637,6 +6964,110 @@ export const FileWriteResponseSchema = z.object({
5637
6964
  requestId: z.string(),
5638
6965
  }),
5639
6966
  });
6967
+ /**
6968
+ * Create outcome. `exists` is its own status rather than an error string: it is
6969
+ * the one failure the client can act on (offer a different name) instead of
6970
+ * merely reporting.
6971
+ */
6972
+ export const FileCreateResultSchema = z.discriminatedUnion("status", [
6973
+ z.object({
6974
+ status: z.literal("ok"),
6975
+ // Echoed back normalized (forward slashes, workspace-relative) so the client
6976
+ // selects the entry it will actually see in the next listing.
6977
+ path: z.string(),
6978
+ kind: FileEntryKindSchema,
6979
+ modifiedAt: z.string(),
6980
+ size: z.number(),
6981
+ }),
6982
+ z.object({ status: z.literal("exists") }),
6983
+ z.object({ status: z.literal("error"), message: z.string() }),
6984
+ ]);
6985
+ export const FileCreateResponseSchema = z.object({
6986
+ type: z.literal("file.create.response"),
6987
+ payload: z.object({
6988
+ cwd: z.string(),
6989
+ path: z.string(),
6990
+ result: FileCreateResultSchema,
6991
+ requestId: z.string(),
6992
+ }),
6993
+ });
6994
+ /**
6995
+ * Delete outcome. `not_empty` means the target is a directory with children and
6996
+ * the request did not set `recursive` — nothing was removed, and the client can
6997
+ * re-ask with the stronger confirmation.
6998
+ */
6999
+ export const FileDeleteResultSchema = z.discriminatedUnion("status", [
7000
+ z.object({
7001
+ status: z.literal("ok"),
7002
+ path: z.string(),
7003
+ kind: FileEntryKindSchema,
7004
+ }),
7005
+ z.object({ status: z.literal("not_found") }),
7006
+ z.object({ status: z.literal("not_empty") }),
7007
+ z.object({ status: z.literal("error"), message: z.string() }),
7008
+ ]);
7009
+ export const FileDeleteResponseSchema = z.object({
7010
+ type: z.literal("file.delete.response"),
7011
+ payload: z.object({
7012
+ cwd: z.string(),
7013
+ path: z.string(),
7014
+ result: FileDeleteResultSchema,
7015
+ requestId: z.string(),
7016
+ }),
7017
+ });
7018
+ /** Rename/move outcome. `exists` means the destination was occupied; nothing moved. */
7019
+ export const FileRenameResultSchema = z.discriminatedUnion("status", [
7020
+ z.object({
7021
+ status: z.literal("ok"),
7022
+ from: z.string(),
7023
+ to: z.string(),
7024
+ kind: FileEntryKindSchema,
7025
+ }),
7026
+ z.object({ status: z.literal("not_found") }),
7027
+ z.object({ status: z.literal("exists") }),
7028
+ z.object({ status: z.literal("error"), message: z.string() }),
7029
+ ]);
7030
+ export const FileRenameResponseSchema = z.object({
7031
+ type: z.literal("file.rename.response"),
7032
+ payload: z.object({
7033
+ cwd: z.string(),
7034
+ path: z.string(),
7035
+ newPath: z.string(),
7036
+ result: FileRenameResultSchema,
7037
+ requestId: z.string(),
7038
+ }),
7039
+ });
7040
+ /**
7041
+ * A refine proposal: the whole rewritten text of each document the model chose
7042
+ * to change, keyed by the id the request minted. Documents it left alone are
7043
+ * simply absent, and ids the request never sent are dropped by the daemon.
7044
+ *
7045
+ * The client diffs each one against the base it pinned, so a truncated or
7046
+ * chatty answer shows up as a diff the user can refuse rather than as a
7047
+ * corrupted file.
7048
+ */
7049
+ export const FileRefineFileSchema = z.object({
7050
+ id: z.string().min(1),
7051
+ content: z.string(),
7052
+ });
7053
+ export const FileRefineResultSchema = z.discriminatedUnion("status", [
7054
+ z.object({
7055
+ status: z.literal("ok"),
7056
+ files: z.array(FileRefineFileSchema),
7057
+ }),
7058
+ z.object({
7059
+ status: z.literal("error"),
7060
+ message: z.string(),
7061
+ }),
7062
+ ]);
7063
+ export const FileRefineResponseSchema = z.object({
7064
+ type: z.literal("file.refine.response"),
7065
+ payload: z.object({
7066
+ cwd: z.string(),
7067
+ result: FileRefineResultSchema,
7068
+ requestId: z.string(),
7069
+ }),
7070
+ });
5640
7071
  export const FileWatchSubscribeResponseSchema = z.object({
5641
7072
  type: z.literal("file.watch.subscribe.response"),
5642
7073
  payload: z.object({
@@ -5685,6 +7116,370 @@ export const CodeSymbolsResponseSchema = z.object({
5685
7116
  requestId: z.string(),
5686
7117
  }),
5687
7118
  });
7119
+ /** 1-based, like `CodeSymbolLocation`. The end pair is present when the server gave a range. */
7120
+ export const CodeDefinitionLocationSchema = z.object({
7121
+ path: z.string(),
7122
+ line: z.number().int().positive(),
7123
+ column: z.number().int().positive(),
7124
+ endLine: z.number().int().positive().optional(),
7125
+ endColumn: z.number().int().positive().optional(),
7126
+ /**
7127
+ * Which registry row answered (`typescript`, `csharp`, …). The multi-hit picker
7128
+ * shows it, so a user looking at two candidates can tell whether a language server
7129
+ * resolved them or the name index guessed — which changes how much to trust the
7130
+ * list. Absent from old daemons.
7131
+ */
7132
+ serverId: z.string().optional(),
7133
+ });
7134
+ /**
7135
+ * Three-valued on purpose. `unavailable` (no server for this language on the host) and
7136
+ * `indexing` (the server is up but still building its project model) are different
7137
+ * answers to the user, and neither is "not found" — reporting either as an empty
7138
+ * result is how a working feature reads as broken.
7139
+ */
7140
+ export const CodeDefinitionStatusSchema = z.enum(["ok", "indexing", "unavailable"]);
7141
+ export const CodeDefinitionResponseSchema = z.object({
7142
+ type: z.literal("code.definition.response"),
7143
+ payload: z.object({
7144
+ cwd: z.string(),
7145
+ path: z.string(),
7146
+ status: CodeDefinitionStatusSchema,
7147
+ locations: z.array(CodeDefinitionLocationSchema),
7148
+ error: z.string().nullable(),
7149
+ requestId: z.string(),
7150
+ }),
7151
+ });
7152
+ export const CodeDocumentSyncResponseSchema = z.object({
7153
+ type: z.literal("code.document.sync.response"),
7154
+ payload: z.object({
7155
+ cwd: z.string(),
7156
+ path: z.string(),
7157
+ ok: z.boolean(),
7158
+ error: z.string().nullable(),
7159
+ requestId: z.string(),
7160
+ }),
7161
+ });
7162
+ export const CodeDocumentCloseResponseSchema = z.object({
7163
+ type: z.literal("code.document.close.response"),
7164
+ payload: z.object({
7165
+ cwd: z.string(),
7166
+ path: z.string(),
7167
+ ok: z.boolean(),
7168
+ error: z.string().nullable(),
7169
+ requestId: z.string(),
7170
+ }),
7171
+ });
7172
+ /** 1-based, like every other position on the wire. */
7173
+ export const CodeHoverRangeSchema = z.object({
7174
+ line: z.number().int().positive(),
7175
+ column: z.number().int().positive(),
7176
+ endLine: z.number().int().positive(),
7177
+ endColumn: z.number().int().positive(),
7178
+ });
7179
+ export const CodeHoverResponseSchema = z.object({
7180
+ type: z.literal("code.hover.response"),
7181
+ payload: z.object({
7182
+ cwd: z.string(),
7183
+ path: z.string(),
7184
+ status: CodeDefinitionStatusSchema,
7185
+ /** Markdown, or null when the server had nothing to say about this position. */
7186
+ markdown: z.string().nullable(),
7187
+ range: CodeHoverRangeSchema.nullable(),
7188
+ serverId: z.string().nullable(),
7189
+ error: z.string().nullable(),
7190
+ requestId: z.string(),
7191
+ }),
7192
+ });
7193
+ export const CodeReferencesResponseSchema = z.object({
7194
+ type: z.literal("code.references.response"),
7195
+ payload: z.object({
7196
+ cwd: z.string(),
7197
+ path: z.string(),
7198
+ status: CodeDefinitionStatusSchema,
7199
+ locations: z.array(CodeDefinitionLocationSchema),
7200
+ error: z.string().nullable(),
7201
+ requestId: z.string(),
7202
+ }),
7203
+ });
7204
+ export const CodeRenameEditSchema = z.object({
7205
+ line: z.number().int().positive(),
7206
+ column: z.number().int().positive(),
7207
+ endLine: z.number().int().positive(),
7208
+ endColumn: z.number().int().positive(),
7209
+ newText: z.string(),
7210
+ /**
7211
+ * The text this edit expects to replace. Carried so the dry run can show what is being
7212
+ * changed rather than only what it becomes — and, on the daemon side, so the run can tell
7213
+ * that a file moved under the plan. For a rename this is always one identifier.
7214
+ */
7215
+ oldText: z.string().default(""),
7216
+ });
7217
+ export const CodeRenameFilePlanSchema = z.object({
7218
+ path: z.string(),
7219
+ edits: z.array(CodeRenameEditSchema),
7220
+ });
7221
+ export const CodeRenamePreviewResponseSchema = z.object({
7222
+ type: z.literal("code.rename.preview.response"),
7223
+ payload: z.object({
7224
+ cwd: z.string(),
7225
+ path: z.string(),
7226
+ newName: z.string(),
7227
+ status: CodeDefinitionStatusSchema,
7228
+ /** Sorted by path, and by position within each file, so an audit reads in order. */
7229
+ files: z.array(CodeRenameFilePlanSchema),
7230
+ /** Blast radius, so the dry-run tab can lead with it. */
7231
+ fileCount: z.number().int().nonnegative(),
7232
+ editCount: z.number().int().nonnegative(),
7233
+ /**
7234
+ * Identity of this exact plan, echoed back on apply. Computed by the daemon so there is
7235
+ * one definition of "the same plan" rather than two that can drift apart.
7236
+ */
7237
+ planId: z.string().default(""),
7238
+ error: z.string().nullable(),
7239
+ requestId: z.string(),
7240
+ }),
7241
+ });
7242
+ /**
7243
+ * Five-valued, because the ways a rename can fail to happen are things a user needs told
7244
+ * apart: still loading, no server, the plan moved, or the server pointed outside the
7245
+ * workspace. Collapsing them into one failure is how "nothing happened" becomes unexplainable.
7246
+ */
7247
+ /**
7248
+ * Whether the run HAPPENED — deliberately not whether everything applied.
7249
+ *
7250
+ * A run where two of fourteen edits no longer fit is still a run that took place, and the
7251
+ * twelve that landed are real. Collapsing that into a failure would hide them, and hiding a
7252
+ * write is the one thing an auditable edit surface must never do. Per-edit fate lives in the
7253
+ * file outcomes; `complete` is the single-glance answer.
7254
+ */
7255
+ export const CodeRenameApplyStatusSchema = z.enum(["ok", "expired", "escaped"]);
7256
+ export const CodeRenameFileOutcomeKindSchema = z.enum(["applied", "partial", "failed"]);
7257
+ /** What happened to one file in a run. */
7258
+ export const CodeRenameFileOutcomeSchema = z.object({
7259
+ path: z.string(),
7260
+ kind: CodeRenameFileOutcomeKindSchema,
7261
+ appliedEdits: z.number().int().nonnegative(),
7262
+ skippedEdits: z.number().int().nonnegative(),
7263
+ /** Why, whenever anything was skipped or the file failed outright. */
7264
+ reason: z.string().nullable(),
7265
+ });
7266
+ export const CodeRenameUndoStatusSchema = z.enum(["ok", "expired"]);
7267
+ export const CodeRenameUndoFileKindSchema = z.enum(["restored", "changedSince", "failed"]);
7268
+ /**
7269
+ * What happened to one file during an undo. `changedSince` is the important one: the file was
7270
+ * edited after the run, so restoring would have destroyed that work and it was left alone.
7271
+ */
7272
+ export const CodeRenameUndoFileSchema = z.object({
7273
+ path: z.string(),
7274
+ kind: CodeRenameUndoFileKindSchema,
7275
+ reason: z.string().nullable(),
7276
+ });
7277
+ export const CodeRenameUndoResponseSchema = z.object({
7278
+ type: z.literal("code.rename.undo.response"),
7279
+ payload: z.object({
7280
+ cwd: z.string(),
7281
+ runId: z.string(),
7282
+ status: CodeRenameUndoStatusSchema,
7283
+ files: z.array(CodeRenameUndoFileSchema),
7284
+ restoredFiles: z.number().int().nonnegative(),
7285
+ /** True only when every file the run wrote was put back. */
7286
+ complete: z.boolean(),
7287
+ error: z.string().nullable(),
7288
+ requestId: z.string(),
7289
+ }),
7290
+ });
7291
+ export const CodeRenameApplyResponseSchema = z.object({
7292
+ type: z.literal("code.rename.apply.response"),
7293
+ payload: z.object({
7294
+ cwd: z.string(),
7295
+ path: z.string(),
7296
+ newName: z.string(),
7297
+ status: CodeRenameApplyStatusSchema,
7298
+ /** Identity of this run, for undo. Null when nothing ran. */
7299
+ runId: z.string().nullable(),
7300
+ files: z.array(CodeRenameFileOutcomeSchema),
7301
+ appliedFiles: z.number().int().nonnegative(),
7302
+ appliedEdits: z.number().int().nonnegative(),
7303
+ skippedEdits: z.number().int().nonnegative(),
7304
+ /** True only when every planned edit landed. */
7305
+ complete: z.boolean(),
7306
+ error: z.string().nullable(),
7307
+ requestId: z.string(),
7308
+ }),
7309
+ });
7310
+ export const LspLanguageStateSchema = z.object({
7311
+ id: z.string(),
7312
+ enabled: z.boolean(),
7313
+ /** Whether the host can actually supply this server right now. */
7314
+ installed: z.boolean(),
7315
+ running: z.boolean(),
7316
+ /** Which discovery rung supplied it (`workspaceBin` / `bundled` / `path`), or null. */
7317
+ rung: z.string().nullable(),
7318
+ bin: z.string(),
7319
+ extensions: z.array(z.string()),
7320
+ /** Plain-words index cost, so the toggle states its own price. */
7321
+ indexCost: z.string(),
7322
+ });
7323
+ export const LspRunningServerSchema = z.object({
7324
+ rootPath: z.string(),
7325
+ serverId: z.string(),
7326
+ uptimeMs: z.number(),
7327
+ lastUsedAt: z.number(),
7328
+ });
7329
+ export const LspServersListResponseSchema = z.object({
7330
+ type: z.literal("lsp.servers.list.response"),
7331
+ payload: z.object({
7332
+ cwd: z.string(),
7333
+ languages: z.array(LspLanguageStateSchema),
7334
+ running: z.array(LspRunningServerSchema),
7335
+ error: z.string().nullable(),
7336
+ requestId: z.string(),
7337
+ }),
7338
+ });
7339
+ export const LspServerStopResponseSchema = z.object({
7340
+ type: z.literal("lsp.server.stop.response"),
7341
+ payload: z.object({
7342
+ rootPath: z.string(),
7343
+ serverId: z.string(),
7344
+ ok: z.boolean(),
7345
+ error: z.string().nullable(),
7346
+ requestId: z.string(),
7347
+ }),
7348
+ });
7349
+ /**
7350
+ * Solution view responses (projects/solution-view).
7351
+ *
7352
+ * COMPAT(solutionView): added in v0.6.8, drop the gate when daemon floor >= v0.6.8.
7353
+ */
7354
+ export const SolutionFormatSchema = z.enum(["sln", "slnx"]);
7355
+ /** One solution a workspace contains. Enough to populate the switcher's picker, nothing more. */
7356
+ export const SolutionRefSchema = z.object({
7357
+ /** Workspace-relative, forward slashes — the identity used by every later request. */
7358
+ path: z.string(),
7359
+ /** File name without the extension, which is what a .NET developer calls the solution. */
7360
+ name: z.string(),
7361
+ format: SolutionFormatSchema,
7362
+ });
7363
+ export const CodeSolutionListResponseSchema = z.object({
7364
+ type: z.literal("code.solution.list.response"),
7365
+ payload: z.object({
7366
+ cwd: z.string(),
7367
+ /**
7368
+ * Empty means the switcher never appears and the Files tab behaves exactly as it does today.
7369
+ * That is also what a disabled feature, a host with no .NET SDK, and a workspace with no
7370
+ * solution all return — the client has one silent case to handle, not four.
7371
+ */
7372
+ solutions: z.array(SolutionRefSchema),
7373
+ error: z.string().nullable(),
7374
+ requestId: z.string(),
7375
+ }),
7376
+ });
7377
+ /**
7378
+ * Solution structure is flat on the wire with parent links, not nested.
7379
+ *
7380
+ * A recursive payload would have to be walked to be used, and every consumer would write that
7381
+ * walk again; the file explorer already turns a flat listing plus an expanded-path set into rows,
7382
+ * so this hands it the same shape it already consumes.
7383
+ */
7384
+ export const SolutionTreeFolderSchema = z.object({
7385
+ /** Solution-internal, e.g. `/Src/`. Folders are virtual: they have no filesystem location. */
7386
+ path: z.string(),
7387
+ name: z.string(),
7388
+ parentPath: z.string().nullable(),
7389
+ });
7390
+ export const SolutionTreeProjectSchema = z.object({
7391
+ id: z.string(),
7392
+ name: z.string(),
7393
+ /**
7394
+ * Workspace-relative when the project sits inside the workspace, absolute (forward-slashed)
7395
+ * when it does not. `outsideWorkspace` says which, so nothing has to guess by inspecting the
7396
+ * string.
7397
+ */
7398
+ path: z.string(),
7399
+ /**
7400
+ * A project the solution names outside the workspace root. Shown and opened like any other —
7401
+ * the solution file is the authority naming it, so this is not free browsing — but editing one
7402
+ * warns, and it is absent from every git surface. See docs/solution-view.md.
7403
+ */
7404
+ outsideWorkspace: z.boolean(),
7405
+ /** The solution folder containing it, or null for a project at the solution root. */
7406
+ folderPath: z.string().nullable(),
7407
+ /** Project type GUID, lowercased. Absent on old daemons. */
7408
+ typeId: z.string().optional(),
7409
+ });
7410
+ export const CodeSolutionGetTreeResponseSchema = z.object({
7411
+ type: z.literal("code.solution.get_tree.response"),
7412
+ payload: z.object({
7413
+ cwd: z.string(),
7414
+ solutionPath: z.string(),
7415
+ name: z.string().default(""),
7416
+ format: SolutionFormatSchema.default("sln"),
7417
+ folders: z.array(SolutionTreeFolderSchema),
7418
+ projects: z.array(SolutionTreeProjectSchema),
7419
+ /** Solution configurations and platforms — first-class .NET concepts no CLI surfaces. */
7420
+ buildTypes: z.array(z.string()),
7421
+ platforms: z.array(z.string()),
7422
+ error: z.string().nullable(),
7423
+ requestId: z.string(),
7424
+ }),
7425
+ });
7426
+ /**
7427
+ * Three-valued for the same reason the code-intelligence family is: "the host cannot supply
7428
+ * this", "MSBuild refused this project", and "here are its files" are different things to tell a
7429
+ * user, and reporting the first two as an empty file list is how a working feature reads as
7430
+ * broken. One project that fails must not blank the tree, so this status is per project.
7431
+ */
7432
+ export const SolutionProjectStatusSchema = z.enum(["ok", "failed", "unavailable"]);
7433
+ /**
7434
+ * One entry in a project's evaluated membership, flat with parent links like the folders above.
7435
+ *
7436
+ * `isImplicit` is what a filesystem tree structurally cannot show and what Phase 2 turns on: an
7437
+ * item contributed by the SDK's default globs is one that creating the file already adds, while
7438
+ * an item the project file itself declares needs a real `.csproj` edit.
7439
+ */
7440
+ export const SolutionProjectNodeSchema = z.discriminatedUnion("kind", [
7441
+ z.object({
7442
+ kind: z.literal("directory"),
7443
+ id: z.string(),
7444
+ parentId: z.string().nullable(),
7445
+ name: z.string(),
7446
+ path: z.string(),
7447
+ outsideWorkspace: z.boolean(),
7448
+ }),
7449
+ z.object({
7450
+ kind: z.literal("file"),
7451
+ id: z.string(),
7452
+ parentId: z.string().nullable(),
7453
+ name: z.string(),
7454
+ path: z.string(),
7455
+ outsideWorkspace: z.boolean(),
7456
+ /** `Compile`, `Content`, `EmbeddedResource`, … — MSBuild's own item type. */
7457
+ itemType: z.string(),
7458
+ isImplicit: z.boolean(),
7459
+ }),
7460
+ ]);
7461
+ export const SolutionPackageReferenceSchema = z.object({
7462
+ name: z.string(),
7463
+ version: z.string().nullable(),
7464
+ });
7465
+ export const CodeSolutionLoadProjectResponseSchema = z.object({
7466
+ type: z.literal("code.solution.load_project.response"),
7467
+ payload: z.object({
7468
+ cwd: z.string(),
7469
+ solutionPath: z.string(),
7470
+ projectPath: z.string(),
7471
+ status: SolutionProjectStatusSchema,
7472
+ nodes: z.array(SolutionProjectNodeSchema),
7473
+ projectReferences: z.array(z.string()),
7474
+ packageReferences: z.array(SolutionPackageReferenceSchema),
7475
+ targetFrameworks: z.array(z.string()),
7476
+ outputType: z.string().nullable(),
7477
+ isSdkStyle: z.boolean(),
7478
+ /** MSBuild's own message when `status` is `failed`, verbatim. */
7479
+ error: z.string().nullable(),
7480
+ requestId: z.string(),
7481
+ }),
7482
+ });
5688
7483
  export const CodeOutlineResponseSchema = z.object({
5689
7484
  type: z.literal("code.outline.response"),
5690
7485
  payload: z.object({
@@ -6113,12 +7908,15 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6113
7908
  FetchRecentProviderSessionsResponseMessageSchema,
6114
7909
  FetchWorkspacesResponseMessageSchema,
6115
7910
  ProjectAddResponseSchema,
7911
+ ProjectScaffoldResponseSchema,
7912
+ ProjectScaffoldProgressSchema,
6116
7913
  OpenProjectResponseMessageSchema,
6117
7914
  StartWorkspaceScriptResponseMessageSchema,
6118
7915
  LegacyListAvailableEditorsResponseMessageSchema,
6119
7916
  LegacyOpenInEditorResponseMessageSchema,
6120
7917
  ArchiveWorkspaceResponseMessageSchema,
6121
7918
  WorkspaceArchivePreflightResponseSchema,
7919
+ WorktreeBaseRefSetResponseSchema,
6122
7920
  WorktreeReattachListResponseSchema,
6123
7921
  WorktreeReattachResponseSchema,
6124
7922
  FetchAgentResponseMessageSchema,
@@ -6148,6 +7946,9 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6148
7946
  SetAgentThinkingResponseMessageSchema,
6149
7947
  SetAgentFeatureResponseMessageSchema,
6150
7948
  AgentDetachResponseMessageSchema,
7949
+ AgentQueueRemoveResponseMessageSchema,
7950
+ AgentQueueReorderResponseMessageSchema,
7951
+ AgentQueueClearResponseMessageSchema,
6151
7952
  AgentSubagentStopResponseMessageSchema,
6152
7953
  AgentBackgroundTaskStopResponseMessageSchema,
6153
7954
  AgentBackgroundTaskClearResponseMessageSchema,
@@ -6160,6 +7961,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6160
7961
  AgentRewindResponseMessageSchema,
6161
7962
  UpdateAgentResponseMessageSchema,
6162
7963
  ProjectRenameResponseSchema,
7964
+ ProjectUpdatedNotificationSchema,
6163
7965
  ProjectRemoveResponseSchema,
6164
7966
  ProjectLinksListResponseSchema,
6165
7967
  ProjectLinksSetResponseSchema,
@@ -6170,6 +7972,9 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6170
7972
  AgentPermissionRequestMessageSchema,
6171
7973
  AgentPermissionResolvedMessageSchema,
6172
7974
  AgentDeletedMessageSchema,
7975
+ HistoryAgentsClearArchivedResponseSchema,
7976
+ AttachmentsImagesStatsResponseSchema,
7977
+ AttachmentsImagesClearResponseSchema,
6173
7978
  AgentArchivedMessageSchema,
6174
7979
  CloseItemsResponseSchema,
6175
7980
  CheckoutStatusResponseSchema,
@@ -6191,11 +7996,16 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6191
7996
  RunsGateRespondResponseSchema,
6192
7997
  RunsCancelResponseSchema,
6193
7998
  RunsClearResponseSchema,
7999
+ RunsDeleteResponseSchema,
6194
8000
  RunsClearedNotificationSchema,
6195
8001
  RunsGraphsListResponseSchema,
6196
8002
  RunsGraphsSaveResponseSchema,
6197
8003
  RunsGraphsDeleteResponseSchema,
6198
8004
  RunsGraphsChangedNotificationSchema,
8005
+ RunsTemplatesListResponseSchema,
8006
+ RunsTemplatesSaveResponseSchema,
8007
+ RunsTemplatesDeleteResponseSchema,
8008
+ RunsTemplatesChangedNotificationSchema,
6199
8009
  RunsStartResponseSchema,
6200
8010
  CheckoutMergeResponseSchema,
6201
8011
  CheckoutMergeFromBaseResponseSchema,
@@ -6222,6 +8032,8 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6222
8032
  GitHubSearchResponseSchema,
6223
8033
  HostingSearchResponseSchema,
6224
8034
  HostingAuthStatusResponseSchema,
8035
+ HostingListRepositoriesResponseSchema,
8036
+ HostingListOwnersResponseSchema,
6225
8037
  DirectorySuggestionsResponseSchema,
6226
8038
  OttoWorktreeListResponseSchema,
6227
8039
  OttoWorktreeArchiveResponseSchema,
@@ -6231,6 +8043,10 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6231
8043
  FileDownloadTokenResponseSchema,
6232
8044
  FileUploadResponseSchema,
6233
8045
  FileWriteResponseSchema,
8046
+ FileCreateResponseSchema,
8047
+ FileDeleteResponseSchema,
8048
+ FileRenameResponseSchema,
8049
+ FileRefineResponseSchema,
6234
8050
  FileWatchSubscribeResponseSchema,
6235
8051
  FileWatchUnsubscribeResponseSchema,
6236
8052
  FileWatchEventSchema,
@@ -6240,6 +8056,19 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6240
8056
  CodeListFilesResponseSchema,
6241
8057
  CodeSymbolsResponseSchema,
6242
8058
  CodeOutlineResponseSchema,
8059
+ CodeDefinitionResponseSchema,
8060
+ CodeDocumentSyncResponseSchema,
8061
+ CodeDocumentCloseResponseSchema,
8062
+ CodeHoverResponseSchema,
8063
+ CodeReferencesResponseSchema,
8064
+ CodeRenamePreviewResponseSchema,
8065
+ CodeRenameApplyResponseSchema,
8066
+ CodeRenameUndoResponseSchema,
8067
+ LspServersListResponseSchema,
8068
+ LspServerStopResponseSchema,
8069
+ CodeSolutionListResponseSchema,
8070
+ CodeSolutionGetTreeResponseSchema,
8071
+ CodeSolutionLoadProjectResponseSchema,
6243
8072
  ListProviderModelsResponseMessageSchema,
6244
8073
  ListProviderModesResponseMessageSchema,
6245
8074
  ListProviderFeaturesResponseMessageSchema,
@@ -6251,7 +8080,13 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
6251
8080
  ProviderUsageListResponseMessageSchema,
6252
8081
  StatsActivityGetResponseMessageSchema,
6253
8082
  ContextReportGetResponseMessageSchema,
8083
+ ContextPromptPreviewGetResponseMessageSchema,
6254
8084
  ContextEdgeConvertResponseMessageSchema,
8085
+ ContextFindingsFixResponseMessageSchema,
8086
+ PersonalityMemoryListResponseMessageSchema,
8087
+ PersonalityMemoryUpdateResponseMessageSchema,
8088
+ PersonalityMemoryTransferResponseMessageSchema,
8089
+ PersonalityMemoryStatsResponseMessageSchema,
6255
8090
  StatsActivityResetResponseMessageSchema,
6256
8091
  UsageLogGetResponseMessageSchema,
6257
8092
  ActivityStatsChangedSchema,