opencode-swarm 7.164.6 → 7.164.7
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 +1 -1
- package/dist/background/pending-delegations.d.ts +3 -0
- package/dist/background/pr-subscriptions.d.ts +1 -8
- package/dist/cli/{coder-settlement-6qgvzx40.js → coder-settlement-p8wwmtnn.js} +14 -14
- package/dist/cli/{config-doctor-d1cbh67h.js → config-doctor-wxf488mw.js} +5 -3
- package/dist/cli/{core-g0cvvj3b.js → core-671ybpbt.js} +2 -2
- package/dist/cli/{curation-policy-7f8x9yg6.js → curation-policy-de7emfqc.js} +7 -6
- package/dist/cli/{curator-drift-0r0scc8r.js → curator-drift-nvxv8xkk.js} +7 -5
- package/dist/cli/{curator-q3qn89wn.js → curator-eaz5rj6k.js} +39 -37
- package/dist/cli/curator-llm-factory-swzyfnh6.js +68 -0
- package/dist/cli/{dispatch-q4v8x2pk.js → dispatch-dqybvj8v.js} +10 -6
- package/dist/cli/{evidence-summary-service-3meyb8rc.js → evidence-summary-service-deqsc8hj.js} +19 -16
- package/dist/cli/{gate-evidence-mhc0w7vp.js → gate-evidence-mafg71qb.js} +14 -8
- package/dist/cli/guardrail-explain-ygq7cx9w.js +69 -0
- package/dist/cli/{guardrail-log-0nd1eqvz.js → guardrail-log-k3w9dq6x.js} +9 -8
- package/dist/cli/{guardrail-reset-mzeyd6jh.js → guardrail-reset-ae2step4.js} +39 -37
- package/dist/cli/hive-promoter-a4cs5sbr.js +83 -0
- package/dist/cli/{index-x6zyc38w.js → index-0zjwnbze.js} +1 -1
- package/dist/cli/{index-qdbgc16x.js → index-1ekzd5wf.js} +20 -12
- package/dist/cli/{index-fxxja498.js → index-2czt53wz.js} +1 -1
- package/dist/cli/{index-g5714mpv.js → index-388sxv1b.js} +17 -8
- package/dist/cli/{index-kvb4dwgf.js → index-3kgg2n6z.js} +12 -6
- package/dist/cli/{index-pvp5tnn2.js → index-3sv8t7ca.js} +2 -2
- package/dist/cli/{index-hdapetg1.js → index-46yrz972.js} +3 -3
- package/dist/cli/{index-zypacgjc.js → index-4vx89ymn.js} +5 -5
- package/dist/cli/{index-6f9yvd67.js → index-581bcqp9.js} +2 -2
- package/dist/cli/{index-gwg7a00b.js → index-63enf73z.js} +20 -15
- package/dist/cli/index-7qefgq4j.js +71 -0
- package/dist/cli/{index-5p1tzpwh.js → index-8ck1jkhd.js} +3 -2
- package/dist/cli/{index-zfwd65c2.js → index-8ntf8jnj.js} +19 -18
- package/dist/cli/{index-m1nrd3yd.js → index-8psybzzf.js} +1 -1
- package/dist/cli/{index-2c3np82d.js → index-8tqqmsav.js} +1 -1
- package/dist/cli/{index-tegp2mny.js → index-93znbdam.js} +9 -5
- package/dist/cli/{index-x76mprg2.js → index-9qkytpwa.js} +185 -376
- package/dist/cli/{index-p56xc59p.js → index-9ydhzegh.js} +28 -17
- package/dist/cli/{index-935ww10s.js → index-bvjwmmdc.js} +1233 -1190
- package/dist/cli/{index-ty3vtqe2.js → index-c4j9sjcs.js} +4 -4
- package/dist/cli/index-cw912re7.js +200 -0
- package/dist/cli/{index-j3hsjynw.js → index-fq1wfsmw.js} +100 -97
- package/dist/cli/{index-59hvr3f4.js → index-gm65h7bh.js} +9 -6
- package/dist/cli/{index-jzvrvdmr.js → index-h3k3rgyr.js} +4 -4
- package/dist/cli/{index-12xzj0hm.js → index-jkwfajh1.js} +4 -4
- package/dist/cli/{index-1dz8hv7p.js → index-kvq1e82k.js} +3 -3
- package/dist/cli/{index-ze5zyv28.js → index-mr3h3sm5.js} +2 -2
- package/dist/cli/{index-rysxqae0.js → index-rey1tzcw.js} +7 -7
- package/dist/cli/index-ry16d01s.js +107 -0
- package/dist/cli/{index-ay9cr8tr.js → index-ry4mwbc2.js} +1 -1
- package/dist/cli/{index-b0m3efpf.js → index-sxxh4ams.js} +2 -2
- package/dist/cli/{index-cyhw0der.js → index-t1qzjhnj.js} +13 -13
- package/dist/cli/{index-j645xpsm.js → index-v1fj9exz.js} +12 -5
- package/dist/cli/{index-3f7hqtgg.js → index-w3gtqey0.js} +4 -1
- package/dist/cli/{index-9qvmsyab.js → index-wgqeqy68.js} +2 -2
- package/dist/cli/{index-g94qqwnz.js → index-x2t2tbm2.js} +2 -2
- package/dist/cli/{index-zjg5ajf5.js → index-x3qzt684.js} +71 -47
- package/dist/cli/{index-t1nkr7cz.js → index-xc3q62ev.js} +2 -2
- package/dist/cli/{index-ew99d4y6.js → index-xy59dnvq.js} +1 -1
- package/dist/cli/{index-4k16cz8r.js → index-y75xd50f.js} +36 -27
- package/dist/cli/{index-7v3bb0h3.js → index-yzn1fm9a.js} +1 -1
- package/dist/cli/{index-yh8wxegw.js → index-zg34ngdf.js} +21 -11
- package/dist/cli/{index-y4z0rhsa.js → index-zg8tvjb0.js} +41 -39
- package/dist/cli/index.js +39 -37
- package/dist/cli/{knowledge-escalator-aca0nhqg.js → knowledge-escalator-t532h2ym.js} +15 -14
- package/dist/cli/{knowledge-events-n5t313ht.js → knowledge-events-hdtc0cxe.js} +13 -12
- package/dist/cli/{knowledge-link-8ea4ksda.js → knowledge-link-hhnmc4g1.js} +6 -5
- package/dist/cli/{knowledge-store-j97ejf6a.js → knowledge-store-45jadhmb.js} +7 -6
- package/dist/cli/{knowledge-validator-01evhydg.js → knowledge-validator-5e6qbq6h.js} +9 -8
- package/dist/cli/{model-preflight-pmnbfzjv.js → model-preflight-bq035tfh.js} +1 -1
- package/dist/cli/{pending-delegations-w73abdk3.js → pending-delegations-z0crvst3.js} +7 -7
- package/dist/cli/{pr-subscriptions-05dzvavk.js → pr-subscriptions-73fs8y3n.js} +7 -6
- package/dist/cli/{pr-workflow-gate-t0tnkx5f.js → pr-workflow-gate-rpdts7wy.js} +39 -37
- package/dist/cli/{runner-fh7t0mn9.js → runner-5qqh83e2.js} +6 -6
- package/dist/cli/{scan-cursor-63zx2qd8.js → scan-cursor-t75qznf3.js} +8 -7
- package/dist/cli/{schema-t1scx08g.js → schema-8dfjsrk6.js} +2 -2
- package/dist/cli/{scope-persistence-1a5qmchf.js → scope-persistence-zfmz4sws.js} +12 -10
- package/dist/cli/{skill-generator-7werbwgv.js → skill-generator-psdb10jw.js} +17 -16
- package/dist/cli/{telemetry-j14cj552.js → telemetry-85gxedng.js} +1 -1
- package/dist/cli/{workspace-snapshot-yq0tc49d.js → workspace-snapshot-eh18vgkn.js} +2 -1
- package/dist/cli/{worktree-collision-ownership-wzdzky33.js → worktree-collision-ownership-xrqssqzd.js} +12 -15
- package/dist/cli/{worktree-isolation-m02ay3kg.js → worktree-isolation-s9m3bgah.js} +39 -37
- package/dist/db/canonical-project.d.ts +10 -3
- package/dist/db/project-db.d.ts +4 -0
- package/dist/evidence/task-gate-repair.d.ts +1 -1
- package/dist/gate-evidence.d.ts +19 -1
- package/dist/index.js +581 -581
- package/dist/plan/ledger.d.ts +2 -0
- package/dist/tools/pre-check-batch.d.ts +1 -1
- package/dist/utils/canonical-root.d.ts +21 -1
- package/dist/utils/filesystem-identity.d.ts +59 -0
- package/package.json +2 -1
- package/dist/cli/curator-llm-factory-dw7md4nr.js +0 -66
- package/dist/cli/guardrail-explain-6cbszhw1.js +0 -67
- package/dist/cli/hive-promoter-w7g879cm.js +0 -81
- package/dist/cli/index-ddszzne4.js +0 -42
|
@@ -3,14 +3,17 @@ import {
|
|
|
3
3
|
EvaluationRunV1Schema,
|
|
4
4
|
GateAuditResultV1Schema,
|
|
5
5
|
getProtectedEvaluationRunIds
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-wgqeqy68.js";
|
|
7
|
+
import {
|
|
8
|
+
validateWorkspaceRoot
|
|
9
|
+
} from "./index-cw912re7.js";
|
|
7
10
|
import {
|
|
8
11
|
DURABILITY_CLASSES,
|
|
9
12
|
getProjectDb,
|
|
10
13
|
projectDbExists,
|
|
11
14
|
readEffectiveSpecSync,
|
|
12
15
|
withDurabilityClass
|
|
13
|
-
} from "./index-
|
|
16
|
+
} from "./index-x3qzt684.js";
|
|
14
17
|
import {
|
|
15
18
|
getWorktreeMergeFailure
|
|
16
19
|
} from "./index-zsrtt42s.js";
|
|
@@ -24,7 +27,7 @@ import {
|
|
|
24
27
|
readTaskEvidence,
|
|
25
28
|
readTaskEvidenceRaw,
|
|
26
29
|
sanitizeTaskId
|
|
27
|
-
} from "./index-
|
|
30
|
+
} from "./index-388sxv1b.js";
|
|
28
31
|
import {
|
|
29
32
|
LedgerStaleWriterError,
|
|
30
33
|
appendLedgerEvent,
|
|
@@ -44,10 +47,10 @@ import {
|
|
|
44
47
|
replayFromLedgerWithStatus,
|
|
45
48
|
takeSnapshotEvent,
|
|
46
49
|
takeSnapshotWithRetry
|
|
47
|
-
} from "./index-
|
|
50
|
+
} from "./index-v1fj9exz.js";
|
|
48
51
|
import {
|
|
49
52
|
appendCoreEventSync
|
|
50
|
-
} from "./index-
|
|
53
|
+
} from "./index-3sv8t7ca.js";
|
|
51
54
|
import {
|
|
52
55
|
init_executor,
|
|
53
56
|
isUntrustedEnvKey,
|
|
@@ -63,7 +66,7 @@ import {
|
|
|
63
66
|
estimateTokens,
|
|
64
67
|
readSwarmFileAsync,
|
|
65
68
|
validateSwarmPath
|
|
66
|
-
} from "./index-
|
|
69
|
+
} from "./index-8tqqmsav.js";
|
|
67
70
|
import {
|
|
68
71
|
bunHash,
|
|
69
72
|
bunWrite,
|
|
@@ -71,25 +74,28 @@ import {
|
|
|
71
74
|
} from "./index-r3mvsbtq.js";
|
|
72
75
|
import {
|
|
73
76
|
PlanSchema
|
|
74
|
-
} from "./index-
|
|
77
|
+
} from "./index-xy59dnvq.js";
|
|
75
78
|
import {
|
|
76
79
|
GIT_BINARY_ENV_VAR,
|
|
77
80
|
describeGitResolution,
|
|
78
81
|
init_git_executable,
|
|
79
82
|
resolveGitExecutable
|
|
80
83
|
} from "./index-9e6fdz2h.js";
|
|
84
|
+
import {
|
|
85
|
+
canonicalRootKeyFresh
|
|
86
|
+
} from "./index-7qefgq4j.js";
|
|
81
87
|
import {
|
|
82
88
|
assertProjectRoot,
|
|
83
89
|
tryAcquireLock,
|
|
84
90
|
withEvidenceLock
|
|
85
|
-
} from "./index-
|
|
91
|
+
} from "./index-2czt53wz.js";
|
|
86
92
|
import {
|
|
87
93
|
invalidateCachedArtifact,
|
|
88
94
|
readCachedParsedFile
|
|
89
|
-
} from "./index-
|
|
95
|
+
} from "./index-t1qzjhnj.js";
|
|
90
96
|
import {
|
|
91
97
|
emit
|
|
92
|
-
} from "./index-
|
|
98
|
+
} from "./index-kvq1e82k.js";
|
|
93
99
|
import {
|
|
94
100
|
ZodError,
|
|
95
101
|
init_zod
|
|
@@ -115,7 +121,7 @@ import {
|
|
|
115
121
|
writeFileSync as writeFileSync2
|
|
116
122
|
} from "fs";
|
|
117
123
|
import * as fsPromises from "fs/promises";
|
|
118
|
-
import * as
|
|
124
|
+
import * as path4 from "path";
|
|
119
125
|
|
|
120
126
|
// src/db/task-checkpoint-receipt.ts
|
|
121
127
|
import { createHash } from "crypto";
|
|
@@ -883,7 +889,7 @@ var _internals = {
|
|
|
883
889
|
|
|
884
890
|
// src/services/run-memory.ts
|
|
885
891
|
import * as crypto from "crypto";
|
|
886
|
-
import * as
|
|
892
|
+
import * as fs from "fs/promises";
|
|
887
893
|
|
|
888
894
|
// src/hooks/context-sanitizer.ts
|
|
889
895
|
function sanitizeContextText(text) {
|
|
@@ -895,206 +901,6 @@ function sanitizeContextText(text) {
|
|
|
895
901
|
|
|
896
902
|
// src/services/run-memory.ts
|
|
897
903
|
init_logger();
|
|
898
|
-
|
|
899
|
-
// src/utils/path-security.ts
|
|
900
|
-
import * as fs from "fs";
|
|
901
|
-
import * as path2 from "path";
|
|
902
|
-
function containsPathTraversal(str) {
|
|
903
|
-
if (/\.\.[/\\]/.test(str))
|
|
904
|
-
return true;
|
|
905
|
-
if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
|
|
906
|
-
return true;
|
|
907
|
-
if (/%2e%2e/i.test(str))
|
|
908
|
-
return true;
|
|
909
|
-
if (/%2e\./i.test(str))
|
|
910
|
-
return true;
|
|
911
|
-
if (/%2e/i.test(str) && /\.\./.test(str))
|
|
912
|
-
return true;
|
|
913
|
-
if (/%252e%252e/i.test(str))
|
|
914
|
-
return true;
|
|
915
|
-
if (/\uff0e/.test(str))
|
|
916
|
-
return true;
|
|
917
|
-
if (/\u3002/.test(str))
|
|
918
|
-
return true;
|
|
919
|
-
if (/\uff65/.test(str))
|
|
920
|
-
return true;
|
|
921
|
-
if (/%2f/i.test(str))
|
|
922
|
-
return true;
|
|
923
|
-
if (/%5c/i.test(str))
|
|
924
|
-
return true;
|
|
925
|
-
return false;
|
|
926
|
-
}
|
|
927
|
-
function containsControlChars(str) {
|
|
928
|
-
for (const ch of str) {
|
|
929
|
-
const code = ch.codePointAt(0);
|
|
930
|
-
if (code === undefined)
|
|
931
|
-
continue;
|
|
932
|
-
if (code <= 31 || code >= 127 && code <= 159)
|
|
933
|
-
return true;
|
|
934
|
-
if (code >= 8234 && code <= 8238)
|
|
935
|
-
return true;
|
|
936
|
-
if (code >= 8294 && code <= 8297)
|
|
937
|
-
return true;
|
|
938
|
-
}
|
|
939
|
-
return false;
|
|
940
|
-
}
|
|
941
|
-
function validateWorkspaceRoot(directory) {
|
|
942
|
-
validateProjectDirectory(directory);
|
|
943
|
-
}
|
|
944
|
-
function validateDirectory(directory) {
|
|
945
|
-
if (!directory || directory.trim() === "") {
|
|
946
|
-
throw new Error("Invalid directory: empty");
|
|
947
|
-
}
|
|
948
|
-
if (containsPathTraversal(directory)) {
|
|
949
|
-
throw new Error("Invalid directory: path traversal detected");
|
|
950
|
-
}
|
|
951
|
-
if (containsControlChars(directory)) {
|
|
952
|
-
throw new Error("Invalid directory: control characters detected");
|
|
953
|
-
}
|
|
954
|
-
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
955
|
-
throw new Error("Invalid directory: absolute path");
|
|
956
|
-
}
|
|
957
|
-
if (/^[A-Za-z]:[/\\]/.test(directory)) {
|
|
958
|
-
throw new Error("Invalid directory: Windows absolute path");
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
function validateProjectDirectory(directory) {
|
|
962
|
-
if (!directory || directory.trim() === "") {
|
|
963
|
-
throw new Error("Invalid project directory: empty");
|
|
964
|
-
}
|
|
965
|
-
if (containsPathTraversal(directory)) {
|
|
966
|
-
throw new Error("Invalid project directory: path traversal detected");
|
|
967
|
-
}
|
|
968
|
-
if (containsControlChars(directory)) {
|
|
969
|
-
throw new Error("Invalid project directory: control characters detected");
|
|
970
|
-
}
|
|
971
|
-
if (!path2.isAbsolute(directory) && !/^[A-Za-z]:[/\\]/.test(directory)) {
|
|
972
|
-
throw new Error("Invalid project directory: must be an absolute path");
|
|
973
|
-
}
|
|
974
|
-
assertNotSystemLocation(directory);
|
|
975
|
-
}
|
|
976
|
-
var DENIED_ROOT_SUBTREES = new Set([
|
|
977
|
-
"etc",
|
|
978
|
-
"usr",
|
|
979
|
-
"bin",
|
|
980
|
-
"sbin",
|
|
981
|
-
"lib",
|
|
982
|
-
"lib64",
|
|
983
|
-
"boot",
|
|
984
|
-
"dev",
|
|
985
|
-
"proc",
|
|
986
|
-
"sys",
|
|
987
|
-
"windows",
|
|
988
|
-
"winnt",
|
|
989
|
-
"program files",
|
|
990
|
-
"program files (x86)",
|
|
991
|
-
"programdata",
|
|
992
|
-
"system volume information"
|
|
993
|
-
]);
|
|
994
|
-
var DENIED_EXACT_ROOT_CHILDREN = new Set(["users", "home", "root"]);
|
|
995
|
-
function assertNotSystemLocation(directory) {
|
|
996
|
-
for (const parser of [path2.win32, path2.posix]) {
|
|
997
|
-
if (!parser.isAbsolute(directory))
|
|
998
|
-
continue;
|
|
999
|
-
const normalized = parser.normalize(directory);
|
|
1000
|
-
const { root } = parser.parse(normalized);
|
|
1001
|
-
const segments = normalized.slice(root.length).split(/[/\\]/).filter((segment) => segment.length > 0);
|
|
1002
|
-
if (segments.length === 0) {
|
|
1003
|
-
throw new Error(`Invalid project directory: filesystem root is not a project root (${directory})`);
|
|
1004
|
-
}
|
|
1005
|
-
const first = (segments[0] ?? "").toLowerCase();
|
|
1006
|
-
if (DENIED_ROOT_SUBTREES.has(first) || segments.length === 1 && DENIED_EXACT_ROOT_CHILDREN.has(first)) {
|
|
1007
|
-
throw new Error(`Invalid project directory: system location is not a project root (${directory})`);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
function resolveNearestExistingCanonical(inputPath, resolver) {
|
|
1012
|
-
try {
|
|
1013
|
-
return resolver(inputPath);
|
|
1014
|
-
} catch {}
|
|
1015
|
-
const lexicallyResolved = path2.resolve(inputPath);
|
|
1016
|
-
let probe = lexicallyResolved;
|
|
1017
|
-
const tail = [];
|
|
1018
|
-
for (let i = 0;i < 4096; i++) {
|
|
1019
|
-
const parent = path2.dirname(probe);
|
|
1020
|
-
if (parent === probe) {
|
|
1021
|
-
return lexicallyResolved;
|
|
1022
|
-
}
|
|
1023
|
-
tail.unshift(path2.basename(probe));
|
|
1024
|
-
probe = parent;
|
|
1025
|
-
try {
|
|
1026
|
-
const canonicalAncestor = resolver(probe);
|
|
1027
|
-
return path2.normalize(path2.join(canonicalAncestor, ...tail));
|
|
1028
|
-
} catch {}
|
|
1029
|
-
}
|
|
1030
|
-
return lexicallyResolved;
|
|
1031
|
-
}
|
|
1032
|
-
function validateSymlinkBoundary(targetPath, rootPath) {
|
|
1033
|
-
const realTarget = resolveNearestExistingCanonical(targetPath, _internals2.realpathSync);
|
|
1034
|
-
const realRoot = resolveNearestExistingCanonical(rootPath, _internals2.realpathSync);
|
|
1035
|
-
const normalizedTarget = path2.normalize(realTarget);
|
|
1036
|
-
const normalizedRoot = path2.normalize(realRoot);
|
|
1037
|
-
if (!normalizedTarget.startsWith(normalizedRoot + path2.sep) && normalizedTarget !== normalizedRoot) {
|
|
1038
|
-
throw new Error(`Symlink resolution escaped boundary: ${realTarget} is not within ${realRoot}`);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
function isCanonicalPathWithinRoot(targetPath, rootPath) {
|
|
1042
|
-
let canonicalRoot;
|
|
1043
|
-
try {
|
|
1044
|
-
canonicalRoot = fs.realpathSync(rootPath);
|
|
1045
|
-
} catch {
|
|
1046
|
-
canonicalRoot = path2.normalize(rootPath);
|
|
1047
|
-
}
|
|
1048
|
-
let probe = path2.normalize(targetPath);
|
|
1049
|
-
for (let i = 0;i < 4096; i++) {
|
|
1050
|
-
try {
|
|
1051
|
-
const canonicalProbe = fs.realpathSync(probe);
|
|
1052
|
-
const relative2 = path2.relative(canonicalRoot, canonicalProbe);
|
|
1053
|
-
return !relative2.startsWith("..") && !path2.isAbsolute(relative2);
|
|
1054
|
-
} catch {
|
|
1055
|
-
const parent = path2.dirname(probe);
|
|
1056
|
-
if (parent === probe)
|
|
1057
|
-
return false;
|
|
1058
|
-
probe = parent;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
return false;
|
|
1062
|
-
}
|
|
1063
|
-
function validateTargetWithinRoot(filePath, root) {
|
|
1064
|
-
if (!filePath || filePath.trim() === "") {
|
|
1065
|
-
return "Empty path";
|
|
1066
|
-
}
|
|
1067
|
-
if (path2.isAbsolute(filePath) || /^[A-Za-z]:[/\\]/.test(filePath)) {
|
|
1068
|
-
return `Absolute path rejected: ${filePath}`;
|
|
1069
|
-
}
|
|
1070
|
-
if (containsPathTraversal(filePath)) {
|
|
1071
|
-
return `Path traversal detected: ${filePath}`;
|
|
1072
|
-
}
|
|
1073
|
-
if (containsControlChars(filePath)) {
|
|
1074
|
-
return `Control characters detected in path: ${filePath}`;
|
|
1075
|
-
}
|
|
1076
|
-
const resolved = path2.resolve(root, filePath);
|
|
1077
|
-
const relative2 = path2.relative(root, resolved);
|
|
1078
|
-
if (relative2.startsWith("..") || path2.isAbsolute(relative2)) {
|
|
1079
|
-
return `Path escapes root: ${filePath}`;
|
|
1080
|
-
}
|
|
1081
|
-
if (!isCanonicalPathWithinRoot(resolved, root)) {
|
|
1082
|
-
return `Path escapes root via symlink/junction: ${filePath}`;
|
|
1083
|
-
}
|
|
1084
|
-
return null;
|
|
1085
|
-
}
|
|
1086
|
-
var _internals2 = {
|
|
1087
|
-
realpathSync: fs.realpathSync,
|
|
1088
|
-
containsPathTraversal,
|
|
1089
|
-
containsControlChars,
|
|
1090
|
-
validateDirectory,
|
|
1091
|
-
validateProjectDirectory,
|
|
1092
|
-
validateSymlinkBoundary,
|
|
1093
|
-
isCanonicalPathWithinRoot,
|
|
1094
|
-
validateTargetWithinRoot
|
|
1095
|
-
};
|
|
1096
|
-
|
|
1097
|
-
// src/services/run-memory.ts
|
|
1098
904
|
var RUN_MEMORY_FILENAME = "run-memory.jsonl";
|
|
1099
905
|
var MAX_SUMMARY_TOKENS = 500;
|
|
1100
906
|
function generateTaskFingerprint(taskId, fileTargets) {
|
|
@@ -1107,7 +913,7 @@ async function recordOutcome(directory, entry) {
|
|
|
1107
913
|
const resolvedPath = validateSwarmPath(directory, RUN_MEMORY_FILENAME);
|
|
1108
914
|
const line = `${JSON.stringify(entry)}
|
|
1109
915
|
`;
|
|
1110
|
-
await
|
|
916
|
+
await fs.appendFile(resolvedPath, line, { encoding: "utf-8" });
|
|
1111
917
|
}
|
|
1112
918
|
async function getTaskHistory(directory, taskId) {
|
|
1113
919
|
validateWorkspaceRoot(directory);
|
|
@@ -1134,14 +940,14 @@ async function recordTaskAttempt(directory, input) {
|
|
|
1134
940
|
try {
|
|
1135
941
|
let history = [];
|
|
1136
942
|
try {
|
|
1137
|
-
history = await
|
|
943
|
+
history = await _internals2.getTaskHistory(directory, input.taskId);
|
|
1138
944
|
} catch (err) {
|
|
1139
945
|
warn(`[run-memory] history read for task ${input.taskId} failed; recording with attemptNumber 1: ${err instanceof Error ? err.message : String(err)}`);
|
|
1140
946
|
}
|
|
1141
947
|
const entry = {
|
|
1142
948
|
timestamp: new Date().toISOString(),
|
|
1143
949
|
taskId: input.taskId,
|
|
1144
|
-
taskFingerprint:
|
|
950
|
+
taskFingerprint: _internals2.generateTaskFingerprint(input.taskId, input.fileTargets ?? []),
|
|
1145
951
|
agent: input.agent,
|
|
1146
952
|
outcome: input.outcome,
|
|
1147
953
|
attemptNumber: history.length + 1
|
|
@@ -1155,7 +961,7 @@ async function recordTaskAttempt(directory, input) {
|
|
|
1155
961
|
if (typeof input.durationMs === "number") {
|
|
1156
962
|
entry.durationMs = input.durationMs;
|
|
1157
963
|
}
|
|
1158
|
-
await
|
|
964
|
+
await _internals2.recordOutcome(directory, entry);
|
|
1159
965
|
} catch (err) {
|
|
1160
966
|
warn(`[run-memory] failed to record ${input.outcome} for task ${input.taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1161
967
|
}
|
|
@@ -1219,10 +1025,10 @@ async function getRunMemorySummary(directory) {
|
|
|
1219
1025
|
if (entries.length === 0) {
|
|
1220
1026
|
return null;
|
|
1221
1027
|
}
|
|
1222
|
-
const groups =
|
|
1028
|
+
const groups = _internals2.groupByTaskId(entries);
|
|
1223
1029
|
const summaries = [];
|
|
1224
1030
|
for (const [taskId, taskEntries] of groups) {
|
|
1225
|
-
const summary =
|
|
1031
|
+
const summary = _internals2.summarizeTask(taskId, taskEntries);
|
|
1226
1032
|
if (summary) {
|
|
1227
1033
|
summaries.push(summary);
|
|
1228
1034
|
}
|
|
@@ -1252,7 +1058,7 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
1252
1058
|
}
|
|
1253
1059
|
return prefix + summaryText + suffix;
|
|
1254
1060
|
}
|
|
1255
|
-
var
|
|
1061
|
+
var _internals2 = {
|
|
1256
1062
|
generateTaskFingerprint,
|
|
1257
1063
|
recordOutcome,
|
|
1258
1064
|
recordTaskAttempt,
|
|
@@ -1263,17 +1069,17 @@ var _internals3 = {
|
|
|
1263
1069
|
};
|
|
1264
1070
|
|
|
1265
1071
|
// src/turbo/epic/state.ts
|
|
1072
|
+
import * as fs2 from "fs";
|
|
1073
|
+
import * as path2 from "path";
|
|
1266
1074
|
init_logger();
|
|
1267
|
-
import * as fs3 from "fs";
|
|
1268
|
-
import * as path3 from "path";
|
|
1269
1075
|
var STATE_FILE = "epic-state.json";
|
|
1270
1076
|
function nowISO() {
|
|
1271
1077
|
return new Date().toISOString();
|
|
1272
1078
|
}
|
|
1273
1079
|
function ensureSwarmDir(directory) {
|
|
1274
|
-
const swarmDir =
|
|
1275
|
-
if (!
|
|
1276
|
-
|
|
1080
|
+
const swarmDir = path2.resolve(directory, ".swarm");
|
|
1081
|
+
if (!fs2.existsSync(swarmDir)) {
|
|
1082
|
+
fs2.mkdirSync(swarmDir, { recursive: true });
|
|
1277
1083
|
}
|
|
1278
1084
|
return swarmDir;
|
|
1279
1085
|
}
|
|
@@ -1284,26 +1090,29 @@ function emptySessionState(sessionID) {
|
|
|
1284
1090
|
return { sessionID, active: false };
|
|
1285
1091
|
}
|
|
1286
1092
|
var stateUnreadableMap = new Map;
|
|
1093
|
+
function stateKey(directory) {
|
|
1094
|
+
return canonicalRootKeyFresh(directory);
|
|
1095
|
+
}
|
|
1287
1096
|
function isStateUnreadable(directory) {
|
|
1288
|
-
return stateUnreadableMap.get(directory) ?? false;
|
|
1097
|
+
return stateUnreadableMap.get(stateKey(directory)) ?? false;
|
|
1289
1098
|
}
|
|
1290
1099
|
function markStateUnreadable(directory, reason) {
|
|
1291
|
-
stateUnreadableMap.set(directory, true);
|
|
1100
|
+
stateUnreadableMap.set(stateKey(directory), true);
|
|
1292
1101
|
error(`[turbo/epic/state] state file unreadable for ${directory}: ${reason} \u2014 failing closed`);
|
|
1293
1102
|
}
|
|
1294
1103
|
function readPersisted(directory) {
|
|
1295
1104
|
try {
|
|
1296
|
-
const filePath =
|
|
1297
|
-
if (!
|
|
1105
|
+
const filePath = path2.join(directory, ".swarm", STATE_FILE);
|
|
1106
|
+
if (!fs2.existsSync(filePath)) {
|
|
1298
1107
|
const seed = emptyPersisted();
|
|
1299
1108
|
try {
|
|
1300
1109
|
ensureSwarmDir(directory);
|
|
1301
|
-
|
|
1110
|
+
fs2.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
|
|
1302
1111
|
`, "utf-8");
|
|
1303
1112
|
} catch {}
|
|
1304
1113
|
return seed;
|
|
1305
1114
|
}
|
|
1306
|
-
const raw =
|
|
1115
|
+
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
1307
1116
|
const parsed = JSON.parse(raw);
|
|
1308
1117
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
1309
1118
|
markStateUnreadable(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
@@ -1321,7 +1130,7 @@ function readPersisted(directory) {
|
|
|
1321
1130
|
}
|
|
1322
1131
|
}
|
|
1323
1132
|
function writePersisted(directory, persisted) {
|
|
1324
|
-
if (stateUnreadableMap.get(directory)) {
|
|
1133
|
+
if (stateUnreadableMap.get(stateKey(directory))) {
|
|
1325
1134
|
throw new Error(`Epic state is unreadable. Please repair .swarm/${STATE_FILE} before continuing.`);
|
|
1326
1135
|
}
|
|
1327
1136
|
let filePath;
|
|
@@ -1329,7 +1138,7 @@ function writePersisted(directory, persisted) {
|
|
|
1329
1138
|
let payload;
|
|
1330
1139
|
try {
|
|
1331
1140
|
ensureSwarmDir(directory);
|
|
1332
|
-
filePath =
|
|
1141
|
+
filePath = path2.join(directory, ".swarm", STATE_FILE);
|
|
1333
1142
|
tmpPath = `${filePath}.tmp.${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1334
1143
|
persisted.updatedAt = nowISO();
|
|
1335
1144
|
payload = `${JSON.stringify(persisted, null, 2)}
|
|
@@ -1340,20 +1149,20 @@ function writePersisted(directory, persisted) {
|
|
|
1340
1149
|
throw new Error(`Epic state persistence prepare failed: ${msg}`);
|
|
1341
1150
|
}
|
|
1342
1151
|
try {
|
|
1343
|
-
|
|
1344
|
-
|
|
1152
|
+
fs2.writeFileSync(tmpPath, payload, "utf-8");
|
|
1153
|
+
fs2.renameSync(tmpPath, filePath);
|
|
1345
1154
|
} catch (error2) {
|
|
1346
1155
|
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
1347
1156
|
error(`[turbo/epic/state] Failed to persist ${STATE_FILE} atomically: ${msg}`);
|
|
1348
1157
|
try {
|
|
1349
|
-
if (
|
|
1350
|
-
|
|
1158
|
+
if (fs2.existsSync(tmpPath))
|
|
1159
|
+
fs2.unlinkSync(tmpPath);
|
|
1351
1160
|
} catch {}
|
|
1352
1161
|
throw new Error(`Epic state persistence failed: ${msg}`);
|
|
1353
1162
|
}
|
|
1354
1163
|
}
|
|
1355
1164
|
function loadEpicSessionState(directory, sessionID) {
|
|
1356
|
-
if (stateUnreadableMap.get(directory))
|
|
1165
|
+
if (stateUnreadableMap.get(stateKey(directory)))
|
|
1357
1166
|
return null;
|
|
1358
1167
|
const persisted = readPersisted(directory);
|
|
1359
1168
|
if (!persisted)
|
|
@@ -1361,7 +1170,7 @@ function loadEpicSessionState(directory, sessionID) {
|
|
|
1361
1170
|
return persisted.sessions[sessionID] ?? null;
|
|
1362
1171
|
}
|
|
1363
1172
|
function saveEpicSessionState(directory, state) {
|
|
1364
|
-
if (stateUnreadableMap.get(directory)) {
|
|
1173
|
+
if (stateUnreadableMap.get(stateKey(directory))) {
|
|
1365
1174
|
throw new Error(`Epic state is unreadable for ${directory}. Repair .swarm/${STATE_FILE} before continuing.`);
|
|
1366
1175
|
}
|
|
1367
1176
|
const persisted = readPersisted(directory);
|
|
@@ -1376,9 +1185,9 @@ function isEpicModeActive(directory, sessionID) {
|
|
|
1376
1185
|
return state?.active === true;
|
|
1377
1186
|
}
|
|
1378
1187
|
function isEpicModeActiveForProject(directory) {
|
|
1379
|
-
if (stateUnreadableMap.get(directory))
|
|
1188
|
+
if (stateUnreadableMap.get(stateKey(directory)))
|
|
1380
1189
|
return false;
|
|
1381
|
-
if (!
|
|
1190
|
+
if (!fs2.existsSync(path2.join(directory, ".swarm", STATE_FILE))) {
|
|
1382
1191
|
return false;
|
|
1383
1192
|
}
|
|
1384
1193
|
const persisted = readPersisted(directory);
|
|
@@ -1429,11 +1238,11 @@ function isLockContentionError(err) {
|
|
|
1429
1238
|
return INDEX_LOCK_ERROR_RE.test(msg);
|
|
1430
1239
|
}
|
|
1431
1240
|
async function commitTaskCompletion(directory, taskId, description, scopePaths) {
|
|
1432
|
-
if (!
|
|
1241
|
+
if (!_internals3.isGitRepo(directory)) {
|
|
1433
1242
|
return { committed: false, reason: "no-git" };
|
|
1434
1243
|
}
|
|
1435
1244
|
try {
|
|
1436
|
-
if (
|
|
1245
|
+
if (_internals3.hasExistingTaskCommit(directory, taskId)) {
|
|
1437
1246
|
return { committed: true, reason: "idempotent-skip" };
|
|
1438
1247
|
}
|
|
1439
1248
|
} catch {}
|
|
@@ -1454,15 +1263,15 @@ async function commitTaskCompletion(directory, taskId, description, scopePaths)
|
|
|
1454
1263
|
for (let attempt = 0;attempt <= INDEX_LOCK_BACKOFF_MS.length; attempt++) {
|
|
1455
1264
|
try {
|
|
1456
1265
|
if (paths.length > 0) {
|
|
1457
|
-
|
|
1266
|
+
_internals3.stageScopedPaths(directory, paths);
|
|
1458
1267
|
}
|
|
1459
|
-
|
|
1460
|
-
const sha =
|
|
1268
|
+
_internals3.commitAllowEmpty(directory, message);
|
|
1269
|
+
const sha = _internals3.gitHeadSha(directory);
|
|
1461
1270
|
return { committed: true, reason: "success", sha };
|
|
1462
1271
|
} catch (err) {
|
|
1463
1272
|
lastError = err;
|
|
1464
1273
|
if (attempt < INDEX_LOCK_BACKOFF_MS.length && isLockContentionError(err)) {
|
|
1465
|
-
await
|
|
1274
|
+
await _internals3.sleep(INDEX_LOCK_BACKOFF_MS[attempt]);
|
|
1466
1275
|
continue;
|
|
1467
1276
|
}
|
|
1468
1277
|
break;
|
|
@@ -1472,7 +1281,7 @@ async function commitTaskCompletion(directory, taskId, description, scopePaths)
|
|
|
1472
1281
|
criticalWarn(`[epic:task-commit] commit for task ${taskId} failed (non-fatal): ${msg}`);
|
|
1473
1282
|
return { committed: false, reason: "commit-failed", error: msg };
|
|
1474
1283
|
}
|
|
1475
|
-
var
|
|
1284
|
+
var _internals3 = {
|
|
1476
1285
|
isGitRepo: (cwd) => isGitRepo(cwd),
|
|
1477
1286
|
stageScopedPaths: (cwd, paths) => {
|
|
1478
1287
|
const CHUNK = 200;
|
|
@@ -1487,7 +1296,7 @@ var _internals4 = {
|
|
|
1487
1296
|
gitHeadSha: (cwd) => {
|
|
1488
1297
|
return _internals.gitExec(["rev-parse", "HEAD"], cwd).trim();
|
|
1489
1298
|
},
|
|
1490
|
-
sleep: (ms) => new Promise((
|
|
1299
|
+
sleep: (ms) => new Promise((resolve2) => setTimeout(resolve2, ms)),
|
|
1491
1300
|
hasExistingTaskCommit: (cwd, taskId) => {
|
|
1492
1301
|
const safeId = scrubTaskIdForGitSubject(taskId);
|
|
1493
1302
|
const escaped = safeId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1504,8 +1313,8 @@ var _internals4 = {
|
|
|
1504
1313
|
};
|
|
1505
1314
|
|
|
1506
1315
|
// src/turbo/lean/conflicts.ts
|
|
1507
|
-
import * as
|
|
1508
|
-
import * as
|
|
1316
|
+
import * as fs3 from "fs";
|
|
1317
|
+
import * as path3 from "path";
|
|
1509
1318
|
|
|
1510
1319
|
// src/utils/path.ts
|
|
1511
1320
|
function normalizePath(p) {
|
|
@@ -1633,12 +1442,12 @@ function isProtectedPath(normalizedPath) {
|
|
|
1633
1442
|
return false;
|
|
1634
1443
|
}
|
|
1635
1444
|
function readTaskScopes(directory, taskId) {
|
|
1636
|
-
const scopePath =
|
|
1445
|
+
const scopePath = path3.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
|
|
1637
1446
|
try {
|
|
1638
|
-
if (!
|
|
1447
|
+
if (!fs3.existsSync(scopePath)) {
|
|
1639
1448
|
return null;
|
|
1640
1449
|
}
|
|
1641
|
-
const raw =
|
|
1450
|
+
const raw = fs3.readFileSync(scopePath, "utf-8");
|
|
1642
1451
|
const parsed = JSON.parse(raw);
|
|
1643
1452
|
if (!parsed || !Array.isArray(parsed.files)) {
|
|
1644
1453
|
return null;
|
|
@@ -1652,15 +1461,15 @@ function readTaskScopes(directory, taskId) {
|
|
|
1652
1461
|
// src/utils/spec-hash.ts
|
|
1653
1462
|
import { createHash as createHash3 } from "crypto";
|
|
1654
1463
|
import * as fsSync2 from "fs";
|
|
1655
|
-
import { join as
|
|
1464
|
+
import { join as join3 } from "path";
|
|
1656
1465
|
async function computeSpecHash(directory) {
|
|
1657
|
-
const spec =
|
|
1466
|
+
const spec = _internals4.readEffectiveSpecSync(directory);
|
|
1658
1467
|
if (!spec)
|
|
1659
1468
|
return null;
|
|
1660
1469
|
return createHash3("sha256").update(spec.content, "utf-8").digest("hex");
|
|
1661
1470
|
}
|
|
1662
1471
|
async function isSpecStale(directory, plan) {
|
|
1663
|
-
const currentHash = await
|
|
1472
|
+
const currentHash = await _internals4.computeSpecHash(directory);
|
|
1664
1473
|
if (!plan.specHash) {
|
|
1665
1474
|
return { stale: false };
|
|
1666
1475
|
}
|
|
@@ -1681,7 +1490,7 @@ async function isSpecStale(directory, plan) {
|
|
|
1681
1490
|
return { stale: false };
|
|
1682
1491
|
}
|
|
1683
1492
|
function isObligationPreserving(directory) {
|
|
1684
|
-
const snapshotPath =
|
|
1493
|
+
const snapshotPath = join3(directory, ".swarm", "spec-snapshot.md");
|
|
1685
1494
|
try {
|
|
1686
1495
|
let recordedContent;
|
|
1687
1496
|
try {
|
|
@@ -1689,15 +1498,15 @@ function isObligationPreserving(directory) {
|
|
|
1689
1498
|
} catch {
|
|
1690
1499
|
return false;
|
|
1691
1500
|
}
|
|
1692
|
-
const currentResult =
|
|
1501
|
+
const currentResult = _internals4.readEffectiveSpecSync(directory);
|
|
1693
1502
|
if (!currentResult || !currentResult.content) {
|
|
1694
1503
|
return false;
|
|
1695
1504
|
}
|
|
1696
1505
|
const currentContent = currentResult.content;
|
|
1697
|
-
const
|
|
1506
|
+
const normalize = (text) => text.replace(/\r\n/g, `
|
|
1698
1507
|
`);
|
|
1699
|
-
const recordedNorm =
|
|
1700
|
-
const currentNorm =
|
|
1508
|
+
const recordedNorm = normalize(recordedContent);
|
|
1509
|
+
const currentNorm = normalize(currentContent);
|
|
1701
1510
|
const splitParagraphs = (text) => text.split(/\n\s*\n/).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
1702
1511
|
const recordedParagraphs = splitParagraphs(recordedNorm);
|
|
1703
1512
|
const currentParagraphs = splitParagraphs(currentNorm);
|
|
@@ -1719,7 +1528,7 @@ function isObligationPreserving(directory) {
|
|
|
1719
1528
|
return false;
|
|
1720
1529
|
}
|
|
1721
1530
|
}
|
|
1722
|
-
var
|
|
1531
|
+
var _internals4 = {
|
|
1723
1532
|
computeSpecDiff,
|
|
1724
1533
|
computeSpecHash,
|
|
1725
1534
|
isObligationPreserving,
|
|
@@ -1727,7 +1536,7 @@ var _internals5 = {
|
|
|
1727
1536
|
readEffectiveSpecSync
|
|
1728
1537
|
};
|
|
1729
1538
|
function computeSpecDiff(directory) {
|
|
1730
|
-
const snapshotPath =
|
|
1539
|
+
const snapshotPath = join3(directory, ".swarm", "spec-snapshot.md");
|
|
1731
1540
|
let recordedContent;
|
|
1732
1541
|
try {
|
|
1733
1542
|
const snapshotSize = fsSync2.statSync(snapshotPath).size;
|
|
@@ -1739,16 +1548,16 @@ function computeSpecDiff(directory) {
|
|
|
1739
1548
|
} catch {
|
|
1740
1549
|
return null;
|
|
1741
1550
|
}
|
|
1742
|
-
const spec =
|
|
1551
|
+
const spec = _internals4.readEffectiveSpecSync(directory);
|
|
1743
1552
|
if (!spec) {
|
|
1744
1553
|
return null;
|
|
1745
1554
|
}
|
|
1746
1555
|
const currentContent = spec.content;
|
|
1747
|
-
const
|
|
1556
|
+
const normalize = (text) => text.replace(/\r\n/g, `
|
|
1748
1557
|
`);
|
|
1749
|
-
const recordedLines =
|
|
1558
|
+
const recordedLines = normalize(recordedContent).split(`
|
|
1750
1559
|
`);
|
|
1751
|
-
const currentLines =
|
|
1560
|
+
const currentLines = normalize(currentContent).split(`
|
|
1752
1561
|
`);
|
|
1753
1562
|
const diffLines = computeLcsDiff(recordedLines, currentLines);
|
|
1754
1563
|
const MAX_DIFF_LINES = 300;
|
|
@@ -1833,7 +1642,7 @@ var startupLedgerCheckedWorkspaces = new Set;
|
|
|
1833
1642
|
var ledgerStaleWorkspaces = new Set;
|
|
1834
1643
|
var recoveryMutexes = new Map;
|
|
1835
1644
|
var PLAN_JSON_CACHE_NAMESPACE = "plan-json:validated:v1";
|
|
1836
|
-
var
|
|
1645
|
+
var _internals5 = {
|
|
1837
1646
|
loadPlan,
|
|
1838
1647
|
loadPlanJsonOnly,
|
|
1839
1648
|
regeneratePlanMarkdown,
|
|
@@ -1931,7 +1740,7 @@ async function retryCasWithBackoff(directory, eventInput, options) {
|
|
|
1931
1740
|
expectedHashPrefix: currentExpected.slice(0, 8),
|
|
1932
1741
|
delayMs
|
|
1933
1742
|
});
|
|
1934
|
-
await new Promise((
|
|
1743
|
+
await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
1935
1744
|
if (options.verifyValid) {
|
|
1936
1745
|
const stillValid = await options.verifyValid();
|
|
1937
1746
|
if (!stillValid)
|
|
@@ -1974,7 +1783,7 @@ async function getLatestLedgerHash(directory) {
|
|
|
1974
1783
|
}
|
|
1975
1784
|
}
|
|
1976
1785
|
async function surfaceLedgerStaleIfPersisted(directory, plan) {
|
|
1977
|
-
const resolvedWorkspace =
|
|
1786
|
+
const resolvedWorkspace = canonicalRootKeyFresh(directory);
|
|
1978
1787
|
if (!ledgerStaleWorkspaces.has(resolvedWorkspace)) {
|
|
1979
1788
|
return plan;
|
|
1980
1789
|
}
|
|
@@ -1993,7 +1802,7 @@ async function surfaceLedgerStaleIfPersisted(directory, plan) {
|
|
|
1993
1802
|
return plan;
|
|
1994
1803
|
}
|
|
1995
1804
|
async function parsePlanJsonCached(directory) {
|
|
1996
|
-
const planJsonPath =
|
|
1805
|
+
const planJsonPath = path4.resolve(directory, ".swarm", "plan.json");
|
|
1997
1806
|
return readCachedParsedFile(planJsonPath, PLAN_JSON_CACHE_NAMESPACE, () => readSwarmFileAsync(directory, "plan.json"), (planJsonContent) => {
|
|
1998
1807
|
if (planJsonContent.includes("\x00") || planJsonContent.includes("\uFFFD")) {
|
|
1999
1808
|
throw new Error("Plan rejected: .swarm/plan.json contains null bytes or invalid encoding");
|
|
@@ -2055,13 +1864,13 @@ async function isPlanMdInSync(directory, plan, cache) {
|
|
|
2055
1864
|
}
|
|
2056
1865
|
async function regeneratePlanMarkdown(directory, plan) {
|
|
2057
1866
|
assertProjectRoot(directory);
|
|
2058
|
-
const swarmDir =
|
|
1867
|
+
const swarmDir = path4.resolve(directory, ".swarm");
|
|
2059
1868
|
const contentHash = computePlanContentHash(plan);
|
|
2060
1869
|
const markdown = derivePlanMarkdown(plan);
|
|
2061
1870
|
const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
|
|
2062
1871
|
${markdown}`;
|
|
2063
|
-
const mdPath =
|
|
2064
|
-
const mdTempPath =
|
|
1872
|
+
const mdPath = path4.join(swarmDir, "plan.md");
|
|
1873
|
+
const mdTempPath = path4.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
2065
1874
|
try {
|
|
2066
1875
|
await bunWrite(mdTempPath, markdownWithHash);
|
|
2067
1876
|
renameSync2(mdTempPath, mdPath);
|
|
@@ -2086,7 +1895,7 @@ async function loadPlan(directory, cache) {
|
|
|
2086
1895
|
const inSync = await isPlanMdInSync(directory, validated, cache);
|
|
2087
1896
|
if (!inSync) {
|
|
2088
1897
|
try {
|
|
2089
|
-
await
|
|
1898
|
+
await _internals5.regeneratePlanMarkdown(directory, validated);
|
|
2090
1899
|
} catch (regenError) {
|
|
2091
1900
|
warn(`Failed to regenerate plan.md: ${regenError instanceof Error ? regenError.message : String(regenError)}. Proceeding with plan.json only.`);
|
|
2092
1901
|
}
|
|
@@ -2094,7 +1903,7 @@ async function loadPlan(directory, cache) {
|
|
|
2094
1903
|
if (await ledgerExists(directory)) {
|
|
2095
1904
|
const planHash = computePlanLedgerHash(validated);
|
|
2096
1905
|
const ledgerHash = await getLatestLedgerHash(directory);
|
|
2097
|
-
const resolvedWorkspace =
|
|
1906
|
+
const resolvedWorkspace = canonicalRootKeyFresh(directory);
|
|
2098
1907
|
if (!startupLedgerCheckedWorkspaces.has(resolvedWorkspace)) {
|
|
2099
1908
|
startupLedgerCheckedWorkspaces.add(resolvedWorkspace);
|
|
2100
1909
|
if (ledgerHash !== "" && planHash !== ledgerHash) {
|
|
@@ -2169,7 +1978,7 @@ async function loadPlan(directory, cache) {
|
|
|
2169
1978
|
try {
|
|
2170
1979
|
assertProjectRoot(directory);
|
|
2171
1980
|
const diffInfo = computeSpecDiff(directory);
|
|
2172
|
-
const specStalenessPath =
|
|
1981
|
+
const specStalenessPath = path4.join(directory, ".swarm", "spec-staleness.json");
|
|
2173
1982
|
await fsPromises.writeFile(specStalenessPath, JSON.stringify({
|
|
2174
1983
|
type: "spec_stale_detected",
|
|
2175
1984
|
timestamp: new Date().toISOString(),
|
|
@@ -2249,11 +2058,11 @@ async function loadPlan(directory, cache) {
|
|
|
2249
2058
|
return migrated;
|
|
2250
2059
|
}
|
|
2251
2060
|
if (await ledgerExists(directory)) {
|
|
2252
|
-
const resolvedDir =
|
|
2061
|
+
const resolvedDir = canonicalRootKeyFresh(directory);
|
|
2253
2062
|
const existingMutex = recoveryMutexes.get(resolvedDir);
|
|
2254
2063
|
if (existingMutex) {
|
|
2255
2064
|
await existingMutex;
|
|
2256
|
-
const postRecoveryPlan = await
|
|
2065
|
+
const postRecoveryPlan = await _internals5.loadPlanJsonOnly(directory);
|
|
2257
2066
|
if (postRecoveryPlan)
|
|
2258
2067
|
return postRecoveryPlan;
|
|
2259
2068
|
}
|
|
@@ -2313,7 +2122,7 @@ async function loadPlan(directory, cache) {
|
|
|
2313
2122
|
return null;
|
|
2314
2123
|
}
|
|
2315
2124
|
async function savePlanWithAutoAcknowledgedRemovals(directory, plan, source, reason, options) {
|
|
2316
|
-
const existing = await
|
|
2125
|
+
const existing = await _internals5.loadPlanJsonOnly(directory);
|
|
2317
2126
|
const newIds = new Set;
|
|
2318
2127
|
for (const phase of plan.phases) {
|
|
2319
2128
|
for (const task of phase.tasks)
|
|
@@ -2363,7 +2172,7 @@ async function savePlan(directory, plan, options) {
|
|
|
2363
2172
|
}
|
|
2364
2173
|
if (options?.preserveCompletedStatuses !== false) {
|
|
2365
2174
|
try {
|
|
2366
|
-
const currentPlan2 = await
|
|
2175
|
+
const currentPlan2 = await _internals5.loadPlanJsonOnly(directory);
|
|
2367
2176
|
if (currentPlan2) {
|
|
2368
2177
|
const completedTaskIds = new Set;
|
|
2369
2178
|
for (const phase of currentPlan2.phases) {
|
|
@@ -2385,7 +2194,7 @@ async function savePlan(directory, plan, options) {
|
|
|
2385
2194
|
} catch {}
|
|
2386
2195
|
}
|
|
2387
2196
|
derivePhaseStatusesInPlace(validated);
|
|
2388
|
-
const currentPlan = await
|
|
2197
|
+
const currentPlan = await _internals5.loadPlanJsonOnly(directory);
|
|
2389
2198
|
const planId = derivePlanId(validated);
|
|
2390
2199
|
const planHashForInit = computePlanLedgerHash(validated);
|
|
2391
2200
|
if (!await ledgerExists(directory)) {
|
|
@@ -2401,9 +2210,9 @@ async function savePlan(directory, plan, options) {
|
|
|
2401
2210
|
} else {
|
|
2402
2211
|
const existingEvents = await readLedgerEvents(directory);
|
|
2403
2212
|
if (existingEvents.length > 0 && existingEvents[0].plan_id !== planId) {
|
|
2404
|
-
const swarmDir2 =
|
|
2405
|
-
const oldLedgerPath =
|
|
2406
|
-
const oldLedgerBackupPath =
|
|
2213
|
+
const swarmDir2 = path4.resolve(directory, ".swarm");
|
|
2214
|
+
const oldLedgerPath = path4.join(swarmDir2, "plan-ledger.jsonl");
|
|
2215
|
+
const oldLedgerBackupPath = path4.join(swarmDir2, `plan-ledger.backup-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
|
|
2407
2216
|
let backupExists = false;
|
|
2408
2217
|
if (existsSync3(oldLedgerPath)) {
|
|
2409
2218
|
try {
|
|
@@ -2443,7 +2252,7 @@ async function savePlan(directory, plan, options) {
|
|
|
2443
2252
|
}
|
|
2444
2253
|
}
|
|
2445
2254
|
if (initSucceeded && backupExists) {
|
|
2446
|
-
const archivePath =
|
|
2255
|
+
const archivePath = path4.join(swarmDir2, `plan-ledger.archived-${Date.now()}-${Math.floor(Math.random() * 1e9)}.jsonl`);
|
|
2447
2256
|
try {
|
|
2448
2257
|
renameSync2(oldLedgerBackupPath, archivePath);
|
|
2449
2258
|
warn(`[savePlan] Ledger identity mismatch (was "${existingEvents[0].plan_id}", now "${planId}") \u2014 archived old ledger to ${archivePath} and reinitializing.`);
|
|
@@ -2468,7 +2277,7 @@ async function savePlan(directory, plan, options) {
|
|
|
2468
2277
|
const toRemove = archivedSiblings.slice(0, archivedSiblings.length - MAX_ARCHIVED_SIBLINGS);
|
|
2469
2278
|
for (const file of toRemove) {
|
|
2470
2279
|
try {
|
|
2471
|
-
unlinkSync2(
|
|
2280
|
+
unlinkSync2(path4.join(swarmDir2, file));
|
|
2472
2281
|
} catch {}
|
|
2473
2282
|
}
|
|
2474
2283
|
}
|
|
@@ -2571,7 +2380,7 @@ async function savePlan(directory, plan, options) {
|
|
|
2571
2380
|
expectedHash: currentHash,
|
|
2572
2381
|
planHashAfter: hashAfter,
|
|
2573
2382
|
verifyValid: async () => {
|
|
2574
|
-
const onDisk = await
|
|
2383
|
+
const onDisk = await _internals5.loadPlanJsonOnly(directory);
|
|
2575
2384
|
if (!onDisk)
|
|
2576
2385
|
return true;
|
|
2577
2386
|
for (const p of onDisk.phases) {
|
|
@@ -2610,7 +2419,7 @@ async function savePlan(directory, plan, options) {
|
|
|
2610
2419
|
expectedHash: currentHash,
|
|
2611
2420
|
planHashAfter: hashAfter,
|
|
2612
2421
|
verifyValid: async () => {
|
|
2613
|
-
const onDisk = await
|
|
2422
|
+
const onDisk = await _internals5.loadPlanJsonOnly(directory);
|
|
2614
2423
|
if (!onDisk)
|
|
2615
2424
|
return true;
|
|
2616
2425
|
for (const p of onDisk.phases) {
|
|
@@ -2651,9 +2460,9 @@ async function savePlan(directory, plan, options) {
|
|
|
2651
2460
|
source: "savePlan_manager"
|
|
2652
2461
|
});
|
|
2653
2462
|
}
|
|
2654
|
-
const swarmDir =
|
|
2655
|
-
const planPath =
|
|
2656
|
-
const tempPath =
|
|
2463
|
+
const swarmDir = path4.resolve(directory, ".swarm");
|
|
2464
|
+
const planPath = path4.join(swarmDir, "plan.json");
|
|
2465
|
+
const tempPath = path4.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
2657
2466
|
try {
|
|
2658
2467
|
await bunWrite(tempPath, JSON.stringify(projectedPlan, null, 2));
|
|
2659
2468
|
options?.preCommitCheck?.();
|
|
@@ -2665,7 +2474,7 @@ async function savePlan(directory, plan, options) {
|
|
|
2665
2474
|
}
|
|
2666
2475
|
invalidateCachedArtifact(planPath);
|
|
2667
2476
|
try {
|
|
2668
|
-
const markerPath =
|
|
2477
|
+
const markerPath = path4.join(swarmDir, ".plan-write-marker");
|
|
2669
2478
|
const inProgressMarker = JSON.stringify({
|
|
2670
2479
|
source: "plan_manager",
|
|
2671
2480
|
timestamp: new Date().toISOString(),
|
|
@@ -2680,8 +2489,8 @@ async function savePlan(directory, plan, options) {
|
|
|
2680
2489
|
const markdown = derivePlanMarkdown(projectedPlan);
|
|
2681
2490
|
const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
|
|
2682
2491
|
${markdown}`;
|
|
2683
|
-
const mdPath =
|
|
2684
|
-
const mdTempPath =
|
|
2492
|
+
const mdPath = path4.join(swarmDir, "plan.md");
|
|
2493
|
+
const mdTempPath = path4.join(swarmDir, `plan.md.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
2685
2494
|
try {
|
|
2686
2495
|
await bunWrite(mdTempPath, markdownWithHash);
|
|
2687
2496
|
renameSync2(mdTempPath, mdPath);
|
|
@@ -2703,7 +2512,7 @@ ${markdown}`;
|
|
|
2703
2512
|
} catch {}
|
|
2704
2513
|
}
|
|
2705
2514
|
try {
|
|
2706
|
-
const markerPath =
|
|
2515
|
+
const markerPath = path4.join(swarmDir, ".plan-write-marker");
|
|
2707
2516
|
const tasksCount = projectedPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
|
|
2708
2517
|
const marker = JSON.stringify({
|
|
2709
2518
|
source: "plan_manager",
|
|
@@ -2751,11 +2560,11 @@ async function rebuildPlan(directory, plan, options) {
|
|
|
2751
2560
|
const targetPlan = plan ?? await replayFromLedger(directory);
|
|
2752
2561
|
if (!targetPlan)
|
|
2753
2562
|
return null;
|
|
2754
|
-
const swarmDir =
|
|
2755
|
-
const planPath =
|
|
2756
|
-
const mdPath =
|
|
2563
|
+
const swarmDir = path4.join(directory, ".swarm");
|
|
2564
|
+
const planPath = path4.join(swarmDir, "plan.json");
|
|
2565
|
+
const mdPath = path4.join(swarmDir, "plan.md");
|
|
2757
2566
|
mkdirSync2(swarmDir, { recursive: true });
|
|
2758
|
-
const tempPlanPath =
|
|
2567
|
+
const tempPlanPath = path4.join(swarmDir, `plan.json.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
2759
2568
|
{
|
|
2760
2569
|
const fd = openSync(tempPlanPath, "w");
|
|
2761
2570
|
try {
|
|
@@ -2768,7 +2577,7 @@ async function rebuildPlan(directory, plan, options) {
|
|
|
2768
2577
|
renameSync2(tempPlanPath, planPath);
|
|
2769
2578
|
invalidateCachedArtifact(planPath);
|
|
2770
2579
|
try {
|
|
2771
|
-
const markerPath =
|
|
2580
|
+
const markerPath = path4.join(swarmDir, ".plan-write-marker");
|
|
2772
2581
|
const inProgressMarker = JSON.stringify({
|
|
2773
2582
|
source: "plan_manager",
|
|
2774
2583
|
timestamp: new Date().toISOString(),
|
|
@@ -2783,13 +2592,13 @@ async function rebuildPlan(directory, plan, options) {
|
|
|
2783
2592
|
const markdown = derivePlanMarkdown(targetPlan);
|
|
2784
2593
|
const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
|
|
2785
2594
|
${markdown}`;
|
|
2786
|
-
const tempMdPath =
|
|
2595
|
+
const tempMdPath = path4.join(swarmDir, `plan.md.rebuild.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
2787
2596
|
await bunWrite(tempMdPath, markdownWithHash);
|
|
2788
2597
|
renameSync2(tempMdPath, mdPath);
|
|
2789
2598
|
invalidateCachedArtifact(mdPath);
|
|
2790
2599
|
} finally {
|
|
2791
2600
|
try {
|
|
2792
|
-
const markerPath =
|
|
2601
|
+
const markerPath = path4.join(swarmDir, ".plan-write-marker");
|
|
2793
2602
|
const tasksCount = targetPlan.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
|
|
2794
2603
|
const marker = JSON.stringify({
|
|
2795
2604
|
source: "plan_manager",
|
|
@@ -2853,14 +2662,14 @@ async function closePlanTerminalState(directory, plan, options) {
|
|
|
2853
2662
|
planHashAfter: hashAfter,
|
|
2854
2663
|
source: "close_terminal"
|
|
2855
2664
|
});
|
|
2856
|
-
const swarmDir =
|
|
2857
|
-
const planPath =
|
|
2858
|
-
const tempPlanPath =
|
|
2665
|
+
const swarmDir = path4.join(directory, ".swarm");
|
|
2666
|
+
const planPath = path4.join(swarmDir, "plan.json");
|
|
2667
|
+
const tempPlanPath = path4.join(swarmDir, `plan.json.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
2859
2668
|
await bunWrite(tempPlanPath, JSON.stringify(validated, null, 2));
|
|
2860
2669
|
renameSync2(tempPlanPath, planPath);
|
|
2861
2670
|
invalidateCachedArtifact(planPath);
|
|
2862
2671
|
try {
|
|
2863
|
-
const markerPath =
|
|
2672
|
+
const markerPath = path4.join(swarmDir, ".plan-write-marker");
|
|
2864
2673
|
const inProgressMarker = JSON.stringify({
|
|
2865
2674
|
source: "plan_manager_close",
|
|
2866
2675
|
timestamp: new Date().toISOString(),
|
|
@@ -2871,18 +2680,18 @@ async function closePlanTerminalState(directory, plan, options) {
|
|
|
2871
2680
|
await bunWrite(markerPath, inProgressMarker);
|
|
2872
2681
|
} catch {}
|
|
2873
2682
|
try {
|
|
2874
|
-
const mdPath =
|
|
2683
|
+
const mdPath = path4.join(swarmDir, "plan.md");
|
|
2875
2684
|
const contentHash = computePlanContentHash(validated);
|
|
2876
2685
|
const markdown = derivePlanMarkdown(validated);
|
|
2877
2686
|
const markdownWithHash = `<!-- PLAN_HASH: ${contentHash} -->
|
|
2878
2687
|
${markdown}`;
|
|
2879
|
-
const mdTempPath =
|
|
2688
|
+
const mdTempPath = path4.join(swarmDir, `plan.md.close.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
|
|
2880
2689
|
await bunWrite(mdTempPath, markdownWithHash);
|
|
2881
2690
|
renameSync2(mdTempPath, mdPath);
|
|
2882
2691
|
invalidateCachedArtifact(mdPath);
|
|
2883
2692
|
} finally {
|
|
2884
2693
|
try {
|
|
2885
|
-
const markerPath =
|
|
2694
|
+
const markerPath = path4.join(swarmDir, ".plan-write-marker");
|
|
2886
2695
|
const tasksCount = validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
|
|
2887
2696
|
const marker = JSON.stringify({
|
|
2888
2697
|
source: "plan_manager_close",
|
|
@@ -2910,7 +2719,7 @@ async function updateTaskStatus(directory, taskId, status, options) {
|
|
|
2910
2719
|
return "pending";
|
|
2911
2720
|
};
|
|
2912
2721
|
{
|
|
2913
|
-
const currentPlan = await
|
|
2722
|
+
const currentPlan = await _internals5.loadPlanJsonOnly(directory);
|
|
2914
2723
|
if (currentPlan) {
|
|
2915
2724
|
const currentTask = currentPlan.phases.flatMap((p) => p.tasks).find((t) => t.id === taskId);
|
|
2916
2725
|
const settled = currentTask && currentTask.status !== "pending" && currentTask.status !== "in_progress";
|
|
@@ -2923,7 +2732,7 @@ async function updateTaskStatus(directory, taskId, status, options) {
|
|
|
2923
2732
|
}
|
|
2924
2733
|
const MAX_OUTER_RETRIES = 1;
|
|
2925
2734
|
for (let attempt = 0;attempt <= MAX_OUTER_RETRIES; attempt++) {
|
|
2926
|
-
const plan = await
|
|
2735
|
+
const plan = await _internals5.loadPlan(directory);
|
|
2927
2736
|
if (plan === null) {
|
|
2928
2737
|
throw new Error(`Plan not found in directory: ${directory}`);
|
|
2929
2738
|
}
|
|
@@ -2954,7 +2763,7 @@ async function updateTaskStatus(directory, taskId, status, options) {
|
|
|
2954
2763
|
if (status === "completed" || status === "blocked") {
|
|
2955
2764
|
const recordedTask = updatedPlan.phases.flatMap((phase) => phase.tasks).find((candidate) => candidate.id === taskId);
|
|
2956
2765
|
try {
|
|
2957
|
-
await
|
|
2766
|
+
await _internals5.recordTaskAttempt(directory, {
|
|
2958
2767
|
taskId,
|
|
2959
2768
|
agent: "plan-status",
|
|
2960
2769
|
outcome: status === "completed" ? "pass" : "fail",
|
|
@@ -2965,8 +2774,8 @@ async function updateTaskStatus(directory, taskId, status, options) {
|
|
|
2965
2774
|
warn(`[plan/manager] run-memory record for ${taskId} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2966
2775
|
}
|
|
2967
2776
|
}
|
|
2968
|
-
if (status === "completed" &&
|
|
2969
|
-
const mergeFailure =
|
|
2777
|
+
if (status === "completed" && _internals5.isGitRepo(directory) && _internals5.isEpicModeActiveForProject(directory)) {
|
|
2778
|
+
const mergeFailure = _internals5.getWorktreeMergeFailure(taskId);
|
|
2970
2779
|
if (mergeFailure) {
|
|
2971
2780
|
criticalWarn(`[plan/manager] Rule 2 auto-commit SKIPPED for ${taskId}: worktree merge-back ${mergeFailure.outcome} at stage '${mergeFailure.stage}'. The task's changes are NOT in the main tree, so no completion marker is written (Rule 3 must not treat this task as satisfied). Resolve the preserved worktree, then re-run the task. Detail: ${mergeFailure.message}`);
|
|
2972
2781
|
return updatedPlan;
|
|
@@ -2980,8 +2789,8 @@ async function updateTaskStatus(directory, taskId, status, options) {
|
|
|
2980
2789
|
break;
|
|
2981
2790
|
}
|
|
2982
2791
|
}
|
|
2983
|
-
const canonicalScope =
|
|
2984
|
-
await
|
|
2792
|
+
const canonicalScope = _internals5.readTaskScopes(directory, taskId);
|
|
2793
|
+
await _internals5.commitTaskCompletion(directory, taskId, taskDescription, canonicalScope ?? undefined);
|
|
2985
2794
|
} catch (commitErr) {
|
|
2986
2795
|
criticalWarn(`[plan/manager] Rule 2 auto-commit for ${taskId} threw (non-fatal): ${commitErr instanceof Error ? commitErr.message : String(commitErr)}`);
|
|
2987
2796
|
}
|
|
@@ -3296,17 +3105,17 @@ init_zod();
|
|
|
3296
3105
|
import {
|
|
3297
3106
|
mkdirSync as mkdirSync3,
|
|
3298
3107
|
readdirSync as readdirSync2,
|
|
3299
|
-
realpathSync
|
|
3108
|
+
realpathSync,
|
|
3300
3109
|
renameSync as renameSync3,
|
|
3301
3110
|
rmSync,
|
|
3302
3111
|
statSync as statSync2
|
|
3303
3112
|
} from "fs";
|
|
3304
|
-
import * as
|
|
3305
|
-
import * as
|
|
3113
|
+
import * as fs6 from "fs/promises";
|
|
3114
|
+
import * as path6 from "path";
|
|
3306
3115
|
|
|
3307
3116
|
// src/evaluation/retention.ts
|
|
3308
|
-
import * as
|
|
3309
|
-
import * as
|
|
3117
|
+
import * as fs4 from "fs/promises";
|
|
3118
|
+
import * as path5 from "path";
|
|
3310
3119
|
function artifactKey(artifact) {
|
|
3311
3120
|
return `${artifact.namespace}/${artifact.id}`;
|
|
3312
3121
|
}
|
|
@@ -3314,10 +3123,10 @@ function validId(value) {
|
|
|
3314
3123
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/.test(value);
|
|
3315
3124
|
}
|
|
3316
3125
|
async function evaluationRunCandidates(directory) {
|
|
3317
|
-
const root =
|
|
3126
|
+
const root = path5.join(directory, ".swarm", "evolution", "runs");
|
|
3318
3127
|
let entries = [];
|
|
3319
3128
|
try {
|
|
3320
|
-
entries = await
|
|
3129
|
+
entries = await fs4.readdir(root, { withFileTypes: true });
|
|
3321
3130
|
} catch (error2) {
|
|
3322
3131
|
if (error2.code === "ENOENT") {
|
|
3323
3132
|
return { candidates: [], corrupt: [] };
|
|
@@ -3333,9 +3142,9 @@ async function evaluationRunCandidates(directory) {
|
|
|
3333
3142
|
if (!validId(id))
|
|
3334
3143
|
continue;
|
|
3335
3144
|
const artifact = { namespace: "evaluation-run", id };
|
|
3336
|
-
const target =
|
|
3145
|
+
const target = path5.join(root, entry.name);
|
|
3337
3146
|
try {
|
|
3338
|
-
const run = EvaluationRunV1Schema.parse(JSON.parse(await
|
|
3147
|
+
const run = EvaluationRunV1Schema.parse(JSON.parse(await fs4.readFile(target, "utf8")));
|
|
3339
3148
|
if (run.runId !== id)
|
|
3340
3149
|
throw new Error("run id/path mismatch");
|
|
3341
3150
|
candidates.push({
|
|
@@ -3351,10 +3160,10 @@ async function evaluationRunCandidates(directory) {
|
|
|
3351
3160
|
return { candidates, corrupt };
|
|
3352
3161
|
}
|
|
3353
3162
|
async function gateAuditCandidates(directory) {
|
|
3354
|
-
const root =
|
|
3163
|
+
const root = path5.join(directory, ".swarm", "evidence", "gate-audit");
|
|
3355
3164
|
let entries = [];
|
|
3356
3165
|
try {
|
|
3357
|
-
entries = await
|
|
3166
|
+
entries = await fs4.readdir(root, { withFileTypes: true });
|
|
3358
3167
|
} catch (error2) {
|
|
3359
3168
|
if (error2.code === "ENOENT") {
|
|
3360
3169
|
return { candidates: [], corrupt: [] };
|
|
@@ -3367,9 +3176,9 @@ async function gateAuditCandidates(directory) {
|
|
|
3367
3176
|
if (!entry.isDirectory() || !validId(entry.name))
|
|
3368
3177
|
continue;
|
|
3369
3178
|
const artifact = { namespace: "gate-audit", id: entry.name };
|
|
3370
|
-
const target =
|
|
3179
|
+
const target = path5.join(root, entry.name);
|
|
3371
3180
|
try {
|
|
3372
|
-
const result = GateAuditResultV1Schema.parse(JSON.parse(await
|
|
3181
|
+
const result = GateAuditResultV1Schema.parse(JSON.parse(await fs4.readFile(path5.join(target, "results.json"), "utf8")));
|
|
3373
3182
|
if (result.runId !== entry.name)
|
|
3374
3183
|
throw new Error("run id/path mismatch");
|
|
3375
3184
|
candidates.push({
|
|
@@ -3400,10 +3209,10 @@ async function protectedArtifacts(directory) {
|
|
|
3400
3209
|
}
|
|
3401
3210
|
async function removeCandidate(candidate) {
|
|
3402
3211
|
if (candidate.kind === "directory") {
|
|
3403
|
-
await
|
|
3212
|
+
await fs4.rm(candidate.target, { recursive: true, force: true });
|
|
3404
3213
|
return;
|
|
3405
3214
|
}
|
|
3406
|
-
await
|
|
3215
|
+
await fs4.unlink(candidate.target);
|
|
3407
3216
|
}
|
|
3408
3217
|
var _retentionInternals = {
|
|
3409
3218
|
protectedArtifacts,
|
|
@@ -3411,7 +3220,7 @@ var _retentionInternals = {
|
|
|
3411
3220
|
};
|
|
3412
3221
|
async function archiveEvaluationArtifacts(args) {
|
|
3413
3222
|
assertProjectRoot(args.directory);
|
|
3414
|
-
return withEvidenceLock(args.directory,
|
|
3223
|
+
return withEvidenceLock(args.directory, path5.join("evolution", "retention-index"), "evaluation-retention", "archive", async () => {
|
|
3415
3224
|
const inventory = await retentionCandidates(args.directory);
|
|
3416
3225
|
let protectedKeys = await _retentionInternals.protectedArtifacts(args.directory);
|
|
3417
3226
|
const protectedRefs = inventory.candidates.filter((candidate) => protectedKeys.has(artifactKey(candidate.artifact))).map((candidate) => candidate.artifact);
|
|
@@ -3460,7 +3269,7 @@ async function archiveEvaluationArtifacts(args) {
|
|
|
3460
3269
|
}
|
|
3461
3270
|
|
|
3462
3271
|
// src/evidence/documents-retention.ts
|
|
3463
|
-
import * as
|
|
3272
|
+
import * as fs5 from "fs";
|
|
3464
3273
|
import * as fsp from "fs/promises";
|
|
3465
3274
|
import * as readline from "readline";
|
|
3466
3275
|
var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
|
|
@@ -3468,13 +3277,13 @@ var MAX_READ_BYTES = 100 * 1024 * 1024;
|
|
|
3468
3277
|
var RENAME_MAX_ATTEMPTS = 5;
|
|
3469
3278
|
var RENAME_BACKOFF_MS = 10;
|
|
3470
3279
|
var RENAME_RETRY_CODES = new Set(["EPERM", "EBUSY", "ENOTEMPTY", "EACCES"]);
|
|
3471
|
-
var
|
|
3280
|
+
var _internals6 = {
|
|
3472
3281
|
stat: (p) => fsp.stat(p),
|
|
3473
|
-
createReadStream: (p, options) =>
|
|
3474
|
-
openSync: (p, flags) =>
|
|
3475
|
-
writeSync: (fd, data, position) =>
|
|
3476
|
-
fsyncSync: (fd) =>
|
|
3477
|
-
closeSync: (fd) =>
|
|
3282
|
+
createReadStream: (p, options) => fs5.createReadStream(p, options),
|
|
3283
|
+
openSync: (p, flags) => fs5.openSync(p, flags),
|
|
3284
|
+
writeSync: (fd, data, position) => fs5.writeSync(fd, data, position),
|
|
3285
|
+
fsyncSync: (fd) => fs5.fsyncSync(fd),
|
|
3286
|
+
closeSync: (fd) => fs5.closeSync(fd),
|
|
3478
3287
|
renameWithRetry: atomicRenameWithRetry,
|
|
3479
3288
|
unlink: (p) => fsp.unlink(p)
|
|
3480
3289
|
};
|
|
@@ -3491,7 +3300,7 @@ async function atomicRenameWithRetry(src, dst, renameFn = fsp.rename) {
|
|
|
3491
3300
|
throw err;
|
|
3492
3301
|
}
|
|
3493
3302
|
if (attempt < RENAME_MAX_ATTEMPTS - 1) {
|
|
3494
|
-
await new Promise((
|
|
3303
|
+
await new Promise((resolve3) => setTimeout(resolve3, RENAME_BACKOFF_MS));
|
|
3495
3304
|
}
|
|
3496
3305
|
}
|
|
3497
3306
|
}
|
|
@@ -3510,9 +3319,9 @@ function emptyResult(dryRun) {
|
|
|
3510
3319
|
};
|
|
3511
3320
|
}
|
|
3512
3321
|
async function readCacheRows(filePath) {
|
|
3513
|
-
const stat2 = await
|
|
3322
|
+
const stat2 = await _internals6.stat(filePath);
|
|
3514
3323
|
const bytesBefore = stat2.size;
|
|
3515
|
-
const stream =
|
|
3324
|
+
const stream = _internals6.createReadStream(filePath, {
|
|
3516
3325
|
encoding: "utf8",
|
|
3517
3326
|
highWaterMark: 64 * 1024
|
|
3518
3327
|
});
|
|
@@ -3588,23 +3397,23 @@ async function rewriteAtomic(filePath, survivors) {
|
|
|
3588
3397
|
`)}
|
|
3589
3398
|
` : "";
|
|
3590
3399
|
let succeeded = false;
|
|
3591
|
-
const fd =
|
|
3400
|
+
const fd = _internals6.openSync(tmpPath, "w");
|
|
3592
3401
|
try {
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
await
|
|
3402
|
+
_internals6.writeSync(fd, payload, 0);
|
|
3403
|
+
_internals6.fsyncSync(fd);
|
|
3404
|
+
_internals6.closeSync(fd);
|
|
3405
|
+
await _internals6.renameWithRetry(tmpPath, filePath);
|
|
3597
3406
|
succeeded = true;
|
|
3598
3407
|
} catch (err) {
|
|
3599
3408
|
try {
|
|
3600
|
-
|
|
3409
|
+
_internals6.closeSync(fd);
|
|
3601
3410
|
} catch {}
|
|
3602
3411
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3603
3412
|
throw new Error(`documents-cache rewrite failed: ${msg}`);
|
|
3604
3413
|
} finally {
|
|
3605
3414
|
if (!succeeded) {
|
|
3606
3415
|
try {
|
|
3607
|
-
await
|
|
3416
|
+
await _internals6.unlink(tmpPath);
|
|
3608
3417
|
} catch {}
|
|
3609
3418
|
}
|
|
3610
3419
|
}
|
|
@@ -3617,7 +3426,7 @@ async function pruneEvidenceDocuments(args) {
|
|
|
3617
3426
|
const filePath = validateSwarmPath(args.directory, EVIDENCE_CACHE_FILE);
|
|
3618
3427
|
let stat2;
|
|
3619
3428
|
try {
|
|
3620
|
-
stat2 = await
|
|
3429
|
+
stat2 = await _internals6.stat(filePath);
|
|
3621
3430
|
} catch (err) {
|
|
3622
3431
|
const code = err?.code;
|
|
3623
3432
|
if (code === "ENOENT")
|
|
@@ -3721,21 +3530,21 @@ function isSecretscanEvidence(evidence) {
|
|
|
3721
3530
|
var sanitizeTaskId2 = sanitizeTaskId;
|
|
3722
3531
|
function validateProjectRoot(directory) {
|
|
3723
3532
|
assertProjectRoot(directory, {
|
|
3724
|
-
realpathSync:
|
|
3725
|
-
statSync:
|
|
3533
|
+
realpathSync: _internals7.realpathSync,
|
|
3534
|
+
statSync: _internals7.statSync
|
|
3726
3535
|
}, "evidence");
|
|
3727
3536
|
}
|
|
3728
3537
|
async function saveEvidence(directory, taskId, evidence, abortSignal) {
|
|
3729
3538
|
abortSignal?.throwIfAborted();
|
|
3730
|
-
|
|
3539
|
+
_internals7.validateProjectRoot(directory);
|
|
3731
3540
|
const sanitizedTaskId = sanitizeTaskId2(taskId);
|
|
3732
|
-
const relativePath =
|
|
3541
|
+
const relativePath = path6.join("evidence", sanitizedTaskId, "evidence.json");
|
|
3733
3542
|
validateSwarmPath(directory, relativePath);
|
|
3734
|
-
|
|
3543
|
+
_internals7.validateEvidence(evidence);
|
|
3735
3544
|
return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
|
|
3736
3545
|
abortSignal?.throwIfAborted();
|
|
3737
3546
|
const evidencePath = validateSwarmPath(directory, relativePath);
|
|
3738
|
-
const evidenceDir =
|
|
3547
|
+
const evidenceDir = path6.dirname(evidencePath);
|
|
3739
3548
|
let bundle;
|
|
3740
3549
|
const existingContent = await readSwarmFileAsync(directory, relativePath);
|
|
3741
3550
|
abortSignal?.throwIfAborted();
|
|
@@ -3779,7 +3588,7 @@ async function saveEvidence(directory, taskId, evidence, abortSignal) {
|
|
|
3779
3588
|
throw new Error(`Evidence bundle size (${bundleJson.length} bytes) exceeds maximum (${EVIDENCE_MAX_JSON_BYTES} bytes)`);
|
|
3780
3589
|
}
|
|
3781
3590
|
mkdirSync3(evidenceDir, { recursive: true });
|
|
3782
|
-
const tempPath =
|
|
3591
|
+
const tempPath = path6.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
|
|
3783
3592
|
try {
|
|
3784
3593
|
await bunWrite(tempPath, bundleJson);
|
|
3785
3594
|
abortSignal?.throwIfAborted();
|
|
@@ -3825,7 +3634,7 @@ function wrapFlatRetrospective(flatEntry, taskId) {
|
|
|
3825
3634
|
}
|
|
3826
3635
|
async function loadEvidence(directory, taskId, options) {
|
|
3827
3636
|
const sanitizedTaskId = sanitizeTaskId2(taskId);
|
|
3828
|
-
const relativePath =
|
|
3637
|
+
const relativePath = path6.join("evidence", sanitizedTaskId, "evidence.json");
|
|
3829
3638
|
if (relativePath.length > 4096) {
|
|
3830
3639
|
return { status: "not_found" };
|
|
3831
3640
|
}
|
|
@@ -3841,7 +3650,7 @@ async function loadEvidence(directory, taskId, options) {
|
|
|
3841
3650
|
return { status: "invalid_schema", errors: ["Invalid JSON"] };
|
|
3842
3651
|
}
|
|
3843
3652
|
if (isFlatRetrospective(parsed)) {
|
|
3844
|
-
const wrappedBundle =
|
|
3653
|
+
const wrappedBundle = _internals7.wrapFlatRetrospective(parsed, sanitizedTaskId);
|
|
3845
3654
|
try {
|
|
3846
3655
|
const validated = EvidenceBundleSchema.parse(wrappedBundle);
|
|
3847
3656
|
if (options?.migrate === false) {
|
|
@@ -3850,12 +3659,12 @@ async function loadEvidence(directory, taskId, options) {
|
|
|
3850
3659
|
try {
|
|
3851
3660
|
validateProjectRoot(directory);
|
|
3852
3661
|
await withEvidenceLock(directory, relativePath, "evidence-loader", sanitizedTaskId, async () => {
|
|
3853
|
-
const evidenceDir =
|
|
3662
|
+
const evidenceDir = path6.dirname(evidencePath);
|
|
3854
3663
|
const bundleJson = JSON.stringify(validated);
|
|
3855
|
-
const tempPath =
|
|
3664
|
+
const tempPath = path6.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
|
|
3856
3665
|
try {
|
|
3857
3666
|
await bunWrite(tempPath, bundleJson);
|
|
3858
|
-
await
|
|
3667
|
+
await fs6.rename(tempPath, evidencePath);
|
|
3859
3668
|
invalidateCachedArtifact(evidencePath);
|
|
3860
3669
|
} catch (writeError) {
|
|
3861
3670
|
try {
|
|
@@ -3898,7 +3707,7 @@ async function listEvidenceTaskIds(directory) {
|
|
|
3898
3707
|
}
|
|
3899
3708
|
const taskIds = [];
|
|
3900
3709
|
for (const entry of entries) {
|
|
3901
|
-
const entryPath =
|
|
3710
|
+
const entryPath = path6.join(evidenceBasePath, entry);
|
|
3902
3711
|
try {
|
|
3903
3712
|
const stats = statSync2(entryPath);
|
|
3904
3713
|
if (!stats.isDirectory()) {
|
|
@@ -3921,7 +3730,7 @@ async function deleteEvidence(directory, taskId) {
|
|
|
3921
3730
|
} catch {
|
|
3922
3731
|
return false;
|
|
3923
3732
|
}
|
|
3924
|
-
const relativePath =
|
|
3733
|
+
const relativePath = path6.join("evidence", sanitizedTaskId);
|
|
3925
3734
|
const evidenceDir = validateSwarmPath(directory, relativePath);
|
|
3926
3735
|
try {
|
|
3927
3736
|
statSync2(evidenceDir);
|
|
@@ -3937,18 +3746,18 @@ async function deleteEvidence(directory, taskId) {
|
|
|
3937
3746
|
}
|
|
3938
3747
|
}
|
|
3939
3748
|
async function checkRequirementCoverage(phase, directory) {
|
|
3940
|
-
const relativePath =
|
|
3941
|
-
const absolutePath =
|
|
3749
|
+
const relativePath = path6.join("evidence", `req-coverage-phase-${phase}.json`);
|
|
3750
|
+
const absolutePath = path6.resolve(directory, ".swarm", relativePath);
|
|
3942
3751
|
try {
|
|
3943
|
-
await
|
|
3752
|
+
await fs6.access(absolutePath);
|
|
3944
3753
|
return { exists: true, path: absolutePath };
|
|
3945
3754
|
} catch {
|
|
3946
3755
|
return { exists: false, path: absolutePath };
|
|
3947
3756
|
}
|
|
3948
3757
|
}
|
|
3949
3758
|
async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
3950
|
-
const taskIds = await
|
|
3951
|
-
const cutoffDate = options?.now ? new Date(options.now) :
|
|
3759
|
+
const taskIds = await _internals7.listEvidenceTaskIds(directory);
|
|
3760
|
+
const cutoffDate = options?.now ? new Date(options.now) : _internals7.now();
|
|
3952
3761
|
cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
|
|
3953
3762
|
const cutoffIso = cutoffDate.toISOString();
|
|
3954
3763
|
const selected = new Set;
|
|
@@ -3960,7 +3769,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
|
3960
3769
|
for (const taskId of taskIds) {
|
|
3961
3770
|
let result;
|
|
3962
3771
|
try {
|
|
3963
|
-
result = await
|
|
3772
|
+
result = await _internals7.loadEvidence(directory, taskId);
|
|
3964
3773
|
} catch {
|
|
3965
3774
|
warn("archive: skipping corrupt or unreadable evidence for task", taskId);
|
|
3966
3775
|
continue;
|
|
@@ -3988,7 +3797,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
|
3988
3797
|
}
|
|
3989
3798
|
if (!options?.dryRun) {
|
|
3990
3799
|
for (const taskId of [...selected].sort()) {
|
|
3991
|
-
const deleted = await
|
|
3800
|
+
const deleted = await _internals7.deleteEvidence(directory, taskId);
|
|
3992
3801
|
if (deleted)
|
|
3993
3802
|
archived.push(taskId);
|
|
3994
3803
|
else
|
|
@@ -4014,7 +3823,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
|
4014
3823
|
};
|
|
4015
3824
|
if (typeof options?.cacheMaxBytes === "number" && options.cacheMaxBytes > 0 || typeof options?.cacheMaxRecords === "number" && options.cacheMaxRecords > 0) {
|
|
4016
3825
|
try {
|
|
4017
|
-
documentsCache = await
|
|
3826
|
+
documentsCache = await _internals7.pruneEvidenceDocuments({
|
|
4018
3827
|
directory,
|
|
4019
3828
|
maxBytes: options?.cacheMaxBytes,
|
|
4020
3829
|
maxRecords: options?.cacheMaxRecords,
|
|
@@ -4042,7 +3851,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
|
4042
3851
|
function validateEvidence(evidence) {
|
|
4043
3852
|
return EvidenceSchema.parse(evidence);
|
|
4044
3853
|
}
|
|
4045
|
-
var
|
|
3854
|
+
var _internals7 = {
|
|
4046
3855
|
wrapFlatRetrospective,
|
|
4047
3856
|
loadEvidence,
|
|
4048
3857
|
listEvidenceTaskIds,
|
|
@@ -4051,7 +3860,7 @@ var _internals8 = {
|
|
|
4051
3860
|
saveEvidence,
|
|
4052
3861
|
deleteEvidence,
|
|
4053
3862
|
pruneEvidenceDocuments,
|
|
4054
|
-
realpathSync
|
|
3863
|
+
realpathSync,
|
|
4055
3864
|
statSync: statSync2,
|
|
4056
3865
|
now: () => new Date
|
|
4057
3866
|
};
|
|
@@ -4166,4 +3975,4 @@ function mergeDurableGateEntriesFromEvidence(taskId, entries, evidence) {
|
|
|
4166
3975
|
return merged;
|
|
4167
3976
|
}
|
|
4168
3977
|
|
|
4169
|
-
export { normalizePath, buildTaskCompletionDescriptor, ensureTaskCheckpointReceipt, updateTaskCheckpointReceipt, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, detectDefaultRemoteBranch, resetToRemoteBranch, resetToMainAfterMerge,
|
|
3978
|
+
export { normalizePath, buildTaskCompletionDescriptor, ensureTaskCheckpointReceipt, updateTaskCheckpointReceipt, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, detectDefaultRemoteBranch, resetToRemoteBranch, resetToMainAfterMerge, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, updateTaskStatus, derivePlanMarkdown, isValidEvidenceType, isSecretscanEvidence, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };
|