opencode-swarm 7.143.6 → 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/dist/background/delegation-health.d.ts +213 -0
- package/dist/background/pending-delegations.d.ts +122 -15
- package/dist/cli/{config-doctor-72zp0k4x.js → config-doctor-7gy47evn.js} +2 -2
- package/dist/cli/{core-aj0wmbg8.js → core-cpcspaw5.js} +1 -1
- package/dist/cli/{curator-llm-factory-x1vjhrnc.js → curator-llm-factory-fyjmgxyp.js} +18 -18
- package/dist/cli/{curator-2gt2835v.js → curator-v97jnwn2.js} +18 -18
- package/dist/cli/{evidence-summary-service-yjr0662r.js → evidence-summary-service-79mtxzhd.js} +6 -6
- package/dist/cli/{gate-evidence-r3c7eqpz.js → gate-evidence-kgyqz1g1.js} +2 -2
- package/dist/cli/{guardrail-explain-f3zysesy.js → guardrail-explain-9ry66z5g.js} +19 -19
- package/dist/cli/{guardrail-log-eat71fnh.js → guardrail-log-fe6smme1.js} +3 -3
- package/dist/cli/{hive-promoter-fyb40am9.js → hive-promoter-vwq1dbh1.js} +18 -18
- package/dist/cli/{index-hjy4txgr.js → index-0ck3e366.js} +1 -1
- package/dist/cli/{index-peqmw3wr.js → index-1pr8x10a.js} +2 -2
- package/dist/cli/{index-02nm8qhk.js → index-269b52r3.js} +3 -3
- package/dist/cli/{index-7bs015gm.js → index-3q9w6e6v.js} +1 -1
- package/dist/cli/{index-w1q9yy1e.js → index-3v3dxf75.js} +3 -3
- package/dist/cli/{index-6vmazy9f.js → index-4fda8kkw.js} +1050 -88
- package/dist/cli/{index-fn0k0vsa.js → index-6p09gyra.js} +1 -1
- package/dist/cli/{index-kc2x117y.js → index-8xertnqz.js} +3 -3
- package/dist/cli/{index-36vry26a.js → index-d45d4zyq.js} +1 -1
- package/dist/cli/{index-nd1j9wkw.js → index-d45yrzkg.js} +1 -1
- package/dist/cli/{index-dtf2va28.js → index-e70d3xr7.js} +20 -20
- package/dist/cli/{index-whyrhjsk.js → index-evvbmbbv.js} +1 -1
- package/dist/cli/{index-gzxz0s50.js → index-ewzmjm7m.js} +1 -1
- package/dist/cli/{index-0adcjpyx.js → index-ez18gjk2.js} +3 -3
- package/dist/cli/{index-5d8ydxae.js → index-j1scj25g.js} +129 -50
- package/dist/cli/{index-wjk00vwk.js → index-jqabjy7t.js} +4 -4
- package/dist/cli/{index-bbv64s17.js → index-jvbfrwt2.js} +1 -1
- package/dist/cli/{index-92bm9ys3.js → index-mw6p3nt6.js} +1 -1
- package/dist/cli/{index-72zhsqgy.js → index-rd2082y4.js} +2 -2
- package/dist/cli/{index-s7nb33pc.js → index-tfkfyshg.js} +1 -1
- package/dist/cli/{index-scj2asek.js → index-vb9cpaz4.js} +5 -5
- package/dist/cli/{index-z4cv77tq.js → index-zv4pwyas.js} +2 -2
- package/dist/cli/index.js +18 -18
- package/dist/cli/{knowledge-escalator-ejpyrby0.js → knowledge-escalator-rmy3jcx4.js} +6 -6
- package/dist/cli/{knowledge-events-yw2kydyy.js → knowledge-events-1tqpbwvj.js} +4 -4
- package/dist/cli/{knowledge-store-j6gkk62e.js → knowledge-store-389ck792.js} +1 -1
- package/dist/cli/{knowledge-validator-xervpe5k.js → knowledge-validator-kvtk6633.js} +2 -2
- package/dist/cli/{pending-delegations-19wg9j08.js → pending-delegations-thy8g30k.js} +23 -1
- package/dist/cli/{scan-cursor-x7a99wmq.js → scan-cursor-ahgm4f5p.js} +2 -2
- package/dist/cli/{schema-z141arx9.js → schema-epdq7ykx.js} +1 -1
- package/dist/cli/{scope-persistence-js90c1f4.js → scope-persistence-37s9n40v.js} +4 -4
- package/dist/cli/{skill-generator-t202jza0.js → skill-generator-4d2t5gem.js} +7 -7
- package/dist/cli/{worktree-collision-ownership-42bb3gcb.js → worktree-collision-ownership-j6d33p49.js} +1 -1
- package/dist/cli/{worktree-isolation-fbfvxw9a.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/index.js +439 -436
- package/dist/services/status-service.d.ts +9 -0
- 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";
|
|
@@ -291,43 +496,359 @@ var FallbackArtifactSchema = exports_external.object({
|
|
|
291
496
|
function storePath(directory) {
|
|
292
497
|
return validateSwarmPath(directory, BACKGROUND_DELEGATIONS_FILE);
|
|
293
498
|
}
|
|
294
|
-
function
|
|
295
|
-
|
|
499
|
+
function checkpointPath(directory) {
|
|
500
|
+
return validateSwarmPath(directory, BACKGROUND_DELEGATIONS_CHECKPOINT_FILE);
|
|
296
501
|
}
|
|
297
|
-
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) {
|
|
298
589
|
let raw;
|
|
299
590
|
try {
|
|
300
|
-
raw =
|
|
591
|
+
raw = fs2.readFileSync(target, "utf-8");
|
|
301
592
|
} catch (err) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
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);
|
|
306
784
|
}
|
|
307
|
-
const
|
|
308
|
-
|
|
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(`
|
|
309
792
|
`)) {
|
|
793
|
+
lineNumber += 1;
|
|
310
794
|
const trimmed = line.trim();
|
|
311
|
-
if (trimmed
|
|
795
|
+
if (!trimmed)
|
|
312
796
|
continue;
|
|
313
797
|
let parsedJson;
|
|
314
798
|
try {
|
|
315
799
|
parsedJson = JSON.parse(trimmed);
|
|
316
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
|
+
}
|
|
317
811
|
continue;
|
|
318
812
|
}
|
|
319
|
-
const
|
|
320
|
-
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)) {
|
|
321
815
|
continue;
|
|
322
|
-
|
|
816
|
+
}
|
|
817
|
+
records.set(parsed.data.correlationId, parsed.data);
|
|
323
818
|
}
|
|
324
|
-
return
|
|
819
|
+
return null;
|
|
325
820
|
}
|
|
326
|
-
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
|
+
}
|
|
327
848
|
let absolutePath;
|
|
328
849
|
try {
|
|
329
850
|
absolutePath = storePath(directory);
|
|
330
|
-
const stat =
|
|
851
|
+
const stat = fs2.statSync(absolutePath);
|
|
331
852
|
if (stat.size > MAX_RECOVERY_LEDGER_BYTES) {
|
|
332
853
|
return {
|
|
333
854
|
status: "uncertain",
|
|
@@ -336,7 +857,13 @@ function scanDelegationsForRecovery(directory) {
|
|
|
336
857
|
}
|
|
337
858
|
} catch (error) {
|
|
338
859
|
if (error.code === "ENOENT") {
|
|
339
|
-
return {
|
|
860
|
+
return {
|
|
861
|
+
status: "ok",
|
|
862
|
+
mode: "legacy",
|
|
863
|
+
records: new Map,
|
|
864
|
+
manifest: null,
|
|
865
|
+
checkpoint: null
|
|
866
|
+
};
|
|
340
867
|
}
|
|
341
868
|
return {
|
|
342
869
|
status: "uncertain",
|
|
@@ -345,7 +872,7 @@ function scanDelegationsForRecovery(directory) {
|
|
|
345
872
|
}
|
|
346
873
|
let raw;
|
|
347
874
|
try {
|
|
348
|
-
raw =
|
|
875
|
+
raw = fs2.readFileSync(absolutePath, "utf-8");
|
|
349
876
|
} catch (error) {
|
|
350
877
|
return {
|
|
351
878
|
status: "uncertain",
|
|
@@ -358,33 +885,392 @@ function scanDelegationsForRecovery(directory) {
|
|
|
358
885
|
reason: `background delegation ledger changed beyond the ${MAX_RECOVERY_LEDGER_BYTES}-byte recovery bound`
|
|
359
886
|
};
|
|
360
887
|
}
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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;
|
|
368
1131
|
continue;
|
|
369
|
-
let parsedJson;
|
|
370
|
-
try {
|
|
371
|
-
parsedJson = JSON.parse(trimmed);
|
|
372
|
-
} catch {
|
|
373
|
-
return {
|
|
374
|
-
status: "uncertain",
|
|
375
|
-
reason: `background delegation ledger has malformed JSON at line ${lineNumber}`
|
|
376
|
-
};
|
|
377
1132
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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}`);
|
|
384
1245
|
}
|
|
385
|
-
|
|
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
|
+
};
|
|
386
1271
|
}
|
|
387
|
-
|
|
1272
|
+
const source = load.mode === "legacy" ? "legacy-ledger" : load.mode;
|
|
1273
|
+
return { status: "ok", owners: [...load.records.values()], source };
|
|
388
1274
|
}
|
|
389
1275
|
function findByCorrelationId(directory, correlationId) {
|
|
390
1276
|
if (!correlationId)
|
|
@@ -397,7 +1283,7 @@ function findByCorrelationId(directory, correlationId) {
|
|
|
397
1283
|
}
|
|
398
1284
|
function appendRecord(directory, record) {
|
|
399
1285
|
ensureSwarmDir(directory);
|
|
400
|
-
|
|
1286
|
+
fs2.appendFileSync(storePath(directory), `${JSON.stringify(record)}
|
|
401
1287
|
`, "utf-8");
|
|
402
1288
|
}
|
|
403
1289
|
function buildPendingRecord(input, now) {
|
|
@@ -430,7 +1316,7 @@ function buildPendingRecord(input, now) {
|
|
|
430
1316
|
...input.generation !== undefined ? { generation: input.generation } : {}
|
|
431
1317
|
};
|
|
432
1318
|
}
|
|
433
|
-
function pendingLaunchIdentity(record) {
|
|
1319
|
+
function pendingLaunchIdentity(record, summaryAware) {
|
|
434
1320
|
return {
|
|
435
1321
|
correlationId: record.correlationId,
|
|
436
1322
|
jobId: record.jobId,
|
|
@@ -448,16 +1334,19 @@ function pendingLaunchIdentity(record) {
|
|
|
448
1334
|
ownedWorkflowLanes: record.ownedWorkflowLanes,
|
|
449
1335
|
promptHash: record.promptHash,
|
|
450
1336
|
workspace: record.workspace,
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
+
}
|
|
457
1345
|
};
|
|
458
1346
|
}
|
|
459
1347
|
function samePendingLaunchIdentity(left, right) {
|
|
460
|
-
|
|
1348
|
+
const summaryAware = isTerminal(left.status) && left.prompt === undefined && left.taskChangeContext === undefined;
|
|
1349
|
+
return isDeepStrictEqual(pendingLaunchIdentity(left, summaryAware), pendingLaunchIdentity(right, summaryAware));
|
|
461
1350
|
}
|
|
462
1351
|
async function recordPendingDelegationDetailed(directory, input, options = {}) {
|
|
463
1352
|
const now = Date.now();
|
|
@@ -476,7 +1365,10 @@ async function recordPendingDelegationDetailed(directory, input, options = {}) {
|
|
|
476
1365
|
if (options.staleTimeoutMs && options.staleTimeoutMs > 0) {
|
|
477
1366
|
sweepStaleLocked(directory, options.staleTimeoutMs, now);
|
|
478
1367
|
}
|
|
479
|
-
const
|
|
1368
|
+
const records = loadRecordsForWrite(directory);
|
|
1369
|
+
if (records === null)
|
|
1370
|
+
return;
|
|
1371
|
+
const existing = findRecordForWrite(records, parsedRecord.data.correlationId);
|
|
480
1372
|
if (existing) {
|
|
481
1373
|
outcome = {
|
|
482
1374
|
status: samePendingLaunchIdentity(existing, parsedRecord.data) ? "duplicate" : "conflict",
|
|
@@ -485,6 +1377,7 @@ async function recordPendingDelegationDetailed(directory, input, options = {}) {
|
|
|
485
1377
|
return;
|
|
486
1378
|
}
|
|
487
1379
|
appendRecord(directory, parsedRecord.data);
|
|
1380
|
+
maybeCompactDelegationsLocked(directory);
|
|
488
1381
|
outcome = { status: "recorded", record: parsedRecord.data };
|
|
489
1382
|
});
|
|
490
1383
|
return outcome;
|
|
@@ -513,7 +1406,10 @@ async function appendDelegationTransition(directory, correlationId, transition)
|
|
|
513
1406
|
try {
|
|
514
1407
|
let next = null;
|
|
515
1408
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
516
|
-
const
|
|
1409
|
+
const records = loadRecordsForWrite(directory);
|
|
1410
|
+
if (records === null)
|
|
1411
|
+
return;
|
|
1412
|
+
const current = findRecordForWrite(records, correlationId);
|
|
517
1413
|
if (!current)
|
|
518
1414
|
return;
|
|
519
1415
|
if (isTerminal(current.status) && transition.status !== "consumed" && transition.status !== "ingestion_error" && !(current.status === "completed" && transition.status === "stale")) {
|
|
@@ -529,6 +1425,7 @@ async function appendDelegationTransition(directory, correlationId, transition)
|
|
|
529
1425
|
...transition.result ? { result: transition.result } : {}
|
|
530
1426
|
};
|
|
531
1427
|
appendRecord(directory, next);
|
|
1428
|
+
maybeCompactDelegationsLocked(directory);
|
|
532
1429
|
});
|
|
533
1430
|
return next;
|
|
534
1431
|
} catch (err) {
|
|
@@ -549,7 +1446,10 @@ function sameJson(left, right) {
|
|
|
549
1446
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
550
1447
|
}
|
|
551
1448
|
function sameTerminalEvent(left, right) {
|
|
552
|
-
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);
|
|
553
1453
|
}
|
|
554
1454
|
function settlementProvenanceFor(record) {
|
|
555
1455
|
const baseline = record.taskChangeContext?.baseline;
|
|
@@ -582,12 +1482,16 @@ async function claimTerminalResult(directory, correlationId, terminalResult) {
|
|
|
582
1482
|
let claim = null;
|
|
583
1483
|
try {
|
|
584
1484
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
585
|
-
const
|
|
1485
|
+
const records = loadRecordsForWrite(directory);
|
|
1486
|
+
if (records === null)
|
|
1487
|
+
return;
|
|
1488
|
+
const current = findRecordForWrite(records, correlationId);
|
|
586
1489
|
if (!current)
|
|
587
1490
|
return;
|
|
588
1491
|
if (current.terminalResult) {
|
|
589
1492
|
if (!sameTerminalEvent(current.terminalResult, parsedTerminal.data)) {
|
|
590
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);
|
|
591
1495
|
return;
|
|
592
1496
|
}
|
|
593
1497
|
claim = {
|
|
@@ -611,6 +1515,7 @@ async function claimTerminalResult(directory, correlationId, terminalResult) {
|
|
|
611
1515
|
};
|
|
612
1516
|
}
|
|
613
1517
|
}
|
|
1518
|
+
const foldUpdatedAt = Math.max(parsedTerminal.data.recordedAt, current.updatedAt);
|
|
614
1519
|
const next = {
|
|
615
1520
|
...current,
|
|
616
1521
|
schemaVersion: 3,
|
|
@@ -618,12 +1523,13 @@ async function claimTerminalResult(directory, correlationId, terminalResult) {
|
|
|
618
1523
|
terminalResult: parsedTerminal.data,
|
|
619
1524
|
result: parsedTerminal.data.result,
|
|
620
1525
|
completedAt: parsedTerminal.data.recordedAt,
|
|
621
|
-
updatedAt:
|
|
1526
|
+
updatedAt: foldUpdatedAt,
|
|
622
1527
|
...coderSettlement ? { coderSettlement } : {}
|
|
623
1528
|
};
|
|
624
1529
|
if (!RecordSchema.safeParse(next).success)
|
|
625
1530
|
return;
|
|
626
1531
|
appendRecord(directory, next);
|
|
1532
|
+
maybeCompactDelegationsLocked(directory);
|
|
627
1533
|
claim = { disposition: "claimed", record: next };
|
|
628
1534
|
});
|
|
629
1535
|
return claim;
|
|
@@ -632,13 +1538,30 @@ async function claimTerminalResult(directory, correlationId, terminalResult) {
|
|
|
632
1538
|
return null;
|
|
633
1539
|
}
|
|
634
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
|
+
}
|
|
635
1555
|
async function claimCoderSettlement(directory, correlationId, operationId, input = {}) {
|
|
636
1556
|
if (!correlationId || !operationId || operationId.length > 256)
|
|
637
1557
|
return null;
|
|
638
1558
|
let claim = null;
|
|
639
1559
|
try {
|
|
640
1560
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
641
|
-
const
|
|
1561
|
+
const records = loadRecordsForWrite(directory);
|
|
1562
|
+
if (records === null)
|
|
1563
|
+
return;
|
|
1564
|
+
const current = findRecordForWrite(records, correlationId);
|
|
642
1565
|
if (!current || current.normalizedAgent !== "coder" || current.terminalResult?.status !== "completed") {
|
|
643
1566
|
return;
|
|
644
1567
|
}
|
|
@@ -684,6 +1607,7 @@ async function claimCoderSettlement(directory, correlationId, operationId, input
|
|
|
684
1607
|
if (!RecordSchema.safeParse(next).success)
|
|
685
1608
|
return;
|
|
686
1609
|
appendRecord(directory, next);
|
|
1610
|
+
maybeCompactDelegationsLocked(directory);
|
|
687
1611
|
claim = {
|
|
688
1612
|
disposition: existing.state === "settling" ? "resume" : "claimed",
|
|
689
1613
|
record: next
|
|
@@ -699,10 +1623,10 @@ function normalizeObservedFile(value) {
|
|
|
699
1623
|
if (typeof value !== "string")
|
|
700
1624
|
return null;
|
|
701
1625
|
const trimmed = value.trim().replace(/\\/g, "/");
|
|
702
|
-
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)) {
|
|
703
1627
|
return null;
|
|
704
1628
|
}
|
|
705
|
-
const normalized =
|
|
1629
|
+
const normalized = path2.posix.normalize(trimmed).replace(/^\.\//, "");
|
|
706
1630
|
if (normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized.endsWith("/")) {
|
|
707
1631
|
return null;
|
|
708
1632
|
}
|
|
@@ -727,7 +1651,10 @@ async function updateCoderSettlement(directory, correlationId, input) {
|
|
|
727
1651
|
let result = null;
|
|
728
1652
|
try {
|
|
729
1653
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
730
|
-
const
|
|
1654
|
+
const records = loadRecordsForWrite(directory);
|
|
1655
|
+
if (records === null)
|
|
1656
|
+
return;
|
|
1657
|
+
const current = findRecordForWrite(records, correlationId);
|
|
731
1658
|
const existing = current?.coderSettlement;
|
|
732
1659
|
if (!current || !existing)
|
|
733
1660
|
return;
|
|
@@ -788,6 +1715,7 @@ async function updateCoderSettlement(directory, correlationId, input) {
|
|
|
788
1715
|
if (!RecordSchema.safeParse(next).success)
|
|
789
1716
|
return;
|
|
790
1717
|
appendRecord(directory, next);
|
|
1718
|
+
maybeCompactDelegationsLocked(directory);
|
|
791
1719
|
result = next;
|
|
792
1720
|
});
|
|
793
1721
|
return result;
|
|
@@ -804,7 +1732,10 @@ async function claimDelegationIngestion(directory, correlationId, options) {
|
|
|
804
1732
|
let claim = null;
|
|
805
1733
|
try {
|
|
806
1734
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
807
|
-
const
|
|
1735
|
+
const records = loadRecordsForWrite(directory);
|
|
1736
|
+
if (records === null)
|
|
1737
|
+
return;
|
|
1738
|
+
const current = findRecordForWrite(records, correlationId);
|
|
808
1739
|
if (!current?.terminalResult)
|
|
809
1740
|
return;
|
|
810
1741
|
if (current.status === "consumed") {
|
|
@@ -843,6 +1774,7 @@ async function claimDelegationIngestion(directory, correlationId, options) {
|
|
|
843
1774
|
updatedAt: now
|
|
844
1775
|
};
|
|
845
1776
|
appendRecord(directory, next);
|
|
1777
|
+
maybeCompactDelegationsLocked(directory);
|
|
846
1778
|
claim = { disposition, record: next };
|
|
847
1779
|
});
|
|
848
1780
|
return claim;
|
|
@@ -857,7 +1789,10 @@ async function recordDelegationIngestionResult(directory, correlationId, claimTo
|
|
|
857
1789
|
let result = null;
|
|
858
1790
|
try {
|
|
859
1791
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
860
|
-
const
|
|
1792
|
+
const records = loadRecordsForWrite(directory);
|
|
1793
|
+
if (records === null)
|
|
1794
|
+
return;
|
|
1795
|
+
const current = findRecordForWrite(records, correlationId);
|
|
861
1796
|
if (!current)
|
|
862
1797
|
return;
|
|
863
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) {
|
|
@@ -880,6 +1815,7 @@ async function recordDelegationIngestionResult(directory, correlationId, claimTo
|
|
|
880
1815
|
updatedAt
|
|
881
1816
|
};
|
|
882
1817
|
appendRecord(directory, next);
|
|
1818
|
+
maybeCompactDelegationsLocked(directory);
|
|
883
1819
|
result = next;
|
|
884
1820
|
});
|
|
885
1821
|
return result;
|
|
@@ -902,7 +1838,10 @@ async function putPendingBackgroundAdvisory(directory, correlationId, input) {
|
|
|
902
1838
|
let result = null;
|
|
903
1839
|
try {
|
|
904
1840
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
905
|
-
const
|
|
1841
|
+
const records = loadRecordsForWrite(directory);
|
|
1842
|
+
if (records === null)
|
|
1843
|
+
return;
|
|
1844
|
+
const current = findRecordForWrite(records, correlationId);
|
|
906
1845
|
if (!current?.terminalResult || current.terminalResult.eventId !== parsed.data.eventId || current.parentSessionId !== parsed.data.parentSessionId) {
|
|
907
1846
|
return;
|
|
908
1847
|
}
|
|
@@ -919,6 +1858,7 @@ async function putPendingBackgroundAdvisory(directory, correlationId, input) {
|
|
|
919
1858
|
updatedAt: createdAt
|
|
920
1859
|
};
|
|
921
1860
|
appendRecord(directory, next);
|
|
1861
|
+
maybeCompactDelegationsLocked(directory);
|
|
922
1862
|
result = parsed.data;
|
|
923
1863
|
});
|
|
924
1864
|
return result;
|
|
@@ -935,7 +1875,10 @@ async function preparePendingBackgroundAdvisories(directory, parentSessionId, op
|
|
|
935
1875
|
const prepared = [];
|
|
936
1876
|
try {
|
|
937
1877
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
938
|
-
|
|
1878
|
+
const records = loadRecordsForWrite(directory);
|
|
1879
|
+
if (records === null)
|
|
1880
|
+
return;
|
|
1881
|
+
for (const current of records) {
|
|
939
1882
|
if (prepared.length >= 1)
|
|
940
1883
|
break;
|
|
941
1884
|
const advisory = current.advisoryInbox;
|
|
@@ -960,6 +1903,7 @@ async function preparePendingBackgroundAdvisories(directory, parentSessionId, op
|
|
|
960
1903
|
updatedAt: now
|
|
961
1904
|
};
|
|
962
1905
|
appendRecord(directory, next);
|
|
1906
|
+
maybeCompactDelegationsLocked(directory);
|
|
963
1907
|
prepared.push(nextAdvisory);
|
|
964
1908
|
}
|
|
965
1909
|
});
|
|
@@ -976,8 +1920,11 @@ async function releaseBackgroundAdvisoryPreparation(directory, parentSessionId,
|
|
|
976
1920
|
}
|
|
977
1921
|
try {
|
|
978
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;
|
|
979
1926
|
const byEvent = new Map;
|
|
980
|
-
for (const current of
|
|
1927
|
+
for (const current of records) {
|
|
981
1928
|
const advisory = current.advisoryInbox;
|
|
982
1929
|
if (advisory?.parentSessionId === parentSessionId && uniqueEventIds.includes(advisory.eventId)) {
|
|
983
1930
|
byEvent.set(advisory.eventId, current);
|
|
@@ -1005,6 +1952,7 @@ async function releaseBackgroundAdvisoryPreparation(directory, parentSessionId,
|
|
|
1005
1952
|
};
|
|
1006
1953
|
appendRecord(directory, next);
|
|
1007
1954
|
}
|
|
1955
|
+
maybeCompactDelegationsLocked(directory);
|
|
1008
1956
|
return true;
|
|
1009
1957
|
});
|
|
1010
1958
|
} catch (err) {
|
|
@@ -1018,8 +1966,11 @@ async function acknowledgeObservedBackgroundAdvisories(directory, parentSessionI
|
|
|
1018
1966
|
let acknowledged = 0;
|
|
1019
1967
|
try {
|
|
1020
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;
|
|
1021
1972
|
const now = Date.now();
|
|
1022
|
-
for (const current of
|
|
1973
|
+
for (const current of records) {
|
|
1023
1974
|
const advisory = current.advisoryInbox;
|
|
1024
1975
|
if (!advisory || advisory.parentSessionId !== parentSessionId || advisory.state !== "pending" || !observedTexts.some((text) => {
|
|
1025
1976
|
const advisoryBlock = text.match(/\[ADVISORIES\]([\s\S]*?)\[\/ADVISORIES\]/);
|
|
@@ -1046,6 +1997,8 @@ async function acknowledgeObservedBackgroundAdvisories(directory, parentSessionI
|
|
|
1046
1997
|
appendRecord(directory, next);
|
|
1047
1998
|
acknowledged += 1;
|
|
1048
1999
|
}
|
|
2000
|
+
if (acknowledged > 0)
|
|
2001
|
+
maybeCompactDelegationsLocked(directory);
|
|
1049
2002
|
});
|
|
1050
2003
|
return acknowledged;
|
|
1051
2004
|
} catch (err) {
|
|
@@ -1069,7 +2022,10 @@ function isTerminal(status) {
|
|
|
1069
2022
|
}
|
|
1070
2023
|
function sweepStaleLocked(directory, timeoutMs, now, statuses = DEFAULT_SWEEPABLE_DELEGATION_STATUSES) {
|
|
1071
2024
|
let swept = 0;
|
|
1072
|
-
|
|
2025
|
+
const records = loadRecordsForWrite(directory);
|
|
2026
|
+
if (records === null)
|
|
2027
|
+
return 0;
|
|
2028
|
+
for (const record of records) {
|
|
1073
2029
|
if (!statuses.has(record.status))
|
|
1074
2030
|
continue;
|
|
1075
2031
|
if (now - record.updatedAt <= timeoutMs)
|
|
@@ -1081,6 +2037,8 @@ function sweepStaleLocked(directory, timeoutMs, now, statuses = DEFAULT_SWEEPABL
|
|
|
1081
2037
|
});
|
|
1082
2038
|
swept += 1;
|
|
1083
2039
|
}
|
|
2040
|
+
if (swept > 0)
|
|
2041
|
+
maybeCompactDelegationsLocked(directory);
|
|
1084
2042
|
return swept;
|
|
1085
2043
|
}
|
|
1086
2044
|
async function sweepStaleDelegations(directory, timeoutMs, options = {}) {
|
|
@@ -1096,18 +2054,18 @@ async function sweepStaleDelegations(directory, timeoutMs, options = {}) {
|
|
|
1096
2054
|
}
|
|
1097
2055
|
function fallbackRelativePath(correlationId) {
|
|
1098
2056
|
const digest = createHash("sha256").update(correlationId).digest("hex");
|
|
1099
|
-
return
|
|
2057
|
+
return path2.join(BACKGROUND_DELEGATION_FALLBACK_DIR, `${digest}.json`);
|
|
1100
2058
|
}
|
|
1101
2059
|
function fallbackPath(directory, correlationId) {
|
|
1102
2060
|
return validateSwarmPath(directory, fallbackRelativePath(correlationId));
|
|
1103
2061
|
}
|
|
1104
2062
|
function fallbackDirectoryPath(directory) {
|
|
1105
|
-
return
|
|
2063
|
+
return path2.dirname(validateSwarmPath(directory, path2.join(BACKGROUND_DELEGATION_FALLBACK_DIR, ".containment-anchor")));
|
|
1106
2064
|
}
|
|
1107
2065
|
async function readFallbackFile(directory, correlationId) {
|
|
1108
2066
|
for (let attempt = 0;attempt < 5; attempt++) {
|
|
1109
2067
|
try {
|
|
1110
|
-
const raw = await
|
|
2068
|
+
const raw = await fs2.promises.readFile(fallbackPath(directory, correlationId), "utf-8");
|
|
1111
2069
|
const parsedJson = JSON.parse(raw);
|
|
1112
2070
|
const parsed = FallbackArtifactSchema.safeParse(parsedJson);
|
|
1113
2071
|
if (!parsed.success || parsed.data.correlationId !== correlationId) {
|
|
@@ -1117,7 +2075,7 @@ async function readFallbackFile(directory, correlationId) {
|
|
|
1117
2075
|
} catch (err) {
|
|
1118
2076
|
const code = err.code;
|
|
1119
2077
|
if (code === "ENOENT" && attempt < 4) {
|
|
1120
|
-
await new Promise((
|
|
2078
|
+
await new Promise((resolve3) => setTimeout(resolve3, 10));
|
|
1121
2079
|
continue;
|
|
1122
2080
|
}
|
|
1123
2081
|
return null;
|
|
@@ -1133,7 +2091,7 @@ async function readDelegationFallback(directory, correlationId) {
|
|
|
1133
2091
|
async function listDelegationFallbacks(directory) {
|
|
1134
2092
|
let entries;
|
|
1135
2093
|
try {
|
|
1136
|
-
entries =
|
|
2094
|
+
entries = fs2.readdirSync(fallbackDirectoryPath(directory), {
|
|
1137
2095
|
withFileTypes: true
|
|
1138
2096
|
}).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).slice(0, MAX_LIVE_BACKGROUND_FALLBACKS);
|
|
1139
2097
|
} catch {
|
|
@@ -1142,7 +2100,7 @@ async function listDelegationFallbacks(directory) {
|
|
|
1142
2100
|
const artifacts = [];
|
|
1143
2101
|
for (const entry of entries) {
|
|
1144
2102
|
try {
|
|
1145
|
-
const raw = await
|
|
2103
|
+
const raw = await fs2.promises.readFile(path2.join(fallbackDirectoryPath(directory), entry.name), "utf-8");
|
|
1146
2104
|
const parsed = FallbackArtifactSchema.safeParse(JSON.parse(raw));
|
|
1147
2105
|
if (parsed.success)
|
|
1148
2106
|
artifacts.push(parsed.data);
|
|
@@ -1155,7 +2113,7 @@ async function scanDelegationFallbacksForRecovery(directory) {
|
|
|
1155
2113
|
let entries;
|
|
1156
2114
|
try {
|
|
1157
2115
|
fallbackDir = fallbackDirectoryPath(directory);
|
|
1158
|
-
entries =
|
|
2116
|
+
entries = fs2.readdirSync(fallbackDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json"));
|
|
1159
2117
|
} catch (error) {
|
|
1160
2118
|
if (error.code === "ENOENT") {
|
|
1161
2119
|
return { status: "ok", owners: [] };
|
|
@@ -1173,17 +2131,17 @@ async function scanDelegationFallbacksForRecovery(directory) {
|
|
|
1173
2131
|
}
|
|
1174
2132
|
const artifacts = [];
|
|
1175
2133
|
for (const entry of entries) {
|
|
1176
|
-
const artifactPath =
|
|
2134
|
+
const artifactPath = path2.join(fallbackDir, entry.name);
|
|
1177
2135
|
let raw;
|
|
1178
2136
|
try {
|
|
1179
|
-
const stat =
|
|
2137
|
+
const stat = fs2.statSync(artifactPath);
|
|
1180
2138
|
if (stat.size > MAX_RECOVERY_FALLBACK_BYTES) {
|
|
1181
2139
|
return {
|
|
1182
2140
|
status: "uncertain",
|
|
1183
2141
|
reason: `background fallback artifact "${entry.name}" exceeds the recovery size bound`
|
|
1184
2142
|
};
|
|
1185
2143
|
}
|
|
1186
|
-
raw = await
|
|
2144
|
+
raw = await fs2.promises.readFile(artifactPath, "utf-8");
|
|
1187
2145
|
} catch (error) {
|
|
1188
2146
|
return {
|
|
1189
2147
|
status: "uncertain",
|
|
@@ -1218,7 +2176,7 @@ async function scanDelegationFallbacksForRecovery(directory) {
|
|
|
1218
2176
|
}
|
|
1219
2177
|
function countFallbackFiles(directory) {
|
|
1220
2178
|
try {
|
|
1221
|
-
return
|
|
2179
|
+
return fs2.readdirSync(fallbackDirectoryPath(directory), {
|
|
1222
2180
|
withFileTypes: true
|
|
1223
2181
|
}).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).length;
|
|
1224
2182
|
} catch (err) {
|
|
@@ -1253,7 +2211,7 @@ async function writeDelegationFallback(directory, input, options = {}) {
|
|
|
1253
2211
|
if (countFallbackFiles(directory) >= maxLive)
|
|
1254
2212
|
return null;
|
|
1255
2213
|
const absPath = fallbackPath(directory, input.correlationId);
|
|
1256
|
-
|
|
2214
|
+
fs2.mkdirSync(path2.dirname(absPath), { recursive: true });
|
|
1257
2215
|
await bunWrite(absPath, `${JSON.stringify(parsed.data)}
|
|
1258
2216
|
`);
|
|
1259
2217
|
return parsed.data;
|
|
@@ -1269,7 +2227,7 @@ async function removeDelegationFallback(directory, correlationId) {
|
|
|
1269
2227
|
try {
|
|
1270
2228
|
return await withEvidenceLock(directory, BACKGROUND_DELEGATION_FALLBACK_DIR, STORE_LOCK_AGENT, FALLBACK_LOCK_TASK, async () => {
|
|
1271
2229
|
try {
|
|
1272
|
-
await
|
|
2230
|
+
await fs2.promises.unlink(fallbackPath(directory, correlationId));
|
|
1273
2231
|
return true;
|
|
1274
2232
|
} catch (err) {
|
|
1275
2233
|
return err.code === "ENOENT";
|
|
@@ -1317,7 +2275,10 @@ async function promoteDelegationFallback(directory, correlationId) {
|
|
|
1317
2275
|
}
|
|
1318
2276
|
let promoted = null;
|
|
1319
2277
|
await withEvidenceLock(directory, BACKGROUND_DELEGATIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
|
|
1320
|
-
const
|
|
2278
|
+
const records = loadRecordsForWrite(directory);
|
|
2279
|
+
if (records === null)
|
|
2280
|
+
return;
|
|
2281
|
+
const current = findRecordForWrite(records, correlationId);
|
|
1321
2282
|
if (current) {
|
|
1322
2283
|
if (samePromotionIdentity(current, fallback.record)) {
|
|
1323
2284
|
promoted = current;
|
|
@@ -1325,12 +2286,13 @@ async function promoteDelegationFallback(directory, correlationId) {
|
|
|
1325
2286
|
return;
|
|
1326
2287
|
}
|
|
1327
2288
|
appendRecord(directory, fallback.record);
|
|
2289
|
+
maybeCompactDelegationsLocked(directory);
|
|
1328
2290
|
promoted = fallback.record;
|
|
1329
2291
|
});
|
|
1330
2292
|
if (!promoted)
|
|
1331
2293
|
return null;
|
|
1332
2294
|
try {
|
|
1333
|
-
await
|
|
2295
|
+
await fs2.promises.unlink(fallbackPath(directory, correlationId));
|
|
1334
2296
|
} catch (err) {
|
|
1335
2297
|
if (err.code !== "ENOENT") {
|
|
1336
2298
|
return null;
|
|
@@ -1356,14 +2318,14 @@ function scanBackgroundCoderReservationsForAdmission(directory) {
|
|
|
1356
2318
|
let raw;
|
|
1357
2319
|
try {
|
|
1358
2320
|
absolutePath = reservationStorePath(directory);
|
|
1359
|
-
const stat =
|
|
2321
|
+
const stat = fs2.statSync(absolutePath);
|
|
1360
2322
|
if (stat.size > MAX_BACKGROUND_CODER_RESERVATION_STORE_BYTES) {
|
|
1361
2323
|
return {
|
|
1362
2324
|
status: "uncertain",
|
|
1363
2325
|
reason: `background coder reservation store exceeds the ${MAX_BACKGROUND_CODER_RESERVATION_STORE_BYTES}-byte safety bound`
|
|
1364
2326
|
};
|
|
1365
2327
|
}
|
|
1366
|
-
raw =
|
|
2328
|
+
raw = fs2.readFileSync(absolutePath, "utf-8");
|
|
1367
2329
|
} catch (error) {
|
|
1368
2330
|
if (error.code === "ENOENT") {
|
|
1369
2331
|
return { status: "ok", reservations: [] };
|
|
@@ -1677,4 +2639,4 @@ async function releaseBackgroundCoderReservation(directory, input) {
|
|
|
1677
2639
|
}
|
|
1678
2640
|
}
|
|
1679
2641
|
|
|
1680
|
-
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, DEFAULT_STALE_DELEGATION_TIMEOUT_MS, DEFAULT_SWEEPABLE_DELEGATION_STATUSES, 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 };
|