zelari-code 2.11.1 → 2.12.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.
@@ -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, path72) {
3292
- if (!path72)
3291
+ function getElementAtPath(obj, path74) {
3292
+ if (!path74)
3293
3293
  return obj;
3294
- return path72.reduce((acc, key) => acc?.[key], obj);
3294
+ return path74.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(path72, issues) {
3622
+ function prefixIssues(path74, issues) {
3623
3623
  return issues.map((iss) => {
3624
3624
  var _a3;
3625
3625
  (_a3 = iss).path ?? (_a3.path = []);
3626
- iss.path.unshift(path72);
3626
+ iss.path.unshift(path74);
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, path72 = []) => {
3844
+ const processError = (error52, path74 = []) => {
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 }, [...path72, ...issue2.path]));
3847
+ issue2.errors.map((issues) => processError({ issues }, [...path74, ...issue2.path]));
3848
3848
  } else if (issue2.code === "invalid_key") {
3849
- processError({ issues: issue2.issues }, [...path72, ...issue2.path]);
3849
+ processError({ issues: issue2.issues }, [...path74, ...issue2.path]);
3850
3850
  } else if (issue2.code === "invalid_element") {
3851
- processError({ issues: issue2.issues }, [...path72, ...issue2.path]);
3851
+ processError({ issues: issue2.issues }, [...path74, ...issue2.path]);
3852
3852
  } else {
3853
- const fullpath = [...path72, ...issue2.path];
3853
+ const fullpath = [...path74, ...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, path72 = []) => {
3880
+ const processError = (error52, path74 = []) => {
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 }, [...path72, ...issue2.path]));
3884
+ issue2.errors.map((issues) => processError({ issues }, [...path74, ...issue2.path]));
3885
3885
  } else if (issue2.code === "invalid_key") {
3886
- processError({ issues: issue2.issues }, [...path72, ...issue2.path]);
3886
+ processError({ issues: issue2.issues }, [...path74, ...issue2.path]);
3887
3887
  } else if (issue2.code === "invalid_element") {
3888
- processError({ issues: issue2.issues }, [...path72, ...issue2.path]);
3888
+ processError({ issues: issue2.issues }, [...path74, ...issue2.path]);
3889
3889
  } else {
3890
- const fullpath = [...path72, ...issue2.path];
3890
+ const fullpath = [...path74, ...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 path72 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
- for (const seg of path72) {
3922
+ const path74 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
+ for (const seg of path74) {
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 path72 = ref.slice(1).split("/").filter(Boolean);
17427
- if (path72.length === 0) {
17426
+ const path74 = ref.slice(1).split("/").filter(Boolean);
17427
+ if (path74.length === 0) {
17428
17428
  return ctx.rootSchema;
17429
17429
  }
17430
17430
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17431
- if (path72[0] === defsKey) {
17432
- const key = path72[1];
17431
+ if (path74[0] === defsKey) {
17432
+ const key = path74[1];
17433
17433
  if (!key || !ctx.defs[key]) {
17434
17434
  throw new Error(`Reference not found: ${ref}`);
17435
17435
  }
@@ -19765,11 +19765,11 @@ var init_tools = __esm({
19765
19765
  if (!ctx.addDocument)
19766
19766
  return "Knowledge vault tool not available.";
19767
19767
  const title = args["title"] || "New Document";
19768
- const path72 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19768
+ const path74 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19769
19769
  const content = args["content"] || "";
19770
19770
  const tags = args["tags"] || [];
19771
19771
  ctx.addDocument({
19772
- path: path72,
19772
+ path: path74,
19773
19773
  title,
19774
19774
  content,
19775
19775
  format: "markdown",
@@ -19778,7 +19778,7 @@ var init_tools = __esm({
19778
19778
  workspaceId: ctx.workspaceId
19779
19779
  });
19780
19780
  ctx.addActivity("vault", "created document", title);
19781
- return `Document "${title}" created at "${path72}".`;
19781
+ return `Document "${title}" created at "${path74}".`;
19782
19782
  }
19783
19783
  }
19784
19784
  ];
@@ -25734,11 +25734,11 @@ var init_synthesisAudit = __esm({
25734
25734
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
25735
25735
  import { join as join4 } from "node:path";
25736
25736
  function loadNfrSpec(zelariRoot) {
25737
- const path72 = join4(zelariRoot, "nfr-spec.json");
25738
- if (!existsSync7(path72))
25737
+ const path74 = join4(zelariRoot, "nfr-spec.json");
25738
+ if (!existsSync7(path74))
25739
25739
  return null;
25740
25740
  try {
25741
- const raw = JSON.parse(readFileSync7(path72, "utf8"));
25741
+ const raw = JSON.parse(readFileSync7(path74, "utf8"));
25742
25742
  if (raw.version !== 1 || !Array.isArray(raw.targets))
25743
25743
  return null;
25744
25744
  return raw;
@@ -28044,9 +28044,9 @@ var init_types5 = __esm({
28044
28044
  import { readFileSync as readFileSync12 } from "node:fs";
28045
28045
  import { join as join10 } from "node:path";
28046
28046
  function readLessonsDeduped(zelariRoot) {
28047
- const path72 = join10(zelariRoot, LESSONS_FILE);
28047
+ const path74 = join10(zelariRoot, LESSONS_FILE);
28048
28048
  try {
28049
- const raw = readFileSync12(path72, "utf8");
28049
+ const raw = readFileSync12(path74, "utf8");
28050
28050
  const byId = /* @__PURE__ */ new Map();
28051
28051
  for (const line of raw.split(/\r?\n/)) {
28052
28052
  if (!line.trim())
@@ -28147,8 +28147,8 @@ function keywordsFrom(check2, signature) {
28147
28147
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
28148
28148
  }
28149
28149
  function writeLesson(zelariRoot, lesson) {
28150
- const path72 = join11(zelariRoot, LESSONS_FILE);
28151
- appendFileSync(path72, `${JSON.stringify(lesson)}
28150
+ const path74 = join11(zelariRoot, LESSONS_FILE);
28151
+ appendFileSync(path74, `${JSON.stringify(lesson)}
28152
28152
  `, "utf8");
28153
28153
  }
28154
28154
  function findSimilar(lessons, signature) {
@@ -30111,9 +30111,9 @@ function findCycle(nodes) {
30111
30111
  if (color.get(start) !== WHITE)
30112
30112
  continue;
30113
30113
  const stack = [[start, 0]];
30114
- const path72 = [];
30114
+ const path74 = [];
30115
30115
  color.set(start, GRAY);
30116
- path72.push(start);
30116
+ path74.push(start);
30117
30117
  while (stack.length > 0) {
30118
30118
  const top = stack[stack.length - 1];
30119
30119
  const [id3, idx] = top;
@@ -30126,17 +30126,17 @@ function findCycle(nodes) {
30126
30126
  continue;
30127
30127
  const c = color.get(dep);
30128
30128
  if (c === GRAY) {
30129
- const at = path72.indexOf(dep);
30130
- return [...path72.slice(at), dep];
30129
+ const at = path74.indexOf(dep);
30130
+ return [...path74.slice(at), dep];
30131
30131
  }
30132
30132
  if (c === WHITE) {
30133
30133
  color.set(dep, GRAY);
30134
- path72.push(dep);
30134
+ path74.push(dep);
30135
30135
  stack.push([dep, 0]);
30136
30136
  }
30137
30137
  } else {
30138
30138
  color.set(id3, BLACK);
30139
- path72.pop();
30139
+ path74.pop();
30140
30140
  stack.pop();
30141
30141
  }
30142
30142
  }
@@ -31056,8 +31056,8 @@ var init_runner = __esm({
31056
31056
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
31057
31057
  pending: []
31058
31058
  };
31059
- const path72 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
31060
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path72}`);
31059
+ const path74 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
31060
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path74}`);
31061
31061
  return snapshot;
31062
31062
  }
31063
31063
  callLog(msg, data) {
@@ -33499,16 +33499,16 @@ function runRetentionFromEnv() {
33499
33499
  maxTotalBytes: Number.isFinite(parseMb) && parseMb > 0 ? Math.round(parseMb * 1024 * 1024) : DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024
33500
33500
  };
33501
33501
  }
33502
- async function dirSize(path72) {
33502
+ async function dirSize(path74) {
33503
33503
  let total = 0;
33504
33504
  let entries;
33505
33505
  try {
33506
- entries = await readdir(path72, { withFileTypes: true });
33506
+ entries = await readdir(path74, { withFileTypes: true });
33507
33507
  } catch {
33508
33508
  return 0;
33509
33509
  }
33510
33510
  for (const entry of entries) {
33511
- const child = join13(path72, entry.name);
33511
+ const child = join13(path74, entry.name);
33512
33512
  if (entry.isDirectory())
33513
33513
  total += await dirSize(child);
33514
33514
  else {
@@ -33535,19 +33535,19 @@ async function enforceRunRetention(runsDir, options = {}) {
33535
33535
  for (const entry of entries) {
33536
33536
  if (!entry.isDirectory())
33537
33537
  continue;
33538
- const path72 = join13(runsDir, entry.name);
33538
+ const path74 = join13(runsDir, entry.name);
33539
33539
  let startedAt = 0;
33540
33540
  let endedAt;
33541
33541
  let completed = false;
33542
33542
  try {
33543
- const manifest = JSON.parse(await readFile(join13(path72, "manifest.json"), "utf8"));
33543
+ const manifest = JSON.parse(await readFile(join13(path74, "manifest.json"), "utf8"));
33544
33544
  startedAt = manifest.startedAt ?? 0;
33545
33545
  endedAt = manifest.endedAt;
33546
33546
  completed = Boolean(endedAt) && manifest.status !== "running";
33547
33547
  } catch {
33548
33548
  completed = false;
33549
33549
  }
33550
- infos.push({ name: entry.name, path: path72, startedAt, endedAt, completed, bytes: await dirSize(path72) });
33550
+ infos.push({ name: entry.name, path: path74, startedAt, endedAt, completed, bytes: await dirSize(path74) });
33551
33551
  }
33552
33552
  const remove = async (info) => {
33553
33553
  await rm(info.path, { recursive: true, force: true });
@@ -34319,12 +34319,12 @@ var init_engine = __esm({
34319
34319
  * content digest) and the returned ref carries the event seq when the
34320
34320
  * emitter resolved one.
34321
34321
  */
34322
- async fsEvidence(observation, path72, sha256, content, extra = {}) {
34322
+ async fsEvidence(observation, path74, sha256, content, extra = {}) {
34323
34323
  const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
34324
- const seq = await this.emitEvidence({ observation, path: path72, ...extra, ...digest ? { digest } : {} });
34324
+ const seq = await this.emitEvidence({ observation, path: path74, ...extra, ...digest ? { digest } : {} });
34325
34325
  return {
34326
34326
  tier: "fs-observation",
34327
- ref: path72,
34327
+ ref: path74,
34328
34328
  capturedAt: Date.now(),
34329
34329
  ...digest ? { digest } : {},
34330
34330
  ...seq !== void 0 ? { seq } : {}
@@ -34706,7 +34706,8 @@ var init_verifier = __esm({
34706
34706
  });
34707
34707
  VERIFIER_SYSTEM_PROMPT = [
34708
34708
  "You are an independent completion verifier.",
34709
- "You receive a work summary and deterministic verification results.",
34709
+ "You receive the original task, a git diff summary, a test output excerpt,",
34710
+ "and deterministic verification results \u2014 never the builder narration.",
34710
34711
  "Answer with a single JSON object and nothing else:",
34711
34712
  '{"verdict":"confirmed|rejected|unknown","score":0..1,"rationale":"..."}',
34712
34713
  "Rules: never confirm when a required deterministic check failed or is unknown;",
@@ -34741,8 +34742,14 @@ var init_verifier = __esm({
34741
34742
  try {
34742
34743
  response = await this.deps.callModel({
34743
34744
  system: VERIFIER_SYSTEM_PROMPT,
34745
+ // Blind review payload: evidence only (task, diff summary, test output
34746
+ // excerpt, deterministic results). Builder narration/reasoning is
34747
+ // structurally excluded — undefined keys are omitted.
34744
34748
  user: JSON.stringify({
34749
+ ...request.task === void 0 ? {} : { task: request.task },
34745
34750
  summary: request.summary,
34751
+ ...request.diffSummary === void 0 ? {} : { diffSummary: request.diffSummary },
34752
+ ...request.testOutputExcerpt === void 0 ? {} : { testOutputExcerpt: request.testOutputExcerpt },
34746
34753
  deterministicResults: request.results.map((r) => ({
34747
34754
  criterionId: r.criterionId,
34748
34755
  status: r.status,
@@ -37457,9 +37464,9 @@ function spillToolOutput(fullText, meta3) {
37457
37464
  const rnd = randomBytes3(3).toString("hex");
37458
37465
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
37459
37466
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
37460
- const path72 = join14(dir, file2);
37461
- writeFileSync11(path72, fullText, "utf8");
37462
- return path72;
37467
+ const path74 = join14(dir, file2);
37468
+ writeFileSync11(path74, fullText, "utf8");
37469
+ return path74;
37463
37470
  } catch {
37464
37471
  return null;
37465
37472
  }
@@ -37505,10 +37512,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
37505
37512
  ${tail2}`;
37506
37513
  }
37507
37514
  if (doSpill) {
37508
- const path72 = spillToolOutput(text, { toolName: opts.toolName });
37509
- if (path72) {
37515
+ const path74 = spillToolOutput(text, { toolName: opts.toolName });
37516
+ if (path74) {
37510
37517
  const spillNote = `
37511
- \u2026 [full output spilled to: ${path72} \u2014 re-read with read_file if you need the complete text] \u2026`;
37518
+ \u2026 [full output spilled to: ${path74} \u2014 re-read with read_file if you need the complete text] \u2026`;
37512
37519
  if (preview.includes("] \u2026\n")) {
37513
37520
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
37514
37521
  `);
@@ -40340,28 +40347,28 @@ var init_storage = __esm({
40340
40347
  VALID_SCALARS = /^(true|false|null|~)$/i;
40341
40348
  Storage = class {
40342
40349
  /** Read a Markdown file with frontmatter. Throws if not found. */
40343
- read(path72) {
40344
- if (!existsSync19(path72)) {
40345
- throw new Error(`File not found: ${path72}`);
40350
+ read(path74) {
40351
+ if (!existsSync19(path74)) {
40352
+ throw new Error(`File not found: ${path74}`);
40346
40353
  }
40347
- const md = readFileSync16(path72, "utf8");
40354
+ const md = readFileSync16(path74, "utf8");
40348
40355
  return parseFrontmatter(md);
40349
40356
  }
40350
40357
  /** Read a Markdown file; returns null if not found. */
40351
- readIfExists(path72) {
40352
- if (!existsSync19(path72)) return null;
40353
- return this.read(path72);
40358
+ readIfExists(path74) {
40359
+ if (!existsSync19(path74)) return null;
40360
+ return this.read(path74);
40354
40361
  }
40355
40362
  /**
40356
40363
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
40357
40364
  * The meta object is serialized as YAML frontmatter; body as Markdown.
40358
40365
  */
40359
- write(path72, meta3, body) {
40360
- mkdirSync10(dirname2(path72), { recursive: true });
40361
- const tmp = path72 + ".tmp-" + process.pid;
40366
+ write(path74, meta3, body) {
40367
+ mkdirSync10(dirname2(path74), { recursive: true });
40368
+ const tmp = path74 + ".tmp-" + process.pid;
40362
40369
  const md = serializeFrontmatter(meta3, body);
40363
40370
  writeFileSync13(tmp, md, "utf8");
40364
- renameSync(tmp, path72);
40371
+ renameSync(tmp, path74);
40365
40372
  }
40366
40373
  /** List all .md files in a directory (non-recursive). */
40367
40374
  listMarkdown(dir) {
@@ -40423,8 +40430,8 @@ function nextPlanTaskId(store6) {
40423
40430
  return `t${store6.counter}`;
40424
40431
  }
40425
40432
  function writePlanTaskArtifact(rootDir, task) {
40426
- const path72 = join18(rootDir, "plan-tasks", `${task.id}.md`);
40427
- mkdirSync11(dirname3(path72), { recursive: true });
40433
+ const path74 = join18(rootDir, "plan-tasks", `${task.id}.md`);
40434
+ mkdirSync11(dirname3(path74), { recursive: true });
40428
40435
  const meta3 = {
40429
40436
  kind: "task",
40430
40437
  id: task.id,
@@ -40445,7 +40452,7 @@ function writePlanTaskArtifact(rootDir, task) {
40445
40452
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
40446
40453
  ""
40447
40454
  ].filter((l) => l !== null).join("\n");
40448
- new Storage().write(path72, meta3, body);
40455
+ new Storage().write(path74, meta3, body);
40449
40456
  }
40450
40457
  function loadHandle(rootDir) {
40451
40458
  const jsonPath = join18(rootDir, "plan.json");
@@ -43733,21 +43740,21 @@ function normalizeAuth(auth) {
43733
43740
  return "agent";
43734
43741
  }
43735
43742
  function readSecrets() {
43736
- const path72 = getSshSecretsPath();
43737
- if (!existsSync23(path72)) return {};
43743
+ const path74 = getSshSecretsPath();
43744
+ if (!existsSync23(path74)) return {};
43738
43745
  try {
43739
- return JSON.parse(readFileSync20(path72, "utf8"));
43746
+ return JSON.parse(readFileSync20(path74, "utf8"));
43740
43747
  } catch {
43741
43748
  return {};
43742
43749
  }
43743
43750
  }
43744
43751
  function writeSecrets(data) {
43745
- const path72 = getSshSecretsPath();
43746
- mkdirSync12(dirname4(path72), { recursive: true });
43747
- writeFileSync15(path72, `${JSON.stringify(data, null, 2)}
43752
+ const path74 = getSshSecretsPath();
43753
+ mkdirSync12(dirname4(path74), { recursive: true });
43754
+ writeFileSync15(path74, `${JSON.stringify(data, null, 2)}
43748
43755
  `, "utf8");
43749
43756
  try {
43750
- chmodSync(path72, 384);
43757
+ chmodSync(path74, 384);
43751
43758
  } catch {
43752
43759
  }
43753
43760
  }
@@ -43776,10 +43783,10 @@ function deleteSshPassword(id3) {
43776
43783
  writeSecrets({ passwords });
43777
43784
  }
43778
43785
  function readStore2() {
43779
- const path72 = getSshTargetsPath();
43780
- if (!existsSync23(path72)) return [];
43786
+ const path74 = getSshTargetsPath();
43787
+ if (!existsSync23(path74)) return [];
43781
43788
  try {
43782
- const parsed = JSON.parse(readFileSync20(path72, "utf8"));
43789
+ const parsed = JSON.parse(readFileSync20(path74, "utf8"));
43783
43790
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
43784
43791
  return list.filter(
43785
43792
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -43794,11 +43801,11 @@ function readStore2() {
43794
43801
  }
43795
43802
  }
43796
43803
  function writeStore2(targets) {
43797
- const path72 = getSshTargetsPath();
43798
- mkdirSync12(dirname4(path72), { recursive: true });
43804
+ const path74 = getSshTargetsPath();
43805
+ mkdirSync12(dirname4(path74), { recursive: true });
43799
43806
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
43800
43807
  writeFileSync15(
43801
- path72,
43808
+ path74,
43802
43809
  `${JSON.stringify({ targets: clean }, null, 2)}
43803
43810
  `,
43804
43811
  "utf8"
@@ -44044,11 +44051,11 @@ function formatSshTargetsForPrompt() {
44044
44051
  ];
44045
44052
  for (const t of targets) {
44046
44053
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
44047
- const path72 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
44054
+ const path74 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
44048
44055
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
44049
44056
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
44050
44057
  lines.push(
44051
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path72}${tags}${allow}`
44058
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path74}${tags}${allow}`
44052
44059
  );
44053
44060
  }
44054
44061
  return lines.join("\n");
@@ -45048,6 +45055,19 @@ function defaultPermissionPolicy(overrides) {
45048
45055
  ...overrides
45049
45056
  };
45050
45057
  }
45058
+ function moreRestrictive(a, b) {
45059
+ return ACTION_RANK2[a] >= ACTION_RANK2[b] ? a : b;
45060
+ }
45061
+ function intersectPermissionPolicy(parent, child) {
45062
+ return {
45063
+ read: moreRestrictive(parent.read, child.read),
45064
+ write: moreRestrictive(parent.write, child.write),
45065
+ execute: moreRestrictive(parent.execute, child.execute),
45066
+ network: moreRestrictive(parent.network, child.network),
45067
+ ui: moreRestrictive(parent.ui, child.ui),
45068
+ auto: parent.auto && child.auto
45069
+ };
45070
+ }
45051
45071
  function resolveToolPermission(toolName, required2, policy) {
45052
45072
  if (!required2.length) {
45053
45073
  return { action: "allow", reason: "", categories: [] };
@@ -45083,12 +45103,13 @@ function resolveToolPermission(toolName, required2, policy) {
45083
45103
  categories: hit
45084
45104
  };
45085
45105
  }
45086
- var sessionToolGrants, sessionCategoryGrants;
45106
+ var sessionToolGrants, sessionCategoryGrants, ACTION_RANK2;
45087
45107
  var init_toolPermissions = __esm({
45088
45108
  "src/cli/safety/toolPermissions.ts"() {
45089
45109
  "use strict";
45090
45110
  sessionToolGrants = /* @__PURE__ */ new Set();
45091
45111
  sessionCategoryGrants = /* @__PURE__ */ new Set();
45112
+ ACTION_RANK2 = { allow: 0, ask: 1, deny: 2 };
45092
45113
  }
45093
45114
  });
45094
45115
 
@@ -45265,10 +45286,194 @@ var init_lifecycleHooks = __esm({
45265
45286
  }
45266
45287
  });
45267
45288
 
45289
+ // src/cli/safety/policyEngine.ts
45290
+ import { readFileSync as readFileSync22 } from "node:fs";
45291
+ import { homedir as homedir10 } from "node:os";
45292
+ import path40 from "node:path";
45293
+ function emptyPolicySet() {
45294
+ return { agents: /* @__PURE__ */ new Map(), warnings: [] };
45295
+ }
45296
+ function agentRulesFor(set2, agent) {
45297
+ return set2.agents.get(agent) ?? EMPTY_POLICY_RULE_SET;
45298
+ }
45299
+ function globToRegExp(pattern) {
45300
+ let src = "^";
45301
+ for (let i = 0; i < pattern.length; i++) {
45302
+ const ch = pattern[i];
45303
+ if (ch === "*") {
45304
+ while (pattern[i + 1] === "*") i++;
45305
+ src += ".*";
45306
+ } else if ("\\^$.|?+()[]{}".includes(ch)) {
45307
+ src += "\\" + ch;
45308
+ } else {
45309
+ src += ch;
45310
+ }
45311
+ }
45312
+ return new RegExp(src + "$");
45313
+ }
45314
+ function normalizeForMatch(value) {
45315
+ return value.replace(/\\/g, "/");
45316
+ }
45317
+ function resolvePolicyRule(rules, value) {
45318
+ const v = normalizeForMatch(value);
45319
+ for (const rule of rules) {
45320
+ if (globToRegExp(normalizeForMatch(rule.match)).test(v)) return rule;
45321
+ }
45322
+ return null;
45323
+ }
45324
+ function mergeRuleEffect(base2, rule) {
45325
+ if (!rule) return base2;
45326
+ return EFFECT_RANK[rule.effect] > EFFECT_RANK[base2] ? rule.effect : base2;
45327
+ }
45328
+ function pathCandidates(value, root) {
45329
+ const norm = normalizeForMatch(value);
45330
+ if (!root) return [norm];
45331
+ const prefix = normalizeForMatch(root).replace(/\/+$/, "") + "/";
45332
+ const stripped = norm.toLowerCase().startsWith(prefix.toLowerCase()) ? norm.slice(prefix.length) : null;
45333
+ return stripped !== null ? [stripped, norm] : [norm];
45334
+ }
45335
+ function matchAgentPolicyRule(rules, required2, args, root) {
45336
+ if (!rules) return null;
45337
+ const a = args !== null && typeof args === "object" ? args : {};
45338
+ if (required2.includes("execute")) {
45339
+ const cmd = a["command"];
45340
+ if (typeof cmd === "string" && cmd !== "") {
45341
+ const hit = resolvePolicyRule(rules.shell, cmd);
45342
+ if (hit) return hit;
45343
+ }
45344
+ }
45345
+ if (required2.includes("write")) {
45346
+ const p3 = typeof a["path"] === "string" ? a["path"] : typeof a["file_path"] === "string" ? a["file_path"] : "";
45347
+ if (p3 !== "") {
45348
+ for (const candidate of pathCandidates(p3, root)) {
45349
+ const hit = resolvePolicyRule(rules.edit, candidate);
45350
+ if (hit) return hit;
45351
+ }
45352
+ }
45353
+ }
45354
+ return null;
45355
+ }
45356
+ function isPolicyEngineDisabled() {
45357
+ const v = process.env.ZELARI_POLICY?.trim().toLowerCase();
45358
+ return v === "0" || v === "false" || v === "no" || v === "off";
45359
+ }
45360
+ function isPlainObject2(v) {
45361
+ return typeof v === "object" && v !== null && !Array.isArray(v);
45362
+ }
45363
+ function parseRuleList(raw, origin, warnings) {
45364
+ if (raw === void 0) return [];
45365
+ if (!Array.isArray(raw)) {
45366
+ warnings.push(`${origin}: expected an array of rules, got ${typeof raw} \u2014 section ignored.`);
45367
+ return [];
45368
+ }
45369
+ const out = [];
45370
+ raw.forEach((item, i) => {
45371
+ const where = `${origin}[${i}]`;
45372
+ if (!isPlainObject2(item)) {
45373
+ warnings.push(`${where}: rule is not an object \u2014 skipped.`);
45374
+ return;
45375
+ }
45376
+ const match = item["match"];
45377
+ const effect = item["effect"];
45378
+ const reason = item["reason"];
45379
+ if (typeof match !== "string" || match.trim() === "") {
45380
+ warnings.push(`${where}: missing or empty "match" \u2014 skipped.`);
45381
+ return;
45382
+ }
45383
+ if (effect !== "allow" && effect !== "ask" && effect !== "deny") {
45384
+ warnings.push(`${where} ("${match}"): "effect" must be allow|ask|deny \u2014 skipped.`);
45385
+ return;
45386
+ }
45387
+ out.push(
45388
+ typeof reason === "string" && reason.trim() !== "" ? { match, effect, reason } : { match, effect }
45389
+ );
45390
+ });
45391
+ return out;
45392
+ }
45393
+ function parsePolicyFile(raw, origin, warnings) {
45394
+ if (!isPlainObject2(raw)) {
45395
+ warnings.push(`${origin}: policy file is not a JSON object \u2014 file ignored.`);
45396
+ return null;
45397
+ }
45398
+ const version2 = raw["version"];
45399
+ if (version2 !== void 0 && version2 !== 1) {
45400
+ warnings.push(`${origin}: unsupported "version" ${JSON.stringify(version2)} (expected 1) \u2014 file ignored.`);
45401
+ return null;
45402
+ }
45403
+ const agentsRaw = raw["agents"];
45404
+ if (agentsRaw === void 0) {
45405
+ warnings.push(`${origin}: no "agents" key \u2014 file ignored.`);
45406
+ return null;
45407
+ }
45408
+ if (!isPlainObject2(agentsRaw)) {
45409
+ warnings.push(`${origin}: "agents" is not an object \u2014 file ignored.`);
45410
+ return null;
45411
+ }
45412
+ const out = /* @__PURE__ */ new Map();
45413
+ for (const [key, val] of Object.entries(agentsRaw)) {
45414
+ const agent = key.trim().toLowerCase();
45415
+ if (!KNOWN_AGENTS.has(agent)) {
45416
+ warnings.push(`${origin}: unknown agent "${key}" (known: ${POLICY_AGENTS.join(" | ")}) \u2014 ignored.`);
45417
+ continue;
45418
+ }
45419
+ if (!isPlainObject2(val)) {
45420
+ warnings.push(`${origin}: agents.${key} is not an object \u2014 ignored.`);
45421
+ continue;
45422
+ }
45423
+ const shell = parseRuleList(val["shell"], `${origin} agents.${key}.shell`, warnings);
45424
+ const edit = parseRuleList(val["edit"], `${origin} agents.${key}.edit`, warnings);
45425
+ out.set(agent, { shell, edit });
45426
+ }
45427
+ return out;
45428
+ }
45429
+ function readPolicyFile(file2, warnings) {
45430
+ let text;
45431
+ try {
45432
+ text = readFileSync22(file2, "utf8");
45433
+ } catch {
45434
+ return /* @__PURE__ */ new Map();
45435
+ }
45436
+ let parsed;
45437
+ try {
45438
+ parsed = JSON.parse(text);
45439
+ } catch (err) {
45440
+ warnings.push(
45441
+ `${file2}: invalid JSON (${err instanceof Error ? err.message : String(err)}) \u2014 file ignored.`
45442
+ );
45443
+ return /* @__PURE__ */ new Map();
45444
+ }
45445
+ return parsePolicyFile(parsed, file2, warnings) ?? /* @__PURE__ */ new Map();
45446
+ }
45447
+ function loadPolicySet(root, opts = {}) {
45448
+ if (isPolicyEngineDisabled()) return emptyPolicySet();
45449
+ const warnings = [];
45450
+ const project = readPolicyFile(path40.join(root, ".zelari", "policy.json"), warnings);
45451
+ const global = readPolicyFile(path40.join(opts.homeDir ?? homedir10(), ".zelari", "policy.json"), warnings);
45452
+ const agents = /* @__PURE__ */ new Map();
45453
+ for (const [agent, g] of global) {
45454
+ agents.set(agent, { shell: [...g.shell], edit: [...g.edit] });
45455
+ }
45456
+ for (const [agent, p3] of project) {
45457
+ const g = agents.get(agent) ?? EMPTY_POLICY_RULE_SET;
45458
+ agents.set(agent, { shell: [...p3.shell, ...g.shell], edit: [...p3.edit, ...g.edit] });
45459
+ }
45460
+ return { agents, warnings };
45461
+ }
45462
+ var POLICY_AGENTS, KNOWN_AGENTS, EMPTY_POLICY_RULE_SET, EFFECT_RANK;
45463
+ var init_policyEngine = __esm({
45464
+ "src/cli/safety/policyEngine.ts"() {
45465
+ "use strict";
45466
+ POLICY_AGENTS = ["lead", "explore", "general", "verify"];
45467
+ KNOWN_AGENTS = new Set(POLICY_AGENTS);
45468
+ EMPTY_POLICY_RULE_SET = { shell: [], edit: [] };
45469
+ EFFECT_RANK = { allow: 0, ask: 1, deny: 2 };
45470
+ }
45471
+ });
45472
+
45268
45473
  // src/cli/toolResultCache.ts
45269
45474
  import { createHash as createHash13 } from "node:crypto";
45270
45475
  import { promises as fs19 } from "node:fs";
45271
- import path40 from "node:path";
45476
+ import path41 from "node:path";
45272
45477
  function isToolCacheEnabled() {
45273
45478
  const raw = process.env.ZELARI_TOOL_CACHE;
45274
45479
  return raw !== "0" && raw !== "false" && raw !== "off";
@@ -45353,7 +45558,7 @@ async function statKey(toolName, input, ctx) {
45353
45558
  if (!input || typeof input !== "object") return null;
45354
45559
  const rawPath = input.path;
45355
45560
  if (typeof rawPath !== "string" || rawPath.length === 0) return null;
45356
- const abs = path40.isAbsolute(rawPath) ? rawPath : path40.join(ctx.cwd, rawPath);
45561
+ const abs = path41.isAbsolute(rawPath) ? rawPath : path41.join(ctx.cwd, rawPath);
45357
45562
  try {
45358
45563
  const st = await fs19.stat(abs);
45359
45564
  return hashKey({
@@ -45402,9 +45607,13 @@ var init_toolResultCache = __esm({
45402
45607
  // src/cli/tools/krakenModel.ts
45403
45608
  var krakenModel_exports = {};
45404
45609
  __export(krakenModel_exports, {
45610
+ inferModelFamily: () => inferModelFamily,
45405
45611
  isCheapModelId: () => isCheapModelId,
45406
45612
  isKrakenAutoModelEnabled: () => isKrakenAutoModelEnabled,
45613
+ parseQualifiedModelRef: () => parseQualifiedModelRef,
45407
45614
  pickCheapModel: () => pickCheapModel,
45615
+ pickDifferentFamily: () => pickDifferentFamily,
45616
+ resolveCrossModelVerifier: () => resolveCrossModelVerifier,
45408
45617
  resolveKrakenPlannerModel: () => resolveKrakenPlannerModel,
45409
45618
  resolveKrakenSubModel: () => resolveKrakenSubModel,
45410
45619
  resolveKrakenSubModelAsync: () => resolveKrakenSubModelAsync,
@@ -45440,6 +45649,53 @@ function isKrakenAutoModelEnabled(env = process.env) {
45440
45649
  if (v === "0" || v === "false" || v === "no" || v === "off") return false;
45441
45650
  return true;
45442
45651
  }
45652
+ function parseQualifiedModelRef(ref) {
45653
+ const s = ref?.trim() ?? "";
45654
+ const slash = s.indexOf("/");
45655
+ if (slash <= 0 || slash === s.length - 1) return null;
45656
+ const provider = s.slice(0, slash).trim();
45657
+ const model = s.slice(slash + 1).trim();
45658
+ if (!provider || !model) return null;
45659
+ return { provider, model };
45660
+ }
45661
+ function inferModelFamily(provider, model) {
45662
+ const p3 = (provider ?? "").trim().toLowerCase();
45663
+ const m = (model ?? "").trim().toLowerCase();
45664
+ const hay = `${p3} ${m}`.trim();
45665
+ if (!hay) return "other";
45666
+ if (/\b(zhipu|glm)\b/.test(hay)) return "zhipu";
45667
+ if (/\b(google|gemini)\b/.test(hay)) return "google";
45668
+ if (/\b(xai|grok)\b/.test(hay)) return "xai";
45669
+ if (/\b(anthropic|claude)\b/.test(hay)) return "anthropic";
45670
+ if (/\b(openai|chatgpt|codex)\b/.test(hay) || /\bgpt\b/.test(hay) || /(^|\s)o[134]($|[-_.])/.test(hay)) {
45671
+ return "openai";
45672
+ }
45673
+ return p3 || "other";
45674
+ }
45675
+ function pickDifferentFamily(builder, candidates) {
45676
+ const builderFamily = inferModelFamily(builder.provider, builder.model);
45677
+ for (const c of candidates) {
45678
+ const provider = c.provider?.trim() ?? "";
45679
+ const model = c.model?.trim() ?? "";
45680
+ if (!provider || !model) continue;
45681
+ if (inferModelFamily(provider, model) !== builderFamily) {
45682
+ return { provider, model };
45683
+ }
45684
+ }
45685
+ return null;
45686
+ }
45687
+ function resolveCrossModelVerifier(builder, candidates, env = process.env) {
45688
+ const cross = (env.ZELARI_KRAKEN_CROSS_MODEL ?? "").trim().toLowerCase();
45689
+ if (cross === "0" || cross === "false" || cross === "off") return null;
45690
+ const specific = env.ZELARI_KRAKEN_VERIFY_MODEL?.trim();
45691
+ if (specific) {
45692
+ const qualified = parseQualifiedModelRef(specific);
45693
+ if (qualified) return qualified;
45694
+ const provider = builder.provider?.trim();
45695
+ return provider ? { provider, model: specific } : null;
45696
+ }
45697
+ return pickDifferentFamily(builder, candidates);
45698
+ }
45443
45699
  function resolveKrakenSubModel(agent, parentModel, env = process.env, opts = {}) {
45444
45700
  const kindKey = agent === "explore" ? "ZELARI_KRAKEN_EXPLORE_MODEL" : agent === "verify" ? "ZELARI_KRAKEN_VERIFY_MODEL" : "ZELARI_KRAKEN_GENERAL_MODEL";
45445
45701
  const specific = env[kindKey]?.trim();
@@ -45452,6 +45708,13 @@ function resolveKrakenSubModel(agent, parentModel, env = process.env, opts = {})
45452
45708
  }
45453
45709
  return shared2;
45454
45710
  }
45711
+ if (agent === "verify" && opts.familyCandidates && opts.familyCandidates.length > 0) {
45712
+ const picked = pickDifferentFamily(
45713
+ { provider: opts.provider ?? "", model: parentModel },
45714
+ opts.familyCandidates
45715
+ );
45716
+ if (picked) return `${picked.provider}/${picked.model}`;
45717
+ }
45455
45718
  if ((agent === "explore" || agent === "verify") && isKrakenAutoModelEnabled(env) && opts.candidates && opts.candidates.length > 0) {
45456
45719
  const picked = pickCheapModel(parentModel, opts.candidates);
45457
45720
  if (picked) return picked;
@@ -45547,7 +45810,11 @@ function createBuiltinToolRegistry(options = {}) {
45547
45810
  const allowMutators = !readOnly && !verifyMode && !gauntletParent;
45548
45811
  const allowBash = (allowMutators || verifyMode) && !gauntletParent;
45549
45812
  const permPolicy = options.permissionPolicy ?? defaultPermissionPolicy();
45550
- const withPerm = (t) => wrapWithPermissions(t, permPolicy, options.onPermissionAsk);
45813
+ const agentPolicyRules = agentRulesFor(
45814
+ loadPolicySet(root),
45815
+ options.policyAgent ?? "lead"
45816
+ );
45817
+ const withPerm = (t) => wrapWithPermissions(t, permPolicy, options.onPermissionAsk, agentPolicyRules, root);
45551
45818
  registry4.register(withPerm(safeReadFile));
45552
45819
  registry4.register(withPerm(safeGrepContent));
45553
45820
  registry4.register(withPerm(safeListFiles));
@@ -45681,6 +45948,10 @@ function createBuiltinToolRegistry(options = {}) {
45681
45948
  root,
45682
45949
  audit,
45683
45950
  sessionId: sessionId2,
45951
+ // P0.4 capability inheritance: tentacles intersect THIS
45952
+ // registry's own policy (permPolicy above) — they can never
45953
+ // exceed it.
45954
+ parentPolicy: permPolicy,
45684
45955
  ...options.subAgentProvider ? { provider: options.subAgentProvider } : {},
45685
45956
  ...options.subAgentModel ? { model: options.subAgentModel } : {}
45686
45957
  }),
@@ -45758,15 +46029,30 @@ function taskAgentToProfile(agent) {
45758
46029
  return "explore";
45759
46030
  }
45760
46031
  function createKrakenSubAgentContextFactory(opts) {
45761
- const { root, audit, sessionId: sessionId2, provider: providerOverride, model: modelOverride } = opts;
46032
+ const { root, audit, sessionId: sessionId2, provider: providerOverride, model: modelOverride, parentPolicy } = opts;
45762
46033
  return async ({ agent, cwd: subCwd }) => {
45763
46034
  const cfg = providerOverride ? await providerConfigFor(providerOverride) : await providerFromEnv();
45764
46035
  if (!cfg) return null;
45765
- const { resolveKrakenSubModel: resolveKrakenSubModel2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
45766
- const model = resolveKrakenSubModel2(agent, modelOverride || cfg.model);
45767
- const subCfg = { ...cfg, model };
46036
+ const { resolveKrakenSubModel: resolveKrakenSubModel2, parseQualifiedModelRef: parseQualifiedModelRef2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
46037
+ const resolvedModel = resolveKrakenSubModel2(agent, modelOverride || cfg.model);
46038
+ let effCfg = cfg;
46039
+ let model = resolvedModel;
46040
+ const ref = parseQualifiedModelRef2(resolvedModel);
46041
+ if (ref) {
46042
+ const cross = ref.provider === cfg.providerId ? cfg : await providerConfigFor(ref.provider);
46043
+ if (cross) {
46044
+ effCfg = cross;
46045
+ model = ref.model;
46046
+ }
46047
+ }
46048
+ const subCfg = { ...effCfg, model };
45768
46049
  const subProfile = taskAgentToProfile(agent);
45769
46050
  const subRoot = subCwd || root;
46051
+ const agentPolicyForSubProfile = defaultPermissionPolicy({ auto: true });
46052
+ const effectiveSubPolicy = intersectPermissionPolicy(
46053
+ parentPolicy ?? agentPolicyForSubProfile,
46054
+ agentPolicyForSubProfile
46055
+ );
45770
46056
  const { registry: subRegistry } = createBuiltinToolRegistry({
45771
46057
  root: subRoot,
45772
46058
  audit,
@@ -45776,7 +46062,9 @@ function createKrakenSubAgentContextFactory(opts) {
45776
46062
  enableSkill: agent === "general",
45777
46063
  diagnostics: false,
45778
46064
  lspProvider: null,
45779
- permissionPolicy: defaultPermissionPolicy({ auto: true })
46065
+ permissionPolicy: effectiveSubPolicy,
46066
+ // P0.5: the tentacle's agent identity drives per-agent policy rules.
46067
+ policyAgent: agent
45780
46068
  });
45781
46069
  return {
45782
46070
  providerStream: buildProviderStream(subCfg),
@@ -45793,7 +46081,7 @@ function createKrakenSubAgentContextFactory(opts) {
45793
46081
  };
45794
46082
  };
45795
46083
  }
45796
- function wrapWithPermissions(original, policy, onAsk) {
46084
+ function wrapWithPermissions(original, policy, onAsk, agentRules, root) {
45797
46085
  const required2 = original.permissions ?? [];
45798
46086
  const decisionProbe = resolveToolPermission(original.name, required2, policy);
45799
46087
  if (decisionProbe.action === "allow" && !required2.includes("write") && !required2.includes("execute")) {
@@ -45802,13 +46090,21 @@ function wrapWithPermissions(original, policy, onAsk) {
45802
46090
  ...original,
45803
46091
  execute: async (input, ctx) => {
45804
46092
  const decision = resolveToolPermission(original.name, required2, policy);
45805
- if (decision.action === "deny") {
45806
- return typedErr(`[permission] ${decision.reason}`);
45807
- }
45808
- if (decision.action === "ask") {
46093
+ const rule = agentRules ? matchAgentPolicyRule(
46094
+ agentRules,
46095
+ required2,
46096
+ input ?? {},
46097
+ root ?? process.cwd()
46098
+ ) : null;
46099
+ const action = mergeRuleEffect(decision.action, rule);
46100
+ const rulePrefix = rule ? `[policy] rule '${rule.match}'${rule.reason ? ` \u2014 ${rule.reason}` : ""}` : "";
46101
+ if (action === "deny") {
46102
+ return typedErr(`[permission] ${rulePrefix || decision.reason}`);
46103
+ }
46104
+ if (action === "ask") {
45809
46105
  if (!onAsk) {
45810
46106
  return typedErr(
45811
- `[permission] ${decision.reason} No interactive approval available (set ZELARI_AUTO=1 to auto-allow, or configure onPermissionAsk).`
46107
+ `[permission] ${rulePrefix ? `${rulePrefix} ` : ""}${decision.reason} No interactive approval available (set ZELARI_AUTO=1 to auto-allow, or configure onPermissionAsk).`
45812
46108
  );
45813
46109
  }
45814
46110
  try {
@@ -46024,6 +46320,7 @@ var init_toolRegistry = __esm({
46024
46320
  init_providerConfig();
46025
46321
  init_toolPermissions();
46026
46322
  init_lifecycleHooks();
46323
+ init_policyEngine();
46027
46324
  init_toolResultCache();
46028
46325
  init_toolTypes();
46029
46326
  init_skills2();
@@ -46044,7 +46341,7 @@ var init_toolRegistry = __esm({
46044
46341
 
46045
46342
  // src/cli/metrics.ts
46046
46343
  import { promises as fs20, existsSync as existsSync25, statSync as statSync4, renameSync as renameSync3, appendFileSync as appendFileSync3, mkdirSync as mkdirSync14 } from "node:fs";
46047
- import path41 from "node:path";
46344
+ import path42 from "node:path";
46048
46345
  import os9 from "node:os";
46049
46346
  async function readMetrics(file2) {
46050
46347
  let raw = "";
@@ -46093,8 +46390,8 @@ var init_metrics3 = __esm({
46093
46390
  file;
46094
46391
  writeQueue = Promise.resolve();
46095
46392
  constructor(file2) {
46096
- this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path41.join(os9.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
46097
- mkdirSync14(path41.dirname(this.file), { recursive: true });
46393
+ this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path42.join(os9.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
46394
+ mkdirSync14(path42.dirname(this.file), { recursive: true });
46098
46395
  }
46099
46396
  /** Metrics file path — doctor/summary readers use this. */
46100
46397
  get filePath() {
@@ -46226,12 +46523,12 @@ Current policy: **lead only**.
46226
46523
  // src/cli/state/fileStateStore.ts
46227
46524
  import { createHash as createHash15, randomUUID as randomUUID3 } from "node:crypto";
46228
46525
  import { promises as fs21 } from "node:fs";
46229
- import * as path43 from "node:path";
46526
+ import * as path45 from "node:path";
46230
46527
  function shortId() {
46231
46528
  return randomUUID3().replace(/-/g, "").slice(0, 12);
46232
46529
  }
46233
46530
  async function writeJsonAtomic(filePath, data) {
46234
- await fs21.mkdir(path43.dirname(filePath), { recursive: true });
46531
+ await fs21.mkdir(path45.dirname(filePath), { recursive: true });
46235
46532
  const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
46236
46533
  await fs21.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
46237
46534
  await fs21.rename(tmp, filePath);
@@ -46292,11 +46589,11 @@ var init_fileStateStore = __esm({
46292
46589
  indexPath = "";
46293
46590
  async init(projectRoot) {
46294
46591
  this.root = projectRoot;
46295
- this.stateDir = path43.join(projectRoot, ".zelari", "state");
46296
- this.commitsDir = path43.join(this.stateDir, "commits");
46297
- this.artifactsDir = path43.join(this.stateDir, "artifacts");
46298
- this.headPath = path43.join(this.stateDir, "HEAD.json");
46299
- this.indexPath = path43.join(this.stateDir, "index.jsonl");
46592
+ this.stateDir = path45.join(projectRoot, ".zelari", "state");
46593
+ this.commitsDir = path45.join(this.stateDir, "commits");
46594
+ this.artifactsDir = path45.join(this.stateDir, "artifacts");
46595
+ this.headPath = path45.join(this.stateDir, "HEAD.json");
46596
+ this.indexPath = path45.join(this.stateDir, "index.jsonl");
46300
46597
  await fs21.mkdir(this.commitsDir, { recursive: true });
46301
46598
  await fs21.mkdir(this.artifactsDir, { recursive: true });
46302
46599
  }
@@ -46309,13 +46606,13 @@ var init_fileStateStore = __esm({
46309
46606
  const discoveries = input.discoveries ?? [];
46310
46607
  const parent = await this.head();
46311
46608
  const id3 = shortId();
46312
- const artifactRel = path43.join("artifacts", id3);
46313
- const artifactAbs = path43.join(this.artifactsDir, id3);
46609
+ const artifactRel = path45.join("artifacts", id3);
46610
+ const artifactAbs = path45.join(this.artifactsDir, id3);
46314
46611
  await fs21.mkdir(artifactAbs, { recursive: true });
46315
46612
  const summary = defaultSummary(input, discoveries);
46316
- await fs21.writeFile(path43.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
46317
- await writeJsonAtomic(path43.join(artifactAbs, "discoveries.json"), discoveries);
46318
- await writeJsonAtomic(path43.join(artifactAbs, "verification.json"), input.verification);
46613
+ await fs21.writeFile(path45.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
46614
+ await writeJsonAtomic(path45.join(artifactAbs, "discoveries.json"), discoveries);
46615
+ await writeJsonAtomic(path45.join(artifactAbs, "verification.json"), input.verification);
46319
46616
  const meta3 = {
46320
46617
  id: id3,
46321
46618
  parentId: parent?.id ?? null,
@@ -46327,14 +46624,14 @@ var init_fileStateStore = __esm({
46327
46624
  workspaceCheckpointId: input.workspaceCheckpointId,
46328
46625
  verification: {
46329
46626
  ...input.verification,
46330
- reportPath: input.verification.reportPath ?? path43.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
46627
+ reportPath: input.verification.reportPath ?? path45.join(".zelari", "state", artifactRel, "verification.json").replace(/\\/g, "/")
46331
46628
  },
46332
46629
  changedPaths: input.changedPaths ?? [],
46333
46630
  stablePromptHash: input.stablePromptHash,
46334
46631
  discoveryCount: discoveries.length,
46335
46632
  artifactDir: artifactRel.replace(/\\/g, "/")
46336
46633
  };
46337
- await writeJsonAtomic(path43.join(this.commitsDir, `${id3}.json`), meta3);
46634
+ await writeJsonAtomic(path45.join(this.commitsDir, `${id3}.json`), meta3);
46338
46635
  await writeJsonAtomic(this.headPath, { id: id3, updatedAt: meta3.createdAt });
46339
46636
  await fs21.appendFile(this.indexPath, JSON.stringify({ id: id3, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
46340
46637
  return stripStored(meta3);
@@ -46345,7 +46642,7 @@ var init_fileStateStore = __esm({
46345
46642
  return this.get(head.id);
46346
46643
  }
46347
46644
  async get(id3) {
46348
- const stored = await readJsonFile(path43.join(this.commitsDir, `${id3}.json`));
46645
+ const stored = await readJsonFile(path45.join(this.commitsDir, `${id3}.json`));
46349
46646
  return stored ? stripStored(stored) : null;
46350
46647
  }
46351
46648
  async list(limit = 20) {
@@ -46384,9 +46681,9 @@ var init_fileStateStore = __esm({
46384
46681
  async loadDiscoveries(id3) {
46385
46682
  const meta3 = id3 ? await this.get(id3) : await this.head();
46386
46683
  if (!meta3) return [];
46387
- const stored = await readJsonFile(path43.join(this.commitsDir, `${meta3.id}.json`));
46684
+ const stored = await readJsonFile(path45.join(this.commitsDir, `${meta3.id}.json`));
46388
46685
  if (!stored?.artifactDir) return [];
46389
- const discPath = path43.join(this.stateDir, stored.artifactDir, "discoveries.json");
46686
+ const discPath = path45.join(this.stateDir, stored.artifactDir, "discoveries.json");
46390
46687
  return await readJsonFile(discPath) ?? [];
46391
46688
  }
46392
46689
  async materializeContext(id3, maxChars = DEFAULT_MATERIALIZE_CHARS) {
@@ -47337,7 +47634,7 @@ var init_mode = __esm({
47337
47634
  });
47338
47635
 
47339
47636
  // src/cli/headless.ts
47340
- import { readFileSync as readFileSync22 } from "node:fs";
47637
+ import { readFileSync as readFileSync23 } from "node:fs";
47341
47638
  function defaultProfileForMode(mode) {
47342
47639
  switch (mode) {
47343
47640
  case "council":
@@ -47358,6 +47655,7 @@ function parseHeadlessFlags(argv) {
47358
47655
  let phase2 = "build";
47359
47656
  let modeExplicit = false;
47360
47657
  let councilFlag = false;
47658
+ let sawModeAuto = false;
47361
47659
  let provider;
47362
47660
  let model;
47363
47661
  let history2;
@@ -47392,7 +47690,7 @@ function parseHeadlessFlags(argv) {
47392
47690
  const next = argv[i + 1];
47393
47691
  if (next) {
47394
47692
  try {
47395
- const fromFile = readFileSync22(next, "utf-8");
47693
+ const fromFile = readFileSync23(next, "utf-8");
47396
47694
  if (fromFile.trim()) task = fromFile;
47397
47695
  } catch {
47398
47696
  }
@@ -47402,11 +47700,17 @@ function parseHeadlessFlags(argv) {
47402
47700
  councilFlag = true;
47403
47701
  } else if (arg === "--mode") {
47404
47702
  const next = argv[i + 1];
47703
+ if (next !== void 0 && next.trim().toLowerCase() === "auto") {
47704
+ sawModeAuto = true;
47705
+ modeExplicit = true;
47706
+ i++;
47707
+ continue;
47708
+ }
47405
47709
  const parsed = next ? parseMode(next) : null;
47406
47710
  if (!parsed) {
47407
47711
  return {
47408
47712
  options: null,
47409
- error: `--mode requires 'kraken', 'council', or 'zelari' (agent=alias), got '${next ?? "(missing)"}'`
47713
+ error: `--mode requires 'kraken', 'council', 'zelari', or 'auto' (agent=alias), got '${next ?? "(missing)"}'`
47410
47714
  };
47411
47715
  }
47412
47716
  mode = parsed;
@@ -47435,7 +47739,7 @@ function parseHeadlessFlags(argv) {
47435
47739
  let raw = null;
47436
47740
  if (arg === "--history-file") {
47437
47741
  try {
47438
- raw = readFileSync22(next, "utf-8");
47742
+ raw = readFileSync23(next, "utf-8");
47439
47743
  } catch {
47440
47744
  raw = null;
47441
47745
  }
@@ -47530,7 +47834,7 @@ function parseHeadlessFlags(argv) {
47530
47834
  const next = argv[i + 1];
47531
47835
  if (next) {
47532
47836
  try {
47533
- const fromFile = readFileSync22(next, "utf-8");
47837
+ const fromFile = readFileSync23(next, "utf-8");
47534
47838
  if (fromFile.trim()) krakenGraph = fromFile;
47535
47839
  } catch {
47536
47840
  }
@@ -47568,6 +47872,7 @@ function parseHeadlessFlags(argv) {
47568
47872
  mode,
47569
47873
  phase: phase2,
47570
47874
  useCouncil: mode === "council",
47875
+ ...sawModeAuto ? { orchestrationAuto: true } : {},
47571
47876
  provider,
47572
47877
  model,
47573
47878
  ...history2 && history2.length > 0 ? { history: history2 } : {},
@@ -48095,7 +48400,7 @@ var init_claudeProvider = __esm({
48095
48400
  // src/cli/memory/legacyImport.ts
48096
48401
  import { createHash as createHash16 } from "node:crypto";
48097
48402
  import { promises as fs22 } from "node:fs";
48098
- import * as path44 from "node:path";
48403
+ import * as path46 from "node:path";
48099
48404
  function sourceId(fact, line) {
48100
48405
  return `jsonl:${fact.id ?? createHash16("sha256").update(line).digest("hex")}`;
48101
48406
  }
@@ -48113,7 +48418,7 @@ function timestamp(value) {
48113
48418
  }
48114
48419
  async function importLegacyMemoryLog(backend, service) {
48115
48420
  const result = { found: 0, imported: 0, skipped: 0, corrupt: 0 };
48116
- const logPath = path44.join(path44.dirname(backend.databasePath), "log.jsonl");
48421
+ const logPath = path46.join(path46.dirname(backend.databasePath), "log.jsonl");
48117
48422
  let raw;
48118
48423
  try {
48119
48424
  raw = await fs22.readFile(logPath, "utf8");
@@ -48296,7 +48601,7 @@ var init_sqliteCodec = __esm({
48296
48601
  // src/cli/memory/sqliteRpc.ts
48297
48602
  import { existsSync as existsSync27 } from "node:fs";
48298
48603
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
48299
- import * as path45 from "node:path";
48604
+ import * as path47 from "node:path";
48300
48605
  import { Worker } from "node:worker_threads";
48301
48606
  function isBusy(error51) {
48302
48607
  const candidate = error51;
@@ -48305,10 +48610,10 @@ function isBusy(error51) {
48305
48610
  );
48306
48611
  }
48307
48612
  function resolveWorkerUrl() {
48308
- const here = path45.dirname(fileURLToPath2(import.meta.url));
48309
- const direct = path45.join(here, "sqliteWorker.mjs");
48613
+ const here = path47.dirname(fileURLToPath2(import.meta.url));
48614
+ const direct = path47.join(here, "sqliteWorker.mjs");
48310
48615
  if (existsSync27(direct)) return pathToFileURL2(direct);
48311
- return pathToFileURL2(path45.join(here, "memory", "sqliteWorker.mjs"));
48616
+ return pathToFileURL2(path47.join(here, "memory", "sqliteWorker.mjs"));
48312
48617
  }
48313
48618
  var SqliteWorkerRpc;
48314
48619
  var init_sqliteRpc = __esm({
@@ -48597,7 +48902,7 @@ WHERE NOT EXISTS (SELECT 1 FROM memory_fts f WHERE f.node_id = n.id);
48597
48902
  // src/cli/memory/sqliteBackend.ts
48598
48903
  import { createHash as createHash17, randomUUID as randomUUID4 } from "node:crypto";
48599
48904
  import { promises as fs23 } from "node:fs";
48600
- import * as path46 from "node:path";
48905
+ import * as path48 from "node:path";
48601
48906
  function boundedLimit(value, fallback = 50) {
48602
48907
  return Math.max(1, Math.min(Math.floor(value ?? fallback), 1e5));
48603
48908
  }
@@ -48659,16 +48964,16 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
48659
48964
  try {
48660
48965
  resolved = await fs23.realpath(projectRoot);
48661
48966
  } catch {
48662
- resolved = path46.resolve(projectRoot);
48967
+ resolved = path48.resolve(projectRoot);
48663
48968
  }
48664
48969
  if (this.initialized && resolved === this.projectRoot) return;
48665
48970
  if (this.initialized) await this.close();
48666
48971
  const filename = this.options.filename ?? "memory.db";
48667
- if (path46.basename(filename) !== filename || filename === "." || filename === "..") {
48972
+ if (path48.basename(filename) !== filename || filename === "." || filename === "..") {
48668
48973
  throw new Error("SQLite memory filename must not contain a path.");
48669
48974
  }
48670
- const zelariDirectory = path46.join(resolved, ".zelari");
48671
- const directory = path46.join(zelariDirectory, "memory");
48975
+ const zelariDirectory = path48.join(resolved, ".zelari");
48976
+ const directory = path48.join(zelariDirectory, "memory");
48672
48977
  for (const candidate of [zelariDirectory, directory]) {
48673
48978
  let stat2;
48674
48979
  try {
@@ -48687,12 +48992,12 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
48687
48992
  }
48688
48993
  }
48689
48994
  const canonicalDirectory = await fs23.realpath(directory);
48690
- const relativeDirectory = path46.relative(resolved, canonicalDirectory);
48691
- if (relativeDirectory.startsWith("..") || path46.isAbsolute(relativeDirectory)) {
48995
+ const relativeDirectory = path48.relative(resolved, canonicalDirectory);
48996
+ if (relativeDirectory.startsWith("..") || path48.isAbsolute(relativeDirectory)) {
48692
48997
  throw new Error("SQLite memory directory resolves outside the active project.");
48693
48998
  }
48694
48999
  this.projectRoot = resolved;
48695
- this.databasePath = path46.join(canonicalDirectory, filename);
49000
+ this.databasePath = path48.join(canonicalDirectory, filename);
48696
49001
  const opened = await this.rpc.open({
48697
49002
  dbPath: this.databasePath,
48698
49003
  schemaSql: SQLITE_MEMORY_BASE_SCHEMA,
@@ -49225,7 +49530,7 @@ __export(serviceFactory_exports, {
49225
49530
  });
49226
49531
  import { createHash as createHash18 } from "node:crypto";
49227
49532
  import { promises as fs24 } from "node:fs";
49228
- import * as path47 from "node:path";
49533
+ import * as path49 from "node:path";
49229
49534
  function isMemoryV2Enabled(env = process.env) {
49230
49535
  if (env.ZELARI_MEMORY === "0") return false;
49231
49536
  if (env.ZELARI_MEMORY_BACKEND === "file" || env.ZELARI_MEMORY_BACKEND === "jsonl") return false;
@@ -49246,7 +49551,7 @@ async function canonicalProjectId(projectRoot) {
49246
49551
  try {
49247
49552
  canonical = await fs24.realpath(projectRoot);
49248
49553
  } catch {
49249
- canonical = path47.resolve(projectRoot);
49554
+ canonical = path49.resolve(projectRoot);
49250
49555
  }
49251
49556
  canonical = canonical.replace(/\\/g, "/").replace(/\/$/, "");
49252
49557
  if (process.platform === "win32") canonical = canonical.toLocaleLowerCase("en-US");
@@ -49314,14 +49619,14 @@ var init_serviceFactory = __esm({
49314
49619
  });
49315
49620
 
49316
49621
  // src/cli/workspace/projectInstructions.ts
49317
- import { existsSync as existsSync28, readFileSync as readFileSync23 } from "node:fs";
49622
+ import { existsSync as existsSync28, readFileSync as readFileSync24 } from "node:fs";
49318
49623
  import { join as join26 } from "node:path";
49319
49624
  function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
49320
49625
  for (const name of CANDIDATES) {
49321
49626
  const full = join26(projectRoot, name);
49322
49627
  if (!existsSync28(full)) continue;
49323
49628
  try {
49324
- let raw = readFileSync23(full, "utf8");
49629
+ let raw = readFileSync24(full, "utf8");
49325
49630
  raw = raw.replace(/\r\n/g, "\n").trim();
49326
49631
  if (!raw) continue;
49327
49632
  if (raw.length <= maxChars) {
@@ -49365,7 +49670,7 @@ __export(workspaceSummary_exports, {
49365
49670
  buildWorkspaceSummary: () => buildWorkspaceSummary,
49366
49671
  buildZelariReadHint: () => buildZelariReadHint
49367
49672
  });
49368
- import { existsSync as existsSync29, readFileSync as readFileSync24, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
49673
+ import { existsSync as existsSync29, readFileSync as readFileSync25, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
49369
49674
  import { join as join27, relative as relative2 } from "node:path";
49370
49675
  function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
49371
49676
  const { maxEntries = 30, maxChars = 3500, maxDeps = 24, maxScripts = 16 } = options;
@@ -49403,7 +49708,7 @@ function buildPlanSummary(projectRoot = process.cwd(), options) {
49403
49708
  if (!existsSync29(planPath)) return null;
49404
49709
  let plan;
49405
49710
  try {
49406
- plan = JSON.parse(readFileSync24(planPath, "utf8"));
49711
+ plan = JSON.parse(readFileSync25(planPath, "utf8"));
49407
49712
  } catch {
49408
49713
  return null;
49409
49714
  }
@@ -49561,7 +49866,7 @@ function readPackageJson(projectRoot) {
49561
49866
  const p3 = join27(projectRoot, "package.json");
49562
49867
  if (!existsSync29(p3)) return null;
49563
49868
  try {
49564
- return JSON.parse(readFileSync24(p3, "utf8"));
49869
+ return JSON.parse(readFileSync25(p3, "utf8"));
49565
49870
  } catch {
49566
49871
  return null;
49567
49872
  }
@@ -49689,7 +49994,7 @@ var composeContext_exports = {};
49689
49994
  __export(composeContext_exports, {
49690
49995
  composeProjectContext: () => composeProjectContext
49691
49996
  });
49692
- import { existsSync as existsSync31, readdirSync as readdirSync7, readFileSync as readFileSync25 } from "node:fs";
49997
+ import { existsSync as existsSync31, readdirSync as readdirSync7, readFileSync as readFileSync26 } from "node:fs";
49693
49998
  import { join as join29 } from "node:path";
49694
49999
  function cap2(text, max, label) {
49695
50000
  if (!text || text.length <= max) return { text: text || "", truncated: false };
@@ -49830,15 +50135,15 @@ function readDurableHeadSync(projectRoot) {
49830
50135
  try {
49831
50136
  const headPath = join29(projectRoot, ".zelari", "state", "HEAD.json");
49832
50137
  if (!existsSync31(headPath)) return "";
49833
- const head = JSON.parse(readFileSync25(headPath, "utf8"));
50138
+ const head = JSON.parse(readFileSync26(headPath, "utf8"));
49834
50139
  if (!head?.id) return "";
49835
50140
  const metaPath = join29(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
49836
50141
  if (!existsSync31(metaPath)) return "";
49837
- const meta3 = JSON.parse(readFileSync25(metaPath, "utf8"));
50142
+ const meta3 = JSON.parse(readFileSync26(metaPath, "utf8"));
49838
50143
  const discPath = meta3.artifactDir ? join29(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join29(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
49839
50144
  let discoveries = [];
49840
50145
  if (existsSync31(discPath)) {
49841
- discoveries = JSON.parse(readFileSync25(discPath, "utf8"));
50146
+ discoveries = JSON.parse(readFileSync26(discPath, "utf8"));
49842
50147
  }
49843
50148
  const reusable = discoveries.filter((d) => d.reusable !== false);
49844
50149
  const lines = [
@@ -49871,13 +50176,13 @@ var planDetect_exports = {};
49871
50176
  __export(planDetect_exports, {
49872
50177
  hasWorkspacePlan: () => hasWorkspacePlan
49873
50178
  });
49874
- import { existsSync as existsSync32, readFileSync as readFileSync26 } from "node:fs";
50179
+ import { existsSync as existsSync32, readFileSync as readFileSync27 } from "node:fs";
49875
50180
  import { join as join30 } from "node:path";
49876
50181
  function hasWorkspacePlan(projectRoot = process.cwd()) {
49877
50182
  const planPath = join30(resolveWorkspaceRoot(projectRoot), "plan.json");
49878
50183
  if (!existsSync32(planPath)) return false;
49879
50184
  try {
49880
- const parsed = JSON.parse(readFileSync26(planPath, "utf8"));
50185
+ const parsed = JSON.parse(readFileSync27(planPath, "utf8"));
49881
50186
  return Array.isArray(parsed.phases) && parsed.phases.length > 0;
49882
50187
  } catch {
49883
50188
  return false;
@@ -49939,7 +50244,7 @@ import {
49939
50244
  existsSync as existsSync33,
49940
50245
  readdirSync as readdirSync8,
49941
50246
  writeFileSync as writeFileSync17,
49942
- readFileSync as readFileSync27,
50247
+ readFileSync as readFileSync28,
49943
50248
  mkdirSync as mkdirSync15,
49944
50249
  renameSync as renameSync4
49945
50250
  } from "node:fs";
@@ -49960,7 +50265,7 @@ function readPlan(ctx) {
49960
50265
  if (existsSync33(jsonPath)) {
49961
50266
  try {
49962
50267
  const parsed = JSON.parse(
49963
- readFileSync27(jsonPath, "utf8")
50268
+ readFileSync28(jsonPath, "utf8")
49964
50269
  );
49965
50270
  const { phases, tasks, milestones, ...root } = parsed;
49966
50271
  return {
@@ -49972,8 +50277,8 @@ function readPlan(ctx) {
49972
50277
  } catch {
49973
50278
  }
49974
50279
  }
49975
- const path72 = workspaceFile(ctx.rootDir, "plan");
49976
- const doc = ctx.storage.readIfExists(path72);
50280
+ const path74 = workspaceFile(ctx.rootDir, "plan");
50281
+ const doc = ctx.storage.readIfExists(path74);
49977
50282
  if (!doc) return { phases: [], tasks: [], milestones: [] };
49978
50283
  const meta3 = doc.meta;
49979
50284
  return {
@@ -50151,7 +50456,7 @@ function addMilestoneRecord(ctx, summary, input) {
50151
50456
  dueDate: input.dueDate,
50152
50457
  targetVersion: version2
50153
50458
  });
50154
- const path72 = join31(ctx.rootDir, "milestones", `${id3}.md`);
50459
+ const path74 = join31(ctx.rootDir, "milestones", `${id3}.md`);
50155
50460
  const meta3 = {
50156
50461
  kind: "milestone",
50157
50462
  id: id3,
@@ -50168,7 +50473,7 @@ function addMilestoneRecord(ctx, summary, input) {
50168
50473
  `Target version: ${version2}`,
50169
50474
  ""
50170
50475
  ].join("\n");
50171
- ctx.storage.write(path72, meta3, body);
50476
+ ctx.storage.write(path74, meta3, body);
50172
50477
  return { id: id3, created: true };
50173
50478
  }
50174
50479
  function readPlanSummary(ctx) {
@@ -50372,7 +50677,7 @@ function addIdeaStub(ctx) {
50372
50677
  const tags = args["tags"] ?? [];
50373
50678
  const category = args["category"] ?? "General";
50374
50679
  const id3 = `${nextAdrId(ctx)}-${slugify3(title)}`;
50375
- const path72 = workspaceArtifact(ctx.rootDir, "decisions", id3);
50680
+ const path74 = workspaceArtifact(ctx.rootDir, "decisions", id3);
50376
50681
  const meta3 = {
50377
50682
  kind: "adr",
50378
50683
  status: "proposed",
@@ -50398,7 +50703,7 @@ function addIdeaStub(ctx) {
50398
50703
  ...consequences.map((c) => `- ${c}`),
50399
50704
  ""
50400
50705
  ].join("\n");
50401
- ctx.storage.write(path72, meta3, body);
50706
+ ctx.storage.write(path74, meta3, body);
50402
50707
  return `ADR ${id3} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
50403
50708
  });
50404
50709
  }
@@ -50480,14 +50785,14 @@ function createDocumentStub(ctx) {
50480
50785
  ctx.storage.write(risksPath, riskMeta, content);
50481
50786
  return `Document "${title}" created at risks.md (workspace root).`;
50482
50787
  }
50483
- const path72 = workspaceArtifact(ctx.rootDir, "docs", slug);
50788
+ const path74 = workspaceArtifact(ctx.rootDir, "docs", slug);
50484
50789
  const meta3 = {
50485
50790
  kind: "doc",
50486
50791
  id: slug,
50487
50792
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
50488
50793
  tags
50489
50794
  };
50490
- ctx.storage.write(path72, meta3, content);
50795
+ ctx.storage.write(path74, meta3, content);
50491
50796
  return `Document "${title}" created at docs/${slug}.md.`;
50492
50797
  });
50493
50798
  }
@@ -50524,7 +50829,7 @@ function searchDocumentsStub(ctx) {
50524
50829
  const results = [];
50525
50830
  for (const file2 of files) {
50526
50831
  if (!existsSync33(file2)) continue;
50527
- const raw = readFileSync27(file2, "utf8");
50832
+ const raw = readFileSync28(file2, "utf8");
50528
50833
  const content = raw.toLowerCase();
50529
50834
  let idx = -1;
50530
50835
  let matchLen = 0;
@@ -50863,21 +51168,21 @@ var init_mcpClient = __esm({
50863
51168
  import {
50864
51169
  existsSync as existsSync34,
50865
51170
  mkdirSync as mkdirSync16,
50866
- readFileSync as readFileSync28,
51171
+ readFileSync as readFileSync29,
50867
51172
  writeFileSync as writeFileSync18
50868
51173
  } from "node:fs";
50869
51174
  import { dirname as dirname9, join as join32 } from "node:path";
50870
- import { homedir as homedir10 } from "node:os";
51175
+ import { homedir as homedir11 } from "node:os";
50871
51176
  function getUserMcpPath() {
50872
- return join32(homedir10(), ".zelari-code", "mcp.json");
51177
+ return join32(homedir11(), ".zelari-code", "mcp.json");
50873
51178
  }
50874
51179
  function getProjectMcpPath(projectRoot) {
50875
51180
  return join32(projectRoot, ".zelari", "mcp.json");
50876
51181
  }
50877
- function readFile4(path72) {
50878
- if (!existsSync34(path72)) return {};
51182
+ function readFile4(path74) {
51183
+ if (!existsSync34(path74)) return {};
50879
51184
  try {
50880
- const parsed = JSON.parse(readFileSync28(path72, "utf8"));
51185
+ const parsed = JSON.parse(readFileSync29(path74, "utf8"));
50881
51186
  const out = {};
50882
51187
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
50883
51188
  if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
@@ -50893,10 +51198,10 @@ function readFile4(path72) {
50893
51198
  return {};
50894
51199
  }
50895
51200
  }
50896
- function writeFile2(path72, servers) {
50897
- mkdirSync16(dirname9(path72), { recursive: true });
51201
+ function writeFile3(path74, servers) {
51202
+ mkdirSync16(dirname9(path74), { recursive: true });
50898
51203
  const body = { mcpServers: servers };
50899
- writeFileSync18(path72, `${JSON.stringify(body, null, 2)}
51204
+ writeFileSync18(path74, `${JSON.stringify(body, null, 2)}
50900
51205
  `, "utf8");
50901
51206
  }
50902
51207
  function listMcpServers(projectRoot) {
@@ -50929,9 +51234,9 @@ function upsertMcpServer(opts) {
50929
51234
  if (!opts.config.command?.trim()) {
50930
51235
  return { ok: false, error: "command is required" };
50931
51236
  }
50932
- let path72;
51237
+ let path74;
50933
51238
  if (opts.scope === "user") {
50934
- path72 = getUserMcpPath();
51239
+ path74 = getUserMcpPath();
50935
51240
  } else {
50936
51241
  const root = opts.projectRoot?.trim();
50937
51242
  if (!root) {
@@ -50940,30 +51245,30 @@ function upsertMcpServer(opts) {
50940
51245
  error: "projectRoot required for project scope (Open Folder first)"
50941
51246
  };
50942
51247
  }
50943
- path72 = getProjectMcpPath(root);
51248
+ path74 = getProjectMcpPath(root);
50944
51249
  }
50945
- const current = readFile4(path72);
51250
+ const current = readFile4(path74);
50946
51251
  current[name] = {
50947
51252
  command: opts.config.command.trim(),
50948
51253
  args: opts.config.args,
50949
51254
  env: opts.config.env,
50950
51255
  enabled: opts.config.enabled !== false
50951
51256
  };
50952
- writeFile2(path72, current);
50953
- return { ok: true, path: path72 };
51257
+ writeFile3(path74, current);
51258
+ return { ok: true, path: path74 };
50954
51259
  }
50955
51260
  function removeMcpServer(opts) {
50956
- const path72 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
50957
- if (!path72) {
51261
+ const path74 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
51262
+ if (!path74) {
50958
51263
  return { ok: false, error: "projectRoot required for project scope" };
50959
51264
  }
50960
- const current = readFile4(path72);
51265
+ const current = readFile4(path74);
50961
51266
  if (!(opts.name in current)) {
50962
- return { ok: false, error: `Server "${opts.name}" not found in ${path72}` };
51267
+ return { ok: false, error: `Server "${opts.name}" not found in ${path74}` };
50963
51268
  }
50964
51269
  delete current[opts.name];
50965
- writeFile2(path72, current);
50966
- return { ok: true, path: path72 };
51270
+ writeFile3(path74, current);
51271
+ return { ok: true, path: path74 };
50967
51272
  }
50968
51273
  var init_mcpConfigIo = __esm({
50969
51274
  "src/cli/mcp/mcpConfigIo.ts"() {
@@ -51105,14 +51410,14 @@ __export(mcpManager_exports, {
51105
51410
  readMcpConfig: () => readMcpConfig,
51106
51411
  registerMcpTools: () => registerMcpTools
51107
51412
  });
51108
- import { existsSync as existsSync35, readFileSync as readFileSync29 } from "node:fs";
51413
+ import { existsSync as existsSync35, readFileSync as readFileSync30 } from "node:fs";
51109
51414
  import { join as join33 } from "node:path";
51110
- import { homedir as homedir11 } from "node:os";
51415
+ import { homedir as homedir12 } from "node:os";
51111
51416
  function readMcpConfig(projectRoot = process.cwd(), opts) {
51112
51417
  const merged = {};
51113
51418
  const paths = [];
51114
51419
  if (process.env["ZELARI_MCP_USER"] !== "0") {
51115
- paths.push(join33(homedir11(), ".zelari-code", "mcp.json"));
51420
+ paths.push(join33(homedir12(), ".zelari-code", "mcp.json"));
51116
51421
  }
51117
51422
  if (!opts?.skipProjectMcp) {
51118
51423
  paths.push(join33(projectRoot, ".zelari", "mcp.json"));
@@ -51120,7 +51425,7 @@ function readMcpConfig(projectRoot = process.cwd(), opts) {
51120
51425
  for (const p3 of paths) {
51121
51426
  if (!existsSync35(p3)) continue;
51122
51427
  try {
51123
- const parsed = JSON.parse(readFileSync29(p3, "utf8"));
51428
+ const parsed = JSON.parse(readFileSync30(p3, "utf8"));
51124
51429
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
51125
51430
  if (!cfg || typeof cfg.command !== "string" || cfg.command.length === 0) continue;
51126
51431
  merged[name] = cfg;
@@ -51359,15 +51664,15 @@ __export(agentsMd_exports, {
51359
51664
  serializeAgentsMd: () => serializeAgentsMd,
51360
51665
  updateAgentsMd: () => updateAgentsMd
51361
51666
  });
51362
- import { existsSync as existsSync36, readFileSync as readFileSync30, writeFileSync as writeFileSync19 } from "node:fs";
51667
+ import { existsSync as existsSync36, readFileSync as readFileSync31, writeFileSync as writeFileSync19 } from "node:fs";
51363
51668
  import { createHash as createHash19 } from "node:crypto";
51364
51669
  import { join as join34 } from "node:path";
51365
51670
  import { readFile as readFile5 } from "node:fs/promises";
51366
51671
  async function readPackageJson2(projectRoot) {
51367
- const path72 = join34(projectRoot, "package.json");
51368
- if (!existsSync36(path72)) return null;
51672
+ const path74 = join34(projectRoot, "package.json");
51673
+ if (!existsSync36(path74)) return null;
51369
51674
  try {
51370
- return JSON.parse(await readFile5(path72, "utf8"));
51675
+ return JSON.parse(await readFile5(path74, "utf8"));
51371
51676
  } catch {
51372
51677
  return null;
51373
51678
  }
@@ -51417,7 +51722,7 @@ async function genConventions(ctx) {
51417
51722
  const lines = [];
51418
51723
  const claudeMd = join34(ctx.projectRoot, "CLAUDE.MD");
51419
51724
  if (existsSync36(claudeMd)) {
51420
- const content = readFileSync30(claudeMd, "utf8");
51725
+ const content = readFileSync31(claudeMd, "utf8");
51421
51726
  const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
51422
51727
  if (match) {
51423
51728
  lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
@@ -51449,9 +51754,9 @@ async function genBuild(ctx) {
51449
51754
  ].join("\n");
51450
51755
  }
51451
51756
  async function genOpenQuestions(ctx) {
51452
- const path72 = join34(ctx.rootDir, "risks.md");
51453
- if (!existsSync36(path72)) return "_No open questions._";
51454
- const content = readFileSync30(path72, "utf8");
51757
+ const path74 = join34(ctx.rootDir, "risks.md");
51758
+ if (!existsSync36(path74)) return "_No open questions._";
51759
+ const content = readFileSync31(path74, "utf8");
51455
51760
  const lines = content.split("\n");
51456
51761
  const questions = [];
51457
51762
  let currentTitle = "";
@@ -51527,7 +51832,7 @@ function titleCase(id3) {
51527
51832
  async function updateAgentsMd(ctx, projectRoot) {
51528
51833
  const agentsPath = join34(projectRoot, "AGENTS.MD");
51529
51834
  if (existsSync36(agentsPath)) {
51530
- const content = readFileSync30(agentsPath, "utf8");
51835
+ const content = readFileSync31(agentsPath, "utf8");
51531
51836
  const hasAnyMarker = AUTO_SECTIONS.some((id3) => content.includes(MARKER_OPEN(id3)));
51532
51837
  if (!hasAnyMarker) {
51533
51838
  return {
@@ -51543,7 +51848,7 @@ async function updateAgentsMd(ctx, projectRoot) {
51543
51848
  }
51544
51849
  let manualContent = "";
51545
51850
  if (existsSync36(agentsPath)) {
51546
- const { manualBlocks } = parseAgentsMd(readFileSync30(agentsPath, "utf8"));
51851
+ const { manualBlocks } = parseAgentsMd(readFileSync31(agentsPath, "utf8"));
51547
51852
  manualContent = manualBlocks.after;
51548
51853
  } else {
51549
51854
  const projectName2 = projectName(projectRoot);
@@ -51559,7 +51864,7 @@ async function updateAgentsMd(ctx, projectRoot) {
51559
51864
  ""
51560
51865
  ].join("\n");
51561
51866
  }
51562
- const oldContent = existsSync36(agentsPath) ? readFileSync30(agentsPath, "utf8") : "";
51867
+ const oldContent = existsSync36(agentsPath) ? readFileSync31(agentsPath, "utf8") : "";
51563
51868
  const { sections: oldSections } = parseAgentsMd(oldContent);
51564
51869
  const changedSections = [];
51565
51870
  for (const id3 of AUTO_SECTIONS) {
@@ -51696,7 +52001,7 @@ var init_completeDesign = __esm({
51696
52001
  });
51697
52002
 
51698
52003
  // src/cli/workspace/planDriftCheck.ts
51699
- import { existsSync as existsSync37, readFileSync as readFileSync31, readdirSync as readdirSync9, statSync as statSync6, writeFileSync as writeFileSync20 } from "node:fs";
52004
+ import { existsSync as existsSync37, readFileSync as readFileSync32, readdirSync as readdirSync9, statSync as statSync6, writeFileSync as writeFileSync20 } from "node:fs";
51700
52005
  import { join as join35 } from "node:path";
51701
52006
  function findCanonicalDoc(rootDir) {
51702
52007
  const docsDir = join35(rootDir, "docs");
@@ -51725,9 +52030,9 @@ function versionKey(value) {
51725
52030
  function firstString2(v) {
51726
52031
  return typeof v === "string" && v.trim().length > 0 ? v : null;
51727
52032
  }
51728
- function readFileSyncSafe(path72) {
52033
+ function readFileSyncSafe(path74) {
51729
52034
  try {
51730
- return readFileSync31(path72, "utf8");
52035
+ return readFileSync32(path74, "utf8");
51731
52036
  } catch {
51732
52037
  return null;
51733
52038
  }
@@ -51742,7 +52047,7 @@ async function runPlanDriftCheck(rootDir) {
51742
52047
  }
51743
52048
  let plan;
51744
52049
  try {
51745
- plan = JSON.parse(readFileSync31(planPath, "utf8"));
52050
+ plan = JSON.parse(readFileSync32(planPath, "utf8"));
51746
52051
  } catch {
51747
52052
  return { ran: false, reason: ".zelari/plan.json corrupt" };
51748
52053
  }
@@ -51868,7 +52173,7 @@ var init_planDriftCheck = __esm({
51868
52173
 
51869
52174
  // src/cli/workspace/projectSmoke.ts
51870
52175
  import { spawn as spawn14 } from "node:child_process";
51871
- import { existsSync as existsSync38, readFileSync as readFileSync32 } from "node:fs";
52176
+ import { existsSync as existsSync38, readFileSync as readFileSync33 } from "node:fs";
51872
52177
  import { join as join36 } from "node:path";
51873
52178
  function pickSmokeScript(scripts) {
51874
52179
  if (!scripts) return null;
@@ -51887,7 +52192,7 @@ async function runProjectSmoke(projectRoot, timeoutMs2 = DEFAULT_TIMEOUT_MS3) {
51887
52192
  }
51888
52193
  let scripts = {};
51889
52194
  try {
51890
- const pkg = JSON.parse(readFileSync32(pkgPath, "utf8"));
52195
+ const pkg = JSON.parse(readFileSync33(pkgPath, "utf8"));
51891
52196
  scripts = pkg.scripts ?? {};
51892
52197
  } catch {
51893
52198
  return { ran: false, reason: "package.json unreadable (skipped)" };
@@ -51975,7 +52280,7 @@ __export(postCouncilHook_exports, {
51975
52280
  runPostCouncilHook: () => runPostCouncilHook
51976
52281
  });
51977
52282
  import { spawn as spawn15 } from "node:child_process";
51978
- import { existsSync as existsSync39, readFileSync as readFileSync33 } from "node:fs";
52283
+ import { existsSync as existsSync39, readFileSync as readFileSync34 } from "node:fs";
51979
52284
  import { join as join37 } from "node:path";
51980
52285
  async function runCompleteDesignPostProcessor(ctx, options) {
51981
52286
  if (options?.runMode === "implementation") {
@@ -51997,7 +52302,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
51997
52302
  }
51998
52303
  let phaseCount = 0;
51999
52304
  try {
52000
- const parsed = JSON.parse(readFileSync33(planJsonPath2, "utf8"));
52305
+ const parsed = JSON.parse(readFileSync34(planJsonPath2, "utf8"));
52001
52306
  phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
52002
52307
  } catch {
52003
52308
  return { ran: false, reason: ".zelari/plan.json corrupt" };
@@ -52190,8 +52495,8 @@ async function runPostCouncilHook(ctx, options) {
52190
52495
  sources: scope.sources
52191
52496
  } : void 0
52192
52497
  });
52193
- const path72 = writeCouncilCompletion(ctx.rootDir, completion);
52194
- completionHook = { ran: true, path: path72, completion };
52498
+ const path74 = writeCouncilCompletion(ctx.rootDir, completion);
52499
+ completionHook = { ran: true, path: path74, completion };
52195
52500
  } catch (err) {
52196
52501
  completionHook = {
52197
52502
  ran: true,
@@ -52232,11 +52537,11 @@ __export(councilFeedback_exports, {
52232
52537
  import {
52233
52538
  promises as fs25,
52234
52539
  existsSync as existsSync40,
52235
- readFileSync as readFileSync34,
52540
+ readFileSync as readFileSync35,
52236
52541
  writeFileSync as writeFileSync21,
52237
52542
  mkdirSync as mkdirSync17
52238
52543
  } from "node:fs";
52239
- import path48 from "node:path";
52544
+ import path50 from "node:path";
52240
52545
  import os10 from "node:os";
52241
52546
  var FeedbackStore;
52242
52547
  var init_councilFeedback = __esm({
@@ -52247,7 +52552,7 @@ var init_councilFeedback = __esm({
52247
52552
  now;
52248
52553
  entries = [];
52249
52554
  constructor(options = {}) {
52250
- this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path48.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
52555
+ this.file = options.file ?? (process.env.ANATHEMA_COUNCIL_FEEDBACK_FILE ?? path50.join(os10.homedir(), ".tmp", "zelari-code", "council-feedback.json"));
52251
52556
  this.now = options.now ?? Date.now;
52252
52557
  this.load();
52253
52558
  }
@@ -52342,7 +52647,7 @@ var init_councilFeedback = __esm({
52342
52647
  load() {
52343
52648
  if (!existsSync40(this.file)) return;
52344
52649
  try {
52345
- const raw = readFileSync34(this.file, "utf-8");
52650
+ const raw = readFileSync35(this.file, "utf-8");
52346
52651
  const parsed = JSON.parse(raw);
52347
52652
  if (parsed && Array.isArray(parsed.entries)) {
52348
52653
  this.entries = parsed.entries.filter(
@@ -52353,7 +52658,7 @@ var init_councilFeedback = __esm({
52353
52658
  }
52354
52659
  }
52355
52660
  save() {
52356
- mkdirSync17(path48.dirname(this.file), { recursive: true });
52661
+ mkdirSync17(path50.dirname(this.file), { recursive: true });
52357
52662
  writeFileSync21(
52358
52663
  this.file,
52359
52664
  JSON.stringify({ entries: this.entries }, null, 2),
@@ -52421,7 +52726,7 @@ import { execFile as execFile3 } from "node:child_process";
52421
52726
  import { promisify as promisify2 } from "node:util";
52422
52727
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
52423
52728
  import { tmpdir as tmpdir2 } from "node:os";
52424
- import path49 from "node:path";
52729
+ import path51 from "node:path";
52425
52730
  import { randomUUID as randomUUID5 } from "node:crypto";
52426
52731
  async function git3(cwd, args, env) {
52427
52732
  const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
@@ -52441,8 +52746,8 @@ async function isGitRepo(cwd) {
52441
52746
  return await gitSafe(cwd, ["rev-parse", "--is-inside-work-tree"]) === "true";
52442
52747
  }
52443
52748
  async function withTempIndex(fn) {
52444
- const dir = mkdtempSync(path49.join(tmpdir2(), "zelari-ckpt-"));
52445
- const indexFile = path49.join(dir, "index");
52749
+ const dir = mkdtempSync(path51.join(tmpdir2(), "zelari-ckpt-"));
52750
+ const indexFile = path51.join(dir, "index");
52446
52751
  try {
52447
52752
  return await fn(indexFile);
52448
52753
  } finally {
@@ -52533,7 +52838,7 @@ async function restoreCheckpoint(cwd, id3) {
52533
52838
  const deleted = [];
52534
52839
  for (const rel2 of added) {
52535
52840
  try {
52536
- rmSync2(path49.join(cwd, rel2), { force: true });
52841
+ rmSync2(path51.join(cwd, rel2), { force: true });
52537
52842
  deleted.push(rel2);
52538
52843
  } catch {
52539
52844
  }
@@ -52634,7 +52939,7 @@ __export(fileBackend_exports, {
52634
52939
  });
52635
52940
  import { randomUUID as randomUUID6 } from "node:crypto";
52636
52941
  import { promises as fs26 } from "node:fs";
52637
- import * as path50 from "node:path";
52942
+ import * as path52 from "node:path";
52638
52943
  function tokenize(text) {
52639
52944
  return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
52640
52945
  }
@@ -52685,8 +52990,8 @@ var init_fileBackend = __esm({
52685
52990
  logPath = "";
52686
52991
  memoryDir = "";
52687
52992
  async init(projectRoot) {
52688
- this.memoryDir = path50.join(projectRoot, ".zelari", "memory");
52689
- this.logPath = path50.join(this.memoryDir, "log.jsonl");
52993
+ this.memoryDir = path52.join(projectRoot, ".zelari", "memory");
52994
+ this.logPath = path52.join(this.memoryDir, "log.jsonl");
52690
52995
  await fs26.mkdir(this.memoryDir, { recursive: true });
52691
52996
  }
52692
52997
  async add(content, metadata2 = {}, graph) {
@@ -52759,12 +53064,12 @@ var init_fileBackend = __esm({
52759
53064
 
52760
53065
  // src/cli/traceStore.ts
52761
53066
  import { promises as fs27 } from "node:fs";
52762
- import * as path51 from "node:path";
53067
+ import * as path53 from "node:path";
52763
53068
  function traceDir(projectRoot) {
52764
- return path51.join(projectRoot, ".zelari", "trace");
53069
+ return path53.join(projectRoot, ".zelari", "trace");
52765
53070
  }
52766
53071
  function tracePath(projectRoot, missionId) {
52767
- return path51.join(traceDir(projectRoot), `${missionId}.json`);
53072
+ return path53.join(traceDir(projectRoot), `${missionId}.json`);
52768
53073
  }
52769
53074
  async function saveTrace(projectRoot, missionId, entries) {
52770
53075
  const dir = traceDir(projectRoot);
@@ -52801,7 +53106,7 @@ __export(zelariMission_exports, {
52801
53106
  });
52802
53107
  import { randomUUID as randomUUID7 } from "node:crypto";
52803
53108
  import { promises as fs28 } from "node:fs";
52804
- import * as path52 from "node:path";
53109
+ import * as path54 from "node:path";
52805
53110
  function resolveMaxIterations(env = process.env) {
52806
53111
  const raw = env.ZELARI_MISSION_MAX_ITER;
52807
53112
  const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
@@ -52844,10 +53149,10 @@ function isMissionAutoStart(env = process.env) {
52844
53149
  return env.ZELARI_MISSION_AUTO === "1";
52845
53150
  }
52846
53151
  async function writeMissionState(projectRoot, state3) {
52847
- const dir = path52.join(projectRoot, ".zelari");
53152
+ const dir = path54.join(projectRoot, ".zelari");
52848
53153
  await fs28.mkdir(dir, { recursive: true });
52849
53154
  await fs28.writeFile(
52850
- path52.join(dir, "mission-state.json"),
53155
+ path54.join(dir, "mission-state.json"),
52851
53156
  JSON.stringify(state3, null, 2) + "\n",
52852
53157
  "utf8"
52853
53158
  );
@@ -53522,7 +53827,7 @@ function safeSocketPath(socketPath) {
53522
53827
  return socketPath.trim();
53523
53828
  }
53524
53829
  function startPermissionBroker(socketPath, handlers, opts) {
53525
- const path72 = safeSocketPath(socketPath);
53830
+ const path74 = safeSocketPath(socketPath);
53526
53831
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
53527
53832
  const sockets = /* @__PURE__ */ new Set();
53528
53833
  const server = createServer2((socket) => {
@@ -53622,10 +53927,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
53622
53927
  return new Promise((resolve7, reject) => {
53623
53928
  const onError = (err) => reject(err);
53624
53929
  server.once("error", onError);
53625
- server.listen(path72, () => {
53930
+ server.listen(path74, () => {
53626
53931
  server.removeListener("error", onError);
53627
53932
  resolve7({
53628
- socketPath: path72,
53933
+ socketPath: path74,
53629
53934
  stop: () => new Promise((res) => {
53630
53935
  for (const s of sockets) s.destroy();
53631
53936
  sockets.clear();
@@ -53636,7 +53941,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
53636
53941
  if (done) return;
53637
53942
  done = true;
53638
53943
  if (process.platform !== "win32") {
53639
- unlink(path72, () => res());
53944
+ unlink(path74, () => res());
53640
53945
  } else {
53641
53946
  res();
53642
53947
  }
@@ -53649,11 +53954,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
53649
53954
  });
53650
53955
  }
53651
53956
  function requestBrokerAsk(socketPath, ask, opts) {
53652
- const path72 = safeSocketPath(socketPath);
53957
+ const path74 = safeSocketPath(socketPath);
53653
53958
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
53654
53959
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
53655
53960
  return new Promise((resolve7, reject) => {
53656
- const socket = connect(path72);
53961
+ const socket = connect(path74);
53657
53962
  let buffer = "";
53658
53963
  let settled = false;
53659
53964
  const settle = (fn) => {
@@ -53668,7 +53973,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
53668
53973
  settle(
53669
53974
  () => reject(
53670
53975
  new Error(
53671
- `permission broker unavailable at "${path72}" (connect timed out after ${connectTimeoutMs}ms)`
53976
+ `permission broker unavailable at "${path74}" (connect timed out after ${connectTimeoutMs}ms)`
53672
53977
  )
53673
53978
  )
53674
53979
  );
@@ -54060,7 +54365,17 @@ function stripTrailingCommas(s) {
54060
54365
  return out;
54061
54366
  }
54062
54367
  async function resolveLlm(opts) {
54063
- const active = opts.provider?.trim() || getProviderConfig().activeProviderId;
54368
+ let active = opts.provider?.trim() || getProviderConfig().activeProviderId;
54369
+ const parent = opts.model?.trim() || getModelForProvider(active) || process.env.ZELARI_MODEL || "";
54370
+ let model = resolveKrakenPlannerModel(parent);
54371
+ const ref = parseQualifiedModelRef(model);
54372
+ if (ref) {
54373
+ const crossKey = await resolveApiKeyWithMeta(ref.provider);
54374
+ if (crossKey?.apiKey && resolveBaseUrl(ref.provider)) {
54375
+ active = ref.provider;
54376
+ }
54377
+ if (active === ref.provider) model = ref.model;
54378
+ }
54064
54379
  const meta3 = await resolveApiKeyWithMeta(active);
54065
54380
  if (!meta3?.apiKey) {
54066
54381
  throw new Error(`No API key for provider '${active}'. Save a key in Settings \u2192 Provider.`);
@@ -54069,8 +54384,6 @@ async function resolveLlm(opts) {
54069
54384
  if (!baseUrl) {
54070
54385
  throw new Error(`No base URL for provider '${active}'. Set a custom endpoint in Settings.`);
54071
54386
  }
54072
- const parent = opts.model?.trim() || getModelForProvider(active) || process.env.ZELARI_MODEL || "";
54073
- const model = resolveKrakenPlannerModel(parent);
54074
54387
  if (!model) {
54075
54388
  throw new Error(`No model selected for provider '${active}'`);
54076
54389
  }
@@ -54403,9 +54716,9 @@ __export(graphMemory_exports, {
54403
54716
  toGraphSnapshot: () => toGraphSnapshot
54404
54717
  });
54405
54718
  import { promises as fs31 } from "node:fs";
54406
- import path56 from "node:path";
54719
+ import path58 from "node:path";
54407
54720
  function snapshotPath(cwd) {
54408
- return path56.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
54721
+ return path58.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
54409
54722
  }
54410
54723
  function toGraphSnapshot(graph, opts) {
54411
54724
  const unresolved = (opts.unresolvedFindings ?? []).map((u) => ({
@@ -54432,7 +54745,7 @@ async function saveGraphSnapshot(cwd, snapshot) {
54432
54745
  try {
54433
54746
  await fs31.access(cwd);
54434
54747
  const file2 = snapshotPath(cwd);
54435
- await fs31.mkdir(path56.dirname(file2), { recursive: true });
54748
+ await fs31.mkdir(path58.dirname(file2), { recursive: true });
54436
54749
  await fs31.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
54437
54750
  } catch {
54438
54751
  }
@@ -54499,7 +54812,7 @@ var SNAPSHOT_DIR, SNAPSHOT_FILE, MAX_SNAPSHOT_FINDINGS_CHARS;
54499
54812
  var init_graphMemory = __esm({
54500
54813
  "src/cli/kraken/graphMemory.ts"() {
54501
54814
  "use strict";
54502
- SNAPSHOT_DIR = path56.join(".zelari", "kraken");
54815
+ SNAPSHOT_DIR = path58.join(".zelari", "kraken");
54503
54816
  SNAPSHOT_FILE = "last-graph.json";
54504
54817
  MAX_SNAPSHOT_FINDINGS_CHARS = 400;
54505
54818
  }
@@ -54515,14 +54828,14 @@ var init_tentacle = __esm({
54515
54828
 
54516
54829
  // src/cli/kraken/workbench.ts
54517
54830
  import { promises as fs32 } from "node:fs";
54518
- import path57 from "node:path";
54831
+ import path59 from "node:path";
54519
54832
  function isWorkbenchEnabled(env = process.env) {
54520
54833
  const v = (env.ZELARI_KRAKEN_WORKBENCH ?? "1").trim().toLowerCase();
54521
54834
  if (v === "0" || v === "false" || v === "no" || v === "off") return false;
54522
54835
  return true;
54523
54836
  }
54524
54837
  function workbenchPath(cwd, graphId) {
54525
- return path57.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
54838
+ return path59.join(cwd, ".zelari", "radio", `workbench-${graphId}.md`);
54526
54839
  }
54527
54840
  function countByStatus2(nodes) {
54528
54841
  const out = { pending: 0, running: 0, done: 0, error: 0, skipped: 0 };
@@ -54685,7 +54998,7 @@ var init_workbench = __esm({
54685
54998
  if (!this.enabled) return null;
54686
54999
  if (!this.dirty && this.lastWrite) return this.lastWrite;
54687
55000
  const out = workbenchPath(this.cwd, this.graphId);
54688
- await fs32.mkdir(path57.dirname(out), { recursive: true });
55001
+ await fs32.mkdir(path59.dirname(out), { recursive: true });
54689
55002
  const body = this.render();
54690
55003
  const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
54691
55004
  await fs32.writeFile(tmp, body, "utf8");
@@ -54882,7 +55195,7 @@ __export(executor_exports, {
54882
55195
  thoroughnessForKind: () => thoroughnessForKind
54883
55196
  });
54884
55197
  import { existsSync as existsSync41 } from "node:fs";
54885
- import path58 from "node:path";
55198
+ import path60 from "node:path";
54886
55199
  function resolveMaxParallel(env = process.env) {
54887
55200
  const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
54888
55201
  if (raw === void 0 || raw === "") return DEFAULT_MAX_PARALLEL;
@@ -54940,7 +55253,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
54940
55253
  }
54941
55254
  function defaultChecksExists(cwd) {
54942
55255
  try {
54943
- return existsSync41(path58.join(cwd, ".zelari", "world", "checks.json"));
55256
+ return existsSync41(path60.join(cwd, ".zelari", "world", "checks.json"));
54944
55257
  } catch {
54945
55258
  return false;
54946
55259
  }
@@ -56336,17 +56649,17 @@ var init_prereqChecks = __esm({
56336
56649
  });
56337
56650
 
56338
56651
  // src/cli/plugins/prefs.ts
56339
- import { existsSync as existsSync43, readFileSync as readFileSync35, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
56340
- import path61 from "node:path";
56652
+ import { existsSync as existsSync43, readFileSync as readFileSync36, writeFileSync as writeFileSync22, mkdirSync as mkdirSync18 } from "node:fs";
56653
+ import path63 from "node:path";
56341
56654
  import os11 from "node:os";
56342
56655
  function getPluginPrefsPath() {
56343
- return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path61.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
56656
+ return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path63.join(os11.homedir(), ".tmp", "zelari-code", "plugins.json");
56344
56657
  }
56345
56658
  function getPluginPrefs() {
56346
56659
  const file2 = getPluginPrefsPath();
56347
56660
  try {
56348
56661
  if (!existsSync43(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
56349
- const raw = readFileSync35(file2, "utf-8");
56662
+ const raw = readFileSync36(file2, "utf-8");
56350
56663
  const parsed = JSON.parse(raw);
56351
56664
  if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
56352
56665
  const clean = {};
@@ -56361,7 +56674,7 @@ function getPluginPrefs() {
56361
56674
  }
56362
56675
  function writePluginPrefs(prefs) {
56363
56676
  const file2 = getPluginPrefsPath();
56364
- mkdirSync18(path61.dirname(file2), { recursive: true });
56677
+ mkdirSync18(path63.dirname(file2), { recursive: true });
56365
56678
  writeFileSync22(file2, JSON.stringify(prefs, null, 2), {
56366
56679
  encoding: "utf-8",
56367
56680
  mode: 384
@@ -56398,7 +56711,7 @@ __export(registry_exports, {
56398
56711
  isBinaryOnPath: () => isBinaryOnPath
56399
56712
  });
56400
56713
  import { existsSync as existsSync44 } from "node:fs";
56401
- import path62 from "node:path";
56714
+ import path64 from "node:path";
56402
56715
  function detectLocalBin(bin) {
56403
56716
  return (cwd) => {
56404
56717
  try {
@@ -56416,7 +56729,7 @@ function isBinaryOnPath(bin, opts = {}) {
56416
56729
  const platform = opts.platform ?? process.platform;
56417
56730
  const exists = opts.exists ?? existsSync44;
56418
56731
  const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
56419
- const pathMod = platform === "win32" ? path62.win32 : path62.posix;
56732
+ const pathMod = platform === "win32" ? path64.win32 : path64.posix;
56420
56733
  const sep4 = platform === "win32" ? ";" : ":";
56421
56734
  const dirs = pathEnv.split(sep4).filter((d) => d.length > 0);
56422
56735
  const candidates = [bin];
@@ -57148,7 +57461,7 @@ __export(atMentions_exports, {
57148
57461
  extractAtMentions: () => extractAtMentions,
57149
57462
  hasAtMentions: () => hasAtMentions
57150
57463
  });
57151
- import { existsSync as existsSync47, readFileSync as readFileSync37, statSync as statSync9 } from "node:fs";
57464
+ import { existsSync as existsSync47, readFileSync as readFileSync38, statSync as statSync9 } from "node:fs";
57152
57465
  import { basename as basename4, isAbsolute as isAbsolute4, relative as relative5, resolve as resolve5, sep as sep2 } from "node:path";
57153
57466
  function isImagePath(abs) {
57154
57467
  const ext = abs.split(".").pop()?.toLowerCase() ?? "";
@@ -57254,7 +57567,7 @@ function resolveMention(token, cwd) {
57254
57567
  note: `image too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
57255
57568
  };
57256
57569
  }
57257
- const dataBase64 = readFileSync37(abs).toString("base64");
57570
+ const dataBase64 = readFileSync38(abs).toString("base64");
57258
57571
  return {
57259
57572
  raw: token,
57260
57573
  path: rel2,
@@ -57265,7 +57578,7 @@ function resolveMention(token, cwd) {
57265
57578
  };
57266
57579
  }
57267
57580
  try {
57268
- const buf = readFileSync37(abs);
57581
+ const buf = readFileSync38(abs);
57269
57582
  const head = buf.subarray(0, 800).toString("utf8");
57270
57583
  if (!isProbablyText(abs, head)) {
57271
57584
  return {
@@ -58250,9 +58563,9 @@ __export(triggerLock_exports, {
58250
58563
  releaseLock: () => releaseLock
58251
58564
  });
58252
58565
  import { promises as fs40 } from "node:fs";
58253
- import * as path67 from "node:path";
58566
+ import * as path69 from "node:path";
58254
58567
  function lockPath(projectRoot) {
58255
- return path67.join(projectRoot, ".zelari", "trigger.lock");
58568
+ return path69.join(projectRoot, ".zelari", "trigger.lock");
58256
58569
  }
58257
58570
  function isPidAlive(pid) {
58258
58571
  try {
@@ -58265,7 +58578,7 @@ function isPidAlive(pid) {
58265
58578
  }
58266
58579
  async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
58267
58580
  const lp = lockPath(projectRoot);
58268
- const dir = path67.dirname(lp);
58581
+ const dir = path69.dirname(lp);
58269
58582
  await fs40.mkdir(dir, { recursive: true });
58270
58583
  try {
58271
58584
  const raw = await fs40.readFile(lp, "utf8");
@@ -58693,12 +59006,12 @@ import {
58693
59006
  existsSync as existsSync49,
58694
59007
  mkdirSync as mkdirSync21,
58695
59008
  readdirSync as readdirSync10,
58696
- readFileSync as readFileSync38,
59009
+ readFileSync as readFileSync39,
58697
59010
  rmSync as rmSync4,
58698
59011
  writeFileSync as writeFileSync24
58699
59012
  } from "node:fs";
58700
59013
  import { dirname as dirname13, join as join45 } from "node:path";
58701
- import { homedir as homedir12 } from "node:os";
59014
+ import { homedir as homedir13 } from "node:os";
58702
59015
  function ensureBuiltinSkillsLoadedSync() {
58703
59016
  if (builtinsLoaded) return;
58704
59017
  for (const spec of BUILTIN_SKILL_MODULES) {
@@ -58709,7 +59022,7 @@ function ensureBuiltinSkillsLoadedSync() {
58709
59022
  }
58710
59023
  }
58711
59024
  function getUserSkillsDir() {
58712
- return join45(homedir12(), ".zelari-code", "skills");
59025
+ return join45(homedir13(), ".zelari-code", "skills");
58713
59026
  }
58714
59027
  function getProjectSkillsDir(projectRoot) {
58715
59028
  return join45(projectRoot, ".zelari", "skills");
@@ -58775,7 +59088,7 @@ function scanSkillsDir(dir, projectRoot, seen, out) {
58775
59088
  const skillPath = skillFilePath(dir, entry);
58776
59089
  if (!existsSync49(skillPath)) continue;
58777
59090
  try {
58778
- const parsed = parseSkillMd(readFileSync38(skillPath, "utf8"), skillPath);
59091
+ const parsed = parseSkillMd(readFileSync39(skillPath, "utf8"), skillPath);
58779
59092
  if (!parsed) continue;
58780
59093
  if (seen.has(parsed.name)) continue;
58781
59094
  seen.add(parsed.name);
@@ -58859,7 +59172,7 @@ function upsertSkill(opts) {
58859
59172
  }
58860
59173
  dir = getProjectSkillsDir(root);
58861
59174
  }
58862
- const path72 = skillFilePath(dir, name);
59175
+ const path74 = skillFilePath(dir, name);
58863
59176
  const content = serializeSkillMd({
58864
59177
  name,
58865
59178
  description,
@@ -58868,13 +59181,13 @@ function upsertSkill(opts) {
58868
59181
  tools: opts.tools,
58869
59182
  cost: opts.cost
58870
59183
  });
58871
- const parsed = parseSkillMd(content, path72);
59184
+ const parsed = parseSkillMd(content, path74);
58872
59185
  if (!parsed) {
58873
59186
  return { ok: false, error: "Generated SKILL.md failed validation" };
58874
59187
  }
58875
- mkdirSync21(dirname13(path72), { recursive: true });
58876
- writeFileSync24(path72, content, "utf8");
58877
- return { ok: true, path: path72 };
59188
+ mkdirSync21(dirname13(path74), { recursive: true });
59189
+ writeFileSync24(path74, content, "utf8");
59190
+ return { ok: true, path: path74 };
58878
59191
  }
58879
59192
  function removeSkill(opts) {
58880
59193
  const name = opts.name.trim().toLowerCase();
@@ -58892,8 +59205,8 @@ function removeSkill(opts) {
58892
59205
  dir = getProjectSkillsDir(root);
58893
59206
  }
58894
59207
  const skillDir = join45(dir, name);
58895
- const path72 = skillFilePath(dir, name);
58896
- if (!existsSync49(path72) && !existsSync49(skillDir)) {
59208
+ const path74 = skillFilePath(dir, name);
59209
+ if (!existsSync49(path74) && !existsSync49(skillDir)) {
58897
59210
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
58898
59211
  }
58899
59212
  try {
@@ -58904,7 +59217,7 @@ function removeSkill(opts) {
58904
59217
  error: err instanceof Error ? err.message : String(err)
58905
59218
  };
58906
59219
  }
58907
- return { ok: true, path: path72 };
59220
+ return { ok: true, path: path74 };
58908
59221
  }
58909
59222
  var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
58910
59223
  var init_skillConfigIo = __esm({
@@ -59023,7 +59336,7 @@ var init_jsonApi = __esm({
59023
59336
  });
59024
59337
 
59025
59338
  // src/cli/memory/mcpAdapter.ts
59026
- import * as path69 from "node:path";
59339
+ import * as path71 from "node:path";
59027
59340
  var id2, projectId, source, SearchSchema, AddSchema, LinkSchema, RetractSchema, MEMORY_MCP_TOOLS, MemoryMcpAdapter;
59028
59341
  var init_mcpAdapter = __esm({
59029
59342
  "src/cli/memory/mcpAdapter.ts"() {
@@ -59193,8 +59506,8 @@ var init_mcpAdapter = __esm({
59193
59506
  this.takeWrite();
59194
59507
  const externalFile = args.source?.file;
59195
59508
  if (externalFile) {
59196
- const normalized = path69.normalize(externalFile);
59197
- if (path69.isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${path69.sep}`)) {
59509
+ const normalized = path71.normalize(externalFile);
59510
+ if (path71.isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${path71.sep}`)) {
59198
59511
  throw new Error("source.file must be project-relative and cannot escape the project");
59199
59512
  }
59200
59513
  }
@@ -59700,14 +60013,14 @@ var init_permissionCli = __esm({
59700
60013
  import {
59701
60014
  existsSync as existsSync50,
59702
60015
  mkdirSync as mkdirSync22,
59703
- readFileSync as readFileSync39,
60016
+ readFileSync as readFileSync40,
59704
60017
  writeFileSync as writeFileSync25
59705
60018
  } from "node:fs";
59706
60019
  import { join as join46 } from "node:path";
59707
- import { homedir as homedir13 } from "node:os";
60020
+ import { homedir as homedir14 } from "node:os";
59708
60021
  import { createHash as createHash20, randomBytes as randomBytes6, timingSafeEqual } from "node:crypto";
59709
60022
  function getZelariHome() {
59710
- return join46(homedir13(), ".zelari-code");
60023
+ return join46(homedir14(), ".zelari-code");
59711
60024
  }
59712
60025
  function getCompanionConfigPath() {
59713
60026
  return join46(getZelariHome(), "companion.json");
@@ -59722,12 +60035,12 @@ function ensureHome() {
59722
60035
  }
59723
60036
  }
59724
60037
  function loadCompanionConfig() {
59725
- const path72 = getCompanionConfigPath();
59726
- if (!existsSync50(path72)) {
60038
+ const path74 = getCompanionConfigPath();
60039
+ if (!existsSync50(path74)) {
59727
60040
  return { projects: [] };
59728
60041
  }
59729
60042
  try {
59730
- const raw = JSON.parse(readFileSync39(path72, "utf8"));
60043
+ const raw = JSON.parse(readFileSync40(path74, "utf8"));
59731
60044
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
59732
60045
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
59733
60046
  ).map((p3) => ({
@@ -59765,16 +60078,16 @@ function loadOrCreateToken(explicit) {
59765
60078
  return { token: explicit.trim(), created: false };
59766
60079
  }
59767
60080
  ensureHome();
59768
- const path72 = getCompanionTokenPath();
59769
- if (existsSync50(path72)) {
59770
- const t = readFileSync39(path72, "utf8").trim();
60081
+ const path74 = getCompanionTokenPath();
60082
+ if (existsSync50(path74)) {
60083
+ const t = readFileSync40(path74, "utf8").trim();
59771
60084
  if (t) return { token: t, created: false };
59772
60085
  }
59773
60086
  const token = randomBytes6(24).toString("base64url");
59774
- writeFileSync25(path72, token + "\n", "utf8");
60087
+ writeFileSync25(path74, token + "\n", "utf8");
59775
60088
  try {
59776
60089
  const fs42 = __require("node:fs");
59777
- fs42.chmodSync?.(path72, 384);
60090
+ fs42.chmodSync?.(path74, 384);
59778
60091
  } catch {
59779
60092
  }
59780
60093
  return { token, created: true };
@@ -59799,17 +60112,17 @@ function mergeProjects(cfg, extraPaths) {
59799
60112
  byId.set(p3.id, p3);
59800
60113
  }
59801
60114
  for (const raw of extraPaths) {
59802
- const path72 = raw.trim();
59803
- if (!path72) continue;
59804
- let id3 = slugFromPath(path72);
60115
+ const path74 = raw.trim();
60116
+ if (!path74) continue;
60117
+ let id3 = slugFromPath(path74);
59805
60118
  let n = 2;
59806
- while (byId.has(id3) && byId.get(id3).path !== path72) {
59807
- id3 = `${slugFromPath(path72)}-${n++}`;
60119
+ while (byId.has(id3) && byId.get(id3).path !== path74) {
60120
+ id3 = `${slugFromPath(path74)}-${n++}`;
59808
60121
  }
59809
60122
  byId.set(id3, {
59810
60123
  id: id3,
59811
- name: slugFromPath(path72),
59812
- path: path72
60124
+ name: slugFromPath(path74),
60125
+ path: path74
59813
60126
  });
59814
60127
  }
59815
60128
  return [...byId.values()];
@@ -60186,9 +60499,9 @@ async function runCompanionServe(opts = {}) {
60186
60499
  return;
60187
60500
  }
60188
60501
  const url2 = parseUrl(req);
60189
- const path72 = url2.pathname.replace(/\/+$/, "") || "/";
60502
+ const path74 = url2.pathname.replace(/\/+$/, "") || "/";
60190
60503
  try {
60191
- if (req.method === "GET" && (path72 === "/health" || path72 === "/v1/health")) {
60504
+ if (req.method === "GET" && (path74 === "/health" || path74 === "/v1/health")) {
60192
60505
  sendJson2(res, 200, {
60193
60506
  ok: true,
60194
60507
  service: "zelari-companion",
@@ -60200,18 +60513,18 @@ async function runCompanionServe(opts = {}) {
60200
60513
  });
60201
60514
  return;
60202
60515
  }
60203
- if (path72.startsWith("/v1")) {
60516
+ if (path74.startsWith("/v1")) {
60204
60517
  if (!tokenMatches(token, getBearer(req))) {
60205
60518
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
60206
60519
  return;
60207
60520
  }
60208
60521
  }
60209
- if (req.method === "GET" && path72 === "/v1/config") {
60522
+ if (req.method === "GET" && path74 === "/v1/config") {
60210
60523
  const snap = buildDesktopConfigSnapshot();
60211
60524
  sendJson2(res, 200, { ok: true, ...snap });
60212
60525
  return;
60213
60526
  }
60214
- if (req.method === "GET" && path72 === "/v1/projects") {
60527
+ if (req.method === "GET" && path74 === "/v1/projects") {
60215
60528
  sendJson2(res, 200, {
60216
60529
  ok: true,
60217
60530
  projects: projects.map((p3) => ({
@@ -60222,7 +60535,7 @@ async function runCompanionServe(opts = {}) {
60222
60535
  });
60223
60536
  return;
60224
60537
  }
60225
- if (req.method === "GET" && path72 === "/v1/runs") {
60538
+ if (req.method === "GET" && path74 === "/v1/runs") {
60226
60539
  sendJson2(res, 200, {
60227
60540
  ok: true,
60228
60541
  active: runs.getActive(),
@@ -60240,7 +60553,7 @@ async function runCompanionServe(opts = {}) {
60240
60553
  });
60241
60554
  return;
60242
60555
  }
60243
- if (req.method === "POST" && path72 === "/v1/runs") {
60556
+ if (req.method === "POST" && path74 === "/v1/runs") {
60244
60557
  const raw = await readBody(req);
60245
60558
  let body = {};
60246
60559
  try {
@@ -60287,7 +60600,7 @@ async function runCompanionServe(opts = {}) {
60287
60600
  });
60288
60601
  return;
60289
60602
  }
60290
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path72);
60603
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path74);
60291
60604
  if (req.method === "GET" && eventsMatch) {
60292
60605
  const runId = eventsMatch[1];
60293
60606
  const run = runs.getRun(runId);
@@ -60352,7 +60665,7 @@ async function runCompanionServe(opts = {}) {
60352
60665
  }, 500);
60353
60666
  return;
60354
60667
  }
60355
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path72);
60668
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path74);
60356
60669
  if (req.method === "POST" && cancelMatch) {
60357
60670
  const runId = cancelMatch[1];
60358
60671
  const result = runs.cancel(runId);
@@ -60502,26 +60815,26 @@ __export(doctor_exports, {
60502
60815
  runDoctor: () => runDoctor
60503
60816
  });
60504
60817
  import { execSync as execSync2 } from "node:child_process";
60505
- import { existsSync as existsSync52, readFileSync as readFileSync40, readlinkSync, statSync as statSync10 } from "node:fs";
60818
+ import { existsSync as existsSync52, readFileSync as readFileSync41, readlinkSync, statSync as statSync10 } from "node:fs";
60506
60819
  import { createRequire as createRequire3 } from "node:module";
60507
60820
  import { fileURLToPath as fileURLToPath3 } from "node:url";
60508
- import path70 from "node:path";
60821
+ import path72 from "node:path";
60509
60822
  function findPackageRoot(start) {
60510
60823
  let dir = start;
60511
60824
  for (let i = 0; i < 6; i += 1) {
60512
- const candidate = path70.join(dir, "package.json");
60825
+ const candidate = path72.join(dir, "package.json");
60513
60826
  if (existsSync52(candidate)) {
60514
60827
  try {
60515
- const pkg = JSON.parse(readFileSync40(candidate, "utf8"));
60828
+ const pkg = JSON.parse(readFileSync41(candidate, "utf8"));
60516
60829
  if (pkg.name === "zelari-code") return dir;
60517
60830
  } catch {
60518
60831
  }
60519
60832
  }
60520
- const parent = path70.dirname(dir);
60833
+ const parent = path72.dirname(dir);
60521
60834
  if (parent === dir) break;
60522
60835
  dir = parent;
60523
60836
  }
60524
- return path70.resolve(__dirname3, "..", "..", "..");
60837
+ return path72.resolve(__dirname3, "..", "..", "..");
60525
60838
  }
60526
60839
  function tryExec(cmd) {
60527
60840
  try {
@@ -60535,8 +60848,8 @@ function tryExec(cmd) {
60535
60848
  }
60536
60849
  function readPackageJson3() {
60537
60850
  try {
60538
- const pkgPath = path70.join(packageRoot, "package.json");
60539
- return JSON.parse(readFileSync40(pkgPath, "utf8"));
60851
+ const pkgPath = path72.join(packageRoot, "package.json");
60852
+ return JSON.parse(readFileSync41(pkgPath, "utf8"));
60540
60853
  } catch {
60541
60854
  return null;
60542
60855
  }
@@ -60551,7 +60864,7 @@ function checkShim(pkgName) {
60551
60864
  }
60552
60865
  const isWin = process.platform === "win32";
60553
60866
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
60554
- const shimPath = path70.join(prefix, shimName);
60867
+ const shimPath = path72.join(prefix, shimName);
60555
60868
  if (!existsSync52(shimPath)) {
60556
60869
  return FAIL(
60557
60870
  `shim not found at ${shimPath}
@@ -60561,7 +60874,7 @@ function checkShim(pkgName) {
60561
60874
  try {
60562
60875
  const st = statSync10(shimPath);
60563
60876
  if (isWin) {
60564
- const content = readFileSync40(shimPath, "utf8");
60877
+ const content = readFileSync41(shimPath, "utf8");
60565
60878
  if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
60566
60879
  return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
60567
60880
  }
@@ -60579,8 +60892,8 @@ function checkShim(pkgName) {
60579
60892
  fix: npm install -g ${pkgName}@latest --force`
60580
60893
  );
60581
60894
  }
60582
- const resolved = path70.resolve(path70.dirname(shimPath), target);
60583
- const expected = path70.join(
60895
+ const resolved = path72.resolve(path72.dirname(shimPath), target);
60896
+ const expected = path72.join(
60584
60897
  prefix,
60585
60898
  "node_modules",
60586
60899
  pkgName,
@@ -60619,7 +60932,7 @@ function checkNode(pkg) {
60619
60932
  return OK(`node ${raw}`);
60620
60933
  }
60621
60934
  function checkBundle() {
60622
- const bundle = path70.join(packageRoot, "dist", "cli", "main.bundled.js");
60935
+ const bundle = path72.join(packageRoot, "dist", "cli", "main.bundled.js");
60623
60936
  if (!existsSync52(bundle)) {
60624
60937
  return FAIL(
60625
60938
  `dist/cli/main.bundled.js missing at ${bundle}
@@ -60640,7 +60953,7 @@ function checkRuntimeDeps() {
60640
60953
  const missing = [];
60641
60954
  for (const dep of required2) {
60642
60955
  try {
60643
- const localReq = createRequire3(path70.join(packageRoot, "package.json"));
60956
+ const localReq = createRequire3(path72.join(packageRoot, "package.json"));
60644
60957
  localReq.resolve(dep);
60645
60958
  } catch {
60646
60959
  missing.push(dep);
@@ -60840,7 +61153,7 @@ var init_doctor = __esm({
60840
61153
  init_metrics3();
60841
61154
  init_contextGrowthSummary();
60842
61155
  require3 = createRequire3(import.meta.url);
60843
- __dirname3 = path70.dirname(fileURLToPath3(import.meta.url));
61156
+ __dirname3 = path72.dirname(fileURLToPath3(import.meta.url));
60844
61157
  packageRoot = findPackageRoot(__dirname3);
60845
61158
  OK = (message) => ({
60846
61159
  ok: true,
@@ -61024,15 +61337,15 @@ __export(inspect_exports, {
61024
61337
  collectInspectReport: () => collectInspectReport,
61025
61338
  runInspect: () => runInspect
61026
61339
  });
61027
- import path71 from "node:path";
61028
- import { existsSync as existsSync53, readFileSync as readFileSync41, readdirSync as readdirSync11 } from "node:fs";
61029
- import { homedir as homedir14 } from "node:os";
61340
+ import path73 from "node:path";
61341
+ import { existsSync as existsSync53, readFileSync as readFileSync42, readdirSync as readdirSync11 } from "node:fs";
61342
+ import { homedir as homedir15 } from "node:os";
61030
61343
  async function collectInspectReport(cwd = process.cwd()) {
61031
61344
  ensureBuiltinSkillsLoadedSync();
61032
61345
  const snap = listSkillsSnapshot(cwd);
61033
61346
  const mcp = listMcpServers(cwd);
61034
- const userMcpPath = path71.join(homedir14(), ".zelari-code", "mcp.json");
61035
- const projectMcpPath = path71.join(cwd, ".zelari", "mcp.json");
61347
+ const userMcpPath = path73.join(homedir15(), ".zelari-code", "mcp.json");
61348
+ const projectMcpPath = path73.join(cwd, ".zelari", "mcp.json");
61036
61349
  const globalHooks = globalHooksDir();
61037
61350
  const projectHooks = projectHooksDir(cwd);
61038
61351
  const projectTrusted = isFolderTrusted(cwd);
@@ -61060,9 +61373,9 @@ async function collectInspectReport(cwd = process.cwd()) {
61060
61373
  configSources: [
61061
61374
  { path: userMcpPath, exists: existsSync53(userMcpPath) },
61062
61375
  { path: projectMcpPath, exists: existsSync53(projectMcpPath) },
61063
- { path: path71.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync53(path71.join(homedir14(), ".zelari-code", "provider.json")) },
61064
- { path: path71.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync53(path71.join(cwd, ".zelari", "AGENTS.md")) },
61065
- { path: path71.join(cwd, "AGENTS.md"), exists: existsSync53(path71.join(cwd, "AGENTS.md")) }
61376
+ { path: path73.join(homedir15(), ".zelari-code", "provider.json"), exists: existsSync53(path73.join(homedir15(), ".zelari-code", "provider.json")) },
61377
+ { path: path73.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync53(path73.join(cwd, ".zelari", "AGENTS.md")) },
61378
+ { path: path73.join(cwd, "AGENTS.md"), exists: existsSync53(path73.join(cwd, "AGENTS.md")) }
61066
61379
  ],
61067
61380
  skills: {
61068
61381
  total: snap.skills.length,
@@ -61102,14 +61415,14 @@ function listJsonFiles(dir) {
61102
61415
  }
61103
61416
  function findAgentsMd(cwd) {
61104
61417
  const candidates = [
61105
- path71.join(cwd, "AGENTS.md"),
61106
- path71.join(cwd, ".zelari", "AGENTS.md")
61418
+ path73.join(cwd, "AGENTS.md"),
61419
+ path73.join(cwd, ".zelari", "AGENTS.md")
61107
61420
  ];
61108
61421
  const found = [];
61109
61422
  for (const c of candidates) {
61110
61423
  if (existsSync53(c)) {
61111
61424
  try {
61112
- const text = readFileSync41(c, "utf8");
61425
+ const text = readFileSync42(c, "utf8");
61113
61426
  found.push(`${c} (${text.length} bytes)`);
61114
61427
  } catch {
61115
61428
  found.push(`${c} (unreadable)`);
@@ -64403,10 +64716,11 @@ import { createHash as createHash14 } from "node:crypto";
64403
64716
  init_runtime2();
64404
64717
  init_verification2();
64405
64718
  import { readFile as readFile3 } from "node:fs/promises";
64406
- import path42 from "node:path";
64719
+ import path43 from "node:path";
64407
64720
  function nativePackEnabled(env = process.env) {
64408
64721
  const v = env.ZELARI_VERIFY_PACK?.toLowerCase();
64409
- return v === "1" || v === "on" || v === "true";
64722
+ if (v === "0" || v === "off" || v === "false") return false;
64723
+ return true;
64410
64724
  }
64411
64725
  function resolvePackCommands(env, scripts) {
64412
64726
  const pick2 = (override, scriptName) => {
@@ -64430,7 +64744,7 @@ function packTimeoutMs(env = process.env) {
64430
64744
  }
64431
64745
  async function readPackageScripts(cwd = process.cwd()) {
64432
64746
  try {
64433
- const raw = await readFile3(path42.join(cwd, "package.json"), "utf-8");
64747
+ const raw = await readFile3(path43.join(cwd, "package.json"), "utf-8");
64434
64748
  const parsed = JSON.parse(raw);
64435
64749
  if (parsed && typeof parsed === "object" && typeof parsed.scripts === "object") {
64436
64750
  return parsed.scripts;
@@ -64469,7 +64783,8 @@ function strictDoneEnabled(surface = "kraken") {
64469
64783
  return true;
64470
64784
  }
64471
64785
  const v = process.env.ZELARI_STRICT_DONE;
64472
- return v === "1" || v === "true";
64786
+ if (v === "0" || v === "false") return false;
64787
+ return true;
64473
64788
  }
64474
64789
  function criterionId(check2, index) {
64475
64790
  const slug = check2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
@@ -64706,6 +65021,145 @@ function strictGateEventPayload(evaluation) {
64706
65021
  };
64707
65022
  }
64708
65023
 
65024
+ // src/cli/kraken/completionProof.ts
65025
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
65026
+ import path44 from "node:path";
65027
+ function verdictOf(evaluation) {
65028
+ return evaluation.evaluation?.verdict ?? (evaluation.blocked ? "BLOCKED" : "PASS");
65029
+ }
65030
+ function cell(text, max = 200) {
65031
+ const flat = text.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").trim();
65032
+ return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
65033
+ }
65034
+ function evidenceCell(result) {
65035
+ if (!result || result.evidence.length === 0) return "\u2014";
65036
+ return result.evidence.map((e) => {
65037
+ const parts = [e.tier];
65038
+ if (e.seq !== void 0) parts.push(`seq ${e.seq}`);
65039
+ if (e.digest) parts.push(`digest ${e.digest.slice(0, 8)}\u2026`);
65040
+ return parts.join(" \xB7 ");
65041
+ }).join("; ");
65042
+ }
65043
+ function proofRows(evaluation) {
65044
+ const nativeById = new Map(
65045
+ (evaluation.native?.criteria ?? []).map((c) => [c.id, c])
65046
+ );
65047
+ const rows = /* @__PURE__ */ new Map();
65048
+ for (const result of evaluation.results ?? []) {
65049
+ const criterion = nativeById.get(result.criterionId);
65050
+ rows.set(result.criterionId, {
65051
+ id: result.criterionId,
65052
+ text: criterion?.text ?? null,
65053
+ required: criterion ? criterion.required : true,
65054
+ status: result.status,
65055
+ result
65056
+ });
65057
+ }
65058
+ for (const unsatisfied of evaluation.evaluation?.unsatisfied ?? []) {
65059
+ if (rows.has(unsatisfied.id)) continue;
65060
+ const criterion = nativeById.get(unsatisfied.id);
65061
+ rows.set(unsatisfied.id, {
65062
+ id: unsatisfied.id,
65063
+ text: criterion?.text ?? null,
65064
+ required: true,
65065
+ // satisfied/unsatisfied lists cover required criteria only
65066
+ status: unsatisfied.status,
65067
+ result: null
65068
+ });
65069
+ }
65070
+ return [...rows.values()];
65071
+ }
65072
+ function renderMarkdown(evaluation, meta3) {
65073
+ const lines = [
65074
+ "# Completion Proof",
65075
+ "",
65076
+ "Strict build-gate proof-of-work (ADR-0023). The machine-readable twin of",
65077
+ "this document \u2014 `completion-proof.json` \u2014 is the exact `verification.run`",
65078
+ "payload sent to the session spine.",
65079
+ "",
65080
+ `- **Verdict**: **${verdictOf(evaluation)}**`,
65081
+ `- **Strict gate**: ${evaluation.strict ? "on" : "off"}`,
65082
+ `- **Turn blocked**: ${evaluation.blocked ? "yes" : "no"}`,
65083
+ `- **Summary**: ${cell(evaluation.summary, 400)}`,
65084
+ `- **Legacy selection gate**: ${evaluation.gate.passed}/${evaluation.gate.total} passed` + (evaluation.gate.failedChecks.length > 0 ? `; failed: ${evaluation.gate.failedChecks.length}` : "") + (evaluation.gate.unknownChecks.length > 0 ? `; unknown: ${evaluation.gate.unknownChecks.length}` : "")
65085
+ ];
65086
+ if (meta3.surface) lines.push(`- **Surface**: ${meta3.surface}`);
65087
+ if (meta3.sessionId) lines.push(`- **Session**: ${meta3.sessionId} (session spine)`);
65088
+ if (meta3.generatedAt !== void 0) {
65089
+ lines.push(`- **Generated**: ${new Date(meta3.generatedAt).toISOString()}`);
65090
+ }
65091
+ const rows = proofRows(evaluation);
65092
+ lines.push("", "## Criteria", "", "| Criterion | Required | Status | Evidence |", "| --- | --- | --- | --- |");
65093
+ if (rows.length === 0) {
65094
+ lines.push("| _none \u2014 no criteria joined this evaluation_ | \u2014 | \u2014 | \u2014 |");
65095
+ }
65096
+ for (const row of rows) {
65097
+ const label = row.text ? `\`${row.id}\` \u2014 ${cell(row.text)}` : `\`${row.id}\``;
65098
+ lines.push(`| ${label} | ${row.required ? "yes" : "no"} | ${row.status} | ${evidenceCell(row.result)} |`);
65099
+ }
65100
+ const unsatisfied = evaluation.evaluation?.unsatisfied ?? [];
65101
+ if (unsatisfied.length > 0) {
65102
+ lines.push("", "### Unsatisfied", "");
65103
+ for (const u of unsatisfied) lines.push(`- \`${u.id}\`: **${u.status}** \u2014 ${cell(u.reason, 300)}`);
65104
+ }
65105
+ const native = evaluation.native;
65106
+ if (native) {
65107
+ lines.push(
65108
+ "",
65109
+ `## Native criteria pack \u2014 ${native.packId}`,
65110
+ "",
65111
+ "| Criterion | Command | Status | Detail |",
65112
+ "| --- | --- | --- | --- |"
65113
+ );
65114
+ for (const result of native.results) {
65115
+ const criterion = native.criteria.find((c) => c.id === result.criterionId);
65116
+ const check2 = criterion?.check;
65117
+ const command = check2?.kind === "command" ? check2.command : "\u2014";
65118
+ const label = criterion?.text ? `\`${result.criterionId}\` \u2014 ${cell(criterion.text, 120)}` : `\`${result.criterionId}\``;
65119
+ lines.push(`| ${label} | \`${cell(command, 120)}\` | ${result.status} | ${cell(result.detail ?? "\u2014")} |`);
65120
+ }
65121
+ }
65122
+ const review = evaluation.review;
65123
+ if (review) {
65124
+ lines.push(
65125
+ "",
65126
+ "## Advisory verifier review",
65127
+ "",
65128
+ "_Advisory only \u2014 this review cannot change the gate verdict above._",
65129
+ "",
65130
+ `- **Verdict**: ${review.verdict}`
65131
+ );
65132
+ if (review.score !== void 0) lines.push(`- **Score**: ${review.score}`);
65133
+ const model = review.effectiveModel.model ?? review.effectiveModel.provider ?? review.effectiveModel.mode;
65134
+ lines.push(`- **Model**: ${model}${review.usedLogprobs ? " (logprobs)" : ""}`);
65135
+ if (review.fallback) lines.push(`- **Fallback**: ${review.fallback}`);
65136
+ if (review.rationale) lines.push(`- **Rationale**: ${cell(review.rationale, 400)}`);
65137
+ }
65138
+ lines.push("", "---", "", "Machine record: `completion-proof.json` (same directory).");
65139
+ return `${lines.join("\n")}
65140
+ `;
65141
+ }
65142
+ function renderCompletionProof(evaluation, meta3 = {}) {
65143
+ return {
65144
+ markdown: renderMarkdown(evaluation, meta3),
65145
+ json: JSON.stringify(strictGateEventPayload(evaluation), null, 2)
65146
+ };
65147
+ }
65148
+ async function writeCompletionProof(evaluation, options = {}) {
65149
+ try {
65150
+ const dir = path44.join(options.baseDir ?? process.cwd(), ".zelari");
65151
+ await mkdir2(dir, { recursive: true });
65152
+ const { markdown, json: json3 } = renderCompletionProof(evaluation, options.meta);
65153
+ const markdownPath = path44.join(dir, "completion-proof.md");
65154
+ const jsonPath = path44.join(dir, "completion-proof.json");
65155
+ await writeFile2(markdownPath, markdown, "utf8");
65156
+ await writeFile2(jsonPath, json3, "utf8");
65157
+ return { markdownPath, jsonPath };
65158
+ } catch {
65159
+ return null;
65160
+ }
65161
+ }
65162
+
64709
65163
  // src/cli/hooks/permissionPicker.ts
64710
65164
  init_toolPermissions();
64711
65165
 
@@ -65746,10 +66200,15 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
65746
66200
  if (event.type === "agent_end") {
65747
66201
  let krakenSuppressFinish = false;
65748
66202
  const krakenSpineEmit = (input) => writerRef.current?.spine?.appendEvent(input) ?? Promise.resolve(null);
66203
+ const writeProofSafe2 = (gate) => writeCompletionProof(gate, { meta: { surface: "kraken", sessionId: sessionId2 } }).then(
66204
+ () => void 0,
66205
+ () => void 0
66206
+ );
65749
66207
  if (event.reason === "completed" && !krakenRepairEnqueued && (isKrakenSelectionEnabled() || nativePackEnabled()) && workPhase === "build") {
65750
66208
  const strictGate = await evaluateStrictBuildGate("build", { emit: krakenSpineEmit });
65751
66209
  const krakenGate = strictGate.gate;
65752
66210
  writerRef.current?.spine?.verificationRun(strictGateEventPayload(strictGate));
66211
+ await writeProofSafe2(strictGate);
65753
66212
  if (krakenGate.blocked) {
65754
66213
  krakenRepairEnqueued = true;
65755
66214
  markRepairTriggered();
@@ -65763,10 +66222,13 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
65763
66222
  krakenSuppressFinish = true;
65764
66223
  }
65765
66224
  }
65766
- if (event.reason === "completed" && krakenRepairEnqueued && !(await evaluateStrictBuildGate("build", { emit: krakenSpineEmit })).blocked) {
66225
+ const repairCheck = event.reason === "completed" && krakenRepairEnqueued ? await evaluateStrictBuildGate("build", { emit: krakenSpineEmit }) : null;
66226
+ if (repairCheck) await writeProofSafe2(repairCheck);
66227
+ if (repairCheck && !repairCheck.blocked) {
65767
66228
  markRepairSucceeded();
65768
66229
  } else if (event.reason === "completed" && krakenRepairEnqueued) {
65769
66230
  const still = await evaluateStrictBuildGate("build", { emit: krakenSpineEmit });
66231
+ await writeProofSafe2(still);
65770
66232
  if (still.blocked) {
65771
66233
  appendSystem(
65772
66234
  setMessages,
@@ -67882,7 +68344,7 @@ init_sessionManager();
67882
68344
  // src/cli/gitOps.ts
67883
68345
  import { execFile as execFile4 } from "node:child_process";
67884
68346
  import { promisify as promisify3 } from "node:util";
67885
- import path53 from "node:path";
68347
+ import path55 from "node:path";
67886
68348
  var execFileAsync3 = promisify3(execFile4);
67887
68349
  async function git4(cwd, args) {
67888
68350
  try {
@@ -67928,7 +68390,7 @@ async function undoWorkingChanges(opts = {}) {
67928
68390
  };
67929
68391
  }
67930
68392
  function defaultProjectRoot() {
67931
- return path53.resolve(__dirname, "..", "..", "..");
68393
+ return path55.resolve(__dirname, "..", "..", "..");
67932
68394
  }
67933
68395
 
67934
68396
  // src/cli/slashHandlers/git.ts
@@ -68215,11 +68677,11 @@ function handleCacheStats(ctx) {
68215
68677
  init_messageHelpers();
68216
68678
  init_serviceFactory();
68217
68679
  import { promises as fs30 } from "node:fs";
68218
- import * as path55 from "node:path";
68680
+ import * as path57 from "node:path";
68219
68681
 
68220
68682
  // src/cli/memory/promotion.ts
68221
68683
  import { promises as fs29 } from "node:fs";
68222
- import * as path54 from "node:path";
68684
+ import * as path56 from "node:path";
68223
68685
  var START = "<!-- zelari:memory-promotions:start -->";
68224
68686
  var END = "<!-- zelari:memory-promotions:end -->";
68225
68687
  var DURABLE_KINDS = /* @__PURE__ */ new Set(["fact", "decision", "constraint", "preference", "procedure"]);
@@ -68230,13 +68692,13 @@ function lineFor(node) {
68230
68692
  }
68231
68693
  async function promoteMemoryToAgentsMd(projectRoot, node) {
68232
68694
  if (node.status !== "active") {
68233
- return { added: false, path: path54.join(projectRoot, "AGENTS.md"), reason: `memory is ${node.status}` };
68695
+ return { added: false, path: path56.join(projectRoot, "AGENTS.md"), reason: `memory is ${node.status}` };
68234
68696
  }
68235
68697
  if (!DURABLE_KINDS.has(node.kind)) {
68236
- return { added: false, path: path54.join(projectRoot, "AGENTS.md"), reason: `${node.kind} is not a durable instruction kind` };
68698
+ return { added: false, path: path56.join(projectRoot, "AGENTS.md"), reason: `${node.kind} is not a durable instruction kind` };
68237
68699
  }
68238
- const root = await fs29.realpath(projectRoot).catch(() => path54.resolve(projectRoot));
68239
- const target = path54.join(root, "AGENTS.md");
68700
+ const root = await fs29.realpath(projectRoot).catch(() => path56.resolve(projectRoot));
68701
+ const target = path56.join(root, "AGENTS.md");
68240
68702
  try {
68241
68703
  const stat2 = await fs29.lstat(target);
68242
68704
  if (stat2.isSymbolicLink() || !stat2.isFile()) throw new Error("AGENTS.md must be a regular project file.");
@@ -68301,22 +68763,22 @@ function sourceLine(source2) {
68301
68763
  return entries.length ? entries.map(([key, value]) => `${key}=${value}`).join(" \xB7 ") : "unknown";
68302
68764
  }
68303
68765
  function isInside(root, target) {
68304
- const relative6 = path55.relative(root, target);
68305
- return relative6 === "" || !relative6.startsWith("..") && !path55.isAbsolute(relative6);
68766
+ const relative6 = path57.relative(root, target);
68767
+ return relative6 === "" || !relative6.startsWith("..") && !path57.isAbsolute(relative6);
68306
68768
  }
68307
68769
  async function safeExportPath(cwd, requested) {
68308
- const lexicalRoot = path55.resolve(cwd);
68770
+ const lexicalRoot = path57.resolve(cwd);
68309
68771
  const root = await fs30.realpath(lexicalRoot).catch(() => lexicalRoot);
68310
- const fallback = path55.join(root, ".zelari", "memory", `export-${Date.now()}.json`);
68311
- const target = requested?.trim() ? path55.resolve(root, requested.trim()) : fallback;
68772
+ const fallback = path57.join(root, ".zelari", "memory", `export-${Date.now()}.json`);
68773
+ const target = requested?.trim() ? path57.resolve(root, requested.trim()) : fallback;
68312
68774
  if (!isInside(root, target)) {
68313
68775
  throw new Error("Export path must stay inside the active project.");
68314
68776
  }
68315
- const parent = path55.dirname(target);
68316
- const relativeParent = path55.relative(root, parent);
68777
+ const parent = path57.dirname(target);
68778
+ const relativeParent = path57.relative(root, parent);
68317
68779
  let cursor = root;
68318
- for (const segment of relativeParent.split(path55.sep).filter(Boolean)) {
68319
- cursor = path55.join(cursor, segment);
68780
+ for (const segment of relativeParent.split(path57.sep).filter(Boolean)) {
68781
+ cursor = path57.join(cursor, segment);
68320
68782
  try {
68321
68783
  const stat2 = await fs30.lstat(cursor);
68322
68784
  if (stat2.isSymbolicLink()) {
@@ -68488,9 +68950,9 @@ ${message}` : message
68488
68950
  }
68489
68951
  case "export": {
68490
68952
  const target = await safeExportPath(ctx.cwd, args.join(" ").trim() || void 0);
68491
- await fs30.mkdir(path55.dirname(target), { recursive: true });
68492
- const root = await fs30.realpath(ctx.cwd).catch(() => path55.resolve(ctx.cwd));
68493
- const realParent = await fs30.realpath(path55.dirname(target));
68953
+ await fs30.mkdir(path57.dirname(target), { recursive: true });
68954
+ const root = await fs30.realpath(ctx.cwd).catch(() => path57.resolve(ctx.cwd));
68955
+ const realParent = await fs30.realpath(path57.dirname(target));
68494
68956
  if (!isInside(root, realParent)) {
68495
68957
  throw new Error("Export path resolves outside the active project.");
68496
68958
  }
@@ -68686,7 +69148,7 @@ import { promises as fs34 } from "node:fs";
68686
69148
  init_zod();
68687
69149
  init_taskTool();
68688
69150
  import { promises as fs33 } from "node:fs";
68689
- import path59 from "node:path";
69151
+ import path61 from "node:path";
68690
69152
  import { randomBytes as randomBytes5 } from "node:crypto";
68691
69153
  var CsvFanoutArgsSchema = external_exports.object({
68692
69154
  csv_path: external_exports.string().min(1),
@@ -68780,8 +69242,8 @@ function resolveMaxConcurrency(env = process.env) {
68780
69242
  }
68781
69243
  async function runCsvFanout(args, deps, opts) {
68782
69244
  const start = Date.now();
68783
- const absCsv = path59.isAbsolute(args.csv_path) ? args.csv_path : path59.join(opts.parentCwd, args.csv_path);
68784
- const absOut = path59.isAbsolute(args.output_csv_path) ? args.output_csv_path : path59.join(opts.parentCwd, args.output_csv_path);
69245
+ const absCsv = path61.isAbsolute(args.csv_path) ? args.csv_path : path61.join(opts.parentCwd, args.csv_path);
69246
+ const absOut = path61.isAbsolute(args.output_csv_path) ? args.output_csv_path : path61.join(opts.parentCwd, args.output_csv_path);
68785
69247
  const { headers: headers2, rows } = await readCsv(absCsv);
68786
69248
  if (headers2.length === 0) {
68787
69249
  throw new Error(`kraken_csv_fanout: ${absCsv} is empty`);
@@ -68837,7 +69299,7 @@ async function runCsvFanout(args, deps, opts) {
68837
69299
  errored += 1;
68838
69300
  errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
68839
69301
  }
68840
- await fs33.mkdir(path59.dirname(absOut), { recursive: true });
69302
+ await fs33.mkdir(path61.dirname(absOut), { recursive: true });
68841
69303
  await queueWrite(serializeCsv(outHeaders, outputRecords));
68842
69304
  }
68843
69305
  }
@@ -69032,7 +69494,7 @@ function splitArgs(s) {
69032
69494
  // src/cli/slashHandlers/krakenWorkbench.ts
69033
69495
  init_messageHelpers();
69034
69496
  import { promises as fs35 } from "node:fs";
69035
- import path60 from "node:path";
69497
+ import path62 from "node:path";
69036
69498
 
69037
69499
  // src/cli/kraken/workbenchView.ts
69038
69500
  var EMPTY = {
@@ -69149,14 +69611,14 @@ function formatWorkbenchForTerminal(p3) {
69149
69611
 
69150
69612
  // src/cli/slashHandlers/krakenWorkbench.ts
69151
69613
  async function handleKrakenWorkbench(ctx) {
69152
- const dir = path60.join(ctx.cwd, ".zelari", "radio");
69614
+ const dir = path62.join(ctx.cwd, ".zelari", "radio");
69153
69615
  let latest = null;
69154
69616
  let latestMtime = 0;
69155
69617
  try {
69156
69618
  const files = await fs35.readdir(dir);
69157
69619
  for (const f of files) {
69158
69620
  if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
69159
- const full = path60.join(dir, f);
69621
+ const full = path62.join(dir, f);
69160
69622
  const stat2 = await fs35.stat(full);
69161
69623
  if (stat2.mtimeMs > latestMtime) {
69162
69624
  latestMtime = stat2.mtimeMs;
@@ -69173,10 +69635,10 @@ async function handleKrakenWorkbench(ctx) {
69173
69635
  const parsed = parseWorkbench(content);
69174
69636
  const rendered = formatWorkbenchForTerminal(parsed);
69175
69637
  if (!rendered.trim()) {
69176
- appendSystem(ctx.setMessages, `[kraken workbench] ${path60.basename(latest)}: (no nodes / no events yet)`);
69638
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path62.basename(latest)}: (no nodes / no events yet)`);
69177
69639
  return;
69178
69640
  }
69179
- appendSystem(ctx.setMessages, `[kraken workbench] ${path60.basename(latest)}:
69641
+ appendSystem(ctx.setMessages, `[kraken workbench] ${path62.basename(latest)}:
69180
69642
  ${rendered}`);
69181
69643
  }
69182
69644
 
@@ -69483,15 +69945,15 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
69483
69945
  // src/cli/slashHandlers/promoteMember.ts
69484
69946
  init_messageHelpers();
69485
69947
  import { promises as fs36 } from "node:fs";
69486
- import path63 from "node:path";
69948
+ import path65 from "node:path";
69487
69949
  import os12 from "node:os";
69488
69950
  async function handlePromoteMember(ctx, memberId) {
69489
69951
  try {
69490
69952
  const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
69491
69953
  const { skill, markdown } = promoteMember2(memberId);
69492
- const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path63.join(os12.homedir(), ".tmp", "zelari-code", "skills");
69954
+ const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path65.join(os12.homedir(), ".tmp", "zelari-code", "skills");
69493
69955
  await fs36.mkdir(skillDir, { recursive: true });
69494
- const filePath = path63.join(skillDir, `${skill.id}.md`);
69956
+ const filePath = path65.join(skillDir, `${skill.id}.md`);
69495
69957
  await fs36.writeFile(filePath, markdown, "utf8");
69496
69958
  appendSystem(
69497
69959
  ctx.setMessages,
@@ -69508,25 +69970,25 @@ async function handlePromoteMember(ctx, memberId) {
69508
69970
  }
69509
69971
 
69510
69972
  // src/cli/branchManager.ts
69511
- import { promises as fs37, existsSync as existsSync45, readFileSync as readFileSync36, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
69512
- import path64 from "node:path";
69973
+ import { promises as fs37, existsSync as existsSync45, readFileSync as readFileSync37, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
69974
+ import path66 from "node:path";
69513
69975
  import os13 from "node:os";
69514
69976
  var META_FILENAME = "meta.json";
69515
69977
  var SESSIONS_SUBDIR = "sessions";
69516
69978
  function getBranchesBaseDir() {
69517
- return process.env.ANATHEMA_BRANCHES_DIR ?? path64.join(os13.homedir(), ".tmp", "zelari-code", "branches");
69979
+ return process.env.ANATHEMA_BRANCHES_DIR ?? path66.join(os13.homedir(), ".tmp", "zelari-code", "branches");
69518
69980
  }
69519
69981
  function getSessionsBaseDir() {
69520
- return process.env.ANATHEMA_SESSIONS_DIR ?? path64.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
69982
+ return process.env.ANATHEMA_SESSIONS_DIR ?? path66.join(os13.homedir(), ".tmp", "zelari-code", "sessions");
69521
69983
  }
69522
69984
  function branchPathFor(name, baseDir) {
69523
- return path64.join(baseDir, name);
69985
+ return path66.join(baseDir, name);
69524
69986
  }
69525
69987
  function metaPathFor(name, baseDir) {
69526
- return path64.join(baseDir, name, META_FILENAME);
69988
+ return path66.join(baseDir, name, META_FILENAME);
69527
69989
  }
69528
69990
  function sessionsPathFor(name, baseDir) {
69529
- return path64.join(baseDir, name, SESSIONS_SUBDIR);
69991
+ return path66.join(baseDir, name, SESSIONS_SUBDIR);
69530
69992
  }
69531
69993
  function readBranchMeta(name, baseDir) {
69532
69994
  const metaPath = metaPathFor(name, baseDir);
@@ -69534,7 +69996,7 @@ function readBranchMeta(name, baseDir) {
69534
69996
  throw new BranchNotFoundError(`Branch "${name}" not found`);
69535
69997
  }
69536
69998
  try {
69537
- const raw = readFileSync36(metaPath, "utf-8");
69999
+ const raw = readFileSync37(metaPath, "utf-8");
69538
70000
  const parsed = JSON.parse(raw);
69539
70001
  if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
69540
70002
  throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
@@ -69551,7 +70013,7 @@ function readBranchMeta(name, baseDir) {
69551
70013
  }
69552
70014
  function writeBranchMeta(name, baseDir, meta3) {
69553
70015
  const metaPath = metaPathFor(name, baseDir);
69554
- mkdirSync19(path64.dirname(metaPath), { recursive: true });
70016
+ mkdirSync19(path66.dirname(metaPath), { recursive: true });
69555
70017
  writeFileSync23(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
69556
70018
  }
69557
70019
  async function countSessions(name, baseDir) {
@@ -69602,14 +70064,14 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
69602
70064
  if (branchExists(name, baseDir)) {
69603
70065
  throw new BranchAlreadyExistsError(name);
69604
70066
  }
69605
- const sourcePath = path64.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
70067
+ const sourcePath = path66.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
69606
70068
  if (!existsSync45(sourcePath)) {
69607
70069
  throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
69608
70070
  }
69609
70071
  const branchPath = branchPathFor(name, baseDir);
69610
70072
  const branchSessionsPath = sessionsPathFor(name, baseDir);
69611
70073
  mkdirSync19(branchSessionsPath, { recursive: true });
69612
- const destPath = path64.join(branchSessionsPath, `${fromSessionId}.jsonl`);
70074
+ const destPath = path66.join(branchSessionsPath, `${fromSessionId}.jsonl`);
69613
70075
  await fs37.copyFile(sourcePath, destPath);
69614
70076
  const meta3 = {
69615
70077
  name,
@@ -69713,14 +70175,14 @@ async function handleBranchCheckout(ctx, branchName) {
69713
70175
  // src/cli/slashHandlers/workspace.ts
69714
70176
  init_messageHelpers();
69715
70177
  import { promises as fs38 } from "node:fs";
69716
- import path65 from "node:path";
70178
+ import path67 from "node:path";
69717
70179
  async function handleWorkspaceShow(ctx, what) {
69718
70180
  try {
69719
- const zelari = path65.join(process.cwd(), ".zelari");
70181
+ const zelari = path67.join(process.cwd(), ".zelari");
69720
70182
  let content;
69721
70183
  switch (what) {
69722
70184
  case "plan": {
69723
- const planPath = path65.join(zelari, "plan.md");
70185
+ const planPath = path67.join(zelari, "plan.md");
69724
70186
  try {
69725
70187
  content = await fs38.readFile(planPath, "utf-8");
69726
70188
  } catch {
@@ -69729,7 +70191,7 @@ async function handleWorkspaceShow(ctx, what) {
69729
70191
  break;
69730
70192
  }
69731
70193
  case "decisions": {
69732
- const decisionsDir = path65.join(zelari, "decisions");
70194
+ const decisionsDir = path67.join(zelari, "decisions");
69733
70195
  try {
69734
70196
  const files = (await fs38.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
69735
70197
  if (files.length === 0) {
@@ -69739,7 +70201,7 @@ async function handleWorkspaceShow(ctx, what) {
69739
70201
  `];
69740
70202
  const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
69741
70203
  for (const f of files) {
69742
- const raw = await fs38.readFile(path65.join(decisionsDir, f), "utf-8");
70204
+ const raw = await fs38.readFile(path67.join(decisionsDir, f), "utf-8");
69743
70205
  const { meta: meta3, body } = parseFrontmatter2(raw);
69744
70206
  const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
69745
70207
  lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
@@ -69752,7 +70214,7 @@ async function handleWorkspaceShow(ctx, what) {
69752
70214
  break;
69753
70215
  }
69754
70216
  case "risks": {
69755
- const risksPath = path65.join(zelari, "risks.md");
70217
+ const risksPath = path67.join(zelari, "risks.md");
69756
70218
  try {
69757
70219
  content = await fs38.readFile(risksPath, "utf-8");
69758
70220
  } catch {
@@ -69761,7 +70223,7 @@ async function handleWorkspaceShow(ctx, what) {
69761
70223
  break;
69762
70224
  }
69763
70225
  case "agents": {
69764
- const agentsPath = path65.join(process.cwd(), "AGENTS.MD");
70226
+ const agentsPath = path67.join(process.cwd(), "AGENTS.MD");
69765
70227
  try {
69766
70228
  content = await fs38.readFile(agentsPath, "utf-8");
69767
70229
  } catch {
@@ -69770,7 +70232,7 @@ async function handleWorkspaceShow(ctx, what) {
69770
70232
  break;
69771
70233
  }
69772
70234
  case "docs": {
69773
- const docsDir = path65.join(zelari, "docs");
70235
+ const docsDir = path67.join(zelari, "docs");
69774
70236
  try {
69775
70237
  const files = (await fs38.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
69776
70238
  content = files.length ? `# Docs (${files.length})
@@ -69812,7 +70274,7 @@ async function handleWorkspaceReset(ctx, force) {
69812
70274
  return;
69813
70275
  }
69814
70276
  try {
69815
- const target = path65.join(process.cwd(), ".zelari");
70277
+ const target = path67.join(process.cwd(), ".zelari");
69816
70278
  await fs38.rm(target, { recursive: true, force: true });
69817
70279
  appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
69818
70280
  } catch (err) {
@@ -69824,7 +70286,7 @@ async function handleWorkspaceReset(ctx, force) {
69824
70286
  init_provider2();
69825
70287
 
69826
70288
  // src/cli/slashHandlers/skills.ts
69827
- import path66 from "node:path";
70289
+ import path68 from "node:path";
69828
70290
  import os14 from "node:os";
69829
70291
 
69830
70292
  // src/cli/skillHistory.ts
@@ -69953,7 +70415,7 @@ function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
69953
70415
  });
69954
70416
  }
69955
70417
  async function handleSkillStats(ctx, skillId) {
69956
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path66.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
70418
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path68.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
69957
70419
  try {
69958
70420
  const records = await readSkillHistory(historyFile);
69959
70421
  const stats = getSkillStats(records, skillId);
@@ -69969,7 +70431,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
69969
70431
  appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
69970
70432
  return;
69971
70433
  }
69972
- const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path66.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
70434
+ const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path68.join(os14.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
69973
70435
  try {
69974
70436
  const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
69975
70437
  appendSystem(ctx.setMessages, formatted);
@@ -71484,6 +71946,54 @@ init_candidateRegistry();
71484
71946
  init_metrics2();
71485
71947
  init_completionGate();
71486
71948
  init_delegationPolicy();
71949
+
71950
+ // src/cli/orchestration/policy.ts
71951
+ var DEFAULT_MAX_SOLO_CHARS = 300;
71952
+ var KRAKEN_SIGNALS = [
71953
+ { re: /\bimplement(?:s|ed|ing)?\b/i, reason: "implementation signal" },
71954
+ { re: /\brefactor(?:ing|ed)?\b/i, reason: "refactor signal" },
71955
+ { re: /\bmigrat(?:e|es|ed|ion|ing)\b/i, reason: "migration signal" },
71956
+ {
71957
+ re: /\b(?:add|build|create|introduce)\b[^.?!]{0,48}\b(?:feature|endpoint|module|service|command|api|registry|runtime)\b/i,
71958
+ reason: "new-capability signal"
71959
+ },
71960
+ {
71961
+ re: /\b(?:unit|integration|e2e|end-to-end)\s+(?:tests?|testing|specs?)\b|\bwrite\s+(?:the\s+|some\s+)?tests?\b/i,
71962
+ reason: "test-writing signal"
71963
+ },
71964
+ {
71965
+ re: /\b\d+\s*-?\s*(?:files?|modules?|packages?|components?|services?|endpoints?|worktrees?)\b/i,
71966
+ reason: "multi-artifact count"
71967
+ },
71968
+ {
71969
+ re: /\b(?:across|spanning|between|touching)\s+(?:all\s+|the\s+)?(?:\w+\s+){0,2}(?:files|modules|packages|layers|surfaces)\b/i,
71970
+ reason: "cross-cutting scope"
71971
+ }
71972
+ ];
71973
+ var QUESTION_RE = /\?\s*$|^(?:who|what|why|when|where|which|how|is|are|was|were|does|do|did|can|could|should|would|will)\b/i;
71974
+ var EXPLAIN_RE = /\b(?:explain|describe|summarize|summarise|clarify|walk\W*me\W*through)\b/i;
71975
+ var READONLY_RE = /\b(?:find|show|list|grep|search|locate|inspect|read|check|review|where\W+is)\b/i;
71976
+ function chooseOrchestration(task, opts = {}) {
71977
+ const text = String(task ?? "").trim();
71978
+ const failClosed = () => ({
71979
+ surface: "solo",
71980
+ reason: "fail-closed default"
71981
+ });
71982
+ if (!text) return failClosed();
71983
+ for (const { re, reason } of KRAKEN_SIGNALS) {
71984
+ if (re.test(text)) return { surface: "kraken", reason };
71985
+ }
71986
+ if (QUESTION_RE.test(text)) return { surface: "solo", reason: "question-shaped task" };
71987
+ if (EXPLAIN_RE.test(text)) return { surface: "solo", reason: "explanation request" };
71988
+ if (READONLY_RE.test(text)) return { surface: "solo", reason: "read-only request" };
71989
+ const budget = opts.maxSoloChars ?? DEFAULT_MAX_SOLO_CHARS;
71990
+ if (text.length <= budget) {
71991
+ return { surface: "solo", reason: "small task, no heavy signals" };
71992
+ }
71993
+ return failClosed();
71994
+ }
71995
+
71996
+ // src/cli/runHeadless.ts
71487
71997
  init_headless();
71488
71998
  init_claudeProvider();
71489
71999
  init_skills2();
@@ -71522,12 +72032,13 @@ function createStreamScrubber2() {
71522
72032
  init_taskTool();
71523
72033
  init_sessionTodos();
71524
72034
  import { promises as fs41 } from "node:fs";
71525
- import path68 from "node:path";
72035
+ import path70 from "node:path";
71526
72036
  import { randomUUID as randomUUID8 } from "node:crypto";
71527
72037
 
71528
72038
  // src/cli/kraken/verifierLifecycle.ts
71529
72039
  init_verification2();
71530
72040
  init_krakenSelectTool();
72041
+ init_krakenModel();
71531
72042
 
71532
72043
  // src/cli/kraken/verifierResolution.ts
71533
72044
  init_providerConfig();
@@ -71571,18 +72082,61 @@ function makeVerifierCallModel(loadStream, identity, timeoutMs2 = 12e4) {
71571
72082
  return { text, provider: identity.provider, model: identity.model };
71572
72083
  };
71573
72084
  }
71574
- function resolveIdentity(selection, session) {
72085
+ function resolveIdentity(selection, session, familyCandidates, env = process.env) {
71575
72086
  if (selection.mode === "fixed") {
71576
72087
  return { provider: selection.provider, model: selection.model };
71577
72088
  }
71578
- return session && session.provider && session.model ? session : null;
72089
+ if (!session || !session.provider || !session.model) return null;
72090
+ if (familyCandidates && familyCandidates.length > 0) {
72091
+ const cross = resolveCrossModelVerifier(session, familyCandidates, env);
72092
+ if (cross) return { provider: cross.provider, model: cross.model };
72093
+ }
72094
+ return session;
72095
+ }
72096
+ function isTestEvidenceCriterion(criterionId2) {
72097
+ const id3 = criterionId2.toLowerCase();
72098
+ return ["test", "typecheck", "build", "lint"].some((k) => id3.includes(k));
72099
+ }
72100
+ function extractTestOutputExcerpt(results, maxChars = 4e3) {
72101
+ const lines = [];
72102
+ for (const r of results) {
72103
+ if (!isTestEvidenceCriterion(r.criterionId)) continue;
72104
+ lines.push([r.criterionId, r.status, r.detail].filter(Boolean).join(" \u2014 "));
72105
+ }
72106
+ if (lines.length === 0) return "";
72107
+ return lines.join("\n").slice(0, maxChars);
72108
+ }
72109
+ async function buildBlindReviewInput(evaluation, deps) {
72110
+ const results = evaluation.results ?? [];
72111
+ const passed = results.filter((r) => r.status === "pass").length;
72112
+ const verdict = evaluation.evaluation?.verdict ?? "UNKNOWN";
72113
+ const summary = `Kraken BUILD turn \u2014 deterministic evidence: ${passed}/${results.length} criteria pass, completion verdict ${verdict}.`;
72114
+ const task = deps.task?.trim();
72115
+ const testOutputExcerpt = extractTestOutputExcerpt(results);
72116
+ let diffSummary;
72117
+ try {
72118
+ const res = await (deps.getDiff ?? getWorkingDiff)({
72119
+ cwd: deps.cwd ?? process.cwd(),
72120
+ maxChars: 8e3,
72121
+ staged: true
72122
+ });
72123
+ if (res && !res.empty && res.diff) diffSummary = res.diff;
72124
+ } catch {
72125
+ }
72126
+ return {
72127
+ ...task ? { task } : {},
72128
+ summary,
72129
+ ...diffSummary !== void 0 ? { diffSummary } : {},
72130
+ ...testOutputExcerpt ? { testOutputExcerpt } : {},
72131
+ results
72132
+ };
71579
72133
  }
71580
72134
  async function runAdvisoryVerifierReview(evaluation, deps = {}) {
71581
72135
  if (!evaluation.evaluation || !evaluation.results) return null;
71582
72136
  const env = deps.env ?? process.env;
71583
72137
  const selection = deps.selection ?? loadVerifierModelSelection();
71584
72138
  if (!verifierReviewEnabled(selection, env)) return null;
71585
- const identity = resolveIdentity(selection, deps.session);
72139
+ const identity = resolveIdentity(selection, deps.session, deps.familyCandidates, env);
71586
72140
  let callModel = deps.callModel;
71587
72141
  if (!callModel) {
71588
72142
  if (!identity || !deps.loadStream) return null;
@@ -71599,11 +72153,9 @@ async function runAdvisoryVerifierReview(evaluation, deps = {}) {
71599
72153
  emit: deps.emit,
71600
72154
  env
71601
72155
  });
71602
- const passed = evaluation.results.filter((r) => r.status === "pass").length;
71603
- const summary = `Kraken BUILD turn \u2014 deterministic evidence: ${passed}/${evaluation.results.length} criteria pass, completion verdict ${evaluation.evaluation.verdict}.`;
72156
+ const blind = await buildBlindReviewInput(evaluation, deps);
71604
72157
  const review = await service.reviewCompletion({
71605
- summary,
71606
- results: evaluation.results,
72158
+ ...blind,
71607
72159
  session: deps.session
71608
72160
  });
71609
72161
  evaluation.review = review;
@@ -71917,6 +72469,13 @@ ${err.stack}` : "";
71917
72469
  model
71918
72470
  });
71919
72471
  }
72472
+ if (opts.orchestrationAuto) {
72473
+ const verdict = chooseOrchestration(opts.task ?? "");
72474
+ const line = `[orchestration] --mode auto -> surface=${verdict.surface} (${verdict.reason})`;
72475
+ if (opts.output === "json") emitEvent({ type: "log", message: line });
72476
+ else process.stderr.write(`[zelari-code --headless] ${line}
72477
+ `);
72478
+ }
71920
72479
  const mode = opts.mode ?? (opts.useCouncil ? "council" : "kraken");
71921
72480
  const profileId = resolveHeadlessProfileId(mode, opts.profile);
71922
72481
  if (opts.output === "json") {
@@ -71990,7 +72549,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
71990
72549
  try {
71991
72550
  let preflightGraph;
71992
72551
  if (opts.runPlan && opts.runPlan.trim() !== "") {
71993
- const planPath = path68.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
72552
+ const planPath = path70.join(cwd, ".zelari", "radio", `plan-${opts.runPlan}.json`);
71994
72553
  log(`loading pre-flight plan: ${planPath}`);
71995
72554
  let raw;
71996
72555
  try {
@@ -72030,8 +72589,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
72030
72589
  log(formatKrakenGraphAscii2(graph));
72031
72590
  if (opts.planOnly) {
72032
72591
  const planId = randomUUID8();
72033
- const planDir = path68.join(cwd, ".zelari", "radio");
72034
- const planPath = path68.join(planDir, `plan-${planId}.json`);
72592
+ const planDir = path70.join(cwd, ".zelari", "radio");
72593
+ const planPath = path70.join(planDir, `plan-${planId}.json`);
72035
72594
  await fs41.mkdir(planDir, { recursive: true });
72036
72595
  await fs41.writeFile(
72037
72596
  planPath,
@@ -72059,6 +72618,18 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
72059
72618
  root: cwd,
72060
72619
  audit,
72061
72620
  sessionId: sessionId2,
72621
+ // P0.4 capability inheritance: tentacles intersect the headless
72622
+ // parent policy. Headless runs are auto-allow (the same literal
72623
+ // the main headless registry below uses), so this is a no-op
72624
+ // today — wired for correctness if that default ever tightens.
72625
+ parentPolicy: {
72626
+ read: "allow",
72627
+ write: "allow",
72628
+ execute: "allow",
72629
+ network: "allow",
72630
+ ui: "allow",
72631
+ auto: true
72632
+ },
72062
72633
  // Anchor every tentacle to the SAME provider/model this run
72063
72634
  // resolved (Desktop's selector, or --provider/--model), instead
72064
72635
  // of the persisted provider.json default the factory falls back
@@ -72151,6 +72722,12 @@ async function registerHeadlessMcp(toolRegistry, opts) {
72151
72722
  }
72152
72723
  }
72153
72724
  }
72725
+ function writeProofSafe(gate, meta3, baseDir = process.cwd()) {
72726
+ return writeCompletionProof(gate, { baseDir, meta: meta3 }).then(
72727
+ () => void 0,
72728
+ () => void 0
72729
+ );
72730
+ }
72154
72731
  async function runHeadlessSingle(opts, provider, model, providerStream) {
72155
72732
  const sessionId2 = crypto.randomUUID();
72156
72733
  const memoryFactory = await Promise.resolve().then(() => (init_serviceFactory(), serviceFactory_exports));
@@ -72489,6 +73066,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
72489
73066
  let strictExit = 0;
72490
73067
  const verifierReviewDeps = {
72491
73068
  session: { provider, model },
73069
+ task: effectiveTask,
72492
73070
  loadStream: async (providerId, modelId) => {
72493
73071
  if (providerId === provider) return providerStream;
72494
73072
  try {
@@ -72516,6 +73094,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
72516
73094
  if (opts.output === "json") {
72517
73095
  emitEvent({ type: "verification_run", ...verificationPayload });
72518
73096
  }
73097
+ await writeProofSafe(strictGate, { surface: "kraken", sessionId: spine.sessionId });
72519
73098
  if (strictGate.blocked) {
72520
73099
  const repairPrompt = buildKrakenRepairPrompt(gate);
72521
73100
  if (opts.output === "json") {
@@ -72549,6 +73128,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
72549
73128
  if (opts.output === "json") {
72550
73129
  emitEvent({ type: "verification_run", ...afterPayload });
72551
73130
  }
73131
+ await writeProofSafe(after, { surface: "kraken", sessionId: spine.sessionId });
72552
73132
  if (!after.blocked) markRepairSucceeded();
72553
73133
  else {
72554
73134
  strictExit = strictGateExitCode(after);
@@ -72582,7 +73162,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
72582
73162
  if (json3) {
72583
73163
  if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
72584
73164
  else {
72585
- await fs41.mkdir(path68.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
73165
+ await fs41.mkdir(path70.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
72586
73166
  await fs41.writeFile(opts.exportSessionPath, json3, "utf8");
72587
73167
  }
72588
73168
  }
@@ -72811,7 +73391,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
72811
73391
  if (json3) {
72812
73392
  if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
72813
73393
  else {
72814
- await fs41.mkdir(path68.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
73394
+ await fs41.mkdir(path70.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
72815
73395
  await fs41.writeFile(opts.exportSessionPath, json3, "utf8");
72816
73396
  }
72817
73397
  }
@@ -73191,6 +73771,7 @@ ${ragContext}` : slicePrompt;
73191
73771
  if (opts.output === "json") {
73192
73772
  emitEvent({ type: "verification_run", ...missionVerificationPayload });
73193
73773
  }
73774
+ await writeProofSafe(missionGate, { surface: "mission", sessionId: spine.sessionId }, projectRoot);
73194
73775
  if (missionGate.blocked) {
73195
73776
  exitCode = strictGateExitCode(missionGate);
73196
73777
  spine.missionPhase("verification", "mission-strict-blocked");
@@ -73225,7 +73806,7 @@ ${ragContext}` : slicePrompt;
73225
73806
  if (json3) {
73226
73807
  if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
73227
73808
  else {
73228
- await fs41.mkdir(path68.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
73809
+ await fs41.mkdir(path70.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
73229
73810
  await fs41.writeFile(opts.exportSessionPath, json3, "utf8");
73230
73811
  }
73231
73812
  }
@@ -73427,8 +74008,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
73427
74008
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
73428
74009
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
73429
74010
  try {
73430
- const path72 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
73431
- name = path72 && /^[a-z0-9]/.test(path72) ? path72 : "imported-skill";
74011
+ const path74 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
74012
+ name = path74 && /^[a-z0-9]/.test(path74) ? path74 : "imported-skill";
73432
74013
  } catch {
73433
74014
  name = "imported-skill";
73434
74015
  }