opencode-swarm 7.122.0 → 7.123.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 (74) hide show
  1. package/.opencode/skills/issue-tracer/SKILL.md +341 -0
  2. package/.opencode/skills/issue-tracer/assets/pr-template.md +59 -0
  3. package/.opencode/skills/issue-tracer/references/critic-gate.md +245 -0
  4. package/.opencode/skills/issue-tracer/references/evidence-artifacts.md +267 -0
  5. package/.opencode/skills/issue-tracer/references/install.md +63 -0
  6. package/.opencode/skills/issue-tracer/references/localization-playbook.md +95 -0
  7. package/.opencode/skills/issue-tracer/references/method-provenance.md +13 -0
  8. package/.opencode/skills/issue-tracer/references/untrusted-content.md +22 -0
  9. package/.opencode/skills/issue-tracer/scripts/scan-deferred.sh +103 -0
  10. package/.opencode/skills/issue-tracer/scripts/trace-init.sh +119 -0
  11. package/dist/cli/{config-doctor-404c8mf8.js → config-doctor-3ksh1534.js} +2 -2
  12. package/dist/cli/{core-xnwasfpc.js → core-cwgb7ev8.js} +1 -1
  13. package/dist/cli/{curation-policy-cj1tpzr5.js → curation-policy-4j4qce5a.js} +5 -5
  14. package/dist/cli/{curator-llm-factory-8rg0f77t.js → curator-llm-factory-mfvd4sqx.js} +22 -20
  15. package/dist/cli/{curator-3rze3pd4.js → curator-tkyn9bzz.js} +22 -20
  16. package/dist/cli/{evidence-summary-service-k14ykm03.js → evidence-summary-service-96ndczbk.js} +7 -5
  17. package/dist/cli/{gate-evidence-tbpezk28.js → gate-evidence-xqks3kk2.js} +3 -3
  18. package/dist/cli/{guardrail-explain-hdsxa2c5.js → guardrail-explain-4vytbchb.js} +23 -21
  19. package/dist/cli/{guardrail-log-dhpp0kmn.js → guardrail-log-z291m4gw.js} +3 -3
  20. package/dist/cli/{hive-promoter-dh38qd9m.js → hive-promoter-y5wsztkc.js} +22 -20
  21. package/dist/cli/{index-zvae4m8g.js → index-17eybc3z.js} +1 -1
  22. package/dist/cli/{index-kyvg2cmp.js → index-19zn67q6.js} +1 -1
  23. package/dist/cli/{index-rbfq77sz.js → index-2j25br0p.js} +12 -10
  24. package/dist/cli/{index-9xpgytj9.js → index-3rr0ese8.js} +1 -1
  25. package/dist/cli/{index-68aggz0v.js → index-6sgqnvpw.js} +2 -2
  26. package/dist/cli/{index-qg11yf8q.js → index-6xnxz6z0.js} +1 -1
  27. package/dist/cli/{index-emkb0bbe.js → index-7cspygmb.js} +277 -673
  28. package/dist/cli/{index-jzpv74mq.js → index-7g91saha.js} +24 -22
  29. package/dist/cli/{index-3r1w7xky.js → index-cqsak7bf.js} +2 -2
  30. package/dist/cli/index-ctpgsj84.js +547 -0
  31. package/dist/cli/index-d6h5hq91.js +564 -0
  32. package/dist/cli/{index-vqcgmy4y.js → index-det5jwfk.js} +1 -1
  33. package/dist/cli/{index-3sx27bxt.js → index-dywekeen.js} +3 -3
  34. package/dist/cli/{index-wtn8wf7h.js → index-gw3m5fw0.js} +1 -1
  35. package/dist/cli/{index-rkd6qwjh.js → index-hns7xjm7.js} +2 -2
  36. package/dist/cli/{index-xdsj4qtd.js → index-j9kxwshw.js} +5 -5
  37. package/dist/cli/{index-ahxrne50.js → index-jyrhy4ww.js} +161 -58
  38. package/dist/cli/{index-j33zb3cx.js → index-m0bmh3w0.js} +4 -4
  39. package/dist/cli/index-m1abzg93.js +68 -0
  40. package/dist/cli/{index-ny13nr0x.js → index-n2712byd.js} +6 -6
  41. package/dist/cli/{index-gr7ssnj9.js → index-q00e4s1s.js} +5 -4
  42. package/dist/cli/{index-3jvqw5hy.js → index-q27bajqb.js} +1 -1
  43. package/dist/cli/{index-qt2yaghg.js → index-vcgfqd8f.js} +1 -63
  44. package/dist/cli/{index-tftv9xh9.js → index-wr8qgpz5.js} +1 -1
  45. package/dist/cli/index.js +22 -20
  46. package/dist/cli/{knowledge-escalator-sya06p12.js → knowledge-escalator-dyxsm78k.js} +6 -6
  47. package/dist/cli/{knowledge-events-xzps61a9.js → knowledge-events-36q6nvae.js} +4 -4
  48. package/dist/cli/{knowledge-link-sy85fw3w.js → knowledge-link-4yd2z8ev.js} +3 -3
  49. package/dist/cli/{knowledge-store-qg66a93n.js → knowledge-store-e07b4s54.js} +4 -4
  50. package/dist/cli/{knowledge-validator-gar6wv4f.js → knowledge-validator-sfyf7wn2.js} +7 -7
  51. package/dist/cli/pr-subscriptions-x653151h.js +1 -1
  52. package/dist/cli/{scan-cursor-nx5y5k7r.js → scan-cursor-hm0aqqae.js} +5 -5
  53. package/dist/cli/{schema-vwxpsk6j.js → schema-13bnng2n.js} +1 -1
  54. package/dist/cli/scope-persistence-d9erwnvb.js +36 -0
  55. package/dist/cli/{skill-generator-nt5qxafq.js → skill-generator-63r20a9k.js} +8 -8
  56. package/dist/config/bundled-skills.d.ts +1 -1
  57. package/dist/config/constants.d.ts +3 -2
  58. package/dist/hooks/delegation-gate.d.ts +19 -1
  59. package/dist/hooks/guardrails/nontransient-circuit.d.ts +47 -0
  60. package/dist/hooks/scope-guard.d.ts +9 -2
  61. package/dist/hooks/write-target-resolver.d.ts +30 -0
  62. package/dist/index.js +406 -370
  63. package/dist/sandbox/win32/restricted-environment-executor.d.ts +3 -2
  64. package/dist/scope/scope-binding.d.ts +89 -0
  65. package/dist/scope/scope-persistence.d.ts +53 -12
  66. package/dist/state.d.ts +25 -1
  67. package/dist/tools/declare-scope.d.ts +5 -1
  68. package/dist/tools/file-extractor-planner.d.ts +31 -0
  69. package/dist/tools/file-extractor.d.ts +2 -7
  70. package/package.json +2 -1
  71. package/dist/cli/index-51gg9947.js +0 -254
  72. package/dist/cli/scope-persistence-mfr36yc9.js +0 -20
  73. package/dist/cli/{index-h07wn84h.js → index-7mkpsw6g.js} +3 -3
  74. package/dist/cli/{pending-delegations-0z3znsec.js → pending-delegations-68cbaqv2.js} +1 -1
@@ -5,11 +5,8 @@ import {
5
5
  import {
6
6
  EVIDENCE_MAX_JSON_BYTES,
7
7
  EvidenceBundleSchema,
8
- EvidenceSchema,
9
- ExecutionProfileSchema,
10
- PlanSchema,
11
- TaskStatusSchema
12
- } from "./index-qt2yaghg.js";
8
+ EvidenceSchema
9
+ } from "./index-vcgfqd8f.js";
13
10
  import {
14
11
  _internals,
15
12
  buildDefaultPolicy,
@@ -17,6 +14,25 @@ import {
17
14
  init_runner_client,
18
15
  probe
19
16
  } from "./index-q1exe2b3.js";
17
+ import {
18
+ LedgerStaleWriterError,
19
+ appendLedgerEvent,
20
+ computeCurrentPlanHash,
21
+ computePlanHash,
22
+ derivePlanId,
23
+ getLatestLedgerSeq,
24
+ initLedger,
25
+ ledgerExists,
26
+ loadLastApprovedPlan,
27
+ readLedgerEvents,
28
+ replayFromLedger,
29
+ replayFromLedgerWithStatus,
30
+ takeSnapshotEvent,
31
+ takeSnapshotWithRetry
32
+ } from "./index-ctpgsj84.js";
33
+ import {
34
+ PlanSchema
35
+ } from "./index-m1abzg93.js";
20
36
  import {
21
37
  readSwarmFileAsync,
22
38
  validateSwarmPath
@@ -25,7 +41,6 @@ import {
25
41
  criticalWarn,
26
42
  error,
27
43
  init_logger,
28
- log,
29
44
  warn
30
45
  } from "./index-zgwm4ryv.js";
31
46
  import {
@@ -33,16 +48,16 @@ import {
33
48
  readTaskEvidence,
34
49
  readTaskEvidenceRaw,
35
50
  sanitizeTaskId
36
- } from "./index-qg11yf8q.js";
51
+ } from "./index-6xnxz6z0.js";
37
52
  import {
38
53
  atomicWriteFile
39
- } from "./index-h07wn84h.js";
40
- import {
41
- withEvidenceLock
42
- } from "./index-76kvm9z5.js";
54
+ } from "./index-7mkpsw6g.js";
43
55
  import {
44
56
  readCachedParsedFile
45
57
  } from "./index-fsrp8wp3.js";
58
+ import {
59
+ withEvidenceLock
60
+ } from "./index-76kvm9z5.js";
46
61
  import {
47
62
  emit
48
63
  } from "./index-q2rrv0cy.js";
@@ -442,12 +457,33 @@ function detectPowerShellEscape(command) {
442
457
  var init_edge_cases = () => {};
443
458
 
444
459
  // src/sandbox/win32/restricted-environment-executor.ts
460
+ import { Buffer } from "buffer";
445
461
  import { spawnSync as spawnSync3 } from "child_process";
446
462
  import * as os2 from "os";
447
463
  import * as path2 from "path";
464
+ function containsUnsafePathSyntax(value) {
465
+ return [...value].some((character) => character.charCodeAt(0) < 32 || UNSAFE_PATH_CHARACTERS.has(character));
466
+ }
467
+ function getSafeSystemRoot() {
468
+ const raw = process.env.SystemRoot;
469
+ if (typeof raw !== "string" || !/^[A-Za-z]:[\\/]/.test(raw) || containsUnsafePathSyntax(raw.slice(2)) || raw.slice(3).split(/[\\/]+/).some((segment) => segment === "." || segment === "..")) {
470
+ return DEFAULT_SYSTEM_ROOT;
471
+ }
472
+ const normalized = path2.win32.normalize(raw);
473
+ return normalized.length > 3 ? normalized.replace(/\\+$/, "") : normalized;
474
+ }
475
+ function getWindowsExecutablePaths() {
476
+ const systemRoot = getSafeSystemRoot();
477
+ return {
478
+ systemRoot,
479
+ cmdExe: path2.win32.join(systemRoot, "System32", "cmd.exe"),
480
+ powerShellExe: path2.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
481
+ };
482
+ }
448
483
  function probeWindowsSandbox() {
449
484
  try {
450
- const result = spawnSync3("cmd", ["/c", "echo", "ok"], {
485
+ const result = spawnSync3(getWindowsExecutablePaths().cmdExe, ["/d", "/s", "/c", "echo ok"], {
486
+ cwd: os2.tmpdir(),
451
487
  windowsHide: true,
452
488
  encoding: "utf-8",
453
489
  timeout: 5000,
@@ -469,14 +505,37 @@ function probeWindowsSandbox() {
469
505
  return false;
470
506
  }
471
507
  }
472
- function psStringEscape(s) {
473
- return s.replace(/[`"$]/g, "`$&");
508
+ function psSingleQuoteEscape(s) {
509
+ return s.replace(/'/g, "''");
510
+ }
511
+ function normalizeInheritedPathEntry(rawEntry) {
512
+ const entry = rawEntry.trim();
513
+ if (!/^[A-Za-z]:[\\/]/.test(entry) || containsUnsafePathSyntax(entry.slice(2)) || entry.slice(3).split(/[\\/]+/).some((segment) => segment === "." || segment === "..")) {
514
+ return null;
515
+ }
516
+ const normalized = path2.win32.normalize(entry);
517
+ return normalized.length > 3 ? normalized.replace(/\\+$/, "") : normalized;
474
518
  }
475
519
  function getSafeWindowsPath() {
476
- const raw = process.env.SystemRoot;
477
- const isValid = typeof raw === "string" && raw.length > 0 && /^[A-Za-z]:[\\/](?:[^\\/;"'<>|*?\r\n]+[\\/]?)*$/.test(raw);
478
- const sysRoot = isValid ? raw : "C:\\Windows";
479
- return `${sysRoot}\\System32;${sysRoot}`;
520
+ const { systemRoot } = getWindowsExecutablePaths();
521
+ const candidates = [
522
+ path2.win32.join(systemRoot, "System32"),
523
+ systemRoot,
524
+ ...(process.env.PATH ?? "").split(";")
525
+ ];
526
+ const seen = new Set;
527
+ const safeEntries = [];
528
+ for (const candidate of candidates) {
529
+ const normalized = normalizeInheritedPathEntry(candidate);
530
+ if (!normalized)
531
+ continue;
532
+ const identity = normalized.toLowerCase();
533
+ if (seen.has(identity))
534
+ continue;
535
+ seen.add(identity);
536
+ safeEntries.push(normalized);
537
+ }
538
+ return safeEntries.join(";");
480
539
  }
481
540
  function isPowerShellNativeCommand(command) {
482
541
  return /^(?:Remove-Item|rm|del|erase|Copy-Item|cp|copy|Move-Item|mv|move|Rename-Item|ren|New-Item|ni|Get-Item|gi|Get-ChildItem|ls|dir|gci|Get-Content|cat|type|gc|Set-Content|sc|Add-Content|ac|Clear-Content|clc|Test-Path|Resolve-Path|Split-Path|Join-Path|Out-File|Get-Date)\b/i.test(command.trimStart());
@@ -557,8 +616,11 @@ class WindowsSandboxExecutor {
557
616
  throw new SandboxError("PowerShell-native command body contains unsafe characters", "UNSAFE_PS_COMMAND");
558
617
  }
559
618
  const safePath = getSafeWindowsPath();
560
- const escapedTemp = psStringEscape(temp);
561
- const escapedCommand = psStringEscape(command);
619
+ const { cmdExe, powerShellExe } = getWindowsExecutablePaths();
620
+ const commandBase64 = Buffer.from(command, "utf8").toString("base64");
621
+ const escapedTemp = psSingleQuoteEscape(temp);
622
+ const escapedPath = psSingleQuoteEscape(safePath);
623
+ const escapedCmdExe = psSingleQuoteEscape(cmdExe);
562
624
  const envOverrideLines = [];
563
625
  if (envOverrides) {
564
626
  for (const [key, value] of Object.entries(envOverrides)) {
@@ -568,27 +630,50 @@ class WindowsSandboxExecutor {
568
630
  if (value === null) {
569
631
  envOverrideLines.push(`Remove-Item Env:${key} -Force -ErrorAction SilentlyContinue;`);
570
632
  } else {
571
- const psEscaped = value.replace(/'/g, "''");
633
+ const psEscaped = psSingleQuoteEscape(value);
572
634
  envOverrideLines.push(`$env:${key} = '${psEscaped}';`);
573
635
  }
574
636
  }
575
637
  }
576
- const commandExec = isPowerShellNativeCommand(command) ? `Invoke-Expression "${escapedCommand}"` : `cmd /c "${escapedCommand}"`;
638
+ const commandExec = isPowerShellNativeCommand(command) ? `Invoke-Expression $command;
639
+ exit 0;` : `$batchPath = $null;
640
+ $batchLocationPushed = $false;
641
+ try {
642
+ if ($command.Contains([char]13) -or $command.Contains([char]10)) {
643
+ $batchName = 'opencode-swarm-' + [Guid]::NewGuid().ToString('N') + '.cmd';
644
+ $batchPath = Join-Path $env:TEMP $batchName;
645
+ [IO.File]::WriteAllText($batchPath, $command, [Text.UTF8Encoding]::new($false));
646
+ Push-Location -LiteralPath $env:TEMP;
647
+ $batchLocationPushed = $true;
648
+ & '${escapedCmdExe}' /d /v:off /s /c ('call "' + $batchName + '"');
649
+ } else {
650
+ & '${escapedCmdExe}' /d /v:off /s /c $command;
651
+ }
652
+ $childExitCode = $LASTEXITCODE;
653
+ } finally {
654
+ if ($batchLocationPushed) {
655
+ Pop-Location;
656
+ }
657
+ if ($null -ne $batchPath) {
658
+ Remove-Item -LiteralPath $batchPath -Force -ErrorAction SilentlyContinue;
659
+ }
660
+ }
661
+ if ($null -eq $childExitCode) {
662
+ throw 'cmd.exe did not report an exit code';
663
+ }
664
+ exit [int]$childExitCode;`;
577
665
  const envOverrideBlock = envOverrideLines.length > 0 ? `
578
666
  ${envOverrideLines.join(`
579
667
  `)}` : "";
580
668
  const psScript = `
581
669
  $ErrorActionPreference = 'Stop';
670
+ $ProgressPreference = 'SilentlyContinue';
582
671
  try {
583
- # Set scoped temp directory
584
672
  $env:TEMP = '${escapedTemp}';
585
673
  $env:TMP = '${escapedTemp}';
586
674
 
587
- # Restrict PATH to safe system paths only
588
- $env:PATH = '${safePath}';
675
+ $env:PATH = '${escapedPath}';
589
676
 
590
- # Remove dangerous environment variables that could be used to bypass restrictions
591
- # Note: LD_PRELOAD, DYLD_* don't apply on Windows but we clear them for completeness
592
677
  $dangerousVars = @(
593
678
  'LD_PRELOAD',
594
679
  'DYLD_INSERT_LIBRARIES',
@@ -605,12 +690,16 @@ try {
605
690
  ${envOverrideBlock}
606
691
 
607
692
  # Execute the command \u2014 PS-native cmdlets via Invoke-Expression, others via the standard command interpreter
693
+ $commandBytes = [Convert]::FromBase64String('${commandBase64}');
694
+ $command = [Text.Encoding]::UTF8.GetString($commandBytes);
608
695
  ${commandExec};
609
696
  } catch {
610
- Write-Error $_.Exception.Message;
697
+ [Console]::Error.WriteLine($_.Exception.Message);
611
698
  exit 1;
612
699
  }`;
613
- return `powershell -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -Command "${psScript.replace(/\n/g, " ").trim()}"`;
700
+ const encodedScript = Buffer.from(psScript, "utf16le").toString("base64");
701
+ const escapedPowerShellExe = psSingleQuoteEscape(powerShellExe);
702
+ return `& '${escapedPowerShellExe}' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -EncodedCommand ${encodedScript}; exit $LASTEXITCODE`;
614
703
  }
615
704
  getEnvOverrides() {
616
705
  return {
@@ -626,7 +715,7 @@ try {
626
715
  };
627
716
  }
628
717
  }
629
- var SANDBOX_UNAVAILABLE_CODES2, _internals4;
718
+ var SANDBOX_UNAVAILABLE_CODES2, DEFAULT_SYSTEM_ROOT = "C:\\Windows", UNSAFE_PATH_CHARACTERS, _internals4;
630
719
  var init_restricted_environment_executor = __esm(() => {
631
720
  init_logger();
632
721
  init_executor();
@@ -637,6 +726,23 @@ var init_restricted_environment_executor = __esm(() => {
637
726
  "EPERM",
638
727
  "ENOSPC"
639
728
  ]);
729
+ UNSAFE_PATH_CHARACTERS = new Set([
730
+ ":",
731
+ '"',
732
+ "'",
733
+ ";",
734
+ "&",
735
+ "|",
736
+ "<",
737
+ ">",
738
+ "^",
739
+ "%",
740
+ "!",
741
+ "?",
742
+ "*",
743
+ "`",
744
+ "$"
745
+ ]);
640
746
  _internals4 = {
641
747
  probeWindowsSandbox
642
748
  };
@@ -857,19 +963,19 @@ var init_executor = __esm(() => {
857
963
 
858
964
  // src/plan/manager.ts
859
965
  import {
860
- closeSync as closeSync2,
966
+ closeSync,
861
967
  copyFileSync,
862
- existsSync as existsSync5,
863
- fsyncSync as fsyncSync2,
864
- mkdirSync as mkdirSync5,
865
- openSync as openSync2,
866
- readdirSync as readdirSync2,
867
- renameSync as renameSync4,
968
+ existsSync as existsSync4,
969
+ fsyncSync,
970
+ mkdirSync as mkdirSync4,
971
+ openSync,
972
+ readdirSync,
973
+ renameSync as renameSync3,
868
974
  unlinkSync as unlinkSync3,
869
- writeFileSync as writeFileSync6
975
+ writeFileSync as writeFileSync5
870
976
  } from "fs";
871
977
  import * as fsPromises from "fs/promises";
872
- import * as path9 from "path";
978
+ import * as path8 from "path";
873
979
 
874
980
  // src/git/branch.ts
875
981
  init_executor();
@@ -2072,508 +2178,6 @@ function computeLcsDiff(oldLines, newLines) {
2072
2178
  });
2073
2179
  }
2074
2180
 
2075
- // src/plan/ledger.ts
2076
- import * as crypto2 from "crypto";
2077
- import * as fs5 from "fs";
2078
- import * as path8 from "path";
2079
- init_logger();
2080
-
2081
- // src/plan/utils.ts
2082
- function derivePlanId(plan) {
2083
- return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
2084
- }
2085
-
2086
- // src/plan/ledger.ts
2087
- var LEDGER_SCHEMA_VERSION = "1.1.0";
2088
- var LEDGER_FILENAME = "plan-ledger.jsonl";
2089
- var LEDGER_LOCK_PATH = path8.join(".swarm", LEDGER_FILENAME);
2090
- var PLAN_JSON_FILENAME = "plan.json";
2091
-
2092
- class LedgerStaleWriterError extends Error {
2093
- constructor(message) {
2094
- super(message);
2095
- this.name = "LedgerStaleWriterError";
2096
- }
2097
- }
2098
- function getLedgerPath(directory) {
2099
- return path8.join(directory, ".swarm", LEDGER_FILENAME);
2100
- }
2101
- function getPlanJsonPath(directory) {
2102
- return path8.join(directory, ".swarm", PLAN_JSON_FILENAME);
2103
- }
2104
- function writeFileFsyncedThenRename(tempPath, targetPath, data) {
2105
- const fd = fs5.openSync(tempPath, "w");
2106
- try {
2107
- fs5.writeFileSync(fd, data, "utf8");
2108
- fs5.fsyncSync(fd);
2109
- } finally {
2110
- fs5.closeSync(fd);
2111
- }
2112
- fs5.renameSync(tempPath, targetPath);
2113
- }
2114
- function computePlanHash(plan) {
2115
- const normalized = {
2116
- schema_version: plan.schema_version,
2117
- title: plan.title,
2118
- swarm: plan.swarm,
2119
- current_phase: plan.current_phase,
2120
- migration_status: plan.migration_status,
2121
- execution_profile: plan.execution_profile,
2122
- phases: plan.phases.map((phase) => ({
2123
- id: phase.id,
2124
- name: phase.name,
2125
- status: phase.status,
2126
- required_agents: phase.required_agents ? [...phase.required_agents].sort() : undefined,
2127
- tasks: phase.tasks.map((task) => ({
2128
- id: task.id,
2129
- phase: task.phase,
2130
- status: task.status,
2131
- size: task.size,
2132
- description: task.description,
2133
- depends: [...task.depends].sort(),
2134
- acceptance: task.acceptance,
2135
- files_touched: [...task.files_touched].sort(),
2136
- evidence_path: task.evidence_path,
2137
- blocked_reason: task.blocked_reason
2138
- }))
2139
- }))
2140
- };
2141
- const jsonString = JSON.stringify(normalized);
2142
- return crypto2.createHash("sha256").update(jsonString, "utf8").digest("hex");
2143
- }
2144
- function computeCurrentPlanHash(directory) {
2145
- const planPath = getPlanJsonPath(directory);
2146
- try {
2147
- const content = fs5.readFileSync(planPath, "utf8");
2148
- const plan = JSON.parse(content);
2149
- return computePlanHash(plan);
2150
- } catch {
2151
- return "";
2152
- }
2153
- }
2154
- async function ledgerExists(directory) {
2155
- const ledgerPath = getLedgerPath(directory);
2156
- return fs5.existsSync(ledgerPath);
2157
- }
2158
- async function getLatestLedgerSeq(directory) {
2159
- const ledgerPath = getLedgerPath(directory);
2160
- if (!fs5.existsSync(ledgerPath)) {
2161
- return 0;
2162
- }
2163
- try {
2164
- const content = fs5.readFileSync(ledgerPath, "utf8");
2165
- const lines = content.trim().split(`
2166
- `).filter((line) => line.trim() !== "");
2167
- if (lines.length === 0) {
2168
- return 0;
2169
- }
2170
- let maxSeq = 0;
2171
- for (const line of lines) {
2172
- try {
2173
- const event = JSON.parse(line);
2174
- if (event.seq > maxSeq) {
2175
- maxSeq = event.seq;
2176
- }
2177
- } catch {}
2178
- }
2179
- return maxSeq;
2180
- } catch {
2181
- return 0;
2182
- }
2183
- }
2184
- async function readLedgerEvents(directory) {
2185
- const ledgerPath = getLedgerPath(directory);
2186
- if (!fs5.existsSync(ledgerPath)) {
2187
- return [];
2188
- }
2189
- try {
2190
- const content = fs5.readFileSync(ledgerPath, "utf8");
2191
- const lines = content.trim().split(`
2192
- `).filter((line) => line.trim() !== "");
2193
- const events = [];
2194
- let skippedCount = 0;
2195
- for (const line of lines) {
2196
- try {
2197
- const event = JSON.parse(line);
2198
- events.push(event);
2199
- } catch {
2200
- skippedCount++;
2201
- }
2202
- }
2203
- if (skippedCount > 0) {
2204
- log(`[ledger] Skipped ${skippedCount} malformed line(s) in plan-ledger.jsonl`);
2205
- }
2206
- events.sort((a, b) => a.seq - b.seq);
2207
- return events;
2208
- } catch {
2209
- return [];
2210
- }
2211
- }
2212
- async function initLedger(directory, planId, initialPlanHash, initialPlan) {
2213
- const ledgerPath = getLedgerPath(directory);
2214
- const planJsonPath = getPlanJsonPath(directory);
2215
- if (fs5.existsSync(ledgerPath)) {
2216
- throw new Error("Ledger already initialized. Use appendLedgerEvent to add events.");
2217
- }
2218
- let planHashAfter = initialPlanHash ?? "";
2219
- let embeddedPlan = initialPlan;
2220
- if (!initialPlanHash) {
2221
- try {
2222
- if (fs5.existsSync(planJsonPath)) {
2223
- const content = fs5.readFileSync(planJsonPath, "utf8");
2224
- const plan = JSON.parse(content);
2225
- planHashAfter = computePlanHash(plan);
2226
- if (!embeddedPlan)
2227
- embeddedPlan = plan;
2228
- }
2229
- } catch {}
2230
- }
2231
- const payload = embeddedPlan ? { plan: embeddedPlan, payload_hash: planHashAfter } : undefined;
2232
- const event = {
2233
- seq: 1,
2234
- timestamp: new Date().toISOString(),
2235
- plan_id: planId,
2236
- event_type: "plan_created",
2237
- source: "initLedger",
2238
- plan_hash_before: "",
2239
- plan_hash_after: planHashAfter,
2240
- schema_version: LEDGER_SCHEMA_VERSION,
2241
- ...payload ? { payload } : {}
2242
- };
2243
- fs5.mkdirSync(path8.join(directory, ".swarm"), { recursive: true });
2244
- const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
2245
- const line = `${JSON.stringify(event)}
2246
- `;
2247
- writeFileFsyncedThenRename(tempPath, ledgerPath, line);
2248
- }
2249
- async function appendLedgerEvent(directory, eventInput, options) {
2250
- return withEvidenceLock(directory, LEDGER_LOCK_PATH, "plan-ledger", "append-ledger-event", async () => {
2251
- const ledgerPath = getLedgerPath(directory);
2252
- const latestSeq = await getLatestLedgerSeq(directory);
2253
- const nextSeq = latestSeq + 1;
2254
- const planHashBefore = computeCurrentPlanHash(directory);
2255
- if (options?.expectedSeq !== undefined && options.expectedSeq !== latestSeq) {
2256
- throw new LedgerStaleWriterError(`Stale writer: expected seq ${options.expectedSeq} but found ${latestSeq}`);
2257
- }
2258
- if (options?.expectedHash !== undefined && options.expectedHash !== planHashBefore) {
2259
- throw new LedgerStaleWriterError(`Stale writer: expected hash ${options.expectedHash} but found ${planHashBefore}`);
2260
- }
2261
- const planHashAfter = options?.planHashAfter ?? planHashBefore;
2262
- const event = {
2263
- ...eventInput,
2264
- seq: nextSeq,
2265
- timestamp: new Date().toISOString(),
2266
- plan_hash_before: planHashBefore,
2267
- plan_hash_after: planHashAfter,
2268
- schema_version: LEDGER_SCHEMA_VERSION
2269
- };
2270
- fs5.mkdirSync(path8.join(directory, ".swarm"), { recursive: true });
2271
- const tempPath = `${ledgerPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
2272
- const line = `${JSON.stringify(event)}
2273
- `;
2274
- if (!fs5.existsSync(ledgerPath)) {
2275
- throw new Error("Ledger not initialized. Call initLedger() first.");
2276
- }
2277
- const existingContent = fs5.readFileSync(ledgerPath, "utf8");
2278
- writeFileFsyncedThenRename(tempPath, ledgerPath, existingContent + line);
2279
- return event;
2280
- });
2281
- }
2282
- async function takeSnapshotWithRetry(directory, plan, options) {
2283
- const MAX_RETRIES = 3;
2284
- const TOTAL_ATTEMPTS = 1 + MAX_RETRIES;
2285
- const telemetrySource = options?.source ?? "save_plan_tool";
2286
- const snapshotOptions = { planHashAfter: options?.planHashAfter };
2287
- let lastError;
2288
- for (let attempt = 1;attempt <= TOTAL_ATTEMPTS; attempt++) {
2289
- try {
2290
- await takeSnapshotEvent(directory, plan, snapshotOptions);
2291
- return;
2292
- } catch (err) {
2293
- lastError = err instanceof Error ? err : new Error(String(err));
2294
- if (attempt < TOTAL_ATTEMPTS) {
2295
- await new Promise((r) => setTimeout(r, 10 * 2 ** (attempt - 1)));
2296
- }
2297
- }
2298
- }
2299
- criticalWarn(`[takeSnapshotWithRetry] Snapshot failed after ${MAX_RETRIES} retries (${TOTAL_ATTEMPTS} attempts): ${lastError.message}`);
2300
- try {
2301
- emit("snapshot_failed", {
2302
- error: lastError.message,
2303
- retries: MAX_RETRIES,
2304
- source: telemetrySource
2305
- });
2306
- } catch {}
2307
- }
2308
- async function takeSnapshotEvent(directory, plan, options) {
2309
- const payloadHash = options?.payloadHashOverride ?? computePlanHash(plan);
2310
- const snapshotPayload = {
2311
- plan,
2312
- payload_hash: payloadHash
2313
- };
2314
- if (options?.approvalMetadata) {
2315
- snapshotPayload.approval = options.approvalMetadata;
2316
- }
2317
- const planId = derivePlanId(plan);
2318
- return appendLedgerEvent(directory, {
2319
- event_type: "snapshot",
2320
- source: options?.source ?? "takeSnapshotEvent",
2321
- plan_id: planId,
2322
- payload: snapshotPayload
2323
- }, { planHashAfter: options?.planHashAfter });
2324
- }
2325
- async function replayFromLedger(directory, options) {
2326
- const { plan } = await replayFromLedgerWithStatus(directory, options);
2327
- return plan;
2328
- }
2329
- async function replayFromLedgerWithStatus(directory, _options) {
2330
- const { events, truncated, badSuffix } = await readLedgerEventsWithIntegrity(directory);
2331
- if (events.length === 0) {
2332
- return { plan: null, truncated, badSuffix };
2333
- }
2334
- if (truncated && badSuffix !== null) {
2335
- await quarantineLedgerSuffix(directory, badSuffix);
2336
- }
2337
- const plan = reconstructPlanFromEvents(directory, events);
2338
- return { plan, truncated, badSuffix };
2339
- }
2340
- function reconstructPlanFromEvents(directory, events) {
2341
- const targetPlanId = events[0].plan_id;
2342
- const relevantEvents = events.filter((e) => e.plan_id === targetPlanId);
2343
- {
2344
- const snapshotEvents = relevantEvents.filter((e) => e.event_type === "snapshot");
2345
- if (snapshotEvents.length > 0) {
2346
- const latestSnapshotEvent = snapshotEvents[snapshotEvents.length - 1];
2347
- const snapshotPayload = latestSnapshotEvent.payload;
2348
- let plan2 = snapshotPayload.plan;
2349
- const eventsAfterSnapshot = relevantEvents.filter((e) => e.seq > latestSnapshotEvent.seq);
2350
- for (const event of eventsAfterSnapshot) {
2351
- plan2 = applyEventToPlan(plan2, event);
2352
- if (plan2 === null) {
2353
- return null;
2354
- }
2355
- }
2356
- return plan2;
2357
- }
2358
- }
2359
- const createdEvent = relevantEvents.find((e) => e.event_type === "plan_created");
2360
- if (createdEvent?.payload && typeof createdEvent.payload === "object" && "plan" in createdEvent.payload) {
2361
- const parseResult = PlanSchema.safeParse(createdEvent.payload.plan);
2362
- if (parseResult.success) {
2363
- let plan2 = parseResult.data;
2364
- const eventsAfterCreated = relevantEvents.filter((e) => e.seq > createdEvent.seq);
2365
- for (const event of eventsAfterCreated) {
2366
- if (plan2 === null)
2367
- return null;
2368
- plan2 = applyEventToPlan(plan2, event);
2369
- }
2370
- return plan2;
2371
- }
2372
- }
2373
- const planJsonPath = getPlanJsonPath(directory);
2374
- if (!fs5.existsSync(planJsonPath)) {
2375
- return null;
2376
- }
2377
- let plan;
2378
- try {
2379
- const content = fs5.readFileSync(planJsonPath, "utf8");
2380
- plan = JSON.parse(content);
2381
- } catch {
2382
- return null;
2383
- }
2384
- for (const event of relevantEvents) {
2385
- if (plan === null) {
2386
- return null;
2387
- }
2388
- plan = applyEventToPlan(plan, event);
2389
- }
2390
- return plan;
2391
- }
2392
- function applyEventToPlan(plan, event) {
2393
- switch (event.event_type) {
2394
- case "plan_created":
2395
- if (event.payload && typeof event.payload === "object" && "plan" in event.payload) {
2396
- const parsed = PlanSchema.safeParse(event.payload.plan);
2397
- if (parsed.success)
2398
- return parsed.data;
2399
- }
2400
- return plan;
2401
- case "task_status_changed":
2402
- if (event.task_id && event.to_status) {
2403
- const parseResult = TaskStatusSchema.safeParse(event.to_status);
2404
- if (!parseResult.success) {
2405
- return plan;
2406
- }
2407
- for (const phase of plan.phases) {
2408
- const task = phase.tasks.find((t) => t.id === event.task_id);
2409
- if (task) {
2410
- task.status = parseResult.data;
2411
- break;
2412
- }
2413
- }
2414
- }
2415
- return plan;
2416
- case "phase_completed":
2417
- if (event.phase_id) {
2418
- const phase = plan.phases.find((p) => p.id === event.phase_id);
2419
- if (phase) {
2420
- phase.status = "complete";
2421
- }
2422
- }
2423
- return plan;
2424
- case "plan_exported":
2425
- return plan;
2426
- case "task_added":
2427
- return plan;
2428
- case "task_removed":
2429
- if (event.task_id) {
2430
- for (const phase of plan.phases) {
2431
- const idx = phase.tasks.findIndex((t) => t.id === event.task_id);
2432
- if (idx !== -1) {
2433
- phase.tasks.splice(idx, 1);
2434
- break;
2435
- }
2436
- }
2437
- }
2438
- return plan;
2439
- case "task_updated":
2440
- return plan;
2441
- case "plan_rebuilt":
2442
- return plan;
2443
- case "task_reordered":
2444
- return plan;
2445
- case "snapshot":
2446
- return plan;
2447
- case "plan_reset":
2448
- return null;
2449
- case "execution_profile_set": {
2450
- const rawProfile = event.payload?.execution_profile;
2451
- if (rawProfile !== undefined) {
2452
- const parsed = ExecutionProfileSchema.safeParse(rawProfile);
2453
- if (parsed.success) {
2454
- return { ...plan, execution_profile: parsed.data };
2455
- }
2456
- }
2457
- return plan;
2458
- }
2459
- case "execution_profile_locked": {
2460
- if (plan.execution_profile) {
2461
- return {
2462
- ...plan,
2463
- execution_profile: { ...plan.execution_profile, locked: true }
2464
- };
2465
- }
2466
- return plan;
2467
- }
2468
- default:
2469
- throw new Error(`applyEventToPlan: unhandled event type "${event.event_type}" at seq ${event.seq}`);
2470
- }
2471
- }
2472
- async function readLedgerEventsWithIntegrity(directory) {
2473
- const ledgerPath = getLedgerPath(directory);
2474
- if (!fs5.existsSync(ledgerPath)) {
2475
- return { events: [], truncated: false, badSuffix: null };
2476
- }
2477
- try {
2478
- const content = fs5.readFileSync(ledgerPath, "utf8");
2479
- const lines = content.split(`
2480
- `);
2481
- const events = [];
2482
- let truncated = false;
2483
- let badSuffix = null;
2484
- for (let i = 0;i < lines.length; i++) {
2485
- const line = lines[i];
2486
- if (line.trim() === "") {
2487
- continue;
2488
- }
2489
- try {
2490
- const event = JSON.parse(line);
2491
- events.push(event);
2492
- } catch {
2493
- truncated = true;
2494
- badSuffix = lines.slice(i).join(`
2495
- `);
2496
- break;
2497
- }
2498
- }
2499
- events.sort((a, b) => a.seq - b.seq);
2500
- return { events, truncated, badSuffix };
2501
- } catch {
2502
- return { events: [], truncated: false, badSuffix: null };
2503
- }
2504
- }
2505
- async function quarantineLedgerSuffix(directory, badSuffix) {
2506
- let salvagedCount = 0;
2507
- for (const line of badSuffix.split(`
2508
- `)) {
2509
- if (line.trim() === "")
2510
- continue;
2511
- try {
2512
- JSON.parse(line);
2513
- salvagedCount++;
2514
- } catch {}
2515
- }
2516
- try {
2517
- const hash = crypto2.createHash("sha256").update(badSuffix, "utf8").digest("hex").slice(0, 12);
2518
- const swarmDir = path8.join(directory, ".swarm");
2519
- try {
2520
- const existing = fs5.readdirSync(swarmDir).filter((name) => name.startsWith("plan-ledger.quarantine.") && name.endsWith(`.${hash}`));
2521
- for (const name of existing) {
2522
- const existingPath = path8.join(swarmDir, name);
2523
- if (fs5.readFileSync(existingPath, "utf8") === badSuffix) {
2524
- return { path: existingPath, salvagedCount };
2525
- }
2526
- }
2527
- } catch {}
2528
- const quarantinePath = path8.join(swarmDir, `plan-ledger.quarantine.${Date.now()}.${hash}`);
2529
- fs5.writeFileSync(quarantinePath, badSuffix, "utf8");
2530
- log(`[ledger] Corrupted suffix quarantined to ${path8.relative(directory, quarantinePath)} (salvageable events: ${salvagedCount})`);
2531
- return { path: quarantinePath, salvagedCount };
2532
- } catch {
2533
- return { path: null, salvagedCount };
2534
- }
2535
- }
2536
- async function loadLastApprovedPlan(directory, expectedPlanId) {
2537
- const events = await readLedgerEvents(directory);
2538
- return findLastApprovedSnapshot(events, expectedPlanId);
2539
- }
2540
- function findLastApprovedSnapshot(events, expectedPlanId, extraFilter) {
2541
- if (events.length === 0) {
2542
- return null;
2543
- }
2544
- for (let i = events.length - 1;i >= 0; i--) {
2545
- const event = events[i];
2546
- if (event.event_type !== "snapshot")
2547
- continue;
2548
- if (event.source !== "critic_approved")
2549
- continue;
2550
- if (expectedPlanId !== undefined && event.plan_id !== expectedPlanId) {
2551
- continue;
2552
- }
2553
- const payload = event.payload;
2554
- if (!payload || typeof payload !== "object" || !payload.plan) {
2555
- continue;
2556
- }
2557
- if (expectedPlanId !== undefined) {
2558
- const payloadPlanId = derivePlanId(payload.plan);
2559
- if (payloadPlanId !== expectedPlanId) {
2560
- continue;
2561
- }
2562
- }
2563
- if (extraFilter && !extraFilter(payload)) {
2564
- continue;
2565
- }
2566
- return {
2567
- plan: payload.plan,
2568
- seq: event.seq,
2569
- timestamp: event.timestamp,
2570
- approval: payload.approval,
2571
- payloadHash: payload.payload_hash
2572
- };
2573
- }
2574
- return null;
2575
- }
2576
-
2577
2181
  // src/plan/manager.ts
2578
2182
  class PlanConcurrentModificationError extends Error {
2579
2183
  constructor(message) {
@@ -2735,7 +2339,7 @@ async function getLatestLedgerHash(directory) {
2735
2339
  }
2736
2340
  }
2737
2341
  async function surfaceLedgerStaleIfPersisted(directory, plan) {
2738
- const resolvedWorkspace = path9.resolve(directory);
2342
+ const resolvedWorkspace = path8.resolve(directory);
2739
2343
  if (!ledgerStaleWorkspaces.has(resolvedWorkspace)) {
2740
2344
  return plan;
2741
2345
  }
@@ -2754,7 +2358,7 @@ async function surfaceLedgerStaleIfPersisted(directory, plan) {
2754
2358
  return plan;
2755
2359
  }
2756
2360
  async function parsePlanJsonCached(directory) {
2757
- const planJsonPath = path9.resolve(directory, ".swarm", "plan.json");
2361
+ const planJsonPath = path8.resolve(directory, ".swarm", "plan.json");
2758
2362
  return readCachedParsedFile(planJsonPath, PLAN_JSON_CACHE_NAMESPACE, () => readSwarmFileAsync(directory, "plan.json"), (planJsonContent) => {
2759
2363
  if (planJsonContent.includes("\x00") || planJsonContent.includes("\uFFFD")) {
2760
2364
  throw new Error("Plan rejected: .swarm/plan.json contains null bytes or invalid encoding");
@@ -2814,16 +2418,16 @@ async function isPlanMdInSync(directory, plan, cache) {
2814
2418
  return false;
2815
2419
  }
2816
2420
  async function regeneratePlanMarkdown(directory, plan) {
2817
- const swarmDir = path9.resolve(directory, ".swarm");
2421
+ const swarmDir = path8.resolve(directory, ".swarm");
2818
2422
  const contentHash = computePlanContentHash(plan);
2819
2423
  const markdown = derivePlanMarkdown(plan);
2820
2424
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
2821
2425
  ${markdown}`;
2822
- const mdPath = path9.join(swarmDir, "plan.md");
2823
- const mdTempPath = path9.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2426
+ const mdPath = path8.join(swarmDir, "plan.md");
2427
+ const mdTempPath = path8.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2824
2428
  try {
2825
2429
  await bunWrite(mdTempPath, markdownWithHash);
2826
- renameSync4(mdTempPath, mdPath);
2430
+ renameSync3(mdTempPath, mdPath);
2827
2431
  } finally {
2828
2432
  try {
2829
2433
  unlinkSync3(mdTempPath);
@@ -2852,7 +2456,7 @@ async function loadPlan(directory, cache) {
2852
2456
  if (await ledgerExists(directory)) {
2853
2457
  const planHash = computePlanHash(validated);
2854
2458
  const ledgerHash = await getLatestLedgerHash(directory);
2855
- const resolvedWorkspace = path9.resolve(directory);
2459
+ const resolvedWorkspace = path8.resolve(directory);
2856
2460
  if (!startupLedgerCheckedWorkspaces.has(resolvedWorkspace)) {
2857
2461
  startupLedgerCheckedWorkspaces.add(resolvedWorkspace);
2858
2462
  if (ledgerHash !== "" && planHash !== ledgerHash) {
@@ -2926,7 +2530,7 @@ async function loadPlan(directory, cache) {
2926
2530
  if (!preserving) {
2927
2531
  try {
2928
2532
  const diffInfo = computeSpecDiff(directory);
2929
- const specStalenessPath = path9.join(directory, ".swarm", "spec-staleness.json");
2533
+ const specStalenessPath = path8.join(directory, ".swarm", "spec-staleness.json");
2930
2534
  await fsPromises.writeFile(specStalenessPath, JSON.stringify({
2931
2535
  type: "spec_stale_detected",
2932
2536
  timestamp: new Date().toISOString(),
@@ -2940,7 +2544,7 @@ async function loadPlan(directory, cache) {
2940
2544
  }, null, 2), "utf-8");
2941
2545
  } catch {}
2942
2546
  try {
2943
- const eventsPath = path9.join(directory, ".swarm", "events.jsonl");
2547
+ const eventsPath = path8.join(directory, ".swarm", "events.jsonl");
2944
2548
  const event = {
2945
2549
  type: "spec_stale_detected",
2946
2550
  timestamp: new Date().toISOString(),
@@ -3006,7 +2610,7 @@ async function loadPlan(directory, cache) {
3006
2610
  return migrated;
3007
2611
  }
3008
2612
  if (await ledgerExists(directory)) {
3009
- const resolvedDir = path9.resolve(directory);
2613
+ const resolvedDir = path8.resolve(directory);
3010
2614
  const existingMutex = recoveryMutexes.get(resolvedDir);
3011
2615
  if (existingMutex) {
3012
2616
  await existingMutex;
@@ -3135,13 +2739,13 @@ async function savePlan(directory, plan, options) {
3135
2739
  } else {
3136
2740
  const existingEvents = await readLedgerEvents(directory);
3137
2741
  if (existingEvents.length > 0 && existingEvents[0].plan_id !== planId) {
3138
- const swarmDir2 = path9.resolve(directory, ".swarm");
3139
- const oldLedgerPath = path9.join(swarmDir2, "plan-ledger.jsonl");
3140
- const oldLedgerBackupPath = path9.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
2742
+ const swarmDir2 = path8.resolve(directory, ".swarm");
2743
+ const oldLedgerPath = path8.join(swarmDir2, "plan-ledger.jsonl");
2744
+ const oldLedgerBackupPath = path8.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
3141
2745
  let backupExists = false;
3142
- if (existsSync5(oldLedgerPath)) {
2746
+ if (existsSync4(oldLedgerPath)) {
3143
2747
  try {
3144
- renameSync4(oldLedgerPath, oldLedgerBackupPath);
2748
+ renameSync3(oldLedgerPath, oldLedgerBackupPath);
3145
2749
  backupExists = true;
3146
2750
  } catch (renameErr) {
3147
2751
  throw new Error(`[savePlan] Cannot reinitialize ledger: could not move old ledger aside (rename failed: ${renameErr instanceof Error ? renameErr.message : String(renameErr)}). The existing ledger has plan_id="${existingEvents[0].plan_id}" which does not match the current plan="${planId}". To proceed, close any programs that may have the ledger file open, or run /swarm reset-session to clear the ledger.`);
@@ -3156,13 +2760,13 @@ async function savePlan(directory, plan, options) {
3156
2760
  const errorMessage2 = String(initErr);
3157
2761
  if (errorMessage2.includes("already initialized")) {
3158
2762
  try {
3159
- if (existsSync5(oldLedgerBackupPath))
2763
+ if (existsSync4(oldLedgerBackupPath))
3160
2764
  unlinkSync3(oldLedgerBackupPath);
3161
2765
  } catch {}
3162
2766
  } else {
3163
- if (existsSync5(oldLedgerBackupPath)) {
2767
+ if (existsSync4(oldLedgerBackupPath)) {
3164
2768
  try {
3165
- renameSync4(oldLedgerBackupPath, oldLedgerPath);
2769
+ renameSync3(oldLedgerBackupPath, oldLedgerPath);
3166
2770
  } catch {
3167
2771
  copyFileSync(oldLedgerBackupPath, oldLedgerPath);
3168
2772
  try {
@@ -3175,32 +2779,32 @@ async function savePlan(directory, plan, options) {
3175
2779
  }
3176
2780
  }
3177
2781
  if (initSucceeded && backupExists) {
3178
- const archivePath = path9.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
2782
+ const archivePath = path8.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
3179
2783
  try {
3180
- renameSync4(oldLedgerBackupPath, archivePath);
2784
+ renameSync3(oldLedgerBackupPath, archivePath);
3181
2785
  warn(`[savePlan] Ledger identity mismatch (was "${existingEvents[0].plan_id}", now "${planId}") \u2014 archived old ledger to ${archivePath} and reinitializing.`);
3182
2786
  } catch (renameErr) {
3183
2787
  warn(`[savePlan] Could not archive old ledger (rename failed: ${renameErr instanceof Error ? renameErr.message : String(renameErr)}). Old ledger may still exist at ${oldLedgerBackupPath}.`);
3184
2788
  try {
3185
- if (existsSync5(oldLedgerBackupPath))
2789
+ if (existsSync4(oldLedgerBackupPath))
3186
2790
  unlinkSync3(oldLedgerBackupPath);
3187
2791
  } catch {}
3188
2792
  }
3189
2793
  } else if (!initSucceeded && backupExists) {
3190
2794
  try {
3191
- if (existsSync5(oldLedgerBackupPath))
2795
+ if (existsSync4(oldLedgerBackupPath))
3192
2796
  unlinkSync3(oldLedgerBackupPath);
3193
2797
  } catch {}
3194
2798
  }
3195
2799
  const MAX_ARCHIVED_SIBLINGS = 5;
3196
2800
  try {
3197
- const allFiles = readdirSync2(swarmDir2);
2801
+ const allFiles = readdirSync(swarmDir2);
3198
2802
  const archivedSiblings = allFiles.filter((f) => f.startsWith("plan-ledger.archived-") && f.endsWith(".jsonl")).sort();
3199
2803
  if (archivedSiblings.length > MAX_ARCHIVED_SIBLINGS) {
3200
2804
  const toRemove = archivedSiblings.slice(0, archivedSiblings.length - MAX_ARCHIVED_SIBLINGS);
3201
2805
  for (const file of toRemove) {
3202
2806
  try {
3203
- unlinkSync3(path9.join(swarmDir2, file));
2807
+ unlinkSync3(path8.join(swarmDir2, file));
3204
2808
  } catch {}
3205
2809
  }
3206
2810
  }
@@ -3343,19 +2947,19 @@ async function savePlan(directory, plan, options) {
3343
2947
  source: "savePlan_manager"
3344
2948
  });
3345
2949
  }
3346
- const swarmDir = path9.resolve(directory, ".swarm");
3347
- const planPath = path9.join(swarmDir, "plan.json");
3348
- const tempPath = path9.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2950
+ const swarmDir = path8.resolve(directory, ".swarm");
2951
+ const planPath = path8.join(swarmDir, "plan.json");
2952
+ const tempPath = path8.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3349
2953
  try {
3350
2954
  await bunWrite(tempPath, JSON.stringify(projectedPlan, null, 2));
3351
- renameSync4(tempPath, planPath);
2955
+ renameSync3(tempPath, planPath);
3352
2956
  } finally {
3353
2957
  try {
3354
2958
  unlinkSync3(tempPath);
3355
2959
  } catch {}
3356
2960
  }
3357
2961
  try {
3358
- const markerPath = path9.join(swarmDir, ".plan-write-marker");
2962
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3359
2963
  const inProgressMarker = JSON.stringify({
3360
2964
  source: "plan_manager",
3361
2965
  timestamp: new Date().toISOString(),
@@ -3370,11 +2974,11 @@ async function savePlan(directory, plan, options) {
3370
2974
  const markdown = derivePlanMarkdown(projectedPlan);
3371
2975
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
3372
2976
  ${markdown}`;
3373
- const mdPath = path9.join(swarmDir, "plan.md");
3374
- const mdTempPath = path9.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2977
+ const mdPath = path8.join(swarmDir, "plan.md");
2978
+ const mdTempPath = path8.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3375
2979
  try {
3376
2980
  await bunWrite(mdTempPath, markdownWithHash);
3377
- renameSync4(mdTempPath, mdPath);
2981
+ renameSync3(mdTempPath, mdPath);
3378
2982
  } finally {
3379
2983
  try {
3380
2984
  unlinkSync3(mdTempPath);
@@ -3392,7 +2996,7 @@ ${markdown}`;
3392
2996
  } catch {}
3393
2997
  }
3394
2998
  try {
3395
- const markerPath = path9.join(swarmDir, ".plan-write-marker");
2999
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3396
3000
  const tasksCount = projectedPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
3397
3001
  const marker = JSON.stringify({
3398
3002
  source: "plan_manager",
@@ -3408,23 +3012,23 @@ async function rebuildPlan(directory, plan, options) {
3408
3012
  const targetPlan = plan ?? await replayFromLedger(directory);
3409
3013
  if (!targetPlan)
3410
3014
  return null;
3411
- const swarmDir = path9.join(directory, ".swarm");
3412
- const planPath = path9.join(swarmDir, "plan.json");
3413
- const mdPath = path9.join(swarmDir, "plan.md");
3414
- mkdirSync5(swarmDir, { recursive: true });
3415
- const tempPlanPath = path9.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3015
+ const swarmDir = path8.join(directory, ".swarm");
3016
+ const planPath = path8.join(swarmDir, "plan.json");
3017
+ const mdPath = path8.join(swarmDir, "plan.md");
3018
+ mkdirSync4(swarmDir, { recursive: true });
3019
+ const tempPlanPath = path8.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3416
3020
  {
3417
- const fd = openSync2(tempPlanPath, "w");
3021
+ const fd = openSync(tempPlanPath, "w");
3418
3022
  try {
3419
- writeFileSync6(fd, JSON.stringify(targetPlan, null, 2), "utf8");
3420
- fsyncSync2(fd);
3023
+ writeFileSync5(fd, JSON.stringify(targetPlan, null, 2), "utf8");
3024
+ fsyncSync(fd);
3421
3025
  } finally {
3422
- closeSync2(fd);
3026
+ closeSync(fd);
3423
3027
  }
3424
3028
  }
3425
- renameSync4(tempPlanPath, planPath);
3029
+ renameSync3(tempPlanPath, planPath);
3426
3030
  try {
3427
- const markerPath = path9.join(swarmDir, ".plan-write-marker");
3031
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3428
3032
  const inProgressMarker = JSON.stringify({
3429
3033
  source: "plan_manager",
3430
3034
  timestamp: new Date().toISOString(),
@@ -3439,12 +3043,12 @@ async function rebuildPlan(directory, plan, options) {
3439
3043
  const markdown = derivePlanMarkdown(targetPlan);
3440
3044
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
3441
3045
  ${markdown}`;
3442
- const tempMdPath = path9.join(swarmDir, `plan.md.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3046
+ const tempMdPath = path8.join(swarmDir, `plan.md.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3443
3047
  await bunWrite(tempMdPath, markdownWithHash);
3444
- renameSync4(tempMdPath, mdPath);
3048
+ renameSync3(tempMdPath, mdPath);
3445
3049
  } finally {
3446
3050
  try {
3447
- const markerPath = path9.join(swarmDir, ".plan-write-marker");
3051
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3448
3052
  const tasksCount = targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
3449
3053
  const marker = JSON.stringify({
3450
3054
  source: "plan_manager",
@@ -3507,13 +3111,13 @@ async function closePlanTerminalState(directory, plan, options) {
3507
3111
  planHashAfter: hashAfter,
3508
3112
  source: "close_terminal"
3509
3113
  });
3510
- const swarmDir = path9.join(directory, ".swarm");
3511
- const planPath = path9.join(swarmDir, "plan.json");
3512
- const tempPlanPath = path9.join(swarmDir, `plan.json.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3114
+ const swarmDir = path8.join(directory, ".swarm");
3115
+ const planPath = path8.join(swarmDir, "plan.json");
3116
+ const tempPlanPath = path8.join(swarmDir, `plan.json.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3513
3117
  await bunWrite(tempPlanPath, JSON.stringify(validated, null, 2));
3514
- renameSync4(tempPlanPath, planPath);
3118
+ renameSync3(tempPlanPath, planPath);
3515
3119
  try {
3516
- const markerPath = path9.join(swarmDir, ".plan-write-marker");
3120
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3517
3121
  const inProgressMarker = JSON.stringify({
3518
3122
  source: "plan_manager_close",
3519
3123
  timestamp: new Date().toISOString(),
@@ -3524,17 +3128,17 @@ async function closePlanTerminalState(directory, plan, options) {
3524
3128
  await bunWrite(markerPath, inProgressMarker);
3525
3129
  } catch {}
3526
3130
  try {
3527
- const mdPath = path9.join(swarmDir, "plan.md");
3131
+ const mdPath = path8.join(swarmDir, "plan.md");
3528
3132
  const contentHash = computePlanContentHash(validated);
3529
3133
  const markdown = derivePlanMarkdown(validated);
3530
3134
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
3531
3135
  ${markdown}`;
3532
- const mdTempPath = path9.join(swarmDir, `plan.md.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3136
+ const mdTempPath = path8.join(swarmDir, `plan.md.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3533
3137
  await bunWrite(mdTempPath, markdownWithHash);
3534
- renameSync4(mdTempPath, mdPath);
3138
+ renameSync3(mdTempPath, mdPath);
3535
3139
  } finally {
3536
3140
  try {
3537
- const markerPath = path9.join(swarmDir, ".plan-write-marker");
3141
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3538
3142
  const tasksCount = validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
3539
3143
  const marker = JSON.stringify({
3540
3144
  source: "plan_manager_close",
@@ -3818,19 +3422,19 @@ function migrateLegacyPlan(planContent, swarmId) {
3818
3422
 
3819
3423
  // src/evidence/manager.ts
3820
3424
  import {
3821
- mkdirSync as mkdirSync6,
3822
- readdirSync as readdirSync3,
3425
+ mkdirSync as mkdirSync5,
3426
+ readdirSync as readdirSync2,
3823
3427
  realpathSync as realpathSync2,
3824
3428
  rmSync,
3825
3429
  statSync as statSync2
3826
3430
  } from "fs";
3827
- import * as fs7 from "fs/promises";
3431
+ import * as fs6 from "fs/promises";
3828
3432
  import * as os4 from "os";
3829
- import * as path13 from "path";
3433
+ import * as path12 from "path";
3830
3434
 
3831
3435
  // src/evaluation/retention.ts
3832
- import * as fs6 from "fs/promises";
3833
- import * as path12 from "path";
3436
+ import * as fs5 from "fs/promises";
3437
+ import * as path11 from "path";
3834
3438
 
3835
3439
  // src/evaluation/contracts.ts
3836
3440
  var IdentifierSchema = exports_external.string().min(1).max(160).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
@@ -4205,13 +3809,13 @@ var TaskSetSnapshotV1Schema = exports_external.object({
4205
3809
 
4206
3810
  // src/evaluation/store.ts
4207
3811
  import { mkdir, readdir, readFile as readFile2 } from "fs/promises";
4208
- import * as path11 from "path";
3812
+ import * as path10 from "path";
4209
3813
 
4210
3814
  // src/evaluation/hashing.ts
4211
- import { createHash as createHash3 } from "crypto";
3815
+ import { createHash as createHash2 } from "crypto";
4212
3816
  import { lstatSync, realpathSync } from "fs";
4213
3817
  import { lstat, opendir, readFile, realpath } from "fs/promises";
4214
- import * as path10 from "path";
3818
+ import * as path9 from "path";
4215
3819
  function canonicalize(value, ancestors) {
4216
3820
  if (value === null || typeof value === "string" || typeof value === "boolean") {
4217
3821
  return value;
@@ -4252,7 +3856,7 @@ function canonicalJson(value) {
4252
3856
  return JSON.stringify(canonicalize(value, new Set));
4253
3857
  }
4254
3858
  function sha256(value) {
4255
- return createHash3("sha256").update(value).digest("hex");
3859
+ return createHash2("sha256").update(value).digest("hex");
4256
3860
  }
4257
3861
  function canonicalHash(value) {
4258
3862
  return sha256(canonicalJson(value));
@@ -4302,9 +3906,9 @@ async function hashTree(projectRoot, relativePath, budget) {
4302
3906
  }
4303
3907
  if (!entryAlreadyCharged)
4304
3908
  chargeHashEntry(budget);
4305
- const relative3 = path10.relative(canonicalRoot, absolutePath).replace(/\\/g, "/");
3909
+ const relative2 = path9.relative(canonicalRoot, absolutePath).replace(/\\/g, "/");
4306
3910
  if (stat.isDirectory()) {
4307
- entries.push({ path: relative3, type: "directory" });
3911
+ entries.push({ path: relative2, type: "directory" });
4308
3912
  const children = [];
4309
3913
  const directory = await _internals9.opendir(absolutePath);
4310
3914
  try {
@@ -4319,12 +3923,12 @@ async function hashTree(projectRoot, relativePath, budget) {
4319
3923
  await closeDirectory(directory);
4320
3924
  }
4321
3925
  for (const child of children.sort()) {
4322
- await visit(path10.join(absolutePath, child), true);
3926
+ await visit(path9.join(absolutePath, child), true);
4323
3927
  }
4324
3928
  return;
4325
3929
  }
4326
3930
  if (!stat.isFile()) {
4327
- throw new Error(`evaluation input must contain only regular files: ${relative3}`);
3931
+ throw new Error(`evaluation input must contain only regular files: ${relative2}`);
4328
3932
  }
4329
3933
  budget.bytes += stat.size;
4330
3934
  if (budget.bytes > budget.limits.maxBytes) {
@@ -4332,7 +3936,7 @@ async function hashTree(projectRoot, relativePath, budget) {
4332
3936
  }
4333
3937
  const content = await _internals9.readFile(absolutePath);
4334
3938
  entries.push({
4335
- path: relative3,
3939
+ path: relative2,
4336
3940
  type: "file",
4337
3941
  bytes: content.byteLength,
4338
3942
  sha256: sha256(content)
@@ -4362,10 +3966,10 @@ async function computeTaskLineageInputHash(projectRoot, task, limits = {}) {
4362
3966
  const normalizedTree = async (relativePath) => {
4363
3967
  const canonicalRoot = await _internals9.realpath(projectRoot);
4364
3968
  const target = await resolveContainedExistingPathAsync(projectRoot, relativePath);
4365
- const targetRelative = path10.relative(canonicalRoot, target).replace(/\\/g, "/");
3969
+ const targetRelative = path9.relative(canonicalRoot, target).replace(/\\/g, "/");
4366
3970
  return (await hashTree(projectRoot, relativePath, budget)).map((entry) => ({
4367
3971
  ...entry,
4368
- path: path10.posix.relative(targetRelative, entry.path) || "."
3972
+ path: path9.posix.relative(targetRelative, entry.path) || "."
4369
3973
  }));
4370
3974
  };
4371
3975
  const scorerArgv = [...task.scorer.argv];
@@ -4394,11 +3998,11 @@ function computeManifestContentHash(manifest) {
4394
3998
  return contentHashWithout(manifest, ["contentHash"]);
4395
3999
  }
4396
4000
  function isWithin(root, candidate) {
4397
- const relative3 = path10.relative(root, candidate);
4398
- return relative3 === "" || !relative3.startsWith("..") && !path10.isAbsolute(relative3);
4001
+ const relative2 = path9.relative(root, candidate);
4002
+ return relative2 === "" || !relative2.startsWith("..") && !path9.isAbsolute(relative2);
4399
4003
  }
4400
4004
  function resolveContainedExistingPath(root, relativePath) {
4401
- if (path10.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
4005
+ if (path9.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
4402
4006
  throw new Error("evaluation path must be a non-NUL relative path");
4403
4007
  }
4404
4008
  const segments = relativePath.replace(/\\/g, "/").split("/").filter(Boolean);
@@ -4410,7 +4014,7 @@ function resolveContainedExistingPath(root, relativePath) {
4410
4014
  for (const segment of segments) {
4411
4015
  if (segment === ".")
4412
4016
  continue;
4413
- cursor = path10.join(cursor, segment);
4017
+ cursor = path9.join(cursor, segment);
4414
4018
  const stat = lstatSync(cursor);
4415
4019
  if (stat.isSymbolicLink()) {
4416
4020
  throw new Error(`evaluation path contains a symlink or reparse point: ${relativePath}`);
@@ -4423,7 +4027,7 @@ function resolveContainedExistingPath(root, relativePath) {
4423
4027
  return canonicalCandidate;
4424
4028
  }
4425
4029
  async function resolveContainedExistingPathAsync(root, relativePath) {
4426
- if (path10.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
4030
+ if (path9.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
4427
4031
  throw new Error("evaluation path must be a non-NUL relative path");
4428
4032
  }
4429
4033
  const segments = relativePath.replace(/\\/g, "/").split("/").filter(Boolean);
@@ -4435,7 +4039,7 @@ async function resolveContainedExistingPathAsync(root, relativePath) {
4435
4039
  for (const segment of segments) {
4436
4040
  if (segment === ".")
4437
4041
  continue;
4438
- cursor = path10.join(cursor, segment);
4042
+ cursor = path9.join(cursor, segment);
4439
4043
  const stat = await _internals9.lstat(cursor);
4440
4044
  if (stat.isSymbolicLink()) {
4441
4045
  throw new Error(`evaluation path contains a symlink or reparse point: ${relativePath}`);
@@ -4456,8 +4060,8 @@ var _internals9 = {
4456
4060
 
4457
4061
  // src/evaluation/store.ts
4458
4062
  var AGENT = "evaluation-store";
4459
- var TEST_LEDGER_RELATIVE = path11.join("evolution", "test-consumption.jsonl");
4460
- var TASK_SPLIT_REGISTRY_RELATIVE = path11.join("evolution", "tasks", "split-registry.json");
4063
+ var TEST_LEDGER_RELATIVE = path10.join("evolution", "test-consumption.jsonl");
4064
+ var TASK_SPLIT_REGISTRY_RELATIVE = path10.join("evolution", "tasks", "split-registry.json");
4461
4065
 
4462
4066
  class EvaluationConflictError extends Error {
4463
4067
  constructor(message) {
@@ -4466,7 +4070,7 @@ class EvaluationConflictError extends Error {
4466
4070
  }
4467
4071
  }
4468
4072
  function swarmPath(directory, ...segments) {
4469
- return path11.join(directory, ".swarm", ...segments);
4073
+ return path10.join(directory, ".swarm", ...segments);
4470
4074
  }
4471
4075
  function serialized(value) {
4472
4076
  return `${canonicalJson(value)}
@@ -4515,7 +4119,7 @@ async function readOptional(filePath) {
4515
4119
  }
4516
4120
  }
4517
4121
  async function writeImmutable(options) {
4518
- await mkdir(path11.dirname(options.filePath), { recursive: true });
4122
+ await mkdir(path10.dirname(options.filePath), { recursive: true });
4519
4123
  return withEvidenceLock(options.directory, options.relativeLockPath, AGENT, options.taskId, async () => {
4520
4124
  const desired = serialized(options.value);
4521
4125
  const existing = await readOptional(options.filePath);
@@ -4543,8 +4147,8 @@ function validateTaskInputs(directory, task) {
4543
4147
  if (!executable)
4544
4148
  throw new Error("project scorer requires an executable");
4545
4149
  const scorerPath = resolveContainedExistingPath(directory, executable);
4546
- const relative4 = path11.relative(environmentRoot, scorerPath);
4547
- if (!relative4 || relative4 === ".." || relative4.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative4)) {
4150
+ const relative3 = path10.relative(environmentRoot, scorerPath);
4151
+ if (!relative3 || relative3 === ".." || relative3.startsWith(`..${path10.sep}`) || path10.isAbsolute(relative3)) {
4548
4152
  throw new Error("project scorer executable must be inside the admitted task environment");
4549
4153
  }
4550
4154
  }
@@ -4556,10 +4160,10 @@ async function admitEvaluationTask(directory, input, inputRoot = directory) {
4556
4160
  }
4557
4161
  validateTaskInputs(inputRoot, task);
4558
4162
  const lineageInputHash = await computeTaskLineageInputHash(inputRoot, task);
4559
- const relative4 = path11.join("evolution", "tasks", task.split, `${task.id}.json`);
4163
+ const relative3 = path10.join("evolution", "tasks", task.split, `${task.id}.json`);
4560
4164
  const admissionLock = TASK_SPLIT_REGISTRY_RELATIVE;
4561
- const filePath = swarmPath(directory, relative4);
4562
- await mkdir(path11.dirname(filePath), { recursive: true });
4165
+ const filePath = swarmPath(directory, relative3);
4166
+ await mkdir(path10.dirname(filePath), { recursive: true });
4563
4167
  return withEvidenceLock(directory, admissionLock, AGENT, task.id, async () => {
4564
4168
  const registryPath = swarmPath(directory, TASK_SPLIT_REGISTRY_RELATIVE);
4565
4169
  const registry = parseTaskSplitRegistry(await readOptional(registryPath));
@@ -4647,11 +4251,11 @@ async function saveTaskSetSnapshot(directory, input, inputRoot = directory) {
4647
4251
  if (computeTaskSetContentHash(snapshot) !== snapshot.contentHash) {
4648
4252
  throw new Error("task-set content hash does not match its canonical inputs");
4649
4253
  }
4650
- const relative4 = path11.join("evolution", "task-sets", `${snapshot.contentHash}.json`);
4254
+ const relative3 = path10.join("evolution", "task-sets", `${snapshot.contentHash}.json`);
4651
4255
  return writeImmutable({
4652
4256
  directory,
4653
- relativeLockPath: relative4,
4654
- filePath: swarmPath(directory, relative4),
4257
+ relativeLockPath: relative3,
4258
+ filePath: swarmPath(directory, relative3),
4655
4259
  taskId: snapshot.id,
4656
4260
  value: snapshot,
4657
4261
  parse: (value) => TaskSetSnapshotV1Schema.parse(value),
@@ -4660,11 +4264,11 @@ async function saveTaskSetSnapshot(directory, input, inputRoot = directory) {
4660
4264
  }
4661
4265
  async function saveGateAuditResult(directory, input) {
4662
4266
  const result = GateAuditResultV1Schema.parse(input);
4663
- const relative4 = path11.join("evidence", "gate-audit", result.runId, "results.json");
4267
+ const relative3 = path10.join("evidence", "gate-audit", result.runId, "results.json");
4664
4268
  return writeImmutable({
4665
4269
  directory,
4666
- relativeLockPath: relative4,
4667
- filePath: swarmPath(directory, relative4),
4270
+ relativeLockPath: relative3,
4271
+ filePath: swarmPath(directory, relative3),
4668
4272
  taskId: result.runId,
4669
4273
  value: result,
4670
4274
  parse: (value) => GateAuditResultV1Schema.parse(value)
@@ -4672,11 +4276,11 @@ async function saveGateAuditResult(directory, input) {
4672
4276
  }
4673
4277
  async function saveGateAuditManifest(directory, input) {
4674
4278
  const manifest = GateAuditManifestV1Schema.parse(input);
4675
- const relative4 = path11.join("evolution", "gate-audit-manifests", `${manifest.id}.json`);
4279
+ const relative3 = path10.join("evolution", "gate-audit-manifests", `${manifest.id}.json`);
4676
4280
  return writeImmutable({
4677
4281
  directory,
4678
- relativeLockPath: relative4,
4679
- filePath: swarmPath(directory, relative4),
4282
+ relativeLockPath: relative3,
4283
+ filePath: swarmPath(directory, relative3),
4680
4284
  taskId: manifest.id,
4681
4285
  value: manifest,
4682
4286
  parse: (value) => GateAuditManifestV1Schema.parse(value)
@@ -4708,7 +4312,7 @@ async function listGateAuditResults(directory) {
4708
4312
  continue;
4709
4313
  }
4710
4314
  try {
4711
- const parsed = GateAuditResultV1Schema.parse(JSON.parse(await readFile2(path11.join(root, entry.name, "results.json"), "utf8")));
4315
+ const parsed = GateAuditResultV1Schema.parse(JSON.parse(await readFile2(path10.join(root, entry.name, "results.json"), "utf8")));
4712
4316
  if (parsed.runId !== entry.name)
4713
4317
  throw new Error("run id/path mismatch");
4714
4318
  results.push(parsed);
@@ -4741,7 +4345,7 @@ async function getProtectedEvaluationRunIds(directory) {
4741
4345
  throw error2;
4742
4346
  }
4743
4347
  for (const filename of files.filter((value) => value.endsWith(".json")).sort()) {
4744
- const decision = PromotionDecisionV1Schema.parse(JSON.parse(await readFile2(path11.join(decisionsDirectory, filename), "utf8")));
4348
+ const decision = PromotionDecisionV1Schema.parse(JSON.parse(await readFile2(path10.join(decisionsDirectory, filename), "utf8")));
4745
4349
  protectedIds.add(decision.runId);
4746
4350
  protectedIds.add(decision.lineage.baselineRunId);
4747
4351
  if (decision.lineage.historicalBestRunId !== "unavailable") {
@@ -4759,10 +4363,10 @@ function validId(value) {
4759
4363
  return /^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/.test(value);
4760
4364
  }
4761
4365
  async function evaluationRunCandidates(directory) {
4762
- const root = path12.join(directory, ".swarm", "evolution", "runs");
4366
+ const root = path11.join(directory, ".swarm", "evolution", "runs");
4763
4367
  let entries = [];
4764
4368
  try {
4765
- entries = await fs6.readdir(root, { withFileTypes: true });
4369
+ entries = await fs5.readdir(root, { withFileTypes: true });
4766
4370
  } catch (error2) {
4767
4371
  if (error2.code === "ENOENT") {
4768
4372
  return { candidates: [], corrupt: [] };
@@ -4778,9 +4382,9 @@ async function evaluationRunCandidates(directory) {
4778
4382
  if (!validId(id))
4779
4383
  continue;
4780
4384
  const artifact = { namespace: "evaluation-run", id };
4781
- const target = path12.join(root, entry.name);
4385
+ const target = path11.join(root, entry.name);
4782
4386
  try {
4783
- const run = EvaluationRunV1Schema.parse(JSON.parse(await fs6.readFile(target, "utf8")));
4387
+ const run = EvaluationRunV1Schema.parse(JSON.parse(await fs5.readFile(target, "utf8")));
4784
4388
  if (run.runId !== id)
4785
4389
  throw new Error("run id/path mismatch");
4786
4390
  candidates.push({
@@ -4796,10 +4400,10 @@ async function evaluationRunCandidates(directory) {
4796
4400
  return { candidates, corrupt };
4797
4401
  }
4798
4402
  async function gateAuditCandidates(directory) {
4799
- const root = path12.join(directory, ".swarm", "evidence", "gate-audit");
4403
+ const root = path11.join(directory, ".swarm", "evidence", "gate-audit");
4800
4404
  let entries = [];
4801
4405
  try {
4802
- entries = await fs6.readdir(root, { withFileTypes: true });
4406
+ entries = await fs5.readdir(root, { withFileTypes: true });
4803
4407
  } catch (error2) {
4804
4408
  if (error2.code === "ENOENT") {
4805
4409
  return { candidates: [], corrupt: [] };
@@ -4812,9 +4416,9 @@ async function gateAuditCandidates(directory) {
4812
4416
  if (!entry.isDirectory() || !validId(entry.name))
4813
4417
  continue;
4814
4418
  const artifact = { namespace: "gate-audit", id: entry.name };
4815
- const target = path12.join(root, entry.name);
4419
+ const target = path11.join(root, entry.name);
4816
4420
  try {
4817
- const result = GateAuditResultV1Schema.parse(JSON.parse(await fs6.readFile(path12.join(target, "results.json"), "utf8")));
4421
+ const result = GateAuditResultV1Schema.parse(JSON.parse(await fs5.readFile(path11.join(target, "results.json"), "utf8")));
4818
4422
  if (result.runId !== entry.name)
4819
4423
  throw new Error("run id/path mismatch");
4820
4424
  candidates.push({
@@ -4845,17 +4449,17 @@ async function protectedArtifacts(directory) {
4845
4449
  }
4846
4450
  async function removeCandidate(candidate) {
4847
4451
  if (candidate.kind === "directory") {
4848
- await fs6.rm(candidate.target, { recursive: true, force: true });
4452
+ await fs5.rm(candidate.target, { recursive: true, force: true });
4849
4453
  return;
4850
4454
  }
4851
- await fs6.unlink(candidate.target);
4455
+ await fs5.unlink(candidate.target);
4852
4456
  }
4853
4457
  var _retentionInternals = {
4854
4458
  protectedArtifacts,
4855
4459
  removeCandidate
4856
4460
  };
4857
4461
  async function archiveEvaluationArtifacts(args) {
4858
- return withEvidenceLock(args.directory, path12.join("evolution", "retention-index"), "evaluation-retention", "archive", async () => {
4462
+ return withEvidenceLock(args.directory, path11.join("evolution", "retention-index"), "evaluation-retention", "archive", async () => {
4859
4463
  const inventory = await retentionCandidates(args.directory);
4860
4464
  let protectedKeys = await _retentionInternals.protectedArtifacts(args.directory);
4861
4465
  const protectedRefs = inventory.candidates.filter((candidate) => protectedKeys.has(artifactKey(candidate.artifact))).map((candidate) => candidate.artifact);
@@ -4942,8 +4546,8 @@ function isWeakConfigContainerRoot(directory) {
4942
4546
  const resolved = realpathSync2(directory);
4943
4547
  return resolved === realpathSync2(os4.tmpdir()) || resolved === realpathSync2(os4.homedir());
4944
4548
  } catch {
4945
- const resolved = path13.resolve(directory);
4946
- return resolved === path13.resolve(os4.tmpdir()) || resolved === path13.resolve(os4.homedir());
4549
+ const resolved = path12.resolve(directory);
4550
+ return resolved === path12.resolve(os4.tmpdir()) || resolved === path12.resolve(os4.homedir());
4947
4551
  }
4948
4552
  }
4949
4553
  function hasAccessibleProjectIndicator(directory, options = {}) {
@@ -4953,7 +4557,7 @@ function hasAccessibleProjectIndicator(directory, options = {}) {
4953
4557
  continue;
4954
4558
  }
4955
4559
  try {
4956
- const indicatorStat = statSync2(path13.join(directory, indicator));
4560
+ const indicatorStat = statSync2(path12.join(directory, indicator));
4957
4561
  if (indicatorStat.isFile() || indicatorStat.isDirectory()) {
4958
4562
  return true;
4959
4563
  }
@@ -4975,14 +4579,14 @@ function validateProjectRoot(directory) {
4975
4579
  if (depth >= MAX_DEPTH)
4976
4580
  break;
4977
4581
  depth++;
4978
- const parent = path13.dirname(current);
4582
+ const parent = path12.dirname(current);
4979
4583
  if (parent === current)
4980
4584
  break;
4981
- if (path13.dirname(parent) === parent) {
4585
+ if (path12.dirname(parent) === parent) {
4982
4586
  current = parent;
4983
4587
  continue;
4984
4588
  }
4985
- const parentSwarm = path13.join(parent, ".swarm");
4589
+ const parentSwarm = path12.join(parent, ".swarm");
4986
4590
  try {
4987
4591
  if (statSync2(parentSwarm).isDirectory()) {
4988
4592
  if (hasAccessibleProjectIndicator(parent, {
@@ -5003,12 +4607,12 @@ function validateProjectRoot(directory) {
5003
4607
  async function saveEvidence(directory, taskId, evidence) {
5004
4608
  _internals10.validateProjectRoot(directory);
5005
4609
  const sanitizedTaskId = sanitizeTaskId2(taskId);
5006
- const relativePath = path13.join("evidence", sanitizedTaskId, "evidence.json");
4610
+ const relativePath = path12.join("evidence", sanitizedTaskId, "evidence.json");
5007
4611
  validateSwarmPath(directory, relativePath);
5008
4612
  _internals10.validateEvidence(evidence);
5009
4613
  return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
5010
4614
  const evidencePath = validateSwarmPath(directory, relativePath);
5011
- const evidenceDir = path13.dirname(evidencePath);
4615
+ const evidenceDir = path12.dirname(evidencePath);
5012
4616
  let bundle;
5013
4617
  const existingContent = await readSwarmFileAsync(directory, relativePath);
5014
4618
  if (existingContent !== null) {
@@ -5050,11 +4654,11 @@ async function saveEvidence(directory, taskId, evidence) {
5050
4654
  if (bundleJson.length > EVIDENCE_MAX_JSON_BYTES) {
5051
4655
  throw new Error(`Evidence bundle size (${bundleJson.length} bytes) exceeds maximum (${EVIDENCE_MAX_JSON_BYTES} bytes)`);
5052
4656
  }
5053
- mkdirSync6(evidenceDir, { recursive: true });
5054
- const tempPath = path13.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4657
+ mkdirSync5(evidenceDir, { recursive: true });
4658
+ const tempPath = path12.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
5055
4659
  try {
5056
4660
  await bunWrite(tempPath, bundleJson);
5057
- await fs7.rename(tempPath, evidencePath);
4661
+ await fs6.rename(tempPath, evidencePath);
5058
4662
  } catch (error2) {
5059
4663
  try {
5060
4664
  rmSync(tempPath, { force: true });
@@ -5095,7 +4699,7 @@ function wrapFlatRetrospective(flatEntry, taskId) {
5095
4699
  }
5096
4700
  async function loadEvidence(directory, taskId) {
5097
4701
  const sanitizedTaskId = sanitizeTaskId2(taskId);
5098
- const relativePath = path13.join("evidence", sanitizedTaskId, "evidence.json");
4702
+ const relativePath = path12.join("evidence", sanitizedTaskId, "evidence.json");
5099
4703
  if (relativePath.length > 4096) {
5100
4704
  return { status: "not_found" };
5101
4705
  }
@@ -5116,12 +4720,12 @@ async function loadEvidence(directory, taskId) {
5116
4720
  const validated = EvidenceBundleSchema.parse(wrappedBundle);
5117
4721
  try {
5118
4722
  await withEvidenceLock(directory, relativePath, "evidence-loader", sanitizedTaskId, async () => {
5119
- const evidenceDir = path13.dirname(evidencePath);
4723
+ const evidenceDir = path12.dirname(evidencePath);
5120
4724
  const bundleJson = JSON.stringify(validated);
5121
- const tempPath = path13.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4725
+ const tempPath = path12.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
5122
4726
  try {
5123
4727
  await bunWrite(tempPath, bundleJson);
5124
- await fs7.rename(tempPath, evidencePath);
4728
+ await fs6.rename(tempPath, evidencePath);
5125
4729
  } catch (writeError) {
5126
4730
  try {
5127
4731
  rmSync(tempPath, { force: true });
@@ -5157,13 +4761,13 @@ async function listEvidenceTaskIds(directory) {
5157
4761
  }
5158
4762
  let entries;
5159
4763
  try {
5160
- entries = readdirSync3(evidenceBasePath);
4764
+ entries = readdirSync2(evidenceBasePath);
5161
4765
  } catch {
5162
4766
  return [];
5163
4767
  }
5164
4768
  const taskIds = [];
5165
4769
  for (const entry of entries) {
5166
- const entryPath = path13.join(evidenceBasePath, entry);
4770
+ const entryPath = path12.join(evidenceBasePath, entry);
5167
4771
  try {
5168
4772
  const stats = statSync2(entryPath);
5169
4773
  if (!stats.isDirectory()) {
@@ -5181,7 +4785,7 @@ async function listEvidenceTaskIds(directory) {
5181
4785
  }
5182
4786
  async function deleteEvidence(directory, taskId) {
5183
4787
  const sanitizedTaskId = sanitizeTaskId2(taskId);
5184
- const relativePath = path13.join("evidence", sanitizedTaskId);
4788
+ const relativePath = path12.join("evidence", sanitizedTaskId);
5185
4789
  const evidenceDir = validateSwarmPath(directory, relativePath);
5186
4790
  try {
5187
4791
  statSync2(evidenceDir);
@@ -5197,10 +4801,10 @@ async function deleteEvidence(directory, taskId) {
5197
4801
  }
5198
4802
  }
5199
4803
  async function checkRequirementCoverage(phase, directory) {
5200
- const relativePath = path13.join("evidence", `req-coverage-phase-${phase}.json`);
5201
- const absolutePath = path13.resolve(directory, ".swarm", relativePath);
4804
+ const relativePath = path12.join("evidence", `req-coverage-phase-${phase}.json`);
4805
+ const absolutePath = path12.resolve(directory, ".swarm", relativePath);
5202
4806
  try {
5203
- await fs7.access(absolutePath);
4807
+ await fs6.access(absolutePath);
5204
4808
  return { exists: true, path: absolutePath };
5205
4809
  } catch {
5206
4810
  return { exists: false, path: absolutePath };
@@ -5399,4 +5003,4 @@ function mergeDurableGateEntriesFromEvidence(taskId, entries, evidence) {
5399
5003
  return merged;
5400
5004
  }
5401
5005
 
5402
- export { getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, initDurableStatusPath, getAllWorktreeMergeFailures, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, derivePlanId, computePlanHash, initLedger, appendLedgerEvent, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, IdentifierSchema, EvaluationTaskV1Schema, GateNameSchema, GateAuditManifestV1Schema, GateAuditResultV1Schema, canonicalJson, canonicalHash, computeTaskInputContentHash, computeTaskSetContentHash, computeManifestContentHash, resolveContainedExistingPath, admitEvaluationTask, saveTaskSetSnapshot, saveGateAuditResult, saveGateAuditManifest, readGateAuditManifest, readGateAuditResult, listGateAuditResults, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };
5006
+ export { getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, initDurableStatusPath, getAllWorktreeMergeFailures, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, IdentifierSchema, EvaluationTaskV1Schema, GateNameSchema, GateAuditManifestV1Schema, GateAuditResultV1Schema, canonicalJson, canonicalHash, computeTaskInputContentHash, computeTaskSetContentHash, computeManifestContentHash, resolveContainedExistingPath, admitEvaluationTask, saveTaskSetSnapshot, saveGateAuditResult, saveGateAuditManifest, readGateAuditManifest, readGateAuditResult, listGateAuditResults, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };