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.
- package/README.md +7 -6
- package/dist/cli/{config-doctor-31yppse4.js → config-doctor-tgnr91q3.js} +2 -2
- package/dist/cli/{core-65c7rvqf.js → core-g2dfh9fq.js} +1 -1
- package/dist/cli/{curation-policy-hs9pc1rp.js → curation-policy-2qnhgdtt.js} +7 -7
- package/dist/cli/{curator-drift-jpap6hv4.js → curator-drift-vfh07mqy.js} +5 -2
- package/dist/cli/{curator-llm-factory-wm27ra25.js → curator-llm-factory-1ftzece0.js} +28 -28
- package/dist/cli/{curator-54n1y4j8.js → curator-nbsag633.js} +28 -28
- package/dist/cli/{evidence-summary-service-hrxcf29a.js → evidence-summary-service-kzxr71yd.js} +9 -9
- package/dist/cli/{gate-evidence-ecx4djaw.js → gate-evidence-z2b1ymnw.js} +5 -5
- package/dist/cli/{guardrail-explain-1729nse8.js → guardrail-explain-vb9k9zwm.js} +29 -29
- package/dist/cli/{guardrail-log-yjj5srkw.js → guardrail-log-0djnvfas.js} +3 -3
- package/dist/cli/{hive-promoter-es2x2b0e.js → hive-promoter-bpt26yew.js} +28 -28
- package/dist/cli/{index-mycqmsz5.js → index-0wp67gqj.js} +452 -159
- package/dist/cli/{index-c60whfgh.js → index-0ymhx85c.js} +2 -2
- package/dist/cli/{index-5q009kc4.js → index-15526t16.js} +8 -3
- package/dist/cli/{index-smsy2p8n.js → index-3eddtgxt.js} +1 -1
- package/dist/cli/{index-4fmztnrq.js → index-4xbyy6aw.js} +2 -2
- package/dist/cli/{index-xybtaph7.js → index-5j6chvj8.js} +1 -1
- package/dist/cli/{index-39k6y018.js → index-5w7ekpy3.js} +2 -2
- package/dist/cli/{index-z7jgvm9b.js → index-5zyk8qgv.js} +2 -2
- package/dist/cli/{index-en4fb04r.js → index-668dpw5e.js} +1 -1
- package/dist/cli/{index-m8txs2a9.js → index-91s653xx.js} +1 -1
- package/dist/cli/{index-y28n2wmq.js → index-a12906mj.js} +1 -1
- package/dist/cli/{index-10mz5js6.js → index-a5eba0t8.js} +2 -2
- package/dist/cli/{index-57stye65.js → index-aymt7hzw.js} +6 -3
- package/dist/cli/{index-1g6xpjk3.js → index-db7wf6ke.js} +7 -7
- package/dist/cli/{index-m1pwkwfe.js → index-dw09fz3w.js} +1 -1
- package/dist/cli/{index-fsrp8wp3.js → index-ed5ykebp.js} +25 -2
- package/dist/cli/{index-jbge0dyh.js → index-en6qq954.js} +3 -3
- package/dist/cli/{index-tqshdzaw.js → index-gvhvz176.js} +2 -2
- package/dist/cli/{index-e1kgxchq.js → index-hgrkhxbk.js} +2 -2
- package/dist/cli/{index-33q2gmgd.js → index-hn0mdcmj.js} +1 -1
- package/dist/cli/{index-a9xny52r.js → index-ht4q3548.js} +2 -2
- package/dist/cli/{index-q344kj84.js → index-prmb5qfp.js} +2 -2
- package/dist/cli/{index-x20cgr2q.js → index-q7ezzgwq.js} +16 -2
- package/dist/cli/{index-ngqxzkqm.js → index-s608d3hw.js} +2 -2
- package/dist/cli/{index-50mwt8fx.js → index-vxbppwvf.js} +5 -5
- package/dist/cli/{index-rrxcfxrd.js → index-wrt6m5ze.js} +1 -1
- package/dist/cli/{index-4qzeef9h.js → index-x9q4bbhd.js} +1 -1
- package/dist/cli/{index-m40ea3zk.js → index-xec724kg.js} +2 -2
- package/dist/cli/{index-k27a4e5g.js → index-xka8s8jw.js} +30 -30
- package/dist/cli/{index-a18hk2fw.js → index-xr07r80d.js} +513 -483
- package/dist/cli/{index-gm3pjfhb.js → index-y1zcsf2a.js} +1 -1
- package/dist/cli/{index-tjwz5tct.js → index-ywysg26s.js} +4 -4
- package/dist/cli/index.js +28 -28
- package/dist/cli/{knowledge-escalator-s6z1x65b.js → knowledge-escalator-pjg2x75c.js} +8 -8
- package/dist/cli/{knowledge-events-nnyn6hst.js → knowledge-events-vxheptk8.js} +6 -6
- package/dist/cli/{knowledge-link-xc6yhxmz.js → knowledge-link-3tq8w6gs.js} +5 -5
- package/dist/cli/{knowledge-store-x4yfc7v3.js → knowledge-store-36mascwz.js} +6 -6
- package/dist/cli/{knowledge-validator-1v047gsa.js → knowledge-validator-anats6rj.js} +9 -9
- package/dist/cli/{pending-delegations-7dbc0j2a.js → pending-delegations-tmyf18p4.js} +5 -5
- package/dist/cli/{pr-subscriptions-m943zn6g.js → pr-subscriptions-hhx6yk24.js} +5 -5
- package/dist/cli/{runner-x1hy047y.js → runner-w30gazew.js} +6 -6
- package/dist/cli/{scan-cursor-jxajj6s7.js → scan-cursor-apbz9eez.js} +7 -7
- package/dist/cli/{schema-fj32eyae.js → schema-x9fr2ey8.js} +3 -1
- package/dist/cli/{scope-persistence-w7zen2wz.js → scope-persistence-3f0d4fwv.js} +4 -4
- package/dist/cli/{skill-generator-rt0jqcsa.js → skill-generator-20czn4kd.js} +10 -10
- package/dist/cli/{telemetry-0yqed3jv.js → telemetry-4tgrfhaz.js} +1 -1
- package/dist/cli/{worktree-collision-ownership-hjrt5hx2.js → worktree-collision-ownership-vexb0b6r.js} +38 -8
- package/dist/config/context-window.d.ts +210 -0
- package/dist/config/schema.d.ts +18 -0
- package/dist/evaluation/ephemeral-agent-dispatcher.d.ts +18 -0
- package/dist/evaluation/model-dispatcher.d.ts +1 -0
- package/dist/hooks/delegate-ack-collector.d.ts +1 -1
- package/dist/hooks/delegation-gate/worktree-collision-ownership.d.ts +7 -0
- package/dist/hooks/delegation-gate/worktree-isolation.d.ts +2 -1
- package/dist/hooks/delegation-gate/worktree-provisioning-owner.d.ts +6 -0
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/init-orphan-recovery.d.ts +4 -1
- package/dist/hooks/knowledge-application-gate.d.ts +2 -1
- package/dist/hooks/knowledge-application.d.ts +5 -4
- package/dist/hooks/knowledge-injector.d.ts +16 -0
- package/dist/hooks/knowledge-types.d.ts +1 -1
- package/dist/hooks/messages-transform.d.ts +18 -0
- package/dist/hooks/model-limits.d.ts +88 -30
- package/dist/index.js +501 -499
- package/dist/memory/injector.d.ts +2 -0
- package/dist/plan/manager.d.ts +2 -0
- package/dist/services/context-budget-service.d.ts +6 -1
- package/dist/services/run-memory.d.ts +53 -5
- package/dist/services/status-service.d.ts +14 -0
- package/dist/state.d.ts +37 -0
- package/dist/tools/context-status.d.ts +3 -1
- package/dist/tools/update-task-status.d.ts +2 -0
- package/dist/turbo/lean/runner.d.ts +10 -0
- package/dist/utils/ephemeral-session-teardown.d.ts +98 -0
- package/dist/utils/path-security.d.ts +95 -1
- package/dist/utils/swarm-artifact-cache.d.ts +13 -0
- package/dist/worktree/index.d.ts +1 -1
- package/dist/worktree/merge.d.ts +36 -3
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
EvaluationRunV1Schema,
|
|
4
4
|
GateAuditResultV1Schema,
|
|
5
5
|
getProtectedEvaluationRunIds
|
|
6
|
-
} from "./index-
|
|
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-
|
|
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-
|
|
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-
|
|
53
|
+
} from "./index-x9q4bbhd.js";
|
|
54
54
|
import {
|
|
55
|
+
invalidateCachedArtifact,
|
|
55
56
|
readCachedParsedFile
|
|
56
|
-
} from "./index-
|
|
57
|
+
} from "./index-ed5ykebp.js";
|
|
57
58
|
import {
|
|
58
59
|
withEvidenceLock
|
|
59
|
-
} from "./index-
|
|
60
|
+
} from "./index-5j6chvj8.js";
|
|
60
61
|
import {
|
|
61
62
|
emit
|
|
62
|
-
} from "./index-
|
|
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
|
|
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/
|
|
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
|
-
|
|
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 =
|
|
1599
|
-
if (!
|
|
1600
|
-
|
|
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 =
|
|
1621
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
1668
|
-
|
|
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 (
|
|
1674
|
-
|
|
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 (!
|
|
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 (!
|
|
2038
|
+
if (!_internals7.isGitRepo(directory)) {
|
|
1757
2039
|
return { committed: false, reason: "no-git" };
|
|
1758
2040
|
}
|
|
1759
2041
|
try {
|
|
1760
|
-
if (
|
|
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
|
-
|
|
2063
|
+
_internals7.stageScopedPaths(directory, paths);
|
|
1782
2064
|
}
|
|
1783
|
-
|
|
1784
|
-
const sha =
|
|
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
|
|
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
|
|
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((
|
|
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
|
|
1832
|
-
import * as
|
|
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 =
|
|
2242
|
+
const scopePath = path7.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
|
|
1961
2243
|
try {
|
|
1962
|
-
if (!
|
|
2244
|
+
if (!fs4.existsSync(scopePath)) {
|
|
1963
2245
|
return null;
|
|
1964
2246
|
}
|
|
1965
|
-
const raw =
|
|
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
|
|
2261
|
+
import { join as join6 } from "path";
|
|
1980
2262
|
async function computeSpecHash(directory) {
|
|
1981
|
-
const spec =
|
|
2263
|
+
const spec = _internals8.readEffectiveSpecSync(directory);
|
|
1982
2264
|
if (!spec)
|
|
1983
2265
|
return null;
|
|
1984
|
-
return
|
|
2266
|
+
return createHash2("sha256").update(spec.content, "utf-8").digest("hex");
|
|
1985
2267
|
}
|
|
1986
2268
|
async function isSpecStale(directory, plan) {
|
|
1987
|
-
const currentHash = await
|
|
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 =
|
|
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 =
|
|
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
|
|
2303
|
+
const normalize2 = (text) => text.replace(/\r\n/g, `
|
|
2022
2304
|
`);
|
|
2023
|
-
const recordedNorm =
|
|
2024
|
-
const currentNorm =
|
|
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
|
|
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 =
|
|
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 =
|
|
2348
|
+
const spec = _internals8.readEffectiveSpecSync(directory);
|
|
2067
2349
|
if (!spec) {
|
|
2068
2350
|
return null;
|
|
2069
2351
|
}
|
|
2070
2352
|
const currentContent = spec.content;
|
|
2071
|
-
const
|
|
2353
|
+
const normalize2 = (text) => text.replace(/\r\n/g, `
|
|
2072
2354
|
`);
|
|
2073
|
-
const recordedLines =
|
|
2355
|
+
const recordedLines = normalize2(recordedContent).split(`
|
|
2074
2356
|
`);
|
|
2075
|
-
const currentLines =
|
|
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
|
|
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((
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2385
|
-
const mdTempPath =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
2701
|
-
const oldLedgerPath =
|
|
2702
|
-
const oldLedgerBackupPath =
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
2909
|
-
const planPath =
|
|
2910
|
-
const tempPath =
|
|
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 =
|
|
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 =
|
|
2936
|
-
const mdTempPath =
|
|
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 =
|
|
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 =
|
|
2974
|
-
const planPath =
|
|
2975
|
-
const mdPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3073
|
-
const planPath =
|
|
3074
|
-
const tempPlanPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
3685
|
+
import * as fs7 from "fs/promises";
|
|
3395
3686
|
import * as os4 from "os";
|
|
3396
|
-
import * as
|
|
3687
|
+
import * as path10 from "path";
|
|
3397
3688
|
|
|
3398
3689
|
// src/evaluation/retention.ts
|
|
3399
|
-
import * as
|
|
3400
|
-
import * as
|
|
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 =
|
|
3699
|
+
const root = path9.join(directory, ".swarm", "evolution", "runs");
|
|
3409
3700
|
let entries = [];
|
|
3410
3701
|
try {
|
|
3411
|
-
entries = await
|
|
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 =
|
|
3718
|
+
const target = path9.join(root, entry.name);
|
|
3428
3719
|
try {
|
|
3429
|
-
const run = EvaluationRunV1Schema.parse(JSON.parse(await
|
|
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 =
|
|
3736
|
+
const root = path9.join(directory, ".swarm", "evidence", "gate-audit");
|
|
3446
3737
|
let entries = [];
|
|
3447
3738
|
try {
|
|
3448
|
-
entries = await
|
|
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 =
|
|
3752
|
+
const target = path9.join(root, entry.name);
|
|
3462
3753
|
try {
|
|
3463
|
-
const result = GateAuditResultV1Schema.parse(JSON.parse(await
|
|
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
|
|
3785
|
+
await fs5.rm(candidate.target, { recursive: true, force: true });
|
|
3495
3786
|
return;
|
|
3496
3787
|
}
|
|
3497
|
-
await
|
|
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,
|
|
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
|
|
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
|
|
3852
|
+
var _internals10 = {
|
|
3562
3853
|
stat: (p) => fsp.stat(p),
|
|
3563
|
-
createReadStream: (p, options) =>
|
|
3564
|
-
openSync: (p, flags) =>
|
|
3565
|
-
writeSync: (fd, data, position) =>
|
|
3566
|
-
fsyncSync: (fd) =>
|
|
3567
|
-
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((
|
|
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
|
|
3894
|
+
const stat2 = await _internals10.stat(filePath);
|
|
3604
3895
|
const bytesBefore = stat2.size;
|
|
3605
|
-
const stream =
|
|
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 =
|
|
3972
|
+
const fd = _internals10.openSync(tmpPath, "w");
|
|
3682
3973
|
try {
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
await
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
3828
|
-
return resolved ===
|
|
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(
|
|
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 =
|
|
4151
|
+
const parent = path10.dirname(current);
|
|
3861
4152
|
if (parent === current)
|
|
3862
4153
|
break;
|
|
3863
|
-
if (
|
|
4154
|
+
if (path10.dirname(parent) === parent) {
|
|
3864
4155
|
current = parent;
|
|
3865
4156
|
continue;
|
|
3866
4157
|
}
|
|
3867
|
-
const parentSwarm =
|
|
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
|
-
|
|
4177
|
+
_internals11.validateProjectRoot(directory);
|
|
3887
4178
|
const sanitizedTaskId = sanitizeTaskId2(taskId);
|
|
3888
|
-
const relativePath =
|
|
4179
|
+
const relativePath = path10.join("evidence", sanitizedTaskId, "evidence.json");
|
|
3889
4180
|
validateSwarmPath(directory, relativePath);
|
|
3890
|
-
|
|
4181
|
+
_internals11.validateEvidence(evidence);
|
|
3891
4182
|
return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
|
|
3892
4183
|
const evidencePath = validateSwarmPath(directory, relativePath);
|
|
3893
|
-
const evidenceDir =
|
|
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 =
|
|
4227
|
+
const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
|
|
3937
4228
|
try {
|
|
3938
4229
|
await bunWrite(tempPath, bundleJson);
|
|
3939
|
-
await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4296
|
+
const evidenceDir = path10.dirname(evidencePath);
|
|
4005
4297
|
const bundleJson = JSON.stringify(validated);
|
|
4006
|
-
const tempPath =
|
|
4298
|
+
const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
|
|
4007
4299
|
try {
|
|
4008
4300
|
await bunWrite(tempPath, bundleJson);
|
|
4009
|
-
await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4086
|
-
const absolutePath =
|
|
4378
|
+
const relativePath = path10.join("evidence", `req-coverage-phase-${phase}.json`);
|
|
4379
|
+
const absolutePath = path10.resolve(directory, ".swarm", relativePath);
|
|
4087
4380
|
try {
|
|
4088
|
-
await
|
|
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
|
|
4096
|
-
const cutoffDate = options?.now ? new Date(options.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
|
|
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
|
|
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
|
|
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
|
|
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 };
|