@evomap/evolver-core 2.0.0-beta.0 → 2.0.0-beta.2
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/assets/gep/genes.jsonl +5 -0
- package/dist/algo/candidateAssembly.js +17 -11
- package/dist/algo/capabilityCandidates.d.ts +2 -0
- package/dist/algo/capabilityCandidates.js +5 -0
- package/dist/algo/conversationSniffer.d.ts +18 -0
- package/dist/algo/conversationSniffer.js +132 -0
- package/dist/algo/cycleEngine.js +2 -0
- package/dist/algo/cycleFailureClassifier.js +13 -5
- package/dist/algo/geneIntake.d.ts +14 -4
- package/dist/algo/geneIntake.js +37 -9
- package/dist/algo/geneSelection.d.ts +6 -0
- package/dist/algo/geneSelection.js +19 -11
- package/dist/algo/index.d.ts +1 -0
- package/dist/algo/index.js +1 -0
- package/dist/assetstore/assetSyncLedger.d.ts +28 -0
- package/dist/assetstore/assetSyncLedger.js +86 -0
- package/dist/assetstore/index.d.ts +3 -1
- package/dist/assetstore/index.js +3 -1
- package/dist/assetstore/localJsonl.d.ts +9 -2
- package/dist/assetstore/localJsonl.js +75 -23
- package/dist/assetstore/pendingSignals.js +3 -1
- package/dist/assetstore/provenance.d.ts +7 -2
- package/dist/assetstore/provenance.js +85 -25
- package/dist/assetstore/reviewFilter.js +2 -1
- package/dist/assetstore/reviewLedger.d.ts +6 -0
- package/dist/assetstore/reviewLedger.js +9 -0
- package/dist/assetstore/seedGenes.d.ts +3 -0
- package/dist/assetstore/seedGenes.js +134 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +11 -1
- package/dist/benchmark/antiGeneImpact.d.ts +16 -0
- package/dist/benchmark/antiGeneImpact.js +34 -0
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +13 -1
- package/dist/events/eventArchive.d.ts +65 -0
- package/dist/events/eventArchive.js +343 -0
- package/dist/events/eventStore.js +2 -12
- package/dist/events/public.d.ts +3 -1
- package/dist/events/public.js +2 -1
- package/dist/events/retention.d.ts +24 -1
- package/dist/events/retention.js +133 -37
- package/dist/exec/autoExec.js +3 -0
- package/dist/exec/claudeBridge.js +23 -0
- package/dist/exec/runnerRegistry.d.ts +8 -6
- package/dist/exec/runnerRegistry.js +71 -18
- package/dist/exec/selfPrObfuscation.d.ts +2 -1
- package/dist/exec/selfPrObfuscation.js +19 -6
- package/dist/hub/agentDirectory.d.ts +90 -0
- package/dist/hub/agentDirectory.js +104 -0
- package/dist/hub/bindings.js +13 -3
- package/dist/hub/capability.d.ts +14 -2
- package/dist/hub/fake.d.ts +4 -2
- package/dist/hub/fake.js +3 -2
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/mailbox/catalog.js +3 -0
- package/dist/mailbox/ipcServer.js +2 -2
- package/dist/mailbox/store.d.ts +14 -0
- package/dist/mailbox/store.js +58 -6
- package/dist/material/consumer.js +9 -6
- package/dist/material/index.d.ts +1 -0
- package/dist/material/index.js +1 -0
- package/dist/material/materialArchive.d.ts +81 -0
- package/dist/material/materialArchive.js +466 -0
- package/dist/material/materialStore.d.ts +1 -0
- package/dist/material/materialStore.js +6 -13
- package/dist/schema/common.d.ts +1 -1
- package/dist/schema/common.js +1 -1
- package/dist/schema/material.d.ts +5 -5
- package/dist/trace/trajectoryExport.js +2 -2
- package/dist/wire/geneHints.d.ts +42 -1
- package/dist/wire/geneHints.js +52 -1
- package/dist/wire/index.d.ts +14 -2
- package/dist/wire/index.js +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeSync, } from 'node:fs';
|
|
2
|
+
import { basename, dirname, extname, join } from 'node:path';
|
|
3
|
+
import { acquireLock, releaseLock } from '../util/fileLock.js';
|
|
4
|
+
import { rootEvent } from './eventSchema.js';
|
|
5
|
+
export const ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS = 10_000;
|
|
6
|
+
const SEGMENT_PATTERN = /^root-events-(\d{16})-(\d{16})\.jsonl$/;
|
|
7
|
+
export class InvalidRootEventLogError extends Error {
|
|
8
|
+
line;
|
|
9
|
+
code = 'ROOT_EVENT_ARCHIVE_INVALID_LOG';
|
|
10
|
+
constructor(line, reason) {
|
|
11
|
+
super(`root event archive refused invalid active log at line ${line}: ${reason}`);
|
|
12
|
+
this.line = line;
|
|
13
|
+
this.name = 'InvalidRootEventLogError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class ArchiveSegmentConflictError extends Error {
|
|
17
|
+
archiveId;
|
|
18
|
+
code = 'ROOT_EVENT_ARCHIVE_SEGMENT_CONFLICT';
|
|
19
|
+
constructor(archiveId) {
|
|
20
|
+
super(`root event archive segment ${archiveId} already exists with different content`);
|
|
21
|
+
this.archiveId = archiveId;
|
|
22
|
+
this.name = 'ArchiveSegmentConflictError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class InvalidRootEventArchiveError extends Error {
|
|
26
|
+
segment;
|
|
27
|
+
line;
|
|
28
|
+
code = 'ROOT_EVENT_ARCHIVE_INVALID_ARCHIVE';
|
|
29
|
+
constructor(segment, line) {
|
|
30
|
+
super(`root event archive segment ${segment} is invalid at line ${line}`);
|
|
31
|
+
this.segment = segment;
|
|
32
|
+
this.line = line;
|
|
33
|
+
this.name = 'InvalidRootEventArchiveError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export class RootEventHistoryGapError extends Error {
|
|
37
|
+
expectedSeq;
|
|
38
|
+
actualSeq;
|
|
39
|
+
code = 'ROOT_EVENT_HISTORY_GAP';
|
|
40
|
+
constructor(expectedSeq, actualSeq) {
|
|
41
|
+
super(`root event history expected seq ${expectedSeq} but found ${actualSeq}`);
|
|
42
|
+
this.expectedSeq = expectedSeq;
|
|
43
|
+
this.actualSeq = actualSeq;
|
|
44
|
+
this.name = 'RootEventHistoryGapError';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function rootEventArchiveDir(path) {
|
|
48
|
+
const ext = extname(path);
|
|
49
|
+
const stem = ext.length > 0 ? basename(path, ext) : basename(path);
|
|
50
|
+
return join(dirname(path), `${stem}.archive`);
|
|
51
|
+
}
|
|
52
|
+
export function rootEventArchiveSegmentName(firstSeq, lastSeq) {
|
|
53
|
+
return `root-events-${String(firstSeq).padStart(16, '0')}-${String(lastSeq).padStart(16, '0')}.jsonl`;
|
|
54
|
+
}
|
|
55
|
+
export function planRootEventArchive(opts) {
|
|
56
|
+
const keepEvents = normalizeKeepEvents(opts.keepEvents);
|
|
57
|
+
const active = readActiveStrict(opts.path);
|
|
58
|
+
const wouldArchive = Math.max(0, active.length - keepEvents);
|
|
59
|
+
const prefix = active.slice(0, wouldArchive);
|
|
60
|
+
readValidatedArchive(opts.path, active);
|
|
61
|
+
const firstSeq = prefix[0]?.event.seq ?? null;
|
|
62
|
+
const lastSeq = prefix[prefix.length - 1]?.event.seq ?? null;
|
|
63
|
+
return {
|
|
64
|
+
mode: 'preview',
|
|
65
|
+
activeRecords: active.length,
|
|
66
|
+
keepEvents,
|
|
67
|
+
wouldArchive,
|
|
68
|
+
retainedRecords: active.length - wouldArchive,
|
|
69
|
+
firstSeq,
|
|
70
|
+
lastSeq,
|
|
71
|
+
archiveId: firstSeq === null || lastSeq === null ? null : `${firstSeq}-${lastSeq}`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function archiveRootEvents(opts) {
|
|
75
|
+
const keepEvents = normalizeKeepEvents(opts.keepEvents);
|
|
76
|
+
const lockPath = `${opts.path}.lock`;
|
|
77
|
+
mkdirSync(dirname(opts.path), { recursive: true });
|
|
78
|
+
acquireLock(lockPath);
|
|
79
|
+
try {
|
|
80
|
+
const active = readActiveStrict(opts.path);
|
|
81
|
+
const archivedRecords = Math.max(0, active.length - keepEvents);
|
|
82
|
+
const prefix = active.slice(0, archivedRecords);
|
|
83
|
+
const tail = active.slice(archivedRecords);
|
|
84
|
+
const firstSeq = prefix[0]?.event.seq ?? null;
|
|
85
|
+
const lastSeq = prefix[prefix.length - 1]?.event.seq ?? null;
|
|
86
|
+
const archiveId = firstSeq === null || lastSeq === null ? null : `${firstSeq}-${lastSeq}`;
|
|
87
|
+
let reusedSegment = false;
|
|
88
|
+
const archivedBySeq = readValidatedArchive(opts.path, active);
|
|
89
|
+
if (archiveId !== null && firstSeq !== null && lastSeq !== null) {
|
|
90
|
+
const archiveDir = rootEventArchiveDir(opts.path);
|
|
91
|
+
const missing = [];
|
|
92
|
+
for (const line of prefix) {
|
|
93
|
+
const archived = archivedBySeq.get(line.event.seq);
|
|
94
|
+
if (archived === undefined) {
|
|
95
|
+
missing.push(line);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (JSON.stringify(archived) !== JSON.stringify(line.event)) {
|
|
99
|
+
throw new ArchiveSegmentConflictError(String(line.event.seq));
|
|
100
|
+
}
|
|
101
|
+
reusedSegment = true;
|
|
102
|
+
}
|
|
103
|
+
for (const segment of contiguousGroups(missing)) {
|
|
104
|
+
const missingFirstSeq = segment[0].event.seq;
|
|
105
|
+
const missingLastSeq = segment[segment.length - 1].event.seq;
|
|
106
|
+
const archiveBytes = serializeLines(segment);
|
|
107
|
+
mkdirSync(archiveDir, { recursive: true });
|
|
108
|
+
const segmentPath = join(archiveDir, rootEventArchiveSegmentName(missingFirstSeq, missingLastSeq));
|
|
109
|
+
if (existsSync(segmentPath))
|
|
110
|
+
throw new ArchiveSegmentConflictError(`${missingFirstSeq}-${missingLastSeq}`);
|
|
111
|
+
durableReplace(`${segmentPath}.tmp`, segmentPath, archiveBytes);
|
|
112
|
+
fsyncDirectoryBestEffort(archiveDir);
|
|
113
|
+
}
|
|
114
|
+
durableReplace(`${opts.path}.archive.tmp`, opts.path, serializeLines(tail));
|
|
115
|
+
fsyncDirectoryBestEffort(dirname(opts.path));
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
mode: 'write',
|
|
119
|
+
activeRecordsBefore: active.length,
|
|
120
|
+
keepEvents,
|
|
121
|
+
archivedRecords,
|
|
122
|
+
retainedRecords: tail.length,
|
|
123
|
+
firstSeq,
|
|
124
|
+
lastSeq,
|
|
125
|
+
archiveId,
|
|
126
|
+
reusedSegment,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
releaseLock(lockPath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function readRootEventHistory(path, archiveDir = rootEventArchiveDir(path)) {
|
|
134
|
+
const bySeq = new Map();
|
|
135
|
+
for (const file of archiveSegmentFiles(archiveDir)) {
|
|
136
|
+
for (const event of readEventsLenient(join(archiveDir, file))) {
|
|
137
|
+
// Archive segments are immutable: the first segment in canonical order owns a seq. A later conflicting
|
|
138
|
+
// segment cannot silently replace it during operational reads. Strict validation still reports the
|
|
139
|
+
// conflict and blocks archive writes.
|
|
140
|
+
if (!bySeq.has(event.seq))
|
|
141
|
+
bySeq.set(event.seq, event);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Preserve the pre-archive fail-soft behavior for routine commands. The active log is the current writer
|
|
145
|
+
// source, so it deterministically wins an active/archive conflict without crashing status/report readers.
|
|
146
|
+
for (const event of readEventsLenient(path))
|
|
147
|
+
bySeq.set(event.seq, event);
|
|
148
|
+
return [...bySeq.values()].sort((a, b) => a.seq - b.seq);
|
|
149
|
+
}
|
|
150
|
+
export function validateRootEventHistory(path) {
|
|
151
|
+
const active = readActiveStrict(path);
|
|
152
|
+
readValidatedArchive(path, active);
|
|
153
|
+
}
|
|
154
|
+
export function inspectRootEventArchive(path) {
|
|
155
|
+
const archiveDir = rootEventArchiveDir(path);
|
|
156
|
+
const files = archiveSegmentFiles(archiveDir);
|
|
157
|
+
let bytes = 0;
|
|
158
|
+
let records = 0;
|
|
159
|
+
let invalidLines = 0;
|
|
160
|
+
let firstSeq = null;
|
|
161
|
+
let lastSeq = null;
|
|
162
|
+
for (const file of files) {
|
|
163
|
+
const raw = readFileSync(join(archiveDir, file), 'utf8');
|
|
164
|
+
bytes += Buffer.byteLength(raw, 'utf8');
|
|
165
|
+
for (const line of raw.split('\n')) {
|
|
166
|
+
if (line.length === 0)
|
|
167
|
+
continue;
|
|
168
|
+
try {
|
|
169
|
+
const event = rootEvent.parse(JSON.parse(line));
|
|
170
|
+
firstSeq ??= event.seq;
|
|
171
|
+
lastSeq = event.seq;
|
|
172
|
+
records += 1;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
invalidLines += 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
exists: files.length > 0,
|
|
181
|
+
segments: files.length,
|
|
182
|
+
bytes,
|
|
183
|
+
records,
|
|
184
|
+
invalidLines,
|
|
185
|
+
firstSeq,
|
|
186
|
+
lastSeq,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function readActiveStrict(path) {
|
|
190
|
+
if (!existsSync(path))
|
|
191
|
+
return [];
|
|
192
|
+
const out = [];
|
|
193
|
+
const lines = readFileSync(path, 'utf8').split('\n');
|
|
194
|
+
let previousSeq = null;
|
|
195
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
196
|
+
const raw = lines[index];
|
|
197
|
+
if (raw.length === 0)
|
|
198
|
+
continue;
|
|
199
|
+
let event;
|
|
200
|
+
try {
|
|
201
|
+
event = rootEvent.parse(JSON.parse(raw));
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
throw new InvalidRootEventLogError(index + 1, 'invalid json or event schema');
|
|
205
|
+
}
|
|
206
|
+
if (previousSeq !== null && event.seq !== previousSeq + 1) {
|
|
207
|
+
throw new InvalidRootEventLogError(index + 1, 'seq must be contiguous and strictly increasing');
|
|
208
|
+
}
|
|
209
|
+
previousSeq = event.seq;
|
|
210
|
+
out.push({ event, raw });
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
function readEventsLenient(path) {
|
|
215
|
+
if (!existsSync(path))
|
|
216
|
+
return [];
|
|
217
|
+
const out = [];
|
|
218
|
+
for (const line of readFileSync(path, 'utf8').split('\n')) {
|
|
219
|
+
if (line.length === 0)
|
|
220
|
+
continue;
|
|
221
|
+
try {
|
|
222
|
+
out.push(rootEvent.parse(JSON.parse(line)));
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// Active reads retain the existing crash-tail behavior. Strict archive writes refuse these lines.
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
}
|
|
230
|
+
function archiveSegmentFiles(archiveDir) {
|
|
231
|
+
if (!existsSync(archiveDir))
|
|
232
|
+
return [];
|
|
233
|
+
return readdirSync(archiveDir).filter((entry) => SEGMENT_PATTERN.test(entry)).sort();
|
|
234
|
+
}
|
|
235
|
+
function readArchivedEventsStrict(archiveDir) {
|
|
236
|
+
const out = new Map();
|
|
237
|
+
for (const file of archiveSegmentFiles(archiveDir)) {
|
|
238
|
+
const lines = readFileSync(join(archiveDir, file), 'utf8').split('\n');
|
|
239
|
+
let previousSeq = null;
|
|
240
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
241
|
+
const line = lines[index];
|
|
242
|
+
if (line.length === 0)
|
|
243
|
+
continue;
|
|
244
|
+
let event;
|
|
245
|
+
try {
|
|
246
|
+
event = rootEvent.parse(JSON.parse(line));
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
throw new InvalidRootEventArchiveError(file, index + 1);
|
|
250
|
+
}
|
|
251
|
+
if (previousSeq !== null && event.seq !== previousSeq + 1) {
|
|
252
|
+
throw new InvalidRootEventArchiveError(file, index + 1);
|
|
253
|
+
}
|
|
254
|
+
previousSeq = event.seq;
|
|
255
|
+
const current = out.get(event.seq);
|
|
256
|
+
if (current !== undefined && JSON.stringify(current) !== JSON.stringify(event)) {
|
|
257
|
+
throw new ArchiveSegmentConflictError(String(event.seq));
|
|
258
|
+
}
|
|
259
|
+
out.set(event.seq, event);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return out;
|
|
263
|
+
}
|
|
264
|
+
function readValidatedArchive(path, active) {
|
|
265
|
+
const archived = readArchivedEventsStrict(rootEventArchiveDir(path));
|
|
266
|
+
for (const line of active) {
|
|
267
|
+
const existing = archived.get(line.event.seq);
|
|
268
|
+
if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(line.event)) {
|
|
269
|
+
throw new ArchiveSegmentConflictError(String(line.event.seq));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
assertContinuousHistory(archived, active);
|
|
273
|
+
return archived;
|
|
274
|
+
}
|
|
275
|
+
function assertContinuousHistory(archived, active) {
|
|
276
|
+
const seqs = new Set(archived.keys());
|
|
277
|
+
for (const line of active)
|
|
278
|
+
seqs.add(line.event.seq);
|
|
279
|
+
const ordered = [...seqs].sort((left, right) => left - right);
|
|
280
|
+
if (ordered.length === 0)
|
|
281
|
+
return;
|
|
282
|
+
let expected = 1;
|
|
283
|
+
for (const actual of ordered) {
|
|
284
|
+
if (actual !== expected)
|
|
285
|
+
throw new RootEventHistoryGapError(expected, actual);
|
|
286
|
+
expected += 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function contiguousGroups(lines) {
|
|
290
|
+
const groups = [];
|
|
291
|
+
for (const line of lines) {
|
|
292
|
+
const current = groups[groups.length - 1];
|
|
293
|
+
const previous = current?.[current.length - 1];
|
|
294
|
+
if (current === undefined || previous === undefined || line.event.seq !== previous.event.seq + 1) {
|
|
295
|
+
groups.push([line]);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
current.push(line);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return groups;
|
|
302
|
+
}
|
|
303
|
+
function serializeLines(lines) {
|
|
304
|
+
return lines.length === 0 ? '' : `${lines.map((line) => line.raw).join('\n')}\n`;
|
|
305
|
+
}
|
|
306
|
+
function durableReplace(tmpPath, finalPath, contents) {
|
|
307
|
+
let fd = null;
|
|
308
|
+
try {
|
|
309
|
+
fd = openSync(tmpPath, 'w', 0o600);
|
|
310
|
+
writeSync(fd, contents, undefined, 'utf8');
|
|
311
|
+
fsyncSync(fd);
|
|
312
|
+
closeSync(fd);
|
|
313
|
+
fd = null;
|
|
314
|
+
renameSync(tmpPath, finalPath);
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
if (fd !== null)
|
|
318
|
+
closeSync(fd);
|
|
319
|
+
rmSync(tmpPath, { force: true });
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function fsyncDirectoryBestEffort(path) {
|
|
324
|
+
let fd = null;
|
|
325
|
+
try {
|
|
326
|
+
fd = openSync(path, 'r');
|
|
327
|
+
fsyncSync(fd);
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
// Windows and some filesystems do not support fsync on directories.
|
|
331
|
+
}
|
|
332
|
+
finally {
|
|
333
|
+
if (fd !== null)
|
|
334
|
+
closeSync(fd);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function normalizeKeepEvents(value) {
|
|
338
|
+
if (value === undefined)
|
|
339
|
+
return ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS;
|
|
340
|
+
if (!Number.isFinite(value) || value < 1)
|
|
341
|
+
throw new RangeError('keepEvents must be a positive integer');
|
|
342
|
+
return Math.floor(value);
|
|
343
|
+
}
|
|
@@ -2,6 +2,7 @@ import { openSync, writeSync, fsyncSync, closeSync, statSync, readSync, mkdirSyn
|
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { monotonicFactory } from 'ulid';
|
|
4
4
|
import { acquireLock, releaseLock } from '../util/fileLock.js';
|
|
5
|
+
import { readRootEventHistory } from './eventArchive.js';
|
|
5
6
|
// Monotonic ULIDs: plain ulid() randomizes the low 80 bits, so two ids minted in the SAME millisecond can sort
|
|
6
7
|
// out of order — the event log wants time-sortable eventIds (and a flaky CI test proved it). The monotonic
|
|
7
8
|
// factory increments within a millisecond, guaranteeing each successive eventId is strictly greater.
|
|
@@ -73,18 +74,7 @@ export class EventStore {
|
|
|
73
74
|
}
|
|
74
75
|
/** 读全部事件 (跳过尾部半行/损坏行). */
|
|
75
76
|
readAll() {
|
|
76
|
-
|
|
77
|
-
return [];
|
|
78
|
-
const out = [];
|
|
79
|
-
for (const l of readFileSync(this.path, 'utf8').split('\n')) {
|
|
80
|
-
if (!l)
|
|
81
|
-
continue;
|
|
82
|
-
try {
|
|
83
|
-
out.push(rootEvent.parse(JSON.parse(l)));
|
|
84
|
-
}
|
|
85
|
-
catch { /* 崩溃半行: 跳过 */ }
|
|
86
|
-
}
|
|
87
|
-
return out;
|
|
77
|
+
return readRootEventHistory(this.path);
|
|
88
78
|
}
|
|
89
79
|
*iterate(fromSeq = 0) {
|
|
90
80
|
for (const e of this.readAll())
|
package/dist/events/public.d.ts
CHANGED
|
@@ -5,10 +5,12 @@ export type { Projector } from './replayer.js';
|
|
|
5
5
|
export { eventCountsProjector, DEFAULT_PROJECTORS } from './projectors.js';
|
|
6
6
|
export type { EventCountsMV } from './projectors.js';
|
|
7
7
|
export { LineTooLargeError, MAX_LINE_BYTES } from './eventStore.js';
|
|
8
|
+
export { ArchiveSegmentConflictError, InvalidRootEventArchiveError, InvalidRootEventLogError, RootEventHistoryGapError, archiveRootEvents, inspectRootEventArchive, planRootEventArchive, readRootEventHistory, rootEventArchiveDir, rootEventArchiveSegmentName, validateRootEventHistory, ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS, } from './eventArchive.js';
|
|
9
|
+
export type { RootEventArchiveOptions, RootEventArchivePlan, RootEventArchiveResult, RootEventArchiveStats, } from './eventArchive.js';
|
|
8
10
|
export { rootEventsPath, evomapHome, mvDir, personalityStatePath, assetsDir, materialDir, materialStorePath, materialWatermarkPath, tracesDir, assetCallLogPath } from './paths.js';
|
|
9
11
|
export { EVENT_SCHEMA_VERSION } from './eventSchema.js';
|
|
10
12
|
export type { RootEvent, RawEvent, HumanNarrative, Actor, Replayability } from './eventSchema.js';
|
|
11
13
|
export { readEvents, statusReport, listCycles, showCycle, listTriggers, dailySummary, buildNarrativeSnapshot, NARRATIVE_DEFAULT_LIMIT, NARRATIVE_MAX_LIMIT, } from './reports.js';
|
|
12
|
-
export { buildRetentionReport, defaultMaterialCursorPath, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
|
|
14
|
+
export { buildRetentionReport, defaultMaterialCursorPath, defaultMaterialCursorPaths, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
|
|
13
15
|
export type { ReportEvent, StatusReport, CycleSummary, CycleTimeline, TriggerRow, DailySummary, NarrativeEntry, NarrativeOutcome, NarrativeSnapshot, NarrativeSnapshotOptions, } from './reports.js';
|
|
14
16
|
export type { MaterialRetentionSnapshot, RetentionReport, RetentionReportOptions, RetentionState, RetentionThresholds, RootRetentionSnapshot, } from './retention.js';
|
package/dist/events/public.js
CHANGED
|
@@ -2,7 +2,8 @@ export { Ingestor, EVENT_TYPES, registerEventType, isKnownEventType, IngestValid
|
|
|
2
2
|
export { Replayer } from './replayer.js';
|
|
3
3
|
export { eventCountsProjector, DEFAULT_PROJECTORS } from './projectors.js';
|
|
4
4
|
export { LineTooLargeError, MAX_LINE_BYTES } from './eventStore.js';
|
|
5
|
+
export { ArchiveSegmentConflictError, InvalidRootEventArchiveError, InvalidRootEventLogError, RootEventHistoryGapError, archiveRootEvents, inspectRootEventArchive, planRootEventArchive, readRootEventHistory, rootEventArchiveDir, rootEventArchiveSegmentName, validateRootEventHistory, ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS, } from './eventArchive.js';
|
|
5
6
|
export { rootEventsPath, evomapHome, mvDir, personalityStatePath, assetsDir, materialDir, materialStorePath, materialWatermarkPath, tracesDir, assetCallLogPath } from './paths.js';
|
|
6
7
|
export { EVENT_SCHEMA_VERSION } from './eventSchema.js';
|
|
7
8
|
export { readEvents, statusReport, listCycles, showCycle, listTriggers, dailySummary, buildNarrativeSnapshot, NARRATIVE_DEFAULT_LIMIT, NARRATIVE_MAX_LIMIT, } from './reports.js';
|
|
8
|
-
export { buildRetentionReport, defaultMaterialCursorPath, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
|
|
9
|
+
export { buildRetentionReport, defaultMaterialCursorPath, defaultMaterialCursorPaths, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
|
|
@@ -4,7 +4,7 @@ export declare const RETENTION_DEFAULT_MAX_ROOT_BYTES: number;
|
|
|
4
4
|
export declare const RETENTION_DEFAULT_MAX_MATERIAL_RECORDS = 10000;
|
|
5
5
|
export declare const RETENTION_DEFAULT_MAX_MATERIAL_BYTES: number;
|
|
6
6
|
export declare const RETENTION_DEFAULT_WATCH_RATIO = 0.8;
|
|
7
|
-
export declare const RETENTION_DEFAULT_ROOT_TAIL_EVENTS
|
|
7
|
+
export declare const RETENTION_DEFAULT_ROOT_TAIL_EVENTS = 10000;
|
|
8
8
|
export interface RetentionThresholds {
|
|
9
9
|
maxRecords: number;
|
|
10
10
|
maxBytes: number;
|
|
@@ -22,6 +22,17 @@ export interface RootRetentionSnapshot {
|
|
|
22
22
|
lastSeq: number | null;
|
|
23
23
|
firstTs: string | null;
|
|
24
24
|
lastTs: string | null;
|
|
25
|
+
archiveSegments: number;
|
|
26
|
+
archiveRecords: number;
|
|
27
|
+
archiveBytes: number;
|
|
28
|
+
archiveInvalidLines: number;
|
|
29
|
+
historyRecords: number;
|
|
30
|
+
historyBytes: number;
|
|
31
|
+
historyConflicts: number;
|
|
32
|
+
historyGaps: number;
|
|
33
|
+
historyIntegrityErrors: number;
|
|
34
|
+
archiveRotationSupported: true;
|
|
35
|
+
archiveRotationSafe: boolean;
|
|
25
36
|
protectTailEvents: number;
|
|
26
37
|
destructivePruneSafe: false;
|
|
27
38
|
reason: string;
|
|
@@ -36,12 +47,22 @@ export interface MaterialRetentionSnapshot {
|
|
|
36
47
|
thresholds: RetentionThresholds;
|
|
37
48
|
firstCapturedAt: string | null;
|
|
38
49
|
lastCapturedAt: string | null;
|
|
50
|
+
archiveSegments: number;
|
|
51
|
+
archiveRecords: number;
|
|
52
|
+
archiveBytes: number;
|
|
53
|
+
archiveInvalidLines: number;
|
|
54
|
+
historyRecords: number;
|
|
55
|
+
historyBytes: number;
|
|
56
|
+
cursorCount: number;
|
|
57
|
+
minCursor: number;
|
|
39
58
|
cursorValid: boolean;
|
|
40
59
|
cursorInRange: boolean;
|
|
41
60
|
cursor: number;
|
|
42
61
|
effectiveCursor: number;
|
|
43
62
|
consumedPrefix: number;
|
|
44
63
|
pending: number;
|
|
64
|
+
archiveRotationSupported: true;
|
|
65
|
+
archiveRotationSafe: boolean;
|
|
45
66
|
destructivePruneSafe: false;
|
|
46
67
|
reason: string;
|
|
47
68
|
}
|
|
@@ -58,6 +79,7 @@ export interface RetentionReportOptions {
|
|
|
58
79
|
rootEventsPath?: string;
|
|
59
80
|
materialStorePath?: string;
|
|
60
81
|
materialCursorPath?: string;
|
|
82
|
+
materialCursorPaths?: readonly string[];
|
|
61
83
|
now?: () => number;
|
|
62
84
|
maxRootEvents?: number;
|
|
63
85
|
maxRootBytes?: number;
|
|
@@ -67,4 +89,5 @@ export interface RetentionReportOptions {
|
|
|
67
89
|
protectRootTailEvents?: number;
|
|
68
90
|
}
|
|
69
91
|
export declare function defaultMaterialCursorPath(path?: string): string;
|
|
92
|
+
export declare function defaultMaterialCursorPaths(path?: string): string[];
|
|
70
93
|
export declare function buildRetentionReport(opts?: RetentionReportOptions): RetentionReport;
|