@bendyline/gezel 1.0.5 → 1.0.7

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.
@@ -1,5 +1,5 @@
1
1
  // src/schemas/craftbook.ts
2
- import { z as z5 } from "zod";
2
+ import { z as z6 } from "zod";
3
3
 
4
4
  // src/roles/tier.ts
5
5
  var MODEL_TIER_ORDER = [
@@ -34,7 +34,9 @@ var NamedScriptCapabilitySchema = z2.enum([
34
34
  "tasks.read",
35
35
  "tasks.write",
36
36
  "memory.read",
37
- "memory.write"
37
+ "memory.write",
38
+ "index.read",
39
+ "index.refresh"
38
40
  ]);
39
41
  var CredentialCapabilitySchema = z2.string().regex(
40
42
  /^credential:[a-zA-Z][\w.:-]*$/,
@@ -428,7 +430,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
428
430
  z3.object({
429
431
  kind: z3.literal("totalMinBytes"),
430
432
  files: z3.array(z3.string().min(1)).min(1),
431
- bytes: z3.number().int().positive()
433
+ bytes: z3.number().int().positive(),
434
+ artifact: z3.boolean().optional()
432
435
  }),
433
436
  /**
434
437
  * At least `min` workspace files with one of `ext` exist (e.g. ≥3 images).
@@ -445,13 +448,15 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
445
448
  ext: z3.array(z3.string().min(1)).min(1),
446
449
  min: z3.number().int().positive(),
447
450
  dir: z3.string().optional(),
448
- verifyImageBytes: z3.boolean().optional()
451
+ verifyImageBytes: z3.boolean().optional(),
452
+ artifact: z3.boolean().optional()
449
453
  }),
450
454
  /** Inline `<style>` + linked `.css` in `file` (default index.html) clears `bytes`. */
451
455
  z3.object({
452
456
  kind: z3.literal("cssMinBytes"),
453
457
  bytes: z3.number().int().positive(),
454
- file: z3.string().optional()
458
+ file: z3.string().optional(),
459
+ artifact: z3.boolean().optional()
455
460
  }),
456
461
  /** A named content sniff passes on `file` (workspace, or the artifacts drawer when `artifact`). */
457
462
  z3.object({
@@ -466,7 +471,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
466
471
  file: z3.string().min(1),
467
472
  path: z3.string().min(1),
468
473
  value: GateJsonScalarSchema,
469
- label: z3.string().min(1).optional()
474
+ label: z3.string().min(1).optional(),
475
+ artifact: z3.boolean().optional()
470
476
  }),
471
477
  /** A CSV file has the expected header/row shape and optional picklist values. */
472
478
  z3.object({
@@ -476,7 +482,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
476
482
  exactColumns: z3.array(z3.string().min(1)).min(1).optional(),
477
483
  minRows: z3.number().int().nonnegative().optional(),
478
484
  consistentColumns: z3.boolean().optional(),
479
- allowedValues: z3.record(z3.string(), z3.array(z3.string().min(1)).min(1)).optional()
485
+ allowedValues: z3.record(z3.string(), z3.array(z3.string().min(1)).min(1)).optional(),
486
+ artifact: z3.boolean().optional()
480
487
  }),
481
488
  /** `file` matches `pattern` (regex), in the workspace or the artifacts drawer when `artifact`. */
482
489
  z3.object({
@@ -511,7 +518,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
511
518
  label: z3.string().min(1).optional()
512
519
  })
513
520
  ).min(1),
514
- flags: z3.string().optional()
521
+ flags: z3.string().optional(),
522
+ artifact: z3.boolean().optional()
515
523
  }),
516
524
  /**
517
525
  * Every inline `<script>` body in `file` (default index.html) parses as
@@ -523,7 +531,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
523
531
  */
524
532
  z3.object({
525
533
  kind: z3.literal("jsParses"),
526
- file: z3.string().optional()
534
+ file: z3.string().optional(),
535
+ artifact: z3.boolean().optional()
527
536
  }),
528
537
  /**
529
538
  * An in-process, zero-spawn static gate for an HTML deliverable: the document
@@ -534,7 +543,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
534
543
  */
535
544
  z3.object({
536
545
  kind: z3.literal("htmlLint"),
537
- file: z3.string().min(1)
546
+ file: z3.string().min(1),
547
+ artifact: z3.boolean().optional()
538
548
  }),
539
549
  /**
540
550
  * Named `node:` imports in `file` resolve to real builtin exports, and a
@@ -547,7 +557,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
547
557
  */
548
558
  z3.object({
549
559
  kind: z3.literal("esmImports"),
550
- file: z3.string().min(1)
560
+ file: z3.string().min(1),
561
+ artifact: z3.boolean().optional()
551
562
  }),
552
563
  /**
553
564
  * `file` parses as source code. For `.ts/.tsx/.js/.mjs/.cjs/.jsx` the
@@ -559,7 +570,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
559
570
  */
560
571
  z3.object({
561
572
  kind: z3.literal("sourceParses"),
562
- file: z3.string().min(1)
573
+ file: z3.string().min(1),
574
+ artifact: z3.boolean().optional()
563
575
  }),
564
576
  /**
565
577
  * The first Markdown table in `file` has the required header set and
@@ -591,6 +603,18 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
591
603
  minRows: z3.number().int().nonnegative().optional(),
592
604
  uniqueBy: z3.string().optional(),
593
605
  format: z3.enum(["json", "csv", "auto"]).optional(),
606
+ /**
607
+ * Accept fields beyond `fields` (default false — an exact field set).
608
+ *
609
+ * Strict is right for a locked-schema data deliverable, where an
610
+ * unexpected key means the writer misunderstood the contract. It is
611
+ * wrong for a REGISTER — a findings list, a drift list — where extra
612
+ * context (a cwe, a title, an id) is a better deliverable, not a
613
+ * broken one, and rejecting it grades schema-guessing instead of the
614
+ * work. The underlying `recordSchema` check has always supported this;
615
+ * only the declarative gate could not say it.
616
+ */
617
+ allowExtraFields: z3.boolean().optional(),
594
618
  artifact: z3.boolean().optional()
595
619
  }),
596
620
  /**
@@ -601,6 +625,12 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
601
625
  * dependency-free files can run (node built-ins ok; the sandbox has no
602
626
  * npm tree): a `node:test`/`assert` file exits nonzero on failure,
603
627
  * which is exactly the contract.
628
+ *
629
+ * Alone among the file-reading checks this takes no `artifact` flag: it
630
+ * does not read through the swappable gate reader at all, it hands the
631
+ * path to the sandbox executor, which resolves against the workspace.
632
+ * Accepting the flag would parse fine and then silently execute the
633
+ * wrong tree — the exact failure mode the flag exists to prevent.
604
634
  */
605
635
  z3.object({
606
636
  kind: z3.literal("nodeRuns"),
@@ -630,17 +660,51 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
630
660
  * `sourcePath` is non-empty, an exact successful `read_file` of that path
631
661
  * qualifies; otherwise one of `tools` must have completed against an
632
662
  * external source. The runtime supplies the live tool-call evidence.
663
+ * `externalOptional` softens only the no-sourcePath branch: authored local
664
+ * sources remain mandatory, while a topic-only workflow may recommend web
665
+ * research without becoming impossible under an offline security policy.
633
666
  */
634
667
  z3.object({
635
668
  kind: z3.literal("researchEvidence"),
636
669
  sourcePath: z3.string().optional(),
637
670
  tools: z3.array(z3.string().min(1)).min(1),
638
- minSuccessful: z3.number().int().positive().optional()
671
+ minSuccessful: z3.number().int().positive().optional(),
672
+ externalOptional: z3.boolean().optional()
673
+ }),
674
+ /**
675
+ * Require a real command run during this activation, verified against the
676
+ * service-written run receipts (`workspace.script.run` / `workspace.npx.run`
677
+ * history events — the model cannot forge them, and the gate never
678
+ * executes anything itself). Exactly one of `script` (a package.json
679
+ * script name) or `bin` (an npx binary) names the command; `args` must
680
+ * match the receipt's extra args exactly (default: argless). `expect`
681
+ * judges the LATEST matching run's exit code — `'fail'` is the
682
+ * red-before-green half of a repro-first fix (the failing test must
683
+ * actually fail), `'pass'` is the suite-green proof after it. A timed-out
684
+ * run satisfies neither. On a drafting task the project's commands run
685
+ * against the unmodified tree, so `onDraft: 'defer'` (the default)
686
+ * approves with an explicit "execution deferred until Apply" note instead
687
+ * of demanding a receipt that would be a lie; `'require'` hard-blocks
688
+ * instead. Books standardize on the argless canonical invocation because
689
+ * command approvals pin one invocation hash per script name.
690
+ */
691
+ z3.object({
692
+ kind: z3.literal("commandEvidence"),
693
+ script: z3.string().min(1).optional(),
694
+ bin: z3.string().min(1).optional(),
695
+ args: z3.array(z3.string()).optional(),
696
+ expect: z3.enum(["pass", "fail"]),
697
+ minRuns: z3.number().int().positive().optional(),
698
+ onDraft: z3.enum(["defer", "require"]).optional(),
699
+ label: z3.string().min(1).optional()
639
700
  }),
640
701
  /**
641
702
  * A PR-review coverage ledger must name every changed path materialized in
642
- * a connector corpus. The ledger is workspace JSON (`reviewedFiles` by
643
- * default); the source records live in the read-only artifacts drawer and
703
+ * a connector corpus. The ledger is JSON (`reviewedFiles` by default) in
704
+ * the workspace, or in the artifacts drawer when `artifact` — a review of
705
+ * a read-only checkout can only write the drawer, so omitting the flag
706
+ * here made the whole Pull Request Review book unsatisfiable on any
707
+ * writes-off project. The source records always live in the drawer and
644
708
  * carry their authoritative path in frontmatter. This prevents a polished
645
709
  * report from passing after only the first context-sized prefix was read.
646
710
  */
@@ -649,7 +713,49 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
649
713
  file: z3.string().min(1),
650
714
  corpusDir: z3.string().min(1),
651
715
  reviewedField: z3.string().min(1).optional(),
652
- recordField: z3.string().min(1).optional()
716
+ recordField: z3.string().min(1).optional(),
717
+ artifact: z3.boolean().optional(),
718
+ /**
719
+ * JSON array of the exact changed paths THIS ledger must cover, as a
720
+ * string — narrows the check from the whole corpus to one slice of it.
721
+ * Written for declarative fanout: a child reviewing batch 2 of 3 gets
722
+ * `"{{paths}}"` from its per-item context and is gated on its own 25
723
+ * files, not on the 68 nobody handed it. Without this a batch child
724
+ * can never pass its own gate, and coverage can only be enforced
725
+ * after a merge — far downstream of the child that under-delivered.
726
+ *
727
+ * Fails closed on anything that is not a JSON array of strings,
728
+ * including an uninterpolated `{{…}}` token: a mis-scoped coverage
729
+ * check that silently widens back to the full corpus would reject
730
+ * every child forever with a verdict none of them can act on.
731
+ */
732
+ expectPaths: z3.string().min(1).optional()
733
+ }),
734
+ /**
735
+ * A fanout-input batch file must reproduce a connector corpus manifest's own
736
+ * batch array exactly — same count, same ordinals, same paths, in order.
737
+ *
738
+ * The upstream sibling of {@link corpusCoverage}: that check proves the work
739
+ * covered the corpus, this one proves the *plan* did. A batch file is the
740
+ * spawn host's `overFile`, so a batch dropped here is never reviewed by
741
+ * anyone and never appears in any coverage ledger. `json-valid` + `minBytes`
742
+ * cannot see it: a batch array truncated at the model's output cap is still
743
+ * syntactically perfect JSON. Wild-caught on a 509-file PR whose scope step
744
+ * published 10 of 21 batches — 259 files silently out of scope, and the run
745
+ * then deadlocked eight attempts deep in the downstream merge gate, which is
746
+ * the wrong place to learn it and the one place that cannot fix it.
747
+ */
748
+ z3.object({
749
+ kind: z3.literal("corpusBatches"),
750
+ file: z3.string().min(1),
751
+ corpusDir: z3.string().min(1),
752
+ /** Filename suffix identifying the manifest inside the corpus. */
753
+ manifestSuffix: z3.string().min(1).optional(),
754
+ /** Manifest field holding the authoritative batch array. */
755
+ itemsField: z3.string().min(1).optional(),
756
+ /** Manifest field holding the total item count the batches must cover. */
757
+ totalField: z3.string().min(1).optional(),
758
+ artifact: z3.boolean().optional()
653
759
  }),
654
760
  /**
655
761
  * The H1 slide titles in `file` must match the numbered slide headings in
@@ -662,7 +768,8 @@ var GateCheckSchema = z3.discriminatedUnion("kind", [
662
768
  file: z3.string().min(1),
663
769
  outlineFile: z3.string().min(1),
664
770
  /** Read outlineFile from artifacts while file uses the check's primary surface. */
665
- outlineArtifact: z3.boolean().optional()
771
+ outlineArtifact: z3.boolean().optional(),
772
+ artifact: z3.boolean().optional()
666
773
  }),
667
774
  /**
668
775
  * Named facts in `file` must come from authorized sources: each fact
@@ -881,16 +988,39 @@ var HookResultSchema = z4.object({
881
988
  message: z4.string().optional()
882
989
  });
883
990
 
991
+ // src/schemas/retrieval.ts
992
+ import { z as z5 } from "zod";
993
+ var RETRIEVAL_MODES = ["off", "lean", "balanced", "deep"];
994
+ var RetrievalModeSchema = z5.enum(RETRIEVAL_MODES);
995
+ var RETRIEVAL_SOURCES = [
996
+ "workspace",
997
+ "artifacts",
998
+ "project-memory",
999
+ "gezel-memory",
1000
+ "shared",
1001
+ // Installed knowledge catalogs (read-only reference corpora — .gezk).
1002
+ // Scoped by the user's registry + the project's knowledgeCatalogs policy.
1003
+ "knowledge"
1004
+ ];
1005
+ var RetrievalSourceSchema = z5.enum(RETRIEVAL_SOURCES);
1006
+ var RetrievalPolicySchema = z5.object({
1007
+ mode: RetrievalModeSchema,
1008
+ /** Hard prompt budget after the mode preset and context-window clamp. */
1009
+ maxTokens: z5.number().int().min(0).max(16e3).optional(),
1010
+ /** Absent means every corpus appropriate to the current session. */
1011
+ sources: z5.array(RetrievalSourceSchema).min(1).optional()
1012
+ });
1013
+
884
1014
  // src/schemas/craftbook.ts
885
- var CraftbookBranchSchema = z5.object({
1015
+ var CraftbookBranchSchema = z6.object({
886
1016
  when: ScriptOutputPredicateSchema,
887
- goto: z5.string()
1017
+ goto: z6.string()
888
1018
  });
889
- var AdvanceWhenSchema = z5.object({
1019
+ var AdvanceWhenSchema = z6.object({
890
1020
  /** Workspace-relative deliverable whose presence signals "this step is done". */
891
- file: z5.string().min(1),
1021
+ file: z6.string().min(1),
892
1022
  /** Liveness floor in bytes (guards against an empty/stub file). Default 1. */
893
- minBytes: z5.number().int().positive().optional(),
1023
+ minBytes: z6.number().int().positive().optional(),
894
1024
  /**
895
1025
  * Named content check the runtime runs before advancing. `html-complete`
896
1026
  * = a non-truncated HTML doc (balanced `<script>` tags + a closing
@@ -911,7 +1041,7 @@ var AdvanceWhenSchema = z5.object({
911
1041
  * `insert_at_marker` targeting `file` this turn). The `sniff`/`minBytes`
912
1042
  * floor still applies on top. Absent → legacy "exists is enough".
913
1043
  */
914
- requireChange: z5.boolean().optional(),
1044
+ requireChange: z6.boolean().optional(),
915
1045
  /**
916
1046
  * Resolve `file` against the project's ARTIFACTS drawer
917
1047
  * (`read_artifact` / `write_artifact`) instead of the shipped workspace.
@@ -921,28 +1051,28 @@ var AdvanceWhenSchema = z5.object({
921
1051
  * deliverable is gated for size/shape exactly like a workspace one.
922
1052
  * Absent → workspace (the default).
923
1053
  */
924
- artifact: z5.boolean().optional(),
1054
+ artifact: z6.boolean().optional(),
925
1055
  /** Step to activate on the signal. Defaults to `next`; must resolve like `next`. */
926
- goto: z5.string().optional()
1056
+ goto: z6.string().optional()
927
1057
  });
928
- var CraftbookStepInputSchema = z5.object({
1058
+ var CraftbookStepInputSchema = z6.object({
929
1059
  /** Path relative to the selected drawer's root. */
930
- file: z5.string().min(1).describe("Path relative to the selected drawer root."),
1060
+ file: z6.string().min(1).describe("Path relative to the selected drawer root."),
931
1061
  /** Read from the artifacts drawer instead of the project workspace. */
932
- artifact: z5.boolean().optional().describe("True for the artifacts drawer; false/omitted for the project workspace.")
1062
+ artifact: z6.boolean().optional().describe("True for the artifacts drawer; false/omitted for the project workspace.")
933
1063
  });
934
- var ModelTierSchema = z5.enum(MODEL_TIER_ORDER);
935
- var CraftbookStepSchema = z5.object({
936
- id: z5.string().min(1),
937
- name: z5.string().min(1),
938
- description: z5.string().optional(),
1064
+ var ModelTierSchema = z6.enum(MODEL_TIER_ORDER);
1065
+ var CraftbookStepSchema = z6.object({
1066
+ id: z6.string().min(1),
1067
+ name: z6.string().min(1),
1068
+ description: z6.string().optional(),
939
1069
  /**
940
1070
  * Per-step prompt body. In bundled/local catalog form, this is loaded
941
1071
  * from the per-step `prompt.md` file (when the version manifest carries
942
1072
  * a separate file) and inlined at resolution time.
943
1073
  */
944
- prompt: z5.string().optional(),
945
- suggestedGezelId: z5.string().optional(),
1074
+ prompt: z6.string().optional(),
1075
+ suggestedGezelId: z6.string().optional(),
946
1076
  /**
947
1077
  * Free-form role hint ("reviewer", "developer", "designer"). When set
948
1078
  * and `suggestedGezelId` / `assignee` are both absent at step
@@ -953,7 +1083,7 @@ var CraftbookStepSchema = z5.object({
953
1083
  * specific gezel id. Override by setting `assignee` or
954
1084
  * `suggestedGezelId` at task create time.
955
1085
  */
956
- suggestedRole: z5.string().optional(),
1086
+ suggestedRole: z6.string().optional(),
957
1087
  /**
958
1088
  * Minimum model tier to run this step unsupervised. Overrides the
959
1089
  * `suggestedRole`'s registry floor (roles/registry.ts) when set;
@@ -962,6 +1092,8 @@ var CraftbookStepSchema = z5.object({
962
1092
  * that clears the floor runs the step.
963
1093
  */
964
1094
  capabilityFloor: ModelTierSchema.optional(),
1095
+ /** Per-phase indexed-context policy; overrides gezel and install defaults. */
1096
+ retrieval: RetrievalPolicySchema.optional(),
965
1097
  assignee: TaskAssigneeSchema.optional(),
966
1098
  /** Setup scripts, run in order when the step activates. Single ref = legacy shape. */
967
1099
  onEnter: ScriptRefListSchema.optional(),
@@ -972,16 +1104,16 @@ var CraftbookStepSchema = z5.object({
972
1104
  */
973
1105
  onExit: ScriptRefListSchema.optional(),
974
1106
  /** Required file inputs for this step, in the order they should be opened. */
975
- consumes: z5.array(CraftbookStepInputSchema).min(1).optional().describe(
1107
+ consumes: z6.array(CraftbookStepInputSchema).min(1).optional().describe(
976
1108
  "Files this step must open before working. Artifact inputs also require an explicit `read_artifact` call in the step prompt."
977
1109
  ),
978
1110
  /** See {@link AdvanceWhenSchema}. */
979
1111
  advanceWhen: AdvanceWhenSchema.optional(),
980
1112
  /** The end-of-step decision. See {@link StepGateSchema} (current) / {@link GateSpecSchema} (legacy). */
981
1113
  gate: StepGateUnionSchema.optional(),
982
- next: z5.string().optional(),
983
- branches: z5.array(CraftbookBranchSchema).optional(),
984
- terminal: z5.boolean().optional(),
1114
+ next: z6.string().optional(),
1115
+ branches: z6.array(CraftbookBranchSchema).optional(),
1116
+ terminal: z6.boolean().optional(),
985
1117
  /**
986
1118
  * Marks the parent step that triggers a declarative per-item fanout
987
1119
  * (see {@link CraftbookSpawnSchema}). When this step activates on a
@@ -989,19 +1121,19 @@ var CraftbookStepSchema = z5.object({
989
1121
  * JSON array on its declared surface and spawns one child task per item — no model
990
1122
  * tool call. Inert unless the craftbook also declares `spawn`.
991
1123
  */
992
- spawnFanout: z5.boolean().optional()
1124
+ spawnFanout: z6.boolean().optional()
993
1125
  });
994
- var CraftbookSpawnSchema = z5.object({
1126
+ var CraftbookSpawnSchema = z6.object({
995
1127
  /** Surface-relative JSON file the parent produces; its array drives the fanout. */
996
- overFile: z5.string().min(1),
1128
+ overFile: z6.string().min(1),
997
1129
  /** Read overFile from the artifacts drawer instead of the project workspace. */
998
- overArtifact: z5.boolean().optional(),
1130
+ overArtifact: z6.boolean().optional(),
999
1131
  /** Dotted path to the array inside `overFile`. Absent → the file itself is the array. */
1000
- itemsPath: z5.string().optional(),
1132
+ itemsPath: z6.string().optional(),
1001
1133
  /** Entry step id of the child template. Defaults to the first `steps` entry. */
1002
- entryStepId: z5.string().optional(),
1134
+ entryStepId: z6.string().optional(),
1003
1135
  /** The per-child step template — same shape as a craftbook's own steps. */
1004
- steps: z5.array(CraftbookStepSchema).min(1)
1136
+ steps: z6.array(CraftbookStepSchema).min(1)
1005
1137
  });
1006
1138
  function validateCraftbookGraph(cb) {
1007
1139
  const problems = [];
@@ -1056,70 +1188,88 @@ function validateCraftbookGraph(cb) {
1056
1188
  function refineCraftbook(cb, ctx) {
1057
1189
  for (const message of validateCraftbookGraph(cb)) {
1058
1190
  ctx.addIssue({
1059
- code: z5.ZodIssueCode.custom,
1191
+ code: z6.ZodIssueCode.custom,
1060
1192
  message,
1061
1193
  path: message.startsWith("entryStepId") ? ["entryStepId"] : ["steps"]
1062
1194
  });
1063
1195
  }
1064
1196
  for (const message of validateCraftbookScriptRefs(cb)) {
1065
- ctx.addIssue({ code: z5.ZodIssueCode.custom, message, path: ["steps"] });
1197
+ ctx.addIssue({ code: z6.ZodIssueCode.custom, message, path: ["steps"] });
1066
1198
  }
1067
1199
  }
1068
- var CraftbookRequirementSchema = z5.discriminatedUnion("kind", [
1200
+ var CraftbookRequirementSchema = z6.discriminatedUnion("kind", [
1069
1201
  /** The project is connected to a GitHub repository. */
1070
- z5.object({ kind: z5.literal("github") }),
1202
+ z6.object({ kind: z6.literal("github") }),
1071
1203
  /** The project's current git branch is not the main line (main/master). */
1072
- z5.object({ kind: z5.literal("non-main-branch") })
1204
+ z6.object({ kind: z6.literal("non-main-branch") })
1205
+ ]);
1206
+ var CraftbookRecommendationSchema = z6.discriminatedUnion("kind", [
1207
+ /**
1208
+ * Works better with the install-wide External services capability
1209
+ * (`securityPolicy.allowExternalServices`) — web search, URL fetch.
1210
+ */
1211
+ z6.object({
1212
+ kind: z6.literal("external-services"),
1213
+ /** Human-readable rationale shown in the nudge ("verifies sources with live web search"). */
1214
+ reason: z6.string().optional()
1215
+ })
1073
1216
  ]);
1074
- var CraftbookRunModesSchema = z5.object({
1075
- scheduled: z5.enum(["supported", "recommended"]).optional(),
1076
- nightShift: z5.enum(["supported", "recommended"]).optional()
1217
+ var CraftbookRunModesSchema = z6.object({
1218
+ scheduled: z6.enum(["supported", "recommended"]).optional(),
1219
+ nightShift: z6.enum(["supported", "recommended"]).optional()
1077
1220
  });
1078
- var CraftbookToolsetNeedSchema = z5.object({
1221
+ var CraftbookToolsetNeedSchema = z6.object({
1079
1222
  /** Catalog toolset id, e.g. `github`, `usb-camera`. */
1080
- toolsetId: z5.string().min(1),
1223
+ toolsetId: z6.string().min(1),
1081
1224
  /** Catalog source provenance (bundled/community), when pinned. */
1082
- sourceId: z5.string().optional(),
1225
+ sourceId: z6.string().optional(),
1083
1226
  /** Optional semver floor; presence is recorded but not yet enforced. */
1084
- minVersion: z5.string().optional(),
1227
+ minVersion: z6.string().optional(),
1085
1228
  /**
1086
1229
  * When true, the need is a suggestion only — surfaced as a hint, never
1087
1230
  * blocks invocation. Default (absent/false) = required: the launcher
1088
1231
  * offers install before the craftbook can run.
1089
1232
  */
1090
- optional: z5.boolean().optional(),
1233
+ optional: z6.boolean().optional(),
1091
1234
  /**
1092
1235
  * When true, pre-authorize every tool this toolset exposes for the
1093
1236
  * duration the craftbook is active — both via a synthesized PreToolUse
1094
1237
  * allow-hook (in-process providers) and the Claude-CLI permission
1095
1238
  * broker. Off by default; opt in for unattended/scheduled craftbooks.
1096
1239
  */
1097
- autoAllow: z5.boolean().optional(),
1240
+ autoAllow: z6.boolean().optional(),
1098
1241
  /** Human-readable rationale shown in the launcher ("pull camera frames"). */
1099
- reason: z5.string().optional()
1242
+ reason: z6.string().optional()
1243
+ });
1244
+ var CraftbookCommandNeedSchema = z6.object({
1245
+ scope: z6.enum(["script", "npx"]),
1246
+ name: z6.string().min(1),
1247
+ args: z6.array(z6.string()).optional(),
1248
+ reason: z6.string().optional(),
1249
+ optional: z6.boolean().optional()
1100
1250
  });
1101
- var CraftbookConnectorNeedSchema = z5.object({
1251
+ var CraftbookConnectorNeedSchema = z6.object({
1102
1252
  /** Catalog connector-type id, e.g. `github-pulls`, `mail-gmail`. */
1103
- typeId: z5.string().min(1),
1253
+ typeId: z6.string().min(1),
1104
1254
  /** Catalog source provenance (bundled/community), when pinned. */
1105
- sourceId: z5.string().optional(),
1255
+ sourceId: z6.string().optional(),
1106
1256
  /**
1107
1257
  * When true, the craftbook still runs without the connector bound —
1108
1258
  * the corpus is a bonus, not the substrate. Default (absent/false) =
1109
1259
  * required: the launcher offers to bind it before the craftbook runs.
1110
1260
  */
1111
- optional: z5.boolean().optional(),
1261
+ optional: z6.boolean().optional(),
1112
1262
  /** Human-readable rationale shown in the launcher ("pull the PR diff"). */
1113
- reason: z5.string().optional()
1263
+ reason: z6.string().optional()
1114
1264
  });
1115
1265
  var CRAFTBOOK_SCRIPT_MAX_BYTES = 64 * 1024;
1116
1266
  var CRAFTBOOK_SCRIPTS_TOTAL_MAX_BYTES = 256 * 1024;
1117
1267
  var CRAFTBOOK_SCRIPTS_MAX_COUNT = 24;
1118
- var CraftbookScriptsSchema = z5.record(ScriptNameSchema, z5.string().min(1).max(CRAFTBOOK_SCRIPT_MAX_BYTES)).superRefine((scripts, ctx) => {
1268
+ var CraftbookScriptsSchema = z6.record(ScriptNameSchema, z6.string().min(1).max(CRAFTBOOK_SCRIPT_MAX_BYTES)).superRefine((scripts, ctx) => {
1119
1269
  const names = Object.keys(scripts);
1120
1270
  if (names.length > CRAFTBOOK_SCRIPTS_MAX_COUNT) {
1121
1271
  ctx.addIssue({
1122
- code: z5.ZodIssueCode.custom,
1272
+ code: z6.ZodIssueCode.custom,
1123
1273
  message: `a craftbook carries at most ${CRAFTBOOK_SCRIPTS_MAX_COUNT} scripts (got ${names.length})`
1124
1274
  });
1125
1275
  }
@@ -1127,14 +1277,14 @@ var CraftbookScriptsSchema = z5.record(ScriptNameSchema, z5.string().min(1).max(
1127
1277
  for (const name of names) total += scripts[name].length;
1128
1278
  if (total > CRAFTBOOK_SCRIPTS_TOTAL_MAX_BYTES) {
1129
1279
  ctx.addIssue({
1130
- code: z5.ZodIssueCode.custom,
1280
+ code: z6.ZodIssueCode.custom,
1131
1281
  message: `craftbook scripts total ${total} bytes \u2014 the ceiling is ${CRAFTBOOK_SCRIPTS_TOTAL_MAX_BYTES}`
1132
1282
  });
1133
1283
  }
1134
1284
  });
1135
- var CraftbookBasedOnSchema = z5.object({
1136
- name: z5.string().min(1),
1137
- url: z5.string().url().regex(/^https?:\/\//i, "basedOn.url must use http or https")
1285
+ var CraftbookBasedOnSchema = z6.object({
1286
+ name: z6.string().min(1),
1287
+ url: z6.string().url().regex(/^https?:\/\//i, "basedOn.url must use http or https")
1138
1288
  });
1139
1289
  function validateCraftbookScriptRefs(cb) {
1140
1290
  if (!cb.scripts) return [];
@@ -1156,32 +1306,32 @@ function validateCraftbookScriptRefs(cb) {
1156
1306
  }
1157
1307
  return problems;
1158
1308
  }
1159
- var CraftbookSchema = z5.object({
1309
+ var CraftbookSchema = z6.object({
1160
1310
  /** Matches catalog folder name. For ad-hoc embedded craftbooks (inline-steps tasks), a generated id. */
1161
- id: z5.string().min(1),
1162
- name: z5.string().min(1),
1163
- description: z5.string().optional(),
1311
+ id: z6.string().min(1),
1312
+ name: z6.string().min(1),
1313
+ description: z6.string().optional(),
1164
1314
  /** Semver from the catalog version this craftbook was resolved from. Unset for ad-hoc embedded books. */
1165
- version: z5.string().optional(),
1315
+ version: z6.string().optional(),
1166
1316
  /** Optional credit + link to the upstream work this craftbook adapts. */
1167
1317
  basedOn: CraftbookBasedOnSchema.optional(),
1168
- plan: z5.string().optional(),
1318
+ plan: z6.string().optional(),
1169
1319
  defaultAssignee: TaskAssigneeSchema.optional(),
1170
- steps: z5.array(CraftbookStepSchema).min(1),
1171
- entryStepId: z5.string().min(1),
1320
+ steps: z6.array(CraftbookStepSchema).min(1),
1321
+ entryStepId: z6.string().min(1),
1172
1322
  /**
1173
1323
  * User-speech phrases that route to this craftbook. Matched
1174
1324
  * case-insensitively as substrings against the user's chat input
1175
1325
  * (or against a slash-command argument). Optional; absent =
1176
1326
  * invocable only via explicit `invoke_craftbook` / Commands panel.
1177
1327
  */
1178
- triggers: z5.array(z5.string()).optional(),
1328
+ triggers: z6.array(z6.string()).optional(),
1179
1329
  /**
1180
1330
  * Pre/PostToolUse hooks installed for the duration the craftbook
1181
1331
  * is active. The MCP bridge consults this list before forwarding
1182
1332
  * a `tools/call`. See `hook.ts` for the decision contract.
1183
1333
  */
1184
- hooks: z5.array(HookSpecSchema).optional(),
1334
+ hooks: z6.array(HookSpecSchema).optional(),
1185
1335
  /**
1186
1336
  * Optional squisq/JSON-Schema object describing the parameters this
1187
1337
  * craftbook collects before it runs. Its top-level `properties` are
@@ -1192,19 +1342,26 @@ var CraftbookSchema = z5.object({
1192
1342
  * structurally; typed as squisq's `SquisqAnnotatedSchema` at the UI
1193
1343
  * boundary. Absent = parameterless (the command is injected directly).
1194
1344
  */
1195
- paramSchema: z5.record(z5.string(), z5.unknown()).optional(),
1345
+ paramSchema: z6.record(z6.string(), z6.unknown()).optional(),
1196
1346
  /**
1197
1347
  * CLI token the launcher stages into the terminal and that the
1198
1348
  * terminal recognizes. Defaults to `id` when absent — e.g. the
1199
1349
  * "Code Review" craftbook (id `review`) sets `command: "code-review"`.
1200
1350
  */
1201
- command: z5.string().regex(/^[a-z][a-z0-9-]*$/).optional(),
1351
+ command: z6.string().regex(/^[a-z][a-z0-9-]*$/).optional(),
1202
1352
  /**
1203
1353
  * Prerequisites a project must satisfy for this craftbook to be
1204
1354
  * applicable. When unmet, the craftbook is hidden from the launcher
1205
1355
  * and not recognized as a terminal command. Absent = always offered.
1206
1356
  */
1207
- requirements: z5.array(CraftbookRequirementSchema).optional(),
1357
+ requirements: z6.array(CraftbookRequirementSchema).optional(),
1358
+ /**
1359
+ * Soft "works better with" hints — never gate, only inform UI copy
1360
+ * (the craftbook start card's permission nudge). See
1361
+ * {@link CraftbookRecommendationSchema}. Carried into the runtime
1362
+ * craftbook and the task snapshot, unlike `requirements`.
1363
+ */
1364
+ recommends: z6.array(CraftbookRecommendationSchema).optional(),
1208
1365
  /** Unattended launch modes this recipe is suitable for. */
1209
1366
  runModes: CraftbookRunModesSchema.optional(),
1210
1367
  /**
@@ -1215,7 +1372,14 @@ var CraftbookSchema = z5.object({
1215
1372
  * `requirements`, this is carried into the runtime craftbook and the task
1216
1373
  * snapshot. Absent = no declared toolset dependencies.
1217
1374
  */
1218
- toolsets: z5.array(CraftbookToolsetNeedSchema).optional(),
1375
+ toolsets: z6.array(CraftbookToolsetNeedSchema).optional(),
1376
+ /**
1377
+ * Project commands this craftbook's `commandEvidence` gates verify runs
1378
+ * of. See {@link CraftbookCommandNeedSchema}. Carried into the runtime
1379
+ * craftbook and the task snapshot so the kickoff path can raise the
1380
+ * first-use approval questions up front.
1381
+ */
1382
+ commands: z6.array(CraftbookCommandNeedSchema).optional(),
1219
1383
  /**
1220
1384
  * Connectors whose mirrored `artifacts/data/` corpus this craftbook reads. The
1221
1385
  * launcher binds and syncs them before the first step runs. See
@@ -1223,7 +1387,7 @@ var CraftbookSchema = z5.object({
1223
1387
  * craftbook and the task snapshot so a running task records what it
1224
1388
  * was launched against. Absent = no connector dependencies.
1225
1389
  */
1226
- connectors: z5.array(CraftbookConnectorNeedSchema).optional(),
1390
+ connectors: z6.array(CraftbookConnectorNeedSchema).optional(),
1227
1391
  /**
1228
1392
  * Embedded script sources (name → TypeScript). See
1229
1393
  * {@link CraftbookScriptsSchema}. Hydrated at resolution time for
@@ -1239,10 +1403,30 @@ var CraftbookSchema = z5.object({
1239
1403
  * task snapshot so the runtime reads it at fanout time.
1240
1404
  */
1241
1405
  spawn: CraftbookSpawnSchema.optional(),
1242
- createdAt: z5.string(),
1243
- updatedAt: z5.string()
1406
+ /**
1407
+ * This craftbook is authored mode-agnostic: it works identically whether
1408
+ * a run edits the workspace in place or drafts a diffpack change
1409
+ * proposal. Concretely, its workspace deliverables and gates evaluate
1410
+ * through the draft overlay, and it carries no mode-specific prose and
1411
+ * no `{{diffpack.*}}` tokens — the runtime injects drafting framing and
1412
+ * re-roots the write tools. Declaring it lets an invocation opt the run
1413
+ * into proposing (`deliveryMode: 'propose'`, or automatically for
1414
+ * unattended runs); the mode is a property of the RUN, never of the
1415
+ * book. Resolution rule in `TaskManager.create`.
1416
+ */
1417
+ diffpackCapable: z6.boolean().optional(),
1418
+ /**
1419
+ * Minimum model tier for the WHOLE book: every step floors at least
1420
+ * here (per-step `capabilityFloor` still overrides absolutely, and a
1421
+ * stricter role floor still wins — see `effectiveCapabilityFloor` in
1422
+ * roles/registry.ts). The honest "this recipe needs a medium model"
1423
+ * declaration, consumed by per-step routing at dispatch.
1424
+ */
1425
+ capabilityFloor: ModelTierSchema.optional(),
1426
+ createdAt: z6.string(),
1427
+ updatedAt: z6.string()
1244
1428
  }).superRefine(refineCraftbook);
1245
- var DeliverableKindSchema = z5.enum([
1429
+ var DeliverableKindSchema = z6.enum([
1246
1430
  "html-game",
1247
1431
  "html-multiscreen-game",
1248
1432
  "html-page",
@@ -1261,97 +1445,101 @@ var DeliverableKindSchema = z5.enum([
1261
1445
  "data-file",
1262
1446
  "generic-file"
1263
1447
  ]);
1264
- var StepDeliverableSchema = z5.object({
1448
+ var StepDeliverableSchema = z6.object({
1265
1449
  /** Workspace-relative file the step must produce. The only required field. */
1266
- path: z5.string().min(1),
1450
+ path: z6.string().min(1),
1267
1451
  /** Artifact class; inferred from the file extension when absent. */
1268
1452
  kind: DeliverableKindSchema.optional(),
1269
1453
  /** Override the class-default byte floor. */
1270
- minBytes: z5.number().int().positive().optional(),
1454
+ minBytes: z6.number().int().positive().optional(),
1271
1455
  /** Gate rejections before the task pauses for help. */
1272
- maxAttempts: z5.number().int().positive().optional(),
1456
+ maxAttempts: z6.number().int().positive().optional(),
1273
1457
  /** Gate the artifacts drawer instead of the workspace. */
1274
- artifact: z5.boolean().optional(),
1458
+ artifact: z6.boolean().optional(),
1275
1459
  /**
1276
1460
  * The deliverable is an EDIT to a pre-existing file (fix/refactor):
1277
1461
  * presence alone never advances the step — the assignee must have
1278
1462
  * written to the file this turn. See {@link AdvanceWhenSchema}.
1279
1463
  */
1280
- requireChange: z5.boolean().optional(),
1464
+ requireChange: z6.boolean().optional(),
1281
1465
  /**
1282
1466
  * For code deliverables: additionally execute the file in the sandbox
1283
1467
  * and require exit 0 (a `node:test`/`assert` file exits nonzero on
1284
1468
  * failure — exactly the contract). Opt-in.
1285
1469
  */
1286
- execute: z5.boolean().optional(),
1470
+ execute: z6.boolean().optional(),
1287
1471
  /** For data deliverables: required column names (adds a tableShape check). */
1288
- columns: z5.array(z5.string().min(1)).min(1).optional(),
1472
+ columns: z6.array(z6.string().min(1)).min(1).optional(),
1289
1473
  /** For data deliverables: minimum row count (adds a tableShape check). */
1290
- minRows: z5.number().int().positive().optional()
1291
- });
1292
- var NewCraftbookStepSchema = z5.object({
1293
- id: z5.string().optional(),
1294
- name: z5.string().min(1),
1295
- description: z5.string().optional(),
1296
- prompt: z5.string().optional(),
1297
- suggestedGezelId: z5.string().optional(),
1474
+ minRows: z6.number().int().positive().optional()
1475
+ });
1476
+ var NewCraftbookStepSchema = z6.object({
1477
+ id: z6.string().optional(),
1478
+ name: z6.string().min(1),
1479
+ description: z6.string().optional(),
1480
+ prompt: z6.string().optional(),
1481
+ suggestedGezelId: z6.string().optional(),
1298
1482
  /** See {@link CraftbookStepSchema.shape.suggestedRole}. */
1299
- suggestedRole: z5.string().optional(),
1483
+ suggestedRole: z6.string().optional(),
1300
1484
  /** See {@link CraftbookStepSchema.shape.capabilityFloor}. */
1301
1485
  capabilityFloor: ModelTierSchema.optional(),
1486
+ /** See {@link CraftbookStepSchema.shape.retrieval}. */
1487
+ retrieval: RetrievalPolicySchema.optional(),
1302
1488
  assignee: TaskAssigneeSchema.optional(),
1303
1489
  onEnter: ScriptRefListSchema.optional(),
1304
1490
  onExit: ScriptRefListSchema.optional(),
1305
- consumes: z5.array(CraftbookStepInputSchema).min(1).optional().describe(
1491
+ consumes: z6.array(CraftbookStepInputSchema).min(1).optional().describe(
1306
1492
  "Files this step must open before working. Artifact inputs also require an explicit `read_artifact` call in the step prompt."
1307
1493
  ),
1308
1494
  advanceWhen: AdvanceWhenSchema.optional(),
1309
1495
  gate: StepGateUnionSchema.optional(),
1310
1496
  /** See {@link StepDeliverableSchema} — one field attaches the enforced gate. */
1311
1497
  deliverable: StepDeliverableSchema.optional(),
1312
- next: z5.string().optional(),
1313
- branches: z5.array(CraftbookBranchSchema).optional(),
1314
- terminal: z5.boolean().optional(),
1498
+ next: z6.string().optional(),
1499
+ branches: z6.array(CraftbookBranchSchema).optional(),
1500
+ terminal: z6.boolean().optional(),
1315
1501
  /** See {@link CraftbookStepSchema.shape.spawnFanout}. */
1316
- spawnFanout: z5.boolean().optional()
1502
+ spawnFanout: z6.boolean().optional()
1317
1503
  });
1318
- var CreateCraftbookRequestSchema = z5.object({
1319
- id: z5.string().optional(),
1320
- name: z5.string().min(1),
1321
- description: z5.string().optional(),
1504
+ var CreateCraftbookRequestSchema = z6.object({
1505
+ id: z6.string().optional(),
1506
+ name: z6.string().min(1),
1507
+ description: z6.string().optional(),
1322
1508
  basedOn: CraftbookBasedOnSchema.optional(),
1323
- plan: z5.string().optional(),
1509
+ plan: z6.string().optional(),
1324
1510
  defaultAssignee: TaskAssigneeSchema.optional(),
1325
- steps: z5.array(NewCraftbookStepSchema).min(1),
1326
- entryStepId: z5.string().optional(),
1327
- paramSchema: z5.record(z5.string(), z5.unknown()).optional(),
1328
- command: z5.string().regex(/^[a-z][a-z0-9-]*$/).optional(),
1329
- requirements: z5.array(CraftbookRequirementSchema).optional(),
1511
+ steps: z6.array(NewCraftbookStepSchema).min(1),
1512
+ entryStepId: z6.string().optional(),
1513
+ paramSchema: z6.record(z6.string(), z6.unknown()).optional(),
1514
+ command: z6.string().regex(/^[a-z][a-z0-9-]*$/).optional(),
1515
+ requirements: z6.array(CraftbookRequirementSchema).optional(),
1516
+ recommends: z6.array(CraftbookRecommendationSchema).optional(),
1330
1517
  runModes: CraftbookRunModesSchema.optional(),
1331
- toolsets: z5.array(CraftbookToolsetNeedSchema).optional(),
1518
+ toolsets: z6.array(CraftbookToolsetNeedSchema).optional(),
1332
1519
  scripts: CraftbookScriptsSchema.optional()
1333
1520
  });
1334
- var UpdateCraftbookRequestSchema = z5.object({
1335
- name: z5.string().min(1).optional(),
1336
- description: z5.string().nullable().optional(),
1521
+ var UpdateCraftbookRequestSchema = z6.object({
1522
+ name: z6.string().min(1).optional(),
1523
+ description: z6.string().nullable().optional(),
1337
1524
  basedOn: CraftbookBasedOnSchema.nullable().optional(),
1338
- plan: z5.string().nullable().optional(),
1525
+ plan: z6.string().nullable().optional(),
1339
1526
  defaultAssignee: TaskAssigneeSchema.nullable().optional(),
1340
- steps: z5.array(NewCraftbookStepSchema).min(1).optional(),
1341
- entryStepId: z5.string().optional(),
1342
- paramSchema: z5.record(z5.string(), z5.unknown()).nullable().optional(),
1343
- command: z5.string().regex(/^[a-z][a-z0-9-]*$/).nullable().optional(),
1344
- requirements: z5.array(CraftbookRequirementSchema).nullable().optional(),
1527
+ steps: z6.array(NewCraftbookStepSchema).min(1).optional(),
1528
+ entryStepId: z6.string().optional(),
1529
+ paramSchema: z6.record(z6.string(), z6.unknown()).nullable().optional(),
1530
+ command: z6.string().regex(/^[a-z][a-z0-9-]*$/).nullable().optional(),
1531
+ requirements: z6.array(CraftbookRequirementSchema).nullable().optional(),
1532
+ recommends: z6.array(CraftbookRecommendationSchema).nullable().optional(),
1345
1533
  runModes: CraftbookRunModesSchema.nullable().optional(),
1346
- toolsets: z5.array(CraftbookToolsetNeedSchema).nullable().optional(),
1534
+ toolsets: z6.array(CraftbookToolsetNeedSchema).nullable().optional(),
1347
1535
  /** Full-replace semantics: the map is the truth. `null` clears all scripts. */
1348
1536
  scripts: CraftbookScriptsSchema.nullable().optional()
1349
1537
  });
1350
- var CraftbookSummarySchema = z5.object({
1351
- id: z5.string(),
1352
- name: z5.string(),
1353
- description: z5.string().optional(),
1354
- version: z5.string().optional(),
1538
+ var CraftbookSummarySchema = z6.object({
1539
+ id: z6.string(),
1540
+ name: z6.string(),
1541
+ description: z6.string().optional(),
1542
+ version: z6.string().optional(),
1355
1543
  basedOn: CraftbookBasedOnSchema.optional(),
1356
1544
  /**
1357
1545
  * Where the craftbook came from. `bundled` — shipped catalog;
@@ -1360,51 +1548,51 @@ var CraftbookSummarySchema = z5.object({
1360
1548
  * folder (often auto-imported from a `.claude/skills` / `agents/skills`
1361
1549
  * SKILL.md). Project craftbooks only surface inside their own project.
1362
1550
  */
1363
- source: z5.enum(["bundled", "local", "project"]),
1364
- stepCount: z5.number().int().nonnegative()
1551
+ source: z6.enum(["bundled", "local", "project"]),
1552
+ stepCount: z6.number().int().nonnegative()
1365
1553
  });
1366
- var ListCraftbooksResponseSchema = z5.object({
1367
- craftbooks: z5.array(CraftbookSummarySchema)
1554
+ var ListCraftbooksResponseSchema = z6.object({
1555
+ craftbooks: z6.array(CraftbookSummarySchema)
1368
1556
  });
1369
- var ProjectCraftbookProvenanceSchema = z5.object({
1370
- installedBy: z5.literal("project-type"),
1371
- typeId: z5.string(),
1372
- typeVersion: z5.string(),
1373
- bookVersion: z5.string(),
1557
+ var ProjectCraftbookProvenanceSchema = z6.object({
1558
+ installedBy: z6.literal("project-type"),
1559
+ typeId: z6.string(),
1560
+ typeVersion: z6.string(),
1561
+ bookVersion: z6.string(),
1374
1562
  /** sha256 over the installed book's canonical recipe JSON. */
1375
- contentHash: z5.string(),
1376
- installedAt: z5.string()
1377
- });
1378
- var CraftbookSuggestionSchema = z5.object({
1379
- id: z5.string(),
1380
- name: z5.string(),
1381
- description: z5.string().optional(),
1382
- source: z5.enum(["bundled", "local", "project"]),
1383
- version: z5.string().optional(),
1563
+ contentHash: z6.string(),
1564
+ installedAt: z6.string()
1565
+ });
1566
+ var CraftbookSuggestionSchema = z6.object({
1567
+ id: z6.string(),
1568
+ name: z6.string(),
1569
+ description: z6.string().optional(),
1570
+ source: z6.enum(["bundled", "local", "project"]),
1571
+ version: z6.string().optional(),
1384
1572
  basedOn: CraftbookBasedOnSchema.optional(),
1385
- stepCount: z5.number().int().nonnegative(),
1386
- tags: z5.array(z5.string()).optional(),
1387
- triggers: z5.array(z5.string()).optional(),
1388
- score: z5.number(),
1389
- semantic: z5.number().optional(),
1390
- lexical: z5.number()
1573
+ stepCount: z6.number().int().nonnegative(),
1574
+ tags: z6.array(z6.string()).optional(),
1575
+ triggers: z6.array(z6.string()).optional(),
1576
+ score: z6.number(),
1577
+ semantic: z6.number().optional(),
1578
+ lexical: z6.number()
1391
1579
  });
1392
- var SuggestCraftbooksResponseSchema = z5.object({
1393
- suggestions: z5.array(CraftbookSuggestionSchema)
1580
+ var SuggestCraftbooksResponseSchema = z6.object({
1581
+ suggestions: z6.array(CraftbookSuggestionSchema)
1394
1582
  });
1395
- var CraftbookResponseSchema = z5.object({
1583
+ var CraftbookResponseSchema = z6.object({
1396
1584
  craftbook: CraftbookSchema
1397
1585
  });
1398
1586
  function slugifyStepId(input) {
1399
1587
  return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "step";
1400
1588
  }
1401
- var StepPositionSchema = z5.object({
1589
+ var StepPositionSchema = z6.object({
1402
1590
  /** Insert immediately after the step with this id. */
1403
- after: z5.string().optional(),
1591
+ after: z6.string().optional(),
1404
1592
  /** Insert immediately before the step with this id. */
1405
- before: z5.string().optional(),
1593
+ before: z6.string().optional(),
1406
1594
  /** Absolute insertion index (clamped to [0, length]). */
1407
- index: z5.number().int().nonnegative().optional()
1595
+ index: z6.number().int().nonnegative().optional()
1408
1596
  });
1409
1597
 
1410
1598
  // src/markdown/frontmatter.ts
@@ -1580,11 +1768,21 @@ var FRONTMATTER_KEYS = [
1580
1768
  "plan",
1581
1769
  "defaultAssignee",
1582
1770
  "requirements",
1771
+ "recommends",
1583
1772
  "runModes",
1584
1773
  "toolsets",
1585
1774
  "connectors",
1586
1775
  "paramSchema",
1587
1776
  "hooks",
1777
+ // Whole-book declarations that are NOT step fields. Omitting one here is
1778
+ // worse than dropping it: the parser reports `unknown key "spawn"` and
1779
+ // refuses the save, so a model that correctly authored a fanout book in
1780
+ // the markdown arm is told its document is malformed.
1781
+ "spawn",
1782
+ "commands",
1783
+ "diffpackCapable",
1784
+ "capabilityFloor",
1785
+ "minGezelVersion",
1588
1786
  "version",
1589
1787
  "releasedAt"
1590
1788
  ];
@@ -1603,7 +1801,10 @@ var STEP_FENCE_KEYS = [
1603
1801
  "gate",
1604
1802
  "next",
1605
1803
  "branches",
1606
- "terminal"
1804
+ "terminal",
1805
+ // Marks the step the spawn block fans out from. Without it a book can
1806
+ // declare `spawn` and have no step that triggers it.
1807
+ "spawnFanout"
1607
1808
  ];
1608
1809
  function parseCraftbookMarkdown(text) {
1609
1810
  const errors = [];
@@ -1791,10 +1992,10 @@ function defaultStepIdForName(name) {
1791
1992
  }
1792
1993
 
1793
1994
  // src/schemas/gezel.ts
1794
- import { z as z17 } from "zod";
1995
+ import { z as z19 } from "zod";
1795
1996
 
1796
1997
  // src/poppetje/schema.ts
1797
- import { z as z6 } from "zod";
1998
+ import { z as z7 } from "zod";
1798
1999
 
1799
2000
  // src/poppetje/catalogs.ts
1800
2001
  var BODY_ARCHETYPES = {
@@ -1940,45 +2141,45 @@ function migrateLegacyAccessory(raw) {
1940
2141
  }
1941
2142
  return r;
1942
2143
  }
1943
- var PoppetjeSchema = z6.preprocess(
2144
+ var PoppetjeSchema = z7.preprocess(
1944
2145
  migrateLegacyAccessory,
1945
- z6.object({
2146
+ z7.object({
1946
2147
  /** Stable id; locks the wood-grain noise seed. Pin to the gezel id at creation. */
1947
- key: z6.string().min(1),
1948
- name: z6.string(),
1949
- bodyShape: z6.enum(BODY_SHAPE_KEYS),
1950
- figureScale: z6.enum(FIGURE_SCALE_KEYS),
2148
+ key: z7.string().min(1),
2149
+ name: z7.string(),
2150
+ bodyShape: z7.enum(BODY_SHAPE_KEYS),
2151
+ figureScale: z7.enum(FIGURE_SCALE_KEYS),
1951
2152
  /** Mid-tone of the head. Paired with skin2 for the lateral gradient. */
1952
- skin: z6.string(),
2153
+ skin: z7.string(),
1953
2154
  /** Darker edge tone. */
1954
- skin2: z6.string(),
1955
- hair: z6.string(),
1956
- hairShape: z6.enum(HAIR_SHAPES),
2155
+ skin2: z7.string(),
2156
+ hair: z7.string(),
2157
+ hairShape: z7.enum(HAIR_SHAPES),
1957
2158
  /** Replaces hair when set; 'hood' renders at body level. */
1958
- hat: z6.enum(HAT_OPTIONS).nullable().optional().default(null),
2159
+ hat: z7.enum(HAT_OPTIONS).nullable().optional().default(null),
1959
2160
  /** Body overlay; null for an unadorned shirt. */
1960
- dress: z6.enum(DRESS_OPTIONS).nullable().optional().default(null),
2161
+ dress: z7.enum(DRESS_OPTIONS).nullable().optional().default(null),
1961
2162
  /** Wearable face accessory (glasses, earring, monocle). User-toggleable. */
1962
- accessory: z6.enum(ACCESSORY_OPTIONS).nullable().optional().default(null),
2163
+ accessory: z7.enum(ACCESSORY_OPTIONS).nullable().optional().default(null),
1963
2164
  /** Facial hair (beard, mustache). Physical — reroll-only. */
1964
- facialHair: z6.enum(FACIAL_HAIR_OPTIONS).nullable().optional().default(null),
2165
+ facialHair: z7.enum(FACIAL_HAIR_OPTIONS).nullable().optional().default(null),
1965
2166
  /** Small facial detail (freckles, mole). Physical — reroll-only. */
1966
- mark: z6.enum(MARK_OPTIONS).nullable().optional().default(null),
1967
- expression: z6.enum(EXPRESSION_OPTIONS).optional().default("smile"),
1968
- shirt: z6.string(),
1969
- shirtAccent: z6.string(),
2167
+ mark: z7.enum(MARK_OPTIONS).nullable().optional().default(null),
2168
+ expression: z7.enum(EXPRESSION_OPTIONS).optional().default("smile"),
2169
+ shirt: z7.string(),
2170
+ shirtAccent: z7.string(),
1970
2171
  /**
1971
2172
  * Painted garment pattern over the shirt (buttons, stripes, sash, …).
1972
2173
  * Files written before patterns existed parse as `plain` — the bare
1973
2174
  * shirt those characters always had.
1974
2175
  */
1975
- shirtPattern: z6.enum(SHIRT_PATTERN_OPTIONS).optional().default("plain")
2176
+ shirtPattern: z7.enum(SHIRT_PATTERN_OPTIONS).optional().default("plain")
1976
2177
  })
1977
2178
  );
1978
2179
 
1979
2180
  // src/schemas/claude.ts
1980
- import { z as z7 } from "zod";
1981
- var ClaudePermissionModeSchema = z7.enum([
2181
+ import { z as z8 } from "zod";
2182
+ var ClaudePermissionModeSchema = z8.enum([
1982
2183
  "default",
1983
2184
  "acceptEdits",
1984
2185
  "plan",
@@ -1986,20 +2187,20 @@ var ClaudePermissionModeSchema = z7.enum([
1986
2187
  ]);
1987
2188
 
1988
2189
  // src/schemas/codex.ts
1989
- import { z as z8 } from "zod";
1990
- var CodexPermissionModeSchema = z8.enum(["plan", "edit", "reviewed", "full"]);
1991
- var LegacyCodexPermissionModeSchema = z8.enum([
2190
+ import { z as z9 } from "zod";
2191
+ var CodexPermissionModeSchema = z9.enum(["plan", "edit", "reviewed", "full"]);
2192
+ var LegacyCodexPermissionModeSchema = z9.enum([
1992
2193
  "default",
1993
2194
  "acceptEdits",
1994
2195
  "bypassPermissions"
1995
2196
  ]);
1996
- var CodexPermissionModeCompatSchema = z8.union([
2197
+ var CodexPermissionModeCompatSchema = z9.union([
1997
2198
  CodexPermissionModeSchema,
1998
2199
  LegacyCodexPermissionModeSchema
1999
2200
  ]);
2000
2201
 
2001
2202
  // src/schemas/entity-id.ts
2002
- import { z as z9 } from "zod";
2203
+ import { z as z10 } from "zod";
2003
2204
 
2004
2205
  // src/entity-id.ts
2005
2206
  var SAFE_ENTITY_ID = /^[A-Za-z0-9@][A-Za-z0-9@._-]{0,199}$/;
@@ -2034,169 +2235,169 @@ function isSafeEntityId(value) {
2034
2235
  }
2035
2236
 
2036
2237
  // src/schemas/entity-id.ts
2037
- var EntityIdSchema = z9.string().refine(isSafeEntityId, {
2238
+ var EntityIdSchema = z10.string().refine(isSafeEntityId, {
2038
2239
  message: "must be a portable single-segment entity id"
2039
2240
  });
2040
2241
 
2041
2242
  // src/schemas/growth.ts
2042
- import { z as z11 } from "zod";
2243
+ import { z as z12 } from "zod";
2043
2244
 
2044
2245
  // src/schemas/tuning-profile-registry.ts
2045
- import { z as z10 } from "zod";
2046
- var TuningProfileIdSchema = z10.string().min(1).describe("Canonical tuning profile id.");
2246
+ import { z as z11 } from "zod";
2247
+ var TuningProfileIdSchema = z11.string().min(1).describe("Canonical tuning profile id.");
2047
2248
 
2048
2249
  // src/schemas/growth.ts
2049
- var GrowthEvidenceSchema = z11.object({
2250
+ var GrowthEvidenceSchema = z12.object({
2050
2251
  /** Memory-file day (YYYY-MM-DD) — rewritten server-side from the matched entry. */
2051
- day: z11.string().regex(/^\d{4}-\d{2}-\d{2}$/),
2052
- kind: z11.enum(["fact", "decision", "pref", "status"]),
2053
- excerpt: z11.string().min(1).max(400)
2054
- });
2055
- var TraitProposalSchema = z11.object({
2056
- id: z11.string(),
2057
- kind: z11.literal("trait"),
2058
- title: z11.string().min(1).max(80),
2252
+ day: z12.string().regex(/^\d{4}-\d{2}-\d{2}$/),
2253
+ kind: z12.enum(["fact", "decision", "pref", "status"]),
2254
+ excerpt: z12.string().min(1).max(400)
2255
+ });
2256
+ var TraitProposalSchema = z12.object({
2257
+ id: z12.string(),
2258
+ kind: z12.literal("trait"),
2259
+ title: z12.string().min(1).max(80),
2059
2260
  /** One imperative second-person sentence destined for the prompt. */
2060
- traitText: z11.string().min(1).max(200),
2061
- evidence: z11.array(GrowthEvidenceSchema).min(1).max(3)
2261
+ traitText: z12.string().min(1).max(200),
2262
+ evidence: z12.array(GrowthEvidenceSchema).min(1).max(3)
2062
2263
  });
2063
- var TuningActionSchema = z11.discriminatedUnion("type", [
2064
- z11.object({ type: z11.literal("profile"), profile: TuningProfileIdSchema }),
2264
+ var TuningActionSchema = z12.discriminatedUnion("type", [
2265
+ z12.object({ type: z12.literal("profile"), profile: TuningProfileIdSchema }),
2065
2266
  /** Resolved + clamped at accept time against the then-current frontmatter. */
2066
- z11.object({ type: z11.literal("temperature"), delta: z11.union([z11.literal(0.1), z11.literal(-0.1)]) })
2267
+ z12.object({ type: z12.literal("temperature"), delta: z12.union([z12.literal(0.1), z12.literal(-0.1)]) })
2067
2268
  ]);
2068
- var TuningProposalSchema = z11.object({
2069
- id: z11.string(),
2070
- kind: z11.literal("tuning"),
2071
- title: z11.string().max(80),
2072
- description: z11.string().max(300),
2269
+ var TuningProposalSchema = z12.object({
2270
+ id: z12.string(),
2271
+ kind: z12.literal("tuning"),
2272
+ title: z12.string().max(80),
2273
+ description: z12.string().max(300),
2073
2274
  action: TuningActionSchema
2074
2275
  });
2075
- var CosmeticProposalSchema = z11.object({
2076
- id: z11.string(),
2077
- kind: z11.literal("cosmetic"),
2078
- title: z11.string().max(80),
2276
+ var CosmeticProposalSchema = z12.object({
2277
+ id: z12.string(),
2278
+ kind: z12.literal("cosmetic"),
2279
+ title: z12.string().max(80),
2079
2280
  /** Key into GROWTH_COSMETICS, or a generic `level-N` milestone marker. */
2080
- cosmeticId: z11.string()
2281
+ cosmeticId: z12.string()
2081
2282
  });
2082
- var GrowthProposalSchema = z11.discriminatedUnion("kind", [
2283
+ var GrowthProposalSchema = z12.discriminatedUnion("kind", [
2083
2284
  TraitProposalSchema,
2084
2285
  TuningProposalSchema,
2085
2286
  CosmeticProposalSchema
2086
2287
  ]);
2087
- var PendingLevelUpSchema = z11.object({
2088
- toLevel: z11.number().int().min(2),
2089
- proposals: z11.array(GrowthProposalSchema).min(1).max(5),
2090
- createdAt: z11.string()
2091
- });
2092
- var GrowthSignalsSchema = z11.object({
2093
- memoryXp: z11.number().int().nonnegative().default(0),
2094
- lessonsXp: z11.number().int().nonnegative().default(0),
2095
- taskXp: z11.number().int().nonnegative().default(0),
2096
- consultXp: z11.number().int().nonnegative().default(0)
2097
- });
2098
- var AdoptedTraitRecordSchema = z11.object({
2099
- traitId: z11.string(),
2100
- text: z11.string(),
2101
- level: z11.number().int(),
2102
- adoptedAt: z11.string(),
2103
- evidence: z11.array(GrowthEvidenceSchema),
2288
+ var PendingLevelUpSchema = z12.object({
2289
+ toLevel: z12.number().int().min(2),
2290
+ proposals: z12.array(GrowthProposalSchema).min(1).max(5),
2291
+ createdAt: z12.string()
2292
+ });
2293
+ var GrowthSignalsSchema = z12.object({
2294
+ memoryXp: z12.number().int().nonnegative().default(0),
2295
+ lessonsXp: z12.number().int().nonnegative().default(0),
2296
+ taskXp: z12.number().int().nonnegative().default(0),
2297
+ consultXp: z12.number().int().nonnegative().default(0)
2298
+ });
2299
+ var AdoptedTraitRecordSchema = z12.object({
2300
+ traitId: z12.string(),
2301
+ text: z12.string(),
2302
+ level: z12.number().int(),
2303
+ adoptedAt: z12.string(),
2304
+ evidence: z12.array(GrowthEvidenceSchema),
2104
2305
  /** Set when the user later retires the trait — kept for the character sheet. */
2105
- removedAt: z11.string().optional()
2306
+ removedAt: z12.string().optional()
2106
2307
  });
2107
- var DeclinedProposalRecordSchema = z11.object({
2108
- kind: z11.enum(["trait", "tuning", "cosmetic"]),
2109
- title: z11.string(),
2308
+ var DeclinedProposalRecordSchema = z12.object({
2309
+ kind: z12.enum(["trait", "tuning", "cosmetic"]),
2310
+ title: z12.string(),
2110
2311
  /** Used for never-re-offer matching on trait proposals. */
2111
- traitText: z11.string().optional(),
2112
- level: z11.number().int(),
2113
- declinedAt: z11.string()
2114
- });
2115
- var GezelGrowthStateSchema = z11.object({
2116
- version: z11.literal(1).default(1),
2117
- level: z11.number().int().min(1).default(1),
2118
- xp: z11.number().int().nonnegative().default(0),
2312
+ traitText: z12.string().optional(),
2313
+ level: z12.number().int(),
2314
+ declinedAt: z12.string()
2315
+ });
2316
+ var GezelGrowthStateSchema = z12.object({
2317
+ version: z12.literal(1).default(1),
2318
+ level: z12.number().int().min(1).default(1),
2319
+ xp: z12.number().int().nonnegative().default(0),
2119
2320
  signals: GrowthSignalsSchema.prefault({}),
2120
- lastComputedAt: z11.string().optional(),
2321
+ lastComputedAt: z12.string().optional(),
2121
2322
  pendingLevelUp: PendingLevelUpSchema.optional(),
2122
- adoptedTraits: z11.array(AdoptedTraitRecordSchema).default([]),
2123
- declinedProposals: z11.array(DeclinedProposalRecordSchema).default([]),
2124
- unlockedCosmetics: z11.array(z11.object({ id: z11.string(), at: z11.string() })).default([])
2323
+ adoptedTraits: z12.array(AdoptedTraitRecordSchema).default([]),
2324
+ declinedProposals: z12.array(DeclinedProposalRecordSchema).default([]),
2325
+ unlockedCosmetics: z12.array(z12.object({ id: z12.string(), at: z12.string() })).default([])
2125
2326
  });
2126
- var GezelGrowthSummarySchema = z11.object({
2127
- level: z11.number().int().min(1),
2327
+ var GezelGrowthSummarySchema = z12.object({
2328
+ level: z12.number().int().min(1),
2128
2329
  /** True when a level-up is waiting for the user's choice. */
2129
- pending: z11.boolean().optional()
2330
+ pending: z12.boolean().optional()
2130
2331
  });
2131
2332
 
2132
2333
  // src/schemas/model-tuning.ts
2133
- import { z as z12 } from "zod";
2134
- var DrySamplerSchema = z12.object({
2135
- multiplier: z12.number().min(0).max(5).describe("DRY penalty strength. 0 disables. 0.8 is a reasonable default."),
2136
- base: z12.number().min(0).max(5).optional().describe("Base of the exponential penalty for repeated tokens. Default 1.75."),
2137
- allowedLength: z12.number().int().min(0).max(64).optional().describe("Minimum n-gram length before DRY kicks in. Default 2.")
2334
+ import { z as z13 } from "zod";
2335
+ var DrySamplerSchema = z13.object({
2336
+ multiplier: z13.number().min(0).max(5).describe("DRY penalty strength. 0 disables. 0.8 is a reasonable default."),
2337
+ base: z13.number().min(0).max(5).optional().describe("Base of the exponential penalty for repeated tokens. Default 1.75."),
2338
+ allowedLength: z13.number().int().min(0).max(64).optional().describe("Minimum n-gram length before DRY kicks in. Default 2.")
2138
2339
  }).describe("DRY anti-repetition sampler (llama.cpp only).");
2139
- var XtcSamplerSchema = z12.object({
2140
- probability: z12.number().min(0).max(1).describe("Probability of triggering XTC on any given step."),
2141
- threshold: z12.number().min(0).max(1).describe("Minimum top-token probability for XTC to fire.")
2340
+ var XtcSamplerSchema = z13.object({
2341
+ probability: z13.number().min(0).max(1).describe("Probability of triggering XTC on any given step."),
2342
+ threshold: z13.number().min(0).max(1).describe("Minimum top-token probability for XTC to fire.")
2142
2343
  }).describe("XTC sampler (llama.cpp only).");
2143
- var SamplingBlockSchema = z12.object({
2144
- temperature: z12.number().min(0).max(2).optional().describe("Sampling temperature. 0 = greedy. Most reasoning models want 0.6\u20131.0."),
2145
- topP: z12.number().min(0).max(1).optional().describe("Nucleus sampling. 0.95 is the common default for Qwen, Gemma, Nemotron."),
2146
- topK: z12.number().int().min(0).max(1e3).optional().describe("Top-k cutoff. 20 for Qwen think, 64 for Gemma, 1 for greedy-on-instruct."),
2147
- minP: z12.number().min(0).max(1).optional().describe("Min-p cutoff (llama.cpp / MLX / Ollama). Qwen recommends 0."),
2148
- maxTokens: z12.number().int().positive().optional().describe("Per-turn output token cap. Maps to num_predict / max_tokens / n_predict."),
2149
- seed: z12.number().int().optional().describe("RNG seed. Unset / negative = random. Best-effort determinism on cloud."),
2150
- repetitionPenalty: z12.number().min(0).max(3).optional().describe("Local engines (Ollama / llama.cpp / MLX): repeat_penalty. 1.0 = off, 1.1 = mild."),
2151
- repetitionContext: z12.number().int().positive().optional().describe("Local engines: window size for repetition penalty (`repeat_last_n`)."),
2152
- frequencyPenalty: z12.number().min(-2).max(2).optional().describe("Cloud (OpenAI) and Ollama / llama.cpp: penalize repeated tokens by frequency."),
2153
- presencePenalty: z12.number().min(-2).max(2).optional().describe("Cloud (OpenAI) and Ollama / llama.cpp: penalize any reused token."),
2344
+ var SamplingBlockSchema = z13.object({
2345
+ temperature: z13.number().min(0).max(2).optional().describe("Sampling temperature. 0 = greedy. Most reasoning models want 0.6\u20131.0."),
2346
+ topP: z13.number().min(0).max(1).optional().describe("Nucleus sampling. 0.95 is the common default for Qwen, Gemma, Nemotron."),
2347
+ topK: z13.number().int().min(0).max(1e3).optional().describe("Top-k cutoff. 20 for Qwen think, 64 for Gemma, 1 for greedy-on-instruct."),
2348
+ minP: z13.number().min(0).max(1).optional().describe("Min-p cutoff (llama.cpp / MLX / Ollama). Qwen recommends 0."),
2349
+ maxTokens: z13.number().int().positive().optional().describe("Per-turn output token cap. Maps to num_predict / max_tokens / n_predict."),
2350
+ seed: z13.number().int().optional().describe("RNG seed. Unset / negative = random. Best-effort determinism on cloud."),
2351
+ repetitionPenalty: z13.number().min(0).max(3).optional().describe("Local engines (Ollama / llama.cpp / MLX): repeat_penalty. 1.0 = off, 1.1 = mild."),
2352
+ repetitionContext: z13.number().int().positive().optional().describe("Local engines: window size for repetition penalty (`repeat_last_n`)."),
2353
+ frequencyPenalty: z13.number().min(-2).max(2).optional().describe("Cloud (OpenAI) and Ollama / llama.cpp: penalize repeated tokens by frequency."),
2354
+ presencePenalty: z13.number().min(-2).max(2).optional().describe("Cloud (OpenAI) and Ollama / llama.cpp: penalize any reused token."),
2154
2355
  dry: DrySamplerSchema.optional(),
2155
2356
  xtc: XtcSamplerSchema.optional()
2156
2357
  }).describe("Sampling parameters applied per-request.");
2157
- var ReasoningBlockSchema = z12.object({
2158
- effort: z12.enum(["low", "medium", "high"]).optional().describe(
2358
+ var ReasoningBlockSchema = z13.object({
2359
+ effort: z13.enum(["low", "medium", "high"]).optional().describe(
2159
2360
  "Cloud reasoning effort. Maps to OpenAI `reasoning.effort` and Anthropic `thinking.budget_tokens` tiers."
2160
2361
  ),
2161
- thinkingBudget: z12.number().int().positive().optional().describe(
2362
+ thinkingBudget: z13.number().int().positive().optional().describe(
2162
2363
  "Explicit thinking-token budget (Anthropic `thinking.budget_tokens`, Nemotron `reasoning_budget`). Wins over `effort` when both set."
2163
2364
  ),
2164
- enableThinking: z12.boolean().optional().describe(
2365
+ enableThinking: z13.boolean().optional().describe(
2165
2366
  "Chat-template toggle for dual-mode models (Qwen3+, Nemotron Nano/Super). Implicit on cloud thinking models."
2166
2367
  ),
2167
- templateKwargs: z12.record(z12.string(), z12.union([z12.string(), z12.number(), z12.boolean()])).optional().describe(
2368
+ templateKwargs: z13.record(z13.string(), z13.union([z13.string(), z13.number(), z13.boolean()])).optional().describe(
2168
2369
  "Chat-template variables that drive this model's reasoning depth, forwarded verbatim as `chat_template_kwargs` on local engines. The names are the model's own \u2014 GPT-OSS reads `reasoning_effort`, Muse Glimmer reads `reasoning_strength` (low|medium|high|xhigh) \u2014 so the manifest declares them rather than the runtime guessing. Lives under `reasoning` (not `engine`) because depth is a per-request choice a tuning profile overrides: `thinking-coding` can ask for xhigh while `instruct` asks for low. Cloud providers ignore it; use `reasoning.effort` there."
2169
2370
  )
2170
2371
  }).describe("Reasoning controls.");
2171
- var StructuredOutputSchema = z12.object({
2172
- responseFormat: z12.enum(["text", "json_object"]).optional().describe("Output mode. `text` = freeform (default). `json_object` = enforce JSON."),
2173
- jsonSchema: z12.unknown().optional().describe("Pin output to a JSON Schema (OpenAI strict mode, llama.cpp --json-schema)."),
2174
- grammar: z12.string().optional().describe("llama.cpp GBNF grammar. Last-resort structured-output knob.")
2372
+ var StructuredOutputSchema = z13.object({
2373
+ responseFormat: z13.enum(["text", "json_object"]).optional().describe("Output mode. `text` = freeform (default). `json_object` = enforce JSON."),
2374
+ jsonSchema: z13.unknown().optional().describe("Pin output to a JSON Schema (OpenAI strict mode, llama.cpp --json-schema)."),
2375
+ grammar: z13.string().optional().describe("llama.cpp GBNF grammar. Last-resort structured-output knob.")
2175
2376
  }).describe("Structured-output controls.");
2176
- var PromptTagsSchema = z12.object({
2177
- enableThinkingTag: z12.string().optional().describe("User-prompt tag that enables thinking for this turn (e.g. `/think`)."),
2178
- disableThinkingTag: z12.string().optional().describe("User-prompt tag that disables thinking for this turn (e.g. `/no_think`).")
2377
+ var PromptTagsSchema = z13.object({
2378
+ enableThinkingTag: z13.string().optional().describe("User-prompt tag that enables thinking for this turn (e.g. `/think`)."),
2379
+ disableThinkingTag: z13.string().optional().describe("User-prompt tag that disables thinking for this turn (e.g. `/no_think`).")
2179
2380
  }).describe("Per-turn reasoning toggle tags.");
2180
- var LlamaCppEngineConfigSchema = z12.object({
2181
- nGpuLayers: z12.number().int().min(-1).optional().describe("`--n-gpu-layers` override. -1 = all. Unset = b9843 `auto`/`--fit`."),
2182
- cpuMoe: z12.boolean().optional().describe("`--cpu-moe`: keep ALL MoE experts in system RAM (attention/dense on GPU)."),
2183
- nCpuMoe: z12.number().int().min(0).optional().describe("`--n-cpu-moe N`: keep the first N layers\u2019 MoE experts in RAM. Partial split."),
2184
- cacheReuse: z12.number().int().min(0).optional().describe(
2381
+ var LlamaCppEngineConfigSchema = z13.object({
2382
+ nGpuLayers: z13.number().int().min(-1).optional().describe("`--n-gpu-layers` override. -1 = all. Unset = b9843 `auto`/`--fit`."),
2383
+ cpuMoe: z13.boolean().optional().describe("`--cpu-moe`: keep ALL MoE experts in system RAM (attention/dense on GPU)."),
2384
+ nCpuMoe: z13.number().int().min(0).optional().describe("`--n-cpu-moe N`: keep the first N layers\u2019 MoE experts in RAM. Partial split."),
2385
+ cacheReuse: z13.number().int().min(0).optional().describe(
2185
2386
  "`--cache-reuse N` prefix-KV reuse chunk. 0 = disable. Unset inherits the global default."
2186
2387
  ),
2187
- swaFull: z12.boolean().optional().describe("`--swa-full`: full-size SWA cache (Gemma family)."),
2188
- flashAttn: z12.enum(["on", "off", "auto"]).optional().describe("`--flash-attn` mode override for this model."),
2189
- ubatchSize: z12.number().int().positive().optional().describe("`--ubatch-size` (inner microbatch) override for this model."),
2190
- contextSize: z12.number().int().positive().optional().describe(
2388
+ swaFull: z13.boolean().optional().describe("`--swa-full`: full-size SWA cache (Gemma family)."),
2389
+ flashAttn: z13.enum(["on", "off", "auto"]).optional().describe("`--flash-attn` mode override for this model."),
2390
+ ubatchSize: z13.number().int().positive().optional().describe("`--ubatch-size` (inner microbatch) override for this model."),
2391
+ contextSize: z13.number().int().positive().optional().describe(
2191
2392
  "Per-turn context ceiling (tokens) this model launches with. Capped by GGUF train ctx."
2192
2393
  ),
2193
- chatTemplate: z12.string().min(1).optional().describe(
2394
+ chatTemplate: z13.string().min(1).optional().describe(
2194
2395
  "`--chat-template` override for GGUFs whose embedded Jinja template is incompatible with llama.cpp tool parsing."
2195
2396
  ),
2196
- threads: z12.number().int().positive().optional().describe("`--threads` override."),
2197
- batchSize: z12.number().int().positive().optional().describe("`--batch-size` override."),
2198
- spec: z12.object({
2199
- type: z12.enum([
2397
+ threads: z13.number().int().positive().optional().describe("`--threads` override."),
2398
+ batchSize: z13.number().int().positive().optional().describe("`--batch-size` override."),
2399
+ spec: z13.object({
2400
+ type: z13.enum([
2200
2401
  "none",
2201
2402
  "draft-mtp",
2202
2403
  "draft-eagle3",
@@ -2208,24 +2409,24 @@ var LlamaCppEngineConfigSchema = z12.object({
2208
2409
  "ngram-map-k4v",
2209
2410
  "ngram-cache"
2210
2411
  ]).optional().describe("`--spec-type` speculative-decoding mode for this model."),
2211
- mtp: z12.boolean().optional().describe(
2412
+ mtp: z13.boolean().optional().describe(
2212
2413
  "VERIFIED capability metadata: this model\u2019s target or companion GGUF carries MTP tensors. Does not enable `draft-mtp` by itself."
2213
2414
  ),
2214
- draftModelId: z12.string().optional().describe("Catalog id / path of the draft model for `draft-simple`."),
2215
- nMax: z12.number().int().positive().optional().describe("`--spec-draft-n-max`: tokens drafted per step.")
2415
+ draftModelId: z13.string().optional().describe("Catalog id / path of the draft model for `draft-simple`."),
2416
+ nMax: z13.number().int().positive().optional().describe("`--spec-draft-n-max`: tokens drafted per step.")
2216
2417
  }).optional().describe("Speculative-decoding config for this model.")
2217
2418
  }).describe("Per-model llama.cpp launch-flag defaults (engine-level, applied at model load).");
2218
- var EngineConfigSchema = z12.object({
2419
+ var EngineConfigSchema = z13.object({
2219
2420
  llamaCpp: LlamaCppEngineConfigSchema.optional()
2220
2421
  }).describe("Per-model engine launch-flag defaults, keyed by engine.");
2221
- var ChatModelTuningBaseSchema = z12.object({
2422
+ var ChatModelTuningBaseSchema = z13.object({
2222
2423
  sampling: SamplingBlockSchema.optional(),
2223
2424
  samplingWhenThinking: SamplingBlockSchema.optional().describe(
2224
2425
  "Sparse override of `sampling` applied when the runtime determines reasoning is engaged. Used by Qwen3+ (different sampling for /think mode) and Nemotron Nano (different sampling for thinking vs instruct)."
2225
2426
  ),
2226
2427
  reasoning: ReasoningBlockSchema.optional(),
2227
2428
  output: StructuredOutputSchema.optional(),
2228
- toolChoice: z12.enum(["auto", "required", "none"]).optional().describe(
2429
+ toolChoice: z13.enum(["auto", "required", "none"]).optional().describe(
2229
2430
  "Tool selection mode (cloud + llama.cpp + MLX). `auto` lets the model decide; `required` forces a tool call this turn; `none` disables tools."
2230
2431
  ),
2231
2432
  promptTags: PromptTagsSchema.optional()
@@ -2234,16 +2435,16 @@ var ChatModelTuningSchema = ChatModelTuningBaseSchema.extend({
2234
2435
  engine: EngineConfigSchema.optional().describe(
2235
2436
  "Per-model ENGINE launch-flag defaults (llama.cpp `--n-gpu-layers`, `--cpu-moe`, `--spec-type`, \u2026). Applied once at model load, not per request \u2014 so it lives on the top-level tuning object, not inside per-request `profiles`."
2236
2437
  ),
2237
- profiles: z12.record(TuningProfileIdSchema, ChatModelTuningBaseSchema.partial()).optional().describe(
2438
+ profiles: z13.record(TuningProfileIdSchema, ChatModelTuningBaseSchema.partial()).optional().describe(
2238
2439
  "Named tuning presets this model implements. Gezel frontmatter `tuningProfile` selects one; the resolver applies the profile as a layer between installDefault and catalog base. Missing requested profiles walk the canonical fallback chain."
2239
2440
  )
2240
2441
  }).describe("Per-model sampling, reasoning, and output defaults.");
2241
2442
 
2242
2443
  // src/schemas/question.ts
2243
- import { z as z14 } from "zod";
2444
+ import { z as z15 } from "zod";
2244
2445
 
2245
2446
  // src/schemas/npm-package.ts
2246
- import { z as z13 } from "zod";
2447
+ import { z as z14 } from "zod";
2247
2448
  var NPM_NAME_SEGMENT = /^[a-z0-9](?:[a-z0-9._~-]*[a-z0-9])?$/;
2248
2449
  var SEMVER_NUMBER = String.raw`(?:0|[1-9]\d*)`;
2249
2450
  var SEMVER_PART = String.raw`(?:${SEMVER_NUMBER}|[xX*])`;
@@ -2275,36 +2476,41 @@ function isValidNpmRegistryVersion(value) {
2275
2476
  return atoms.length > 0 && atoms.every((atom) => SEMVER_ATOM.test(atom));
2276
2477
  });
2277
2478
  }
2278
- var NpmPackageNameSchema = z13.string().refine(isValidNpmPackageName, {
2479
+ var NpmPackageNameSchema = z14.string().refine(isValidNpmPackageName, {
2279
2480
  message: "must be a lowercase npm registry package name (for example zod or @types/node)"
2280
2481
  });
2281
- var NpmRegistryVersionSchema = z13.string().refine(isValidNpmRegistryVersion, {
2482
+ var NpmRegistryVersionSchema = z14.string().refine(isValidNpmRegistryVersion, {
2282
2483
  message: "must be a registry dist-tag or semver range"
2283
2484
  });
2284
- var NpmRegistryPackageRequestSchema = z13.object({
2485
+ var NpmRegistryPackageRequestSchema = z14.object({
2285
2486
  package: NpmPackageNameSchema,
2286
2487
  version: NpmRegistryVersionSchema.optional()
2287
2488
  }).strict();
2288
2489
 
2289
2490
  // src/schemas/question.ts
2290
- var NpmInstallApprovalDecisionSchema = z14.object({
2491
+ var NpmInstallApprovalDecisionSchema = z15.object({
2291
2492
  package: NpmPackageNameSchema,
2292
2493
  version: NpmRegistryVersionSchema,
2293
- decision: z14.enum(["install", "always", "decline"])
2494
+ decision: z15.enum(["install", "always", "decline"])
2294
2495
  });
2295
- var QuestionAnswerSchema = z14.object({
2496
+ var QuestionAnswerSchema = z15.object({
2296
2497
  /** Indices into `choices` the user picked. Empty when only write-in. */
2297
- selectedChoices: z14.array(z14.number().int().min(0)).optional(),
2498
+ selectedChoices: z15.array(z15.number().int().min(0)).optional(),
2298
2499
  /** Free-text the user typed. Empty when they only clicked choices. */
2299
- writeIn: z14.string().optional(),
2500
+ writeIn: z15.string().optional(),
2300
2501
  /**
2301
2502
  * Set when the user explicitly dismissed BUT wants the gezel to
2302
2503
  * proceed anyway with sensible defaults. Triggers a synthetic
2303
2504
  * follow-up turn with a `[The user wants you to proceed…]` seed
2304
2505
  * so the gezel knows to make decisions on the user's behalf.
2305
- * UI label: "Just do whatever".
2506
+ *
2507
+ * The plain question card no longer offers this — a "decide for me"
2508
+ * button next to a question the gezel is blocked on invited the one
2509
+ * answer that teaches it nothing. It stays on the approval cards that
2510
+ * mean it literally (declining an image generation, an npm install,
2511
+ * a schedule) and on the permission broker's dismissal path.
2306
2512
  */
2307
- declined: z14.boolean().optional(),
2513
+ declined: z15.boolean().optional(),
2308
2514
  /**
2309
2515
  * Set when the user just wants the question to go away — no
2310
2516
  * follow-up turn, no work done, nothing for the gezel to act on.
@@ -2313,97 +2519,112 @@ var QuestionAnswerSchema = z14.object({
2313
2519
  * from `declined` so the model never sees a "user wants defaults"
2314
2520
  * signal that the user didn't intend. UI label: "Skip".
2315
2521
  */
2316
- silentSkip: z14.boolean().optional(),
2522
+ silentSkip: z15.boolean().optional(),
2317
2523
  /**
2318
2524
  * Per-package decisions for `npm-install-approval` questions. When
2319
2525
  * set, the answer handler installs / always-allows / declines each
2320
2526
  * package and emits a single follow-up summary into the session.
2321
2527
  */
2322
- npmInstallDecisions: z14.array(NpmInstallApprovalDecisionSchema).optional(),
2323
- at: z14.string()
2528
+ npmInstallDecisions: z15.array(NpmInstallApprovalDecisionSchema).optional(),
2529
+ at: z15.string()
2324
2530
  });
2325
- var NpmInstallApprovalPackageSchema = z14.object({
2531
+ var NpmInstallApprovalPackageSchema = z15.object({
2326
2532
  package: NpmPackageNameSchema,
2327
2533
  version: NpmRegistryVersionSchema
2328
2534
  });
2329
- var CommandApprovalScopeSchema = z14.enum(["script", "npx"]);
2330
- var CommandApprovalInputFileSchema = z14.object({
2535
+ var CommandApprovalScopeSchema = z15.enum(["script", "npx"]);
2536
+ var CommandApprovalInputFileSchema = z15.object({
2331
2537
  /** Workspace-relative, slash-normalized path shown in the approval prompt. */
2332
- path: z14.string().min(1),
2333
- sha256: z14.string().regex(/^[a-f0-9]{64}$/)
2538
+ path: z15.string().min(1),
2539
+ sha256: z15.string().regex(/^[a-f0-9]{64}$/)
2334
2540
  });
2335
- var CommandApprovalIntentSchema = z14.object({
2336
- kind: z14.literal("command-approval"),
2541
+ var CommandApprovalIntentSchema = z15.object({
2542
+ kind: z15.literal("command-approval"),
2337
2543
  scope: CommandApprovalScopeSchema,
2338
- name: z14.string().min(1),
2339
- body: z14.string().optional(),
2340
- args: z14.array(z14.string()).optional(),
2341
- inputFiles: z14.array(CommandApprovalInputFileSchema).max(128).optional()
2342
- });
2343
- var ToolPermissionIntentSchema = z14.object({
2344
- kind: z14.literal("tool-permission"),
2345
- toolName: z14.string(),
2346
- toolInput: z14.record(z14.string(), z14.unknown())
2347
- });
2348
- var ToolsetInstallApprovalIntentSchema = z14.object({
2349
- kind: z14.literal("toolset-install-approval"),
2350
- toolsetId: z14.string(),
2351
- sourceId: z14.string(),
2352
- version: z14.string(),
2353
- targetProjectId: z14.string(),
2354
- craftbookId: z14.string()
2355
- });
2356
- var ImageGenerationApprovalIntentSchema = z14.object({
2357
- kind: z14.literal("image-generation-approval"),
2358
- provider: z14.string(),
2359
- model: z14.string(),
2544
+ name: z15.string().min(1),
2545
+ body: z15.string().optional(),
2546
+ args: z15.array(z15.string()).optional(),
2547
+ inputFiles: z15.array(CommandApprovalInputFileSchema).max(128).optional()
2548
+ });
2549
+ var ToolPermissionIntentSchema = z15.object({
2550
+ kind: z15.literal("tool-permission"),
2551
+ toolName: z15.string(),
2552
+ toolInput: z15.record(z15.string(), z15.unknown())
2553
+ });
2554
+ var ClaudeUserQuestionOptionSchema = z15.object({
2555
+ label: z15.string().min(1),
2556
+ description: z15.string().optional()
2557
+ });
2558
+ var ClaudeUserQuestionIntentSchema = z15.object({
2559
+ kind: z15.literal("claude-user-question"),
2560
+ /** The CLI's short topic chip for the question (e.g. "Gate block"). */
2561
+ header: z15.string().optional(),
2562
+ /** Options with descriptions; labels mirror the Question's `choices`. */
2563
+ options: z15.array(ClaudeUserQuestionOptionSchema),
2564
+ /** 0-based position within the originating multi-question call. */
2565
+ questionIndex: z15.number().int().min(0),
2566
+ /** Total questions in the originating call (cards appear sequentially). */
2567
+ questionCount: z15.number().int().min(1)
2568
+ });
2569
+ var ToolsetInstallApprovalIntentSchema = z15.object({
2570
+ kind: z15.literal("toolset-install-approval"),
2571
+ toolsetId: z15.string(),
2572
+ sourceId: z15.string(),
2573
+ version: z15.string(),
2574
+ targetProjectId: z15.string(),
2575
+ craftbookId: z15.string()
2576
+ });
2577
+ var ImageGenerationApprovalIntentSchema = z15.object({
2578
+ kind: z15.literal("image-generation-approval"),
2579
+ provider: z15.string(),
2580
+ model: z15.string(),
2360
2581
  /** Truncated prompt the model is about to send. Surfaced verbatim. */
2361
- promptPreview: z14.string(),
2582
+ promptPreview: z15.string(),
2362
2583
  /** Resolved generation size, e.g. '2K 16:9' or '1024x1024'. Optional. */
2363
- estimatedSize: z14.string().optional()
2584
+ estimatedSize: z15.string().optional()
2364
2585
  });
2365
- var VideoGenerationApprovalIntentSchema = z14.object({
2366
- kind: z14.literal("video-generation-approval"),
2367
- provider: z14.string(),
2368
- model: z14.string(),
2369
- promptPreview: z14.string(),
2586
+ var VideoGenerationApprovalIntentSchema = z15.object({
2587
+ kind: z15.literal("video-generation-approval"),
2588
+ provider: z15.string(),
2589
+ model: z15.string(),
2590
+ promptPreview: z15.string(),
2370
2591
  /** Resolved clip shape, e.g. '704×480 · 97f · 24fps'. Optional. */
2371
- estimatedSize: z14.string().optional()
2592
+ estimatedSize: z15.string().optional()
2372
2593
  });
2373
- var ScheduleApprovalIntentSchema = z14.object({
2374
- kind: z14.literal("schedule-approval"),
2375
- typeId: z14.string(),
2376
- craftbookId: z14.string(),
2594
+ var ScheduleApprovalIntentSchema = z15.object({
2595
+ kind: z15.literal("schedule-approval"),
2596
+ typeId: z15.string(),
2597
+ craftbookId: z15.string(),
2377
2598
  /**
2378
2599
  * Recurrence flavor. Absent → 'scheduled' (every question written
2379
2600
  * before this field existed is a cron schedule). 'night-shift' hosts
2380
2601
  * run inside the Night Shift window instead of on a user-visible cron;
2381
2602
  * the card copy switches accordingly.
2382
2603
  */
2383
- runMode: z14.enum(["scheduled", "night-shift"]).optional(),
2604
+ runMode: z15.enum(["scheduled", "night-shift"]).optional(),
2384
2605
  /**
2385
2606
  * 5-field cron expression (UTC). Surfaced verbatim on the card for
2386
2607
  * 'scheduled' hosts; for 'night-shift' hosts it is the internal
2387
2608
  * heartbeat and the card shows the window instead.
2388
2609
  */
2389
- cron: z14.string(),
2390
- overlap: z14.enum(["skip", "queue", "concurrent"]).optional()
2610
+ cron: z15.string(),
2611
+ overlap: z15.enum(["skip", "queue", "concurrent"]).optional()
2391
2612
  });
2392
- var NightShiftReviewIntentSchema = z14.object({
2393
- kind: z14.literal("night-shift-review"),
2613
+ var NightShiftReviewIntentSchema = z15.object({
2614
+ kind: z15.literal("night-shift-review"),
2394
2615
  /** The window's day key (see `nightShiftWindowKey`). */
2395
- windowKey: z14.string(),
2396
- tasksCompleted: z14.number(),
2397
- reports: z14.array(
2398
- z14.object({
2399
- projectId: z14.string(),
2400
- path: z14.string(),
2401
- title: z14.string().optional(),
2402
- actionCount: z14.number()
2616
+ windowKey: z15.string(),
2617
+ tasksCompleted: z15.number(),
2618
+ reports: z15.array(
2619
+ z15.object({
2620
+ projectId: z15.string(),
2621
+ path: z15.string(),
2622
+ title: z15.string().optional(),
2623
+ actionCount: z15.number()
2403
2624
  })
2404
2625
  )
2405
2626
  });
2406
- var TaskPausedReasonSchema = z14.enum([
2627
+ var TaskPausedReasonSchema = z15.enum([
2407
2628
  "gate_exhausted",
2408
2629
  "gate_plateau",
2409
2630
  "gate_unsatisfiable",
@@ -2411,25 +2632,26 @@ var TaskPausedReasonSchema = z14.enum([
2411
2632
  "step_stalled",
2412
2633
  "budget_exhausted"
2413
2634
  ]);
2414
- var TaskPausedIntentSchema = z14.object({
2415
- kind: z14.literal("task-paused"),
2635
+ var TaskPausedIntentSchema = z15.object({
2636
+ kind: z15.literal("task-paused"),
2416
2637
  /** `projectId/num` of the paused task — the dedup key. */
2417
- taskRef: z14.string(),
2418
- stepId: z14.string().optional(),
2638
+ taskRef: z15.string(),
2639
+ stepId: z15.string().optional(),
2419
2640
  reason: TaskPausedReasonSchema
2420
2641
  });
2421
- var QuestionIntentSchema = z14.discriminatedUnion("kind", [
2422
- z14.object({
2423
- kind: z14.literal("npm-install-approval"),
2642
+ var QuestionIntentSchema = z15.discriminatedUnion("kind", [
2643
+ z15.object({
2644
+ kind: z15.literal("npm-install-approval"),
2424
2645
  /**
2425
2646
  * Packages that need approval. Always at least one; multiple when
2426
2647
  * the gezel batched an install call (encouraged) or when we merged
2427
2648
  * a later request into the same pending question for dedup.
2428
2649
  */
2429
- packages: z14.array(NpmInstallApprovalPackageSchema).min(1)
2650
+ packages: z15.array(NpmInstallApprovalPackageSchema).min(1)
2430
2651
  }),
2431
2652
  CommandApprovalIntentSchema,
2432
2653
  ToolPermissionIntentSchema,
2654
+ ClaudeUserQuestionIntentSchema,
2433
2655
  ToolsetInstallApprovalIntentSchema,
2434
2656
  ImageGenerationApprovalIntentSchema,
2435
2657
  VideoGenerationApprovalIntentSchema,
@@ -2437,61 +2659,61 @@ var QuestionIntentSchema = z14.discriminatedUnion("kind", [
2437
2659
  NightShiftReviewIntentSchema,
2438
2660
  TaskPausedIntentSchema
2439
2661
  ]);
2440
- var QuestionSchema = z14.object({
2441
- id: z14.string(),
2442
- projectId: z14.string(),
2443
- gezelId: z14.string(),
2444
- sessionId: z14.string(),
2662
+ var QuestionSchema = z15.object({
2663
+ id: z15.string(),
2664
+ projectId: z15.string(),
2665
+ gezelId: z15.string(),
2666
+ sessionId: z15.string(),
2445
2667
  /** Body of the question — supports markdown. */
2446
- prompt: z14.string().min(1),
2668
+ prompt: z15.string().min(1),
2447
2669
  /** Optional preset choices. Empty / omitted => write-in only. */
2448
- choices: z14.array(z14.string()).max(20).optional(),
2670
+ choices: z15.array(z15.string()).max(20).optional(),
2449
2671
  /** Whether the user can also type a write-in alongside choices. Default true. */
2450
- allowWriteIn: z14.boolean().optional(),
2672
+ allowWriteIn: z15.boolean().optional(),
2451
2673
  /** Whether multiple choices may be selected. Default false. */
2452
- multiSelect: z14.boolean().optional(),
2674
+ multiSelect: z15.boolean().optional(),
2453
2675
  /**
2454
2676
  * Approval-flow attachment: a task this question is *about*. Stored in
2455
2677
  * `projectId/num` form so existing parsing helpers work. The UI shows
2456
2678
  * the task title + status above the prompt and offers an "Open task"
2457
2679
  * link.
2458
2680
  */
2459
- taskRef: z14.string().optional(),
2681
+ taskRef: z15.string().optional(),
2460
2682
  /**
2461
2683
  * Approval-flow attachment: a document this question is *about*.
2462
2684
  * Project-relative path when `projectId` is set, otherwise into the
2463
2685
  * global `~/.gezel/documents/` library. The UI renders a collapsed
2464
2686
  * preview + "Open document" link.
2465
2687
  */
2466
- documentPath: z14.string().optional(),
2688
+ documentPath: z15.string().optional(),
2467
2689
  /**
2468
2690
  * Service-created specialized-question marker (see `QuestionIntent`
2469
2691
  * for context). Absent for plain user-facing questions asked via
2470
2692
  * the `ask_user_question` MCP tool.
2471
2693
  */
2472
2694
  intent: QuestionIntentSchema.optional(),
2473
- createdAt: z14.string(),
2695
+ createdAt: z15.string(),
2474
2696
  /** Set once the user has answered (or declined). */
2475
2697
  answer: QuestionAnswerSchema.optional()
2476
2698
  });
2477
2699
 
2478
2700
  // src/schemas/recognition.ts
2479
- import { z as z15 } from "zod";
2480
- var ImageExifSchema = z15.object({
2481
- make: z15.string().optional(),
2482
- model: z15.string().optional(),
2483
- lensModel: z15.string().optional(),
2484
- dateTimeOriginal: z15.string().optional(),
2485
- orientation: z15.number().int().min(1).max(8).optional(),
2486
- software: z15.string().optional(),
2487
- imageDescription: z15.string().optional()
2488
- });
2489
- var ImageStaticMetaSchema = z15.object({
2490
- format: z15.enum(["png", "jpeg", "gif", "webp", "svg", "unknown"]),
2491
- width: z15.number().int().positive().optional(),
2492
- height: z15.number().int().positive().optional(),
2493
- byteLength: z15.number().int().nonnegative(),
2494
- sha256: z15.string().regex(/^[a-f0-9]{64}$/),
2701
+ import { z as z16 } from "zod";
2702
+ var ImageExifSchema = z16.object({
2703
+ make: z16.string().optional(),
2704
+ model: z16.string().optional(),
2705
+ lensModel: z16.string().optional(),
2706
+ dateTimeOriginal: z16.string().optional(),
2707
+ orientation: z16.number().int().min(1).max(8).optional(),
2708
+ software: z16.string().optional(),
2709
+ imageDescription: z16.string().optional()
2710
+ });
2711
+ var ImageStaticMetaSchema = z16.object({
2712
+ format: z16.enum(["png", "jpeg", "gif", "webp", "svg", "unknown"]),
2713
+ width: z16.number().int().positive().optional(),
2714
+ height: z16.number().int().positive().optional(),
2715
+ byteLength: z16.number().int().nonnegative(),
2716
+ sha256: z16.string().regex(/^[a-f0-9]{64}$/),
2495
2717
  /**
2496
2718
  * PNG `tEXt`/`iTXt`/`zTXt` keyword→value pairs. Generation provenance lives
2497
2719
  * here (A1111 writes `parameters`, ComfyUI writes `prompt`/`workflow`) and
@@ -2500,153 +2722,171 @@ var ImageStaticMetaSchema = z15.object({
2500
2722
  * Attacker-controlled: anyone can author a PNG whose `Description` chunk
2501
2723
  * reads "Ignore previous instructions". Renderers MUST fence and cap this.
2502
2724
  */
2503
- pngText: z15.record(z15.string(), z15.string()).optional(),
2725
+ pngText: z16.record(z16.string(), z16.string()).optional(),
2504
2726
  exif: ImageExifSchema.optional(),
2505
2727
  /** Withheld from every prompt. See the schema doc above. */
2506
- gps: z15.object({ lat: z15.number(), lon: z15.number() }).optional(),
2728
+ gps: z16.object({ lat: z16.number(), lon: z16.number() }).optional(),
2507
2729
  /** True when the file carried location data we deliberately dropped. */
2508
- gpsRedacted: z15.boolean().optional(),
2730
+ gpsRedacted: z16.boolean().optional(),
2509
2731
  /**
2510
2732
  * Heuristic from dimensions, format, and metadata — drives `auto` mode
2511
2733
  * selection without paying a classifier call.
2512
2734
  */
2513
- likelyScreenshot: z15.boolean().optional()
2735
+ likelyScreenshot: z16.boolean().optional()
2514
2736
  });
2515
- var RecognitionModeSchema = z15.enum(["describe", "ocr", "ui", "extract"]);
2516
- var RecognitionModeRequestSchema = z15.enum(["auto", "describe", "ocr", "ui", "extract"]);
2517
- var ImageRecognitionSchema = z15.object({
2518
- schemaVersion: z15.literal(1),
2519
- sha256: z15.string().regex(/^[a-f0-9]{64}$/),
2737
+ var RecognitionModeSchema = z16.enum(["describe", "ocr", "ui", "extract"]);
2738
+ var RecognitionModeRequestSchema = z16.enum(["auto", "describe", "ocr", "ui", "extract"]);
2739
+ var ImageRecognitionSchema = z16.object({
2740
+ schemaVersion: z16.literal(1),
2741
+ sha256: z16.string().regex(/^[a-f0-9]{64}$/),
2520
2742
  meta: ImageStaticMetaSchema,
2521
- modes: z15.array(RecognitionModeSchema).min(1),
2522
- description: z15.string().optional(),
2523
- ocrText: z15.string().optional(),
2524
- structured: z15.object({
2525
- templateId: z15.string().optional(),
2526
- data: z15.unknown()
2743
+ modes: z16.array(RecognitionModeSchema).min(1),
2744
+ description: z16.string().optional(),
2745
+ ocrText: z16.string().optional(),
2746
+ structured: z16.object({
2747
+ templateId: z16.string().optional(),
2748
+ data: z16.unknown()
2527
2749
  }).optional(),
2528
- engine: z15.enum(["llama-cpp", "mlx", "mock", "none"]),
2529
- modelId: z15.string(),
2530
- status: z15.enum(["ok", "partial", "failed", "static-only"]),
2531
- failureReason: z15.string().optional(),
2532
- durationMs: z15.number().int().nonnegative(),
2533
- at: z15.string()
2750
+ engine: z16.enum(["llama-cpp", "mlx", "mock", "none"]),
2751
+ modelId: z16.string(),
2752
+ status: z16.enum(["ok", "partial", "failed", "static-only"]),
2753
+ failureReason: z16.string().optional(),
2754
+ durationMs: z16.number().int().nonnegative(),
2755
+ at: z16.string()
2534
2756
  });
2535
- var MessageImageDigestSchema = z15.object({
2757
+ var MessageImageDigestSchema = z16.object({
2536
2758
  /** The markdown ref exactly as it appears in the message body. */
2537
- ref: z15.string(),
2538
- sha256: z15.string().regex(/^[a-f0-9]{64}$/),
2759
+ ref: z16.string(),
2760
+ sha256: z16.string().regex(/^[a-f0-9]{64}$/),
2539
2761
  /** Pre-rendered, already capped and fenced-safe. */
2540
- digest: z15.string(),
2541
- modelId: z15.string(),
2542
- modes: z15.array(RecognitionModeSchema),
2543
- status: z15.enum(["ok", "partial", "failed", "static-only"]),
2544
- at: z15.string()
2762
+ digest: z16.string(),
2763
+ modelId: z16.string(),
2764
+ modes: z16.array(RecognitionModeSchema),
2765
+ status: z16.enum(["ok", "partial", "failed", "static-only"]),
2766
+ at: z16.string()
2545
2767
  });
2546
- var RecognitionRequestSchema = z15.object({
2768
+ var RecognitionRequestSchema = z16.object({
2547
2769
  /** Project-relative artifact path, e.g. `attachments/<uuid>.png`. */
2548
- artifactPath: z15.string().min(1).optional(),
2549
- data: z15.string().min(1).optional(),
2550
- mimeType: z15.string().optional(),
2770
+ artifactPath: z16.string().min(1).optional(),
2771
+ data: z16.string().min(1).optional(),
2772
+ mimeType: z16.string().optional(),
2551
2773
  mode: RecognitionModeRequestSchema.default("auto"),
2552
2774
  /** JSON Schema for `extract` mode — fed to llama-server `response_format`. */
2553
- schema: z15.unknown().optional(),
2775
+ schema: z16.unknown().optional(),
2554
2776
  /** Overrides the configured recognition model for this call. */
2555
- model: z15.string().optional()
2777
+ model: z16.string().optional()
2556
2778
  });
2557
- var RecognitionHealthSchema = z15.object({
2558
- state: z15.enum(["ok", "no-model", "not-configured", "error"]),
2559
- modelId: z15.string().optional(),
2560
- detail: z15.string().optional()
2779
+ var RecognitionHealthSchema = z16.object({
2780
+ state: z16.enum(["ok", "no-model", "not-configured", "error"]),
2781
+ modelId: z16.string().optional(),
2782
+ detail: z16.string().optional()
2561
2783
  });
2562
- var RecognitionPullEventSchema = z15.union([
2563
- z15.object({
2564
- type: z15.literal("progress"),
2565
- bytesWritten: z15.number().int().nonnegative(),
2566
- totalBytes: z15.number().int().nonnegative().optional()
2784
+ var RecognitionPullEventSchema = z16.union([
2785
+ z16.object({
2786
+ type: z16.literal("progress"),
2787
+ bytesWritten: z16.number().int().nonnegative(),
2788
+ totalBytes: z16.number().int().nonnegative().optional()
2567
2789
  }),
2568
- z15.object({ type: z15.literal("error"), error: z15.string() }),
2569
- z15.object({ type: z15.literal("done"), id: z15.string() })
2790
+ z16.object({ type: z16.literal("error"), error: z16.string() }),
2791
+ z16.object({ type: z16.literal("done"), id: z16.string() })
2570
2792
  ]);
2571
- var RecognitionCatalogEntrySchema = z15.object({
2572
- id: z15.string(),
2573
- name: z15.string(),
2574
- description: z15.string(),
2575
- license: z15.string(),
2576
- approxSizeBytes: z15.number().int().nonnegative(),
2577
- recoScore: z15.number()
2793
+ var RecognitionCatalogEntrySchema = z16.object({
2794
+ id: z16.string(),
2795
+ name: z16.string(),
2796
+ description: z16.string(),
2797
+ license: z16.string(),
2798
+ approxSizeBytes: z16.number().int().nonnegative(),
2799
+ recoScore: z16.number()
2578
2800
  });
2579
- var ListRecognitionCatalogResponseSchema = z15.object({
2580
- models: z15.array(RecognitionCatalogEntrySchema)
2801
+ var ListRecognitionCatalogResponseSchema = z16.object({
2802
+ models: z16.array(RecognitionCatalogEntrySchema)
2581
2803
  });
2582
- var InstalledRecognitionModelSchema = z15.object({
2583
- id: z15.string(),
2584
- name: z15.string(),
2585
- approxSizeBytes: z15.number().int().nonnegative(),
2586
- installedAt: z15.string(),
2587
- weightsPath: z15.string().optional(),
2588
- mmprojPath: z15.string().optional()
2804
+ var InstalledRecognitionModelSchema = z16.object({
2805
+ id: z16.string(),
2806
+ name: z16.string(),
2807
+ approxSizeBytes: z16.number().int().nonnegative(),
2808
+ installedAt: z16.string(),
2809
+ weightsPath: z16.string().optional(),
2810
+ mmprojPath: z16.string().optional()
2811
+ });
2812
+ var ListInstalledRecognitionModelsResponseSchema = z16.object({
2813
+ models: z16.array(InstalledRecognitionModelSchema)
2814
+ });
2815
+
2816
+ // src/schemas/session-lineage.ts
2817
+ import { z as z17 } from "zod";
2818
+ var SessionLinkSchema = z17.object({
2819
+ sessionId: z17.string(),
2820
+ gezelId: z17.string()
2589
2821
  });
2590
- var ListInstalledRecognitionModelsResponseSchema = z15.object({
2591
- models: z15.array(InstalledRecognitionModelSchema)
2822
+ var SessionParentSchema = SessionLinkSchema.extend({
2823
+ kind: z17.enum(["delegation", "consultation", "task-entry", "task-handoff"])
2592
2824
  });
2593
2825
 
2594
2826
  // src/schemas/session-telemetry.ts
2595
- import { z as z16 } from "zod";
2596
- var SessionGpuTaskSchema = z16.enum([
2827
+ import { z as z18 } from "zod";
2828
+ var SessionGpuTaskSchema = z18.enum([
2597
2829
  "image_generation",
2598
2830
  "video_generation",
2599
2831
  "image_recognition"
2600
2832
  ]);
2601
- var SessionTurnTelemetrySchema = z16.object({
2833
+ var SessionTurnPhaseSchema = z18.enum(["preparing", "recall", "provider"]);
2834
+ var SessionTurnTelemetrySchema = z18.object({
2602
2835
  /** Epoch ms when the in-flight turn started. */
2603
- startedAt: z16.number(),
2604
- streamedContentChars: z16.number().int().nonnegative(),
2605
- toolCalls: z16.number().int().nonnegative(),
2606
- fileMutations: z16.number().int().nonnegative()
2607
- });
2608
- var SessionTelemetrySchema = z16.object({
2609
- sessionId: z16.string(),
2610
- gezelId: z16.string(),
2611
- projectId: z16.string(),
2836
+ startedAt: z18.number(),
2837
+ phase: SessionTurnPhaseSchema,
2838
+ /** Epoch ms when `phase` last changed. */
2839
+ phaseStartedAt: z18.number(),
2840
+ /** Provider requests issued by this user-visible turn, including retries. */
2841
+ providerRequestsStarted: z18.number().int().nonnegative(),
2842
+ streamedContentChars: z18.number().int().nonnegative(),
2843
+ toolCalls: z18.number().int().nonnegative(),
2844
+ fileMutations: z18.number().int().nonnegative()
2845
+ });
2846
+ var SessionTelemetrySchema = z18.object({
2847
+ sessionId: z18.string(),
2848
+ gezelId: z18.string(),
2849
+ projectId: z18.string(),
2612
2850
  /** True while a send is currently running for this session. */
2613
- inflight: z16.boolean(),
2614
- turnsStarted: z16.number().int().nonnegative(),
2615
- deltaChunks: z16.number().int().nonnegative(),
2616
- streamedContentChars: z16.number().int().nonnegative(),
2617
- wirePulses: z16.number().int().nonnegative(),
2618
- heartbeats: z16.number().int().nonnegative(),
2619
- enginePhaseEvents: z16.number().int().nonnegative(),
2851
+ inflight: z18.boolean(),
2852
+ turnsStarted: z18.number().int().nonnegative(),
2853
+ /** Provider requests issued across all turns in this daemon process. */
2854
+ providerRequestsStarted: z18.number().int().nonnegative(),
2855
+ deltaChunks: z18.number().int().nonnegative(),
2856
+ streamedContentChars: z18.number().int().nonnegative(),
2857
+ wirePulses: z18.number().int().nonnegative(),
2858
+ heartbeats: z18.number().int().nonnegative(),
2859
+ enginePhaseEvents: z18.number().int().nonnegative(),
2620
2860
  /**
2621
2861
  * `engine_phase === 'generating'` transitions — roughly one per completion
2622
2862
  * request the engine served (the slot-launch granularity stall logic and
2623
2863
  * the eval chatter threshold were calibrated against).
2624
2864
  */
2625
- generationSpurts: z16.number().int().nonnegative(),
2626
- toolCalls: z16.number().int().nonnegative(),
2627
- toolArgChars: z16.number().int().nonnegative(),
2628
- fileMutations: z16.number().int().nonnegative(),
2629
- gpuEvents: z16.number().int().nonnegative(),
2865
+ generationSpurts: z18.number().int().nonnegative(),
2866
+ toolCalls: z18.number().int().nonnegative(),
2867
+ toolArgChars: z18.number().int().nonnegative(),
2868
+ fileMutations: z18.number().int().nonnegative(),
2869
+ gpuEvents: z18.number().int().nonnegative(),
2630
2870
  gpuTaskActive: SessionGpuTaskSchema.nullable(),
2631
2871
  /** Epoch ms of the last streamed signal (delta / pulse / heartbeat / phase). */
2632
- lastStreamActivityAt: z16.number().nullable(),
2633
- lastToolActivityAt: z16.number().nullable(),
2634
- lastMutationAt: z16.number().nullable(),
2635
- lastGpuActivityAt: z16.number().nullable(),
2872
+ lastStreamActivityAt: z18.number().nullable(),
2873
+ lastToolActivityAt: z18.number().nullable(),
2874
+ lastMutationAt: z18.number().nullable(),
2875
+ lastGpuActivityAt: z18.number().nullable(),
2636
2876
  /** Max of all activity timestamps — "when did ANY progress signal last fire". */
2637
- lastProgressAt: z16.number().nullable(),
2877
+ lastProgressAt: z18.number().nullable(),
2638
2878
  /** Counters scoped to the currently-running turn; null between turns. */
2639
2879
  currentTurn: SessionTurnTelemetrySchema.nullable()
2640
2880
  });
2641
- var SessionTelemetryListResponseSchema = z16.object({
2881
+ var SessionTelemetryListResponseSchema = z18.object({
2642
2882
  /** Bumped when counter semantics change; consumers gate on it. */
2643
- version: z16.literal(1),
2644
- capturedAt: z16.number(),
2645
- sessions: z16.array(SessionTelemetrySchema)
2883
+ version: z18.literal(1),
2884
+ capturedAt: z18.number(),
2885
+ sessions: z18.array(SessionTelemetrySchema)
2646
2886
  });
2647
2887
 
2648
2888
  // src/schemas/gezel.ts
2649
- var ProviderNameSchema = z17.enum([
2889
+ var ProviderNameSchema = z19.enum([
2650
2890
  "copilot",
2651
2891
  "openai",
2652
2892
  "anthropic",
@@ -2668,27 +2908,27 @@ var ProviderNameSchema = z17.enum([
2668
2908
  // forward-pass is remoted — so `remote` is NOT a local provider.
2669
2909
  "remote"
2670
2910
  ]);
2671
- var GezelGenderSchema = z17.enum(["male", "female", "non-binary"]);
2672
- var FixedFunctionConfigSchema = z17.object({
2911
+ var GezelGenderSchema = z19.enum(["male", "female", "non-binary"]);
2912
+ var FixedFunctionConfigSchema = z19.object({
2673
2913
  /** MCP tool name to forward to (e.g. `'generate_image'`). */
2674
- tool: z17.string().min(1),
2914
+ tool: z19.string().min(1),
2675
2915
  /** Argument key on the tool that receives the user's message text. */
2676
- promptKey: z17.string().min(1).default("prompt"),
2916
+ promptKey: z19.string().min(1).default("prompt"),
2677
2917
  /** Defaults merged into every call; user text on `promptKey` always wins. */
2678
- defaults: z17.record(z17.string(), z17.unknown()).optional()
2918
+ defaults: z19.record(z19.string(), z19.unknown()).optional()
2679
2919
  });
2680
- var GezelTraitSchema = z17.object({
2681
- id: z17.string(),
2920
+ var GezelTraitSchema = z19.object({
2921
+ id: z19.string(),
2682
2922
  /** One imperative second-person sentence, rendered as a prompt bullet. */
2683
- text: z17.string().min(1).max(200),
2684
- adoptedAt: z17.string(),
2685
- source: z17.enum(["levelup", "manual"]).optional()
2923
+ text: z19.string().min(1).max(200),
2924
+ adoptedAt: z19.string(),
2925
+ source: z19.enum(["levelup", "manual"]).optional()
2686
2926
  });
2687
- var GezelFrontmatterSchema = z17.object({
2927
+ var GezelFrontmatterSchema = z19.object({
2688
2928
  id: EntityIdSchema.optional(),
2689
- name: z17.string(),
2690
- description: z17.string().optional(),
2691
- role: z17.string().optional(),
2929
+ name: z19.string(),
2930
+ description: z19.string().optional(),
2931
+ role: z19.string().optional(),
2692
2932
  /**
2693
2933
  * Kebab-case slug derived from `role` (or `gezel-N` when role is absent),
2694
2934
  * globally unique across the install. Collisions get `-2`, `-3`, …
@@ -2696,7 +2936,7 @@ var GezelFrontmatterSchema = z17.object({
2696
2936
  * identifier for @-mentions and as the sole rendered identifier when
2697
2937
  * `config.roleBasedNameOnlyMode` is enabled.
2698
2938
  */
2699
- roleBasedName: z17.string().optional(),
2939
+ roleBasedName: z19.string().optional(),
2700
2940
  /**
2701
2941
  * One of `male` / `female` / `non-binary`. Assigned at creation time
2702
2942
  * from the matching gendered first-name pool (with a small chance of
@@ -2705,9 +2945,9 @@ var GezelFrontmatterSchema = z17.object({
2705
2945
  * own prompt. Absent on legacy gezels, where references omit pronouns.
2706
2946
  */
2707
2947
  gender: GezelGenderSchema.optional(),
2708
- model: z17.string().optional(),
2948
+ model: z19.string().optional(),
2709
2949
  provider: ProviderNameSchema.optional(),
2710
- reasoningEffort: z17.string().optional(),
2950
+ reasoningEffort: z19.string().optional(),
2711
2951
  /**
2712
2952
  * Per-gezel sampling / reasoning / structured-output / tool-call overrides.
2713
2953
  * Sparse — only set fields override the catalog's recommended defaults.
@@ -2740,8 +2980,8 @@ var GezelFrontmatterSchema = z17.object({
2740
2980
  * low-temperature `thinking-precise` without locking the user out.
2741
2981
  */
2742
2982
  suggestedTuningProfile: TuningProfileIdSchema.optional(),
2743
- tools: z17.array(z17.string()).optional(),
2744
- tags: z17.array(z17.string()).optional(),
2983
+ tools: z19.array(z19.string()).optional(),
2984
+ tags: z19.array(z19.string()).optional(),
2745
2985
  /**
2746
2986
  * When set, switches this gezel into "fixed-function" mode: chat
2747
2987
  * messages bypass the LLM and forward to the named MCP tool. See
@@ -2751,14 +2991,20 @@ var GezelFrontmatterSchema = z17.object({
2751
2991
  */
2752
2992
  fixedFunction: FixedFunctionConfigSchema.optional(),
2753
2993
  /** Ollama-only: override the context window (tokens) for this gezel. */
2754
- numCtx: z17.number().int().positive().optional(),
2994
+ numCtx: z19.number().int().positive().optional(),
2755
2995
  /** When false, suppresses auto-recall on session start for this gezel. */
2756
- autoRecall: z17.boolean().optional(),
2996
+ autoRecall: z19.boolean().optional(),
2997
+ /**
2998
+ * Per-gezel proactive indexed-context policy. Supersedes `autoRecall` when
2999
+ * present; absent inherits the install default. The generic `search` tool is
3000
+ * still available in `off` mode.
3001
+ */
3002
+ retrieval: RetrievalPolicySchema.optional(),
2757
3003
  /**
2758
3004
  * Chat bubble font id (one of `GEZEL_CHAT_FONTS[*].id`). When unset or
2759
3005
  * unrecognized, chat bubbles inherit the app default (Hanken Grotesk).
2760
3006
  */
2761
- font: z17.string().optional(),
3007
+ font: z19.string().optional(),
2762
3008
  /**
2763
3009
  * Kokoro TTS voice id (one of `KOKORO_VOICES[*].id`, e.g. `af_heart`,
2764
3010
  * `bm_george`). Drives spoken audio rendering — `synthesize_speech`
@@ -2767,7 +3013,7 @@ var GezelFrontmatterSchema = z17.object({
2767
3013
  * dialog. Absent on legacy gezels — synthesize falls back to the
2768
3014
  * default voice (`af_heart`) when missing.
2769
3015
  */
2770
- voice: z17.string().optional(),
3016
+ voice: z19.string().optional(),
2771
3017
  /**
2772
3018
  * Provenance: the id of the gilde catalog template this gezel was
2773
3019
  * created from, if any. Written by exact-template or about-omitted
@@ -2775,7 +3021,7 @@ var GezelFrontmatterSchema = z17.object({
2775
3021
  * Absent on bespoke-generated or hand-authored gezels. The UI uses
2776
3022
  * this to offer "reset to original template" on the about editor.
2777
3023
  */
2778
- templateId: z17.string().optional(),
3024
+ templateId: z19.string().optional(),
2779
3025
  /**
2780
3026
  * Provenance: the semver of the template version installed at create
2781
3027
  * time. Paired with `templateId`. Absent on gezels created before this
@@ -2783,7 +3029,7 @@ var GezelFrontmatterSchema = z17.object({
2783
3029
  * source version" and offers a refresh to current latest without
2784
3030
  * comparing.
2785
3031
  */
2786
- templateVersion: z17.string().optional(),
3032
+ templateVersion: z19.string().optional(),
2787
3033
  /**
2788
3034
  * Copilot-only: when true, deny the Copilot CLI's built-in tools
2789
3035
  * (bash, web_fetch, view, str_replace_editor, read_file, write_file,
@@ -2792,7 +3038,7 @@ var GezelFrontmatterSchema = z17.object({
2792
3038
  * itself defaults to the sandboxed MCP surface.
2793
3039
  * Provider other than copilot: ignored.
2794
3040
  */
2795
- sandboxCopilot: z17.boolean().optional(),
3041
+ sandboxCopilot: z19.boolean().optional(),
2796
3042
  /**
2797
3043
  * `anthropic-cli`-only: per-gezel override for the Claude CLI permission
2798
3044
  * mode. Forwarded as `--permission-mode <value>` to each `claude` invocation.
@@ -2818,7 +3064,7 @@ var GezelFrontmatterSchema = z17.object({
2818
3064
  * abstract sigil instead of the parametric poppetje. Default (absent /
2819
3065
  * false) renders the poppetje everywhere. Toggled from Gezel Detail.
2820
3066
  */
2821
- iconOverride: z17.boolean().optional(),
3067
+ iconOverride: z19.boolean().optional(),
2822
3068
  /**
2823
3069
  * Standing behavior traits, adopted through the growth system (or
2824
3070
  * hand-authored). Rendered as their own `### Traits` block in the
@@ -2827,7 +3073,7 @@ var GezelFrontmatterSchema = z17.object({
2827
3073
  * The frontmatter list is AUTHORITATIVE for what's active — growth.json
2828
3074
  * keeps the evidence-bearing adoption log.
2829
3075
  */
2830
- traits: z17.array(GezelTraitSchema).max(8).optional(),
3076
+ traits: z19.array(GezelTraitSchema).max(8).optional(),
2831
3077
  /**
2832
3078
  * Overrides `config.recognition.mode` for this gezel. A gezel whose job is
2833
3079
  * reading screenshots sets `always`; everyone else inherits.
@@ -2837,46 +3083,48 @@ var GezelFrontmatterSchema = z17.object({
2837
3083
  * support burden. Native vision is a property of the model *install*, not of
2838
3084
  * the gezel, so it has no frontmatter counterpart.
2839
3085
  */
2840
- recognition: z17.enum(["auto", "always", "off"]).optional()
3086
+ recognition: z19.enum(["auto", "always", "off"]).optional()
2841
3087
  });
2842
- var GezelSectionSchema = z17.object({
2843
- heading: z17.string(),
2844
- template: z17.string().optional(),
2845
- params: z17.record(z17.string(), z17.string()).optional(),
2846
- body: z17.string()
3088
+ var GezelSectionSchema = z19.object({
3089
+ heading: z19.string(),
3090
+ template: z19.string().optional(),
3091
+ params: z19.record(z19.string(), z19.string()).optional(),
3092
+ body: z19.string()
2847
3093
  });
2848
- var ParsedGezelSchema = z17.object({
3094
+ var ParsedGezelSchema = z19.object({
2849
3095
  frontmatter: GezelFrontmatterSchema,
2850
- sections: z17.array(GezelSectionSchema),
2851
- source: z17.string()
3096
+ sections: z19.array(GezelSectionSchema),
3097
+ source: z19.string()
2852
3098
  });
2853
- var GezelSummarySchema = z17.object({
3099
+ var GezelSummarySchema = z19.object({
2854
3100
  id: EntityIdSchema,
2855
- name: z17.string(),
2856
- description: z17.string().optional(),
2857
- role: z17.string().optional(),
3101
+ name: z19.string(),
3102
+ description: z19.string().optional(),
3103
+ role: z19.string().optional(),
2858
3104
  /** Mirrors `GezelFrontmatter.roleBasedName`. */
2859
- roleBasedName: z17.string().optional(),
3105
+ roleBasedName: z19.string().optional(),
2860
3106
  /** Mirrors `GezelFrontmatter.gender`. */
2861
3107
  gender: GezelGenderSchema.optional(),
2862
- model: z17.string().optional(),
3108
+ model: z19.string().optional(),
2863
3109
  provider: ProviderNameSchema.optional(),
2864
- reasoningEffort: z17.string().optional(),
3110
+ reasoningEffort: z19.string().optional(),
2865
3111
  /** Mirrors `GezelFrontmatter.tuningProfile`. */
2866
3112
  tuningProfile: TuningProfileIdSchema.optional(),
2867
3113
  /** Mirrors `GezelFrontmatter.suggestedTuningProfile`. */
2868
3114
  suggestedTuningProfile: TuningProfileIdSchema.optional(),
2869
- numCtx: z17.number().int().positive().optional(),
2870
- autoRecall: z17.boolean().optional(),
2871
- font: z17.string().optional(),
3115
+ numCtx: z19.number().int().positive().optional(),
3116
+ autoRecall: z19.boolean().optional(),
3117
+ /** Mirrors `GezelFrontmatter.retrieval`. */
3118
+ retrieval: RetrievalPolicySchema.optional(),
3119
+ font: z19.string().optional(),
2872
3120
  /** Mirrors `GezelFrontmatter.voice` — Kokoro TTS voice id. */
2873
- voice: z17.string().optional(),
3121
+ voice: z19.string().optional(),
2874
3122
  /** Mirrors `GezelFrontmatter.templateId` when the gezel came from a gilde template. */
2875
- templateId: z17.string().optional(),
3123
+ templateId: z19.string().optional(),
2876
3124
  /** Mirrors `GezelFrontmatter.templateVersion`. */
2877
- templateVersion: z17.string().optional(),
3125
+ templateVersion: z19.string().optional(),
2878
3126
  /** Mirrors `GezelFrontmatter.sandboxCopilot`. */
2879
- sandboxCopilot: z17.boolean().optional(),
3127
+ sandboxCopilot: z19.boolean().optional(),
2880
3128
  /** Mirrors `GezelFrontmatter.claudePermissionMode`. */
2881
3129
  claudePermissionMode: ClaudePermissionModeSchema.optional(),
2882
3130
  /** Mirrors `GezelFrontmatter.codexPermissionMode`. */
@@ -2889,7 +3137,7 @@ var GezelSummarySchema = z17.object({
2889
3137
  * affordance.
2890
3138
  */
2891
3139
  fixedFunction: FixedFunctionConfigSchema.optional(),
2892
- icon: z17.string().optional(),
3140
+ icon: z19.string().optional(),
2893
3141
  /**
2894
3142
  * The resolved poppetje character data for this gezel. Inlined on
2895
3143
  * every list/detail response so the UI can render the parametric SVG
@@ -2899,9 +3147,9 @@ var GezelSummarySchema = z17.object({
2899
3147
  */
2900
3148
  poppetje: PoppetjeSchema.optional(),
2901
3149
  /** Mirrors `GezelFrontmatter.iconOverride`. */
2902
- iconOverride: z17.boolean().optional(),
3150
+ iconOverride: z19.boolean().optional(),
2903
3151
  /** Mirrors `GezelFrontmatter.recognition`. */
2904
- recognition: z17.enum(["auto", "always", "off"]).optional(),
3152
+ recognition: z19.enum(["auto", "always", "off"]).optional(),
2905
3153
  /**
2906
3154
  * Where this gezel lives. `global` (the default when absent — back-compat
2907
3155
  * with every gezel on disk before this field existed) is the install-wide
@@ -2911,15 +3159,15 @@ var GezelSummarySchema = z17.object({
2911
3159
  * badges project-scoped gezels and the roster only surfaces them inside
2912
3160
  * their own project.
2913
3161
  */
2914
- scope: z17.enum(["global", "project"]).optional(),
3162
+ scope: z19.enum(["global", "project"]).optional(),
2915
3163
  /**
2916
3164
  * Filesystem ownership boundary, distinct from `scope` above. Shared gezel
2917
3165
  * identity lives in the installer-managed machine root; chats, memories,
2918
3166
  * growth, credentials, and installed toolsets remain in the user home.
2919
3167
  */
2920
- storageScope: z17.enum(["user", "machine-shared"]).optional(),
3168
+ storageScope: z19.enum(["user", "machine-shared"]).optional(),
2921
3169
  /** Mirrors `GezelFrontmatter.traits`. */
2922
- traits: z17.array(GezelTraitSchema).optional(),
3170
+ traits: z19.array(GezelTraitSchema).optional(),
2923
3171
  /**
2924
3172
  * Lightweight growth summary (level + pending level-up flag),
2925
3173
  * hydrated from growth.json and inlined on list/detail responses —
@@ -2927,11 +3175,11 @@ var GezelSummarySchema = z17.object({
2927
3175
  * without N+1 follow-up requests.
2928
3176
  */
2929
3177
  growth: GezelGrowthSummarySchema.optional(),
2930
- updatedAt: z17.string()
3178
+ updatedAt: z19.string()
2931
3179
  });
2932
3180
  var GezelDetailSchema = GezelSummarySchema.extend({
2933
3181
  parsed: ParsedGezelSchema,
2934
- about: z17.string(),
3182
+ about: z19.string(),
2935
3183
  /**
2936
3184
  * Optional contents of the per-gezel `tools.md` file. When present
2937
3185
  * (non-null), fully replaces the auto-injected `## Tools available
@@ -2941,43 +3189,87 @@ var GezelDetailSchema = GezelSummarySchema.extend({
2941
3189
  * default) means no override file exists and the runtime renders
2942
3190
  * the auto-block from the live MCP bridge.
2943
3191
  */
2944
- toolsMd: z17.string().nullable().default(null)
3192
+ toolsMd: z19.string().nullable().default(null)
2945
3193
  });
2946
- var ToolCallImageSchema = z17.object({
3194
+ var ToolCallImageSchema = z19.object({
2947
3195
  /** Path relative to the project's artifacts/ root (e.g. `sessions/2026-04-19_143015_snake-test/tool-3-img-0.png`). */
2948
- path: z17.string(),
3196
+ path: z19.string(),
2949
3197
  /** MIME type of the image, used by the UI to set the right `<img>` src URL. */
2950
- mimeType: z17.string()
3198
+ mimeType: z19.string()
2951
3199
  });
2952
- var ToolCallAudioSchema = z17.object({
3200
+ var ToolCallAudioSchema = z19.object({
2953
3201
  /** Path relative to the project's artifacts/ root. */
2954
- path: z17.string(),
3202
+ path: z19.string(),
2955
3203
  /** MIME type, e.g. `audio/wav`. */
2956
- mimeType: z17.string(),
3204
+ mimeType: z19.string(),
2957
3205
  /** Duration in seconds when known — used by the UI to show length without preloading the blob. */
2958
- durationSeconds: z17.number().optional(),
3206
+ durationSeconds: z19.number().optional(),
2959
3207
  /** Voice id used to produce this audio (TTS only). */
2960
- voice: z17.string().optional()
3208
+ voice: z19.string().optional()
2961
3209
  });
2962
- var ToolCallVideoSchema = z17.object({
3210
+ var ToolCallVideoSchema = z19.object({
2963
3211
  /** Path relative to the project's artifacts/ root (e.g. `generated/video-123.mp4`). */
2964
- path: z17.string(),
3212
+ path: z19.string(),
2965
3213
  /** MIME type, e.g. `video/mp4`. */
2966
- mimeType: z17.string(),
3214
+ mimeType: z19.string(),
2967
3215
  /** Optional poster-frame artifact path for the `<video poster>` attribute. */
2968
- posterPath: z17.string().optional()
3216
+ posterPath: z19.string().optional()
2969
3217
  });
2970
- var ChatMessageToolCallSchema = z17.object({
2971
- name: z17.string(),
2972
- durationMs: z17.number(),
2973
- success: z17.boolean(),
2974
- errorMessage: z17.string().optional(),
3218
+ var ToolCardStepSchema = z19.object({
3219
+ id: z19.string(),
3220
+ name: z19.string(),
3221
+ status: z19.enum(["done", "active", "pending"])
3222
+ });
3223
+ var CraftbookStartCardSchema = z19.object({
3224
+ kind: z19.literal("craftbook-start"),
3225
+ craftbookId: z19.string(),
3226
+ craftbookName: z19.string(),
3227
+ /** Task ref `projectId/num`. */
3228
+ taskRef: z19.string(),
3229
+ /** The TASK's project — may differ from the session's when invoked cross-project. */
3230
+ projectId: z19.string(),
3231
+ /** Task status at event time. */
3232
+ status: z19.string(),
3233
+ activeStepId: z19.string().optional(),
3234
+ steps: z19.array(ToolCardStepSchema),
3235
+ /** True when the call idempotently returned an already-running task. */
3236
+ reused: z19.boolean().optional(),
3237
+ /**
3238
+ * The craftbook declares it works better with the External services
3239
+ * capability. The UI nudges (with the optional author rationale) only
3240
+ * while the resolved security policy has it disabled.
3241
+ */
3242
+ recommendsExternalServices: z19.object({ reason: z19.string().optional() }).optional()
3243
+ });
3244
+ var TaskStepAdvanceCardSchema = z19.object({
3245
+ kind: z19.literal("task-step-advance"),
3246
+ craftbookId: z19.string(),
3247
+ craftbookName: z19.string(),
3248
+ taskRef: z19.string(),
3249
+ projectId: z19.string(),
3250
+ status: z19.string(),
3251
+ completedStepId: z19.string(),
3252
+ completedStepName: z19.string().optional(),
3253
+ /** Absent when the advance took the task terminal (complete/canceled). */
3254
+ activeStepId: z19.string().optional(),
3255
+ activeStepName: z19.string().optional(),
3256
+ steps: z19.array(ToolCardStepSchema)
3257
+ });
3258
+ var ToolCallCardSchema = z19.discriminatedUnion("kind", [
3259
+ CraftbookStartCardSchema,
3260
+ TaskStepAdvanceCardSchema
3261
+ ]);
3262
+ var ChatMessageToolCallSchema = z19.object({
3263
+ name: z19.string(),
3264
+ durationMs: z19.number(),
3265
+ success: z19.boolean(),
3266
+ errorMessage: z19.string().optional(),
2975
3267
  /** File path the tool touched, for the References pane. */
2976
- path: z17.string().optional(),
3268
+ path: z19.string().optional(),
2977
3269
  /** Ordered file paths touched by a batched filesystem tool. */
2978
- paths: z17.array(z17.string()).optional(),
3270
+ paths: z19.array(z19.string()).optional(),
2979
3271
  /** Compact, human-readable one-liner ("→ Freja: update the game loop · file: workspace/index.html"). */
2980
- argsSummary: z17.string().optional(),
3272
+ argsSummary: z19.string().optional(),
2981
3273
  /**
2982
3274
  * The tool call's FULL arguments, rendered as readable text (field by
2983
3275
  * field, bulky values shown in full — not truncated like
@@ -2988,60 +3280,132 @@ var ChatMessageToolCallSchema = z17.object({
2988
3280
  * arguments are not a secret vector in this codebase (secrets live in
2989
3281
  * the toolset-config path), so this is not separately redacted.
2990
3282
  */
2991
- argsFull: z17.string().optional(),
3283
+ argsFull: z19.string().optional(),
2992
3284
  /** Short full response, or a bounded beginning/end summary for a long response. */
2993
- resultText: z17.string().optional(),
3285
+ resultText: z19.string().optional(),
2994
3286
  /** True when `resultText` is a bounded summary rather than the complete response. */
2995
- resultTruncated: z17.boolean().optional(),
3287
+ resultTruncated: z19.boolean().optional(),
2996
3288
  /** Image artifacts the tool returned (e.g. browser_snapshot screenshots). */
2997
- images: z17.array(ToolCallImageSchema).optional(),
3289
+ images: z19.array(ToolCallImageSchema).optional(),
2998
3290
  /** Audio artifacts the tool returned (e.g. synthesize_speech WAV). */
2999
- audios: z17.array(ToolCallAudioSchema).optional(),
3291
+ audios: z19.array(ToolCallAudioSchema).optional(),
3000
3292
  /** Video artifacts the tool returned (e.g. generate_video mp4). */
3001
- videos: z17.array(ToolCallVideoSchema).optional(),
3293
+ videos: z19.array(ToolCallVideoSchema).optional(),
3002
3294
  /**
3003
3295
  * Unified diff describing the change a surgical-edit tool made
3004
3296
  * (`replace_in_file`, `apply_patch`, `insert_at_marker`). Used by the UI
3005
3297
  * to render an inline diff under the tool-call row. Capped at ~100KB
3006
3298
  * server-side; larger diffs are truncated with a marker line.
3007
3299
  */
3008
- diff: z17.string().optional(),
3009
- addedLines: z17.number().int().nonnegative().optional(),
3010
- removedLines: z17.number().int().nonnegative().optional()
3011
- });
3012
- var ChatMessageSchema = z17.object({
3013
- role: z17.enum(["user", "assistant"]),
3014
- content: z17.string(),
3015
- at: z17.string(),
3016
- from: z17.object({
3017
- gezelId: z17.string(),
3018
- gezelName: z17.string()
3300
+ diff: z19.string().optional(),
3301
+ addedLines: z19.number().int().nonnegative().optional(),
3302
+ removedLines: z19.number().int().nonnegative().optional(),
3303
+ /** Rich inline card payload for tools with special renderings — see ToolCallCardSchema. */
3304
+ card: ToolCallCardSchema.optional(),
3305
+ /**
3306
+ * Offset into the turn's `ChatMessage.reasoning` string marking where
3307
+ * this call fired — the same trick `intents[].afterChars` plays on
3308
+ * `content`. Lets the collapsed Thinking expander splice a small
3309
+ * marker into the trace at the spot the model stopped deliberating
3310
+ * and acted, so an expanded trace reads in causal order instead of as
3311
+ * one undifferentiated block above an unrelated tool list.
3312
+ *
3313
+ * Sourced from the PROVIDER's running reasoning length
3314
+ * (`LLMSession.getCurrentTurnReasoningLength`), never from a
3315
+ * manager-side count of `reasoning_delta` events: the persisted trace
3316
+ * is whatever `getLastTurnReasoning()` returns, which several local
3317
+ * providers build by extracting `<think>` blocks per tool-loop
3318
+ * iteration rather than from the delta stream. An offset into a
3319
+ * different string would drift silently.
3320
+ *
3321
+ * Absent for providers that don't implement the getter, and on every
3322
+ * message written before this field existed — the expander then
3323
+ * renders the plain trace it always did.
3324
+ */
3325
+ afterReasoningChars: z19.number().int().min(0).optional()
3326
+ });
3327
+ var ChatSessionSourceSchema = z19.object({
3328
+ kind: z19.literal("external"),
3329
+ /** Stable integration id, e.g. `pi`. */
3330
+ appId: z19.string().min(1),
3331
+ /** Human-facing application name, e.g. `Pi`. */
3332
+ appName: z19.string().min(1),
3333
+ /** The external application's stable conversation/session identifier. */
3334
+ externalConversationId: z19.string().min(1),
3335
+ /** External threads are ledger views; the owning app controls replies. */
3336
+ readOnly: z19.literal(true),
3337
+ /** Working-directory hint supplied by the external app, when available. */
3338
+ workingDirectory: z19.string().optional(),
3339
+ /** Project id/name hint supplied by the external app, when available. */
3340
+ projectHint: z19.string().optional()
3341
+ });
3342
+ var ReferencedFileKindSchema = z19.enum(["artifact", "workspace"]);
3343
+ var ReferencedFileSchema = z19.object({
3344
+ kind: ReferencedFileKindSchema,
3345
+ path: z19.string()
3346
+ });
3347
+ var ChatMessageSchema = z19.object({
3348
+ role: z19.enum(["user", "assistant"]),
3349
+ content: z19.string(),
3350
+ at: z19.string(),
3351
+ from: z19.object({
3352
+ gezelId: z19.string(),
3353
+ gezelName: z19.string()
3019
3354
  }).optional(),
3020
3355
  /**
3021
- * Artifact paths (relative to the project's `artifacts/` directory)
3022
- * the assistant reply referenced in its body text. Populated on save
3023
- * by the server-side reference parser, and used by the chat UI to
3024
- * render a chip row under the bubble and to linkify inline code spans
3025
- * that match a real artifact. Back-stops Copilot's tool-call
3026
- * blindspot and any "AI wrote a file outside the MCP tools" paths.
3356
+ * Real files the assistant reply named in its body text — artifacts
3357
+ * drawer and workspace tree alike. Populated on save by the
3358
+ * server-side reference parser, and used by the chat UI to render a
3359
+ * chip row under the bubble and to linkify inline code spans that
3360
+ * match a real file. Back-stops Copilot's tool-call blindspot and any
3361
+ * "AI wrote a file outside the MCP tools" path.
3362
+ */
3363
+ referencedFiles: z19.array(ReferencedFileSchema).optional(),
3364
+ /**
3365
+ * The artifact-only projection of {@link referencedFiles}, still
3366
+ * written so an older `@bendyline/gezel-cli` (or any out-of-tree
3367
+ * reader) keeps working against a session file this daemon wrote.
3368
+ * Read `referencedFiles` in new code — this field cannot represent a
3369
+ * workspace path.
3370
+ *
3371
+ * @deprecated superseded by `referencedFiles`
3027
3372
  */
3028
- referencedArtifacts: z17.array(z17.string()).optional(),
3373
+ referencedArtifacts: z19.array(z19.string()).optional(),
3029
3374
  /**
3030
3375
  * Task refs (`<projectId>/<num>`) the assistant reply mentioned. Same
3031
- * shape as `referencedArtifacts` — populated on save, gated on the
3376
+ * shape as `referencedFiles` — populated on save, gated on the
3032
3377
  * ref actually existing in the task store, surfaced in the chat
3033
3378
  * bubble as click-through chips. Catches the common "I created task
3034
3379
  * gezel-ux-roadmap/2" mention so the user can jump to it without
3035
3380
  * scrolling the body for the ref.
3036
3381
  */
3037
- referencedTasks: z17.array(z17.string()).optional(),
3382
+ referencedTasks: z19.array(z19.string()).optional(),
3383
+ /**
3384
+ * Indexed-context sources consulted for THIS user turn (proactive
3385
+ * retrieval). Citations only — source/path/line/score, never the retrieved
3386
+ * text (which lives solely in the provider prompt). Lets the UI render a
3387
+ * "consulted N sources" row so proactive RAG is visible diligence instead
3388
+ * of invisible machinery.
3389
+ */
3390
+ retrieval: z19.object({
3391
+ hits: z19.array(
3392
+ z19.object({
3393
+ source: RetrievalSourceSchema,
3394
+ projectId: z19.string().optional(),
3395
+ path: z19.string().optional(),
3396
+ line: z19.number().int().positive().optional(),
3397
+ lineEnd: z19.number().int().positive().optional(),
3398
+ score: z19.number()
3399
+ })
3400
+ )
3401
+ }).optional(),
3038
3402
  /**
3039
3403
  * Tool calls the assistant fired during this turn. Populated on the
3040
3404
  * final assistant message; the UI renders them as a collapsible
3041
3405
  * "thinking" expando above the reply body so the user can still see
3042
3406
  * what ran even after the live stream has closed.
3043
3407
  */
3044
- toolCalls: z17.array(ChatMessageToolCallSchema).optional(),
3408
+ toolCalls: z19.array(ChatMessageToolCallSchema).optional(),
3045
3409
  /**
3046
3410
  * Phase announcements the model emitted via Copilot's `report_intent`
3047
3411
  * tool during this turn. Each entry carries the intent label and an
@@ -3052,10 +3416,10 @@ var ChatMessageSchema = z17.object({
3052
3416
  * replay and history export are unaffected. Copilot-only; other
3053
3417
  * providers don't emit these.
3054
3418
  */
3055
- intents: z17.array(
3056
- z17.object({
3057
- label: z17.string(),
3058
- afterChars: z17.number().int().min(0)
3419
+ intents: z19.array(
3420
+ z19.object({
3421
+ label: z19.string(),
3422
+ afterChars: z19.number().int().min(0)
3059
3423
  })
3060
3424
  ).optional(),
3061
3425
  /**
@@ -3065,7 +3429,7 @@ var ChatMessageSchema = z17.object({
3065
3429
  * itself lives in the per-project questions file; this id is just
3066
3430
  * the foreign key.
3067
3431
  */
3068
- pendingQuestionId: z17.string().optional(),
3432
+ pendingQuestionId: z19.string().optional(),
3069
3433
  /**
3070
3434
  * Marks the message as a system-generated synthesis rather than a real
3071
3435
  * model turn.
@@ -3094,7 +3458,7 @@ var ChatMessageSchema = z17.object({
3094
3458
  * UI renders these as muted bubbles; the model sees them as normal
3095
3459
  * assistant turns (the role label is what matters to the API).
3096
3460
  */
3097
- synthetic: z17.enum([
3461
+ synthetic: z19.enum([
3098
3462
  "compaction-summary",
3099
3463
  "context-loop-halt",
3100
3464
  "turn-aborted",
@@ -3112,7 +3476,7 @@ var ChatMessageSchema = z17.object({
3112
3476
  * from loaded transcripts and the live-timeline handler skips its bubble
3113
3477
  * while still opening the assistant's streaming slot.
3114
3478
  */
3115
- hidden: z17.boolean().optional(),
3479
+ hidden: z19.boolean().optional(),
3116
3480
  /**
3117
3481
  * This user message was delivered from the session's mid-turn queue
3118
3482
  * as a nudge — typed while the previous turn was still streaming and
@@ -3120,7 +3484,26 @@ var ChatMessageSchema = z17.object({
3120
3484
  * Display-only marker: the model sees a normal user turn; the UI
3121
3485
  * renders a small "nudged" chip on the bubble.
3122
3486
  */
3123
- nudge: z17.boolean().optional(),
3487
+ nudge: z19.boolean().optional(),
3488
+ /**
3489
+ * The machinery — not the human — authored this `role: 'user'` message.
3490
+ * Task dispatch seeds, step handoffs, and project-page reaction seeds
3491
+ * all arrive as user turns because that is the only role a provider
3492
+ * will accept mid-conversation, but the person never typed them.
3493
+ *
3494
+ * Display-only marker: the model still sees an ordinary user turn, and
3495
+ * the UI labels the bubble **System** instead of "You". Without it the
3496
+ * flagship Home thread opened with "YOU · call `advance_task_step` to
3497
+ * hand off" — internal plumbing quoted back at the user in their own
3498
+ * voice, which reads as a bug and undoes the warm-companion framing the
3499
+ * whole product rests on.
3500
+ *
3501
+ * Set from the turn's `messageOrigin` (service-side `TurnMessageOrigin`);
3502
+ * an enum rather than a boolean so a future dispatch/reaction split can
3503
+ * widen it without a second field. Messages written before this field
3504
+ * existed are inferred at read time — see `Store.listTimeline`.
3505
+ */
3506
+ origin: z19.enum(["system"]).optional(),
3124
3507
  /**
3125
3508
  * Persistent warnings attached to this assistant turn — fabricated
3126
3509
  * tool-use detection, degraded provider state, etc. The streaming
@@ -3130,7 +3513,7 @@ var ChatMessageSchema = z17.object({
3130
3513
  * reload. Populated by the chat manager just before `events.publish`
3131
3514
  * fires the `complete` event.
3132
3515
  */
3133
- warnings: z17.array(z17.string()).optional(),
3516
+ warnings: z19.array(z19.string()).optional(),
3134
3517
  /**
3135
3518
  * Chain-of-thought captured during this turn. Local providers
3136
3519
  * (ollama, llama-cpp, mlx, ds4) extract `<think>…</think>` /
@@ -3143,14 +3526,14 @@ var ChatMessageSchema = z17.object({
3143
3526
  * Responses hides reasoning server-side and leaves this unset.
3144
3527
  * Empty / whitespace-only captures are dropped.
3145
3528
  */
3146
- reasoning: z17.string().optional(),
3529
+ reasoning: z19.string().optional(),
3147
3530
  /**
3148
3531
  * Observed wall-clock span of the streamed private-reasoning trace,
3149
3532
  * measured from the first `reasoning_delta` to the last. Optional
3150
3533
  * because older messages and providers that only expose reasoning at
3151
3534
  * commit time have no trustworthy phase timing.
3152
3535
  */
3153
- reasoningDurationMs: z17.number().int().nonnegative().optional(),
3536
+ reasoningDurationMs: z19.number().int().nonnegative().optional(),
3154
3537
  /**
3155
3538
  * Tool-call bodies the model emitted that the salvage layer
3156
3539
  * couldn't parse — the literal text from `<|tool_call|>` markers
@@ -3165,10 +3548,10 @@ var ChatMessageSchema = z17.object({
3165
3548
  * hundred chars per body in the populator so a long fabricated body
3166
3549
  * doesn't blow up the session file.
3167
3550
  */
3168
- attemptedToolCalls: z17.array(
3169
- z17.object({
3170
- body: z17.string(),
3171
- reason: z17.string().optional()
3551
+ attemptedToolCalls: z19.array(
3552
+ z19.object({
3553
+ body: z19.string(),
3554
+ reason: z19.string().optional()
3172
3555
  })
3173
3556
  ).optional(),
3174
3557
  /**
@@ -3184,28 +3567,28 @@ var ChatMessageSchema = z17.object({
3184
3567
  * daemon restart, a provider reset, or a context-pressure rebuild — leaving
3185
3568
  * the model replaying a bare `![](attachments/9f3.png)`.
3186
3569
  */
3187
- recognizedImages: z17.array(MessageImageDigestSchema).optional()
3570
+ recognizedImages: z19.array(MessageImageDigestSchema).optional()
3188
3571
  });
3189
- var ChatTurnErrorDetailSchema = z17.object({
3190
- code: z17.string().max(64).optional(),
3572
+ var ChatTurnErrorDetailSchema = z19.object({
3573
+ code: z19.string().max(64).optional(),
3191
3574
  /** Component that failed — a provider name (`llama-cpp`) or a subsystem. */
3192
- engine: z17.string().max(64).optional(),
3575
+ engine: z19.string().max(64).optional(),
3193
3576
  /** Correlation key, also written into the engine's own incident log. */
3194
- incidentId: z17.string().max(64).optional(),
3577
+ incidentId: z19.string().max(64).optional(),
3195
3578
  /** Native crash class from the exit snapshot, e.g. `cuda-out-of-memory`. */
3196
- panicKind: z17.string().max(64).optional(),
3197
- exitCode: z17.number().int().nullable().optional(),
3198
- signal: z17.string().max(32).nullable().optional(),
3579
+ panicKind: z19.string().max(64).optional(),
3580
+ exitCode: z19.number().int().nullable().optional(),
3581
+ signal: z19.string().max(32).nullable().optional(),
3199
3582
  /**
3200
3583
  * Request-independent launch facts copied from the crash snapshot, which
3201
3584
  * is contractually free of prompts, tool arguments, and secrets. Bounded
3202
3585
  * by the extractor. A machine profile cannot reconstruct which model at
3203
3586
  * which context size with which KV type crashed; this can.
3204
3587
  */
3205
- diagnostics: z17.record(z17.string(), z17.union([z17.string(), z17.number(), z17.boolean()])).optional()
3588
+ diagnostics: z19.record(z19.string(), z19.union([z19.string(), z19.number(), z19.boolean()])).optional()
3206
3589
  });
3207
- var ChatEventSchema = z17.discriminatedUnion("type", [
3208
- z17.object({ type: z17.literal("delta"), content: z17.string() }),
3590
+ var ChatEventSchema = z19.discriminatedUnion("type", [
3591
+ z19.object({ type: z19.literal("delta"), content: z19.string() }),
3209
3592
  /**
3210
3593
  * Live private-reasoning tokens (ds4's think phase), streamed on their
3211
3594
  * own channel so they never mix into the visible `delta` stream, the
@@ -3213,14 +3596,14 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3213
3596
  * renders them as a distinct "thinking" block that collapses into the
3214
3597
  * committed message's reasoning expander once `complete` lands.
3215
3598
  */
3216
- z17.object({ type: z17.literal("reasoning_delta"), content: z17.string() }),
3217
- z17.object({ type: z17.literal("complete"), message: ChatMessageSchema }),
3599
+ z19.object({ type: z19.literal("reasoning_delta"), content: z19.string() }),
3600
+ z19.object({ type: z19.literal("complete"), message: ChatMessageSchema }),
3218
3601
  /**
3219
3602
  * The user submitted a message, but a cold provider session is still being
3220
3603
  * created. Project timelines render this as a temporary pending row until
3221
3604
  * the durable `user_message` or a terminal `error` arrives.
3222
3605
  */
3223
- z17.object({ type: z17.literal("user_message_pending"), preview: z17.string() }),
3606
+ z19.object({ type: z19.literal("user_message_pending"), preview: z19.string() }),
3224
3607
  /**
3225
3608
  * Emitted right after the user's message is appended to the session
3226
3609
  * record. The legacy session-scoped UI inserted user messages locally
@@ -3228,27 +3611,27 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3228
3611
  * envelope streams need it so the interleaved timeline can render the
3229
3612
  * user's bubble immediately, before any assistant deltas.
3230
3613
  */
3231
- z17.object({ type: z17.literal("user_message"), message: ChatMessageSchema }),
3614
+ z19.object({ type: z19.literal("user_message"), message: ChatMessageSchema }),
3232
3615
  /**
3233
3616
  * Emitted when the assistant invokes an MCP tool (OpenAI + Mock paths
3234
3617
  * only — Copilot runs tools inside its subprocess, invisible to us).
3235
3618
  * The UI surfaces these as "thinking" breadcrumbs.
3236
3619
  */
3237
- z17.object({
3238
- type: z17.literal("tool"),
3239
- name: z17.string(),
3240
- durationMs: z17.number(),
3241
- success: z17.boolean(),
3242
- errorMessage: z17.string().optional(),
3620
+ z19.object({
3621
+ type: z19.literal("tool"),
3622
+ name: z19.string(),
3623
+ durationMs: z19.number(),
3624
+ success: z19.boolean(),
3625
+ errorMessage: z19.string().optional(),
3243
3626
  /**
3244
3627
  * File path the tool touched (if any). Set for tools that take a `path`
3245
3628
  * argument: readFile, writeFile, read_artifact, write_artifact,
3246
3629
  * read_document, write_document. Lets the UI build a References panel
3247
3630
  * without guessing.
3248
3631
  */
3249
- path: z17.string().optional(),
3632
+ path: z19.string().optional(),
3250
3633
  /** Ordered file paths touched by a batched filesystem tool. */
3251
- paths: z17.array(z17.string()).optional(),
3634
+ paths: z19.array(z19.string()).optional(),
3252
3635
  /**
3253
3636
  * Compact human-readable preview of the non-bulky arguments. Example:
3254
3637
  * `gezel: "Maya", message: "what's the status of..."`. Values are
@@ -3256,44 +3639,46 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3256
3639
  * UI to render a useful in-progress tool line instead of just the
3257
3640
  * tool name.
3258
3641
  */
3259
- argsSummary: z17.string().optional(),
3642
+ argsSummary: z19.string().optional(),
3260
3643
  /** Full, readable args for the expand + copy affordance. See the persisted `ChatMessageToolCall.argsFull`. */
3261
- argsFull: z17.string().optional(),
3644
+ argsFull: z19.string().optional(),
3262
3645
  /** Short full response, or a bounded beginning/end summary. */
3263
- resultText: z17.string().optional(),
3646
+ resultText: z19.string().optional(),
3264
3647
  /** True when `resultText` is a bounded summary rather than the complete response. */
3265
- resultTruncated: z17.boolean().optional(),
3648
+ resultTruncated: z19.boolean().optional(),
3266
3649
  /**
3267
3650
  * Image artifacts the tool returned (most commonly browser screenshots).
3268
3651
  * Paths are relative to the project's artifacts/ root and resolved
3269
3652
  * to URLs by the UI via the artifact-read endpoint.
3270
3653
  */
3271
- images: z17.array(ToolCallImageSchema).optional(),
3654
+ images: z19.array(ToolCallImageSchema).optional(),
3272
3655
  /**
3273
3656
  * Audio artifacts the tool returned (synthesize_speech narrations,
3274
3657
  * voice memos transcribed via transcribe_audio). Same artifact-path
3275
3658
  * resolution as `images`.
3276
3659
  */
3277
- audios: z17.array(ToolCallAudioSchema).optional(),
3660
+ audios: z19.array(ToolCallAudioSchema).optional(),
3278
3661
  /**
3279
3662
  * Video artifacts the tool returned (`generate_video`). Same
3280
3663
  * artifact-path resolution as `images`; rendered as a `<video>`
3281
3664
  * player in the chat row.
3282
3665
  */
3283
- videos: z17.array(ToolCallVideoSchema).optional(),
3666
+ videos: z19.array(ToolCallVideoSchema).optional(),
3284
3667
  /**
3285
3668
  * Unified diff describing the change a surgical-edit tool made
3286
3669
  * (`replace_in_file`, `apply_patch`, `insert_at_marker`). Streams to the
3287
3670
  * UI mid-turn so the chat bubble can render an inline diff under
3288
3671
  * the tool-call row even before the assistant message is finalized.
3289
3672
  */
3290
- diff: z17.string().optional(),
3291
- addedLines: z17.number().int().nonnegative().optional(),
3292
- removedLines: z17.number().int().nonnegative().optional()
3673
+ diff: z19.string().optional(),
3674
+ addedLines: z19.number().int().nonnegative().optional(),
3675
+ removedLines: z19.number().int().nonnegative().optional(),
3676
+ /** Rich inline card payload for tools with special renderings — see ToolCallCardSchema. */
3677
+ card: ToolCallCardSchema.optional()
3293
3678
  }),
3294
- z17.object({
3295
- type: z17.literal("error"),
3296
- error: z17.string(),
3679
+ z19.object({
3680
+ type: z19.literal("error"),
3681
+ error: z19.string(),
3297
3682
  // Not `detail` — three sibling variants in this union already use that
3298
3683
  // name for free-form progress prose, and one union with two meanings for
3299
3684
  // one key is a trap.
@@ -3304,8 +3689,8 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3304
3689
  * surface. This is terminal for the live UI, but is deliberately not an
3305
3690
  * error: it must not poison the session or render failure recovery UI.
3306
3691
  */
3307
- z17.object({ type: z17.literal("cancelled") }),
3308
- z17.object({ type: z17.literal("done") }),
3692
+ z19.object({ type: z19.literal("cancelled") }),
3693
+ z19.object({ type: z19.literal("done") }),
3309
3694
  /**
3310
3695
  * Emitted when a turn ends up waiting in the provider queue for more
3311
3696
  * than a brief grace period (~200ms). `aheadOf` is an approximate
@@ -3315,7 +3700,7 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3315
3700
  * emit this event — avoids flashing the indicator on the happy path
3316
3701
  * where the queue is empty.
3317
3702
  */
3318
- z17.object({ type: z17.literal("queued"), aheadOf: z17.number().int().min(0) }),
3703
+ z19.object({ type: z19.literal("queued"), aheadOf: z19.number().int().min(0) }),
3319
3704
  /**
3320
3705
  * Ollama-only: emitted for each bare framing chunk that arrives
3321
3706
  * on the wire without visible content / tool_calls — the
@@ -3325,7 +3710,7 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3325
3710
  * when the model isn't producing visible output. Reset on the
3326
3711
  * next real `delta`, `tool`, or `complete`.
3327
3712
  */
3328
- z17.object({ type: z17.literal("wire_pulse") }),
3713
+ z19.object({ type: z19.literal("wire_pulse") }),
3329
3714
  /**
3330
3715
  * Live tool-argument stream. Fired while the model is generating a
3331
3716
  * structured tool call — most visibly a multi-minute `write_file`
@@ -3337,7 +3722,7 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3337
3722
  * "working" block (same pattern as `reasoning_delta`) and drops the
3338
3723
  * block when the corresponding `tool` event lands.
3339
3724
  */
3340
- z17.object({ type: z17.literal("tool_args_delta"), name: z17.string(), content: z17.string() }),
3725
+ z19.object({ type: z19.literal("tool_args_delta"), name: z19.string(), content: z19.string() }),
3341
3726
  /**
3342
3727
  * Emitted when a provider tells us it's still doing work — even though
3343
3728
  * no visible text/tool event has arrived. Today this is wired from
@@ -3348,24 +3733,24 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3348
3733
  * Optional `label` carries a short phase hint ('thinking', 'tool',
3349
3734
  * etc.) that the streaming bubble can surface as a status line.
3350
3735
  */
3351
- z17.object({ type: z17.literal("heartbeat"), label: z17.string().optional() }),
3736
+ z19.object({ type: z19.literal("heartbeat"), label: z19.string().optional() }),
3352
3737
  /**
3353
3738
  * Provider-side warning surfaced mid-turn (e.g. Copilot rate-limit,
3354
3739
  * context pressure, degraded mode). The UI renders these inline on
3355
3740
  * the streaming bubble so the user sees them immediately instead of
3356
3741
  * only finding out when the turn completes or times out.
3357
3742
  */
3358
- z17.object({
3359
- type: z17.literal("warning"),
3360
- message: z17.string(),
3743
+ z19.object({
3744
+ type: z19.literal("warning"),
3745
+ message: z19.string(),
3361
3746
  /**
3362
3747
  * Optional in-app destination for a warning's inline action. Kept
3363
3748
  * deliberately narrow: warnings are still readable prose when an older
3364
3749
  * client ignores this additive field.
3365
3750
  */
3366
- action: z17.object({
3367
- kind: z17.literal("settings"),
3368
- section: z17.enum(["llamaCpp", "mlx", "ds4"])
3751
+ action: z19.object({
3752
+ kind: z19.literal("settings"),
3753
+ section: z19.enum(["llamaCpp", "mlx", "ds4"])
3369
3754
  }).optional()
3370
3755
  }),
3371
3756
  /**
@@ -3376,18 +3761,18 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3376
3761
  * offset on the final assistant message so completed bubbles render
3377
3762
  * the same segmentation on reload.
3378
3763
  */
3379
- z17.object({ type: z17.literal("intent"), label: z17.string() }),
3764
+ z19.object({ type: z19.literal("intent"), label: z19.string() }),
3380
3765
  /**
3381
3766
  * Emitted when a new message is enqueued on the per-session queue
3382
3767
  * because the session already has a turn in flight. The timeline
3383
3768
  * renders a "ghost bubble" under the session's streaming bubble
3384
3769
  * showing the queued text preview. Cleared via `queue_removed`.
3385
3770
  */
3386
- z17.object({
3387
- type: z17.literal("queue_enqueued"),
3388
- queueId: z17.string(),
3389
- preview: z17.string(),
3390
- enqueuedAt: z17.string(),
3771
+ z19.object({
3772
+ type: z19.literal("queue_enqueued"),
3773
+ queueId: z19.string(),
3774
+ preview: z19.string(),
3775
+ enqueuedAt: z19.string(),
3391
3776
  /**
3392
3777
  * The entry was queued as a mid-turn nudge — the ghost bubble labels
3393
3778
  * it "nudge" and contiguous nudges merge into one turn on drain.
@@ -3395,7 +3780,7 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3395
3780
  * every coalesce/edit); the edit affordance fetches it lazily via
3396
3781
  * `GET /api/sessions/:id/queue`.
3397
3782
  */
3398
- nudge: z17.boolean().optional()
3783
+ nudge: z19.boolean().optional()
3399
3784
  }),
3400
3785
  /**
3401
3786
  * Emitted when a queued entry leaves the queue — either because
@@ -3404,10 +3789,10 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3404
3789
  * dropped without running (`reason: 'canceled'` via user action,
3405
3790
  * `reason: 'rejected'` via session archive / delete / shutdown).
3406
3791
  */
3407
- z17.object({
3408
- type: z17.literal("queue_removed"),
3409
- queueId: z17.string(),
3410
- reason: z17.enum(["started", "canceled", "rejected"])
3792
+ z19.object({
3793
+ type: z19.literal("queue_removed"),
3794
+ queueId: z19.string(),
3795
+ reason: z19.enum(["started", "canceled", "rejected"])
3411
3796
  }),
3412
3797
  /**
3413
3798
  * Local-provider context policy: emitted when accumulated conversation
@@ -3415,11 +3800,11 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3415
3800
  * The UI may suggest starting fresh because this event is never emitted
3416
3801
  * for a first-turn standing system/tool prefix.
3417
3802
  */
3418
- z17.object({
3419
- type: z17.literal("context_warning"),
3420
- estimatedTokens: z17.number(),
3421
- numCtx: z17.number(),
3422
- model: z17.string()
3803
+ z19.object({
3804
+ type: z19.literal("context_warning"),
3805
+ estimatedTokens: z19.number(),
3806
+ numCtx: z19.number(),
3807
+ model: z19.string()
3423
3808
  }),
3424
3809
  /**
3425
3810
  * Local-provider context policy: emitted right after in-flight compaction collapses
@@ -3428,10 +3813,10 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3428
3813
  * for a "compacted" variant and refreshes the visible timeline (older
3429
3814
  * bubbles are now gone from disk).
3430
3815
  */
3431
- z17.object({
3432
- type: z17.literal("context_compacted"),
3433
- removedCount: z17.number().int().nonnegative(),
3434
- model: z17.string()
3816
+ z19.object({
3817
+ type: z19.literal("context_compacted"),
3818
+ removedCount: z19.number().int().nonnegative(),
3819
+ model: z19.string()
3435
3820
  }),
3436
3821
  /**
3437
3822
  * Emitted when the chat manager detects a self-chat / compaction loop —
@@ -3440,10 +3825,10 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3440
3825
  * compaction cycle without making progress. The pipeline halts the turn
3441
3826
  * so the user can intervene; the UI surfaces a "looks stuck" banner.
3442
3827
  */
3443
- z17.object({
3444
- type: z17.literal("context_loop"),
3445
- compactionsThisSend: z17.number().int().positive(),
3446
- reason: z17.string()
3828
+ z19.object({
3829
+ type: z19.literal("context_loop"),
3830
+ compactionsThisSend: z19.number().int().positive(),
3831
+ reason: z19.string()
3447
3832
  }),
3448
3833
  /**
3449
3834
  * Emitted when the Keurmeester (frontier quality inspector) steps in
@@ -3451,23 +3836,23 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3451
3836
  * UI renders a "stepped in" notice on the thread; the full case
3452
3837
  * record lives under `~/.gezel/keurmeester/cases/` keyed by caseId.
3453
3838
  */
3454
- z17.object({
3455
- type: z17.literal("keurmeester_intervention"),
3456
- caseId: z17.string(),
3457
- gezelId: z17.string(),
3458
- gezelName: z17.string(),
3459
- action: z17.string(),
3460
- summary: z17.string()
3839
+ z19.object({
3840
+ type: z19.literal("keurmeester_intervention"),
3841
+ caseId: z19.string(),
3842
+ gezelId: z19.string(),
3843
+ gezelName: z19.string(),
3844
+ action: z19.string(),
3845
+ summary: z19.string()
3461
3846
  }),
3462
3847
  /**
3463
3848
  * Emitted once per session the first time auto-recall runs, so the UI
3464
3849
  * can render a "pulled N memories from prior work" chip above the first
3465
3850
  * assistant reply.
3466
3851
  */
3467
- z17.object({
3468
- type: z17.literal("recall_applied"),
3469
- hitCount: z17.number(),
3470
- query: z17.string()
3852
+ z19.object({
3853
+ type: z19.literal("recall_applied"),
3854
+ hitCount: z19.number(),
3855
+ query: z19.string()
3471
3856
  }),
3472
3857
  /**
3473
3858
  * Emitted when a gezel posts a structured question via the
@@ -3475,14 +3860,14 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3475
3860
  * questions on this event so the in-chat card, Home pane, and Home
3476
3861
  * tab badge all light up together.
3477
3862
  */
3478
- z17.object({ type: z17.literal("question_asked"), question: QuestionSchema }),
3863
+ z19.object({ type: z19.literal("question_asked"), question: QuestionSchema }),
3479
3864
  /**
3480
3865
  * Emitted when the user submits (or declines) an answer. The UI
3481
3866
  * uses the same fan-out as `question_asked` to refresh every
3482
3867
  * surface; the chat bubble's pending card collapses to its
3483
3868
  * answered state.
3484
3869
  */
3485
- z17.object({ type: z17.literal("question_answered"), question: QuestionSchema }),
3870
+ z19.object({ type: z19.literal("question_answered"), question: QuestionSchema }),
3486
3871
  /**
3487
3872
  * A durable task audit event, fanned onto the project's live stream after
3488
3873
  * it has been appended to History. This keeps lightweight clients (most
@@ -3490,13 +3875,13 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3490
3875
  * second task lifecycle bus. `task.tick` heartbeats are intentionally not
3491
3876
  * published; this channel is for user-meaningful changes.
3492
3877
  */
3493
- z17.object({
3494
- type: z17.literal("task_event"),
3495
- eventId: z17.string(),
3496
- kind: z17.string(),
3497
- summary: z17.string(),
3498
- at: z17.string(),
3499
- taskRef: z17.string().optional(),
3878
+ z19.object({
3879
+ type: z19.literal("task_event"),
3880
+ eventId: z19.string(),
3881
+ kind: z19.string(),
3882
+ summary: z19.string(),
3883
+ at: z19.string(),
3884
+ taskRef: z19.string().optional(),
3500
3885
  /**
3501
3886
  * Gezel responsible for the event, when History recorded one. Kept
3502
3887
  * separate from the human-readable summary so fixed-presentation
@@ -3504,18 +3889,18 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3504
3889
  * their own naming mode instead of leaking the friendly name embedded
3505
3890
  * in the audit prose.
3506
3891
  */
3507
- gezelId: z17.string().optional()
3892
+ gezelId: z19.string().optional()
3508
3893
  }),
3509
3894
  /**
3510
3895
  * Emitted when a gezel crosses a growth level threshold and a pending
3511
3896
  * level-up is created. The UI refreshes growth badges/dots and raises
3512
3897
  * a single calm OS notification when the window is hidden.
3513
3898
  */
3514
- z17.object({
3515
- type: z17.literal("growth_level_up"),
3516
- gezelId: z17.string(),
3517
- gezelName: z17.string(),
3518
- toLevel: z17.number().int()
3899
+ z19.object({
3900
+ type: z19.literal("growth_level_up"),
3901
+ gezelId: z19.string(),
3902
+ gezelName: z19.string(),
3903
+ toLevel: z19.number().int()
3519
3904
  }),
3520
3905
  /**
3521
3906
  * llama-cpp-only: lifecycle phase of the supervised on-device engine
@@ -3538,17 +3923,39 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3538
3923
  * the phase label so users who want to know what's happening can
3539
3924
  * see, without cluttering the happy-path status line.
3540
3925
  */
3541
- z17.object({
3542
- type: z17.literal("engine_phase"),
3543
- provider: z17.enum(["llama-cpp", "mlx", "ds4"]),
3544
- phase: z17.enum(["starting", "loading_model", "prefill", "generating", "ready"]),
3545
- detail: z17.string().optional(),
3546
- progress: z17.number().min(0).max(1).optional(),
3547
- ttftMs: z17.number().int().nonnegative().optional()
3926
+ z19.object({
3927
+ type: z19.literal("engine_phase"),
3928
+ provider: z19.enum(["llama-cpp", "mlx", "ds4"]),
3929
+ phase: z19.enum(["starting", "loading_model", "prefill", "generating", "ready"]),
3930
+ /**
3931
+ * Human-readable subject for an ephemeral background completion. Unlike
3932
+ * `detail` (engine diagnostics), this names the user's work — for example
3933
+ * "Indexing src/app.ts". Ordinary chat turns leave it absent.
3934
+ */
3935
+ activity: z19.string().optional(),
3936
+ detail: z19.string().optional(),
3937
+ progress: z19.number().min(0).max(1).optional(),
3938
+ ttftMs: z19.number().int().nonnegative().optional(),
3939
+ /**
3940
+ * Exact cumulative completion tokens decoded so far this turn, as the
3941
+ * engine counts them (llama-server `timings.predicted_n`, MLX's
3942
+ * streamed `usage.output_tokens`). Present only on `generating`, and
3943
+ * only for engines that publish a running counter — the UI falls back
3944
+ * to an explicitly-approximate character estimate when it is absent,
3945
+ * so this field is what lets a readout drop its "≈".
3946
+ */
3947
+ outputTokens: z19.number().int().nonnegative().optional(),
3948
+ /**
3949
+ * Exact decode rate right now, engine-measured over the generation
3950
+ * phase alone (llama-server `timings.predicted_per_second`, MLX's
3951
+ * `generation_tps`). Same contract as `outputTokens`: absent means the
3952
+ * UI must derive and mark its own estimate.
3953
+ */
3954
+ tokensPerSec: z19.number().nonnegative().optional()
3548
3955
  }),
3549
3956
  /**
3550
- * Per-turn telemetry for locally-hosted providers (llama-cpp +
3551
- * Ollama). Emitted once at turn end with the concrete token counts
3957
+ * Per-turn telemetry for locally-hosted providers (llama-cpp, Ollama,
3958
+ * MLX, and DS4). Emitted once at turn end with the concrete token counts
3552
3959
  * the UI needs for a speed readout — input tokens, output tokens,
3553
3960
  * wall-clock duration, and tokens-per-second on the generation
3554
3961
  * phase. UI accumulates these in a rolling window to show an
@@ -3559,14 +3966,21 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3559
3966
  * local speed metric isn't meaningful when the latency is
3560
3967
  * dominated by network round-trips.
3561
3968
  */
3562
- z17.object({
3563
- type: z17.literal("turn_stats"),
3564
- provider: z17.enum(["llama-cpp", "ollama", "mlx", "ds4"]),
3565
- promptTokens: z17.number().int().nonnegative(),
3566
- completionTokens: z17.number().int().nonnegative(),
3567
- durationMs: z17.number().int().nonnegative(),
3969
+ z19.object({
3970
+ type: z19.literal("turn_stats"),
3971
+ provider: z19.enum(["llama-cpp", "ollama", "mlx", "ds4"]),
3972
+ /**
3973
+ * Model that generated the turn, as the session recorded it. Lets the
3974
+ * UI bucket speed by model instead of averaging a 27B and a 4B into
3975
+ * one meaningless number. Optional because older daemons (and remote
3976
+ * peers on an older wire) don't send it.
3977
+ */
3978
+ model: z19.string().optional(),
3979
+ promptTokens: z19.number().int().nonnegative(),
3980
+ completionTokens: z19.number().int().nonnegative(),
3981
+ durationMs: z19.number().int().nonnegative(),
3568
3982
  /** Generation speed in tokens/sec — completionTokens / generationSeconds. */
3569
- tokensPerSec: z17.number().nonnegative().optional()
3983
+ tokensPerSec: z19.number().nonnegative().optional()
3570
3984
  }),
3571
3985
  /**
3572
3986
  * Static engine-level metrics that don't change during a session —
@@ -3578,11 +3992,11 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3578
3992
  * `engine_phase` (every session waiting on the same supervisor
3579
3993
  * startup gets a copy).
3580
3994
  */
3581
- z17.object({
3582
- type: z17.literal("engine_stats"),
3583
- provider: z17.enum(["llama-cpp", "mlx", "ds4"]),
3995
+ z19.object({
3996
+ type: z19.literal("engine_stats"),
3997
+ provider: z19.enum(["llama-cpp", "mlx", "ds4"]),
3584
3998
  /** Total bytes allocated across all GGUF buffers + KV cache. */
3585
- ramAllocBytes: z17.number().nonnegative()
3999
+ ramAllocBytes: z19.number().nonnegative()
3586
4000
  }),
3587
4001
  /**
3588
4002
  * VRAM tenancy change — a non-LLM workload has taken (or released)
@@ -3612,9 +4026,9 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3612
4026
  * `step`/`totalSteps` drive a real progress bar; `secondsPerStep`
3613
4027
  * lets the UI show an ETA.
3614
4028
  */
3615
- z17.object({
3616
- type: z17.literal("gpu_swap"),
3617
- state: z17.enum(["started", "progress", "ended"]),
4029
+ z19.object({
4030
+ type: z19.literal("gpu_swap"),
4031
+ state: z19.enum(["started", "progress", "ended"]),
3618
4032
  /**
3619
4033
  * Shared with session telemetry so a new workload can't light up the
3620
4034
  * bubble while staying invisible to stall detection.
@@ -3627,12 +4041,12 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3627
4041
  * seconds", which reads as a bug.
3628
4042
  */
3629
4043
  task: SessionGpuTaskSchema,
3630
- detail: z17.string().optional(),
3631
- prompt: z17.string().optional(),
3632
- progress: z17.number().min(0).max(1).optional(),
3633
- step: z17.number().int().nonnegative().optional(),
3634
- totalSteps: z17.number().int().positive().optional(),
3635
- secondsPerStep: z17.number().nonnegative().optional()
4044
+ detail: z19.string().optional(),
4045
+ prompt: z19.string().optional(),
4046
+ progress: z19.number().min(0).max(1).optional(),
4047
+ step: z19.number().int().nonnegative().optional(),
4048
+ totalSteps: z19.number().int().positive().optional(),
4049
+ secondsPerStep: z19.number().nonnegative().optional()
3636
4050
  }),
3637
4051
  /**
3638
4052
  * The turn is parked inside a synchronous `ask_gezel` /
@@ -3655,10 +4069,10 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3655
4069
  * side, same as the `ask_gezel` tool result), so the UI can render
3656
4070
  * it verbatim.
3657
4071
  */
3658
- z17.object({
3659
- type: z17.literal("awaiting_gezel"),
3660
- state: z17.enum(["started", "ended"]),
3661
- targetGezelName: z17.string()
4072
+ z19.object({
4073
+ type: z19.literal("awaiting_gezel"),
4074
+ state: z19.enum(["started", "ended"]),
4075
+ targetGezelName: z19.string()
3662
4076
  }),
3663
4077
  /**
3664
4078
  * A new project was created (via the New Project dialog, the
@@ -3669,10 +4083,10 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3669
4083
  * for the next manual refresh / tab-focus poll. Not a renderable
3670
4084
  * timeline event (like `growth_level_up`); the chat surfaces ignore it.
3671
4085
  */
3672
- z17.object({
3673
- type: z17.literal("project_created"),
3674
- projectId: z17.string(),
3675
- name: z17.string()
4086
+ z19.object({
4087
+ type: z19.literal("project_created"),
4088
+ projectId: z19.string(),
4089
+ name: z19.string()
3676
4090
  }),
3677
4091
  /**
3678
4092
  * A project was deleted (via the Project Actions menu, or an equivalent
@@ -3682,10 +4096,10 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3682
4096
  * waiting for the next manual refresh / tab-focus poll. Not a renderable
3683
4097
  * timeline event; the chat surfaces ignore it.
3684
4098
  */
3685
- z17.object({
3686
- type: z17.literal("project_deleted"),
3687
- projectId: z17.string(),
3688
- name: z17.string()
4099
+ z19.object({
4100
+ type: z19.literal("project_deleted"),
4101
+ projectId: z19.string(),
4102
+ name: z19.string()
3689
4103
  }),
3690
4104
  /**
3691
4105
  * A new shared gezel joined the global roster. Emitted on the global
@@ -3694,10 +4108,10 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3694
4108
  * gezels deliberately do not emit this event because they do not belong in
3695
4109
  * the global Gezellen list.
3696
4110
  */
3697
- z17.object({
3698
- type: z17.literal("gezel_created"),
3699
- gezelId: z17.string(),
3700
- name: z17.string()
4111
+ z19.object({
4112
+ type: z19.literal("gezel_created"),
4113
+ gezelId: z19.string(),
4114
+ name: z19.string()
3701
4115
  }),
3702
4116
  /**
3703
4117
  * Global (project-less) signal that Night Shift mode flipped ON/OFF.
@@ -3705,10 +4119,10 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3705
4119
  * menu pill reflects the live state. `source` is the active driver
3706
4120
  * (scheduled window vs. a manual shift), null when inactive.
3707
4121
  */
3708
- z17.object({
3709
- type: z17.literal("night_shift"),
3710
- active: z17.boolean(),
3711
- source: z17.enum(["scheduled", "manual"]).nullable()
4122
+ z19.object({
4123
+ type: z19.literal("night_shift"),
4124
+ active: z19.boolean(),
4125
+ source: z19.enum(["scheduled", "manual"]).nullable()
3712
4126
  }),
3713
4127
  /**
3714
4128
  * Global signal from the meester status generator. `started` fires
@@ -3717,10 +4131,23 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3717
4131
  * produced nothing usable — the Home greeting refetches on the
3718
4132
  * terminal states instead of polling.
3719
4133
  */
3720
- z17.object({
3721
- type: z17.literal("meester_status"),
3722
- state: z17.enum(["started", "ended", "failed"]),
3723
- generatedAt: z17.string().optional()
4134
+ z19.object({
4135
+ type: z19.literal("meester_status"),
4136
+ state: z19.enum(["started", "ended", "failed"]),
4137
+ generatedAt: z19.string().optional()
4138
+ }),
4139
+ /**
4140
+ * Global signal from the ambient dashboard generator. `ended` means a
4141
+ * fresh PNG landed on disk (filename is the dated file under
4142
+ * `~/.gezel/ambient/`); the Electron shell reacts to it by re-applying
4143
+ * the wallpaper when the user opted in, and the Settings card
4144
+ * refreshes its preview.
4145
+ */
4146
+ z19.object({
4147
+ type: z19.literal("ambient_dashboard"),
4148
+ state: z19.enum(["started", "ended", "failed"]),
4149
+ generatedAt: z19.string().optional(),
4150
+ filename: z19.string().optional()
3724
4151
  }),
3725
4152
  /**
3726
4153
  * Global (history-free) heartbeat from the boekwachter indexing loops —
@@ -3728,23 +4155,29 @@ var ChatEventSchema = z17.discriminatedUnion("type", [
3728
4155
  * indicator pill; complements (doesn't replace) the polled per-project
3729
4156
  * index status. `pending` = files still awaiting AI enrichment when known.
3730
4157
  */
3731
- z17.object({
3732
- type: z17.literal("index_progress"),
3733
- phase: z17.enum(["scan", "shadow", "enrich", "review", "digest"]),
3734
- state: z17.enum(["started", "progress", "ended"]),
3735
- projectId: z17.string().optional(),
3736
- detail: z17.string().optional(),
3737
- pending: z17.number().int().nonnegative().optional(),
4158
+ z19.object({
4159
+ type: z19.literal("index_progress"),
4160
+ phase: z19.enum(["scan", "shadow", "enrich", "review", "digest"]),
4161
+ state: z19.enum(["started", "progress", "ended"]),
4162
+ projectId: z19.string().optional(),
4163
+ detail: z19.string().optional(),
4164
+ pending: z19.number().int().nonnegative().optional(),
3738
4165
  /** The concrete autonomous gezel doing this work, when the project has one. */
3739
- gezelId: z17.string().optional(),
4166
+ gezelId: z19.string().optional(),
3740
4167
  /** Snapshot of their display name so transient progress remains human-readable. */
3741
- gezelName: z17.string().optional()
4168
+ gezelName: z19.string().optional()
3742
4169
  })
3743
4170
  ]);
3744
- var ChatEventEnvelopeSchema = z17.object({
3745
- sessionId: z17.string(),
3746
- gezelId: z17.string(),
3747
- projectId: z17.string(),
4171
+ var ChatEventEnvelopeSchema = z19.object({
4172
+ sessionId: z19.string(),
4173
+ gezelId: z19.string(),
4174
+ projectId: z19.string(),
4175
+ /** Present when this live session was opened by another session. */
4176
+ parentSession: SessionParentSchema.optional(),
4177
+ /** Immediate task-step session that handed work to this live session. */
4178
+ handoffFrom: SessionLinkSchema.optional(),
4179
+ /** Present for externally-owned read-only threads, including live turns. */
4180
+ sessionSource: ChatSessionSourceSchema.optional(),
3748
4181
  event: ChatEventSchema
3749
4182
  });
3750
4183
 
@@ -3875,8 +4308,8 @@ function promoteBareChannelNames(text) {
3875
4308
  }
3876
4309
 
3877
4310
  // src/schemas/report-action.ts
3878
- import { z as z18 } from "zod";
3879
- var ReportActionKindSchema = z18.enum(["fire-craftbook", "create-task", "apply-edits"]);
4311
+ import { z as z20 } from "zod";
4312
+ var ReportActionKindSchema = z20.enum(["fire-craftbook", "create-task", "apply-edits"]);
3880
4313
  var commonFields = {
3881
4314
  /**
3882
4315
  * Author-supplied stable slug — keeps lifecycle state attached across
@@ -3884,121 +4317,121 @@ var commonFields = {
3884
4317
  * content hash (`a-<hash>`), which is stable only while the block's
3885
4318
  * body is byte-identical.
3886
4319
  */
3887
- id: z18.string().regex(/^[a-z0-9][a-z0-9_-]*$/i).optional(),
4320
+ id: z20.string().regex(/^[a-z0-9][a-z0-9_-]*$/i).optional(),
3888
4321
  /** Short human label for the card ("Fix the unchecked null in parser.ts"). */
3889
- title: z18.string().min(1),
4322
+ title: z20.string().min(1),
3890
4323
  /** One-or-two-sentence rationale shown under the title. */
3891
- reason: z18.string().optional(),
4324
+ reason: z20.string().optional(),
3892
4325
  /**
3893
4326
  * Target project. Defaults to the report's own project. Cross-project
3894
4327
  * targets are a primary case — the bundled oversight report lives in
3895
4328
  * `default` but recommends work in specific projects.
3896
4329
  */
3897
- projectId: z18.string().optional()
4330
+ projectId: z20.string().optional()
3898
4331
  };
3899
- var FireCraftbookActionSchema = z18.object({
3900
- kind: z18.literal("fire-craftbook"),
4332
+ var FireCraftbookActionSchema = z20.object({
4333
+ kind: z20.literal("fire-craftbook"),
3901
4334
  ...commonFields,
3902
- craftbookId: z18.string().min(1),
4335
+ craftbookId: z20.string().min(1),
3903
4336
  /** Invocation params for the craftbook's paramSchema (stringified values). */
3904
- params: z18.record(z18.string(), z18.string()).optional()
4337
+ params: z20.record(z20.string(), z20.string()).optional()
3905
4338
  });
3906
- var CreateTaskActionSchema = z18.object({
3907
- kind: z18.literal("create-task"),
4339
+ var CreateTaskActionSchema = z20.object({
4340
+ kind: z20.literal("create-task"),
3908
4341
  ...commonFields,
3909
4342
  /** Full work instruction for the bespoke task's single step. */
3910
- prompt: z18.string().min(1),
4343
+ prompt: z20.string().min(1),
3911
4344
  /** Role to recruit for the work (ensure_gezel jobTitle). Default: software developer. */
3912
- role: z18.string().optional()
4345
+ role: z20.string().optional()
3913
4346
  });
3914
- var ApplyEditsActionSchema = z18.object({
3915
- kind: z18.literal("apply-edits"),
4347
+ var ApplyEditsActionSchema = z20.object({
4348
+ kind: z20.literal("apply-edits"),
3916
4349
  ...commonFields,
3917
- edits: z18.array(
3918
- z18.object({
4350
+ edits: z20.array(
4351
+ z20.object({
3919
4352
  /** Workspace-relative target file in the TARGET project. */
3920
- path: z18.string().min(1),
4353
+ path: z20.string().min(1),
3921
4354
  /** Artifacts-relative sidecar `.diff` path in the REPORT's project. */
3922
- diffArtifact: z18.string().min(1)
4355
+ diffArtifact: z20.string().min(1)
3923
4356
  })
3924
4357
  ).min(1)
3925
4358
  });
3926
- var ReportActionSchema = z18.discriminatedUnion("kind", [
4359
+ var ReportActionSchema = z20.discriminatedUnion("kind", [
3927
4360
  FireCraftbookActionSchema,
3928
4361
  CreateTaskActionSchema,
3929
4362
  ApplyEditsActionSchema
3930
4363
  ]);
3931
- var ReportActionStateSchema = z18.enum([
4364
+ var ReportActionStateSchema = z20.enum([
3932
4365
  "suggested",
3933
4366
  "fired",
3934
4367
  "applied",
3935
4368
  "failed",
3936
4369
  "dismissed"
3937
4370
  ]);
3938
- var ReportActionRecordSchema = z18.object({
3939
- actionId: z18.string(),
4371
+ var ReportActionRecordSchema = z20.object({
4372
+ actionId: z20.string(),
3940
4373
  /** Artifacts-relative path of the report the action came from. */
3941
- reportPath: z18.string(),
4374
+ reportPath: z20.string(),
3942
4375
  kind: ReportActionKindSchema,
3943
- contentHash: z18.string(),
3944
- firstSeenAt: z18.string(),
4376
+ contentHash: z20.string(),
4377
+ firstSeenAt: z20.string(),
3945
4378
  state: ReportActionStateSchema,
3946
4379
  /** Task materialized by fire-craftbook / create-task. */
3947
- taskRef: z18.string().optional(),
3948
- firedAt: z18.string().optional(),
4380
+ taskRef: z20.string().optional(),
4381
+ firedAt: z20.string().optional(),
3949
4382
  /** Stamped when the fired task settles. */
3950
- settledAt: z18.string().optional(),
3951
- outcome: z18.enum(["complete", "canceled"]).optional(),
4383
+ settledAt: z20.string().optional(),
4384
+ outcome: z20.enum(["complete", "canceled"]).optional(),
3952
4385
  /** apply-edits per-file results. */
3953
- results: z18.array(
3954
- z18.object({
3955
- path: z18.string(),
3956
- ok: z18.boolean(),
3957
- error: z18.string().optional()
4386
+ results: z20.array(
4387
+ z20.object({
4388
+ path: z20.string(),
4389
+ ok: z20.boolean(),
4390
+ error: z20.string().optional()
3958
4391
  })
3959
4392
  ).optional()
3960
4393
  });
3961
- var ReportActionParseIssueSchema = z18.object({
4394
+ var ReportActionParseIssueSchema = z20.object({
3962
4395
  /** Zero-based index among the report's gezel-action fences. */
3963
- index: z18.number().int(),
3964
- message: z18.string(),
4396
+ index: z20.number().int(),
4397
+ message: z20.string(),
3965
4398
  /** Raw fence body, for the "unreadable action block" card. */
3966
- raw: z18.string()
4399
+ raw: z20.string()
3967
4400
  });
3968
- var ReportActionViewSchema = z18.object({
4401
+ var ReportActionViewSchema = z20.object({
3969
4402
  action: ReportActionSchema,
3970
- id: z18.string(),
3971
- contentHash: z18.string(),
4403
+ id: z20.string(),
4404
+ contentHash: z20.string(),
3972
4405
  state: ReportActionStateSchema,
3973
- taskRef: z18.string().optional(),
3974
- firedAt: z18.string().optional(),
3975
- settledAt: z18.string().optional(),
3976
- outcome: z18.enum(["complete", "canceled"]).optional(),
3977
- results: z18.array(z18.object({ path: z18.string(), ok: z18.boolean(), error: z18.string().optional() })).optional(),
4406
+ taskRef: z20.string().optional(),
4407
+ firedAt: z20.string().optional(),
4408
+ settledAt: z20.string().optional(),
4409
+ outcome: z20.enum(["complete", "canceled"]).optional(),
4410
+ results: z20.array(z20.object({ path: z20.string(), ok: z20.boolean(), error: z20.string().optional() })).optional(),
3978
4411
  /** The stored record predates a changed block body (report regenerated). */
3979
- contentChanged: z18.boolean().optional()
4412
+ contentChanged: z20.boolean().optional()
3980
4413
  });
3981
- var ReportActionsResponseSchema = z18.object({
3982
- actions: z18.array(ReportActionViewSchema),
3983
- issues: z18.array(ReportActionParseIssueSchema),
4414
+ var ReportActionsResponseSchema = z20.object({
4415
+ actions: z20.array(ReportActionViewSchema),
4416
+ issues: z20.array(ReportActionParseIssueSchema),
3984
4417
  /** Records whose action vanished from the regenerated report. */
3985
- stale: z18.array(ReportActionRecordSchema)
4418
+ stale: z20.array(ReportActionRecordSchema)
3986
4419
  });
3987
- var FireReportActionRequestSchema = z18.object({
4420
+ var FireReportActionRequestSchema = z20.object({
3988
4421
  /** Artifacts-relative report path. */
3989
- path: z18.string().min(1),
3990
- actionId: z18.string().min(1),
4422
+ path: z20.string().min(1),
4423
+ actionId: z20.string().min(1),
3991
4424
  /** apply-edits: none. fire-craftbook: overrides the block's params. */
3992
- params: z18.record(z18.string(), z18.string()).optional()
4425
+ params: z20.record(z20.string(), z20.string()).optional()
3993
4426
  });
3994
- var FireReportActionResponseSchema = z18.object({
4427
+ var FireReportActionResponseSchema = z20.object({
3995
4428
  record: ReportActionRecordSchema,
3996
4429
  /** Set for fire-craftbook / create-task. */
3997
- taskRef: z18.string().optional()
4430
+ taskRef: z20.string().optional()
3998
4431
  });
3999
- var DismissReportActionRequestSchema = z18.object({
4000
- path: z18.string().min(1),
4001
- actionId: z18.string().min(1)
4432
+ var DismissReportActionRequestSchema = z20.object({
4433
+ path: z20.string().min(1),
4434
+ actionId: z20.string().min(1)
4002
4435
  });
4003
4436
 
4004
4437
  // src/markdown/report-actions.ts