zelari-code 2.37.2 → 2.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/cli/asyncLimit.js +53 -0
  2. package/dist/cli/asyncLimit.js.map +1 -0
  3. package/dist/cli/budget/restoreRuntime.js +10 -5
  4. package/dist/cli/budget/restoreRuntime.js.map +1 -1
  5. package/dist/cli/crossProviderFailover.js +32 -0
  6. package/dist/cli/crossProviderFailover.js.map +1 -1
  7. package/dist/cli/harnessState.js +7 -3
  8. package/dist/cli/harnessState.js.map +1 -1
  9. package/dist/cli/headless/liveTurnAbort.js +1 -1
  10. package/dist/cli/headless/liveTurnAbort.js.map +1 -1
  11. package/dist/cli/headless/runOneTurn.js +15 -5
  12. package/dist/cli/headless/runOneTurn.js.map +1 -1
  13. package/dist/cli/headlessSpine.js +7 -3
  14. package/dist/cli/headlessSpine.js.map +1 -1
  15. package/dist/cli/kraken/cachedShell.js +162 -0
  16. package/dist/cli/kraken/cachedShell.js.map +1 -0
  17. package/dist/cli/kraken/contractCompiler.js +6 -1
  18. package/dist/cli/kraken/contractCompiler.js.map +1 -1
  19. package/dist/cli/kraken/executor.js +27 -17
  20. package/dist/cli/kraken/executor.js.map +1 -1
  21. package/dist/cli/kraken/nativeVerification.js +9 -1
  22. package/dist/cli/kraken/nativeVerification.js.map +1 -1
  23. package/dist/cli/kraken/worktreeCleanupBatch.js +110 -0
  24. package/dist/cli/kraken/worktreeCleanupBatch.js.map +1 -0
  25. package/dist/cli/main.bundled.js +1323 -510
  26. package/dist/cli/main.bundled.js.map +4 -4
  27. package/dist/cli/memory/legacyImport.js +66 -14
  28. package/dist/cli/memory/legacyImport.js.map +1 -1
  29. package/dist/cli/memory/sqliteBackend.js +19 -5
  30. package/dist/cli/memory/sqliteBackend.js.map +1 -1
  31. package/dist/cli/providerFailover.js +21 -2
  32. package/dist/cli/providerFailover.js.map +1 -1
  33. package/dist/cli/runHeadless.js +32 -1
  34. package/dist/cli/runHeadless.js.map +1 -1
  35. package/dist/cli/serve/harnessServer.js +6 -2
  36. package/dist/cli/serve/harnessServer.js.map +1 -1
  37. package/dist/cli/serve/sessionControl.js.map +1 -1
  38. package/dist/cli/sessionSpine.js +26 -10
  39. package/dist/cli/sessionSpine.js.map +1 -1
  40. package/dist/cli/toolRegistry.js +23 -4
  41. package/dist/cli/toolRegistry.js.map +1 -1
  42. package/dist/cli/tools/krakenModel.js +72 -9
  43. package/dist/cli/tools/krakenModel.js.map +1 -1
  44. package/dist/cli/tools/krakenRadio.js +60 -3
  45. package/dist/cli/tools/krakenRadio.js.map +1 -1
  46. package/dist/cli/tools/krakenWorktree.js +105 -4
  47. package/dist/cli/tools/krakenWorktree.js.map +1 -1
  48. package/dist/cli/tools/taskTool.js +64 -40
  49. package/dist/cli/tools/taskTool.js.map +1 -1
  50. package/dist/cli/tools/tentacleHeartbeat.js +47 -0
  51. package/dist/cli/tools/tentacleHeartbeat.js.map +1 -0
  52. package/package.json +3 -2
@@ -19560,11 +19560,11 @@ var init_listFiles = __esm({
19560
19560
  return 1;
19561
19561
  return a.name.localeCompare(b.name);
19562
19562
  });
19563
- const MAX_ENTRIES2 = 500;
19564
- const truncated = entries.length > MAX_ENTRIES2;
19563
+ const MAX_ENTRIES3 = 500;
19564
+ const truncated = entries.length > MAX_ENTRIES3;
19565
19565
  const warnings = entries.length === 0 ? ["DIR_EMPTY"] : [];
19566
19566
  const status = entries.length === 0 ? "empty" : truncated ? "partial" : "complete";
19567
- return typedOk({ dir: target, entries: truncated ? entries.slice(0, MAX_ENTRIES2) : entries, truncated }, {
19567
+ return typedOk({ dir: target, entries: truncated ? entries.slice(0, MAX_ENTRIES3) : entries, truncated }, {
19568
19568
  status,
19569
19569
  counts: { filesWalked: entries.length },
19570
19570
  ...warnings.length > 0 ? { warnings } : {},
@@ -22452,6 +22452,12 @@ var init_worktreeWorkspace = __esm({
22452
22452
 
22453
22453
  // packages/core/dist/core/requestSnapshot.js
22454
22454
  import { createHash as createHash3 } from "node:crypto";
22455
+ function resolveRequestSnapshotMode(env = process.env) {
22456
+ const raw = env.ZELARI_REQUEST_SNAPSHOT?.trim().toLowerCase();
22457
+ if (raw === "lite" || raw === "off")
22458
+ return raw;
22459
+ return "full";
22460
+ }
22455
22461
  function stableStringify(value) {
22456
22462
  if (value === null || typeof value !== "object")
22457
22463
  return JSON.stringify(value) ?? "null";
@@ -22467,43 +22473,96 @@ function stableStringify(value) {
22467
22473
  function sha256Hex(input) {
22468
22474
  return createHash3("sha256").update(input, "utf8").digest("hex").slice(0, 32);
22469
22475
  }
22470
- function cloneMessages(messages) {
22476
+ function cloneMessagesDeep(messages) {
22471
22477
  return structuredClone(messages);
22472
22478
  }
22479
+ function cloneMessagesLite(messages) {
22480
+ return messages.map((m) => ({ ...m }));
22481
+ }
22473
22482
  function canonicalTools(tools) {
22474
22483
  return [...tools].sort((a, b) => a.name.localeCompare(b.name));
22475
22484
  }
22485
+ function cloneToolsDeep(tools) {
22486
+ return canonicalTools(tools).map((t) => structuredClone(t));
22487
+ }
22488
+ function cloneToolsLite(tools) {
22489
+ return canonicalTools(tools).map((t) => ({ ...t }));
22490
+ }
22491
+ function __resetRequestSnapshotMemoForTests() {
22492
+ headerMemo = null;
22493
+ }
22476
22494
  function createRoutedRequestSnapshot(params) {
22495
+ const mode = params.mode ?? resolveRequestSnapshotMode();
22477
22496
  let split = 0;
22478
22497
  while (split < params.messages.length && params.messages[split].role === "system") {
22479
22498
  split++;
22480
22499
  }
22481
- const systemMessages = cloneMessages(params.messages.slice(0, split));
22482
- const conversation = cloneMessages(params.messages.slice(split));
22483
- const tools = canonicalTools(params.tools).map((t) => structuredClone(t));
22484
- const header = stableStringify({
22485
- provider: params.provider,
22486
- model: params.model,
22487
- systemMessages,
22488
- tools
22489
- });
22490
- const request = stableStringify({
22491
- provider: params.provider,
22492
- model: params.model,
22493
- systemMessages,
22494
- tools,
22495
- conversation
22496
- });
22497
- return {
22498
- provider: params.provider,
22499
- model: params.model,
22500
+ const lite = mode === "lite";
22501
+ const cloneMsg = lite ? cloneMessagesLite : cloneMessagesDeep;
22502
+ const systemMessages = cloneMsg(params.messages.slice(0, split));
22503
+ const conversation = cloneMsg(params.messages.slice(split));
22504
+ const createdAt = Date.now();
22505
+ const { provider, model } = params;
22506
+ if (!lite) {
22507
+ const tools2 = cloneToolsDeep(params.tools);
22508
+ const header = stableStringify({ provider, model, systemMessages, tools: tools2 });
22509
+ const request = stableStringify({
22510
+ provider,
22511
+ model,
22512
+ systemMessages,
22513
+ tools: tools2,
22514
+ conversation
22515
+ });
22516
+ return {
22517
+ provider,
22518
+ model,
22519
+ systemMessages,
22520
+ conversation,
22521
+ tools: tools2,
22522
+ headerFingerprint: sha256Hex(header),
22523
+ requestFingerprint: sha256Hex(request),
22524
+ createdAt
22525
+ };
22526
+ }
22527
+ const systemKey = sha256Hex(stableStringify(systemMessages));
22528
+ let tools;
22529
+ let headerFp;
22530
+ if (headerMemo && headerMemo.toolsRef === params.tools && headerMemo.provider === provider && headerMemo.model === model && headerMemo.systemKey === systemKey) {
22531
+ tools = headerMemo.tools;
22532
+ headerFp = headerMemo.headerFingerprint;
22533
+ } else {
22534
+ tools = cloneToolsLite(params.tools);
22535
+ }
22536
+ let requestFp;
22537
+ const snap = {
22538
+ provider,
22539
+ model,
22500
22540
  systemMessages,
22501
22541
  conversation,
22502
22542
  tools,
22503
- headerFingerprint: sha256Hex(header),
22504
- requestFingerprint: sha256Hex(request),
22505
- createdAt: Date.now()
22543
+ createdAt,
22544
+ get headerFingerprint() {
22545
+ if (headerFp === void 0) {
22546
+ headerFp = sha256Hex(stableStringify({ provider, model, systemMessages, tools }));
22547
+ headerMemo = {
22548
+ toolsRef: params.tools,
22549
+ provider,
22550
+ model,
22551
+ systemKey,
22552
+ tools,
22553
+ headerFingerprint: headerFp
22554
+ };
22555
+ }
22556
+ return headerFp;
22557
+ },
22558
+ get requestFingerprint() {
22559
+ if (requestFp === void 0) {
22560
+ requestFp = sha256Hex(stableStringify({ provider, model, systemMessages, tools, conversation }));
22561
+ }
22562
+ return requestFp;
22563
+ }
22506
22564
  };
22565
+ return snap;
22507
22566
  }
22508
22567
  function compareReplayPrefix(snapshot, messages) {
22509
22568
  const base2 = snapshot.conversation;
@@ -22517,9 +22576,11 @@ function compareReplayPrefix(snapshot, messages) {
22517
22576
  }
22518
22577
  return { exact: true, matchingMessages: matching };
22519
22578
  }
22579
+ var headerMemo;
22520
22580
  var init_requestSnapshot = __esm({
22521
22581
  "packages/core/dist/core/requestSnapshot.js"() {
22522
22582
  "use strict";
22583
+ headerMemo = null;
22523
22584
  }
22524
22585
  });
22525
22586
 
@@ -23520,15 +23581,22 @@ async function readSessionLog(filePath) {
23520
23581
  }
23521
23582
  throw err;
23522
23583
  }
23584
+ return parseSessionLogText(filePath, content);
23585
+ }
23586
+ function parseSessionLogText(filePath, content) {
23587
+ const { events, issues } = parseSessionLogLines(content.split("\n"));
23588
+ return { path: filePath, events, issues, ok: issues.length === 0 };
23589
+ }
23590
+ function parseSessionLogLines(lines, opts = {}) {
23523
23591
  const events = [];
23524
23592
  const issues = [];
23525
- let expected = 1;
23526
- const lines = content.split("\n");
23593
+ const base2 = opts.linesConsumed ?? 0;
23594
+ let expected = opts.expected ?? 1;
23527
23595
  for (let i = 0; i < lines.length; i++) {
23528
23596
  const trimmed = lines[i].trim();
23529
23597
  if (!trimmed)
23530
23598
  continue;
23531
- const lineNo = i + 1;
23599
+ const lineNo = base2 + i + 1;
23532
23600
  let parsed;
23533
23601
  try {
23534
23602
  parsed = JSON.parse(trimmed);
@@ -23562,7 +23630,7 @@ async function readSessionLog(filePath) {
23562
23630
  expected = envelope.seq + 1;
23563
23631
  }
23564
23632
  }
23565
- return { path: filePath, events, issues, ok: issues.length === 0 };
23633
+ return { events, issues, expected, linesConsumed: base2 + lines.length };
23566
23634
  }
23567
23635
  function parseVerification(e) {
23568
23636
  const raw = Array.isArray(e.data.results) ? e.data.results : [];
@@ -23810,7 +23878,7 @@ async function createExecutionContext(options = {}) {
23810
23878
  workspaceKind: workspace.kind
23811
23879
  }
23812
23880
  });
23813
- const fs48 = options.fs ?? new NodeFsProvider(workspace);
23881
+ const fs49 = options.fs ?? new NodeFsProvider(workspace);
23814
23882
  const shell = options.shell ?? new NodeShellProvider(workspace);
23815
23883
  const subagent = options.subagent ?? NOOP_SUBAGENT_PROVIDER;
23816
23884
  const env = options.env ?? process.env;
@@ -23818,7 +23886,7 @@ async function createExecutionContext(options = {}) {
23818
23886
  sessionId: sessionId2,
23819
23887
  profile,
23820
23888
  workspace,
23821
- fs: fs48,
23889
+ fs: fs49,
23822
23890
  shell,
23823
23891
  subagent,
23824
23892
  appendSessionEvent: (input) => writer.append(input),
@@ -25589,6 +25657,60 @@ var init_scopeDiscipline = __esm({
25589
25657
  }
25590
25658
  });
25591
25659
 
25660
+ // packages/core/dist/verification/commandConcurrency.js
25661
+ function isVerifyParallelEnabled(env = process.env) {
25662
+ const raw = env.ZELARI_VERIFY_PARALLEL?.trim().toLowerCase();
25663
+ return raw !== void 0 && PARALLEL_TRUTHY.has(raw);
25664
+ }
25665
+ function resolveCommandConcurrency(env = process.env) {
25666
+ if (!isVerifyParallelEnabled(env))
25667
+ return 1;
25668
+ const raw = Number(env.ZELARI_VERIFY_CONCURRENCY);
25669
+ if (!Number.isFinite(raw) || raw <= 0)
25670
+ return DEFAULT_COMMAND_CONCURRENCY;
25671
+ return Math.floor(raw);
25672
+ }
25673
+ var DEFAULT_COMMAND_CONCURRENCY, PARALLEL_TRUTHY;
25674
+ var init_commandConcurrency = __esm({
25675
+ "packages/core/dist/verification/commandConcurrency.js"() {
25676
+ "use strict";
25677
+ DEFAULT_COMMAND_CONCURRENCY = 3;
25678
+ PARALLEL_TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
25679
+ }
25680
+ });
25681
+
25682
+ // packages/core/dist/verification/runWithLimit.js
25683
+ async function runWithLimit(items, limit, fn) {
25684
+ const slots = Math.min(items.length, Math.max(1, Number.isFinite(limit) ? Math.floor(limit) : 1));
25685
+ if (slots <= 0)
25686
+ return;
25687
+ let cursor = 0;
25688
+ let failed = false;
25689
+ let firstError;
25690
+ const worker = async () => {
25691
+ while (!failed && cursor < items.length) {
25692
+ const index = cursor;
25693
+ cursor += 1;
25694
+ try {
25695
+ await fn(items[index], index);
25696
+ } catch (err) {
25697
+ if (!failed) {
25698
+ failed = true;
25699
+ firstError = err;
25700
+ }
25701
+ }
25702
+ }
25703
+ };
25704
+ await Promise.all(Array.from({ length: slots }, () => worker()));
25705
+ if (failed)
25706
+ throw firstError;
25707
+ }
25708
+ var init_runWithLimit = __esm({
25709
+ "packages/core/dist/verification/runWithLimit.js"() {
25710
+ "use strict";
25711
+ }
25712
+ });
25713
+
25592
25714
  // packages/core/dist/verification/engine.js
25593
25715
  import { createHash as createHash8 } from "node:crypto";
25594
25716
  function defaultSha256(input) {
@@ -25597,11 +25719,14 @@ function defaultSha256(input) {
25597
25719
  function tail(text, max = 400) {
25598
25720
  return text.length > max ? `\u2026${text.slice(text.length - max)}` : text;
25599
25721
  }
25600
- var VerificationEngine;
25722
+ var CACHED_DETAIL_NOTE, VerificationEngine;
25601
25723
  var init_engine = __esm({
25602
25724
  "packages/core/dist/verification/engine.js"() {
25603
25725
  "use strict";
25604
25726
  init_scopeDiscipline();
25727
+ init_commandConcurrency();
25728
+ init_runWithLimit();
25729
+ CACHED_DETAIL_NOTE = "cached \u2014 tree unchanged since last run";
25605
25730
  VerificationEngine = class {
25606
25731
  services;
25607
25732
  options;
@@ -25612,8 +25737,15 @@ var init_engine = __esm({
25612
25737
  /** Evaluate criteria deterministically; optionally emit the spine event. */
25613
25738
  async evaluate(criteria, context = {}) {
25614
25739
  const results = [];
25615
- for (const criterion of criteria) {
25616
- results.push(await this.evaluateOne(criterion, context.scope));
25740
+ const limit = this.options.commandConcurrency ?? resolveCommandConcurrency();
25741
+ if (limit <= 1) {
25742
+ for (const criterion of criteria) {
25743
+ results.push(await this.evaluateOne(criterion, context.scope));
25744
+ }
25745
+ } else {
25746
+ await runWithLimit(criteria, limit, async (criterion, index) => {
25747
+ results[index] = await this.evaluateOne(criterion, context.scope);
25748
+ });
25617
25749
  }
25618
25750
  if (this.options.emit) {
25619
25751
  try {
@@ -25704,13 +25836,18 @@ var init_engine = __esm({
25704
25836
  }
25705
25837
  }
25706
25838
  async evalCommand(check2, done) {
25707
- const sha256 = this.options.sha256 ?? defaultSha256;
25839
+ const sha2562 = this.options.sha256 ?? defaultSha256;
25708
25840
  const shell = this.services.shell;
25709
25841
  if (!shell) {
25710
25842
  return done({ status: "unknown", evidence: [], detail: "shell provider unavailable" });
25711
25843
  }
25712
25844
  const result = await shell.exec(check2.command, { timeoutMs: check2.timeoutMs });
25713
- const digest = sha256(result.stdout);
25845
+ const digest = sha2562(result.stdout);
25846
+ const cached2 = result.cached === true;
25847
+ const finish2 = (patch) => cached2 ? done({
25848
+ ...patch,
25849
+ detail: patch.detail ? `${patch.detail} (${CACHED_DETAIL_NOTE})` : CACHED_DETAIL_NOTE
25850
+ }) : done(patch);
25714
25851
  const seq = await this.emitEvidence({
25715
25852
  observation: "command",
25716
25853
  command: check2.command,
@@ -25718,7 +25855,9 @@ var init_engine = __esm({
25718
25855
  timedOut: result.timedOut,
25719
25856
  digest,
25720
25857
  stdoutTail: tail(result.stdout),
25721
- stderrTail: tail(result.stderr)
25858
+ stderrTail: tail(result.stderr),
25859
+ // Additive (optional) field: replay of older logs without it still parses.
25860
+ ...cached2 ? { cached: true } : {}
25722
25861
  });
25723
25862
  const evidence = [
25724
25863
  {
@@ -25730,32 +25869,32 @@ var init_engine = __esm({
25730
25869
  }
25731
25870
  ];
25732
25871
  if (result.timedOut) {
25733
- return done({ status: "unknown", evidence, detail: `command timed out (${check2.timeoutMs ?? "default"}ms)` });
25872
+ return finish2({ status: "unknown", evidence, detail: `command timed out (${check2.timeoutMs ?? "default"}ms)` });
25734
25873
  }
25735
25874
  const expectExit = check2.expectExit ?? 0;
25736
25875
  if (result.exitCode !== expectExit) {
25737
- return done({
25876
+ return finish2({
25738
25877
  status: "fail",
25739
25878
  evidence,
25740
25879
  detail: `exit ${result.exitCode} (expected ${expectExit}) \u2014 stderr: ${tail(result.stderr)}`
25741
25880
  });
25742
25881
  }
25743
25882
  if (check2.expectStdoutIncludes && !result.stdout.includes(check2.expectStdoutIncludes)) {
25744
- return done({
25883
+ return finish2({
25745
25884
  status: "fail",
25746
25885
  evidence,
25747
25886
  detail: `stdout missing "${check2.expectStdoutIncludes}" \u2014 got: ${tail(result.stdout)}`
25748
25887
  });
25749
25888
  }
25750
- return done({ status: "pass", evidence });
25889
+ return finish2({ status: "pass", evidence });
25751
25890
  }
25752
25891
  /**
25753
25892
  * F3: the fs observation is logged to the spine (path, existence, optional
25754
25893
  * content digest) and the returned ref carries the event seq when the
25755
25894
  * emitter resolved one.
25756
25895
  */
25757
- async fsEvidence(observation, path101, sha256, content, extra = {}) {
25758
- const digest = sha256 && content !== void 0 ? sha256(content) : void 0;
25896
+ async fsEvidence(observation, path101, sha2562, content, extra = {}) {
25897
+ const digest = sha2562 && content !== void 0 ? sha2562(content) : void 0;
25759
25898
  const seq = await this.emitEvidence({ observation, path: path101, ...extra, ...digest ? { digest } : {} });
25760
25899
  return {
25761
25900
  tier: "fs-observation",
@@ -25766,26 +25905,26 @@ var init_engine = __esm({
25766
25905
  };
25767
25906
  }
25768
25907
  async evalFileExists(check2, done) {
25769
- const sha256 = this.options.sha256 ?? defaultSha256;
25770
- const fs48 = this.services.fs;
25771
- if (!fs48)
25908
+ const sha2562 = this.options.sha256 ?? defaultSha256;
25909
+ const fs49 = this.services.fs;
25910
+ if (!fs49)
25772
25911
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
25773
- const exists = await fs48.exists(check2.path);
25912
+ const exists = await fs49.exists(check2.path);
25774
25913
  if (!exists) {
25775
25914
  await this.emitEvidence({ observation: "file-exists", path: check2.path, exists: false });
25776
25915
  return done({ status: "fail", evidence: [], detail: `file not found: ${check2.path}` });
25777
25916
  }
25778
- const content = await fs48.readFile(check2.path).catch(() => "");
25917
+ const content = await fs49.readFile(check2.path).catch(() => "");
25779
25918
  return done({
25780
25919
  status: "pass",
25781
- evidence: [await this.fsEvidence("file-exists", check2.path, sha256, content, { exists: true })]
25920
+ evidence: [await this.fsEvidence("file-exists", check2.path, sha2562, content, { exists: true })]
25782
25921
  });
25783
25922
  }
25784
25923
  async evalFileAbsent(check2, done) {
25785
- const fs48 = this.services.fs;
25786
- if (!fs48)
25924
+ const fs49 = this.services.fs;
25925
+ if (!fs49)
25787
25926
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
25788
- const exists = await fs48.exists(check2.path);
25927
+ const exists = await fs49.exists(check2.path);
25789
25928
  if (exists) {
25790
25929
  await this.emitEvidence({ observation: "file-absent", path: check2.path, exists: true });
25791
25930
  return done({ status: "fail", evidence: [], detail: `file still present: ${check2.path}` });
@@ -25796,18 +25935,18 @@ var init_engine = __esm({
25796
25935
  });
25797
25936
  }
25798
25937
  async evalFileContains(check2, done) {
25799
- const sha256 = this.options.sha256 ?? defaultSha256;
25800
- const fs48 = this.services.fs;
25801
- if (!fs48)
25938
+ const sha2562 = this.options.sha256 ?? defaultSha256;
25939
+ const fs49 = this.services.fs;
25940
+ if (!fs49)
25802
25941
  return done({ status: "unknown", evidence: [], detail: "fs provider unavailable" });
25803
25942
  let content;
25804
25943
  try {
25805
- content = await fs48.readFile(check2.path);
25944
+ content = await fs49.readFile(check2.path);
25806
25945
  } catch {
25807
25946
  await this.emitEvidence({ observation: "file-contains", path: check2.path, readable: false });
25808
25947
  return done({ status: "fail", evidence: [], detail: `file not readable: ${check2.path}` });
25809
25948
  }
25810
- const evidence = [await this.fsEvidence("file-contains", check2.path, sha256, content)];
25949
+ const evidence = [await this.fsEvidence("file-contains", check2.path, sha2562, content)];
25811
25950
  let hit;
25812
25951
  try {
25813
25952
  hit = new RegExp(check2.pattern, "m").test(content);
@@ -26355,6 +26494,7 @@ __export(verification_exports, {
26355
26494
  CommandCheckSchema: () => CommandCheckSchema,
26356
26495
  CriterionSchema: () => CriterionSchema,
26357
26496
  CriterionSourceSchema: () => CriterionSourceSchema,
26497
+ DEFAULT_COMMAND_CONCURRENCY: () => DEFAULT_COMMAND_CONCURRENCY,
26358
26498
  DEFAULT_VERIFIER_CONFIG: () => DEFAULT_VERIFIER_CONFIG,
26359
26499
  DETERMINISTIC_EVIDENCE_TIERS: () => DETERMINISTIC_EVIDENCE_TIERS,
26360
26500
  DeterministicCheckSchema: () => DeterministicCheckSchema,
@@ -26383,9 +26523,11 @@ __export(verification_exports, {
26383
26523
  evaluateResourceReserveGate: () => evaluateResourceReserveGate,
26384
26524
  isEventBackedEvidence: () => isEventBackedEvidence,
26385
26525
  isGeneratedPath: () => isGeneratedPath,
26526
+ isVerifyParallelEnabled: () => isVerifyParallelEnabled,
26386
26527
  lastVerificationRun: () => lastVerificationRun,
26387
26528
  parseNameOnlyDiff: () => parseNameOnlyDiff,
26388
26529
  parseVerificationRunPayload: () => parseVerificationRunPayload,
26530
+ resolveCommandConcurrency: () => resolveCommandConcurrency,
26389
26531
  snapshotToCompletionEvaluation: () => snapshotToCompletionEvaluation,
26390
26532
  strictBuildGate: () => strictBuildGate,
26391
26533
  verificationCostRatio: () => verificationCostRatio,
@@ -26402,6 +26544,7 @@ var init_verification = __esm({
26402
26544
  init_metrics();
26403
26545
  init_verifier();
26404
26546
  init_scopeDiscipline();
26547
+ init_commandConcurrency();
26405
26548
  init_resourceReserveGate();
26406
26549
  }
26407
26550
  });
@@ -26749,6 +26892,102 @@ var init_verificationAdapters = __esm({
26749
26892
  }
26750
26893
  });
26751
26894
 
26895
+ // src/cli/kraken/cachedShell.ts
26896
+ import { execFile as execFile2 } from "node:child_process";
26897
+ import { createHash as createHash9 } from "node:crypto";
26898
+ import { promisify } from "node:util";
26899
+ function verifyCacheEnabled(env = process.env) {
26900
+ const v = env.ZELARI_VERIFY_CACHE?.toLowerCase();
26901
+ if (v === "0" || v === "off" || v === "false") return false;
26902
+ return true;
26903
+ }
26904
+ function sha256(input) {
26905
+ return createHash9("sha256").update(input).digest("hex");
26906
+ }
26907
+ async function readTreeState(root) {
26908
+ try {
26909
+ const opts = { maxBuffer: GIT_MAX_BUFFER, timeout: GIT_TIMEOUT_MS, windowsHide: true };
26910
+ const [head, status] = await Promise.all([
26911
+ execFileAsync("git", ["-C", root, "rev-parse", "HEAD"], opts),
26912
+ execFileAsync("git", ["-C", root, "status", "--porcelain", "--untracked-files=all"], opts)
26913
+ ]);
26914
+ return `HEAD:${String(head.stdout).trim()}|${sha256(String(status.stdout))}`;
26915
+ } catch {
26916
+ return null;
26917
+ }
26918
+ }
26919
+ function wrapWithVerifyCache(shell, options = {}) {
26920
+ const env = options.env ?? process.env;
26921
+ if (!verifyCacheEnabled(env)) return shell;
26922
+ return new CachedShellProvider(shell, options);
26923
+ }
26924
+ var execFileAsync, MAX_ENTRIES, TREE_TTL_MS, GIT_MAX_BUFFER, GIT_TIMEOUT_MS, cache, CachedShellProvider;
26925
+ var init_cachedShell = __esm({
26926
+ "src/cli/kraken/cachedShell.ts"() {
26927
+ "use strict";
26928
+ execFileAsync = promisify(execFile2);
26929
+ MAX_ENTRIES = 32;
26930
+ TREE_TTL_MS = 5e3;
26931
+ GIT_MAX_BUFFER = 8 * 1024 * 1024;
26932
+ GIT_TIMEOUT_MS = 1e4;
26933
+ cache = /* @__PURE__ */ new Map();
26934
+ CachedShellProvider = class {
26935
+ constructor(inner, options = {}) {
26936
+ this.inner = inner;
26937
+ this.root = options.root ?? process.cwd();
26938
+ }
26939
+ root;
26940
+ /**
26941
+ * Tree token memo, PER INSTANCE (5 s TTL). Deliberately not module-level: a
26942
+ * stale token would keep matching the pre-write key for up to the TTL and
26943
+ * serve the PRE-repair result after the repair wrote files — a false PASS
26944
+ * window on the gate. Every evaluation builds its own decorator, so a write
26945
+ * made between evaluations is always observed; the memo only avoids
26946
+ * re-running git for the sibling criteria of the SAME evaluation.
26947
+ */
26948
+ treeMemo;
26949
+ async treeState() {
26950
+ const now = Date.now();
26951
+ if (this.treeMemo && now - this.treeMemo.at < TREE_TTL_MS) return this.treeMemo.value;
26952
+ const value = await readTreeState(this.root);
26953
+ this.treeMemo = { at: now, value };
26954
+ return value;
26955
+ }
26956
+ async exec(command, options = {}) {
26957
+ const tree = await this.treeState();
26958
+ if (tree === null) return await this.inner.exec(command, options);
26959
+ const key = sha256(
26960
+ JSON.stringify({
26961
+ command,
26962
+ cwd: options.cwd ?? "",
26963
+ timeoutMs: options.timeoutMs ?? null,
26964
+ tree,
26965
+ // Additive safety field: two evaluation roots in ONE process must
26966
+ // never collide on the same relative cwd.
26967
+ root: this.root
26968
+ })
26969
+ );
26970
+ const started = Date.now();
26971
+ const hit = cache.get(key);
26972
+ if (hit !== void 0) {
26973
+ cache.delete(key);
26974
+ cache.set(key, hit);
26975
+ return { ...hit, cached: true, durationMs: Date.now() - started };
26976
+ }
26977
+ const result = await this.inner.exec(command, options);
26978
+ if (!result.timedOut && result.exitCode !== null) {
26979
+ cache.set(key, { ...result });
26980
+ if (cache.size > MAX_ENTRIES) {
26981
+ const oldest = cache.keys().next().value;
26982
+ if (oldest !== void 0) cache.delete(oldest);
26983
+ }
26984
+ }
26985
+ return result;
26986
+ }
26987
+ };
26988
+ }
26989
+ });
26990
+
26752
26991
  // src/cli/kraken/nativeVerification.ts
26753
26992
  var nativeVerification_exports = {};
26754
26993
  __export(nativeVerification_exports, {
@@ -26829,7 +27068,7 @@ async function evaluateNativePack(deps = {}) {
26829
27068
  if (!commands.typecheckCommand && !commands.testCommand && !commands.buildCommand) return null;
26830
27069
  const criteria = buildNativeCriteria(commands, packTimeoutMs(env));
26831
27070
  if (criteria.length === 0) return null;
26832
- const shell = deps.shell ?? new NodeShellProvider(new LocalWorkspace(cwd));
27071
+ const shell = deps.shell ?? wrapWithVerifyCache(new NodeShellProvider(new LocalWorkspace(cwd)), { root: cwd, env });
26833
27072
  const engine = deps.emit ? new VerificationEngine({ shell }, { emit: deps.emit }) : new VerificationEngine({ shell });
26834
27073
  const results = await engine.evaluate(criteria, { packId: ZELARI_CODING_PACK_ID });
26835
27074
  return { packId: ZELARI_CODING_PACK_ID, criteria, results };
@@ -26841,6 +27080,7 @@ var init_nativeVerification = __esm({
26841
27080
  init_runtime();
26842
27081
  init_verification();
26843
27082
  init_verificationAdapters();
27083
+ init_cachedShell();
26844
27084
  EMPTY_PLAN = {
26845
27085
  typecheckCommand: null,
26846
27086
  testCommand: null,
@@ -27207,7 +27447,7 @@ async function evaluateContractCriteria(contract, deps = {}) {
27207
27447
  const cwd = deps.cwd ?? process.cwd();
27208
27448
  const criteria = compileVerificationCriteria(contract, { timeoutMs: deps.timeoutMs });
27209
27449
  if (criteria.length === 0) return null;
27210
- const shell = deps.shell ?? new NodeShellProvider(new LocalWorkspace(cwd));
27450
+ const shell = deps.shell ?? wrapWithVerifyCache(new NodeShellProvider(new LocalWorkspace(cwd)), { root: cwd });
27211
27451
  const engine = deps.emit ? new VerificationEngine({ shell }, { emit: deps.emit }) : new VerificationEngine({ shell });
27212
27452
  const results = await engine.evaluate(criteria, { packId: CONTRACT_CRITERIA_PACK_ID });
27213
27453
  return { criteria, results };
@@ -27219,6 +27459,7 @@ var init_contractCompiler = __esm({
27219
27459
  init_runtime();
27220
27460
  init_verification();
27221
27461
  init_policyEngine();
27462
+ init_cachedShell();
27222
27463
  CONTRACT_CRITERIA_PACK_ID = "task-contract/v1";
27223
27464
  CONTRACT_COMMAND_TIMEOUT_MS = 6e5;
27224
27465
  MAX_COMMAND_LENGTH = 512;
@@ -27242,7 +27483,7 @@ __export(verificationBridge_exports, {
27242
27483
  strictGateEventPayload: () => strictGateEventPayload,
27243
27484
  strictGateExitCode: () => strictGateExitCode
27244
27485
  });
27245
- import { createHash as createHash9 } from "node:crypto";
27486
+ import { createHash as createHash10 } from "node:crypto";
27246
27487
  function strictDoneEnabled(surface = "kraken", env = process.env) {
27247
27488
  if (surface === "mission") {
27248
27489
  const v2 = env.ZELARI_MISSION_STRICT;
@@ -27318,7 +27559,7 @@ function krakenResultsToContract(requiredChecks, results, now = Date.now()) {
27318
27559
  return { criteria, results: verifications };
27319
27560
  }
27320
27561
  function sha256Hex2(input) {
27321
- return createHash9("sha256").update(input).digest("hex");
27562
+ return createHash10("sha256").update(input).digest("hex");
27322
27563
  }
27323
27564
  function matchNoteToToolTrace(note, trace) {
27324
27565
  const n = normalize3(note);
@@ -28993,6 +29234,9 @@ var init_AgentHarness = __esm({
28993
29234
  maxToolLoopIterations;
28994
29235
  maxToolLoopHardCap;
28995
29236
  cancelled = false;
29237
+ /** Host-supplied cancel cause (`turn_timeout` vs user Stop). */
29238
+ cancelReason;
29239
+ cancelEventEmitted = false;
28996
29240
  activeController = null;
28997
29241
  queue = [];
28998
29242
  /**
@@ -29347,13 +29591,28 @@ ${shared.content}`,
29347
29591
  * After cancel() returns, the harness should be discarded by the
29348
29592
  * caller (the run() generator finishes after the current turn ends).
29349
29593
  * For mid-stream interrupt + new-prompt injection, see Task C.3.2.
29594
+ *
29595
+ * `reason` is optional host context: `turn_timeout` is the Desktop sidecar
29596
+ * idle watchdog (not a user Stop). The error event message must not claim
29597
+ * the user cancelled when they did not.
29350
29598
  */
29351
- cancel() {
29599
+ cancel(reason) {
29352
29600
  if (this.cancelled)
29353
29601
  return;
29354
29602
  this.cancelled = true;
29603
+ if (reason)
29604
+ this.cancelReason = reason;
29355
29605
  this.activeController?.abort();
29356
29606
  }
29607
+ buildCancelEvent() {
29608
+ this.cancelEventEmitted = true;
29609
+ const watchdog = this.cancelReason === "turn_timeout";
29610
+ return createBrainEvent("error", this.sessionId, {
29611
+ severity: "cancelled",
29612
+ message: watchdog ? "Turn cancelled: Desktop idle watchdog saw no events (silent model thinking or a tentacle with no tools). This was not a user Stop." : "Run cancelled by user.",
29613
+ code: watchdog ? "turn_timeout" : "cancelled"
29614
+ });
29615
+ }
29357
29616
  /** Current size of the queued user-prompt buffer. */
29358
29617
  get queueLength() {
29359
29618
  return this.queue.length;
@@ -29398,6 +29657,7 @@ ${shared.content}`,
29398
29657
  async *run() {
29399
29658
  const startTime = Date.now();
29400
29659
  this.activeController = new AbortController();
29660
+ this.cancelEventEmitted = false;
29401
29661
  this.toolCallCache = /* @__PURE__ */ new Map();
29402
29662
  this.toolCallCounts = /* @__PURE__ */ new Map();
29403
29663
  this.textToolReentries = 0;
@@ -29643,6 +29903,11 @@ ${shared.content}`,
29643
29903
  } catch {
29644
29904
  }
29645
29905
  }
29906
+ if (this.cancelled && !this.cancelEventEmitted) {
29907
+ const cancelEvent = this.buildCancelEvent();
29908
+ this.emit(cancelEvent);
29909
+ yield cancelEvent;
29910
+ }
29646
29911
  const agentEnd = createBrainEvent("agent_end", this.sessionId, {
29647
29912
  reason: hadError ? "error" : this.cancelled ? "cancelled" : "completed",
29648
29913
  durationMs: Date.now() - startTime,
@@ -29705,6 +29970,8 @@ ${shared.content}`,
29705
29970
  emitSnapshot(tools, generation, messages = this.messagesForProvider()) {
29706
29971
  if (!this.config.onRequestSnapshot)
29707
29972
  return;
29973
+ if (resolveRequestSnapshotMode() === "off")
29974
+ return;
29708
29975
  try {
29709
29976
  this.config.onRequestSnapshot(createRoutedRequestSnapshot({
29710
29977
  messages,
@@ -29754,11 +30021,7 @@ ${shared.content}`,
29754
30021
  let lastLoopCheckLen = 0;
29755
30022
  for await (const delta of stream) {
29756
30023
  if (this.cancelled) {
29757
- const cancelEvent = createBrainEvent("error", this.sessionId, {
29758
- severity: "cancelled",
29759
- message: "Run cancelled by user.",
29760
- code: "cancelled"
29761
- });
30024
+ const cancelEvent = this.buildCancelEvent();
29762
30025
  this.emit(cancelEvent);
29763
30026
  yield cancelEvent;
29764
30027
  break;
@@ -31002,6 +31265,7 @@ __export(harness_exports, {
31002
31265
  TEXT_TOOLS_PARTIAL_USER: () => TEXT_TOOLS_PARTIAL_USER,
31003
31266
  TOOL_CALL_TRUNCATED_RECOVERY_MARKER: () => TOOL_CALL_TRUNCATED_RECOVERY_MARKER,
31004
31267
  TOOL_CALL_TRUNCATED_RECOVERY_USER: () => TOOL_CALL_TRUNCATED_RECOVERY_USER,
31268
+ __resetRequestSnapshotMemoForTests: () => __resetRequestSnapshotMemoForTests,
31005
31269
  canonicalTools: () => canonicalTools,
31006
31270
  collapseLoopedAssistantText: () => collapseLoopedAssistantText,
31007
31271
  compareReplayPrefix: () => compareReplayPrefix,
@@ -31024,6 +31288,7 @@ __export(harness_exports, {
31024
31288
  recordRequest: () => recordRequest,
31025
31289
  recordToolResult: () => recordToolResult,
31026
31290
  recordUsage: () => recordUsage,
31291
+ resolveRequestSnapshotMode: () => resolveRequestSnapshotMode,
31027
31292
  runExtensionPreToolUse: () => runExtensionPreToolUse,
31028
31293
  sha256Hex: () => sha256Hex,
31029
31294
  splitHookCommandLine: () => splitHookCommandLine,
@@ -37360,6 +37625,146 @@ var init_agentAdapter = __esm({
37360
37625
  }
37361
37626
  });
37362
37627
 
37628
+ // packages/core/dist/session/replayCache.js
37629
+ import { promises as fs14 } from "node:fs";
37630
+ function isReplayCacheEnabled(env = process.env) {
37631
+ const v = (env.ZELARI_SPINE_REPLAY_CACHE ?? "").trim().toLowerCase();
37632
+ return v === "1" || v === "true" || v === "yes" || v === "on";
37633
+ }
37634
+ function utf8IncompleteTailBytes(buf) {
37635
+ const max = Math.min(4, buf.length);
37636
+ for (let back = 1; back <= max; back++) {
37637
+ const b = buf[buf.length - back];
37638
+ if (b < 128)
37639
+ return 0;
37640
+ if (b >= 192) {
37641
+ const need = b >= 240 ? 4 : b >= 224 ? 3 : 2;
37642
+ return back >= need ? 0 : need - back;
37643
+ }
37644
+ }
37645
+ return 0;
37646
+ }
37647
+ async function readAt(fh, position, length) {
37648
+ const buf = Buffer.allocUnsafe(length);
37649
+ let done = 0;
37650
+ while (done < length) {
37651
+ const { bytesRead } = await fh.read(buf, done, length - done, position + done);
37652
+ if (bytesRead === 0)
37653
+ break;
37654
+ done += bytesRead;
37655
+ }
37656
+ return done === length ? buf : buf.subarray(0, done);
37657
+ }
37658
+ function toReport(filePath, entry) {
37659
+ const events = entry.events.slice();
37660
+ const issues = entry.issues.slice();
37661
+ return { path: filePath, events, issues, ok: issues.length === 0 };
37662
+ }
37663
+ async function readSessionLogCached(filePath, cache4, env = process.env) {
37664
+ if (!cache4 || !isReplayCacheEnabled(env))
37665
+ return readSessionLog(filePath);
37666
+ return cache4.read(filePath);
37667
+ }
37668
+ var NL, SessionLogCache;
37669
+ var init_replayCache = __esm({
37670
+ "packages/core/dist/session/replayCache.js"() {
37671
+ "use strict";
37672
+ init_replay();
37673
+ NL = 10;
37674
+ SessionLogCache = class {
37675
+ entries = /* @__PURE__ */ new Map();
37676
+ /** Files currently cached (diagnostics). */
37677
+ get size() {
37678
+ return this.entries.size;
37679
+ }
37680
+ /** The cached entry for `filePath` (copy; tests + diagnostics), if any. */
37681
+ peek(filePath) {
37682
+ const entry = this.entries.get(filePath);
37683
+ return entry ? { ...entry } : void 0;
37684
+ }
37685
+ /** Read the log, incrementally when a usable entry exists. */
37686
+ async read(filePath) {
37687
+ const cached2 = this.entries.get(filePath);
37688
+ let stat8;
37689
+ try {
37690
+ stat8 = await fs14.stat(filePath);
37691
+ } catch (err) {
37692
+ if (err.code === "ENOENT") {
37693
+ this.entries.delete(filePath);
37694
+ return { path: filePath, events: [], issues: [], ok: true };
37695
+ }
37696
+ throw err;
37697
+ }
37698
+ if (cached2 && stat8.size >= cached2.byteSize && stat8.mtimeMs >= cached2.mtimeMs) {
37699
+ if (stat8.size > cached2.byteSize)
37700
+ return this.extend(filePath, cached2, stat8);
37701
+ return toReport(filePath, cached2);
37702
+ }
37703
+ return this.full(filePath, stat8);
37704
+ }
37705
+ /** Append-path read: only the bytes written since the last read are parsed. */
37706
+ async extend(filePath, cached2, stat8) {
37707
+ const fh = await fs14.open(filePath, "r");
37708
+ let chunk;
37709
+ try {
37710
+ chunk = await readAt(fh, cached2.byteSize, stat8.size - cached2.byteSize);
37711
+ } finally {
37712
+ await fh.close();
37713
+ }
37714
+ const nl = chunk.lastIndexOf(NL);
37715
+ const headEnd = nl === -1 ? 0 : nl + 1;
37716
+ const tail2 = chunk.subarray(headEnd);
37717
+ const keep = tail2.subarray(0, tail2.length - utf8IncompleteTailBytes(tail2));
37718
+ const head = cached2.partial + chunk.subarray(0, headEnd).toString("utf8");
37719
+ const lines = headEnd > 0 ? head.split("\n").slice(0, -1) : [];
37720
+ const parsed = parseSessionLogLines(lines, {
37721
+ expected: cached2.expectedSeq,
37722
+ linesConsumed: cached2.linesConsumed
37723
+ });
37724
+ const entry = {
37725
+ byteSize: cached2.byteSize + headEnd + keep.length,
37726
+ mtimeMs: stat8.mtimeMs,
37727
+ events: parsed.events.length > 0 ? cached2.events.concat(parsed.events) : cached2.events,
37728
+ issues: parsed.issues.length > 0 ? cached2.issues.concat(parsed.issues) : cached2.issues,
37729
+ expectedSeq: parsed.expected,
37730
+ partial: (headEnd > 0 ? "" : cached2.partial) + keep.toString("utf8"),
37731
+ linesConsumed: parsed.linesConsumed
37732
+ };
37733
+ this.entries.set(filePath, entry);
37734
+ return toReport(filePath, entry);
37735
+ }
37736
+ /** Cold / rotated read: same parse as `readSessionLog`, then seed the entry. */
37737
+ async full(filePath, stat8) {
37738
+ let content;
37739
+ try {
37740
+ content = await fs14.readFile(filePath, "utf-8");
37741
+ } catch (err) {
37742
+ if (err.code === "ENOENT") {
37743
+ this.entries.delete(filePath);
37744
+ return { path: filePath, events: [], issues: [], ok: true };
37745
+ }
37746
+ throw err;
37747
+ }
37748
+ const lines = content.split("\n");
37749
+ const parsed = parseSessionLogLines(lines);
37750
+ const linesConsumed = content.length === 0 ? 0 : content.endsWith("\n") ? lines.length - 1 : lines.length;
37751
+ const entry = {
37752
+ // Bytes actually read (never the pre-read stat: the log may have grown).
37753
+ byteSize: Buffer.byteLength(content, "utf8"),
37754
+ mtimeMs: stat8.mtimeMs,
37755
+ events: parsed.events,
37756
+ issues: parsed.issues,
37757
+ expectedSeq: parsed.expected,
37758
+ partial: "",
37759
+ linesConsumed
37760
+ };
37761
+ this.entries.set(filePath, entry);
37762
+ return { path: filePath, events: entry.events.slice(), issues: entry.issues.slice(), ok: entry.issues.length === 0 };
37763
+ }
37764
+ };
37765
+ }
37766
+ });
37767
+
37363
37768
  // packages/core/dist/session/lineage.js
37364
37769
  async function forkSession(store6, parentSessionId, options = {}) {
37365
37770
  const parent = await store6.read(parentSessionId);
@@ -37923,6 +38328,7 @@ var init_session = __esm({
37923
38328
  init_agentAdapter();
37924
38329
  init_writer();
37925
38330
  init_replay();
38331
+ init_replayCache();
37926
38332
  init_store();
37927
38333
  init_lineage();
37928
38334
  init_exportSession();
@@ -38416,12 +38822,12 @@ var CORE_VERSION;
38416
38822
  var init_version = __esm({
38417
38823
  "packages/core/dist/version.js"() {
38418
38824
  "use strict";
38419
- CORE_VERSION = "2.37.2";
38825
+ CORE_VERSION = "2.38.0";
38420
38826
  }
38421
38827
  });
38422
38828
 
38423
38829
  // packages/core/dist/runtime/fingerprints.js
38424
- import { createHash as createHash10 } from "node:crypto";
38830
+ import { createHash as createHash11 } from "node:crypto";
38425
38831
  function toolFingerprintHash(tools) {
38426
38832
  const canonical = [...tools].map((t) => ({
38427
38833
  name: t.name,
@@ -38430,7 +38836,7 @@ function toolFingerprintHash(tools) {
38430
38836
  ...t.outputContractVersion !== void 0 ? { outputContractVersion: t.outputContractVersion } : {},
38431
38837
  ...t.capabilityFlags !== void 0 ? { capabilityFlags: [...t.capabilityFlags].sort() } : {}
38432
38838
  })).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
38433
- return createHash10("sha256").update(stableStringify(canonical)).digest("hex");
38839
+ return createHash11("sha256").update(stableStringify(canonical)).digest("hex");
38434
38840
  }
38435
38841
  function skillFingerprintHash(skills) {
38436
38842
  const canonical = [...skills].map((s) => ({
@@ -38438,7 +38844,7 @@ function skillFingerprintHash(skills) {
38438
38844
  ...s.version !== void 0 ? { version: s.version } : {},
38439
38845
  ...s.contentDigest !== void 0 ? { contentDigest: s.contentDigest } : {}
38440
38846
  })).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
38441
- return createHash10("sha256").update(stableStringify(canonical)).digest("hex");
38847
+ return createHash11("sha256").update(stableStringify(canonical)).digest("hex");
38442
38848
  }
38443
38849
  var init_fingerprints = __esm({
38444
38850
  "packages/core/dist/runtime/fingerprints.js"() {
@@ -38476,6 +38882,7 @@ __export(dist_exports, {
38476
38882
  CommandCheckSchema: () => CommandCheckSchema,
38477
38883
  CriterionSchema: () => CriterionSchema,
38478
38884
  CriterionSourceSchema: () => CriterionSourceSchema,
38885
+ DEFAULT_COMMAND_CONCURRENCY: () => DEFAULT_COMMAND_CONCURRENCY,
38479
38886
  DEFAULT_CONTEXT_POLICY: () => DEFAULT_CONTEXT_POLICY,
38480
38887
  DEFAULT_HEARTBEAT_STALE_MS: () => DEFAULT_HEARTBEAT_STALE_MS,
38481
38888
  DEFAULT_MAX_NODES: () => DEFAULT_MAX_NODES,
@@ -38602,6 +39009,7 @@ __export(dist_exports, {
38602
39009
  SessionActorSchema: () => SessionActorSchema,
38603
39010
  SessionEventEnvelopeSchema: () => SessionEventEnvelopeSchema,
38604
39011
  SessionJsonlWriter: () => SessionJsonlWriter,
39012
+ SessionLogCache: () => SessionLogCache,
38605
39013
  SessionLogLockedError: () => SessionLogLockedError,
38606
39014
  SessionLogWriter: () => SessionLogWriter,
38607
39015
  SessionStore: () => SessionStore,
@@ -38640,6 +39048,7 @@ __export(dist_exports, {
38640
39048
  WorkspacePathEscapeError: () => WorkspacePathEscapeError,
38641
39049
  WorktreeWorkspace: () => WorktreeWorkspace,
38642
39050
  ZELARI_CODING_PACK_ID: () => ZELARI_CODING_PACK_ID,
39051
+ __resetRequestSnapshotMemoForTests: () => __resetRequestSnapshotMemoForTests,
38643
39052
  analyzeScope: () => analyzeScope,
38644
39053
  applyCompletionRetry: () => applyCompletionRetry,
38645
39054
  applyDeterministicAutofix: () => applyDeterministicAutofix,
@@ -38801,6 +39210,7 @@ __export(dist_exports, {
38801
39210
  isFollowUpControlEvent: () => isFollowUpControlEvent,
38802
39211
  isGeneratedPath: () => isGeneratedPath,
38803
39212
  isModelSurfaceEvent: () => isModelSurfaceEvent,
39213
+ isReplayCacheEnabled: () => isReplayCacheEnabled,
38804
39214
  isReviewerKind: () => isReviewerKind,
38805
39215
  isSearchTool: () => isSearchTool,
38806
39216
  isSeqShadowed: () => isSeqShadowed,
@@ -38809,6 +39219,7 @@ __export(dist_exports, {
38809
39219
  isSteerControlEvent: () => isSteerControlEvent,
38810
39220
  isValidTool: () => isValidTool,
38811
39221
  isVerificationReserveProtected: () => isVerificationReserveProtected,
39222
+ isVerifyParallelEnabled: () => isVerifyParallelEnabled,
38812
39223
  isVerifyToolCheckSkipped: () => isVerifyToolCheckSkipped,
38813
39224
  jaccardSimilarity: () => jaccardSimilarity,
38814
39225
  jsonBytes: () => jsonBytes,
@@ -38847,6 +39258,8 @@ __export(dist_exports, {
38847
39258
  parseNameOnlyDiff: () => parseNameOnlyDiff,
38848
39259
  parsePersonaVerdict: () => parsePersonaVerdict,
38849
39260
  parseProjectRootFromWorkspaceContext: () => parseProjectRootFromWorkspaceContext,
39261
+ parseSessionLogLines: () => parseSessionLogLines,
39262
+ parseSessionLogText: () => parseSessionLogText,
38850
39263
  parseTextToolCalls: () => parseTextToolCalls,
38851
39264
  parseTextToolCallsDetailed: () => parseTextToolCallsDetailed,
38852
39265
  parseThinking: () => parseThinking,
@@ -38864,6 +39277,7 @@ __export(dist_exports, {
38864
39277
  readLessonsDeduped: () => readLessonsDeduped,
38865
39278
  readSession: () => readSession,
38866
39279
  readSessionLog: () => readSessionLog,
39280
+ readSessionLogCached: () => readSessionLogCached,
38867
39281
  recallLessons: () => recallLessons,
38868
39282
  recencyScore: () => recencyScore,
38869
39283
  recordRequest: () => recordRequest,
@@ -38880,6 +39294,7 @@ __export(dist_exports, {
38880
39294
  renderSteers: () => renderSteers,
38881
39295
  replayChairmanTextTools: () => replayChairmanTextTools,
38882
39296
  resolveAgentSkills: () => resolveAgentSkills,
39297
+ resolveCommandConcurrency: () => resolveCommandConcurrency,
38883
39298
  resolveCouncilRunMode: () => resolveCouncilRunMode,
38884
39299
  resolveHeartbeatStaleMs: () => resolveHeartbeatStaleMs,
38885
39300
  resolveInterventions: () => resolveInterventions,
@@ -38887,6 +39302,7 @@ __export(dist_exports, {
38887
39302
  resolveMaxTentacles: () => resolveMaxTentacles,
38888
39303
  resolvePlanTimeoutMs: () => resolvePlanTimeoutMs,
38889
39304
  resolveProfile: () => resolveProfile,
39305
+ resolveRequestSnapshotMode: () => resolveRequestSnapshotMode,
38890
39306
  resolveResponseLanguage: () => resolveResponseLanguage,
38891
39307
  resolveRoleSystemPrompt: () => resolveRoleSystemPrompt,
38892
39308
  resolveSessionsDir: () => resolveSessionsDir,
@@ -39121,7 +39537,7 @@ var init_graphStatus = __esm({
39121
39537
  });
39122
39538
 
39123
39539
  // src/cli/sessionManager.ts
39124
- import { promises as fs14, existsSync as existsSync13, readFileSync as readFileSync15, writeFileSync as writeFileSync11, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, statSync } from "node:fs";
39540
+ import { promises as fs15, existsSync as existsSync13, readFileSync as readFileSync15, writeFileSync as writeFileSync11, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, statSync } from "node:fs";
39125
39541
  import path30 from "node:path";
39126
39542
  import { randomUUID as randomUUID2 } from "node:crypto";
39127
39543
  function getSessionBaseDir() {
@@ -39131,7 +39547,7 @@ function getCurrentSessionFile() {
39131
39547
  return currentSessionPath();
39132
39548
  }
39133
39549
  async function ensureSessionDir() {
39134
- await fs14.mkdir(getSessionBaseDir(), { recursive: true });
39550
+ await fs15.mkdir(getSessionBaseDir(), { recursive: true });
39135
39551
  }
39136
39552
  function getCurrentSessionId() {
39137
39553
  const file2 = getCurrentSessionFile();
@@ -39181,7 +39597,7 @@ async function listSessions() {
39181
39597
  const baseDir = getSessionBaseDir();
39182
39598
  let entries;
39183
39599
  try {
39184
- entries = await fs14.readdir(baseDir);
39600
+ entries = await fs15.readdir(baseDir);
39185
39601
  } catch (err) {
39186
39602
  if (err.code === "ENOENT") return [];
39187
39603
  throw err;
@@ -39571,16 +39987,16 @@ var init_budgetRuntime = __esm({
39571
39987
 
39572
39988
  // src/cli/budget/restoreRuntime.ts
39573
39989
  import path31 from "node:path";
39574
- async function restoreBudgetRuntimeFromSession(budget, sessionId2, baseDir) {
39990
+ async function restoreBudgetRuntimeFromSession(budget, sessionId2, baseDir, cache4) {
39575
39991
  const eventsPath = path31.join(resolveSessionsDir({ baseDir }), sessionId2, "events.jsonl");
39576
- const report = await readSessionLog(eventsPath).catch(() => null);
39992
+ const report = await readSessionLogCached(eventsPath, cache4).catch(() => null);
39577
39993
  if (!report || report.events.length === 0) return false;
39578
39994
  budget.adoptLedgerFromEvents(report.events);
39579
39995
  return true;
39580
39996
  }
39581
- async function lastHarnessManifestHash(sessionId2, baseDir) {
39997
+ async function lastHarnessManifestHash(sessionId2, baseDir, cache4) {
39582
39998
  const eventsPath = path31.join(resolveSessionsDir({ baseDir }), sessionId2, "events.jsonl");
39583
- const report = await readSessionLog(eventsPath).catch(() => null);
39999
+ const report = await readSessionLogCached(eventsPath, cache4).catch(() => null);
39584
40000
  if (!report) return null;
39585
40001
  for (let i = report.events.length - 1; i >= 0; i--) {
39586
40002
  const e = report.events[i];
@@ -40046,7 +40462,7 @@ async function wrapSessionWriter(inner, sessionId2, options = {}) {
40046
40462
  enforcement: resolveResourceEnforcement()
40047
40463
  });
40048
40464
  if (spine.resumedFromSeq !== void 0 && spine.resumedFromSeq > 0) {
40049
- await restoreBudgetRuntimeFromSession(budget, sessionId2, options.baseDir);
40465
+ await restoreBudgetRuntimeFromSession(budget, sessionId2, options.baseDir, spine.replayCache);
40050
40466
  }
40051
40467
  spine.attachBudgetRuntime(budget);
40052
40468
  await noteHarnessLifecycle(
@@ -40077,7 +40493,7 @@ async function noteHarnessLifecycle(spine, sessionId2, profileId, budget, baseDi
40077
40493
  resourcePolicy: budget.policy
40078
40494
  });
40079
40495
  if (spine.resumedFromSeq !== void 0 && spine.resumedFromSeq > 0) {
40080
- if (await lastHarnessManifestHash(sessionId2, baseDir) === null) {
40496
+ if (await lastHarnessManifestHash(sessionId2, baseDir, spine.replayCache) === null) {
40081
40497
  spine.harnessManifest(manifest, manifestHash);
40082
40498
  }
40083
40499
  } else {
@@ -40127,6 +40543,13 @@ var init_sessionSpine = __esm({
40127
40543
  /** Seq the log continued from when adopting an existing session. */
40128
40544
  resumedFromSeq;
40129
40545
  sessionsDir;
40546
+ /**
40547
+ * PERF-4a: session log cache — ONE per session (this mirror). Every read of
40548
+ * `<sessionId>/events.jsonl` goes through it; the kill switch
40549
+ * ZELARI_SPINE_REPLAY_CACHE defaults OFF (see @zelari/core/session
40550
+ * replayCache.ts), and shared with the host helpers that take a cache.
40551
+ */
40552
+ replayCache = new SessionLogCache();
40130
40553
  /**
40131
40554
  * Adopt a 1.x sessionId into the spine: continue the seq when the log
40132
40555
  * already exists (resume), otherwise start fresh with `session.started`.
@@ -40137,7 +40560,10 @@ var init_sessionSpine = __esm({
40137
40560
  if (!spineEnabled()) return mirror;
40138
40561
  try {
40139
40562
  const sessionDir = path33.join(mirror.sessionsDir, sessionId2);
40140
- const report = await readSessionLog(path33.join(sessionDir, "events.jsonl"));
40563
+ const report = await readSessionLogCached(
40564
+ path33.join(sessionDir, "events.jsonl"),
40565
+ mirror.replayCache
40566
+ );
40141
40567
  const existed = report.events.length > 0 || report.issues.length > 0;
40142
40568
  if (report.events.some((e) => e.kind === "task.contract" || e.kind === "user.message")) {
40143
40569
  mirror.contractSeeded = true;
@@ -40312,8 +40738,9 @@ var init_sessionSpine = __esm({
40312
40738
  */
40313
40739
  async derivedPriorTurns() {
40314
40740
  if (this.status !== "active" && this.status !== "closed") return null;
40315
- const report = await readSessionLog(
40316
- path33.join(this.sessionsDir, this.sessionId, "events.jsonl")
40741
+ const report = await readSessionLogCached(
40742
+ path33.join(this.sessionsDir, this.sessionId, "events.jsonl"),
40743
+ this.replayCache
40317
40744
  ).catch(() => null);
40318
40745
  if (!report || report.events.length === 0) return null;
40319
40746
  return deriveMessages(report.events);
@@ -40322,8 +40749,9 @@ var init_sessionSpine = __esm({
40322
40749
  async compactionStateSnapshot(toSeq) {
40323
40750
  if (this.status !== "active" && this.status !== "closed") return null;
40324
40751
  await this.flush();
40325
- const report = await readSessionLog(
40326
- path33.join(this.sessionsDir, this.sessionId, "events.jsonl")
40752
+ const report = await readSessionLogCached(
40753
+ path33.join(this.sessionsDir, this.sessionId, "events.jsonl"),
40754
+ this.replayCache
40327
40755
  ).catch(() => null);
40328
40756
  if (!report || report.events.length === 0) return null;
40329
40757
  return buildCompactionStateSnapshot(report.events, toSeq);
@@ -40335,8 +40763,9 @@ var init_sessionSpine = __esm({
40335
40763
  */
40336
40764
  async lastVerificationRun() {
40337
40765
  if (this.status !== "active" && this.status !== "closed") return null;
40338
- const report = await readSessionLog(
40339
- path33.join(this.sessionsDir, this.sessionId, "events.jsonl")
40766
+ const report = await readSessionLogCached(
40767
+ path33.join(this.sessionsDir, this.sessionId, "events.jsonl"),
40768
+ this.replayCache
40340
40769
  ).catch(() => null);
40341
40770
  if (!report) return null;
40342
40771
  return lastVerificationRun(report.events);
@@ -40472,7 +40901,7 @@ var init_sessionSpine = __esm({
40472
40901
  seq = seq.then(async (s) => {
40473
40902
  try {
40474
40903
  const eventsPath = path33.join(this.sessionsDir, this.sessionId, "events.jsonl");
40475
- const report = await readSessionLog(eventsPath).catch(() => null);
40904
+ const report = await readSessionLogCached(eventsPath, this.replayCache).catch(() => null);
40476
40905
  if (!report || typeof s !== "number") return s;
40477
40906
  const current = latestTaskContract(report.events);
40478
40907
  if (!current) return s;
@@ -40740,7 +41169,7 @@ async function loadObservationIndex(sessionId2, baseDir) {
40740
41169
  mtimeMs = 0;
40741
41170
  }
40742
41171
  }
40743
- const hit = cache.get(sessionId2);
41172
+ const hit = cache2.get(sessionId2);
40744
41173
  if (hit && !exists) return hit;
40745
41174
  if (hit && hit.filePath === filePath && hit.mtimeMs === mtimeMs) return hit;
40746
41175
  const events = existsSync15(filePath) ? await readSession(filePath) : [];
@@ -40769,12 +41198,12 @@ async function loadObservationIndex(sessionId2, baseDir) {
40769
41198
  if (e.toolCallId) byToolCallId.set(e.toolCallId, seq);
40770
41199
  }
40771
41200
  const index = { filePath, mtimeMs, bySeq, byToolCallId, names };
40772
- cache.set(sessionId2, index);
41201
+ cache2.set(sessionId2, index);
40773
41202
  return index;
40774
41203
  }
40775
41204
  function invalidateObservationIndex(sessionId2) {
40776
- if (sessionId2) cache.delete(sessionId2);
40777
- else cache.clear();
41205
+ if (sessionId2) cache2.delete(sessionId2);
41206
+ else cache2.clear();
40778
41207
  }
40779
41208
  async function getObservationBySeq(sessionId2, seq, baseDir) {
40780
41209
  const index = await loadObservationIndex(sessionId2, baseDir);
@@ -40782,7 +41211,7 @@ async function getObservationBySeq(sessionId2, seq, baseDir) {
40782
41211
  }
40783
41212
  function lookupSeqSync(sessionId2, toolCallId, baseDir) {
40784
41213
  const filePath = sessionFilePath(sessionId2, baseDir);
40785
- const hit = cache.get(sessionId2);
41214
+ const hit = cache2.get(sessionId2);
40786
41215
  if (hit && hit.filePath === filePath) return hit.byToolCallId.get(toolCallId);
40787
41216
  return void 0;
40788
41217
  }
@@ -40797,10 +41226,10 @@ function emptyIndex(sessionId2, baseDir) {
40797
41226
  }
40798
41227
  function ingestLiveEvent(sessionId2, event, baseDir) {
40799
41228
  if (!sessionId2) return;
40800
- let index = cache.get(sessionId2);
41229
+ let index = cache2.get(sessionId2);
40801
41230
  if (!index) {
40802
41231
  index = emptyIndex(sessionId2, baseDir);
40803
- cache.set(sessionId2, index);
41232
+ cache2.set(sessionId2, index);
40804
41233
  }
40805
41234
  if (isToolStart(event)) {
40806
41235
  index.names.set(event.toolCallId, event.toolName);
@@ -40828,19 +41257,19 @@ function applySessionSurface(messages) {
40828
41257
  const lookup = sid ? (id3) => lookupSeqSync(sid, id3) : void 0;
40829
41258
  return projectSessionSurface(messages, void 0, lookup).messages;
40830
41259
  }
40831
- var cache;
41260
+ var cache2;
40832
41261
  var init_observationStore = __esm({
40833
41262
  "src/cli/hooks/observationStore.ts"() {
40834
41263
  "use strict";
40835
41264
  init_harness();
40836
41265
  init_sessionManager();
40837
41266
  init_sessionSurface();
40838
- cache = /* @__PURE__ */ new Map();
41267
+ cache2 = /* @__PURE__ */ new Map();
40839
41268
  }
40840
41269
  });
40841
41270
 
40842
41271
  // packages/core/dist/core/tools/toolOutputSpill.js
40843
- import { createHash as createHash11, randomBytes as randomBytes3 } from "node:crypto";
41272
+ import { createHash as createHash12, randomBytes as randomBytes3 } from "node:crypto";
40844
41273
  import { existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync12 } from "node:fs";
40845
41274
  import { homedir as homedir5, tmpdir as tmpdir2 } from "node:os";
40846
41275
  import { join as join14 } from "node:path";
@@ -40870,7 +41299,7 @@ function spillToolOutput(fullText, meta3) {
40870
41299
  if (!existsSync16(dir)) {
40871
41300
  mkdirSync7(dir, { recursive: true });
40872
41301
  }
40873
- const hash3 = createHash11("sha256").update(fullText).digest("hex").slice(0, 12);
41302
+ const hash3 = createHash12("sha256").update(fullText).digest("hex").slice(0, 12);
40874
41303
  const stamp = Date.now().toString(36);
40875
41304
  const rnd = randomBytes3(3).toString("hex");
40876
41305
  const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
@@ -41173,7 +41602,7 @@ var init_registry2 = __esm({
41173
41602
 
41174
41603
  // src/cli/safety/sandboxPath.ts
41175
41604
  import path35 from "node:path";
41176
- import fs15 from "node:fs";
41605
+ import fs16 from "node:fs";
41177
41606
  function normalizedCase(p3) {
41178
41607
  return IS_CASE_FOLDING ? p3.toLowerCase() : p3;
41179
41608
  }
@@ -41207,7 +41636,7 @@ function assertRealAncestorContained(resolvedLexical, realRoot, userPath) {
41207
41636
  for (; ; ) {
41208
41637
  if (probe === path35.dirname(probe)) break;
41209
41638
  try {
41210
- realProbe = fs15.realpathSync(probe);
41639
+ realProbe = fs16.realpathSync(probe);
41211
41640
  break;
41212
41641
  } catch (err) {
41213
41642
  if (err?.code === "ENOENT") {
@@ -41235,7 +41664,7 @@ function resolveSandboxedCore(userPath, options = {}) {
41235
41664
  assertLexicalContainment(resolved, root, userPath);
41236
41665
  let realRoot = null;
41237
41666
  try {
41238
- realRoot = fs15.realpathSync(root);
41667
+ realRoot = fs16.realpathSync(root);
41239
41668
  } catch {
41240
41669
  return resolved;
41241
41670
  }
@@ -41254,7 +41683,7 @@ function verifyContainment(resolvedAbsolute, options = {}) {
41254
41683
  assertLexicalContainment(resolved, root, resolvedAbsolute);
41255
41684
  let realRoot = null;
41256
41685
  try {
41257
- realRoot = fs15.realpathSync(root);
41686
+ realRoot = fs16.realpathSync(root);
41258
41687
  } catch {
41259
41688
  return;
41260
41689
  }
@@ -41278,7 +41707,7 @@ var init_sandboxPath = __esm({
41278
41707
  });
41279
41708
 
41280
41709
  // src/cli/tools/krakenRadio.ts
41281
- import { appendFileSync as appendFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync16, readdirSync as readdirSync3 } from "node:fs";
41710
+ import { appendFileSync as appendFileSync2, closeSync, existsSync as existsSync17, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync16, readdirSync as readdirSync3, writeSync } from "node:fs";
41282
41711
  import path36 from "node:path";
41283
41712
  function radioDir(cwd) {
41284
41713
  return path36.join(cwd, ".zelari", "radio");
@@ -41287,6 +41716,35 @@ function radioPath(cwd, sessionId2) {
41287
41716
  const safe = (sessionId2 || "default").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
41288
41717
  return path36.join(radioDir(cwd), `${safe}.jsonl`);
41289
41718
  }
41719
+ function appendRadioLine(file2, line) {
41720
+ try {
41721
+ const cached2 = radioFds.get(file2);
41722
+ if (cached2 !== void 0) {
41723
+ writeSync(cached2, line, null, "utf8");
41724
+ return;
41725
+ }
41726
+ if (radioFds.size >= MAX_CACHED_RADIO_FDS) {
41727
+ appendFileSync2(file2, line, "utf8");
41728
+ return;
41729
+ }
41730
+ const fd = openSync(file2, "a");
41731
+ writeSync(fd, line, null, "utf8");
41732
+ radioFds.set(file2, fd);
41733
+ } catch {
41734
+ const stale = radioFds.get(file2);
41735
+ if (stale !== void 0) {
41736
+ radioFds.delete(file2);
41737
+ try {
41738
+ closeSync(stale);
41739
+ } catch {
41740
+ }
41741
+ }
41742
+ try {
41743
+ appendFileSync2(file2, line, "utf8");
41744
+ } catch {
41745
+ }
41746
+ }
41747
+ }
41290
41748
  function appendKrakenRadio(cwd, sessionId2, event) {
41291
41749
  try {
41292
41750
  const dir = radioDir(cwd);
@@ -41312,8 +41770,8 @@ function appendKrakenRadio(cwd, sessionId2, event) {
41312
41770
  ...event.symbolsA !== void 0 ? { symbolsA: event.symbolsA } : {},
41313
41771
  ...event.symbolsB !== void 0 ? { symbolsB: event.symbolsB } : {}
41314
41772
  };
41315
- appendFileSync2(radioPath(cwd, sessionId2), `${JSON.stringify(row)}
41316
- `, "utf8");
41773
+ appendRadioLine(radioPath(cwd, sessionId2), `${JSON.stringify(row)}
41774
+ `);
41317
41775
  } catch {
41318
41776
  }
41319
41777
  }
@@ -41350,9 +41808,12 @@ function formatKrakenRadioStatus(cwd, sessionId2, limit = 12) {
41350
41808
  });
41351
41809
  return [`Kraken radio (last ${events.length}) session=${sessionId2}:`, ...lines].join("\n");
41352
41810
  }
41811
+ var MAX_CACHED_RADIO_FDS, radioFds;
41353
41812
  var init_krakenRadio = __esm({
41354
41813
  "src/cli/tools/krakenRadio.ts"() {
41355
41814
  "use strict";
41815
+ MAX_CACHED_RADIO_FDS = 32;
41816
+ radioFds = /* @__PURE__ */ new Map();
41356
41817
  }
41357
41818
  });
41358
41819
 
@@ -41366,7 +41827,7 @@ import {
41366
41827
  realpathSync
41367
41828
  } from "node:fs";
41368
41829
  import { join as join15, basename } from "node:path";
41369
- import { createHash as createHash12 } from "node:crypto";
41830
+ import { createHash as createHash13 } from "node:crypto";
41370
41831
  function resolveWorkspaceRoot(projectRoot = process.cwd()) {
41371
41832
  const candidates = [
41372
41833
  join15(projectRoot, ".zelari"),
@@ -41382,7 +41843,7 @@ function resolveWorkspaceRoot(projectRoot = process.cwd()) {
41382
41843
  return candidates[0];
41383
41844
  }
41384
41845
  function hashProject(projectPath) {
41385
- return createHash12("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
41846
+ return createHash13("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
41386
41847
  }
41387
41848
  function isWritableDir(dir) {
41388
41849
  try {
@@ -42107,12 +42568,12 @@ var init_taskTouchGuard = __esm({
42107
42568
  });
42108
42569
 
42109
42570
  // src/cli/gitOps.ts
42110
- import { execFile as execFile2 } from "node:child_process";
42111
- import { promisify } from "node:util";
42571
+ import { execFile as execFile3 } from "node:child_process";
42572
+ import { promisify as promisify2 } from "node:util";
42112
42573
  import path38 from "node:path";
42113
42574
  async function git2(cwd, args) {
42114
42575
  try {
42115
- const { stdout } = await execFileAsync("git", ["-C", cwd, ...args], {
42576
+ const { stdout } = await execFileAsync2("git", ["-C", cwd, ...args], {
42116
42577
  maxBuffer: 16 * 1024 * 1024
42117
42578
  });
42118
42579
  return stdout;
@@ -42164,11 +42625,11 @@ async function undoWorkingChanges(opts = {}) {
42164
42625
  function defaultProjectRoot() {
42165
42626
  return path38.resolve(__dirname, "..", "..", "..");
42166
42627
  }
42167
- var execFileAsync;
42628
+ var execFileAsync2;
42168
42629
  var init_gitOps = __esm({
42169
42630
  "src/cli/gitOps.ts"() {
42170
42631
  "use strict";
42171
- execFileAsync = promisify(execFile2);
42632
+ execFileAsync2 = promisify2(execFile3);
42172
42633
  }
42173
42634
  });
42174
42635
 
@@ -42303,7 +42764,7 @@ var auditLogger_exports = {};
42303
42764
  __export(auditLogger_exports, {
42304
42765
  AuditLogger: () => AuditLogger
42305
42766
  });
42306
- import { promises as fs16 } from "node:fs";
42767
+ import { promises as fs17 } from "node:fs";
42307
42768
  import path39 from "node:path";
42308
42769
  function defaultAuditPath() {
42309
42770
  return auditLogPath();
@@ -42350,8 +42811,8 @@ var init_auditLogger = __esm({
42350
42811
  async append(entry) {
42351
42812
  const line = JSON.stringify(entry) + "\n";
42352
42813
  this.writeQueue = this.writeQueue.then(async () => {
42353
- await fs16.mkdir(path39.dirname(this.logPath), { recursive: true });
42354
- await fs16.appendFile(this.logPath, line, "utf-8");
42814
+ await fs17.mkdir(path39.dirname(this.logPath), { recursive: true });
42815
+ await fs17.appendFile(this.logPath, line, "utf-8");
42355
42816
  });
42356
42817
  return this.writeQueue;
42357
42818
  }
@@ -42570,11 +43031,54 @@ var init_engine2 = __esm({
42570
43031
  }
42571
43032
  });
42572
43033
 
43034
+ // src/cli/kraken/worktreeCleanupBatch.ts
43035
+ function resolveWorktreeCleanupMode(env = process.env) {
43036
+ const v = (env.ZELARI_KRAKEN_WORKTREE_CLEANUP ?? "").trim().toLowerCase();
43037
+ if (v === "eager") return "eager";
43038
+ return "batch";
43039
+ }
43040
+ function isKrakenWorktreeCleanupBatched(env = process.env) {
43041
+ return batchScopes > 0 && resolveWorktreeCleanupMode(env) === "batch";
43042
+ }
43043
+ function beginKrakenWorktreeCleanupBatch(env = process.env) {
43044
+ if (resolveWorktreeCleanupMode(env) !== "batch") return false;
43045
+ batchScopes += 1;
43046
+ return true;
43047
+ }
43048
+ function queueWorktreeCleanup(repoRoot, branch) {
43049
+ pendingPruneRoots.add(repoRoot);
43050
+ if (!branch) return;
43051
+ const queued = pendingBranches.get(repoRoot) ?? /* @__PURE__ */ new Set();
43052
+ queued.add(branch);
43053
+ pendingBranches.set(repoRoot, queued);
43054
+ }
43055
+ function takeQueuedWorktreeCleanup() {
43056
+ if (batchScopes > 0) batchScopes -= 1;
43057
+ const repoRoots = /* @__PURE__ */ new Set([...pendingPruneRoots, ...pendingBranches.keys()]);
43058
+ const branchesByRoot = /* @__PURE__ */ new Map();
43059
+ for (const repoRoot of repoRoots) {
43060
+ const queued = pendingBranches.get(repoRoot);
43061
+ if (queued && queued.size > 0) branchesByRoot.set(repoRoot, [...queued]);
43062
+ pendingPruneRoots.delete(repoRoot);
43063
+ pendingBranches.delete(repoRoot);
43064
+ }
43065
+ return { repoRoots: [...repoRoots], branchesByRoot };
43066
+ }
43067
+ var pendingPruneRoots, pendingBranches, batchScopes;
43068
+ var init_worktreeCleanupBatch = __esm({
43069
+ "src/cli/kraken/worktreeCleanupBatch.ts"() {
43070
+ "use strict";
43071
+ pendingPruneRoots = /* @__PURE__ */ new Set();
43072
+ pendingBranches = /* @__PURE__ */ new Map();
43073
+ batchScopes = 0;
43074
+ }
43075
+ });
43076
+
42573
43077
  // src/cli/tools/krakenWorktree.ts
42574
- import { execFile as execFile3 } from "node:child_process";
43078
+ import { execFile as execFile4 } from "node:child_process";
42575
43079
  import { existsSync as existsSync24, mkdirSync as mkdirSync12, rmSync } from "node:fs";
42576
43080
  import path41 from "node:path";
42577
- import { promisify as promisify2 } from "node:util";
43081
+ import { promisify as promisify3 } from "node:util";
42578
43082
  import { randomBytes as randomBytes4 } from "node:crypto";
42579
43083
  function isKrakenWorktreeEnabled(env = process.env) {
42580
43084
  const v = (env.ZELARI_KRAKEN_WORKTREE ?? "").trim().toLowerCase();
@@ -42590,9 +43094,29 @@ function isKrakenWorktreeAutoMergeEnabled(env = process.env) {
42590
43094
  if (v === "0" || v === "false" || v === "no" || v === "off") return false;
42591
43095
  return true;
42592
43096
  }
43097
+ async function flushKrakenWorktreeCleanupBatch() {
43098
+ const { repoRoots, branchesByRoot } = takeQueuedWorktreeCleanup();
43099
+ const branches = [];
43100
+ const deleted = [];
43101
+ let pruned = 0;
43102
+ for (const repoRoot of repoRoots) {
43103
+ await git3(repoRoot, ["worktree", "prune"]);
43104
+ pruned += 1;
43105
+ const list = branchesByRoot.get(repoRoot);
43106
+ if (list && list.length > 0) {
43107
+ branches.push(...list);
43108
+ const r = await git3(repoRoot, ["branch", "-D", ...list]);
43109
+ for (const line of r.stdout.split("\n")) {
43110
+ const m = /^Deleted branch (\S+)/.exec(line.trim());
43111
+ if (m?.[1]) deleted.push(m[1]);
43112
+ }
43113
+ }
43114
+ }
43115
+ return { pruned, branches, deleted, repoRoots };
43116
+ }
42593
43117
  async function git3(cwd, args) {
42594
43118
  try {
42595
- const { stdout, stderr } = await execFileAsync2("git", ["-C", cwd, ...args], {
43119
+ const { stdout, stderr } = await execFileAsync3("git", ["-C", cwd, ...args], {
42596
43120
  maxBuffer: 16 * 1024 * 1024,
42597
43121
  windowsHide: true
42598
43122
  });
@@ -42608,10 +43132,15 @@ async function git3(cwd, args) {
42608
43132
  }
42609
43133
  }
42610
43134
  async function resolveGitRoot(cwd) {
43135
+ const key = path41.resolve(cwd);
43136
+ const memo = gitRootMemo.get(key);
43137
+ if (memo) return memo;
42611
43138
  const r = await git3(cwd, ["rev-parse", "--show-toplevel"]);
42612
43139
  if (!r.ok) return null;
42613
43140
  const root = r.stdout.trim();
42614
- return root || null;
43141
+ if (!root) return null;
43142
+ gitRootMemo.set(key, root);
43143
+ return root;
42615
43144
  }
42616
43145
  async function createKrakenWorktree(cwd, label) {
42617
43146
  const repoRoot = await resolveGitRoot(cwd);
@@ -42741,9 +43270,14 @@ async function cleanupKrakenWorktree(handle, env = process.env) {
42741
43270
  }
42742
43271
  } catch {
42743
43272
  }
43273
+ const branch = handle.branch.startsWith("kraken/") ? handle.branch : null;
43274
+ if (isKrakenWorktreeCleanupBatched(env)) {
43275
+ queueWorktreeCleanup(handle.repoRoot, branch);
43276
+ return;
43277
+ }
42744
43278
  await git3(handle.repoRoot, ["worktree", "prune"]);
42745
- if (handle.branch.startsWith("kraken/")) {
42746
- await git3(handle.repoRoot, ["branch", "-D", handle.branch]);
43279
+ if (branch) {
43280
+ await git3(handle.repoRoot, ["branch", "-D", branch]);
42747
43281
  }
42748
43282
  }
42749
43283
  function formatWorktreeFooter(handle, opts = {}) {
@@ -42757,11 +43291,14 @@ function formatWorktreeFooter(handle, opts = {}) {
42757
43291
  }
42758
43292
  return `worktree used: branch=${handle.branch} path=${handle.path}`;
42759
43293
  }
42760
- var execFileAsync2;
43294
+ var execFileAsync3, gitRootMemo;
42761
43295
  var init_krakenWorktree = __esm({
42762
43296
  "src/cli/tools/krakenWorktree.ts"() {
42763
43297
  "use strict";
42764
- execFileAsync2 = promisify2(execFile3);
43298
+ init_worktreeCleanupBatch();
43299
+ init_worktreeCleanupBatch();
43300
+ execFileAsync3 = promisify3(execFile4);
43301
+ gitRootMemo = /* @__PURE__ */ new Map();
42765
43302
  }
42766
43303
  });
42767
43304
 
@@ -43086,6 +43623,43 @@ var init_metrics2 = __esm({
43086
43623
  }
43087
43624
  });
43088
43625
 
43626
+ // src/cli/tools/tentacleHeartbeat.ts
43627
+ function resolveTentacleHeartbeatMs(env = process.env) {
43628
+ const raw = env.ZELARI_TENTACLE_HEARTBEAT_MS?.trim();
43629
+ if (raw === "0" || raw === "off") return 0;
43630
+ if (!raw) return TENTACLE_HEARTBEAT_DEFAULT_MS;
43631
+ const n = Number.parseInt(raw, 10);
43632
+ return Number.isFinite(n) && n >= 0 ? n : TENTACLE_HEARTBEAT_DEFAULT_MS;
43633
+ }
43634
+ function formatTentacleElapsed(ms) {
43635
+ const totalSec = Math.max(0, Math.floor(ms / 1e3));
43636
+ const m = Math.floor(totalSec / 60);
43637
+ const s = totalSec % 60;
43638
+ return m > 0 ? `${m}m ${s}s` : `${s}s`;
43639
+ }
43640
+ function tentacleHeartbeatCaption(elapsedMs) {
43641
+ return `reasoning \xB7 ${formatTentacleElapsed(elapsedMs)}`;
43642
+ }
43643
+ function startTentacleHeartbeat(onBeat, opts) {
43644
+ const intervalMs = opts?.intervalMs ?? resolveTentacleHeartbeatMs();
43645
+ if (intervalMs <= 0) return () => {
43646
+ };
43647
+ const now = opts?.now ?? Date.now;
43648
+ const started = now();
43649
+ const timer = setInterval(() => {
43650
+ onBeat(tentacleHeartbeatCaption(now() - started));
43651
+ }, intervalMs);
43652
+ timer.unref?.();
43653
+ return () => clearInterval(timer);
43654
+ }
43655
+ var TENTACLE_HEARTBEAT_DEFAULT_MS;
43656
+ var init_tentacleHeartbeat = __esm({
43657
+ "src/cli/tools/tentacleHeartbeat.ts"() {
43658
+ "use strict";
43659
+ TENTACLE_HEARTBEAT_DEFAULT_MS = 15e3;
43660
+ }
43661
+ });
43662
+
43089
43663
  // src/cli/kraken/tentacle.ts
43090
43664
  var tentacle_exports = {};
43091
43665
  __export(tentacle_exports, {
@@ -43100,15 +43674,46 @@ var init_tentacle = __esm({
43100
43674
  }
43101
43675
  });
43102
43676
 
43677
+ // src/cli/asyncLimit.ts
43678
+ async function runWithLimit2(items, limit, fn) {
43679
+ const slots = Math.min(
43680
+ items.length,
43681
+ Math.max(1, Number.isFinite(limit) ? Math.floor(limit) : 1)
43682
+ );
43683
+ if (slots <= 0) return;
43684
+ let cursor = 0;
43685
+ let failed = false;
43686
+ const worker = async () => {
43687
+ while (!failed && cursor < items.length) {
43688
+ const index = cursor;
43689
+ cursor += 1;
43690
+ try {
43691
+ await fn(items[index], index);
43692
+ } catch (err) {
43693
+ failed = true;
43694
+ throw err;
43695
+ }
43696
+ }
43697
+ };
43698
+ const workers = [];
43699
+ for (let i = 0; i < slots; i += 1) workers.push(worker());
43700
+ await Promise.all(workers);
43701
+ }
43702
+ var init_asyncLimit = __esm({
43703
+ "src/cli/asyncLimit.ts"() {
43704
+ "use strict";
43705
+ }
43706
+ });
43707
+
43103
43708
  // src/cli/checkpoint/checkpointManager.ts
43104
- import { execFile as execFile4 } from "node:child_process";
43105
- import { promisify as promisify3 } from "node:util";
43709
+ import { execFile as execFile5 } from "node:child_process";
43710
+ import { promisify as promisify4 } from "node:util";
43106
43711
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
43107
43712
  import { tmpdir as tmpdir3 } from "node:os";
43108
43713
  import path42 from "node:path";
43109
43714
  import { randomUUID as randomUUID3 } from "node:crypto";
43110
43715
  async function git4(cwd, args, env) {
43111
- const { stdout } = await execFileAsync3("git", ["-C", cwd, ...args], {
43716
+ const { stdout } = await execFileAsync4("git", ["-C", cwd, ...args], {
43112
43717
  maxBuffer: 64 * 1024 * 1024,
43113
43718
  env: env ? { ...process.env, ...env } : process.env
43114
43719
  });
@@ -43230,11 +43835,11 @@ async function restoreCheckpoint(cwd, id3) {
43230
43835
  async function dropCheckpoint(cwd, id3) {
43231
43836
  return await gitSafe(cwd, ["update-ref", "-d", `${REF_PREFIX}${id3}`]) !== null;
43232
43837
  }
43233
- var execFileAsync3, REF_PREFIX;
43838
+ var execFileAsync4, REF_PREFIX;
43234
43839
  var init_checkpointManager = __esm({
43235
43840
  "src/cli/checkpoint/checkpointManager.ts"() {
43236
43841
  "use strict";
43237
- execFileAsync3 = promisify3(execFile4);
43842
+ execFileAsync4 = promisify4(execFile5);
43238
43843
  REF_PREFIX = "refs/zelari/checkpoints/";
43239
43844
  }
43240
43845
  });
@@ -43296,7 +43901,7 @@ var init_transactional = __esm({
43296
43901
  });
43297
43902
 
43298
43903
  // src/cli/workspace/worldModel.ts
43299
- import { promises as fs17 } from "node:fs";
43904
+ import { promises as fs18 } from "node:fs";
43300
43905
  import path43 from "node:path";
43301
43906
  import { spawn as spawn8 } from "node:child_process";
43302
43907
  function worldDir(cwd) {
@@ -43304,18 +43909,18 @@ function worldDir(cwd) {
43304
43909
  }
43305
43910
  async function ensureWorldDir(cwd) {
43306
43911
  const dir = worldDir(cwd);
43307
- await fs17.mkdir(dir, { recursive: true });
43912
+ await fs18.mkdir(dir, { recursive: true });
43308
43913
  return dir;
43309
43914
  }
43310
43915
  async function appendTimeline(cwd, entry) {
43311
43916
  const dir = await ensureWorldDir(cwd);
43312
43917
  const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }) + "\n";
43313
- await fs17.appendFile(path43.join(dir, TIMELINE_FILE), line, "utf8");
43918
+ await fs18.appendFile(path43.join(dir, TIMELINE_FILE), line, "utf8");
43314
43919
  }
43315
43920
  async function readChecks(cwd) {
43316
43921
  const p3 = path43.join(worldDir(cwd), CHECKS_FILE);
43317
43922
  try {
43318
- const raw = await fs17.readFile(p3, "utf8");
43923
+ const raw = await fs18.readFile(p3, "utf8");
43319
43924
  const parsed = JSON.parse(raw);
43320
43925
  return Array.isArray(parsed.checks) ? parsed.checks : [];
43321
43926
  } catch {
@@ -43483,11 +44088,11 @@ var init_worldModel = __esm({
43483
44088
 
43484
44089
  ${args.content}
43485
44090
  `;
43486
- await fs17.appendFile(file2, block, "utf8");
44091
+ await fs18.appendFile(file2, block, "utf8");
43487
44092
  } else {
43488
- await fs17.writeFile(file2, args.content, "utf8");
44093
+ await fs18.writeFile(file2, args.content, "utf8");
43489
44094
  }
43490
- const st = await fs17.stat(file2);
44095
+ const st = await fs18.stat(file2);
43491
44096
  await appendTimeline(ctx.cwd, { kind: "hypothesis_update", bytes: st.size, append: !!args.append });
43492
44097
  return typedOk({ path: file2, bytes: st.size });
43493
44098
  } catch (err) {
@@ -43516,7 +44121,7 @@ ${args.content}
43516
44121
  const dir = await ensureWorldDir(ctx.cwd);
43517
44122
  const file2 = path43.join(dir, CHECKS_FILE);
43518
44123
  const body = { checks: args.checks };
43519
- await fs17.writeFile(file2, JSON.stringify(body, null, 2) + "\n", "utf8");
44124
+ await fs18.writeFile(file2, JSON.stringify(body, null, 2) + "\n", "utf8");
43520
44125
  await appendTimeline(ctx.cwd, { kind: "checks_set", count: args.checks.length });
43521
44126
  return typedOk({ path: file2, count: args.checks.length });
43522
44127
  } catch (err) {
@@ -43601,7 +44206,7 @@ __export(graphMemory_exports, {
43601
44206
  saveGraphSnapshot: () => saveGraphSnapshot,
43602
44207
  toGraphSnapshot: () => toGraphSnapshot
43603
44208
  });
43604
- import { promises as fs18 } from "node:fs";
44209
+ import { promises as fs19 } from "node:fs";
43605
44210
  import path44 from "node:path";
43606
44211
  function snapshotPath(cwd) {
43607
44212
  return path44.join(cwd, SNAPSHOT_DIR, SNAPSHOT_FILE);
@@ -43629,16 +44234,16 @@ function toGraphSnapshot(graph, opts) {
43629
44234
  }
43630
44235
  async function saveGraphSnapshot(cwd, snapshot) {
43631
44236
  try {
43632
- await fs18.access(cwd);
44237
+ await fs19.access(cwd);
43633
44238
  const file2 = snapshotPath(cwd);
43634
- await fs18.mkdir(path44.dirname(file2), { recursive: true });
43635
- await fs18.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
44239
+ await fs19.mkdir(path44.dirname(file2), { recursive: true });
44240
+ await fs19.writeFile(file2, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
43636
44241
  } catch {
43637
44242
  }
43638
44243
  }
43639
44244
  async function loadGraphSnapshot(cwd) {
43640
44245
  try {
43641
- const raw = await fs18.readFile(snapshotPath(cwd), "utf8");
44246
+ const raw = await fs19.readFile(snapshotPath(cwd), "utf8");
43642
44247
  const parsed = JSON.parse(raw);
43643
44248
  if (!parsed || !Array.isArray(parsed.nodes)) return null;
43644
44249
  return parsed;
@@ -43705,7 +44310,7 @@ var init_graphMemory = __esm({
43705
44310
  });
43706
44311
 
43707
44312
  // src/cli/kraken/workbench.ts
43708
- import { promises as fs19 } from "node:fs";
44313
+ import { promises as fs20 } from "node:fs";
43709
44314
  import path45 from "node:path";
43710
44315
  function isWorkbenchEnabled(env = process.env) {
43711
44316
  const v = (env.ZELARI_KRAKEN_WORKBENCH ?? "1").trim().toLowerCase();
@@ -43876,11 +44481,11 @@ var init_workbench = __esm({
43876
44481
  if (!this.enabled) return null;
43877
44482
  if (!this.dirty && this.lastWrite) return this.lastWrite;
43878
44483
  const out = workbenchPath(this.cwd, this.graphId);
43879
- await fs19.mkdir(path45.dirname(out), { recursive: true });
44484
+ await fs20.mkdir(path45.dirname(out), { recursive: true });
43880
44485
  const body = this.render();
43881
44486
  const tmp = `${out}.${process.pid}.${Date.now()}.tmp`;
43882
- await fs19.writeFile(tmp, body, "utf8");
43883
- await fs19.rename(tmp, out);
44487
+ await fs20.writeFile(tmp, body, "utf8");
44488
+ await fs20.rename(tmp, out);
43884
44489
  this.dirty = false;
43885
44490
  this.lastWrite = Promise.resolve(out);
43886
44491
  return out;
@@ -44460,9 +45065,11 @@ var init_reputationStore = __esm({
44460
45065
  // src/cli/tools/krakenModel.ts
44461
45066
  var krakenModel_exports = {};
44462
45067
  __export(krakenModel_exports, {
45068
+ familyCandidatesFromRegistry: () => familyCandidatesFromRegistry,
44463
45069
  inferModelFamily: () => inferModelFamily,
44464
45070
  isCheapModelId: () => isCheapModelId,
44465
45071
  isKrakenAutoModelEnabled: () => isKrakenAutoModelEnabled,
45072
+ isKrakenCrossModelEnabled: () => isKrakenCrossModelEnabled,
44466
45073
  isUnknownModelError: () => isUnknownModelError,
44467
45074
  parseQualifiedModelRef: () => parseQualifiedModelRef,
44468
45075
  pickCheapModel: () => pickCheapModel,
@@ -44503,6 +45110,10 @@ function isKrakenAutoModelEnabled(env = process.env) {
44503
45110
  if (v === "0" || v === "false" || v === "no" || v === "off") return false;
44504
45111
  return true;
44505
45112
  }
45113
+ function isKrakenCrossModelEnabled(env = process.env) {
45114
+ const v = (env.ZELARI_KRAKEN_CROSS_MODEL ?? "").trim().toLowerCase();
45115
+ return !(v === "0" || v === "false" || v === "no" || v === "off");
45116
+ }
44506
45117
  function parseQualifiedModelRef(ref) {
44507
45118
  const s = ref?.trim() ?? "";
44508
45119
  const slash = s.indexOf("/");
@@ -44539,8 +45150,7 @@ function pickDifferentFamily(builder, candidates) {
44539
45150
  return null;
44540
45151
  }
44541
45152
  function resolveCrossModelVerifier(builder, candidates, env = process.env) {
44542
- const cross = (env.ZELARI_KRAKEN_CROSS_MODEL ?? "").trim().toLowerCase();
44543
- if (cross === "0" || cross === "false" || cross === "off") return null;
45153
+ if (!isKrakenCrossModelEnabled(env)) return null;
44544
45154
  const specific = env.ZELARI_KRAKEN_VERIFY_MODEL?.trim();
44545
45155
  if (specific) {
44546
45156
  const qualified = parseQualifiedModelRef(specific);
@@ -44562,7 +45172,7 @@ function resolveKrakenSubModel(agent, parentModel, env = process.env, opts = {})
44562
45172
  }
44563
45173
  return shared;
44564
45174
  }
44565
- if (agent === "verify" && opts.familyCandidates && opts.familyCandidates.length > 0) {
45175
+ if (agent === "verify" && opts.familyCandidates && opts.familyCandidates.length > 0 && isKrakenCrossModelEnabled(env)) {
44566
45176
  const picked = pickDifferentFamily(
44567
45177
  { provider: opts.provider ?? "", model: parentModel },
44568
45178
  opts.familyCandidates
@@ -44592,20 +45202,40 @@ function resolvePersonaModel(kind2, parentModel, env = process.env, opts = {}) {
44592
45202
  if (specific) return specific;
44593
45203
  return resolveKrakenSubModel("verify", parentModel, env, opts);
44594
45204
  }
45205
+ function familyCandidatesFromRegistry(registry4) {
45206
+ if (!registry4 || typeof registry4 !== "object") return [];
45207
+ const out = [];
45208
+ for (const [key, entry] of Object.entries(registry4)) {
45209
+ const provider = key?.trim() ?? "";
45210
+ if (!provider) continue;
45211
+ const models = entry?.models;
45212
+ if (!Array.isArray(models)) continue;
45213
+ for (const raw of models) {
45214
+ const id3 = typeof raw === "string" ? raw : raw && typeof raw.id === "string" ? raw.id : "";
45215
+ const model = id3.trim();
45216
+ if (model) out.push({ provider, model });
45217
+ }
45218
+ }
45219
+ return out;
45220
+ }
44595
45221
  async function resolveKrakenSubModelAsync(agent, parentModel, env = process.env, opts = {}) {
44596
45222
  let candidates = [];
44597
- if (opts.provider) {
44598
- try {
44599
- const mod = await Promise.resolve().then(() => (init_modelDiscovery(), modelDiscovery_exports));
45223
+ let familyCandidates = [];
45224
+ try {
45225
+ const mod = await Promise.resolve().then(() => (init_modelDiscovery(), modelDiscovery_exports));
45226
+ if (opts.provider) {
44600
45227
  const ids = mod.getDiscoveredModelIds(opts.provider);
44601
45228
  if (Array.isArray(ids)) candidates = ids;
44602
- } catch {
44603
- candidates = [];
44604
45229
  }
45230
+ familyCandidates = familyCandidatesFromRegistry(mod.loadModelsRegistry());
45231
+ } catch {
45232
+ candidates = [];
45233
+ familyCandidates = [];
44605
45234
  }
44606
45235
  return resolveKrakenSubModel(agent, parentModel, env, {
44607
45236
  provider: opts.provider,
44608
- candidates
45237
+ candidates,
45238
+ familyCandidates
44609
45239
  });
44610
45240
  }
44611
45241
  var CHEAP_RE, FLAGSHIP_RE;
@@ -45068,6 +45698,7 @@ var init_executor = __esm({
45068
45698
  init_tentacle();
45069
45699
  init_krakenWorktree();
45070
45700
  init_krakenRadio();
45701
+ init_asyncLimit();
45071
45702
  init_transactional();
45072
45703
  init_worldModel();
45073
45704
  init_graphStatus();
@@ -45238,11 +45869,13 @@ var init_executor = __esm({
45238
45869
  }, this.graphTimeoutMs);
45239
45870
  graphTimer.unref?.();
45240
45871
  }
45872
+ const worktreeCleanupBatched = beginKrakenWorktreeCleanupBatch(process.env);
45241
45873
  try {
45242
45874
  return await this.schedule(graph);
45243
45875
  } finally {
45244
45876
  if (graphTimer) clearTimeout(graphTimer);
45245
45877
  this.signal?.removeEventListener("abort", onAbort);
45878
+ if (worktreeCleanupBatched) await flushKrakenWorktreeCleanupBatch();
45246
45879
  }
45247
45880
  }
45248
45881
  /** The scheduling loop proper. See {@link execute} for the cancellation wrapper. */
@@ -46029,6 +46662,7 @@ ${upstream}` : node.prompt,
46029
46662
  const memory = this.deps.memoryService;
46030
46663
  if (!memory || this.deps.memoryAutoWrite === false) return;
46031
46664
  try {
46665
+ const edges = [];
46032
46666
  for (const node of graph.nodes.values()) {
46033
46667
  const nodeMemoryId = this.memoryIds.get(node.id);
46034
46668
  if (!nodeMemoryId) continue;
@@ -46037,22 +46671,21 @@ ${upstream}` : node.prompt,
46037
46671
  if (!dependencyMemoryId) continue;
46038
46672
  if (node.kind === "verify") {
46039
46673
  const verdict = parseVerifyVerdict(node.result).verdict;
46040
- await memory.connect({
46674
+ edges.push({
46041
46675
  from: dependencyMemoryId,
46042
46676
  to: nodeMemoryId,
46043
- relation: verdict === "pass" ? "validated_by" : verdict === "fail" ? "invalidated_by" : "related_to",
46044
- createdBy: "kraken-orchestrator"
46677
+ relation: verdict === "pass" ? "validated_by" : verdict === "fail" ? "invalidated_by" : "related_to"
46045
46678
  });
46046
46679
  } else {
46047
- await memory.connect({
46048
- from: nodeMemoryId,
46049
- to: dependencyMemoryId,
46050
- relation: "derived_from",
46051
- createdBy: "kraken-orchestrator"
46052
- });
46680
+ edges.push({ from: nodeMemoryId, to: dependencyMemoryId, relation: "derived_from" });
46053
46681
  }
46054
46682
  }
46055
46683
  }
46684
+ await runWithLimit2(
46685
+ edges,
46686
+ 8,
46687
+ (edge) => memory.connect({ ...edge, createdBy: "kraken-orchestrator" }).catch(() => void 0)
46688
+ );
46056
46689
  const counts = countByStatus(graph);
46057
46690
  const outcome = await memory.remember({
46058
46691
  kind: converged ? "outcome" : "failure",
@@ -46064,14 +46697,17 @@ ${upstream}` : node.prompt,
46064
46697
  metadata: { graphId: graph.id, converged, counts, writeClass: "auto" },
46065
46698
  writeClass: "auto"
46066
46699
  });
46067
- for (const memoryId of this.memoryIds.values()) {
46068
- await memory.connect({
46069
- from: outcome.id,
46070
- to: memoryId,
46071
- relation: "derived_from",
46072
- createdBy: "kraken-orchestrator"
46073
- });
46074
- }
46700
+ const outcomeEdges = [...this.memoryIds.values()].map((memoryId) => ({
46701
+ from: outcome.id,
46702
+ to: memoryId,
46703
+ relation: "derived_from",
46704
+ createdBy: "kraken-orchestrator"
46705
+ }));
46706
+ await runWithLimit2(
46707
+ outcomeEdges,
46708
+ 8,
46709
+ (edge) => memory.connect(edge).catch(() => void 0)
46710
+ );
46075
46711
  await memory.consolidate({
46076
46712
  source: { agent: "kraken-orchestrator", sessionId: this.sessionId },
46077
46713
  minOccurrences: 2
@@ -46875,36 +47511,54 @@ ${taskUserContent}`,
46875
47511
  emitActivity({ type: "agent_tool", agentId: liveId, toolCallId: ev.toolCallId, tool: startedTools.get(ev.toolCallId) ?? "unknown", status: ev.isError ? "failed" : "completed", durationMs: ev.durationMs, ts: Date.now() });
46876
47512
  }
46877
47513
  };
46878
- let { result, error: error51, aborted: aborted2, usage, toolTrace } = await runSubAgent(harness, {
46879
- ...opts.signal ? { signal: opts.signal } : {},
46880
- onEvent: onHarnessEvent
47514
+ const stopHeartbeat = startTentacleHeartbeat((caption) => {
47515
+ emitActivity({
47516
+ type: "agent_status",
47517
+ agentId: liveId,
47518
+ status: "running",
47519
+ message: caption,
47520
+ ts: Date.now()
47521
+ });
46881
47522
  });
46882
- if (!aborted2 && !result && sub.fallback && sub.fallback.model !== sub.model) {
46883
- const { isUnknownModelError: isUnknownModelError2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
46884
- if (isUnknownModelError2(error51)) {
46885
- emitPhase(`model ${sub.model} unavailable \u2014 retrying with ${sub.fallback.model}`);
46886
- const retryConfig = {
46887
- ...config2,
46888
- model: sub.fallback.model,
46889
- provider: sub.fallback.provider,
46890
- providerStream: sub.fallback.providerStream
46891
- };
46892
- try {
46893
- harness = deps.harnessFactory ? deps.harnessFactory(retryConfig) : new (await Promise.resolve().then(() => (init_harness(), harness_exports))).AgentHarness(retryConfig);
46894
- const retry = await runSubAgent(harness, {
46895
- ...opts.signal ? { signal: opts.signal } : {},
46896
- onEvent: onHarnessEvent
46897
- });
46898
- result = retry.result;
46899
- error51 = retry.error;
46900
- aborted2 = retry.aborted;
46901
- usage = retry.usage;
46902
- toolTrace = retry.toolTrace;
46903
- sub = { ...sub, model: sub.fallback.model, provider: sub.fallback.provider };
46904
- } catch (err) {
46905
- error51 = err instanceof Error ? err.message : String(err);
47523
+ let result;
47524
+ let error51;
47525
+ let aborted2;
47526
+ let usage;
47527
+ let toolTrace;
47528
+ try {
47529
+ ({ result, error: error51, aborted: aborted2, usage, toolTrace } = await runSubAgent(harness, {
47530
+ ...opts.signal ? { signal: opts.signal } : {},
47531
+ onEvent: onHarnessEvent
47532
+ }));
47533
+ if (!aborted2 && !result && sub.fallback && sub.fallback.model !== sub.model) {
47534
+ const { isUnknownModelError: isUnknownModelError2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
47535
+ if (isUnknownModelError2(error51)) {
47536
+ emitPhase(`model ${sub.model} unavailable \u2014 retrying with ${sub.fallback.model}`);
47537
+ const retryConfig = {
47538
+ ...config2,
47539
+ model: sub.fallback.model,
47540
+ provider: sub.fallback.provider,
47541
+ providerStream: sub.fallback.providerStream
47542
+ };
47543
+ try {
47544
+ harness = deps.harnessFactory ? deps.harnessFactory(retryConfig) : new (await Promise.resolve().then(() => (init_harness(), harness_exports))).AgentHarness(retryConfig);
47545
+ const retry = await runSubAgent(harness, {
47546
+ ...opts.signal ? { signal: opts.signal } : {},
47547
+ onEvent: onHarnessEvent
47548
+ });
47549
+ result = retry.result;
47550
+ error51 = retry.error;
47551
+ aborted2 = retry.aborted;
47552
+ usage = retry.usage;
47553
+ toolTrace = retry.toolTrace;
47554
+ sub = { ...sub, model: sub.fallback.model, provider: sub.fallback.provider };
47555
+ } catch (err) {
47556
+ error51 = err instanceof Error ? err.message : String(err);
47557
+ }
46906
47558
  }
46907
47559
  }
47560
+ } finally {
47561
+ stopHeartbeat();
46908
47562
  }
46909
47563
  const durationMs = Date.now() - started;
46910
47564
  if (aborted2) {
@@ -46914,7 +47568,7 @@ ${taskUserContent}`,
46914
47568
  agent,
46915
47569
  thoroughness,
46916
47570
  description: args.description,
46917
- detail: "cancelled: node timeout",
47571
+ detail: "cancelled by parent",
46918
47572
  model: sub.model,
46919
47573
  worktree: worktree?.path ?? null,
46920
47574
  durationMs,
@@ -46926,7 +47580,7 @@ ${taskUserContent}`,
46926
47580
  detail: "cancelled",
46927
47581
  durationMs
46928
47582
  });
46929
- return { ok: false, agent, error: "task: sub-agent cancelled (node timeout)", cancelled: true };
47583
+ return { ok: false, agent, error: "task: sub-agent cancelled by parent", cancelled: true };
46930
47584
  }
46931
47585
  if (!result) {
46932
47586
  if (worktree) await cleanupKrakenWorktree(worktree);
@@ -47204,6 +47858,7 @@ var init_taskTool = __esm({
47204
47858
  init_verifyReport();
47205
47859
  init_metrics2();
47206
47860
  init_dist();
47861
+ init_tentacleHeartbeat();
47207
47862
  TASK_TOOL_TIMEOUT_MS = 27e5;
47208
47863
  EXPLORE_PROMPT = [
47209
47864
  "You are a focused EXPLORE tentacle of Kraken (parent super-agent).",
@@ -49136,7 +49791,7 @@ var init_planTaskTools = __esm({
49136
49791
  });
49137
49792
 
49138
49793
  // src/cli/tools/inspectTypecheckSafety.ts
49139
- import { promises as fs20 } from "node:fs";
49794
+ import { promises as fs21 } from "node:fs";
49140
49795
  import path49 from "node:path";
49141
49796
  import { spawn as spawn9 } from "node:child_process";
49142
49797
  async function scanTsbuildinfo(root) {
@@ -49146,7 +49801,7 @@ async function scanTsbuildinfo(root) {
49146
49801
  const dir = stack.pop();
49147
49802
  let entries;
49148
49803
  try {
49149
- entries = await fs20.readdir(dir, { withFileTypes: true });
49804
+ entries = await fs21.readdir(dir, { withFileTypes: true });
49150
49805
  } catch {
49151
49806
  continue;
49152
49807
  }
@@ -49191,7 +49846,7 @@ async function cleanupArtifacts(root, relPaths) {
49191
49846
  const failed = [];
49192
49847
  for (const rel2 of relPaths) {
49193
49848
  try {
49194
- await fs20.unlink(path49.join(root, rel2));
49849
+ await fs21.unlink(path49.join(root, rel2));
49195
49850
  cleaned.push(rel2);
49196
49851
  } catch {
49197
49852
  failed.push(rel2);
@@ -49215,8 +49870,8 @@ var init_inspectTypecheckSafety = __esm({
49215
49870
 
49216
49871
  // src/cli/tools/inspectCommand.ts
49217
49872
  import { spawn as spawn10 } from "node:child_process";
49218
- import { createHash as createHash13 } from "node:crypto";
49219
- import { existsSync as existsSync28, promises as fs21 } from "node:fs";
49873
+ import { createHash as createHash14 } from "node:crypto";
49874
+ import { existsSync as existsSync28, promises as fs22 } from "node:fs";
49220
49875
  import os2 from "node:os";
49221
49876
  import path50 from "node:path";
49222
49877
  function resolveNodeModuleBin(start, rel2) {
@@ -49302,7 +49957,7 @@ function buildInspectCommand(op, ctx) {
49302
49957
  }
49303
49958
  case "typecheck": {
49304
49959
  const project = path50.resolve(ctx.cwd, op.project ?? "tsconfig.json");
49305
- const hash3 = createHash13("sha256").update(project).digest("hex").slice(0, 16);
49960
+ const hash3 = createHash14("sha256").update(project).digest("hex").slice(0, 16);
49306
49961
  const tsBuildInfoFile = path50.join(os2.tmpdir(), "zelari-inspect", `${hash3}.tsbuildinfo`);
49307
49962
  return {
49308
49963
  ok: true,
@@ -49441,7 +50096,7 @@ function createInspectCommandTool(rootOrDeps) {
49441
50096
  if (op.operation === "typecheck") {
49442
50097
  const tsc = built.argv[0];
49443
50098
  try {
49444
- await fs21.access(tsc);
50099
+ await fs22.access(tsc);
49445
50100
  } catch {
49446
50101
  return typedErr(
49447
50102
  `TYPESCRIPT_UNAVAILABLE: no TypeScript compiler at ${tsc} \u2014 inspect_command typecheck uses the project toolchain (walk-up to node_modules/typescript/bin/tsc) and refuses to guess. Install dependencies or fall back to read_file/grep_content.`
@@ -50837,11 +51492,11 @@ var init_store2 = __esm({
50837
51492
  });
50838
51493
 
50839
51494
  // src/cli/semantic/index.ts
50840
- import { promises as fs22, existsSync as existsSync29, readFileSync as readFileSync21 } from "node:fs";
51495
+ import { promises as fs23, existsSync as existsSync29, readFileSync as readFileSync21 } from "node:fs";
50841
51496
  import path53 from "node:path";
50842
- import { createHash as createHash14 } from "node:crypto";
51497
+ import { createHash as createHash15 } from "node:crypto";
50843
51498
  function getIndexPath(root) {
50844
- const hash3 = createHash14("sha1").update(path53.resolve(root)).digest("hex").slice(0, 16);
51499
+ const hash3 = createHash15("sha1").update(path53.resolve(root)).digest("hex").slice(0, 16);
50845
51500
  return process.env.ZELARI_SEMANTIC_FILE ?? path53.join(semanticStateDir(), `${hash3}.json`);
50846
51501
  }
50847
51502
  async function collectSourceFiles(root, maxFiles = 1500) {
@@ -50850,7 +51505,7 @@ async function collectSourceFiles(root, maxFiles = 1500) {
50850
51505
  if (out.length >= maxFiles) return;
50851
51506
  let entries;
50852
51507
  try {
50853
- entries = await fs22.readdir(dir, { withFileTypes: true });
51508
+ entries = await fs23.readdir(dir, { withFileTypes: true });
50854
51509
  } catch {
50855
51510
  return;
50856
51511
  }
@@ -50880,7 +51535,7 @@ async function buildIndex(files, embed, options) {
50880
51535
  for (const file2 of files) {
50881
51536
  let text;
50882
51537
  try {
50883
- text = await fs22.readFile(file2, "utf8");
51538
+ text = await fs23.readFile(file2, "utf8");
50884
51539
  } catch {
50885
51540
  continue;
50886
51541
  }
@@ -50913,10 +51568,10 @@ async function buildIndex(files, embed, options) {
50913
51568
  }
50914
51569
  async function saveIndex(root, data) {
50915
51570
  const file2 = getIndexPath(root);
50916
- await fs22.mkdir(path53.dirname(file2), { recursive: true });
51571
+ await fs23.mkdir(path53.dirname(file2), { recursive: true });
50917
51572
  const tmp = `${file2}.tmp-${process.pid}`;
50918
- await fs22.writeFile(tmp, JSON.stringify(data), "utf8");
50919
- await fs22.rename(tmp, file2);
51573
+ await fs23.writeFile(tmp, JSON.stringify(data), "utf8");
51574
+ await fs23.rename(tmp, file2);
50920
51575
  }
50921
51576
  function loadIndex(root) {
50922
51577
  const file2 = getIndexPath(root);
@@ -52177,13 +52832,13 @@ var init_tools5 = __esm({
52177
52832
  });
52178
52833
 
52179
52834
  // src/cli/tools/screenshotTool.ts
52180
- import { execFile as execFile5 } from "node:child_process";
52835
+ import { execFile as execFile6 } from "node:child_process";
52181
52836
  import { mkdir as mkdir3, readFile as readFile8, stat as stat7 } from "node:fs/promises";
52182
52837
  import path57 from "node:path";
52183
- import { promisify as promisify4 } from "node:util";
52838
+ import { promisify as promisify5 } from "node:util";
52184
52839
  async function captureWindows(target) {
52185
52840
  const script = `Add-Type -AssemblyName System.Windows.Forms,System.Drawing;$b=[System.Windows.Forms.SystemInformation]::VirtualScreen;$bmp=New-Object System.Drawing.Bitmap $b.Width,$b.Height;$g=[System.Drawing.Graphics]::FromImage($bmp);$g.CopyFromScreen($b.Left,$b.Top,0,0,$bmp.Size);$g.Dispose(); $bmp.Save('${target.replace(/'/g, "''")}'); $bmp.Dispose();`;
52186
- await execFileAsync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
52841
+ await execFileAsync5("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
52187
52842
  timeout: 12e3,
52188
52843
  windowsHide: true
52189
52844
  });
@@ -52197,7 +52852,7 @@ async function captureUnixLike(target) {
52197
52852
  let lastErr = null;
52198
52853
  for (const [bin, args] of attempts) {
52199
52854
  try {
52200
- await execFileAsync4(bin, args, { timeout: 12e3 });
52855
+ await execFileAsync5(bin, args, { timeout: 12e3 });
52201
52856
  return;
52202
52857
  } catch (e) {
52203
52858
  lastErr = e;
@@ -52251,13 +52906,13 @@ function createScreenshotTool(deps = {}) {
52251
52906
  }
52252
52907
  };
52253
52908
  }
52254
- var execFileAsync4, SCREENSHOT_MAX_BYTES2;
52909
+ var execFileAsync5, SCREENSHOT_MAX_BYTES2;
52255
52910
  var init_screenshotTool = __esm({
52256
52911
  "src/cli/tools/screenshotTool.ts"() {
52257
52912
  "use strict";
52258
52913
  init_zod();
52259
52914
  init_toolTypes();
52260
- execFileAsync4 = promisify4(execFile5);
52915
+ execFileAsync5 = promisify5(execFile6);
52261
52916
  SCREENSHOT_MAX_BYTES2 = 8 * 1024 * 1024;
52262
52917
  }
52263
52918
  });
@@ -53580,6 +54235,163 @@ var init_resolveStream = __esm({
53580
54235
  }
53581
54236
  });
53582
54237
 
54238
+ // src/cli/providerFailover.ts
54239
+ function providerFailover(options) {
54240
+ const isTransient = options.isTransientFailure ?? DEFAULT_IS_TRANSIENT;
54241
+ const primaryFailedMsg = options.fallbackLabel ? `[failover] primary failed, switching to ${options.fallbackLabel}` : "[failover] primary failed, switching to fallback";
54242
+ const primaryThrewMsg = options.fallbackLabel ? `[failover] primary threw, switching to ${options.fallbackLabel}: ` : "[failover] primary threw: ";
54243
+ const fallbackFailedMsg = options.fallbackLabel ? `[failover] fallback (${options.fallbackLabel}) also failed: ` : "[failover] fallback also failed: ";
54244
+ const notify = (phase2, message) => {
54245
+ if (!options.onFailover) return;
54246
+ try {
54247
+ options.onFailover({
54248
+ phase: phase2,
54249
+ message,
54250
+ ...options.fallbackLabel ? { label: options.fallbackLabel } : {}
54251
+ });
54252
+ } catch {
54253
+ }
54254
+ };
54255
+ return async function* (params) {
54256
+ const triedFallback = { value: false };
54257
+ try {
54258
+ for await (const delta of options.primary(params)) {
54259
+ if (isTransient(delta)) {
54260
+ yield delta;
54261
+ triedFallback.value = true;
54262
+ yield {
54263
+ kind: "error",
54264
+ message: primaryFailedMsg
54265
+ };
54266
+ notify("swap-delta", primaryFailedMsg);
54267
+ break;
54268
+ }
54269
+ yield delta;
54270
+ }
54271
+ } catch (err) {
54272
+ triedFallback.value = true;
54273
+ const threwMsg = `${primaryThrewMsg}${err instanceof Error ? err.message : String(err)}`;
54274
+ yield {
54275
+ kind: "error",
54276
+ message: threwMsg
54277
+ };
54278
+ notify("swap-throw", threwMsg);
54279
+ }
54280
+ if (triedFallback.value) {
54281
+ try {
54282
+ for await (const delta of options.fallback(params)) {
54283
+ yield delta;
54284
+ }
54285
+ } catch (err) {
54286
+ const failedMsg = `${fallbackFailedMsg}${err instanceof Error ? err.message : String(err)}`;
54287
+ yield {
54288
+ kind: "error",
54289
+ message: failedMsg
54290
+ };
54291
+ notify("fallback-failed", failedMsg);
54292
+ return;
54293
+ }
54294
+ }
54295
+ };
54296
+ }
54297
+ var DEFAULT_IS_TRANSIENT;
54298
+ var init_providerFailover = __esm({
54299
+ "src/cli/providerFailover.ts"() {
54300
+ "use strict";
54301
+ DEFAULT_IS_TRANSIENT = (delta) => delta.kind === "error";
54302
+ }
54303
+ });
54304
+
54305
+ // src/cli/crossProviderFailover.ts
54306
+ var crossProviderFailover_exports = {};
54307
+ __export(crossProviderFailover_exports, {
54308
+ isHeadlessFailoverEnabled: () => isHeadlessFailoverEnabled,
54309
+ resolveFailoverStream: () => resolveFailoverStream,
54310
+ wrapWithHeadlessFailover: () => wrapWithHeadlessFailover
54311
+ });
54312
+ async function resolveFailoverStream(options) {
54313
+ if (!options.failoverEnabled) {
54314
+ return {
54315
+ fallback: options.primary,
54316
+ fallbackLabel: void 0,
54317
+ warning: "",
54318
+ reason: "disabled"
54319
+ };
54320
+ }
54321
+ const requested = options.envValue?.trim();
54322
+ if (!requested || requested.length === 0) {
54323
+ return {
54324
+ fallback: options.primary,
54325
+ fallbackLabel: void 0,
54326
+ warning: "",
54327
+ reason: "unset"
54328
+ };
54329
+ }
54330
+ const valid = new Set(options.validProviderIds);
54331
+ if (!valid.has(requested)) {
54332
+ return {
54333
+ fallback: options.primary,
54334
+ fallbackLabel: void 0,
54335
+ warning: `[failover] ANATHEMA_FAILOVER_PROVIDER="${requested}" is not a known provider. Falling back to v3-G same-provider behavior. Available: ${options.validProviderIds.join(", ")}.`,
54336
+ reason: "unknown"
54337
+ };
54338
+ }
54339
+ if (requested === options.primaryProviderId) {
54340
+ return {
54341
+ fallback: options.primary,
54342
+ fallbackLabel: void 0,
54343
+ warning: "",
54344
+ reason: "same-as-primary"
54345
+ };
54346
+ }
54347
+ const fallbackConfig = await options.lookupFallbackConfig(requested);
54348
+ if (!fallbackConfig) {
54349
+ return {
54350
+ fallback: options.primary,
54351
+ fallbackLabel: void 0,
54352
+ warning: `[failover] No API key configured for provider "${requested}". Falling back to v3-G same-provider behavior.`,
54353
+ reason: "missing-key"
54354
+ };
54355
+ }
54356
+ return {
54357
+ fallback: options.buildStream(fallbackConfig),
54358
+ fallbackLabel: requested,
54359
+ warning: "",
54360
+ reason: "resolved"
54361
+ };
54362
+ }
54363
+ function isHeadlessFailoverEnabled(env = process.env) {
54364
+ return env.ZELARI_HEADLESS_FAILOVER === "1" && env.ANATHEMA_FAILOVER !== "0";
54365
+ }
54366
+ async function wrapWithHeadlessFailover(options) {
54367
+ const env = options.env ?? process.env;
54368
+ if (!isHeadlessFailoverEnabled(env)) {
54369
+ return options.primary;
54370
+ }
54371
+ const resolved = await resolveFailoverStream({
54372
+ failoverEnabled: true,
54373
+ envValue: env.ANATHEMA_FAILOVER_PROVIDER,
54374
+ primaryProviderId: options.primaryProviderId,
54375
+ primary: options.primary,
54376
+ validProviderIds: options.validProviderIds,
54377
+ lookupFallbackConfig: options.lookupFallbackConfig,
54378
+ buildStream: options.buildStream
54379
+ });
54380
+ if (resolved.warning) options.onWarning?.(resolved.warning);
54381
+ return providerFailover({
54382
+ primary: options.primary,
54383
+ fallback: resolved.fallback,
54384
+ ...resolved.fallbackLabel ? { fallbackLabel: resolved.fallbackLabel } : {},
54385
+ ...options.onFailover ? { onFailover: options.onFailover } : {}
54386
+ });
54387
+ }
54388
+ var init_crossProviderFailover = __esm({
54389
+ "src/cli/crossProviderFailover.ts"() {
54390
+ "use strict";
54391
+ init_providerFailover();
54392
+ }
54393
+ });
54394
+
53583
54395
  // src/cli/safety/toolPermissions.ts
53584
54396
  var toolPermissions_exports = {};
53585
54397
  __export(toolPermissions_exports, {
@@ -53766,7 +54578,7 @@ function recordNonUserContent(source2, text, tool) {
53766
54578
  const excerpt = normalize5(raw).slice(0, MAX_EXCERPT);
53767
54579
  if (excerpt.length < PROVENANCE_MIN_MATCH * 2) return;
53768
54580
  ring.push({ source: source2, tool, excerpt, at: Date.now() });
53769
- if (ring.length > MAX_ENTRIES) ring.splice(0, ring.length - MAX_ENTRIES);
54581
+ if (ring.length > MAX_ENTRIES2) ring.splice(0, ring.length - MAX_ENTRIES2);
53770
54582
  } catch {
53771
54583
  }
53772
54584
  }
@@ -53822,11 +54634,11 @@ function safeJson2(v) {
53822
54634
  return "";
53823
54635
  }
53824
54636
  }
53825
- var MAX_ENTRIES, MAX_EXCERPT, PROVENANCE_MIN_MATCH, SHINGLE_STEP, MAX_SHINGLES, ring;
54637
+ var MAX_ENTRIES2, MAX_EXCERPT, PROVENANCE_MIN_MATCH, SHINGLE_STEP, MAX_SHINGLES, ring;
53826
54638
  var init_provenance = __esm({
53827
54639
  "src/cli/safety/provenance.ts"() {
53828
54640
  "use strict";
53829
- MAX_ENTRIES = 40;
54641
+ MAX_ENTRIES2 = 40;
53830
54642
  MAX_EXCERPT = 8192;
53831
54643
  PROVENANCE_MIN_MATCH = 48;
53832
54644
  SHINGLE_STEP = 24;
@@ -54019,7 +54831,7 @@ var init_lifecycleHooks = __esm({
54019
54831
  });
54020
54832
 
54021
54833
  // src/cli/safety/astGate.ts
54022
- import { promises as fs23 } from "node:fs";
54834
+ import { promises as fs24 } from "node:fs";
54023
54835
  import path59 from "node:path";
54024
54836
  function astGateEnabled() {
54025
54837
  return process.env.ZELARI_AST_GATE !== "0";
@@ -54069,7 +54881,7 @@ function wrapWithAstGate(original, opts) {
54069
54881
  if (astGateEnabled()) gateTarget = containedPathOf(args);
54070
54882
  if (gateTarget !== null && isAstSupported(gateTarget)) {
54071
54883
  try {
54072
- preContent = await fs23.readFile(gateTarget, "utf8");
54884
+ preContent = await fs24.readFile(gateTarget, "utf8");
54073
54885
  } catch (err) {
54074
54886
  if (err?.code === "ENOENT") preContent = null;
54075
54887
  }
@@ -54098,16 +54910,16 @@ function wrapWithAstGate(original, opts) {
54098
54910
  );
54099
54911
  return result;
54100
54912
  }
54101
- const post = await fs23.readFile(absPath, "utf8");
54913
+ const post = await fs24.readFile(absPath, "utf8");
54102
54914
  const syntaxError = firstSyntaxError(ts, path59.basename(absPath), post);
54103
54915
  if (!syntaxError) return result;
54104
54916
  const parseError = `${syntaxError.message} (line ${syntaxError.line}, col ${syntaxError.character})`;
54105
54917
  let revertedTo;
54106
54918
  if (preContent === null) {
54107
- await fs23.unlink(absPath).catch(() => void 0);
54919
+ await fs24.unlink(absPath).catch(() => void 0);
54108
54920
  revertedTo = "absent";
54109
54921
  } else {
54110
- await fs23.writeFile(absPath, preContent, "utf8");
54922
+ await fs24.writeFile(absPath, preContent, "utf8");
54111
54923
  revertedTo = snapshotIdOf(preContent);
54112
54924
  }
54113
54925
  const relLabel = path59.relative(opts.root, absPath) || path59.basename(absPath);
@@ -54533,8 +55345,8 @@ var init_resourceClaims = __esm({
54533
55345
  });
54534
55346
 
54535
55347
  // src/cli/toolResultCache.ts
54536
- import { createHash as createHash15 } from "node:crypto";
54537
- import { promises as fs24 } from "node:fs";
55348
+ import { createHash as createHash16 } from "node:crypto";
55349
+ import { promises as fs25 } from "node:fs";
54538
55350
  import path60 from "node:path";
54539
55351
  function isToolCacheEnabled() {
54540
55352
  const raw = process.env.ZELARI_TOOL_CACHE;
@@ -54546,7 +55358,7 @@ function resolveToolCacheTtlMs() {
54546
55358
  return Number.isFinite(n) && n >= 0 ? n : TOOL_CACHE_DEFAULT_TTL_MS;
54547
55359
  }
54548
55360
  function hashKey(parts) {
54549
- return createHash15("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
55361
+ return createHash16("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
54550
55362
  }
54551
55363
  function resultBytes(result) {
54552
55364
  try {
@@ -54622,7 +55434,7 @@ async function statKey(toolName, input, ctx) {
54622
55434
  if (typeof rawPath !== "string" || rawPath.length === 0) return null;
54623
55435
  const abs = path60.isAbsolute(rawPath) ? rawPath : path60.join(ctx.cwd, rawPath);
54624
55436
  try {
54625
- const st = await fs24.stat(abs);
55437
+ const st = await fs25.stat(abs);
54626
55438
  return hashKey({
54627
55439
  tool: toolName,
54628
55440
  args: input,
@@ -55047,9 +55859,11 @@ function createKrakenSubAgentContextFactory(opts) {
55047
55859
  return async ({ agent, cwd: subCwd }) => {
55048
55860
  const cfg = providerOverride ? await providerConfigFor(providerOverride) : await providerFromEnv();
55049
55861
  if (!cfg) return null;
55050
- const { resolveKrakenSubModel: resolveKrakenSubModel2, parseQualifiedModelRef: parseQualifiedModelRef2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
55862
+ const { resolveKrakenSubModelAsync: resolveKrakenSubModelAsync2, parseQualifiedModelRef: parseQualifiedModelRef2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
55051
55863
  const parentModel = modelOverride || cfg.model;
55052
- const resolvedModel = resolveKrakenSubModel2(agent, parentModel);
55864
+ const resolvedModel = await resolveKrakenSubModelAsync2(agent, parentModel, process.env, {
55865
+ provider: cfg.providerId
55866
+ });
55053
55867
  let effCfg = cfg;
55054
55868
  let model = resolvedModel;
55055
55869
  const ref = parseQualifiedModelRef2(resolvedModel);
@@ -55082,15 +55896,31 @@ function createKrakenSubAgentContextFactory(opts) {
55082
55896
  // P0.5: the tentacle's agent identity drives per-agent policy rules.
55083
55897
  policyAgent: agent
55084
55898
  });
55899
+ const wrapTentacleStream = (primary, primaryProviderId) => wrapWithHeadlessFailover({
55900
+ primary,
55901
+ primaryProviderId,
55902
+ validProviderIds: PROVIDERS.map((p3) => p3.id),
55903
+ lookupFallbackConfig: (id3) => providerConfigFor(id3),
55904
+ buildStream: (config2) => buildProviderStream(config2)
55905
+ });
55906
+ const providerStream = await wrapTentacleStream(
55907
+ buildProviderStream(subCfg),
55908
+ subCfg.providerId
55909
+ );
55085
55910
  return {
55086
- providerStream: buildProviderStream(subCfg),
55911
+ providerStream,
55087
55912
  model,
55088
55913
  provider: subCfg.providerId,
55089
55914
  ...model !== parentModel ? {
55090
55915
  fallback: {
55091
55916
  model: parentModel,
55092
55917
  provider: cfg.providerId,
55093
- providerStream: buildProviderStream({ ...cfg, model: parentModel })
55918
+ // Wrap the 404-parent fallback too so a later model retry still
55919
+ // has transport failover. Orthogonal to isUnknownModelError.
55920
+ providerStream: await wrapTentacleStream(
55921
+ buildProviderStream({ ...cfg, model: parentModel }),
55922
+ cfg.providerId
55923
+ )
55094
55924
  }
55095
55925
  } : {},
55096
55926
  registry: subRegistry,
@@ -55577,6 +56407,8 @@ var init_toolRegistry = __esm({
55577
56407
  init_openai_compatible();
55578
56408
  init_resolveStream();
55579
56409
  init_providerConfig();
56410
+ init_crossProviderFailover();
56411
+ init_keyStore();
55580
56412
  init_toolPermissions();
55581
56413
  init_destructiveCommands();
55582
56414
  init_provenance();
@@ -55617,12 +56449,12 @@ var init_toolRegistry = __esm({
55617
56449
  });
55618
56450
 
55619
56451
  // src/cli/metrics.ts
55620
- import { promises as fs25, existsSync as existsSync33, statSync as statSync4, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync15 } from "node:fs";
56452
+ import { promises as fs26, existsSync as existsSync33, statSync as statSync4, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync15 } from "node:fs";
55621
56453
  import path62 from "node:path";
55622
56454
  async function readMetrics(file2) {
55623
56455
  let raw = "";
55624
56456
  try {
55625
- raw = await fs25.readFile(file2, "utf-8");
56457
+ raw = await fs26.readFile(file2, "utf-8");
55626
56458
  } catch {
55627
56459
  return [];
55628
56460
  }
@@ -56000,8 +56832,8 @@ __export(completionProofProbe_exports, {
56000
56832
  gatherGitAttestation: () => gatherGitAttestation,
56001
56833
  harnessManifest: () => harnessManifest
56002
56834
  });
56003
- import { execFile as execFile6 } from "node:child_process";
56004
- import { promisify as promisify5 } from "node:util";
56835
+ import { execFile as execFile7 } from "node:child_process";
56836
+ import { promisify as promisify6 } from "node:util";
56005
56837
  async function readHarnessVersion() {
56006
56838
  if (cachedHarnessVersion !== null) return cachedHarnessVersion;
56007
56839
  try {
@@ -56032,7 +56864,7 @@ async function harnessManifest(env = process.env) {
56032
56864
  }
56033
56865
  async function git5(cwd, args) {
56034
56866
  try {
56035
- const { stdout } = await execFileAsync5("git", ["-C", cwd, ...args], {
56867
+ const { stdout } = await execFileAsync6("git", ["-C", cwd, ...args], {
56036
56868
  maxBuffer: 32 * 1024 * 1024,
56037
56869
  windowsHide: true
56038
56870
  });
@@ -56074,7 +56906,7 @@ function activeTaskContractSnapshot() {
56074
56906
  return void 0;
56075
56907
  }
56076
56908
  }
56077
- var HARNESS_VERSION_FALLBACK, ADAPTER_IDS, cachedHarnessVersion, execFileAsync5;
56909
+ var HARNESS_VERSION_FALLBACK, ADAPTER_IDS, cachedHarnessVersion, execFileAsync6;
56078
56910
  var init_completionProofProbe = __esm({
56079
56911
  "src/cli/kraken/completionProofProbe.ts"() {
56080
56912
  "use strict";
@@ -56082,14 +56914,14 @@ var init_completionProofProbe = __esm({
56082
56914
  HARNESS_VERSION_FALLBACK = "2.13.0";
56083
56915
  ADAPTER_IDS = ["node", "python", "rust", "go", "java", "dotnet"];
56084
56916
  cachedHarnessVersion = null;
56085
- execFileAsync5 = promisify5(execFile6);
56917
+ execFileAsync6 = promisify6(execFile7);
56086
56918
  }
56087
56919
  });
56088
56920
 
56089
56921
  // src/cli/kraken/completionProofAttestation.ts
56090
- import { createHash as createHash16 } from "node:crypto";
56922
+ import { createHash as createHash17 } from "node:crypto";
56091
56923
  function sha256Hex3(data) {
56092
- return createHash16("sha256").update(data, "utf8").digest("hex");
56924
+ return createHash17("sha256").update(data, "utf8").digest("hex");
56093
56925
  }
56094
56926
  function canonicalJson(value) {
56095
56927
  return JSON.stringify(canonicalValue(value));
@@ -56549,21 +57381,21 @@ var init_askUserTimeout = __esm({
56549
57381
  });
56550
57382
 
56551
57383
  // src/cli/state/fileStateStore.ts
56552
- import { createHash as createHash17, randomUUID as randomUUID5 } from "node:crypto";
56553
- import { promises as fs26 } from "node:fs";
57384
+ import { createHash as createHash18, randomUUID as randomUUID5 } from "node:crypto";
57385
+ import { promises as fs27 } from "node:fs";
56554
57386
  import * as path65 from "node:path";
56555
57387
  function shortId() {
56556
57388
  return randomUUID5().replace(/-/g, "").slice(0, 12);
56557
57389
  }
56558
57390
  async function writeJsonAtomic(filePath, data) {
56559
- await fs26.mkdir(path65.dirname(filePath), { recursive: true });
57391
+ await fs27.mkdir(path65.dirname(filePath), { recursive: true });
56560
57392
  const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
56561
- await fs26.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
56562
- await fs26.rename(tmp, filePath);
57393
+ await fs27.writeFile(tmp, JSON.stringify(data, null, 2) + "\n", "utf8");
57394
+ await fs27.rename(tmp, filePath);
56563
57395
  }
56564
57396
  async function readJsonFile(filePath) {
56565
57397
  try {
56566
- const raw = await fs26.readFile(filePath, "utf8");
57398
+ const raw = await fs27.readFile(filePath, "utf8");
56567
57399
  return JSON.parse(raw);
56568
57400
  } catch {
56569
57401
  return null;
@@ -56601,7 +57433,7 @@ async function getStateStore(projectRoot, env = process.env) {
56601
57433
  }
56602
57434
  }
56603
57435
  function hashStablePrompt(stable) {
56604
- return createHash17("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
57436
+ return createHash18("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
56605
57437
  }
56606
57438
  var DEFAULT_MATERIALIZE_CHARS, FileDurableStateStore, NoopDurableStateStore;
56607
57439
  var init_fileStateStore = __esm({
@@ -56622,8 +57454,8 @@ var init_fileStateStore = __esm({
56622
57454
  this.artifactsDir = path65.join(this.stateDir, "artifacts");
56623
57455
  this.headPath = path65.join(this.stateDir, "HEAD.json");
56624
57456
  this.indexPath = path65.join(this.stateDir, "index.jsonl");
56625
- await fs26.mkdir(this.commitsDir, { recursive: true });
56626
- await fs26.mkdir(this.artifactsDir, { recursive: true });
57457
+ await fs27.mkdir(this.commitsDir, { recursive: true });
57458
+ await fs27.mkdir(this.artifactsDir, { recursive: true });
56627
57459
  }
56628
57460
  async commit(input) {
56629
57461
  if (!input.force && input.verification.ran && !input.verification.ok) {
@@ -56636,9 +57468,9 @@ var init_fileStateStore = __esm({
56636
57468
  const id3 = shortId();
56637
57469
  const artifactRel = path65.join("artifacts", id3);
56638
57470
  const artifactAbs = path65.join(this.artifactsDir, id3);
56639
- await fs26.mkdir(artifactAbs, { recursive: true });
57471
+ await fs27.mkdir(artifactAbs, { recursive: true });
56640
57472
  const summary = defaultSummary(input, discoveries);
56641
- await fs26.writeFile(path65.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
57473
+ await fs27.writeFile(path65.join(artifactAbs, "summary.md"), summary + "\n", "utf8");
56642
57474
  await writeJsonAtomic(path65.join(artifactAbs, "discoveries.json"), discoveries);
56643
57475
  await writeJsonAtomic(path65.join(artifactAbs, "verification.json"), input.verification);
56644
57476
  const meta3 = {
@@ -56661,7 +57493,7 @@ var init_fileStateStore = __esm({
56661
57493
  };
56662
57494
  await writeJsonAtomic(path65.join(this.commitsDir, `${id3}.json`), meta3);
56663
57495
  await writeJsonAtomic(this.headPath, { id: id3, updatedAt: meta3.createdAt });
56664
- await fs26.appendFile(this.indexPath, JSON.stringify({ id: id3, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
57496
+ await fs27.appendFile(this.indexPath, JSON.stringify({ id: id3, createdAt: meta3.createdAt, label: meta3.label }) + "\n", "utf8");
56665
57497
  return stripStored(meta3);
56666
57498
  }
56667
57499
  async head() {
@@ -56676,7 +57508,7 @@ var init_fileStateStore = __esm({
56676
57508
  async list(limit = 20) {
56677
57509
  let raw;
56678
57510
  try {
56679
- raw = await fs26.readFile(this.indexPath, "utf8");
57511
+ raw = await fs27.readFile(this.indexPath, "utf8");
56680
57512
  } catch {
56681
57513
  return [];
56682
57514
  }
@@ -57649,7 +58481,10 @@ async function countVerificationEvidenceInLog(mirror, sessionId2) {
57649
58481
  if (mirror.status !== "active" && mirror.status !== "closed") return -1;
57650
58482
  try {
57651
58483
  await mirror.flush().catch(() => void 0);
57652
- const report = await readSessionLog(path67.join(mirror.sessionsDir, sessionId2, "events.jsonl"));
58484
+ const report = await readSessionLogCached(
58485
+ path67.join(mirror.sessionsDir, sessionId2, "events.jsonl"),
58486
+ mirror.replayCache
58487
+ );
57653
58488
  return report.events.filter((e) => e.kind === "verification.evidence").length;
57654
58489
  } catch (err) {
57655
58490
  process.stderr.write(
@@ -57682,7 +58517,7 @@ async function openHeadlessSpine(opts) {
57682
58517
  if (spine.status === "active") {
57683
58518
  const budget = new BudgetRuntime(profileId, { enforcement: resolveResourceEnforcement() });
57684
58519
  if (spine.resumedFromSeq !== void 0 && spine.resumedFromSeq > 0) {
57685
- await restoreBudgetRuntimeFromSession(budget, opts.sessionId, opts.baseDir);
58520
+ await restoreBudgetRuntimeFromSession(budget, opts.sessionId, opts.baseDir, spine.replayCache);
57686
58521
  }
57687
58522
  spine.attachBudgetRuntime(budget);
57688
58523
  const manifestSpecs = opts.toolSpecs?.filter((spec) => profileTools.includes(spec.name));
@@ -58442,11 +59277,11 @@ var init_planDetect = __esm({
58442
59277
  });
58443
59278
 
58444
59279
  // src/cli/memory/legacyImport.ts
58445
- import { createHash as createHash18 } from "node:crypto";
58446
- import { promises as fs27 } from "node:fs";
59280
+ import { createHash as createHash19 } from "node:crypto";
59281
+ import { promises as fs28 } from "node:fs";
58447
59282
  import * as path68 from "node:path";
58448
59283
  function sourceId(fact, line) {
58449
- return `jsonl:${fact.id ?? createHash18("sha256").update(line).digest("hex")}`;
59284
+ return `jsonl:${fact.id ?? createHash19("sha256").update(line).digest("hex")}`;
58450
59285
  }
58451
59286
  function kind(metadata2) {
58452
59287
  const raw = metadata2.memoryKind;
@@ -58460,15 +59295,38 @@ function timestamp(value) {
58460
59295
  const parsed = Date.parse(value);
58461
59296
  return Number.isFinite(parsed) ? new Date(parsed).toISOString() : void 0;
58462
59297
  }
59298
+ function isPermanentSkip(error51) {
59299
+ return error51 instanceof MemoryPolicyError || error51?.name === "ZodError";
59300
+ }
59301
+ async function isImportComplete(markerPath) {
59302
+ try {
59303
+ const marker = JSON.parse(await fs28.readFile(markerPath, "utf8"));
59304
+ return typeof marker?.found === "number" && typeof marker?.at === "string";
59305
+ } catch {
59306
+ return false;
59307
+ }
59308
+ }
59309
+ async function markImportComplete(markerPath, found) {
59310
+ const temporary = `${markerPath}.tmp`;
59311
+ try {
59312
+ await fs28.writeFile(temporary, `${JSON.stringify({ found, at: (/* @__PURE__ */ new Date()).toISOString() })}
59313
+ `, "utf8");
59314
+ await fs28.rename(temporary, markerPath);
59315
+ } catch {
59316
+ }
59317
+ }
58463
59318
  async function importLegacyMemoryLog(backend, service) {
58464
59319
  const result = { found: 0, imported: 0, skipped: 0, corrupt: 0 };
58465
- const logPath = path68.join(path68.dirname(backend.databasePath), "log.jsonl");
59320
+ const memoryDir = path68.dirname(backend.databasePath);
59321
+ const markerPath = path68.join(memoryDir, LEGACY_IMPORT_MARKER);
59322
+ if (await isImportComplete(markerPath)) return result;
58466
59323
  let raw;
58467
59324
  try {
58468
- raw = await fs27.readFile(logPath, "utf8");
59325
+ raw = await fs28.readFile(path68.join(memoryDir, "log.jsonl"), "utf8");
58469
59326
  } catch {
58470
59327
  return result;
58471
59328
  }
59329
+ const rows = [];
58472
59330
  for (const line of raw.split(/\r?\n/)) {
58473
59331
  const trimmed = line.trim();
58474
59332
  if (!trimmed) continue;
@@ -58485,17 +59343,22 @@ async function importLegacyMemoryLog(backend, service) {
58485
59343
  result.corrupt += 1;
58486
59344
  continue;
58487
59345
  }
58488
- const importId = sourceId(fact, trimmed);
58489
- if (await backend.hasImport(importId)) {
59346
+ rows.push({ fact, content, importId: sourceId(fact, trimmed) });
59347
+ }
59348
+ const known = rows.length > 0 ? await backend.hasImports([...new Set(rows.map((row) => row.importId))]) : /* @__PURE__ */ new Set();
59349
+ const importedHere = /* @__PURE__ */ new Set();
59350
+ let unresolved = 0;
59351
+ for (const row of rows) {
59352
+ if (known.has(row.importId) || importedHere.has(row.importId)) {
58490
59353
  result.skipped += 1;
58491
59354
  continue;
58492
59355
  }
58493
- const metadata2 = fact.metadata && typeof fact.metadata === "object" ? fact.metadata : {};
58494
- const createdAt = timestamp(fact.createdAt);
59356
+ const metadata2 = row.fact.metadata && typeof row.fact.metadata === "object" ? row.fact.metadata : {};
59357
+ const createdAt = timestamp(row.fact.createdAt);
58495
59358
  try {
58496
59359
  const node = await service.remember({
58497
59360
  kind: kind(metadata2),
58498
- content,
59361
+ content: row.content,
58499
59362
  importance: typeof metadata2.importance === "number" ? metadata2.importance : 0.55,
58500
59363
  confidence: typeof metadata2.confidence === "number" ? metadata2.confidence : 0.65,
58501
59364
  source: {
@@ -58507,24 +59370,30 @@ async function importLegacyMemoryLog(backend, service) {
58507
59370
  ...createdAt ? { createdAt, recordedAt: createdAt } : {},
58508
59371
  metadata: {
58509
59372
  ...metadata2,
58510
- legacyId: fact.id,
58511
- legacyCreatedAt: fact.createdAt,
58512
- ...fact.graph ? { legacyGraph: fact.graph } : {}
59373
+ legacyId: row.fact.id,
59374
+ legacyCreatedAt: row.fact.createdAt,
59375
+ ...row.fact.graph ? { legacyGraph: row.fact.graph } : {}
58513
59376
  },
58514
59377
  writeClass: "auto"
58515
59378
  });
58516
- await backend.recordImport(importId, node.id);
59379
+ await backend.recordImport(row.importId, node.id);
59380
+ importedHere.add(row.importId);
58517
59381
  result.imported += 1;
58518
- } catch {
59382
+ } catch (error51) {
58519
59383
  result.skipped += 1;
59384
+ if (!isPermanentSkip(error51)) unresolved += 1;
58520
59385
  }
58521
59386
  }
59387
+ const everyRowResolved = result.imported + result.skipped + result.corrupt === result.found;
59388
+ if (everyRowResolved && unresolved === 0) await markImportComplete(markerPath, result.found);
58522
59389
  return result;
58523
59390
  }
59391
+ var LEGACY_IMPORT_MARKER;
58524
59392
  var init_legacyImport = __esm({
58525
59393
  "src/cli/memory/legacyImport.ts"() {
58526
59394
  "use strict";
58527
59395
  init_memory();
59396
+ LEGACY_IMPORT_MARKER = "legacy-import-done.json";
58528
59397
  }
58529
59398
  });
58530
59399
 
@@ -58944,8 +59813,8 @@ WHERE NOT EXISTS (SELECT 1 FROM memory_fts f WHERE f.node_id = n.id);
58944
59813
  });
58945
59814
 
58946
59815
  // src/cli/memory/sqliteBackend.ts
58947
- import { createHash as createHash19, randomUUID as randomUUID6 } from "node:crypto";
58948
- import { promises as fs28 } from "node:fs";
59816
+ import { createHash as createHash20, randomUUID as randomUUID6 } from "node:crypto";
59817
+ import { promises as fs29 } from "node:fs";
58949
59818
  import * as path70 from "node:path";
58950
59819
  function boundedLimit(value, fallback = 50) {
58951
59820
  return Math.max(1, Math.min(Math.floor(value ?? fallback), 1e5));
@@ -58959,9 +59828,9 @@ function withOptional(target, key, value) {
58959
59828
  return { ...target, [key]: value };
58960
59829
  }
58961
59830
  function contentHash(content) {
58962
- return createHash19("sha256").update(content).digest("hex");
59831
+ return createHash20("sha256").update(content).digest("hex");
58963
59832
  }
58964
- var NODE_COLUMNS, INSERT_NODE_SQL, SOURCE_COLUMNS, SQLiteMemoryBackend;
59833
+ var NODE_COLUMNS, INSERT_NODE_SQL, IMPORT_ID_CHUNK, SOURCE_COLUMNS, SQLiteMemoryBackend;
58965
59834
  var init_sqliteBackend = __esm({
58966
59835
  "src/cli/memory/sqliteBackend.ts"() {
58967
59836
  "use strict";
@@ -58975,6 +59844,7 @@ var init_sqliteBackend = __esm({
58975
59844
  valid_until, recorded_at, retracted_at, embedding_ref, metadata_json`;
58976
59845
  INSERT_NODE_SQL = `INSERT INTO memory_nodes (${NODE_COLUMNS})
58977
59846
  VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
59847
+ IMPORT_ID_CHUNK = 500;
58978
59848
  SOURCE_COLUMNS = {
58979
59849
  agent: "agent",
58980
59850
  sessionId: "sessionId",
@@ -59006,7 +59876,7 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
59006
59876
  async init(projectRoot) {
59007
59877
  let resolved;
59008
59878
  try {
59009
- resolved = await fs28.realpath(projectRoot);
59879
+ resolved = await fs29.realpath(projectRoot);
59010
59880
  } catch {
59011
59881
  resolved = path70.resolve(projectRoot);
59012
59882
  }
@@ -59021,21 +59891,21 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
59021
59891
  for (const candidate of [zelariDirectory, directory]) {
59022
59892
  let stat8;
59023
59893
  try {
59024
- stat8 = await fs28.lstat(candidate);
59894
+ stat8 = await fs29.lstat(candidate);
59025
59895
  } catch (error51) {
59026
59896
  if (error51.code !== "ENOENT") throw error51;
59027
59897
  try {
59028
- await fs28.mkdir(candidate);
59898
+ await fs29.mkdir(candidate);
59029
59899
  } catch (mkdirError) {
59030
59900
  if (mkdirError.code !== "EEXIST") throw mkdirError;
59031
59901
  }
59032
- stat8 = await fs28.lstat(candidate);
59902
+ stat8 = await fs29.lstat(candidate);
59033
59903
  }
59034
59904
  if (stat8.isSymbolicLink() || !stat8.isDirectory()) {
59035
59905
  throw new Error(`Memory directory is not a real directory: ${candidate}`);
59036
59906
  }
59037
59907
  }
59038
- const canonicalDirectory = await fs28.realpath(directory);
59908
+ const canonicalDirectory = await fs29.realpath(directory);
59039
59909
  const relativeDirectory = path70.relative(resolved, canonicalDirectory);
59040
59910
  if (relativeDirectory.startsWith("..") || path70.isAbsolute(relativeDirectory)) {
59041
59911
  throw new Error("SQLite memory directory resolves outside the active project.");
@@ -59538,14 +60408,24 @@ VALUES (${Array.from({ length: 19 }, () => "?").join(",")})`;
59538
60408
  versions: versionRows.map(decodeVersion).filter((version2) => Boolean(version2))
59539
60409
  };
59540
60410
  }
59541
- async hasImport(sourceId2) {
60411
+ /** Batched `hasImport`: one `IN` query per 500 ids instead of one RPC per row. */
60412
+ async hasImports(ids) {
59542
60413
  this.assertReady();
59543
- const row = await this.rpc.statement({
59544
- sql: "SELECT source_id FROM memory_imports WHERE source_id=?",
59545
- params: [sourceId2],
59546
- mode: "get"
59547
- });
59548
- return Boolean(row);
60414
+ const found = /* @__PURE__ */ new Set();
60415
+ const unique = [...new Set(ids)];
60416
+ for (let index = 0; index < unique.length; index += IMPORT_ID_CHUNK) {
60417
+ const chunk = unique.slice(index, index + IMPORT_ID_CHUNK);
60418
+ const rows = await this.rpc.statement({
60419
+ sql: `SELECT source_id FROM memory_imports WHERE source_id IN (${chunk.map(() => "?").join(",")})`,
60420
+ params: chunk,
60421
+ mode: "all"
60422
+ });
60423
+ for (const row of rows) found.add(String(row.source_id));
60424
+ }
60425
+ return found;
60426
+ }
60427
+ async hasImport(sourceId2) {
60428
+ return (await this.hasImports([sourceId2])).has(sourceId2);
59549
60429
  }
59550
60430
  async recordImport(sourceId2, memoryId) {
59551
60431
  this.assertReady();
@@ -59572,8 +60452,8 @@ __export(serviceFactory_exports, {
59572
60452
  isMemorySemanticEnabled: () => isMemorySemanticEnabled,
59573
60453
  isMemoryV2Enabled: () => isMemoryV2Enabled
59574
60454
  });
59575
- import { createHash as createHash20 } from "node:crypto";
59576
- import { promises as fs29 } from "node:fs";
60455
+ import { createHash as createHash21 } from "node:crypto";
60456
+ import { promises as fs30 } from "node:fs";
59577
60457
  import * as path71 from "node:path";
59578
60458
  function isMemoryV2Enabled(env = process.env) {
59579
60459
  if (env.ZELARI_MEMORY === "0") return false;
@@ -59593,13 +60473,13 @@ function semanticMinScore(env) {
59593
60473
  async function canonicalProjectId(projectRoot) {
59594
60474
  let canonical;
59595
60475
  try {
59596
- canonical = await fs29.realpath(projectRoot);
60476
+ canonical = await fs30.realpath(projectRoot);
59597
60477
  } catch {
59598
60478
  canonical = path71.resolve(projectRoot);
59599
60479
  }
59600
60480
  canonical = canonical.replace(/\\/g, "/").replace(/\/$/, "");
59601
60481
  if (process.platform === "win32") canonical = canonical.toLocaleLowerCase("en-US");
59602
- return `project_${createHash20("sha256").update(canonical).digest("hex").slice(0, 24)}`;
60482
+ return `project_${createHash21("sha256").update(canonical).digest("hex").slice(0, 24)}`;
59603
60483
  }
59604
60484
  async function getMemoryService(projectRoot, env = process.env, options = {}) {
59605
60485
  const projectId2 = await canonicalProjectId(projectRoot);
@@ -60222,32 +61102,32 @@ __export(loadDurableContext_exports, {
60222
61102
  loadDurableContext: () => loadDurableContext
60223
61103
  });
60224
61104
  function clearDurableContextCache() {
60225
- cache2 = null;
61105
+ cache3 = null;
60226
61106
  }
60227
61107
  async function loadDurableContext(projectRoot, opts) {
60228
61108
  const env = opts?.env ?? process.env;
60229
61109
  if (!isStateEnabled(env)) return "";
60230
61110
  const cacheMs = opts?.cacheMs ?? DEFAULT_CACHE_MS;
60231
61111
  const now = Date.now();
60232
- if (cache2 && cache2.projectRoot === projectRoot && now - cache2.at < cacheMs) {
60233
- return cache2.text;
61112
+ if (cache3 && cache3.projectRoot === projectRoot && now - cache3.at < cacheMs) {
61113
+ return cache3.text;
60234
61114
  }
60235
61115
  try {
60236
61116
  const store6 = await getStateStore(projectRoot, env);
60237
61117
  const text = await store6.materializeContext(void 0, opts?.maxChars);
60238
- cache2 = { text: text || "", at: now, projectRoot };
60239
- return cache2.text;
61118
+ cache3 = { text: text || "", at: now, projectRoot };
61119
+ return cache3.text;
60240
61120
  } catch {
60241
61121
  return "";
60242
61122
  }
60243
61123
  }
60244
- var DEFAULT_CACHE_MS, cache2;
61124
+ var DEFAULT_CACHE_MS, cache3;
60245
61125
  var init_loadDurableContext = __esm({
60246
61126
  "src/cli/state/loadDurableContext.ts"() {
60247
61127
  "use strict";
60248
61128
  init_fileStateStore();
60249
61129
  DEFAULT_CACHE_MS = 2e3;
60250
- cache2 = null;
61130
+ cache3 = null;
60251
61131
  }
60252
61132
  });
60253
61133
 
@@ -62011,7 +62891,7 @@ __export(agentsMd_exports, {
62011
62891
  updateAgentsMd: () => updateAgentsMd
62012
62892
  });
62013
62893
  import { existsSync as existsSync44, readFileSync as readFileSync32, writeFileSync as writeFileSync20 } from "node:fs";
62014
- import { createHash as createHash21 } from "node:crypto";
62894
+ import { createHash as createHash22 } from "node:crypto";
62015
62895
  import { join as join34 } from "node:path";
62016
62896
  import { readFile as readFile10 } from "node:fs/promises";
62017
62897
  async function readPackageJson3(projectRoot) {
@@ -62226,7 +63106,7 @@ async function updateAgentsMd(ctx, projectRoot) {
62226
63106
  return { changed: true, sections: changedSections };
62227
63107
  }
62228
63108
  function hash2(s) {
62229
- return createHash21("sha256").update(s).digest("hex").slice(0, 16);
63109
+ return createHash22("sha256").update(s).digest("hex").slice(0, 16);
62230
63110
  }
62231
63111
  var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
62232
63112
  var init_agentsMd = __esm({
@@ -62954,7 +63834,7 @@ __export(councilFeedback_exports, {
62954
63834
  FeedbackStore: () => FeedbackStore
62955
63835
  });
62956
63836
  import {
62957
- promises as fs30,
63837
+ promises as fs31,
62958
63838
  existsSync as existsSync49,
62959
63839
  readFileSync as readFileSync37,
62960
63840
  writeFileSync as writeFileSync22,
@@ -63087,7 +63967,7 @@ var init_councilFeedback = __esm({
63087
63967
  /** Async variant of load for callers that prefer async IO. */
63088
63968
  async loadAsync() {
63089
63969
  try {
63090
- const raw = await fs30.readFile(this.file, "utf-8");
63970
+ const raw = await fs31.readFile(this.file, "utf-8");
63091
63971
  const parsed = JSON.parse(raw);
63092
63972
  if (parsed && Array.isArray(parsed.entries)) {
63093
63973
  this.entries = parsed.entries.filter(
@@ -63407,7 +64287,7 @@ __export(fileBackend_exports, {
63407
64287
  isMemoryEnabled: () => isMemoryEnabled
63408
64288
  });
63409
64289
  import { randomUUID as randomUUID7 } from "node:crypto";
63410
- import { promises as fs31 } from "node:fs";
64290
+ import { promises as fs32 } from "node:fs";
63411
64291
  import * as path75 from "node:path";
63412
64292
  function tokenize2(text) {
63413
64293
  return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 3);
@@ -63463,7 +64343,7 @@ var init_fileBackend = __esm({
63463
64343
  async init(projectRoot) {
63464
64344
  this.memoryDir = path75.join(projectRoot, ".zelari", "memory");
63465
64345
  this.logPath = path75.join(this.memoryDir, "log.jsonl");
63466
- await fs31.mkdir(this.memoryDir, { recursive: true });
64346
+ await fs32.mkdir(this.memoryDir, { recursive: true });
63467
64347
  }
63468
64348
  async add(content, metadata2 = {}, graph) {
63469
64349
  const fact = {
@@ -63473,7 +64353,7 @@ var init_fileBackend = __esm({
63473
64353
  ...graph ? { graph } : {},
63474
64354
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
63475
64355
  };
63476
- await fs31.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
64356
+ await fs32.appendFile(this.logPath, JSON.stringify(fact) + "\n", "utf8");
63477
64357
  return fact.id;
63478
64358
  }
63479
64359
  async search(query, options = {}) {
@@ -63501,7 +64381,7 @@ var init_fileBackend = __esm({
63501
64381
  async readAll() {
63502
64382
  let raw;
63503
64383
  try {
63504
- raw = await fs31.readFile(this.logPath, "utf8");
64384
+ raw = await fs32.readFile(this.logPath, "utf8");
63505
64385
  } catch {
63506
64386
  return [];
63507
64387
  }
@@ -63534,7 +64414,7 @@ var init_fileBackend = __esm({
63534
64414
  });
63535
64415
 
63536
64416
  // src/cli/traceStore.ts
63537
- import { promises as fs32 } from "node:fs";
64417
+ import { promises as fs33 } from "node:fs";
63538
64418
  import * as path76 from "node:path";
63539
64419
  function traceDir(projectRoot) {
63540
64420
  return path76.join(projectRoot, ".zelari", "trace");
@@ -63544,13 +64424,13 @@ function tracePath(projectRoot, missionId) {
63544
64424
  }
63545
64425
  async function saveTrace(projectRoot, missionId, entries) {
63546
64426
  const dir = traceDir(projectRoot);
63547
- await fs32.mkdir(dir, { recursive: true });
64427
+ await fs33.mkdir(dir, { recursive: true });
63548
64428
  const payload = {
63549
64429
  missionId,
63550
64430
  ts: Date.now(),
63551
64431
  entries
63552
64432
  };
63553
- await fs32.writeFile(
64433
+ await fs33.writeFile(
63554
64434
  tracePath(projectRoot, missionId),
63555
64435
  JSON.stringify(payload, null, 2) + "\n",
63556
64436
  "utf8"
@@ -63581,7 +64461,7 @@ __export(zelariMission_exports, {
63581
64461
  runZelariMission: () => runZelariMission
63582
64462
  });
63583
64463
  import { randomUUID as randomUUID8 } from "node:crypto";
63584
- import { promises as fs33 } from "node:fs";
64464
+ import { promises as fs34 } from "node:fs";
63585
64465
  import * as path77 from "node:path";
63586
64466
  function resolveMaxIterations(env = process.env) {
63587
64467
  const raw = env.ZELARI_MISSION_MAX_ITER;
@@ -63626,8 +64506,8 @@ function isMissionAutoStart(env = process.env) {
63626
64506
  }
63627
64507
  async function writeMissionState(projectRoot, state3) {
63628
64508
  const dir = path77.join(projectRoot, ".zelari");
63629
- await fs33.mkdir(dir, { recursive: true });
63630
- await fs33.writeFile(
64509
+ await fs34.mkdir(dir, { recursive: true });
64510
+ await fs34.writeFile(
63631
64511
  path77.join(dir, "mission-state.json"),
63632
64512
  JSON.stringify(state3, null, 2) + "\n",
63633
64513
  "utf8"
@@ -63641,7 +64521,7 @@ async function writeMissionState(projectRoot, state3) {
63641
64521
  }
63642
64522
  async function loadMissionState(projectRoot) {
63643
64523
  try {
63644
- const raw = await fs33.readFile(
64524
+ const raw = await fs34.readFile(
63645
64525
  path77.join(projectRoot, ".zelari", "mission-state.json"),
63646
64526
  "utf8"
63647
64527
  );
@@ -66919,11 +67799,11 @@ var init_policy = __esm({
66919
67799
  });
66920
67800
 
66921
67801
  // src/cli/orchestration/facts.ts
66922
- import { promises as fs43 } from "node:fs";
67802
+ import { promises as fs44 } from "node:fs";
66923
67803
  import path88 from "node:path";
66924
67804
  async function collectRepoFileCount(root = process.cwd()) {
66925
67805
  try {
66926
- await fs43.readdir(root);
67806
+ await fs44.readdir(root);
66927
67807
  } catch {
66928
67808
  return void 0;
66929
67809
  }
@@ -66934,7 +67814,7 @@ async function collectRepoFileCount(root = process.cwd()) {
66934
67814
  const dir = queue.pop();
66935
67815
  let entries;
66936
67816
  try {
66937
- entries = await fs43.readdir(dir, { withFileTypes: true });
67817
+ entries = await fs44.readdir(dir, { withFileTypes: true });
66938
67818
  } catch {
66939
67819
  continue;
66940
67820
  }
@@ -67212,8 +68092,8 @@ function contractFor(t) {
67212
68092
  blockers
67213
68093
  };
67214
68094
  }
67215
- async function readHarnessState(sessionDir) {
67216
- const report = await readSessionLog(path89.join(sessionDir, "events.jsonl"));
68095
+ async function readHarnessState(sessionDir, cache4) {
68096
+ const report = await readSessionLogCached(path89.join(sessionDir, "events.jsonl"), cache4);
67217
68097
  return deriveHarnessState(report.events);
67218
68098
  }
67219
68099
  var init_harnessState = __esm({
@@ -67514,7 +68394,7 @@ var init_controlBridge = __esm({
67514
68394
  function attachHeadlessLiveCancel(opts) {
67515
68395
  const abort = new AbortController();
67516
68396
  const controlQueue = new RuntimeControlQueue();
67517
- const cancel = () => {
68397
+ const cancel = (_reason) => {
67518
68398
  if (!abort.signal.aborted) abort.abort();
67519
68399
  return true;
67520
68400
  };
@@ -67837,7 +68717,7 @@ var init_sandboxedFs = __esm({
67837
68717
  // src/cli/extensions/loader.ts
67838
68718
  import { join as join44 } from "node:path";
67839
68719
  import { readdirSync as readdirSync11, readFileSync as readFileSync43 } from "node:fs";
67840
- import { createHash as createHash22 } from "node:crypto";
68720
+ import { createHash as createHash23 } from "node:crypto";
67841
68721
  import { pathToFileURL as pathToFileURL3 } from "node:url";
67842
68722
  function globalExtensionsDir() {
67843
68723
  return join44(zelariHome(), "extensions");
@@ -67846,7 +68726,7 @@ function projectExtensionsDir(projectRoot) {
67846
68726
  return join44(projectRoot, ".zelari", "extensions");
67847
68727
  }
67848
68728
  function sha256File(file2) {
67849
- return createHash22("sha256").update(readFileSync43(file2)).digest("hex");
68729
+ return createHash23("sha256").update(readFileSync43(file2)).digest("hex");
67850
68730
  }
67851
68731
  function candidateFiles(dir) {
67852
68732
  let names;
@@ -67877,7 +68757,7 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
67877
68757
  skipped
67878
68758
  };
67879
68759
  const fsRoot = options.fsRoot ?? process.cwd();
67880
- let fs48 = null;
68760
+ let fs49 = null;
67881
68761
  const pending = [];
67882
68762
  for (const dir of dirs) {
67883
68763
  const files = candidateFiles(dir.path);
@@ -67932,9 +68812,9 @@ async function loadExtensionsFromDirs(dirs, options = {}) {
67932
68812
  skipped.push(`${file2}: missing ZelariExtension export`);
67933
68813
  continue;
67934
68814
  }
67935
- if (!fs48) fs48 = bindSandboxedFs(fsRoot);
68815
+ if (!fs49) fs49 = bindSandboxedFs(fsRoot);
67936
68816
  try {
67937
- await runtime.registry.registerExtension(ext, { fs: fs48 });
68817
+ await runtime.registry.registerExtension(ext, { fs: fs49 });
67938
68818
  runtime.loaded.push({ id: ext.id, file: file2, scope });
67939
68819
  } catch (err) {
67940
68820
  const msg = err instanceof Error ? err.message : String(err);
@@ -67992,7 +68872,7 @@ var init_loader = __esm({
67992
68872
  });
67993
68873
 
67994
68874
  // src/cli/headless/runOneTurn.ts
67995
- import { promises as fs44 } from "node:fs";
68875
+ import { promises as fs45 } from "node:fs";
67996
68876
  import path92 from "node:path";
67997
68877
  function planModeFromOpts(opts) {
67998
68878
  return (opts.phase ?? "build") === "plan";
@@ -68072,10 +68952,10 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
68072
68952
  })() : void 0;
68073
68953
  const unregisterLiveTurnControl = process.env.ZELARI_SERVE_HARNESS === "1" ? registerLiveTurnControl({
68074
68954
  queue: controlQueue,
68075
- cancel: () => {
68955
+ cancel: (reason) => {
68076
68956
  const cancelHook = harnessHolder.cancel;
68077
68957
  if (!cancelHook) return false;
68078
- cancelHook();
68958
+ cancelHook(reason);
68079
68959
  return true;
68080
68960
  }
68081
68961
  }) : void 0;
@@ -68361,7 +69241,7 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
68361
69241
  memoryContextChars: 2e3
68362
69242
  } : {}
68363
69243
  });
68364
- harnessHolder.cancel = () => harness.cancel();
69244
+ harnessHolder.cancel = (reason) => harness.cancel(reason);
68365
69245
  const readBuildProgress = () => {
68366
69246
  const getter = harness.getBuildProgress;
68367
69247
  return typeof getter === "function" ? getter.call(harness) : { mutationsAttempted: 0, mutationsSucceeded: 0 };
@@ -68548,11 +69428,11 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
68548
69428
  process.stdout.write(pass.textBuffer.join(""));
68549
69429
  }
68550
69430
  process.stdout.write("");
68551
- if (pass.finalReason !== "error" && opts.output === "json" && wantWrites && pass.successfulWrites === 0) {
69431
+ if (pass.finalReason !== "error" && pass.finalReason !== "cancelled" && opts.output === "json" && wantWrites && pass.successfulWrites === 0) {
68552
69432
  emitEvent({ type: "log", message: "[headless] BUILD failed: zero successful mutations after liveness recovery" });
68553
69433
  }
68554
69434
  try {
68555
- const closeStatus = pass.finalReason === "error" ? "error" : strictExit !== 0 ? "stopped" : "completed";
69435
+ const closeStatus = pass.finalReason === "error" ? "error" : pass.finalReason === "cancelled" ? "cancelled" : strictExit !== 0 ? "stopped" : "completed";
68556
69436
  await spine.close(closeStatus);
68557
69437
  } catch {
68558
69438
  }
@@ -68563,8 +69443,8 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
68563
69443
  if (json3) {
68564
69444
  if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
68565
69445
  else {
68566
- await fs44.mkdir(path92.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
68567
- await fs44.writeFile(opts.exportSessionPath, json3, "utf8");
69446
+ await fs45.mkdir(path92.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
69447
+ await fs45.writeFile(opts.exportSessionPath, json3, "utf8");
68568
69448
  }
68569
69449
  }
68570
69450
  } catch {
@@ -69611,7 +70491,7 @@ __export(triggerLock_exports, {
69611
70491
  lockPath: () => lockPath,
69612
70492
  releaseLock: () => releaseLock
69613
70493
  });
69614
- import { promises as fs45 } from "node:fs";
70494
+ import { promises as fs46 } from "node:fs";
69615
70495
  import * as path93 from "node:path";
69616
70496
  function lockPath(projectRoot) {
69617
70497
  return path93.join(projectRoot, ".zelari", "trigger.lock");
@@ -69628,9 +70508,9 @@ function isPidAlive(pid) {
69628
70508
  async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
69629
70509
  const lp = lockPath(projectRoot);
69630
70510
  const dir = path93.dirname(lp);
69631
- await fs45.mkdir(dir, { recursive: true });
70511
+ await fs46.mkdir(dir, { recursive: true });
69632
70512
  try {
69633
- const raw = await fs45.readFile(lp, "utf8");
70513
+ const raw = await fs46.readFile(lp, "utf8");
69634
70514
  const existing = JSON.parse(raw);
69635
70515
  if (existing.pid && isPidAlive(existing.pid)) {
69636
70516
  return { acquired: false, heldBy: existing.pid, lockPath: lp };
@@ -69641,13 +70521,13 @@ async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date())
69641
70521
  pid: process.pid,
69642
70522
  acquiredAt: now().toISOString()
69643
70523
  };
69644
- await fs45.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
70524
+ await fs46.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
69645
70525
  return { acquired: true, lockPath: lp };
69646
70526
  }
69647
70527
  async function releaseLock(projectRoot) {
69648
70528
  const lp = lockPath(projectRoot);
69649
70529
  try {
69650
- await fs45.unlink(lp);
70530
+ await fs46.unlink(lp);
69651
70531
  } catch {
69652
70532
  }
69653
70533
  }
@@ -69658,7 +70538,7 @@ var init_triggerLock = __esm({
69658
70538
  });
69659
70539
 
69660
70540
  // src/cli/runHeadless.ts
69661
- import { promises as fs46 } from "node:fs";
70541
+ import { promises as fs47 } from "node:fs";
69662
70542
  import path94 from "node:path";
69663
70543
  import { randomUUID as randomUUID10 } from "node:crypto";
69664
70544
  async function runHeadless(opts) {
@@ -69730,12 +70610,42 @@ ${err.stack}` : "";
69730
70610
  return 1;
69731
70611
  }
69732
70612
  const { buildProviderStream: buildProviderStream2 } = await Promise.resolve().then(() => (init_resolveStream(), resolveStream_exports));
69733
- providerStream = buildProviderStream2({
70613
+ const primaryStream = buildProviderStream2({
69734
70614
  providerId: provider,
69735
70615
  apiKey: key.apiKey,
69736
70616
  baseUrl: key.baseUrl,
69737
70617
  model
69738
70618
  });
70619
+ const { isHeadlessFailoverEnabled: isHeadlessFailoverEnabled2, wrapWithHeadlessFailover: wrapWithHeadlessFailover2 } = await Promise.resolve().then(() => (init_crossProviderFailover(), crossProviderFailover_exports));
70620
+ if (isHeadlessFailoverEnabled2()) {
70621
+ const { PROVIDERS: PROVIDERS2 } = await Promise.resolve().then(() => (init_keyStore(), keyStore_exports));
70622
+ const { providerConfigFor: providerConfigFor2 } = await Promise.resolve().then(() => (init_openai_compatible(), openai_compatible_exports));
70623
+ providerStream = await wrapWithHeadlessFailover2({
70624
+ primary: primaryStream,
70625
+ primaryProviderId: provider,
70626
+ validProviderIds: PROVIDERS2.map((p3) => p3.id),
70627
+ lookupFallbackConfig: async (id3) => providerConfigFor2(id3),
70628
+ buildStream: (config2) => buildProviderStream2(config2),
70629
+ onWarning: (warning) => {
70630
+ try {
70631
+ process.stderr.write(`[zelari-code --headless] ${warning}
70632
+ `);
70633
+ } catch {
70634
+ }
70635
+ },
70636
+ onFailover: (info) => {
70637
+ try {
70638
+ process.stderr.write(
70639
+ `[zelari-code --headless] [failover] ${info.phase}${info.label ? ` \u2192 ${info.label}` : ""}: ${info.message}
70640
+ `
70641
+ );
70642
+ } catch {
70643
+ }
70644
+ }
70645
+ });
70646
+ } else {
70647
+ providerStream = primaryStream;
70648
+ }
69739
70649
  }
69740
70650
  return dispatchHeadlessTurn(opts, provider, model, providerStream, {
69741
70651
  // H10-fix3: the gate already ran above on the SAME input (no chdir in
@@ -69907,7 +70817,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
69907
70817
  log(`loading pre-flight plan: ${planPath}`);
69908
70818
  let raw;
69909
70819
  try {
69910
- raw = await fs46.readFile(planPath, "utf8");
70820
+ raw = await fs47.readFile(planPath, "utf8");
69911
70821
  } catch (e) {
69912
70822
  log(`plan file not found: ${planPath} (${e.message})`);
69913
70823
  exitCode = 1;
@@ -69948,8 +70858,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
69948
70858
  const planId = randomUUID10();
69949
70859
  const planDir = path94.join(cwd, ".zelari", "radio");
69950
70860
  const planPath = path94.join(planDir, `plan-${planId}.json`);
69951
- await fs46.mkdir(planDir, { recursive: true });
69952
- await fs46.writeFile(
70861
+ await fs47.mkdir(planDir, { recursive: true });
70862
+ await fs47.writeFile(
69953
70863
  planPath,
69954
70864
  JSON.stringify(
69955
70865
  { id: graph.id, nodes: [...graph.nodes.values()] },
@@ -70338,8 +71248,8 @@ async function runHeadlessCouncilBody(opts, provider, model, providerStream, ext
70338
71248
  if (json3) {
70339
71249
  if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
70340
71250
  else {
70341
- await fs46.mkdir(path94.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
70342
- await fs46.writeFile(opts.exportSessionPath, json3, "utf8");
71251
+ await fs47.mkdir(path94.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
71252
+ await fs47.writeFile(opts.exportSessionPath, json3, "utf8");
70343
71253
  }
70344
71254
  }
70345
71255
  } catch {
@@ -70810,8 +71720,8 @@ ${ragContext}` : slicePrompt;
70810
71720
  if (json3) {
70811
71721
  if (opts.exportSessionPath === "-") process.stdout.write(json3 + "\n");
70812
71722
  else {
70813
- await fs46.mkdir(path94.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
70814
- await fs46.writeFile(opts.exportSessionPath, json3, "utf8");
71723
+ await fs47.mkdir(path94.dirname(opts.exportSessionPath), { recursive: true }).catch(() => void 0);
71724
+ await fs47.writeFile(opts.exportSessionPath, json3, "utf8");
70815
71725
  }
70816
71726
  }
70817
71727
  } catch {
@@ -72282,7 +73192,7 @@ import {
72282
73192
  writeFileSync as writeFileSync26
72283
73193
  } from "node:fs";
72284
73194
  import { join as join47 } from "node:path";
72285
- import { createHash as createHash23, randomBytes as randomBytes7, timingSafeEqual } from "node:crypto";
73195
+ import { createHash as createHash24, randomBytes as randomBytes7, timingSafeEqual } from "node:crypto";
72286
73196
  function getZelariHome() {
72287
73197
  return zelariHome();
72288
73198
  }
@@ -72350,16 +73260,16 @@ function loadOrCreateToken(explicit) {
72350
73260
  const token = randomBytes7(24).toString("base64url");
72351
73261
  writeFileSync26(path101, token + "\n", "utf8");
72352
73262
  try {
72353
- const fs48 = __require("node:fs");
72354
- fs48.chmodSync?.(path101, 384);
73263
+ const fs49 = __require("node:fs");
73264
+ fs49.chmodSync?.(path101, 384);
72355
73265
  } catch {
72356
73266
  }
72357
73267
  return { token, created: true };
72358
73268
  }
72359
73269
  function tokenMatches(expected, provided) {
72360
73270
  if (!provided) return false;
72361
- const a = createHash23("sha256").update(expected).digest();
72362
- const b = createHash23("sha256").update(provided).digest();
73271
+ const a = createHash24("sha256").update(expected).digest();
73272
+ const b = createHash24("sha256").update(provided).digest();
72363
73273
  try {
72364
73274
  return timingSafeEqual(a, b);
72365
73275
  } catch {
@@ -72628,7 +73538,7 @@ var init_askUserBridge = __esm({
72628
73538
  });
72629
73539
 
72630
73540
  // src/cli/serve/spineLockSweep.ts
72631
- import { promises as fs47 } from "node:fs";
73541
+ import { promises as fs48 } from "node:fs";
72632
73542
  import path96 from "node:path";
72633
73543
  async function sweepOrphanSpineLocks(sessionsDir2, options = {}) {
72634
73544
  const dir = sessionsDir2 ?? resolveSessionsDir();
@@ -72642,14 +73552,14 @@ async function sweepOrphanSpineLocks(sessionsDir2, options = {}) {
72642
73552
  const result = { swept: 0, kept: 0, errors: 0 };
72643
73553
  let entries;
72644
73554
  try {
72645
- entries = await fs47.readdir(dir);
73555
+ entries = await fs48.readdir(dir);
72646
73556
  } catch {
72647
73557
  return result;
72648
73558
  }
72649
73559
  for (const sessionId2 of entries) {
72650
73560
  const lockPath2 = path96.join(dir, sessionId2, "writer.lock");
72651
73561
  try {
72652
- const raw = await fs47.readFile(lockPath2, "utf-8");
73562
+ const raw = await fs48.readFile(lockPath2, "utf-8");
72653
73563
  let lockInfo = {};
72654
73564
  try {
72655
73565
  lockInfo = JSON.parse(raw);
@@ -72666,7 +73576,7 @@ async function sweepOrphanSpineLocks(sessionsDir2, options = {}) {
72666
73576
  result.kept += 1;
72667
73577
  continue;
72668
73578
  }
72669
- await fs47.rm(lockPath2, { force: true });
73579
+ await fs48.rm(lockPath2, { force: true });
72670
73580
  result.swept += 1;
72671
73581
  onSwept(sessionId2, verdict.reason);
72672
73582
  } catch (err) {
@@ -72886,7 +73796,8 @@ function startHarnessServer(options = {}) {
72886
73796
  };
72887
73797
  }
72888
73798
  if (isCancel) {
72889
- const delivered = live.cancel();
73799
+ const reason = typeof params.reason === "string" && params.reason.trim().length > 0 ? params.reason.trim() : void 0;
73800
+ const delivered = live.cancel(reason);
72890
73801
  if (delivered) {
72891
73802
  write(JSON.stringify(controlAppliedEvent(controlId, "cancel", "cancel")));
72892
73803
  }
@@ -77973,107 +78884,8 @@ init_metrics3();
77973
78884
  init_modelPricing();
77974
78885
  init_openai_compatible();
77975
78886
  init_resolveStream();
77976
- import { useState as useState6, useRef as useRef4, useCallback as useCallback2 } from "react";
77977
-
77978
- // src/cli/providerFailover.ts
77979
- var DEFAULT_IS_TRANSIENT = (delta) => delta.kind === "error";
77980
- function providerFailover(options) {
77981
- const isTransient = options.isTransientFailure ?? DEFAULT_IS_TRANSIENT;
77982
- const primaryFailedMsg = options.fallbackLabel ? `[failover] primary failed, switching to ${options.fallbackLabel}` : "[failover] primary failed, switching to fallback";
77983
- const primaryThrewMsg = options.fallbackLabel ? `[failover] primary threw, switching to ${options.fallbackLabel}: ` : "[failover] primary threw: ";
77984
- const fallbackFailedMsg = options.fallbackLabel ? `[failover] fallback (${options.fallbackLabel}) also failed: ` : "[failover] fallback also failed: ";
77985
- return async function* (params) {
77986
- const triedFallback = { value: false };
77987
- try {
77988
- for await (const delta of options.primary(params)) {
77989
- if (isTransient(delta)) {
77990
- yield delta;
77991
- triedFallback.value = true;
77992
- yield {
77993
- kind: "error",
77994
- message: primaryFailedMsg
77995
- };
77996
- break;
77997
- }
77998
- yield delta;
77999
- }
78000
- } catch (err) {
78001
- triedFallback.value = true;
78002
- yield {
78003
- kind: "error",
78004
- message: `${primaryThrewMsg}${err instanceof Error ? err.message : String(err)}`
78005
- };
78006
- }
78007
- if (triedFallback.value) {
78008
- try {
78009
- for await (const delta of options.fallback(params)) {
78010
- yield delta;
78011
- }
78012
- } catch (err) {
78013
- yield {
78014
- kind: "error",
78015
- message: `${fallbackFailedMsg}${err instanceof Error ? err.message : String(err)}`
78016
- };
78017
- return;
78018
- }
78019
- }
78020
- };
78021
- }
78022
-
78023
- // src/cli/crossProviderFailover.ts
78024
- async function resolveFailoverStream(options) {
78025
- if (!options.failoverEnabled) {
78026
- return {
78027
- fallback: options.primary,
78028
- fallbackLabel: void 0,
78029
- warning: "",
78030
- reason: "disabled"
78031
- };
78032
- }
78033
- const requested = options.envValue?.trim();
78034
- if (!requested || requested.length === 0) {
78035
- return {
78036
- fallback: options.primary,
78037
- fallbackLabel: void 0,
78038
- warning: "",
78039
- reason: "unset"
78040
- };
78041
- }
78042
- const valid = new Set(options.validProviderIds);
78043
- if (!valid.has(requested)) {
78044
- return {
78045
- fallback: options.primary,
78046
- fallbackLabel: void 0,
78047
- warning: `[failover] ANATHEMA_FAILOVER_PROVIDER="${requested}" is not a known provider. Falling back to v3-G same-provider behavior. Available: ${options.validProviderIds.join(", ")}.`,
78048
- reason: "unknown"
78049
- };
78050
- }
78051
- if (requested === options.primaryProviderId) {
78052
- return {
78053
- fallback: options.primary,
78054
- fallbackLabel: void 0,
78055
- warning: "",
78056
- reason: "same-as-primary"
78057
- };
78058
- }
78059
- const fallbackConfig = await options.lookupFallbackConfig(requested);
78060
- if (!fallbackConfig) {
78061
- return {
78062
- fallback: options.primary,
78063
- fallbackLabel: void 0,
78064
- warning: `[failover] No API key configured for provider "${requested}". Falling back to v3-G same-provider behavior.`,
78065
- reason: "missing-key"
78066
- };
78067
- }
78068
- return {
78069
- fallback: options.buildStream(fallbackConfig),
78070
- fallbackLabel: requested,
78071
- warning: "",
78072
- reason: "resolved"
78073
- };
78074
- }
78075
-
78076
- // src/cli/hooks/useChatTurn.ts
78887
+ init_providerFailover();
78888
+ init_crossProviderFailover();
78077
78889
  init_shellResolver();
78078
78890
  init_keyStore();
78079
78891
  init_providerConfig();
@@ -78090,6 +78902,7 @@ init_completionProof();
78090
78902
  init_verifyStatus();
78091
78903
  init_nativeVerification();
78092
78904
  init_spineTelemetry();
78905
+ import { useState as useState6, useRef as useRef4, useCallback as useCallback2 } from "react";
78093
78906
 
78094
78907
  // src/cli/hooks/permissionPicker.ts
78095
78908
  init_toolPermissions();
@@ -81335,11 +82148,11 @@ function handleCacheStats(ctx) {
81335
82148
  // src/cli/slashHandlers/memory.ts
81336
82149
  init_messageHelpers();
81337
82150
  init_serviceFactory();
81338
- import { promises as fs35 } from "node:fs";
82151
+ import { promises as fs36 } from "node:fs";
81339
82152
  import * as path80 from "node:path";
81340
82153
 
81341
82154
  // src/cli/memory/promotion.ts
81342
- import { promises as fs34 } from "node:fs";
82155
+ import { promises as fs35 } from "node:fs";
81343
82156
  import * as path79 from "node:path";
81344
82157
  var START = "<!-- zelari:memory-promotions:start -->";
81345
82158
  var END = "<!-- zelari:memory-promotions:end -->";
@@ -81356,17 +82169,17 @@ async function promoteMemoryToAgentsMd(projectRoot, node) {
81356
82169
  if (!DURABLE_KINDS.has(node.kind)) {
81357
82170
  return { added: false, path: path79.join(projectRoot, "AGENTS.md"), reason: `${node.kind} is not a durable instruction kind` };
81358
82171
  }
81359
- const root = await fs34.realpath(projectRoot).catch(() => path79.resolve(projectRoot));
82172
+ const root = await fs35.realpath(projectRoot).catch(() => path79.resolve(projectRoot));
81360
82173
  const target = path79.join(root, "AGENTS.md");
81361
82174
  try {
81362
- const stat8 = await fs34.lstat(target);
82175
+ const stat8 = await fs35.lstat(target);
81363
82176
  if (stat8.isSymbolicLink() || !stat8.isFile()) throw new Error("AGENTS.md must be a regular project file.");
81364
82177
  } catch (error51) {
81365
82178
  if (error51.code !== "ENOENT") throw error51;
81366
82179
  }
81367
82180
  let current = "";
81368
82181
  try {
81369
- current = await fs34.readFile(target, "utf8");
82182
+ current = await fs35.readFile(target, "utf8");
81370
82183
  } catch (error51) {
81371
82184
  if (error51.code !== "ENOENT") throw error51;
81372
82185
  }
@@ -81389,11 +82202,11 @@ ${END}
81389
82202
  `;
81390
82203
  }
81391
82204
  const temporary = `${target}.tmp-${process.pid}-${Date.now()}`;
81392
- await fs34.writeFile(temporary, updated, { encoding: "utf8", flag: "wx" });
82205
+ await fs35.writeFile(temporary, updated, { encoding: "utf8", flag: "wx" });
81393
82206
  try {
81394
- await fs34.rename(temporary, target);
82207
+ await fs35.rename(temporary, target);
81395
82208
  } catch (error51) {
81396
- await fs34.unlink(temporary).catch(() => void 0);
82209
+ await fs35.unlink(temporary).catch(() => void 0);
81397
82210
  throw error51;
81398
82211
  }
81399
82212
  return { added: true, path: target };
@@ -81427,7 +82240,7 @@ function isInside(root, target) {
81427
82240
  }
81428
82241
  async function safeExportPath(cwd, requested) {
81429
82242
  const lexicalRoot = path80.resolve(cwd);
81430
- const root = await fs35.realpath(lexicalRoot).catch(() => lexicalRoot);
82243
+ const root = await fs36.realpath(lexicalRoot).catch(() => lexicalRoot);
81431
82244
  const fallback = path80.join(root, ".zelari", "memory", `export-${Date.now()}.json`);
81432
82245
  const target = requested?.trim() ? path80.resolve(root, requested.trim()) : fallback;
81433
82246
  if (!isInside(root, target)) {
@@ -81439,7 +82252,7 @@ async function safeExportPath(cwd, requested) {
81439
82252
  for (const segment of relativeParent.split(path80.sep).filter(Boolean)) {
81440
82253
  cursor = path80.join(cursor, segment);
81441
82254
  try {
81442
- const stat8 = await fs35.lstat(cursor);
82255
+ const stat8 = await fs36.lstat(cursor);
81443
82256
  if (stat8.isSymbolicLink()) {
81444
82257
  throw new Error("Export path must not traverse a symbolic link.");
81445
82258
  }
@@ -81449,7 +82262,7 @@ async function safeExportPath(cwd, requested) {
81449
82262
  }
81450
82263
  }
81451
82264
  try {
81452
- if ((await fs35.lstat(target)).isSymbolicLink()) {
82265
+ if ((await fs36.lstat(target)).isSymbolicLink()) {
81453
82266
  throw new Error("Export target must not be a symbolic link.");
81454
82267
  }
81455
82268
  } catch (error51) {
@@ -81609,13 +82422,13 @@ ${message}` : message
81609
82422
  }
81610
82423
  case "export": {
81611
82424
  const target = await safeExportPath(ctx.cwd, args.join(" ").trim() || void 0);
81612
- await fs35.mkdir(path80.dirname(target), { recursive: true });
81613
- const root = await fs35.realpath(ctx.cwd).catch(() => path80.resolve(ctx.cwd));
81614
- const realParent = await fs35.realpath(path80.dirname(target));
82425
+ await fs36.mkdir(path80.dirname(target), { recursive: true });
82426
+ const root = await fs36.realpath(ctx.cwd).catch(() => path80.resolve(ctx.cwd));
82427
+ const realParent = await fs36.realpath(path80.dirname(target));
81615
82428
  if (!isInside(root, realParent)) {
81616
82429
  throw new Error("Export path resolves outside the active project.");
81617
82430
  }
81618
- await fs35.writeFile(target, JSON.stringify(await memory.export(), null, 2) + "\n", "utf8");
82431
+ await fs36.writeFile(target, JSON.stringify(await memory.export(), null, 2) + "\n", "utf8");
81619
82432
  emit(`[memory] export written to ${target}`);
81620
82433
  return;
81621
82434
  }
@@ -81820,12 +82633,12 @@ ${digest}
81820
82633
  init_auditLogger();
81821
82634
  init_toolRegistry();
81822
82635
  init_messageHelpers();
81823
- import { promises as fs37 } from "node:fs";
82636
+ import { promises as fs38 } from "node:fs";
81824
82637
 
81825
82638
  // src/cli/tools/krakenCsvFanout.ts
81826
82639
  init_zod();
81827
82640
  init_taskTool();
81828
- import { promises as fs36 } from "node:fs";
82641
+ import { promises as fs37 } from "node:fs";
81829
82642
  import path81 from "node:path";
81830
82643
  import { randomBytes as randomBytes6 } from "node:crypto";
81831
82644
  var CsvFanoutArgsSchema = external_exports.object({
@@ -81847,7 +82660,7 @@ var CsvFanoutArgsSchema = external_exports.object({
81847
82660
  max_runtime_seconds: external_exports.number().int().positive().optional()
81848
82661
  });
81849
82662
  async function readCsv(filePath) {
81850
- const text = await fs36.readFile(filePath, "utf8");
82663
+ const text = await fs37.readFile(filePath, "utf8");
81851
82664
  return parseCsv(text);
81852
82665
  }
81853
82666
  function parseCsv(text) {
@@ -81977,7 +82790,7 @@ async function runCsvFanout(args, deps, opts) {
81977
82790
  errored += 1;
81978
82791
  errors.push(`${row[args.id_column] ?? i}: ${res.error}`);
81979
82792
  }
81980
- await fs36.mkdir(path81.dirname(absOut), { recursive: true });
82793
+ await fs37.mkdir(path81.dirname(absOut), { recursive: true });
81981
82794
  await queueWrite(serializeCsv(outHeaders, outputRecords));
81982
82795
  }
81983
82796
  }
@@ -81999,8 +82812,8 @@ async function runCsvFanout(args, deps, opts) {
81999
82812
  }
82000
82813
  async function atomicWrite(file2, contents) {
82001
82814
  const tmp = `${file2}.${process.pid}.${Date.now()}.${randomBytes6(6).toString("hex")}.tmp`;
82002
- await fs36.writeFile(tmp, contents, "utf8");
82003
- await fs36.rename(tmp, file2);
82815
+ await fs37.writeFile(tmp, contents, "utf8");
82816
+ await fs37.rename(tmp, file2);
82004
82817
  }
82005
82818
 
82006
82819
  // src/cli/slashHandlers/krakenFanout.ts
@@ -82098,7 +82911,7 @@ async function handleKrakenFanout(ctx, raw) {
82098
82911
  }
82099
82912
  const absCsv = isAbsolute3(parsed.args.csv_path) ? parsed.args.csv_path : joinPath(ctx.cwd, parsed.args.csv_path);
82100
82913
  try {
82101
- await fs37.access(absCsv);
82914
+ await fs38.access(absCsv);
82102
82915
  } catch {
82103
82916
  appendSystem(ctx.setMessages, `[kraken fanout] source CSV not found: ${absCsv}`);
82104
82917
  return;
@@ -82171,7 +82984,7 @@ function splitArgs(s) {
82171
82984
 
82172
82985
  // src/cli/slashHandlers/krakenWorkbench.ts
82173
82986
  init_messageHelpers();
82174
- import { promises as fs38 } from "node:fs";
82987
+ import { promises as fs39 } from "node:fs";
82175
82988
  import path82 from "node:path";
82176
82989
 
82177
82990
  // src/cli/kraken/workbenchView.ts
@@ -82293,11 +83106,11 @@ async function handleKrakenWorkbench(ctx) {
82293
83106
  let latest = null;
82294
83107
  let latestMtime = 0;
82295
83108
  try {
82296
- const files = await fs38.readdir(dir);
83109
+ const files = await fs39.readdir(dir);
82297
83110
  for (const f of files) {
82298
83111
  if (!f.startsWith("workbench-") || !f.endsWith(".md")) continue;
82299
83112
  const full = path82.join(dir, f);
82300
- const stat8 = await fs38.stat(full);
83113
+ const stat8 = await fs39.stat(full);
82301
83114
  if (stat8.mtimeMs > latestMtime) {
82302
83115
  latestMtime = stat8.mtimeMs;
82303
83116
  latest = full;
@@ -82309,7 +83122,7 @@ async function handleKrakenWorkbench(ctx) {
82309
83122
  appendSystem(ctx.setMessages, "[kraken workbench] no workbench file found (.zelari/radio/workbench-*.md)");
82310
83123
  return;
82311
83124
  }
82312
- const content = await fs38.readFile(latest, "utf8");
83125
+ const content = await fs39.readFile(latest, "utf8");
82313
83126
  const parsed = parseWorkbench(content);
82314
83127
  const rendered = formatWorkbenchForTerminal(parsed);
82315
83128
  if (!rendered.trim()) {
@@ -82623,20 +83436,20 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
82623
83436
  // src/cli/slashHandlers/promoteMember.ts
82624
83437
  init_messageHelpers();
82625
83438
  init_paths();
82626
- import { promises as fs39 } from "node:fs";
83439
+ import { promises as fs40 } from "node:fs";
82627
83440
  import path85 from "node:path";
82628
83441
  async function handlePromoteMember(ctx, memberId) {
82629
83442
  try {
82630
83443
  const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
82631
83444
  const { skill, markdown } = promoteMember2(memberId);
82632
83445
  const skillDir = skillsDir();
82633
- await fs39.mkdir(skillDir, { recursive: true });
83446
+ await fs40.mkdir(skillDir, { recursive: true });
82634
83447
  const filePath = path85.join(skillDir, `${skill.id}.md`);
82635
- const previous = await fs39.readFile(filePath, "utf8").catch(() => null);
82636
- const { createHash: createHash24 } = await import("node:crypto");
82637
- const sha = (s) => createHash24("sha256").update(s, "utf8").digest("hex");
83448
+ const previous = await fs40.readFile(filePath, "utf8").catch(() => null);
83449
+ const { createHash: createHash25 } = await import("node:crypto");
83450
+ const sha = (s) => createHash25("sha256").update(s, "utf8").digest("hex");
82638
83451
  const lineage = `<!-- lineage: genome=sha256:${sha(markdown)} parent=${previous ? `sha256:${sha(previous)}` : "none"} promotedBy=user promotedAt=${(/* @__PURE__ */ new Date()).toISOString()} -->`;
82639
- await fs39.writeFile(filePath, `${markdown}
83452
+ await fs40.writeFile(filePath, `${markdown}
82640
83453
  ${lineage}
82641
83454
  `, "utf8");
82642
83455
  appendSystem(
@@ -82656,7 +83469,7 @@ ${lineage}
82656
83469
 
82657
83470
  // src/cli/branchManager.ts
82658
83471
  init_paths();
82659
- import { promises as fs40, existsSync as existsSync55, readFileSync as readFileSync41, writeFileSync as writeFileSync24, mkdirSync as mkdirSync21, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
83472
+ import { promises as fs41, existsSync as existsSync55, readFileSync as readFileSync41, writeFileSync as writeFileSync24, mkdirSync as mkdirSync21, statSync as statSync7, rmSync as rmSync3 } from "node:fs";
82660
83473
  import path86 from "node:path";
82661
83474
  var META_FILENAME = "meta.json";
82662
83475
  var SESSIONS_SUBDIR = "sessions";
@@ -82704,7 +83517,7 @@ function writeBranchMeta(name, baseDir, meta3) {
82704
83517
  async function countSessions(name, baseDir) {
82705
83518
  const sessionsPath = sessionsPathFor(name, baseDir);
82706
83519
  try {
82707
- const entries = await fs40.readdir(sessionsPath);
83520
+ const entries = await fs41.readdir(sessionsPath);
82708
83521
  return entries.filter((e) => e.endsWith(".jsonl")).length;
82709
83522
  } catch (err) {
82710
83523
  if (err.code === "ENOENT") return 0;
@@ -82757,7 +83570,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
82757
83570
  const branchSessionsPath = sessionsPathFor(name, baseDir);
82758
83571
  mkdirSync21(branchSessionsPath, { recursive: true });
82759
83572
  const destPath = path86.join(branchSessionsPath, `${fromSessionId}.jsonl`);
82760
- await fs40.copyFile(sourcePath, destPath);
83573
+ await fs41.copyFile(sourcePath, destPath);
82761
83574
  const meta3 = {
82762
83575
  name,
82763
83576
  createdAt: Date.now(),
@@ -82775,7 +83588,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
82775
83588
  async function listBranches(baseDir = getBranchesBaseDir()) {
82776
83589
  let entries;
82777
83590
  try {
82778
- entries = await fs40.readdir(baseDir);
83591
+ entries = await fs41.readdir(baseDir);
82779
83592
  } catch (err) {
82780
83593
  if (err.code === "ENOENT") return [];
82781
83594
  throw err;
@@ -82859,7 +83672,7 @@ async function handleBranchCheckout(ctx, branchName) {
82859
83672
 
82860
83673
  // src/cli/slashHandlers/workspace.ts
82861
83674
  init_messageHelpers();
82862
- import { promises as fs41 } from "node:fs";
83675
+ import { promises as fs42 } from "node:fs";
82863
83676
  import path87 from "node:path";
82864
83677
  async function handleWorkspaceShow(ctx, what) {
82865
83678
  try {
@@ -82869,7 +83682,7 @@ async function handleWorkspaceShow(ctx, what) {
82869
83682
  case "plan": {
82870
83683
  const planPath = path87.join(zelari, "plan.md");
82871
83684
  try {
82872
- content = await fs41.readFile(planPath, "utf-8");
83685
+ content = await fs42.readFile(planPath, "utf-8");
82873
83686
  } catch {
82874
83687
  content = "(no plan.md yet \u2014 run a council session first)";
82875
83688
  }
@@ -82878,7 +83691,7 @@ async function handleWorkspaceShow(ctx, what) {
82878
83691
  case "decisions": {
82879
83692
  const decisionsDir = path87.join(zelari, "decisions");
82880
83693
  try {
82881
- const files = (await fs41.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
83694
+ const files = (await fs42.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
82882
83695
  if (files.length === 0) {
82883
83696
  content = "(no ADRs yet \u2014 invoke /council to generate some)";
82884
83697
  } else {
@@ -82886,7 +83699,7 @@ async function handleWorkspaceShow(ctx, what) {
82886
83699
  `];
82887
83700
  const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
82888
83701
  for (const f of files) {
82889
- const raw = await fs41.readFile(path87.join(decisionsDir, f), "utf-8");
83702
+ const raw = await fs42.readFile(path87.join(decisionsDir, f), "utf-8");
82890
83703
  const { meta: meta3, body } = parseFrontmatter2(raw);
82891
83704
  const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
82892
83705
  lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
@@ -82901,7 +83714,7 @@ async function handleWorkspaceShow(ctx, what) {
82901
83714
  case "risks": {
82902
83715
  const risksPath = path87.join(zelari, "risks.md");
82903
83716
  try {
82904
- content = await fs41.readFile(risksPath, "utf-8");
83717
+ content = await fs42.readFile(risksPath, "utf-8");
82905
83718
  } catch {
82906
83719
  content = "(no risks.md yet)";
82907
83720
  }
@@ -82910,7 +83723,7 @@ async function handleWorkspaceShow(ctx, what) {
82910
83723
  case "agents": {
82911
83724
  const agentsPath = path87.join(process.cwd(), "AGENTS.MD");
82912
83725
  try {
82913
- content = await fs41.readFile(agentsPath, "utf-8");
83726
+ content = await fs42.readFile(agentsPath, "utf-8");
82914
83727
  } catch {
82915
83728
  content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
82916
83729
  }
@@ -82919,7 +83732,7 @@ async function handleWorkspaceShow(ctx, what) {
82919
83732
  case "docs": {
82920
83733
  const docsDir = path87.join(zelari, "docs");
82921
83734
  try {
82922
- const files = (await fs41.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
83735
+ const files = (await fs42.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
82923
83736
  content = files.length ? `# Docs (${files.length})
82924
83737
 
82925
83738
  ` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
@@ -82960,7 +83773,7 @@ async function handleWorkspaceReset(ctx, force) {
82960
83773
  }
82961
83774
  try {
82962
83775
  const target = path87.join(process.cwd(), ".zelari");
82963
- await fs41.rm(target, { recursive: true, force: true });
83776
+ await fs42.rm(target, { recursive: true, force: true });
82964
83777
  appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
82965
83778
  } catch (err) {
82966
83779
  appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
@@ -82971,13 +83784,13 @@ async function handleWorkspaceReset(ctx, force) {
82971
83784
  init_provider2();
82972
83785
 
82973
83786
  // src/cli/skillHistory.ts
82974
- import { promises as fs42, existsSync as existsSync56, statSync as statSync8, renameSync as renameSync6, appendFileSync as appendFileSync5, mkdirSync as mkdirSync22 } from "node:fs";
83787
+ import { promises as fs43, existsSync as existsSync56, statSync as statSync8, renameSync as renameSync6, appendFileSync as appendFileSync5, mkdirSync as mkdirSync22 } from "node:fs";
82975
83788
  init_paths();
82976
83789
  var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
82977
83790
  async function readSkillHistory(file2) {
82978
83791
  let raw = "";
82979
83792
  try {
82980
- raw = await fs42.readFile(file2, "utf-8");
83793
+ raw = await fs43.readFile(file2, "utf-8");
82981
83794
  } catch {
82982
83795
  return [];
82983
83796
  }