@kubitnodes/stellaris 0.3.21 → 0.3.23

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 (77) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/index.js +1007 -598
  3. package/dist/server/index.js +1252 -671
  4. package/package.json +3 -1
  5. package/web/dist/assets/{BackLink-23Q1JvmZ.js → BackLink-DGYs80bb.js} +1 -1
  6. package/web/dist/assets/Button-f4jgBZY1.js +1 -0
  7. package/web/dist/assets/ChannelForms-q9zdj4p2.js +1 -0
  8. package/web/dist/assets/ChannelView-CGs_aWL3.js +2 -0
  9. package/web/dist/assets/CitizenView-gcIo_sKt.js +2 -0
  10. package/web/dist/assets/CitizensView-DZ7S8vnJ.js +1 -0
  11. package/web/dist/assets/{CliIcon-BOSygXeI.js → CliIcon-CLHaA65A.js} +1 -1
  12. package/web/dist/assets/CronsView-1wns6Uwv.js +1 -0
  13. package/web/dist/assets/KnowledgeView-Cxfi2-Pt.js +1 -0
  14. package/web/dist/assets/Markdown-BhtIFcWc.js +30 -0
  15. package/web/dist/assets/MessageItem-Bk9eBp1U.js +1 -0
  16. package/web/dist/assets/MetricsView-CTEH6rJn.js +1 -0
  17. package/web/dist/assets/NeedsYouView-2VNqH2Sb.js +1 -0
  18. package/web/dist/assets/Overview-CS9HCSmm.js +1 -0
  19. package/web/dist/assets/{Pane-BendOaje.js → Pane-CJYRVkwh.js} +1 -1
  20. package/web/dist/assets/{Picker-BuVnakvi.js → Picker-DoxCN6C7.js} +1 -1
  21. package/web/dist/assets/ProjectView-CJ3r4UdN.js +1 -0
  22. package/web/dist/assets/ProposalView-DLn9dNPp.js +2 -0
  23. package/web/dist/assets/ProposalsView-B-cOxTsQ.js +1 -0
  24. package/web/dist/assets/Quota-Bo7UdWTV.js +1 -0
  25. package/web/dist/assets/RunnersView-DlQruhwp.js +1 -0
  26. package/web/dist/assets/SocietyView-Bl4ErnV8.js +1 -0
  27. package/web/dist/assets/StickyList-MVP34ytV.js +1 -0
  28. package/web/dist/assets/SubjectThread-BFFnHxIF.js +1 -0
  29. package/web/dist/assets/TaskFileView-GZ0eQFUb.js +4 -0
  30. package/web/dist/assets/TaskView-DppFelLW.js +3 -0
  31. package/web/dist/assets/TasksView-DDNXzeG4.js +1 -0
  32. package/web/dist/assets/ThreadForms-C846TvtS.js +1 -0
  33. package/web/dist/assets/ThreadItem-C8cou6wD.js +1 -0
  34. package/web/dist/assets/ThreadView-DvSGDYoD.js +1 -0
  35. package/web/dist/assets/citizen-B2Dlbcpe.js +1 -0
  36. package/web/dist/assets/compose-BmqQmcZH.js +1 -0
  37. package/web/dist/assets/format-DT52SbbR.js +55 -0
  38. package/web/dist/assets/governance-Cr5OlQnw.js +1 -0
  39. package/web/dist/assets/index-BTVy_xFF.js +23 -0
  40. package/web/dist/assets/index-DzV6CCnG.css +2 -0
  41. package/web/dist/assets/mentions-D5hNgtg6.js +3 -0
  42. package/web/dist/assets/{tasks-CyXIxoAy.js → tasks-R3YgpYTe.js} +1 -1
  43. package/web/dist/assets/useMatch-pEF6RJ6Q.js +1 -0
  44. package/web/dist/index.html +9 -10
  45. package/web/dist/assets/ChannelForms-0Cwh59E7.js +0 -1
  46. package/web/dist/assets/ChannelView-CqWV1zza.js +0 -2
  47. package/web/dist/assets/CitizenView-s5OdjXmB.js +0 -2
  48. package/web/dist/assets/CitizensView-r6SZLnVl.js +0 -1
  49. package/web/dist/assets/CronsView-Y-bAE0l2.js +0 -1
  50. package/web/dist/assets/KnowledgeView-CI4PJWwm.js +0 -1
  51. package/web/dist/assets/Markdown-DyckB74r.js +0 -32
  52. package/web/dist/assets/MessageItem-DI05jbrI.js +0 -1
  53. package/web/dist/assets/MetricsView-DKh-lEW5.js +0 -1
  54. package/web/dist/assets/NeedsYouView-C6FfiTsg.js +0 -1
  55. package/web/dist/assets/Overview-D5RvWBg5.js +0 -1
  56. package/web/dist/assets/ProjectView-blBEh70W.js +0 -1
  57. package/web/dist/assets/ProposalView-CMntpKTc.js +0 -2
  58. package/web/dist/assets/ProposalsView-CVUAV2Mm.js +0 -1
  59. package/web/dist/assets/RunnersView-B-wuWVgB.js +0 -1
  60. package/web/dist/assets/SocietyView-CaVbfJWz.js +0 -1
  61. package/web/dist/assets/SubjectThread-Bd_-sDhJ.js +0 -1
  62. package/web/dist/assets/TaskFileView-CO4Y97OT.js +0 -4
  63. package/web/dist/assets/TaskView-DRZoHALo.js +0 -3
  64. package/web/dist/assets/TasksView-CQVQo3zF.js +0 -1
  65. package/web/dist/assets/ThreadForms-CUSeWu0c.js +0 -1
  66. package/web/dist/assets/ThreadItem-BPYYXh3V.js +0 -1
  67. package/web/dist/assets/ThreadView-BcI2i6FI.js +0 -1
  68. package/web/dist/assets/citizen-Eff1h9rA.js +0 -1
  69. package/web/dist/assets/compose-D73Agshx.js +0 -1
  70. package/web/dist/assets/format-fw0G_Vff.js +0 -55
  71. package/web/dist/assets/governance-sRsfOw52.js +0 -1
  72. package/web/dist/assets/index-BkOq4PSU.css +0 -2
  73. package/web/dist/assets/index-CYiok0pT.js +0 -23
  74. package/web/dist/assets/link-Cl7vKbOz.js +0 -1
  75. package/web/dist/assets/mentions-BCdV0bMK.js +0 -1
  76. package/web/dist/assets/useMatch-D-tEHQEw.js +0 -1
  77. package/web/dist/assets/useStickyScroll-Bn4YyEXL.js +0 -1
package/dist/cli/index.js CHANGED
@@ -9,7 +9,7 @@ import { Command } from "commander";
9
9
  import { chmod as chmod2, cp, mkdir as mkdir4, readdir as readdir3, readFile as readFile4, rename as rename3, rm as rm2, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
10
10
  import path6 from "node:path";
11
11
  import { decodeTime, monotonicFactory } from "ulid";
12
- import { z as z13 } from "zod";
12
+ import { z as z16 } from "zod";
13
13
 
14
14
  // packages/shared/dist/ids.js
15
15
  import { z } from "zod";
@@ -64,7 +64,8 @@ var VERB_NAMES = [
64
64
  "create_cron",
65
65
  "update_cron",
66
66
  "remove_cron",
67
- "remove_skill"
67
+ "remove_skill",
68
+ "upload_image"
68
69
  ];
69
70
  var VerbNameSchema = z2.enum(VERB_NAMES);
70
71
  var CharterTriggerSchema = z2.enum(["user_post", "ops_event", "heartbeat"]);
@@ -93,6 +94,7 @@ var USER_ROLE = "user";
93
94
  var USER_NAME = "user";
94
95
  var MEMBER_VERBS = [
95
96
  "post_message",
97
+ "upload_image",
96
98
  "read_inbox",
97
99
  "search",
98
100
  "open_thread",
@@ -169,7 +171,7 @@ var SEED_ROLES = [
169
171
  ];
170
172
 
171
173
  // packages/shared/dist/board.js
172
- import { z as z4 } from "zod";
174
+ import { z as z5 } from "zod";
173
175
 
174
176
  // packages/shared/dist/crons.js
175
177
  import { Cron as Pattern } from "croner";
@@ -288,6 +290,79 @@ function describeSchedule(schedule, societyZone) {
288
290
  return schedule.timezone === void 0 ? `\`${schedule.cron}\` in the society's time zone, ${societyZone}` : `\`${schedule.cron}\` in ${schedule.timezone}`;
289
291
  }
290
292
 
293
+ // packages/shared/dist/quota.js
294
+ import { z as z4 } from "zod";
295
+ var QuotaWindowSchema = z4.object({
296
+ /**
297
+ * The CLI's own name for the window: Claude's `five_hour`, `seven_day`, `seven_day_opus`, and
298
+ * `model:<label>` for a model's weekly window; Codex's `primary` and `secondary`.
299
+ */
300
+ key: z4.string().min(1).max(80),
301
+ /** What a scoped window counts, as the CLI labels it, such as a model's name. */
302
+ label: z4.string().min(1).max(80).optional(),
303
+ /** The window's length in minutes, when known. */
304
+ minutes: z4.number().int().positive().nullable().default(null),
305
+ /** How much of the window is used, in percent, or null when the CLI did not say. */
306
+ usedPercent: z4.number().nonnegative().nullable().default(null),
307
+ /** When the window starts afresh, when known. */
308
+ resetsAt: IsoDateTimeSchema.nullable().default(null),
309
+ /** When the CLI last reported this window. */
310
+ observedAt: IsoDateTimeSchema
311
+ });
312
+ var QuotaStatusSchema = z4.enum(["allowed", "warning", "limited", "none"]);
313
+ var QuotaCreditsSchema = z4.object({
314
+ hasCredits: z4.boolean(),
315
+ unlimited: z4.boolean(),
316
+ balance: z4.string().max(40).nullable()
317
+ });
318
+ var QuotaExtraUsageSchema = z4.object({
319
+ enabled: z4.boolean(),
320
+ monthlyLimit: z4.number().nonnegative().nullable(),
321
+ usedCredits: z4.number().nonnegative().nullable(),
322
+ currency: z4.string().min(1).max(8).nullable()
323
+ });
324
+ var CliQuotaSchema = z4.object({
325
+ /** When the CLI last reported anything here. */
326
+ observedAt: IsoDateTimeSchema,
327
+ /** Whether it came from a turn's stream or from asking the CLI. */
328
+ source: z4.enum(["turn", "read"]),
329
+ status: QuotaStatusSchema,
330
+ /** While `limited`: when work can go on, when known, and the window that stopped it. */
331
+ limitedUntil: IsoDateTimeSchema.nullable().default(null),
332
+ limitedBy: z4.string().min(1).max(80).nullable().default(null),
333
+ /** The plan's name as the CLI gives it, such as `max` or `pro`. */
334
+ plan: z4.string().min(1).max(40).nullable().default(null),
335
+ windows: z4.array(QuotaWindowSchema).max(24).default([]),
336
+ credits: QuotaCreditsSchema.nullable().default(null),
337
+ extraUsage: QuotaExtraUsageSchema.nullable().default(null)
338
+ });
339
+ function mergeQuota(previous, next) {
340
+ if (previous === void 0) {
341
+ return next;
342
+ }
343
+ const windows = new Map(previous.windows.map((window) => [window.key, window]));
344
+ for (const window of next.windows) {
345
+ windows.set(window.key, window);
346
+ }
347
+ return {
348
+ ...next,
349
+ plan: next.plan ?? previous.plan,
350
+ credits: next.credits ?? previous.credits,
351
+ extraUsage: next.extraUsage ?? previous.extraUsage,
352
+ windows: [...windows.values()]
353
+ };
354
+ }
355
+ function readings(quota) {
356
+ const windows = quota.windows.map((window) => `${window.key}=${window.usedPercent === null ? "?" : Math.round(window.usedPercent)}@${window.resetsAt === null ? "?" : Math.round(Date.parse(window.resetsAt) / 6e4)}`).toSorted();
357
+ return windows.join(",");
358
+ }
359
+ function quotaChanged(previous, next) {
360
+ if (previous === void 0) {
361
+ return true;
362
+ }
363
+ return previous.status !== next.status || previous.limitedUntil !== next.limitedUntil || previous.plan !== next.plan || previous.credits?.balance !== next.credits?.balance || previous.extraUsage?.usedCredits !== next.extraUsage?.usedCredits || readings(previous) !== readings(next);
364
+ }
365
+
291
366
  // packages/shared/dist/board.js
292
367
  var ASK_CHANNEL = "asks";
293
368
  var SOCIETY_CHANNELS = ["general", "governance", ASK_CHANNEL];
@@ -300,24 +375,24 @@ var SOCIETY_SCOPE = "society";
300
375
  function wakeScope(member, project2) {
301
376
  return project2 !== null && member.memberships.includes(project2) ? project2 : SOCIETY_SCOPE;
302
377
  }
303
- var CliKindSchema = z4.enum(["claude", "codex"]);
304
- var ArchivedChannelSchema = z4.object({
378
+ var CliKindSchema = z5.enum(["claude", "codex"]);
379
+ var ArchivedChannelSchema = z5.object({
305
380
  name: NameSchema,
306
381
  at: IsoDateTimeSchema,
307
382
  by: NameSchema,
308
- reason: z4.string().min(1)
383
+ reason: z5.string().min(1)
309
384
  });
310
- var SocietySchema = z4.object({
311
- name: z4.string().min(1),
312
- version: z4.literal(1),
385
+ var SocietySchema = z5.object({
386
+ name: z5.string().min(1),
387
+ version: z5.literal(1),
313
388
  createdAt: IsoDateTimeSchema,
314
389
  /** The channels that take posts; an archived one moves to `archivedChannels`. */
315
- channels: z4.array(NameSchema),
316
- archivedChannels: z4.array(ArchivedChannelSchema).default([]),
390
+ channels: z5.array(NameSchema),
391
+ archivedChannels: z5.array(ArchivedChannelSchema).default([]),
317
392
  /** The time zone crons are read in when they name none, and every prompt tells the time in; the user's to set. */
318
393
  timezone: TimeZoneSchema.default(DEFAULT_TIME_ZONE)
319
394
  });
320
- var CompletionEffectSchema = z4.enum(["none", "merge", "ghpr"]);
395
+ var CompletionEffectSchema = z5.enum(["none", "merge", "ghpr"]);
321
396
  var GITHUB_REMOTE = [
322
397
  /^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?\/?$/,
323
398
  /^git@github\.com:([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?$/,
@@ -333,7 +408,7 @@ function githubRepository(remote) {
333
408
  return null;
334
409
  }
335
410
  var PULL_REQUEST_URL = /^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)\/pull\/([1-9]\d*)\/?$/;
336
- var PullRequestUrlSchema = z4.string().trim().regex(PULL_REQUEST_URL, "a pull request's address, https://github.com/<owner>/<repo>/pull/<number>");
411
+ var PullRequestUrlSchema = z5.string().trim().regex(PULL_REQUEST_URL, "a pull request's address, https://github.com/<owner>/<repo>/pull/<number>");
337
412
  function parsePullRequestUrl(url) {
338
413
  const match = PULL_REQUEST_URL.exec(url.trim());
339
414
  return match?.[1] === void 0 || match[2] === void 0 || match[3] === void 0 ? null : { owner: match[1], name: match[2], number: Number(match[3]) };
@@ -341,72 +416,72 @@ function parsePullRequestUrl(url) {
341
416
  function sameRepository(a, b) {
342
417
  return a.owner.toLowerCase() === b.owner.toLowerCase() && a.name.toLowerCase() === b.name.toLowerCase();
343
418
  }
344
- var MergeSubjectSchema = z4.string().trim().min(1).max(300).regex(/^[^\n]+$/, "a merge commit's subject is one line");
345
- var PullRequestLinkSchema = z4.object({
419
+ var MergeSubjectSchema = z5.string().trim().min(1).max(300).regex(/^[^\n]+$/, "a merge commit's subject is one line");
420
+ var PullRequestLinkSchema = z5.object({
346
421
  url: PullRequestUrlSchema,
347
- number: z4.number().int().positive(),
422
+ number: z5.number().int().positive(),
348
423
  subject: MergeSubjectSchema.optional(),
349
- body: z4.string().max(2e4).optional(),
424
+ body: z5.string().max(2e4).optional(),
350
425
  linkedBy: NameSchema,
351
426
  linkedAt: IsoDateTimeSchema,
352
427
  /**
353
428
  * Once its `ghpr` task was abandoned, how the board's closing of it on GitHub went: `ok` false
354
429
  * leaves it open, for the reason `detail` gives.
355
430
  */
356
- closed: z4.object({ at: IsoDateTimeSchema, ok: z4.boolean(), detail: z4.string() }).optional()
431
+ closed: z5.object({ at: IsoDateTimeSchema, ok: z5.boolean(), detail: z5.string() }).optional()
357
432
  });
358
- var DefaultBranchSchema = z4.string().trim().min(1).max(100).regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/, "a branch name is letters, digits, and . _ / -").refine((branch) => !branch.includes("..") && !branch.includes("//") && !branch.endsWith("/") && !branch.endsWith(".") && !branch.endsWith(".lock"), "not a branch name git takes").refine((branch) => !branch.startsWith("task/") && !branch.startsWith("agent/"), "task/ and agent/ hold the board's own branches");
359
- var StageIdSchema = z4.string().regex(/^s[1-9][0-9]*$/, "expected a stage id such as s1");
433
+ var DefaultBranchSchema = z5.string().trim().min(1).max(100).regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/, "a branch name is letters, digits, and . _ / -").refine((branch) => !branch.includes("..") && !branch.includes("//") && !branch.endsWith("/") && !branch.endsWith(".") && !branch.endsWith(".lock"), "not a branch name git takes").refine((branch) => !branch.startsWith("task/") && !branch.startsWith("agent/"), "task/ and agent/ hold the board's own branches");
434
+ var StageIdSchema = z5.string().regex(/^s[1-9][0-9]*$/, "expected a stage id such as s1");
360
435
  var stageFields = {
361
- name: z4.string().trim().min(1).max(120),
436
+ name: z5.string().trim().min(1).max(120),
362
437
  role: NameSchema.optional(),
363
438
  agent: NameSchema.optional(),
364
- gate: z4.boolean().default(false)
439
+ gate: z5.boolean().default(false)
365
440
  };
366
441
  function oneAssignee(stage) {
367
442
  return stage.role === void 0 || stage.agent === void 0;
368
443
  }
369
444
  var ONE_ASSIGNEE = { message: "a stage names a role or an agent, not both" };
370
- var PlanStageSchema = z4.object(stageFields).refine(oneAssignee, ONE_ASSIGNEE);
371
- var PlanEditStageSchema = z4.object({ id: StageIdSchema.optional(), ...stageFields }).refine(oneAssignee, ONE_ASSIGNEE);
372
- var PlanSchema = z4.array(PlanStageSchema);
373
- var PlanEditSchema = z4.array(PlanEditStageSchema);
374
- var ProjectSchema = z4.object({
445
+ var PlanStageSchema = z5.object(stageFields).refine(oneAssignee, ONE_ASSIGNEE);
446
+ var PlanEditStageSchema = z5.object({ id: StageIdSchema.optional(), ...stageFields }).refine(oneAssignee, ONE_ASSIGNEE);
447
+ var PlanSchema = z5.array(PlanStageSchema);
448
+ var PlanEditSchema = z5.array(PlanEditStageSchema);
449
+ var ProjectSchema = z5.object({
375
450
  slug: NameSchema,
376
- name: z4.string().min(1),
377
- repo: z4.string().nullable(),
378
- defaultBranch: z4.string().min(1),
451
+ name: z5.string().min(1),
452
+ repo: z5.string().nullable(),
453
+ defaultBranch: z5.string().min(1),
379
454
  /** The channels that take posts; an archived one moves to `archivedChannels`. */
380
- channels: z4.array(NameSchema),
381
- archivedChannels: z4.array(ArchivedChannelSchema).default([]),
382
- members: z4.array(NameSchema),
383
- approvers: z4.array(NameSchema),
384
- requiredCapabilities: z4.array(z4.string()),
455
+ channels: z5.array(NameSchema),
456
+ archivedChannels: z5.array(ArchivedChannelSchema).default([]),
457
+ members: z5.array(NameSchema),
458
+ approvers: z5.array(NameSchema),
459
+ requiredCapabilities: z5.array(z5.string()),
385
460
  createdAt: IsoDateTimeSchema,
386
461
  onDone: CompletionEffectSchema.default("none"),
387
462
  /** The runner the project lives on, which holds its repository; set on the project's first turn. */
388
463
  runner: NameSchema.optional(),
389
464
  /** Set once the project is archived: nobody belongs to it, and nothing more is posted, filed, or joined there. */
390
- archived: z4.object({ at: IsoDateTimeSchema, by: NameSchema, reason: z4.string().min(1) }).optional()
465
+ archived: z5.object({ at: IsoDateTimeSchema, by: NameSchema, reason: z5.string().min(1) }).optional()
391
466
  });
392
- var TaskStepSchema = z4.object({
393
- action: z4.enum(["advanced", "returned", "abandoned", "landed", "reopened"]),
467
+ var TaskStepSchema = z5.object({
468
+ action: z5.enum(["advanced", "returned", "abandoned", "landed", "reopened"]),
394
469
  stage: StageIdSchema,
395
470
  to: StageIdSchema.nullable()
396
471
  });
397
- var ProposalStepSchema = z4.object({ action: z4.enum(["approved", "rejected"]) });
398
- var MessageStepSchema = z4.union([TaskStepSchema, ProposalStepSchema]);
472
+ var ProposalStepSchema = z5.object({ action: z5.enum(["approved", "rejected"]) });
473
+ var MessageStepSchema = z5.union([TaskStepSchema, ProposalStepSchema]);
399
474
  function isTaskStep(step) {
400
475
  return "stage" in step;
401
476
  }
402
- var KnowledgeNoticeSchema = z4.object({
403
- kind: z4.literal("knowledge"),
404
- action: z4.enum(["written", "updated", "removed"]),
477
+ var KnowledgeNoticeSchema = z5.object({
478
+ kind: z5.literal("knowledge"),
479
+ action: z5.enum(["written", "updated", "removed"]),
405
480
  topic: NameSchema,
406
481
  project: NameSchema.nullable()
407
482
  });
408
- var MessageNoticeSchema = z4.discriminatedUnion("kind", [KnowledgeNoticeSchema]);
409
- var MessageFrontmatterSchema = z4.object({
483
+ var MessageNoticeSchema = z5.discriminatedUnion("kind", [KnowledgeNoticeSchema]);
484
+ var MessageFrontmatterSchema = z5.object({
410
485
  id: UlidSchema,
411
486
  author: NameSchema,
412
487
  channel: ChannelRefSchema,
@@ -420,7 +495,7 @@ var MessageFrontmatterSchema = z4.object({
420
495
  /** On a post a verb made about its work, such as a knowledge topic's note: what it records. */
421
496
  notice: MessageNoticeSchema.optional(),
422
497
  ts: IsoDateTimeSchema,
423
- mentions: z4.array(NameSchema)
498
+ mentions: z5.array(NameSchema)
424
499
  });
425
500
  var MessageRefSchema = MessageFrontmatterSchema.pick({
426
501
  id: true,
@@ -429,16 +504,16 @@ var MessageRefSchema = MessageFrontmatterSchema.pick({
429
504
  thread: true,
430
505
  ts: true
431
506
  });
432
- var TaskStatusSchema = z4.enum(["open", "claimed", "done", "abandoned"]);
433
- var ThreadStateSchema = z4.enum(["open", "closed"]);
434
- var ThreadSubjectSchema = z4.object({
435
- kind: z4.enum(["task", "proposal"]),
507
+ var TaskStatusSchema = z5.enum(["open", "claimed", "done", "abandoned"]);
508
+ var ThreadStateSchema = z5.enum(["open", "closed"]);
509
+ var ThreadSubjectSchema = z5.object({
510
+ kind: z5.enum(["task", "proposal"]),
436
511
  id: UlidSchema
437
512
  });
438
- var ThreadFrontmatterSchema = z4.object({
513
+ var ThreadFrontmatterSchema = z5.object({
439
514
  id: UlidSchema,
440
515
  channel: ChannelRefSchema,
441
- title: z4.string().min(1).max(200),
516
+ title: z5.string().min(1).max(200),
442
517
  subject: ThreadSubjectSchema.optional(),
443
518
  state: ThreadStateSchema,
444
519
  openedBy: NameSchema,
@@ -446,28 +521,28 @@ var ThreadFrontmatterSchema = z4.object({
446
521
  closedBy: NameSchema.optional(),
447
522
  closedAt: IsoDateTimeSchema.optional()
448
523
  });
449
- var StageSchema = z4.object({
524
+ var StageSchema = z5.object({
450
525
  id: StageIdSchema,
451
- name: z4.string().min(1),
526
+ name: z5.string().min(1),
452
527
  role: NameSchema.optional(),
453
528
  agent: NameSchema.optional(),
454
- gate: z4.boolean().default(false),
529
+ gate: z5.boolean().default(false),
455
530
  /** Everyone who has held the stage, oldest first. */
456
- holders: z4.array(NameSchema).default([]),
531
+ holders: z5.array(NameSchema).default([]),
457
532
  completedBy: NameSchema.optional(),
458
533
  completedAt: IsoDateTimeSchema.optional()
459
534
  });
460
- var TaskReturnSchema = z4.object({
535
+ var TaskReturnSchema = z5.object({
461
536
  from: StageIdSchema,
462
537
  by: NameSchema,
463
538
  at: IsoDateTimeSchema
464
539
  });
465
- var TaskFrontmatterSchema = z4.object({
540
+ var TaskFrontmatterSchema = z5.object({
466
541
  id: UlidSchema,
467
542
  project: NameSchema,
468
543
  /** The channel of the project the task is filed in, where its thread hangs. */
469
544
  channel: NameSchema.default("general"),
470
- title: z4.string().min(1),
545
+ title: z5.string().min(1),
471
546
  status: TaskStatusSchema,
472
547
  createdBy: NameSchema,
473
548
  createdAt: IsoDateTimeSchema,
@@ -476,19 +551,19 @@ var TaskFrontmatterSchema = z4.object({
476
551
  claimedBy: NameSchema.optional(),
477
552
  leaseExpiresAt: IsoDateTimeSchema.optional(),
478
553
  parentId: UlidSchema.optional(),
479
- blockedBy: z4.array(UlidSchema),
480
- requiredCapabilities: z4.array(z4.string()),
481
- stages: z4.array(StageSchema).min(1),
554
+ blockedBy: z5.array(UlidSchema),
555
+ requiredCapabilities: z5.array(z5.string()),
556
+ stages: z5.array(StageSchema).min(1),
482
557
  stage: StageIdSchema,
483
558
  /** When the current stage last became current or lost its holder: the waiting clock. */
484
559
  stageSince: IsoDateTimeSchema,
485
560
  /** The highest stage number issued, so a removed stage's id is never reused. */
486
- stageSeq: z4.number().int().positive(),
561
+ stageSeq: z5.number().int().positive(),
487
562
  onDone: CompletionEffectSchema.default("none"),
488
563
  /** The pull request a `ghpr` task lands through. */
489
564
  pullRequest: PullRequestLinkSchema.optional(),
490
565
  /** The last stage is complete and the completion effect is running. */
491
- completing: z4.boolean().default(false),
566
+ completing: z5.boolean().default(false),
492
567
  /** The latest send-back while its rework lasts: cleared once the task reaches that stage again. */
493
568
  returned: TaskReturnSchema.optional()
494
569
  });
@@ -512,79 +587,90 @@ function mayHoldStage(member, task2) {
512
587
  }
513
588
  return !(stage.gate && task2.stages.slice(0, index).some((s) => s.holders.includes(member.name)));
514
589
  }
515
- var ModelNameSchema = z4.string().trim().min(1).max(120).regex(/^[\w.:/[\]-]+$/, "a model name is one word of letters, digits, and . _ : / - [ ]");
516
- var EffortSchema = z4.string().trim().min(1).max(32).regex(/^[a-z][a-z0-9_-]*$/, "an effort is one lowercase word, such as high");
517
- var EffortOptionSchema = z4.object({
590
+ var ModelNameSchema = z5.string().trim().min(1).max(120).regex(/^[\w.:/[\]-]+$/, "a model name is one word of letters, digits, and . _ : / - [ ]");
591
+ var EffortSchema = z5.string().trim().min(1).max(32).regex(/^[a-z][a-z0-9_-]*$/, "an effort is one lowercase word, such as high");
592
+ var EffortOptionSchema = z5.object({
518
593
  id: EffortSchema,
519
- description: z4.string().default("")
594
+ description: z5.string().default("")
520
595
  });
521
- var ModelOptionSchema = z4.object({
596
+ var ModelOptionSchema = z5.object({
522
597
  id: ModelNameSchema,
523
- name: z4.string(),
524
- description: z4.string().default(""),
525
- isDefault: z4.boolean().default(false),
526
- efforts: z4.array(EffortOptionSchema).default([]),
598
+ name: z5.string(),
599
+ description: z5.string().default(""),
600
+ isDefault: z5.boolean().default(false),
601
+ efforts: z5.array(EffortOptionSchema).default([]),
527
602
  defaultEffort: EffortSchema.optional()
528
603
  });
529
- var RunnerModelsSchema = z4.object({
604
+ var RunnerModelsSchema = z5.object({
530
605
  runner: NameSchema,
531
606
  cli: CliKindSchema,
532
- models: z4.array(ModelOptionSchema)
607
+ models: z5.array(ModelOptionSchema)
533
608
  });
534
- var AgentStatusSchema = z4.enum(["active", "retired"]);
535
- var AgentSchema = z4.object({
609
+ var AgentStatusSchema = z5.enum(["active", "retired"]);
610
+ var AgentSchema = z5.object({
536
611
  name: NameSchema,
537
612
  role: NameSchema,
538
613
  cli: CliKindSchema.nullable(),
539
- model: z4.string().optional(),
614
+ model: z5.string().optional(),
540
615
  /** The reasoning effort set for the agent's turns; the model's own default applies otherwise. */
541
- effort: z4.string().optional(),
616
+ effort: z5.string().optional(),
542
617
  /** The runner preferred for the agent's turns where nothing else decides; any runner with its CLI otherwise. */
543
618
  homeRunner: NameSchema.optional(),
544
- memberships: z4.array(NameSchema),
545
- subscriptions: z4.array(ChannelRefSchema),
619
+ memberships: z5.array(NameSchema),
620
+ subscriptions: z5.array(ChannelRefSchema),
546
621
  status: AgentStatusSchema,
547
622
  createdAt: IsoDateTimeSchema,
548
- tokenHash: z4.string().min(1),
623
+ tokenHash: z5.string().min(1),
549
624
  retiredAt: IsoDateTimeSchema.optional(),
550
- retiredReason: z4.string().optional()
625
+ retiredReason: z5.string().optional()
551
626
  });
552
- var MemberSchema = z4.object({
627
+ var MemberSchema = z5.object({
553
628
  name: NameSchema,
554
629
  role: NameSchema,
555
630
  cli: CliKindSchema.nullable(),
556
631
  homeRunner: NameSchema.optional(),
557
632
  status: AgentStatusSchema,
558
- resident: z4.boolean().default(false),
633
+ resident: z5.boolean().default(false),
559
634
  /** The model configured on the record, if any; the CLI's own default applies otherwise. */
560
- model: z4.string().optional(),
635
+ model: z5.string().optional(),
561
636
  /** The model the CLI reported on the citizen's last turn. */
562
- lastModel: z4.string().optional(),
637
+ lastModel: z5.string().optional(),
563
638
  /** The reasoning effort configured on the record, if any; the model's own default applies otherwise. */
564
- effort: z4.string().optional(),
639
+ effort: z5.string().optional(),
565
640
  /** The citizen's own skills, by name. */
566
- skills: z4.array(NameSchema).default([]),
567
- memberships: z4.array(NameSchema),
568
- subscriptions: z4.array(ChannelRefSchema),
569
- claimsHeld: z4.number().int().nonnegative().default(0),
570
- tasksDone: z4.number().int().nonnegative().default(0),
641
+ skills: z5.array(NameSchema).default([]),
642
+ memberships: z5.array(NameSchema),
643
+ subscriptions: z5.array(ChannelRefSchema),
644
+ claimsHeld: z5.number().int().nonnegative().default(0),
645
+ tasksDone: z5.number().int().nonnegative().default(0),
571
646
  lastTurnAt: IsoDateTimeSchema.optional(),
572
- lastTurnOutcome: z4.string().optional(),
647
+ lastTurnOutcome: z5.string().optional(),
573
648
  createdAt: IsoDateTimeSchema,
574
649
  retiredAt: IsoDateTimeSchema.optional()
575
650
  });
576
- var RunnerOsSchema = z4.enum(["linux", "windows", "darwin"]);
577
- var RunnerSchema = z4.object({
651
+ var RunnerOsSchema = z5.enum(["linux", "windows", "darwin"]);
652
+ var GitIdentityPartSchema = z5.enum(["name", "email"]);
653
+ var RunnerSchema = z5.object({
578
654
  name: NameSchema,
579
655
  os: RunnerOsSchema,
580
- clis: z4.array(CliKindSchema),
581
- capabilities: z4.array(z4.string()),
656
+ clis: z5.array(CliKindSchema),
657
+ capabilities: z5.array(z5.string()),
582
658
  /** The release the runner reported when it last registered; absent until it has. */
583
- version: z4.string().optional(),
584
- status: z4.enum(["connected", "disconnected"]),
585
- lastSeen: IsoDateTimeSchema.optional()
659
+ version: z5.string().optional(),
660
+ /**
661
+ * What the runner's account lacked of a git identity when it last registered, empty when it had
662
+ * both; absent while the runner has not said, as one from before runners checked never does.
663
+ */
664
+ gitIdentityMissing: z5.array(GitIdentityPartSchema).optional(),
665
+ status: z5.enum(["connected", "disconnected"]),
666
+ lastSeen: IsoDateTimeSchema.optional(),
667
+ /**
668
+ * Each CLI's plan quota as its login on this runner last reported it, from a turn's stream or a
669
+ * read; absent until one has.
670
+ */
671
+ quotas: z5.partialRecord(CliKindSchema, CliQuotaSchema).optional()
586
672
  });
587
- var ProposalKindSchema = z4.enum([
673
+ var ProposalKindSchema = z5.enum([
588
674
  "role",
589
675
  "member",
590
676
  "channel",
@@ -593,42 +679,42 @@ var ProposalKindSchema = z4.enum([
593
679
  "skill",
594
680
  "archive"
595
681
  ]);
596
- var ProposalStatusSchema = z4.enum([
682
+ var ProposalStatusSchema = z5.enum([
597
683
  "proposed",
598
684
  "approved",
599
685
  "rejected",
600
686
  "provisioned",
601
687
  "retired"
602
688
  ]);
603
- var MemberProposalSchema = z4.object({
689
+ var MemberProposalSchema = z5.object({
604
690
  name: NameSchema,
605
691
  role: NameSchema,
606
692
  cli: CliKindSchema,
607
- model: z4.string().optional(),
693
+ model: z5.string().optional(),
608
694
  homeRunner: NameSchema.optional(),
609
- memberships: z4.array(NameSchema).default([]),
610
- subscriptions: z4.array(ChannelRefSchema).default([]),
611
- seedInstructions: z4.string().optional()
695
+ memberships: z5.array(NameSchema).default([]),
696
+ subscriptions: z5.array(ChannelRefSchema).default([]),
697
+ seedInstructions: z5.string().optional()
612
698
  });
613
- var ChannelProposalSchema = z4.object({
699
+ var ChannelProposalSchema = z5.object({
614
700
  project: NameSchema.nullable().default(null),
615
701
  name: NameSchema,
616
- purpose: z4.string().min(1)
702
+ purpose: z5.string().min(1)
617
703
  });
618
- var RoleChangesSchema = z4.strictObject({
619
- maxReplicas: z4.number().int().min(1).optional(),
620
- backlogThreshold: z4.number().positive().optional(),
621
- wakeTriggers: z4.array(CharterTriggerSchema).optional(),
622
- verbs: z4.array(VerbNameSchema).optional()
704
+ var RoleChangesSchema = z5.strictObject({
705
+ maxReplicas: z5.number().int().min(1).optional(),
706
+ backlogThreshold: z5.number().positive().optional(),
707
+ wakeTriggers: z5.array(CharterTriggerSchema).optional(),
708
+ verbs: z5.array(VerbNameSchema).optional()
623
709
  }).refine((changes) => Object.values(changes).some((value) => value !== void 0), {
624
710
  message: "a reallocation changes at least one of maxReplicas, backlogThreshold, wakeTriggers, verbs"
625
711
  });
626
- var ReallocationProposalSchema = z4.object({
712
+ var ReallocationProposalSchema = z5.object({
627
713
  role: NameSchema,
628
714
  changes: RoleChangesSchema,
629
- description: z4.string().min(1)
715
+ description: z5.string().min(1)
630
716
  });
631
- var LegacyReallocationSchema = z4.object({ description: z4.string().min(1) });
717
+ var LegacyReallocationSchema = z5.object({ description: z5.string().min(1) });
632
718
  function applyRoleChanges(role2, changes) {
633
719
  return {
634
720
  ...role2,
@@ -663,18 +749,18 @@ function roleChangeLines(role2, changes) {
663
749
  }
664
750
  return lines;
665
751
  }
666
- var RetirementProposalSchema = z4.object({
752
+ var RetirementProposalSchema = z5.object({
667
753
  agent: NameSchema,
668
- reason: z4.string().min(1)
754
+ reason: z5.string().min(1)
669
755
  });
670
- var ArchiveProposalSchema = z4.object({
756
+ var ArchiveProposalSchema = z5.object({
671
757
  project: NameSchema,
672
- reason: z4.string().min(1)
758
+ reason: z5.string().min(1)
673
759
  });
674
- var SkillProposalSchema = z4.object({
760
+ var SkillProposalSchema = z5.object({
675
761
  name: NameSchema,
676
- summary: z4.string().min(1).max(200),
677
- body: z4.string().min(1).optional()
762
+ summary: z5.string().min(1).max(200),
763
+ body: z5.string().min(1).optional()
678
764
  });
679
765
  var ProposalCharterSchemas = {
680
766
  role: RoleCharterSchema,
@@ -777,23 +863,23 @@ function describeCharter(kind, charter) {
777
863
  }
778
864
  return `${kind} ${JSON.stringify(charter)}`;
779
865
  }
780
- var SkillSchema = z4.object({
866
+ var SkillSchema = z5.object({
781
867
  name: NameSchema,
782
- summary: z4.string().default(""),
783
- scope: z4.enum(["own", "society"]),
784
- path: z4.string().min(1)
868
+ summary: z5.string().default(""),
869
+ scope: z5.enum(["own", "society"]),
870
+ path: z5.string().min(1)
785
871
  });
786
- var RemovedSkillSchema = z4.object({
872
+ var RemovedSkillSchema = z5.object({
787
873
  name: NameSchema,
788
- reason: z4.string().min(1),
874
+ reason: z5.string().min(1),
789
875
  removedBy: NameSchema,
790
876
  removedAt: IsoDateTimeSchema
791
877
  });
792
- var HomeConflictSchema = z4.object({
878
+ var HomeConflictSchema = z5.object({
793
879
  /** The copy, relative to the home. */
794
- path: z4.string().min(1),
880
+ path: z5.string().min(1),
795
881
  /** The file beside it, whose version the board kept. */
796
- file: z4.string().min(1),
882
+ file: z5.string().min(1),
797
883
  /** When the copy reached the board. */
798
884
  since: IsoDateTimeSchema
799
885
  });
@@ -801,79 +887,79 @@ var CONFLICT_COPY = /^(.+)\.conflict-[^/.]+$/;
801
887
  function conflictCopyOf(copy) {
802
888
  return CONFLICT_COPY.exec(copy)?.[1] ?? null;
803
889
  }
804
- var CommitIdSchema = z4.string().regex(/^[0-9a-f]{40}([0-9a-f]{24})?$/);
805
- var HomeFileChangeSchema = z4.object({
806
- path: z4.string().min(1),
807
- status: z4.enum(["added", "modified", "deleted"]),
890
+ var CommitIdSchema = z5.string().regex(/^[0-9a-f]{40}([0-9a-f]{24})?$/);
891
+ var HomeFileChangeSchema = z5.object({
892
+ path: z5.string().min(1),
893
+ status: z5.enum(["added", "modified", "deleted"]),
808
894
  /** Lines added and removed, or null for a binary file. */
809
- added: z4.number().int().nonnegative().nullable(),
810
- removed: z4.number().int().nonnegative().nullable()
895
+ added: z5.number().int().nonnegative().nullable(),
896
+ removed: z5.number().int().nonnegative().nullable()
811
897
  });
812
- var HomeChangeSchema = z4.object({
898
+ var HomeChangeSchema = z5.object({
813
899
  commit: CommitIdSchema,
814
900
  at: IsoDateTimeSchema,
815
- kind: z4.enum(["turn", "board", "merge"]),
816
- author: z4.string(),
901
+ kind: z5.enum(["turn", "board", "merge"]),
902
+ author: z5.string(),
817
903
  /** The turn that made it, from the runner's commit message. */
818
904
  turnId: UlidSchema.optional(),
819
- subject: z4.string(),
820
- files: z4.array(HomeFileChangeSchema)
905
+ subject: z5.string(),
906
+ files: z5.array(HomeFileChangeSchema)
821
907
  });
822
- var HomeHistorySchema = z4.object({
823
- changes: z4.array(HomeChangeSchema),
908
+ var HomeHistorySchema = z5.object({
909
+ changes: z5.array(HomeChangeSchema),
824
910
  /** Whether older commits remain past the ones read. */
825
- more: z4.boolean()
911
+ more: z5.boolean()
826
912
  });
827
913
  var HomeFileDiffSchema = HomeFileChangeSchema.pick({ path: true, status: true }).extend({
828
- patch: z4.string().nullable(),
914
+ patch: z5.string().nullable(),
829
915
  /** The patch was cut at its size limit. */
830
- truncated: z4.boolean()
916
+ truncated: z5.boolean()
831
917
  });
832
- var KnowledgeSchema = z4.object({
918
+ var KnowledgeSchema = z5.object({
833
919
  topic: NameSchema,
834
920
  project: NameSchema.nullable(),
835
- revision: z4.number().int().min(0),
921
+ revision: z5.number().int().min(0),
836
922
  updatedBy: NameSchema,
837
923
  updatedAt: IsoDateTimeSchema
838
924
  });
839
- var RemovedKnowledgeSchema = z4.object({
925
+ var RemovedKnowledgeSchema = z5.object({
840
926
  topic: NameSchema,
841
927
  project: NameSchema.nullable(),
842
928
  removedBy: NameSchema,
843
929
  removedAt: IsoDateTimeSchema
844
930
  });
845
- var DashboardSchema = z4.object({
931
+ var DashboardSchema = z5.object({
846
932
  project: NameSchema,
847
- revision: z4.number().int().min(0),
933
+ revision: z5.number().int().min(0),
848
934
  /** Absent until someone updates the dashboard the project began with. */
849
935
  updatedBy: NameSchema.optional(),
850
936
  updatedAt: IsoDateTimeSchema.optional(),
851
- body: z4.string()
937
+ body: z5.string()
852
938
  });
853
- var ProposalOriginSchema = z4.object({
939
+ var ProposalOriginSchema = z5.object({
854
940
  scope: NameSchema,
855
941
  thread: UlidSchema.optional(),
856
942
  channel: NameSchema.optional()
857
943
  });
858
944
  var SKILL_FILES_LIMIT = 100;
859
945
  var SKILL_BYTES_LIMIT = 10 * 1024 * 1024;
860
- var SkillFileSchema = z4.object({
861
- path: z4.string().min(1),
862
- size: z4.number().int().nonnegative(),
863
- executable: z4.boolean()
946
+ var SkillFileSchema = z5.object({
947
+ path: z5.string().min(1),
948
+ size: z5.number().int().nonnegative(),
949
+ executable: z5.boolean()
864
950
  });
865
- var SkillFilesSchema = z4.object({
951
+ var SkillFilesSchema = z5.object({
866
952
  /** The commit of the proposer's home the files came from. */
867
953
  commit: CommitIdSchema,
868
954
  takenAt: IsoDateTimeSchema,
869
- files: z4.array(SkillFileSchema)
955
+ files: z5.array(SkillFileSchema)
870
956
  });
871
957
  var SkillFileContentSchema = SkillFileSchema.extend({
872
- text: z4.string().nullable(),
958
+ text: z5.string().nullable(),
873
959
  /** The text was cut at its size limit. */
874
- truncated: z4.boolean()
960
+ truncated: z5.boolean()
875
961
  });
876
- var ProposalFrontmatterSchema = z4.object({
962
+ var ProposalFrontmatterSchema = z5.object({
877
963
  id: UlidSchema,
878
964
  kind: ProposalKindSchema,
879
965
  proposedBy: NameSchema,
@@ -885,146 +971,185 @@ var ProposalFrontmatterSchema = z4.object({
885
971
  skillFiles: SkillFilesSchema.optional(),
886
972
  decidedBy: NameSchema.optional(),
887
973
  decidedAt: IsoDateTimeSchema.optional(),
888
- reason: z4.string().optional(),
889
- charter: z4.record(z4.string(), z4.unknown()),
974
+ reason: z5.string().optional(),
975
+ charter: z5.record(z5.string(), z5.unknown()),
890
976
  /** What approval created, for kinds the board provisions: the agent, channel, role, or retirement. */
891
977
  provisionedAt: IsoDateTimeSchema.optional(),
892
- provision: z4.record(z4.string(), z4.unknown()).optional()
978
+ provision: z5.record(z5.string(), z5.unknown()).optional()
893
979
  });
894
- var DecisionSchema = z4.object({
980
+ var DecisionSchema = z5.object({
895
981
  id: UlidSchema,
896
982
  proposalId: UlidSchema,
897
983
  decidedBy: NameSchema,
898
- outcome: z4.enum(["approved", "rejected"]),
899
- reason: z4.string().optional(),
984
+ outcome: z5.enum(["approved", "rejected"]),
985
+ reason: z5.string().optional(),
900
986
  ts: IsoDateTimeSchema
901
987
  });
902
988
 
903
989
  // packages/shared/dist/verbs.js
904
- import { z as z5 } from "zod";
905
- var MomentSchema = z5.iso.datetime({ offset: true });
906
- var CronTitleSchema = z5.string().trim().min(1).max(120);
907
- var CronNoteSchema = z5.string().trim().min(1).max(4e3);
990
+ import { z as z7 } from "zod";
991
+
992
+ // packages/shared/dist/images.js
993
+ import { z as z6 } from "zod";
994
+ var IMAGE_LIMIT_BYTES = 8 * 1024 * 1024;
995
+ var ImageTypeSchema = z6.enum(["png", "jpeg", "gif", "webp"]);
996
+ function startsWith(bytes, prefix, at = 0) {
997
+ for (let index = 0; index < prefix.length; index += 1) {
998
+ if (bytes[at + index] !== prefix[index]) {
999
+ return false;
1000
+ }
1001
+ }
1002
+ return true;
1003
+ }
1004
+ var ascii = (text) => new TextEncoder().encode(text);
1005
+ function imageTypeOf(bytes) {
1006
+ if (startsWith(bytes, [137, 80, 78, 71, 13, 10, 26, 10])) {
1007
+ return "png";
1008
+ }
1009
+ if (startsWith(bytes, [255, 216, 255])) {
1010
+ return "jpeg";
1011
+ }
1012
+ if (startsWith(bytes, ascii("GIF87a")) || startsWith(bytes, ascii("GIF89a"))) {
1013
+ return "gif";
1014
+ }
1015
+ if (startsWith(bytes, ascii("RIFF")) && startsWith(bytes, ascii("WEBP"), 8)) {
1016
+ return "webp";
1017
+ }
1018
+ return null;
1019
+ }
1020
+ var ImagePathSchema = z6.string().min(1).max(4096).refine((value) => !value.includes("\0"), { message: "a path with no NUL character" });
1021
+ var IMAGE_SCHEME = "image:";
1022
+ var UploadedImageSchema = z6.object({
1023
+ id: UlidSchema,
1024
+ type: ImageTypeSchema,
1025
+ size: z6.number().int().positive(),
1026
+ markdown: z6.string()
1027
+ });
1028
+
1029
+ // packages/shared/dist/verbs.js
1030
+ var MomentSchema = z7.iso.datetime({ offset: true });
1031
+ var CronTitleSchema = z7.string().trim().min(1).max(120);
1032
+ var CronNoteSchema = z7.string().trim().min(1).max(4e3);
908
1033
  var VerbInputs = {
909
- post_message: z5.object({
1034
+ post_message: z7.object({
910
1035
  /** Required outside a thread; in one it may be left out, and must otherwise be the thread's. */
911
1036
  channel: ChannelRefSchema.optional(),
912
- body: z5.string().min(1),
1037
+ body: z7.string().min(1),
913
1038
  thread_id: UlidSchema.optional()
914
1039
  }),
915
- read_inbox: z5.object({
1040
+ read_inbox: z7.object({
916
1041
  since_cursor: UlidSchema.nullable().optional(),
917
- limit: z5.number().int().positive().max(200).default(50),
918
- advance: z5.boolean().default(true)
1042
+ limit: z7.number().int().positive().max(200).default(50),
1043
+ advance: z7.boolean().default(true)
919
1044
  }),
920
- search: z5.object({
921
- query: z5.string().min(1),
1045
+ search: z7.object({
1046
+ query: z7.string().min(1),
922
1047
  project: NameSchema.optional(),
923
1048
  channel: ChannelRefSchema.optional(),
924
- limit: z5.number().int().positive().max(100).default(20)
1049
+ limit: z7.number().int().positive().max(100).default(20)
925
1050
  }),
926
1051
  /** On a task or a proposal, whose id the thread takes, or on a channel with a title. */
927
- open_thread: z5.object({
1052
+ open_thread: z7.object({
928
1053
  task_id: UlidSchema.optional(),
929
1054
  proposal_id: UlidSchema.optional(),
930
1055
  channel: ChannelRefSchema.optional(),
931
- title: z5.string().min(1).max(200).optional()
1056
+ title: z7.string().min(1).max(200).optional()
932
1057
  }),
933
- close_thread: z5.object({ thread_id: UlidSchema, summary: z5.string().min(1) }),
934
- create_task: z5.object({
1058
+ close_thread: z7.object({ thread_id: UlidSchema, summary: z7.string().min(1) }),
1059
+ create_task: z7.object({
935
1060
  project: NameSchema,
936
- title: z5.string().min(1).max(200),
937
- body: z5.string().default(""),
1061
+ title: z7.string().min(1).max(200),
1062
+ body: z7.string().default(""),
938
1063
  parent_id: UlidSchema.optional(),
939
- required_capabilities: z5.array(z5.string()).default([]),
1064
+ required_capabilities: z7.array(z7.string()).default([]),
940
1065
  /** The plan; without it the task gets one stage, work, that anyone in the project may take. */
941
- stages: z5.array(PlanStageSchema).min(1).optional(),
1066
+ stages: z7.array(PlanStageSchema).min(1).optional(),
942
1067
  /** A channel of the project the task belongs to, such as a release's; its thread opens there. */
943
1068
  channel: NameSchema.default("general")
944
1069
  }),
945
- claim_task: z5.object({ task_id: UlidSchema }),
946
- release_task: z5.object({ task_id: UlidSchema }),
947
- update_task: z5.object({
1070
+ claim_task: z7.object({ task_id: UlidSchema }),
1071
+ release_task: z7.object({ task_id: UlidSchema }),
1072
+ update_task: z7.object({
948
1073
  task_id: UlidSchema,
949
1074
  /** Move the task back to this earlier stage. */
950
1075
  stage: StageIdSchema.optional(),
951
- status: z5.enum(["abandoned"]).optional(),
1076
+ status: z7.enum(["abandoned"]).optional(),
952
1077
  /** Posted to the task's thread as the caller, marked with the step when the task moved. */
953
- note: z5.string().min(1).optional(),
954
- blocked_by: z5.array(UlidSchema).optional(),
1078
+ note: z7.string().min(1).optional(),
1079
+ blocked_by: z7.array(UlidSchema).optional(),
955
1080
  /**
956
1081
  * The pull request a `ghpr` task lands through, on its project's repository, with the merge
957
1082
  * commit's subject and body; null unlinks it.
958
1083
  */
959
- pull_request: z5.object({
1084
+ pull_request: z7.object({
960
1085
  url: PullRequestUrlSchema,
961
1086
  merge_subject: MergeSubjectSchema.optional(),
962
- merge_body: z5.string().max(2e4).optional()
1087
+ merge_body: z7.string().max(2e4).optional()
963
1088
  }).nullable().optional()
964
1089
  }),
965
- get_task: z5.object({ task_id: UlidSchema }),
966
- subscribe: z5.object({ channel: ChannelRefSchema }),
967
- unsubscribe: z5.object({ channel: ChannelRefSchema }),
968
- propose: z5.object({
1090
+ get_task: z7.object({ task_id: UlidSchema }),
1091
+ subscribe: z7.object({ channel: ChannelRefSchema }),
1092
+ unsubscribe: z7.object({ channel: ChannelRefSchema }),
1093
+ propose: z7.object({
969
1094
  kind: ProposalKindSchema,
970
- charter: z5.record(z5.string(), z5.unknown()),
971
- rationale: z5.string().default("")
1095
+ charter: z7.record(z7.string(), z7.unknown()),
1096
+ rationale: z7.string().default("")
972
1097
  }),
973
- approve: z5.object({ proposal_id: UlidSchema, reason: z5.string().optional() }),
974
- reject: z5.object({ proposal_id: UlidSchema, reason: z5.string().min(1) }),
975
- create_project: z5.object({
1098
+ approve: z7.object({ proposal_id: UlidSchema, reason: z7.string().optional() }),
1099
+ reject: z7.object({ proposal_id: UlidSchema, reason: z7.string().min(1) }),
1100
+ create_project: z7.object({
976
1101
  slug: NameSchema,
977
- name: z5.string().min(1).optional(),
978
- repo: z5.string().min(1).nullable().default(null),
1102
+ name: z7.string().min(1).optional(),
1103
+ repo: z7.string().min(1).nullable().default(null),
979
1104
  default_branch: DefaultBranchSchema.default("main"),
980
1105
  on_done: CompletionEffectSchema.optional()
981
1106
  }),
982
- join_project: z5.object({ project: NameSchema, agent: NameSchema.optional() }),
983
- leave_project: z5.object({ project: NameSchema, agent: NameSchema.optional() }),
984
- write_knowledge: z5.object({
1107
+ join_project: z7.object({ project: NameSchema, agent: NameSchema.optional() }),
1108
+ leave_project: z7.object({ project: NameSchema, agent: NameSchema.optional() }),
1109
+ write_knowledge: z7.object({
985
1110
  /** A project you belong to, or null for society knowledge, which the steward and the user curate. */
986
1111
  project: NameSchema.nullable().default(null),
987
1112
  topic: NameSchema,
988
- body: z5.string().min(1),
1113
+ body: z7.string().min(1),
989
1114
  /** The `revision` in the frontmatter of the topic you read, 0 when it names none or for a new topic. */
990
- revision: z5.number().int().min(0)
1115
+ revision: z7.number().int().min(0)
991
1116
  }),
992
- remove_knowledge: z5.object({
1117
+ remove_knowledge: z7.object({
993
1118
  /** As for write_knowledge: a project you belong to, or null for society knowledge. */
994
1119
  project: NameSchema.nullable().default(null),
995
1120
  topic: NameSchema
996
1121
  }),
997
- plan_task: z5.object({
1122
+ plan_task: z7.object({
998
1123
  task_id: UlidSchema,
999
- stages: z5.array(PlanEditStageSchema),
1124
+ stages: z7.array(PlanEditStageSchema),
1000
1125
  on_done: CompletionEffectSchema.optional()
1001
1126
  }),
1002
1127
  /** The note is posted to the task's thread as the caller, marked with the step. */
1003
- advance_task: z5.object({ task_id: UlidSchema, note: z5.string().min(1).optional() }),
1004
- configure_project: z5.object({
1128
+ advance_task: z7.object({ task_id: UlidSchema, note: z7.string().min(1).optional() }),
1129
+ configure_project: z7.object({
1005
1130
  project: NameSchema,
1006
1131
  on_done: CompletionEffectSchema.optional(),
1007
1132
  /** A new display name; the slug, which everything refers to the project by, never changes. */
1008
- name: z5.string().trim().min(1).max(120).optional(),
1133
+ name: z7.string().trim().min(1).max(120).optional(),
1009
1134
  /** The branch tasks start from and land on from now; tasks in play keep their branches. */
1010
1135
  default_branch: DefaultBranchSchema.optional()
1011
1136
  }),
1012
- archive_project: z5.object({ project: NameSchema, reason: z5.string().min(1) }),
1013
- create_channel: z5.object({
1137
+ archive_project: z7.object({ project: NameSchema, reason: z7.string().min(1) }),
1138
+ create_channel: z7.object({
1014
1139
  /** A project, or null for a channel of the society. */
1015
1140
  project: NameSchema.nullable().default(null),
1016
1141
  name: NameSchema,
1017
- purpose: z5.string().min(1)
1142
+ purpose: z7.string().min(1)
1018
1143
  }),
1019
- archive_channel: z5.object({ channel: ChannelRefSchema, reason: z5.string().min(1) }),
1020
- update_dashboard: z5.object({
1144
+ archive_channel: z7.object({ channel: ChannelRefSchema, reason: z7.string().min(1) }),
1145
+ update_dashboard: z7.object({
1021
1146
  project: NameSchema,
1022
1147
  /** The whole dashboard as markdown; the board keeps its frontmatter. */
1023
- body: z5.string().min(1),
1148
+ body: z7.string().min(1),
1024
1149
  /** The `revision` in the frontmatter of the dashboard you read, 0 when it names none. */
1025
- revision: z5.number().int().min(0)
1150
+ revision: z7.number().int().min(0)
1026
1151
  }),
1027
- create_cron: z5.object({
1152
+ create_cron: z7.object({
1028
1153
  title: CronTitleSchema,
1029
1154
  /** What to do when it fires; quoted in the prompt of every turn it starts. */
1030
1155
  note: CronNoteSchema,
@@ -1043,10 +1168,10 @@ var VerbInputs = {
1043
1168
  /** An open channel of that place other than general, whose conversation it fires in. */
1044
1169
  channel: NameSchema.optional()
1045
1170
  }),
1046
- update_cron: z5.object({
1171
+ update_cron: z7.object({
1047
1172
  cron_id: UlidSchema,
1048
1173
  /** Pause it, or resume it, which starts its schedule afresh from now. */
1049
- paused: z5.boolean().optional(),
1174
+ paused: z7.boolean().optional(),
1050
1175
  title: CronTitleSchema.optional(),
1051
1176
  note: CronNoteSchema.optional(),
1052
1177
  /** A new expression, or `at` for one time; either starts the schedule afresh. */
@@ -1055,12 +1180,16 @@ var VerbInputs = {
1055
1180
  timezone: TimeZoneSchema.nullable().optional(),
1056
1181
  at: MomentSchema.optional()
1057
1182
  }),
1058
- remove_cron: z5.object({ cron_id: UlidSchema, reason: z5.string().trim().min(1) }),
1059
- remove_skill: z5.object({ name: NameSchema, reason: z5.string().trim().min(1) })
1183
+ remove_cron: z7.object({ cron_id: UlidSchema, reason: z7.string().trim().min(1) }),
1184
+ remove_skill: z7.object({ name: NameSchema, reason: z7.string().trim().min(1) }),
1185
+ upload_image: z7.object({
1186
+ /** The image file on the machine your turn runs on: absolute, or relative to your working directory. */
1187
+ path: ImagePathSchema
1188
+ })
1060
1189
  };
1061
1190
 
1062
1191
  // packages/shared/dist/events.js
1063
- import { z as z6 } from "zod";
1192
+ import { z as z8 } from "zod";
1064
1193
  var BOARD_EVENT_TYPES = [
1065
1194
  "society.initialized",
1066
1195
  "project.added",
@@ -1105,6 +1234,7 @@ var BOARD_EVENT_TYPES = [
1105
1234
  "user.signed_out",
1106
1235
  "user.token_rotated",
1107
1236
  "runner.changed",
1237
+ "runner.quota",
1108
1238
  "ops.signal",
1109
1239
  "paused.changed",
1110
1240
  "wake.requested",
@@ -1123,30 +1253,39 @@ var BOARD_EVENT_TYPES = [
1123
1253
  "cron.updated",
1124
1254
  "cron.fired",
1125
1255
  "cron.skipped",
1126
- "cron.ended"
1256
+ "cron.ended",
1257
+ "image.uploaded"
1127
1258
  ];
1128
- var BoardEventTypeSchema = z6.enum(BOARD_EVENT_TYPES);
1129
- var BoardEventSchema = z6.object({
1259
+ var BoardEventTypeSchema = z8.enum(BOARD_EVENT_TYPES);
1260
+ var BoardEventSchema = z8.object({
1130
1261
  id: UlidSchema,
1131
1262
  ts: IsoDateTimeSchema,
1132
1263
  type: BoardEventTypeSchema,
1133
1264
  actor: NameSchema,
1134
- payload: z6.record(z6.string(), z6.unknown())
1135
- });
1136
- var UsageSchema = z6.object({
1137
- inputTokens: z6.number().int().nonnegative(),
1138
- outputTokens: z6.number().int().nonnegative(),
1139
- cacheReadTokens: z6.number().int().nonnegative().default(0),
1140
- cacheWriteTokens: z6.number().int().nonnegative().default(0)
1141
- });
1142
- var TurnStatusSchema = z6.object({
1143
- summary: z6.string().min(1),
1144
- claimsHeld: z6.array(UlidSchema).default([]),
1145
- blockedOn: z6.array(z6.string()).default([]),
1146
- needsUserDecision: z6.boolean().default(false),
1147
- memoryUpdated: z6.boolean().default(false)
1148
- });
1149
- var TurnExitReasonSchema = z6.enum([
1265
+ payload: z8.record(z8.string(), z8.unknown())
1266
+ });
1267
+ var UsageSchema = z8.object({
1268
+ inputTokens: z8.number().int().nonnegative(),
1269
+ outputTokens: z8.number().int().nonnegative(),
1270
+ cacheReadTokens: z8.number().int().nonnegative().default(0),
1271
+ cacheWriteTokens: z8.number().int().nonnegative().default(0)
1272
+ });
1273
+ function addUsage(a, b) {
1274
+ return {
1275
+ inputTokens: a.inputTokens + b.inputTokens,
1276
+ outputTokens: a.outputTokens + b.outputTokens,
1277
+ cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,
1278
+ cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens
1279
+ };
1280
+ }
1281
+ var TurnStatusSchema = z8.object({
1282
+ summary: z8.string().min(1),
1283
+ claimsHeld: z8.array(UlidSchema).default([]),
1284
+ blockedOn: z8.array(z8.string()).default([]),
1285
+ needsUserDecision: z8.boolean().default(false),
1286
+ memoryUpdated: z8.boolean().default(false)
1287
+ });
1288
+ var TurnExitReasonSchema = z8.enum([
1150
1289
  "completed",
1151
1290
  "timeout",
1152
1291
  "error",
@@ -1154,50 +1293,50 @@ var TurnExitReasonSchema = z6.enum([
1154
1293
  "blocked",
1155
1294
  "stopped"
1156
1295
  ]);
1157
- var AgentEventSchema = z6.discriminatedUnion("type", [
1158
- z6.object({
1159
- type: z6.literal("turn_started"),
1296
+ var AgentEventSchema = z8.discriminatedUnion("type", [
1297
+ z8.object({
1298
+ type: z8.literal("turn_started"),
1160
1299
  agent: NameSchema,
1161
- session: z6.string(),
1300
+ session: z8.string(),
1162
1301
  runner: NameSchema,
1163
1302
  /** The model the CLI reports running, when it reports one. */
1164
- model: z6.string().optional()
1303
+ model: z8.string().optional()
1165
1304
  }),
1166
1305
  /**
1167
1306
  * The CLI took a steer into the running turn. Adapters name the steer; the server adds the
1168
1307
  * messages it delivered and the text the CLI was given.
1169
1308
  */
1170
- z6.object({
1171
- type: z6.literal("steered"),
1172
- steer: z6.string().min(1),
1173
- messages: z6.array(UlidSchema).optional(),
1174
- text: z6.string().optional()
1309
+ z8.object({
1310
+ type: z8.literal("steered"),
1311
+ steer: z8.string().min(1),
1312
+ messages: z8.array(UlidSchema).optional(),
1313
+ text: z8.string().optional()
1175
1314
  }),
1176
- z6.object({ type: z6.literal("text"), delta: z6.string() }),
1177
- z6.object({ type: z6.literal("tool_call"), name: z6.string(), input: z6.unknown() }),
1178
- z6.object({
1179
- type: z6.literal("tool_result"),
1180
- name: z6.string(),
1181
- ok: z6.boolean(),
1315
+ z8.object({ type: z8.literal("text"), delta: z8.string() }),
1316
+ z8.object({ type: z8.literal("tool_call"), name: z8.string(), input: z8.unknown() }),
1317
+ z8.object({
1318
+ type: z8.literal("tool_result"),
1319
+ name: z8.string(),
1320
+ ok: z8.boolean(),
1182
1321
  /** What the tool returned, cut to `OUTPUT_LIMIT` characters by `capOutput`. */
1183
- output: z6.string().optional()
1322
+ output: z8.string().optional()
1184
1323
  }),
1185
- z6.object({ type: z6.literal("approval_requested"), kind: z6.string(), detail: z6.unknown() }),
1186
- z6.object({
1187
- type: z6.literal("turn_completed"),
1324
+ z8.object({ type: z8.literal("approval_requested"), kind: z8.string(), detail: z8.unknown() }),
1325
+ z8.object({
1326
+ type: z8.literal("turn_completed"),
1188
1327
  usage: UsageSchema,
1189
- costUsd: z6.number().nonnegative(),
1328
+ costUsd: z8.number().nonnegative(),
1190
1329
  status: TurnStatusSchema.nullable(),
1191
1330
  exitReason: TurnExitReasonSchema
1192
1331
  }),
1193
- z6.object({ type: z6.literal("error"), message: z6.string() })
1332
+ z8.object({ type: z8.literal("error"), message: z8.string() })
1194
1333
  ]);
1195
- var TranscriptEntrySchema = z6.object({
1334
+ var TranscriptEntrySchema = z8.object({
1196
1335
  ts: IsoDateTimeSchema,
1197
1336
  event: AgentEventSchema
1198
1337
  });
1199
- var LiveTurnEventSchema = z6.object({
1200
- seq: z6.number().int().positive(),
1338
+ var LiveTurnEventSchema = z8.object({
1339
+ seq: z8.number().int().positive(),
1201
1340
  ts: IsoDateTimeSchema,
1202
1341
  agent: NameSchema,
1203
1342
  project: NameSchema,
@@ -1209,97 +1348,107 @@ var LiveTurnEventSchema = z6.object({
1209
1348
  });
1210
1349
 
1211
1350
  // packages/shared/dist/turns.js
1212
- import { z as z8 } from "zod";
1351
+ import { z as z10 } from "zod";
1213
1352
 
1214
1353
  // packages/shared/dist/runner.js
1215
- import { z as z7 } from "zod";
1354
+ import { z as z9 } from "zod";
1216
1355
  var HOME_SCRATCH = "scratch";
1217
1356
  var HOME_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
1218
- var RunnerHelloSchema = z7.object({
1219
- protocol: z7.number().int().positive(),
1220
- version: z7.string().min(1),
1357
+ var RunnerHelloSchema = z9.object({
1358
+ protocol: z9.number().int().positive(),
1359
+ version: z9.string().min(1),
1221
1360
  os: RunnerOsSchema,
1222
- clis: z7.array(CliKindSchema),
1361
+ clis: z9.array(CliKindSchema),
1223
1362
  /** The CLIs whose adapter can keep a session warm between turns. */
1224
- residentClis: z7.array(CliKindSchema).default([]),
1363
+ residentClis: z9.array(CliKindSchema).default([]),
1225
1364
  /** The CLIs whose adapter can take input into a running turn; the server steers only these. */
1226
- steerableClis: z7.array(CliKindSchema).default([]),
1365
+ steerableClis: z9.array(CliKindSchema).default([]),
1227
1366
  /** The CLIs whose adapter can stop a running turn; the server stops only these. */
1228
- stoppableClis: z7.array(CliKindSchema).default([]),
1367
+ stoppableClis: z9.array(CliKindSchema).default([]),
1229
1368
  /** Whether the runner answers file reads on its projects' branches; one from before never does. */
1230
- branchReads: z7.boolean().default(false),
1369
+ branchReads: z9.boolean().default(false),
1231
1370
  /** Whether the runner lists what a branch changed; one from before never does. */
1232
- branchChanges: z7.boolean().default(false),
1233
- capabilities: z7.array(z7.string()).default([]),
1371
+ branchChanges: z9.boolean().default(false),
1372
+ /** Whether the runner answers a read of a CLI's quota; one from before never does. */
1373
+ quotaReads: z9.boolean().default(false),
1374
+ /** Whether the runner reads an image a turn running there uploads; one from before never does. */
1375
+ imageReads: z9.boolean().default(false),
1376
+ capabilities: z9.array(z9.string()).default([]),
1377
+ /**
1378
+ * What the runner's account lacks of the git identity citizens' own commits carry, checked
1379
+ * before each hello outside any repository; empty when it has both. Absent from a runner from
1380
+ * before the check, which no default fills in, so the board marks only what a runner reported.
1381
+ */
1382
+ gitIdentityMissing: z9.array(GitIdentityPartSchema).optional(),
1234
1383
  /** Turns the runner runs at once, or null for no limit. */
1235
- slots: z7.number().int().positive().nullable(),
1384
+ slots: z9.number().int().positive().nullable(),
1236
1385
  /** Turns the runner is still running, when it registers again; the server fails the rest of its turns. */
1237
- turns: z7.array(UlidSchema).default([])
1386
+ turns: z9.array(UlidSchema).default([])
1238
1387
  });
1239
- var RunnerWelcomeSchema = z7.object({ name: NameSchema, version: z7.string() });
1240
- var RunnerEnrollRequestSchema = z7.object({
1241
- protocol: z7.number().int().positive(),
1242
- version: z7.string().min(1).max(64),
1243
- hostname: z7.string().min(1).max(253),
1388
+ var RunnerWelcomeSchema = z9.object({ name: NameSchema, version: z9.string() });
1389
+ var RunnerEnrollRequestSchema = z9.object({
1390
+ protocol: z9.number().int().positive(),
1391
+ version: z9.string().min(1).max(64),
1392
+ hostname: z9.string().min(1).max(253),
1244
1393
  os: RunnerOsSchema,
1245
- clis: z7.array(CliKindSchema).max(8),
1246
- capabilities: z7.array(z7.string().min(1).max(64)).max(32).default([])
1394
+ clis: z9.array(CliKindSchema).max(8),
1395
+ capabilities: z9.array(z9.string().min(1).max(64)).max(32).default([])
1247
1396
  });
1248
- var RunnerEnrollmentSchema = z7.object({
1249
- deviceCode: z7.string().min(1),
1250
- userCode: z7.string().min(1),
1397
+ var RunnerEnrollmentSchema = z9.object({
1398
+ deviceCode: z9.string().min(1),
1399
+ userCode: z9.string().min(1),
1251
1400
  expiresAt: IsoDateTimeSchema,
1252
- intervalMs: z7.number().int().positive()
1401
+ intervalMs: z9.number().int().positive()
1253
1402
  });
1254
- var RunnerEnrollPollSchema = z7.object({ deviceCode: z7.string().min(1) });
1255
- var RunnerEnrollStatusSchema = z7.discriminatedUnion("status", [
1256
- z7.object({ status: z7.literal("pending") }),
1257
- z7.object({ status: z7.literal("approved"), name: NameSchema, token: z7.string().min(1) }),
1258
- z7.object({ status: z7.literal("denied") })
1403
+ var RunnerEnrollPollSchema = z9.object({ deviceCode: z9.string().min(1) });
1404
+ var RunnerEnrollStatusSchema = z9.discriminatedUnion("status", [
1405
+ z9.object({ status: z9.literal("pending") }),
1406
+ z9.object({ status: z9.literal("approved"), name: NameSchema, token: z9.string().min(1) }),
1407
+ z9.object({ status: z9.literal("denied") })
1259
1408
  ]);
1260
- var PendingEnrollmentSchema = z7.object({
1261
- userCode: z7.string().min(1),
1262
- hostname: z7.string(),
1409
+ var PendingEnrollmentSchema = z9.object({
1410
+ userCode: z9.string().min(1),
1411
+ hostname: z9.string(),
1263
1412
  os: RunnerOsSchema,
1264
- clis: z7.array(CliKindSchema),
1265
- capabilities: z7.array(z7.string()),
1266
- version: z7.string(),
1413
+ clis: z9.array(CliKindSchema),
1414
+ capabilities: z9.array(z9.string()),
1415
+ version: z9.string(),
1267
1416
  requestedAt: IsoDateTimeSchema,
1268
1417
  expiresAt: IsoDateTimeSchema
1269
1418
  });
1270
- var ProjectRepoSchema = z7.object({
1419
+ var ProjectRepoSchema = z9.object({
1271
1420
  slug: NameSchema,
1272
- origin: z7.string().nullable(),
1273
- defaultBranch: z7.string().min(1),
1421
+ origin: z9.string().nullable(),
1422
+ defaultBranch: z9.string().min(1),
1274
1423
  /**
1275
1424
  * The board lands finished tasks on the default branch, itself or through their pull requests,
1276
1425
  * so the runner lets nothing else move it.
1277
1426
  */
1278
- boardLands: z7.boolean()
1427
+ boardLands: z9.boolean()
1279
1428
  });
1280
- var TurnWorkspaceSchema = z7.discriminatedUnion("kind", [
1281
- z7.object({
1282
- kind: z7.literal("home"),
1429
+ var TurnWorkspaceSchema = z9.discriminatedUnion("kind", [
1430
+ z9.object({
1431
+ kind: z9.literal("home"),
1283
1432
  thread: UlidSchema.optional(),
1284
1433
  channel: NameSchema.optional()
1285
1434
  }),
1286
- z7.object({
1287
- kind: z7.literal("project"),
1435
+ z9.object({
1436
+ kind: z9.literal("project"),
1288
1437
  repo: ProjectRepoSchema,
1289
- branches: z7.array(z7.string().min(1)),
1438
+ branches: z9.array(z9.string().min(1)),
1290
1439
  thread: UlidSchema.optional(),
1291
1440
  channel: NameSchema.optional()
1292
1441
  }),
1293
- z7.object({ kind: z7.literal("task"), repo: ProjectRepoSchema, taskId: UlidSchema })
1442
+ z9.object({ kind: z9.literal("task"), repo: ProjectRepoSchema, taskId: UlidSchema })
1294
1443
  ]);
1295
- var TurnJobSchema = z7.object({
1444
+ var TurnJobSchema = z9.object({
1296
1445
  turnId: UlidSchema,
1297
1446
  agent: NameSchema,
1298
1447
  role: NameSchema,
1299
1448
  cli: CliKindSchema,
1300
- model: z7.string().optional(),
1449
+ model: z9.string().optional(),
1301
1450
  /** The reasoning effort set on the agent; absent for the model's own default. */
1302
- effort: z7.string().optional(),
1451
+ effort: z9.string().optional(),
1303
1452
  /** A project slug, or the society scope. */
1304
1453
  scope: NameSchema,
1305
1454
  /** The thread whose conversation the turn is in; absent for the home conversation. */
@@ -1310,125 +1459,130 @@ var TurnJobSchema = z7.object({
1310
1459
  * The session to resume, or null to start one: the CLI's adapter picks a fresh session's id, and
1311
1460
  * the outcome reports it.
1312
1461
  */
1313
- session: z7.string().min(1).nullable(),
1462
+ session: z9.string().min(1).nullable(),
1314
1463
  /** The running total the session reported after its last turn, which a resumed session starts from. */
1315
- costSoFarUsd: z7.number().nonnegative(),
1464
+ costSoFarUsd: z9.number().nonnegative(),
1316
1465
  /** The rendered charter, norms, memory core, and skills index, with `PATH_TOKENS` for places. */
1317
- instructions: z7.string(),
1318
- prompt: z7.string(),
1319
- mcp: z7.object({ url: z7.string().min(1), token: z7.string().min(1) }),
1320
- limits: z7.object({
1321
- timeoutMs: z7.number().int().positive().nullable(),
1322
- maxTurns: z7.number().int().positive().nullable()
1466
+ instructions: z9.string(),
1467
+ prompt: z9.string(),
1468
+ mcp: z9.object({ url: z9.string().min(1), token: z9.string().min(1) }),
1469
+ limits: z9.object({
1470
+ timeoutMs: z9.number().int().positive().nullable(),
1471
+ maxTurns: z9.number().int().positive().nullable()
1323
1472
  }),
1324
1473
  /** Present when the runner keeps the conversation's session warm between turns, under `key`. */
1325
- resident: z7.object({ key: z7.string().min(1), idleMs: z7.number().int().positive() }).optional(),
1474
+ resident: z9.object({ key: z9.string().min(1), idleMs: z9.number().int().positive() }).optional(),
1326
1475
  workspace: TurnWorkspaceSchema
1327
1476
  });
1328
- var TurnWorkSchema = z7.object({ branch: z7.string().min(1), head: z7.string().min(1) });
1329
- var TurnEventsSchema = z7.object({ entries: z7.array(TranscriptEntrySchema) });
1330
- var TurnOutcomeSchema = z7.object({
1477
+ var TurnWorkSchema = z9.object({ branch: z9.string().min(1), head: z9.string().min(1) });
1478
+ var TurnEventsSchema = z9.object({ entries: z9.array(TranscriptEntrySchema) });
1479
+ var TurnOutcomeSchema = z9.object({
1331
1480
  exitReason: TurnExitReasonSchema,
1332
1481
  status: TurnStatusSchema.nullable(),
1333
- error: z7.string().nullable(),
1482
+ error: z9.string().nullable(),
1334
1483
  usage: UsageSchema,
1335
- costUsd: z7.number().nonnegative(),
1484
+ costUsd: z9.number().nonnegative(),
1336
1485
  /** The session's running total after this turn, for CLIs that report one. */
1337
- sessionCostUsd: z7.number().nonnegative().optional(),
1486
+ sessionCostUsd: z9.number().nonnegative().optional(),
1338
1487
  /** The session the CLI used, which differs from the job's for CLIs that assign their own ids. */
1339
- session: z7.string().min(1),
1488
+ session: z9.string().min(1),
1340
1489
  /** The model the CLI reported, when it reported one. */
1341
- model: z7.string().nullable(),
1490
+ model: z9.string().nullable(),
1342
1491
  work: TurnWorkSchema.nullable(),
1343
1492
  /**
1344
1493
  * Whether the turn's own workspace, a thread's, a channel's, or a task's, holds work on no
1345
1494
  * branch after the hand-back: uncommitted changes, or commits no branch contains.
1346
1495
  */
1347
- leftovers: z7.boolean().default(false)
1496
+ leftovers: z9.boolean().default(false),
1497
+ /**
1498
+ * The CLI's plan quota as the turn's stream last reported it, when it reported any; a runner from
1499
+ * before never sends it.
1500
+ */
1501
+ quota: CliQuotaSchema.optional()
1348
1502
  });
1349
- var TurnAckSchema = z7.object({ dropWorkspace: z7.boolean() });
1350
- var LandRequestSchema = z7.object({
1503
+ var TurnAckSchema = z9.object({ dropWorkspace: z9.boolean() });
1504
+ var LandRequestSchema = z9.object({
1351
1505
  repo: ProjectRepoSchema,
1352
- branch: z7.string().min(1),
1506
+ branch: z9.string().min(1),
1353
1507
  /**
1354
1508
  * For a `ghpr` task, the pull request to merge in place of the branch, which must hold the
1355
1509
  * branch's files, and the merge commit's subject and body, the pull request's title and an
1356
1510
  * empty body when left out.
1357
1511
  */
1358
- pullRequest: z7.object({
1359
- url: z7.string().min(1),
1360
- subject: z7.string().min(1).optional(),
1361
- body: z7.string().optional()
1512
+ pullRequest: z9.object({
1513
+ url: z9.string().min(1),
1514
+ subject: z9.string().min(1).optional(),
1515
+ body: z9.string().optional()
1362
1516
  }).optional()
1363
1517
  });
1364
- var CloseRequestSchema = z7.object({
1518
+ var CloseRequestSchema = z9.object({
1365
1519
  repo: ProjectRepoSchema,
1366
- url: z7.string().min(1),
1367
- comment: z7.string().min(1)
1520
+ url: z9.string().min(1),
1521
+ comment: z9.string().min(1)
1368
1522
  });
1369
- var MergeOutcomeSchema = z7.object({ ok: z7.boolean(), detail: z7.string() });
1370
- var ModelListSchema = z7.array(ModelOptionSchema);
1523
+ var MergeOutcomeSchema = z9.object({ ok: z9.boolean(), detail: z9.string() });
1524
+ var ModelListSchema = z9.array(ModelOptionSchema);
1371
1525
  var BRANCH_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
1372
- var BranchPathSchema = z7.string().max(4096).refine((value) => value === "" || value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== ".." && !segment.includes("\0")), { message: "a path inside the branch, with no empty, `.`, or `..` segment" });
1373
- var BranchNameSchema = z7.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/);
1374
- var BranchReadSchema = z7.object({
1526
+ var BranchPathSchema = z9.string().max(4096).refine((value) => value === "" || value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== ".." && !segment.includes("\0")), { message: "a path inside the branch, with no empty, `.`, or `..` segment" });
1527
+ var BranchNameSchema = z9.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/);
1528
+ var BranchReadSchema = z9.object({
1375
1529
  repo: ProjectRepoSchema,
1376
1530
  branch: BranchNameSchema,
1377
1531
  path: BranchPathSchema
1378
1532
  });
1379
- var BranchCommitSchema = z7.object({
1380
- id: z7.string().min(1),
1533
+ var BranchCommitSchema = z9.object({
1534
+ id: z9.string().min(1),
1381
1535
  at: IsoDateTimeSchema,
1382
- author: z7.string()
1536
+ author: z9.string()
1383
1537
  });
1384
- var BranchFileSchema = z7.discriminatedUnion("kind", [
1385
- z7.object({
1386
- kind: z7.literal("file"),
1538
+ var BranchFileSchema = z9.discriminatedUnion("kind", [
1539
+ z9.object({
1540
+ kind: z9.literal("file"),
1387
1541
  path: BranchPathSchema,
1388
1542
  commit: BranchCommitSchema,
1389
- size: z7.number().int().nonnegative(),
1390
- content: z7.string().nullable()
1543
+ size: z9.number().int().nonnegative(),
1544
+ content: z9.string().nullable()
1391
1545
  }),
1392
- z7.object({
1393
- kind: z7.literal("dir"),
1546
+ z9.object({
1547
+ kind: z9.literal("dir"),
1394
1548
  path: BranchPathSchema,
1395
1549
  commit: BranchCommitSchema,
1396
- entries: z7.array(z7.object({
1397
- name: z7.string().min(1),
1398
- kind: z7.enum(["file", "dir"]),
1399
- size: z7.number().int().nonnegative().nullable()
1550
+ entries: z9.array(z9.object({
1551
+ name: z9.string().min(1),
1552
+ kind: z9.enum(["file", "dir"]),
1553
+ size: z9.number().int().nonnegative().nullable()
1400
1554
  }))
1401
1555
  })
1402
1556
  ]);
1403
- var BranchChangesReadSchema = z7.object({
1557
+ var BranchChangesReadSchema = z9.object({
1404
1558
  repo: ProjectRepoSchema,
1405
1559
  branch: BranchNameSchema
1406
1560
  });
1407
- var BranchFileChangeSchema = z7.object({
1408
- path: z7.string().min(1),
1409
- status: z7.enum(["added", "modified", "deleted"]),
1561
+ var BranchFileChangeSchema = z9.object({
1562
+ path: z9.string().min(1),
1563
+ status: z9.enum(["added", "modified", "deleted"]),
1410
1564
  /** Lines added and removed, or null for a binary file. */
1411
- added: z7.number().int().nonnegative().nullable(),
1412
- removed: z7.number().int().nonnegative().nullable(),
1565
+ added: z9.number().int().nonnegative().nullable(),
1566
+ removed: z9.number().int().nonnegative().nullable(),
1413
1567
  /** Absent when the file changed only through a merge of another branch. */
1414
- lastChange: z7.object({ author: z7.string(), at: IsoDateTimeSchema }).optional()
1568
+ lastChange: z9.object({ author: z9.string(), at: IsoDateTimeSchema }).optional()
1415
1569
  });
1416
- var BranchChangesSchema = z7.object({
1570
+ var BranchChangesSchema = z9.object({
1417
1571
  head: BranchCommitSchema,
1418
- files: z7.array(BranchFileChangeSchema),
1419
- total: z7.number().int().nonnegative()
1572
+ files: z9.array(BranchFileChangeSchema),
1573
+ total: z9.number().int().nonnegative()
1420
1574
  });
1421
- var BranchDiffReadSchema = z7.object({
1575
+ var BranchDiffReadSchema = z9.object({
1422
1576
  repo: ProjectRepoSchema,
1423
1577
  branch: BranchNameSchema,
1424
1578
  path: BranchPathSchema.min(1)
1425
1579
  });
1426
- var BranchFileDiffSchema = z7.object({
1580
+ var BranchFileDiffSchema = z9.object({
1427
1581
  head: BranchCommitSchema,
1428
1582
  path: BranchPathSchema,
1429
1583
  status: BranchFileChangeSchema.shape.status,
1430
- patch: z7.string().nullable(),
1431
- truncated: z7.boolean()
1584
+ patch: z9.string().nullable(),
1585
+ truncated: z9.boolean()
1432
1586
  });
1433
1587
  function patchHunks(patch, limit) {
1434
1588
  if (/^Binary files .* differ$/m.test(patch)) {
@@ -1442,64 +1596,88 @@ function patchHunks(patch, limit) {
1442
1596
  const cut = hunks.lastIndexOf("\n", limit);
1443
1597
  return { patch: hunks.slice(0, cut === -1 ? limit : cut), truncated: true };
1444
1598
  }
1445
- var WorkspaceConversationSchema = z7.discriminatedUnion("kind", [
1446
- z7.object({ kind: z7.literal("thread"), id: UlidSchema }),
1447
- z7.object({ kind: z7.literal("channel"), scope: NameSchema, name: NameSchema })
1599
+ var TurnImageReadSchema = z9.object({
1600
+ turnId: UlidSchema,
1601
+ path: ImagePathSchema
1602
+ });
1603
+ var ImageRefusalSchema = z9.enum(["missing", "outside", "not_image", "too_big"]);
1604
+ var TurnImageSchema = z9.discriminatedUnion("kind", [
1605
+ z9.object({
1606
+ kind: z9.literal("image"),
1607
+ type: ImageTypeSchema,
1608
+ size: z9.number().int().positive(),
1609
+ content: z9.string()
1610
+ }),
1611
+ z9.object({ kind: z9.literal("refused"), why: ImageRefusalSchema, detail: z9.string() })
1448
1612
  ]);
1449
- var HeldWorkspaceSchema = z7.object({
1613
+ var WorkspaceConversationSchema = z9.discriminatedUnion("kind", [
1614
+ z9.object({ kind: z9.literal("thread"), id: UlidSchema }),
1615
+ z9.object({ kind: z9.literal("channel"), scope: NameSchema, name: NameSchema })
1616
+ ]);
1617
+ var HeldWorkspaceSchema = z9.object({
1450
1618
  agent: NameSchema,
1451
1619
  scope: NameSchema,
1452
1620
  conversation: WorkspaceConversationSchema
1453
1621
  });
1454
- var HeldWorkspacesSchema = z7.object({ workspaces: z7.array(HeldWorkspaceSchema) });
1455
- var SweepResultSchema = z7.object({ kept: z7.array(HeldWorkspaceSchema) });
1456
- var TurnSteerSchema = z7.object({ id: z7.uuid(), text: z7.string().min(1) });
1457
- var RunnerMessageSchema = z7.discriminatedUnion("type", [
1458
- z7.object({ type: z7.literal("turn"), job: TurnJobSchema }),
1459
- z7.object({ type: z7.literal("land"), request: z7.string().min(1), land: LandRequestSchema }),
1622
+ var HeldWorkspacesSchema = z9.object({ workspaces: z9.array(HeldWorkspaceSchema) });
1623
+ var SweepResultSchema = z9.object({ kept: z9.array(HeldWorkspaceSchema) });
1624
+ var TurnSteerSchema = z9.object({ id: z9.uuid(), text: z9.string().min(1) });
1625
+ var RunnerMessageSchema = z9.discriminatedUnion("type", [
1626
+ z9.object({ type: z9.literal("turn"), job: TurnJobSchema }),
1627
+ z9.object({ type: z9.literal("land"), request: z9.string().min(1), land: LandRequestSchema }),
1460
1628
  /** Answered with a `MergeOutcome`. */
1461
- z7.object({ type: z7.literal("close"), request: z7.string().min(1), close: CloseRequestSchema }),
1462
- z7.object({ type: z7.literal("models"), request: z7.string().min(1), cli: CliKindSchema }),
1629
+ z9.object({ type: z9.literal("close"), request: z9.string().min(1), close: CloseRequestSchema }),
1630
+ z9.object({ type: z9.literal("models"), request: z9.string().min(1), cli: CliKindSchema }),
1631
+ /**
1632
+ * Answered with a `CliQuota` read from the CLI without a turn, or null when its adapter cannot
1633
+ * read one. Sent only to runners whose hello says `quotaReads`.
1634
+ */
1635
+ z9.object({ type: z9.literal("quota"), request: z9.string().min(1), cli: CliKindSchema }),
1463
1636
  /** Answered with a `BranchFile`, or null when the branch or the path does not exist. */
1464
- z7.object({ type: z7.literal("file"), request: z7.string().min(1), read: BranchReadSchema }),
1637
+ z9.object({ type: z9.literal("file"), request: z9.string().min(1), read: BranchReadSchema }),
1465
1638
  /** Answered with `BranchChanges`, or null when the repository or the branch does not exist. */
1466
- z7.object({
1467
- type: z7.literal("changes"),
1468
- request: z7.string().min(1),
1639
+ z9.object({
1640
+ type: z9.literal("changes"),
1641
+ request: z9.string().min(1),
1469
1642
  read: BranchChangesReadSchema
1470
1643
  }),
1471
1644
  /** Answered with a `BranchFileDiff`, or null when the branch does not exist or left the path alone. */
1472
- z7.object({ type: z7.literal("diff"), request: z7.string().min(1), read: BranchDiffReadSchema }),
1645
+ z9.object({ type: z9.literal("diff"), request: z9.string().min(1), read: BranchDiffReadSchema }),
1646
+ /**
1647
+ * Answered with a `TurnImage`, or null when the turn is not running there. Sent only to runners
1648
+ * whose hello says `imageReads`.
1649
+ */
1650
+ z9.object({ type: z9.literal("image"), request: z9.string().min(1), read: TurnImageReadSchema }),
1473
1651
  /** Answered with whether the CLI accepted it; that it took it comes as a `steered` event. */
1474
- z7.object({
1475
- type: z7.literal("steer"),
1476
- request: z7.string().min(1),
1652
+ z9.object({
1653
+ type: z9.literal("steer"),
1654
+ request: z9.string().min(1),
1477
1655
  turnId: UlidSchema,
1478
1656
  steer: TurnSteerSchema
1479
1657
  }),
1480
1658
  /** Answered with whether the turn was there to stop; it ends `stopped`. */
1481
- z7.object({ type: z7.literal("stop"), request: z7.string().min(1), turnId: UlidSchema }),
1659
+ z9.object({ type: z9.literal("stop"), request: z9.string().min(1), turnId: UlidSchema }),
1482
1660
  /**
1483
1661
  * A conversation ended: the runner removes every citizen's workspace for it but those a turn
1484
1662
  * still uses, and answers with a `SweepResult` naming those it kept for holding work on no branch.
1485
1663
  */
1486
- z7.object({
1487
- type: z7.literal("sweep"),
1488
- request: z7.string().min(1),
1664
+ z9.object({
1665
+ type: z9.literal("sweep"),
1666
+ request: z9.string().min(1),
1489
1667
  conversation: WorkspaceConversationSchema
1490
1668
  })
1491
1669
  ]);
1492
- var RunnerAnswerSchema = z7.discriminatedUnion("ok", [
1493
- z7.object({ ok: z7.literal(true), value: z7.unknown() }),
1494
- z7.object({ ok: z7.literal(false), error: z7.string() })
1670
+ var RunnerAnswerSchema = z9.discriminatedUnion("ok", [
1671
+ z9.object({ ok: z9.literal(true), value: z9.unknown() }),
1672
+ z9.object({ ok: z9.literal(false), error: z9.string() })
1495
1673
  ]);
1496
- var WarmSessionsSchema = z7.object({ warm: z7.array(z7.string()) });
1497
- var FileManifestSchema = z7.object({ files: z7.record(z7.string(), z7.string()) });
1674
+ var WarmSessionsSchema = z9.object({ warm: z9.array(z9.string()) });
1675
+ var FileManifestSchema = z9.object({ files: z9.record(z9.string(), z9.string()) });
1498
1676
  function fileVersion(sha, executable) {
1499
1677
  return executable ? `${sha}+x` : sha;
1500
1678
  }
1501
- var FileReadSchema = z7.object({ paths: z7.array(z7.string()) });
1502
- var FileContentsSchema = z7.object({ files: z7.record(z7.string(), z7.string()) });
1679
+ var FileReadSchema = z9.object({ paths: z9.array(z9.string()) });
1680
+ var FileContentsSchema = z9.object({ files: z9.record(z9.string(), z9.string()) });
1503
1681
  function isSafeRelativePath(file) {
1504
1682
  if (file.length === 0 || file.startsWith("/") || file.includes("\\") || file.includes("\0")) {
1505
1683
  return false;
@@ -1508,7 +1686,7 @@ function isSafeRelativePath(file) {
1508
1686
  }
1509
1687
 
1510
1688
  // packages/shared/dist/turns.js
1511
- var TriggerKindSchema = z8.enum([
1689
+ var TriggerKindSchema = z10.enum([
1512
1690
  "mention",
1513
1691
  "stage",
1514
1692
  "task_done",
@@ -1522,11 +1700,11 @@ var TriggerKindSchema = z8.enum([
1522
1700
  "closing",
1523
1701
  "cron"
1524
1702
  ]);
1525
- var TriggerSchema = z8.object({
1703
+ var TriggerSchema = z10.object({
1526
1704
  kind: TriggerKindSchema,
1527
1705
  from: NameSchema.optional(),
1528
- fromUser: z8.boolean().default(false),
1529
- reason: z8.string().default(""),
1706
+ fromUser: z10.boolean().default(false),
1707
+ reason: z10.string().default(""),
1530
1708
  taskId: UlidSchema.optional(),
1531
1709
  messageId: UlidSchema.optional(),
1532
1710
  /** The channel the waking message was posted in. */
@@ -1534,17 +1712,17 @@ var TriggerSchema = z8.object({
1534
1712
  /** The cron that came due, for a `cron` wake. */
1535
1713
  cronId: UlidSchema.optional()
1536
1714
  });
1537
- var TurnThreadSchema = z8.object({ id: UlidSchema, task: z8.boolean() });
1538
- var TurnDispatchSchema = z8.object({
1715
+ var TurnThreadSchema = z10.object({ id: UlidSchema, task: z10.boolean() });
1716
+ var TurnDispatchSchema = z10.object({
1539
1717
  agent: NameSchema,
1540
1718
  project: NameSchema,
1541
1719
  thread: TurnThreadSchema.optional(),
1542
1720
  channel: NameSchema.optional(),
1543
1721
  trigger: TriggerSchema,
1544
- priority: z8.number().int().min(0).max(2),
1545
- onboarding: z8.boolean().default(false),
1722
+ priority: z10.number().int().min(0).max(2),
1723
+ onboarding: z10.boolean().default(false),
1546
1724
  /** Every cron that came due for this turn, whatever wake it merged into; its prompt quotes each. */
1547
- crons: z8.array(UlidSchema).optional()
1725
+ crons: z10.array(UlidSchema).optional()
1548
1726
  });
1549
1727
  function conversationPart(conversation) {
1550
1728
  return conversation.thread ?? (conversation.channel === void 0 ? void 0 : `#${conversation.channel}`);
@@ -1555,7 +1733,7 @@ function channelConversation(scope, channel) {
1555
1733
  const name = at < 0 ? channel : channel.slice(at + 1);
1556
1734
  return place === scope && name !== "general" ? name : void 0;
1557
1735
  }
1558
- var TurnRecordSchema = z8.object({
1736
+ var TurnRecordSchema = z10.object({
1559
1737
  /** Assigned by the board when the turn begins; the turn's transcript is filed under it. */
1560
1738
  id: UlidSchema.optional(),
1561
1739
  agent: NameSchema,
@@ -1566,31 +1744,31 @@ var TurnRecordSchema = z8.object({
1566
1744
  channel: NameSchema.optional(),
1567
1745
  runner: NameSchema,
1568
1746
  cli: CliKindSchema.nullable(),
1569
- session: z8.string().nullable(),
1747
+ session: z10.string().nullable(),
1570
1748
  trigger: TriggerSchema,
1571
1749
  startedAt: IsoDateTimeSchema,
1572
1750
  endedAt: IsoDateTimeSchema.nullable(),
1573
1751
  exitReason: TurnExitReasonSchema.nullable(),
1574
1752
  status: TurnStatusSchema.nullable(),
1575
- error: z8.string().nullable(),
1753
+ error: z10.string().nullable(),
1576
1754
  usage: UsageSchema.nullable(),
1577
- costUsd: z8.number().nonnegative().default(0),
1755
+ costUsd: z10.number().nonnegative().default(0),
1578
1756
  /**
1579
1757
  * The running total the CLI reported for the session after this turn, when it reports one. A
1580
1758
  * resumed session starts from it, so the next turn's own cost is measured from here.
1581
1759
  */
1582
- sessionCostUsd: z8.number().nonnegative().optional(),
1583
- toolCalls: z8.number().int().nonnegative().default(0),
1760
+ sessionCostUsd: z10.number().nonnegative().optional(),
1761
+ toolCalls: z10.number().int().nonnegative().default(0),
1584
1762
  /** The model the CLI reported for this turn, else the model configured for the agent, else null. */
1585
- model: z8.string().nullable().default(null),
1763
+ model: z10.string().nullable().default(null),
1586
1764
  /** The task branch the turn left work on, with its commit, as the runner reported it. */
1587
1765
  work: TurnWorkSchema.optional(),
1588
1766
  /** Who stopped the turn, for one that ended `stopped`. */
1589
1767
  stoppedBy: NameSchema.optional(),
1590
1768
  /** The crons whose fire started the turn, which record how it ended. */
1591
- crons: z8.array(UlidSchema).optional()
1769
+ crons: z10.array(UlidSchema).optional()
1592
1770
  });
1593
- var RunningTurnSchema = z8.object({
1771
+ var RunningTurnSchema = z10.object({
1594
1772
  turnId: UlidSchema,
1595
1773
  agent: NameSchema,
1596
1774
  scope: NameSchema,
@@ -1598,49 +1776,49 @@ var RunningTurnSchema = z8.object({
1598
1776
  channel: NameSchema.optional(),
1599
1777
  cli: CliKindSchema,
1600
1778
  askedIn: ChannelRefSchema.optional(),
1601
- steerable: z8.boolean(),
1602
- stoppable: z8.boolean()
1779
+ steerable: z10.boolean(),
1780
+ stoppable: z10.boolean()
1603
1781
  });
1604
- var SessionsFileSchema = z8.object({
1605
- claude: z8.string().optional(),
1606
- codex: z8.string().optional(),
1782
+ var SessionsFileSchema = z10.object({
1783
+ claude: z10.string().optional(),
1784
+ codex: z10.string().optional(),
1607
1785
  runner: NameSchema.optional()
1608
1786
  });
1609
- var WakeRequestSchema = z8.object({
1787
+ var WakeRequestSchema = z10.object({
1610
1788
  agent: NameSchema,
1611
1789
  project: NameSchema,
1612
- reason: z8.string().default("manual wake"),
1790
+ reason: z10.string().default("manual wake"),
1613
1791
  /** A manual wake, or a reflection turn requested ahead of the cadence. */
1614
- kind: z8.enum(["manual", "reflection"]).default("manual")
1792
+ kind: z10.enum(["manual", "reflection"]).default("manual")
1615
1793
  });
1616
- var TurnHistoryEntrySchema = z8.object({
1794
+ var TurnHistoryEntrySchema = z10.object({
1617
1795
  /** The id of the event that ended the turn. */
1618
1796
  id: UlidSchema,
1619
1797
  /** The turn's own id, when it has a transcript to open; turns before transcripts have none. */
1620
1798
  turnId: UlidSchema.optional(),
1621
1799
  ts: IsoDateTimeSchema,
1622
- outcome: z8.enum(["completed", "failed"]),
1800
+ outcome: z10.enum(["completed", "failed"]),
1623
1801
  /** A project slug, or the society scope. */
1624
1802
  project: NameSchema,
1625
1803
  /** The thread whose conversation the turn was in; absent for the home conversation. */
1626
1804
  thread: UlidSchema.optional(),
1627
1805
  /** The channel, never general, whose conversation the turn was in. */
1628
1806
  channel: NameSchema.optional(),
1629
- trigger: z8.string(),
1630
- exitReason: z8.string().nullable(),
1631
- costUsd: z8.number(),
1807
+ trigger: z10.string(),
1808
+ exitReason: z10.string().nullable(),
1809
+ costUsd: z10.number(),
1632
1810
  /** The tokens the turn used; absent for turns logged before turns recorded them. */
1633
1811
  usage: UsageSchema.optional(),
1634
- model: z8.string().nullable(),
1635
- summary: z8.string().nullable(),
1636
- error: z8.string().nullable(),
1812
+ model: z10.string().nullable(),
1813
+ summary: z10.string().nullable(),
1814
+ error: z10.string().nullable(),
1637
1815
  /** When the log recorded the turn's start; absent when the start is missing from the log. */
1638
1816
  startedAt: IsoDateTimeSchema.optional(),
1639
- toolCalls: z8.number().int().nonnegative().optional()
1817
+ toolCalls: z10.number().int().nonnegative().optional()
1640
1818
  });
1641
1819
 
1642
1820
  // packages/shared/dist/ops.js
1643
- import { z as z9 } from "zod";
1821
+ import { z as z11 } from "zod";
1644
1822
  var OPS_SIGNAL_KINDS = [
1645
1823
  "waiting_stage",
1646
1824
  "backlog",
@@ -1656,16 +1834,16 @@ var OPS_SIGNAL_KINDS = [
1656
1834
  "stuck_workspace",
1657
1835
  "cron_failing"
1658
1836
  ];
1659
- var OpsSignalKindSchema = z9.enum(OPS_SIGNAL_KINDS);
1660
- var OpsSignalSchema = z9.object({
1837
+ var OpsSignalKindSchema = z11.enum(OPS_SIGNAL_KINDS);
1838
+ var OpsSignalSchema = z11.object({
1661
1839
  kind: OpsSignalKindSchema,
1662
1840
  /** Identifies the condition, for example `backlog:demo:engineer`, so a persisting one is not logged again every pass. */
1663
- key: z9.string().min(1),
1841
+ key: z11.string().min(1),
1664
1842
  /** One line a reader can act on. */
1665
- summary: z9.string().min(1),
1843
+ summary: z11.string().min(1),
1666
1844
  /** The counter or timer value behind the signal. */
1667
- value: z9.number(),
1668
- threshold: z9.number().optional(),
1845
+ value: z11.number(),
1846
+ threshold: z11.number().optional(),
1669
1847
  project: NameSchema.optional(),
1670
1848
  agent: NameSchema.optional(),
1671
1849
  role: NameSchema.optional(),
@@ -1673,8 +1851,8 @@ var OpsSignalSchema = z9.object({
1673
1851
  });
1674
1852
 
1675
1853
  // packages/shared/dist/metrics.js
1676
- import { z as z10 } from "zod";
1677
- var MetricsWindowSchema = z10.enum(["24h", "7d", "all"]);
1854
+ import { z as z12 } from "zod";
1855
+ var MetricsWindowSchema = z12.enum(["24h", "7d", "all"]);
1678
1856
  var METRICS_WINDOW_MS = {
1679
1857
  "24h": 24 * 36e5,
1680
1858
  "7d": 7 * 24 * 36e5,
@@ -1685,9 +1863,9 @@ function isBoardAction(tool) {
1685
1863
  const verb = /^mcp__board__(.+)$/.exec(tool)?.[1];
1686
1864
  return verb !== void 0 && !READ_VERBS.some((read2) => read2 === verb);
1687
1865
  }
1688
- var CountSchema = z10.number().int().nonnegative();
1689
- var MsSchema = z10.number().nonnegative();
1690
- var MetricsSchema = z10.object({
1866
+ var CountSchema = z12.number().int().nonnegative();
1867
+ var MsSchema = z12.number().nonnegative();
1868
+ var MetricsSchema = z12.object({
1691
1869
  window: MetricsWindowSchema,
1692
1870
  since: IsoDateTimeSchema.nullable(),
1693
1871
  until: IsoDateTimeSchema,
@@ -1695,12 +1873,12 @@ var MetricsSchema = z10.object({
1695
1873
  * Finished turns that changed nothing on the board. Reflections are left out, since they write
1696
1874
  * memory, not the board; turns without a transcript are counted apart, their actions unknown.
1697
1875
  */
1698
- idle: z10.object({
1876
+ idle: z12.object({
1699
1877
  turns: CountSchema,
1700
1878
  idle: CountSchema,
1701
1879
  unknown: CountSchema,
1702
- byTrigger: z10.array(z10.object({ trigger: z10.string(), turns: CountSchema, idle: CountSchema })),
1703
- byAgent: z10.array(z10.object({
1880
+ byTrigger: z12.array(z12.object({ trigger: z12.string(), turns: CountSchema, idle: CountSchema })),
1881
+ byAgent: z12.array(z12.object({
1704
1882
  agent: NameSchema,
1705
1883
  role: NameSchema.nullable(),
1706
1884
  turns: CountSchema,
@@ -1708,33 +1886,33 @@ var MetricsSchema = z10.object({
1708
1886
  }))
1709
1887
  }),
1710
1888
  /** Work sent back to an earlier stage: over the tasks finished in the window, and by where. */
1711
- sentBack: z10.object({
1889
+ sentBack: z12.object({
1712
1890
  finished: CountSchema,
1713
1891
  sendBacks: CountSchema,
1714
1892
  tasksSentBack: CountSchema,
1715
- byStage: z10.array(z10.object({ project: NameSchema, stage: z10.string(), count: CountSchema })),
1716
- bySender: z10.array(z10.object({ agent: NameSchema, count: CountSchema }))
1893
+ byStage: z12.array(z12.object({ project: NameSchema, stage: z12.string(), count: CountSchema })),
1894
+ bySender: z12.array(z12.object({ agent: NameSchema, count: CountSchema }))
1717
1895
  }),
1718
1896
  /** Posts in the threads of the tasks finished in the window, the board's own notices left out. */
1719
- messages: z10.object({
1897
+ messages: z12.object({
1720
1898
  finished: CountSchema,
1721
1899
  messages: CountSchema,
1722
- byProject: z10.array(z10.object({ project: NameSchema, finished: CountSchema, messages: CountSchema })),
1723
- busiest: z10.array(z10.object({
1900
+ byProject: z12.array(z12.object({ project: NameSchema, finished: CountSchema, messages: CountSchema })),
1901
+ busiest: z12.array(z12.object({
1724
1902
  taskId: UlidSchema,
1725
- title: z10.string(),
1903
+ title: z12.string(),
1726
1904
  project: NameSchema,
1727
1905
  messages: CountSchema
1728
1906
  }))
1729
1907
  }),
1730
1908
  /** Time from a mention to the start of the turn it woke, in the conversation it was made in. */
1731
- latency: z10.object({
1909
+ latency: z12.object({
1732
1910
  mentions: CountSchema,
1733
1911
  /** Mentions no turn has followed yet. */
1734
1912
  unanswered: CountSchema,
1735
1913
  medianMs: MsSchema.nullable(),
1736
1914
  slowestMs: MsSchema.nullable(),
1737
- byAgent: z10.array(z10.object({
1915
+ byAgent: z12.array(z12.object({
1738
1916
  agent: NameSchema,
1739
1917
  mentions: CountSchema,
1740
1918
  medianMs: MsSchema,
@@ -1742,81 +1920,141 @@ var MetricsSchema = z10.object({
1742
1920
  }))
1743
1921
  }),
1744
1922
  /** The user's decisions: proposals decided, questions answered, and stages passed or sent back. */
1745
- decisions: z10.object({
1923
+ decisions: z12.object({
1746
1924
  total: CountSchema,
1747
- byDay: z10.array(z10.object({
1748
- day: z10.string(),
1925
+ byDay: z12.array(z12.object({
1926
+ day: z12.string(),
1749
1927
  proposals: CountSchema,
1750
1928
  answers: CountSchema,
1751
1929
  stages: CountSchema
1752
1930
  }))
1753
1931
  }),
1754
1932
  /** Tasks that needed a capability no connected runner offered, and whether they still do. */
1755
- blocked: z10.array(z10.object({
1933
+ blocked: z12.array(z12.object({
1756
1934
  taskId: UlidSchema,
1757
- title: z10.string().nullable(),
1935
+ title: z12.string().nullable(),
1758
1936
  project: NameSchema.nullable(),
1759
- summary: z10.string(),
1937
+ summary: z12.string(),
1760
1938
  firstAt: IsoDateTimeSchema,
1761
1939
  lastAt: IsoDateTimeSchema,
1762
- holds: z10.boolean()
1940
+ holds: z12.boolean()
1763
1941
  }))
1764
1942
  });
1943
+ var CitizenTokensSchema = z12.object({
1944
+ agent: NameSchema,
1945
+ turns: CountSchema,
1946
+ withUsage: CountSchema,
1947
+ usage: UsageSchema.nullable()
1948
+ });
1949
+ var TokenUseSchema = z12.object({
1950
+ window: MetricsWindowSchema,
1951
+ since: IsoDateTimeSchema.nullable(),
1952
+ until: IsoDateTimeSchema,
1953
+ citizens: z12.array(CitizenTokensSchema)
1954
+ });
1765
1955
 
1766
1956
  // packages/shared/dist/config.js
1767
- import { z as z11 } from "zod";
1768
- var LogLevelSchema = z11.enum(["trace", "debug", "info", "warn", "error", "fatal"]);
1769
- var GithubSignInSchema = z11.object({
1770
- clientId: z11.string().min(1),
1771
- clientSecret: z11.string().min(1),
1957
+ import { z as z13 } from "zod";
1958
+ var LogLevelSchema = z13.enum(["trace", "debug", "info", "warn", "error", "fatal"]);
1959
+ var GithubSignInSchema = z13.object({
1960
+ clientId: z13.string().min(1),
1961
+ clientSecret: z13.string().min(1),
1772
1962
  /** Logins, lowercased; GitHub logins are case-insensitive. */
1773
- users: z11.array(z11.string().min(1)).min(1)
1963
+ users: z13.array(z13.string().min(1)).min(1)
1774
1964
  });
1775
- var ServerConfigSchema = z11.object({
1776
- dataDir: z11.string().min(1),
1777
- host: z11.string().min(1),
1778
- port: z11.number().int().positive().max(65535),
1965
+ var ServerConfigSchema = z13.object({
1966
+ dataDir: z13.string().min(1),
1967
+ host: z13.string().min(1),
1968
+ port: z13.number().int().positive().max(65535),
1779
1969
  /**
1780
1970
  * An address every runner's agents reach this server at, for example https://board.example, or
1781
1971
  * null for each runner to use the address it reaches the server at itself.
1782
1972
  */
1783
- publicUrl: z11.string().min(1).nullable(),
1973
+ publicUrl: z13.string().min(1).nullable(),
1784
1974
  logLevel: LogLevelSchema,
1785
1975
  /** Turns running at once across every runner, or null for no limit beyond each runner's own. */
1786
- concurrency: z11.number().int().positive().nullable(),
1976
+ concurrency: z13.number().int().positive().nullable(),
1787
1977
  /** How long one turn may run before it is stopped, or null for no limit. */
1788
- turnTimeoutMs: z11.number().int().positive().nullable(),
1978
+ turnTimeoutMs: z13.number().int().positive().nullable(),
1789
1979
  /** Rounds of tool calls one Claude turn may take, or null for no limit. */
1790
- toolRounds: z11.number().int().positive().nullable(),
1980
+ toolRounds: z13.number().int().positive().nullable(),
1791
1981
  /** How long a resident session stays warm after its last turn. */
1792
- residentIdleMs: z11.number().int().positive(),
1982
+ residentIdleMs: z13.number().int().positive(),
1793
1983
  /** Signing in to the board with GitHub, or null when only a token lets anyone in. */
1794
1984
  github: GithubSignInSchema.nullable(),
1795
1985
  /**
1796
1986
  * Reverse proxies in front of the server whose X-Forwarded-For entries name the client, for the
1797
1987
  * rate limits on what anyone may ask without a token: 0 when clients connect directly.
1798
1988
  */
1799
- trustedProxies: z11.number().int().min(0).max(16)
1989
+ trustedProxies: z13.number().int().min(0).max(16)
1800
1990
  });
1801
- var RunnerConfigSchema = z11.object({
1991
+ var RunnerConfigSchema = z13.object({
1802
1992
  /** The board server, for example http://127.0.0.1:4700. */
1803
- serverUrl: z11.string().url(),
1993
+ serverUrl: z13.string().url(),
1804
1994
  /**
1805
1995
  * The runner's token, from `runner add`, or null for the one it saved when it was enrolled, or
1806
1996
  * to enroll it.
1807
1997
  */
1808
- token: z11.string().min(1).nullable(),
1998
+ token: z13.string().min(1).nullable(),
1809
1999
  /** The runner's own data directory: the board mirror, agent homes, repositories, worktrees. */
1810
- dataDir: z11.string().min(1),
2000
+ dataDir: z13.string().min(1),
1811
2001
  logLevel: LogLevelSchema,
1812
2002
  /** Turns this machine runs at once, or null for no limit. */
1813
- slots: z11.number().int().positive().nullable(),
1814
- clis: z11.array(CliKindSchema).min(1),
1815
- capabilities: z11.array(z11.string())
2003
+ slots: z13.number().int().positive().nullable(),
2004
+ clis: z13.array(CliKindSchema).min(1),
2005
+ capabilities: z13.array(z13.string())
1816
2006
  });
1817
2007
 
1818
2008
  // packages/shared/dist/version.js
1819
- var STELLARIS_VERSION = "0.3.21";
2009
+ var STELLARIS_VERSION = "0.3.23";
2010
+
2011
+ // packages/shared/dist/mentions.js
2012
+ import { parse, postprocess, preprocess } from "micromark";
2013
+ import { gfm } from "micromark-extension-gfm";
2014
+ var MENTION_PATTERN = /(^|[^\w@])@([a-z0-9][a-z0-9-]{0,31})(?![\w-])/g;
2015
+ var CODE = /* @__PURE__ */ new Set(["codeText", "codeFenced", "codeIndented"]);
2016
+ var SLOW = {
2017
+ disable: {
2018
+ null: [
2019
+ "wwwAutolink",
2020
+ "protocolAutolink",
2021
+ "emailAutolink",
2022
+ "gfmFootnoteCall",
2023
+ "gfmPotentialFootnoteCall"
2024
+ ]
2025
+ }
2026
+ };
2027
+ function codeRanges(markdown) {
2028
+ const shift = markdown.startsWith("\uFEFF") ? 1 : 0;
2029
+ const events = postprocess(parse({ extensions: [gfm(), SLOW] }).document().write(preprocess()(markdown, void 0, true)));
2030
+ return events.flatMap(([kind, token]) => kind === "enter" && CODE.has(token.type) ? [[token.start.offset + shift, token.end.offset + shift]] : []);
2031
+ }
2032
+ function addMentions(found, text) {
2033
+ for (const match of text.matchAll(MENTION_PATTERN)) {
2034
+ const name = match[2];
2035
+ if (name !== void 0) {
2036
+ found.add(name);
2037
+ }
2038
+ }
2039
+ }
2040
+ function mentionsIn(markdown) {
2041
+ if (markdown.search(MENTION_PATTERN) === -1) {
2042
+ return [];
2043
+ }
2044
+ const found = /* @__PURE__ */ new Set();
2045
+ let from = 0;
2046
+ for (const [start, end] of codeRanges(markdown)) {
2047
+ addMentions(found, markdown.slice(from, start));
2048
+ from = end;
2049
+ }
2050
+ addMentions(found, markdown.slice(from));
2051
+ return [...found].toSorted();
2052
+ }
2053
+ function mentionsAnywhere(text) {
2054
+ const found = /* @__PURE__ */ new Set();
2055
+ addMentions(found, text);
2056
+ return [...found].toSorted();
2057
+ }
1820
2058
 
1821
2059
  // packages/board-core/dist/errors.js
1822
2060
  var BoardError = class extends Error {
@@ -2459,33 +2697,59 @@ function filesOf(lines, status) {
2459
2697
  return [...files.values()];
2460
2698
  }
2461
2699
 
2700
+ // packages/board-core/dist/mentions.js
2701
+ import vm from "node:vm";
2702
+ var MENTION_PARSE_MS = 500;
2703
+ var context = vm.createContext({});
2704
+ var call = new vm.Script("work()");
2705
+ function within(work, ms) {
2706
+ let result;
2707
+ context["work"] = () => {
2708
+ result = work();
2709
+ };
2710
+ try {
2711
+ call.runInContext(context, { timeout: ms });
2712
+ } catch (error) {
2713
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
2714
+ return void 0;
2715
+ }
2716
+ throw error;
2717
+ } finally {
2718
+ context["work"] = void 0;
2719
+ }
2720
+ return result;
2721
+ }
2722
+ function postMentions(body) {
2723
+ return within(() => mentionsIn(body), MENTION_PARSE_MS) ?? mentionsAnywhere(body);
2724
+ }
2725
+
2462
2726
  // packages/board-core/dist/metrics.js
2463
- import { z as z12 } from "zod";
2464
- var TurnPayload = z12.object({
2465
- turnId: z12.string().optional(),
2466
- project: z12.string(),
2467
- thread: z12.string().optional(),
2468
- channel: z12.string().optional(),
2469
- trigger: z12.string()
2470
- });
2471
- var TaskPayload = z12.object({
2472
- taskId: z12.string(),
2473
- project: z12.string(),
2474
- from: z12.string().optional()
2475
- });
2476
- var MessagePayload = z12.object({
2477
- id: z12.string().optional(),
2478
- channel: z12.string(),
2479
- thread: z12.string().nullable().optional(),
2480
- mentions: z12.array(z12.string()).default([])
2481
- });
2482
- var SteeredPayload = z12.object({ messages: z12.array(z12.string()) });
2483
- var SignalPayload = z12.object({
2484
- kind: z12.string(),
2485
- key: z12.string(),
2486
- summary: z12.string(),
2487
- taskId: z12.string().optional(),
2488
- project: z12.string().optional()
2727
+ import { z as z14 } from "zod";
2728
+ var TurnPayload = z14.object({
2729
+ turnId: z14.string().optional(),
2730
+ project: z14.string(),
2731
+ thread: z14.string().optional(),
2732
+ channel: z14.string().optional(),
2733
+ trigger: z14.string()
2734
+ });
2735
+ var TaskPayload = z14.object({
2736
+ taskId: z14.string(),
2737
+ project: z14.string(),
2738
+ from: z14.string().optional()
2739
+ });
2740
+ var MessagePayload = z14.object({
2741
+ id: z14.string().optional(),
2742
+ channel: z14.string(),
2743
+ thread: z14.string().nullable().optional(),
2744
+ mentions: z14.array(z14.string()).default([])
2745
+ });
2746
+ var SteeredPayload = z14.object({ messages: z14.array(z14.string()) });
2747
+ var SignalPayload = z14.object({
2748
+ kind: z14.string(),
2749
+ key: z14.string(),
2750
+ summary: z14.string(),
2751
+ taskId: z14.string().optional(),
2752
+ project: z14.string().optional()
2489
2753
  });
2490
2754
  function median(values) {
2491
2755
  const sorted = values.toSorted((a, b) => a - b);
@@ -2774,6 +3038,59 @@ function computeMetrics(input) {
2774
3038
  };
2775
3039
  }
2776
3040
 
3041
+ // packages/board-core/dist/turn-history.js
3042
+ import { z as z15 } from "zod";
3043
+ var TurnHistoryPayloadSchema = z15.object({
3044
+ project: NameSchema,
3045
+ thread: UlidSchema.optional(),
3046
+ channel: NameSchema.optional(),
3047
+ trigger: z15.string().default("manual"),
3048
+ exitReason: z15.string().nullable().default(null),
3049
+ costUsd: z15.number().default(0),
3050
+ usage: UsageSchema.optional(),
3051
+ model: z15.string().nullable().default(null),
3052
+ summary: z15.string().nullable().default(null),
3053
+ error: z15.string().nullable().default(null),
3054
+ toolCalls: z15.number().int().nonnegative().optional(),
3055
+ turnId: UlidSchema.optional()
3056
+ });
3057
+ function tokenUse(events, window, now) {
3058
+ const span = METRICS_WINDOW_MS[window];
3059
+ const since = span === null ? null : now.getTime() - span;
3060
+ const rows = /* @__PURE__ */ new Map();
3061
+ for (const event of events) {
3062
+ if (event.type !== "turn.completed" && event.type !== "turn.failed") {
3063
+ continue;
3064
+ }
3065
+ if (since !== null && Date.parse(event.ts) < since) {
3066
+ continue;
3067
+ }
3068
+ const turn2 = TurnHistoryPayloadSchema.safeParse(event.payload);
3069
+ if (!turn2.success) {
3070
+ continue;
3071
+ }
3072
+ const row = rows.get(event.actor) ?? {
3073
+ agent: event.actor,
3074
+ turns: 0,
3075
+ withUsage: 0,
3076
+ usage: null
3077
+ };
3078
+ const usage = turn2.data.usage;
3079
+ rows.set(event.actor, {
3080
+ agent: event.actor,
3081
+ turns: row.turns + 1,
3082
+ withUsage: row.withUsage + (usage === void 0 ? 0 : 1),
3083
+ usage: usage === void 0 ? row.usage : row.usage === null ? usage : addUsage(row.usage, usage)
3084
+ });
3085
+ }
3086
+ return {
3087
+ window,
3088
+ since: since === null ? null : new Date(since).toISOString(),
3089
+ until: now.toISOString(),
3090
+ citizens: [...rows.values()].toSorted((a, b) => a.agent.localeCompare(b.agent))
3091
+ };
3092
+ }
3093
+
2777
3094
  // packages/board-core/dist/mutex.js
2778
3095
  var Mutex = class {
2779
3096
  tail = Promise.resolve();
@@ -2980,6 +3297,13 @@ var BoardPaths = class {
2980
3297
  eventLog() {
2981
3298
  return path5.join(this.events(), "log.jsonl");
2982
3299
  }
3300
+ /** Images citizens uploaded, by id: outside the projection, which every runner mirrors. */
3301
+ images() {
3302
+ return path5.join(this.dataDir, "images");
3303
+ }
3304
+ image(id) {
3305
+ return path5.join(this.images(), id);
3306
+ }
2983
3307
  state() {
2984
3308
  return path5.join(this.dataDir, "state");
2985
3309
  }
@@ -3015,41 +3339,27 @@ function namesList(names) {
3015
3339
  return names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names.at(-1) ?? ""}`;
3016
3340
  }
3017
3341
  var SKIPPED_IN_HOME = /* @__PURE__ */ new Set([".git", "turns", ".claude", ".codex"]);
3018
- var RunnerTokensSchema = z13.object({
3019
- runners: z13.record(z13.string(), z13.object({ tokenHash: z13.string().min(1) }))
3342
+ var RunnerTokensSchema = z16.object({
3343
+ runners: z16.record(z16.string(), z16.object({ tokenHash: z16.string().min(1) }))
3020
3344
  });
3021
- var SignInsSchema = z13.object({
3022
- signIns: z13.array(z13.object({
3023
- tokenHash: z13.string().min(1),
3024
- login: z13.string().min(1),
3345
+ var SignInsSchema = z16.object({
3346
+ signIns: z16.array(z16.object({
3347
+ tokenHash: z16.string().min(1),
3348
+ login: z16.string().min(1),
3025
3349
  /** The account's picture at the identity provider; sign-ins from before it have none. */
3026
- avatarUrl: z13.url().optional(),
3350
+ avatarUrl: z16.url().optional(),
3027
3351
  createdAt: IsoDateTimeSchema,
3028
3352
  expiresAt: IsoDateTimeSchema
3029
3353
  })).default([])
3030
3354
  });
3031
- var TurnHistoryPayloadSchema = z13.object({
3032
- project: NameSchema,
3033
- thread: UlidSchema.optional(),
3034
- channel: NameSchema.optional(),
3035
- trigger: z13.string().default("manual"),
3036
- exitReason: z13.string().nullable().default(null),
3037
- costUsd: z13.number().default(0),
3038
- usage: UsageSchema.optional(),
3039
- model: z13.string().nullable().default(null),
3040
- summary: z13.string().nullable().default(null),
3041
- error: z13.string().nullable().default(null),
3042
- toolCalls: z13.number().int().nonnegative().optional(),
3043
- turnId: UlidSchema.optional()
3044
- });
3045
- var TurnStartPayloadSchema = z13.object({ project: NameSchema, turnId: UlidSchema.optional() });
3046
- var CursorsSchema = z13.object({
3047
- digest: z13.string().nullable(),
3048
- scopes: z13.record(z13.string(), z13.string()).default({}),
3049
- threadsFrom: z13.record(z13.string(), z13.string().nullable()).default({}),
3050
- channelsFrom: z13.record(z13.string(), z13.string().nullable()).default({}),
3355
+ var TurnStartPayloadSchema = z16.object({ project: NameSchema, turnId: UlidSchema.optional() });
3356
+ var CursorsSchema = z16.object({
3357
+ digest: z16.string().nullable(),
3358
+ scopes: z16.record(z16.string(), z16.string()).default({}),
3359
+ threadsFrom: z16.record(z16.string(), z16.string().nullable()).default({}),
3360
+ channelsFrom: z16.record(z16.string(), z16.string().nullable()).default({}),
3051
3361
  /** How far a work role has read news, which goes to whichever of its general conversations reads first. */
3052
- news: z13.string().optional()
3362
+ news: z16.string().optional()
3053
3363
  });
3054
3364
  var NO_CURSORS = { digest: null, scopes: {}, threadsFrom: {}, channelsFrom: {} };
3055
3365
  function conversationKey(scope, part) {
@@ -3088,9 +3398,8 @@ function byId(a, b) {
3088
3398
  function turnTime(record) {
3089
3399
  return record.endedAt ?? record.startedAt;
3090
3400
  }
3091
- var PausedSchema = z13.object({ paused: z13.boolean() });
3401
+ var PausedSchema = z16.object({ paused: z16.boolean() });
3092
3402
  var DEFAULT_LEASE_MS = 30 * 60 * 1e3;
3093
- var MENTION_PATTERN = /(^|[^\w@])@([a-z0-9][a-z0-9-]{0,31})(?![\w-])/g;
3094
3403
  function withoutMentions(text) {
3095
3404
  return text.replaceAll(MENTION_PATTERN, "$1$2");
3096
3405
  }
@@ -3114,7 +3423,7 @@ var OPS_WAKE_TRIGGER = "ops_event";
3114
3423
  var FRONT_DESK_TRIGGER = "user_post";
3115
3424
  var OPS_CHANNELS = ["governance"];
3116
3425
  var FRONT_DESK_CHANNELS = [ASK_CHANNEL];
3117
- var AlignmentsSchema = z13.object({ applied: z13.array(z13.string()).default([]) });
3426
+ var AlignmentsSchema = z16.object({ applied: z16.array(z16.string()).default([]) });
3118
3427
  var FOLLOW_DECISIONS = "ops-readers-follow-decisions";
3119
3428
  var THREAD_RECORDS = "threads-as-records";
3120
3429
  var DIGEST_CURSORS = "digest-replaces-inbox";
@@ -3128,10 +3437,11 @@ var CHANNEL_CONVERSATIONS = "channel-conversations";
3128
3437
  var DASHBOARD_UPDATES = "dashboard-updates-granted";
3129
3438
  var CRON_VERBS = "cron-verbs-granted";
3130
3439
  var KNOWLEDGE_NOTICES = "knowledge-notices";
3131
- var KnowledgeEventSchema = z13.object({
3440
+ var IMAGE_UPLOADS = "image-uploads-granted";
3441
+ var KnowledgeEventSchema = z16.object({
3132
3442
  topic: NameSchema,
3133
3443
  project: NameSchema.nullable(),
3134
- replaced: z13.boolean().optional()
3444
+ replaced: z16.boolean().optional()
3135
3445
  });
3136
3446
  var CRON_VERB_NAMES = ["create_cron", "update_cron", "remove_cron"];
3137
3447
  var OLD_DASHBOARD_LINE = "Agents may edit this file. It is rendered by the board UI.";
@@ -3249,16 +3559,6 @@ function unanswered(messages) {
3249
3559
  }
3250
3560
  return waiting;
3251
3561
  }
3252
- function extractMentions(body) {
3253
- const found = /* @__PURE__ */ new Set();
3254
- for (const match of body.matchAll(MENTION_PATTERN)) {
3255
- const name = match[2];
3256
- if (name !== void 0) {
3257
- found.add(name);
3258
- }
3259
- }
3260
- return [...found].toSorted();
3261
- }
3262
3562
  function snippetAround(text, query, radius = 80) {
3263
3563
  const index = text.toLowerCase().indexOf(query.toLowerCase());
3264
3564
  if (index === -1) {
@@ -3273,6 +3573,7 @@ var Board = class _Board {
3273
3573
  mutex = new Mutex();
3274
3574
  /** The turns in flight, which the server hosting them supplies; none until it does. */
3275
3575
  turnsInFlight = () => [];
3576
+ imageSource = null;
3276
3577
  newId = monotonicFactory();
3277
3578
  events;
3278
3579
  actionCounts = /* @__PURE__ */ new Map();
@@ -3337,7 +3638,8 @@ var Board = class _Board {
3337
3638
  CHANNEL_CONVERSATIONS,
3338
3639
  DASHBOARD_UPDATES,
3339
3640
  CRON_VERBS,
3340
- KNOWLEDGE_NOTICES
3641
+ KNOWLEDGE_NOTICES,
3642
+ IMAGE_UPLOADS
3341
3643
  ].filter((name) => !applied.includes(name));
3342
3644
  if (pending.length === 0) {
3343
3645
  return;
@@ -3367,13 +3669,13 @@ var Board = class _Board {
3367
3669
  await this.openAsksChannel();
3368
3670
  }
3369
3671
  if (pending.includes(KNOWLEDGE_REMOVAL)) {
3370
- await this.grantBesideKnowledge("remove_knowledge");
3672
+ await this.grantBeside("write_knowledge", "remove_knowledge");
3371
3673
  }
3372
3674
  if (pending.includes(CHANNEL_CONVERSATIONS)) {
3373
3675
  await this.startChannelCursors();
3374
3676
  }
3375
3677
  if (pending.includes(DASHBOARD_UPDATES)) {
3376
- await this.grantBesideKnowledge("update_dashboard");
3678
+ await this.grantBeside("write_knowledge", "update_dashboard");
3377
3679
  await this.rewordDashboards();
3378
3680
  }
3379
3681
  if (pending.includes(CRON_VERBS)) {
@@ -3382,6 +3684,9 @@ var Board = class _Board {
3382
3684
  if (pending.includes(KNOWLEDGE_NOTICES)) {
3383
3685
  await this.noticeKnowledgeNotes();
3384
3686
  }
3687
+ if (pending.includes(IMAGE_UPLOADS)) {
3688
+ await this.grantBeside("post_message", "upload_image");
3689
+ }
3385
3690
  await writeJson(file, { applied: [...applied, ...pending] });
3386
3691
  }
3387
3692
  /**
@@ -3423,12 +3728,12 @@ var Board = class _Board {
3423
3728
  }
3424
3729
  }
3425
3730
  /**
3426
- * Grants `verb` once to every charter granting `write_knowledge`, the work roles' charters in
3427
- * practice; seed roles have it from their seed.
3731
+ * Grants `verb` once to every charter granting `beside`, the work roles' charters in practice;
3732
+ * seed roles have it from their seed.
3428
3733
  */
3429
- async grantBesideKnowledge(verb) {
3734
+ async grantBeside(beside, verb) {
3430
3735
  for (const charter of await this.listRoles()) {
3431
- if (charter.verbs.includes("write_knowledge") && !charter.verbs.includes(verb)) {
3736
+ if (charter.verbs.includes(beside) && !charter.verbs.includes(verb)) {
3432
3737
  await this.writeRoleUnlocked(USER_NAME, { ...charter, verbs: [...charter.verbs, verb] }, { verbsAdded: [verb] });
3433
3738
  }
3434
3739
  }
@@ -3586,7 +3891,7 @@ var Board = class _Board {
3586
3891
  }
3587
3892
  }
3588
3893
  for (const file of await listFiles(this.paths.tasks(slug))) {
3589
- const raw = await readMarkdown(path6.join(this.paths.tasks(slug), file), z13.record(z13.string(), z13.unknown()));
3894
+ const raw = await readMarkdown(path6.join(this.paths.tasks(slug), file), z16.record(z16.string(), z16.unknown()));
3590
3895
  const legacy = raw.data["thread"];
3591
3896
  if (legacy === void 0) {
3592
3897
  continue;
@@ -3664,7 +3969,7 @@ var Board = class _Board {
3664
3969
  * turns, so it follows nothing now.
3665
3970
  */
3666
3971
  async renameInboxCursors() {
3667
- const Legacy = z13.object({ inbox: z13.string().nullable() });
3972
+ const Legacy = z16.object({ inbox: z16.string().nullable() });
3668
3973
  for (const agent2 of await this.listAgents()) {
3669
3974
  const file = this.paths.agentCursors(agent2.name);
3670
3975
  if (await exists(file)) {
@@ -3710,7 +4015,7 @@ ${charter.purpose}
3710
4015
  */
3711
4016
  async alignSeedRole(seed) {
3712
4017
  const file = this.paths.role(seed.name);
3713
- const raw = await readMarkdown(file, z13.record(z13.string(), z13.unknown()));
4018
+ const raw = await readMarkdown(file, z16.record(z16.string(), z16.unknown()));
3714
4019
  const current = RoleCharterSchema.parse(raw.data);
3715
4020
  const verbsAdded = seed.verbs.filter((verb) => !current.verbs.includes(verb));
3716
4021
  const fieldsAligned = RoleCharterSchema.keyof().options.filter((key) => key === "verbs" || seed[key] === void 0 ? false : seed.name === USER_ROLE ? JSON.stringify(seed[key]) !== JSON.stringify(current[key]) : !(key in raw.data));
@@ -4027,7 +4332,7 @@ ${charter.purpose}
4027
4332
  /** The society's norms, the one knowledge topic every turn loads. Empty until the steward writes it. */
4028
4333
  async readSocietyNorms() {
4029
4334
  const file = this.paths.societyKnowledgeFile("norms");
4030
- return await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
4335
+ return await exists(file) ? (await readMarkdown(file, z16.record(z16.string(), z16.unknown()))).body : "";
4031
4336
  }
4032
4337
  /** Knowledge topics of a project, or of the society when the project is null. */
4033
4338
  async listKnowledge(project2) {
@@ -4047,7 +4352,7 @@ ${charter.purpose}
4047
4352
  */
4048
4353
  async knowledgeAt(file, project2, topic) {
4049
4354
  const { data, body } = await readLooseMarkdown(file);
4050
- const revision = z13.number().int().min(0).safeParse(data["revision"]);
4355
+ const revision = z16.number().int().min(0).safeParse(data["revision"]);
4051
4356
  const updatedBy = NameSchema.safeParse(data["updatedBy"]);
4052
4357
  const updatedAt = IsoDateTimeSchema.safeParse(data["updatedAt"]);
4053
4358
  return {
@@ -4380,8 +4685,11 @@ ${DASHBOARD_LINE}
4380
4685
  throw new BoardError("NOT_FOUND", `runner ${name} not found`);
4381
4686
  }
4382
4687
  const doc = await readMarkdown(file, RunnerSchema);
4688
+ const { gitIdentityMissing: reported, ...rest } = doc.data;
4689
+ const gitIdentityMissing = patch.gitIdentityMissing === void 0 ? reported : patch.gitIdentityMissing ?? void 0;
4383
4690
  const next = RunnerSchema.parse({
4384
- ...doc.data,
4691
+ ...rest,
4692
+ ...gitIdentityMissing === void 0 ? {} : { gitIdentityMissing: [...gitIdentityMissing] },
4385
4693
  ...patch.status === void 0 ? {} : { status: patch.status },
4386
4694
  ...patch.os === void 0 ? {} : { os: patch.os },
4387
4695
  ...patch.clis === void 0 ? {} : { clis: [...patch.clis] },
@@ -4409,6 +4717,36 @@ ${DASHBOARD_LINE}
4409
4717
  return next;
4410
4718
  });
4411
4719
  }
4720
+ /**
4721
+ * Records a CLI's plan quota as a runner reported it, from a turn's stream or a read, merged over
4722
+ * what the record held. The record is written on every report, so it says when the numbers were
4723
+ * last seen; `runner.quota` is appended only when what a reader would see changes, since every
4724
+ * turn reports.
4725
+ */
4726
+ async recordQuota(name, cli, quota) {
4727
+ return this.mutex.run(async () => {
4728
+ const file = this.paths.runner(name);
4729
+ if (!await exists(file)) {
4730
+ throw new BoardError("NOT_FOUND", `runner ${name} not found`);
4731
+ }
4732
+ const doc = await readMarkdown(file, RunnerSchema);
4733
+ const previous = doc.data.quotas?.[cli];
4734
+ const merged = mergeQuota(previous, quota);
4735
+ const next = RunnerSchema.parse({
4736
+ ...doc.data,
4737
+ quotas: { ...doc.data.quotas, [cli]: merged }
4738
+ });
4739
+ await writeMarkdown(file, next, doc.body);
4740
+ if (quotaChanged(previous, merged)) {
4741
+ await this.events.append("runner.quota", SYSTEM_ACTOR.name, {
4742
+ name,
4743
+ cli,
4744
+ status: merged.status
4745
+ });
4746
+ }
4747
+ return next;
4748
+ });
4749
+ }
4412
4750
  /**
4413
4751
  * Logs an operations signal as an `ops.signal` event. No channel carries it: the scheduler wakes
4414
4752
  * its readers from the event, the runner lists it in their prompts, and the interface's log shows
@@ -4977,6 +5315,60 @@ ${current.body}`);
4977
5315
  return updated;
4978
5316
  });
4979
5317
  }
5318
+ /**
5319
+ * Keeps an image a citizen's turn names by its path, read on the runner the turn runs on, which
5320
+ * refuses a file outside the turn's worktree, the citizen's home, and the system temp folder.
5321
+ * Only the bytes of a PNG, JPEG, GIF, or WebP of at most `IMAGE_LIMIT_BYTES` are kept, checked
5322
+ * here again whatever the runner said, under a new id outside the projection, so no runner
5323
+ * mirrors them and no prompt carries them: a post shows the image by its `image:` address.
5324
+ */
5325
+ async uploadImage(actor, input) {
5326
+ const args = VerbInputs.upload_image.parse(input);
5327
+ await this.authorize(actor, "upload_image");
5328
+ if (actor.scope === void 0) {
5329
+ throw new BoardError("INVALID_STATE", "upload_image reads the file on the machine a turn runs on, so only a turn can call it");
5330
+ }
5331
+ if (this.imageSource === null) {
5332
+ throw new BoardError("NOT_FOUND", "this board has no runners to read an image from");
5333
+ }
5334
+ const bytes = await this.imageSource(actor, args.path);
5335
+ const type = imageTypeOf(bytes);
5336
+ if (type === null) {
5337
+ throw new BoardError("VALIDATION", `${args.path} is not a PNG, JPEG, GIF, or WebP image by its first bytes`);
5338
+ }
5339
+ if (bytes.byteLength > IMAGE_LIMIT_BYTES) {
5340
+ throw new BoardError("VALIDATION", `${args.path} is ${bytes.byteLength} bytes, over the ${IMAGE_LIMIT_BYTES} an image may be`);
5341
+ }
5342
+ return this.mutex.run(async () => {
5343
+ const id = this.newId();
5344
+ await writeFileAtomic(this.paths.image(id), bytes);
5345
+ await this.events.append("image.uploaded", actor.name, {
5346
+ id,
5347
+ type,
5348
+ size: bytes.byteLength
5349
+ });
5350
+ const name = path6.basename(args.path).replaceAll(/[[\]\\\r\n]/g, "");
5351
+ return {
5352
+ id,
5353
+ type,
5354
+ size: bytes.byteLength,
5355
+ markdown: `![${name}](${IMAGE_SCHEME}${id})`
5356
+ };
5357
+ });
5358
+ }
5359
+ /** An uploaded image's bytes and kind, which its first bytes tell. */
5360
+ async readImage(id) {
5361
+ const file = this.paths.image(UlidSchema.parse(id));
5362
+ if (!await exists(file)) {
5363
+ throw new BoardError("NOT_FOUND", `image ${id} not found`);
5364
+ }
5365
+ const bytes = await readFile4(file);
5366
+ const type = imageTypeOf(bytes);
5367
+ if (type === null) {
5368
+ throw new BoardError("NOT_FOUND", `image ${id} is not an image the board shows`);
5369
+ }
5370
+ return { type, bytes };
5371
+ }
4980
5372
  /** Every cron, ended ones included, oldest first. */
4981
5373
  async listCrons() {
4982
5374
  const crons = [];
@@ -5368,6 +5760,13 @@ ${current.body}`);
5368
5760
  watchTurns(source) {
5369
5761
  this.turnsInFlight = source;
5370
5762
  }
5763
+ /**
5764
+ * Where `upload_image` reads a file from: the runner the calling turn runs on, through the server
5765
+ * that hosts the runners. Without one, as in the admin CLI, nothing can be uploaded.
5766
+ */
5767
+ readImagesWith(source) {
5768
+ this.imageSource = source;
5769
+ }
5371
5770
  /**
5372
5771
  * Refuses to end conversations while another citizen's turn is in one of them: `cut` picks the
5373
5772
  * turns that would lose theirs. The caller's own turn is exempt, since ending a conversation
@@ -6197,6 +6596,8 @@ ${rationale}`}`);
6197
6596
  return this.updateCron(actor, VerbInputs.update_cron.parse(input));
6198
6597
  case "remove_cron":
6199
6598
  return this.removeCron(actor, VerbInputs.remove_cron.parse(input));
6599
+ case "upload_image":
6600
+ return this.uploadImage(actor, VerbInputs.upload_image.parse(input));
6200
6601
  default:
6201
6602
  throw new BoardError("VALIDATION", `unknown verb ${String(verb)}`);
6202
6603
  }
@@ -6243,7 +6644,7 @@ ${rationale}`}`);
6243
6644
  }
6244
6645
  async readAgentRoleBody(agent2) {
6245
6646
  const file = this.paths.agentRole(agent2);
6246
- return await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
6647
+ return await exists(file) ? (await readMarkdown(file, z16.record(z16.string(), z16.unknown()))).body : "";
6247
6648
  }
6248
6649
  /** The steps of one finished turn, as the runner handed them over when it ended. */
6249
6650
  /**
@@ -6274,6 +6675,14 @@ ${rationale}`}`);
6274
6675
  }
6275
6676
  return computeMetrics({ events, tasks, citizens, actions, activeSignals, window, now });
6276
6677
  }
6678
+ /**
6679
+ * Each citizen's finished turns over a window and the tokens of those that counted them, from
6680
+ * the event log alone, which holds every turn's end whatever runner it ran on.
6681
+ */
6682
+ async tokenUse(window) {
6683
+ const events = await this.events.readSince(null, Number.MAX_SAFE_INTEGER);
6684
+ return tokenUse(events, window, this.now());
6685
+ }
6277
6686
  /** The board actions a finished turn took, from its transcript; null when it kept none. */
6278
6687
  async turnActions(agent2, turnId) {
6279
6688
  const known = this.actionCounts.get(turnId);
@@ -6643,7 +7052,7 @@ ${rationale}`}`);
6643
7052
  return { project: slug, revision: 0, body: "" };
6644
7053
  }
6645
7054
  const { data, body } = await readLooseMarkdown(file);
6646
- const revision = z13.number().int().min(0).safeParse(data["revision"]);
7055
+ const revision = z16.number().int().min(0).safeParse(data["revision"]);
6647
7056
  const updatedBy = NameSchema.safeParse(data["updatedBy"]);
6648
7057
  const updatedAt = IsoDateTimeSchema.safeParse(data["updatedAt"]);
6649
7058
  return {
@@ -6917,7 +7326,7 @@ ${charter.purpose}
6917
7326
 
6918
7327
  ${charter.purpose}
6919
7328
 
6920
- Route every lesson with one question: about me, my craft, or the user, it goes in memory/core.md; about this codebase, it goes in the project's knowledge directory; something everyone should know, post it.
7329
+ Route every lesson with one question: about me, my craft, or the user, it goes in memory/core.md; about how this codebase behaves, it goes in the repo's AGENTS.md or README; about this project as a place (runners, access, flow, where results live), it goes in the project's knowledge; something everyone should know, post it.
6921
7330
  ` + (seed.length === 0 ? "" : `
6922
7331
  ${SEED_INSTRUCTIONS_HEADING}
6923
7332
 
@@ -6934,7 +7343,7 @@ ${seed}
6934
7343
  continue;
6935
7344
  }
6936
7345
  const file = this.paths.agentRole(agent2.name);
6937
- const body = await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
7346
+ const body = await exists(file) ? (await readMarkdown(file, z16.record(z16.string(), z16.unknown()))).body : "";
6938
7347
  const at = body.indexOf(`
6939
7348
  ${SEED_INSTRUCTIONS_HEADING}
6940
7349
  `);
@@ -7772,7 +8181,7 @@ ${charter.purpose}
7772
8181
  id: this.newId(),
7773
8182
  ...head,
7774
8183
  ts: this.now().toISOString(),
7775
- mentions: extractMentions(body)
8184
+ mentions: postMentions(body)
7776
8185
  });
7777
8186
  const { author } = head;
7778
8187
  await writeMarkdown(this.paths.messageFile(dir, frontmatter.id, author), frontmatter, body);