opencode-swarm 7.148.5 → 7.149.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/background/candidate-contract.d.ts +1 -1
- package/dist/background/pending-delegations.d.ts +6 -0
- package/dist/background/pr-review-contract.d.ts +20 -0
- package/dist/cli/{config-doctor-6z7x0jnz.js → config-doctor-d3nsq1nz.js} +3 -3
- package/dist/cli/{core-0ph6pb2y.js → core-y9d858n6.js} +3 -3
- package/dist/cli/{curation-policy-fbvctttr.js → curation-policy-nck9g765.js} +6 -6
- package/dist/cli/{curator-pffqw3br.js → curator-f5m0hh2p.js} +32 -31
- package/dist/cli/{curator-llm-factory-mbsn281q.js → curator-llm-factory-mzh9ns5s.js} +32 -31
- package/dist/cli/{evidence-summary-service-9ajw5tpc.js → evidence-summary-service-601x7ne7.js} +15 -14
- package/dist/cli/{gate-evidence-wc8wzwaw.js → gate-evidence-kjs9vsx9.js} +9 -9
- package/dist/cli/{guardrail-explain-a8706sq1.js → guardrail-explain-j291sss5.js} +33 -32
- package/dist/cli/{guardrail-log-wx43bj32.js → guardrail-log-1jykfrqc.js} +6 -4
- package/dist/cli/{guardrail-reset-sy0vztrd.js → guardrail-reset-tr07m077.js} +32 -31
- package/dist/cli/{hive-promoter-jvyzykgx.js → hive-promoter-162ws6xp.js} +32 -31
- package/dist/cli/{index-x3d9asnz.js → index-1aztaa2s.js} +3 -3
- package/dist/cli/{index-c6jqkx9d.js → index-38ema5k2.js} +2 -2
- package/dist/cli/{index-nq4tkfpc.js → index-4sdhbxgq.js} +1 -1
- package/dist/cli/{index-vyg5b8ee.js → index-5gntxwxt.js} +5 -5
- package/dist/cli/{index-0yzt06r5.js → index-64d17yd0.js} +2 -2
- package/dist/cli/{index-3aev3a0c.js → index-7m8rk72d.js} +491 -369
- package/dist/cli/{index-vhefvcta.js → index-apwcvfjt.js} +2 -2
- package/dist/cli/{index-arjq717j.js → index-cna8m7my.js} +1 -1
- package/dist/cli/{index-59wqcatm.js → index-cqhf7fkw.js} +1 -1
- package/dist/cli/{index-jzz52as5.js → index-dkrz1vv5.js} +4 -4
- package/dist/cli/{index-kg4knw6r.js → index-dtmgt2xn.js} +11 -10
- package/dist/cli/{index-579qg534.js → index-eaq9b0qm.js} +1 -1
- package/dist/cli/{index-zhn0q7nf.js → index-ekvs3ade.js} +18 -17
- package/dist/cli/index-exn0ztsv.js +881 -0
- package/dist/cli/{index-ag9bv589.js → index-f53yyerg.js} +34 -33
- package/dist/cli/{index-q7ke7kwd.js → index-fc4x86sy.js} +9 -12
- package/dist/cli/{index-zf2vwa7y.js → index-hxbrsb40.js} +2 -2
- package/dist/cli/{index-k1s8gxjh.js → index-jb9tb1sd.js} +1 -1
- package/dist/cli/{index-haf092ak.js → index-jg2gwzvq.js} +11 -11
- package/dist/cli/{index-qwmrt51t.js → index-k2sm9677.js} +1 -1
- package/dist/cli/{index-5r2mcgpa.js → index-kgk65rde.js} +1 -1
- package/dist/cli/{index-vrp2zva8.js → index-m8dn0qnm.js} +1 -1
- package/dist/cli/{index-1w5nnyew.js → index-mdwtj4xm.js} +6 -6
- package/dist/cli/{index-j382eh60.js → index-n99s022c.js} +1 -1
- package/dist/cli/{index-yezd5ks6.js → index-pf22x0dr.js} +3 -1
- package/dist/cli/{index-8djgxw2w.js → index-prxrmc3b.js} +2 -2
- package/dist/cli/{index-kj6m9ry3.js → index-srwp8fs7.js} +4 -4
- package/dist/cli/{index-5zq3892p.js → index-t994f8fp.js} +4 -4
- package/dist/cli/{index-t25evmna.js → index-vy6q9d8j.js} +3 -2
- package/dist/cli/{index-ne45ew6z.js → index-xe83cwvn.js} +10 -10
- package/dist/cli/{index-ghbkknjz.js → index-xggpmtfx.js} +73 -42
- package/dist/cli/{index-qbsc2btq.js → index-yjj3s8af.js} +3 -3
- package/dist/cli/index.js +32 -31
- package/dist/cli/{knowledge-escalator-bvrtfdyb.js → knowledge-escalator-whkxxgxj.js} +14 -14
- package/dist/cli/{knowledge-events-rpvkevbm.js → knowledge-events-rnqzt87h.js} +12 -12
- package/dist/cli/{knowledge-link-spw879rs.js → knowledge-link-mzn8x0qd.js} +5 -5
- package/dist/cli/{knowledge-store-wjy2kshh.js → knowledge-store-7de59rnz.js} +6 -6
- package/dist/cli/{knowledge-validator-cq2cgfcx.js → knowledge-validator-bpk17jf4.js} +8 -8
- package/dist/cli/{pending-delegations-f4wvyzbf.js → pending-delegations-qzy6rnvw.js} +3 -3
- package/dist/cli/{pr-subscriptions-f4xw1rxy.js → pr-subscriptions-q862pvz1.js} +3 -3
- package/dist/cli/{runner-rk26nk58.js → runner-1aygs98c.js} +6 -6
- package/dist/cli/{scan-cursor-7n0zs72g.js → scan-cursor-0p51fh61.js} +7 -7
- package/dist/cli/{schema-e59spg93.js → schema-8g2nxhjf.js} +1 -1
- package/dist/cli/{scope-persistence-d75p900m.js → scope-persistence-hhkr0c46.js} +8 -10
- package/dist/cli/{skill-generator-c65y1h6a.js → skill-generator-pk9gcz7r.js} +16 -16
- package/dist/cli/{telemetry-1pnsftp0.js → telemetry-5deacb7w.js} +1 -1
- package/dist/cli/{workspace-snapshot-nq25j94x.js → workspace-snapshot-8269e1p9.js} +2 -2
- package/dist/cli/{worktree-collision-ownership-wqy7mgb3.js → worktree-collision-ownership-9v8e0dz7.js} +8 -8
- package/dist/cli/{worktree-isolation-ay9d58w4.js → worktree-isolation-1hwwfbdw.js} +32 -31
- package/dist/commands/close.d.ts +1 -0
- package/dist/events/core-events.d.ts +250 -0
- package/dist/failures/invocation-failure.d.ts +7 -0
- package/dist/full-auto/oversight.d.ts +5 -3
- package/dist/hooks/full-auto-delegation.d.ts +24 -0
- package/dist/hooks/full-auto-input-probe.d.ts +11 -0
- package/dist/hooks/pr-workflow-gate.d.ts +37 -6
- package/dist/hooks/steering-consumed.d.ts +12 -4
- package/dist/index.js +598 -613
- package/dist/observability/catalog.d.ts +9 -7
- package/dist/services/session-reflection.d.ts +7 -0
- package/dist/services/spec-drift-recovery.d.ts +9 -2
- package/dist/state.d.ts +10 -0
- package/dist/telemetry.d.ts +24 -1
- package/dist/tools/dispatch-lanes.d.ts +50 -1
- package/dist/tools/write-pr-review-artifact.d.ts +2 -0
- package/package.json +3 -1
- package/dist/cli/{curator-drift-fv4rttf4.js → curator-drift-2h1d2fnq.js} +1 -1
- package/dist/cli/{index-g8wwj0sz.js → index-vnn7qq8c.js} +3 -3
|
@@ -0,0 +1,881 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
telemetry
|
|
4
|
+
} from "./index-xggpmtfx.js";
|
|
5
|
+
import {
|
|
6
|
+
init_logger,
|
|
7
|
+
warn
|
|
8
|
+
} from "./index-zgwm4ryv.js";
|
|
9
|
+
|
|
10
|
+
// src/events/core-events.ts
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
init_logger();
|
|
14
|
+
var CORE_EVENT_LIMITS = {
|
|
15
|
+
activeMaxBytes: 2 * 1024 * 1024,
|
|
16
|
+
activeMaxEntries: 20000,
|
|
17
|
+
ageMaxMs: 7 * 24 * 60 * 60 * 1000,
|
|
18
|
+
compactMaxBytes: 512 * 1024,
|
|
19
|
+
readMaxBytes: 3 * 1024 * 1024,
|
|
20
|
+
checkInterval: 25,
|
|
21
|
+
warnCooldownMs: 60000,
|
|
22
|
+
headerMaxBytes: 64 * 1024,
|
|
23
|
+
authorityIndexMaxEntries: 20000,
|
|
24
|
+
maxLineBytes: 256 * 1024
|
|
25
|
+
};
|
|
26
|
+
var AUTHORITY_RETRY = "coder_retry_circuit_breaker";
|
|
27
|
+
var AUTHORITY_REPAIR = "task_workflow_repaired";
|
|
28
|
+
var AUTHORITY_DRIFT_ACK = "spec_drift_acknowledged";
|
|
29
|
+
var AUTHORITY_DRIFT_REPAIR = "spec_drift_repaired";
|
|
30
|
+
var RETRY_ACTIONS = new Set([
|
|
31
|
+
"sounding_board_consultation",
|
|
32
|
+
"simplification",
|
|
33
|
+
"user_escalation"
|
|
34
|
+
]);
|
|
35
|
+
function authorityKeyFor(event) {
|
|
36
|
+
switch (event.type) {
|
|
37
|
+
case AUTHORITY_RETRY: {
|
|
38
|
+
if (typeof event.taskId === "string" && typeof event.retryEpoch === "number" && Number.isFinite(event.retryEpoch) && typeof event.action === "string" && RETRY_ACTIONS.has(event.action)) {
|
|
39
|
+
return `retry|${event.taskId}|${event.retryEpoch}|${event.action}`;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
case AUTHORITY_REPAIR: {
|
|
44
|
+
if (typeof event.taskId === "string" && typeof event.transitionId === "string") {
|
|
45
|
+
return `repair|${event.taskId}|${event.transitionId}`;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
case AUTHORITY_DRIFT_ACK:
|
|
50
|
+
case AUTHORITY_DRIFT_REPAIR: {
|
|
51
|
+
if (typeof event.transitionId === "string") {
|
|
52
|
+
return `drift|${String(event.type)}|${event.transitionId}`;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
default:
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
var MANIFEST_TYPE = "swarm-events-manifest";
|
|
61
|
+
var MANIFEST_SCHEMA = 1;
|
|
62
|
+
var BY_TYPE_MAX_KEYS = 64;
|
|
63
|
+
var BY_TYPE_OTHER = "__other__";
|
|
64
|
+
function emptyFolded() {
|
|
65
|
+
return {
|
|
66
|
+
totalEvents: 0,
|
|
67
|
+
byType: {},
|
|
68
|
+
corrupt: 0,
|
|
69
|
+
dropped: 0,
|
|
70
|
+
oldestTimestamp: null,
|
|
71
|
+
newestTimestamp: null
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function emptyManifest() {
|
|
75
|
+
return {
|
|
76
|
+
v: 1,
|
|
77
|
+
type: MANIFEST_TYPE,
|
|
78
|
+
schemaVersion: MANIFEST_SCHEMA,
|
|
79
|
+
folded: emptyFolded(),
|
|
80
|
+
updatedAt: new Date().toISOString()
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function parseManifestLine(line) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (!trimmed)
|
|
86
|
+
return null;
|
|
87
|
+
try {
|
|
88
|
+
const obj = JSON.parse(trimmed);
|
|
89
|
+
if (typeof obj !== "object" || obj === null)
|
|
90
|
+
return null;
|
|
91
|
+
const rec = obj;
|
|
92
|
+
if (rec.type !== MANIFEST_TYPE || rec.v !== 1 || rec.schemaVersion !== MANIFEST_SCHEMA || typeof rec.folded !== "object" || rec.folded === null) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const f = rec.folded;
|
|
96
|
+
const manifest = emptyManifest();
|
|
97
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
98
|
+
manifest.folded.totalEvents = num(f.totalEvents);
|
|
99
|
+
manifest.folded.corrupt = num(f.corrupt);
|
|
100
|
+
manifest.folded.dropped = num(f.dropped);
|
|
101
|
+
if (typeof f.byType === "object" && f.byType !== null) {
|
|
102
|
+
for (const [k, v] of Object.entries(f.byType)) {
|
|
103
|
+
if (typeof v === "number" && Number.isFinite(v) && v >= 0) {
|
|
104
|
+
manifest.folded.byType[k] = v;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (typeof f.oldestTimestamp === "string") {
|
|
109
|
+
manifest.folded.oldestTimestamp = f.oldestTimestamp;
|
|
110
|
+
}
|
|
111
|
+
if (typeof f.newestTimestamp === "string") {
|
|
112
|
+
manifest.folded.newestTimestamp = f.newestTimestamp;
|
|
113
|
+
}
|
|
114
|
+
manifest.updatedAt = typeof rec.updatedAt === "string" ? rec.updatedAt : new Date().toISOString();
|
|
115
|
+
return manifest;
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
var _internals = {
|
|
121
|
+
appendFileSync: fs.appendFileSync,
|
|
122
|
+
readFileSync: fs.readFileSync,
|
|
123
|
+
existsSync: fs.existsSync,
|
|
124
|
+
mkdirSync: fs.mkdirSync,
|
|
125
|
+
statSync: fs.statSync,
|
|
126
|
+
renameSync: fs.renameSync,
|
|
127
|
+
writeFileSync: fs.writeFileSync,
|
|
128
|
+
unlinkSync: fs.unlinkSync,
|
|
129
|
+
openSync: fs.openSync,
|
|
130
|
+
closeSync: fs.closeSync,
|
|
131
|
+
readSync: fs.readSync,
|
|
132
|
+
now: () => Date.now(),
|
|
133
|
+
limits: CORE_EVENT_LIMITS,
|
|
134
|
+
emitHealth: emitCoreEventsHealth
|
|
135
|
+
};
|
|
136
|
+
function eventsFilePath(directory) {
|
|
137
|
+
return path.join(directory, ".swarm", "events.jsonl");
|
|
138
|
+
}
|
|
139
|
+
function lockFilePath(directory) {
|
|
140
|
+
return path.join(directory, ".swarm", "events.lock");
|
|
141
|
+
}
|
|
142
|
+
function authorityIndexPath(directory) {
|
|
143
|
+
return path.join(directory, ".swarm", "events-authority-index.json");
|
|
144
|
+
}
|
|
145
|
+
function tmpPathFor(finalPath) {
|
|
146
|
+
return `${finalPath}.${process.pid}.tmp`;
|
|
147
|
+
}
|
|
148
|
+
function readBoundedChunk(filePath, maxBytes, offset = 0) {
|
|
149
|
+
try {
|
|
150
|
+
if (!_internals.existsSync(filePath)) {
|
|
151
|
+
return { text: "", truncated: false };
|
|
152
|
+
}
|
|
153
|
+
const fd = _internals.openSync(filePath, "r");
|
|
154
|
+
try {
|
|
155
|
+
const size = _internals.statSync(filePath).size;
|
|
156
|
+
const readable = Math.max(0, size - offset);
|
|
157
|
+
const truncated = readable > maxBytes;
|
|
158
|
+
const len = Math.min(readable, maxBytes);
|
|
159
|
+
const buf = Buffer.alloc(len);
|
|
160
|
+
let read = 0;
|
|
161
|
+
while (read < len) {
|
|
162
|
+
const n = _internals.readSync(fd, buf, read, len - read, offset + read);
|
|
163
|
+
if (n <= 0)
|
|
164
|
+
break;
|
|
165
|
+
read += n;
|
|
166
|
+
}
|
|
167
|
+
return { text: buf.toString("utf-8", 0, read), truncated };
|
|
168
|
+
} finally {
|
|
169
|
+
_internals.closeSync(fd);
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
warnThrottled("bounded read failed (transient I/O)");
|
|
173
|
+
return { text: "", truncated: false };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function fileSizeOrZero(filePath) {
|
|
177
|
+
try {
|
|
178
|
+
if (!_internals.existsSync(filePath))
|
|
179
|
+
return 0;
|
|
180
|
+
return _internals.statSync(filePath).size;
|
|
181
|
+
} catch {
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function fileEndsWithNewline(filePath) {
|
|
186
|
+
try {
|
|
187
|
+
if (!_internals.existsSync(filePath))
|
|
188
|
+
return true;
|
|
189
|
+
const size = _internals.statSync(filePath).size;
|
|
190
|
+
if (size === 0)
|
|
191
|
+
return true;
|
|
192
|
+
const fd = _internals.openSync(filePath, "r");
|
|
193
|
+
try {
|
|
194
|
+
const buf = Buffer.alloc(1);
|
|
195
|
+
const n = _internals.readSync(fd, buf, 0, 1, size - 1);
|
|
196
|
+
return n <= 0 || buf[0] === 10;
|
|
197
|
+
} finally {
|
|
198
|
+
_internals.closeSync(fd);
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function eventDiscriminator(event) {
|
|
205
|
+
if (typeof event.type === "string" && event.type.length > 0) {
|
|
206
|
+
return event.type.slice(0, 64);
|
|
207
|
+
}
|
|
208
|
+
if (typeof event.event === "string" && event.event.length > 0) {
|
|
209
|
+
return event.event.slice(0, 64);
|
|
210
|
+
}
|
|
211
|
+
return "__untyped__";
|
|
212
|
+
}
|
|
213
|
+
function eventTimestamp(event) {
|
|
214
|
+
if (typeof event.timestamp !== "string")
|
|
215
|
+
return null;
|
|
216
|
+
const ts = Date.parse(event.timestamp);
|
|
217
|
+
return Number.isNaN(ts) ? null : event.timestamp;
|
|
218
|
+
}
|
|
219
|
+
function foldLineInto(folded, event) {
|
|
220
|
+
folded.totalEvents += 1;
|
|
221
|
+
const key = eventDiscriminator(event);
|
|
222
|
+
const keys = Object.keys(folded.byType);
|
|
223
|
+
if (folded.byType[key] !== undefined || keys.length < BY_TYPE_MAX_KEYS) {
|
|
224
|
+
folded.byType[key] = (folded.byType[key] ?? 0) + 1;
|
|
225
|
+
} else {
|
|
226
|
+
folded.byType[BY_TYPE_OTHER] = (folded.byType[BY_TYPE_OTHER] ?? 0) + 1;
|
|
227
|
+
}
|
|
228
|
+
const ts = eventTimestamp(event);
|
|
229
|
+
if (ts !== null) {
|
|
230
|
+
if (folded.oldestTimestamp === null || ts < folded.oldestTimestamp) {
|
|
231
|
+
folded.oldestTimestamp = ts;
|
|
232
|
+
}
|
|
233
|
+
if (folded.newestTimestamp === null || ts > folded.newestTimestamp) {
|
|
234
|
+
folded.newestTimestamp = ts;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function cloneFolded(folded) {
|
|
239
|
+
return {
|
|
240
|
+
totalEvents: folded.totalEvents,
|
|
241
|
+
byType: { ...folded.byType },
|
|
242
|
+
corrupt: folded.corrupt,
|
|
243
|
+
dropped: folded.dropped,
|
|
244
|
+
oldestTimestamp: folded.oldestTimestamp,
|
|
245
|
+
newestTimestamp: folded.newestTimestamp
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function emptyAuthorityIndex() {
|
|
249
|
+
return { version: 1, entries: {}, evicted: 0 };
|
|
250
|
+
}
|
|
251
|
+
function loadAuthorityIndex(directory) {
|
|
252
|
+
const indexPath = authorityIndexPath(directory);
|
|
253
|
+
try {
|
|
254
|
+
if (!_internals.existsSync(indexPath))
|
|
255
|
+
return { kind: "absent" };
|
|
256
|
+
const raw = _internals.readFileSync(indexPath, "utf-8");
|
|
257
|
+
const parsed = JSON.parse(raw);
|
|
258
|
+
if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.entries !== "object" || parsed.entries === null) {
|
|
259
|
+
return { kind: "corrupt" };
|
|
260
|
+
}
|
|
261
|
+
const entries = {};
|
|
262
|
+
for (const [k, v] of Object.entries(parsed.entries)) {
|
|
263
|
+
if (typeof v === "string")
|
|
264
|
+
entries[k] = v;
|
|
265
|
+
}
|
|
266
|
+
const evictedRaw = parsed.evicted;
|
|
267
|
+
return {
|
|
268
|
+
kind: "ok",
|
|
269
|
+
index: {
|
|
270
|
+
version: 1,
|
|
271
|
+
entries,
|
|
272
|
+
evicted: typeof evictedRaw === "number" && Number.isFinite(evictedRaw) && evictedRaw >= 0 ? evictedRaw : 0
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
} catch {
|
|
276
|
+
return { kind: "corrupt" };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function mergeAuthorityKeys(directory, state, keys) {
|
|
280
|
+
if (keys.length === 0)
|
|
281
|
+
return;
|
|
282
|
+
const base = state.kind === "ok" ? {
|
|
283
|
+
version: 1,
|
|
284
|
+
entries: { ...state.index.entries },
|
|
285
|
+
evicted: state.index.evicted
|
|
286
|
+
} : emptyAuthorityIndex();
|
|
287
|
+
const now = new Date().toISOString();
|
|
288
|
+
for (const key of keys)
|
|
289
|
+
base.entries[key] = now;
|
|
290
|
+
const limit = _internals.limits.authorityIndexMaxEntries;
|
|
291
|
+
const allKeys = Object.keys(base.entries);
|
|
292
|
+
if (allKeys.length > limit) {
|
|
293
|
+
allKeys.sort((a, b) => base.entries[a] <= base.entries[b] ? -1 : 1);
|
|
294
|
+
const excess = allKeys.length - limit;
|
|
295
|
+
for (let i = 0;i < excess; i += 1) {
|
|
296
|
+
delete base.entries[allKeys[i]];
|
|
297
|
+
}
|
|
298
|
+
base.evicted += excess;
|
|
299
|
+
}
|
|
300
|
+
authorityIndexAtomicReplace(directory, base);
|
|
301
|
+
}
|
|
302
|
+
function authorityIndexAtomicReplace(directory, index) {
|
|
303
|
+
const finalPath = authorityIndexPath(directory);
|
|
304
|
+
const tmpPath = tmpPathFor(finalPath);
|
|
305
|
+
try {
|
|
306
|
+
if (_internals.existsSync(tmpPath)) {
|
|
307
|
+
try {
|
|
308
|
+
_internals.unlinkSync(tmpPath);
|
|
309
|
+
} catch {}
|
|
310
|
+
}
|
|
311
|
+
_internals.writeFileSync(tmpPath, `${JSON.stringify(index)}
|
|
312
|
+
`, "utf-8");
|
|
313
|
+
renameSyncWithRetry(tmpPath, finalPath);
|
|
314
|
+
} catch {
|
|
315
|
+
try {
|
|
316
|
+
if (_internals.existsSync(tmpPath)) {
|
|
317
|
+
_internals.unlinkSync(tmpPath);
|
|
318
|
+
}
|
|
319
|
+
} catch {}
|
|
320
|
+
throw new Error("core-events authority index atomic replace failed");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function atomicReplace(directory, content) {
|
|
324
|
+
const lines = content.split(`
|
|
325
|
+
`);
|
|
326
|
+
if (lines.length < 2 || lines[lines.length - 1] !== "") {
|
|
327
|
+
throw new Error("core-events atomic replace failed (framing)");
|
|
328
|
+
}
|
|
329
|
+
if (parseManifestLine(lines[0] ?? "") === null) {
|
|
330
|
+
throw new Error("core-events atomic replace failed (manifest)");
|
|
331
|
+
}
|
|
332
|
+
for (let i = 1;i < lines.length - 1; i += 1) {
|
|
333
|
+
const line = lines[i];
|
|
334
|
+
if (line.trim() === "")
|
|
335
|
+
continue;
|
|
336
|
+
JSON.parse(line);
|
|
337
|
+
}
|
|
338
|
+
const finalPath = eventsFilePath(directory);
|
|
339
|
+
const tmpPath = tmpPathFor(finalPath);
|
|
340
|
+
try {
|
|
341
|
+
if (_internals.existsSync(tmpPath)) {
|
|
342
|
+
try {
|
|
343
|
+
_internals.unlinkSync(tmpPath);
|
|
344
|
+
} catch {}
|
|
345
|
+
}
|
|
346
|
+
_internals.writeFileSync(tmpPath, content, "utf-8");
|
|
347
|
+
if (_internals.readFileSync(tmpPath, "utf-8") !== content) {
|
|
348
|
+
throw new Error("tmp verify mismatch");
|
|
349
|
+
}
|
|
350
|
+
renameSyncWithRetry(tmpPath, finalPath);
|
|
351
|
+
} catch {
|
|
352
|
+
try {
|
|
353
|
+
if (_internals.existsSync(tmpPath)) {
|
|
354
|
+
_internals.unlinkSync(tmpPath);
|
|
355
|
+
}
|
|
356
|
+
} catch {}
|
|
357
|
+
throw new Error("core-events atomic replace failed");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
var LOCK_RETRY_ATTEMPTS = 20;
|
|
361
|
+
var LOCK_RETRY_DELAY_MS = 5;
|
|
362
|
+
var LOCK_STALE_MS = 5 * 60000;
|
|
363
|
+
var sleepScratch = new Int32Array(new SharedArrayBuffer(4));
|
|
364
|
+
function syncSleep(ms) {
|
|
365
|
+
try {
|
|
366
|
+
Atomics.wait(sleepScratch, 0, 0, ms);
|
|
367
|
+
} catch {
|
|
368
|
+
const start = _internals.now();
|
|
369
|
+
while (_internals.now() - start < ms) {}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
var RENAME_RETRY_DELAYS_MS = [10, 25, 50, 100, 200];
|
|
373
|
+
var RETRYABLE_RENAME_CODES = new Set(["EPERM", "EBUSY", "EACCES", "EEXIST"]);
|
|
374
|
+
function renameSyncWithRetry(fromPath, toPath) {
|
|
375
|
+
let lastError;
|
|
376
|
+
for (let attempt = 0;attempt <= RENAME_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
377
|
+
try {
|
|
378
|
+
_internals.renameSync(fromPath, toPath);
|
|
379
|
+
return;
|
|
380
|
+
} catch (err) {
|
|
381
|
+
lastError = err;
|
|
382
|
+
const code = err?.code;
|
|
383
|
+
if (!code || !RETRYABLE_RENAME_CODES.has(code))
|
|
384
|
+
throw err;
|
|
385
|
+
if (attempt < RENAME_RETRY_DELAYS_MS.length) {
|
|
386
|
+
syncSleep(RENAME_RETRY_DELAYS_MS[attempt]);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
throw lastError;
|
|
391
|
+
}
|
|
392
|
+
function tryLockOnce(directory) {
|
|
393
|
+
const lockPath = lockFilePath(directory);
|
|
394
|
+
try {
|
|
395
|
+
_internals.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
396
|
+
const fd = _internals.openSync(lockPath, "wx");
|
|
397
|
+
_internals.closeSync(fd);
|
|
398
|
+
return true;
|
|
399
|
+
} catch {
|
|
400
|
+
try {
|
|
401
|
+
const age = _internals.now() - _internals.statSync(lockPath).mtimeMs;
|
|
402
|
+
if (age > LOCK_STALE_MS) {
|
|
403
|
+
try {
|
|
404
|
+
_internals.unlinkSync(lockPath);
|
|
405
|
+
} catch {}
|
|
406
|
+
try {
|
|
407
|
+
const fd = _internals.openSync(lockPath, "wx");
|
|
408
|
+
_internals.closeSync(fd);
|
|
409
|
+
return true;
|
|
410
|
+
} catch {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
} catch {}
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function withCoreEventStoreLock(directory, fn) {
|
|
419
|
+
for (let attempt = 0;attempt < LOCK_RETRY_ATTEMPTS; attempt += 1) {
|
|
420
|
+
if (tryLockOnce(directory)) {
|
|
421
|
+
try {
|
|
422
|
+
return fn();
|
|
423
|
+
} finally {
|
|
424
|
+
try {
|
|
425
|
+
_internals.unlinkSync(lockFilePath(directory));
|
|
426
|
+
} catch {}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (attempt < LOCK_RETRY_ATTEMPTS - 1) {
|
|
430
|
+
syncSleep(LOCK_RETRY_DELAY_MS);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
var _appendCount = 0;
|
|
436
|
+
var _lastWarnAt = 0;
|
|
437
|
+
function shouldRunMaintenance(linesAppended = 1) {
|
|
438
|
+
_appendCount += linesAppended;
|
|
439
|
+
if (_appendCount >= _internals.limits.checkInterval) {
|
|
440
|
+
_appendCount = 0;
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
function warnThrottled(message) {
|
|
446
|
+
const now = _internals.now();
|
|
447
|
+
if (now - _lastWarnAt < _internals.limits.warnCooldownMs)
|
|
448
|
+
return;
|
|
449
|
+
_lastWarnAt = now;
|
|
450
|
+
warn(`core-events: ${message}`);
|
|
451
|
+
}
|
|
452
|
+
function readStoreFull(directory) {
|
|
453
|
+
const filePath = eventsFilePath(directory);
|
|
454
|
+
if (!_internals.existsSync(filePath)) {
|
|
455
|
+
return { manifest: null, lines: [], corruptLines: 0 };
|
|
456
|
+
}
|
|
457
|
+
let text;
|
|
458
|
+
try {
|
|
459
|
+
text = _internals.readFileSync(filePath, "utf-8");
|
|
460
|
+
} catch {
|
|
461
|
+
return { manifest: null, lines: [], corruptLines: 0 };
|
|
462
|
+
}
|
|
463
|
+
const rawLines = text.split(`
|
|
464
|
+
`);
|
|
465
|
+
const lines = [];
|
|
466
|
+
let corruptLines = 0;
|
|
467
|
+
let manifest = null;
|
|
468
|
+
for (let i = 0;i < rawLines.length; i += 1) {
|
|
469
|
+
const line = rawLines[i];
|
|
470
|
+
if (line.trim() === "")
|
|
471
|
+
continue;
|
|
472
|
+
if (i === 0) {
|
|
473
|
+
manifest = parseManifestLine(line);
|
|
474
|
+
if (manifest)
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
const parsed = JSON.parse(line);
|
|
479
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
480
|
+
lines.push({ line, event: parsed });
|
|
481
|
+
} else {
|
|
482
|
+
corruptLines += 1;
|
|
483
|
+
}
|
|
484
|
+
} catch {
|
|
485
|
+
corruptLines += 1;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return { manifest, lines, corruptLines };
|
|
489
|
+
}
|
|
490
|
+
function coverageFor(fileExists, lineCount, truncated) {
|
|
491
|
+
if (!fileExists || lineCount === 0)
|
|
492
|
+
return "empty";
|
|
493
|
+
return truncated ? "truncated" : "complete";
|
|
494
|
+
}
|
|
495
|
+
function readCoreEvents(directory) {
|
|
496
|
+
const filePath = eventsFilePath(directory);
|
|
497
|
+
if (!_internals.existsSync(filePath)) {
|
|
498
|
+
return { text: "", truncated: false, coverage: "empty" };
|
|
499
|
+
}
|
|
500
|
+
const size = fileSizeOrZero(filePath);
|
|
501
|
+
const truncated = size > _internals.limits.readMaxBytes;
|
|
502
|
+
const offset = truncated ? Math.max(0, size - _internals.limits.readMaxBytes) : 0;
|
|
503
|
+
let text = readBoundedChunk(filePath, _internals.limits.readMaxBytes, offset).text;
|
|
504
|
+
if (truncated) {
|
|
505
|
+
const firstNewline = text.indexOf(`
|
|
506
|
+
`);
|
|
507
|
+
text = firstNewline === -1 ? "" : text.slice(firstNewline + 1);
|
|
508
|
+
}
|
|
509
|
+
const lines = text.split(`
|
|
510
|
+
`);
|
|
511
|
+
const start = parseManifestLine(lines[0] ?? "") !== null ? 1 : 0;
|
|
512
|
+
if (start === 1) {
|
|
513
|
+
text = lines.slice(1).join(`
|
|
514
|
+
`);
|
|
515
|
+
}
|
|
516
|
+
const eventLineCount = text.split(`
|
|
517
|
+
`).filter((l) => l.trim() !== "").length;
|
|
518
|
+
const coverage = start === 1 ? truncated ? "truncated" : "complete" : coverageFor(true, eventLineCount, truncated);
|
|
519
|
+
return { text, truncated, coverage };
|
|
520
|
+
}
|
|
521
|
+
function getCoreEventCoverage(directory) {
|
|
522
|
+
const filePath = eventsFilePath(directory);
|
|
523
|
+
if (!_internals.existsSync(filePath))
|
|
524
|
+
return "empty";
|
|
525
|
+
const size = fileSizeOrZero(filePath);
|
|
526
|
+
if (size === 0)
|
|
527
|
+
return "empty";
|
|
528
|
+
return size > _internals.limits.readMaxBytes ? "truncated" : "complete";
|
|
529
|
+
}
|
|
530
|
+
var CORE_EVENT_LOCKED = "CORE_EVENT_STORE_LOCKED";
|
|
531
|
+
var CORE_EVENT_LINE_TOO_LARGE = "CORE_EVENT_LINE_TOO_LARGE";
|
|
532
|
+
var CORE_EVENT_NO_STORE = "CORE_EVENT_NO_STORE";
|
|
533
|
+
function appendCoreEventSync(directory, event, options) {
|
|
534
|
+
const line = `${JSON.stringify(event)}
|
|
535
|
+
`;
|
|
536
|
+
const lineBytes = Buffer.byteLength(line);
|
|
537
|
+
if (lineBytes - 1 > _internals.limits.maxLineBytes) {
|
|
538
|
+
throw new Error(CORE_EVENT_LINE_TOO_LARGE);
|
|
539
|
+
}
|
|
540
|
+
const filePath = eventsFilePath(directory);
|
|
541
|
+
const swarmDir = path.join(directory, ".swarm");
|
|
542
|
+
if (options?.ensureSwarmDir === false) {
|
|
543
|
+
if (!_internals.existsSync(swarmDir)) {
|
|
544
|
+
throw new Error(CORE_EVENT_NO_STORE);
|
|
545
|
+
}
|
|
546
|
+
} else {
|
|
547
|
+
_internals.mkdirSync(swarmDir, { recursive: true });
|
|
548
|
+
}
|
|
549
|
+
const authorityKey = authorityKeyFor(event);
|
|
550
|
+
const wrote = withCoreEventStoreLock(directory, () => {
|
|
551
|
+
if (authorityKey && options?.dedupeOnAuthorityKey) {
|
|
552
|
+
if (authorityKeyPresent(directory, authorityKey, event)) {
|
|
553
|
+
return "skipped";
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (!_internals.existsSync(filePath)) {
|
|
557
|
+
const manifest = emptyManifest();
|
|
558
|
+
atomicReplace(directory, `${JSON.stringify(manifest)}
|
|
559
|
+
${line}`);
|
|
560
|
+
} else {
|
|
561
|
+
const prefix = fileEndsWithNewline(filePath) ? "" : `
|
|
562
|
+
`;
|
|
563
|
+
_internals.appendFileSync(filePath, `${prefix}${line}`, "utf-8");
|
|
564
|
+
}
|
|
565
|
+
if (authorityKey) {
|
|
566
|
+
const state = loadAuthorityIndex(directory);
|
|
567
|
+
mergeAuthorityKeys(directory, state, [authorityKey]);
|
|
568
|
+
}
|
|
569
|
+
return "appended";
|
|
570
|
+
});
|
|
571
|
+
if (wrote === null) {
|
|
572
|
+
throw new Error(CORE_EVENT_LOCKED);
|
|
573
|
+
}
|
|
574
|
+
if (shouldRunMaintenance()) {
|
|
575
|
+
runMaintenance(directory);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function authorityKeyPresent(directory, key, event) {
|
|
579
|
+
const state = loadAuthorityIndex(directory);
|
|
580
|
+
if (state.kind === "ok" && state.index.entries[key] !== undefined) {
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
for (const candidate of scanWindowFor(directory, (e) => authorityKeyFor(e) === key)) {
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
function appendCoreEventsSync(directory, events, options) {
|
|
589
|
+
if (events.length === 0)
|
|
590
|
+
return;
|
|
591
|
+
const lines = events.map((event) => `${JSON.stringify(event)}
|
|
592
|
+
`);
|
|
593
|
+
for (const line of lines) {
|
|
594
|
+
if (Buffer.byteLength(line) - 1 > _internals.limits.maxLineBytes) {
|
|
595
|
+
throw new Error(CORE_EVENT_LINE_TOO_LARGE);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const filePath = eventsFilePath(directory);
|
|
599
|
+
const swarmDir = path.join(directory, ".swarm");
|
|
600
|
+
if (options?.ensureSwarmDir === false) {
|
|
601
|
+
if (!_internals.existsSync(swarmDir)) {
|
|
602
|
+
throw new Error(CORE_EVENT_NO_STORE);
|
|
603
|
+
}
|
|
604
|
+
} else {
|
|
605
|
+
_internals.mkdirSync(swarmDir, { recursive: true });
|
|
606
|
+
}
|
|
607
|
+
const wrote = withCoreEventStoreLock(directory, () => {
|
|
608
|
+
let appended = 0;
|
|
609
|
+
let framingChecked = false;
|
|
610
|
+
for (let i = 0;i < events.length; i += 1) {
|
|
611
|
+
const event = events[i];
|
|
612
|
+
const authorityKey = authorityKeyFor(event);
|
|
613
|
+
if (authorityKey && options?.dedupeOnAuthorityKey) {
|
|
614
|
+
if (authorityKeyPresent(directory, authorityKey, event)) {
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (appended === 0 && !_internals.existsSync(filePath)) {
|
|
619
|
+
const manifest = emptyManifest();
|
|
620
|
+
atomicReplace(directory, `${JSON.stringify(manifest)}
|
|
621
|
+
${lines[i]}`);
|
|
622
|
+
framingChecked = true;
|
|
623
|
+
} else {
|
|
624
|
+
const prefix = !framingChecked && !fileEndsWithNewline(filePath) ? `
|
|
625
|
+
` : "";
|
|
626
|
+
_internals.appendFileSync(filePath, `${prefix}${lines[i]}`, "utf-8");
|
|
627
|
+
framingChecked = true;
|
|
628
|
+
}
|
|
629
|
+
if (authorityKey) {
|
|
630
|
+
const state = loadAuthorityIndex(directory);
|
|
631
|
+
mergeAuthorityKeys(directory, state, [authorityKey]);
|
|
632
|
+
}
|
|
633
|
+
appended += 1;
|
|
634
|
+
}
|
|
635
|
+
return appended;
|
|
636
|
+
});
|
|
637
|
+
if (wrote === null) {
|
|
638
|
+
throw new Error(CORE_EVENT_LOCKED);
|
|
639
|
+
}
|
|
640
|
+
if (shouldRunMaintenance(events.length)) {
|
|
641
|
+
runMaintenance(directory);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function scanWindowFor(directory, predicate) {
|
|
645
|
+
const window = readCoreEvents(directory);
|
|
646
|
+
const matches = [];
|
|
647
|
+
for (const line of window.text.split(`
|
|
648
|
+
`)) {
|
|
649
|
+
if (line.trim() === "")
|
|
650
|
+
continue;
|
|
651
|
+
try {
|
|
652
|
+
const parsed = JSON.parse(line);
|
|
653
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && predicate(parsed)) {
|
|
654
|
+
matches.push(parsed);
|
|
655
|
+
}
|
|
656
|
+
} catch {}
|
|
657
|
+
}
|
|
658
|
+
return matches;
|
|
659
|
+
}
|
|
660
|
+
function selfHealAuthorityKeys(directory, keys) {
|
|
661
|
+
try {
|
|
662
|
+
const state = loadAuthorityIndex(directory);
|
|
663
|
+
if (state.kind === "corrupt")
|
|
664
|
+
return;
|
|
665
|
+
mergeAuthorityKeys(directory, state, keys);
|
|
666
|
+
} catch {}
|
|
667
|
+
}
|
|
668
|
+
function hasAuthorityEvent(directory, key, predicate) {
|
|
669
|
+
const state = loadAuthorityIndex(directory);
|
|
670
|
+
if (state.kind === "corrupt") {
|
|
671
|
+
throw new Error("CORE_EVENT_AUTHORITY_INDEX_UNREADABLE");
|
|
672
|
+
}
|
|
673
|
+
if (state.kind === "ok" && state.index.entries[key] !== undefined) {
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
const found = scanWindowFor(directory, predicate);
|
|
677
|
+
if (found.length > 0) {
|
|
678
|
+
selfHealAuthorityKeys(directory, [key]);
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
function hasSpecDriftAuditEvent(directory, kind, transitionId) {
|
|
684
|
+
return hasAuthorityEvent(directory, `drift|${kind}|${transitionId}`, (e) => e.type === kind && e.transitionId === transitionId);
|
|
685
|
+
}
|
|
686
|
+
function hasTaskRepairAuditEvent(directory, taskId, transitionId) {
|
|
687
|
+
return hasAuthorityEvent(directory, `repair|${taskId}|${transitionId}`, (e) => e.type === AUTHORITY_REPAIR && e.taskId === taskId && e.transitionId === transitionId);
|
|
688
|
+
}
|
|
689
|
+
function foldPass(directory, trigger, forceFull, preloadedAuthorityState) {
|
|
690
|
+
const filePath = eventsFilePath(directory);
|
|
691
|
+
if (!_internals.existsSync(filePath))
|
|
692
|
+
return;
|
|
693
|
+
const view = readStoreFull(directory);
|
|
694
|
+
if (view.manifest === null && view.lines.length === 0)
|
|
695
|
+
return;
|
|
696
|
+
const folded = view.manifest ? cloneFolded(view.manifest.folded) : emptyFolded();
|
|
697
|
+
const now = _internals.now();
|
|
698
|
+
const agePruned = [];
|
|
699
|
+
const kept = [];
|
|
700
|
+
for (const entry of view.lines) {
|
|
701
|
+
if (entry.event === null) {
|
|
702
|
+
agePruned.push(entry);
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
if (authorityKeyFor(entry.event) !== null) {
|
|
706
|
+
kept.push(entry);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
const ts = eventTimestamp(entry.event);
|
|
710
|
+
if (ts !== null && now - Date.parse(ts) > _internals.limits.ageMaxMs) {
|
|
711
|
+
agePruned.push(entry);
|
|
712
|
+
} else {
|
|
713
|
+
kept.push(entry);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const retained = [];
|
|
717
|
+
let bytes = Buffer.byteLength(JSON.stringify(view.manifest ?? emptyManifest()));
|
|
718
|
+
for (let i = kept.length - 1;i >= 0; i -= 1) {
|
|
719
|
+
const entry = kept[i];
|
|
720
|
+
const lineBytes = Buffer.byteLength(entry.line) + 1;
|
|
721
|
+
if (retained.length >= _internals.limits.activeMaxEntries || bytes + lineBytes > _internals.limits.activeMaxBytes) {
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
retained.push(entry);
|
|
725
|
+
bytes += lineBytes;
|
|
726
|
+
}
|
|
727
|
+
retained.reverse();
|
|
728
|
+
const retainedSet = new Set(retained);
|
|
729
|
+
const candidates = [];
|
|
730
|
+
for (const entry of view.lines) {
|
|
731
|
+
if (entry.event === null) {
|
|
732
|
+
candidates.push(entry);
|
|
733
|
+
} else if (!retainedSet.has(entry)) {
|
|
734
|
+
candidates.push(entry);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
const toFold = new Set;
|
|
738
|
+
let foldedBytes = 0;
|
|
739
|
+
for (const cand of candidates) {
|
|
740
|
+
const cb = Buffer.byteLength(cand.line) + 1;
|
|
741
|
+
if (!forceFull && foldedBytes + cb > _internals.limits.compactMaxBytes && toFold.size > 0) {
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
toFold.add(cand);
|
|
745
|
+
foldedBytes += cb;
|
|
746
|
+
}
|
|
747
|
+
const indexKeys = [];
|
|
748
|
+
for (const cand of toFold) {
|
|
749
|
+
if (cand.event !== null) {
|
|
750
|
+
const key = authorityKeyFor(cand.event);
|
|
751
|
+
if (key)
|
|
752
|
+
indexKeys.push(key);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
if (indexKeys.length > 0) {
|
|
756
|
+
mergeAuthorityKeys(directory, loadAuthorityIndex(directory), indexKeys);
|
|
757
|
+
}
|
|
758
|
+
const agePrunedSet = new Set(agePruned);
|
|
759
|
+
let dropped = 0;
|
|
760
|
+
for (const cand of toFold) {
|
|
761
|
+
if (cand.event === null) {
|
|
762
|
+
folded.corrupt += 1;
|
|
763
|
+
} else {
|
|
764
|
+
foldLineInto(folded, cand.event);
|
|
765
|
+
if (agePrunedSet.has(cand))
|
|
766
|
+
dropped += 1;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
folded.dropped += dropped;
|
|
770
|
+
folded.corrupt += view.corruptLines;
|
|
771
|
+
const finalRetained = [];
|
|
772
|
+
for (const entry of view.lines) {
|
|
773
|
+
if (!toFold.has(entry))
|
|
774
|
+
finalRetained.push(entry);
|
|
775
|
+
}
|
|
776
|
+
const manifest = {
|
|
777
|
+
...emptyManifest(),
|
|
778
|
+
folded,
|
|
779
|
+
updatedAt: new Date().toISOString()
|
|
780
|
+
};
|
|
781
|
+
const outLines = [`${JSON.stringify(manifest)}`];
|
|
782
|
+
for (const entry of finalRetained)
|
|
783
|
+
outLines.push(entry.line);
|
|
784
|
+
atomicReplace(directory, outLines.length > 1 ? `${outLines.join(`
|
|
785
|
+
`)}
|
|
786
|
+
` : `${outLines[0]}
|
|
787
|
+
`);
|
|
788
|
+
emitHealth(directory, {
|
|
789
|
+
trigger,
|
|
790
|
+
accepted: folded.totalEvents + finalRetained.length,
|
|
791
|
+
compacted: toFold.size - dropped,
|
|
792
|
+
retained: finalRetained.length,
|
|
793
|
+
dropped,
|
|
794
|
+
corrupt: folded.corrupt,
|
|
795
|
+
oldest: folded.oldestTimestamp,
|
|
796
|
+
newest: folded.newestTimestamp,
|
|
797
|
+
bytes: fileSizeOrZero(filePath),
|
|
798
|
+
limitBytes: _internals.limits.activeMaxBytes
|
|
799
|
+
}, preloadedAuthorityState);
|
|
800
|
+
}
|
|
801
|
+
function runMaintenance(directory) {
|
|
802
|
+
try {
|
|
803
|
+
const filePath = eventsFilePath(directory);
|
|
804
|
+
if (!_internals.existsSync(filePath))
|
|
805
|
+
return;
|
|
806
|
+
const drainThreshold = _internals.limits.activeMaxBytes + _internals.limits.headerMaxBytes + 2048;
|
|
807
|
+
withCoreEventStoreLock(directory, () => {
|
|
808
|
+
const authorityState = loadAuthorityIndex(directory);
|
|
809
|
+
foldPass(directory, "compaction", false, authorityState);
|
|
810
|
+
let prevSize = Number.POSITIVE_INFINITY;
|
|
811
|
+
for (let pass = 0;pass < 64; pass += 1) {
|
|
812
|
+
const size = fileSizeOrZero(filePath);
|
|
813
|
+
if (size <= drainThreshold)
|
|
814
|
+
break;
|
|
815
|
+
if (size >= prevSize)
|
|
816
|
+
break;
|
|
817
|
+
prevSize = size;
|
|
818
|
+
foldPass(directory, "compaction", false, authorityState);
|
|
819
|
+
}
|
|
820
|
+
return true;
|
|
821
|
+
});
|
|
822
|
+
} catch {
|
|
823
|
+
warnThrottled("maintenance pass failed");
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
function finalizeCoreEventsForClose(directory) {
|
|
827
|
+
try {
|
|
828
|
+
const filePath = eventsFilePath(directory);
|
|
829
|
+
if (!_internals.existsSync(filePath))
|
|
830
|
+
return;
|
|
831
|
+
withCoreEventStoreLock(directory, () => {
|
|
832
|
+
const authorityState = loadAuthorityIndex(directory);
|
|
833
|
+
let prev = Number.POSITIVE_INFINITY;
|
|
834
|
+
for (let pass = 0;pass < 1e4; pass += 1) {
|
|
835
|
+
const view = readStoreFull(directory);
|
|
836
|
+
if (view.manifest !== null)
|
|
837
|
+
break;
|
|
838
|
+
if (view.lines.length === 0)
|
|
839
|
+
break;
|
|
840
|
+
if (view.lines.length >= prev)
|
|
841
|
+
break;
|
|
842
|
+
prev = view.lines.length;
|
|
843
|
+
foldPass(directory, "close", false, authorityState);
|
|
844
|
+
}
|
|
845
|
+
foldPass(directory, "close", true, authorityState);
|
|
846
|
+
return true;
|
|
847
|
+
});
|
|
848
|
+
} catch {
|
|
849
|
+
warnThrottled("finalize failed");
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function emitCoreEventsHealth(directory, payload, preloadedAuthorityState) {
|
|
853
|
+
let authorityCount = 0;
|
|
854
|
+
let authorityEvicted = 0;
|
|
855
|
+
const state = preloadedAuthorityState ?? loadAuthorityIndex(directory);
|
|
856
|
+
if (state.kind === "ok") {
|
|
857
|
+
authorityCount = Object.keys(state.index.entries).length;
|
|
858
|
+
authorityEvicted = state.index.evicted;
|
|
859
|
+
}
|
|
860
|
+
try {
|
|
861
|
+
telemetry.coreEventsHealth({
|
|
862
|
+
trigger: payload.trigger,
|
|
863
|
+
accepted_count: payload.accepted,
|
|
864
|
+
compacted_count: payload.compacted,
|
|
865
|
+
retained_count: payload.retained,
|
|
866
|
+
dropped_count: payload.dropped,
|
|
867
|
+
corrupt_count: payload.corrupt,
|
|
868
|
+
authority_index_count: authorityCount,
|
|
869
|
+
authority_evicted_count: authorityEvicted,
|
|
870
|
+
oldest_timestamp: payload.oldest,
|
|
871
|
+
newest_timestamp: payload.newest,
|
|
872
|
+
bytes: payload.bytes,
|
|
873
|
+
limit_bytes: payload.limitBytes
|
|
874
|
+
});
|
|
875
|
+
} catch {}
|
|
876
|
+
}
|
|
877
|
+
function emitHealth(directory, payload, preloadedAuthorityState) {
|
|
878
|
+
_internals.emitHealth(directory, payload, preloadedAuthorityState);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
export { readCoreEvents, getCoreEventCoverage, CORE_EVENT_LOCKED, appendCoreEventSync, appendCoreEventsSync, hasSpecDriftAuditEvent, hasTaskRepairAuditEvent, finalizeCoreEventsForClose };
|