@otto-code/protocol 0.8.10 → 0.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/agent-labels.d.ts +3 -0
  2. package/dist/agent-labels.js +10 -0
  3. package/dist/agent-personalities.d.ts +4 -3
  4. package/dist/agent-personalities.js +13 -20
  5. package/dist/agent-queue.d.ts +87 -0
  6. package/dist/agent-queue.js +106 -0
  7. package/dist/agent-types.d.ts +24 -4
  8. package/dist/binary-frames/terminal.d.ts +4 -0
  9. package/dist/binary-frames/terminal.js +1 -0
  10. package/dist/brain.d.ts +2412 -0
  11. package/dist/brain.js +1100 -0
  12. package/dist/chat/rpc-schemas.js +1 -0
  13. package/dist/chat/types.js +1 -0
  14. package/dist/client-capabilities.d.ts +3 -0
  15. package/dist/client-capabilities.js +14 -0
  16. package/dist/code-intelligence.d.ts +917 -0
  17. package/dist/code-intelligence.js +699 -0
  18. package/dist/communications.d.ts +1106 -0
  19. package/dist/communications.js +384 -0
  20. package/dist/context.d.ts +787 -0
  21. package/dist/context.js +295 -0
  22. package/dist/daemon-config.d.ts +383 -0
  23. package/dist/daemon-config.js +401 -0
  24. package/dist/file-operations.d.ts +380 -0
  25. package/dist/file-operations.js +282 -0
  26. package/dist/generated/validation/ws-outbound.aot.js +67260 -57392
  27. package/dist/git-hosting.d.ts +117 -0
  28. package/dist/git-hosting.js +109 -0
  29. package/dist/git-operations.d.ts +255 -0
  30. package/dist/git-operations.js +221 -0
  31. package/dist/integration-authorization.d.ts +195 -0
  32. package/dist/integration-authorization.js +125 -0
  33. package/dist/kanban.d.ts +341 -0
  34. package/dist/kanban.js +273 -0
  35. package/dist/loop/rpc-schemas.d.ts +6 -6
  36. package/dist/loop/rpc-schemas.js +1 -0
  37. package/dist/meetings.d.ts +95 -0
  38. package/dist/meetings.js +57 -0
  39. package/dist/messages.d.ts +11175 -11039
  40. package/dist/messages.js +4756 -8700
  41. package/dist/orchestration.d.ts +726 -0
  42. package/dist/orchestration.js +232 -0
  43. package/dist/personality-schemas.d.ts +221 -0
  44. package/dist/personality-schemas.js +340 -0
  45. package/dist/preview.d.ts +140 -0
  46. package/dist/preview.js +98 -0
  47. package/dist/project-knowledge.d.ts +740 -0
  48. package/dist/project-knowledge.js +229 -0
  49. package/dist/project-links.d.ts +102 -0
  50. package/dist/project-links.js +62 -0
  51. package/dist/provider-config.d.ts +87 -2
  52. package/dist/provider-config.js +110 -0
  53. package/dist/provider-manifest.js +7 -0
  54. package/dist/provider-snapshot-codec.d.ts +18 -0
  55. package/dist/provider-snapshot-codec.js +71 -0
  56. package/dist/refine.d.ts +93 -0
  57. package/dist/refine.js +78 -0
  58. package/dist/schedule/rpc-schemas.d.ts +55 -111
  59. package/dist/schedule/types.d.ts +16 -37
  60. package/dist/schedule/types.js +1 -11
  61. package/dist/search/text-match.d.ts +55 -0
  62. package/dist/search/text-match.js +262 -0
  63. package/dist/speech.d.ts +180 -0
  64. package/dist/speech.js +177 -0
  65. package/dist/storage.d.ts +79 -0
  66. package/dist/storage.js +107 -0
  67. package/dist/suggested-tasks.d.ts +106 -0
  68. package/dist/suggested-tasks.js +81 -0
  69. package/dist/terminal-compatibility.d.ts +55 -0
  70. package/dist/terminal-compatibility.js +35 -0
  71. package/dist/terminal-input-mode.d.ts +4 -0
  72. package/dist/terminal-input-mode.js +35 -6
  73. package/dist/terminal-key-input.js +15 -6
  74. package/dist/terminal-profiles.d.ts +35 -0
  75. package/dist/terminal-profiles.js +246 -4
  76. package/dist/tool-call-display.d.ts +2 -2
  77. package/dist/tool-call-display.js +6 -6
  78. package/dist/usage-stats.d.ts +255 -0
  79. package/dist/usage-stats.js +162 -0
  80. package/dist/validation/ws-outbound-schema-metadata.d.ts +1906 -266
  81. package/dist/worktree-ops.d.ts +81 -0
  82. package/dist/worktree-ops.js +88 -0
  83. package/package.json +1 -1
package/dist/brain.js ADDED
@@ -0,0 +1,1100 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Otto Brain wire schemas and their inferred types.
4
+ *
5
+ * Otto Brain is a fork-only capability, so its schemas live in their own
6
+ * protocol module rather than inside `messages.ts`, matching how `kanban.ts`,
7
+ * `artifacts/rpc-schemas.ts`, `communications.ts` and `orchestration.ts` already
8
+ * work. `messages.ts` imports what it needs and composes the message unions.
9
+ */
10
+ export const BrainProfileSchema = z
11
+ .object({
12
+ contextSize: z.number().default(0),
13
+ cacheTypeK: z.string().default(""),
14
+ cacheTypeV: z.string().default(""),
15
+ flashAttention: z.boolean().default(true),
16
+ gpuLayers: z.number().default(0),
17
+ vision: z.boolean().default(false),
18
+ enabledComponents: z.array(z.string()).optional(),
19
+ reasoningBudget: z.number().default(0),
20
+ /**
21
+ * Tri-state: true keeps the reasoning trace across the whole history, false
22
+ * trims it to the last assistant message, null/absent leaves the chat
23
+ * template's own default in charge. Nullable rather than plain optional
24
+ * because "the template decides" is a real stored state, not a missing one.
25
+ */
26
+ // COMPAT(brainPreserveReasoningTriState): widened from boolean in v0.8.11.
27
+ preserveReasoning: z.boolean().nullable().optional(),
28
+ /**
29
+ * Sampler settings, all optional: a brain older than these fields simply
30
+ * does not send them, and the editor renders whatever `fields` describes.
31
+ */
32
+ // COMPAT(brainSamplerProfile): added in v0.8.11, drop the gate when floor >= v0.8.11.
33
+ temperature: z.number().optional(),
34
+ topP: z.number().optional(),
35
+ topK: z.number().optional(),
36
+ minP: z.number().optional(),
37
+ presencePenalty: z.number().optional(),
38
+ repeatPenalty: z.number().optional(),
39
+ parallelSlots: z.number().default(1),
40
+ /** Chats whose KV state may be parked in host RAM; 0 = the engine default. */
41
+ cachedChats: z.number().default(0),
42
+ contextMultiplier: z.number().default(1),
43
+ calibrationRequired: z.boolean().default(true),
44
+ hostingProfileId: z.string().nullable().default(null),
45
+ /** Matches the brain's own default: a profile written before this field
46
+ * existed meant "use the family default", which is `inherit`. */
47
+ hostingProfileMode: z.enum(["inherit", "off", "custom"]).default("inherit"),
48
+ })
49
+ .passthrough();
50
+ export const BrainProfileFieldSchema = z
51
+ .object({
52
+ key: z.string(),
53
+ label: z.string().default(""),
54
+ kind: z.string().default("number"),
55
+ /** One sentence on what the field does, for the client's tooltip. */
56
+ // COMPAT(brainFieldDescription): added in v0.8.11, drop the gate when floor >= v0.8.11.
57
+ description: z.string().nullable().optional(),
58
+ step: z.number().nullable().optional(),
59
+ min: z.number().nullable().optional(),
60
+ max: z.number().nullable().optional(),
61
+ /** Decimal places the value carries; absent means an integer. */
62
+ // COMPAT(brainFieldPrecision): added in v0.8.11, drop the gate when floor >= v0.8.11.
63
+ precision: z.number().nullable().optional(),
64
+ options: z.array(z.union([z.string(), z.number()])).default([]),
65
+ optionLabels: z.array(z.string()).default([]),
66
+ /**
67
+ * What each option stores, index-aligned with `options`. Absent means the
68
+ * option is itself the value; present when the stored value cannot be an
69
+ * option, as for a true/false/null tri-state.
70
+ */
71
+ // COMPAT(brainFieldOptionValues): added in v0.8.11, drop the gate when floor >= v0.8.11.
72
+ optionValues: z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(),
73
+ available: z.boolean().default(true),
74
+ unavailableReason: z.string().nullable().optional(),
75
+ })
76
+ .passthrough();
77
+ export const BrainCalibrationInfoSchema = z
78
+ .object({
79
+ state: z.string().default("unknown"),
80
+ kvBytesPerToken: z.number().nullable().default(null),
81
+ measuredAt: z.string().nullable().default(null),
82
+ measuredOn: z.string().nullable().default(null),
83
+ })
84
+ .passthrough();
85
+ export const BrainModelScoreSchema = z
86
+ .object({
87
+ id: z.string().nullable().default(null),
88
+ displayName: z.string().default(""),
89
+ overall: z.number().default(0),
90
+ runs: z.number().default(0),
91
+ std: z.number().default(0),
92
+ grade: z.string().default(""),
93
+ rank: z.number().nullable().optional(),
94
+ })
95
+ .passthrough();
96
+ export const BrainLogsWatchRequestSchema = z.object({
97
+ type: z.literal("brain.logs.watch.request"),
98
+ watching: z.boolean(),
99
+ requestId: z.string(),
100
+ });
101
+ export const BrainLogLineAddedStatusPayloadSchema = z
102
+ .object({
103
+ status: z.literal("brain_log_line_added"),
104
+ line: z.string(),
105
+ })
106
+ .passthrough();
107
+ /**
108
+ * What the brain on the far side can actually serve. Passthrough so it can grow.
109
+ *
110
+ * Declared here rather than beside the Brain Console RPCs below because
111
+ * BrainHostStatusSchema carries it, and a zod const must be initialised before
112
+ * the schema that references it (see docs on the AOT validator's declaration
113
+ * ordering). Moving it back down produces a module-evaluation TDZ error.
114
+ */
115
+ export const BrainCapabilitiesSchema = z
116
+ .object({
117
+ profiles: z.boolean().default(false),
118
+ budget: z.boolean().default(false),
119
+ logs: z.boolean().default(false),
120
+ delete: z.boolean().default(false),
121
+ load: z.boolean().default(false),
122
+ resources: z.boolean().default(false),
123
+ inventory: z.boolean().default(false),
124
+ /**
125
+ * GET /__host/events: a live SSE stream of complete status snapshots.
126
+ *
127
+ * The daemon reads this before deciding how to watch the brain. False (the
128
+ * default, and what every brain built before the stream reports) keeps the
129
+ * daemon on the ordinary status poll, which is why nothing else about the
130
+ * management API had to change for pushed status to ship.
131
+ */
132
+ events: z.boolean().default(false),
133
+ logEvents: z.boolean().default(false),
134
+ /**
135
+ * Status events include bounded live inference stages, token counts and
136
+ * throughput. False for the first event-stream generation, whose snapshots
137
+ * only moved at phase boundaries.
138
+ */
139
+ liveInference: z.boolean().default(false),
140
+ /** Whether writes are permitted right now (the brain's allowRemoteConfig). */
141
+ writable: z.boolean().default(false),
142
+ /** The remote brain owns benchmark jobs and can list/cancel them. */
143
+ jobs: z.boolean().default(false),
144
+ /** The brain can ask its owning daemon to restart it. */
145
+ restart: z.boolean().default(false),
146
+ /** The host can keep multiple independently supervised model processes resident. */
147
+ processPool: z.boolean().default(false),
148
+ })
149
+ .passthrough();
150
+ // The brain's host status, as the daemon derives it: liveness plus the fields
151
+ // proxied from the brain's own `/__host/status`. Passthrough on the opaque
152
+ // sub-objects so the brain can evolve them without a protocol bump.
153
+ export const BrainHostStatusSchema = z
154
+ .object({
155
+ running: z.boolean(),
156
+ pid: z.number().nullable().optional(),
157
+ version: z.string().nullable().optional(),
158
+ /** The llama.cpp runtime resolved by the Brain host, or "not installed". */
159
+ runtime: z.string().nullable().optional(),
160
+ /**
161
+ * Which generation of the brain's management contract the far side speaks.
162
+ *
163
+ * Additive and separate from `version`, which is the package build. A daemon
164
+ * reads this plus `capabilities` instead of pinning an exact brain version;
165
+ * absent means a brain from before the field existed.
166
+ */
167
+ apiVersion: z.number().nullable().optional(),
168
+ host: z.string().nullable().optional(),
169
+ port: z.number().nullable().optional(),
170
+ displayHost: z.string().nullable().optional(),
171
+ secure: z.boolean().optional(),
172
+ state: z.string().nullable().optional(),
173
+ model: z.string().nullable().optional(),
174
+ modelId: z.string().nullable().optional(),
175
+ /** Every independently hosted resident model process. */
176
+ residents: z
177
+ .array(z
178
+ .object({
179
+ state: z.string(),
180
+ model: z.string().nullable().optional(),
181
+ modelId: z.string().nullable().optional(),
182
+ pid: z.number().nullable().optional(),
183
+ upstream: z.string().nullable().optional(),
184
+ vramBytes: z.number().nullable().optional(),
185
+ })
186
+ .passthrough())
187
+ .optional(),
188
+ vramBytes: z.number().nullable().optional(),
189
+ loadSeconds: z.number().nullable().optional(),
190
+ startedAt: z.string().nullable().optional(),
191
+ lastError: z.string().nullable().optional(),
192
+ telemetry: z.record(z.string(), z.unknown()).nullable().optional(),
193
+ scheduler: z.record(z.string(), z.unknown()).nullable().optional(),
194
+ recent: z.array(z.record(z.string(), z.unknown())).optional(),
195
+ /**
196
+ * What the brain on the far side can serve. Carried here rather than fetched
197
+ * from /__host/capabilities so the client gets it with the status it is
198
+ * already polling, and so it cannot go stale when the owner toggles remote
199
+ * configuration. Null from a brain that predates the management API.
200
+ */
201
+ capabilities: BrainCapabilitiesSchema.nullable().optional(),
202
+ /** Live CPU/RAM/GPU telemetry, only when the request asked for it. */
203
+ resources: z.record(z.string(), z.unknown()).nullable().optional(),
204
+ /** How many log lines the brain currently holds, for the Logs tab. */
205
+ logLineCount: z.number().nullable().optional(),
206
+ /**
207
+ * Which long-running op currently owns the brain, if any. Cheap to carry:
208
+ * the brain reads it from a small file beside its pid file, so this rides on
209
+ * the liveness poll rather than needing `resources: true`.
210
+ *
211
+ * `kind` is a plain string, not an enum: the brain may grow ops this client
212
+ * has never heard of, and the protocol contract forbids narrowing a field
213
+ * later. Unknown kinds fall through to the ordinary busy states.
214
+ */
215
+ activity: z
216
+ .object({
217
+ kind: z.string(),
218
+ target: z.string().nullable().optional(),
219
+ /** Completion in [0,1], for ops that can measure it. */
220
+ progress: z.number().nullable().optional(),
221
+ startedAt: z.string().nullable().optional(),
222
+ })
223
+ .passthrough()
224
+ .nullable()
225
+ .optional(),
226
+ /**
227
+ * Whether any in-flight completion is currently mid-reasoning: reasoning
228
+ * deltas have arrived and no content has yet. Also on the liveness poll,
229
+ * because it is one boolean the router already knows.
230
+ */
231
+ reasoning: z.boolean().nullable().optional(),
232
+ /**
233
+ * Exact aggregate lifecycle counts for requests currently dispatched to
234
+ * llama-server. Additive in host API v2; absent on older brains.
235
+ */
236
+ inference: z
237
+ .object({
238
+ activeRequests: z.number().int().nonnegative().optional(),
239
+ processing: z.number().int().nonnegative().optional(),
240
+ thinking: z.number().int().nonnegative().optional(),
241
+ generating: z.number().int().nonnegative().optional(),
242
+ })
243
+ .passthrough()
244
+ .nullable()
245
+ .optional(),
246
+ /**
247
+ * Slot occupancy split by phase, so a client can tell a prompt being
248
+ * ingested from a model that has started answering. Rides here rather than
249
+ * inside `resources` on purpose: `resources` costs an `nvidia-smi` spawn and
250
+ * is off by default, and this is the half the status rail needs every poll.
251
+ */
252
+ slots: z
253
+ .object({
254
+ total: z.number().nullable().optional(),
255
+ busy: z.number().nullable().optional(),
256
+ idle: z.number().nullable().optional(),
257
+ prefill: z.number().nullable().optional(),
258
+ decode: z.number().nullable().optional(),
259
+ /** Bounded-rate per-slot performance samples; host API v2 and newer. */
260
+ threads: z
261
+ .array(z
262
+ .object({
263
+ slot: z.number().optional(),
264
+ phase: z.enum(["prefill", "decode"]).optional(),
265
+ promptTokens: z.number().nullable().optional(),
266
+ generatedTokens: z.number().nullable().optional(),
267
+ promptTokensPerSecond: z.number().nullable().optional(),
268
+ tokensPerSecond: z.number().nullable().optional(),
269
+ })
270
+ .passthrough())
271
+ .optional(),
272
+ })
273
+ .passthrough()
274
+ .nullable()
275
+ .optional(),
276
+ /** Jobs the scheduler is holding because no slot is free, or a swap is mid-flight. */
277
+ queued: z.number().nullable().optional(),
278
+ /** Whether the daemon reached the brain on its last probe. */
279
+ reachable: z.boolean().nullable().optional(),
280
+ })
281
+ .passthrough();
282
+ export const BrainHostStatusResultSchema = z.object({
283
+ status: BrainHostStatusSchema,
284
+ error: z.string().nullable(),
285
+ requestId: z.string(),
286
+ });
287
+ export const BrainHostStatusRequestSchema = z.object({
288
+ type: z.literal("brain.host.status.request"),
289
+ /**
290
+ * Ask for live CPU/RAM/GPU telemetry alongside the status. Off by default on
291
+ * purpose: it costs an `nvidia-smi` spawn on the brain, and this RPC is also
292
+ * the liveness poll. Only a surface actually
293
+ * rendering the numbers should turn it on.
294
+ */
295
+ resources: z.boolean().default(false),
296
+ requestId: z.string(),
297
+ });
298
+ export const BrainHostStatusResponseSchema = z.object({
299
+ type: z.literal("brain.host.status.response"),
300
+ payload: BrainHostStatusResultSchema,
301
+ });
302
+ export const BrainHostStartRequestSchema = z.object({
303
+ type: z.literal("brain.host.start.request"),
304
+ // Optional model fragment/id to load on start; null = the brain's default.
305
+ model: z.string().nullable().default(null),
306
+ requestId: z.string(),
307
+ });
308
+ export const BrainHostStartResponseSchema = z.object({
309
+ type: z.literal("brain.host.start.response"),
310
+ payload: BrainHostStatusResultSchema,
311
+ });
312
+ export const BrainHostStopRequestSchema = z.object({
313
+ type: z.literal("brain.host.stop.request"),
314
+ requestId: z.string(),
315
+ });
316
+ export const BrainHostStopResponseSchema = z.object({
317
+ type: z.literal("brain.host.stop.response"),
318
+ payload: BrainHostStatusResultSchema,
319
+ });
320
+ export const BrainHostRestartRequestSchema = z.object({
321
+ type: z.literal("brain.host.restart.request"),
322
+ model: z.string().nullable().default(null),
323
+ requestId: z.string(),
324
+ });
325
+ export const BrainHostRestartResponseSchema = z.object({
326
+ type: z.literal("brain.host.restart.response"),
327
+ payload: BrainHostStatusResultSchema,
328
+ });
329
+ // Benchmark rankings/variance/latest, proxied from the brain's `/__host/evals`.
330
+ export const BrainEvalsSchema = z
331
+ .object({
332
+ rankings: z.array(z.record(z.string(), z.unknown())).default([]),
333
+ latest: z.array(z.record(z.string(), z.unknown())).default([]),
334
+ variance: z.array(z.record(z.string(), z.unknown())).default([]),
335
+ runCount: z.number().default(0),
336
+ })
337
+ .passthrough();
338
+ export const BrainEvalsGetRequestSchema = z.object({
339
+ type: z.literal("brain.evals.get.request"),
340
+ requestId: z.string(),
341
+ });
342
+ export const BrainEvalsGetResponseSchema = z.object({
343
+ type: z.literal("brain.evals.get.response"),
344
+ payload: z.object({
345
+ evals: BrainEvalsSchema.nullable(),
346
+ error: z.string().nullable(),
347
+ requestId: z.string(),
348
+ }),
349
+ });
350
+ // Brain network auto-discovery: the daemon enumerates this host's bind
351
+ // addresses and probes the local `tailscale` CLI so the client can offer the
352
+ // operator a pick-list of likely listen hosts (and pre-fill the tailscale TLS
353
+ // mode) instead of asking them to hunt for IPs by hand.
354
+ // Gated by server_info.features.brainNetworkDiscovery.
355
+ export const BrainTailscaleInfoSchema = z
356
+ .object({
357
+ // Whether the tailscale CLI is present and its daemon answers.
358
+ available: z.boolean(),
359
+ // This machine's MagicDNS name, e.g. greyskull.tail279562.ts.net.
360
+ hostname: z.string().nullable().optional(),
361
+ // The tailnet IPv4 address, for a tailnet-only bind.
362
+ ipv4: z.string().nullable().optional(),
363
+ // The default directory the brain writes issued certificates to.
364
+ certDir: z.string().nullable().optional(),
365
+ })
366
+ .passthrough();
367
+ // One candidate value for `listen.host`, with a human label for the pick-list.
368
+ export const BrainBindAddressSchema = z
369
+ .object({
370
+ // The literal value written to listen.host (an IP, 0.0.0.0, or "tailscale").
371
+ value: z.string(),
372
+ // Display label, e.g. "Local only", "All interfaces", "192.168.1.42 (en0)".
373
+ label: z.string(),
374
+ kind: z.enum(["loopback", "all", "lan", "tailscale"]),
375
+ })
376
+ .passthrough();
377
+ export const BrainNetworkInfoSchema = z
378
+ .object({
379
+ addresses: z.array(BrainBindAddressSchema).default([]),
380
+ tailscale: BrainTailscaleInfoSchema.nullable().optional(),
381
+ })
382
+ .passthrough();
383
+ // Detected model names for the settings pickers. Read from the brain's
384
+ // /v1/models when it is reachable (local child up, or remote); empty otherwise,
385
+ // which the client renders as a disabled picker. Gated by features.brainStatus.
386
+ export const BrainModelsListRequestSchema = z.object({
387
+ type: z.literal("brain.models.list.request"),
388
+ requestId: z.string(),
389
+ });
390
+ export const BrainModelsListResponseSchema = z.object({
391
+ type: z.literal("brain.models.list.response"),
392
+ payload: z.object({
393
+ models: z.array(z.string()).default([]),
394
+ error: z.string().nullable(),
395
+ requestId: z.string(),
396
+ }),
397
+ });
398
+ // Read/write a *remote* brain's own config (its /__host/config). Only valid in
399
+ // brain.mode "remote"; the config is the remote brain's effective config with
400
+ // secrets redacted. Editable fields are model-related (defaultModel,
401
+ // maxLoadedModels, lockedModels, lockModel);
402
+ // network/TLS/auth stay host-owned. Gated by features.brainRemote.
403
+ export const BrainRemoteConfigSchema = z.record(z.string(), z.unknown());
404
+ export const BrainRemoteConfigGetRequestSchema = z.object({
405
+ type: z.literal("brain.remote.config.get.request"),
406
+ requestId: z.string(),
407
+ });
408
+ export const BrainRemoteConfigGetResponseSchema = z.object({
409
+ type: z.literal("brain.remote.config.get.response"),
410
+ payload: z.object({
411
+ config: BrainRemoteConfigSchema.nullable(),
412
+ error: z.string().nullable(),
413
+ requestId: z.string(),
414
+ }),
415
+ });
416
+ export const BrainRemoteConfigPatchRequestSchema = z.object({
417
+ type: z.literal("brain.remote.config.patch.request"),
418
+ patch: BrainRemoteConfigSchema,
419
+ requestId: z.string(),
420
+ });
421
+ export const BrainRemoteConfigPatchResponseSchema = z.object({
422
+ type: z.literal("brain.remote.config.patch.response"),
423
+ payload: z.object({
424
+ config: BrainRemoteConfigSchema.nullable(),
425
+ error: z.string().nullable(),
426
+ requestId: z.string(),
427
+ }),
428
+ });
429
+ export const BrainNetworkDiscoverRequestSchema = z.object({
430
+ type: z.literal("brain.network.discover.request"),
431
+ requestId: z.string(),
432
+ });
433
+ export const BrainNetworkDiscoverResponseSchema = z.object({
434
+ type: z.literal("brain.network.discover.response"),
435
+ payload: z.object({
436
+ info: BrainNetworkInfoSchema.nullable(),
437
+ error: z.string().nullable(),
438
+ requestId: z.string(),
439
+ }),
440
+ });
441
+ // An installed local model, from `otto-brain scan`. Passthrough so the brain's
442
+ // scan row can grow fields without a protocol bump.
443
+ export const BrainInstalledModelSchema = z
444
+ .object({
445
+ model: z.string().default(""),
446
+ arch: z.string().default(""),
447
+ quant: z.string().default(""),
448
+ size: z.string().default(""),
449
+ ctx: z.string().default(""),
450
+ vision: z.string().default(""),
451
+ calibrated: z.string().default(""),
452
+ features: z.string().default(""),
453
+ source: z.string().default(""),
454
+ })
455
+ .passthrough();
456
+ // A downloadable catalog model, annotated with whether it is already installed
457
+ // (the daemon reuses the brain's authoritative catalog↔model join). Passthrough
458
+ // over the catalog entry's optional metadata.
459
+ export const BrainCatalogModelSchema = z
460
+ .object({
461
+ id: z.string(),
462
+ name: z.string().default(""),
463
+ family: z.string().nullable().optional(),
464
+ favorite: z.boolean().default(false),
465
+ installed: z.boolean().default(false),
466
+ publisher: z.string().default(""),
467
+ repo: z.string().default(""),
468
+ quant: z.string().default(""),
469
+ params: z.string().default(""),
470
+ sizeBytes: z.number().nullable().optional(),
471
+ size: z.string().default(""),
472
+ vision: z.boolean().default(false),
473
+ thinking: z.boolean().default(false),
474
+ contextMax: z.number().nullable().optional(),
475
+ tier: z.string().default(""),
476
+ useCases: z.array(z.string()).default([]),
477
+ why: z.string().default(""),
478
+ components: z
479
+ .array(z.object({
480
+ id: z.string(),
481
+ label: z.string().default(""),
482
+ description: z.string().default(""),
483
+ role: z.string().default(""),
484
+ hfRepo: z.string().optional(),
485
+ file: z.string().default(""),
486
+ bytes: z.number().nullable().optional(),
487
+ required: z.boolean().default(false),
488
+ defaultDownload: z.boolean().default(false),
489
+ defaultLoad: z.boolean().default(false),
490
+ minRuntimeBuild: z.number().optional(),
491
+ }))
492
+ .optional(),
493
+ })
494
+ .passthrough();
495
+ // An installed llama.cpp runtime, from `otto-brain runtime list`.
496
+ export const BrainRuntimeSchema = z
497
+ .object({
498
+ label: z.string().default(""),
499
+ // A human-readable runtime identity. The filesystem-safe `label` remains
500
+ // for older hosts and destructive operations, not for UI presentation.
501
+ displayName: z.string().default(""),
502
+ version: z.string().default(""),
503
+ source: z.string().default(""),
504
+ dir: z.string().default(""),
505
+ })
506
+ .passthrough();
507
+ // A tracked long-running brain operation. The client polls brain.jobs.list and
508
+ // renders progress. `percent` is null when the job reports no measurable
509
+ // progress (indeterminate). Terminal jobs linger briefly so the UI can show
510
+ // the outcome before they are pruned.
511
+ export const BrainJobKindSchema = z.enum([
512
+ "pull",
513
+ "runtime-install",
514
+ "runtime-remove",
515
+ "calibrate",
516
+ "sweep",
517
+ "bench",
518
+ ]);
519
+ export const BrainJobStatusSchema = z.enum(["running", "succeeded", "failed", "canceled"]);
520
+ export const BrainJobSchema = z
521
+ .object({
522
+ id: z.string(),
523
+ kind: BrainJobKindSchema,
524
+ // A short human label, e.g. "Download Phi-4 (14B)".
525
+ label: z.string().default(""),
526
+ // The subject id (catalog id, model name, or build tag) this job acts on.
527
+ target: z.string().nullable().default(null),
528
+ status: BrainJobStatusSchema.default("running"),
529
+ /** Null once admitted; a positive value means this operation is pending. */
530
+ queuePosition: z.number().int().positive().nullable().optional(),
531
+ percent: z.number().nullable().default(null),
532
+ // The latest progress line (e.g. "extracting…", "budget 512: done").
533
+ message: z.string().nullable().default(null),
534
+ error: z.string().nullable().default(null),
535
+ startedAt: z.string().default(""),
536
+ finishedAt: z.string().nullable().default(null),
537
+ })
538
+ .passthrough();
539
+ // Every job-starting RPC returns the created (or refused) job under this shape.
540
+ export const BrainJobResultSchema = z.object({
541
+ job: BrainJobSchema.nullable(),
542
+ error: z.string().nullable(),
543
+ requestId: z.string(),
544
+ });
545
+ // Every job-listing RPC returns the active + recently-finished jobs.
546
+ export const BrainJobsResultSchema = z.object({
547
+ jobs: z.array(BrainJobSchema).default([]),
548
+ error: z.string().nullable(),
549
+ requestId: z.string(),
550
+ });
551
+ // Installed models - `otto-brain scan`.
552
+ export const BrainModelsScanRequestSchema = z.object({
553
+ type: z.literal("brain.models.scan.request"),
554
+ requestId: z.string(),
555
+ });
556
+ export const BrainModelsScanResponseSchema = z.object({
557
+ type: z.literal("brain.models.scan.response"),
558
+ payload: z.object({
559
+ models: z.array(BrainInstalledModelSchema).default([]),
560
+ error: z.string().nullable(),
561
+ requestId: z.string(),
562
+ }),
563
+ });
564
+ // Downloadable catalog - `otto-brain catalog`.
565
+ export const BrainCatalogListRequestSchema = z.object({
566
+ type: z.literal("brain.catalog.list.request"),
567
+ requestId: z.string(),
568
+ });
569
+ export const BrainCatalogListResponseSchema = z.object({
570
+ type: z.literal("brain.catalog.list.response"),
571
+ payload: z.object({
572
+ models: z.array(BrainCatalogModelSchema).default([]),
573
+ error: z.string().nullable(),
574
+ requestId: z.string(),
575
+ }),
576
+ });
577
+ // Installed runtimes - `otto-brain runtime list`.
578
+ export const BrainRuntimeListRequestSchema = z.object({
579
+ type: z.literal("brain.runtime.list.request"),
580
+ requestId: z.string(),
581
+ });
582
+ export const BrainRuntimeListResponseSchema = z.object({
583
+ type: z.literal("brain.runtime.list.response"),
584
+ payload: z.object({
585
+ runtimes: z.array(BrainRuntimeSchema).default([]),
586
+ error: z.string().nullable(),
587
+ requestId: z.string(),
588
+ }),
589
+ });
590
+ // Download a catalog model - starts a `pull` job.
591
+ export const BrainModelsPullRequestSchema = z.object({
592
+ type: z.literal("brain.models.pull.request"),
593
+ // Catalog id or name fragment.
594
+ model: z.string(),
595
+ quant: z.string().optional(),
596
+ // COMPAT(brainModelBundles): added in v0.8.7, drop the gate when floor >= v0.8.7.
597
+ components: z.array(z.string()).optional(),
598
+ // COMPAT(brainBundleDownloadQueue): added in v0.8.8, drop the gate when floor >= v0.8.8.
599
+ // Advisory aggregate byte budget for one selected quant plus its components.
600
+ expectedBytes: z.number().nonnegative().optional(),
601
+ requestId: z.string(),
602
+ });
603
+ export const BrainModelsPullResponseSchema = z.object({
604
+ type: z.literal("brain.models.pull.response"),
605
+ payload: BrainJobResultSchema,
606
+ });
607
+ // Install a llama.cpp runtime - starts a `runtime-install` job.
608
+ export const BrainRuntimeInstallRequestSchema = z.object({
609
+ type: z.literal("brain.runtime.install.request"),
610
+ // Optional llama.cpp release build tag; null = the brain's default.
611
+ build: z.string().nullable().default(null),
612
+ requestId: z.string(),
613
+ });
614
+ export const BrainRuntimeInstallResponseSchema = z.object({
615
+ type: z.literal("brain.runtime.install.response"),
616
+ payload: BrainJobResultSchema,
617
+ });
618
+ export const BrainRuntimeRemoveRequestSchema = z.object({
619
+ type: z.literal("brain.runtime.remove.request"),
620
+ name: z.string(),
621
+ requestId: z.string(),
622
+ });
623
+ export const BrainRuntimeRemoveResponseSchema = z.object({
624
+ type: z.literal("brain.runtime.remove.response"),
625
+ payload: BrainJobResultSchema,
626
+ });
627
+ // Measure real KV bytes/token for a model - starts a `calibrate` job. Needs a
628
+ // runtime + GPU; refused with a helpful error otherwise.
629
+ export const BrainCalibrateRequestSchema = z.object({
630
+ type: z.literal("brain.calibrate.request"),
631
+ model: z.string(),
632
+ requestId: z.string(),
633
+ });
634
+ export const BrainCalibrateResponseSchema = z.object({
635
+ type: z.literal("brain.calibrate.response"),
636
+ payload: BrainJobResultSchema,
637
+ });
638
+ // Find the best reasoning budget for a model - starts a `sweep` job.
639
+ export const BrainSweepRequestSchema = z.object({
640
+ type: z.literal("brain.sweep.request"),
641
+ model: z.string(),
642
+ requestId: z.string(),
643
+ });
644
+ export const BrainSweepResponseSchema = z.object({
645
+ type: z.literal("brain.sweep.response"),
646
+ payload: BrainJobResultSchema,
647
+ });
648
+ // Run the agentic-coding benchmark - starts a `bench` job. `model` is an
649
+ // optional comma list of name fragments; null lets the brain pick.
650
+ export const BrainBenchRequestSchema = z.object({
651
+ type: z.literal("brain.bench.request"),
652
+ model: z.string().nullable().default(null),
653
+ requestId: z.string(),
654
+ });
655
+ export const BrainBenchResponseSchema = z.object({
656
+ type: z.literal("brain.bench.response"),
657
+ payload: BrainJobResultSchema,
658
+ });
659
+ // Poll the active + recently-finished jobs.
660
+ export const BrainJobsListRequestSchema = z.object({
661
+ type: z.literal("brain.jobs.list.request"),
662
+ requestId: z.string(),
663
+ });
664
+ export const BrainJobsListResponseSchema = z.object({
665
+ type: z.literal("brain.jobs.list.response"),
666
+ payload: BrainJobsResultSchema,
667
+ });
668
+ // Cancel a running job; returns the refreshed job list.
669
+ export const BrainJobsCancelRequestSchema = z.object({
670
+ type: z.literal("brain.jobs.cancel.request"),
671
+ jobId: z.string(),
672
+ requestId: z.string(),
673
+ });
674
+ export const BrainJobsCancelResponseSchema = z.object({
675
+ type: z.literal("brain.jobs.cancel.response"),
676
+ payload: BrainJobsResultSchema,
677
+ });
678
+ // One GGUF repo from a Hugging Face search. Passthrough so the brain's search
679
+ // row can grow fields without a protocol bump.
680
+ export const BrainHfSearchResultSchema = z
681
+ .object({
682
+ repo: z.string().default(""),
683
+ downloads: z.number().default(0),
684
+ likes: z.number().default(0),
685
+ gated: z.boolean().default(false),
686
+ // A short, source-authored excerpt extracted from the repository's model card.
687
+ summary: z.string().nullable().optional(),
688
+ // True when any quant of this repo is already on disk.
689
+ installed: z.boolean().default(false),
690
+ })
691
+ .passthrough();
692
+ // One downloadable quantization of a repo - `otto-brain add <repo> --list-quants`.
693
+ export const BrainRepoQuantSchema = z
694
+ .object({
695
+ quant: z.string().default(""),
696
+ size: z.string().default(""),
697
+ sizeBytes: z.number().default(0),
698
+ files: z.number().default(0),
699
+ fileNames: z.array(z.string()).optional(),
700
+ // True when this specific quant is already on disk.
701
+ installed: z.boolean().default(false),
702
+ // The installed model's stable id, when this quant is already on disk.
703
+ // Optional so daemons predating quant deletion remain compatible.
704
+ modelId: z.string().nullable().optional(),
705
+ // The shared projector detected in this repository. It is repeated on
706
+ // each quant row because the quant picker is the unit that discovers and
707
+ // presents a downloadable Hugging Face bundle.
708
+ projector: z
709
+ .object({
710
+ file: z.string(),
711
+ sizeBytes: z.number(),
712
+ // COMPAT(brainDiscoveredProjectorState): added in v0.8.7, remove after 2027-02-11.
713
+ installed: z.boolean().optional(),
714
+ })
715
+ .optional(),
716
+ })
717
+ .passthrough();
718
+ // Search Hugging Face for GGUF models - `otto-brain search <query>`.
719
+ export const BrainHfSearchRequestSchema = z.object({
720
+ type: z.literal("brain.hf.search.request"),
721
+ query: z.string(),
722
+ limit: z.number().nullable().default(null),
723
+ requestId: z.string(),
724
+ });
725
+ export const BrainHfSearchResponseSchema = z.object({
726
+ type: z.literal("brain.hf.search.response"),
727
+ payload: z.object({
728
+ results: z.array(BrainHfSearchResultSchema).default([]),
729
+ error: z.string().nullable(),
730
+ requestId: z.string(),
731
+ }),
732
+ });
733
+ // List the quantizations a repo offers - `otto-brain add <repo> --list-quants`.
734
+ export const BrainHfQuantsRequestSchema = z.object({
735
+ type: z.literal("brain.hf.quants.request"),
736
+ repo: z.string(),
737
+ requestId: z.string(),
738
+ });
739
+ export const BrainHfQuantsResponseSchema = z.object({
740
+ type: z.literal("brain.hf.quants.response"),
741
+ payload: z.object({
742
+ quants: z.array(BrainRepoQuantSchema).default([]),
743
+ error: z.string().nullable(),
744
+ requestId: z.string(),
745
+ }),
746
+ });
747
+ // Download a chosen quant of an arbitrary HF repo - starts a `pull` job.
748
+ export const BrainModelsAddRequestSchema = z.object({
749
+ type: z.literal("brain.models.add.request"),
750
+ repo: z.string(),
751
+ quant: z.string(),
752
+ // COMPAT(brainDiscoveredBundleComponents): added in v0.8.7, remove after 2027-02-11.
753
+ components: z.array(z.string()).optional(),
754
+ // COMPAT(brainBundleDownloadQueue): added in v0.8.8, drop the gate when floor >= v0.8.8.
755
+ expectedBytes: z.number().nonnegative().optional(),
756
+ requestId: z.string(),
757
+ });
758
+ export const BrainModelsAddResponseSchema = z.object({
759
+ type: z.literal("brain.models.add.response"),
760
+ payload: BrainJobResultSchema,
761
+ });
762
+ /** A Brain-owned, named llama-server template composition. */
763
+ export const BrainHostingProfileSchema = z
764
+ .object({
765
+ id: z.string(),
766
+ name: z.string(),
767
+ family: z.string(),
768
+ description: z.string().default(""),
769
+ template: z.string().nullable().default(null),
770
+ systemPromptAddendum: z.string().nullable().default(null),
771
+ templateKwargs: z.record(z.string(), z.unknown()).default({}),
772
+ })
773
+ .passthrough();
774
+ /** A note on a field. `blocksStart` means this combination cannot load at all. */
775
+ export const BrainProfileWarningSchema = z
776
+ .object({
777
+ field: z.string().nullable().default(null),
778
+ severity: z.string().default("info"),
779
+ message: z.string().default(""),
780
+ blocksStart: z.boolean().default(false),
781
+ })
782
+ .passthrough();
783
+ /** The VRAM breakdown for a profile. Raw bytes; the client formats at the edge. */
784
+ export const BrainBudgetSchema = z
785
+ .object({
786
+ weightsBytes: z.number().default(0),
787
+ mmprojBytes: z.number().default(0),
788
+ componentBytes: z.number().optional(),
789
+ drafterKvBytes: z.number().optional(),
790
+ imageProcessingBytes: z.number().optional(),
791
+ kvBytes: z.number().default(0),
792
+ overheadBytes: z.number().default(0),
793
+ totalBytes: z.number().default(0),
794
+ usableBytes: z.number().default(0),
795
+ totalVramBytes: z.number().default(0),
796
+ reserveBytes: z.number().default(0),
797
+ kvBytesPerToken: z.number().default(0),
798
+ source: z.string().default("unknown"),
799
+ theoreticalKvBytesPerToken: z.number().nullable().default(null),
800
+ fits: z.boolean().default(false),
801
+ headroomBytes: z.number().default(0),
802
+ utilization: z.number().default(0),
803
+ })
804
+ .passthrough();
805
+ /**
806
+ * One installed model with everything the Models tab shows, joined by the brain.
807
+ * The client must not have to correlate the scan, the metadata and the rankings
808
+ * itself: they key on different things and only the brain knows the file layout.
809
+ */
810
+ export const BrainInventoryModelSchema = z
811
+ .object({
812
+ id: z.string(),
813
+ displayName: z.string().default(""),
814
+ family: z.string().nullable().optional(),
815
+ publisher: z.string().nullable().default(null),
816
+ quant: z.string().nullable().default(null),
817
+ sizeBytes: z.number().default(0),
818
+ mmprojBytes: z.number().default(0),
819
+ origin: z.string().nullable().default(null),
820
+ arch: z.string().nullable().default(null),
821
+ contextLength: z.number().nullable().default(null),
822
+ blockCount: z.number().nullable().default(null),
823
+ headCountKv: z.number().nullable().default(null),
824
+ hasProjector: z.boolean().default(false),
825
+ components: z
826
+ .array(z.object({
827
+ id: z.string(),
828
+ label: z.string().default(""),
829
+ description: z.string().default(""),
830
+ role: z.string().default(""),
831
+ bytes: z.number().default(0),
832
+ available: z.boolean().default(false),
833
+ unavailableReason: z.string().optional(),
834
+ required: z.boolean().default(false),
835
+ defaultDownload: z.boolean().default(false),
836
+ defaultLoad: z.boolean().default(false),
837
+ minRuntimeBuild: z.number().optional(),
838
+ }))
839
+ .nullable()
840
+ .optional(),
841
+ reasoning: z.boolean().default(false),
842
+ mtp: z.boolean().default(false),
843
+ distilled: z.boolean().default(false),
844
+ useCases: z.array(z.string()).default([]),
845
+ tier: z.string().nullable().default(null),
846
+ profile: BrainProfileSchema.nullable().default(null),
847
+ calibration: BrainCalibrationInfoSchema.nullable().default(null),
848
+ budget: BrainBudgetSchema.nullable().default(null),
849
+ maxContextThatFits: z.number().nullable().default(null),
850
+ score: BrainModelScoreSchema.nullable().default(null),
851
+ state: z.string().default("not-loaded"),
852
+ warnings: z.array(BrainProfileWarningSchema).default([]),
853
+ })
854
+ .passthrough();
855
+ export const BrainDiskUsageSchema = z
856
+ .object({
857
+ freeBytes: z.number().default(0),
858
+ totalBytes: z.number().default(0),
859
+ modelBytes: z.number().default(0),
860
+ })
861
+ .passthrough();
862
+ // The joined inventory - GET /__host/models.
863
+ export const BrainModelsInventoryRequestSchema = z.object({
864
+ type: z.literal("brain.models.inventory.request"),
865
+ requestId: z.string(),
866
+ });
867
+ export const BrainModelsInventoryResponseSchema = z.object({
868
+ type: z.literal("brain.models.inventory.response"),
869
+ payload: z.object({
870
+ models: z.array(BrainInventoryModelSchema).default([]),
871
+ disk: BrainDiskUsageSchema.nullable().default(null),
872
+ error: z.string().nullable(),
873
+ requestId: z.string(),
874
+ }),
875
+ });
876
+ // Read a model's profile plus the descriptors an editor renders from.
877
+ export const BrainModelProfileGetRequestSchema = z.object({
878
+ type: z.literal("brain.model.profile.get.request"),
879
+ modelId: z.string(),
880
+ requestId: z.string(),
881
+ });
882
+ export const BrainModelProfileGetResponseSchema = z.object({
883
+ type: z.literal("brain.model.profile.get.response"),
884
+ payload: z.object({
885
+ profile: BrainProfileSchema.nullable().default(null),
886
+ fields: z.array(BrainProfileFieldSchema).default([]),
887
+ warnings: z.array(BrainProfileWarningSchema).default([]),
888
+ calibration: BrainCalibrationInfoSchema.nullable().default(null),
889
+ /** True when a previous resident-model edit still awaits a reload. */
890
+ requiresRestart: z.boolean().default(false),
891
+ hostingProfiles: z.array(BrainHostingProfileSchema).default([]),
892
+ familyHostingProfileId: z.string().nullable().default(null),
893
+ error: z.string().nullable(),
894
+ requestId: z.string(),
895
+ }),
896
+ });
897
+ // Write the editable fields. `patch` carries only the eight the brain accepts;
898
+ // anything else is ignored there rather than rejected, so an older client that
899
+ // sends a field a newer brain dropped still succeeds.
900
+ export const BrainModelProfileSetRequestSchema = z.object({
901
+ type: z.literal("brain.model.profile.set.request"),
902
+ modelId: z.string(),
903
+ patch: z.record(z.string(), z.unknown()),
904
+ requestId: z.string(),
905
+ });
906
+ export const BrainModelProfileSetResponseSchema = z.object({
907
+ type: z.literal("brain.model.profile.set.response"),
908
+ payload: z.object({
909
+ profile: BrainProfileSchema.nullable().default(null),
910
+ fields: z.array(BrainProfileFieldSchema).default([]),
911
+ /** Human-readable notes about anything clamped or ignored. */
912
+ adjustments: z.array(z.string()).default([]),
913
+ warnings: z.array(BrainProfileWarningSchema).default([]),
914
+ calibration: BrainCalibrationInfoSchema.nullable().default(null),
915
+ budget: BrainBudgetSchema.nullable().default(null),
916
+ maxContextThatFits: z.number().nullable().default(null),
917
+ /** True when the edited model is the resident one, so a restart applies it. */
918
+ requiresRestart: z.boolean().default(false),
919
+ hostingProfiles: z.array(BrainHostingProfileSchema).default([]),
920
+ familyHostingProfileId: z.string().nullable().default(null),
921
+ error: z.string().nullable(),
922
+ requestId: z.string(),
923
+ }),
924
+ });
925
+ // The budget for a hypothetical profile - GET /__host/model/budget. `overrides`
926
+ // are string-encoded field values so the UI can preview a budget while a control
927
+ // is mid-drag, without persisting a value the user is scrubbing past.
928
+ export const BrainModelBudgetGetRequestSchema = z.object({
929
+ type: z.literal("brain.model.budget.get.request"),
930
+ modelId: z.string(),
931
+ overrides: z.record(z.string(), z.string()).default({}),
932
+ requestId: z.string(),
933
+ });
934
+ export const BrainModelBudgetGetResponseSchema = z.object({
935
+ type: z.literal("brain.model.budget.get.response"),
936
+ payload: z.object({
937
+ profile: BrainProfileSchema.nullable().default(null),
938
+ budget: BrainBudgetSchema.nullable().default(null),
939
+ maxContextThatFits: z.number().nullable().default(null),
940
+ gpu: z.record(z.string(), z.unknown()).nullable().default(null),
941
+ warnings: z.array(BrainProfileWarningSchema).default([]),
942
+ error: z.string().nullable(),
943
+ requestId: z.string(),
944
+ }),
945
+ });
946
+ // Load a model into the running brain. Distinct from brain.host.start, which
947
+ // restarts the daemon's child and has no remote equivalent.
948
+ export const BrainModelLoadRequestSchema = z.object({
949
+ type: z.literal("brain.model.load.request"),
950
+ modelId: z.string(),
951
+ requestId: z.string(),
952
+ });
953
+ export const BrainModelLoadResponseSchema = z.object({
954
+ type: z.literal("brain.model.load.response"),
955
+ payload: z.object({
956
+ status: BrainHostStatusSchema.nullable().default(null),
957
+ /** The profile actually used: the brain clamps context to fit VRAM. */
958
+ profile: BrainProfileSchema.nullable().default(null),
959
+ error: z.string().nullable(),
960
+ requestId: z.string(),
961
+ }),
962
+ });
963
+ export const BrainModelUnloadRequestSchema = z.object({
964
+ type: z.literal("brain.model.unload.request"),
965
+ requestId: z.string(),
966
+ });
967
+ export const BrainModelUnloadResponseSchema = z.object({
968
+ type: z.literal("brain.model.unload.response"),
969
+ payload: z.object({
970
+ status: BrainHostStatusSchema.nullable().default(null),
971
+ error: z.string().nullable(),
972
+ requestId: z.string(),
973
+ }),
974
+ });
975
+ // Delete a model's files. The brain refuses while that model is loaded.
976
+ export const BrainModelDeleteRequestSchema = z.object({
977
+ type: z.literal("brain.model.delete.request"),
978
+ modelId: z.string(),
979
+ requestId: z.string(),
980
+ });
981
+ export const BrainModelDeleteResponseSchema = z.object({
982
+ type: z.literal("brain.model.delete.response"),
983
+ payload: z.object({
984
+ deleted: z.array(z.string()).default([]),
985
+ freedBytes: z.number().default(0),
986
+ includesProjector: z.boolean().default(false),
987
+ /** How many models remain after the re-scan. */
988
+ remaining: z.number().default(0),
989
+ error: z.string().nullable(),
990
+ requestId: z.string(),
991
+ }),
992
+ });
993
+ export const BrainModelComponentDeleteRequestSchema = z.object({
994
+ type: z.literal("brain.model.component.delete.request"),
995
+ modelId: z.string(),
996
+ componentId: z.string(),
997
+ requestId: z.string(),
998
+ });
999
+ export const BrainModelComponentDeleteResponseSchema = z.object({
1000
+ type: z.literal("brain.model.component.delete.response"),
1001
+ payload: z.object({
1002
+ deleted: z.array(z.string()).default([]),
1003
+ freedBytes: z.number().default(0),
1004
+ error: z.string().nullable(),
1005
+ requestId: z.string(),
1006
+ }),
1007
+ });
1008
+ // Rename a model's display name. The brain rejects a collision with another
1009
+ // model's current id/displayName: /v1/models keys its id on displayName, and
1010
+ // both the completion path and defaultModel/switchTo resolve a model by
1011
+ // displayName === name || id === name - a duplicate would strand one model
1012
+ // unreachable by name with no error anywhere else in the chain.
1013
+ export const BrainModelRenameRequestSchema = z.object({
1014
+ type: z.literal("brain.model.rename.request"),
1015
+ modelId: z.string(),
1016
+ displayName: z.string(),
1017
+ requestId: z.string(),
1018
+ });
1019
+ export const BrainModelRenameResponseSchema = z.object({
1020
+ type: z.literal("brain.model.rename.response"),
1021
+ payload: z.object({
1022
+ displayName: z.string().nullable().default(null),
1023
+ error: z.string().nullable(),
1024
+ requestId: z.string(),
1025
+ }),
1026
+ });
1027
+ // Reset a model's display name back to its scan-derived default.
1028
+ export const BrainModelRenameResetRequestSchema = z.object({
1029
+ type: z.literal("brain.model.rename.reset.request"),
1030
+ modelId: z.string(),
1031
+ requestId: z.string(),
1032
+ });
1033
+ export const BrainModelRenameResetResponseSchema = z.object({
1034
+ type: z.literal("brain.model.rename.reset.response"),
1035
+ payload: z.object({
1036
+ displayName: z.string().nullable().default(null),
1037
+ error: z.string().nullable(),
1038
+ requestId: z.string(),
1039
+ }),
1040
+ });
1041
+ // Tail the current Brain service log, including managed child processes and host operations.
1042
+ export const BrainLogsTailRequestSchema = z.object({
1043
+ type: z.literal("brain.logs.tail.request"),
1044
+ limit: z.number().nullable().default(null),
1045
+ requestId: z.string(),
1046
+ });
1047
+ export const BrainLogsTailResponseSchema = z.object({
1048
+ type: z.literal("brain.logs.tail.response"),
1049
+ payload: z.object({
1050
+ lines: z.array(z.string()).default([]),
1051
+ total: z.number().default(0),
1052
+ state: z.string().nullable().default(null),
1053
+ command: z.string().nullable().default(null),
1054
+ error: z.string().nullable(),
1055
+ requestId: z.string(),
1056
+ }),
1057
+ });
1058
+ /**
1059
+ * Declare whether this client wants live Brain log lines pushed to it.
1060
+ *
1061
+ * Log lines are the one Brain push with no coalescing - the Logs tab exists to
1062
+ * show the exact ordered stream, so the daemon cannot merge them the way it
1063
+ * merges status snapshots. That makes them the heaviest Brain feed on the wire,
1064
+ * and until this request existed every connected client paid for them whether or
1065
+ * not the Logs tab was open. A phone on the relay paid the worst of it: decrypt,
1066
+ * parse and validate every llama-server line only for the client to drop it,
1067
+ * because `applyBrainLogLineAdded` no-ops when the Logs query holds no data.
1068
+ *
1069
+ * Watching is per socket, not per session, so a desktop with the Logs tab open
1070
+ * does not turn the feed on for the same account's phone. Nothing is lost by not
1071
+ * watching: the brain owns the durable ring buffer, so opening the tab tails it
1072
+ * (`brain.logs.tail.request`) and the push only appends from there.
1073
+ */
1074
+ export const BrainLogsWatchResponseSchema = z.object({
1075
+ type: z.literal("brain.logs.watch.response"),
1076
+ payload: z.object({
1077
+ watching: z.boolean(),
1078
+ requestId: z.string(),
1079
+ }),
1080
+ });
1081
+ /**
1082
+ * The brain's own state, pushed the moment it changes.
1083
+ *
1084
+ * A complete cheap `BrainHostStatus` snapshot, never a delta - which is what
1085
+ * makes a missed message and a reconnect the same, idempotent, recovery. It
1086
+ * excludes `resources`, whose collection spawns `nvidia-smi` on the brain host
1087
+ * and stays an opt-in pull for the Overview tab.
1088
+ *
1089
+ * Scoped by the daemon connection that delivers it: the client writes it under
1090
+ * that runtime's `serverId`, so two connected hosts cannot overwrite each
1091
+ * other's brain state. Gated by `server_info.features.brainStatusPush`, which
1092
+ * is true only when both the daemon and the brain it reaches support the stream.
1093
+ */
1094
+ export const BrainStatusChangedStatusPayloadSchema = z
1095
+ .object({
1096
+ status: z.literal("brain_status_changed"),
1097
+ brain: BrainHostStatusSchema,
1098
+ })
1099
+ .passthrough();
1100
+ //# sourceMappingURL=brain.js.map