@paradow/ledge 0.19.1 → 0.21.0

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 (38) hide show
  1. package/dist/cli/{attach-3BKQSEWC.js → attach-IVICSJYB.js} +11 -11
  2. package/dist/cli/{bridge-APNFBPNE.js → bridge-H3ZPGIUY.js} +2 -2
  3. package/dist/cli/{chunk-3HCID6NW.js → chunk-24AHC55Y.js} +3 -3
  4. package/dist/cli/{chunk-3EIRKAX7.js → chunk-2VSEIOGA.js} +4 -4
  5. package/dist/cli/{chunk-TRTS24PF.js → chunk-35BPMR6P.js} +2 -2
  6. package/dist/cli/{chunk-43D6NMYH.js → chunk-6IYPRUQG.js} +1 -1
  7. package/dist/cli/{chunk-ZUYOA55W.js → chunk-AJFC33TH.js} +2 -2
  8. package/dist/cli/{chunk-LOOOHYZR.js → chunk-ALEZJFJ3.js} +8 -8
  9. package/dist/cli/{chunk-BVBX222F.js → chunk-EBBAOQ2Z.js} +7 -6
  10. package/dist/cli/{chunk-WP77O5LC.js → chunk-EKXZ45MQ.js} +1182 -621
  11. package/dist/cli/{chunk-HP3G2TYJ.js → chunk-EX66KH66.js} +730 -646
  12. package/dist/cli/{chunk-7ZAA75HG.js → chunk-HOOHS3MX.js} +1 -1
  13. package/dist/cli/{chunk-FQZ3IX6E.js → chunk-K7UEKSB2.js} +2 -2
  14. package/dist/cli/{chunk-GXFU4XAX.js → chunk-KACRWFN3.js} +1 -1
  15. package/dist/cli/chunk-NFXFY5CT.js +6 -0
  16. package/dist/cli/{chunk-WJ6YMZTT.js → chunk-NLL2EQJ2.js} +2 -2
  17. package/dist/cli/{chunk-KKNE7BFZ.js → chunk-WRE3PUTV.js} +35 -11
  18. package/dist/cli/{chunk-WCS24ICX.js → chunk-ZQON4EV7.js} +2 -2
  19. package/dist/cli/{chunk-Y5DJ5INP.js → chunk-ZZMZDAF6.js} +3 -3
  20. package/dist/cli/{daemon-YHRAZSRS.js → daemon-5FPZG5R3.js} +87 -25
  21. package/dist/cli/decide-2ZKSX7O5.js +56 -0
  22. package/dist/cli/{handover-UXISKYPS.js → handover-AZ35QZRK.js} +5 -5
  23. package/dist/cli/{hook-5UAARBWU.js → hook-KANGO46Y.js} +21 -14
  24. package/dist/cli/index.js +23 -16
  25. package/dist/cli/launch-DEKJ5HI4.js +34 -0
  26. package/dist/cli/{send-NLVR6PL2.js → send-BFPJBXXH.js} +5 -5
  27. package/dist/cli/{setup-EEIXRAMU.js → setup-47ODKJBM.js} +4 -4
  28. package/dist/cli/{status-2OCYHOWZ.js → status-JPF4NZNI.js} +3 -3
  29. package/dist/cli/{task-AOOWHPMN.js → task-UTRG4KA5.js} +5 -5
  30. package/dist/cli/{team-TVNSVYWW.js → team-SZJHGA7D.js} +6 -6
  31. package/dist/cli/{tui-5XPPX4GR.js → tui-GEJHBWP5.js} +817 -367
  32. package/dist/cli/{uninstall-UUN7LIIT.js → uninstall-QNWRD27D.js} +4 -4
  33. package/dist/cli/{version-HB3S4IVG.js → version-YUO6SLAF.js} +1 -1
  34. package/dist/cli/who-NYPVBO64.js +11 -0
  35. package/package.json +1 -1
  36. package/dist/cli/chunk-TVIAHOOD.js +0 -6
  37. package/dist/cli/launch-YQYCDZ7N.js +0 -34
  38. package/dist/cli/who-52MENZTT.js +0 -11
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paths
3
- } from "./chunk-7ZAA75HG.js";
3
+ } from "./chunk-HOOHS3MX.js";
4
4
 
5
5
  // ../protocol/src/handover.ts
6
6
  import { z } from "zod";
@@ -164,6 +164,19 @@ import { z as z3 } from "zod";
164
164
  var TASK_TITLE_MAX = 200;
165
165
  var TASK_TEXT_MAX = 2e4;
166
166
  var LEDGE_GIVER = "ledge";
167
+ var TASK_OPS = ["give", "take", "pass", "done", "refuse", "cancel", "drop"];
168
+ var TRANSMISSION_OPS = ["give", "pass", "done", "refuse", "cancel"];
169
+ var TaskLogEntrySchema = z3.object({
170
+ at: z3.string(),
171
+ by: z3.string(),
172
+ what: z3.string(),
173
+ op: z3.enum(TASK_OPS).optional(),
174
+ from: z3.string().optional(),
175
+ to: z3.string().optional(),
176
+ fromName: z3.string().optional(),
177
+ toName: z3.string().optional(),
178
+ note: z3.string().optional()
179
+ });
167
180
  var TaskSchema = z3.object({
168
181
  id: z3.string(),
169
182
  title: z3.string(),
@@ -180,8 +193,12 @@ var TaskSchema = z3.object({
180
193
  seen: z3.boolean().optional(),
181
194
  at: z3.string(),
182
195
  updatedAt: z3.string(),
183
- log: z3.array(z3.object({ at: z3.string(), by: z3.string(), what: z3.string() }))
196
+ log: z3.array(TaskLogEntrySchema)
184
197
  });
198
+ var handOver = (op) => TRANSMISSION_OPS.includes(op ?? "");
199
+ function transmissions(t) {
200
+ return t.log.flatMap((e, index) => handOver(e.op) && e.from !== void 0 && e.to !== void 0 ? [{ index, at: e.at, op: e.op, from: e.from, to: e.to, fromName: e.fromName ?? "", toName: e.toName ?? "", ...e.note !== void 0 ? { note: e.note } : {} }] : []);
201
+ }
185
202
  var givenByLedge = (t) => t.giver === LEDGE_GIVER;
186
203
  var taskClosed = (t) => t.state === "done" || t.state === "refused" || t.state === "dropped";
187
204
  var TaskCmdSchema = z3.object({
@@ -208,170 +225,205 @@ var TaskResultSchema = z3.object({
208
225
  });
209
226
 
210
227
  // ../protocol/src/messages.ts
211
- import { z as z18 } from "zod";
228
+ import { z as z19 } from "zod";
212
229
 
213
230
  // ../protocol/src/chat.ts
231
+ import { z as z5 } from "zod";
232
+
233
+ // ../protocol/src/attachments.ts
214
234
  import { z as z4 } from "zod";
215
- var ChatStepSchema = z4.object({
216
- kind: z4.enum(["sessions", "pane", "reply", "git", "repo"]),
217
- id: z4.string().optional(),
218
- name: z4.string().optional(),
219
- path: z4.string().optional()
235
+ var ATTACHMENT_ID = /^[0-9a-f]{64}$/;
236
+ var AttachmentIdSchema = z4.string().regex(ATTACHMENT_ID);
237
+ var IMAGE_MIMES = ["image/webp", "image/jpeg", "image/png", "image/gif"];
238
+ var ImageMimeSchema = z4.enum(IMAGE_MIMES);
239
+ var ATTACHMENT_MAX = 4e5;
240
+ var ATTACHMENT_LOCAL_MAX = 2e6;
241
+ var PREVIEW_MAX = 4e4;
242
+ var ATTACHMENTS_MAX = 5;
243
+ var b64Max = (bytes) => Math.ceil(bytes / 3) * 4;
244
+ var AttachmentRefSchema = z4.object({
245
+ id: AttachmentIdSchema,
246
+ mime: ImageMimeSchema,
247
+ width: z4.number().int().positive(),
248
+ height: z4.number().int().positive(),
249
+ bytes: z4.number().int().positive(),
250
+ preview: z4.boolean()
220
251
  });
221
- var ChatProposalSchema = z4.object({
222
- cwd: z4.string().min(1),
223
- project: z4.string(),
224
- prompt: z4.string().optional(),
252
+ var ImageUploadSchema = z4.object({
253
+ mime: ImageMimeSchema,
254
+ data: z4.string().min(1).max(b64Max(ATTACHMENT_LOCAL_MAX)),
255
+ preview: z4.object({ mime: ImageMimeSchema, data: z4.string().min(1).max(b64Max(PREVIEW_MAX)) }).optional()
256
+ });
257
+ var AttachmentErrorReasonSchema = z4.enum(["too_large", "not_image", "unreadable", "not_found", "offline"]);
258
+ var requestId = z4.string().min(1).max(100);
259
+ var AttachmentPutSchema = z4.object({ type: z4.literal("attachment.put"), requestId, id: z4.string().optional(), image: ImageUploadSchema });
260
+ var AttachmentGetSchema = z4.object({ type: z4.literal("attachment.get"), requestId, attachment: AttachmentIdSchema, preview: z4.boolean().optional(), id: z4.string().optional(), machine: z4.string().optional() });
261
+ var AttachmentStoredSchema = z4.object({ type: z4.literal("attachment.stored"), requestId, ref: AttachmentRefSchema });
262
+ var AttachmentDataSchema = z4.object({ type: z4.literal("attachment.data"), requestId, attachment: AttachmentIdSchema, mime: ImageMimeSchema, data: z4.string() });
263
+ var AttachmentErrorSchema = z4.object({ type: z4.literal("attachment.error"), requestId, reason: AttachmentErrorReasonSchema });
264
+
265
+ // ../protocol/src/chat.ts
266
+ var ChatStepSchema = z5.object({
267
+ kind: z5.enum(["sessions", "pane", "reply", "git", "repo"]),
268
+ id: z5.string().optional(),
269
+ name: z5.string().optional(),
270
+ path: z5.string().optional()
271
+ });
272
+ var ChatProposalSchema = z5.object({
273
+ cwd: z5.string().min(1),
274
+ project: z5.string(),
275
+ prompt: z5.string().optional(),
225
276
  tool: ToolSchema,
226
- launchedId: z4.string().optional(),
227
- error: z4.string().optional()
277
+ launchedId: z5.string().optional(),
278
+ error: z5.string().optional()
228
279
  });
229
280
  var MAX_PROPOSALS = 8;
230
- var ChatProposalsSchema = z4.array(ChatProposalSchema).min(1).max(MAX_PROPOSALS);
231
- var ChatRelaySchema = z4.object({
232
- id: z4.string().min(1),
233
- name: z4.string(),
281
+ var ChatProposalsSchema = z5.array(ChatProposalSchema).min(1).max(MAX_PROPOSALS);
282
+ var ChatRelaySchema = z5.object({
283
+ id: z5.string().min(1),
284
+ name: z5.string(),
234
285
  tool: ToolSchema,
235
- text: z4.string().min(1),
236
- status: z4.enum(["queued", "sent"]).optional(),
237
- sentAt: z4.string().datetime().optional(),
238
- error: z4.string().optional()
286
+ text: z5.string().min(1),
287
+ status: z5.enum(["queued", "sent"]).optional(),
288
+ sentAt: z5.string().datetime().optional(),
289
+ error: z5.string().optional()
239
290
  });
240
- var ChatCloseSchema = z4.object({
241
- id: z4.string().min(1),
242
- name: z4.string(),
291
+ var ChatCloseSchema = z5.object({
292
+ id: z5.string().min(1),
293
+ name: z5.string(),
243
294
  tool: ToolSchema,
244
- closedAt: z4.string().datetime().optional(),
245
- how: z4.enum(["exit", "killed"]).optional(),
246
- error: z4.string().optional()
295
+ closedAt: z5.string().datetime().optional(),
296
+ how: z5.enum(["exit", "killed"]).optional(),
297
+ error: z5.string().optional()
247
298
  });
248
- var ChatClosesSchema = z4.array(ChatCloseSchema).min(1).max(MAX_PROPOSALS);
249
- var ChatProjectProposalSchema = z4.object({
250
- name: z4.string().min(1).max(80),
251
- description: z4.string().max(500).optional(),
252
- repos: z4.array(z4.object({ path: z4.string().min(1).max(4096), why: z4.string().min(1).max(200) })).min(1).max(50),
253
- addTo: z4.string().min(1).max(80).optional()
299
+ var ChatClosesSchema = z5.array(ChatCloseSchema).min(1).max(MAX_PROPOSALS);
300
+ var ChatProjectProposalSchema = z5.object({
301
+ name: z5.string().min(1).max(80),
302
+ description: z5.string().max(500).optional(),
303
+ repos: z5.array(z5.object({ path: z5.string().min(1).max(4096), why: z5.string().min(1).max(200) })).min(1).max(50),
304
+ addTo: z5.string().min(1).max(80).optional()
254
305
  });
255
- var ChatTeamSeatSchema = z4.object({
256
- role: z4.string().min(1),
257
- label: z4.string(),
306
+ var ChatTeamSeatSchema = z5.object({
307
+ role: z5.string().min(1),
308
+ label: z5.string(),
258
309
  tool: ToolSchema,
259
- model: z4.string().optional(),
260
- effort: z4.string().optional(),
310
+ model: z5.string().optional(),
311
+ effort: z5.string().optional(),
261
312
  autonomy: AutonomySchema.optional(),
262
- name: z4.string().optional()
313
+ name: z5.string().optional()
263
314
  });
264
- var ChatTeamProposalSchema = z4.object({
265
- cwd: z4.string().min(1),
266
- project: z4.string(),
267
- projectName: z4.string().optional(),
268
- name: z4.string().min(1),
269
- template: z4.string().optional(),
270
- seats: z4.array(ChatTeamSeatSchema).min(1).max(8),
271
- teamId: z4.string().optional(),
272
- launchedAt: z4.string().datetime().optional(),
273
- error: z4.string().optional(),
274
- savedPath: z4.string().optional()
315
+ var ChatTeamProposalSchema = z5.object({
316
+ cwd: z5.string().min(1),
317
+ project: z5.string(),
318
+ projectName: z5.string().optional(),
319
+ name: z5.string().min(1),
320
+ template: z5.string().optional(),
321
+ seats: z5.array(ChatTeamSeatSchema).min(1).max(8),
322
+ teamId: z5.string().optional(),
323
+ launchedAt: z5.string().datetime().optional(),
324
+ error: z5.string().optional(),
325
+ savedPath: z5.string().optional()
275
326
  });
276
- var ChatTaskProposalSchema = z4.object({
277
- to: z4.string().min(1),
278
- toName: z4.string().optional(),
279
- team: z4.string().optional(),
280
- title: z4.string().min(1).max(200),
281
- text: z4.string().max(2e4).optional(),
282
- taskId: z4.string().optional(),
283
- ownerName: z4.string().optional(),
284
- state: z4.enum(["open", "taken", "done", "refused", "dropped"]).optional(),
285
- givenAt: z4.string().datetime().optional(),
286
- error: z4.string().optional()
327
+ var ChatTaskProposalSchema = z5.object({
328
+ to: z5.string().min(1),
329
+ toName: z5.string().optional(),
330
+ team: z5.string().optional(),
331
+ title: z5.string().min(1).max(200),
332
+ text: z5.string().max(2e4).optional(),
333
+ taskId: z5.string().optional(),
334
+ ownerName: z5.string().optional(),
335
+ state: z5.enum(["open", "taken", "done", "refused", "dropped"]).optional(),
336
+ givenAt: z5.string().datetime().optional(),
337
+ error: z5.string().optional()
287
338
  });
288
- var ChatTasksSchema = z4.array(ChatTaskProposalSchema).min(1).max(MAX_PROPOSALS);
289
- var ChatTurnSchema = z4.object({
290
- machineId: z4.string().optional(),
291
- machineName: z4.string().optional(),
292
- choices: z4.array(z4.object({ id: z4.string(), label: z4.string() })).optional(),
293
- qid: z4.string().min(1),
294
- question: z4.string(),
295
- askedAt: z4.string().datetime(),
296
- steps: z4.array(ChatStepSchema),
297
- answer: z4.string().optional(),
298
- answeredAt: z4.string().datetime().optional(),
299
- error: z4.string().optional(),
339
+ var ChatTasksSchema = z5.array(ChatTaskProposalSchema).min(1).max(MAX_PROPOSALS);
340
+ var ChatTurnSchema = z5.object({
341
+ machineId: z5.string().optional(),
342
+ machineName: z5.string().optional(),
343
+ choices: z5.array(z5.object({ id: z5.string(), label: z5.string() })).optional(),
344
+ qid: z5.string().min(1),
345
+ question: z5.string(),
346
+ askedAt: z5.string().datetime(),
347
+ steps: z5.array(ChatStepSchema),
348
+ answer: z5.string().optional(),
349
+ answeredAt: z5.string().datetime().optional(),
350
+ error: z5.string().optional(),
300
351
  proposal: ChatProposalSchema.optional(),
301
352
  proposals: ChatProposalsSchema.optional(),
302
353
  relay: ChatRelaySchema.optional(),
303
354
  closes: ChatClosesSchema.optional(),
304
355
  projectProposal: ChatProjectProposalSchema.optional(),
305
356
  team: ChatTeamProposalSchema.optional(),
306
- tasks: ChatTasksSchema.optional()
357
+ tasks: ChatTasksSchema.optional(),
358
+ attachments: z5.array(AttachmentRefSchema).optional()
307
359
  });
308
360
  var proposalRows = (t) => t?.proposals ?? (t?.proposal ? [t.proposal] : []);
309
361
 
310
362
  // ../protocol/src/repos.ts
311
- import { z as z5 } from "zod";
312
- var ReposInspectSchema = z5.object({
313
- type: z5.literal("repos.inspect"),
314
- requestId: z5.string().min(1).max(100),
315
- paths: z5.array(z5.string().min(1)).min(1).max(50),
316
- machineId: z5.string().optional()
363
+ import { z as z6 } from "zod";
364
+ var ReposInspectSchema = z6.object({
365
+ type: z6.literal("repos.inspect"),
366
+ requestId: z6.string().min(1).max(100),
367
+ paths: z6.array(z6.string().min(1)).min(1).max(50),
368
+ machineId: z6.string().optional()
317
369
  });
318
- var RepoInfoSchema = z5.object({ path: z5.string(), exists: z5.boolean(), branch: z5.string().nullable() });
319
- var ReposInfoSchema = z5.object({ type: z5.literal("repos.info"), requestId: z5.string(), repos: z5.array(RepoInfoSchema) });
370
+ var RepoInfoSchema = z6.object({ path: z6.string(), exists: z6.boolean(), branch: z6.string().nullable() });
371
+ var ReposInfoSchema = z6.object({ type: z6.literal("repos.info"), requestId: z6.string(), repos: z6.array(RepoInfoSchema) });
320
372
 
321
373
  // ../protocol/src/models.ts
322
- import { z as z6 } from "zod";
323
- var ModelIdSchema = z6.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:/@[\]=,+-]*$/, "not a model id");
324
- var EffortSchema = z6.string().min(1).max(20).regex(/^[a-z]+$/, "not an effort");
325
- var ModelChoiceSchema = z6.object({
374
+ import { z as z7 } from "zod";
375
+ var ModelIdSchema = z7.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:/@[\]=,+-]*$/, "not a model id");
376
+ var EffortSchema = z7.string().min(1).max(20).regex(/^[a-z]+$/, "not an effort");
377
+ var ModelChoiceSchema = z7.object({
326
378
  id: ModelIdSchema,
327
- label: z6.string(),
328
- description: z6.string().optional(),
329
- efforts: z6.array(EffortSchema).optional(),
379
+ label: z7.string(),
380
+ description: z7.string().optional(),
381
+ efforts: z7.array(EffortSchema).optional(),
330
382
  defaultEffort: EffortSchema.optional(),
331
- isDefault: z6.boolean().optional()
383
+ isDefault: z7.boolean().optional()
332
384
  });
333
- var ModelsListSchema = z6.object({
334
- type: z6.literal("models.list"),
335
- requestId: z6.string().min(1).max(100),
385
+ var ModelsListSchema = z7.object({
386
+ type: z7.literal("models.list"),
387
+ requestId: z7.string().min(1).max(100),
336
388
  tool: ToolSchema,
337
- machineId: z6.string().optional()
389
+ machineId: z7.string().optional()
338
390
  });
339
- var ModelsListingSchema = z6.object({
340
- type: z6.literal("models.listing"),
341
- requestId: z6.string(),
391
+ var ModelsListingSchema = z7.object({
392
+ type: z7.literal("models.listing"),
393
+ requestId: z7.string(),
342
394
  tool: ToolSchema,
343
- models: z6.array(ModelChoiceSchema),
344
- error: z6.string().optional()
395
+ models: z7.array(ModelChoiceSchema),
396
+ error: z7.string().optional()
345
397
  });
346
398
 
347
399
  // ../protocol/src/scan.ts
348
- import { z as z7 } from "zod";
349
- var ReposScanSchema = z7.object({ type: z7.literal("repos.scan"), requestId: z7.string().min(1).max(100), machineId: z7.string().optional() });
350
- var FoundRepoSchema = z7.object({ path: z7.string(), branch: z7.string().nullable(), remote: z7.string().optional(), manifest: z7.string().max(200).optional() });
351
- var ReposFoundSchema = z7.object({ type: z7.literal("repos.found"), requestId: z7.string(), repos: z7.array(FoundRepoSchema).max(500), truncated: z7.boolean(), error: z7.string().optional() });
400
+ import { z as z8 } from "zod";
401
+ var ReposScanSchema = z8.object({ type: z8.literal("repos.scan"), requestId: z8.string().min(1).max(100), machineId: z8.string().optional() });
402
+ var FoundRepoSchema = z8.object({ path: z8.string(), branch: z8.string().nullable(), remote: z8.string().optional(), manifest: z8.string().max(200).optional() });
403
+ var ReposFoundSchema = z8.object({ type: z8.literal("repos.found"), requestId: z8.string(), repos: z8.array(FoundRepoSchema).max(500), truncated: z8.boolean(), error: z8.string().optional() });
352
404
 
353
405
  // ../protocol/src/documents.ts
354
- import { z as z8 } from "zod";
406
+ import { z as z9 } from "zod";
355
407
  var DOCUMENT_MAX_BYTES = 1048576;
356
- var path = z8.string().min(1).max(4096);
357
- var requestId = z8.string().min(1);
358
- var DocumentReadSchema = z8.object({
359
- type: z8.literal("document.read"),
360
- requestId,
408
+ var path = z9.string().min(1).max(4096);
409
+ var requestId2 = z9.string().min(1);
410
+ var DocumentReadSchema = z9.object({
411
+ type: z9.literal("document.read"),
412
+ requestId: requestId2,
361
413
  path,
362
- machineId: z8.string().optional(),
363
- sessionId: z8.string().min(1).optional(),
414
+ machineId: z9.string().optional(),
415
+ sessionId: z9.string().min(1).optional(),
364
416
  basePath: path.optional()
365
417
  });
366
- var DocumentContentSchema = z8.object({
367
- type: z8.literal("document.content"),
368
- requestId,
418
+ var DocumentContentSchema = z9.object({
419
+ type: z9.literal("document.content"),
420
+ requestId: requestId2,
369
421
  path,
370
- text: z8.string(),
371
- bytes: z8.number().int().nonnegative(),
372
- modifiedAt: z8.string().datetime()
422
+ text: z9.string(),
423
+ bytes: z9.number().int().nonnegative(),
424
+ modifiedAt: z9.string().datetime()
373
425
  });
374
- var DocumentErrorCodeSchema = z8.enum([
426
+ var DocumentErrorCodeSchema = z9.enum([
375
427
  "not_found",
376
428
  "not_file",
377
429
  "unsupported_path",
@@ -383,47 +435,47 @@ var DocumentErrorCodeSchema = z8.enum([
383
435
  "session_missing",
384
436
  "read_failed"
385
437
  ]);
386
- var DocumentErrorSchema = z8.object({
387
- type: z8.literal("document.error"),
388
- requestId,
438
+ var DocumentErrorSchema = z9.object({
439
+ type: z9.literal("document.error"),
440
+ requestId: requestId2,
389
441
  path,
390
442
  code: DocumentErrorCodeSchema
391
443
  });
392
444
 
393
445
  // ../protocol/src/routing.ts
394
- import { z as z9 } from "zod";
395
- var MachineInventorySchema = z9.object({
396
- id: z9.string().min(1).max(80),
397
- label: z9.string().min(1).max(120),
398
- online: z9.boolean(),
399
- local: z9.boolean(),
400
- sessions: z9.array(z9.object({ id: z9.string(), name: z9.string(), project: z9.string(), cwd: z9.string(), topic: z9.string().optional(), state: z9.string(), readOnly: z9.boolean() })).max(500),
401
- folders: z9.array(z9.object({ project: z9.string(), cwd: z9.string() })).max(100)
446
+ import { z as z10 } from "zod";
447
+ var MachineInventorySchema = z10.object({
448
+ id: z10.string().min(1).max(80),
449
+ label: z10.string().min(1).max(120),
450
+ online: z10.boolean(),
451
+ local: z10.boolean(),
452
+ sessions: z10.array(z10.object({ id: z10.string(), name: z10.string(), project: z10.string(), cwd: z10.string(), topic: z10.string().optional(), state: z10.string(), readOnly: z10.boolean() })).max(500),
453
+ folders: z10.array(z10.object({ project: z10.string(), cwd: z10.string() })).max(100)
402
454
  });
403
- var RoutingContextSchema = z9.object({
404
- machines: z9.array(MachineInventorySchema).min(1).max(20),
405
- history: z9.array(z9.object({ question: z9.string().max(2e4), answer: z9.string().max(2e4).optional(), machineId: z9.string().optional() })).max(50)
455
+ var RoutingContextSchema = z10.object({
456
+ machines: z10.array(MachineInventorySchema).min(1).max(20),
457
+ history: z10.array(z10.object({ question: z10.string().max(2e4), answer: z10.string().max(2e4).optional(), machineId: z10.string().optional() })).max(50)
406
458
  });
407
- var RouteResultSchema = z9.object({
459
+ var RouteResultSchema = z10.object({
408
460
  /** Null means an answer or a clarification, never an implicit local destination. */
409
- machineId: z9.string().nullable(),
410
- text: z9.string().min(1).max(2e4),
411
- choices: z9.array(z9.string()).max(20).optional()
461
+ machineId: z10.string().nullable(),
462
+ text: z10.string().min(1).max(2e4),
463
+ choices: z10.array(z10.string()).max(20).optional()
412
464
  });
413
465
 
414
466
  // ../protocol/src/ssh.ts
415
- import { z as z10 } from "zod";
416
- var SshAliasSchema = z10.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/);
467
+ import { z as z11 } from "zod";
468
+ var SshAliasSchema = z11.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/);
417
469
 
418
470
  // ../protocol/src/machines.ts
419
- import { z as z11 } from "zod";
420
- var MachineIdSchema = z11.string().regex(/^[a-zA-Z0-9_-]{1,64}$/);
421
- var MachineSchema = z11.object({
471
+ import { z as z12 } from "zod";
472
+ var MachineIdSchema = z12.string().regex(/^[a-zA-Z0-9_-]{1,64}$/);
473
+ var MachineSchema = z12.object({
422
474
  id: MachineIdSchema,
423
- label: z11.string().trim().min(1).max(80),
475
+ label: z12.string().trim().min(1).max(80),
424
476
  ssh: SshAliasSchema.optional()
425
477
  });
426
- var MachinesSchema = z11.array(MachineSchema).min(1).max(20).superRefine((list, ctx) => {
478
+ var MachinesSchema = z12.array(MachineSchema).min(1).max(20).superRefine((list, ctx) => {
427
479
  if (list[0]?.id !== "local" || list[0].ssh !== void 0) ctx.addIssue({ code: "custom", message: "the first machine is local, without ssh", path: [0] });
428
480
  const ids = /* @__PURE__ */ new Set(), aliases = /* @__PURE__ */ new Set();
429
481
  list.forEach((m, i) => {
@@ -438,53 +490,53 @@ var MachinesSchema = z11.array(MachineSchema).min(1).max(20).superRefine((list,
438
490
  });
439
491
 
440
492
  // ../protocol/src/island.ts
441
- import { z as z12 } from "zod";
442
- var IslandScaleSchema = z12.union([z12.literal("auto"), z12.literal(1), z12.literal(1.25), z12.literal(1.5), z12.literal(2)]);
443
- var IslandPrefsSchema = z12.object({
493
+ import { z as z13 } from "zod";
494
+ var IslandScaleSchema = z13.union([z13.literal("auto"), z13.literal(1), z13.literal(1.25), z13.literal(1.5), z13.literal(2)]);
495
+ var IslandPrefsSchema = z13.object({
444
496
  /** Shown at the top of the screen; hidden, Desktop still notifies. */
445
- visible: z12.boolean(),
497
+ visible: z13.boolean(),
446
498
  /** `primary` (whichever it is after a reconfiguration), or a monitor's id as the shell lists it (`list_monitors`: the OS
447
499
  * name, `#index` without one). `second`, written by older fronts, is the first monitor that is not the primary. */
448
- screen: z12.string().trim().min(1).max(200),
449
- position: z12.enum(["center", "left", "right"]),
500
+ screen: z13.string().trim().min(1).max(200),
501
+ position: z13.enum(["center", "left", "right"]),
450
502
  /** A strip under a maximised window. */
451
- discreet: z12.boolean(),
503
+ discreet: z13.boolean(),
452
504
  islandScale: IslandScaleSchema,
453
505
  panelScale: IslandScaleSchema,
454
506
  /** What the island shows besides asks and questions, which it always shows. */
455
- shows: z12.object({
507
+ shows: z13.object({
456
508
  /** Allow, deny or answer in a line from the island; off, its cards only say « Open in Ledge ». */
457
- quick: z12.boolean(),
509
+ quick: z13.boolean(),
458
510
  /** The 6 s bubble when an agent finishes. */
459
- finished: z12.boolean(),
511
+ finished: z13.boolean(),
460
512
  /** An agent's sentence in a bubble. */
461
- say: z12.boolean(),
513
+ say: z13.boolean(),
462
514
  /** The sub-agents next to their main agent. */
463
- helpers: z12.boolean()
515
+ helpers: z13.boolean()
464
516
  }),
465
517
  /** The global shortcut that opens the island, as shown (`Ctrl Alt L`). */
466
- shortcut: z12.string().trim().min(1).max(40)
518
+ shortcut: z13.string().trim().min(1).max(40)
467
519
  });
468
520
 
469
521
  // ../protocol/src/projects.ts
470
- import { z as z13 } from "zod";
522
+ import { z as z14 } from "zod";
471
523
  var PROJECT_COLORS = { blue: "#3b82f6", green: "#10b981", amber: "#f59e0b", violet: "#8b5cf6", teal: "#14b8a6", pink: "#ec4899", red: "#ef4444", slate: "#64748b" };
472
- var ProjectColorSchema = z13.enum(["blue", "green", "amber", "violet", "teal", "pink", "red", "slate"]);
473
- var ProjectIdSchema = z13.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/);
524
+ var ProjectColorSchema = z14.enum(["blue", "green", "amber", "violet", "teal", "pink", "red", "slate"]);
525
+ var ProjectIdSchema = z14.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/);
474
526
  function normalizePath(raw) {
475
527
  const p = raw.trim().replace(/\/+/g, "/");
476
528
  return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
477
529
  }
478
- var PathSchema = z13.string().max(4096).refine((p) => p.startsWith("/") && p === normalizePath(p), "an absolute, normalized path");
479
- var RepoSchema = z13.object({ machine: MachineIdSchema, path: PathSchema });
480
- var ProjectSchema = z13.object({
530
+ var PathSchema = z14.string().max(4096).refine((p) => p.startsWith("/") && p === normalizePath(p), "an absolute, normalized path");
531
+ var RepoSchema = z14.object({ machine: MachineIdSchema, path: PathSchema });
532
+ var ProjectSchema = z14.object({
481
533
  id: ProjectIdSchema,
482
- name: z13.string().trim().min(1).max(80),
534
+ name: z14.string().trim().min(1).max(80),
483
535
  color: ProjectColorSchema,
484
- description: z13.string().max(500).default(""),
485
- repos: z13.array(RepoSchema).max(50)
536
+ description: z14.string().max(500).default(""),
537
+ repos: z14.array(RepoSchema).max(50)
486
538
  });
487
- var RuleSchema = z13.object({ machine: MachineIdSchema, path: PathSchema, project: ProjectIdSchema });
539
+ var RuleSchema = z14.object({ machine: MachineIdSchema, path: PathSchema, project: ProjectIdSchema });
488
540
  function projectsProblems(p) {
489
541
  const out = [];
490
542
  const ids = /* @__PURE__ */ new Set(), names = /* @__PURE__ */ new Set(), repos = /* @__PURE__ */ new Set(), rules = /* @__PURE__ */ new Set();
@@ -510,7 +562,7 @@ ${rule.path}`;
510
562
  });
511
563
  return out;
512
564
  }
513
- var ProjectsSchema = z13.object({ projects: z13.array(ProjectSchema).max(100), rules: z13.array(RuleSchema).max(1e3) }).superRefine((p, ctx) => {
565
+ var ProjectsSchema = z14.object({ projects: z14.array(ProjectSchema).max(100), rules: z14.array(RuleSchema).max(1e3) }).superRefine((p, ctx) => {
514
566
  for (const problem of projectsProblems(p)) ctx.addIssue({ code: "custom", message: problem.code, path: problem.path });
515
567
  });
516
568
  var DEFAULT_PROJECTS = { projects: [], rules: [] };
@@ -528,51 +580,54 @@ function projectOf(session, state) {
528
580
  }
529
581
 
530
582
  // ../protocol/src/thread.ts
531
- import { z as z14 } from "zod";
532
- var ToolStatusSchema = z14.enum(["running", "success", "failed", "unknown"]);
533
- var FileChangeSchema = z14.object({ path: z14.string().min(1), from: z14.string().min(1).optional(), change: z14.enum(["add", "edit", "delete", "write"]), diff: z14.string().optional() });
534
- var base = { id: z14.string().min(1), at: z14.string().datetime(), turn: z14.number().int().nonnegative(), helperId: z14.string().optional() };
535
- var ThreadItemSchema = z14.discriminatedUnion("kind", [
536
- z14.object({ kind: z14.literal("prompt"), ...base, text: z14.string() }),
537
- z14.object({ kind: z14.literal("message"), ...base, text: z14.string() }),
583
+ import { z as z15 } from "zod";
584
+ var ToolStatusSchema = z15.enum(["running", "success", "failed", "unknown"]);
585
+ var FileChangeSchema = z15.object({ path: z15.string().min(1), from: z15.string().min(1).optional(), change: z15.enum(["add", "edit", "delete", "write"]), diff: z15.string().optional() });
586
+ var base = { id: z15.string().min(1), at: z15.string().datetime(), turn: z15.number().int().nonnegative(), helperId: z15.string().optional() };
587
+ var ThreadItemSchema = z15.discriminatedUnion("kind", [
588
+ z15.object({ kind: z15.literal("prompt"), ...base, text: z15.string(), images: z15.array(AttachmentRefSchema).optional() }),
589
+ z15.object({ kind: z15.literal("message"), ...base, text: z15.string() }),
538
590
  /** `id` is the harness's tool_use_id when it gives one, so the result updates the same row; `byClose`: a question
539
591
  * dismissed by Ledge closing the session. */
540
- z14.object({ kind: z14.literal("tool"), ...base, tool: z14.string(), label: z14.string(), input: z14.string().optional(), output: z14.string().optional(), error: z14.string().optional(), status: ToolStatusSchema, durationMs: z14.number().nonnegative().optional(), files: z14.array(FileChangeSchema).default([]), byClose: z14.literal(true).optional() }),
592
+ z15.object({ kind: z15.literal("tool"), ...base, tool: z15.string(), label: z15.string(), input: z15.string().optional(), output: z15.string().optional(), error: z15.string().optional(), status: ToolStatusSchema, durationMs: z15.number().nonnegative().optional(), files: z15.array(FileChangeSchema).default([]), byClose: z15.literal(true).optional() }),
541
593
  /** `unknown`: answered in the terminal, the daemon only saw the prompt go away; `always`: allowed and not asked again;
542
594
  * `byClose`: denied by Ledge closing the session. */
543
- z14.object({ kind: z14.literal("permission"), ...base, tool: z14.string(), text: z14.string(), decision: z14.enum(["accept", "deny", "always", "unknown"]).optional(), byClose: z14.literal(true).optional() }),
544
- z14.object({ kind: z14.literal("reply"), ...base, text: z14.string() }),
545
- z14.object({ kind: z14.literal("helper"), ...base, task: z14.string(), agentType: z14.string().optional(), state: z14.enum(["working", "exited"]) }),
595
+ z15.object({ kind: z15.literal("permission"), ...base, tool: z15.string(), text: z15.string(), decision: z15.enum(["accept", "deny", "always", "unknown"]).optional(), byClose: z15.literal(true).optional() }),
596
+ z15.object({ kind: z15.literal("reply"), ...base, text: z15.string() }),
597
+ z15.object({ kind: z15.literal("helper"), ...base, task: z15.string(), agentType: z15.string().optional(), state: z15.enum(["working", "exited"]) }),
546
598
  /** A message this session sent to another agent; `id` is the tool_use_id, `to` the address it used, `peer` the Ledge session id, `received` the paired item's id in the peer's thread, `via` set when Ledge's mailbox carries it. */
547
- z14.object({ kind: z14.literal("sent"), ...base, to: z14.string(), peer: z14.string().optional(), summary: z14.string().optional(), text: z14.string(), status: z14.enum(["sending", "delivered", "read", "failed"]), received: z14.string().optional(), via: z14.literal("ledge").optional() }),
599
+ z15.object({ kind: z15.literal("sent"), ...base, to: z15.string(), peer: z15.string().optional(), summary: z15.string().optional(), text: z15.string(), status: z15.enum(["sending", "delivered", "read", "failed"]), received: z15.string().optional(), via: z15.literal("ledge").optional() }),
548
600
  /** A message another agent sent to this session; `from` is its from-name (else its address), `sent` the paired item's id in the peer's thread. */
549
- z14.object({ kind: z14.literal("received"), ...base, from: z14.string(), peer: z14.string().optional(), text: z14.string(), sent: z14.string().optional() }),
601
+ z15.object({ kind: z15.literal("received"), ...base, from: z15.string(), peer: z15.string().optional(), text: z15.string(), sent: z15.string().optional() }),
550
602
  /** An automated notice of the agent's harness about messages between sessions (Claude Code's `[Cross-session idle
551
603
  * notice]` / `[Cross-session delivery notice]`): a system event, never the user's words. `name` is the session it is
552
604
  * about as quoted, `peer` its Ledge session id; a delivery notice says the outcome of `count` messages to `recipients`. */
553
- z14.object({
554
- kind: z14.literal("notice"),
605
+ z15.object({
606
+ kind: z15.literal("notice"),
555
607
  ...base,
556
- notice: z14.enum(["idle", "exited", "unavailable", "expired", "delivery", "other"]),
608
+ notice: z15.enum(["idle", "exited", "unavailable", "expired", "delivery", "other"]),
557
609
  /** An `other` notice from Ledge: `task`, the task a `<ledge-task>` is about; `team`, the team a `<ledge-notice team>` seated the
558
610
  * session in (roles spec §3.3). An older front, which knows neither, shows it as any other notice. */
559
- task: z14.string().optional(),
560
- team: z14.string().optional(),
561
- name: z14.string().optional(),
562
- peer: z14.string().optional(),
563
- time: z14.string().optional(),
564
- summary: z14.string().optional(),
565
- status: z14.enum(["held", "denied", "expired", "delivered", "refused", "dropped", "unknown"]).optional(),
566
- count: z14.number().int().positive().optional(),
567
- recipients: z14.array(z14.string()).optional(),
568
- reason: z14.string().optional(),
569
- text: z14.string()
611
+ task: z15.string().optional(),
612
+ team: z15.string().optional(),
613
+ /** A task this session handed over (`outgoing`), and how (issue #92). */
614
+ taskOp: z15.enum(TRANSMISSION_OPS).optional(),
615
+ outgoing: z15.literal(true).optional(),
616
+ name: z15.string().optional(),
617
+ peer: z15.string().optional(),
618
+ time: z15.string().optional(),
619
+ summary: z15.string().optional(),
620
+ status: z15.enum(["held", "denied", "expired", "delivered", "refused", "dropped", "unknown"]).optional(),
621
+ count: z15.number().int().positive().optional(),
622
+ recipients: z15.array(z15.string()).optional(),
623
+ reason: z15.string().optional(),
624
+ text: z15.string()
570
625
  })
571
626
  ]);
572
627
 
573
628
  // ../protocol/src/decisions.ts
574
- import { z as z15 } from "zod";
575
- var DecisionSchema = z15.enum(["accept", "deny", "always"]);
629
+ import { z as z16 } from "zod";
630
+ var DecisionSchema = z16.enum(["accept", "deny", "always"]);
576
631
  var NOTE_MAX = 4e3;
577
632
  var NOTE_NOT_TYPED = "note_not_typed";
578
633
  var DECISION_OFFERS = {
@@ -602,12 +657,12 @@ function pendingKey(p) {
602
657
  }
603
658
 
604
659
  // ../protocol/src/mobile.ts
605
- import { z as z16 } from "zod";
660
+ import { z as z17 } from "zod";
606
661
  var DEFAULT_RELAY_URL = "https://app.kl-ledge.dev";
607
- var RelayUrlSchema = z16.string().transform((v, ctx) => {
662
+ var RelayUrlSchema = z17.string().transform((v, ctx) => {
608
663
  const bad = () => {
609
- ctx.addIssue({ code: z16.ZodIssueCode.custom, message: "invalid relay URL" });
610
- return z16.NEVER;
664
+ ctx.addIssue({ code: z17.ZodIssueCode.custom, message: "invalid relay URL" });
665
+ return z17.NEVER;
611
666
  };
612
667
  let u;
613
668
  try {
@@ -620,236 +675,248 @@ var RelayUrlSchema = z16.string().transform((v, ctx) => {
620
675
  if (u.username || u.password || u.pathname !== "/" || u.search || u.hash) return bad();
621
676
  return u.origin;
622
677
  });
623
- var MobileConfigSchema = z16.object({ enabled: z16.boolean(), relayUrl: RelayUrlSchema });
624
- var MobileLinkStateSchema = z16.discriminatedUnion("state", [
625
- z16.object({ state: z16.literal("off") }),
626
- z16.object({ state: z16.literal("connecting") }),
627
- z16.object({ state: z16.literal("online") }),
628
- z16.object({ state: z16.literal("offline"), retryAt: z16.string().datetime(), error: z16.string() }),
629
- z16.object({ state: z16.literal("signed_out"), reason: z16.enum(["other_pc", "session_invalid"]) }),
678
+ var MobileConfigSchema = z17.object({ enabled: z17.boolean(), relayUrl: RelayUrlSchema });
679
+ var MobileLinkStateSchema = z17.discriminatedUnion("state", [
680
+ z17.object({ state: z17.literal("off") }),
681
+ z17.object({ state: z17.literal("connecting") }),
682
+ z17.object({ state: z17.literal("online") }),
683
+ z17.object({ state: z17.literal("offline"), retryAt: z17.string().datetime(), error: z17.string() }),
684
+ z17.object({ state: z17.literal("signed_out"), reason: z17.enum(["other_pc", "session_invalid"]) }),
630
685
  /** The mobile service did not start (a broken mobile.json, a missing key store): nothing is linked until the daemon restarts. */
631
- z16.object({ state: z16.literal("unavailable"), error: z16.string().max(200) })
686
+ z17.object({ state: z17.literal("unavailable"), error: z17.string().max(200) })
632
687
  ]);
633
- var MobilePairingSchema = z16.object({
634
- id: z16.string(),
635
- label: z16.string(),
636
- code: z16.string().nullable(),
637
- expiresAt: z16.string().datetime(),
638
- state: z16.enum(["waiting", "confirm", "approved", "linked", "expired", "refused"])
688
+ var MobilePairingSchema = z17.object({
689
+ id: z17.string(),
690
+ label: z17.string(),
691
+ code: z17.string().nullable(),
692
+ expiresAt: z17.string().datetime(),
693
+ state: z17.enum(["waiting", "confirm", "approved", "linked", "expired", "refused"])
639
694
  });
640
- var MobileStateSchema = z16.object({
641
- enabled: z16.boolean(),
642
- relayUrl: z16.string(),
643
- account: z16.object({ login: z16.string(), accountId: z16.string() }).nullable(),
644
- signin: z16.object({ userCode: z16.string(), verificationUri: z16.string(), expiresAt: z16.string().datetime() }).nullable(),
695
+ var MobileStateSchema = z17.object({
696
+ enabled: z17.boolean(),
697
+ relayUrl: z17.string(),
698
+ account: z17.object({ login: z17.string(), accountId: z17.string() }).nullable(),
699
+ signin: z17.object({ userCode: z17.string(), verificationUri: z17.string(), expiresAt: z17.string().datetime() }).nullable(),
645
700
  /** The last sign-in that did not end with a session; cleared by the next one. */
646
- signinError: z16.enum(["expired", "denied", "not_allowed", "failed"]).nullable(),
701
+ signinError: z17.enum(["expired", "denied", "not_allowed", "failed"]).nullable(),
647
702
  link: MobileLinkStateSchema,
648
- devices: z16.array(z16.object({ id: z16.string(), label: z16.string(), approvedAt: z16.string(), online: z16.boolean(), lastSeenAt: z16.string().optional() })),
649
- pairings: z16.array(MobilePairingSchema)
703
+ devices: z17.array(z17.object({ id: z17.string(), label: z17.string(), approvedAt: z17.string(), online: z17.boolean(), lastSeenAt: z17.string().optional() })),
704
+ pairings: z17.array(MobilePairingSchema)
650
705
  });
651
706
 
652
707
  // ../protocol/src/teams.ts
653
- import { z as z17 } from "zod";
654
- var RoleIdSchema = z17.string().regex(/^[a-z0-9][a-z0-9_-]{0,39}$/);
708
+ import { z as z18 } from "zod";
709
+ var RoleIdSchema = z18.string().regex(/^[a-z0-9][a-z0-9_-]{0,39}$/);
655
710
  var ROLE_LABEL_MAX = 40;
656
711
  var ROLE_PROMPT_MAX = 8e3;
657
- var RoleSchema = z17.object({
712
+ var RoleSchema = z18.object({
658
713
  id: RoleIdSchema,
659
- label: z17.string().min(1).max(ROLE_LABEL_MAX),
660
- prompt: z17.string().max(ROLE_PROMPT_MAX),
714
+ label: z18.string().min(1).max(ROLE_LABEL_MAX),
715
+ prompt: z18.string().max(ROLE_PROMPT_MAX),
661
716
  tool: ToolSchema.optional(),
662
717
  model: ModelIdSchema.optional(),
663
718
  effort: EffortSchema.optional(),
664
719
  autonomy: AutonomySchema.optional(),
665
- source: z17.enum(["repo", "home"]),
666
- path: z17.string()
720
+ source: z18.enum(["repo", "home"]),
721
+ path: z18.string()
667
722
  });
668
- var DefinitionProblemSchema = z17.object({ path: z17.string(), reason: z17.string() });
723
+ var DefinitionProblemSchema = z18.object({ path: z18.string(), reason: z18.string() });
669
724
  var TEAM_SEATS_MAX = 8;
670
- var SeatSchema = z17.object({
725
+ var SeatSchema = z18.object({
671
726
  role: RoleIdSchema,
672
727
  tool: ToolSchema.optional(),
673
728
  model: ModelIdSchema.optional(),
674
729
  effort: EffortSchema.optional(),
675
- name: z17.string().min(1).max(40).optional()
730
+ name: z18.string().min(1).max(40).optional()
676
731
  });
677
732
  var uniqueRoles = (seats) => new Set(seats.map((s) => s.role)).size === seats.length;
678
- var SeatsSchema = z17.array(SeatSchema).min(1).max(TEAM_SEATS_MAX).refine(uniqueRoles, "a role twice in one team");
679
- var TeamNameSchema = z17.string().trim().min(1).max(60);
680
- var TeamTemplateSchema = z17.object({ id: z17.string().min(1).max(64), name: TeamNameSchema, seats: SeatsSchema, source: z17.enum(["repo", "home"]), path: z17.string() });
681
- var TeamIdSchema = z17.string().regex(/^team-[0-9a-f]{4,8}$/);
682
- var TeamStateSchema = z17.enum(["active", "resting", "dissolved"]);
683
- var TeamSchema = z17.object({
733
+ var SeatsSchema = z18.array(SeatSchema).min(1).max(TEAM_SEATS_MAX).refine(uniqueRoles, "a role twice in one team");
734
+ var TeamNameSchema = z18.string().trim().min(1).max(60);
735
+ var TeamTemplateSchema = z18.object({ id: z18.string().min(1).max(64), name: TeamNameSchema, seats: SeatsSchema, source: z18.enum(["repo", "home"]), path: z18.string() });
736
+ var TeamIdSchema = z18.string().regex(/^team-[0-9a-f]{4,8}$/);
737
+ var TeamStateSchema = z18.enum(["active", "resting", "dissolved"]);
738
+ var TeamSchema = z18.object({
684
739
  id: TeamIdSchema,
685
740
  name: TeamNameSchema,
686
- template: z17.string().optional(),
687
- project: z17.string().nullable(),
688
- machine: z17.string(),
689
- cwd: z17.string(),
741
+ template: z18.string().optional(),
742
+ project: z18.string().nullable(),
743
+ machine: z18.string(),
744
+ cwd: z18.string(),
690
745
  state: TeamStateSchema,
691
- seats: z17.array(z17.object({ role: RoleIdSchema, sessionId: z17.string().nullable() })).max(TEAM_SEATS_MAX * 2),
692
- createdAt: z17.string(),
693
- updatedAt: z17.string(),
694
- restingAt: z17.string().optional(),
695
- dissolvedAt: z17.string().optional()
746
+ seats: z18.array(z18.object({ role: RoleIdSchema, sessionId: z18.string().nullable() })).max(TEAM_SEATS_MAX * 2),
747
+ createdAt: z18.string(),
748
+ updatedAt: z18.string(),
749
+ restingAt: z18.string().optional(),
750
+ dissolvedAt: z18.string().optional()
696
751
  });
697
- var RolesListSchema = z17.object({ type: z17.literal("roles.list"), cwd: z17.string().min(1).optional(), requestId: z17.string().max(100).optional(), machineId: z17.string().optional() });
698
- var RolesListingSchema = z17.object({ type: z17.literal("roles.listing"), requestId: z17.string().optional(), roles: z17.array(RoleSchema), templates: z17.array(TeamTemplateSchema).default([]), problems: z17.array(DefinitionProblemSchema) });
699
- var TeamLaunchSchema = z17.object({
700
- type: z17.literal("team.launch"),
701
- rid: z17.string().min(1).max(100),
702
- cwd: z17.string().min(1).optional(),
703
- template: z17.string().min(1).max(64).optional(),
752
+ var RolesListSchema = z18.object({ type: z18.literal("roles.list"), cwd: z18.string().min(1).optional(), requestId: z18.string().max(100).optional(), machineId: z18.string().optional() });
753
+ var RolesListingSchema = z18.object({ type: z18.literal("roles.listing"), requestId: z18.string().optional(), roles: z18.array(RoleSchema), templates: z18.array(TeamTemplateSchema).default([]), problems: z18.array(DefinitionProblemSchema) });
754
+ var TeamLaunchSchema = z18.object({
755
+ type: z18.literal("team.launch"),
756
+ rid: z18.string().min(1).max(100),
757
+ cwd: z18.string().min(1).optional(),
758
+ template: z18.string().min(1).max(64).optional(),
704
759
  seats: SeatsSchema.optional(),
705
760
  name: TeamNameSchema.optional(),
706
- qid: z17.string().min(1).optional(),
707
- machineId: z17.string().optional()
761
+ qid: z18.string().min(1).optional(),
762
+ machineId: z18.string().optional()
708
763
  });
709
- var TeamJoinSchema = z17.object({ type: z17.literal("team.join"), team: TeamIdSchema, session: z17.string().min(1), role: RoleIdSchema });
710
- var TeamLeaveSchema = z17.object({ type: z17.literal("team.leave"), team: TeamIdSchema, session: z17.string().min(1) });
711
- var TeamResumeSchema = z17.object({ type: z17.literal("team.resume"), team: TeamIdSchema });
712
- var TeamDissolveSchema = z17.object({ type: z17.literal("team.dissolve"), team: TeamIdSchema });
713
- var TeamSaveSchema = z17.object({ type: z17.literal("team.save"), rid: z17.string().min(1).max(100), name: TeamNameSchema, seats: SeatsSchema, qid: z17.string().min(1).optional() });
714
- var TeamsMessageSchema = z17.object({ type: z17.literal("teams"), teams: z17.array(TeamSchema) });
715
- var TeamUpdatedSchema = z17.object({ type: z17.literal("team.updated"), team: TeamSchema });
716
- var TeamLaunchedSchema = z17.object({ type: z17.literal("team.launched"), rid: z17.string(), team: TeamSchema });
717
- var TeamFailedSchema = z17.object({ type: z17.literal("team.failed"), rid: z17.string(), reason: z17.string(), team: TeamSchema.optional() });
718
- var TeamSavedSchema = z17.object({ type: z17.literal("team.saved"), rid: z17.string(), path: z17.string().optional(), reason: z17.string().optional() });
764
+ var TeamJoinSchema = z18.object({ type: z18.literal("team.join"), team: TeamIdSchema, session: z18.string().min(1), role: RoleIdSchema });
765
+ var TeamLeaveSchema = z18.object({ type: z18.literal("team.leave"), team: TeamIdSchema, session: z18.string().min(1) });
766
+ var TeamResumeSchema = z18.object({ type: z18.literal("team.resume"), team: TeamIdSchema });
767
+ var TeamDissolveSchema = z18.object({ type: z18.literal("team.dissolve"), team: TeamIdSchema });
768
+ var TeamSaveSchema = z18.object({ type: z18.literal("team.save"), rid: z18.string().min(1).max(100), name: TeamNameSchema, seats: SeatsSchema, qid: z18.string().min(1).optional() });
769
+ var TeamsMessageSchema = z18.object({ type: z18.literal("teams"), teams: z18.array(TeamSchema) });
770
+ var TeamUpdatedSchema = z18.object({ type: z18.literal("team.updated"), team: TeamSchema });
771
+ var TeamLaunchedSchema = z18.object({ type: z18.literal("team.launched"), rid: z18.string(), team: TeamSchema });
772
+ var TeamFailedSchema = z18.object({ type: z18.literal("team.failed"), rid: z18.string(), reason: z18.string(), team: TeamSchema.optional() });
773
+ var TeamSavedSchema = z18.object({ type: z18.literal("team.saved"), rid: z18.string(), path: z18.string().optional(), reason: z18.string().optional() });
719
774
 
720
775
  // ../protocol/src/messages.ts
721
776
  var PROTOCOL_PORT = 47831;
722
- var RefusalCodeSchema = z18.enum(["stale", "read_only", "no_prompt", "not_taken", "busy", "unknown_session", "nothing_to_decide", "not_offered", "free_text", "note_not_typed", "unavailable", "invalid"]);
723
- var SessionEventSchema = z18.object({
724
- name: z18.enum(["SessionStart", "UserPromptSubmit", "PreToolUse", "PermissionRequest", "Notification", "Stop", "SessionEnd", "PaneChanged", "PaneGone", "PanePrompt", "PaneIdle", "InjectFailed", "Decided", "PostToolUse", "PostToolUseFailure", "StopFailure", "SubagentStart", "SubagentStop", "PaneTitle", "Renamed", "NativeRequest", "AgentMessage", "QueueUpdate"]),
725
- at: z18.string().datetime(),
726
- source: z18.enum(["hook", "pane", "daemon"]),
727
- tool: z18.string().optional(),
728
- text: z18.string().optional(),
729
- options: z18.array(z18.string()).optional(),
777
+ var RefusalCodeSchema = z19.enum(["stale", "read_only", "no_prompt", "not_taken", "busy", "unknown_session", "nothing_to_decide", "not_offered", "free_text", "note_not_typed", "unavailable", "invalid", "attachment_missing", "attachments_not_accepted"]);
778
+ var SessionEventSchema = z19.object({
779
+ name: z19.enum(["SessionStart", "UserPromptSubmit", "PreToolUse", "PermissionRequest", "Notification", "Stop", "SessionEnd", "PaneChanged", "PaneGone", "PanePrompt", "PaneIdle", "InjectFailed", "Decided", "PostToolUse", "PostToolUseFailure", "StopFailure", "SubagentStart", "SubagentStop", "PaneTitle", "Renamed", "NativeRequest", "AgentMessage", "QueueUpdate"]),
780
+ at: z19.string().datetime(),
781
+ source: z19.enum(["hook", "pane", "daemon"]),
782
+ tool: z19.string().optional(),
783
+ text: z19.string().optional(),
784
+ options: z19.array(z19.string()).optional(),
730
785
  /** Each option's description, by index ('' when it has none); left out when no option has one. */
731
- optionDetails: z18.array(z18.string()).optional(),
732
- kind: z18.enum(["question", "permission", "plan", "diff"]).optional(),
786
+ optionDetails: z19.array(z19.string()).optional(),
787
+ kind: z19.enum(["question", "permission", "plan", "diff"]).optional(),
733
788
  /** PanePrompt: what to review in full before deciding (a plan read in the terminal), when `text` is its summary. */
734
- body: z18.string().optional(),
735
- exitCode: z18.number().int().nullable().optional(),
736
- helperId: z18.string().optional(),
789
+ body: z19.string().optional(),
790
+ exitCode: z19.number().int().nullable().optional(),
791
+ helperId: z19.string().optional(),
737
792
  /** The helper's description, once known (Claude Code: its `.meta.json`, written after SubagentStart). */
738
- task: z18.string().optional(),
793
+ task: z19.string().optional(),
739
794
  /** PostToolUseFailure: the user interrupted the turn (Esc); Claude then waits for input with no Stop. */
740
- interrupt: z18.boolean().optional(),
795
+ interrupt: z19.boolean().optional(),
796
+ /** UserPromptSubmit: the images the prompt carried, by attachment id. */
797
+ attachments: z19.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(),
741
798
  /** QueueUpdate: OMP's queue as it stands. */
742
- queue: z18.array(z18.object({ text: z18.string(), mode: z18.enum(["steer", "follow_up"]) })).optional(),
799
+ queue: z19.array(z19.object({ text: z19.string(), mode: z19.enum(["steer", "follow_up"]) })).optional(),
743
800
  /** AgentMessage: the harness's own message, its text so far (`streaming`) or whole. `messageId` is unique for the session's life. */
744
- messageId: z18.string().optional(),
745
- streaming: z18.boolean().optional(),
801
+ messageId: z19.string().optional(),
802
+ streaming: z19.boolean().optional(),
746
803
  /** NativeRequest: the dialog's id, and OMP's `ask` questions when it is one. */
747
- requestId: z18.string().optional(),
748
- questions: z18.array(AskQuestionSchema).optional(),
804
+ requestId: z19.string().optional(),
805
+ questions: z19.array(AskQuestionSchema).optional(),
749
806
  /** Stop: its text is already in the thread as AgentMessage cards, which the reply card must not repeat. */
750
- streamed: z18.boolean().optional(),
807
+ streamed: z19.boolean().optional(),
751
808
  /** Native tracking (spec 2026-10-13 §2): the harness's id of the tool call, so its result lands on the same row. */
752
- toolUseId: z18.string().optional(),
809
+ toolUseId: z19.string().optional(),
753
810
  /** The tool's input as the harness gave it, every string capped by the hook. */
754
- toolInput: z18.record(z18.string(), z18.unknown()).optional(),
811
+ toolInput: z19.record(z19.string(), z19.unknown()).optional(),
755
812
  /** PostToolUse: the result as text; SubagentStop: nothing (the report travels in `text`). */
756
- output: z18.string().optional(),
757
- durationMs: z18.number().nonnegative().optional(),
813
+ output: z19.string().optional(),
814
+ durationMs: z19.number().nonnegative().optional(),
758
815
  /** PostToolUseFailure: the harness's error message. */
759
- error: z18.string().optional(),
816
+ error: z19.string().optional(),
760
817
  /** The session's transcript, or the helper's on SubagentStop; the daemon reads its tail for the agent's messages. */
761
- transcriptPath: z18.string().optional(),
818
+ transcriptPath: z19.string().optional(),
762
819
  /** Decided: what was chosen when the daemon knows it. */
763
820
  decision: DecisionSchema.optional(),
764
821
  /** Decided: the request was refused by Ledge closing the session (its interrupt keys), not by the user's answer. */
765
- byClose: z18.literal(true).optional(),
822
+ byClose: z19.literal(true).optional(),
766
823
  /** The agent's own session id when the hook names it, so the daemon can resume that conversation. */
767
- agentSessionId: z18.string().optional()
824
+ agentSessionId: z19.string().optional()
768
825
  });
769
- var RecentSchema = z18.object({ cwd: z18.string().min(1), project: z18.string().min(1), lastAt: z18.string().datetime() });
826
+ var RecentSchema = z19.object({ cwd: z19.string().min(1), project: z19.string().min(1), lastAt: z19.string().datetime() });
770
827
  var SEMVER = /^\d+\.\d+\.\d+$/;
771
- var DaemonInfoSchema = z18.object({
772
- version: z18.string(),
773
- install: z18.enum(["npm", "git", "unknown"]),
774
- systemd: z18.boolean(),
775
- manual: z18.string().optional(),
776
- routing: z18.boolean().optional(),
777
- archive: z18.boolean().optional(),
828
+ var DaemonInfoSchema = z19.object({
829
+ version: z19.string(),
830
+ install: z19.enum(["npm", "git", "unknown"]),
831
+ systemd: z19.boolean(),
832
+ manual: z19.string().optional(),
833
+ routing: z19.boolean().optional(),
834
+ archive: z19.boolean().optional(),
778
835
  /** Takes `model` and `effort` on `session.launch` and answers `models.list` (#58). */
779
- models: z18.boolean().optional(),
836
+ models: z19.boolean().optional(),
780
837
  /** Drives OMP over its RPC: launching `omp` sessions is possible when the binary is there (`models.list` says so). */
781
- omp: z18.boolean().optional(),
838
+ omp: z19.boolean().optional(),
782
839
  /** Takes `session.model`: a live session's model and effort changed (#58, lot 2). */
783
- modelSwitch: z18.boolean().optional(),
840
+ modelSwitch: z19.boolean().optional(),
784
841
  /** Takes `approval` on an OMP `session.launch` (auto mode). */
785
- ompApproval: z18.boolean().optional(),
842
+ ompApproval: z19.boolean().optional(),
786
843
  /** Its home folder, so a client shows the folders under it as `~` (absent from older daemons). */
787
- home: z18.string().optional(),
788
- mobile: z18.boolean().optional(),
844
+ home: z19.string().optional(),
845
+ mobile: z19.boolean().optional(),
789
846
  /** Knows roles, teams and tasks given by Ledge (spec 2026-10-25 roles). */
790
- teams: z18.boolean().optional()
847
+ teams: z19.boolean().optional()
791
848
  });
792
- var UpdateFailureSchema = z18.enum(["git-install", "no-systemd", "sessions-at-risk", "npm-failed", "busy"]);
793
- var ServerMessageSchema = z18.discriminatedUnion("type", [
794
- z18.object({ type: z18.literal("chat.routed"), qid: z18.string(), result: RouteResultSchema }),
849
+ var UpdateFailureSchema = z19.enum(["git-install", "no-systemd", "sessions-at-risk", "npm-failed", "busy"]);
850
+ var ServerMessageSchema = z19.discriminatedUnion("type", [
851
+ z19.object({ type: z19.literal("chat.routed"), qid: z19.string(), result: RouteResultSchema }),
795
852
  DocumentContentSchema,
796
853
  DocumentErrorSchema,
854
+ AttachmentStoredSchema,
855
+ AttachmentDataSchema,
856
+ AttachmentErrorSchema,
797
857
  ReposInfoSchema,
798
858
  ReposFoundSchema,
799
859
  ModelsListingSchema,
800
860
  /** `recents`: the folders of past sessions, most recent first. `launcher`: the command that runs `ledge` inside WSL
801
861
  * (`[node, cli]`), so the overlay can attach a terminal to a session with `<launcher> attach <id>`. `daemon`: absent
802
862
  * from daemons older than the auto-update. */
803
- z18.object({ type: z18.literal("snapshot"), sessions: z18.array(SessionSchema), recents: z18.array(RecentSchema).optional(), launcher: z18.array(z18.string()).optional(), daemon: DaemonInfoSchema.optional(), sshAlias: SshAliasSchema.optional() }),
804
- z18.object({ type: z18.literal("session.updated"), session: SessionSchema }),
863
+ z19.object({ type: z19.literal("snapshot"), sessions: z19.array(SessionSchema), recents: z19.array(RecentSchema).optional(), launcher: z19.array(z19.string()).optional(), daemon: DaemonInfoSchema.optional(), sshAlias: SshAliasSchema.optional() }),
864
+ z19.object({ type: z19.literal("session.updated"), session: SessionSchema }),
805
865
  /** OMP: the instructions taken back from its queue by an interrupt that restores it; the asker puts them back in its draft. */
806
- z18.object({ type: z18.literal("session.queue.restored"), id: z18.string(), texts: z18.array(z18.string()) }),
807
- z18.object({ type: z18.literal("session.removed"), id: z18.string() }),
808
- z18.object({ type: z18.literal("session.preview"), id: z18.string(), lines: z18.array(z18.string()) }),
866
+ z19.object({
867
+ type: z19.literal("session.queue.restored"),
868
+ id: z19.string(),
869
+ texts: z19.array(z19.string()),
870
+ /** The same instructions with the images they carried; `imagesDropped`: some images could not be put back. */
871
+ entries: z19.array(z19.object({ text: z19.string(), attachments: z19.array(AttachmentIdSchema).optional() })).optional(),
872
+ imagesDropped: z19.boolean().optional()
873
+ }),
874
+ z19.object({ type: z19.literal("session.removed"), id: z19.string() }),
875
+ z19.object({ type: z19.literal("session.preview"), id: z19.string(), lines: z19.array(z19.string()) }),
809
876
  /** A session's thread, on request (spec 2026-10-13 §5); then each created or updated item, broadcast. */
810
- z18.object({ type: z18.literal("session.thread"), id: z18.string(), items: z18.array(ThreadItemSchema) }),
811
- z18.object({ type: z18.literal("session.thread.item"), id: z18.string(), item: ThreadItemSchema }),
877
+ z19.object({ type: z19.literal("session.thread"), id: z19.string(), items: z19.array(ThreadItemSchema) }),
878
+ z19.object({ type: z19.literal("session.thread.item"), id: z19.string(), item: ThreadItemSchema }),
812
879
  /** Answer to the requester of a `session.launch`: the session exists (its `session.updated` is broadcast as usual). */
813
- z18.object({ type: z18.literal("session.launched"), id: z18.string() }),
880
+ z19.object({ type: z19.literal("session.launched"), id: z19.string() }),
814
881
  /** Answer to the requester of a `session.close`, once per row closed: the session `id` and how it ended. */
815
- z18.object({ type: z18.literal("session.closed"), qid: z18.string(), row: z18.number().int().min(0), id: z18.string(), how: z18.enum(["exit", "killed"]) }),
816
- z18.object({ type: z18.literal("recents"), recents: z18.array(RecentSchema) }),
882
+ z19.object({ type: z19.literal("session.closed"), qid: z19.string(), row: z19.number().int().min(0), id: z19.string(), how: z19.enum(["exit", "killed"]) }),
883
+ z19.object({ type: z19.literal("recents"), recents: z19.array(RecentSchema) }),
817
884
  /** Answer to the requester of a `folder.list` or `folder.create`: `path` resolved (`~` expanded), `parent` null at the
818
885
  * root, `entries` its subfolders by name (hidden ones left out). A failure keeps the asked path and says why in `error`,
819
886
  * never in an `error` message, which the launch form reads as a failed launch. */
820
- z18.object({
821
- type: z18.literal("folder.listing"),
822
- path: z18.string(),
823
- parent: z18.string().nullable(),
824
- entries: z18.array(z18.string()),
825
- error: z18.string().optional(),
887
+ z19.object({
888
+ type: z19.literal("folder.listing"),
889
+ path: z19.string(),
890
+ parent: z19.string().nullable(),
891
+ entries: z19.array(z19.string()),
892
+ error: z19.string().optional(),
826
893
  /** The request this answers, when it carried one. */
827
- requestId: z18.string().optional(),
894
+ requestId: z19.string().optional(),
828
895
  /** Those `entries` that are Git repositories, with their branch (the short commit when HEAD is detached). */
829
- repos: z18.array(z18.object({ name: z18.string(), branch: z18.string() })).optional()
896
+ repos: z19.array(z19.object({ name: z19.string(), branch: z19.string() })).optional()
830
897
  }),
831
898
  /** The chat (spec 2026-10-11 §5): what Ledge reads, its words as they come, the final text, or why it stopped; the day's turns after each snapshot. */
832
- z18.object({ type: z18.literal("chat.step"), qid: z18.string(), step: ChatStepSchema }),
833
- z18.object({ type: z18.literal("chat.delta"), qid: z18.string(), text: z18.string() }),
834
- z18.object({ type: z18.literal("chat.done"), qid: z18.string(), text: z18.string(), at: z18.string().datetime(), proposal: ChatProposalSchema.optional(), proposals: ChatProposalsSchema.optional(), relay: ChatRelaySchema.optional(), closes: ChatClosesSchema.optional(), projectProposal: ChatProjectProposalSchema.optional(), team: ChatTeamProposalSchema.optional(), tasks: ChatTasksSchema.optional() }),
835
- z18.object({ type: z18.literal("chat.error"), qid: z18.string(), message: z18.string() }),
836
- z18.object({ type: z18.literal("chat.history"), turns: z18.array(ChatTurnSchema) }),
899
+ z19.object({ type: z19.literal("chat.step"), qid: z19.string(), step: ChatStepSchema }),
900
+ z19.object({ type: z19.literal("chat.delta"), qid: z19.string(), text: z19.string() }),
901
+ z19.object({ type: z19.literal("chat.done"), qid: z19.string(), text: z19.string(), at: z19.string().datetime(), proposal: ChatProposalSchema.optional(), proposals: ChatProposalsSchema.optional(), relay: ChatRelaySchema.optional(), closes: ChatClosesSchema.optional(), projectProposal: ChatProjectProposalSchema.optional(), team: ChatTeamProposalSchema.optional(), tasks: ChatTasksSchema.optional() }),
902
+ z19.object({ type: z19.literal("chat.error"), qid: z19.string(), message: z19.string() }),
903
+ z19.object({ type: z19.literal("chat.history"), turns: z19.array(ChatTurnSchema) }),
837
904
  /** The daemon updating itself: npm is running, then the service restarts (the socket closes right after). */
838
- z18.object({ type: z18.literal("update.progress"), version: z18.string(), step: z18.enum(["installing", "restarting"]) }),
905
+ z19.object({ type: z19.literal("update.progress"), version: z19.string(), step: z19.enum(["installing", "restarting"]) }),
839
906
  /** Nothing was changed. `detail`: the useful end of npm's output; `command`: what the person can run instead. */
840
- z18.object({ type: z18.literal("update.failed"), version: z18.string(), reason: UpdateFailureSchema, detail: z18.string().optional(), command: z18.string().optional() }),
907
+ z19.object({ type: z19.literal("update.failed"), version: z19.string(), reason: UpdateFailureSchema, detail: z19.string().optional(), command: z19.string().optional() }),
841
908
  /** The shared machines list (spec 2026-10-14 §5), sent to local clients only. */
842
- z18.object({ type: z18.literal("machines"), machines: MachinesSchema }),
909
+ z19.object({ type: z19.literal("machines"), machines: MachinesSchema }),
843
910
  /** The local daemon's projects and folder rules (spec 2026-10-16 build §5), sent to local clients only. */
844
- z18.object({ type: z18.literal("projects"), projects: ProjectsSchema }),
911
+ z19.object({ type: z19.literal("projects"), projects: ProjectsSchema }),
845
912
  /** The island's settings shared by the local fronts; null until a front sets them (the island then sends its own). Local clients only. */
846
- z18.object({ type: z18.literal("island"), prefs: IslandPrefsSchema.nullable() }),
913
+ z19.object({ type: z19.literal("island"), prefs: IslandPrefsSchema.nullable() }),
847
914
  /** Mobile access (lot 3), after the island in each snapshot and on every change. Local clients only. */
848
- z18.object({ type: z18.literal("mobile"), state: MobileStateSchema }),
915
+ z19.object({ type: z19.literal("mobile"), state: MobileStateSchema }),
849
916
  /** The daemon's immediate answer to a `session.message`: delivered (`accepted`), held for later (`queued`), left to Claude's own SendMessage (`native`), or refused with a `reason`. */
850
- z18.object({ type: z18.literal("message.ack"), rid: z18.string(), outcome: z18.enum(["accepted", "queued", "native", "refused"]), reason: z18.string().optional() }),
917
+ z19.object({ type: z19.literal("message.ack"), rid: z19.string(), outcome: z19.enum(["accepted", "queued", "native", "refused"]), reason: z19.string().optional() }),
851
918
  /** The answer to a `message.read`: the body with its sender, target and time, or the refusal's `reason`. */
852
- z18.object({ type: z18.literal("message.body"), id: z18.string(), from: z18.string().optional(), to: z18.string().optional(), at: z18.string().optional(), text: z18.string().optional(), reason: z18.string().optional() }),
919
+ z19.object({ type: z19.literal("message.body"), id: z19.string(), from: z19.string().optional(), to: z19.string().optional(), at: z19.string().optional(), text: z19.string().optional(), reason: z19.string().optional() }),
853
920
  TaskResultSchema,
854
921
  SessionHandoverResultSchema,
855
922
  ContextPrefsMessageSchema,
@@ -861,22 +928,24 @@ var ServerMessageSchema = z18.discriminatedUnion("type", [
861
928
  TeamLaunchedSchema,
862
929
  TeamFailedSchema,
863
930
  TeamSavedSchema,
864
- z18.object({
865
- type: z18.literal("error"),
866
- message: z18.string(),
867
- id: z18.string().optional(),
931
+ z19.object({
932
+ type: z19.literal("error"),
933
+ message: z19.string(),
934
+ id: z19.string().optional(),
868
935
  /** Le type du message client refusé. */
869
- request: z18.string().optional(),
936
+ request: z19.string().optional(),
870
937
  /** Ce que le front doit reconnaître au-delà du texte (NOTE_NOT_TYPED) ; une chaîne libre, qu'un front plus ancien ignore. */
871
- code: z18.string().optional()
938
+ code: z19.string().optional()
872
939
  })
873
940
  ]);
874
941
  var MESSAGE_MAX = 2e4;
875
942
  var SUMMARY_MAX = 500;
876
- var launchRows = z18.array(z18.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
877
- var ClientMessageSchema = z18.discriminatedUnion("type", [
878
- z18.object({ type: z18.literal("chat.route"), qid: z18.string().min(1).max(100), text: z18.string().min(1).max(2e4), tool: ToolSchema.optional(), context: RoutingContextSchema }),
943
+ var launchRows = z19.array(z19.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
944
+ var ClientMessageSchema = z19.discriminatedUnion("type", [
945
+ z19.object({ type: z19.literal("chat.route"), qid: z19.string().min(1).max(100), text: z19.string().min(1).max(2e4), tool: ToolSchema.optional(), context: RoutingContextSchema }),
879
946
  DocumentReadSchema,
947
+ AttachmentPutSchema,
948
+ AttachmentGetSchema,
880
949
  ReposInspectSchema,
881
950
  ReposScanSchema,
882
951
  ModelsListSchema,
@@ -884,18 +953,18 @@ var ClientMessageSchema = z18.discriminatedUnion("type", [
884
953
  * free one otherwise), `prompt` the agent's first instruction, `continue` resumes the folder's last conversation.
885
954
  * With `qid` it is the chat card of that turn: the daemon launches the turn's proposal and ignores the other fields
886
955
  * but `rows`, `model` and `effort`. */
887
- z18.object({
888
- type: z18.literal("session.launch"),
956
+ z19.object({
957
+ type: z19.literal("session.launch"),
889
958
  tool: ToolSchema,
890
- cwd: z18.string().min(1),
891
- name: z18.string().optional(),
892
- prompt: z18.string().optional(),
893
- continue: z18.boolean().optional(),
894
- qid: z18.string().min(1).optional(),
959
+ cwd: z19.string().min(1),
960
+ name: z19.string().optional(),
961
+ prompt: z19.string().optional(),
962
+ continue: z19.boolean().optional(),
963
+ qid: z19.string().min(1).optional(),
895
964
  /** With `qid`: the card's rows to launch (#24), indexes into the turn's `proposals`, each once; the first row without it. */
896
965
  rows: launchRows.optional(),
897
966
  /** Folders in addition to `cwd` (Claude: `--add-dir`); resolved like `cwd`. */
898
- dirs: z18.array(z18.string().min(1)).max(20).optional(),
967
+ dirs: z19.array(z19.string().min(1)).max(20).optional(),
899
968
  /** The agent's model and effort (#58), the agent's own default when left out; with `qid`, for every row launched. */
900
969
  model: ModelIdSchema.optional(),
901
970
  effort: EffortSchema.optional(),
@@ -906,98 +975,98 @@ var ClientMessageSchema = z18.discriminatedUnion("type", [
906
975
  /** A live team the new session joins at once, in `role`. */
907
976
  team: TeamIdSchema.optional(),
908
977
  /** Routing by the fleet, removed before the message reaches the daemon. */
909
- machineId: z18.string().optional()
978
+ machineId: z19.string().optional()
910
979
  }),
911
980
  /** The launch form's folder browser: list a folder's subfolders (home when no `path`), or make `name` inside `parent`
912
981
  * and list it. Both are answered with a `folder.listing`. */
913
- z18.object({
914
- type: z18.literal("folder.list"),
915
- path: z18.string().optional(),
982
+ z19.object({
983
+ type: z19.literal("folder.list"),
984
+ path: z19.string().optional(),
916
985
  /** Routing by the fleet, removed before the message reaches the daemon. */
917
- machineId: z18.string().optional(),
986
+ machineId: z19.string().optional(),
918
987
  /** Copied into the `folder.listing` that answers; the fleet relays only that listing, from the machine asked. */
919
- requestId: z18.string().min(1).max(100).optional()
988
+ requestId: z19.string().min(1).max(100).optional()
920
989
  }),
921
- z18.object({
922
- type: z18.literal("folder.create"),
923
- parent: z18.string().min(1),
924
- name: z18.string().min(1),
990
+ z19.object({
991
+ type: z19.literal("folder.create"),
992
+ parent: z19.string().min(1),
993
+ name: z19.string().min(1),
925
994
  /** As for `folder.list`: routing by the fleet, and the request its `folder.listing` answers. */
926
- machineId: z18.string().optional(),
927
- requestId: z18.string().min(1).max(100).optional()
995
+ machineId: z19.string().optional(),
996
+ requestId: z19.string().min(1).max(100).optional()
928
997
  }),
929
998
  /** `steer`: OMP only, redirect the turn running now instead of queueing the text after it. */
930
999
  /** OMP's `ask`: the answers to every question of the dialog `requestId`, in order (`selected` labels, and/or free `text`); `cancel` declines it. */
931
- z18.object({ type: z18.literal("session.answer"), id: z18.string(), requestId: z18.string().min(1).max(200), answers: z18.array(z18.object({ id: z18.string(), selected: z18.array(z18.string().max(2e3)).max(100), text: z18.string().max(2e4).optional() })).max(50), cancel: z18.boolean().optional() }),
932
- z18.object({ type: z18.literal("session.reply"), id: z18.string(), text: z18.string().min(1), steer: z18.boolean().optional() }),
1000
+ z19.object({ type: z19.literal("session.answer"), id: z19.string(), requestId: z19.string().min(1).max(200), answers: z19.array(z19.object({ id: z19.string(), selected: z19.array(z19.string().max(2e3)).max(100), text: z19.string().max(2e4).optional() })).max(50), cancel: z19.boolean().optional() }),
1001
+ z19.object({ type: z19.literal("session.reply"), id: z19.string(), text: z19.string(), attachments: z19.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(), steer: z19.boolean().optional() }),
933
1002
  /** The chat card's instruction (spec 2026-10-12 relay §4): sent now if the session is free, else queued until it is. */
934
- z18.object({ type: z18.literal("session.relay"), qid: z18.string().min(1) }),
1003
+ z19.object({ type: z19.literal("session.relay"), qid: z19.string().min(1) }),
935
1004
  /** Takes the card's queued instruction back out of the queue. */
936
- z18.object({ type: z18.literal("session.relay.cancel"), qid: z18.string().min(1) }),
1005
+ z19.object({ type: z19.literal("session.relay.cancel"), qid: z19.string().min(1) }),
937
1006
  /** The chat's close card: closes the turn's sessions, `rows` (indexes into its `closes`) or all of them. */
938
- z18.object({ type: z18.literal("session.close"), qid: z18.string().min(1), rows: launchRows.optional() }),
1007
+ z19.object({ type: z19.literal("session.close"), qid: z19.string().min(1), rows: launchRows.optional() }),
939
1008
  /** `always` and `note` only where the harness offers them (`offers`); a note goes with `deny`, for the agent to read. */
940
- z18.object({
941
- type: z18.literal("session.decide"),
942
- id: z18.string(),
1009
+ z19.object({
1010
+ type: z19.literal("session.decide"),
1011
+ id: z19.string(),
943
1012
  decision: DecisionSchema,
944
- note: z18.string().trim().min(1).max(NOTE_MAX).optional(),
1013
+ note: z19.string().trim().min(1).max(NOTE_MAX).optional(),
945
1014
  /** The request the user saw when deciding: refused (the request changed) unless it is still the pending one. Left out by older fronts and the island.
946
1015
  * `key` (pendingKey) covers its whole content; older fronts send kind and text only. */
947
- expect: PendingSchema.pick({ kind: true, text: true }).extend({ key: z18.string().max(64).optional() }).optional()
1016
+ expect: PendingSchema.pick({ kind: true, text: true }).extend({ key: z19.string().max(64).optional() }).optional()
948
1017
  }),
949
- z18.object({ type: z18.literal("session.focus"), id: z18.string() }),
950
- z18.object({ type: z18.literal("session.kill"), id: z18.string() }),
1018
+ z19.object({ type: z19.literal("session.focus"), id: z19.string() }),
1019
+ z19.object({ type: z19.literal("session.kill"), id: z19.string() }),
951
1020
  /** `restoreQueue` (OMP): also take back what waits in its queue, returned as `session.queue.restored` to put in the draft. */
952
- z18.object({ type: z18.literal("session.interrupt"), id: z18.string(), restoreQueue: z18.boolean().optional() }),
1021
+ z19.object({ type: z19.literal("session.interrupt"), id: z19.string(), restoreQueue: z19.boolean().optional() }),
953
1022
  /** Removes a finished (exited) or read-only (hook_only) session from the list; refused for a live launcher session. */
954
- z18.object({ type: z18.literal("session.dismiss"), id: z18.string() }),
1023
+ z19.object({ type: z19.literal("session.dismiss"), id: z19.string() }),
955
1024
  /** Puts a session in the archive: it leaves the live list but is kept. */
956
- z18.object({ type: z18.literal("session.archive"), id: z18.string() }),
1025
+ z19.object({ type: z19.literal("session.archive"), id: z19.string() }),
957
1026
  /** Takes an archived session back out of the archive, as it was. */
958
- z18.object({ type: z18.literal("session.restore"), id: z18.string() }),
1027
+ z19.object({ type: z19.literal("session.restore"), id: z19.string() }),
959
1028
  /** Starts the agent again on an archived session's conversation. */
960
- z18.object({ type: z18.literal("session.resume"), id: z18.string() }),
1029
+ z19.object({ type: z19.literal("session.resume"), id: z19.string() }),
961
1030
  /** Changes a live session's model and effort (#58, lot 2), `null` for the agent's own default: the daemon restarts the
962
1031
  * agent on the same conversation with them, at once when the session is free, else at its next free moment. */
963
- z18.object({ type: z18.literal("session.model"), id: z18.string(), model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
1032
+ z19.object({ type: z19.literal("session.model"), id: z19.string(), model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
964
1033
  /** Deletes a session for good, archived or not. */
965
- z18.object({ type: z18.literal("session.delete"), id: z18.string() }),
1034
+ z19.object({ type: z19.literal("session.delete"), id: z19.string() }),
966
1035
  /** The user names the session's topic from the overlay (500 characters at most, the hook's prompt cap); an empty topic hands back to the automatic sources. */
967
- z18.object({ type: z18.literal("session.rename"), id: z18.string(), topic: z18.string().max(500) }),
968
- z18.object({ type: z18.literal("session.thread"), id: z18.string() }),
969
- z18.object({ type: z18.literal("session.register"), session: SessionSchema }),
1036
+ z19.object({ type: z19.literal("session.rename"), id: z19.string(), topic: z19.string().max(500) }),
1037
+ z19.object({ type: z19.literal("session.thread"), id: z19.string() }),
1038
+ z19.object({ type: z19.literal("session.register"), session: SessionSchema }),
970
1039
  /** `agent` and `cwd` describe the session when the daemon does not know it yet (a hook from an agent started without ledge). */
971
- z18.object({ type: z18.literal("session.event"), id: z18.string(), event: SessionEventSchema, agent: ToolSchema.optional(), cwd: z18.string().optional() }),
972
- z18.object({ type: z18.literal("session.unregister"), id: z18.string(), exitCode: z18.number().int().nullable() }),
973
- z18.object({ type: z18.literal("snapshot.request") }),
974
- z18.object({ type: z18.literal("chat.ask"), qid: z18.string().min(1), text: z18.string().min(1), tool: ToolSchema.optional() }),
975
- z18.object({ type: z18.literal("chat.cancel"), qid: z18.string().min(1) }),
1040
+ z19.object({ type: z19.literal("session.event"), id: z19.string(), event: SessionEventSchema, agent: ToolSchema.optional(), cwd: z19.string().optional() }),
1041
+ z19.object({ type: z19.literal("session.unregister"), id: z19.string(), exitCode: z19.number().int().nullable() }),
1042
+ z19.object({ type: z19.literal("snapshot.request") }),
1043
+ z19.object({ type: z19.literal("chat.ask"), qid: z19.string().min(1), text: z19.string(), images: z19.array(ImageUploadSchema).max(ATTACHMENTS_MAX).optional(), tool: ToolSchema.optional() }),
1044
+ z19.object({ type: z19.literal("chat.cancel"), qid: z19.string().min(1) }),
976
1045
  /** Starts a new conversation with Ledge: the day's turns are cleared and the next question opens a new harness session. */
977
- z18.object({ type: z18.literal("chat.reset") }),
1046
+ z19.object({ type: z19.literal("chat.reset") }),
978
1047
  /** Installs `@paradow/ledge@version` and restarts the service (spec 2026-10-12 auto-update §6). */
979
- z18.object({ type: z18.literal("update.daemon"), version: z18.string().regex(SEMVER) }),
1048
+ z19.object({ type: z19.literal("update.daemon"), version: z19.string().regex(SEMVER) }),
980
1049
  /** Replaces the daemon's machines list (first connected front wins); refused unless valid. */
981
- z18.object({ type: z18.literal("machines.set"), machines: MachinesSchema }),
1050
+ z19.object({ type: z19.literal("machines.set"), machines: MachinesSchema }),
982
1051
  /** Replaces the local daemon's projects and rules as a whole; refused unless valid. */
983
- z18.object({ type: z18.literal("projects.set"), projects: ProjectsSchema }),
1052
+ z19.object({ type: z19.literal("projects.set"), projects: ProjectsSchema }),
984
1053
  /** Replaces the island's settings as a whole; refused unless valid. */
985
- z18.object({ type: z18.literal("island.set"), prefs: IslandPrefsSchema }),
1054
+ z19.object({ type: z19.literal("island.set"), prefs: IslandPrefsSchema }),
986
1055
  /** Mobile access (lot 3), local fronts only. A changed `relayUrl` drops the relay session. */
987
- z18.object({ type: z18.literal("mobile.set"), enabled: z18.boolean(), relayUrl: RelayUrlSchema }),
988
- z18.object({ type: z18.literal("mobile.signin") }),
989
- z18.object({ type: z18.literal("mobile.signin.cancel") }),
990
- z18.object({ type: z18.literal("mobile.signout") }),
1056
+ z19.object({ type: z19.literal("mobile.set"), enabled: z19.boolean(), relayUrl: RelayUrlSchema }),
1057
+ z19.object({ type: z19.literal("mobile.signin") }),
1058
+ z19.object({ type: z19.literal("mobile.signin.cancel") }),
1059
+ z19.object({ type: z19.literal("mobile.signout") }),
991
1060
  /** While the link is offline: try again now. */
992
- z18.object({ type: z18.literal("mobile.retry") }),
993
- z18.object({ type: z18.literal("mobile.pairing.approve"), id: z18.string().min(1).max(100) }),
994
- z18.object({ type: z18.literal("mobile.pairing.deny"), id: z18.string().min(1).max(100) }),
1061
+ z19.object({ type: z19.literal("mobile.retry") }),
1062
+ z19.object({ type: z19.literal("mobile.pairing.approve"), id: z19.string().min(1).max(100) }),
1063
+ z19.object({ type: z19.literal("mobile.pairing.deny"), id: z19.string().min(1).max(100) }),
995
1064
  /** `id`: the device's fingerprint. */
996
- z18.object({ type: z18.literal("mobile.device.revoke"), id: z18.string().min(1).max(100) }),
1065
+ z19.object({ type: z19.literal("mobile.device.revoke"), id: z19.string().min(1).max(100) }),
997
1066
  /** One session writing to another by Ledge name (`to` may end in ` [ref]`); `intercept` (the Claude hook): answer `native` unless `to` is a live non-Claude session. */
998
- z18.object({ type: z18.literal("session.message"), rid: z18.string().min(1), from: z18.string().min(1), to: z18.string().min(1), text: z18.string().min(1).max(MESSAGE_MAX), summary: z18.string().max(SUMMARY_MAX).optional(), intercept: z18.boolean().optional() }),
1067
+ z19.object({ type: z19.literal("session.message"), rid: z19.string().min(1), from: z19.string().min(1), to: z19.string().min(1), text: z19.string().min(1).max(MESSAGE_MAX), summary: z19.string().max(SUMMARY_MAX).optional(), intercept: z19.boolean().optional() }),
999
1068
  /** `ledge read <id>`: a message's whole body, for its receiver or its sender (`by`, a session id). */
1000
- z18.object({ type: z18.literal("message.read"), id: z18.string().min(1).max(40), by: z18.string().min(1) }),
1069
+ z19.object({ type: z19.literal("message.read"), id: z19.string().min(1).max(40), by: z19.string().min(1) }),
1001
1070
  TaskCmdSchema,
1002
1071
  SessionHandoverSchema,
1003
1072
  ContextSetSchema,
@@ -1101,27 +1170,27 @@ function pickName(taken, preferred) {
1101
1170
  }
1102
1171
 
1103
1172
  // ../protocol/src/proposal.ts
1104
- import { z as z19 } from "zod";
1105
- var ProposalBlockSchema = z19.object({ cwd: z19.string().trim().min(1), prompt: z19.string().optional() });
1106
- var RelayBlockSchema = z19.object({ session: z19.string().trim().min(1), text: z19.string().trim().min(1) });
1107
- var ProjectBlockSchema = z19.object({
1108
- name: z19.string().trim().min(1).max(80),
1109
- description: z19.string().max(500).optional(),
1110
- repos: z19.array(z19.object({ path: z19.string().min(1).max(4096), why: z19.string().trim().min(1).max(200) })).min(1).max(50),
1111
- addTo: z19.string().trim().max(80).optional()
1173
+ import { z as z20 } from "zod";
1174
+ var ProposalBlockSchema = z20.object({ cwd: z20.string().trim().min(1), prompt: z20.string().optional() });
1175
+ var RelayBlockSchema = z20.object({ session: z20.string().trim().min(1), text: z20.string().trim().min(1) });
1176
+ var ProjectBlockSchema = z20.object({
1177
+ name: z20.string().trim().min(1).max(80),
1178
+ description: z20.string().max(500).optional(),
1179
+ repos: z20.array(z20.object({ path: z20.string().min(1).max(4096), why: z20.string().trim().min(1).max(200) })).min(1).max(50),
1180
+ addTo: z20.string().trim().max(80).optional()
1112
1181
  });
1113
- var CloseBlockSchema = z19.object({ session: z19.string().trim().min(1) });
1114
- var TeamBlockSchema = z19.object({
1115
- cwd: z19.string().trim().min(1),
1116
- template: z19.string().trim().min(1).max(64).optional(),
1117
- name: z19.string().trim().min(1).max(60).optional(),
1118
- seats: z19.array(z19.object({ role: z19.string().trim().min(1).max(40), tool: z19.enum(["claude", "codex", "cursor", "omp"]).optional(), model: z19.string().max(200).optional(), effort: z19.string().max(20).optional(), name: z19.string().max(40).optional() })).min(1).max(8).optional()
1182
+ var CloseBlockSchema = z20.object({ session: z20.string().trim().min(1) });
1183
+ var TeamBlockSchema = z20.object({
1184
+ cwd: z20.string().trim().min(1),
1185
+ template: z20.string().trim().min(1).max(64).optional(),
1186
+ name: z20.string().trim().min(1).max(60).optional(),
1187
+ seats: z20.array(z20.object({ role: z20.string().trim().min(1).max(40), tool: z20.enum(["claude", "codex", "cursor", "omp"]).optional(), model: z20.string().max(200).optional(), effort: z20.string().max(20).optional(), name: z20.string().max(40).optional() })).min(1).max(8).optional()
1119
1188
  }).refine((b) => !!b.template || !!b.seats?.length, "a template or seats");
1120
- var TaskBlockSchema = z19.object({
1121
- to: z19.string().trim().min(1).max(80),
1122
- team: z19.string().trim().max(40).optional(),
1123
- title: z19.string().trim().min(1).max(200),
1124
- text: z19.string().max(2e4).optional()
1189
+ var TaskBlockSchema = z20.object({
1190
+ to: z20.string().trim().min(1).max(80),
1191
+ team: z20.string().trim().max(40).optional(),
1192
+ title: z20.string().trim().min(1).max(200),
1193
+ text: z20.string().max(2e4).optional()
1125
1194
  });
1126
1195
  var BLOCK = /```ledge-(session|relay|project|close|team|task)[ \t]*\n?([\s\S]*?)(?:```|$)/g;
1127
1196
  function extractProposal(answer) {
@@ -1194,80 +1263,80 @@ function sessionTitle(s) {
1194
1263
  }
1195
1264
 
1196
1265
  // ../protocol/src/relay.ts
1197
- import { z as z20 } from "zod";
1266
+ import { z as z21 } from "zod";
1198
1267
  var SEALED_MAX = 1048576;
1199
- var RelayPeerSchema = z20.union([z20.literal("pc"), z20.string().regex(/^d_[A-Za-z0-9_-]{16,40}$/)]);
1200
- var RelayClientMessageSchema = z20.object({
1201
- type: z20.literal("send"),
1268
+ var RelayPeerSchema = z21.union([z21.literal("pc"), z21.string().regex(/^d_[A-Za-z0-9_-]{16,40}$/)]);
1269
+ var RelayClientMessageSchema = z21.object({
1270
+ type: z21.literal("send"),
1202
1271
  to: RelayPeerSchema,
1203
- body: z20.string().max(SEALED_MAX)
1272
+ body: z21.string().max(SEALED_MAX)
1204
1273
  });
1205
- var RelayServerMessageSchema = z20.discriminatedUnion("type", [
1206
- z20.object({ type: z20.literal("welcome"), accountId: z20.string(), self: RelayPeerSchema, online: z20.array(RelayPeerSchema) }),
1207
- z20.object({ type: z20.literal("deliver"), from: RelayPeerSchema, body: z20.string() }),
1208
- z20.object({ type: z20.literal("presence"), peer: RelayPeerSchema, online: z20.boolean() }),
1274
+ var RelayServerMessageSchema = z21.discriminatedUnion("type", [
1275
+ z21.object({ type: z21.literal("welcome"), accountId: z21.string(), self: RelayPeerSchema, online: z21.array(RelayPeerSchema) }),
1276
+ z21.object({ type: z21.literal("deliver"), from: RelayPeerSchema, body: z21.string() }),
1277
+ z21.object({ type: z21.literal("presence"), peer: RelayPeerSchema, online: z21.boolean() }),
1209
1278
  /**
1210
1279
  * A frame was refused; `to` names the target when it is known.
1211
1280
  * Contract: an error concerning a sealed body (`offline`, `too_large`, or a socket closed with 4008
1212
1281
  * `rate_limited`) means that body was not delivered, so the sequence on that channel is lost: the
1213
1282
  * sender must re-handshake with that peer before sending to it again.
1214
1283
  */
1215
- z20.object({
1216
- type: z20.literal("error"),
1217
- code: z20.enum(["bad_frame", "too_large", "bad_target", "offline", "rate_limited"]),
1284
+ z21.object({
1285
+ type: z21.literal("error"),
1286
+ code: z21.enum(["bad_frame", "too_large", "bad_target", "offline", "rate_limited"]),
1218
1287
  to: RelayPeerSchema.optional()
1219
1288
  })
1220
1289
  ]);
1221
1290
  var PAIR_LABEL_MAX = 40;
1222
- var KeyB64 = z20.string().regex(/^[A-Za-z0-9_-]{87}$/);
1223
- var Nonce = z20.string().regex(/^[A-Za-z0-9_-]{43}$/);
1224
- var Fingerprint = z20.string().regex(/^[A-Za-z0-9_-]{22}$/);
1225
- var SealedFrameSchema = z20.discriminatedUnion("k", [
1291
+ var KeyB64 = z21.string().regex(/^[A-Za-z0-9_-]{87}$/);
1292
+ var Nonce = z21.string().regex(/^[A-Za-z0-9_-]{43}$/);
1293
+ var Fingerprint = z21.string().regex(/^[A-Za-z0-9_-]{22}$/);
1294
+ var SealedFrameSchema = z21.discriminatedUnion("k", [
1226
1295
  /** `e` is the ephemeral key, `f` the fingerprint of the sender's static key. */
1227
- z20.object({ k: z20.literal("hello"), e: KeyB64, f: Fingerprint }),
1228
- z20.object({ k: z20.literal("msg"), s: z20.string() }),
1229
- z20.object({ k: z20.literal("pair_commit"), c: Nonce, label: z20.string().trim().min(1).max(PAIR_LABEL_MAX) }),
1230
- z20.object({ k: z20.literal("pair_offer"), pc: KeyB64, n: Nonce }),
1231
- z20.object({ k: z20.literal("pair_reveal"), d: KeyB64, n: Nonce }),
1232
- z20.object({ k: z20.literal("pair_ok") }),
1233
- z20.object({ k: z20.literal("pair_no") }),
1234
- z20.object({ k: z20.literal("denied") }),
1235
- z20.object({ k: z20.literal("rekey") })
1296
+ z21.object({ k: z21.literal("hello"), e: KeyB64, f: Fingerprint }),
1297
+ z21.object({ k: z21.literal("msg"), s: z21.string() }),
1298
+ z21.object({ k: z21.literal("pair_commit"), c: Nonce, label: z21.string().trim().min(1).max(PAIR_LABEL_MAX) }),
1299
+ z21.object({ k: z21.literal("pair_offer"), pc: KeyB64, n: Nonce }),
1300
+ z21.object({ k: z21.literal("pair_reveal"), d: KeyB64, n: Nonce }),
1301
+ z21.object({ k: z21.literal("pair_ok") }),
1302
+ z21.object({ k: z21.literal("pair_no") }),
1303
+ z21.object({ k: z21.literal("denied") }),
1304
+ z21.object({ k: z21.literal("rekey") })
1236
1305
  ]);
1237
1306
 
1238
1307
  // ../protocol/src/relayAuth.ts
1239
- import { z as z21 } from "zod";
1308
+ import { z as z22 } from "zod";
1240
1309
  var RELAY_CLOSE_SIGNED_OUT = 4001;
1241
- var DeviceStartResponseSchema = z21.object({
1242
- deviceCode: z21.string().min(1),
1243
- userCode: z21.string().min(1),
1244
- verificationUri: z21.string().min(1),
1245
- expiresIn: z21.number().int().positive(),
1246
- interval: z21.number().int().positive()
1310
+ var DeviceStartResponseSchema = z22.object({
1311
+ deviceCode: z22.string().min(1),
1312
+ userCode: z22.string().min(1),
1313
+ verificationUri: z22.string().min(1),
1314
+ expiresIn: z22.number().int().positive(),
1315
+ interval: z22.number().int().positive()
1247
1316
  });
1248
- var DevicePollResponseSchema = z21.discriminatedUnion("status", [
1249
- z21.object({ status: z21.literal("pending") }),
1250
- z21.object({ status: z21.literal("slow_down"), interval: z21.number().int().positive() }),
1317
+ var DevicePollResponseSchema = z22.discriminatedUnion("status", [
1318
+ z22.object({ status: z22.literal("pending") }),
1319
+ z22.object({ status: z22.literal("slow_down"), interval: z22.number().int().positive() }),
1251
1320
  /** `token` is the relay session token, shown this once only. */
1252
- z21.object({ status: z21.literal("ok"), token: z21.string().min(1), accountId: z21.string().min(1), login: z21.string().min(1) }),
1253
- z21.object({ status: z21.literal("expired") }),
1254
- z21.object({ status: z21.literal("denied") }),
1255
- z21.object({ status: z21.literal("not_allowed") })
1321
+ z22.object({ status: z22.literal("ok"), token: z22.string().min(1), accountId: z22.string().min(1), login: z22.string().min(1) }),
1322
+ z22.object({ status: z22.literal("expired") }),
1323
+ z22.object({ status: z22.literal("denied") }),
1324
+ z22.object({ status: z22.literal("not_allowed") })
1256
1325
  ]);
1257
- var DeviceRevokeResponseSchema = z21.object({ revoked: z21.number().int().min(0) });
1258
- var MeResponseSchema = z21.object({
1259
- accountId: z21.string().min(1),
1260
- login: z21.string().min(1),
1326
+ var DeviceRevokeResponseSchema = z22.object({ revoked: z22.number().int().min(0) });
1327
+ var MeResponseSchema = z22.object({
1328
+ accountId: z22.string().min(1),
1329
+ login: z22.string().min(1),
1261
1330
  peer: RelayPeerSchema,
1262
- kind: z21.enum(["pc", "device"])
1331
+ kind: z22.enum(["pc", "device"])
1263
1332
  });
1264
1333
 
1265
1334
  // ../protocol/src/phone.ts
1266
- import { z as z23 } from "zod";
1335
+ import { z as z24 } from "zod";
1267
1336
 
1268
1337
  // ../protocol/src/push.ts
1269
- import { z as z22 } from "zod";
1270
- var B64U = z22.string().regex(/^[A-Za-z0-9_-]*$/);
1338
+ import { z as z23 } from "zod";
1339
+ var B64U = z23.string().regex(/^[A-Za-z0-9_-]*$/);
1271
1340
  var PUSH_HOSTS = ["*.push.apple.com", "fcm.googleapis.com", "updates.push.services.mozilla.com", "*.notify.windows.com"];
1272
1341
  function pushEndpointAllowed(url) {
1273
1342
  let u;
@@ -1284,29 +1353,29 @@ function pushEndpointAllowed(url) {
1284
1353
  return host.length > suffix.length && host.endsWith(suffix);
1285
1354
  });
1286
1355
  }
1287
- var PushSendSchema = z22.object({
1288
- endpoint: z22.string().url().max(1024),
1356
+ var PushSendSchema = z23.object({
1357
+ endpoint: z23.string().url().max(1024),
1289
1358
  body: B64U.max(5462),
1290
- ttl: z22.number().int().min(0).max(86400),
1291
- urgency: z22.enum(["normal", "high"]),
1292
- topic: z22.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
1359
+ ttl: z23.number().int().min(0).max(86400),
1360
+ urgency: z23.enum(["normal", "high"]),
1361
+ topic: z23.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
1293
1362
  });
1294
- var PushSendResultSchema = z22.object({
1295
- result: z22.enum(["sent", "gone", "failed"]),
1296
- status: z22.number().int().optional()
1363
+ var PushSendResultSchema = z23.object({
1364
+ result: z23.enum(["sent", "gone", "failed"]),
1365
+ status: z23.number().int().optional()
1297
1366
  });
1298
- var PushSubscriptionSchema = z22.object({
1299
- endpoint: z22.string().url().max(1024).refine(pushEndpointAllowed),
1300
- keys: z22.object({ p256dh: B64U.length(87), auth: B64U.length(22) })
1367
+ var PushSubscriptionSchema = z23.object({
1368
+ endpoint: z23.string().url().max(1024).refine(pushEndpointAllowed),
1369
+ keys: z23.object({ p256dh: B64U.length(87), auth: B64U.length(22) })
1301
1370
  });
1302
- var PushPayloadSchema = z22.object({
1303
- v: z22.literal(1),
1304
- kind: z22.enum(["request", "question", "finished", "chat"]),
1305
- title: z22.string().max(80),
1306
- body: z22.string().max(120),
1307
- url: z22.string().regex(/^\/#\/[A-Za-z0-9/%._~-]*$/).max(300),
1308
- badge: z22.number().int().min(0).max(999),
1309
- tag: z22.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
1371
+ var PushPayloadSchema = z23.object({
1372
+ v: z23.literal(1),
1373
+ kind: z23.enum(["request", "question", "finished", "chat"]),
1374
+ title: z23.string().max(80),
1375
+ body: z23.string().max(120),
1376
+ url: z23.string().regex(/^\/#\/[A-Za-z0-9/%._~-]*$/).max(300),
1377
+ badge: z23.number().int().min(0).max(999),
1378
+ tag: z23.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
1310
1379
  });
1311
1380
 
1312
1381
  // ../protocol/src/phone.ts
@@ -1327,22 +1396,22 @@ var THREAD_TEXT_MAX = 8192;
1327
1396
  var TOOL_TEXT_MAX = 2048;
1328
1397
  var PENDING_BODY_MAX = 65536;
1329
1398
  var SNAPSHOT_BODY_MAX = 2048;
1330
- var RiskSchema = z23.enum(["low", "high"]);
1331
- var RiskReasonSchema = z23.enum(["question", "read_only_command", "edit_inside_folder", "pushes", "destroys", "deploys", "publishes", "pipes_to_shell", "network", "writes_outside_folder", "sensitive_path", "unsafe_flag", "shell_syntax", "script", "plan", "unknown"]);
1332
- var OperationIdSchema = z23.string().uuid();
1333
- var OperationStatusSchema = z23.enum(["received", "applied", "rejected", "failed"]);
1334
- var OperationReasonSchema = z23.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired"]);
1335
- var CapabilitiesSchema = z23.object({
1399
+ var RiskSchema = z24.enum(["low", "high"]);
1400
+ var RiskReasonSchema = z24.enum(["question", "read_only_command", "edit_inside_folder", "pushes", "destroys", "deploys", "publishes", "pipes_to_shell", "network", "writes_outside_folder", "sensitive_path", "unsafe_flag", "shell_syntax", "script", "plan", "unknown"]);
1401
+ var OperationIdSchema = z24.string().uuid();
1402
+ var OperationStatusSchema = z24.enum(["received", "applied", "rejected", "failed"]);
1403
+ var OperationReasonSchema = z24.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired"]);
1404
+ var CapabilitiesSchema = z24.object({
1336
1405
  /** A question can be answered in free text. */
1337
- freeTextAnswer: z23.boolean(),
1406
+ freeTextAnswer: z24.boolean(),
1338
1407
  /** The session's accept/deny can be typed; absent = true. */
1339
- decide: z23.boolean().optional(),
1408
+ decide: z24.boolean().optional(),
1340
1409
  /** OMP's dialog questions can be answered from the phone; absent = false. */
1341
- questions: z23.boolean().optional()
1410
+ questions: z24.boolean().optional()
1342
1411
  });
1343
1412
  var UNTYPABLE = /[\u0000-\u001f\u007f-\u009f\u200b\u200e\u200f\u2028-\u202e\u2066-\u2069\ufeff]/;
1344
- var CapabilitiesByToolSchema = z23.object({ claude: CapabilitiesSchema, codex: CapabilitiesSchema, cursor: CapabilitiesSchema, omp: CapabilitiesSchema });
1345
- var PhonePendingSchema = PendingSchema.extend({ key: z23.string().max(64), risk: RiskSchema, riskReason: RiskReasonSchema, cut: z23.literal(true).optional() });
1413
+ var CapabilitiesByToolSchema = z24.object({ claude: CapabilitiesSchema, codex: CapabilitiesSchema, cursor: CapabilitiesSchema, omp: CapabilitiesSchema });
1414
+ var PhonePendingSchema = PendingSchema.extend({ key: z24.string().max(64), risk: RiskSchema, riskReason: RiskReasonSchema, cut: z24.literal(true).optional() });
1346
1415
  var PhoneSessionSchema = SessionSchema.pick({
1347
1416
  id: true,
1348
1417
  name: true,
@@ -1365,202 +1434,210 @@ var PhoneSessionSchema = SessionSchema.pick({
1365
1434
  /** Its context and its relief (context handover spec §3.2): the phone shows the gauge; the relief is started from the PC. */
1366
1435
  context: true,
1367
1436
  relieves: true
1368
- }).extend({ pending: PhonePendingSchema.optional(), unread: z23.boolean(), handover: z23.object({ state: HandoverStateSchema, toName: z23.string().max(80).optional() }).strict().optional() });
1369
- var B64U2 = z23.string().regex(/^[A-Za-z0-9_-]*$/);
1370
- var CredentialSchema = z23.object({
1437
+ }).extend({ pending: PhonePendingSchema.optional(), unread: z24.boolean(), handover: z24.object({ state: HandoverStateSchema, toName: z24.string().max(80).optional() }).strict().optional() });
1438
+ var B64U2 = z24.string().regex(/^[A-Za-z0-9_-]*$/);
1439
+ var CredentialSchema = z24.object({
1371
1440
  id: B64U2.max(1366),
1372
1441
  publicKey: B64U2.max(2048),
1373
- alg: z23.union([z23.literal(-7), z23.literal(-257)]),
1442
+ alg: z24.union([z24.literal(-7), z24.literal(-257)]),
1374
1443
  clientDataJSON: B64U2.max(4096),
1375
1444
  authenticatorData: B64U2.max(4096),
1376
- transports: z23.array(z23.string().max(20)).max(8).optional()
1445
+ transports: z24.array(z24.string().max(20)).max(8).optional()
1377
1446
  });
1378
- var AssertionSchema = z23.object({
1447
+ var AssertionSchema = z24.object({
1379
1448
  id: B64U2.max(1366),
1380
1449
  clientDataJSON: B64U2.max(4096),
1381
1450
  authenticatorData: B64U2.max(4096),
1382
1451
  signature: B64U2.max(1024)
1383
1452
  });
1384
- var ConfirmPurposeSchema = z23.enum(["register", "decide", "launch", "close"]);
1453
+ var ConfirmPurposeSchema = z24.enum(["register", "decide", "launch", "close"]);
1385
1454
  var PhoneChatStepSchema = ChatStepSchema.pick({ kind: true, id: true, name: true }).strict();
1386
- var PhoneChatProposalSchema = z23.object({
1387
- project: z23.string().max(80),
1455
+ var PhoneChatProposalSchema = z24.object({
1456
+ project: z24.string().max(80),
1388
1457
  tool: ToolSchema,
1389
- prompt: z23.string().max(TOOL_TEXT_MAX).optional(),
1390
- launchedId: z23.string().optional(),
1391
- failed: z23.literal(true).optional()
1458
+ prompt: z24.string().max(TOOL_TEXT_MAX).optional(),
1459
+ launchedId: z24.string().optional(),
1460
+ failed: z24.literal(true).optional()
1392
1461
  }).strict();
1393
- var PhoneChatRelaySchema = z23.object({
1394
- id: z23.string().min(1),
1395
- name: z23.string(),
1462
+ var PhoneChatRelaySchema = z24.object({
1463
+ id: z24.string().min(1),
1464
+ name: z24.string(),
1396
1465
  tool: ToolSchema,
1397
- text: z23.string().max(TOOL_TEXT_MAX),
1398
- status: z23.enum(["queued", "sent"]).optional(),
1399
- sentAt: z23.string().datetime().optional(),
1400
- failed: z23.literal(true).optional()
1466
+ text: z24.string().max(TOOL_TEXT_MAX),
1467
+ status: z24.enum(["queued", "sent"]).optional(),
1468
+ sentAt: z24.string().datetime().optional(),
1469
+ failed: z24.literal(true).optional()
1401
1470
  }).strict();
1402
- var PhoneChatCloseSchema = z23.object({
1403
- id: z23.string().min(1),
1404
- name: z23.string(),
1471
+ var PhoneChatCloseSchema = z24.object({
1472
+ id: z24.string().min(1),
1473
+ name: z24.string(),
1405
1474
  tool: ToolSchema,
1406
- how: z23.enum(["exit", "killed"]).optional(),
1407
- closedAt: z23.string().datetime().optional(),
1408
- failed: z23.literal(true).optional()
1475
+ how: z24.enum(["exit", "killed"]).optional(),
1476
+ closedAt: z24.string().datetime().optional(),
1477
+ failed: z24.literal(true).optional()
1409
1478
  }).strict();
1410
- var PhoneChatTeamSchema = z23.object({
1411
- project: z23.string().max(80),
1412
- name: z23.string().max(60),
1413
- template: z23.string().max(64).optional(),
1414
- seats: z23.array(z23.object({ role: z23.string().max(40), label: z23.string().max(40), tool: ToolSchema, model: z23.string().max(200).optional(), effort: z23.string().max(20).optional(), autonomy: AutonomySchema.optional() }).strict()).min(1).max(8),
1415
- teamId: z23.string().max(40).optional(),
1416
- launchedAt: z23.string().datetime().optional(),
1417
- failed: z23.literal(true).optional()
1479
+ var PhoneChatTeamSchema = z24.object({
1480
+ project: z24.string().max(80),
1481
+ name: z24.string().max(60),
1482
+ template: z24.string().max(64).optional(),
1483
+ seats: z24.array(z24.object({ role: z24.string().max(40), label: z24.string().max(40), tool: ToolSchema, model: z24.string().max(200).optional(), effort: z24.string().max(20).optional(), autonomy: AutonomySchema.optional() }).strict()).min(1).max(8),
1484
+ teamId: z24.string().max(40).optional(),
1485
+ launchedAt: z24.string().datetime().optional(),
1486
+ failed: z24.literal(true).optional()
1418
1487
  }).strict();
1419
- var PhoneChatTaskSchema = z23.object({
1420
- to: z23.string().max(80),
1421
- toName: z23.string().max(80).optional(),
1422
- title: z23.string().max(200),
1423
- text: z23.string().max(TOOL_TEXT_MAX).optional(),
1424
- taskId: z23.string().max(40).optional(),
1425
- ownerName: z23.string().max(80).optional(),
1426
- state: z23.enum(["open", "taken", "done", "refused", "dropped"]).optional(),
1427
- failed: z23.literal(true).optional()
1488
+ var PhoneChatTaskSchema = z24.object({
1489
+ to: z24.string().max(80),
1490
+ toName: z24.string().max(80).optional(),
1491
+ title: z24.string().max(200),
1492
+ text: z24.string().max(TOOL_TEXT_MAX).optional(),
1493
+ taskId: z24.string().max(40).optional(),
1494
+ ownerName: z24.string().max(80).optional(),
1495
+ state: z24.enum(["open", "taken", "done", "refused", "dropped"]).optional(),
1496
+ failed: z24.literal(true).optional()
1428
1497
  }).strict();
1429
- var PhoneTeamSchema = z23.object({
1430
- id: z23.string().max(40),
1431
- name: z23.string().max(60),
1432
- project: z23.string().max(80).nullable(),
1433
- state: z23.enum(["active", "resting"]),
1434
- seats: z23.array(z23.object({ role: z23.string().max(40), sessionId: z23.string().max(100).nullable() }).strict()).max(16),
1435
- done: z23.number().int().min(0),
1436
- given: z23.number().int().min(0)
1498
+ var PhoneTeamSchema = z24.object({
1499
+ id: z24.string().max(40),
1500
+ name: z24.string().max(60),
1501
+ project: z24.string().max(80).nullable(),
1502
+ state: z24.enum(["active", "resting"]),
1503
+ seats: z24.array(z24.object({ role: z24.string().max(40), sessionId: z24.string().max(100).nullable() }).strict()).max(16),
1504
+ done: z24.number().int().min(0),
1505
+ given: z24.number().int().min(0)
1437
1506
  }).strict();
1438
- var PhoneTaskSchema = z23.object({
1439
- id: z23.string().max(40),
1440
- title: z23.string().max(200),
1441
- owner: z23.string().max(100),
1442
- ownerName: z23.string().max(80),
1443
- ownerRole: z23.string().max(40).optional(),
1444
- team: z23.string().max(40).optional(),
1445
- teamName: z23.string().max(60).optional(),
1446
- state: z23.enum(["open", "taken", "done", "refused", "dropped"]),
1447
- result: z23.string().max(TOOL_TEXT_MAX).optional(),
1448
- cut: z23.literal(true).optional(),
1449
- at: z23.string(),
1450
- updatedAt: z23.string(),
1451
- seen: z23.literal(true).optional()
1507
+ var PhoneTaskSchema = z24.object({
1508
+ id: z24.string().max(40),
1509
+ title: z24.string().max(200),
1510
+ owner: z24.string().max(100),
1511
+ ownerName: z24.string().max(80),
1512
+ ownerRole: z24.string().max(40).optional(),
1513
+ team: z24.string().max(40).optional(),
1514
+ teamName: z24.string().max(60).optional(),
1515
+ state: z24.enum(["open", "taken", "done", "refused", "dropped"]),
1516
+ result: z24.string().max(TOOL_TEXT_MAX).optional(),
1517
+ cut: z24.literal(true).optional(),
1518
+ at: z24.string(),
1519
+ updatedAt: z24.string(),
1520
+ seen: z24.literal(true).optional()
1452
1521
  }).strict();
1453
- var ChatErrorCodeSchema = z23.enum(["busy", "timeout", "cancelled", "failed"]);
1454
- var PhoneChatTurnSchema = z23.object({
1522
+ var ChatErrorCodeSchema = z24.enum(["busy", "timeout", "cancelled", "failed"]);
1523
+ var PhoneChatTurnSchema = z24.object({
1455
1524
  qid: OperationIdSchema,
1456
- question: z23.string().max(MESSAGE_MAX),
1457
- askedAt: z23.string().datetime(),
1458
- steps: z23.array(PhoneChatStepSchema).max(50),
1459
- answer: z23.string().max(THREAD_TEXT_MAX).optional(),
1460
- answeredAt: z23.string().datetime().optional(),
1525
+ question: z24.string().max(MESSAGE_MAX),
1526
+ askedAt: z24.string().datetime(),
1527
+ steps: z24.array(PhoneChatStepSchema).max(50),
1528
+ answer: z24.string().max(THREAD_TEXT_MAX).optional(),
1529
+ answeredAt: z24.string().datetime().optional(),
1461
1530
  error: ChatErrorCodeSchema.optional(),
1462
- proposals: z23.array(PhoneChatProposalSchema).max(MAX_PROPOSALS).optional(),
1531
+ proposals: z24.array(PhoneChatProposalSchema).max(MAX_PROPOSALS).optional(),
1463
1532
  relay: PhoneChatRelaySchema.optional(),
1464
- closes: z23.array(PhoneChatCloseSchema).min(1).max(MAX_PROPOSALS).optional(),
1465
- cut: z23.literal(true).optional(),
1533
+ closes: z24.array(PhoneChatCloseSchema).min(1).max(MAX_PROPOSALS).optional(),
1534
+ cut: z24.literal(true).optional(),
1466
1535
  team: PhoneChatTeamSchema.optional(),
1467
- tasks: z23.array(PhoneChatTaskSchema).min(1).max(MAX_PROPOSALS).optional()
1536
+ tasks: z24.array(PhoneChatTaskSchema).min(1).max(MAX_PROPOSALS).optional(),
1537
+ attachments: z24.array(AttachmentRefSchema).max(ATTACHMENTS_MAX).optional()
1468
1538
  }).strict();
1469
1539
  var CHAT_HISTORY_BUDGET = 393216;
1470
- var sessionId = z23.string().min(1).max(100);
1471
- var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key: z23.string().min(1).max(64) });
1472
- var CardRowsSchema = z23.array(z23.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
1473
- var PhoneClientMessageSchema = z23.discriminatedUnion("type", [
1474
- z23.object({ type: z23.literal("snapshot.request") }),
1475
- z23.object({ type: z23.literal("session.thread"), id: sessionId }),
1540
+ var sessionId = z24.string().min(1).max(100);
1541
+ var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key: z24.string().min(1).max(64) });
1542
+ var CardRowsSchema = z24.array(z24.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
1543
+ var PhoneClientMessageSchema = z24.discriminatedUnion("type", [
1544
+ z24.object({ type: z24.literal("snapshot.request") }),
1545
+ z24.object({ type: z24.literal("session.thread"), id: sessionId }),
1476
1546
  /** The session's request with its whole body (up to `PENDING_BODY_MAX`). */
1477
- z23.object({ type: z23.literal("session.pending"), id: sessionId }),
1547
+ z24.object({ type: z24.literal("session.pending"), id: sessionId }),
1478
1548
  /** The session whose terminal the phone shows; null when it shows none. */
1479
- z23.object({ type: z23.literal("session.watch"), id: sessionId.nullable() }),
1480
- z23.object({ type: z23.literal("session.read"), id: sessionId, at: z23.string().datetime() }),
1549
+ z24.object({ type: z24.literal("session.watch"), id: sessionId.nullable() }),
1550
+ z24.object({ type: z24.literal("session.read"), id: sessionId, at: z24.string().datetime() }),
1481
1551
  /** Asks for a challenge. One object for both purposes: the gateway refuses a `decide` without `id` and `expect`. */
1482
- z23.object({ type: z23.literal("confirm.prepare"), purpose: ConfirmPurposeSchema, id: sessionId.optional(), expect: PhoneExpectSchema.optional(), qid: OperationIdSchema.optional() }),
1483
- z23.object({ type: z23.literal("credential.register"), op: OperationIdSchema, credential: CredentialSchema }),
1484
- z23.object({
1485
- type: z23.literal("session.decide"),
1552
+ z24.object({ type: z24.literal("confirm.prepare"), purpose: ConfirmPurposeSchema, id: sessionId.optional(), expect: PhoneExpectSchema.optional(), qid: OperationIdSchema.optional() }),
1553
+ z24.object({ type: z24.literal("credential.register"), op: OperationIdSchema, credential: CredentialSchema }),
1554
+ z24.object({
1555
+ type: z24.literal("session.decide"),
1486
1556
  op: OperationIdSchema,
1487
1557
  id: sessionId,
1488
1558
  decision: DecisionSchema,
1489
- note: z23.string().trim().min(1).max(NOTE_MAX).optional(),
1559
+ note: z24.string().trim().min(1).max(NOTE_MAX).optional(),
1490
1560
  /** The request the phone showed: refused unless it is still the pending one. */
1491
1561
  expect: PhoneExpectSchema,
1492
1562
  /** The device check over the challenge of `confirm.prepare`; required by the daemon for a high-risk accept. */
1493
1563
  assertion: AssertionSchema.optional()
1494
1564
  }),
1495
- z23.object({
1496
- type: z23.literal("session.reply"),
1565
+ z24.object({
1566
+ type: z24.literal("session.reply"),
1497
1567
  op: OperationIdSchema,
1498
1568
  id: sessionId,
1499
- text: z23.string().min(1).max(MESSAGE_MAX),
1569
+ text: z24.string().max(MESSAGE_MAX),
1570
+ attachments: z24.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(),
1500
1571
  /** The request the reply answers, or null when the phone showed none: refused if the session's request is no longer that one. */
1501
1572
  expect: PhoneExpectSchema.nullable(),
1502
1573
  assertion: AssertionSchema.optional()
1503
1574
  }),
1504
1575
  /** OMP's `ask`: the answers to every question of the dialog `requestId`, in order; `cancel` declines it. Bounds as the local `session.answer`. */
1505
- z23.object({
1506
- type: z23.literal("session.answer"),
1576
+ z24.object({
1577
+ type: z24.literal("session.answer"),
1507
1578
  op: OperationIdSchema,
1508
1579
  id: sessionId,
1509
- requestId: z23.string().min(1).max(200),
1510
- answers: z23.array(z23.object({ id: z23.string().max(200), selected: z23.array(z23.string().max(2e3)).max(100), text: z23.string().max(2e4).optional() })).max(50),
1511
- cancel: z23.literal(true).optional(),
1580
+ requestId: z24.string().min(1).max(200),
1581
+ answers: z24.array(z24.object({ id: z24.string().max(200), selected: z24.array(z24.string().max(2e3)).max(100), text: z24.string().max(2e4).optional() })).max(50),
1582
+ cancel: z24.literal(true).optional(),
1512
1583
  expect: PhoneExpectSchema
1513
1584
  }),
1514
- z23.object({ type: z23.literal("session.interrupt"), op: OperationIdSchema, id: sessionId }),
1585
+ z24.object({ type: z24.literal("session.interrupt"), op: OperationIdSchema, id: sessionId }),
1515
1586
  /** The phone drops its passkey from the daemon. */
1516
- z23.object({ type: z23.literal("device.forget"), op: OperationIdSchema }),
1587
+ z24.object({ type: z24.literal("device.forget"), op: OperationIdSchema }),
1517
1588
  /** The phone's push subscription, with the VAPID key it was made with (so a key change is seen). */
1518
- z23.object({ type: z23.literal("push.subscribe"), op: OperationIdSchema, subscription: PushSubscriptionSchema, key: B64U2.length(87) }),
1519
- z23.object({ type: z23.literal("push.unsubscribe"), op: OperationIdSchema }),
1589
+ z24.object({ type: z24.literal("push.subscribe"), op: OperationIdSchema, subscription: PushSubscriptionSchema, key: B64U2.length(87) }),
1590
+ z24.object({ type: z24.literal("push.unsubscribe"), op: OperationIdSchema }),
1520
1591
  /** The phone's own chat with Ledge. */
1521
- z23.object({ type: z23.literal("chat.ask"), qid: OperationIdSchema, text: z23.string().trim().min(1).max(MESSAGE_MAX) }),
1522
- z23.object({ type: z23.literal("chat.cancel"), qid: OperationIdSchema }),
1523
- z23.object({ type: z23.literal("chat.reset") }),
1524
- z23.object({ type: z23.literal("chat.history") }),
1525
- z23.object({ type: z23.literal("chat.relay"), op: OperationIdSchema, qid: OperationIdSchema }),
1526
- z23.object({ type: z23.literal("chat.launch"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
1527
- z23.object({ type: z23.literal("chat.close"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
1592
+ z24.object({ type: z24.literal("chat.ask"), qid: OperationIdSchema, text: z24.string().trim().max(MESSAGE_MAX), attachments: z24.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional() }),
1593
+ z24.object({ type: z24.literal("chat.cancel"), qid: OperationIdSchema }),
1594
+ z24.object({ type: z24.literal("chat.reset") }),
1595
+ z24.object({ type: z24.literal("chat.history") }),
1596
+ /** An image to store, sent alone before the message that names it; no `id`, and at most `ATTACHMENT_MAX` bytes. */
1597
+ AttachmentPutSchema.pick({ type: true, requestId: true }).extend({ image: ImageUploadSchema.extend({ data: z24.string().min(1).max(b64Max(ATTACHMENT_MAX)) }) }),
1598
+ AttachmentGetSchema.pick({ type: true, requestId: true, attachment: true, preview: true }),
1599
+ z24.object({ type: z24.literal("chat.relay"), op: OperationIdSchema, qid: OperationIdSchema }),
1600
+ z24.object({ type: z24.literal("chat.launch"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
1601
+ z24.object({ type: z24.literal("chat.close"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
1528
1602
  /** The turn's team card (roles spec §3.8): launched like a session card, with the device check (purpose `launch`, the turn). */
1529
- z23.object({ type: z23.literal("chat.team"), op: OperationIdSchema, qid: OperationIdSchema, assertion: AssertionSchema.optional() }),
1603
+ z24.object({ type: z24.literal("chat.team"), op: OperationIdSchema, qid: OperationIdSchema, assertion: AssertionSchema.optional() }),
1530
1604
  /** A row of the turn's task card: given by Ledge. */
1531
- z23.object({ type: z23.literal("chat.task"), op: OperationIdSchema, qid: OperationIdSchema, row: z23.number().int().min(0).max(MAX_PROPOSALS - 1) }),
1605
+ z24.object({ type: z24.literal("chat.task"), op: OperationIdSchema, qid: OperationIdSchema, row: z24.number().int().min(0).max(MAX_PROPOSALS - 1) }),
1532
1606
  /** « Reprendre » a resting team. */
1533
- z23.object({ type: z23.literal("team.resume"), op: OperationIdSchema, team: z23.string().min(1).max(40) }),
1607
+ z24.object({ type: z24.literal("team.resume"), op: OperationIdSchema, team: z24.string().min(1).max(40) }),
1534
1608
  /** Puts a closed task Ledge gave away from « À traiter ». */
1535
- z23.object({ type: z23.literal("task.seen"), op: OperationIdSchema, task: z23.string().min(1).max(40) }),
1609
+ z24.object({ type: z24.literal("task.seen"), op: OperationIdSchema, task: z24.string().min(1).max(40) }),
1536
1610
  /** Whether the phone's app is in front: a device that is visible gets no push. */
1537
- z23.object({ type: z23.literal("presence"), visible: z23.boolean() })
1611
+ z24.object({ type: z24.literal("presence"), visible: z24.boolean() })
1538
1612
  ]);
1539
- var PhoneServerMessageSchema = z23.discriminatedUnion("type", [
1540
- z23.object({ type: z23.literal("snapshot"), protocol: z23.literal(PHONE_PROTOCOL), sessions: z23.array(PhoneSessionSchema), capabilities: CapabilitiesByToolSchema, daemon: z23.object({ version: z23.string(), name: z23.string().max(40), boot: z23.string().uuid().optional() }), device: z23.object({ confirm: z23.boolean(), push: z23.enum(["on", "off", "stale", "unavailable"]).optional(), chat: z23.literal(true).optional(), contextNear: ContextPrefsSchema.shape.near.optional() }).optional() }),
1541
- z23.object({ type: z23.literal("confirm.challenge"), purpose: ConfirmPurposeSchema, challenge: B64U2.max(64), rpId: z23.string().max(253), credentialId: B64U2.max(1366).optional(), expiresInMs: z23.number().int().positive() }),
1542
- z23.object({ type: z23.literal("session.updated"), session: PhoneSessionSchema }),
1543
- z23.object({ type: z23.literal("session.removed"), id: z23.string() }),
1613
+ var PhoneServerMessageSchema = z24.discriminatedUnion("type", [
1614
+ z24.object({ type: z24.literal("snapshot"), protocol: z24.literal(PHONE_PROTOCOL), sessions: z24.array(PhoneSessionSchema), capabilities: CapabilitiesByToolSchema, daemon: z24.object({ version: z24.string(), name: z24.string().max(40), boot: z24.string().uuid().optional() }), device: z24.object({ confirm: z24.boolean(), push: z24.enum(["on", "off", "stale", "unavailable"]).optional(), chat: z24.literal(true).optional(), contextNear: ContextPrefsSchema.shape.near.optional() }).optional() }),
1615
+ z24.object({ type: z24.literal("confirm.challenge"), purpose: ConfirmPurposeSchema, challenge: B64U2.max(64), rpId: z24.string().max(253), credentialId: B64U2.max(1366).optional(), expiresInMs: z24.number().int().positive() }),
1616
+ z24.object({ type: z24.literal("session.updated"), session: PhoneSessionSchema }),
1617
+ z24.object({ type: z24.literal("session.removed"), id: z24.string() }),
1544
1618
  /** `pending.cut` is set when even this body was shortened. */
1545
- z23.object({ type: z23.literal("session.pending"), id: z23.string(), pending: PhonePendingSchema }),
1546
- z23.object({ type: z23.literal("session.preview"), id: z23.string(), lines: z23.array(z23.string()) }),
1619
+ z24.object({ type: z24.literal("session.pending"), id: z24.string(), pending: PhonePendingSchema }),
1620
+ z24.object({ type: z24.literal("session.preview"), id: z24.string(), lines: z24.array(z24.string()) }),
1547
1621
  /** `cut`: the ids of the items shortened; `more`: older items were left out. */
1548
- z23.object({ type: z23.literal("session.thread"), id: z23.string(), items: z23.array(ThreadItemSchema), cut: z23.array(z23.string()), more: z23.boolean() }),
1549
- z23.object({ type: z23.literal("session.thread.item"), id: z23.string(), item: ThreadItemSchema, cut: z23.literal(true).optional() }),
1550
- z23.object({ type: z23.literal("operation.result"), op: OperationIdSchema, status: OperationStatusSchema, reason: OperationReasonSchema.optional() }),
1551
- z23.object({ type: z23.literal("chat.history"), turns: z23.array(PhoneChatTurnSchema).max(50) }),
1552
- z23.object({ type: z23.literal("chat.step"), qid: OperationIdSchema, step: PhoneChatStepSchema }),
1622
+ z24.object({ type: z24.literal("session.thread"), id: z24.string(), items: z24.array(ThreadItemSchema), cut: z24.array(z24.string()), more: z24.boolean() }),
1623
+ z24.object({ type: z24.literal("session.thread.item"), id: z24.string(), item: ThreadItemSchema, cut: z24.literal(true).optional() }),
1624
+ z24.object({ type: z24.literal("operation.result"), op: OperationIdSchema, status: OperationStatusSchema, reason: OperationReasonSchema.optional() }),
1625
+ z24.object({ type: z24.literal("chat.history"), turns: z24.array(PhoneChatTurnSchema).max(50) }),
1626
+ z24.object({ type: z24.literal("chat.step"), qid: OperationIdSchema, step: PhoneChatStepSchema }),
1553
1627
  /** The whole text so far, not an increment. */
1554
- z23.object({ type: z23.literal("chat.delta"), qid: OperationIdSchema, text: z23.string().max(THREAD_TEXT_MAX) }),
1555
- z23.object({ type: z23.literal("chat.done"), qid: OperationIdSchema, turn: PhoneChatTurnSchema }),
1556
- z23.object({ type: z23.literal("chat.error"), qid: OperationIdSchema, code: ChatErrorCodeSchema }),
1628
+ z24.object({ type: z24.literal("chat.delta"), qid: OperationIdSchema, text: z24.string().max(THREAD_TEXT_MAX) }),
1629
+ z24.object({ type: z24.literal("chat.done"), qid: OperationIdSchema, turn: PhoneChatTurnSchema }),
1630
+ z24.object({ type: z24.literal("chat.error"), qid: OperationIdSchema, code: ChatErrorCodeSchema }),
1557
1631
  /** The live teams, after each snapshot and on every change. */
1558
- z23.object({ type: z23.literal("teams"), teams: z23.array(PhoneTeamSchema).max(50) }),
1632
+ z24.object({ type: z24.literal("teams"), teams: z24.array(PhoneTeamSchema).max(50) }),
1559
1633
  /** The tasks Ledge gave, open or closed lately, after each snapshot; then each change. */
1560
- z23.object({ type: z23.literal("tasks"), tasks: z23.array(PhoneTaskSchema).max(200) }),
1561
- z23.object({ type: z23.literal("task.updated"), task: PhoneTaskSchema }),
1634
+ z24.object({ type: z24.literal("tasks"), tasks: z24.array(PhoneTaskSchema).max(200) }),
1635
+ z24.object({ type: z24.literal("task.updated"), task: PhoneTaskSchema }),
1636
+ AttachmentStoredSchema,
1637
+ AttachmentDataSchema,
1638
+ AttachmentErrorSchema,
1562
1639
  /** Never carries free text. */
1563
- z23.object({ type: z23.literal("error"), code: z23.enum(["invalid", "unknown_session", "too_large"]), request: z23.string().max(40).optional() })
1640
+ z24.object({ type: z24.literal("error"), code: z24.enum(["invalid", "unknown_session", "too_large"]), request: z24.string().max(40).optional() })
1564
1641
  ]);
1565
1642
 
1566
1643
  // src/auth.ts
@@ -1683,6 +1760,12 @@ function readDaemonToken() {
1683
1760
  }
1684
1761
 
1685
1762
  export {
1763
+ ATTACHMENT_ID,
1764
+ ATTACHMENT_MAX,
1765
+ ATTACHMENT_LOCAL_MAX,
1766
+ PREVIEW_MAX,
1767
+ ATTACHMENTS_MAX,
1768
+ AttachmentRefSchema,
1686
1769
  CONTEXT_NEAR,
1687
1770
  CONTEXT_NEAR_MIN,
1688
1771
  CONTEXT_NEAR_MAX,
@@ -1711,13 +1794,14 @@ export {
1711
1794
  ModelIdSchema,
1712
1795
  EffortSchema,
1713
1796
  RouteResultSchema,
1714
- DEFAULT_RELAY_URL,
1715
- MobileConfigSchema,
1716
1797
  TASK_TITLE_MAX,
1717
1798
  TASK_TEXT_MAX,
1718
1799
  LEDGE_GIVER,
1800
+ transmissions,
1719
1801
  givenByLedge,
1720
1802
  taskClosed,
1803
+ DEFAULT_RELAY_URL,
1804
+ MobileConfigSchema,
1721
1805
  ROLE_LABEL_MAX,
1722
1806
  ROLE_PROMPT_MAX,
1723
1807
  RoleSchema,