zelari-code 2.0.0-alpha.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cli/headless.js +9 -0
  2. package/dist/cli/headless.js.map +1 -1
  3. package/dist/cli/headlessE2eSession.test.js +189 -0
  4. package/dist/cli/headlessE2eSession.test.js.map +1 -0
  5. package/dist/cli/headlessSpine.js +6 -0
  6. package/dist/cli/headlessSpine.js.map +1 -1
  7. package/dist/cli/hooks/chatState.js.map +1 -1
  8. package/dist/cli/hooks/eventsToMessages.js.map +1 -1
  9. package/dist/cli/hooks/useChatTurn.js +5 -3
  10. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  11. package/dist/cli/hooks/useSession.js +0 -2
  12. package/dist/cli/hooks/useSession.js.map +1 -1
  13. package/dist/cli/kraken/evidenceAnchoring.test.js +92 -0
  14. package/dist/cli/kraken/evidenceAnchoring.test.js.map +1 -0
  15. package/dist/cli/kraken/nativeVerification.js +113 -0
  16. package/dist/cli/kraken/nativeVerification.js.map +1 -0
  17. package/dist/cli/kraken/nativeVerification.test.js +207 -0
  18. package/dist/cli/kraken/nativeVerification.test.js.map +1 -0
  19. package/dist/cli/kraken/strictDefaults.test.js +149 -0
  20. package/dist/cli/kraken/strictDefaults.test.js.map +1 -0
  21. package/dist/cli/kraken/verificationBridge.js +86 -8
  22. package/dist/cli/kraken/verificationBridge.js.map +1 -1
  23. package/dist/cli/kraken/verificationBridge.session.test.js +2 -2
  24. package/dist/cli/kraken/verificationBridge.session.test.js.map +1 -1
  25. package/dist/cli/kraken/verificationBridge.test.js +38 -22
  26. package/dist/cli/kraken/verificationBridge.test.js.map +1 -1
  27. package/dist/cli/kraken/verifierAdvisoryLock.test.js +154 -0
  28. package/dist/cli/kraken/verifierAdvisoryLock.test.js.map +1 -0
  29. package/dist/cli/kraken/verifierResolution.js +28 -0
  30. package/dist/cli/kraken/verifierResolution.js.map +1 -0
  31. package/dist/cli/kraken/verifierRoundTrip.test.js +166 -0
  32. package/dist/cli/kraken/verifierRoundTrip.test.js.map +1 -0
  33. package/dist/cli/legacyContextIsolation.test.js +3 -2
  34. package/dist/cli/legacyContextIsolation.test.js.map +1 -1
  35. package/dist/cli/main.bundled.js +707 -314
  36. package/dist/cli/main.bundled.js.map +4 -4
  37. package/dist/cli/missionContinuation.test.js +117 -0
  38. package/dist/cli/missionContinuation.test.js.map +1 -0
  39. package/dist/cli/profileMatrix.test.js +130 -0
  40. package/dist/cli/profileMatrix.test.js.map +1 -0
  41. package/dist/cli/runHeadless.js +39 -4
  42. package/dist/cli/runHeadless.js.map +1 -1
  43. package/dist/cli/sessionSpine.js +29 -3
  44. package/dist/cli/sessionSpine.js.map +1 -1
  45. package/dist/cli/zelariMission.js +16 -0
  46. package/dist/cli/zelariMission.js.map +1 -1
  47. package/package.json +2 -2
@@ -3288,10 +3288,10 @@ function mergeDefs(...defs) {
3288
3288
  function cloneDef(schema) {
3289
3289
  return mergeDefs(schema._zod.def);
3290
3290
  }
3291
- function getElementAtPath(obj, path63) {
3292
- if (!path63)
3291
+ function getElementAtPath(obj, path64) {
3292
+ if (!path64)
3293
3293
  return obj;
3294
- return path63.reduce((acc, key) => acc?.[key], obj);
3294
+ return path64.reduce((acc, key) => acc?.[key], obj);
3295
3295
  }
3296
3296
  function promiseAllObject(promisesObj) {
3297
3297
  const keys = Object.keys(promisesObj);
@@ -3619,11 +3619,11 @@ function explicitlyAborted(x, startIndex = 0) {
3619
3619
  }
3620
3620
  return false;
3621
3621
  }
3622
- function prefixIssues(path63, issues) {
3622
+ function prefixIssues(path64, issues) {
3623
3623
  return issues.map((iss) => {
3624
3624
  var _a3;
3625
3625
  (_a3 = iss).path ?? (_a3.path = []);
3626
- iss.path.unshift(path63);
3626
+ iss.path.unshift(path64);
3627
3627
  return iss;
3628
3628
  });
3629
3629
  }
@@ -3841,16 +3841,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
3841
3841
  }
3842
3842
  function formatError(error51, mapper = (issue2) => issue2.message) {
3843
3843
  const fieldErrors = { _errors: [] };
3844
- const processError = (error52, path63 = []) => {
3844
+ const processError = (error52, path64 = []) => {
3845
3845
  for (const issue2 of error52.issues) {
3846
3846
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3847
- issue2.errors.map((issues) => processError({ issues }, [...path63, ...issue2.path]));
3847
+ issue2.errors.map((issues) => processError({ issues }, [...path64, ...issue2.path]));
3848
3848
  } else if (issue2.code === "invalid_key") {
3849
- processError({ issues: issue2.issues }, [...path63, ...issue2.path]);
3849
+ processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3850
3850
  } else if (issue2.code === "invalid_element") {
3851
- processError({ issues: issue2.issues }, [...path63, ...issue2.path]);
3851
+ processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3852
3852
  } else {
3853
- const fullpath = [...path63, ...issue2.path];
3853
+ const fullpath = [...path64, ...issue2.path];
3854
3854
  if (fullpath.length === 0) {
3855
3855
  fieldErrors._errors.push(mapper(issue2));
3856
3856
  } else {
@@ -3877,17 +3877,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
3877
3877
  }
3878
3878
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
3879
3879
  const result = { errors: [] };
3880
- const processError = (error52, path63 = []) => {
3880
+ const processError = (error52, path64 = []) => {
3881
3881
  var _a3, _b;
3882
3882
  for (const issue2 of error52.issues) {
3883
3883
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3884
- issue2.errors.map((issues) => processError({ issues }, [...path63, ...issue2.path]));
3884
+ issue2.errors.map((issues) => processError({ issues }, [...path64, ...issue2.path]));
3885
3885
  } else if (issue2.code === "invalid_key") {
3886
- processError({ issues: issue2.issues }, [...path63, ...issue2.path]);
3886
+ processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3887
3887
  } else if (issue2.code === "invalid_element") {
3888
- processError({ issues: issue2.issues }, [...path63, ...issue2.path]);
3888
+ processError({ issues: issue2.issues }, [...path64, ...issue2.path]);
3889
3889
  } else {
3890
- const fullpath = [...path63, ...issue2.path];
3890
+ const fullpath = [...path64, ...issue2.path];
3891
3891
  if (fullpath.length === 0) {
3892
3892
  result.errors.push(mapper(issue2));
3893
3893
  continue;
@@ -3919,8 +3919,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
3919
3919
  }
3920
3920
  function toDotPath(_path) {
3921
3921
  const segs = [];
3922
- const path63 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
- for (const seg of path63) {
3922
+ const path64 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
+ for (const seg of path64) {
3924
3924
  if (typeof seg === "number")
3925
3925
  segs.push(`[${seg}]`);
3926
3926
  else if (typeof seg === "symbol")
@@ -17423,13 +17423,13 @@ function resolveRef(ref, ctx) {
17423
17423
  if (!ref.startsWith("#")) {
17424
17424
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
17425
17425
  }
17426
- const path63 = ref.slice(1).split("/").filter(Boolean);
17427
- if (path63.length === 0) {
17426
+ const path64 = ref.slice(1).split("/").filter(Boolean);
17427
+ if (path64.length === 0) {
17428
17428
  return ctx.rootSchema;
17429
17429
  }
17430
17430
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17431
- if (path63[0] === defsKey) {
17432
- const key = path63[1];
17431
+ if (path64[0] === defsKey) {
17432
+ const key = path64[1];
17433
17433
  if (!key || !ctx.defs[key]) {
17434
17434
  throw new Error(`Reference not found: ${ref}`);
17435
17435
  }
@@ -19750,11 +19750,11 @@ var init_tools = __esm({
19750
19750
  if (!ctx.addDocument)
19751
19751
  return "Knowledge vault tool not available.";
19752
19752
  const title = args["title"] || "New Document";
19753
- const path63 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19753
+ const path64 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19754
19754
  const content = args["content"] || "";
19755
19755
  const tags = args["tags"] || [];
19756
19756
  ctx.addDocument({
19757
- path: path63,
19757
+ path: path64,
19758
19758
  title,
19759
19759
  content,
19760
19760
  format: "markdown",
@@ -19763,7 +19763,7 @@ var init_tools = __esm({
19763
19763
  workspaceId: ctx.workspaceId
19764
19764
  });
19765
19765
  ctx.addActivity("vault", "created document", title);
19766
- return `Document "${title}" created at "${path63}".`;
19766
+ return `Document "${title}" created at "${path64}".`;
19767
19767
  }
19768
19768
  }
19769
19769
  ];
@@ -24197,11 +24197,11 @@ var init_synthesisAudit = __esm({
24197
24197
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
24198
24198
  import { join as join2 } from "node:path";
24199
24199
  function loadNfrSpec(zelariRoot) {
24200
- const path63 = join2(zelariRoot, "nfr-spec.json");
24201
- if (!existsSync7(path63))
24200
+ const path64 = join2(zelariRoot, "nfr-spec.json");
24201
+ if (!existsSync7(path64))
24202
24202
  return null;
24203
24203
  try {
24204
- const raw = JSON.parse(readFileSync7(path63, "utf8"));
24204
+ const raw = JSON.parse(readFileSync7(path64, "utf8"));
24205
24205
  if (raw.version !== 1 || !Array.isArray(raw.targets))
24206
24206
  return null;
24207
24207
  return raw;
@@ -26507,9 +26507,9 @@ var init_types4 = __esm({
26507
26507
  import { readFileSync as readFileSync12 } from "node:fs";
26508
26508
  import { join as join8 } from "node:path";
26509
26509
  function readLessonsDeduped(zelariRoot) {
26510
- const path63 = join8(zelariRoot, LESSONS_FILE);
26510
+ const path64 = join8(zelariRoot, LESSONS_FILE);
26511
26511
  try {
26512
- const raw = readFileSync12(path63, "utf8");
26512
+ const raw = readFileSync12(path64, "utf8");
26513
26513
  const byId = /* @__PURE__ */ new Map();
26514
26514
  for (const line of raw.split(/\r?\n/)) {
26515
26515
  if (!line.trim())
@@ -26610,8 +26610,8 @@ function keywordsFrom(check2, signature) {
26610
26610
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
26611
26611
  }
26612
26612
  function writeLesson(zelariRoot, lesson) {
26613
- const path63 = join9(zelariRoot, LESSONS_FILE);
26614
- appendFileSync(path63, `${JSON.stringify(lesson)}
26613
+ const path64 = join9(zelariRoot, LESSONS_FILE);
26614
+ appendFileSync(path64, `${JSON.stringify(lesson)}
26615
26615
  `, "utf8");
26616
26616
  }
26617
26617
  function findSimilar(lessons, signature) {
@@ -27225,9 +27225,9 @@ function findCycle(nodes) {
27225
27225
  if (color.get(start) !== WHITE)
27226
27226
  continue;
27227
27227
  const stack = [[start, 0]];
27228
- const path63 = [];
27228
+ const path64 = [];
27229
27229
  color.set(start, GRAY);
27230
- path63.push(start);
27230
+ path64.push(start);
27231
27231
  while (stack.length > 0) {
27232
27232
  const top = stack[stack.length - 1];
27233
27233
  const [id, idx] = top;
@@ -27240,17 +27240,17 @@ function findCycle(nodes) {
27240
27240
  continue;
27241
27241
  const c = color.get(dep);
27242
27242
  if (c === GRAY) {
27243
- const at = path63.indexOf(dep);
27244
- return [...path63.slice(at), dep];
27243
+ const at = path64.indexOf(dep);
27244
+ return [...path64.slice(at), dep];
27245
27245
  }
27246
27246
  if (c === WHITE) {
27247
27247
  color.set(dep, GRAY);
27248
- path63.push(dep);
27248
+ path64.push(dep);
27249
27249
  stack.push([dep, 0]);
27250
27250
  }
27251
27251
  } else {
27252
27252
  color.set(id, BLACK);
27253
- path63.pop();
27253
+ path64.pop();
27254
27254
  stack.pop();
27255
27255
  }
27256
27256
  }
@@ -28170,8 +28170,8 @@ var init_runner = __esm({
28170
28170
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
28171
28171
  pending: []
28172
28172
  };
28173
- const path63 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
28174
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path63}`);
28173
+ const path64 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
28174
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path64}`);
28175
28175
  return snapshot;
28176
28176
  }
28177
28177
  callLog(msg, data) {
@@ -28271,7 +28271,14 @@ var init_types8 = __esm({
28271
28271
  "council.member",
28272
28272
  "mission.phase",
28273
28273
  "mission.replan",
28274
+ // F4 (doc §6 advisory continuation): driver → host advice record. State-only
28275
+ // (never model-surface): the recommendation must not feed the model loop.
28276
+ "mission.progress",
28274
28277
  "verification.run",
28278
+ // F3 (ADR-0023 §5 evidence traceability): per-observation state event —
28279
+ // the session-log anchor EvidenceRef.seq points at (command output, fs
28280
+ // observation, digest). Not model-surface. Schema review per ADR-0021.
28281
+ "verification.evidence",
28275
28282
  "note"
28276
28283
  ];
28277
28284
  SessionEventEnvelopeSchema = external_exports.object({
@@ -28590,6 +28597,7 @@ function buildProjection(events, issues = []) {
28590
28597
  toolResults: 0,
28591
28598
  verifications: [],
28592
28599
  missionPhases: [],
28600
+ missionAdvice: [],
28593
28601
  replans: 0,
28594
28602
  issues
28595
28603
  };
@@ -28625,6 +28633,14 @@ function buildProjection(events, issues = []) {
28625
28633
  case "mission.replan":
28626
28634
  projection.replans += 1;
28627
28635
  break;
28636
+ case "mission.progress":
28637
+ projection.missionAdvice.push({
28638
+ seq: e.seq,
28639
+ recommendation: String(e.data.recommendation ?? ""),
28640
+ rationale: String(e.data.rationale ?? "")
28641
+ });
28642
+ break;
28643
+ break;
28628
28644
  }
28629
28645
  }
28630
28646
  return projection;
@@ -29365,7 +29381,12 @@ var init_runtime2 = __esm({
29365
29381
  });
29366
29382
 
29367
29383
  // packages/core/dist/verification/types.js
29368
- var CriterionSourceSchema, CommandCheckSchema, FileExistsCheckSchema, FileContainsCheckSchema, FileAbsentCheckSchema, NoneCheckSchema, DeterministicCheckSchema, CriterionSchema, EvidenceTierSchema, EvidenceRefSchema, VerificationStatusSchema, VerificationSourceSchema, VerificationResultSchema;
29384
+ function isEventBackedEvidence(ref) {
29385
+ if (!EVENT_BACKED_EVIDENCE_TIERS.includes(ref.tier))
29386
+ return true;
29387
+ return typeof ref.seq === "number" && ref.seq > 0;
29388
+ }
29389
+ var CriterionSourceSchema, CommandCheckSchema, FileExistsCheckSchema, FileContainsCheckSchema, FileAbsentCheckSchema, NoneCheckSchema, DeterministicCheckSchema, CriterionSchema, EvidenceTierSchema, EvidenceRefSchema, EVENT_BACKED_EVIDENCE_TIERS, VerificationStatusSchema, VerificationSourceSchema, VerificationResultSchema;
29369
29390
  var init_types9 = __esm({
29370
29391
  "packages/core/dist/verification/types.js"() {
29371
29392
  "use strict";
@@ -29428,7 +29449,11 @@ var init_types9 = __esm({
29428
29449
  "human"
29429
29450
  ]);
29430
29451
  EvidenceRefSchema = external_exports.object({
29431
- /** Seq of the originating session event, when the evidence is logged. */
29452
+ /**
29453
+ * Seq of the originating session event. Required (by policy) for
29454
+ * event-backed tiers on the 2.0 path: evidence without a session anchor
29455
+ * is narration, not traceable output (F3 / ADR-0023 §5).
29456
+ */
29432
29457
  seq: external_exports.number().int().positive().optional(),
29433
29458
  tier: EvidenceTierSchema,
29434
29459
  /** Human-readable pointer: command line, file path, tool call id… */
@@ -29437,6 +29462,11 @@ var init_types9 = __esm({
29437
29462
  /** sha256 of the captured output (audit chain). */
29438
29463
  digest: external_exports.string().optional()
29439
29464
  });
29465
+ EVENT_BACKED_EVIDENCE_TIERS = [
29466
+ "tool-output",
29467
+ "command-output",
29468
+ "fs-observation"
29469
+ ];
29440
29470
  VerificationStatusSchema = external_exports.enum(["pass", "fail", "unknown"]);
29441
29471
  VerificationSourceSchema = external_exports.enum([
29442
29472
  "deterministic-engine",
@@ -29482,21 +29512,52 @@ var init_engine = __esm({
29482
29512
  results.push(await this.evaluateOne(criterion));
29483
29513
  }
29484
29514
  if (this.options.emit) {
29485
- await this.options.emit({
29486
- kind: "verification.run",
29515
+ try {
29516
+ await this.options.emit({
29517
+ kind: "verification.run",
29518
+ actor: { type: "system", role: "verification" },
29519
+ data: {
29520
+ source: "deterministic-engine",
29521
+ packId: context.packId,
29522
+ results: results.map((r) => ({
29523
+ criterionId: r.criterionId,
29524
+ status: r.status,
29525
+ evidence: r.evidence.map((e) => ({
29526
+ tier: e.tier,
29527
+ ref: e.ref,
29528
+ digest: e.digest,
29529
+ ...e.seq !== void 0 ? { seq: e.seq } : {}
29530
+ }))
29531
+ }))
29532
+ }
29533
+ });
29534
+ } catch {
29535
+ }
29536
+ }
29537
+ return results;
29538
+ }
29539
+ /**
29540
+ * F3: append one `verification.evidence` state event with the raw
29541
+ * observation and resolve to its session seq when the emitter returns it.
29542
+ * Spine failures degrade to `undefined` — an unreachable log must never
29543
+ * fail verification, it only leaves the evidence unanchored.
29544
+ */
29545
+ async emitEvidence(data) {
29546
+ if (!this.options.emit)
29547
+ return void 0;
29548
+ try {
29549
+ const out = await this.options.emit({
29550
+ kind: "verification.evidence",
29487
29551
  actor: { type: "system", role: "verification" },
29488
- data: {
29489
- source: "deterministic-engine",
29490
- packId: context.packId,
29491
- results: results.map((r) => ({
29492
- criterionId: r.criterionId,
29493
- status: r.status,
29494
- evidence: r.evidence.map((e) => ({ tier: e.tier, ref: e.ref, digest: e.digest }))
29495
- }))
29496
- }
29552
+ data
29497
29553
  });
29554
+ if (out && typeof out === "object" && typeof out.seq === "number") {
29555
+ return out.seq;
29556
+ }
29557
+ return void 0;
29558
+ } catch {
29559
+ return void 0;
29498
29560
  }
29499
- return results;
29500
29561
  }
29501
29562
  async evaluateOne(criterion) {
29502
29563
  const started = this.options.now?.() ?? Date.now();
@@ -29536,12 +29597,23 @@ var init_engine = __esm({
29536
29597
  return done({ status: "unknown", evidence: [], detail: "shell provider unavailable" });
29537
29598
  }
29538
29599
  const result = await shell.exec(check2.command, { timeoutMs: check2.timeoutMs });
29600
+ const digest = sha256(result.stdout);
29601
+ const seq = await this.emitEvidence({
29602
+ observation: "command",
29603
+ command: check2.command,
29604
+ exitCode: result.exitCode,
29605
+ timedOut: result.timedOut,
29606
+ digest,
29607
+ stdoutTail: tail(result.stdout),
29608
+ stderrTail: tail(result.stderr)
29609
+ });
29539
29610
  const evidence = [
29540
29611
  {
29541
29612
  tier: "command-output",
29542
29613
  ref: `${check2.command} \u2192 exit ${result.exitCode ?? "signal"}`,
29543
29614
  capturedAt: Date.now(),
29544
- digest: sha256(result.stdout)
29615
+ digest,
29616
+ ...seq !== void 0 ? { seq } : {}
29545
29617
  }
29546
29618
  ];
29547
29619
  if (result.timedOut) {
@@ -29564,8 +29636,21 @@ var init_engine = __esm({
29564
29636
  }
29565
29637
  return done({ status: "pass", evidence });
29566
29638
  }
29567
- fsEvidence(path63, content, sha256) {
29568
- return { tier: "fs-observation", ref: path63, capturedAt: Date.now(), digest: sha256(content) };
29639
+ /**
29640
+ * F3: the fs observation is logged to the spine (path, existence, optional
29641
+ * content digest) and the returned ref carries the event seq when the
29642
+ * emitter resolved one.
29643
+ */
29644
+ async fsEvidence(observation, path64, sha256, content, extra = {}) {
29645
+ const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
29646
+ const seq = await this.emitEvidence({ observation, path: path64, ...extra, ...digest ? { digest } : {} });
29647
+ return {
29648
+ tier: "fs-observation",
29649
+ ref: path64,
29650
+ capturedAt: Date.now(),
29651
+ ...digest ? { digest } : {},
29652
+ ...seq !== void 0 ? { seq } : {}
29653
+ };
29569
29654
  }
29570
29655
  async evalFileExists(check2, done) {
29571
29656
  const sha256 = this.options.sha256 ?? defaultSha256;
@@ -29573,17 +29658,29 @@ var init_engine = __esm({
29573
29658
  if (!fs37)
29574
29659
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
29575
29660
  const exists = await fs37.exists(check2.path);
29576
- if (!exists)
29661
+ if (!exists) {
29662
+ await this.emitEvidence({ observation: "file-exists", path: check2.path, exists: false });
29577
29663
  return done({ status: "fail", evidence: [], detail: `file not found: ${check2.path}` });
29664
+ }
29578
29665
  const content = await fs37.readFile(check2.path).catch(() => "");
29579
- return done({ status: "pass", evidence: [this.fsEvidence(check2.path, content, sha256)] });
29666
+ return done({
29667
+ status: "pass",
29668
+ evidence: [await this.fsEvidence("file-exists", check2.path, sha256, content, { exists: true })]
29669
+ });
29580
29670
  }
29581
29671
  async evalFileAbsent(check2, done) {
29582
29672
  const fs37 = this.services.fs;
29583
29673
  if (!fs37)
29584
29674
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
29585
29675
  const exists = await fs37.exists(check2.path);
29586
- return exists ? done({ status: "fail", evidence: [], detail: `file still present: ${check2.path}` }) : done({ status: "pass", evidence: [{ tier: "fs-observation", ref: check2.path, capturedAt: Date.now() }] });
29676
+ if (exists) {
29677
+ await this.emitEvidence({ observation: "file-absent", path: check2.path, exists: true });
29678
+ return done({ status: "fail", evidence: [], detail: `file still present: ${check2.path}` });
29679
+ }
29680
+ return done({
29681
+ status: "pass",
29682
+ evidence: [await this.fsEvidence("file-absent", check2.path, void 0, void 0, { exists: false })]
29683
+ });
29587
29684
  }
29588
29685
  async evalFileContains(check2, done) {
29589
29686
  const sha256 = this.options.sha256 ?? defaultSha256;
@@ -29594,9 +29691,10 @@ var init_engine = __esm({
29594
29691
  try {
29595
29692
  content = await fs37.readFile(check2.path);
29596
29693
  } catch {
29694
+ await this.emitEvidence({ observation: "file-contains", path: check2.path, readable: false });
29597
29695
  return done({ status: "fail", evidence: [], detail: `file not readable: ${check2.path}` });
29598
29696
  }
29599
- const evidence = [this.fsEvidence(check2.path, content, sha256)];
29697
+ const evidence = [await this.fsEvidence("file-contains", check2.path, sha256, content)];
29600
29698
  let hit;
29601
29699
  try {
29602
29700
  hit = new RegExp(check2.pattern, "m").test(content);
@@ -29622,6 +29720,7 @@ function evaluateCompletion(criteria, results, policy = STRICT_ALL_POLICY) {
29622
29720
  const order = new Map(criteria.map((c, i) => [c.id, i]));
29623
29721
  const satisfied = [];
29624
29722
  const unsatisfied = [];
29723
+ let unbackedSatisfied = false;
29625
29724
  const ids = [...requiredIds].sort((a, b) => (order.get(a) ?? 0) - (order.get(b) ?? 0));
29626
29725
  for (const id of ids) {
29627
29726
  const result = byId.get(id);
@@ -29646,7 +29745,8 @@ function evaluateCompletion(criteria, results, policy = STRICT_ALL_POLICY) {
29646
29745
  continue;
29647
29746
  }
29648
29747
  const admissible = policy.admissibleTiers;
29649
- if (admissible && !result.evidence.some((e) => admissible.includes(e.tier))) {
29748
+ const tierOk = (e) => !admissible || admissible.includes(e.tier);
29749
+ if (!result.evidence.some(tierOk)) {
29650
29750
  const tiers = [...new Set(result.evidence.map((e) => e.tier))].join(", ");
29651
29751
  unsatisfied.push({
29652
29752
  id,
@@ -29655,7 +29755,18 @@ function evaluateCompletion(criteria, results, policy = STRICT_ALL_POLICY) {
29655
29755
  });
29656
29756
  continue;
29657
29757
  }
29758
+ const backed = result.evidence.filter(tierOk).some(isEventBackedEvidence);
29759
+ if (policy.requireEventBackedEvidence && !backed) {
29760
+ unsatisfied.push({
29761
+ id,
29762
+ status: "unknown",
29763
+ reason: "pass without event-backed evidence \u2014 no EvidenceRef.seq anchored to a session event"
29764
+ });
29765
+ continue;
29766
+ }
29658
29767
  satisfied.push(id);
29768
+ if (!backed)
29769
+ unbackedSatisfied = true;
29659
29770
  }
29660
29771
  const verdict = unsatisfied.length === 0 ? "PASS" : unsatisfied.some((u) => u.status === "fail") ? "REPAIR_REQUIRED" : "BLOCKED";
29661
29772
  return {
@@ -29663,6 +29774,7 @@ function evaluateCompletion(criteria, results, policy = STRICT_ALL_POLICY) {
29663
29774
  satisfied,
29664
29775
  unsatisfied,
29665
29776
  evidenceComplete: unsatisfied.length === 0,
29777
+ eventBackedEvidenceComplete: unsatisfied.length === 0 && !unbackedSatisfied,
29666
29778
  summary: verdict === "PASS" ? `complete: ${satisfied.length}/${ids.length} required criteria pass with evidence` : `incomplete (${verdict}): ${unsatisfied.map((u) => `${u.id}=${u.status}`).join(", ")}`
29667
29779
  };
29668
29780
  }
@@ -29670,6 +29782,7 @@ var STRICT_ALL_POLICY, DETERMINISTIC_EVIDENCE_TIERS, STRICT_BUILD_POLICY, strict
29670
29782
  var init_completionPolicy = __esm({
29671
29783
  "packages/core/dist/verification/completionPolicy.js"() {
29672
29784
  "use strict";
29785
+ init_types9();
29673
29786
  STRICT_ALL_POLICY = { mode: "strict", required: "*" };
29674
29787
  DETERMINISTIC_EVIDENCE_TIERS = [
29675
29788
  "tool-output",
@@ -29680,7 +29793,8 @@ var init_completionPolicy = __esm({
29680
29793
  STRICT_BUILD_POLICY = {
29681
29794
  mode: "strict",
29682
29795
  required: "*",
29683
- admissibleTiers: DETERMINISTIC_EVIDENCE_TIERS
29796
+ admissibleTiers: DETERMINISTIC_EVIDENCE_TIERS,
29797
+ requireEventBackedEvidence: true
29684
29798
  };
29685
29799
  strictBuildGate = evaluateCompletion;
29686
29800
  }
@@ -29762,6 +29876,9 @@ function snapshotToCompletionEvaluation(snap) {
29762
29876
  satisfied: [...snap.satisfied],
29763
29877
  unsatisfied,
29764
29878
  evidenceComplete: snap.evidenceComplete && unsatisfied.length === 0 && verdict === "PASS",
29879
+ // F3: snapshot summaries do not carry per-ref seq — event-backedness is
29880
+ // auditable from the raw spine events (verification.evidence), unknown here.
29881
+ eventBackedEvidenceComplete: false,
29765
29882
  summary: snap.summary
29766
29883
  };
29767
29884
  }
@@ -29776,6 +29893,7 @@ var init_sessionEvidence = __esm({
29776
29893
  // packages/core/dist/verification/criteriaPack.v1.js
29777
29894
  function codingCriteriaPack(options = {}) {
29778
29895
  const timeoutMs = options.commandTimeoutMs ?? 6e5;
29896
+ const withDefault = (o, d) => o === void 0 ? d : o;
29779
29897
  const command = (command2) => command2 ? { kind: "command", command: command2, timeoutMs } : void 0;
29780
29898
  const criteria = [
29781
29899
  {
@@ -29783,21 +29901,21 @@ function codingCriteriaPack(options = {}) {
29783
29901
  text: "Static checks (typecheck) pass with no new errors.",
29784
29902
  source: "criteria-pack",
29785
29903
  required: true,
29786
- check: command(options.typecheckCommand ?? "npm run typecheck")
29904
+ check: command(withDefault(options.typecheckCommand, "npm run typecheck"))
29787
29905
  },
29788
29906
  {
29789
29907
  id: "correctness.specification",
29790
29908
  text: "The test suite passes \u2014 behavior matches the specification.",
29791
29909
  source: "criteria-pack",
29792
29910
  required: true,
29793
- check: command(options.testCommand ?? "npm run test")
29911
+ check: command(withDefault(options.testCommand, "npm run test"))
29794
29912
  },
29795
29913
  {
29796
29914
  id: "correctness.observable-output",
29797
29915
  text: "The project builds successfully (observable output artifact).",
29798
29916
  source: "criteria-pack",
29799
29917
  required: true,
29800
- check: command(options.buildCommand ?? "npm run build")
29918
+ check: command(withDefault(options.buildCommand, "npm run build"))
29801
29919
  },
29802
29920
  {
29803
29921
  id: "quality.scope-discipline",
@@ -30095,7 +30213,8 @@ function deriveMissionState(projection) {
30095
30213
  verifications: projection.verifications.length,
30096
30214
  lastSeq: projection.lastSeq,
30097
30215
  interrupted: projection.lastSeq > 0 && projection.endedAt === void 0,
30098
- forkParent: projection.fork?.parentSessionId
30216
+ forkParent: projection.fork?.parentSessionId,
30217
+ lastAdvice: projection.missionAdvice[projection.missionAdvice.length - 1]
30099
30218
  };
30100
30219
  }
30101
30220
  function missionSnapshot(state3) {
@@ -30113,11 +30232,99 @@ var init_missionState = __esm({
30113
30232
  }
30114
30233
  });
30115
30234
 
30235
+ // packages/core/dist/mission/continuationPolicy.js
30236
+ function requiredBlockers(progress) {
30237
+ const blockers = [];
30238
+ if (progress.ratio === null) {
30239
+ blockers.push("no verification criteria recorded yet");
30240
+ return blockers;
30241
+ }
30242
+ const missing = progress.criteriaTotal - progress.criteriaPassed;
30243
+ if (missing > 0) {
30244
+ blockers.push(`${missing}/${progress.criteriaTotal} required criteria not passing`);
30245
+ }
30246
+ if (progress.criteriaPassed === progress.criteriaTotal && !progress.evidenceComplete) {
30247
+ blockers.push("all criteria pass but evidence is incomplete (not event-backed)");
30248
+ }
30249
+ return blockers;
30250
+ }
30251
+ function evaluateMissionContinuation(input) {
30252
+ const { progress, verifier, budget, userSteer } = input;
30253
+ const base = { goalRewrite: false, doneByScore: false };
30254
+ const trend = verifier ? { tier: verifier.tier, value: verifier.value } : void 0;
30255
+ const blockers = requiredBlockers(progress);
30256
+ if (userSteer === "stop") {
30257
+ return {
30258
+ ...base,
30259
+ recommendation: "wind-down",
30260
+ rationale: "user steer: explicit stop \u2014 winding down without claiming done",
30261
+ blockers,
30262
+ trend
30263
+ };
30264
+ }
30265
+ if (userSteer === "continue") {
30266
+ return {
30267
+ ...base,
30268
+ recommendation: "continue",
30269
+ rationale: "user steer: explicit continue \u2014 overrides any trend-derived advice",
30270
+ blockers,
30271
+ trend
30272
+ };
30273
+ }
30274
+ if (budget && budget.iterationsUsed >= budget.iterationsMax) {
30275
+ return {
30276
+ ...base,
30277
+ recommendation: "hold-for-user",
30278
+ rationale: `iteration budget exhausted (${budget.iterationsUsed}/${budget.iterationsMax}) \u2014 operator decides: raise budget or hand off`,
30279
+ blockers,
30280
+ trend
30281
+ };
30282
+ }
30283
+ if (blockers.length > 0) {
30284
+ const trendNote = verifier ? ` \xB7 verifier trend recorded as context only (tier ${verifier.tier}${verifier.value === null ? "" : `, ${verifier.value.toFixed(2)}`}) \u2014 trend is never authority` : "";
30285
+ return {
30286
+ ...base,
30287
+ recommendation: "continue",
30288
+ rationale: `required criteria incomplete \u2192 no early-stop${trendNote}`,
30289
+ blockers,
30290
+ trend
30291
+ };
30292
+ }
30293
+ const blended = verifier?.tier === "blended" ? verifier.value : null;
30294
+ const rejected = verifier?.verdict === "rejected" || blended !== null && blended <= REJECTED_BLEND;
30295
+ if (rejected) {
30296
+ return {
30297
+ ...base,
30298
+ recommendation: "hold-for-user",
30299
+ rationale: "deterministic evidence is complete and PASSing, but the verifier flags risk \u2014 attention requested; the deterministic verdict stands untouched",
30300
+ blockers: [],
30301
+ trend
30302
+ };
30303
+ }
30304
+ const confirmed = verifier?.verdict === "confirmed" || blended !== null && blended >= CONFIRMED_BLEND;
30305
+ return {
30306
+ ...base,
30307
+ recommendation: "wind-down",
30308
+ rationale: confirmed ? "required criteria pass with evidence and the verifier confirms \u2014 candidate done (final say: driver + CompletionPolicy)" : "required criteria pass with evidence \u2014 candidate done on deterministic evidence (final say: driver + CompletionPolicy)",
30309
+ blockers: [],
30310
+ trend
30311
+ };
30312
+ }
30313
+ var CONFIRMED_BLEND, REJECTED_BLEND;
30314
+ var init_continuationPolicy = __esm({
30315
+ "packages/core/dist/mission/continuationPolicy.js"() {
30316
+ "use strict";
30317
+ CONFIRMED_BLEND = 0.8;
30318
+ REJECTED_BLEND = 0.35;
30319
+ }
30320
+ });
30321
+
30116
30322
  // packages/core/dist/mission/index.js
30117
30323
  var init_mission2 = __esm({
30118
30324
  "packages/core/dist/mission/index.js"() {
30119
30325
  "use strict";
30120
30326
  init_missionState();
30327
+ init_continuationPolicy();
30121
30328
  }
30122
30329
  });
30123
30330
 
@@ -30157,6 +30364,7 @@ __export(dist_exports, {
30157
30364
  DETERMINISTIC_EVIDENCE_TIERS: () => DETERMINISTIC_EVIDENCE_TIERS,
30158
30365
  DOOM_LOOP_THRESHOLD: () => DOOM_LOOP_THRESHOLD,
30159
30366
  DeterministicCheckSchema: () => DeterministicCheckSchema,
30367
+ EVENT_BACKED_EVIDENCE_TIERS: () => EVENT_BACKED_EVIDENCE_TIERS,
30160
30368
  EXPERIMENTAL_FLAGS: () => EXPERIMENTAL_FLAGS,
30161
30369
  EventBus: () => EventBus,
30162
30370
  EvidenceRefSchema: () => EvidenceRefSchema,
@@ -30302,6 +30510,7 @@ __export(dist_exports, {
30302
30510
  emptyContextGrowthStats: () => emptyContextGrowthStats,
30303
30511
  enforceDesignPhaseToolEmissions: () => enforceDesignPhaseToolEmissions,
30304
30512
  evaluateCompletion: () => evaluateCompletion,
30513
+ evaluateMissionContinuation: () => evaluateMissionContinuation,
30305
30514
  executeTool: () => executeTool,
30306
30515
  exportSession: () => exportSession,
30307
30516
  exportSessionJson: () => exportSessionJson,
@@ -30357,6 +30566,7 @@ __export(dist_exports, {
30357
30566
  isBrainToolExecutionStartEvent: () => isBrainToolExecutionStartEvent,
30358
30567
  isBrainToolExecutionUpdateEvent: () => isBrainToolExecutionUpdateEvent,
30359
30568
  isConverged: () => isConverged,
30569
+ isEventBackedEvidence: () => isEventBackedEvidence,
30360
30570
  isExperimentalEnabled: () => isExperimentalEnabled,
30361
30571
  isModelSurfaceEvent: () => isModelSurfaceEvent,
30362
30572
  isReviewerKind: () => isReviewerKind,
@@ -30817,7 +31027,7 @@ var init_sessionSpine = __esm({
30817
31027
  this.sessionsDir = resolveSessionsDir({ baseDir: options.baseDir });
30818
31028
  }
30819
31029
  writer = null;
30820
- chain = Promise.resolve();
31030
+ chain = Promise.resolve(null);
30821
31031
  streamBuffers = /* @__PURE__ */ new Map();
30822
31032
  warned = false;
30823
31033
  status = "disabled";
@@ -30953,18 +31163,44 @@ var init_sessionSpine = __esm({
30953
31163
  data: { phase: phase2, ...note ? { note } : {} }
30954
31164
  });
30955
31165
  }
31166
+ /**
31167
+ * F4 (doc §6): append an advisory continuation record. State-only — the
31168
+ * recommendation must never feed the model loop or rewrite the goal.
31169
+ */
31170
+ missionProgress(advice) {
31171
+ void this.append({
31172
+ kind: "mission.progress",
31173
+ actor: ACTOR_SYSTEM,
31174
+ data: {
31175
+ recommendation: advice.recommendation,
31176
+ rationale: advice.rationale,
31177
+ ...advice.blockers ? { blockers: advice.blockers } : {},
31178
+ ...advice.trend ? { trend: advice.trend } : {},
31179
+ ...advice.iteration !== void 0 ? { iteration: advice.iteration } : {}
31180
+ }
31181
+ });
31182
+ }
30956
31183
  note(text, data) {
30957
31184
  void this.append({ kind: "note", actor: ACTOR_SYSTEM, data: { note: text, ...data } });
30958
31185
  }
30959
31186
  append(input) {
30960
- if (!this.writer || this.status === "closed") return Promise.resolve();
30961
- this.chain = this.chain.then(() => this.writer.append(input)).then(() => void 0).catch((err) => {
31187
+ if (!this.writer || this.status === "closed") return Promise.resolve(null);
31188
+ this.chain = this.chain.then(() => this.writer.append(input)).then((envelope) => envelope.seq).catch((err) => {
30962
31189
  this.status = "degraded";
30963
31190
  this.writer = null;
30964
31191
  this.warnOnce(err);
31192
+ return null;
30965
31193
  });
30966
31194
  return this.chain;
30967
31195
  }
31196
+ /**
31197
+ * F3 (ADR-0023 §5): append one spine event and resolve to its assigned
31198
+ * seq — the anchor EvidenceRef.seq points at. Null when degraded or
31199
+ * disabled: callers must treat null as "not traceable", never as failure.
31200
+ */
31201
+ appendEvent(input) {
31202
+ return this.append(input);
31203
+ }
30968
31204
  warnOnce(err) {
30969
31205
  if (this.warned || this.options.quiet) return;
30970
31206
  this.warned = true;
@@ -32583,9 +32819,9 @@ function spillToolOutput(fullText, meta3) {
32583
32819
  const rnd = randomBytes2(3).toString("hex");
32584
32820
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
32585
32821
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
32586
- const path63 = join11(dir, file2);
32587
- writeFileSync11(path63, fullText, "utf8");
32588
- return path63;
32822
+ const path64 = join11(dir, file2);
32823
+ writeFileSync11(path64, fullText, "utf8");
32824
+ return path64;
32589
32825
  } catch {
32590
32826
  return null;
32591
32827
  }
@@ -32631,10 +32867,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
32631
32867
  ${tail2}`;
32632
32868
  }
32633
32869
  if (doSpill) {
32634
- const path63 = spillToolOutput(text, { toolName: opts.toolName });
32635
- if (path63) {
32870
+ const path64 = spillToolOutput(text, { toolName: opts.toolName });
32871
+ if (path64) {
32636
32872
  const spillNote = `
32637
- \u2026 [full output spilled to: ${path63} \u2014 re-read with read_file if you need the complete text] \u2026`;
32873
+ \u2026 [full output spilled to: ${path64} \u2014 re-read with read_file if you need the complete text] \u2026`;
32638
32874
  if (preview.includes("] \u2026\n")) {
32639
32875
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
32640
32876
  `);
@@ -35297,28 +35533,28 @@ var init_storage = __esm({
35297
35533
  VALID_SCALARS = /^(true|false|null|~)$/i;
35298
35534
  Storage = class {
35299
35535
  /** Read a Markdown file with frontmatter. Throws if not found. */
35300
- read(path63) {
35301
- if (!existsSync19(path63)) {
35302
- throw new Error(`File not found: ${path63}`);
35536
+ read(path64) {
35537
+ if (!existsSync19(path64)) {
35538
+ throw new Error(`File not found: ${path64}`);
35303
35539
  }
35304
- const md = readFileSync16(path63, "utf8");
35540
+ const md = readFileSync16(path64, "utf8");
35305
35541
  return parseFrontmatter(md);
35306
35542
  }
35307
35543
  /** Read a Markdown file; returns null if not found. */
35308
- readIfExists(path63) {
35309
- if (!existsSync19(path63)) return null;
35310
- return this.read(path63);
35544
+ readIfExists(path64) {
35545
+ if (!existsSync19(path64)) return null;
35546
+ return this.read(path64);
35311
35547
  }
35312
35548
  /**
35313
35549
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
35314
35550
  * The meta object is serialized as YAML frontmatter; body as Markdown.
35315
35551
  */
35316
- write(path63, meta3, body) {
35317
- mkdirSync11(dirname2(path63), { recursive: true });
35318
- const tmp = path63 + ".tmp-" + process.pid;
35552
+ write(path64, meta3, body) {
35553
+ mkdirSync11(dirname2(path64), { recursive: true });
35554
+ const tmp = path64 + ".tmp-" + process.pid;
35319
35555
  const md = serializeFrontmatter(meta3, body);
35320
35556
  writeFileSync13(tmp, md, "utf8");
35321
- renameSync2(tmp, path63);
35557
+ renameSync2(tmp, path64);
35322
35558
  }
35323
35559
  /** List all .md files in a directory (non-recursive). */
35324
35560
  listMarkdown(dir) {
@@ -35380,8 +35616,8 @@ function nextPlanTaskId(store6) {
35380
35616
  return `t${store6.counter}`;
35381
35617
  }
35382
35618
  function writePlanTaskArtifact(rootDir, task) {
35383
- const path63 = join15(rootDir, "plan-tasks", `${task.id}.md`);
35384
- mkdirSync12(dirname3(path63), { recursive: true });
35619
+ const path64 = join15(rootDir, "plan-tasks", `${task.id}.md`);
35620
+ mkdirSync12(dirname3(path64), { recursive: true });
35385
35621
  const meta3 = {
35386
35622
  kind: "task",
35387
35623
  id: task.id,
@@ -35402,7 +35638,7 @@ function writePlanTaskArtifact(rootDir, task) {
35402
35638
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
35403
35639
  ""
35404
35640
  ].filter((l) => l !== null).join("\n");
35405
- new Storage().write(path63, meta3, body);
35641
+ new Storage().write(path64, meta3, body);
35406
35642
  }
35407
35643
  function loadHandle(rootDir) {
35408
35644
  const jsonPath = join15(rootDir, "plan.json");
@@ -37960,21 +38196,21 @@ function normalizeAuth(auth) {
37960
38196
  return "agent";
37961
38197
  }
37962
38198
  function readSecrets() {
37963
- const path63 = getSshSecretsPath();
37964
- if (!existsSync23(path63)) return {};
38199
+ const path64 = getSshSecretsPath();
38200
+ if (!existsSync23(path64)) return {};
37965
38201
  try {
37966
- return JSON.parse(readFileSync20(path63, "utf8"));
38202
+ return JSON.parse(readFileSync20(path64, "utf8"));
37967
38203
  } catch {
37968
38204
  return {};
37969
38205
  }
37970
38206
  }
37971
38207
  function writeSecrets(data) {
37972
- const path63 = getSshSecretsPath();
37973
- mkdirSync13(dirname4(path63), { recursive: true });
37974
- writeFileSync15(path63, `${JSON.stringify(data, null, 2)}
38208
+ const path64 = getSshSecretsPath();
38209
+ mkdirSync13(dirname4(path64), { recursive: true });
38210
+ writeFileSync15(path64, `${JSON.stringify(data, null, 2)}
37975
38211
  `, "utf8");
37976
38212
  try {
37977
- chmodSync(path63, 384);
38213
+ chmodSync(path64, 384);
37978
38214
  } catch {
37979
38215
  }
37980
38216
  }
@@ -38003,10 +38239,10 @@ function deleteSshPassword(id) {
38003
38239
  writeSecrets({ passwords });
38004
38240
  }
38005
38241
  function readStore2() {
38006
- const path63 = getSshTargetsPath();
38007
- if (!existsSync23(path63)) return [];
38242
+ const path64 = getSshTargetsPath();
38243
+ if (!existsSync23(path64)) return [];
38008
38244
  try {
38009
- const parsed = JSON.parse(readFileSync20(path63, "utf8"));
38245
+ const parsed = JSON.parse(readFileSync20(path64, "utf8"));
38010
38246
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
38011
38247
  return list.filter(
38012
38248
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -38021,11 +38257,11 @@ function readStore2() {
38021
38257
  }
38022
38258
  }
38023
38259
  function writeStore2(targets) {
38024
- const path63 = getSshTargetsPath();
38025
- mkdirSync13(dirname4(path63), { recursive: true });
38260
+ const path64 = getSshTargetsPath();
38261
+ mkdirSync13(dirname4(path64), { recursive: true });
38026
38262
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
38027
38263
  writeFileSync15(
38028
- path63,
38264
+ path64,
38029
38265
  `${JSON.stringify({ targets: clean }, null, 2)}
38030
38266
  `,
38031
38267
  "utf8"
@@ -38271,11 +38507,11 @@ function formatSshTargetsForPrompt() {
38271
38507
  ];
38272
38508
  for (const t of targets) {
38273
38509
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
38274
- const path63 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
38510
+ const path64 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
38275
38511
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
38276
38512
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
38277
38513
  lines.push(
38278
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path63}${tags}${allow}`
38514
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path64}${tags}${allow}`
38279
38515
  );
38280
38516
  }
38281
38517
  return lines.join("\n");
@@ -39985,6 +40221,9 @@ function parseHeadlessFlags(argv) {
39985
40221
  i++;
39986
40222
  } else if (arg === "--strict-done") {
39987
40223
  strictDone = true;
40224
+ } else if (arg === "--no-strict-done") {
40225
+ strictDone = false;
40226
+ process.env.ZELARI_MISSION_STRICT = "0";
39988
40227
  } else if (arg === "--kraken-graph") {
39989
40228
  krakenGraph = argv[i + 1];
39990
40229
  i++;
@@ -40138,12 +40377,18 @@ async function openHeadlessSpine(opts) {
40138
40377
  verificationRun(payload) {
40139
40378
  spine.verificationRun(payload);
40140
40379
  },
40380
+ appendEvent(input) {
40381
+ return spine.appendEvent(input);
40382
+ },
40141
40383
  lastVerificationRun() {
40142
40384
  return spine.lastVerificationRun();
40143
40385
  },
40144
40386
  missionPhase(phase2, note) {
40145
40387
  spine.missionPhase(phase2, note);
40146
40388
  },
40389
+ missionProgress(advice) {
40390
+ spine.missionProgress(advice);
40391
+ },
40147
40392
  note(text, data) {
40148
40393
  spine.note(text, data);
40149
40394
  },
@@ -40253,12 +40498,12 @@ var init_headlessSpine = __esm({
40253
40498
  // src/cli/state/fileStateStore.ts
40254
40499
  import { createHash as createHash10, randomUUID as randomUUID2 } from "node:crypto";
40255
40500
  import { promises as fs21 } from "node:fs";
40256
- import * as path40 from "node:path";
40501
+ import * as path41 from "node:path";
40257
40502
  function shortId() {
40258
40503
  return randomUUID2().replace(/-/g, "").slice(0, 12);
40259
40504
  }
40260
40505
  async function writeJsonAtomic(filePath, data) {
40261
- await fs21.mkdir(path40.dirname(filePath), { recursive: true });
40506
+ await fs21.mkdir(path41.dirname(filePath), { recursive: true });
40262
40507
  const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
40263
40508
  await fs21.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
40264
40509
  await fs21.rename(tmp, filePath);
@@ -40319,11 +40564,11 @@ var init_fileStateStore = __esm({
40319
40564
  indexPath = "";
40320
40565
  async init(projectRoot) {
40321
40566
  this.root = projectRoot;
40322
- this.stateDir = path40.join(projectRoot, ".zelari", "state");
40323
- this.commitsDir = path40.join(this.stateDir, "commits");
40324
- this.artifactsDir = path40.join(this.stateDir, "artifacts");
40325
- this.headPath = path40.join(this.stateDir, "HEAD.json");
40326
- this.indexPath = path40.join(this.stateDir, "index.jsonl");
40567
+ this.stateDir = path41.join(projectRoot, ".zelari", "state");
40568
+ this.commitsDir = path41.join(this.stateDir, "commits");
40569
+ this.artifactsDir = path41.join(this.stateDir, "artifacts");
40570
+ this.headPath = path41.join(this.stateDir, "HEAD.json");
40571
+ this.indexPath = path41.join(this.stateDir, "index.jsonl");
40327
40572
  await fs21.mkdir(this.commitsDir, { recursive: true });
40328
40573
  await fs21.mkdir(this.artifactsDir, { recursive: true });
40329
40574
  }
@@ -40336,13 +40581,13 @@ var init_fileStateStore = __esm({
40336
40581
  const discoveries = input.discoveries ?? [];
40337
40582
  const parent = await this.head();
40338
40583
  const id = shortId();
40339
- const artifactRel = path40.join("artifacts", id);
40340
- const artifactAbs = path40.join(this.artifactsDir, id);
40584
+ const artifactRel = path41.join("artifacts", id);
40585
+ const artifactAbs = path41.join(this.artifactsDir, id);
40341
40586
  await fs21.mkdir(artifactAbs, { recursive: true });
40342
40587
  const summary = defaultSummary(input, discoveries);
40343
- await fs21.writeFile(path40.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
40344
- await writeJsonAtomic(path40.join(artifactAbs, "discoveries.json"), discoveries);
40345
- await writeJsonAtomic(path40.join(artifactAbs, "verification.json"), input.verification);
40588
+ await fs21.writeFile(path41.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
40589
+ await writeJsonAtomic(path41.join(artifactAbs, "discoveries.json"), discoveries);
40590
+ await writeJsonAtomic(path41.join(artifactAbs, "verification.json"), input.verification);
40346
40591
  const meta3 = {
40347
40592
  id,
40348
40593
  parentId: parent?.id ?? null,
@@ -40354,14 +40599,14 @@ var init_fileStateStore = __esm({
40354
40599
  workspaceCheckpointId: input.workspaceCheckpointId,
40355
40600
  verification: {
40356
40601
  ...input.verification,
40357
- reportPath: input.verification.reportPath ?? path40.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
40602
+ reportPath: input.verification.reportPath ?? path41.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
40358
40603
  },
40359
40604
  changedPaths: input.changedPaths ?? [],
40360
40605
  stablePromptHash: input.stablePromptHash,
40361
40606
  discoveryCount: discoveries.length,
40362
40607
  artifactDir: artifactRel.replace(/\\/g, "/")
40363
40608
  };
40364
- await writeJsonAtomic(path40.join(this.commitsDir, `${id}.json`), meta3);
40609
+ await writeJsonAtomic(path41.join(this.commitsDir, `${id}.json`), meta3);
40365
40610
  await writeJsonAtomic(this.headPath, { id, updatedAt: meta3.createdAt });
40366
40611
  await fs21.appendFile(this.indexPath, JSON.stringify({ id, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
40367
40612
  return stripStored(meta3);
@@ -40372,7 +40617,7 @@ var init_fileStateStore = __esm({
40372
40617
  return this.get(head.id);
40373
40618
  }
40374
40619
  async get(id) {
40375
- const stored = await readJsonFile(path40.join(this.commitsDir, `${id}.json`));
40620
+ const stored = await readJsonFile(path41.join(this.commitsDir, `${id}.json`));
40376
40621
  return stored ? stripStored(stored) : null;
40377
40622
  }
40378
40623
  async list(limit = 20) {
@@ -40411,9 +40656,9 @@ var init_fileStateStore = __esm({
40411
40656
  async loadDiscoveries(id) {
40412
40657
  const meta3 = id ? await this.get(id) : await this.head();
40413
40658
  if (!meta3) return [];
40414
- const stored = await readJsonFile(path40.join(this.commitsDir, `${meta3.id}.json`));
40659
+ const stored = await readJsonFile(path41.join(this.commitsDir, `${meta3.id}.json`));
40415
40660
  if (!stored?.artifactDir) return [];
40416
- const discPath = path40.join(this.stateDir, stored.artifactDir, "discoveries.json");
40661
+ const discPath = path41.join(this.stateDir, stored.artifactDir, "discoveries.json");
40417
40662
  return await readJsonFile(discPath) ?? [];
40418
40663
  }
40419
40664
  async materializeContext(id, maxChars = DEFAULT_MATERIALIZE_CHARS) {
@@ -42123,8 +42368,8 @@ function readPlan(ctx) {
42123
42368
  } catch {
42124
42369
  }
42125
42370
  }
42126
- const path63 = workspaceFile(ctx.rootDir, "plan");
42127
- const doc = ctx.storage.readIfExists(path63);
42371
+ const path64 = workspaceFile(ctx.rootDir, "plan");
42372
+ const doc = ctx.storage.readIfExists(path64);
42128
42373
  if (!doc) return { phases: [], tasks: [], milestones: [] };
42129
42374
  const meta3 = doc.meta;
42130
42375
  return {
@@ -42302,7 +42547,7 @@ function addMilestoneRecord(ctx, summary, input) {
42302
42547
  dueDate: input.dueDate,
42303
42548
  targetVersion: version2
42304
42549
  });
42305
- const path63 = join25(ctx.rootDir, "milestones", `${id}.md`);
42550
+ const path64 = join25(ctx.rootDir, "milestones", `${id}.md`);
42306
42551
  const meta3 = {
42307
42552
  kind: "milestone",
42308
42553
  id,
@@ -42319,7 +42564,7 @@ function addMilestoneRecord(ctx, summary, input) {
42319
42564
  `Target version: ${version2}`,
42320
42565
  ""
42321
42566
  ].join("\n");
42322
- ctx.storage.write(path63, meta3, body);
42567
+ ctx.storage.write(path64, meta3, body);
42323
42568
  return { id, created: true };
42324
42569
  }
42325
42570
  function readPlanSummary(ctx) {
@@ -42523,7 +42768,7 @@ function addIdeaStub(ctx) {
42523
42768
  const tags = args["tags"] ?? [];
42524
42769
  const category = args["category"] ?? "General";
42525
42770
  const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
42526
- const path63 = workspaceArtifact(ctx.rootDir, "decisions", id);
42771
+ const path64 = workspaceArtifact(ctx.rootDir, "decisions", id);
42527
42772
  const meta3 = {
42528
42773
  kind: "adr",
42529
42774
  status: "proposed",
@@ -42549,7 +42794,7 @@ function addIdeaStub(ctx) {
42549
42794
  ...consequences.map((c) => `- ${c}`),
42550
42795
  ""
42551
42796
  ].join("\n");
42552
- ctx.storage.write(path63, meta3, body);
42797
+ ctx.storage.write(path64, meta3, body);
42553
42798
  return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
42554
42799
  });
42555
42800
  }
@@ -42631,14 +42876,14 @@ function createDocumentStub(ctx) {
42631
42876
  ctx.storage.write(risksPath, riskMeta, content);
42632
42877
  return `Document "${title}" created at risks.md (workspace root).`;
42633
42878
  }
42634
- const path63 = workspaceArtifact(ctx.rootDir, "docs", slug);
42879
+ const path64 = workspaceArtifact(ctx.rootDir, "docs", slug);
42635
42880
  const meta3 = {
42636
42881
  kind: "doc",
42637
42882
  id: slug,
42638
42883
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
42639
42884
  tags
42640
42885
  };
42641
- ctx.storage.write(path63, meta3, content);
42886
+ ctx.storage.write(path64, meta3, content);
42642
42887
  return `Document "${title}" created at docs/${slug}.md.`;
42643
42888
  });
42644
42889
  }
@@ -42859,15 +43104,15 @@ __export(updater_exports, {
42859
43104
  import { createRequire as createRequire2 } from "node:module";
42860
43105
  import { spawn as spawn12 } from "node:child_process";
42861
43106
  import { existsSync as existsSync32 } from "node:fs";
42862
- import path41 from "node:path";
43107
+ import path42 from "node:path";
42863
43108
  import { fileURLToPath } from "node:url";
42864
43109
  function resolveBundledNpmCli(execPath = process.execPath) {
42865
- const dir = path41.dirname(execPath);
43110
+ const dir = path42.dirname(execPath);
42866
43111
  const candidates = [
42867
43112
  // Windows: C:\...\node.exe → C:\...\node_modules\npm\bin\npm-cli.js
42868
- path41.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
43113
+ path42.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
42869
43114
  // POSIX: <prefix>/bin/node → <prefix>/lib/node_modules/npm/bin/npm-cli.js
42870
- path41.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
43115
+ path42.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
42871
43116
  ];
42872
43117
  for (const candidate of candidates) {
42873
43118
  try {
@@ -42884,7 +43129,7 @@ function looksLikeBrokenShim(exitCode, output) {
42884
43129
  }
42885
43130
  function getCurrentVersion() {
42886
43131
  try {
42887
- const pkgPath = path41.resolve(__dirname2, "..", "..", "package.json");
43132
+ const pkgPath = path42.resolve(__dirname2, "..", "..", "package.json");
42888
43133
  const pkg = require2(pkgPath);
42889
43134
  return pkg.version;
42890
43135
  } catch {
@@ -43007,7 +43252,7 @@ var init_updater = __esm({
43007
43252
  "use strict";
43008
43253
  init_cmdline();
43009
43254
  require2 = createRequire2(import.meta.url);
43010
- __dirname2 = path41.dirname(fileURLToPath(import.meta.url));
43255
+ __dirname2 = path42.dirname(fileURLToPath(import.meta.url));
43011
43256
  REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
43012
43257
  }
43013
43258
  });
@@ -43195,10 +43440,10 @@ function getUserMcpPath() {
43195
43440
  function getProjectMcpPath(projectRoot) {
43196
43441
  return join26(projectRoot, ".zelari", "mcp.json");
43197
43442
  }
43198
- function readFile2(path63) {
43199
- if (!existsSync33(path63)) return {};
43443
+ function readFile3(path64) {
43444
+ if (!existsSync33(path64)) return {};
43200
43445
  try {
43201
- const parsed = JSON.parse(readFileSync28(path63, "utf8"));
43446
+ const parsed = JSON.parse(readFileSync28(path64, "utf8"));
43202
43447
  const out = {};
43203
43448
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
43204
43449
  if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
@@ -43214,17 +43459,17 @@ function readFile2(path63) {
43214
43459
  return {};
43215
43460
  }
43216
43461
  }
43217
- function writeFile(path63, servers) {
43218
- mkdirSync16(dirname7(path63), { recursive: true });
43462
+ function writeFile(path64, servers) {
43463
+ mkdirSync16(dirname7(path64), { recursive: true });
43219
43464
  const body = { mcpServers: servers };
43220
- writeFileSync18(path63, `${JSON.stringify(body, null, 2)}
43465
+ writeFileSync18(path64, `${JSON.stringify(body, null, 2)}
43221
43466
  `, "utf8");
43222
43467
  }
43223
43468
  function listMcpServers(projectRoot) {
43224
43469
  const userPath = getUserMcpPath();
43225
- const userServers = readFile2(userPath);
43470
+ const userServers = readFile3(userPath);
43226
43471
  const projectPath = projectRoot && projectRoot.trim() ? getProjectMcpPath(projectRoot.trim()) : null;
43227
- const projectServers = projectPath ? readFile2(projectPath) : {};
43472
+ const projectServers = projectPath ? readFile3(projectPath) : {};
43228
43473
  const servers = [];
43229
43474
  for (const [name, cfg] of Object.entries(userServers)) {
43230
43475
  servers.push({ name, ...cfg, scope: "user", path: userPath });
@@ -43250,9 +43495,9 @@ function upsertMcpServer(opts) {
43250
43495
  if (!opts.config.command?.trim()) {
43251
43496
  return { ok: false, error: "command is required" };
43252
43497
  }
43253
- let path63;
43498
+ let path64;
43254
43499
  if (opts.scope === "user") {
43255
- path63 = getUserMcpPath();
43500
+ path64 = getUserMcpPath();
43256
43501
  } else {
43257
43502
  const root = opts.projectRoot?.trim();
43258
43503
  if (!root) {
@@ -43261,30 +43506,30 @@ function upsertMcpServer(opts) {
43261
43506
  error: "projectRoot required for project scope (Open Folder first)"
43262
43507
  };
43263
43508
  }
43264
- path63 = getProjectMcpPath(root);
43509
+ path64 = getProjectMcpPath(root);
43265
43510
  }
43266
- const current = readFile2(path63);
43511
+ const current = readFile3(path64);
43267
43512
  current[name] = {
43268
43513
  command: opts.config.command.trim(),
43269
43514
  args: opts.config.args,
43270
43515
  env: opts.config.env,
43271
43516
  enabled: opts.config.enabled !== false
43272
43517
  };
43273
- writeFile(path63, current);
43274
- return { ok: true, path: path63 };
43518
+ writeFile(path64, current);
43519
+ return { ok: true, path: path64 };
43275
43520
  }
43276
43521
  function removeMcpServer(opts) {
43277
- const path63 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
43278
- if (!path63) {
43522
+ const path64 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
43523
+ if (!path64) {
43279
43524
  return { ok: false, error: "projectRoot required for project scope" };
43280
43525
  }
43281
- const current = readFile2(path63);
43526
+ const current = readFile3(path64);
43282
43527
  if (!(opts.name in current)) {
43283
- return { ok: false, error: `Server "${opts.name}" not found in ${path63}` };
43528
+ return { ok: false, error: `Server "${opts.name}" not found in ${path64}` };
43284
43529
  }
43285
43530
  delete current[opts.name];
43286
- writeFile(path63, current);
43287
- return { ok: true, path: path63 };
43531
+ writeFile(path64, current);
43532
+ return { ok: true, path: path64 };
43288
43533
  }
43289
43534
  var init_mcpConfigIo = __esm({
43290
43535
  "src/cli/mcp/mcpConfigIo.ts"() {
@@ -43683,12 +43928,12 @@ __export(agentsMd_exports, {
43683
43928
  import { existsSync as existsSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync19 } from "node:fs";
43684
43929
  import { createHash as createHash11 } from "node:crypto";
43685
43930
  import { join as join28 } from "node:path";
43686
- import { readFile as readFile3 } from "node:fs/promises";
43931
+ import { readFile as readFile4 } from "node:fs/promises";
43687
43932
  async function readPackageJson2(projectRoot) {
43688
- const path63 = join28(projectRoot, "package.json");
43689
- if (!existsSync35(path63)) return null;
43933
+ const path64 = join28(projectRoot, "package.json");
43934
+ if (!existsSync35(path64)) return null;
43690
43935
  try {
43691
- return JSON.parse(await readFile3(path63, "utf8"));
43936
+ return JSON.parse(await readFile4(path64, "utf8"));
43692
43937
  } catch {
43693
43938
  return null;
43694
43939
  }
@@ -43770,9 +44015,9 @@ async function genBuild(ctx) {
43770
44015
  ].join("\n");
43771
44016
  }
43772
44017
  async function genOpenQuestions(ctx) {
43773
- const path63 = join28(ctx.rootDir, "risks.md");
43774
- if (!existsSync35(path63)) return "_No open questions._";
43775
- const content = readFileSync30(path63, "utf8");
44018
+ const path64 = join28(ctx.rootDir, "risks.md");
44019
+ if (!existsSync35(path64)) return "_No open questions._";
44020
+ const content = readFileSync30(path64, "utf8");
43776
44021
  const lines = content.split("\n");
43777
44022
  const questions = [];
43778
44023
  let currentTitle = "";
@@ -44046,9 +44291,9 @@ function versionKey(value) {
44046
44291
  function firstString2(v) {
44047
44292
  return typeof v === "string" && v.trim().length > 0 ? v : null;
44048
44293
  }
44049
- function readFileSyncSafe(path63) {
44294
+ function readFileSyncSafe(path64) {
44050
44295
  try {
44051
- return readFileSync31(path63, "utf8");
44296
+ return readFileSync31(path64, "utf8");
44052
44297
  } catch {
44053
44298
  return null;
44054
44299
  }
@@ -44511,8 +44756,8 @@ async function runPostCouncilHook(ctx, options) {
44511
44756
  sources: scope.sources
44512
44757
  } : void 0
44513
44758
  });
44514
- const path63 = writeCouncilCompletion(ctx.rootDir, completion);
44515
- completionHook = { ran: true, path: path63, completion };
44759
+ const path64 = writeCouncilCompletion(ctx.rootDir, completion);
44760
+ completionHook = { ran: true, path: path64, completion };
44516
44761
  } catch (err) {
44517
44762
  completionHook = {
44518
44763
  ran: true,
@@ -44557,7 +44802,7 @@ import {
44557
44802
  writeFileSync as writeFileSync21,
44558
44803
  mkdirSync as mkdirSync17
44559
44804
  } from "node:fs";
44560
- import path42 from "node:path";
44805
+ import path43 from "node:path";
44561
44806
  import os10 from "node:os";
44562
44807
  var FeedbackStore;
44563
44808
  var init_councilFeedback = __esm({
@@ -44568,7 +44813,7 @@ var init_councilFeedback = __esm({
44568
44813
  now;
44569
44814
  entries = [];
44570
44815
  constructor(options = {}) {
44571
- this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path42.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
44816
+ this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path43.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
44572
44817
  this.now = options.now ?? Date.now;
44573
44818
  this.load();
44574
44819
  }
@@ -44674,7 +44919,7 @@ var init_councilFeedback = __esm({
44674
44919
  }
44675
44920
  }
44676
44921
  save() {
44677
- mkdirSync17(path42.dirname(this.file), { recursive: true });
44922
+ mkdirSync17(path43.dirname(this.file), { recursive: true });
44678
44923
  writeFileSync21(
44679
44924
  this.file,
44680
44925
  JSON.stringify({ entries: this.entries }, null, 2),
@@ -44742,7 +44987,7 @@ import { execFile as execFile3 } from "node:child_process";
44742
44987
  import { promisify as promisify2 } from "node:util";
44743
44988
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
44744
44989
  import { tmpdir as tmpdir2 } from "node:os";
44745
- import path43 from "node:path";
44990
+ import path44 from "node:path";
44746
44991
  import { randomUUID as randomUUID3 } from "node:crypto";
44747
44992
  async function git3(cwd, args, env) {
44748
44993
  const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
@@ -44762,8 +45007,8 @@ async function isGitRepo(cwd) {
44762
45007
  return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
44763
45008
  }
44764
45009
  async function withTempIndex(fn) {
44765
- const dir = mkdtempSync(path43.join(tmpdir2(), "zelari-ckpt-"));
44766
- const indexFile = path43.join(dir, "index");
45010
+ const dir = mkdtempSync(path44.join(tmpdir2(), "zelari-ckpt-"));
45011
+ const indexFile = path44.join(dir, "index");
44767
45012
  try {
44768
45013
  return await fn(indexFile);
44769
45014
  } finally {
@@ -44854,7 +45099,7 @@ async function restoreCheckpoint(cwd, id) {
44854
45099
  const deleted = [];
44855
45100
  for (const rel2 of added) {
44856
45101
  try {
44857
- rmSync2(path43.join(cwd, rel2), { force: true });
45102
+ rmSync2(path44.join(cwd, rel2), { force: true });
44858
45103
  deleted.push(rel2);
44859
45104
  } catch {
44860
45105
  }
@@ -44955,7 +45200,7 @@ __export(fileBackend_exports, {
44955
45200
  });
44956
45201
  import { randomUUID as randomUUID4 } from "node:crypto";
44957
45202
  import { promises as fs23 } from "node:fs";
44958
- import * as path44 from "node:path";
45203
+ import * as path45 from "node:path";
44959
45204
  function tokenize(text) {
44960
45205
  return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
44961
45206
  }
@@ -44999,8 +45244,8 @@ var init_fileBackend = __esm({
44999
45244
  logPath = "";
45000
45245
  memoryDir = "";
45001
45246
  async init(projectRoot) {
45002
- this.memoryDir = path44.join(projectRoot, ".zelari", "memory");
45003
- this.logPath = path44.join(this.memoryDir, "log.jsonl");
45247
+ this.memoryDir = path45.join(projectRoot, ".zelari", "memory");
45248
+ this.logPath = path45.join(this.memoryDir, "log.jsonl");
45004
45249
  await fs23.mkdir(this.memoryDir, { recursive: true });
45005
45250
  }
45006
45251
  async add(content, metadata = {}, graph) {
@@ -45073,12 +45318,12 @@ var init_fileBackend = __esm({
45073
45318
 
45074
45319
  // src/cli/traceStore.ts
45075
45320
  import { promises as fs24 } from "node:fs";
45076
- import * as path45 from "node:path";
45321
+ import * as path46 from "node:path";
45077
45322
  function traceDir(projectRoot) {
45078
- return path45.join(projectRoot, ".zelari", "trace");
45323
+ return path46.join(projectRoot, ".zelari", "trace");
45079
45324
  }
45080
45325
  function tracePath(projectRoot, missionId) {
45081
- return path45.join(traceDir(projectRoot), `${missionId}.json`);
45326
+ return path46.join(traceDir(projectRoot), `${missionId}.json`);
45082
45327
  }
45083
45328
  async function saveTrace(projectRoot, missionId, entries) {
45084
45329
  const dir = traceDir(projectRoot);
@@ -45113,7 +45358,7 @@ __export(zelariMission_exports, {
45113
45358
  });
45114
45359
  import { randomUUID as randomUUID5 } from "node:crypto";
45115
45360
  import { promises as fs25 } from "node:fs";
45116
- import * as path46 from "node:path";
45361
+ import * as path47 from "node:path";
45117
45362
  function resolveMaxIterations(env = process.env) {
45118
45363
  const raw = env.ZELARI_MISSION_MAX_ITER;
45119
45364
  const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
@@ -45141,10 +45386,10 @@ function isMissionAutoStart(env = process.env) {
45141
45386
  return env.ZELARI_MISSION_AUTO === "1";
45142
45387
  }
45143
45388
  async function writeMissionState(projectRoot, state3) {
45144
- const dir = path46.join(projectRoot, ".zelari");
45389
+ const dir = path47.join(projectRoot, ".zelari");
45145
45390
  await fs25.mkdir(dir, { recursive: true });
45146
45391
  await fs25.writeFile(
45147
- path46.join(dir, "mission-state.json"),
45392
+ path47.join(dir, "mission-state.json"),
45148
45393
  JSON.stringify(state3, null, 2) + "\n",
45149
45394
  "utf8"
45150
45395
  );
@@ -45376,6 +45621,17 @@ async function runZelariMission(userMessage, brief, deps) {
45376
45621
  deps.emit(`[zelari] state commit skipped: ${commitRes.error}`);
45377
45622
  }
45378
45623
  }
45624
+ const missionProgress = {
45625
+ criteriaTotal: 1,
45626
+ criteriaPassed: completionOk ? 1 : 0,
45627
+ ratio: completionOk ? 1 : 0,
45628
+ evidenceComplete: completionOk && (typeof result.writeCount === "number" ? result.writeCount > 0 : true)
45629
+ };
45630
+ const advice = evaluateMissionContinuation({
45631
+ progress: missionProgress,
45632
+ budget: { iterationsUsed: implStep, iterationsMax: maxIter }
45633
+ });
45634
+ deps.onMissionProgress?.(advice, step);
45379
45635
  if (completionOk) {
45380
45636
  state3.status = "success";
45381
45637
  deps.onMissionPhase?.("done", "mvp-green");
@@ -45422,6 +45678,7 @@ var DEFAULT_MAX_ITER, DEFAULT_MAX_STALL;
45422
45678
  var init_zelariMission = __esm({
45423
45679
  "src/cli/zelariMission.ts"() {
45424
45680
  "use strict";
45681
+ init_mission2();
45425
45682
  init_fileBackend();
45426
45683
  init_checkpointManager();
45427
45684
  init_fileStateStore();
@@ -45754,7 +46011,7 @@ function safeSocketPath(socketPath) {
45754
46011
  return socketPath.trim();
45755
46012
  }
45756
46013
  function startPermissionBroker(socketPath, handlers, opts) {
45757
- const path63 = safeSocketPath(socketPath);
46014
+ const path64 = safeSocketPath(socketPath);
45758
46015
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
45759
46016
  const sockets = /* @__PURE__ */ new Set();
45760
46017
  const server = createServer2((socket) => {
@@ -45854,10 +46111,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
45854
46111
  return new Promise((resolve3, reject) => {
45855
46112
  const onError = (err) => reject(err);
45856
46113
  server.once("error", onError);
45857
- server.listen(path63, () => {
46114
+ server.listen(path64, () => {
45858
46115
  server.removeListener("error", onError);
45859
46116
  resolve3({
45860
- socketPath: path63,
46117
+ socketPath: path64,
45861
46118
  stop: () => new Promise((res) => {
45862
46119
  for (const s of sockets) s.destroy();
45863
46120
  sockets.clear();
@@ -45868,7 +46125,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
45868
46125
  if (done) return;
45869
46126
  done = true;
45870
46127
  if (process.platform !== "win32") {
45871
- unlink(path63, () => res());
46128
+ unlink(path64, () => res());
45872
46129
  } else {
45873
46130
  res();
45874
46131
  }
@@ -45881,11 +46138,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
45881
46138
  });
45882
46139
  }
45883
46140
  function requestBrokerAsk(socketPath, ask, opts) {
45884
- const path63 = safeSocketPath(socketPath);
46141
+ const path64 = safeSocketPath(socketPath);
45885
46142
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
45886
46143
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
45887
46144
  return new Promise((resolve3, reject) => {
45888
- const socket = connect(path63);
46145
+ const socket = connect(path64);
45889
46146
  let buffer = "";
45890
46147
  let settled = false;
45891
46148
  const settle = (fn) => {
@@ -45900,7 +46157,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
45900
46157
  settle(
45901
46158
  () => reject(
45902
46159
  new Error(
45903
- `permission broker unavailable at "${path63}" (connect timed out after ${connectTimeoutMs}ms)`
46160
+ `permission broker unavailable at "${path64}" (connect timed out after ${connectTimeoutMs}ms)`
45904
46161
  )
45905
46162
  )
45906
46163
  );
@@ -46633,9 +46890,9 @@ __export(graphMemory_exports, {
46633
46890
  toGraphSnapshot: () => toGraphSnapshot
46634
46891
  });
46635
46892
  import { promises as fs26 } from "node:fs";
46636
- import path48 from "node:path";
46893
+ import path49 from "node:path";
46637
46894
  function snapshotPath(cwd) {
46638
- return path48.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
46895
+ return path49.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
46639
46896
  }
46640
46897
  function toGraphSnapshot(graph, opts) {
46641
46898
  const unresolved = (opts.unresolvedFindings ?? []).map((u) => ({
@@ -46662,7 +46919,7 @@ async function saveGraphSnapshot(cwd, snapshot) {
46662
46919
  try {
46663
46920
  await fs26.access(cwd);
46664
46921
  const file2 = snapshotPath(cwd);
46665
- await fs26.mkdir(path48.dirname(file2), { recursive: true });
46922
+ await fs26.mkdir(path49.dirname(file2), { recursive: true });
46666
46923
  await fs26.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
46667
46924
  } catch {
46668
46925
  }
@@ -46729,7 +46986,7 @@ var SNAPSHOT_DIR, SNAPSHOT_FILE, MAX_SNAPSHOT_FINDINGS_CHARS;
46729
46986
  var init_graphMemory = __esm({
46730
46987
  "src/cli/kraken/graphMemory.ts"() {
46731
46988
  "use strict";
46732
- SNAPSHOT_DIR = path48.join(".zelari", "kraken");
46989
+ SNAPSHOT_DIR = path49.join(".zelari", "kraken");
46733
46990
  SNAPSHOT_FILE = "last-graph.json";
46734
46991
  MAX_SNAPSHOT_FINDINGS_CHARS = 400;
46735
46992
  }
@@ -46745,14 +47002,14 @@ var init_tentacle = __esm({
46745
47002
 
46746
47003
  // src/cli/kraken/workbench.ts
46747
47004
  import { promises as fs27 } from "node:fs";
46748
- import path49 from "node:path";
47005
+ import path50 from "node:path";
46749
47006
  function isWorkbenchEnabled(env = process.env) {
46750
47007
  const v = (env.ZELARI_KRAKEN_WORKBENCH ?? "1").trim().toLowerCase();
46751
47008
  if (v === "0" || v === "false" || v === "no" || v === "off") return false;
46752
47009
  return true;
46753
47010
  }
46754
47011
  function workbenchPath(cwd, graphId) {
46755
- return path49.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
47012
+ return path50.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
46756
47013
  }
46757
47014
  function countByStatus2(nodes) {
46758
47015
  const out = { pending: 0, running: 0, done: 0, error: 0, skipped: 0 };
@@ -46915,7 +47172,7 @@ var init_workbench = __esm({
46915
47172
  if (!this.enabled) return null;
46916
47173
  if (!this.dirty && this.lastWrite) return this.lastWrite;
46917
47174
  const out = workbenchPath(this.cwd, this.graphId);
46918
- await fs27.mkdir(path49.dirname(out), { recursive: true });
47175
+ await fs27.mkdir(path50.dirname(out), { recursive: true });
46919
47176
  const body = this.render();
46920
47177
  const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
46921
47178
  await fs27.writeFile(tmp, body, "utf8");
@@ -47112,7 +47369,7 @@ __export(executor_exports, {
47112
47369
  thoroughnessForKind: () => thoroughnessForKind
47113
47370
  });
47114
47371
  import { existsSync as existsSync40 } from "node:fs";
47115
- import path50 from "node:path";
47372
+ import path51 from "node:path";
47116
47373
  function resolveMaxParallel(env = process.env) {
47117
47374
  const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
47118
47375
  if (raw === void 0 || raw === "") return DEFAULT_MAX_PARALLEL;
@@ -47170,7 +47427,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
47170
47427
  }
47171
47428
  function defaultChecksExists(cwd) {
47172
47429
  try {
47173
- return existsSync40(path50.join(cwd, ".zelari", "world", "checks.json"));
47430
+ return existsSync40(path51.join(cwd, ".zelari", "world", "checks.json"));
47174
47431
  } catch {
47175
47432
  return false;
47176
47433
  }
@@ -48509,10 +48766,10 @@ var init_prereqChecks = __esm({
48509
48766
 
48510
48767
  // src/cli/plugins/prefs.ts
48511
48768
  import { existsSync as existsSync42, readFileSync as readFileSync35, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
48512
- import path53 from "node:path";
48769
+ import path54 from "node:path";
48513
48770
  import os11 from "node:os";
48514
48771
  function getPluginPrefsPath() {
48515
- return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path53.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
48772
+ return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path54.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
48516
48773
  }
48517
48774
  function getPluginPrefs() {
48518
48775
  const file2 = getPluginPrefsPath();
@@ -48533,7 +48790,7 @@ function getPluginPrefs() {
48533
48790
  }
48534
48791
  function writePluginPrefs(prefs) {
48535
48792
  const file2 = getPluginPrefsPath();
48536
- mkdirSync18(path53.dirname(file2), { recursive: true });
48793
+ mkdirSync18(path54.dirname(file2), { recursive: true });
48537
48794
  writeFileSync22(file2, JSON.stringify(prefs, null, 2), {
48538
48795
  encoding: "utf-8",
48539
48796
  mode: 384
@@ -48570,7 +48827,7 @@ __export(registry_exports, {
48570
48827
  isBinaryOnPath: () => isBinaryOnPath
48571
48828
  });
48572
48829
  import { existsSync as existsSync43 } from "node:fs";
48573
- import path54 from "node:path";
48830
+ import path55 from "node:path";
48574
48831
  function detectLocalBin(bin) {
48575
48832
  return (cwd) => {
48576
48833
  try {
@@ -48588,7 +48845,7 @@ function isBinaryOnPath(bin, opts = {}) {
48588
48845
  const platform = opts.platform ?? process.platform;
48589
48846
  const exists = opts.exists ?? existsSync43;
48590
48847
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
48591
- const pathMod = platform === "win32" ? path54.win32 : path54.posix;
48848
+ const pathMod = platform === "win32" ? path55.win32 : path55.posix;
48592
48849
  const sep2 = platform === "win32" ? ";" : ":";
48593
48850
  const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
48594
48851
  const candidates = [bin];
@@ -49535,9 +49792,9 @@ __export(triggerLock_exports, {
49535
49792
  releaseLock: () => releaseLock
49536
49793
  });
49537
49794
  import { promises as fs35 } from "node:fs";
49538
- import * as path59 from "node:path";
49795
+ import * as path60 from "node:path";
49539
49796
  function lockPath(projectRoot) {
49540
- return path59.join(projectRoot, ".zelari", "trigger.lock");
49797
+ return path60.join(projectRoot, ".zelari", "trigger.lock");
49541
49798
  }
49542
49799
  function isPidAlive(pid) {
49543
49800
  try {
@@ -49550,7 +49807,7 @@ function isPidAlive(pid) {
49550
49807
  }
49551
49808
  async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
49552
49809
  const lp = lockPath(projectRoot);
49553
- const dir = path59.dirname(lp);
49810
+ const dir = path60.dirname(lp);
49554
49811
  await fs35.mkdir(dir, { recursive: true });
49555
49812
  try {
49556
49813
  const raw = await fs35.readFile(lp, "utf8");
@@ -50144,7 +50401,7 @@ function upsertSkill(opts) {
50144
50401
  }
50145
50402
  dir = getProjectSkillsDir(root);
50146
50403
  }
50147
- const path63 = skillFilePath(dir, name);
50404
+ const path64 = skillFilePath(dir, name);
50148
50405
  const content = serializeSkillMd({
50149
50406
  name,
50150
50407
  description,
@@ -50153,13 +50410,13 @@ function upsertSkill(opts) {
50153
50410
  tools: opts.tools,
50154
50411
  cost: opts.cost
50155
50412
  });
50156
- const parsed = parseSkillMd(content, path63);
50413
+ const parsed = parseSkillMd(content, path64);
50157
50414
  if (!parsed) {
50158
50415
  return { ok: false, error: "Generated SKILL.md failed validation" };
50159
50416
  }
50160
- mkdirSync21(dirname10(path63), { recursive: true });
50161
- writeFileSync24(path63, content, "utf8");
50162
- return { ok: true, path: path63 };
50417
+ mkdirSync21(dirname10(path64), { recursive: true });
50418
+ writeFileSync24(path64, content, "utf8");
50419
+ return { ok: true, path: path64 };
50163
50420
  }
50164
50421
  function removeSkill(opts) {
50165
50422
  const name = opts.name.trim().toLowerCase();
@@ -50177,8 +50434,8 @@ function removeSkill(opts) {
50177
50434
  dir = getProjectSkillsDir(root);
50178
50435
  }
50179
50436
  const skillDir = join37(dir, name);
50180
- const path63 = skillFilePath(dir, name);
50181
- if (!existsSync48(path63) && !existsSync48(skillDir)) {
50437
+ const path64 = skillFilePath(dir, name);
50438
+ if (!existsSync48(path64) && !existsSync48(skillDir)) {
50182
50439
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
50183
50440
  }
50184
50441
  try {
@@ -50189,7 +50446,7 @@ function removeSkill(opts) {
50189
50446
  error: err instanceof Error ? err.message : String(err)
50190
50447
  };
50191
50448
  }
50192
- return { ok: true, path: path63 };
50449
+ return { ok: true, path: path64 };
50193
50450
  }
50194
50451
  var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
50195
50452
  var init_skillConfigIo = __esm({
@@ -50507,12 +50764,12 @@ function ensureHome() {
50507
50764
  }
50508
50765
  }
50509
50766
  function loadCompanionConfig() {
50510
- const path63 = getCompanionConfigPath();
50511
- if (!existsSync49(path63)) {
50767
+ const path64 = getCompanionConfigPath();
50768
+ if (!existsSync49(path64)) {
50512
50769
  return { projects: [] };
50513
50770
  }
50514
50771
  try {
50515
- const raw = JSON.parse(readFileSync39(path63, "utf8"));
50772
+ const raw = JSON.parse(readFileSync39(path64, "utf8"));
50516
50773
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
50517
50774
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
50518
50775
  ).map((p3) => ({
@@ -50550,16 +50807,16 @@ function loadOrCreateToken(explicit) {
50550
50807
  return { token: explicit.trim(), created: false };
50551
50808
  }
50552
50809
  ensureHome();
50553
- const path63 = getCompanionTokenPath();
50554
- if (existsSync49(path63)) {
50555
- const t = readFileSync39(path63, "utf8").trim();
50810
+ const path64 = getCompanionTokenPath();
50811
+ if (existsSync49(path64)) {
50812
+ const t = readFileSync39(path64, "utf8").trim();
50556
50813
  if (t) return { token: t, created: false };
50557
50814
  }
50558
50815
  const token = randomBytes5(24).toString("base64url");
50559
- writeFileSync25(path63, token + "\n", "utf8");
50816
+ writeFileSync25(path64, token + "\n", "utf8");
50560
50817
  try {
50561
50818
  const fs37 = __require("node:fs");
50562
- fs37.chmodSync?.(path63, 384);
50819
+ fs37.chmodSync?.(path64, 384);
50563
50820
  } catch {
50564
50821
  }
50565
50822
  return { token, created: true };
@@ -50584,17 +50841,17 @@ function mergeProjects(cfg, extraPaths) {
50584
50841
  byId.set(p3.id, p3);
50585
50842
  }
50586
50843
  for (const raw of extraPaths) {
50587
- const path63 = raw.trim();
50588
- if (!path63) continue;
50589
- let id = slugFromPath(path63);
50844
+ const path64 = raw.trim();
50845
+ if (!path64) continue;
50846
+ let id = slugFromPath(path64);
50590
50847
  let n = 2;
50591
- while (byId.has(id) && byId.get(id).path !== path63) {
50592
- id = `${slugFromPath(path63)}-${n++}`;
50848
+ while (byId.has(id) && byId.get(id).path !== path64) {
50849
+ id = `${slugFromPath(path64)}-${n++}`;
50593
50850
  }
50594
50851
  byId.set(id, {
50595
50852
  id,
50596
- name: slugFromPath(path63),
50597
- path: path63
50853
+ name: slugFromPath(path64),
50854
+ path: path64
50598
50855
  });
50599
50856
  }
50600
50857
  return [...byId.values()];
@@ -50971,9 +51228,9 @@ async function runCompanionServe(opts = {}) {
50971
51228
  return;
50972
51229
  }
50973
51230
  const url2 = parseUrl(req);
50974
- const path63 = url2.pathname.replace(/\/+$/, "") || "/";
51231
+ const path64 = url2.pathname.replace(/\/+$/, "") || "/";
50975
51232
  try {
50976
- if (req.method === "GET" && (path63 === "/health" || path63 === "/v1/health")) {
51233
+ if (req.method === "GET" && (path64 === "/health" || path64 === "/v1/health")) {
50977
51234
  sendJson2(res, 200, {
50978
51235
  ok: true,
50979
51236
  service: "zelari-companion",
@@ -50985,18 +51242,18 @@ async function runCompanionServe(opts = {}) {
50985
51242
  });
50986
51243
  return;
50987
51244
  }
50988
- if (path63.startsWith("/v1")) {
51245
+ if (path64.startsWith("/v1")) {
50989
51246
  if (!tokenMatches(token, getBearer(req))) {
50990
51247
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
50991
51248
  return;
50992
51249
  }
50993
51250
  }
50994
- if (req.method === "GET" && path63 === "/v1/config") {
51251
+ if (req.method === "GET" && path64 === "/v1/config") {
50995
51252
  const snap = buildDesktopConfigSnapshot();
50996
51253
  sendJson2(res, 200, { ok: true, ...snap });
50997
51254
  return;
50998
51255
  }
50999
- if (req.method === "GET" && path63 === "/v1/projects") {
51256
+ if (req.method === "GET" && path64 === "/v1/projects") {
51000
51257
  sendJson2(res, 200, {
51001
51258
  ok: true,
51002
51259
  projects: projects.map((p3) => ({
@@ -51007,7 +51264,7 @@ async function runCompanionServe(opts = {}) {
51007
51264
  });
51008
51265
  return;
51009
51266
  }
51010
- if (req.method === "GET" && path63 === "/v1/runs") {
51267
+ if (req.method === "GET" && path64 === "/v1/runs") {
51011
51268
  sendJson2(res, 200, {
51012
51269
  ok: true,
51013
51270
  active: runs.getActive(),
@@ -51025,7 +51282,7 @@ async function runCompanionServe(opts = {}) {
51025
51282
  });
51026
51283
  return;
51027
51284
  }
51028
- if (req.method === "POST" && path63 === "/v1/runs") {
51285
+ if (req.method === "POST" && path64 === "/v1/runs") {
51029
51286
  const raw = await readBody(req);
51030
51287
  let body = {};
51031
51288
  try {
@@ -51072,7 +51329,7 @@ async function runCompanionServe(opts = {}) {
51072
51329
  });
51073
51330
  return;
51074
51331
  }
51075
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path63);
51332
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path64);
51076
51333
  if (req.method === "GET" && eventsMatch) {
51077
51334
  const runId = eventsMatch[1];
51078
51335
  const run = runs.getRun(runId);
@@ -51137,7 +51394,7 @@ async function runCompanionServe(opts = {}) {
51137
51394
  }, 500);
51138
51395
  return;
51139
51396
  }
51140
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path63);
51397
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path64);
51141
51398
  if (req.method === "POST" && cancelMatch) {
51142
51399
  const runId = cancelMatch[1];
51143
51400
  const result = runs.cancel(runId);
@@ -51290,11 +51547,11 @@ import { execSync as execSync2 } from "node:child_process";
51290
51547
  import { existsSync as existsSync51, readFileSync as readFileSync40, readlinkSync, statSync as statSync10 } from "node:fs";
51291
51548
  import { createRequire as createRequire3 } from "node:module";
51292
51549
  import { fileURLToPath as fileURLToPath2 } from "node:url";
51293
- import path61 from "node:path";
51550
+ import path62 from "node:path";
51294
51551
  function findPackageRoot(start) {
51295
51552
  let dir = start;
51296
51553
  for (let i = 0; i < 6; i += 1) {
51297
- const candidate = path61.join(dir, "package.json");
51554
+ const candidate = path62.join(dir, "package.json");
51298
51555
  if (existsSync51(candidate)) {
51299
51556
  try {
51300
51557
  const pkg = JSON.parse(readFileSync40(candidate, "utf8"));
@@ -51302,11 +51559,11 @@ function findPackageRoot(start) {
51302
51559
  } catch {
51303
51560
  }
51304
51561
  }
51305
- const parent = path61.dirname(dir);
51562
+ const parent = path62.dirname(dir);
51306
51563
  if (parent === dir) break;
51307
51564
  dir = parent;
51308
51565
  }
51309
- return path61.resolve(__dirname3, "..", "..", "..");
51566
+ return path62.resolve(__dirname3, "..", "..", "..");
51310
51567
  }
51311
51568
  function tryExec(cmd) {
51312
51569
  try {
@@ -51320,7 +51577,7 @@ function tryExec(cmd) {
51320
51577
  }
51321
51578
  function readPackageJson3() {
51322
51579
  try {
51323
- const pkgPath = path61.join(packageRoot, "package.json");
51580
+ const pkgPath = path62.join(packageRoot, "package.json");
51324
51581
  return JSON.parse(readFileSync40(pkgPath, "utf8"));
51325
51582
  } catch {
51326
51583
  return null;
@@ -51336,7 +51593,7 @@ function checkShim(pkgName) {
51336
51593
  }
51337
51594
  const isWin = process.platform === "win32";
51338
51595
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
51339
- const shimPath = path61.join(prefix, shimName);
51596
+ const shimPath = path62.join(prefix, shimName);
51340
51597
  if (!existsSync51(shimPath)) {
51341
51598
  return FAIL(
51342
51599
  `shim not found at ${shimPath}
@@ -51364,8 +51621,8 @@ function checkShim(pkgName) {
51364
51621
  fix: npm install -g ${pkgName}@latest --force`
51365
51622
  );
51366
51623
  }
51367
- const resolved = path61.resolve(path61.dirname(shimPath), target);
51368
- const expected = path61.join(
51624
+ const resolved = path62.resolve(path62.dirname(shimPath), target);
51625
+ const expected = path62.join(
51369
51626
  prefix,
51370
51627
  "node_modules",
51371
51628
  pkgName,
@@ -51404,7 +51661,7 @@ function checkNode(pkg) {
51404
51661
  return OK(`node ${raw}`);
51405
51662
  }
51406
51663
  function checkBundle() {
51407
- const bundle = path61.join(packageRoot, "dist", "cli", "main.bundled.js");
51664
+ const bundle = path62.join(packageRoot, "dist", "cli", "main.bundled.js");
51408
51665
  if (!existsSync51(bundle)) {
51409
51666
  return FAIL(
51410
51667
  `dist/cli/main.bundled.js missing at ${bundle}
@@ -51425,7 +51682,7 @@ function checkRuntimeDeps() {
51425
51682
  const missing = [];
51426
51683
  for (const dep of required2) {
51427
51684
  try {
51428
- const localReq = createRequire3(path61.join(packageRoot, "package.json"));
51685
+ const localReq = createRequire3(path62.join(packageRoot, "package.json"));
51429
51686
  localReq.resolve(dep);
51430
51687
  } catch {
51431
51688
  missing.push(dep);
@@ -51625,7 +51882,7 @@ var init_doctor = __esm({
51625
51882
  init_metrics2();
51626
51883
  init_contextGrowthSummary();
51627
51884
  require3 = createRequire3(import.meta.url);
51628
- __dirname3 = path61.dirname(fileURLToPath2(import.meta.url));
51885
+ __dirname3 = path62.dirname(fileURLToPath2(import.meta.url));
51629
51886
  packageRoot = findPackageRoot(__dirname3);
51630
51887
  OK = (message) => ({
51631
51888
  ok: true,
@@ -51809,15 +52066,15 @@ __export(inspect_exports, {
51809
52066
  collectInspectReport: () => collectInspectReport,
51810
52067
  runInspect: () => runInspect
51811
52068
  });
51812
- import path62 from "node:path";
52069
+ import path63 from "node:path";
51813
52070
  import { existsSync as existsSync52, readFileSync as readFileSync41, readdirSync as readdirSync11 } from "node:fs";
51814
52071
  import { homedir as homedir14 } from "node:os";
51815
52072
  async function collectInspectReport(cwd = process.cwd()) {
51816
52073
  ensureBuiltinSkillsLoadedSync();
51817
52074
  const snap = listSkillsSnapshot(cwd);
51818
52075
  const mcp = listMcpServers(cwd);
51819
- const userMcpPath = path62.join(homedir14(), ".zelari-code", "mcp.json");
51820
- const projectMcpPath = path62.join(cwd, ".zelari", "mcp.json");
52076
+ const userMcpPath = path63.join(homedir14(), ".zelari-code", "mcp.json");
52077
+ const projectMcpPath = path63.join(cwd, ".zelari", "mcp.json");
51821
52078
  const globalHooks = globalHooksDir();
51822
52079
  const projectHooks = projectHooksDir(cwd);
51823
52080
  const projectTrusted = isFolderTrusted(cwd);
@@ -51845,9 +52102,9 @@ async function collectInspectReport(cwd = process.cwd()) {
51845
52102
  configSources: [
51846
52103
  { path: userMcpPath, exists: existsSync52(userMcpPath) },
51847
52104
  { path: projectMcpPath, exists: existsSync52(projectMcpPath) },
51848
- { path: path62.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync52(path62.join(homedir14(), ".zelari-code", "provider.json")) },
51849
- { path: path62.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync52(path62.join(cwd, ".zelari", "AGENTS.md")) },
51850
- { path: path62.join(cwd, "AGENTS.md"), exists: existsSync52(path62.join(cwd, "AGENTS.md")) }
52105
+ { path: path63.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync52(path63.join(homedir14(), ".zelari-code", "provider.json")) },
52106
+ { path: path63.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync52(path63.join(cwd, ".zelari", "AGENTS.md")) },
52107
+ { path: path63.join(cwd, "AGENTS.md"), exists: existsSync52(path63.join(cwd, "AGENTS.md")) }
51851
52108
  ],
51852
52109
  skills: {
51853
52110
  total: snap.skills.length,
@@ -51887,8 +52144,8 @@ function listJsonFiles(dir) {
51887
52144
  }
51888
52145
  function findAgentsMd(cwd) {
51889
52146
  const candidates = [
51890
- path62.join(cwd, "AGENTS.md"),
51891
- path62.join(cwd, ".zelari", "AGENTS.md")
52147
+ path63.join(cwd, "AGENTS.md"),
52148
+ path63.join(cwd, ".zelari", "AGENTS.md")
51892
52149
  ];
51893
52150
  const found = [];
51894
52151
  for (const c of candidates) {
@@ -55163,8 +55420,76 @@ init_completionGate();
55163
55420
  // src/cli/kraken/verificationBridge.ts
55164
55421
  init_candidateRegistry();
55165
55422
  init_completionGate();
55423
+
55424
+ // src/cli/kraken/nativeVerification.ts
55425
+ init_runtime2();
55166
55426
  init_verification2();
55167
- function strictDoneEnabled() {
55427
+ import { readFile as readFile2 } from "node:fs/promises";
55428
+ import path40 from "node:path";
55429
+ function nativePackEnabled(env = process.env) {
55430
+ const v = env.ZELARI_VERIFY_PACK?.toLowerCase();
55431
+ return v === "1" || v === "on" || v === "true";
55432
+ }
55433
+ function resolvePackCommands(env, scripts) {
55434
+ const pick2 = (override, scriptName) => {
55435
+ if (override !== void 0) {
55436
+ const trimmed = override.trim();
55437
+ return trimmed === "" ? null : trimmed;
55438
+ }
55439
+ const script = scripts[scriptName];
55440
+ return typeof script === "string" && script.length > 0 ? `npm run ${scriptName}` : null;
55441
+ };
55442
+ return {
55443
+ typecheckCommand: pick2(env.ZELARI_VERIFY_TYPECHECK_CMD, "typecheck"),
55444
+ testCommand: pick2(env.ZELARI_VERIFY_TEST_CMD, "test"),
55445
+ buildCommand: pick2(env.ZELARI_VERIFY_BUILD_CMD, "build")
55446
+ };
55447
+ }
55448
+ function packTimeoutMs(env = process.env) {
55449
+ const raw = Number(env.ZELARI_VERIFY_TIMEOUT_MS);
55450
+ if (!Number.isFinite(raw) || raw <= 0) return 6e5;
55451
+ return Math.min(Math.floor(raw), 36e5);
55452
+ }
55453
+ async function readPackageScripts(cwd = process.cwd()) {
55454
+ try {
55455
+ const raw = await readFile2(path40.join(cwd, "package.json"), "utf-8");
55456
+ const parsed = JSON.parse(raw);
55457
+ if (parsed && typeof parsed === "object" && typeof parsed.scripts === "object") {
55458
+ return parsed.scripts;
55459
+ }
55460
+ return {};
55461
+ } catch {
55462
+ return {};
55463
+ }
55464
+ }
55465
+ function buildNativeCriteria(commands, timeoutMs) {
55466
+ const pack = codingCriteriaPack({ ...commands, commandTimeoutMs: timeoutMs });
55467
+ return pack.criteria.filter(
55468
+ (c) => !(c.required && (!c.check || c.check.kind === "none"))
55469
+ );
55470
+ }
55471
+ async function evaluateNativePack(deps = {}) {
55472
+ const env = deps.env ?? process.env;
55473
+ if (!nativePackEnabled(env)) return null;
55474
+ const cwd = deps.cwd ?? process.cwd();
55475
+ const commands = resolvePackCommands(env, await readPackageScripts(cwd));
55476
+ if (!commands.typecheckCommand && !commands.testCommand && !commands.buildCommand) return null;
55477
+ const criteria = buildNativeCriteria(commands, packTimeoutMs(env));
55478
+ if (criteria.length === 0) return null;
55479
+ const shell = deps.shell ?? new NodeShellProvider(new LocalWorkspace(cwd));
55480
+ const engine = deps.emit ? new VerificationEngine({ shell }, { emit: deps.emit }) : new VerificationEngine({ shell });
55481
+ const results = await engine.evaluate(criteria, { packId: ZELARI_CODING_PACK_ID });
55482
+ return { packId: ZELARI_CODING_PACK_ID, criteria, results };
55483
+ }
55484
+
55485
+ // src/cli/kraken/verificationBridge.ts
55486
+ init_verification2();
55487
+ function strictDoneEnabled(surface = "kraken") {
55488
+ if (surface === "mission") {
55489
+ const v2 = process.env.ZELARI_MISSION_STRICT;
55490
+ if (v2 === "0" || v2 === "false") return false;
55491
+ return true;
55492
+ }
55168
55493
  const v = process.env.ZELARI_STRICT_DONE;
55169
55494
  return v === "1" || v === "true";
55170
55495
  }
@@ -55220,27 +55545,62 @@ function krakenResultsToContract(requiredChecks, results, now = Date.now()) {
55220
55545
  });
55221
55546
  return { criteria, results: verifications };
55222
55547
  }
55223
- function evaluateStrictBuildGate(mode) {
55548
+ async function anchorSelectionEvidence(results, emit) {
55549
+ if (!emit) return;
55550
+ for (const r of results) {
55551
+ for (const ev of r.evidence) {
55552
+ if (ev.seq !== void 0) continue;
55553
+ if (ev.tier === "verifier-llm" || ev.tier === "human") continue;
55554
+ try {
55555
+ const appended = await emit({
55556
+ kind: "verification.evidence",
55557
+ actor: { type: "system", role: "verification" },
55558
+ data: {
55559
+ observation: "verify-report-note",
55560
+ criterionId: r.criterionId,
55561
+ ref: ev.ref,
55562
+ tier: ev.tier
55563
+ }
55564
+ });
55565
+ const seq = appended && typeof appended === "object" && "seq" in appended ? Number(appended.seq) : NaN;
55566
+ if (Number.isFinite(seq) && seq > 0) ev.seq = seq;
55567
+ } catch {
55568
+ }
55569
+ }
55570
+ }
55571
+ }
55572
+ async function evaluateStrictBuildGate(mode, options = {}) {
55224
55573
  const gate = evaluateKrakenCompletionGate(mode);
55225
- if (!gate.selectionUsed || gate.total === 0 || !strictDoneEnabled()) {
55574
+ if (!gate.selectionUsed || gate.total === 0 || !strictDoneEnabled(options.surface ?? "kraken")) {
55226
55575
  return {
55227
55576
  gate,
55228
55577
  strict: false,
55229
55578
  evaluation: null,
55579
+ native: null,
55230
55580
  blocked: gate.blocked,
55231
55581
  summary: gate.blocked ? `blocked: ${gate.failedChecks.length} failed, ${gate.unknownChecks.length} unknown` : "open"
55232
55582
  };
55233
55583
  }
55234
55584
  const checks = krakenRequiredChecks();
55235
55585
  const contract = krakenResultsToContract(checks, getKrakenCheckResults());
55236
- const evaluation = evaluateCompletion(contract.criteria, contract.results, STRICT_BUILD_POLICY);
55586
+ await anchorSelectionEvidence(contract.results, options.emit);
55587
+ const native = await evaluateNativePack({
55588
+ cwd: options.cwd,
55589
+ env: options.env,
55590
+ shell: options.shell,
55591
+ emit: options.emit
55592
+ }).catch(() => null);
55593
+ const allCriteria = [...contract.criteria, ...native?.criteria ?? []];
55594
+ const allResults = [...contract.results, ...native?.results ?? []];
55595
+ const evaluation = evaluateCompletion(allCriteria, allResults, STRICT_BUILD_POLICY);
55237
55596
  const blocked = gate.blocked || evaluation.verdict !== "PASS";
55238
55597
  return {
55239
55598
  gate,
55240
55599
  strict: true,
55241
55600
  evaluation,
55601
+ native,
55242
55602
  blocked,
55243
- summary: blocked ? `blocked (strict ${evaluation?.verdict ?? "n/a"}): ${gate.passed}/${gate.total} legacy-pass, evidence ${evaluation?.evidenceComplete ? "complete" : "incomplete"}` : `open (strict PASS): ${evaluation?.satisfied.length ?? 0}/${gate.total} criteria pass with evidence`
55603
+ summary: blocked ? `blocked (strict ${evaluation?.verdict ?? "n/a"}): ${gate.passed}/${gate.total} legacy-pass, evidence ${evaluation?.evidenceComplete ? "complete" : "incomplete"}` : `open (strict PASS): ${evaluation?.satisfied.length ?? 0}/${allCriteria.length} criteria pass with evidence`
55244
55604
  };
55245
55605
  }
55246
55606
  var STRICT_DONE_EXIT_CODE = 4;
@@ -55249,7 +55609,7 @@ function strictGateExitCode(evaluation) {
55249
55609
  }
55250
55610
  function strictGateEventPayload(evaluation) {
55251
55611
  return {
55252
- engine: "kraken-legacy+completion-policy",
55612
+ engine: evaluation.native ? "kraken-legacy+completion-policy+criteria-pack" : "kraken-legacy+completion-policy",
55253
55613
  strict: evaluation.strict,
55254
55614
  verdict: evaluation.evaluation?.verdict ?? (evaluation.blocked ? "BLOCKED" : "PASS"),
55255
55615
  legacy: {
@@ -55263,6 +55623,17 @@ function strictGateEventPayload(evaluation) {
55263
55623
  unsatisfied: evaluation.evaluation.unsatisfied,
55264
55624
  complete: evaluation.evaluation.evidenceComplete
55265
55625
  } : null,
55626
+ // F2: deterministic pack results — real command evidence, replayable.
55627
+ native: evaluation.native ? {
55628
+ packId: evaluation.native.packId,
55629
+ criteria: evaluation.native.criteria.map((c) => ({ id: c.id, required: c.required })),
55630
+ results: evaluation.native.results.map((r) => ({
55631
+ criterionId: r.criterionId,
55632
+ status: r.status,
55633
+ evidence: r.evidence.map((e) => ({ tier: e.tier, ref: e.ref, digest: e.digest, ...e.seq !== void 0 ? { seq: e.seq } : {} })),
55634
+ detail: r.detail
55635
+ }))
55636
+ } : null,
55266
55637
  summary: evaluation.summary
55267
55638
  };
55268
55639
  }
@@ -56017,8 +56388,9 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
56017
56388
  progressRuntime.observe(event);
56018
56389
  if (event.type === "agent_end") {
56019
56390
  let krakenSuppressFinish = false;
56391
+ const krakenSpineEmit = (input) => writerRef.current?.spine?.appendEvent(input) ?? Promise.resolve(null);
56020
56392
  if (event.reason === "completed" && !krakenRepairEnqueued && isKrakenSelectionEnabled() && workPhase === "build") {
56021
- const strictGate = evaluateStrictBuildGate("build");
56393
+ const strictGate = await evaluateStrictBuildGate("build", { emit: krakenSpineEmit });
56022
56394
  const krakenGate = strictGate.gate;
56023
56395
  writerRef.current?.spine?.verificationRun(strictGateEventPayload(strictGate));
56024
56396
  if (krakenGate.blocked) {
@@ -56034,10 +56406,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
56034
56406
  krakenSuppressFinish = true;
56035
56407
  }
56036
56408
  }
56037
- if (event.reason === "completed" && krakenRepairEnqueued && !evaluateStrictBuildGate("build").blocked) {
56409
+ if (event.reason === "completed" && krakenRepairEnqueued && !(await evaluateStrictBuildGate("build", { emit: krakenSpineEmit })).blocked) {
56038
56410
  markRepairSucceeded();
56039
56411
  } else if (event.reason === "completed" && krakenRepairEnqueued) {
56040
- const still = evaluateStrictBuildGate("build");
56412
+ const still = await evaluateStrictBuildGate("build", { emit: krakenSpineEmit });
56041
56413
  if (still.blocked) {
56042
56414
  appendSystem(
56043
56415
  setMessages,
@@ -58064,7 +58436,7 @@ init_sessionManager();
58064
58436
  // src/cli/gitOps.ts
58065
58437
  import { execFile as execFile4 } from "node:child_process";
58066
58438
  import { promisify as promisify3 } from "node:util";
58067
- import path47 from "node:path";
58439
+ import path48 from "node:path";
58068
58440
  var execFileAsync3 = promisify3(execFile4);
58069
58441
  async function git4(cwd, args) {
58070
58442
  try {
@@ -58110,7 +58482,7 @@ async function undoWorkingChanges(opts = {}) {
58110
58482
  };
58111
58483
  }
58112
58484
  function defaultProjectRoot() {
58113
- return path47.resolve(__dirname, "..", "..", "..");
58485
+ return path48.resolve(__dirname, "..", "..", "..");
58114
58486
  }
58115
58487
 
58116
58488
  // src/cli/slashHandlers/git.ts
@@ -58562,7 +58934,7 @@ import { promises as fs29 } from "node:fs";
58562
58934
  init_zod();
58563
58935
  init_taskTool();
58564
58936
  import { promises as fs28 } from "node:fs";
58565
- import path51 from "node:path";
58937
+ import path52 from "node:path";
58566
58938
  import { randomBytes as randomBytes4 } from "node:crypto";
58567
58939
  var CsvFanoutArgsSchema = external_exports.object({
58568
58940
  csv_path: external_exports.string().min(1),
@@ -58656,8 +59028,8 @@ function resolveMaxConcurrency(env = process.env) {
58656
59028
  }
58657
59029
  async function runCsvFanout(args, deps, opts) {
58658
59030
  const start = Date.now();
58659
- const absCsv = path51.isAbsolute(args.csv_path) ? args.csv_path : path51.join(opts.parentCwd, args.csv_path);
58660
- const absOut = path51.isAbsolute(args.output_csv_path) ? args.output_csv_path : path51.join(opts.parentCwd, args.output_csv_path);
59031
+ const absCsv = path52.isAbsolute(args.csv_path) ? args.csv_path : path52.join(opts.parentCwd, args.csv_path);
59032
+ const absOut = path52.isAbsolute(args.output_csv_path) ? args.output_csv_path : path52.join(opts.parentCwd, args.output_csv_path);
58661
59033
  const { headers: headers2, rows } = await readCsv(absCsv);
58662
59034
  if (headers2.length === 0) {
58663
59035
  throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
@@ -58713,7 +59085,7 @@ async function runCsvFanout(args, deps, opts) {
58713
59085
  errored += 1;
58714
59086
  errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
58715
59087
  }
58716
- await fs28.mkdir(path51.dirname(absOut), { recursive: true });
59088
+ await fs28.mkdir(path52.dirname(absOut), { recursive: true });
58717
59089
  await queueWrite(serializeCsv(outHeaders, outputRecords));
58718
59090
  }
58719
59091
  }
@@ -58908,7 +59280,7 @@ function splitArgs(s) {
58908
59280
  // src/cli/slashHandlers/krakenWorkbench.ts
58909
59281
  init_messageHelpers();
58910
59282
  import { promises as fs30 } from "node:fs";
58911
- import path52 from "node:path";
59283
+ import path53 from "node:path";
58912
59284
 
58913
59285
  // src/cli/kraken/workbenchView.ts
58914
59286
  var EMPTY = {
@@ -59025,14 +59397,14 @@ function formatWorkbenchForTerminal(p3) {
59025
59397
 
59026
59398
  // src/cli/slashHandlers/krakenWorkbench.ts
59027
59399
  async function handleKrakenWorkbench(ctx) {
59028
- const dir = path52.join(ctx.cwd, ".zelari", "radio");
59400
+ const dir = path53.join(ctx.cwd, ".zelari", "radio");
59029
59401
  let latest = null;
59030
59402
  let latestMtime = 0;
59031
59403
  try {
59032
59404
  const files = await fs30.readdir(dir);
59033
59405
  for (const f of files) {
59034
59406
  if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
59035
- const full = path52.join(dir, f);
59407
+ const full = path53.join(dir, f);
59036
59408
  const stat = await fs30.stat(full);
59037
59409
  if (stat.mtimeMs > latestMtime) {
59038
59410
  latestMtime = stat.mtimeMs;
@@ -59049,10 +59421,10 @@ async function handleKrakenWorkbench(ctx) {
59049
59421
  const parsed = parseWorkbench(content);
59050
59422
  const rendered = formatWorkbenchForTerminal(parsed);
59051
59423
  if (!rendered.trim()) {
59052
- appendSystem(ctx.setMessages, `[kraken workbench] ${path52.basename(latest)}: (no nodes / no events yet)`);
59424
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path53.basename(latest)}: (no nodes / no events yet)`);
59053
59425
  return;
59054
59426
  }
59055
- appendSystem(ctx.setMessages, `[kraken workbench] ${path52.basename(latest)}:
59427
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path53.basename(latest)}:
59056
59428
  ${rendered}`);
59057
59429
  }
59058
59430
 
@@ -59359,15 +59731,15 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
59359
59731
  // src/cli/slashHandlers/promoteMember.ts
59360
59732
  init_messageHelpers();
59361
59733
  import { promises as fs31 } from "node:fs";
59362
- import path55 from "node:path";
59734
+ import path56 from "node:path";
59363
59735
  import os12 from "node:os";
59364
59736
  async function handlePromoteMember(ctx, memberId) {
59365
59737
  try {
59366
59738
  const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
59367
59739
  const { skill, markdown } = promoteMember2(memberId);
59368
- const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path55.join(os12.homedir(), ".tmp", "zelari-code", "skills");
59740
+ const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path56.join(os12.homedir(), ".tmp", "zelari-code", "skills");
59369
59741
  await fs31.mkdir(skillDir, { recursive: true });
59370
- const filePath = path55.join(skillDir, `${skill.id}.md`);
59742
+ const filePath = path56.join(skillDir, `${skill.id}.md`);
59371
59743
  await fs31.writeFile(filePath, markdown, "utf8");
59372
59744
  appendSystem(
59373
59745
  ctx.setMessages,
@@ -59385,24 +59757,24 @@ async function handlePromoteMember(ctx, memberId) {
59385
59757
 
59386
59758
  // src/cli/branchManager.ts
59387
59759
  import { promises as fs32, existsSync as existsSync44, readFileSync as readFileSync36, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
59388
- import path56 from "node:path";
59760
+ import path57 from "node:path";
59389
59761
  import os13 from "node:os";
59390
59762
  var META_FILENAME = "meta.json";
59391
59763
  var SESSIONS_SUBDIR = "sessions";
59392
59764
  function getBranchesBaseDir() {
59393
- return process.env.ANATHEMA_BRANCHES_DIR ?? path56.join(os13.homedir(), ".tmp", "zelari-code", "branches");
59765
+ return process.env.ANATHEMA_BRANCHES_DIR ?? path57.join(os13.homedir(), ".tmp", "zelari-code", "branches");
59394
59766
  }
59395
59767
  function getSessionsBaseDir() {
59396
- return process.env.ANATHEMA_SESSIONS_DIR ?? path56.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
59768
+ return process.env.ANATHEMA_SESSIONS_DIR ?? path57.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
59397
59769
  }
59398
59770
  function branchPathFor(name, baseDir) {
59399
- return path56.join(baseDir, name);
59771
+ return path57.join(baseDir, name);
59400
59772
  }
59401
59773
  function metaPathFor(name, baseDir) {
59402
- return path56.join(baseDir, name, META_FILENAME);
59774
+ return path57.join(baseDir, name, META_FILENAME);
59403
59775
  }
59404
59776
  function sessionsPathFor(name, baseDir) {
59405
- return path56.join(baseDir, name, SESSIONS_SUBDIR);
59777
+ return path57.join(baseDir, name, SESSIONS_SUBDIR);
59406
59778
  }
59407
59779
  function readBranchMeta(name, baseDir) {
59408
59780
  const metaPath = metaPathFor(name, baseDir);
@@ -59427,7 +59799,7 @@ function readBranchMeta(name, baseDir) {
59427
59799
  }
59428
59800
  function writeBranchMeta(name, baseDir, meta3) {
59429
59801
  const metaPath = metaPathFor(name, baseDir);
59430
- mkdirSync19(path56.dirname(metaPath), { recursive: true });
59802
+ mkdirSync19(path57.dirname(metaPath), { recursive: true });
59431
59803
  writeFileSync23(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
59432
59804
  }
59433
59805
  async function countSessions(name, baseDir) {
@@ -59478,14 +59850,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
59478
59850
  if (branchExists(name, baseDir)) {
59479
59851
  throw new BranchAlreadyExistsError(name);
59480
59852
  }
59481
- const sourcePath = path56.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
59853
+ const sourcePath = path57.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
59482
59854
  if (!existsSync44(sourcePath)) {
59483
59855
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
59484
59856
  }
59485
59857
  const branchPath = branchPathFor(name, baseDir);
59486
59858
  const branchSessionsPath = sessionsPathFor(name, baseDir);
59487
59859
  mkdirSync19(branchSessionsPath, { recursive: true });
59488
- const destPath = path56.join(branchSessionsPath, `${fromSessionId}.jsonl`);
59860
+ const destPath = path57.join(branchSessionsPath, `${fromSessionId}.jsonl`);
59489
59861
  await fs32.copyFile(sourcePath, destPath);
59490
59862
  const meta3 = {
59491
59863
  name,
@@ -59589,14 +59961,14 @@ async function handleBranchCheckout(ctx, branchName) {
59589
59961
  // src/cli/slashHandlers/workspace.ts
59590
59962
  init_messageHelpers();
59591
59963
  import { promises as fs33 } from "node:fs";
59592
- import path57 from "node:path";
59964
+ import path58 from "node:path";
59593
59965
  async function handleWorkspaceShow(ctx, what) {
59594
59966
  try {
59595
- const zelari = path57.join(process.cwd(), ".zelari");
59967
+ const zelari = path58.join(process.cwd(), ".zelari");
59596
59968
  let content;
59597
59969
  switch (what) {
59598
59970
  case "plan": {
59599
- const planPath = path57.join(zelari, "plan.md");
59971
+ const planPath = path58.join(zelari, "plan.md");
59600
59972
  try {
59601
59973
  content = await fs33.readFile(planPath, "utf-8");
59602
59974
  } catch {
@@ -59605,7 +59977,7 @@ async function handleWorkspaceShow(ctx, what) {
59605
59977
  break;
59606
59978
  }
59607
59979
  case "decisions": {
59608
- const decisionsDir = path57.join(zelari, "decisions");
59980
+ const decisionsDir = path58.join(zelari, "decisions");
59609
59981
  try {
59610
59982
  const files = (await fs33.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
59611
59983
  if (files.length === 0) {
@@ -59615,7 +59987,7 @@ async function handleWorkspaceShow(ctx, what) {
59615
59987
  `];
59616
59988
  const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
59617
59989
  for (const f of files) {
59618
- const raw = await fs33.readFile(path57.join(decisionsDir, f), "utf-8");
59990
+ const raw = await fs33.readFile(path58.join(decisionsDir, f), "utf-8");
59619
59991
  const { meta: meta3, body } = parseFrontmatter2(raw);
59620
59992
  const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
59621
59993
  lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
@@ -59628,7 +60000,7 @@ async function handleWorkspaceShow(ctx, what) {
59628
60000
  break;
59629
60001
  }
59630
60002
  case "risks": {
59631
- const risksPath = path57.join(zelari, "risks.md");
60003
+ const risksPath = path58.join(zelari, "risks.md");
59632
60004
  try {
59633
60005
  content = await fs33.readFile(risksPath, "utf-8");
59634
60006
  } catch {
@@ -59637,7 +60009,7 @@ async function handleWorkspaceShow(ctx, what) {
59637
60009
  break;
59638
60010
  }
59639
60011
  case "agents": {
59640
- const agentsPath = path57.join(process.cwd(), "AGENTS.MD");
60012
+ const agentsPath = path58.join(process.cwd(), "AGENTS.MD");
59641
60013
  try {
59642
60014
  content = await fs33.readFile(agentsPath, "utf-8");
59643
60015
  } catch {
@@ -59646,7 +60018,7 @@ async function handleWorkspaceShow(ctx, what) {
59646
60018
  break;
59647
60019
  }
59648
60020
  case "docs": {
59649
- const docsDir = path57.join(zelari, "docs");
60021
+ const docsDir = path58.join(zelari, "docs");
59650
60022
  try {
59651
60023
  const files = (await fs33.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
59652
60024
  content = files.length ? `# Docs (${files.length})
@@ -59688,7 +60060,7 @@ async function handleWorkspaceReset(ctx, force) {
59688
60060
  return;
59689
60061
  }
59690
60062
  try {
59691
- const target = path57.join(process.cwd(), ".zelari");
60063
+ const target = path58.join(process.cwd(), ".zelari");
59692
60064
  await fs33.rm(target, { recursive: true, force: true });
59693
60065
  appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
59694
60066
  } catch (err) {
@@ -59700,7 +60072,7 @@ async function handleWorkspaceReset(ctx, force) {
59700
60072
  init_provider2();
59701
60073
 
59702
60074
  // src/cli/slashHandlers/skills.ts
59703
- import path58 from "node:path";
60075
+ import path59 from "node:path";
59704
60076
  import os14 from "node:os";
59705
60077
 
59706
60078
  // src/cli/skillHistory.ts
@@ -59829,7 +60201,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
59829
60201
  });
59830
60202
  }
59831
60203
  async function handleSkillStats(ctx, skillId) {
59832
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path58.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
60204
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path59.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
59833
60205
  try {
59834
60206
  const records = await readSkillHistory(historyFile);
59835
60207
  const stats = getSkillStats(records, skillId);
@@ -59845,7 +60217,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
59845
60217
  appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
59846
60218
  return;
59847
60219
  }
59848
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path58.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
60220
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path59.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
59849
60221
  try {
59850
60222
  const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
59851
60223
  appendSystem(ctx.setMessages, formatted);
@@ -61389,7 +61761,7 @@ function createStreamScrubber2() {
61389
61761
  init_taskTool();
61390
61762
  init_sessionTodos();
61391
61763
  import { promises as fs36 } from "node:fs";
61392
- import path60 from "node:path";
61764
+ import path61 from "node:path";
61393
61765
  import { randomUUID as randomUUID6 } from "node:crypto";
61394
61766
  init_headlessSpine();
61395
61767
  async function runHeadless(opts) {
@@ -61546,7 +61918,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
61546
61918
  try {
61547
61919
  let preflightGraph;
61548
61920
  if (opts.runPlan && opts.runPlan.trim() !== "") {
61549
- const planPath = path60.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
61921
+ const planPath = path61.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
61550
61922
  log(`loading pre-flight plan: ${planPath}`);
61551
61923
  let raw;
61552
61924
  try {
@@ -61586,8 +61958,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
61586
61958
  log(formatKrakenGraphAscii2(graph));
61587
61959
  if (opts.planOnly) {
61588
61960
  const planId = randomUUID6();
61589
- const planDir = path60.join(cwd, ".zelari", "radio");
61590
- const planPath = path60.join(planDir, `plan-${planId}.json`);
61961
+ const planDir = path61.join(cwd, ".zelari", "radio");
61962
+ const planPath = path61.join(planDir, `plan-${planId}.json`);
61591
61963
  await fs36.mkdir(planDir, { recursive: true });
61592
61964
  await fs36.writeFile(
61593
61965
  planPath,
@@ -62035,7 +62407,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
62035
62407
  }
62036
62408
  let strictExit = 0;
62037
62409
  if (pass.finalReason === "completed" && pass.exitCode === 0 && opts.mode === "kraken" && isKrakenSelectionEnabled() && !planModeFromOpts(opts)) {
62038
- const strictGate = evaluateStrictBuildGate("build");
62410
+ const strictGate = await evaluateStrictBuildGate("build", { emit: (input) => spine.appendEvent(input) });
62039
62411
  const gate = strictGate.gate;
62040
62412
  const verificationPayload = strictGateEventPayload(strictGate);
62041
62413
  spine.verificationRun(verificationPayload);
@@ -62068,7 +62440,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
62068
62440
  successfulWrites: pass.successfulWrites + repair.successfulWrites,
62069
62441
  emittedWrites: pass.emittedWrites + repair.emittedWrites
62070
62442
  };
62071
- const after = evaluateStrictBuildGate("build");
62443
+ const after = await evaluateStrictBuildGate("build", { emit: (input) => spine.appendEvent(input) });
62072
62444
  const afterPayload = strictGateEventPayload(after);
62073
62445
  spine.verificationRun(afterPayload);
62074
62446
  if (opts.output === "json") {
@@ -62140,7 +62512,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
62140
62512
  if (json2) {
62141
62513
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
62142
62514
  else {
62143
- await fs36.mkdir(path60.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62515
+ await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62144
62516
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
62145
62517
  }
62146
62518
  }
@@ -62307,7 +62679,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
62307
62679
  if (json2) {
62308
62680
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
62309
62681
  else {
62310
- await fs36.mkdir(path60.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62682
+ await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62311
62683
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
62312
62684
  }
62313
62685
  }
@@ -62393,6 +62765,13 @@ ${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMv
62393
62765
  emit,
62394
62766
  buildViaAgent,
62395
62767
  onMissionPhase: (phase2, note) => spine.missionPhase(phase2, note),
62768
+ onMissionProgress: (advice, iteration) => spine.missionProgress({
62769
+ recommendation: advice.recommendation,
62770
+ rationale: advice.rationale,
62771
+ blockers: advice.blockers,
62772
+ ...advice.trend ? { trend: advice.trend } : {},
62773
+ iteration
62774
+ }),
62396
62775
  runSlice: async ({
62397
62776
  userMessage: slicePrompt,
62398
62777
  runMode,
@@ -62597,8 +62976,22 @@ ${ragContext}` : slicePrompt;
62597
62976
  });
62598
62977
  if (state3.status === "error") exitCode = exitCode || 3;
62599
62978
  else if (state3.status === "success") {
62600
- exitCode = 0;
62601
- spine.missionPhase("done", "mission-success");
62979
+ const missionGate = await evaluateStrictBuildGate("build", {
62980
+ emit: (input) => spine.appendEvent(input),
62981
+ surface: "mission"
62982
+ });
62983
+ const missionVerificationPayload = strictGateEventPayload(missionGate);
62984
+ spine.verificationRun(missionVerificationPayload);
62985
+ if (opts.output === "json") {
62986
+ emitEvent({ type: "verification_run", ...missionVerificationPayload });
62987
+ }
62988
+ if (missionGate.blocked) {
62989
+ exitCode = strictGateExitCode(missionGate);
62990
+ spine.missionPhase("verification", "mission-strict-blocked");
62991
+ } else {
62992
+ exitCode = 0;
62993
+ spine.missionPhase("done", "mission-success");
62994
+ }
62602
62995
  } else if (state3.status === "stalled" || state3.status === "stopped") {
62603
62996
  exitCode = exitCode || 0;
62604
62997
  spine.missionPhase("verification", `mission-${state3.status}`);
@@ -62626,7 +63019,7 @@ ${ragContext}` : slicePrompt;
62626
63019
  if (json2) {
62627
63020
  if (opts.exportSessionPath === "-") process.stdout.write(json2 + "\n");
62628
63021
  else {
62629
- await fs36.mkdir(path60.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
63022
+ await fs36.mkdir(path61.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
62630
63023
  await fs36.writeFile(opts.exportSessionPath, json2, "utf8");
62631
63024
  }
62632
63025
  }
@@ -62840,8 +63233,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
62840
63233
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
62841
63234
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
62842
63235
  try {
62843
- const path63 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
62844
- name = path63 && /^[a-z0-9]/.test(path63) ? path63 : "imported-skill";
63236
+ const path64 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
63237
+ name = path64 && /^[a-z0-9]/.test(path64) ? path64 : "imported-skill";
62845
63238
  } catch {
62846
63239
  name = "imported-skill";
62847
63240
  }