opencode-swarm 7.143.5 → 7.144.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/skills/swarm-pr-review/SKILL.md +5 -5
- package/.opencode/skills/swarm-pr-review/references/lane-output-recoverability.md +129 -0
- package/dist/background/delegation-health.d.ts +213 -0
- package/dist/background/pending-delegations.d.ts +164 -16
- package/dist/background/pr-review-trigger-contract.d.ts +17 -0
- package/dist/cli/{config-doctor-y8wfw196.js → config-doctor-7gy47evn.js} +2 -2
- package/dist/cli/{core-zszmexgm.js → core-cpcspaw5.js} +1 -1
- package/dist/cli/{curator-llm-factory-xsg7wesn.js → curator-llm-factory-fyjmgxyp.js} +18 -18
- package/dist/cli/{curator-e2sp925h.js → curator-v97jnwn2.js} +18 -18
- package/dist/cli/{evidence-summary-service-pfppq7mv.js → evidence-summary-service-79mtxzhd.js} +6 -6
- package/dist/cli/{gate-evidence-23jnh3mb.js → gate-evidence-kgyqz1g1.js} +2 -2
- package/dist/cli/{guardrail-explain-byzmrgg1.js → guardrail-explain-9ry66z5g.js} +19 -19
- package/dist/cli/{guardrail-log-nr2g7hbw.js → guardrail-log-fe6smme1.js} +3 -3
- package/dist/cli/{hive-promoter-cw08380z.js → hive-promoter-vwq1dbh1.js} +18 -18
- package/dist/cli/{index-qdarzed6.js → index-0ck3e366.js} +1 -1
- package/dist/cli/{index-xmje20we.js → index-1pr8x10a.js} +2 -2
- package/dist/cli/{index-p844tcrm.js → index-269b52r3.js} +3 -3
- package/dist/cli/{index-04x9wx7d.js → index-3q9w6e6v.js} +1 -1
- package/dist/cli/{index-5866smj5.js → index-3v3dxf75.js} +3 -3
- package/dist/cli/{index-8aabbfhv.js → index-4fda8kkw.js} +1057 -92
- package/dist/cli/{index-jqxgdvr9.js → index-6p09gyra.js} +1 -1
- package/dist/cli/{index-mnj0ter5.js → index-8xertnqz.js} +3 -3
- package/dist/cli/{index-a1p87gew.js → index-d45d4zyq.js} +1 -1
- package/dist/cli/{index-y8r7z30j.js → index-d45yrzkg.js} +1 -1
- package/dist/cli/{index-1kr1vey5.js → index-e70d3xr7.js} +20 -20
- package/dist/cli/{index-a4dkd7m7.js → index-evvbmbbv.js} +1 -1
- package/dist/cli/{index-dg1v2gnh.js → index-ewzmjm7m.js} +1 -1
- package/dist/cli/{index-47z3jbxk.js → index-ez18gjk2.js} +3 -3
- package/dist/cli/{index-51axj9vd.js → index-j1scj25g.js} +481 -80
- package/dist/cli/{index-m7v7gggs.js → index-jqabjy7t.js} +4 -4
- package/dist/cli/{index-7en3eq3k.js → index-jvbfrwt2.js} +1 -1
- package/dist/cli/{index-tmc43mkc.js → index-mw6p3nt6.js} +1 -1
- package/dist/cli/{index-x64vt6sc.js → index-rd2082y4.js} +2 -2
- package/dist/cli/{index-q2nd6q9e.js → index-tfkfyshg.js} +1 -1
- package/dist/cli/{index-mbczeqs3.js → index-vb9cpaz4.js} +5 -5
- package/dist/cli/{index-at0h6cd6.js → index-zv4pwyas.js} +2 -2
- package/dist/cli/index.js +18 -18
- package/dist/cli/{knowledge-escalator-1g4p844a.js → knowledge-escalator-rmy3jcx4.js} +6 -6
- package/dist/cli/{knowledge-events-5699zzw0.js → knowledge-events-1tqpbwvj.js} +4 -4
- package/dist/cli/{knowledge-store-m5qjn5kd.js → knowledge-store-389ck792.js} +1 -1
- package/dist/cli/{knowledge-validator-xxnp8ear.js → knowledge-validator-kvtk6633.js} +2 -2
- package/dist/cli/{pending-delegations-b3pt3w5w.js → pending-delegations-thy8g30k.js} +27 -1
- package/dist/cli/{scan-cursor-qvky2yem.js → scan-cursor-ahgm4f5p.js} +2 -2
- package/dist/cli/{schema-wgb37f83.js → schema-epdq7ykx.js} +1 -1
- package/dist/cli/{scope-persistence-0wrb7ptt.js → scope-persistence-37s9n40v.js} +4 -4
- package/dist/cli/{skill-generator-khaw3jjw.js → skill-generator-4d2t5gem.js} +7 -7
- package/dist/cli/{worktree-collision-ownership-bfz3drxm.js → worktree-collision-ownership-j6d33p49.js} +1 -1
- package/dist/cli/{worktree-isolation-bv41gn3k.js → worktree-isolation-4628yaka.js} +18 -18
- package/dist/evaluation/contracts.d.ts +4 -4
- package/dist/evaluation/gate-ground-truth.d.ts +1 -1
- package/dist/hooks/init-orphan-recovery.d.ts +2 -0
- package/dist/hooks/pr-workflow-gate.d.ts +135 -1
- package/dist/index.js +446 -441
- package/dist/services/status-service.d.ts +9 -0
- package/dist/tools/complete-pr-workflow.d.ts +3 -1
- package/dist/tools/pr-workflow-status.d.ts +2 -2
- package/dist/tools/write-pr-review-trigger-eval.d.ts +2 -1
- package/package.json +1 -1
|
@@ -20,20 +20,225 @@ import {
|
|
|
20
20
|
// src/background/pending-delegations.ts
|
|
21
21
|
init_zod();
|
|
22
22
|
import { createHash } from "crypto";
|
|
23
|
-
import * as
|
|
24
|
-
import * as
|
|
23
|
+
import * as fs2 from "fs";
|
|
24
|
+
import * as path2 from "path";
|
|
25
25
|
import { isDeepStrictEqual } from "util";
|
|
26
26
|
init_logger();
|
|
27
|
+
|
|
28
|
+
// src/background/delegation-health.ts
|
|
29
|
+
init_zod();
|
|
30
|
+
import * as fs from "fs";
|
|
31
|
+
import * as path from "path";
|
|
32
|
+
var BACKGROUND_DELEGATIONS_HEALTH_FILE = "background-delegations-health.json";
|
|
33
|
+
var MAX_RECOVERY_LEDGER_BYTES = 4 * 1024 * 1024;
|
|
34
|
+
var DelegationCheckpointAuditSchema = exports_external.object({
|
|
35
|
+
dispatchCount: exports_external.number().int().nonnegative(),
|
|
36
|
+
terminalsByStatus: exports_external.object({
|
|
37
|
+
completed: exports_external.number().int().nonnegative(),
|
|
38
|
+
error: exports_external.number().int().nonnegative(),
|
|
39
|
+
cancelled: exports_external.number().int().nonnegative(),
|
|
40
|
+
stale: exports_external.number().int().nonnegative()
|
|
41
|
+
}).strict(),
|
|
42
|
+
settledCount: exports_external.number().int().nonnegative(),
|
|
43
|
+
preservedCount: exports_external.number().int().nonnegative(),
|
|
44
|
+
lateTerminalCount: exports_external.number().int().nonnegative(),
|
|
45
|
+
compactedTransitionCount: exports_external.number().int().nonnegative(),
|
|
46
|
+
compactedRecordCount: exports_external.number().int().nonnegative(),
|
|
47
|
+
firstDispatchAt: exports_external.number().int().nonnegative().nullable(),
|
|
48
|
+
lastTerminalAt: exports_external.number().int().nonnegative().nullable(),
|
|
49
|
+
lastCompactionAt: exports_external.number().int().nonnegative()
|
|
50
|
+
}).strict();
|
|
51
|
+
var CheckpointSectionSchema = exports_external.object({
|
|
52
|
+
sequence: exports_external.number().int().positive(),
|
|
53
|
+
createdAt: exports_external.number().int().nonnegative(),
|
|
54
|
+
liveRecords: exports_external.number().int().nonnegative(),
|
|
55
|
+
closedSummaries: exports_external.number().int().nonnegative(),
|
|
56
|
+
bytes: exports_external.number().int().nonnegative(),
|
|
57
|
+
audit: DelegationCheckpointAuditSchema
|
|
58
|
+
}).strict();
|
|
59
|
+
var RecoverySectionSchema = exports_external.object({
|
|
60
|
+
source: exports_external.enum([
|
|
61
|
+
"checkpoint+tail",
|
|
62
|
+
"checkpoint+ledger-suffix",
|
|
63
|
+
"legacy-ledger",
|
|
64
|
+
"unknown"
|
|
65
|
+
]),
|
|
66
|
+
at: exports_external.number().int().nonnegative(),
|
|
67
|
+
ok: exports_external.boolean(),
|
|
68
|
+
reason: exports_external.string().min(1).max(2000).optional(),
|
|
69
|
+
repairHint: exports_external.string().min(1).max(2000).optional()
|
|
70
|
+
}).strict();
|
|
71
|
+
var UncertaintySectionSchema = exports_external.object({
|
|
72
|
+
reason: exports_external.string().min(1).max(2000),
|
|
73
|
+
at: exports_external.number().int().nonnegative(),
|
|
74
|
+
source: exports_external.string().min(1).max(256),
|
|
75
|
+
repairHint: exports_external.string().min(1).max(2000).optional()
|
|
76
|
+
}).strict();
|
|
77
|
+
var DelegationLedgerHealthSchema = exports_external.object({
|
|
78
|
+
schemaVersion: exports_external.literal(1),
|
|
79
|
+
updatedAt: exports_external.number().int().nonnegative(),
|
|
80
|
+
ledger: exports_external.object({
|
|
81
|
+
bytes: exports_external.number().int().nonnegative(),
|
|
82
|
+
limitBytes: exports_external.number().int().positive(),
|
|
83
|
+
pressurePct: exports_external.number().min(0).max(100),
|
|
84
|
+
band: exports_external.enum(["ok", "nominal", "compact-overdue", "fail-closed"])
|
|
85
|
+
}).strict(),
|
|
86
|
+
checkpoint: CheckpointSectionSchema.nullable(),
|
|
87
|
+
recovery: RecoverySectionSchema.nullable(),
|
|
88
|
+
lastUncertainty: UncertaintySectionSchema.nullable(),
|
|
89
|
+
counts: exports_external.object({
|
|
90
|
+
activeOwners: exports_external.number().int().nonnegative(),
|
|
91
|
+
pendingAdvisories: exports_external.number().int().nonnegative(),
|
|
92
|
+
lateTerminals: exports_external.number().int().nonnegative(),
|
|
93
|
+
orphanWorktreeOwners: exports_external.number().int().nonnegative()
|
|
94
|
+
}).strict()
|
|
95
|
+
}).strict();
|
|
96
|
+
function sleepSync(ms) {
|
|
97
|
+
try {
|
|
98
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
99
|
+
} catch {
|
|
100
|
+
const start = Date.now();
|
|
101
|
+
while (Date.now() - start < ms) {}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
var _healthInternals = {
|
|
105
|
+
renameOnce: (from, to) => {
|
|
106
|
+
fs.renameSync(from, to);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
function healthArtifactPath(directory) {
|
|
110
|
+
return validateSwarmPath(directory, BACKGROUND_DELEGATIONS_HEALTH_FILE);
|
|
111
|
+
}
|
|
112
|
+
function readDelegationHealthArtifact(directory) {
|
|
113
|
+
let raw;
|
|
114
|
+
try {
|
|
115
|
+
raw = fs.readFileSync(healthArtifactPath(directory), "utf-8");
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const parsed = DelegationLedgerHealthSchema.safeParse(JSON.parse(raw));
|
|
121
|
+
return parsed.success ? parsed.data : null;
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function writeDelegationHealthArtifact(directory, update) {
|
|
127
|
+
const existing = readDelegationHealthArtifact(directory);
|
|
128
|
+
const base = existing ?? {
|
|
129
|
+
schemaVersion: 1,
|
|
130
|
+
updatedAt: 0,
|
|
131
|
+
ledger: {
|
|
132
|
+
bytes: 0,
|
|
133
|
+
limitBytes: MAX_RECOVERY_LEDGER_BYTES,
|
|
134
|
+
pressurePct: 0,
|
|
135
|
+
band: "ok"
|
|
136
|
+
},
|
|
137
|
+
checkpoint: null,
|
|
138
|
+
recovery: null,
|
|
139
|
+
lastUncertainty: null,
|
|
140
|
+
counts: {
|
|
141
|
+
activeOwners: 0,
|
|
142
|
+
pendingAdvisories: 0,
|
|
143
|
+
lateTerminals: 0,
|
|
144
|
+
orphanWorktreeOwners: 0
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const next = {
|
|
148
|
+
schemaVersion: 1,
|
|
149
|
+
updatedAt: Date.now(),
|
|
150
|
+
ledger: update.ledger ?? base.ledger,
|
|
151
|
+
checkpoint: update.checkpoint === undefined ? base.checkpoint : update.checkpoint,
|
|
152
|
+
recovery: update.recovery === undefined ? base.recovery : update.recovery,
|
|
153
|
+
lastUncertainty: update.lastUncertainty ?? base.lastUncertainty,
|
|
154
|
+
counts: update.counts ?? base.counts
|
|
155
|
+
};
|
|
156
|
+
if (update.uncertainty) {
|
|
157
|
+
next.lastUncertainty = update.uncertainty;
|
|
158
|
+
}
|
|
159
|
+
const parsed = DelegationLedgerHealthSchema.safeParse(next);
|
|
160
|
+
if (!parsed.success)
|
|
161
|
+
return null;
|
|
162
|
+
try {
|
|
163
|
+
fs.mkdirSync(path.resolve(directory, ".swarm"), { recursive: true });
|
|
164
|
+
const target = healthArtifactPath(directory);
|
|
165
|
+
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
166
|
+
fs.writeFileSync(tmp, `${JSON.stringify(parsed.data)}
|
|
167
|
+
`, "utf-8");
|
|
168
|
+
let lastError;
|
|
169
|
+
for (let attempt = 0;attempt < 5; attempt++) {
|
|
170
|
+
try {
|
|
171
|
+
_healthInternals.renameOnce(tmp, target);
|
|
172
|
+
lastError = undefined;
|
|
173
|
+
break;
|
|
174
|
+
} catch (err) {
|
|
175
|
+
lastError = err;
|
|
176
|
+
const code = err.code;
|
|
177
|
+
if (code !== "EEXIST" && code !== "EBUSY" && code !== "EPERM") {
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
if (attempt < 4)
|
|
181
|
+
sleepSync(15);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (lastError !== undefined) {
|
|
185
|
+
try {
|
|
186
|
+
fs.rmSync(tmp, { force: true });
|
|
187
|
+
} catch {}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
return parsed.data;
|
|
191
|
+
} catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function collectDelegationLedgerHealth(directory, options) {
|
|
196
|
+
const artifact = readDelegationHealthArtifact(directory);
|
|
197
|
+
let ledgerBytes = 0;
|
|
198
|
+
let ledgerExists = false;
|
|
199
|
+
try {
|
|
200
|
+
ledgerBytes = fs.statSync(validateSwarmPath(directory, "background-delegations.jsonl")).size;
|
|
201
|
+
ledgerExists = true;
|
|
202
|
+
} catch {
|
|
203
|
+
ledgerExists = false;
|
|
204
|
+
}
|
|
205
|
+
if (!artifact && !ledgerExists)
|
|
206
|
+
return null;
|
|
207
|
+
const limitBytes = options.ledgerLimitBytes;
|
|
208
|
+
const pressurePct = Math.min(100, Math.round(ledgerBytes / limitBytes * 1000) / 10);
|
|
209
|
+
const band = ledgerBytes > limitBytes ? "fail-closed" : ledgerBytes > options.highWaterBytes ? "compact-overdue" : ledgerBytes > options.lowWaterBytes ? "nominal" : "ok";
|
|
210
|
+
return {
|
|
211
|
+
schemaVersion: 1,
|
|
212
|
+
updatedAt: artifact?.updatedAt ?? 0,
|
|
213
|
+
ledger: { bytes: ledgerBytes, limitBytes, pressurePct, band },
|
|
214
|
+
checkpoint: artifact?.checkpoint ?? null,
|
|
215
|
+
recovery: artifact?.recovery ?? null,
|
|
216
|
+
lastUncertainty: artifact?.lastUncertainty ?? null,
|
|
217
|
+
counts: artifact?.counts ?? {
|
|
218
|
+
activeOwners: 0,
|
|
219
|
+
pendingAdvisories: 0,
|
|
220
|
+
lateTerminals: 0,
|
|
221
|
+
orphanWorktreeOwners: 0
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// src/background/pending-delegations.ts
|
|
27
226
|
var BACKGROUND_DELEGATIONS_FILE = "background-delegations.jsonl";
|
|
28
227
|
var BACKGROUND_DELEGATION_FALLBACK_DIR = "background-delegation-fallback";
|
|
29
228
|
var BACKGROUND_CODER_RESERVATIONS_FILE = "background-coder-reservations.json";
|
|
229
|
+
var BACKGROUND_DELEGATIONS_CHECKPOINT_FILE = "background-delegations.checkpoint.json";
|
|
230
|
+
var BACKGROUND_DELEGATIONS_MANIFEST_FILE = "background-delegations.manifest.json";
|
|
30
231
|
var MAX_LIVE_BACKGROUND_FALLBACKS = 256;
|
|
31
232
|
var MAX_LIVE_BACKGROUND_CODER_RESERVATIONS = 256;
|
|
32
233
|
var MAX_BACKGROUND_OBSERVED_FILES = 5000;
|
|
33
234
|
var MAX_BACKGROUND_ADVISORY_CHARS = 4000;
|
|
34
235
|
var MAX_BACKGROUND_DELEGATION_GENERATION = 1e6;
|
|
35
|
-
var MAX_RECOVERY_LEDGER_BYTES = 4 * 1024 * 1024;
|
|
36
236
|
var MAX_RECOVERY_FALLBACK_BYTES = 1024 * 1024;
|
|
237
|
+
var DELEGATION_COMPACTION_LOW_WATER_BYTES = 256 * 1024;
|
|
238
|
+
var DELEGATION_COMPACTION_HIGH_WATER_BYTES = 1024 * 1024;
|
|
239
|
+
var MAX_CHECKPOINT_BYTES = 2 * 1024 * 1024;
|
|
240
|
+
var MAX_CHECKPOINT_RECORDS = 2048;
|
|
241
|
+
var TOMBSTONE_MIN_AGE_MS = 72 * 60 * 60 * 1000;
|
|
37
242
|
var STORE_LOCK_AGENT = "background";
|
|
38
243
|
var STORE_LOCK_TASK = "background-delegations";
|
|
39
244
|
var FALLBACK_LOCK_TASK = "background-delegation-fallback";
|
|
@@ -41,6 +246,8 @@ var RESERVATION_LOCK_TASK = "background-coder-reservations";
|
|
|
41
246
|
var ADVISORY_PREPARE_LEASE_MS = 30000;
|
|
42
247
|
var INGESTION_CLAIM_LEASE_MS = 30000;
|
|
43
248
|
var BACKGROUND_INGESTION_LEASE_MS = 30000;
|
|
249
|
+
var DEFAULT_STALE_DELEGATION_TIMEOUT_MS = 30 * 60000;
|
|
250
|
+
var DEFAULT_SWEEPABLE_DELEGATION_STATUSES = new Set(["pending", "running", "ingestion_error"]);
|
|
44
251
|
var ResultSchema = exports_external.object({
|
|
45
252
|
text: exports_external.string().optional(),
|
|
46
253
|
error: exports_external.string().optional(),
|
|
@@ -289,43 +496,359 @@ var FallbackArtifactSchema = exports_external.object({
|
|
|
289
496
|
function storePath(directory) {
|
|
290
497
|
return validateSwarmPath(directory, BACKGROUND_DELEGATIONS_FILE);
|
|
291
498
|
}
|
|
292
|
-
function
|
|
293
|
-
|
|
499
|
+
function checkpointPath(directory) {
|
|
500
|
+
return validateSwarmPath(directory, BACKGROUND_DELEGATIONS_CHECKPOINT_FILE);
|
|
294
501
|
}
|
|
295
|
-
function
|
|
502
|
+
function manifestPath(directory) {
|
|
503
|
+
return validateSwarmPath(directory, BACKGROUND_DELEGATIONS_MANIFEST_FILE);
|
|
504
|
+
}
|
|
505
|
+
var SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
506
|
+
var CheckpointSchema = exports_external.object({
|
|
507
|
+
schemaVersion: exports_external.literal(1),
|
|
508
|
+
sequence: exports_external.number().int().positive(),
|
|
509
|
+
writerId: exports_external.string().min(1).max(128),
|
|
510
|
+
rootPath: exports_external.string().min(1).max(4096),
|
|
511
|
+
createdAt: exports_external.number().int().nonnegative(),
|
|
512
|
+
cutLedgerBytes: exports_external.number().int().nonnegative(),
|
|
513
|
+
cutLedgerDigest: exports_external.string().regex(SHA256_HEX),
|
|
514
|
+
records: exports_external.array(RecordSchema).max(MAX_CHECKPOINT_RECORDS),
|
|
515
|
+
closed: exports_external.array(RecordSchema),
|
|
516
|
+
audit: DelegationCheckpointAuditSchema,
|
|
517
|
+
payloadChecksum: exports_external.string().regex(SHA256_HEX)
|
|
518
|
+
}).strict();
|
|
519
|
+
var ManifestSchema = exports_external.object({
|
|
520
|
+
schemaVersion: exports_external.literal(1),
|
|
521
|
+
sequence: exports_external.number().int().positive(),
|
|
522
|
+
checkpointChecksum: exports_external.string().regex(SHA256_HEX),
|
|
523
|
+
writerId: exports_external.string().min(1).max(128),
|
|
524
|
+
rootPath: exports_external.string().min(1).max(4096),
|
|
525
|
+
updatedAt: exports_external.number().int().nonnegative()
|
|
526
|
+
}).strict();
|
|
527
|
+
var WRITER_ID = `w-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
528
|
+
var _checkpointInternals = {
|
|
529
|
+
renameWithRetry: (from, to) => {
|
|
530
|
+
let lastError;
|
|
531
|
+
for (let attempt = 0;attempt < 5; attempt++) {
|
|
532
|
+
try {
|
|
533
|
+
_checkpointInternals.renameOnce(from, to);
|
|
534
|
+
return;
|
|
535
|
+
} catch (err) {
|
|
536
|
+
lastError = err;
|
|
537
|
+
const code = err.code;
|
|
538
|
+
if (code !== "EEXIST" && code !== "EBUSY" && code !== "EPERM") {
|
|
539
|
+
throw err;
|
|
540
|
+
}
|
|
541
|
+
_checkpointInternals.syncSleep(15);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
throw lastError;
|
|
545
|
+
},
|
|
546
|
+
renameOnce: (from, to) => {
|
|
547
|
+
fs2.renameSync(from, to);
|
|
548
|
+
},
|
|
549
|
+
syncSleep: (ms) => {
|
|
550
|
+
try {
|
|
551
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
552
|
+
} catch {
|
|
553
|
+
const start = Date.now();
|
|
554
|
+
while (Date.now() - start < ms) {}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
function writeDurableFileSync(target, contents) {
|
|
559
|
+
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
560
|
+
try {
|
|
561
|
+
const fd = fs2.openSync(tmp, "w");
|
|
562
|
+
try {
|
|
563
|
+
if (typeof contents === "string") {
|
|
564
|
+
fs2.writeSync(fd, contents);
|
|
565
|
+
} else {
|
|
566
|
+
fs2.writeSync(fd, contents, 0, contents.length);
|
|
567
|
+
}
|
|
568
|
+
fs2.fsyncSync(fd);
|
|
569
|
+
} finally {
|
|
570
|
+
fs2.closeSync(fd);
|
|
571
|
+
}
|
|
572
|
+
_checkpointInternals.renameWithRetry(tmp, target);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
try {
|
|
575
|
+
fs2.rmSync(tmp, { force: true });
|
|
576
|
+
} catch {}
|
|
577
|
+
throw err;
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
const dfd = fs2.openSync(path2.dirname(target), "r");
|
|
581
|
+
try {
|
|
582
|
+
fs2.fsyncSync(dfd);
|
|
583
|
+
} finally {
|
|
584
|
+
fs2.closeSync(dfd);
|
|
585
|
+
}
|
|
586
|
+
} catch {}
|
|
587
|
+
}
|
|
588
|
+
function readJsonFileWithRetry(target) {
|
|
296
589
|
let raw;
|
|
297
590
|
try {
|
|
298
|
-
raw =
|
|
591
|
+
raw = fs2.readFileSync(target, "utf-8");
|
|
299
592
|
} catch (err) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return
|
|
593
|
+
const code = err.code;
|
|
594
|
+
if (code === "ENOENT")
|
|
595
|
+
return { status: "absent" };
|
|
596
|
+
return {
|
|
597
|
+
status: "invalid",
|
|
598
|
+
reason: `unreadable (${code ?? "unknown error"})`
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
try {
|
|
602
|
+
return { status: "ok", value: JSON.parse(raw) };
|
|
603
|
+
} catch (err) {
|
|
604
|
+
return {
|
|
605
|
+
status: "invalid",
|
|
606
|
+
reason: `malformed JSON (${err instanceof Error ? err.message : String(err)})`
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function readDelegationManifest(directory) {
|
|
611
|
+
const read = readJsonFileWithRetry(manifestPath(directory));
|
|
612
|
+
if (read.status === "absent")
|
|
613
|
+
return { kind: "absent" };
|
|
614
|
+
if (read.status === "invalid")
|
|
615
|
+
return { kind: "invalid", reason: read.reason };
|
|
616
|
+
const parsed = ManifestSchema.safeParse(read.value);
|
|
617
|
+
if (!parsed.success) {
|
|
618
|
+
return { kind: "invalid", reason: "manifest fails schema validation" };
|
|
619
|
+
}
|
|
620
|
+
return { kind: "ok", manifest: parsed.data };
|
|
621
|
+
}
|
|
622
|
+
function readDelegationCheckpoint(directory) {
|
|
623
|
+
let bytes = 0;
|
|
624
|
+
try {
|
|
625
|
+
bytes = fs2.statSync(checkpointPath(directory)).size;
|
|
626
|
+
} catch {}
|
|
627
|
+
if (bytes > MAX_CHECKPOINT_BYTES) {
|
|
628
|
+
return {
|
|
629
|
+
kind: "invalid",
|
|
630
|
+
reason: `checkpoint exceeds the ${MAX_CHECKPOINT_BYTES}-byte bound`
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
const read = readJsonFileWithRetry(checkpointPath(directory));
|
|
634
|
+
if (read.status === "absent")
|
|
635
|
+
return { kind: "absent" };
|
|
636
|
+
if (read.status === "invalid")
|
|
637
|
+
return { kind: "invalid", reason: read.reason };
|
|
638
|
+
const parsed = CheckpointSchema.safeParse(read.value);
|
|
639
|
+
if (!parsed.success) {
|
|
640
|
+
return { kind: "invalid", reason: "checkpoint fails schema validation" };
|
|
641
|
+
}
|
|
642
|
+
return { kind: "ok", checkpoint: parsed.data, bytes };
|
|
643
|
+
}
|
|
644
|
+
function sha256Hex(data) {
|
|
645
|
+
return createHash("sha256").update(data).digest("hex");
|
|
646
|
+
}
|
|
647
|
+
function checkpointPayloadChecksum(checkpoint) {
|
|
648
|
+
return sha256Hex(JSON.stringify([
|
|
649
|
+
checkpoint.schemaVersion,
|
|
650
|
+
checkpoint.sequence,
|
|
651
|
+
checkpoint.writerId,
|
|
652
|
+
checkpoint.rootPath,
|
|
653
|
+
checkpoint.createdAt,
|
|
654
|
+
checkpoint.cutLedgerBytes,
|
|
655
|
+
checkpoint.cutLedgerDigest,
|
|
656
|
+
checkpoint.records,
|
|
657
|
+
checkpoint.closed,
|
|
658
|
+
checkpoint.audit
|
|
659
|
+
]));
|
|
660
|
+
}
|
|
661
|
+
var CHECKPOINT_REBIND_HINT = "remove .swarm/background-delegations.manifest.json (manifest only) after verifying no in-flight background delegations; recovery then falls back to the full-ledger read. Only if you also want to discard pre-cut closed summaries and audit history \u2014 which for a compacted (rolled) ledger exist ONLY in .swarm/background-delegations.checkpoint.json \u2014 remove the checkpoint too or use the full reset below";
|
|
662
|
+
var CHECKPOINT_RESET_HINT = "the rolled tail no longer carries pre-checkpoint state: stop swarm processes, verify no in-flight background delegations, and remove .swarm/background-delegations.manifest.json, .swarm/background-delegations.checkpoint.json, and .swarm/background-delegations.jsonl to reset the store (or restore them from backup)";
|
|
663
|
+
function loadFoldedState(directory, options) {
|
|
664
|
+
const manifestRead = readDelegationManifest(directory);
|
|
665
|
+
if (manifestRead.kind === "invalid") {
|
|
666
|
+
return {
|
|
667
|
+
status: "uncertain",
|
|
668
|
+
reason: `background delegation manifest is invalid: ${manifestRead.reason}`,
|
|
669
|
+
repairHint: CHECKPOINT_RESET_HINT
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
if (manifestRead.kind === "absent") {
|
|
673
|
+
return loadLegacyLedger(directory, options.strict);
|
|
674
|
+
}
|
|
675
|
+
const manifest = manifestRead.manifest;
|
|
676
|
+
if (manifest.rootPath !== path2.resolve(directory)) {
|
|
677
|
+
return {
|
|
678
|
+
status: "uncertain",
|
|
679
|
+
reason: "background delegation checkpoint is bound to a different project root",
|
|
680
|
+
repairHint: CHECKPOINT_REBIND_HINT
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
const checkpointRead = readDelegationCheckpoint(directory);
|
|
684
|
+
if (checkpointRead.kind === "invalid" || checkpointRead.kind === "absent") {
|
|
685
|
+
const detail = checkpointRead.kind === "invalid" ? `: ${checkpointRead.reason}` : " (file missing)";
|
|
686
|
+
return {
|
|
687
|
+
status: "uncertain",
|
|
688
|
+
reason: `background delegation checkpoint is invalid${detail} while a manifest is published`,
|
|
689
|
+
repairHint: CHECKPOINT_RESET_HINT
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
const checkpoint = checkpointRead.checkpoint;
|
|
693
|
+
if (checkpoint.sequence !== manifest.sequence && checkpoint.sequence !== manifest.sequence + 1) {
|
|
694
|
+
return {
|
|
695
|
+
status: "uncertain",
|
|
696
|
+
reason: `background delegation checkpoint/manifest sequence mismatch (checkpoint ${checkpoint.sequence}, manifest ${manifest.sequence})`,
|
|
697
|
+
repairHint: CHECKPOINT_RESET_HINT
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
if (checkpoint.sequence === manifest.sequence && manifest.checkpointChecksum !== checkpoint.payloadChecksum) {
|
|
701
|
+
return {
|
|
702
|
+
status: "uncertain",
|
|
703
|
+
reason: "background delegation manifest checksum does not match its checkpoint",
|
|
704
|
+
repairHint: CHECKPOINT_RESET_HINT
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
if (checkpointPayloadChecksum(checkpoint) !== checkpoint.payloadChecksum) {
|
|
708
|
+
return {
|
|
709
|
+
status: "uncertain",
|
|
710
|
+
reason: "background delegation checkpoint payload checksum mismatch",
|
|
711
|
+
repairHint: CHECKPOINT_RESET_HINT
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
let stat;
|
|
715
|
+
try {
|
|
716
|
+
stat = fs2.statSync(storePath(directory));
|
|
717
|
+
} catch (error) {
|
|
718
|
+
return {
|
|
719
|
+
status: "uncertain",
|
|
720
|
+
reason: `background delegation ledger metadata is unreadable: ${error instanceof Error ? error.message : String(error)}`
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
let tail;
|
|
724
|
+
let mode;
|
|
725
|
+
if (stat.size >= checkpoint.cutLedgerBytes && checkpoint.cutLedgerBytes > 0) {
|
|
726
|
+
const prefix = readLedgerRegion(directory, 0, checkpoint.cutLedgerBytes);
|
|
727
|
+
if (sha256Hex(prefix) === checkpoint.cutLedgerDigest) {
|
|
728
|
+
const suffixBytes = stat.size - checkpoint.cutLedgerBytes;
|
|
729
|
+
if (suffixBytes > MAX_RECOVERY_LEDGER_BYTES) {
|
|
730
|
+
return {
|
|
731
|
+
status: "uncertain",
|
|
732
|
+
reason: `background delegation ledger changed beyond the ${MAX_RECOVERY_LEDGER_BYTES}-byte recovery bound`
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
tail = suffixBytes === 0 ? Buffer.alloc(0) : readLedgerRegion(directory, checkpoint.cutLedgerBytes, suffixBytes);
|
|
736
|
+
mode = "checkpoint+ledger-suffix";
|
|
737
|
+
} else {
|
|
738
|
+
tail = readBoundedRecentLedgerWindow(directory, stat.size);
|
|
739
|
+
mode = "checkpoint+tail";
|
|
740
|
+
}
|
|
741
|
+
} else {
|
|
742
|
+
tail = readBoundedRecentLedgerWindow(directory, stat.size);
|
|
743
|
+
mode = "checkpoint+tail";
|
|
744
|
+
}
|
|
745
|
+
const records = new Map;
|
|
746
|
+
for (const record of checkpoint.records) {
|
|
747
|
+
records.set(record.correlationId, record);
|
|
748
|
+
}
|
|
749
|
+
for (const summary of checkpoint.closed) {
|
|
750
|
+
records.set(summary.correlationId, summary);
|
|
751
|
+
}
|
|
752
|
+
const foldError = foldLedgerTail(records, tail, options.strict, checkpoint);
|
|
753
|
+
if (foldError)
|
|
754
|
+
return { status: "uncertain", reason: foldError };
|
|
755
|
+
return {
|
|
756
|
+
status: "ok",
|
|
757
|
+
mode,
|
|
758
|
+
records,
|
|
759
|
+
manifest,
|
|
760
|
+
checkpoint
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
function readLedgerRegion(directory, offset, length) {
|
|
764
|
+
const fd = fs2.openSync(storePath(directory), "r");
|
|
765
|
+
try {
|
|
766
|
+
const buffer = Buffer.alloc(length);
|
|
767
|
+
let read = 0;
|
|
768
|
+
while (read < length) {
|
|
769
|
+
const n = fs2.readSync(fd, buffer, read, length - read, offset + read);
|
|
770
|
+
if (n <= 0)
|
|
771
|
+
break;
|
|
772
|
+
read += n;
|
|
773
|
+
}
|
|
774
|
+
return read === length ? buffer : buffer.subarray(0, read);
|
|
775
|
+
} finally {
|
|
776
|
+
fs2.closeSync(fd);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function readBoundedRecentLedgerWindow(directory, size) {
|
|
780
|
+
if (size === 0)
|
|
781
|
+
return Buffer.alloc(0);
|
|
782
|
+
if (size <= MAX_RECOVERY_LEDGER_BYTES) {
|
|
783
|
+
return readLedgerRegion(directory, 0, size);
|
|
304
784
|
}
|
|
305
|
-
const
|
|
306
|
-
|
|
785
|
+
const window = readLedgerRegion(directory, size - MAX_RECOVERY_LEDGER_BYTES, MAX_RECOVERY_LEDGER_BYTES);
|
|
786
|
+
const firstNewline = window.indexOf(10);
|
|
787
|
+
return firstNewline === -1 ? Buffer.alloc(0) : window.subarray(firstNewline + 1);
|
|
788
|
+
}
|
|
789
|
+
function foldLedgerTail(records, tail, strict, checkpoint) {
|
|
790
|
+
let lineNumber = 0;
|
|
791
|
+
for (const line of tail.toString("utf-8").split(`
|
|
307
792
|
`)) {
|
|
793
|
+
lineNumber += 1;
|
|
308
794
|
const trimmed = line.trim();
|
|
309
|
-
if (trimmed
|
|
795
|
+
if (!trimmed)
|
|
310
796
|
continue;
|
|
311
797
|
let parsedJson;
|
|
312
798
|
try {
|
|
313
799
|
parsedJson = JSON.parse(trimmed);
|
|
314
800
|
} catch {
|
|
801
|
+
if (strict) {
|
|
802
|
+
return `background delegation ledger has malformed JSON at line ${lineNumber}`;
|
|
803
|
+
}
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
const parsed = RecordSchema.safeParse(parsedJson);
|
|
807
|
+
if (!parsed.success) {
|
|
808
|
+
if (strict) {
|
|
809
|
+
return `background delegation ledger has an invalid record at line ${lineNumber}`;
|
|
810
|
+
}
|
|
315
811
|
continue;
|
|
316
812
|
}
|
|
317
|
-
const
|
|
318
|
-
if (!
|
|
813
|
+
const existing = checkpoint ? records.get(parsed.data.correlationId) : undefined;
|
|
814
|
+
if (checkpoint && existing && !(parsed.data.updatedAt > existing.updatedAt) && !(parsed.data.updatedAt === existing.updatedAt && parsed.data.updatedAt > checkpoint.createdAt)) {
|
|
319
815
|
continue;
|
|
320
|
-
|
|
816
|
+
}
|
|
817
|
+
records.set(parsed.data.correlationId, parsed.data);
|
|
321
818
|
}
|
|
322
|
-
return
|
|
819
|
+
return null;
|
|
323
820
|
}
|
|
324
|
-
function
|
|
821
|
+
function loadLegacyLedger(directory, strict) {
|
|
822
|
+
if (!strict) {
|
|
823
|
+
let raw2;
|
|
824
|
+
try {
|
|
825
|
+
raw2 = fs2.readFileSync(storePath(directory), "utf-8");
|
|
826
|
+
} catch (err) {
|
|
827
|
+
if (err.code !== "ENOENT") {
|
|
828
|
+
warn(`[background] readDelegations failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
829
|
+
}
|
|
830
|
+
return {
|
|
831
|
+
status: "ok",
|
|
832
|
+
mode: "legacy",
|
|
833
|
+
records: new Map,
|
|
834
|
+
manifest: null,
|
|
835
|
+
checkpoint: null
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
const records2 = new Map;
|
|
839
|
+
const foldError2 = foldLedgerTail(records2, Buffer.from(raw2, "utf-8"), false);
|
|
840
|
+
return {
|
|
841
|
+
status: "ok",
|
|
842
|
+
mode: "legacy",
|
|
843
|
+
records: records2,
|
|
844
|
+
manifest: null,
|
|
845
|
+
checkpoint: null
|
|
846
|
+
};
|
|
847
|
+
}
|
|
325
848
|
let absolutePath;
|
|
326
849
|
try {
|
|
327
850
|
absolutePath = storePath(directory);
|
|
328
|
-
const stat =
|
|
851
|
+
const stat = fs2.statSync(absolutePath);
|
|
329
852
|
if (stat.size > MAX_RECOVERY_LEDGER_BYTES) {
|
|
330
853
|
return {
|
|
331
854
|
status: "uncertain",
|
|
@@ -334,7 +857,13 @@ function scanDelegationsForRecovery(directory) {
|
|
|
334
857
|
}
|
|
335
858
|
} catch (error) {
|
|
336
859
|
if (error.code === "ENOENT") {
|
|
337
|
-
return {
|
|
860
|
+
return {
|
|
861
|
+
status: "ok",
|
|
862
|
+
mode: "legacy",
|
|
863
|
+
records: new Map,
|
|
864
|
+
manifest: null,
|
|
865
|
+
checkpoint: null
|
|
866
|
+
};
|
|
338
867
|
}
|
|
339
868
|
return {
|
|
340
869
|
status: "uncertain",
|
|
@@ -343,7 +872,7 @@ function scanDelegationsForRecovery(directory) {
|
|
|
343
872
|
}
|
|
344
873
|
let raw;
|
|
345
874
|
try {
|
|
346
|
-
raw =
|
|
875
|
+
raw = fs2.readFileSync(absolutePath, "utf-8");
|
|
347
876
|
} catch (error) {
|
|
348
877
|
return {
|
|
349
878
|
status: "uncertain",
|
|
@@ -356,33 +885,392 @@ function scanDelegationsForRecovery(directory) {
|
|
|
356
885
|
reason: `background delegation ledger changed beyond the ${MAX_RECOVERY_LEDGER_BYTES}-byte recovery bound`
|
|
357
886
|
};
|
|
358
887
|
}
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
888
|
+
const records = new Map;
|
|
889
|
+
const foldError = foldLedgerTail(records, Buffer.from(raw, "utf-8"), true);
|
|
890
|
+
if (foldError)
|
|
891
|
+
return { status: "uncertain", reason: foldError };
|
|
892
|
+
return {
|
|
893
|
+
status: "ok",
|
|
894
|
+
mode: "legacy",
|
|
895
|
+
records,
|
|
896
|
+
manifest: null,
|
|
897
|
+
checkpoint: null
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function loadRecordsForWrite(directory) {
|
|
901
|
+
const load = loadFoldedState(directory, { strict: false });
|
|
902
|
+
if (load.status === "uncertain") {
|
|
903
|
+
recordLedgerUncertainty(directory, load.reason, "mutation", load.repairHint);
|
|
904
|
+
return null;
|
|
905
|
+
}
|
|
906
|
+
return [...load.records.values()];
|
|
907
|
+
}
|
|
908
|
+
function findRecordForWrite(records, correlationId) {
|
|
909
|
+
if (!records)
|
|
910
|
+
return null;
|
|
911
|
+
for (const record of records) {
|
|
912
|
+
if (record.correlationId === correlationId)
|
|
913
|
+
return record;
|
|
914
|
+
}
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
function recordLedgerUncertainty(directory, reason, source, repairHint) {
|
|
918
|
+
try {
|
|
919
|
+
writeDelegationHealthArtifact(directory, {
|
|
920
|
+
uncertainty: {
|
|
921
|
+
reason,
|
|
922
|
+
at: Date.now(),
|
|
923
|
+
source,
|
|
924
|
+
...repairHint ? { repairHint } : {}
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
} catch {}
|
|
928
|
+
}
|
|
929
|
+
function isUnsettledWorktreeOwner(record) {
|
|
930
|
+
return Boolean(record.worktree) && !(record.coderSettlement?.state === "settled" && record.coderSettlement.outcome?.kind === "standard-worktree" && (record.coderSettlement.outcome.result === "merged" || record.coderSettlement.outcome.result === "unchanged"));
|
|
931
|
+
}
|
|
932
|
+
function isCheckpointLiveRecord(record) {
|
|
933
|
+
if (!isTerminal(record.status))
|
|
934
|
+
return true;
|
|
935
|
+
if (record.advisoryInbox?.state === "pending")
|
|
936
|
+
return true;
|
|
937
|
+
const settlement = record.coderSettlement;
|
|
938
|
+
if (settlement && settlement.state !== "settled")
|
|
939
|
+
return true;
|
|
940
|
+
if (settlement?.state === "settled" && isUnsettledWorktreeOwner(record)) {
|
|
941
|
+
return true;
|
|
942
|
+
}
|
|
943
|
+
if (isActiveCoderOwner(record))
|
|
944
|
+
return true;
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
function buildClosedSummary(record) {
|
|
948
|
+
const summary = { ...record };
|
|
949
|
+
delete summary.prompt;
|
|
950
|
+
delete summary.taskChangeContext;
|
|
951
|
+
if (summary.result) {
|
|
952
|
+
summary.result = stripResultBody(summary.result);
|
|
953
|
+
}
|
|
954
|
+
if (summary.terminalResult) {
|
|
955
|
+
summary.terminalResult = {
|
|
956
|
+
...summary.terminalResult,
|
|
957
|
+
result: stripResultBody(summary.terminalResult.result)
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
const parsed = RecordSchema.safeParse(summary);
|
|
961
|
+
return parsed.success ? parsed.data : null;
|
|
962
|
+
}
|
|
963
|
+
function stripResultBody(result) {
|
|
964
|
+
const { text: _text, error: _error, ...rest } = result;
|
|
965
|
+
return rest;
|
|
966
|
+
}
|
|
967
|
+
function emptyAudit() {
|
|
968
|
+
return {
|
|
969
|
+
dispatchCount: 0,
|
|
970
|
+
terminalsByStatus: { completed: 0, error: 0, cancelled: 0, stale: 0 },
|
|
971
|
+
settledCount: 0,
|
|
972
|
+
preservedCount: 0,
|
|
973
|
+
lateTerminalCount: 0,
|
|
974
|
+
compactedTransitionCount: 0,
|
|
975
|
+
compactedRecordCount: 0,
|
|
976
|
+
firstDispatchAt: null,
|
|
977
|
+
lastTerminalAt: null,
|
|
978
|
+
lastCompactionAt: 0
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function terminalEventStatusOf(record) {
|
|
982
|
+
if (record.terminalResult) {
|
|
983
|
+
return record.terminalResult.status;
|
|
984
|
+
}
|
|
985
|
+
if (record.status === "completed" || record.status === "error" || record.status === "cancelled" || record.status === "stale") {
|
|
986
|
+
return record.status;
|
|
987
|
+
}
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
function mergeCheckpointAudit(previous, previousCheckpoint, folded, healthLateTerminals, retiredLineCount, newlySummarizedCount, now) {
|
|
991
|
+
const prev = previous ?? emptyAudit();
|
|
992
|
+
const prevById = new Map;
|
|
993
|
+
if (previousCheckpoint) {
|
|
994
|
+
for (const record of previousCheckpoint.records) {
|
|
995
|
+
prevById.set(record.correlationId, record);
|
|
996
|
+
}
|
|
997
|
+
for (const summary of previousCheckpoint.closed) {
|
|
998
|
+
prevById.set(summary.correlationId, summary);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
const audit = {
|
|
1002
|
+
dispatchCount: prev.dispatchCount,
|
|
1003
|
+
terminalsByStatus: { ...prev.terminalsByStatus },
|
|
1004
|
+
settledCount: prev.settledCount,
|
|
1005
|
+
preservedCount: prev.preservedCount,
|
|
1006
|
+
lateTerminalCount: Math.max(prev.lateTerminalCount, healthLateTerminals),
|
|
1007
|
+
compactedTransitionCount: capCounter(prev.compactedTransitionCount, retiredLineCount),
|
|
1008
|
+
compactedRecordCount: capCounter(prev.compactedRecordCount, newlySummarizedCount),
|
|
1009
|
+
firstDispatchAt: prev.firstDispatchAt,
|
|
1010
|
+
lastTerminalAt: prev.lastTerminalAt,
|
|
1011
|
+
lastCompactionAt: now
|
|
1012
|
+
};
|
|
1013
|
+
let minCreated = Number.POSITIVE_INFINITY;
|
|
1014
|
+
let maxTerminal = 0;
|
|
1015
|
+
for (const record of folded) {
|
|
1016
|
+
const prevRecord = prevById.get(record.correlationId);
|
|
1017
|
+
if (!prevRecord) {
|
|
1018
|
+
audit.dispatchCount += 1;
|
|
1019
|
+
}
|
|
1020
|
+
const terminalStatus = terminalEventStatusOf(record);
|
|
1021
|
+
if (terminalStatus) {
|
|
1022
|
+
const prevStatus = prevRecord ? terminalEventStatusOf(prevRecord) : null;
|
|
1023
|
+
if (prevStatus !== terminalStatus) {
|
|
1024
|
+
audit.terminalsByStatus[terminalStatus] += 1;
|
|
1025
|
+
}
|
|
1026
|
+
maxTerminal = Math.max(maxTerminal, record.terminalResult?.recordedAt ?? record.completedAt ?? 0);
|
|
1027
|
+
}
|
|
1028
|
+
if (record.coderSettlement?.state === "settled" && prevRecord?.coderSettlement?.state !== "settled") {
|
|
1029
|
+
audit.settledCount += 1;
|
|
1030
|
+
}
|
|
1031
|
+
if (record.coderSettlement?.state === "preserved" && prevRecord?.coderSettlement?.state !== "preserved") {
|
|
1032
|
+
audit.preservedCount += 1;
|
|
1033
|
+
}
|
|
1034
|
+
minCreated = Math.min(minCreated, record.createdAt);
|
|
1035
|
+
}
|
|
1036
|
+
audit.firstDispatchAt = Number.isFinite(minCreated) ? Math.min(prev.firstDispatchAt ?? Number.POSITIVE_INFINITY, minCreated) : prev.firstDispatchAt;
|
|
1037
|
+
audit.lastTerminalAt = Math.max(prev.lastTerminalAt ?? 0, maxTerminal) || null;
|
|
1038
|
+
return audit;
|
|
1039
|
+
}
|
|
1040
|
+
function capCounter(previous, delta) {
|
|
1041
|
+
return Number.isSafeInteger(previous + delta) ? previous + delta : Number.MAX_SAFE_INTEGER;
|
|
1042
|
+
}
|
|
1043
|
+
async function compactBackgroundDelegations(directory, options = {}) {
|
|
1044
|
+
try {
|
|
1045
|
+
let outcome = {
|
|
1046
|
+
status: "skipped",
|
|
1047
|
+
reason: "not attempted"
|
|
1048
|
+
};
|
|
1049
|
+
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
1050
|
+
outcome = compactDelegationsLocked(directory, options);
|
|
1051
|
+
});
|
|
1052
|
+
return outcome;
|
|
1053
|
+
} catch (err) {
|
|
1054
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1055
|
+
warn(`[background] compactBackgroundDelegations failed: ${message}`);
|
|
1056
|
+
return { status: "uncertain", reason: message };
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
function compactDelegationsLocked(directory, options) {
|
|
1060
|
+
const load = loadFoldedState(directory, { strict: false });
|
|
1061
|
+
if (load.status === "uncertain") {
|
|
1062
|
+
recordLedgerUncertainty(directory, load.reason, "compaction", load.repairHint);
|
|
1063
|
+
return { status: "uncertain", reason: load.reason };
|
|
1064
|
+
}
|
|
1065
|
+
let ledgerBytes;
|
|
1066
|
+
try {
|
|
1067
|
+
ledgerBytes = fs2.readFileSync(storePath(directory));
|
|
1068
|
+
} catch (err) {
|
|
1069
|
+
if (err.code === "ENOENT") {
|
|
1070
|
+
return { status: "skipped", reason: "no ledger to compact" };
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
status: "uncertain",
|
|
1074
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
if (ledgerBytes.length <= DELEGATION_COMPACTION_HIGH_WATER_BYTES && !options.force) {
|
|
1078
|
+
return {
|
|
1079
|
+
status: "skipped",
|
|
1080
|
+
reason: "ledger below the compaction high-water mark"
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
const cutLedgerBytes = ledgerBytes.length;
|
|
1084
|
+
const retiredLineCount = countCompleteLines(ledgerBytes);
|
|
1085
|
+
const now = Date.now();
|
|
1086
|
+
const folded = [...load.records.values()];
|
|
1087
|
+
const live = [];
|
|
1088
|
+
const closedCandidates = [];
|
|
1089
|
+
for (const record of folded) {
|
|
1090
|
+
if (isCheckpointLiveRecord(record)) {
|
|
1091
|
+
live.push(record);
|
|
1092
|
+
} else {
|
|
1093
|
+
closedCandidates.push(buildClosedSummary(record) ?? record);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (live.length > MAX_CHECKPOINT_RECORDS) {
|
|
1097
|
+
recordLedgerUncertainty(directory, `checkpoint live-record budget exceeded (${live.length} > ${MAX_CHECKPOINT_RECORDS}); compaction skipped`, "compaction");
|
|
1098
|
+
return {
|
|
1099
|
+
status: "skipped",
|
|
1100
|
+
reason: `live record count ${live.length} exceeds the checkpoint budget ${MAX_CHECKPOINT_RECORDS}`
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
closedCandidates.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
1104
|
+
const baseOverhead = Buffer.byteLength(JSON.stringify({
|
|
1105
|
+
schemaVersion: 1,
|
|
1106
|
+
sequence: 1,
|
|
1107
|
+
writerId: WRITER_ID,
|
|
1108
|
+
rootPath: path2.resolve(directory),
|
|
1109
|
+
createdAt: now,
|
|
1110
|
+
cutLedgerBytes,
|
|
1111
|
+
cutLedgerDigest: sha256Hex(ledgerBytes),
|
|
1112
|
+
records: live,
|
|
1113
|
+
closed: [],
|
|
1114
|
+
audit: emptyAudit(),
|
|
1115
|
+
payloadChecksum: "0".repeat(64)
|
|
1116
|
+
}), "utf-8");
|
|
1117
|
+
let used = baseOverhead;
|
|
1118
|
+
const keptClosed = [];
|
|
1119
|
+
let evicted = 0;
|
|
1120
|
+
for (const entry of closedCandidates) {
|
|
1121
|
+
const cost = Buffer.byteLength(JSON.stringify(entry), "utf-8") + 1;
|
|
1122
|
+
if (used + cost > MAX_CHECKPOINT_BYTES) {
|
|
1123
|
+
if (now - entry.updatedAt < TOMBSTONE_MIN_AGE_MS) {
|
|
1124
|
+
recordLedgerUncertainty(directory, "checkpoint byte budget would evict a young closed summary; compaction skipped", "compaction");
|
|
1125
|
+
return {
|
|
1126
|
+
status: "skipped",
|
|
1127
|
+
reason: "checkpoint byte budget would evict a young closed summary"
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
evicted += 1;
|
|
366
1131
|
continue;
|
|
367
|
-
let parsedJson;
|
|
368
|
-
try {
|
|
369
|
-
parsedJson = JSON.parse(trimmed);
|
|
370
|
-
} catch {
|
|
371
|
-
return {
|
|
372
|
-
status: "uncertain",
|
|
373
|
-
reason: `background delegation ledger has malformed JSON at line ${lineNumber}`
|
|
374
|
-
};
|
|
375
1132
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
1133
|
+
used += cost;
|
|
1134
|
+
keptClosed.push(entry);
|
|
1135
|
+
}
|
|
1136
|
+
const sequence = Math.max(load.manifest?.sequence ?? 0, load.checkpoint?.sequence ?? 0, 0) + 1;
|
|
1137
|
+
let healthLateTerminals = 0;
|
|
1138
|
+
try {
|
|
1139
|
+
const artifact = readDelegationHealthArtifact(directory);
|
|
1140
|
+
healthLateTerminals = artifact?.counts.lateTerminals ?? 0;
|
|
1141
|
+
} catch {
|
|
1142
|
+
healthLateTerminals = 0;
|
|
1143
|
+
}
|
|
1144
|
+
const previousClosedIds = new Set((load.checkpoint?.closed ?? []).map((entry) => entry.correlationId));
|
|
1145
|
+
const newlySummarized = keptClosed.filter((entry) => !previousClosedIds.has(entry.correlationId)).length;
|
|
1146
|
+
const audit = mergeCheckpointAudit(load.checkpoint?.audit ?? null, load.checkpoint, folded, healthLateTerminals, retiredLineCount, newlySummarized + evicted, now);
|
|
1147
|
+
const checkpoint = {
|
|
1148
|
+
schemaVersion: 1,
|
|
1149
|
+
sequence,
|
|
1150
|
+
writerId: WRITER_ID,
|
|
1151
|
+
rootPath: path2.resolve(directory),
|
|
1152
|
+
createdAt: now,
|
|
1153
|
+
cutLedgerBytes,
|
|
1154
|
+
cutLedgerDigest: sha256Hex(ledgerBytes),
|
|
1155
|
+
records: live,
|
|
1156
|
+
closed: keptClosed,
|
|
1157
|
+
audit,
|
|
1158
|
+
payloadChecksum: ""
|
|
1159
|
+
};
|
|
1160
|
+
checkpoint.payloadChecksum = checkpointPayloadChecksum(checkpoint);
|
|
1161
|
+
const checkpointJson = `${JSON.stringify(checkpoint)}
|
|
1162
|
+
`;
|
|
1163
|
+
if (Buffer.byteLength(checkpointJson, "utf-8") > MAX_CHECKPOINT_BYTES) {
|
|
1164
|
+
recordLedgerUncertainty(directory, "serialized checkpoint exceeds the byte budget; compaction skipped", "compaction");
|
|
1165
|
+
return {
|
|
1166
|
+
status: "skipped",
|
|
1167
|
+
reason: "serialized checkpoint exceeds the byte budget"
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
writeDurableFileSync(checkpointPath(directory), checkpointJson);
|
|
1171
|
+
writeDurableFileSync(manifestPath(directory), `${JSON.stringify({
|
|
1172
|
+
schemaVersion: 1,
|
|
1173
|
+
sequence,
|
|
1174
|
+
checkpointChecksum: checkpoint.payloadChecksum,
|
|
1175
|
+
writerId: WRITER_ID,
|
|
1176
|
+
rootPath: checkpoint.rootPath,
|
|
1177
|
+
updatedAt: now
|
|
1178
|
+
})}
|
|
1179
|
+
`);
|
|
1180
|
+
let currentSize;
|
|
1181
|
+
try {
|
|
1182
|
+
currentSize = fs2.statSync(storePath(directory)).size;
|
|
1183
|
+
} catch (err) {
|
|
1184
|
+
return {
|
|
1185
|
+
status: "uncertain",
|
|
1186
|
+
reason: `ledger vanished during compaction: ${err instanceof Error ? err.message : String(err)}`
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
if (currentSize < cutLedgerBytes) {
|
|
1190
|
+
return {
|
|
1191
|
+
status: "skipped",
|
|
1192
|
+
reason: "ledger shrank during compaction; checkpoint published unrolled"
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
const tailContent = currentSize > cutLedgerBytes ? readLedgerRegion(directory, cutLedgerBytes, currentSize - cutLedgerBytes) : Buffer.alloc(0);
|
|
1196
|
+
writeDurableFileSync(storePath(directory), tailContent);
|
|
1197
|
+
try {
|
|
1198
|
+
const pressurePct = Math.min(100, Math.round(tailContent.length / MAX_RECOVERY_LEDGER_BYTES * 1000) / 10);
|
|
1199
|
+
writeDelegationHealthArtifact(directory, {
|
|
1200
|
+
ledger: {
|
|
1201
|
+
bytes: tailContent.length,
|
|
1202
|
+
limitBytes: MAX_RECOVERY_LEDGER_BYTES,
|
|
1203
|
+
pressurePct,
|
|
1204
|
+
band: tailContent.length > DELEGATION_COMPACTION_HIGH_WATER_BYTES ? "compact-overdue" : tailContent.length > DELEGATION_COMPACTION_LOW_WATER_BYTES ? "nominal" : "ok"
|
|
1205
|
+
},
|
|
1206
|
+
checkpoint: {
|
|
1207
|
+
sequence,
|
|
1208
|
+
createdAt: now,
|
|
1209
|
+
liveRecords: live.length,
|
|
1210
|
+
closedSummaries: keptClosed.length,
|
|
1211
|
+
bytes: Buffer.byteLength(checkpointJson, "utf-8"),
|
|
1212
|
+
audit
|
|
1213
|
+
},
|
|
1214
|
+
counts: {
|
|
1215
|
+
activeOwners: folded.filter((record) => isActiveCoderOwner(record)).length,
|
|
1216
|
+
pendingAdvisories: folded.filter((record) => record.advisoryInbox?.state === "pending").length,
|
|
1217
|
+
lateTerminals: audit.lateTerminalCount,
|
|
1218
|
+
orphanWorktreeOwners: folded.filter(isUnsettledWorktreeOwner).length
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
} catch {}
|
|
1222
|
+
return {
|
|
1223
|
+
status: "compacted",
|
|
1224
|
+
sequence,
|
|
1225
|
+
tailBytes: tailContent.length,
|
|
1226
|
+
checkpointBytes: Buffer.byteLength(checkpointJson, "utf-8")
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
function countCompleteLines(buffer) {
|
|
1230
|
+
let count = 0;
|
|
1231
|
+
for (const byte of buffer) {
|
|
1232
|
+
if (byte === 10)
|
|
1233
|
+
count += 1;
|
|
1234
|
+
}
|
|
1235
|
+
return count;
|
|
1236
|
+
}
|
|
1237
|
+
function maybeCompactDelegationsLocked(directory) {
|
|
1238
|
+
try {
|
|
1239
|
+
const stat = fs2.statSync(storePath(directory));
|
|
1240
|
+
if (stat.size <= DELEGATION_COMPACTION_HIGH_WATER_BYTES)
|
|
1241
|
+
return;
|
|
1242
|
+
const outcome = compactDelegationsLocked(directory, {});
|
|
1243
|
+
if (outcome.status === "uncertain") {
|
|
1244
|
+
warn(`[background] post-append compaction skipped: ${outcome.reason}`);
|
|
382
1245
|
}
|
|
383
|
-
|
|
1246
|
+
} catch (err) {
|
|
1247
|
+
warn(`[background] post-append compaction check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
function ensureSwarmDir(directory) {
|
|
1251
|
+
fs2.mkdirSync(path2.resolve(directory, ".swarm"), { recursive: true });
|
|
1252
|
+
}
|
|
1253
|
+
function readDelegations(directory) {
|
|
1254
|
+
const load = loadFoldedState(directory, { strict: false });
|
|
1255
|
+
if (load.status === "uncertain") {
|
|
1256
|
+
warn(`[background] readDelegations: authoritative state uncertain (${load.reason}); returning empty`);
|
|
1257
|
+
return [];
|
|
1258
|
+
}
|
|
1259
|
+
return [...load.records.values()];
|
|
1260
|
+
}
|
|
1261
|
+
function scanDelegationsForRecovery(directory) {
|
|
1262
|
+
const load = loadFoldedState(directory, { strict: true });
|
|
1263
|
+
if (load.status === "uncertain") {
|
|
1264
|
+
const manifestKind = readDelegationManifest(directory).kind;
|
|
1265
|
+
return {
|
|
1266
|
+
status: "uncertain",
|
|
1267
|
+
reason: load.reason,
|
|
1268
|
+
source: manifestKind === "absent" ? "legacy-ledger" : "unknown",
|
|
1269
|
+
...load.repairHint ? { repairHint: load.repairHint } : {}
|
|
1270
|
+
};
|
|
384
1271
|
}
|
|
385
|
-
|
|
1272
|
+
const source = load.mode === "legacy" ? "legacy-ledger" : load.mode;
|
|
1273
|
+
return { status: "ok", owners: [...load.records.values()], source };
|
|
386
1274
|
}
|
|
387
1275
|
function findByCorrelationId(directory, correlationId) {
|
|
388
1276
|
if (!correlationId)
|
|
@@ -395,7 +1283,7 @@ function findByCorrelationId(directory, correlationId) {
|
|
|
395
1283
|
}
|
|
396
1284
|
function appendRecord(directory, record) {
|
|
397
1285
|
ensureSwarmDir(directory);
|
|
398
|
-
|
|
1286
|
+
fs2.appendFileSync(storePath(directory), `${JSON.stringify(record)}
|
|
399
1287
|
`, "utf-8");
|
|
400
1288
|
}
|
|
401
1289
|
function buildPendingRecord(input, now) {
|
|
@@ -428,7 +1316,7 @@ function buildPendingRecord(input, now) {
|
|
|
428
1316
|
...input.generation !== undefined ? { generation: input.generation } : {}
|
|
429
1317
|
};
|
|
430
1318
|
}
|
|
431
|
-
function pendingLaunchIdentity(record) {
|
|
1319
|
+
function pendingLaunchIdentity(record, summaryAware) {
|
|
432
1320
|
return {
|
|
433
1321
|
correlationId: record.correlationId,
|
|
434
1322
|
jobId: record.jobId,
|
|
@@ -446,16 +1334,19 @@ function pendingLaunchIdentity(record) {
|
|
|
446
1334
|
ownedWorkflowLanes: record.ownedWorkflowLanes,
|
|
447
1335
|
promptHash: record.promptHash,
|
|
448
1336
|
workspace: record.workspace,
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
1337
|
+
...summaryAware ? {} : {
|
|
1338
|
+
taskChangeContext: record.taskChangeContext,
|
|
1339
|
+
workflowGeneration: record.workflowGeneration,
|
|
1340
|
+
worktree: record.worktree,
|
|
1341
|
+
coderReservationId: record.coderReservationId,
|
|
1342
|
+
prompt: record.prompt,
|
|
1343
|
+
generation: record.generation ?? 1
|
|
1344
|
+
}
|
|
455
1345
|
};
|
|
456
1346
|
}
|
|
457
1347
|
function samePendingLaunchIdentity(left, right) {
|
|
458
|
-
|
|
1348
|
+
const summaryAware = isTerminal(left.status) && left.prompt === undefined && left.taskChangeContext === undefined;
|
|
1349
|
+
return isDeepStrictEqual(pendingLaunchIdentity(left, summaryAware), pendingLaunchIdentity(right, summaryAware));
|
|
459
1350
|
}
|
|
460
1351
|
async function recordPendingDelegationDetailed(directory, input, options = {}) {
|
|
461
1352
|
const now = Date.now();
|
|
@@ -474,7 +1365,10 @@ async function recordPendingDelegationDetailed(directory, input, options = {}) {
|
|
|
474
1365
|
if (options.staleTimeoutMs && options.staleTimeoutMs > 0) {
|
|
475
1366
|
sweepStaleLocked(directory, options.staleTimeoutMs, now);
|
|
476
1367
|
}
|
|
477
|
-
const
|
|
1368
|
+
const records = loadRecordsForWrite(directory);
|
|
1369
|
+
if (records === null)
|
|
1370
|
+
return;
|
|
1371
|
+
const existing = findRecordForWrite(records, parsedRecord.data.correlationId);
|
|
478
1372
|
if (existing) {
|
|
479
1373
|
outcome = {
|
|
480
1374
|
status: samePendingLaunchIdentity(existing, parsedRecord.data) ? "duplicate" : "conflict",
|
|
@@ -483,6 +1377,7 @@ async function recordPendingDelegationDetailed(directory, input, options = {}) {
|
|
|
483
1377
|
return;
|
|
484
1378
|
}
|
|
485
1379
|
appendRecord(directory, parsedRecord.data);
|
|
1380
|
+
maybeCompactDelegationsLocked(directory);
|
|
486
1381
|
outcome = { status: "recorded", record: parsedRecord.data };
|
|
487
1382
|
});
|
|
488
1383
|
return outcome;
|
|
@@ -511,7 +1406,10 @@ async function appendDelegationTransition(directory, correlationId, transition)
|
|
|
511
1406
|
try {
|
|
512
1407
|
let next = null;
|
|
513
1408
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
514
|
-
const
|
|
1409
|
+
const records = loadRecordsForWrite(directory);
|
|
1410
|
+
if (records === null)
|
|
1411
|
+
return;
|
|
1412
|
+
const current = findRecordForWrite(records, correlationId);
|
|
515
1413
|
if (!current)
|
|
516
1414
|
return;
|
|
517
1415
|
if (isTerminal(current.status) && transition.status !== "consumed" && transition.status !== "ingestion_error" && !(current.status === "completed" && transition.status === "stale")) {
|
|
@@ -527,6 +1425,7 @@ async function appendDelegationTransition(directory, correlationId, transition)
|
|
|
527
1425
|
...transition.result ? { result: transition.result } : {}
|
|
528
1426
|
};
|
|
529
1427
|
appendRecord(directory, next);
|
|
1428
|
+
maybeCompactDelegationsLocked(directory);
|
|
530
1429
|
});
|
|
531
1430
|
return next;
|
|
532
1431
|
} catch (err) {
|
|
@@ -547,7 +1446,10 @@ function sameJson(left, right) {
|
|
|
547
1446
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
548
1447
|
}
|
|
549
1448
|
function sameTerminalEvent(left, right) {
|
|
550
|
-
return left.eventId === right.eventId && left.status === right.status &&
|
|
1449
|
+
return left.eventId === right.eventId && left.status === right.status && sameRetainedResult(left.result, right.result);
|
|
1450
|
+
}
|
|
1451
|
+
function sameRetainedResult(left, right) {
|
|
1452
|
+
return left.chars === right.chars && left.truncated === right.truncated && left.digest === right.digest && left.outputRef === right.outputRef && left.outputPreviewChars === right.outputPreviewChars && left.outputDegraded === right.outputDegraded && left.outputArtifactError === right.outputArtifactError && left.transcriptIncomplete === right.transcriptIncomplete && left.messageCount === right.messageCount && sameJson(left.salvagedWorkflowLanes, right.salvagedWorkflowLanes);
|
|
551
1453
|
}
|
|
552
1454
|
function settlementProvenanceFor(record) {
|
|
553
1455
|
const baseline = record.taskChangeContext?.baseline;
|
|
@@ -580,12 +1482,16 @@ async function claimTerminalResult(directory, correlationId, terminalResult) {
|
|
|
580
1482
|
let claim = null;
|
|
581
1483
|
try {
|
|
582
1484
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
583
|
-
const
|
|
1485
|
+
const records = loadRecordsForWrite(directory);
|
|
1486
|
+
if (records === null)
|
|
1487
|
+
return;
|
|
1488
|
+
const current = findRecordForWrite(records, correlationId);
|
|
584
1489
|
if (!current)
|
|
585
1490
|
return;
|
|
586
1491
|
if (current.terminalResult) {
|
|
587
1492
|
if (!sameTerminalEvent(current.terminalResult, parsedTerminal.data)) {
|
|
588
1493
|
warn(`[background] claimTerminalResult: different terminal event for ` + `correlationId=${correlationId}; ` + `existing={status: ${current.terminalResult.status}, eventId: ${current.terminalResult.eventId}} ` + `incoming={status: ${parsedTerminal.data.status}, eventId: ${parsedTerminal.data.eventId}}; ` + `rejected`);
|
|
1494
|
+
incrementLateTerminalCount(directory);
|
|
589
1495
|
return;
|
|
590
1496
|
}
|
|
591
1497
|
claim = {
|
|
@@ -609,6 +1515,7 @@ async function claimTerminalResult(directory, correlationId, terminalResult) {
|
|
|
609
1515
|
};
|
|
610
1516
|
}
|
|
611
1517
|
}
|
|
1518
|
+
const foldUpdatedAt = Math.max(parsedTerminal.data.recordedAt, current.updatedAt);
|
|
612
1519
|
const next = {
|
|
613
1520
|
...current,
|
|
614
1521
|
schemaVersion: 3,
|
|
@@ -616,12 +1523,13 @@ async function claimTerminalResult(directory, correlationId, terminalResult) {
|
|
|
616
1523
|
terminalResult: parsedTerminal.data,
|
|
617
1524
|
result: parsedTerminal.data.result,
|
|
618
1525
|
completedAt: parsedTerminal.data.recordedAt,
|
|
619
|
-
updatedAt:
|
|
1526
|
+
updatedAt: foldUpdatedAt,
|
|
620
1527
|
...coderSettlement ? { coderSettlement } : {}
|
|
621
1528
|
};
|
|
622
1529
|
if (!RecordSchema.safeParse(next).success)
|
|
623
1530
|
return;
|
|
624
1531
|
appendRecord(directory, next);
|
|
1532
|
+
maybeCompactDelegationsLocked(directory);
|
|
625
1533
|
claim = { disposition: "claimed", record: next };
|
|
626
1534
|
});
|
|
627
1535
|
return claim;
|
|
@@ -630,13 +1538,30 @@ async function claimTerminalResult(directory, correlationId, terminalResult) {
|
|
|
630
1538
|
return null;
|
|
631
1539
|
}
|
|
632
1540
|
}
|
|
1541
|
+
function incrementLateTerminalCount(directory) {
|
|
1542
|
+
try {
|
|
1543
|
+
const artifact = readDelegationHealthArtifact(directory);
|
|
1544
|
+
const current = artifact?.counts.lateTerminals ?? 0;
|
|
1545
|
+
writeDelegationHealthArtifact(directory, {
|
|
1546
|
+
counts: {
|
|
1547
|
+
activeOwners: artifact?.counts.activeOwners ?? 0,
|
|
1548
|
+
pendingAdvisories: artifact?.counts.pendingAdvisories ?? 0,
|
|
1549
|
+
lateTerminals: current + 1,
|
|
1550
|
+
orphanWorktreeOwners: artifact?.counts.orphanWorktreeOwners ?? 0
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
} catch {}
|
|
1554
|
+
}
|
|
633
1555
|
async function claimCoderSettlement(directory, correlationId, operationId, input = {}) {
|
|
634
1556
|
if (!correlationId || !operationId || operationId.length > 256)
|
|
635
1557
|
return null;
|
|
636
1558
|
let claim = null;
|
|
637
1559
|
try {
|
|
638
1560
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
639
|
-
const
|
|
1561
|
+
const records = loadRecordsForWrite(directory);
|
|
1562
|
+
if (records === null)
|
|
1563
|
+
return;
|
|
1564
|
+
const current = findRecordForWrite(records, correlationId);
|
|
640
1565
|
if (!current || current.normalizedAgent !== "coder" || current.terminalResult?.status !== "completed") {
|
|
641
1566
|
return;
|
|
642
1567
|
}
|
|
@@ -682,6 +1607,7 @@ async function claimCoderSettlement(directory, correlationId, operationId, input
|
|
|
682
1607
|
if (!RecordSchema.safeParse(next).success)
|
|
683
1608
|
return;
|
|
684
1609
|
appendRecord(directory, next);
|
|
1610
|
+
maybeCompactDelegationsLocked(directory);
|
|
685
1611
|
claim = {
|
|
686
1612
|
disposition: existing.state === "settling" ? "resume" : "claimed",
|
|
687
1613
|
record: next
|
|
@@ -697,10 +1623,10 @@ function normalizeObservedFile(value) {
|
|
|
697
1623
|
if (typeof value !== "string")
|
|
698
1624
|
return null;
|
|
699
1625
|
const trimmed = value.trim().replace(/\\/g, "/");
|
|
700
|
-
if (trimmed.length === 0 || trimmed.length > 4096 || trimmed.includes("\x00") ||
|
|
1626
|
+
if (trimmed.length === 0 || trimmed.length > 4096 || trimmed.includes("\x00") || path2.posix.isAbsolute(trimmed) || /^[A-Za-z]:\//.test(trimmed)) {
|
|
701
1627
|
return null;
|
|
702
1628
|
}
|
|
703
|
-
const normalized =
|
|
1629
|
+
const normalized = path2.posix.normalize(trimmed).replace(/^\.\//, "");
|
|
704
1630
|
if (normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized.endsWith("/")) {
|
|
705
1631
|
return null;
|
|
706
1632
|
}
|
|
@@ -725,7 +1651,10 @@ async function updateCoderSettlement(directory, correlationId, input) {
|
|
|
725
1651
|
let result = null;
|
|
726
1652
|
try {
|
|
727
1653
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
728
|
-
const
|
|
1654
|
+
const records = loadRecordsForWrite(directory);
|
|
1655
|
+
if (records === null)
|
|
1656
|
+
return;
|
|
1657
|
+
const current = findRecordForWrite(records, correlationId);
|
|
729
1658
|
const existing = current?.coderSettlement;
|
|
730
1659
|
if (!current || !existing)
|
|
731
1660
|
return;
|
|
@@ -786,6 +1715,7 @@ async function updateCoderSettlement(directory, correlationId, input) {
|
|
|
786
1715
|
if (!RecordSchema.safeParse(next).success)
|
|
787
1716
|
return;
|
|
788
1717
|
appendRecord(directory, next);
|
|
1718
|
+
maybeCompactDelegationsLocked(directory);
|
|
789
1719
|
result = next;
|
|
790
1720
|
});
|
|
791
1721
|
return result;
|
|
@@ -802,7 +1732,10 @@ async function claimDelegationIngestion(directory, correlationId, options) {
|
|
|
802
1732
|
let claim = null;
|
|
803
1733
|
try {
|
|
804
1734
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
805
|
-
const
|
|
1735
|
+
const records = loadRecordsForWrite(directory);
|
|
1736
|
+
if (records === null)
|
|
1737
|
+
return;
|
|
1738
|
+
const current = findRecordForWrite(records, correlationId);
|
|
806
1739
|
if (!current?.terminalResult)
|
|
807
1740
|
return;
|
|
808
1741
|
if (current.status === "consumed") {
|
|
@@ -841,6 +1774,7 @@ async function claimDelegationIngestion(directory, correlationId, options) {
|
|
|
841
1774
|
updatedAt: now
|
|
842
1775
|
};
|
|
843
1776
|
appendRecord(directory, next);
|
|
1777
|
+
maybeCompactDelegationsLocked(directory);
|
|
844
1778
|
claim = { disposition, record: next };
|
|
845
1779
|
});
|
|
846
1780
|
return claim;
|
|
@@ -855,7 +1789,10 @@ async function recordDelegationIngestionResult(directory, correlationId, claimTo
|
|
|
855
1789
|
let result = null;
|
|
856
1790
|
try {
|
|
857
1791
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
858
|
-
const
|
|
1792
|
+
const records = loadRecordsForWrite(directory);
|
|
1793
|
+
if (records === null)
|
|
1794
|
+
return;
|
|
1795
|
+
const current = findRecordForWrite(records, correlationId);
|
|
859
1796
|
if (!current)
|
|
860
1797
|
return;
|
|
861
1798
|
if (success && current.status === "consumed" && current.ingestion?.state === "consumed" && current.ingestion.claimToken === claimToken || !success && current.status === "ingestion_error" && current.ingestion?.state === "retryable" && current.ingestion.claimToken === claimToken) {
|
|
@@ -878,6 +1815,7 @@ async function recordDelegationIngestionResult(directory, correlationId, claimTo
|
|
|
878
1815
|
updatedAt
|
|
879
1816
|
};
|
|
880
1817
|
appendRecord(directory, next);
|
|
1818
|
+
maybeCompactDelegationsLocked(directory);
|
|
881
1819
|
result = next;
|
|
882
1820
|
});
|
|
883
1821
|
return result;
|
|
@@ -900,7 +1838,10 @@ async function putPendingBackgroundAdvisory(directory, correlationId, input) {
|
|
|
900
1838
|
let result = null;
|
|
901
1839
|
try {
|
|
902
1840
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
903
|
-
const
|
|
1841
|
+
const records = loadRecordsForWrite(directory);
|
|
1842
|
+
if (records === null)
|
|
1843
|
+
return;
|
|
1844
|
+
const current = findRecordForWrite(records, correlationId);
|
|
904
1845
|
if (!current?.terminalResult || current.terminalResult.eventId !== parsed.data.eventId || current.parentSessionId !== parsed.data.parentSessionId) {
|
|
905
1846
|
return;
|
|
906
1847
|
}
|
|
@@ -917,6 +1858,7 @@ async function putPendingBackgroundAdvisory(directory, correlationId, input) {
|
|
|
917
1858
|
updatedAt: createdAt
|
|
918
1859
|
};
|
|
919
1860
|
appendRecord(directory, next);
|
|
1861
|
+
maybeCompactDelegationsLocked(directory);
|
|
920
1862
|
result = parsed.data;
|
|
921
1863
|
});
|
|
922
1864
|
return result;
|
|
@@ -933,7 +1875,10 @@ async function preparePendingBackgroundAdvisories(directory, parentSessionId, op
|
|
|
933
1875
|
const prepared = [];
|
|
934
1876
|
try {
|
|
935
1877
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
936
|
-
|
|
1878
|
+
const records = loadRecordsForWrite(directory);
|
|
1879
|
+
if (records === null)
|
|
1880
|
+
return;
|
|
1881
|
+
for (const current of records) {
|
|
937
1882
|
if (prepared.length >= 1)
|
|
938
1883
|
break;
|
|
939
1884
|
const advisory = current.advisoryInbox;
|
|
@@ -958,6 +1903,7 @@ async function preparePendingBackgroundAdvisories(directory, parentSessionId, op
|
|
|
958
1903
|
updatedAt: now
|
|
959
1904
|
};
|
|
960
1905
|
appendRecord(directory, next);
|
|
1906
|
+
maybeCompactDelegationsLocked(directory);
|
|
961
1907
|
prepared.push(nextAdvisory);
|
|
962
1908
|
}
|
|
963
1909
|
});
|
|
@@ -974,8 +1920,11 @@ async function releaseBackgroundAdvisoryPreparation(directory, parentSessionId,
|
|
|
974
1920
|
}
|
|
975
1921
|
try {
|
|
976
1922
|
return await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
1923
|
+
const records = loadRecordsForWrite(directory);
|
|
1924
|
+
if (records === null)
|
|
1925
|
+
return false;
|
|
977
1926
|
const byEvent = new Map;
|
|
978
|
-
for (const current of
|
|
1927
|
+
for (const current of records) {
|
|
979
1928
|
const advisory = current.advisoryInbox;
|
|
980
1929
|
if (advisory?.parentSessionId === parentSessionId && uniqueEventIds.includes(advisory.eventId)) {
|
|
981
1930
|
byEvent.set(advisory.eventId, current);
|
|
@@ -1003,6 +1952,7 @@ async function releaseBackgroundAdvisoryPreparation(directory, parentSessionId,
|
|
|
1003
1952
|
};
|
|
1004
1953
|
appendRecord(directory, next);
|
|
1005
1954
|
}
|
|
1955
|
+
maybeCompactDelegationsLocked(directory);
|
|
1006
1956
|
return true;
|
|
1007
1957
|
});
|
|
1008
1958
|
} catch (err) {
|
|
@@ -1016,8 +1966,11 @@ async function acknowledgeObservedBackgroundAdvisories(directory, parentSessionI
|
|
|
1016
1966
|
let acknowledged = 0;
|
|
1017
1967
|
try {
|
|
1018
1968
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
1969
|
+
const records = loadRecordsForWrite(directory);
|
|
1970
|
+
if (records === null)
|
|
1971
|
+
return;
|
|
1019
1972
|
const now = Date.now();
|
|
1020
|
-
for (const current of
|
|
1973
|
+
for (const current of records) {
|
|
1021
1974
|
const advisory = current.advisoryInbox;
|
|
1022
1975
|
if (!advisory || advisory.parentSessionId !== parentSessionId || advisory.state !== "pending" || !observedTexts.some((text) => {
|
|
1023
1976
|
const advisoryBlock = text.match(/\[ADVISORIES\]([\s\S]*?)\[\/ADVISORIES\]/);
|
|
@@ -1044,6 +1997,8 @@ async function acknowledgeObservedBackgroundAdvisories(directory, parentSessionI
|
|
|
1044
1997
|
appendRecord(directory, next);
|
|
1045
1998
|
acknowledged += 1;
|
|
1046
1999
|
}
|
|
2000
|
+
if (acknowledged > 0)
|
|
2001
|
+
maybeCompactDelegationsLocked(directory);
|
|
1047
2002
|
});
|
|
1048
2003
|
return acknowledged;
|
|
1049
2004
|
} catch (err) {
|
|
@@ -1065,10 +2020,13 @@ function findOpenAsyncLaneBatches(directory) {
|
|
|
1065
2020
|
function isTerminal(status) {
|
|
1066
2021
|
return status === "completed" || status === "error" || status === "cancelled" || status === "stale" || status === "consumed";
|
|
1067
2022
|
}
|
|
1068
|
-
function sweepStaleLocked(directory, timeoutMs, now) {
|
|
2023
|
+
function sweepStaleLocked(directory, timeoutMs, now, statuses = DEFAULT_SWEEPABLE_DELEGATION_STATUSES) {
|
|
1069
2024
|
let swept = 0;
|
|
1070
|
-
|
|
1071
|
-
|
|
2025
|
+
const records = loadRecordsForWrite(directory);
|
|
2026
|
+
if (records === null)
|
|
2027
|
+
return 0;
|
|
2028
|
+
for (const record of records) {
|
|
2029
|
+
if (!statuses.has(record.status))
|
|
1072
2030
|
continue;
|
|
1073
2031
|
if (now - record.updatedAt <= timeoutMs)
|
|
1074
2032
|
continue;
|
|
@@ -1079,13 +2037,16 @@ function sweepStaleLocked(directory, timeoutMs, now) {
|
|
|
1079
2037
|
});
|
|
1080
2038
|
swept += 1;
|
|
1081
2039
|
}
|
|
2040
|
+
if (swept > 0)
|
|
2041
|
+
maybeCompactDelegationsLocked(directory);
|
|
1082
2042
|
return swept;
|
|
1083
2043
|
}
|
|
1084
|
-
async function sweepStaleDelegations(directory, timeoutMs) {
|
|
2044
|
+
async function sweepStaleDelegations(directory, timeoutMs, options = {}) {
|
|
1085
2045
|
if (!timeoutMs || timeoutMs <= 0)
|
|
1086
2046
|
return 0;
|
|
2047
|
+
const statuses = options.statuses ?? DEFAULT_SWEEPABLE_DELEGATION_STATUSES;
|
|
1087
2048
|
try {
|
|
1088
|
-
return await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => sweepStaleLocked(directory, timeoutMs, Date.now()));
|
|
2049
|
+
return await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => sweepStaleLocked(directory, timeoutMs, Date.now(), statuses));
|
|
1089
2050
|
} catch (err) {
|
|
1090
2051
|
warn(`[background] sweepStaleDelegations failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1091
2052
|
return 0;
|
|
@@ -1093,18 +2054,18 @@ async function sweepStaleDelegations(directory, timeoutMs) {
|
|
|
1093
2054
|
}
|
|
1094
2055
|
function fallbackRelativePath(correlationId) {
|
|
1095
2056
|
const digest = createHash("sha256").update(correlationId).digest("hex");
|
|
1096
|
-
return
|
|
2057
|
+
return path2.join(BACKGROUND_DELEGATION_FALLBACK_DIR, `${digest}.json`);
|
|
1097
2058
|
}
|
|
1098
2059
|
function fallbackPath(directory, correlationId) {
|
|
1099
2060
|
return validateSwarmPath(directory, fallbackRelativePath(correlationId));
|
|
1100
2061
|
}
|
|
1101
2062
|
function fallbackDirectoryPath(directory) {
|
|
1102
|
-
return
|
|
2063
|
+
return path2.dirname(validateSwarmPath(directory, path2.join(BACKGROUND_DELEGATION_FALLBACK_DIR, ".containment-anchor")));
|
|
1103
2064
|
}
|
|
1104
2065
|
async function readFallbackFile(directory, correlationId) {
|
|
1105
2066
|
for (let attempt = 0;attempt < 5; attempt++) {
|
|
1106
2067
|
try {
|
|
1107
|
-
const raw = await
|
|
2068
|
+
const raw = await fs2.promises.readFile(fallbackPath(directory, correlationId), "utf-8");
|
|
1108
2069
|
const parsedJson = JSON.parse(raw);
|
|
1109
2070
|
const parsed = FallbackArtifactSchema.safeParse(parsedJson);
|
|
1110
2071
|
if (!parsed.success || parsed.data.correlationId !== correlationId) {
|
|
@@ -1114,7 +2075,7 @@ async function readFallbackFile(directory, correlationId) {
|
|
|
1114
2075
|
} catch (err) {
|
|
1115
2076
|
const code = err.code;
|
|
1116
2077
|
if (code === "ENOENT" && attempt < 4) {
|
|
1117
|
-
await new Promise((
|
|
2078
|
+
await new Promise((resolve3) => setTimeout(resolve3, 10));
|
|
1118
2079
|
continue;
|
|
1119
2080
|
}
|
|
1120
2081
|
return null;
|
|
@@ -1130,7 +2091,7 @@ async function readDelegationFallback(directory, correlationId) {
|
|
|
1130
2091
|
async function listDelegationFallbacks(directory) {
|
|
1131
2092
|
let entries;
|
|
1132
2093
|
try {
|
|
1133
|
-
entries =
|
|
2094
|
+
entries = fs2.readdirSync(fallbackDirectoryPath(directory), {
|
|
1134
2095
|
withFileTypes: true
|
|
1135
2096
|
}).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).slice(0, MAX_LIVE_BACKGROUND_FALLBACKS);
|
|
1136
2097
|
} catch {
|
|
@@ -1139,7 +2100,7 @@ async function listDelegationFallbacks(directory) {
|
|
|
1139
2100
|
const artifacts = [];
|
|
1140
2101
|
for (const entry of entries) {
|
|
1141
2102
|
try {
|
|
1142
|
-
const raw = await
|
|
2103
|
+
const raw = await fs2.promises.readFile(path2.join(fallbackDirectoryPath(directory), entry.name), "utf-8");
|
|
1143
2104
|
const parsed = FallbackArtifactSchema.safeParse(JSON.parse(raw));
|
|
1144
2105
|
if (parsed.success)
|
|
1145
2106
|
artifacts.push(parsed.data);
|
|
@@ -1152,7 +2113,7 @@ async function scanDelegationFallbacksForRecovery(directory) {
|
|
|
1152
2113
|
let entries;
|
|
1153
2114
|
try {
|
|
1154
2115
|
fallbackDir = fallbackDirectoryPath(directory);
|
|
1155
|
-
entries =
|
|
2116
|
+
entries = fs2.readdirSync(fallbackDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json"));
|
|
1156
2117
|
} catch (error) {
|
|
1157
2118
|
if (error.code === "ENOENT") {
|
|
1158
2119
|
return { status: "ok", owners: [] };
|
|
@@ -1170,17 +2131,17 @@ async function scanDelegationFallbacksForRecovery(directory) {
|
|
|
1170
2131
|
}
|
|
1171
2132
|
const artifacts = [];
|
|
1172
2133
|
for (const entry of entries) {
|
|
1173
|
-
const artifactPath =
|
|
2134
|
+
const artifactPath = path2.join(fallbackDir, entry.name);
|
|
1174
2135
|
let raw;
|
|
1175
2136
|
try {
|
|
1176
|
-
const stat =
|
|
2137
|
+
const stat = fs2.statSync(artifactPath);
|
|
1177
2138
|
if (stat.size > MAX_RECOVERY_FALLBACK_BYTES) {
|
|
1178
2139
|
return {
|
|
1179
2140
|
status: "uncertain",
|
|
1180
2141
|
reason: `background fallback artifact "${entry.name}" exceeds the recovery size bound`
|
|
1181
2142
|
};
|
|
1182
2143
|
}
|
|
1183
|
-
raw = await
|
|
2144
|
+
raw = await fs2.promises.readFile(artifactPath, "utf-8");
|
|
1184
2145
|
} catch (error) {
|
|
1185
2146
|
return {
|
|
1186
2147
|
status: "uncertain",
|
|
@@ -1215,7 +2176,7 @@ async function scanDelegationFallbacksForRecovery(directory) {
|
|
|
1215
2176
|
}
|
|
1216
2177
|
function countFallbackFiles(directory) {
|
|
1217
2178
|
try {
|
|
1218
|
-
return
|
|
2179
|
+
return fs2.readdirSync(fallbackDirectoryPath(directory), {
|
|
1219
2180
|
withFileTypes: true
|
|
1220
2181
|
}).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).length;
|
|
1221
2182
|
} catch (err) {
|
|
@@ -1250,7 +2211,7 @@ async function writeDelegationFallback(directory, input, options = {}) {
|
|
|
1250
2211
|
if (countFallbackFiles(directory) >= maxLive)
|
|
1251
2212
|
return null;
|
|
1252
2213
|
const absPath = fallbackPath(directory, input.correlationId);
|
|
1253
|
-
|
|
2214
|
+
fs2.mkdirSync(path2.dirname(absPath), { recursive: true });
|
|
1254
2215
|
await bunWrite(absPath, `${JSON.stringify(parsed.data)}
|
|
1255
2216
|
`);
|
|
1256
2217
|
return parsed.data;
|
|
@@ -1266,7 +2227,7 @@ async function removeDelegationFallback(directory, correlationId) {
|
|
|
1266
2227
|
try {
|
|
1267
2228
|
return await withEvidenceLock(directory, BACKGROUND_DELEGATION_FALLBACK_DIR, STORE_LOCK_AGENT, FALLBACK_LOCK_TASK, async () => {
|
|
1268
2229
|
try {
|
|
1269
|
-
await
|
|
2230
|
+
await fs2.promises.unlink(fallbackPath(directory, correlationId));
|
|
1270
2231
|
return true;
|
|
1271
2232
|
} catch (err) {
|
|
1272
2233
|
return err.code === "ENOENT";
|
|
@@ -1314,7 +2275,10 @@ async function promoteDelegationFallback(directory, correlationId) {
|
|
|
1314
2275
|
}
|
|
1315
2276
|
let promoted = null;
|
|
1316
2277
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
1317
|
-
const
|
|
2278
|
+
const records = loadRecordsForWrite(directory);
|
|
2279
|
+
if (records === null)
|
|
2280
|
+
return;
|
|
2281
|
+
const current = findRecordForWrite(records, correlationId);
|
|
1318
2282
|
if (current) {
|
|
1319
2283
|
if (samePromotionIdentity(current, fallback.record)) {
|
|
1320
2284
|
promoted = current;
|
|
@@ -1322,12 +2286,13 @@ async function promoteDelegationFallback(directory, correlationId) {
|
|
|
1322
2286
|
return;
|
|
1323
2287
|
}
|
|
1324
2288
|
appendRecord(directory, fallback.record);
|
|
2289
|
+
maybeCompactDelegationsLocked(directory);
|
|
1325
2290
|
promoted = fallback.record;
|
|
1326
2291
|
});
|
|
1327
2292
|
if (!promoted)
|
|
1328
2293
|
return null;
|
|
1329
2294
|
try {
|
|
1330
|
-
await
|
|
2295
|
+
await fs2.promises.unlink(fallbackPath(directory, correlationId));
|
|
1331
2296
|
} catch (err) {
|
|
1332
2297
|
if (err.code !== "ENOENT") {
|
|
1333
2298
|
return null;
|
|
@@ -1353,14 +2318,14 @@ function scanBackgroundCoderReservationsForAdmission(directory) {
|
|
|
1353
2318
|
let raw;
|
|
1354
2319
|
try {
|
|
1355
2320
|
absolutePath = reservationStorePath(directory);
|
|
1356
|
-
const stat =
|
|
2321
|
+
const stat = fs2.statSync(absolutePath);
|
|
1357
2322
|
if (stat.size > MAX_BACKGROUND_CODER_RESERVATION_STORE_BYTES) {
|
|
1358
2323
|
return {
|
|
1359
2324
|
status: "uncertain",
|
|
1360
2325
|
reason: `background coder reservation store exceeds the ${MAX_BACKGROUND_CODER_RESERVATION_STORE_BYTES}-byte safety bound`
|
|
1361
2326
|
};
|
|
1362
2327
|
}
|
|
1363
|
-
raw =
|
|
2328
|
+
raw = fs2.readFileSync(absolutePath, "utf-8");
|
|
1364
2329
|
} catch (error) {
|
|
1365
2330
|
if (error.code === "ENOENT") {
|
|
1366
2331
|
return { status: "ok", reservations: [] };
|
|
@@ -1674,4 +2639,4 @@ async function releaseBackgroundCoderReservation(directory, input) {
|
|
|
1674
2639
|
}
|
|
1675
2640
|
}
|
|
1676
2641
|
|
|
1677
|
-
export { BACKGROUND_DELEGATIONS_FILE, BACKGROUND_DELEGATION_FALLBACK_DIR, BACKGROUND_CODER_RESERVATIONS_FILE, MAX_LIVE_BACKGROUND_FALLBACKS, MAX_LIVE_BACKGROUND_CODER_RESERVATIONS, MAX_BACKGROUND_OBSERVED_FILES, MAX_BACKGROUND_ADVISORY_CHARS, BACKGROUND_INGESTION_LEASE_MS, readDelegations, scanDelegationsForRecovery, findByCorrelationId, recordPendingDelegationDetailed, recordPendingDelegation, buildPromptSnapshot, appendDelegationTransition, buildBackgroundCompletionEventId, claimTerminalResult, claimCoderSettlement, normalizeBackgroundObservedFiles, updateCoderSettlement, claimDelegationIngestion, recordDelegationIngestionResult, putPendingBackgroundAdvisory, preparePendingBackgroundAdvisories, acknowledgeObservedBackgroundAdvisories, releasePreparedBackgroundAdvisories, findByBatchId, findOpenAsyncLaneBatches, sweepStaleDelegations, readDelegationFallback, listDelegationFallbacks, scanDelegationFallbacksForRecovery, writeDelegationFallback, removeDelegationFallback, findDelegationForCompletion, promoteDelegationFallback, buildBackgroundCoderReservationId, scanBackgroundCoderReservationsForAdmission, reserveBackgroundCoderSlot, bindBackgroundCoderReservation, releaseBackgroundCoderReservation };
|
|
2642
|
+
export { MAX_RECOVERY_LEDGER_BYTES, collectDelegationLedgerHealth, BACKGROUND_DELEGATIONS_FILE, BACKGROUND_DELEGATION_FALLBACK_DIR, BACKGROUND_CODER_RESERVATIONS_FILE, BACKGROUND_DELEGATIONS_CHECKPOINT_FILE, BACKGROUND_DELEGATIONS_MANIFEST_FILE, MAX_LIVE_BACKGROUND_FALLBACKS, MAX_LIVE_BACKGROUND_CODER_RESERVATIONS, MAX_BACKGROUND_OBSERVED_FILES, MAX_BACKGROUND_ADVISORY_CHARS, DELEGATION_COMPACTION_LOW_WATER_BYTES, DELEGATION_COMPACTION_HIGH_WATER_BYTES, MAX_CHECKPOINT_BYTES, MAX_CHECKPOINT_RECORDS, TOMBSTONE_MIN_AGE_MS, BACKGROUND_INGESTION_LEASE_MS, DEFAULT_STALE_DELEGATION_TIMEOUT_MS, DEFAULT_SWEEPABLE_DELEGATION_STATUSES, _checkpointInternals, isUnsettledWorktreeOwner, compactBackgroundDelegations, readDelegations, scanDelegationsForRecovery, findByCorrelationId, recordPendingDelegationDetailed, recordPendingDelegation, buildPromptSnapshot, appendDelegationTransition, buildBackgroundCompletionEventId, claimTerminalResult, claimCoderSettlement, normalizeBackgroundObservedFiles, updateCoderSettlement, claimDelegationIngestion, recordDelegationIngestionResult, putPendingBackgroundAdvisory, preparePendingBackgroundAdvisories, acknowledgeObservedBackgroundAdvisories, releasePreparedBackgroundAdvisories, findByBatchId, findOpenAsyncLaneBatches, sweepStaleDelegations, readDelegationFallback, listDelegationFallbacks, scanDelegationFallbacksForRecovery, writeDelegationFallback, removeDelegationFallback, findDelegationForCompletion, promoteDelegationFallback, buildBackgroundCoderReservationId, scanBackgroundCoderReservationsForAdmission, reserveBackgroundCoderSlot, bindBackgroundCoderReservation, releaseBackgroundCoderReservation };
|