opencode-swarm 7.138.4 → 7.139.1

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 (91) hide show
  1. package/README.md +7 -6
  2. package/dist/cli/{config-doctor-31yppse4.js → config-doctor-tgnr91q3.js} +2 -2
  3. package/dist/cli/{core-65c7rvqf.js → core-g2dfh9fq.js} +1 -1
  4. package/dist/cli/{curation-policy-hs9pc1rp.js → curation-policy-2qnhgdtt.js} +7 -7
  5. package/dist/cli/{curator-drift-jpap6hv4.js → curator-drift-vfh07mqy.js} +5 -2
  6. package/dist/cli/{curator-llm-factory-wm27ra25.js → curator-llm-factory-1ftzece0.js} +28 -28
  7. package/dist/cli/{curator-54n1y4j8.js → curator-nbsag633.js} +28 -28
  8. package/dist/cli/{evidence-summary-service-hrxcf29a.js → evidence-summary-service-kzxr71yd.js} +9 -9
  9. package/dist/cli/{gate-evidence-ecx4djaw.js → gate-evidence-z2b1ymnw.js} +5 -5
  10. package/dist/cli/{guardrail-explain-1729nse8.js → guardrail-explain-vb9k9zwm.js} +29 -29
  11. package/dist/cli/{guardrail-log-yjj5srkw.js → guardrail-log-0djnvfas.js} +3 -3
  12. package/dist/cli/{hive-promoter-es2x2b0e.js → hive-promoter-bpt26yew.js} +28 -28
  13. package/dist/cli/{index-mycqmsz5.js → index-0wp67gqj.js} +452 -159
  14. package/dist/cli/{index-c60whfgh.js → index-0ymhx85c.js} +2 -2
  15. package/dist/cli/{index-5q009kc4.js → index-15526t16.js} +8 -3
  16. package/dist/cli/{index-smsy2p8n.js → index-3eddtgxt.js} +1 -1
  17. package/dist/cli/{index-4fmztnrq.js → index-4xbyy6aw.js} +2 -2
  18. package/dist/cli/{index-xybtaph7.js → index-5j6chvj8.js} +1 -1
  19. package/dist/cli/{index-39k6y018.js → index-5w7ekpy3.js} +2 -2
  20. package/dist/cli/{index-z7jgvm9b.js → index-5zyk8qgv.js} +2 -2
  21. package/dist/cli/{index-en4fb04r.js → index-668dpw5e.js} +1 -1
  22. package/dist/cli/{index-m8txs2a9.js → index-91s653xx.js} +1 -1
  23. package/dist/cli/{index-y28n2wmq.js → index-a12906mj.js} +1 -1
  24. package/dist/cli/{index-10mz5js6.js → index-a5eba0t8.js} +2 -2
  25. package/dist/cli/{index-57stye65.js → index-aymt7hzw.js} +6 -3
  26. package/dist/cli/{index-1g6xpjk3.js → index-db7wf6ke.js} +7 -7
  27. package/dist/cli/{index-m1pwkwfe.js → index-dw09fz3w.js} +1 -1
  28. package/dist/cli/{index-fsrp8wp3.js → index-ed5ykebp.js} +25 -2
  29. package/dist/cli/{index-jbge0dyh.js → index-en6qq954.js} +3 -3
  30. package/dist/cli/{index-tqshdzaw.js → index-gvhvz176.js} +2 -2
  31. package/dist/cli/{index-e1kgxchq.js → index-hgrkhxbk.js} +2 -2
  32. package/dist/cli/{index-33q2gmgd.js → index-hn0mdcmj.js} +1 -1
  33. package/dist/cli/{index-a9xny52r.js → index-ht4q3548.js} +2 -2
  34. package/dist/cli/{index-q344kj84.js → index-prmb5qfp.js} +2 -2
  35. package/dist/cli/{index-x20cgr2q.js → index-q7ezzgwq.js} +16 -2
  36. package/dist/cli/{index-ngqxzkqm.js → index-s608d3hw.js} +2 -2
  37. package/dist/cli/{index-50mwt8fx.js → index-vxbppwvf.js} +5 -5
  38. package/dist/cli/{index-rrxcfxrd.js → index-wrt6m5ze.js} +1 -1
  39. package/dist/cli/{index-4qzeef9h.js → index-x9q4bbhd.js} +1 -1
  40. package/dist/cli/{index-m40ea3zk.js → index-xec724kg.js} +2 -2
  41. package/dist/cli/{index-k27a4e5g.js → index-xka8s8jw.js} +30 -30
  42. package/dist/cli/{index-a18hk2fw.js → index-xr07r80d.js} +513 -483
  43. package/dist/cli/{index-gm3pjfhb.js → index-y1zcsf2a.js} +1 -1
  44. package/dist/cli/{index-tjwz5tct.js → index-ywysg26s.js} +4 -4
  45. package/dist/cli/index.js +28 -28
  46. package/dist/cli/{knowledge-escalator-s6z1x65b.js → knowledge-escalator-pjg2x75c.js} +8 -8
  47. package/dist/cli/{knowledge-events-nnyn6hst.js → knowledge-events-vxheptk8.js} +6 -6
  48. package/dist/cli/{knowledge-link-xc6yhxmz.js → knowledge-link-3tq8w6gs.js} +5 -5
  49. package/dist/cli/{knowledge-store-x4yfc7v3.js → knowledge-store-36mascwz.js} +6 -6
  50. package/dist/cli/{knowledge-validator-1v047gsa.js → knowledge-validator-anats6rj.js} +9 -9
  51. package/dist/cli/{pending-delegations-7dbc0j2a.js → pending-delegations-tmyf18p4.js} +5 -5
  52. package/dist/cli/{pr-subscriptions-m943zn6g.js → pr-subscriptions-hhx6yk24.js} +5 -5
  53. package/dist/cli/{runner-x1hy047y.js → runner-w30gazew.js} +6 -6
  54. package/dist/cli/{scan-cursor-jxajj6s7.js → scan-cursor-apbz9eez.js} +7 -7
  55. package/dist/cli/{schema-fj32eyae.js → schema-x9fr2ey8.js} +3 -1
  56. package/dist/cli/{scope-persistence-w7zen2wz.js → scope-persistence-3f0d4fwv.js} +4 -4
  57. package/dist/cli/{skill-generator-rt0jqcsa.js → skill-generator-20czn4kd.js} +10 -10
  58. package/dist/cli/{telemetry-0yqed3jv.js → telemetry-4tgrfhaz.js} +1 -1
  59. package/dist/cli/{worktree-collision-ownership-hjrt5hx2.js → worktree-collision-ownership-vexb0b6r.js} +38 -8
  60. package/dist/config/context-window.d.ts +210 -0
  61. package/dist/config/schema.d.ts +18 -0
  62. package/dist/evaluation/ephemeral-agent-dispatcher.d.ts +18 -0
  63. package/dist/evaluation/model-dispatcher.d.ts +1 -0
  64. package/dist/hooks/delegate-ack-collector.d.ts +1 -1
  65. package/dist/hooks/delegation-gate/worktree-collision-ownership.d.ts +7 -0
  66. package/dist/hooks/delegation-gate/worktree-isolation.d.ts +2 -1
  67. package/dist/hooks/delegation-gate/worktree-provisioning-owner.d.ts +6 -0
  68. package/dist/hooks/index.d.ts +2 -2
  69. package/dist/hooks/init-orphan-recovery.d.ts +4 -1
  70. package/dist/hooks/knowledge-application-gate.d.ts +2 -1
  71. package/dist/hooks/knowledge-application.d.ts +5 -4
  72. package/dist/hooks/knowledge-injector.d.ts +16 -0
  73. package/dist/hooks/knowledge-types.d.ts +1 -1
  74. package/dist/hooks/messages-transform.d.ts +18 -0
  75. package/dist/hooks/model-limits.d.ts +88 -30
  76. package/dist/index.js +501 -499
  77. package/dist/memory/injector.d.ts +2 -0
  78. package/dist/plan/manager.d.ts +2 -0
  79. package/dist/services/context-budget-service.d.ts +6 -1
  80. package/dist/services/run-memory.d.ts +53 -5
  81. package/dist/services/status-service.d.ts +14 -0
  82. package/dist/state.d.ts +37 -0
  83. package/dist/tools/context-status.d.ts +3 -1
  84. package/dist/tools/update-task-status.d.ts +2 -0
  85. package/dist/turbo/lean/runner.d.ts +10 -0
  86. package/dist/utils/ephemeral-session-teardown.d.ts +98 -0
  87. package/dist/utils/path-security.d.ts +95 -1
  88. package/dist/utils/swarm-artifact-cache.d.ts +13 -0
  89. package/dist/worktree/index.d.ts +1 -1
  90. package/dist/worktree/merge.d.ts +36 -3
  91. package/package.json +1 -1
@@ -3,7 +3,7 @@ import {
3
3
  EvaluationRunV1Schema,
4
4
  GateAuditResultV1Schema,
5
5
  getProtectedEvaluationRunIds
6
- } from "./index-ngqxzkqm.js";
6
+ } from "./index-s608d3hw.js";
7
7
  import {
8
8
  readEffectiveSpecSync
9
9
  } from "./index-9ss2m4rs.js";
@@ -12,7 +12,7 @@ import {
12
12
  readTaskEvidence,
13
13
  readTaskEvidenceRaw,
14
14
  sanitizeTaskId
15
- } from "./index-39k6y018.js";
15
+ } from "./index-5w7ekpy3.js";
16
16
  import {
17
17
  EVIDENCE_MAX_JSON_BYTES,
18
18
  EvidenceBundleSchema,
@@ -40,7 +40,7 @@ import {
40
40
  replayFromLedgerWithStatus,
41
41
  takeSnapshotEvent,
42
42
  takeSnapshotWithRetry
43
- } from "./index-4fmztnrq.js";
43
+ } from "./index-4xbyy6aw.js";
44
44
  import {
45
45
  PlanSchema
46
46
  } from "./index-1kz6da87.js";
@@ -50,16 +50,17 @@ import {
50
50
  import {
51
51
  readSwarmFileAsync,
52
52
  validateSwarmPath
53
- } from "./index-4qzeef9h.js";
53
+ } from "./index-x9q4bbhd.js";
54
54
  import {
55
+ invalidateCachedArtifact,
55
56
  readCachedParsedFile
56
- } from "./index-fsrp8wp3.js";
57
+ } from "./index-ed5ykebp.js";
57
58
  import {
58
59
  withEvidenceLock
59
- } from "./index-xybtaph7.js";
60
+ } from "./index-5j6chvj8.js";
60
61
  import {
61
62
  emit
62
- } from "./index-10mz5js6.js";
63
+ } from "./index-a5eba0t8.js";
63
64
  import {
64
65
  ZodError,
65
66
  init_zod
@@ -980,7 +981,7 @@ import {
980
981
  writeFileSync as writeFileSync4
981
982
  } from "fs";
982
983
  import * as fsPromises from "fs/promises";
983
- import * as path7 from "path";
984
+ import * as path8 from "path";
984
985
 
985
986
  // src/git/branch.ts
986
987
  init_executor();
@@ -1586,18 +1587,299 @@ var _internals5 = {
1586
1587
  readLaneEnvFileFromDiskSync
1587
1588
  };
1588
1589
 
1589
- // src/turbo/epic/state.ts
1590
+ // src/services/run-memory.ts
1591
+ import * as crypto2 from "crypto";
1592
+ import * as fs2 from "fs/promises";
1593
+
1594
+ // src/hooks/context-sanitizer.ts
1595
+ function sanitizeContextText(text) {
1596
+ return text.split("").filter((char) => {
1597
+ const code = char.charCodeAt(0);
1598
+ return code === 9 || code === 10 || code === 13 || code > 31 && code !== 127;
1599
+ }).join("").replace(/[\u00AD\u200B-\u200F\u2060-\u2064\uFEFF]/g, "").replace(/[\u202A-\u202E\u2066-\u2069]/g, "").replace(/```/g, "` ` `").replace(/^system\s*:/gim, "[BLOCKED]:").replace(/<system\b[^>]*>/gi, "[BLOCKED-TAG]").replace(/<\/system>/gi, "[/BLOCKED-TAG]").replace(/<tool_call\b[^>]*>/gi, "[BLOCKED-TOOL]").replace(/<\/tool_call>/gi, "[/BLOCKED-TOOL]").replace(/<\/\w+>/g, "[/BLOCKED-TAG]");
1600
+ }
1601
+
1602
+ // src/services/run-memory.ts
1590
1603
  init_logger();
1591
- import * as fs2 from "fs";
1604
+
1605
+ // src/utils/path-security.ts
1592
1606
  import * as path5 from "path";
1607
+ function containsPathTraversal(str) {
1608
+ if (/\.\.[/\\]/.test(str))
1609
+ return true;
1610
+ if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
1611
+ return true;
1612
+ if (/%2e%2e/i.test(str))
1613
+ return true;
1614
+ if (/%2e\./i.test(str))
1615
+ return true;
1616
+ if (/%2e/i.test(str) && /\.\./.test(str))
1617
+ return true;
1618
+ if (/%252e%252e/i.test(str))
1619
+ return true;
1620
+ if (/\uff0e/.test(str))
1621
+ return true;
1622
+ if (/\u3002/.test(str))
1623
+ return true;
1624
+ if (/\uff65/.test(str))
1625
+ return true;
1626
+ if (/%2f/i.test(str))
1627
+ return true;
1628
+ if (/%5c/i.test(str))
1629
+ return true;
1630
+ return false;
1631
+ }
1632
+ function containsControlChars(str) {
1633
+ for (const ch of str) {
1634
+ const code = ch.codePointAt(0);
1635
+ if (code === undefined)
1636
+ continue;
1637
+ if (code <= 31 || code >= 127 && code <= 159)
1638
+ return true;
1639
+ if (code >= 8234 && code <= 8238)
1640
+ return true;
1641
+ if (code >= 8294 && code <= 8297)
1642
+ return true;
1643
+ }
1644
+ return false;
1645
+ }
1646
+ function validateWorkspaceRoot(directory) {
1647
+ validateProjectDirectory(directory);
1648
+ }
1649
+ function validateProjectDirectory(directory) {
1650
+ if (!directory || directory.trim() === "") {
1651
+ throw new Error("Invalid project directory: empty");
1652
+ }
1653
+ if (containsPathTraversal(directory)) {
1654
+ throw new Error("Invalid project directory: path traversal detected");
1655
+ }
1656
+ if (containsControlChars(directory)) {
1657
+ throw new Error("Invalid project directory: control characters detected");
1658
+ }
1659
+ if (!path5.isAbsolute(directory) && !/^[A-Za-z]:[/\\]/.test(directory)) {
1660
+ throw new Error("Invalid project directory: must be an absolute path");
1661
+ }
1662
+ assertNotSystemLocation(directory);
1663
+ }
1664
+ var DENIED_ROOT_SUBTREES = new Set([
1665
+ "etc",
1666
+ "usr",
1667
+ "bin",
1668
+ "sbin",
1669
+ "lib",
1670
+ "lib64",
1671
+ "boot",
1672
+ "dev",
1673
+ "proc",
1674
+ "sys",
1675
+ "windows",
1676
+ "winnt",
1677
+ "program files",
1678
+ "program files (x86)",
1679
+ "programdata",
1680
+ "system volume information"
1681
+ ]);
1682
+ var DENIED_EXACT_ROOT_CHILDREN = new Set(["users", "home", "root"]);
1683
+ function assertNotSystemLocation(directory) {
1684
+ for (const parser of [path5.win32, path5.posix]) {
1685
+ if (!parser.isAbsolute(directory))
1686
+ continue;
1687
+ const normalized = parser.normalize(directory);
1688
+ const { root } = parser.parse(normalized);
1689
+ const segments = normalized.slice(root.length).split(/[/\\]/).filter((segment) => segment.length > 0);
1690
+ if (segments.length === 0) {
1691
+ throw new Error(`Invalid project directory: filesystem root is not a project root (${directory})`);
1692
+ }
1693
+ const first = (segments[0] ?? "").toLowerCase();
1694
+ if (DENIED_ROOT_SUBTREES.has(first) || segments.length === 1 && DENIED_EXACT_ROOT_CHILDREN.has(first)) {
1695
+ throw new Error(`Invalid project directory: system location is not a project root (${directory})`);
1696
+ }
1697
+ }
1698
+ }
1699
+
1700
+ // src/services/run-memory.ts
1701
+ var RUN_MEMORY_FILENAME = "run-memory.jsonl";
1702
+ var MAX_SUMMARY_TOKENS = 500;
1703
+ function generateTaskFingerprint(taskId, fileTargets) {
1704
+ const sortedFiles = [...fileTargets].sort().join(",");
1705
+ const hash = crypto2.createHash("sha256").update(taskId + sortedFiles).digest("hex");
1706
+ return hash.slice(0, 8);
1707
+ }
1708
+ async function recordOutcome(directory, entry) {
1709
+ validateWorkspaceRoot(directory);
1710
+ const resolvedPath = validateSwarmPath(directory, RUN_MEMORY_FILENAME);
1711
+ const line = `${JSON.stringify(entry)}
1712
+ `;
1713
+ await fs2.appendFile(resolvedPath, line, { encoding: "utf-8" });
1714
+ }
1715
+ async function getTaskHistory(directory, taskId) {
1716
+ validateWorkspaceRoot(directory);
1717
+ const content = await readSwarmFileAsync(directory, RUN_MEMORY_FILENAME);
1718
+ if (!content) {
1719
+ return [];
1720
+ }
1721
+ const entries = [];
1722
+ const lines = content.split(`
1723
+ `);
1724
+ for (const line of lines) {
1725
+ if (!line.trim())
1726
+ continue;
1727
+ try {
1728
+ const entry = JSON.parse(line);
1729
+ if (entry.taskId === taskId) {
1730
+ entries.push(entry);
1731
+ }
1732
+ } catch {}
1733
+ }
1734
+ return entries;
1735
+ }
1736
+ async function recordTaskAttempt(directory, input) {
1737
+ try {
1738
+ let history = [];
1739
+ try {
1740
+ history = await _internals6.getTaskHistory(directory, input.taskId);
1741
+ } catch (err) {
1742
+ warn(`[run-memory] history read for task ${input.taskId} failed; recording with attemptNumber 1: ${err instanceof Error ? err.message : String(err)}`);
1743
+ }
1744
+ const entry = {
1745
+ timestamp: new Date().toISOString(),
1746
+ taskId: input.taskId,
1747
+ taskFingerprint: _internals6.generateTaskFingerprint(input.taskId, input.fileTargets ?? []),
1748
+ agent: input.agent,
1749
+ outcome: input.outcome,
1750
+ attemptNumber: history.length + 1
1751
+ };
1752
+ if (input.failureReason) {
1753
+ entry.failureReason = input.failureReason;
1754
+ }
1755
+ if (input.fileTargets && input.fileTargets.length > 0) {
1756
+ entry.filesModified = input.fileTargets;
1757
+ }
1758
+ if (typeof input.durationMs === "number") {
1759
+ entry.durationMs = input.durationMs;
1760
+ }
1761
+ await _internals6.recordOutcome(directory, entry);
1762
+ } catch (err) {
1763
+ warn(`[run-memory] failed to record ${input.outcome} for task ${input.taskId}: ${err instanceof Error ? err.message : String(err)}`);
1764
+ }
1765
+ }
1766
+ function groupByTaskId(entries) {
1767
+ const groups = new Map;
1768
+ for (const entry of entries) {
1769
+ const existing = groups.get(entry.taskId) || [];
1770
+ existing.push(entry);
1771
+ groups.set(entry.taskId, existing);
1772
+ }
1773
+ return groups;
1774
+ }
1775
+ function summarizeTask(taskId, entries) {
1776
+ let lastFailure;
1777
+ let lastFailureIndex = -1;
1778
+ let lastPassIndex = -1;
1779
+ let lastPass;
1780
+ let failCount = 0;
1781
+ entries.forEach((entry, index) => {
1782
+ if (entry.outcome === "fail" || entry.outcome === "retry") {
1783
+ failCount++;
1784
+ lastFailure = entry;
1785
+ lastFailureIndex = index;
1786
+ } else if (entry.outcome === "pass") {
1787
+ lastPass = entry;
1788
+ lastPassIndex = index;
1789
+ }
1790
+ });
1791
+ if (!lastFailure) {
1792
+ return null;
1793
+ }
1794
+ const passResolvesFailure = lastPassIndex > lastFailureIndex;
1795
+ if (lastPass && passResolvesFailure) {
1796
+ const passAttempt = lastPass.attemptNumber;
1797
+ const failAttempt = lastFailure.attemptNumber;
1798
+ const reason = sanitizeContextText(lastFailure.failureReason || "unknown");
1799
+ return `Task ${taskId}: FAILED attempt ${failAttempt} \u2014 ${reason}. Passed on attempt ${passAttempt}.`;
1800
+ } else {
1801
+ const reason = sanitizeContextText(lastFailure.failureReason || "unknown");
1802
+ return `Task ${taskId}: FAILED ${failCount} times \u2014 last: ${reason}. Still failing.`;
1803
+ }
1804
+ }
1805
+ async function getRunMemorySummary(directory) {
1806
+ validateWorkspaceRoot(directory);
1807
+ const content = await readSwarmFileAsync(directory, RUN_MEMORY_FILENAME);
1808
+ if (!content) {
1809
+ return null;
1810
+ }
1811
+ const entries = [];
1812
+ const lines = content.split(`
1813
+ `);
1814
+ for (const line of lines) {
1815
+ if (!line.trim())
1816
+ continue;
1817
+ try {
1818
+ const entry = JSON.parse(line);
1819
+ entries.push(entry);
1820
+ } catch {}
1821
+ }
1822
+ if (entries.length === 0) {
1823
+ return null;
1824
+ }
1825
+ const groups = _internals6.groupByTaskId(entries);
1826
+ const summaries = [];
1827
+ for (const [taskId, taskEntries] of groups) {
1828
+ const summary = _internals6.summarizeTask(taskId, taskEntries);
1829
+ if (summary) {
1830
+ summaries.push(summary);
1831
+ }
1832
+ }
1833
+ if (summaries.length === 0) {
1834
+ return null;
1835
+ }
1836
+ const prefix = `[FOR: architect, coder]
1837
+ ## RUN MEMORY \u2014 Previous Task Outcomes
1838
+ `;
1839
+ const suffix = `
1840
+ Use this data to avoid repeating known failure patterns.`;
1841
+ let summaryText = summaries.join(`
1842
+ `);
1843
+ const estimateTokens = (text) => {
1844
+ return Math.ceil(text.length * 0.33);
1845
+ };
1846
+ const totalText = prefix + summaryText + suffix;
1847
+ const estimatedTokens = estimateTokens(totalText);
1848
+ if (estimatedTokens > MAX_SUMMARY_TOKENS) {
1849
+ const prefixTokens = estimateTokens(prefix);
1850
+ const suffixTokens = estimateTokens(suffix);
1851
+ const availableContentTokens = MAX_SUMMARY_TOKENS - prefixTokens - suffixTokens;
1852
+ if (availableContentTokens > 0) {
1853
+ const maxContentChars = Math.floor(availableContentTokens / 0.33);
1854
+ summaryText = summaryText.slice(0, maxContentChars);
1855
+ } else {
1856
+ summaryText = "";
1857
+ }
1858
+ }
1859
+ return prefix + summaryText + suffix;
1860
+ }
1861
+ var _internals6 = {
1862
+ generateTaskFingerprint,
1863
+ recordOutcome,
1864
+ recordTaskAttempt,
1865
+ getTaskHistory,
1866
+ getRunMemorySummary,
1867
+ groupByTaskId,
1868
+ summarizeTask
1869
+ };
1870
+
1871
+ // src/turbo/epic/state.ts
1872
+ init_logger();
1873
+ import * as fs3 from "fs";
1874
+ import * as path6 from "path";
1593
1875
  var STATE_FILE = "epic-state.json";
1594
1876
  function nowISO() {
1595
1877
  return new Date().toISOString();
1596
1878
  }
1597
1879
  function ensureSwarmDir(directory) {
1598
- const swarmDir = path5.resolve(directory, ".swarm");
1599
- if (!fs2.existsSync(swarmDir)) {
1600
- fs2.mkdirSync(swarmDir, { recursive: true });
1880
+ const swarmDir = path6.resolve(directory, ".swarm");
1881
+ if (!fs3.existsSync(swarmDir)) {
1882
+ fs3.mkdirSync(swarmDir, { recursive: true });
1601
1883
  }
1602
1884
  return swarmDir;
1603
1885
  }
@@ -1617,17 +1899,17 @@ function markStateUnreadable(directory, reason) {
1617
1899
  }
1618
1900
  function readPersisted(directory) {
1619
1901
  try {
1620
- const filePath = path5.join(directory, ".swarm", STATE_FILE);
1621
- if (!fs2.existsSync(filePath)) {
1902
+ const filePath = path6.join(directory, ".swarm", STATE_FILE);
1903
+ if (!fs3.existsSync(filePath)) {
1622
1904
  const seed = emptyPersisted();
1623
1905
  try {
1624
1906
  ensureSwarmDir(directory);
1625
- fs2.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
1907
+ fs3.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
1626
1908
  `, "utf-8");
1627
1909
  } catch {}
1628
1910
  return seed;
1629
1911
  }
1630
- const raw = fs2.readFileSync(filePath, "utf-8");
1912
+ const raw = fs3.readFileSync(filePath, "utf-8");
1631
1913
  const parsed = JSON.parse(raw);
1632
1914
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
1633
1915
  markStateUnreadable(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
@@ -1653,7 +1935,7 @@ function writePersisted(directory, persisted) {
1653
1935
  let payload;
1654
1936
  try {
1655
1937
  ensureSwarmDir(directory);
1656
- filePath = path5.join(directory, ".swarm", STATE_FILE);
1938
+ filePath = path6.join(directory, ".swarm", STATE_FILE);
1657
1939
  tmpPath = `${filePath}.tmp.${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1658
1940
  persisted.updatedAt = nowISO();
1659
1941
  payload = `${JSON.stringify(persisted, null, 2)}
@@ -1664,14 +1946,14 @@ function writePersisted(directory, persisted) {
1664
1946
  throw new Error(`Epic state persistence prepare failed: ${msg}`);
1665
1947
  }
1666
1948
  try {
1667
- fs2.writeFileSync(tmpPath, payload, "utf-8");
1668
- fs2.renameSync(tmpPath, filePath);
1949
+ fs3.writeFileSync(tmpPath, payload, "utf-8");
1950
+ fs3.renameSync(tmpPath, filePath);
1669
1951
  } catch (error2) {
1670
1952
  const msg = error2 instanceof Error ? error2.message : String(error2);
1671
1953
  error(`[turbo/epic/state] Failed to persist ${STATE_FILE} atomically: ${msg}`);
1672
1954
  try {
1673
- if (fs2.existsSync(tmpPath))
1674
- fs2.unlinkSync(tmpPath);
1955
+ if (fs3.existsSync(tmpPath))
1956
+ fs3.unlinkSync(tmpPath);
1675
1957
  } catch {}
1676
1958
  throw new Error(`Epic state persistence failed: ${msg}`);
1677
1959
  }
@@ -1702,7 +1984,7 @@ function isEpicModeActive(directory, sessionID) {
1702
1984
  function isEpicModeActiveForProject(directory) {
1703
1985
  if (stateUnreadableMap.get(directory))
1704
1986
  return false;
1705
- if (!fs2.existsSync(path5.join(directory, ".swarm", STATE_FILE))) {
1987
+ if (!fs3.existsSync(path6.join(directory, ".swarm", STATE_FILE))) {
1706
1988
  return false;
1707
1989
  }
1708
1990
  const persisted = readPersisted(directory);
@@ -1753,11 +2035,11 @@ function isLockContentionError(err) {
1753
2035
  return INDEX_LOCK_ERROR_RE.test(msg);
1754
2036
  }
1755
2037
  async function commitTaskCompletion(directory, taskId, description, scopePaths) {
1756
- if (!_internals6.isGitRepo(directory)) {
2038
+ if (!_internals7.isGitRepo(directory)) {
1757
2039
  return { committed: false, reason: "no-git" };
1758
2040
  }
1759
2041
  try {
1760
- if (_internals6.hasExistingTaskCommit(directory, taskId)) {
2042
+ if (_internals7.hasExistingTaskCommit(directory, taskId)) {
1761
2043
  return { committed: true, reason: "idempotent-skip" };
1762
2044
  }
1763
2045
  } catch {}
@@ -1778,15 +2060,15 @@ async function commitTaskCompletion(directory, taskId, description, scopePaths)
1778
2060
  for (let attempt = 0;attempt <= INDEX_LOCK_BACKOFF_MS.length; attempt++) {
1779
2061
  try {
1780
2062
  if (paths.length > 0) {
1781
- _internals6.stageScopedPaths(directory, paths);
2063
+ _internals7.stageScopedPaths(directory, paths);
1782
2064
  }
1783
- _internals6.commitAllowEmpty(directory, message);
1784
- const sha = _internals6.gitHeadSha(directory);
2065
+ _internals7.commitAllowEmpty(directory, message);
2066
+ const sha = _internals7.gitHeadSha(directory);
1785
2067
  return { committed: true, reason: "success", sha };
1786
2068
  } catch (err) {
1787
2069
  lastError = err;
1788
2070
  if (attempt < INDEX_LOCK_BACKOFF_MS.length && isLockContentionError(err)) {
1789
- await _internals6.sleep(INDEX_LOCK_BACKOFF_MS[attempt]);
2071
+ await _internals7.sleep(INDEX_LOCK_BACKOFF_MS[attempt]);
1790
2072
  continue;
1791
2073
  }
1792
2074
  break;
@@ -1796,7 +2078,7 @@ async function commitTaskCompletion(directory, taskId, description, scopePaths)
1796
2078
  criticalWarn(`[epic:task-commit] commit for task ${taskId} failed (non-fatal): ${msg}`);
1797
2079
  return { committed: false, reason: "commit-failed", error: msg };
1798
2080
  }
1799
- var _internals6 = {
2081
+ var _internals7 = {
1800
2082
  isGitRepo: (cwd) => isGitRepo(cwd),
1801
2083
  stageScopedPaths: (cwd, paths) => {
1802
2084
  const CHUNK = 200;
@@ -1811,7 +2093,7 @@ var _internals6 = {
1811
2093
  gitHeadSha: (cwd) => {
1812
2094
  return _internals5.gitExec(["rev-parse", "HEAD"], cwd).trim();
1813
2095
  },
1814
- sleep: (ms) => new Promise((resolve2) => setTimeout(resolve2, ms)),
2096
+ sleep: (ms) => new Promise((resolve3) => setTimeout(resolve3, ms)),
1815
2097
  hasExistingTaskCommit: (cwd, taskId) => {
1816
2098
  const safeId = scrubTaskIdForGitSubject(taskId);
1817
2099
  const escaped = safeId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1828,8 +2110,8 @@ var _internals6 = {
1828
2110
  };
1829
2111
 
1830
2112
  // src/turbo/lean/conflicts.ts
1831
- import * as fs3 from "fs";
1832
- import * as path6 from "path";
2113
+ import * as fs4 from "fs";
2114
+ import * as path7 from "path";
1833
2115
 
1834
2116
  // src/utils/path.ts
1835
2117
  function normalizePath(p) {
@@ -1957,12 +2239,12 @@ function isProtectedPath(normalizedPath) {
1957
2239
  return false;
1958
2240
  }
1959
2241
  function readTaskScopes(directory, taskId) {
1960
- const scopePath = path6.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
2242
+ const scopePath = path7.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
1961
2243
  try {
1962
- if (!fs3.existsSync(scopePath)) {
2244
+ if (!fs4.existsSync(scopePath)) {
1963
2245
  return null;
1964
2246
  }
1965
- const raw = fs3.readFileSync(scopePath, "utf-8");
2247
+ const raw = fs4.readFileSync(scopePath, "utf-8");
1966
2248
  const parsed = JSON.parse(raw);
1967
2249
  if (!parsed || !Array.isArray(parsed.files)) {
1968
2250
  return null;
@@ -1974,17 +2256,17 @@ function readTaskScopes(directory, taskId) {
1974
2256
  }
1975
2257
 
1976
2258
  // src/utils/spec-hash.ts
1977
- import { createHash } from "crypto";
2259
+ import { createHash as createHash2 } from "crypto";
1978
2260
  import * as fsSync2 from "fs";
1979
- import { join as join5 } from "path";
2261
+ import { join as join6 } from "path";
1980
2262
  async function computeSpecHash(directory) {
1981
- const spec = _internals7.readEffectiveSpecSync(directory);
2263
+ const spec = _internals8.readEffectiveSpecSync(directory);
1982
2264
  if (!spec)
1983
2265
  return null;
1984
- return createHash("sha256").update(spec.content, "utf-8").digest("hex");
2266
+ return createHash2("sha256").update(spec.content, "utf-8").digest("hex");
1985
2267
  }
1986
2268
  async function isSpecStale(directory, plan) {
1987
- const currentHash = await _internals7.computeSpecHash(directory);
2269
+ const currentHash = await _internals8.computeSpecHash(directory);
1988
2270
  if (!plan.specHash) {
1989
2271
  return { stale: false };
1990
2272
  }
@@ -2005,7 +2287,7 @@ async function isSpecStale(directory, plan) {
2005
2287
  return { stale: false };
2006
2288
  }
2007
2289
  function isObligationPreserving(directory) {
2008
- const snapshotPath = join5(directory, ".swarm", "spec-snapshot.md");
2290
+ const snapshotPath = join6(directory, ".swarm", "spec-snapshot.md");
2009
2291
  try {
2010
2292
  let recordedContent;
2011
2293
  try {
@@ -2013,15 +2295,15 @@ function isObligationPreserving(directory) {
2013
2295
  } catch {
2014
2296
  return false;
2015
2297
  }
2016
- const currentResult = _internals7.readEffectiveSpecSync(directory);
2298
+ const currentResult = _internals8.readEffectiveSpecSync(directory);
2017
2299
  if (!currentResult || !currentResult.content) {
2018
2300
  return false;
2019
2301
  }
2020
2302
  const currentContent = currentResult.content;
2021
- const normalize = (text) => text.replace(/\r\n/g, `
2303
+ const normalize2 = (text) => text.replace(/\r\n/g, `
2022
2304
  `);
2023
- const recordedNorm = normalize(recordedContent);
2024
- const currentNorm = normalize(currentContent);
2305
+ const recordedNorm = normalize2(recordedContent);
2306
+ const currentNorm = normalize2(currentContent);
2025
2307
  const splitParagraphs = (text) => text.split(/\n\s*\n/).map((p) => p.trim()).filter((p) => p.length > 0);
2026
2308
  const recordedParagraphs = splitParagraphs(recordedNorm);
2027
2309
  const currentParagraphs = splitParagraphs(currentNorm);
@@ -2043,7 +2325,7 @@ function isObligationPreserving(directory) {
2043
2325
  return false;
2044
2326
  }
2045
2327
  }
2046
- var _internals7 = {
2328
+ var _internals8 = {
2047
2329
  computeSpecDiff,
2048
2330
  computeSpecHash,
2049
2331
  isObligationPreserving,
@@ -2051,7 +2333,7 @@ var _internals7 = {
2051
2333
  readEffectiveSpecSync
2052
2334
  };
2053
2335
  function computeSpecDiff(directory) {
2054
- const snapshotPath = join5(directory, ".swarm", "spec-snapshot.md");
2336
+ const snapshotPath = join6(directory, ".swarm", "spec-snapshot.md");
2055
2337
  let recordedContent;
2056
2338
  try {
2057
2339
  const snapshotSize = fsSync2.statSync(snapshotPath).size;
@@ -2063,16 +2345,16 @@ function computeSpecDiff(directory) {
2063
2345
  } catch {
2064
2346
  return null;
2065
2347
  }
2066
- const spec = _internals7.readEffectiveSpecSync(directory);
2348
+ const spec = _internals8.readEffectiveSpecSync(directory);
2067
2349
  if (!spec) {
2068
2350
  return null;
2069
2351
  }
2070
2352
  const currentContent = spec.content;
2071
- const normalize = (text) => text.replace(/\r\n/g, `
2353
+ const normalize2 = (text) => text.replace(/\r\n/g, `
2072
2354
  `);
2073
- const recordedLines = normalize(recordedContent).split(`
2355
+ const recordedLines = normalize2(recordedContent).split(`
2074
2356
  `);
2075
- const currentLines = normalize(currentContent).split(`
2357
+ const currentLines = normalize2(currentContent).split(`
2076
2358
  `);
2077
2359
  const diffLines = computeLcsDiff(recordedLines, currentLines);
2078
2360
  const MAX_DIFF_LINES = 300;
@@ -2157,7 +2439,7 @@ var startupLedgerCheckedWorkspaces = new Set;
2157
2439
  var ledgerStaleWorkspaces = new Set;
2158
2440
  var recoveryMutexes = new Map;
2159
2441
  var PLAN_JSON_CACHE_NAMESPACE = "plan-json:validated:v1";
2160
- var _internals8 = {
2442
+ var _internals9 = {
2161
2443
  loadPlan,
2162
2444
  loadPlanJsonOnly,
2163
2445
  regeneratePlanMarkdown,
@@ -2165,7 +2447,8 @@ var _internals8 = {
2165
2447
  isEpicModeActiveForProject,
2166
2448
  readTaskScopes,
2167
2449
  commitTaskCompletion,
2168
- getWorktreeMergeFailure
2450
+ getWorktreeMergeFailure,
2451
+ recordTaskAttempt
2169
2452
  };
2170
2453
  var CAS_BACKOFF_START_MS = 5;
2171
2454
  var CAS_BACKOFF_CAP_MS = 250;
@@ -2254,7 +2537,7 @@ async function retryCasWithBackoff(directory, eventInput, options) {
2254
2537
  expectedHashPrefix: currentExpected.slice(0, 8),
2255
2538
  delayMs
2256
2539
  });
2257
- await new Promise((resolve3) => setTimeout(resolve3, delayMs));
2540
+ await new Promise((resolve4) => setTimeout(resolve4, delayMs));
2258
2541
  if (options.verifyValid) {
2259
2542
  const stillValid = await options.verifyValid();
2260
2543
  if (!stillValid)
@@ -2297,7 +2580,7 @@ async function getLatestLedgerHash(directory) {
2297
2580
  }
2298
2581
  }
2299
2582
  async function surfaceLedgerStaleIfPersisted(directory, plan) {
2300
- const resolvedWorkspace = path7.resolve(directory);
2583
+ const resolvedWorkspace = path8.resolve(directory);
2301
2584
  if (!ledgerStaleWorkspaces.has(resolvedWorkspace)) {
2302
2585
  return plan;
2303
2586
  }
@@ -2316,7 +2599,7 @@ async function surfaceLedgerStaleIfPersisted(directory, plan) {
2316
2599
  return plan;
2317
2600
  }
2318
2601
  async function parsePlanJsonCached(directory) {
2319
- const planJsonPath = path7.resolve(directory, ".swarm", "plan.json");
2602
+ const planJsonPath = path8.resolve(directory, ".swarm", "plan.json");
2320
2603
  return readCachedParsedFile(planJsonPath, PLAN_JSON_CACHE_NAMESPACE, () => readSwarmFileAsync(directory, "plan.json"), (planJsonContent) => {
2321
2604
  if (planJsonContent.includes("\x00") || planJsonContent.includes("\uFFFD")) {
2322
2605
  throw new Error("Plan rejected: .swarm/plan.json contains null bytes or invalid encoding");
@@ -2376,13 +2659,13 @@ async function isPlanMdInSync(directory, plan, cache) {
2376
2659
  return false;
2377
2660
  }
2378
2661
  async function regeneratePlanMarkdown(directory, plan) {
2379
- const swarmDir = path7.resolve(directory, ".swarm");
2662
+ const swarmDir = path8.resolve(directory, ".swarm");
2380
2663
  const contentHash = computePlanContentHash(plan);
2381
2664
  const markdown = derivePlanMarkdown(plan);
2382
2665
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
2383
2666
  ${markdown}`;
2384
- const mdPath = path7.join(swarmDir, "plan.md");
2385
- const mdTempPath = path7.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2667
+ const mdPath = path8.join(swarmDir, "plan.md");
2668
+ const mdTempPath = path8.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2386
2669
  try {
2387
2670
  await bunWrite(mdTempPath, markdownWithHash);
2388
2671
  renameSync2(mdTempPath, mdPath);
@@ -2391,6 +2674,7 @@ ${markdown}`;
2391
2674
  unlinkSync2(mdTempPath);
2392
2675
  } catch {}
2393
2676
  }
2677
+ invalidateCachedArtifact(mdPath);
2394
2678
  }
2395
2679
  async function loadPlan(directory, cache) {
2396
2680
  const planJsonContent = await readSwarmFileAsync(directory, "plan.json", cache);
@@ -2406,7 +2690,7 @@ async function loadPlan(directory, cache) {
2406
2690
  const inSync = await isPlanMdInSync(directory, validated, cache);
2407
2691
  if (!inSync) {
2408
2692
  try {
2409
- await _internals8.regeneratePlanMarkdown(directory, validated);
2693
+ await _internals9.regeneratePlanMarkdown(directory, validated);
2410
2694
  } catch (regenError) {
2411
2695
  warn(`Failed to regenerate plan.md: ${regenError instanceof Error ? regenError.message : String(regenError)}. Proceeding with plan.json only.`);
2412
2696
  }
@@ -2414,7 +2698,7 @@ async function loadPlan(directory, cache) {
2414
2698
  if (await ledgerExists(directory)) {
2415
2699
  const planHash = computePlanHash(validated);
2416
2700
  const ledgerHash = await getLatestLedgerHash(directory);
2417
- const resolvedWorkspace = path7.resolve(directory);
2701
+ const resolvedWorkspace = path8.resolve(directory);
2418
2702
  if (!startupLedgerCheckedWorkspaces.has(resolvedWorkspace)) {
2419
2703
  startupLedgerCheckedWorkspaces.add(resolvedWorkspace);
2420
2704
  if (ledgerHash !== "" && planHash !== ledgerHash) {
@@ -2488,7 +2772,7 @@ async function loadPlan(directory, cache) {
2488
2772
  if (!preserving) {
2489
2773
  try {
2490
2774
  const diffInfo = computeSpecDiff(directory);
2491
- const specStalenessPath = path7.join(directory, ".swarm", "spec-staleness.json");
2775
+ const specStalenessPath = path8.join(directory, ".swarm", "spec-staleness.json");
2492
2776
  await fsPromises.writeFile(specStalenessPath, JSON.stringify({
2493
2777
  type: "spec_stale_detected",
2494
2778
  timestamp: new Date().toISOString(),
@@ -2500,9 +2784,10 @@ async function loadPlan(directory, cache) {
2500
2784
  diff: diffInfo?.diff ?? null,
2501
2785
  changedSections: diffInfo?.changedSections ?? []
2502
2786
  }, null, 2), "utf-8");
2787
+ invalidateCachedArtifact(specStalenessPath);
2503
2788
  } catch {}
2504
2789
  try {
2505
- const eventsPath = path7.join(directory, ".swarm", "events.jsonl");
2790
+ const eventsPath = path8.join(directory, ".swarm", "events.jsonl");
2506
2791
  const event = {
2507
2792
  type: "spec_stale_detected",
2508
2793
  timestamp: new Date().toISOString(),
@@ -2568,11 +2853,11 @@ async function loadPlan(directory, cache) {
2568
2853
  return migrated;
2569
2854
  }
2570
2855
  if (await ledgerExists(directory)) {
2571
- const resolvedDir = path7.resolve(directory);
2856
+ const resolvedDir = path8.resolve(directory);
2572
2857
  const existingMutex = recoveryMutexes.get(resolvedDir);
2573
2858
  if (existingMutex) {
2574
2859
  await existingMutex;
2575
- const postRecoveryPlan = await _internals8.loadPlanJsonOnly(directory);
2860
+ const postRecoveryPlan = await _internals9.loadPlanJsonOnly(directory);
2576
2861
  if (postRecoveryPlan)
2577
2862
  return postRecoveryPlan;
2578
2863
  }
@@ -2632,7 +2917,7 @@ async function loadPlan(directory, cache) {
2632
2917
  return null;
2633
2918
  }
2634
2919
  async function savePlanWithAutoAcknowledgedRemovals(directory, plan, source, reason, options) {
2635
- const existing = await _internals8.loadPlanJsonOnly(directory);
2920
+ const existing = await _internals9.loadPlanJsonOnly(directory);
2636
2921
  const newIds = new Set;
2637
2922
  for (const phase of plan.phases) {
2638
2923
  for (const task of phase.tasks)
@@ -2660,7 +2945,7 @@ async function savePlan(directory, plan, options) {
2660
2945
  const validated = PlanSchema.parse(plan);
2661
2946
  if (options?.preserveCompletedStatuses !== false) {
2662
2947
  try {
2663
- const currentPlan2 = await _internals8.loadPlanJsonOnly(directory);
2948
+ const currentPlan2 = await _internals9.loadPlanJsonOnly(directory);
2664
2949
  if (currentPlan2) {
2665
2950
  const completedTaskIds = new Set;
2666
2951
  for (const phase of currentPlan2.phases) {
@@ -2682,7 +2967,7 @@ async function savePlan(directory, plan, options) {
2682
2967
  } catch {}
2683
2968
  }
2684
2969
  derivePhaseStatusesInPlace(validated);
2685
- const currentPlan = await _internals8.loadPlanJsonOnly(directory);
2970
+ const currentPlan = await _internals9.loadPlanJsonOnly(directory);
2686
2971
  const planId = derivePlanId(validated);
2687
2972
  const planHashForInit = computePlanHash(validated);
2688
2973
  if (!await ledgerExists(directory)) {
@@ -2697,9 +2982,9 @@ async function savePlan(directory, plan, options) {
2697
2982
  } else {
2698
2983
  const existingEvents = await readLedgerEvents(directory);
2699
2984
  if (existingEvents.length > 0 && existingEvents[0].plan_id !== planId) {
2700
- const swarmDir2 = path7.resolve(directory, ".swarm");
2701
- const oldLedgerPath = path7.join(swarmDir2, "plan-ledger.jsonl");
2702
- const oldLedgerBackupPath = path7.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
2985
+ const swarmDir2 = path8.resolve(directory, ".swarm");
2986
+ const oldLedgerPath = path8.join(swarmDir2, "plan-ledger.jsonl");
2987
+ const oldLedgerBackupPath = path8.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
2703
2988
  let backupExists = false;
2704
2989
  if (existsSync3(oldLedgerPath)) {
2705
2990
  try {
@@ -2737,7 +3022,7 @@ async function savePlan(directory, plan, options) {
2737
3022
  }
2738
3023
  }
2739
3024
  if (initSucceeded && backupExists) {
2740
- const archivePath = path7.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
3025
+ const archivePath = path8.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
2741
3026
  try {
2742
3027
  renameSync2(oldLedgerBackupPath, archivePath);
2743
3028
  warn(`[savePlan] Ledger identity mismatch (was "${existingEvents[0].plan_id}", now "${planId}") \u2014 archived old ledger to ${archivePath} and reinitializing.`);
@@ -2762,7 +3047,7 @@ async function savePlan(directory, plan, options) {
2762
3047
  const toRemove = archivedSiblings.slice(0, archivedSiblings.length - MAX_ARCHIVED_SIBLINGS);
2763
3048
  for (const file of toRemove) {
2764
3049
  try {
2765
- unlinkSync2(path7.join(swarmDir2, file));
3050
+ unlinkSync2(path8.join(swarmDir2, file));
2766
3051
  } catch {}
2767
3052
  }
2768
3053
  }
@@ -2827,7 +3112,7 @@ async function savePlan(directory, plan, options) {
2827
3112
  expectedHash: currentHash,
2828
3113
  planHashAfter: hashAfter,
2829
3114
  verifyValid: async () => {
2830
- const onDisk = await _internals8.loadPlanJsonOnly(directory);
3115
+ const onDisk = await _internals9.loadPlanJsonOnly(directory);
2831
3116
  if (!onDisk)
2832
3117
  return true;
2833
3118
  for (const p of onDisk.phases) {
@@ -2865,7 +3150,7 @@ async function savePlan(directory, plan, options) {
2865
3150
  expectedHash: currentHash,
2866
3151
  planHashAfter: hashAfter,
2867
3152
  verifyValid: async () => {
2868
- const onDisk = await _internals8.loadPlanJsonOnly(directory);
3153
+ const onDisk = await _internals9.loadPlanJsonOnly(directory);
2869
3154
  if (!onDisk)
2870
3155
  return true;
2871
3156
  for (const p of onDisk.phases) {
@@ -2905,9 +3190,9 @@ async function savePlan(directory, plan, options) {
2905
3190
  source: "savePlan_manager"
2906
3191
  });
2907
3192
  }
2908
- const swarmDir = path7.resolve(directory, ".swarm");
2909
- const planPath = path7.join(swarmDir, "plan.json");
2910
- const tempPath = path7.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3193
+ const swarmDir = path8.resolve(directory, ".swarm");
3194
+ const planPath = path8.join(swarmDir, "plan.json");
3195
+ const tempPath = path8.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2911
3196
  try {
2912
3197
  await bunWrite(tempPath, JSON.stringify(projectedPlan, null, 2));
2913
3198
  renameSync2(tempPath, planPath);
@@ -2916,8 +3201,9 @@ async function savePlan(directory, plan, options) {
2916
3201
  unlinkSync2(tempPath);
2917
3202
  } catch {}
2918
3203
  }
3204
+ invalidateCachedArtifact(planPath);
2919
3205
  try {
2920
- const markerPath = path7.join(swarmDir, ".plan-write-marker");
3206
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
2921
3207
  const inProgressMarker = JSON.stringify({
2922
3208
  source: "plan_manager",
2923
3209
  timestamp: new Date().toISOString(),
@@ -2932,8 +3218,8 @@ async function savePlan(directory, plan, options) {
2932
3218
  const markdown = derivePlanMarkdown(projectedPlan);
2933
3219
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
2934
3220
  ${markdown}`;
2935
- const mdPath = path7.join(swarmDir, "plan.md");
2936
- const mdTempPath = path7.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3221
+ const mdPath = path8.join(swarmDir, "plan.md");
3222
+ const mdTempPath = path8.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2937
3223
  try {
2938
3224
  await bunWrite(mdTempPath, markdownWithHash);
2939
3225
  renameSync2(mdTempPath, mdPath);
@@ -2942,6 +3228,7 @@ ${markdown}`;
2942
3228
  unlinkSync2(mdTempPath);
2943
3229
  } catch {}
2944
3230
  }
3231
+ invalidateCachedArtifact(mdPath);
2945
3232
  } catch (mdError) {
2946
3233
  const message = mdError instanceof Error ? mdError.message : String(mdError);
2947
3234
  warn(`[savePlan] plan.md write failed (non-fatal, plan.json is authoritative): ${message}`);
@@ -2954,7 +3241,7 @@ ${markdown}`;
2954
3241
  } catch {}
2955
3242
  }
2956
3243
  try {
2957
- const markerPath = path7.join(swarmDir, ".plan-write-marker");
3244
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
2958
3245
  const tasksCount = projectedPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
2959
3246
  const marker = JSON.stringify({
2960
3247
  source: "plan_manager",
@@ -2970,11 +3257,11 @@ async function rebuildPlan(directory, plan, options) {
2970
3257
  const targetPlan = plan ?? await replayFromLedger(directory);
2971
3258
  if (!targetPlan)
2972
3259
  return null;
2973
- const swarmDir = path7.join(directory, ".swarm");
2974
- const planPath = path7.join(swarmDir, "plan.json");
2975
- const mdPath = path7.join(swarmDir, "plan.md");
3260
+ const swarmDir = path8.join(directory, ".swarm");
3261
+ const planPath = path8.join(swarmDir, "plan.json");
3262
+ const mdPath = path8.join(swarmDir, "plan.md");
2976
3263
  mkdirSync3(swarmDir, { recursive: true });
2977
- const tempPlanPath = path7.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3264
+ const tempPlanPath = path8.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
2978
3265
  {
2979
3266
  const fd = openSync(tempPlanPath, "w");
2980
3267
  try {
@@ -2985,8 +3272,9 @@ async function rebuildPlan(directory, plan, options) {
2985
3272
  }
2986
3273
  }
2987
3274
  renameSync2(tempPlanPath, planPath);
3275
+ invalidateCachedArtifact(planPath);
2988
3276
  try {
2989
- const markerPath = path7.join(swarmDir, ".plan-write-marker");
3277
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
2990
3278
  const inProgressMarker = JSON.stringify({
2991
3279
  source: "plan_manager",
2992
3280
  timestamp: new Date().toISOString(),
@@ -3001,12 +3289,13 @@ async function rebuildPlan(directory, plan, options) {
3001
3289
  const markdown = derivePlanMarkdown(targetPlan);
3002
3290
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
3003
3291
  ${markdown}`;
3004
- const tempMdPath = path7.join(swarmDir, `plan.md.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3292
+ const tempMdPath = path8.join(swarmDir, `plan.md.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3005
3293
  await bunWrite(tempMdPath, markdownWithHash);
3006
3294
  renameSync2(tempMdPath, mdPath);
3295
+ invalidateCachedArtifact(mdPath);
3007
3296
  } finally {
3008
3297
  try {
3009
- const markerPath = path7.join(swarmDir, ".plan-write-marker");
3298
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3010
3299
  const tasksCount = targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
3011
3300
  const marker = JSON.stringify({
3012
3301
  source: "plan_manager",
@@ -3069,13 +3358,14 @@ async function closePlanTerminalState(directory, plan, options) {
3069
3358
  planHashAfter: hashAfter,
3070
3359
  source: "close_terminal"
3071
3360
  });
3072
- const swarmDir = path7.join(directory, ".swarm");
3073
- const planPath = path7.join(swarmDir, "plan.json");
3074
- const tempPlanPath = path7.join(swarmDir, `plan.json.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3361
+ const swarmDir = path8.join(directory, ".swarm");
3362
+ const planPath = path8.join(swarmDir, "plan.json");
3363
+ const tempPlanPath = path8.join(swarmDir, `plan.json.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3075
3364
  await bunWrite(tempPlanPath, JSON.stringify(validated, null, 2));
3076
3365
  renameSync2(tempPlanPath, planPath);
3366
+ invalidateCachedArtifact(planPath);
3077
3367
  try {
3078
- const markerPath = path7.join(swarmDir, ".plan-write-marker");
3368
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3079
3369
  const inProgressMarker = JSON.stringify({
3080
3370
  source: "plan_manager_close",
3081
3371
  timestamp: new Date().toISOString(),
@@ -3086,17 +3376,18 @@ async function closePlanTerminalState(directory, plan, options) {
3086
3376
  await bunWrite(markerPath, inProgressMarker);
3087
3377
  } catch {}
3088
3378
  try {
3089
- const mdPath = path7.join(swarmDir, "plan.md");
3379
+ const mdPath = path8.join(swarmDir, "plan.md");
3090
3380
  const contentHash = computePlanContentHash(validated);
3091
3381
  const markdown = derivePlanMarkdown(validated);
3092
3382
  const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
3093
3383
  ${markdown}`;
3094
- const mdTempPath = path7.join(swarmDir, `plan.md.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3384
+ const mdTempPath = path8.join(swarmDir, `plan.md.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
3095
3385
  await bunWrite(mdTempPath, markdownWithHash);
3096
3386
  renameSync2(mdTempPath, mdPath);
3387
+ invalidateCachedArtifact(mdPath);
3097
3388
  } finally {
3098
3389
  try {
3099
- const markerPath = path7.join(swarmDir, ".plan-write-marker");
3390
+ const markerPath = path8.join(swarmDir, ".plan-write-marker");
3100
3391
  const tasksCount = validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
3101
3392
  const marker = JSON.stringify({
3102
3393
  source: "plan_manager_close",
@@ -3391,13 +3682,13 @@ import {
3391
3682
  rmSync,
3392
3683
  statSync as statSync2
3393
3684
  } from "fs";
3394
- import * as fs6 from "fs/promises";
3685
+ import * as fs7 from "fs/promises";
3395
3686
  import * as os4 from "os";
3396
- import * as path9 from "path";
3687
+ import * as path10 from "path";
3397
3688
 
3398
3689
  // src/evaluation/retention.ts
3399
- import * as fs4 from "fs/promises";
3400
- import * as path8 from "path";
3690
+ import * as fs5 from "fs/promises";
3691
+ import * as path9 from "path";
3401
3692
  function artifactKey(artifact) {
3402
3693
  return `${artifact.namespace}/${artifact.id}`;
3403
3694
  }
@@ -3405,10 +3696,10 @@ function validId(value) {
3405
3696
  return /^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/.test(value);
3406
3697
  }
3407
3698
  async function evaluationRunCandidates(directory) {
3408
- const root = path8.join(directory, ".swarm", "evolution", "runs");
3699
+ const root = path9.join(directory, ".swarm", "evolution", "runs");
3409
3700
  let entries = [];
3410
3701
  try {
3411
- entries = await fs4.readdir(root, { withFileTypes: true });
3702
+ entries = await fs5.readdir(root, { withFileTypes: true });
3412
3703
  } catch (error2) {
3413
3704
  if (error2.code === "ENOENT") {
3414
3705
  return { candidates: [], corrupt: [] };
@@ -3424,9 +3715,9 @@ async function evaluationRunCandidates(directory) {
3424
3715
  if (!validId(id))
3425
3716
  continue;
3426
3717
  const artifact = { namespace: "evaluation-run", id };
3427
- const target = path8.join(root, entry.name);
3718
+ const target = path9.join(root, entry.name);
3428
3719
  try {
3429
- const run = EvaluationRunV1Schema.parse(JSON.parse(await fs4.readFile(target, "utf8")));
3720
+ const run = EvaluationRunV1Schema.parse(JSON.parse(await fs5.readFile(target, "utf8")));
3430
3721
  if (run.runId !== id)
3431
3722
  throw new Error("run id/path mismatch");
3432
3723
  candidates.push({
@@ -3442,10 +3733,10 @@ async function evaluationRunCandidates(directory) {
3442
3733
  return { candidates, corrupt };
3443
3734
  }
3444
3735
  async function gateAuditCandidates(directory) {
3445
- const root = path8.join(directory, ".swarm", "evidence", "gate-audit");
3736
+ const root = path9.join(directory, ".swarm", "evidence", "gate-audit");
3446
3737
  let entries = [];
3447
3738
  try {
3448
- entries = await fs4.readdir(root, { withFileTypes: true });
3739
+ entries = await fs5.readdir(root, { withFileTypes: true });
3449
3740
  } catch (error2) {
3450
3741
  if (error2.code === "ENOENT") {
3451
3742
  return { candidates: [], corrupt: [] };
@@ -3458,9 +3749,9 @@ async function gateAuditCandidates(directory) {
3458
3749
  if (!entry.isDirectory() || !validId(entry.name))
3459
3750
  continue;
3460
3751
  const artifact = { namespace: "gate-audit", id: entry.name };
3461
- const target = path8.join(root, entry.name);
3752
+ const target = path9.join(root, entry.name);
3462
3753
  try {
3463
- const result = GateAuditResultV1Schema.parse(JSON.parse(await fs4.readFile(path8.join(target, "results.json"), "utf8")));
3754
+ const result = GateAuditResultV1Schema.parse(JSON.parse(await fs5.readFile(path9.join(target, "results.json"), "utf8")));
3464
3755
  if (result.runId !== entry.name)
3465
3756
  throw new Error("run id/path mismatch");
3466
3757
  candidates.push({
@@ -3491,17 +3782,17 @@ async function protectedArtifacts(directory) {
3491
3782
  }
3492
3783
  async function removeCandidate(candidate) {
3493
3784
  if (candidate.kind === "directory") {
3494
- await fs4.rm(candidate.target, { recursive: true, force: true });
3785
+ await fs5.rm(candidate.target, { recursive: true, force: true });
3495
3786
  return;
3496
3787
  }
3497
- await fs4.unlink(candidate.target);
3788
+ await fs5.unlink(candidate.target);
3498
3789
  }
3499
3790
  var _retentionInternals = {
3500
3791
  protectedArtifacts,
3501
3792
  removeCandidate
3502
3793
  };
3503
3794
  async function archiveEvaluationArtifacts(args) {
3504
- return withEvidenceLock(args.directory, path8.join("evolution", "retention-index"), "evaluation-retention", "archive", async () => {
3795
+ return withEvidenceLock(args.directory, path9.join("evolution", "retention-index"), "evaluation-retention", "archive", async () => {
3505
3796
  const inventory = await retentionCandidates(args.directory);
3506
3797
  let protectedKeys = await _retentionInternals.protectedArtifacts(args.directory);
3507
3798
  const protectedRefs = inventory.candidates.filter((candidate) => protectedKeys.has(artifactKey(candidate.artifact))).map((candidate) => candidate.artifact);
@@ -3550,7 +3841,7 @@ async function archiveEvaluationArtifacts(args) {
3550
3841
  }
3551
3842
 
3552
3843
  // src/evidence/documents-retention.ts
3553
- import * as fs5 from "fs";
3844
+ import * as fs6 from "fs";
3554
3845
  import * as fsp from "fs/promises";
3555
3846
  import * as readline from "readline";
3556
3847
  var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
@@ -3558,13 +3849,13 @@ var MAX_READ_BYTES = 100 * 1024 * 1024;
3558
3849
  var RENAME_MAX_ATTEMPTS = 5;
3559
3850
  var RENAME_BACKOFF_MS = 10;
3560
3851
  var RENAME_RETRY_CODES = new Set(["EPERM", "EBUSY", "ENOTEMPTY", "EACCES"]);
3561
- var _internals9 = {
3852
+ var _internals10 = {
3562
3853
  stat: (p) => fsp.stat(p),
3563
- createReadStream: (p, options) => fs5.createReadStream(p, options),
3564
- openSync: (p, flags) => fs5.openSync(p, flags),
3565
- writeSync: (fd, data, position) => fs5.writeSync(fd, data, position),
3566
- fsyncSync: (fd) => fs5.fsyncSync(fd),
3567
- closeSync: (fd) => fs5.closeSync(fd),
3854
+ createReadStream: (p, options) => fs6.createReadStream(p, options),
3855
+ openSync: (p, flags) => fs6.openSync(p, flags),
3856
+ writeSync: (fd, data, position) => fs6.writeSync(fd, data, position),
3857
+ fsyncSync: (fd) => fs6.fsyncSync(fd),
3858
+ closeSync: (fd) => fs6.closeSync(fd),
3568
3859
  renameWithRetry: atomicRenameWithRetry,
3569
3860
  unlink: (p) => fsp.unlink(p)
3570
3861
  };
@@ -3581,7 +3872,7 @@ async function atomicRenameWithRetry(src, dst, renameFn = fsp.rename) {
3581
3872
  throw err;
3582
3873
  }
3583
3874
  if (attempt < RENAME_MAX_ATTEMPTS - 1) {
3584
- await new Promise((resolve3) => setTimeout(resolve3, RENAME_BACKOFF_MS));
3875
+ await new Promise((resolve4) => setTimeout(resolve4, RENAME_BACKOFF_MS));
3585
3876
  }
3586
3877
  }
3587
3878
  }
@@ -3600,9 +3891,9 @@ function emptyResult(dryRun) {
3600
3891
  };
3601
3892
  }
3602
3893
  async function readCacheRows(filePath) {
3603
- const stat2 = await _internals9.stat(filePath);
3894
+ const stat2 = await _internals10.stat(filePath);
3604
3895
  const bytesBefore = stat2.size;
3605
- const stream = _internals9.createReadStream(filePath, {
3896
+ const stream = _internals10.createReadStream(filePath, {
3606
3897
  encoding: "utf8",
3607
3898
  highWaterMark: 64 * 1024
3608
3899
  });
@@ -3678,23 +3969,23 @@ async function rewriteAtomic(filePath, survivors) {
3678
3969
  `)}
3679
3970
  ` : "";
3680
3971
  let succeeded = false;
3681
- const fd = _internals9.openSync(tmpPath, "w");
3972
+ const fd = _internals10.openSync(tmpPath, "w");
3682
3973
  try {
3683
- _internals9.writeSync(fd, payload, 0);
3684
- _internals9.fsyncSync(fd);
3685
- _internals9.closeSync(fd);
3686
- await _internals9.renameWithRetry(tmpPath, filePath);
3974
+ _internals10.writeSync(fd, payload, 0);
3975
+ _internals10.fsyncSync(fd);
3976
+ _internals10.closeSync(fd);
3977
+ await _internals10.renameWithRetry(tmpPath, filePath);
3687
3978
  succeeded = true;
3688
3979
  } catch (err) {
3689
3980
  try {
3690
- _internals9.closeSync(fd);
3981
+ _internals10.closeSync(fd);
3691
3982
  } catch {}
3692
3983
  const msg = err instanceof Error ? err.message : String(err);
3693
3984
  throw new Error(`documents-cache rewrite failed: ${msg}`);
3694
3985
  } finally {
3695
3986
  if (!succeeded) {
3696
3987
  try {
3697
- await _internals9.unlink(tmpPath);
3988
+ await _internals10.unlink(tmpPath);
3698
3989
  } catch {}
3699
3990
  }
3700
3991
  }
@@ -3706,7 +3997,7 @@ async function pruneEvidenceDocuments(args) {
3706
3997
  const filePath = validateSwarmPath(args.directory, EVIDENCE_CACHE_FILE);
3707
3998
  let stat2;
3708
3999
  try {
3709
- stat2 = await _internals9.stat(filePath);
4000
+ stat2 = await _internals10.stat(filePath);
3710
4001
  } catch (err) {
3711
4002
  const code = err?.code;
3712
4003
  if (code === "ENOENT")
@@ -3824,8 +4115,8 @@ function isWeakConfigContainerRoot(directory) {
3824
4115
  const resolved = realpathSync(directory);
3825
4116
  return resolved === realpathSync(os4.tmpdir()) || resolved === realpathSync(os4.homedir());
3826
4117
  } catch {
3827
- const resolved = path9.resolve(directory);
3828
- return resolved === path9.resolve(os4.tmpdir()) || resolved === path9.resolve(os4.homedir());
4118
+ const resolved = path10.resolve(directory);
4119
+ return resolved === path10.resolve(os4.tmpdir()) || resolved === path10.resolve(os4.homedir());
3829
4120
  }
3830
4121
  }
3831
4122
  function hasAccessibleProjectIndicator(directory, options = {}) {
@@ -3835,7 +4126,7 @@ function hasAccessibleProjectIndicator(directory, options = {}) {
3835
4126
  continue;
3836
4127
  }
3837
4128
  try {
3838
- const indicatorStat = statSync2(path9.join(directory, indicator));
4129
+ const indicatorStat = statSync2(path10.join(directory, indicator));
3839
4130
  if (indicatorStat.isFile() || indicatorStat.isDirectory()) {
3840
4131
  return true;
3841
4132
  }
@@ -3857,14 +4148,14 @@ function validateProjectRoot(directory) {
3857
4148
  if (depth >= MAX_DEPTH)
3858
4149
  break;
3859
4150
  depth++;
3860
- const parent = path9.dirname(current);
4151
+ const parent = path10.dirname(current);
3861
4152
  if (parent === current)
3862
4153
  break;
3863
- if (path9.dirname(parent) === parent) {
4154
+ if (path10.dirname(parent) === parent) {
3864
4155
  current = parent;
3865
4156
  continue;
3866
4157
  }
3867
- const parentSwarm = path9.join(parent, ".swarm");
4158
+ const parentSwarm = path10.join(parent, ".swarm");
3868
4159
  try {
3869
4160
  if (statSync2(parentSwarm).isDirectory()) {
3870
4161
  if (hasAccessibleProjectIndicator(parent, {
@@ -3883,14 +4174,14 @@ function validateProjectRoot(directory) {
3883
4174
  }
3884
4175
  }
3885
4176
  async function saveEvidence(directory, taskId, evidence) {
3886
- _internals10.validateProjectRoot(directory);
4177
+ _internals11.validateProjectRoot(directory);
3887
4178
  const sanitizedTaskId = sanitizeTaskId2(taskId);
3888
- const relativePath = path9.join("evidence", sanitizedTaskId, "evidence.json");
4179
+ const relativePath = path10.join("evidence", sanitizedTaskId, "evidence.json");
3889
4180
  validateSwarmPath(directory, relativePath);
3890
- _internals10.validateEvidence(evidence);
4181
+ _internals11.validateEvidence(evidence);
3891
4182
  return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
3892
4183
  const evidencePath = validateSwarmPath(directory, relativePath);
3893
- const evidenceDir = path9.dirname(evidencePath);
4184
+ const evidenceDir = path10.dirname(evidencePath);
3894
4185
  let bundle;
3895
4186
  const existingContent = await readSwarmFileAsync(directory, relativePath);
3896
4187
  if (existingContent !== null) {
@@ -3933,16 +4224,17 @@ async function saveEvidence(directory, taskId, evidence) {
3933
4224
  throw new Error(`Evidence bundle size (${bundleJson.length} bytes) exceeds maximum (${EVIDENCE_MAX_JSON_BYTES} bytes)`);
3934
4225
  }
3935
4226
  mkdirSync4(evidenceDir, { recursive: true });
3936
- const tempPath = path9.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4227
+ const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
3937
4228
  try {
3938
4229
  await bunWrite(tempPath, bundleJson);
3939
- await fs6.rename(tempPath, evidencePath);
4230
+ await fs7.rename(tempPath, evidencePath);
3940
4231
  } catch (error2) {
3941
4232
  try {
3942
4233
  rmSync(tempPath, { force: true });
3943
4234
  } catch {}
3944
4235
  throw error2;
3945
4236
  }
4237
+ invalidateCachedArtifact(evidencePath);
3946
4238
  return updatedBundle;
3947
4239
  });
3948
4240
  }
@@ -3977,7 +4269,7 @@ function wrapFlatRetrospective(flatEntry, taskId) {
3977
4269
  }
3978
4270
  async function loadEvidence(directory, taskId, options) {
3979
4271
  const sanitizedTaskId = sanitizeTaskId2(taskId);
3980
- const relativePath = path9.join("evidence", sanitizedTaskId, "evidence.json");
4272
+ const relativePath = path10.join("evidence", sanitizedTaskId, "evidence.json");
3981
4273
  if (relativePath.length > 4096) {
3982
4274
  return { status: "not_found" };
3983
4275
  }
@@ -3993,7 +4285,7 @@ async function loadEvidence(directory, taskId, options) {
3993
4285
  return { status: "invalid_schema", errors: ["Invalid JSON"] };
3994
4286
  }
3995
4287
  if (isFlatRetrospective(parsed)) {
3996
- const wrappedBundle = _internals10.wrapFlatRetrospective(parsed, sanitizedTaskId);
4288
+ const wrappedBundle = _internals11.wrapFlatRetrospective(parsed, sanitizedTaskId);
3997
4289
  try {
3998
4290
  const validated = EvidenceBundleSchema.parse(wrappedBundle);
3999
4291
  if (options?.migrate === false) {
@@ -4001,12 +4293,13 @@ async function loadEvidence(directory, taskId, options) {
4001
4293
  }
4002
4294
  try {
4003
4295
  await withEvidenceLock(directory, relativePath, "evidence-loader", sanitizedTaskId, async () => {
4004
- const evidenceDir = path9.dirname(evidencePath);
4296
+ const evidenceDir = path10.dirname(evidencePath);
4005
4297
  const bundleJson = JSON.stringify(validated);
4006
- const tempPath = path9.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4298
+ const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4007
4299
  try {
4008
4300
  await bunWrite(tempPath, bundleJson);
4009
- await fs6.rename(tempPath, evidencePath);
4301
+ await fs7.rename(tempPath, evidencePath);
4302
+ invalidateCachedArtifact(evidencePath);
4010
4303
  } catch (writeError) {
4011
4304
  try {
4012
4305
  rmSync(tempPath, { force: true });
@@ -4048,7 +4341,7 @@ async function listEvidenceTaskIds(directory) {
4048
4341
  }
4049
4342
  const taskIds = [];
4050
4343
  for (const entry of entries) {
4051
- const entryPath = path9.join(evidenceBasePath, entry);
4344
+ const entryPath = path10.join(evidenceBasePath, entry);
4052
4345
  try {
4053
4346
  const stats = statSync2(entryPath);
4054
4347
  if (!stats.isDirectory()) {
@@ -4066,7 +4359,7 @@ async function listEvidenceTaskIds(directory) {
4066
4359
  }
4067
4360
  async function deleteEvidence(directory, taskId) {
4068
4361
  const sanitizedTaskId = sanitizeTaskId2(taskId);
4069
- const relativePath = path9.join("evidence", sanitizedTaskId);
4362
+ const relativePath = path10.join("evidence", sanitizedTaskId);
4070
4363
  const evidenceDir = validateSwarmPath(directory, relativePath);
4071
4364
  try {
4072
4365
  statSync2(evidenceDir);
@@ -4082,18 +4375,18 @@ async function deleteEvidence(directory, taskId) {
4082
4375
  }
4083
4376
  }
4084
4377
  async function checkRequirementCoverage(phase, directory) {
4085
- const relativePath = path9.join("evidence", `req-coverage-phase-${phase}.json`);
4086
- const absolutePath = path9.resolve(directory, ".swarm", relativePath);
4378
+ const relativePath = path10.join("evidence", `req-coverage-phase-${phase}.json`);
4379
+ const absolutePath = path10.resolve(directory, ".swarm", relativePath);
4087
4380
  try {
4088
- await fs6.access(absolutePath);
4381
+ await fs7.access(absolutePath);
4089
4382
  return { exists: true, path: absolutePath };
4090
4383
  } catch {
4091
4384
  return { exists: false, path: absolutePath };
4092
4385
  }
4093
4386
  }
4094
4387
  async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
4095
- const taskIds = await _internals10.listEvidenceTaskIds(directory);
4096
- const cutoffDate = options?.now ? new Date(options.now) : _internals10.now();
4388
+ const taskIds = await _internals11.listEvidenceTaskIds(directory);
4389
+ const cutoffDate = options?.now ? new Date(options.now) : _internals11.now();
4097
4390
  cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
4098
4391
  const cutoffIso = cutoffDate.toISOString();
4099
4392
  const selected = new Set;
@@ -4105,7 +4398,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
4105
4398
  for (const taskId of taskIds) {
4106
4399
  let result;
4107
4400
  try {
4108
- result = await _internals10.loadEvidence(directory, taskId);
4401
+ result = await _internals11.loadEvidence(directory, taskId);
4109
4402
  } catch {
4110
4403
  warn("archive: skipping corrupt or unreadable evidence for task", taskId);
4111
4404
  continue;
@@ -4133,7 +4426,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
4133
4426
  }
4134
4427
  if (!options?.dryRun) {
4135
4428
  for (const taskId of [...selected].sort()) {
4136
- const deleted = await _internals10.deleteEvidence(directory, taskId);
4429
+ const deleted = await _internals11.deleteEvidence(directory, taskId);
4137
4430
  if (deleted)
4138
4431
  archived.push(taskId);
4139
4432
  else
@@ -4159,7 +4452,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
4159
4452
  };
4160
4453
  if (typeof options?.cacheMaxBytes === "number" && options.cacheMaxBytes > 0 || typeof options?.cacheMaxRecords === "number" && options.cacheMaxRecords > 0) {
4161
4454
  try {
4162
- documentsCache = await _internals10.pruneEvidenceDocuments({
4455
+ documentsCache = await _internals11.pruneEvidenceDocuments({
4163
4456
  directory,
4164
4457
  maxBytes: options?.cacheMaxBytes,
4165
4458
  maxRecords: options?.cacheMaxRecords,
@@ -4187,7 +4480,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
4187
4480
  function validateEvidence(evidence) {
4188
4481
  return EvidenceSchema.parse(evidence);
4189
4482
  }
4190
- var _internals10 = {
4483
+ var _internals11 = {
4191
4484
  wrapFlatRetrospective,
4192
4485
  loadEvidence,
4193
4486
  listEvidenceTaskIds,
@@ -4309,4 +4602,4 @@ function mergeDurableGateEntriesFromEvidence(taskId, entries, evidence) {
4309
4602
  return merged;
4310
4603
  }
4311
4604
 
4312
- export { getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };
4605
+ export { getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, containsPathTraversal, containsControlChars, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };