zelari-code 2.17.0 → 2.18.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, path90) {
3292
- if (!path90)
3291
+ function getElementAtPath(obj, path91) {
3292
+ if (!path91)
3293
3293
  return obj;
3294
- return path90.reduce((acc, key) => acc?.[key], obj);
3294
+ return path91.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(path90, issues) {
3622
+ function prefixIssues(path91, issues) {
3623
3623
  return issues.map((iss) => {
3624
3624
  var _a3;
3625
3625
  (_a3 = iss).path ?? (_a3.path = []);
3626
- iss.path.unshift(path90);
3626
+ iss.path.unshift(path91);
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, path90 = []) => {
3844
+ const processError = (error52, path91 = []) => {
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 }, [...path90, ...issue2.path]));
3847
+ issue2.errors.map((issues) => processError({ issues }, [...path91, ...issue2.path]));
3848
3848
  } else if (issue2.code === "invalid_key") {
3849
- processError({ issues: issue2.issues }, [...path90, ...issue2.path]);
3849
+ processError({ issues: issue2.issues }, [...path91, ...issue2.path]);
3850
3850
  } else if (issue2.code === "invalid_element") {
3851
- processError({ issues: issue2.issues }, [...path90, ...issue2.path]);
3851
+ processError({ issues: issue2.issues }, [...path91, ...issue2.path]);
3852
3852
  } else {
3853
- const fullpath = [...path90, ...issue2.path];
3853
+ const fullpath = [...path91, ...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, path90 = []) => {
3880
+ const processError = (error52, path91 = []) => {
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 }, [...path90, ...issue2.path]));
3884
+ issue2.errors.map((issues) => processError({ issues }, [...path91, ...issue2.path]));
3885
3885
  } else if (issue2.code === "invalid_key") {
3886
- processError({ issues: issue2.issues }, [...path90, ...issue2.path]);
3886
+ processError({ issues: issue2.issues }, [...path91, ...issue2.path]);
3887
3887
  } else if (issue2.code === "invalid_element") {
3888
- processError({ issues: issue2.issues }, [...path90, ...issue2.path]);
3888
+ processError({ issues: issue2.issues }, [...path91, ...issue2.path]);
3889
3889
  } else {
3890
- const fullpath = [...path90, ...issue2.path];
3890
+ const fullpath = [...path91, ...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 path90 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
- for (const seg of path90) {
3922
+ const path91 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3923
+ for (const seg of path91) {
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 path90 = ref.slice(1).split("/").filter(Boolean);
17427
- if (path90.length === 0) {
17426
+ const path91 = ref.slice(1).split("/").filter(Boolean);
17427
+ if (path91.length === 0) {
17428
17428
  return ctx.rootSchema;
17429
17429
  }
17430
17430
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
17431
- if (path90[0] === defsKey) {
17432
- const key = path90[1];
17431
+ if (path91[0] === defsKey) {
17432
+ const key = path91[1];
17433
17433
  if (!key || !ctx.defs[key]) {
17434
17434
  throw new Error(`Reference not found: ${ref}`);
17435
17435
  }
@@ -18777,11 +18777,18 @@ function filterByInclude(entries, include) {
18777
18777
  if (include.length === 0 || include.length === 1 && include[0] === "*") {
18778
18778
  return entries.filter((e) => e.type === "file");
18779
18779
  }
18780
- const regexes = include.map(globToRegex);
18780
+ const pathRegexes = [];
18781
+ const baseRegexes = [];
18782
+ for (const g of include) {
18783
+ (g.includes("/") || g.includes("**") ? pathRegexes : baseRegexes).push(globToRegex(g));
18784
+ }
18781
18785
  return entries.filter((e) => {
18782
18786
  if (e.type !== "file")
18783
18787
  return false;
18784
- return regexes.some((re) => re.test(e.name));
18788
+ if (pathRegexes.some((re) => re.test(e.name)))
18789
+ return true;
18790
+ const base2 = e.name.slice(e.name.lastIndexOf("/") + 1);
18791
+ return baseRegexes.some((re) => re.test(base2));
18785
18792
  });
18786
18793
  }
18787
18794
  var REGEX_ESCAPE, DEFAULT_EXCLUDES;
@@ -18828,8 +18835,8 @@ function coerceStringList(value, fallback) {
18828
18835
  }
18829
18836
  return fallback;
18830
18837
  }
18831
- function hasRecursiveGlob(include) {
18832
- return include.some((g) => g.includes("**"));
18838
+ function hasPathAnchoredGlob(include) {
18839
+ return include.some((g) => g.includes("/") || g.includes("**"));
18833
18840
  }
18834
18841
  function scopeWarnings(allEntries, include, matched) {
18835
18842
  const warnings = [];
@@ -18837,15 +18844,17 @@ function scopeWarnings(allEntries, include, matched) {
18837
18844
  if (filesWalked === 0)
18838
18845
  return warnings;
18839
18846
  if (matched === 0) {
18840
- warnings.push(`SEARCH_EMPTY_SCOPE: include globs matched 0 of ${filesWalked} files walked \u2014 '*' matches only one path segment; use '**/<glob>' for recursive matching. Do not interpret this result as "pattern not found".`);
18847
+ const cause = hasPathAnchoredGlob(include) ? `path-anchored globs (with '/') match the relative path at exactly that level; use '**/<glob>' for recursive matching` : `flat globs ('*.ts') match the basename at any depth, so no file with these names/extensions exists in the walked tree`;
18848
+ warnings.push(`SEARCH_EMPTY_SCOPE: include globs matched 0 of ${filesWalked} files walked \u2014 ${cause}. Do not interpret this result as "pattern not found".`);
18841
18849
  return warnings;
18842
18850
  }
18843
- if (hasRecursiveGlob(include) || matched >= filesWalked)
18851
+ if (!hasPathAnchoredGlob(include) || matched >= filesWalked)
18844
18852
  return warnings;
18845
- const recursiveRegexes = compileGlobs(include.map((g) => `**/${g}`));
18853
+ const anchored = include.filter((g) => g.includes("/") || g.includes("**"));
18854
+ const recursiveRegexes = compileGlobs(anchored.map((g) => `**/${g}`));
18846
18855
  const wouldMatch = allEntries.filter((e) => e.type === "file" && matchesAnyCompiled(e.name, recursiveRegexes)).length;
18847
18856
  if (wouldMatch > matched) {
18848
- warnings.push(`include globs matched ${matched} of ${filesWalked} files walked \u2014 '*' matches only one path segment; a '**/*.ts'-style recursive glob would have matched ${wouldMatch - matched} more file(s) in subdirectories`);
18857
+ warnings.push(`include globs matched ${matched} of ${filesWalked} files walked \u2014 a path-anchored glob ('${anchored[0]}') matches only that exact level; a '**/${anchored[0]}'-style recursive glob would have matched ${wouldMatch - matched} more file(s) in subdirectories`);
18849
18858
  }
18850
18859
  return warnings;
18851
18860
  }
@@ -18912,7 +18921,10 @@ var init_search = __esm({
18912
18921
  /**
18913
18922
  * Glob pattern(s) to INCLUDE when path is a directory.
18914
18923
  * Accepts a string OR string[] (models often emit a bare string).
18915
- * Default ['*'] = all files. Ignored when path is a file.
18924
+ * A glob without '/' (e.g. '*.ts') matches the file basename at any depth
18925
+ * (grep --include style); a glob with '/' (e.g. 'src/*.ts') matches the
18926
+ * relative path at exactly that level. Default ['*'] = all files.
18927
+ * Ignored when path is a file.
18916
18928
  */
18917
18929
  include: stringOrStringArray.optional().default(["*"]),
18918
18930
  /**
@@ -18925,7 +18937,7 @@ var init_search = __esm({
18925
18937
  });
18926
18938
  grepContentTool = {
18927
18939
  name: "grep_content",
18928
- description: 'Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). Glob semantics: "*" matches ONE path segment only ("*.ts" does NOT match "sub/file.ts"); use "**" for recursive matching ("**/*.ts" matches at any depth). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Returns matches with line numbers and context, plus filesWalked/filesInTree counts and a warning when the include globs matched suspiciously few files.',
18940
+ description: `Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). Glob semantics (grep --include style): a glob without '/' (e.g. "*.md") matches the file basename at ANY depth; a glob with '/' (e.g. "src/*.ts") matches the relative path at exactly that level; "**" is explicit recursion ("**/*.ts" matches at any depth, same as the bare form). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Returns matches with line numbers and context, plus filesWalked/filesInTree counts and a warning when the include globs matched suspiciously few files.`,
18929
18941
  permissions: ["read"],
18930
18942
  sideEffect: "none",
18931
18943
  timeoutMs: 3e4,
@@ -19875,11 +19887,11 @@ var init_tools = __esm({
19875
19887
  if (!ctx.addDocument)
19876
19888
  return "Knowledge vault tool not available.";
19877
19889
  const title = args["title"] || "New Document";
19878
- const path90 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19890
+ const path91 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
19879
19891
  const content = args["content"] || "";
19880
19892
  const tags = args["tags"] || [];
19881
19893
  ctx.addDocument({
19882
- path: path90,
19894
+ path: path91,
19883
19895
  title,
19884
19896
  content,
19885
19897
  format: "markdown",
@@ -19888,7 +19900,7 @@ var init_tools = __esm({
19888
19900
  workspaceId: ctx.workspaceId
19889
19901
  });
19890
19902
  ctx.addActivity("vault", "created document", title);
19891
- return `Document "${title}" created at "${path90}".`;
19903
+ return `Document "${title}" created at "${path91}".`;
19892
19904
  }
19893
19905
  }
19894
19906
  ];
@@ -23328,7 +23340,7 @@ function extractToolObjects(text) {
23328
23340
  }
23329
23341
  return out;
23330
23342
  }
23331
- var BUILD_LIVENESS_RECOVERY_PROMPT, AgentHarness, DOOM_LOOP_THRESHOLD;
23343
+ var TOOL_CALL_TRUNCATED_RECOVERY_MARKER, TOOL_CALL_TRUNCATED_RECOVERY_USER, BUILD_LIVENESS_RECOVERY_PROMPT, AgentHarness, DOOM_LOOP_THRESHOLD;
23332
23344
  var init_AgentHarness = __esm({
23333
23345
  "packages/core/dist/core/AgentHarness.js"() {
23334
23346
  "use strict";
@@ -23340,6 +23352,8 @@ var init_AgentHarness = __esm({
23340
23352
  init_textLoopDetect();
23341
23353
  init_ObserverBus();
23342
23354
  init_textLoopDetect();
23355
+ TOOL_CALL_TRUNCATED_RECOVERY_MARKER = "[harness] Previous tool call was truncated";
23356
+ TOOL_CALL_TRUNCATED_RECOVERY_USER = `${TOOL_CALL_TRUNCATED_RECOVERY_MARKER} by the provider before completion (finish_reason=tool_calls but no complete tool_call arrived). Retry with a shorter payload or split the work into smaller tool calls.`;
23343
23357
  BUILD_LIVENESS_RECOVERY_PROMPT = "[build-liveness] The requested task requires an on-disk implementation, but no successful project mutation has occurred yet. Continue working. Inspect only as needed, then make the required change with an available mutating tool. Do not merely describe a patch or claim completion.";
23344
23358
  AgentHarness = class {
23345
23359
  config;
@@ -24343,7 +24357,8 @@ ${cached2}`
24343
24357
  if (turnToolResults.length > 0) {
24344
24358
  finishRef.value = "tool_calls";
24345
24359
  }
24346
- if (finishRef.value === "tool_calls" && turnToolCalls.length === 0 && turnToolResults.length === 0) {
24360
+ const truncatedToolCall = finishRef.value === "tool_calls" && turnToolCalls.length === 0 && turnToolResults.length === 0;
24361
+ if (truncatedToolCall) {
24347
24362
  const truncErr = createBrainEvent("error", this.sessionId, {
24348
24363
  severity: "recoverable",
24349
24364
  message: "Tool call was truncated (finish_reason=tool_calls but no complete tool_call received). The provider cut the response mid-arguments. Retry with a shorter payload or split the work.",
@@ -24383,6 +24398,15 @@ ${cached2}`
24383
24398
  content: tr.content
24384
24399
  });
24385
24400
  }
24401
+ if (truncatedToolCall) {
24402
+ const last = this.config.messages[this.config.messages.length - 1];
24403
+ if (!(last?.role === "user" && typeof last.content === "string" && last.content.includes(TOOL_CALL_TRUNCATED_RECOVERY_MARKER))) {
24404
+ this.config.messages.push({
24405
+ role: "user",
24406
+ content: TOOL_CALL_TRUNCATED_RECOVERY_USER
24407
+ });
24408
+ }
24409
+ }
24386
24410
  break;
24387
24411
  } else if (delta.kind === "error") {
24388
24412
  finishRef.providerError = true;
@@ -25317,6 +25341,8 @@ __export(harness_exports, {
25317
25341
  SessionJsonlWriter: () => SessionJsonlWriter,
25318
25342
  TEXT_LOOP_RECOVERY_SYSTEM: () => TEXT_LOOP_RECOVERY_SYSTEM,
25319
25343
  TEXT_LOOP_RECOVERY_USER_PROMPT: () => TEXT_LOOP_RECOVERY_USER_PROMPT,
25344
+ TOOL_CALL_TRUNCATED_RECOVERY_MARKER: () => TOOL_CALL_TRUNCATED_RECOVERY_MARKER,
25345
+ TOOL_CALL_TRUNCATED_RECOVERY_USER: () => TOOL_CALL_TRUNCATED_RECOVERY_USER,
25320
25346
  canonicalTools: () => canonicalTools,
25321
25347
  collapseLoopedAssistantText: () => collapseLoopedAssistantText,
25322
25348
  compareReplayPrefix: () => compareReplayPrefix,
@@ -26138,11 +26164,11 @@ var init_synthesisAudit = __esm({
26138
26164
  import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
26139
26165
  import { join as join4 } from "node:path";
26140
26166
  function loadNfrSpec(zelariRoot) {
26141
- const path90 = join4(zelariRoot, "nfr-spec.json");
26142
- if (!existsSync7(path90))
26167
+ const path91 = join4(zelariRoot, "nfr-spec.json");
26168
+ if (!existsSync7(path91))
26143
26169
  return null;
26144
26170
  try {
26145
- const raw = JSON.parse(readFileSync7(path90, "utf8"));
26171
+ const raw = JSON.parse(readFileSync7(path91, "utf8"));
26146
26172
  if (raw.version !== 1 || !Array.isArray(raw.targets))
26147
26173
  return null;
26148
26174
  return raw;
@@ -28448,9 +28474,9 @@ var init_types5 = __esm({
28448
28474
  import { readFileSync as readFileSync12 } from "node:fs";
28449
28475
  import { join as join10 } from "node:path";
28450
28476
  function readLessonsDeduped(zelariRoot) {
28451
- const path90 = join10(zelariRoot, LESSONS_FILE);
28477
+ const path91 = join10(zelariRoot, LESSONS_FILE);
28452
28478
  try {
28453
- const raw = readFileSync12(path90, "utf8");
28479
+ const raw = readFileSync12(path91, "utf8");
28454
28480
  const byId = /* @__PURE__ */ new Map();
28455
28481
  for (const line of raw.split(/\r?\n/)) {
28456
28482
  if (!line.trim())
@@ -28551,8 +28577,8 @@ function keywordsFrom(check2, signature) {
28551
28577
  return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
28552
28578
  }
28553
28579
  function writeLesson(zelariRoot, lesson) {
28554
- const path90 = join11(zelariRoot, LESSONS_FILE);
28555
- appendFileSync(path90, `${JSON.stringify(lesson)}
28580
+ const path91 = join11(zelariRoot, LESSONS_FILE);
28581
+ appendFileSync(path91, `${JSON.stringify(lesson)}
28556
28582
  `, "utf8");
28557
28583
  }
28558
28584
  function findSimilar(lessons, signature) {
@@ -30515,9 +30541,9 @@ function findCycle(nodes) {
30515
30541
  if (color.get(start) !== WHITE)
30516
30542
  continue;
30517
30543
  const stack = [[start, 0]];
30518
- const path90 = [];
30544
+ const path91 = [];
30519
30545
  color.set(start, GRAY);
30520
- path90.push(start);
30546
+ path91.push(start);
30521
30547
  while (stack.length > 0) {
30522
30548
  const top = stack[stack.length - 1];
30523
30549
  const [id3, idx] = top;
@@ -30530,17 +30556,17 @@ function findCycle(nodes) {
30530
30556
  continue;
30531
30557
  const c = color.get(dep);
30532
30558
  if (c === GRAY) {
30533
- const at = path90.indexOf(dep);
30534
- return [...path90.slice(at), dep];
30559
+ const at = path91.indexOf(dep);
30560
+ return [...path91.slice(at), dep];
30535
30561
  }
30536
30562
  if (c === WHITE) {
30537
30563
  color.set(dep, GRAY);
30538
- path90.push(dep);
30564
+ path91.push(dep);
30539
30565
  stack.push([dep, 0]);
30540
30566
  }
30541
30567
  } else {
30542
30568
  color.set(id3, BLACK);
30543
- path90.pop();
30569
+ path91.pop();
30544
30570
  stack.pop();
30545
30571
  }
30546
30572
  }
@@ -31460,8 +31486,8 @@ var init_runner = __esm({
31460
31486
  failed: [...this.tentaclesById.values()].filter((r) => r.status === "error"),
31461
31487
  pending: []
31462
31488
  };
31463
- const path90 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
31464
- this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path90}`);
31489
+ const path91 = await this.host.saveSnapshot(snapshot, ".zelari/kraken/snapshots");
31490
+ this.host.log(`checkpoint: ${label ?? "auto"} \u2192 ${path91}`);
31465
31491
  return snapshot;
31466
31492
  }
31467
31493
  callLog(msg, data) {
@@ -31587,6 +31613,15 @@ var init_types9 = __esm({
31587
31613
  "resource.reserve_entered",
31588
31614
  // 2.6.1 (closure plan §9): hard-limit overrun telemetry — state-only.
31589
31615
  "resource.overrun",
31616
+ // ADR-0024 v1.1 (amended 2026-08-30): per-node ENVELOPE for kraken-graph
31617
+ // runs — written by the HOST (the sole spine writer) around the executor's
31618
+ // tentacle-run seam. State-only (never model-surface): data = {nodeId,
31619
+ // agent, graphId?, ok?, cancelled?, durationMs?}; node labels, prompts and
31620
+ // turn output stay on the kraken radio JSONL. One pair per attempt.
31621
+ // ADR-0021 schema review: additive state kinds need no SCHEMA_VERSION bump —
31622
+ // older readers skip them via the tolerant replay (schema-mismatch issues).
31623
+ "graph.node_started",
31624
+ "graph.node_ended",
31590
31625
  "note"
31591
31626
  ];
31592
31627
  SessionEventEnvelopeSchema = external_exports.object({
@@ -33912,16 +33947,16 @@ function runRetentionFromEnv() {
33912
33947
  maxTotalBytes: Number.isFinite(parseMb) && parseMb > 0 ? Math.round(parseMb * 1024 * 1024) : DEFAULT_RUN_RETENTION_MAX_MB * 1024 * 1024
33913
33948
  };
33914
33949
  }
33915
- async function dirSize(path90) {
33950
+ async function dirSize(path91) {
33916
33951
  let total = 0;
33917
33952
  let entries;
33918
33953
  try {
33919
- entries = await readdir(path90, { withFileTypes: true });
33954
+ entries = await readdir(path91, { withFileTypes: true });
33920
33955
  } catch {
33921
33956
  return 0;
33922
33957
  }
33923
33958
  for (const entry of entries) {
33924
- const child = join13(path90, entry.name);
33959
+ const child = join13(path91, entry.name);
33925
33960
  if (entry.isDirectory())
33926
33961
  total += await dirSize(child);
33927
33962
  else {
@@ -33948,19 +33983,19 @@ async function enforceRunRetention(runsDir, options = {}) {
33948
33983
  for (const entry of entries) {
33949
33984
  if (!entry.isDirectory())
33950
33985
  continue;
33951
- const path90 = join13(runsDir, entry.name);
33986
+ const path91 = join13(runsDir, entry.name);
33952
33987
  let startedAt = 0;
33953
33988
  let endedAt;
33954
33989
  let completed = false;
33955
33990
  try {
33956
- const manifest = JSON.parse(await readFile(join13(path90, "manifest.json"), "utf8"));
33991
+ const manifest = JSON.parse(await readFile(join13(path91, "manifest.json"), "utf8"));
33957
33992
  startedAt = manifest.startedAt ?? 0;
33958
33993
  endedAt = manifest.endedAt;
33959
33994
  completed = Boolean(endedAt) && manifest.status !== "running";
33960
33995
  } catch {
33961
33996
  completed = false;
33962
33997
  }
33963
- infos.push({ name: entry.name, path: path90, startedAt, endedAt, completed, bytes: await dirSize(path90) });
33998
+ infos.push({ name: entry.name, path: path91, startedAt, endedAt, completed, bytes: await dirSize(path91) });
33964
33999
  }
33965
34000
  const remove = async (info) => {
33966
34001
  await rm(info.path, { recursive: true, force: true });
@@ -34732,12 +34767,12 @@ var init_engine = __esm({
34732
34767
  * content digest) and the returned ref carries the event seq when the
34733
34768
  * emitter resolved one.
34734
34769
  */
34735
- async fsEvidence(observation, path90, sha256, content, extra = {}) {
34770
+ async fsEvidence(observation, path91, sha256, content, extra = {}) {
34736
34771
  const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
34737
- const seq = await this.emitEvidence({ observation, path: path90, ...extra, ...digest ? { digest } : {} });
34772
+ const seq = await this.emitEvidence({ observation, path: path91, ...extra, ...digest ? { digest } : {} });
34738
34773
  return {
34739
34774
  tier: "fs-observation",
34740
- ref: path90,
34775
+ ref: path91,
34741
34776
  capturedAt: Date.now(),
34742
34777
  ...digest ? { digest } : {},
34743
34778
  ...seq !== void 0 ? { seq } : {}
@@ -35777,6 +35812,8 @@ __export(dist_exports, {
35777
35812
  TEXT_LOOP_RECOVERY_SYSTEM: () => TEXT_LOOP_RECOVERY_SYSTEM,
35778
35813
  TEXT_LOOP_RECOVERY_USER_PROMPT: () => TEXT_LOOP_RECOVERY_USER_PROMPT,
35779
35814
  TIER_RANK: () => TIER_RANK,
35815
+ TOOL_CALL_TRUNCATED_RECOVERY_MARKER: () => TOOL_CALL_TRUNCATED_RECOVERY_MARKER,
35816
+ TOOL_CALL_TRUNCATED_RECOVERY_USER: () => TOOL_CALL_TRUNCATED_RECOVERY_USER,
35780
35817
  TOOL_DEFINITIONS: () => TOOL_DEFINITIONS,
35781
35818
  TOOL_USE_PROTOCOL_DIRECTIVE: () => TOOL_USE_PROTOCOL_DIRECTIVE,
35782
35819
  TURN_COMPLETION_MODULE: () => TURN_COMPLETION_MODULE,
@@ -37078,6 +37115,15 @@ function mapBrainEventToSpine(ev) {
37078
37115
  ...ev.memberName ? { member: ev.memberName } : {}
37079
37116
  }
37080
37117
  };
37118
+ case "error":
37119
+ if (ev.code === "tool_call_truncated") {
37120
+ return {
37121
+ kind: "user.message",
37122
+ actor: ACTOR_USER,
37123
+ data: { text: TOOL_CALL_TRUNCATED_RECOVERY_USER }
37124
+ };
37125
+ }
37126
+ return null;
37081
37127
  default:
37082
37128
  return null;
37083
37129
  }
@@ -37134,6 +37180,7 @@ var MAX_STREAM_BUFFERS, SessionSpineMirror, SpineMirroringWriter;
37134
37180
  var init_sessionSpine = __esm({
37135
37181
  "src/cli/sessionSpine.ts"() {
37136
37182
  "use strict";
37183
+ init_harness();
37137
37184
  init_session();
37138
37185
  init_session();
37139
37186
  init_verification2();
@@ -37880,9 +37927,9 @@ function spillToolOutput(fullText, meta3) {
37880
37927
  const rnd = randomBytes3(3).toString("hex");
37881
37928
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
37882
37929
  const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
37883
- const path90 = join14(dir, file2);
37884
- writeFileSync11(path90, fullText, "utf8");
37885
- return path90;
37930
+ const path91 = join14(dir, file2);
37931
+ writeFileSync11(path91, fullText, "utf8");
37932
+ return path91;
37886
37933
  } catch {
37887
37934
  return null;
37888
37935
  }
@@ -37928,10 +37975,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
37928
37975
  ${tail2}`;
37929
37976
  }
37930
37977
  if (doSpill) {
37931
- const path90 = spillToolOutput(text, { toolName: opts.toolName });
37932
- if (path90) {
37978
+ const path91 = spillToolOutput(text, { toolName: opts.toolName });
37979
+ if (path91) {
37933
37980
  const spillNote = `
37934
- \u2026 [full output spilled to: ${path90} \u2014 re-read with read_file if you need the complete text] \u2026`;
37981
+ \u2026 [full output spilled to: ${path91} \u2014 re-read with read_file if you need the complete text] \u2026`;
37935
37982
  if (preview.includes("] \u2026\n")) {
37936
37983
  preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
37937
37984
  `);
@@ -41044,28 +41091,28 @@ var init_storage = __esm({
41044
41091
  VALID_SCALARS = /^(true|false|null|~)$/i;
41045
41092
  Storage = class {
41046
41093
  /** Read a Markdown file with frontmatter. Throws if not found. */
41047
- read(path90) {
41048
- if (!existsSync19(path90)) {
41049
- throw new Error(`File not found: ${path90}`);
41094
+ read(path91) {
41095
+ if (!existsSync19(path91)) {
41096
+ throw new Error(`File not found: ${path91}`);
41050
41097
  }
41051
- const md = readFileSync16(path90, "utf8");
41098
+ const md = readFileSync16(path91, "utf8");
41052
41099
  return parseFrontmatter(md);
41053
41100
  }
41054
41101
  /** Read a Markdown file; returns null if not found. */
41055
- readIfExists(path90) {
41056
- if (!existsSync19(path90)) return null;
41057
- return this.read(path90);
41102
+ readIfExists(path91) {
41103
+ if (!existsSync19(path91)) return null;
41104
+ return this.read(path91);
41058
41105
  }
41059
41106
  /**
41060
41107
  * Write a Markdown file atomically (tmp + rename). Creates parent dirs.
41061
41108
  * The meta object is serialized as YAML frontmatter; body as Markdown.
41062
41109
  */
41063
- write(path90, meta3, body) {
41064
- mkdirSync10(dirname2(path90), { recursive: true });
41065
- const tmp = path90 + ".tmp-" + process.pid;
41110
+ write(path91, meta3, body) {
41111
+ mkdirSync10(dirname2(path91), { recursive: true });
41112
+ const tmp = path91 + ".tmp-" + process.pid;
41066
41113
  const md = serializeFrontmatter(meta3, body);
41067
41114
  writeFileSync13(tmp, md, "utf8");
41068
- renameSync(tmp, path90);
41115
+ renameSync(tmp, path91);
41069
41116
  }
41070
41117
  /** List all .md files in a directory (non-recursive). */
41071
41118
  listMarkdown(dir) {
@@ -41127,8 +41174,8 @@ function nextPlanTaskId(store6) {
41127
41174
  return `t${store6.counter}`;
41128
41175
  }
41129
41176
  function writePlanTaskArtifact(rootDir, task) {
41130
- const path90 = join18(rootDir, "plan-tasks", `${task.id}.md`);
41131
- mkdirSync11(dirname3(path90), { recursive: true });
41177
+ const path91 = join18(rootDir, "plan-tasks", `${task.id}.md`);
41178
+ mkdirSync11(dirname3(path91), { recursive: true });
41132
41179
  const meta3 = {
41133
41180
  kind: "task",
41134
41181
  id: task.id,
@@ -41149,7 +41196,7 @@ function writePlanTaskArtifact(rootDir, task) {
41149
41196
  task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
41150
41197
  ""
41151
41198
  ].filter((l) => l !== null).join("\n");
41152
- new Storage().write(path90, meta3, body);
41199
+ new Storage().write(path91, meta3, body);
41153
41200
  }
41154
41201
  function loadHandle(rootDir) {
41155
41202
  const jsonPath = join18(rootDir, "plan.json");
@@ -44930,21 +44977,21 @@ function normalizeAuth(auth) {
44930
44977
  return "agent";
44931
44978
  }
44932
44979
  function readSecrets() {
44933
- const path90 = getSshSecretsPath();
44934
- if (!existsSync25(path90)) return {};
44980
+ const path91 = getSshSecretsPath();
44981
+ if (!existsSync25(path91)) return {};
44935
44982
  try {
44936
- return JSON.parse(readFileSync20(path90, "utf8"));
44983
+ return JSON.parse(readFileSync20(path91, "utf8"));
44937
44984
  } catch {
44938
44985
  return {};
44939
44986
  }
44940
44987
  }
44941
44988
  function writeSecrets(data) {
44942
- const path90 = getSshSecretsPath();
44943
- mkdirSync12(dirname4(path90), { recursive: true });
44944
- writeFileSync15(path90, `${JSON.stringify(data, null, 2)}
44989
+ const path91 = getSshSecretsPath();
44990
+ mkdirSync12(dirname4(path91), { recursive: true });
44991
+ writeFileSync15(path91, `${JSON.stringify(data, null, 2)}
44945
44992
  `, "utf8");
44946
44993
  try {
44947
- chmodSync(path90, 384);
44994
+ chmodSync(path91, 384);
44948
44995
  } catch {
44949
44996
  }
44950
44997
  }
@@ -44973,10 +45020,10 @@ function deleteSshPassword(id3) {
44973
45020
  writeSecrets({ passwords });
44974
45021
  }
44975
45022
  function readStore2() {
44976
- const path90 = getSshTargetsPath();
44977
- if (!existsSync25(path90)) return [];
45023
+ const path91 = getSshTargetsPath();
45024
+ if (!existsSync25(path91)) return [];
44978
45025
  try {
44979
- const parsed = JSON.parse(readFileSync20(path90, "utf8"));
45026
+ const parsed = JSON.parse(readFileSync20(path91, "utf8"));
44980
45027
  const list = Array.isArray(parsed.targets) ? parsed.targets : [];
44981
45028
  return list.filter(
44982
45029
  (t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
@@ -44991,11 +45038,11 @@ function readStore2() {
44991
45038
  }
44992
45039
  }
44993
45040
  function writeStore2(targets) {
44994
- const path90 = getSshTargetsPath();
44995
- mkdirSync12(dirname4(path90), { recursive: true });
45041
+ const path91 = getSshTargetsPath();
45042
+ mkdirSync12(dirname4(path91), { recursive: true });
44996
45043
  const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
44997
45044
  writeFileSync15(
44998
- path90,
45045
+ path91,
44999
45046
  `${JSON.stringify({ targets: clean }, null, 2)}
45000
45047
  `,
45001
45048
  "utf8"
@@ -45241,11 +45288,11 @@ function formatSshTargetsForPrompt() {
45241
45288
  ];
45242
45289
  for (const t of targets) {
45243
45290
  const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
45244
- const path90 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
45291
+ const path91 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
45245
45292
  const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
45246
45293
  const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
45247
45294
  lines.push(
45248
- `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path90}${tags}${allow}`
45295
+ `- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path91}${tags}${allow}`
45249
45296
  );
45250
45297
  }
45251
45298
  return lines.join("\n");
@@ -54236,8 +54283,8 @@ function readPlan(ctx) {
54236
54283
  } catch {
54237
54284
  }
54238
54285
  }
54239
- const path90 = workspaceFile(ctx.rootDir, "plan");
54240
- const doc = ctx.storage.readIfExists(path90);
54286
+ const path91 = workspaceFile(ctx.rootDir, "plan");
54287
+ const doc = ctx.storage.readIfExists(path91);
54241
54288
  if (!doc) return { phases: [], tasks: [], milestones: [] };
54242
54289
  const meta3 = doc.meta;
54243
54290
  return {
@@ -54415,7 +54462,7 @@ function addMilestoneRecord(ctx, summary, input) {
54415
54462
  dueDate: input.dueDate,
54416
54463
  targetVersion: version2
54417
54464
  });
54418
- const path90 = join31(ctx.rootDir, "milestones", `${id3}.md`);
54465
+ const path91 = join31(ctx.rootDir, "milestones", `${id3}.md`);
54419
54466
  const meta3 = {
54420
54467
  kind: "milestone",
54421
54468
  id: id3,
@@ -54432,7 +54479,7 @@ function addMilestoneRecord(ctx, summary, input) {
54432
54479
  `Target version: ${version2}`,
54433
54480
  ""
54434
54481
  ].join("\n");
54435
- ctx.storage.write(path90, meta3, body);
54482
+ ctx.storage.write(path91, meta3, body);
54436
54483
  return { id: id3, created: true };
54437
54484
  }
54438
54485
  function readPlanSummary(ctx) {
@@ -54636,7 +54683,7 @@ function addIdeaStub(ctx) {
54636
54683
  const tags = args["tags"] ?? [];
54637
54684
  const category = args["category"] ?? "General";
54638
54685
  const id3 = `${nextAdrId(ctx)}-${slugify3(title)}`;
54639
- const path90 = workspaceArtifact(ctx.rootDir, "decisions", id3);
54686
+ const path91 = workspaceArtifact(ctx.rootDir, "decisions", id3);
54640
54687
  const meta3 = {
54641
54688
  kind: "adr",
54642
54689
  status: "proposed",
@@ -54662,7 +54709,7 @@ function addIdeaStub(ctx) {
54662
54709
  ...consequences.map((c) => `- ${c}`),
54663
54710
  ""
54664
54711
  ].join("\n");
54665
- ctx.storage.write(path90, meta3, body);
54712
+ ctx.storage.write(path91, meta3, body);
54666
54713
  return `ADR ${id3} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
54667
54714
  });
54668
54715
  }
@@ -54744,14 +54791,14 @@ function createDocumentStub(ctx) {
54744
54791
  ctx.storage.write(risksPath, riskMeta, content);
54745
54792
  return `Document "${title}" created at risks.md (workspace root).`;
54746
54793
  }
54747
- const path90 = workspaceArtifact(ctx.rootDir, "docs", slug);
54794
+ const path91 = workspaceArtifact(ctx.rootDir, "docs", slug);
54748
54795
  const meta3 = {
54749
54796
  kind: "doc",
54750
54797
  id: slug,
54751
54798
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
54752
54799
  tags
54753
54800
  };
54754
- ctx.storage.write(path90, meta3, content);
54801
+ ctx.storage.write(path91, meta3, content);
54755
54802
  return `Document "${title}" created at docs/${slug}.md.`;
54756
54803
  });
54757
54804
  }
@@ -55138,10 +55185,10 @@ function getUserMcpPath() {
55138
55185
  function getProjectMcpPath(projectRoot) {
55139
55186
  return join32(projectRoot, ".zelari", "mcp.json");
55140
55187
  }
55141
- function readFile6(path90) {
55142
- if (!existsSync37(path90)) return {};
55188
+ function readFile6(path91) {
55189
+ if (!existsSync37(path91)) return {};
55143
55190
  try {
55144
- const parsed = JSON.parse(readFileSync29(path90, "utf8"));
55191
+ const parsed = JSON.parse(readFileSync29(path91, "utf8"));
55145
55192
  const out = {};
55146
55193
  for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
55147
55194
  if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
@@ -55157,10 +55204,10 @@ function readFile6(path90) {
55157
55204
  return {};
55158
55205
  }
55159
55206
  }
55160
- function writeFile2(path90, servers) {
55161
- mkdirSync16(dirname9(path90), { recursive: true });
55207
+ function writeFile2(path91, servers) {
55208
+ mkdirSync16(dirname9(path91), { recursive: true });
55162
55209
  const body = { mcpServers: servers };
55163
- writeFileSync18(path90, `${JSON.stringify(body, null, 2)}
55210
+ writeFileSync18(path91, `${JSON.stringify(body, null, 2)}
55164
55211
  `, "utf8");
55165
55212
  }
55166
55213
  function listMcpServers(projectRoot) {
@@ -55193,9 +55240,9 @@ function upsertMcpServer(opts) {
55193
55240
  if (!opts.config.command?.trim()) {
55194
55241
  return { ok: false, error: "command is required" };
55195
55242
  }
55196
- let path90;
55243
+ let path91;
55197
55244
  if (opts.scope === "user") {
55198
- path90 = getUserMcpPath();
55245
+ path91 = getUserMcpPath();
55199
55246
  } else {
55200
55247
  const root = opts.projectRoot?.trim();
55201
55248
  if (!root) {
@@ -55204,30 +55251,30 @@ function upsertMcpServer(opts) {
55204
55251
  error: "projectRoot required for project scope (Open Folder first)"
55205
55252
  };
55206
55253
  }
55207
- path90 = getProjectMcpPath(root);
55254
+ path91 = getProjectMcpPath(root);
55208
55255
  }
55209
- const current = readFile6(path90);
55256
+ const current = readFile6(path91);
55210
55257
  current[name] = {
55211
55258
  command: opts.config.command.trim(),
55212
55259
  args: opts.config.args,
55213
55260
  env: opts.config.env,
55214
55261
  enabled: opts.config.enabled !== false
55215
55262
  };
55216
- writeFile2(path90, current);
55217
- return { ok: true, path: path90 };
55263
+ writeFile2(path91, current);
55264
+ return { ok: true, path: path91 };
55218
55265
  }
55219
55266
  function removeMcpServer(opts) {
55220
- const path90 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
55221
- if (!path90) {
55267
+ const path91 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
55268
+ if (!path91) {
55222
55269
  return { ok: false, error: "projectRoot required for project scope" };
55223
55270
  }
55224
- const current = readFile6(path90);
55271
+ const current = readFile6(path91);
55225
55272
  if (!(opts.name in current)) {
55226
- return { ok: false, error: `Server "${opts.name}" not found in ${path90}` };
55273
+ return { ok: false, error: `Server "${opts.name}" not found in ${path91}` };
55227
55274
  }
55228
55275
  delete current[opts.name];
55229
- writeFile2(path90, current);
55230
- return { ok: true, path: path90 };
55276
+ writeFile2(path91, current);
55277
+ return { ok: true, path: path91 };
55231
55278
  }
55232
55279
  var init_mcpConfigIo = __esm({
55233
55280
  "src/cli/mcp/mcpConfigIo.ts"() {
@@ -55628,10 +55675,10 @@ import { createHash as createHash20 } from "node:crypto";
55628
55675
  import { join as join34 } from "node:path";
55629
55676
  import { readFile as readFile7 } from "node:fs/promises";
55630
55677
  async function readPackageJson3(projectRoot) {
55631
- const path90 = join34(projectRoot, "package.json");
55632
- if (!existsSync39(path90)) return null;
55678
+ const path91 = join34(projectRoot, "package.json");
55679
+ if (!existsSync39(path91)) return null;
55633
55680
  try {
55634
- return JSON.parse(await readFile7(path90, "utf8"));
55681
+ return JSON.parse(await readFile7(path91, "utf8"));
55635
55682
  } catch {
55636
55683
  return null;
55637
55684
  }
@@ -55713,9 +55760,9 @@ async function genBuild(ctx) {
55713
55760
  ].join("\n");
55714
55761
  }
55715
55762
  async function genOpenQuestions(ctx) {
55716
- const path90 = join34(ctx.rootDir, "risks.md");
55717
- if (!existsSync39(path90)) return "_No open questions._";
55718
- const content = readFileSync31(path90, "utf8");
55763
+ const path91 = join34(ctx.rootDir, "risks.md");
55764
+ if (!existsSync39(path91)) return "_No open questions._";
55765
+ const content = readFileSync31(path91, "utf8");
55719
55766
  const lines = content.split("\n");
55720
55767
  const questions = [];
55721
55768
  let currentTitle = "";
@@ -55989,9 +56036,9 @@ function versionKey(value) {
55989
56036
  function firstString2(v) {
55990
56037
  return typeof v === "string" && v.trim().length > 0 ? v : null;
55991
56038
  }
55992
- function readFileSyncSafe(path90) {
56039
+ function readFileSyncSafe(path91) {
55993
56040
  try {
55994
- return readFileSync32(path90, "utf8");
56041
+ return readFileSync32(path91, "utf8");
55995
56042
  } catch {
55996
56043
  return null;
55997
56044
  }
@@ -56454,8 +56501,8 @@ async function runPostCouncilHook(ctx, options) {
56454
56501
  sources: scope.sources
56455
56502
  } : void 0
56456
56503
  });
56457
- const path90 = writeCouncilCompletion(ctx.rootDir, completion);
56458
- completionHook = { ran: true, path: path90, completion };
56504
+ const path91 = writeCouncilCompletion(ctx.rootDir, completion);
56505
+ completionHook = { ran: true, path: path91, completion };
56459
56506
  } catch (err) {
56460
56507
  completionHook = {
56461
56508
  ran: true,
@@ -57791,7 +57838,7 @@ function safeSocketPath(socketPath) {
57791
57838
  return socketPath.trim();
57792
57839
  }
57793
57840
  function startPermissionBroker(socketPath, handlers, opts) {
57794
- const path90 = safeSocketPath(socketPath);
57841
+ const path91 = safeSocketPath(socketPath);
57795
57842
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
57796
57843
  const sockets = /* @__PURE__ */ new Set();
57797
57844
  const server = createServer2((socket) => {
@@ -57891,10 +57938,10 @@ function startPermissionBroker(socketPath, handlers, opts) {
57891
57938
  return new Promise((resolve9, reject) => {
57892
57939
  const onError = (err) => reject(err);
57893
57940
  server.once("error", onError);
57894
- server.listen(path90, () => {
57941
+ server.listen(path91, () => {
57895
57942
  server.removeListener("error", onError);
57896
57943
  resolve9({
57897
- socketPath: path90,
57944
+ socketPath: path91,
57898
57945
  stop: () => new Promise((res) => {
57899
57946
  for (const s of sockets) s.destroy();
57900
57947
  sockets.clear();
@@ -57905,7 +57952,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
57905
57952
  if (done) return;
57906
57953
  done = true;
57907
57954
  if (process.platform !== "win32") {
57908
- unlink(path90, () => res());
57955
+ unlink(path91, () => res());
57909
57956
  } else {
57910
57957
  res();
57911
57958
  }
@@ -57918,11 +57965,11 @@ function startPermissionBroker(socketPath, handlers, opts) {
57918
57965
  });
57919
57966
  }
57920
57967
  function requestBrokerAsk(socketPath, ask, opts) {
57921
- const path90 = safeSocketPath(socketPath);
57968
+ const path91 = safeSocketPath(socketPath);
57922
57969
  const requestTimeoutMs = opts?.requestTimeoutMs ?? PERMISSION_BROKER_DEFAULT_TIMEOUT_MS;
57923
57970
  const connectTimeoutMs = opts?.connectTimeoutMs ?? PERMISSION_BROKER_DEFAULT_CONNECT_TIMEOUT_MS;
57924
57971
  return new Promise((resolve9, reject) => {
57925
- const socket = connect(path90);
57972
+ const socket = connect(path91);
57926
57973
  let buffer = "";
57927
57974
  let settled = false;
57928
57975
  const settle = (fn) => {
@@ -57937,7 +57984,7 @@ function requestBrokerAsk(socketPath, ask, opts) {
57937
57984
  settle(
57938
57985
  () => reject(
57939
57986
  new Error(
57940
- `permission broker unavailable at "${path90}" (connect timed out after ${connectTimeoutMs}ms)`
57987
+ `permission broker unavailable at "${path91}" (connect timed out after ${connectTimeoutMs}ms)`
57941
57988
  )
57942
57989
  )
57943
57990
  );
@@ -58843,6 +58890,12 @@ var init_graphMemory = __esm({
58843
58890
  });
58844
58891
 
58845
58892
  // src/cli/kraken/tentacle.ts
58893
+ var tentacle_exports = {};
58894
+ __export(tentacle_exports, {
58895
+ TASK_TOOL_TIMEOUT_MS: () => TASK_TOOL_TIMEOUT_MS,
58896
+ runSubAgent: () => runSubAgent,
58897
+ runTentacle: () => runTentacle
58898
+ });
58846
58899
  var init_tentacle = __esm({
58847
58900
  "src/cli/kraken/tentacle.ts"() {
58848
58901
  "use strict";
@@ -65742,6 +65795,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
65742
65795
  return exitCode;
65743
65796
  }
65744
65797
  const audit = new AuditLogger2();
65798
+ const { runTentacle: runTentacle2 } = await Promise.resolve().then(() => (init_tentacle(), tentacle_exports));
65745
65799
  const executor = new KrakenGraphExecutor2({
65746
65800
  taskToolDeps: {
65747
65801
  createSubAgentContext: createKrakenSubAgentContextFactory2({
@@ -65774,7 +65828,11 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
65774
65828
  parentCwd: cwd,
65775
65829
  sessionId: sessionId2,
65776
65830
  goal: prompt,
65777
- signal: abort.signal
65831
+ signal: abort.signal,
65832
+ // ADR-0024 v1.1: same delegate (`runTentacle`), wrapped so each node
65833
+ // turn leaves a graph.node_started / graph.node_ended envelope pair on
65834
+ // the spine. The executor owns scheduling; the HOST owns the spine.
65835
+ runTentacleFn: (runOpts) => nodeSpineEnvelopeRun(spine, runOpts, () => runTentacle2(runOpts))
65778
65836
  });
65779
65837
  const summary = await executor.execute(graph);
65780
65838
  if (summary.cancelled) log("graph cancelled \u2014 partial results below");
@@ -65819,6 +65877,37 @@ ${formatKrakenGraphDigest2(
65819
65877
  await emitHarnessStateEvent({ spine, workspaceRoot: cwd, output: opts.output, emitEvent });
65820
65878
  }
65821
65879
  }
65880
+ function nodeSpineEnvelopeRun(spine, runOpts, run) {
65881
+ const startedAt = Date.now();
65882
+ const noteNode = (kind2, extra) => {
65883
+ if (!runOpts.nodeId) return;
65884
+ void spine.appendEvent({
65885
+ kind: kind2,
65886
+ actor: { type: "system" },
65887
+ data: {
65888
+ nodeId: runOpts.nodeId,
65889
+ agent: runOpts.agent,
65890
+ ...runOpts.graphId ? { graphId: runOpts.graphId } : {},
65891
+ ...extra
65892
+ }
65893
+ });
65894
+ };
65895
+ noteNode("graph.node_started");
65896
+ return run().then(
65897
+ (res) => {
65898
+ noteNode("graph.node_ended", {
65899
+ ok: res.ok,
65900
+ ...!res.ok && res.cancelled ? { cancelled: true } : {},
65901
+ durationMs: Date.now() - startedAt
65902
+ });
65903
+ return res;
65904
+ },
65905
+ (err) => {
65906
+ noteNode("graph.node_ended", { ok: false, durationMs: Date.now() - startedAt });
65907
+ throw err;
65908
+ }
65909
+ );
65910
+ }
65822
65911
  async function buildCouncilToolRegistry(planMode, opts, memoryService, memoryAutoWrite = false, extras) {
65823
65912
  const cwd = opts ? resolveHeadlessCwd(opts) : process.cwd();
65824
65913
  const { registry: toolRegistry } = createBuiltinToolRegistry({
@@ -67062,7 +67151,7 @@ function upsertSkill(opts) {
67062
67151
  }
67063
67152
  dir = getProjectSkillsDir(root);
67064
67153
  }
67065
- const path90 = skillFilePath(dir, name);
67154
+ const path91 = skillFilePath(dir, name);
67066
67155
  const content = serializeSkillMd({
67067
67156
  name,
67068
67157
  description,
@@ -67071,13 +67160,13 @@ function upsertSkill(opts) {
67071
67160
  tools: opts.tools,
67072
67161
  cost: opts.cost
67073
67162
  });
67074
- const parsed = parseSkillMd(content, path90);
67163
+ const parsed = parseSkillMd(content, path91);
67075
67164
  if (!parsed) {
67076
67165
  return { ok: false, error: "Generated SKILL.md failed validation" };
67077
67166
  }
67078
- mkdirSync21(dirname13(path90), { recursive: true });
67079
- writeFileSync24(path90, content, "utf8");
67080
- return { ok: true, path: path90 };
67167
+ mkdirSync21(dirname13(path91), { recursive: true });
67168
+ writeFileSync24(path91, content, "utf8");
67169
+ return { ok: true, path: path91 };
67081
67170
  }
67082
67171
  function removeSkill(opts) {
67083
67172
  const name = opts.name.trim().toLowerCase();
@@ -67095,8 +67184,8 @@ function removeSkill(opts) {
67095
67184
  dir = getProjectSkillsDir(root);
67096
67185
  }
67097
67186
  const skillDir = join46(dir, name);
67098
- const path90 = skillFilePath(dir, name);
67099
- if (!existsSync52(path90) && !existsSync52(skillDir)) {
67187
+ const path91 = skillFilePath(dir, name);
67188
+ if (!existsSync52(path91) && !existsSync52(skillDir)) {
67100
67189
  return { ok: false, error: `Skill "${name}" not found in ${dir}` };
67101
67190
  }
67102
67191
  try {
@@ -67107,7 +67196,7 @@ function removeSkill(opts) {
67107
67196
  error: err instanceof Error ? err.message : String(err)
67108
67197
  };
67109
67198
  }
67110
- return { ok: true, path: path90 };
67199
+ return { ok: true, path: path91 };
67111
67200
  }
67112
67201
  var NAME_RE, BUILTIN_SKILL_MODULES, builtinsLoaded;
67113
67202
  var init_skillConfigIo = __esm({
@@ -67925,12 +68014,12 @@ function ensureHome() {
67925
68014
  }
67926
68015
  }
67927
68016
  function loadCompanionConfig() {
67928
- const path90 = getCompanionConfigPath();
67929
- if (!existsSync53(path90)) {
68017
+ const path91 = getCompanionConfigPath();
68018
+ if (!existsSync53(path91)) {
67930
68019
  return { projects: [] };
67931
68020
  }
67932
68021
  try {
67933
- const raw = JSON.parse(readFileSync41(path90, "utf8"));
68022
+ const raw = JSON.parse(readFileSync41(path91, "utf8"));
67934
68023
  const projects = Array.isArray(raw.projects) ? raw.projects.filter(
67935
68024
  (p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
67936
68025
  ).map((p3) => ({
@@ -67968,16 +68057,16 @@ function loadOrCreateToken(explicit) {
67968
68057
  return { token: explicit.trim(), created: false };
67969
68058
  }
67970
68059
  ensureHome();
67971
- const path90 = getCompanionTokenPath();
67972
- if (existsSync53(path90)) {
67973
- const t = readFileSync41(path90, "utf8").trim();
68060
+ const path91 = getCompanionTokenPath();
68061
+ if (existsSync53(path91)) {
68062
+ const t = readFileSync41(path91, "utf8").trim();
67974
68063
  if (t) return { token: t, created: false };
67975
68064
  }
67976
68065
  const token = randomBytes7(24).toString("base64url");
67977
- writeFileSync25(path90, token + "\n", "utf8");
68066
+ writeFileSync25(path91, token + "\n", "utf8");
67978
68067
  try {
67979
68068
  const fs45 = __require("node:fs");
67980
- fs45.chmodSync?.(path90, 384);
68069
+ fs45.chmodSync?.(path91, 384);
67981
68070
  } catch {
67982
68071
  }
67983
68072
  return { token, created: true };
@@ -68002,17 +68091,17 @@ function mergeProjects(cfg, extraPaths) {
68002
68091
  byId.set(p3.id, p3);
68003
68092
  }
68004
68093
  for (const raw of extraPaths) {
68005
- const path90 = raw.trim();
68006
- if (!path90) continue;
68007
- let id3 = slugFromPath(path90);
68094
+ const path91 = raw.trim();
68095
+ if (!path91) continue;
68096
+ let id3 = slugFromPath(path91);
68008
68097
  let n = 2;
68009
- while (byId.has(id3) && byId.get(id3).path !== path90) {
68010
- id3 = `${slugFromPath(path90)}-${n++}`;
68098
+ while (byId.has(id3) && byId.get(id3).path !== path91) {
68099
+ id3 = `${slugFromPath(path91)}-${n++}`;
68011
68100
  }
68012
68101
  byId.set(id3, {
68013
68102
  id: id3,
68014
- name: slugFromPath(path90),
68015
- path: path90
68103
+ name: slugFromPath(path91),
68104
+ path: path91
68016
68105
  });
68017
68106
  }
68018
68107
  return [...byId.values()];
@@ -68987,9 +69076,9 @@ async function runCompanionServe(opts = {}) {
68987
69076
  return;
68988
69077
  }
68989
69078
  const url2 = parseUrl(req);
68990
- const path90 = url2.pathname.replace(/\/+$/, "") || "/";
69079
+ const path91 = url2.pathname.replace(/\/+$/, "") || "/";
68991
69080
  try {
68992
- if (req.method === "GET" && (path90 === "/health" || path90 === "/v1/health")) {
69081
+ if (req.method === "GET" && (path91 === "/health" || path91 === "/v1/health")) {
68993
69082
  sendJson2(res, 200, {
68994
69083
  ok: true,
68995
69084
  service: "zelari-companion",
@@ -69001,18 +69090,18 @@ async function runCompanionServe(opts = {}) {
69001
69090
  });
69002
69091
  return;
69003
69092
  }
69004
- if (path90.startsWith("/v1")) {
69093
+ if (path91.startsWith("/v1")) {
69005
69094
  if (!tokenMatches(token, getBearer(req))) {
69006
69095
  sendJson2(res, 401, { ok: false, error: "unauthorized" });
69007
69096
  return;
69008
69097
  }
69009
69098
  }
69010
- if (req.method === "GET" && path90 === "/v1/config") {
69099
+ if (req.method === "GET" && path91 === "/v1/config") {
69011
69100
  const snap = buildDesktopConfigSnapshot();
69012
69101
  sendJson2(res, 200, { ok: true, ...snap });
69013
69102
  return;
69014
69103
  }
69015
- if (req.method === "GET" && path90 === "/v1/projects") {
69104
+ if (req.method === "GET" && path91 === "/v1/projects") {
69016
69105
  sendJson2(res, 200, {
69017
69106
  ok: true,
69018
69107
  projects: projects.map((p3) => ({
@@ -69023,7 +69112,7 @@ async function runCompanionServe(opts = {}) {
69023
69112
  });
69024
69113
  return;
69025
69114
  }
69026
- if (req.method === "GET" && path90 === "/v1/runs") {
69115
+ if (req.method === "GET" && path91 === "/v1/runs") {
69027
69116
  sendJson2(res, 200, {
69028
69117
  ok: true,
69029
69118
  active: runs.getActive(),
@@ -69041,7 +69130,7 @@ async function runCompanionServe(opts = {}) {
69041
69130
  });
69042
69131
  return;
69043
69132
  }
69044
- if (req.method === "POST" && path90 === "/v1/runs") {
69133
+ if (req.method === "POST" && path91 === "/v1/runs") {
69045
69134
  const raw = await readBody(req);
69046
69135
  let body = {};
69047
69136
  try {
@@ -69089,7 +69178,7 @@ async function runCompanionServe(opts = {}) {
69089
69178
  });
69090
69179
  return;
69091
69180
  }
69092
- const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path90);
69181
+ const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path91);
69093
69182
  if (req.method === "GET" && eventsMatch) {
69094
69183
  const runId = eventsMatch[1];
69095
69184
  const run = runs.getRun(runId);
@@ -69154,7 +69243,7 @@ async function runCompanionServe(opts = {}) {
69154
69243
  }, 500);
69155
69244
  return;
69156
69245
  }
69157
- const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path90);
69246
+ const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path91);
69158
69247
  if (req.method === "POST" && cancelMatch) {
69159
69248
  const runId = cancelMatch[1];
69160
69249
  const result = runs.cancel(runId);
@@ -69165,7 +69254,7 @@ async function runCompanionServe(opts = {}) {
69165
69254
  sendJson2(res, 200, { ok: true, cancelled: runId });
69166
69255
  return;
69167
69256
  }
69168
- const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path90);
69257
+ const steerMatch = /^\/v1\/runs\/([^/]+)\/steer$/.exec(path91);
69169
69258
  if (req.method === "POST" && steerMatch) {
69170
69259
  const runId = steerMatch[1];
69171
69260
  const raw = await readBody(req);
@@ -69845,21 +69934,88 @@ var init_fixBudget = __esm({
69845
69934
  }
69846
69935
  });
69847
69936
 
69937
+ // src/cli/commands/inspectSession.ts
69938
+ var inspectSession_exports = {};
69939
+ __export(inspectSession_exports, {
69940
+ renderInspectReport: () => renderInspectReport,
69941
+ runInspectSession: () => runInspectSession
69942
+ });
69943
+ import path89 from "node:path";
69944
+ import { existsSync as existsSync56 } from "node:fs";
69945
+ function renderInspectReport(state3) {
69946
+ const lines = [
69947
+ `session ${state3.session.sessionId} status=${state3.session.status} turns=${state3.execution.turnsTotal}`
69948
+ ];
69949
+ const contractByTurn = new Map(state3.execution.contracts.map((c) => [c.turn, c]));
69950
+ for (const turn of state3.turns) {
69951
+ const contract = contractByTurn.get(turn.index);
69952
+ const verification = turn.verification ? `${turn.verification.verdict}${turn.verification.strict ? "" : " (non-strict)"}` : "unknown";
69953
+ const blockers = contract?.blockers ?? [];
69954
+ const completion = contract?.complete ? "complete" : blockers.length > 0 ? `incomplete \u2014 ${blockers.length} blockers: ${blockers.join(", ")}` : "incomplete";
69955
+ lines.push(
69956
+ ` turn ${turn.index} [${turn.outcome}] verification: ${verification} contract: ${completion}`
69957
+ );
69958
+ }
69959
+ lines.push("");
69960
+ lines.push("support lens:");
69961
+ const projections = state3.support.contextProjections;
69962
+ const last = projections[projections.length - 1];
69963
+ lines.push(
69964
+ ` context projections: ${projections.length}` + (last ? ` (last: ${last.contextChars} chars \u2192 ${last.returnedCount} items)` : "")
69965
+ );
69966
+ lines.push(` memory events: ${state3.support.memoryEvents}`);
69967
+ const saved = state3.support.tokensSavedByCompaction;
69968
+ lines.push(
69969
+ ` compactions: ${state3.support.compactions}${saved !== void 0 ? ` (${saved} tokens saved)` : ""}`
69970
+ );
69971
+ return lines.join("\n");
69972
+ }
69973
+ async function runInspectSession(opts) {
69974
+ const sessionsDir = resolveSessionsDir({ workspaceRoot: opts.cwd ?? process.cwd() });
69975
+ const sessionDir = path89.join(sessionsDir, opts.sessionId);
69976
+ const eventsPath = path89.join(sessionDir, "events.jsonl");
69977
+ if (!existsSync56(sessionDir)) {
69978
+ console.error(`zelari-code inspect: no session directory at ${sessionDir}`);
69979
+ return 1;
69980
+ }
69981
+ if (!existsSync56(eventsPath)) {
69982
+ console.error(`zelari-code inspect: session directory has no events.jsonl at ${eventsPath}`);
69983
+ return 1;
69984
+ }
69985
+ let state3;
69986
+ try {
69987
+ state3 = await readHarnessState(sessionDir);
69988
+ } catch (err) {
69989
+ const msg = err instanceof Error ? err.message : String(err);
69990
+ console.error(`zelari-code inspect: cannot read session at ${sessionDir}: ${msg}`);
69991
+ return 1;
69992
+ }
69993
+ console.log(opts.json ? JSON.stringify(state3, null, 2) : renderInspectReport(state3));
69994
+ return 0;
69995
+ }
69996
+ var init_inspectSession = __esm({
69997
+ "src/cli/commands/inspectSession.ts"() {
69998
+ "use strict";
69999
+ init_session();
70000
+ init_harnessState();
70001
+ }
70002
+ });
70003
+
69848
70004
  // src/cli/commands/inspect.ts
69849
70005
  var inspect_exports = {};
69850
70006
  __export(inspect_exports, {
69851
70007
  collectInspectReport: () => collectInspectReport,
69852
70008
  runInspect: () => runInspect
69853
70009
  });
69854
- import path89 from "node:path";
69855
- import { existsSync as existsSync56, readFileSync as readFileSync43, readdirSync as readdirSync12 } from "node:fs";
70010
+ import path90 from "node:path";
70011
+ import { existsSync as existsSync57, readFileSync as readFileSync43, readdirSync as readdirSync12 } from "node:fs";
69856
70012
  import { homedir as homedir17 } from "node:os";
69857
70013
  async function collectInspectReport(cwd = process.cwd()) {
69858
70014
  ensureBuiltinSkillsLoadedSync();
69859
70015
  const snap = listSkillsSnapshot(cwd);
69860
70016
  const mcp = listMcpServers(cwd);
69861
- const userMcpPath = path89.join(homedir17(), ".zelari-code", "mcp.json");
69862
- const projectMcpPath = path89.join(cwd, ".zelari", "mcp.json");
70017
+ const userMcpPath = path90.join(homedir17(), ".zelari-code", "mcp.json");
70018
+ const projectMcpPath = path90.join(cwd, ".zelari", "mcp.json");
69863
70019
  const globalHooks = globalHooksDir();
69864
70020
  const projectHooks = projectHooksDir(cwd);
69865
70021
  const projectTrusted = isFolderTrusted(cwd);
@@ -69885,11 +70041,11 @@ async function collectInspectReport(cwd = process.cwd()) {
69885
70041
  folders: listTrustedFolders()
69886
70042
  },
69887
70043
  configSources: [
69888
- { path: userMcpPath, exists: existsSync56(userMcpPath) },
69889
- { path: projectMcpPath, exists: existsSync56(projectMcpPath) },
69890
- { path: path89.join(homedir17(), ".zelari-code", "provider.json"), exists: existsSync56(path89.join(homedir17(), ".zelari-code", "provider.json")) },
69891
- { path: path89.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync56(path89.join(cwd, ".zelari", "AGENTS.md")) },
69892
- { path: path89.join(cwd, "AGENTS.md"), exists: existsSync56(path89.join(cwd, "AGENTS.md")) }
70044
+ { path: userMcpPath, exists: existsSync57(userMcpPath) },
70045
+ { path: projectMcpPath, exists: existsSync57(projectMcpPath) },
70046
+ { path: path90.join(homedir17(), ".zelari-code", "provider.json"), exists: existsSync57(path90.join(homedir17(), ".zelari-code", "provider.json")) },
70047
+ { path: path90.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync57(path90.join(cwd, ".zelari", "AGENTS.md")) },
70048
+ { path: path90.join(cwd, "AGENTS.md"), exists: existsSync57(path90.join(cwd, "AGENTS.md")) }
69893
70049
  ],
69894
70050
  skills: {
69895
70051
  total: snap.skills.length,
@@ -69902,7 +70058,7 @@ async function collectInspectReport(cwd = process.cwd()) {
69902
70058
  user: mcp.servers.filter((s) => s.scope === "user").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
69903
70059
  project: mcp.servers.filter((s) => s.scope === "project").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
69904
70060
  projectTrusted,
69905
- projectConfigExists: existsSync56(projectMcpPath)
70061
+ projectConfigExists: existsSync57(projectMcpPath)
69906
70062
  },
69907
70063
  hooks: {
69908
70064
  global: {
@@ -69929,12 +70085,12 @@ function listJsonFiles(dir) {
69929
70085
  }
69930
70086
  function findAgentsMd(cwd) {
69931
70087
  const candidates = [
69932
- path89.join(cwd, "AGENTS.md"),
69933
- path89.join(cwd, ".zelari", "AGENTS.md")
70088
+ path90.join(cwd, "AGENTS.md"),
70089
+ path90.join(cwd, ".zelari", "AGENTS.md")
69934
70090
  ];
69935
70091
  const found = [];
69936
70092
  for (const c of candidates) {
69937
- if (existsSync56(c)) {
70093
+ if (existsSync57(c)) {
69938
70094
  try {
69939
70095
  const text = readFileSync43(c, "utf8");
69940
70096
  found.push(`${c} (${text.length} bytes)`);
@@ -79561,8 +79717,8 @@ function normalizeDraft(raw, sourceUrl, provider, model) {
79561
79717
  let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
79562
79718
  if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
79563
79719
  try {
79564
- const path90 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
79565
- name = path90 && /^[a-z0-9]/.test(path90) ? path90 : "imported-skill";
79720
+ const path91 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
79721
+ name = path91 && /^[a-z0-9]/.test(path91) ? path91 : "imported-skill";
79566
79722
  } catch {
79567
79723
  name = "imported-skill";
79568
79724
  }
@@ -79897,6 +80053,14 @@ function pickRootComponent() {
79897
80053
  process.exit(1);
79898
80054
  }
79899
80055
  if (argv.includes("--inspect") || argv.includes("inspect")) {
80056
+ const inspectAt = argv.findIndex((a) => a === "inspect" || a === "--inspect");
80057
+ const sessionId2 = inspectAt >= 0 ? argv.slice(inspectAt + 1).find((a) => !a.startsWith("-")) : void 0;
80058
+ if (sessionId2) {
80059
+ const { runInspectSession: runInspectSession2 } = (init_inspectSession(), __toCommonJS(inspectSession_exports));
80060
+ const json4 = argv.includes("--json");
80061
+ void runInspectSession2({ sessionId: sessionId2, json: json4 }).then((code) => process.exit(code));
80062
+ return { kind: "done" };
80063
+ }
79900
80064
  const { runInspect: runInspect2 } = (init_inspect(), __toCommonJS(inspect_exports));
79901
80065
  const json3 = argv.includes("--json");
79902
80066
  void runInspect2({ json: json3 }).then((code) => process.exit(code));