omp-fabric 1.23.0 → 1.24.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/CHANGELOG.md +22 -0
- package/dist/capture/wrapper.d.ts.map +1 -1
- package/dist/chunks/{chunk-SDZXNTBF.js → chunk-2DY7NDAZ.js} +2 -2
- package/dist/chunks/{chunk-DTG3DYVC.js → chunk-2HOIGAHB.js} +3 -3
- package/dist/chunks/{chunk-3GMSYFLT.js → chunk-5EZWO4C5.js} +3 -3
- package/dist/chunks/{chunk-BPMSLBX4.js → chunk-5LNZXB4X.js} +2 -2
- package/dist/chunks/{chunk-RA2IB2QT.js → chunk-5P4VR2IZ.js} +4 -4
- package/dist/chunks/{chunk-R4EFMCIY.js → chunk-6DJFJLM6.js} +2 -2
- package/dist/chunks/{chunk-ZQ72MAHY.js → chunk-7OEFSXWZ.js} +3 -2
- package/dist/chunks/{chunk-ZQ72MAHY.js.map → chunk-7OEFSXWZ.js.map} +2 -2
- package/dist/chunks/{chunk-WCV26AOT.js → chunk-CBQOS3IW.js} +112 -4
- package/dist/chunks/chunk-CBQOS3IW.js.map +7 -0
- package/dist/chunks/{chunk-GUJNTA7Z.js → chunk-H63XBYD3.js} +4 -2
- package/dist/chunks/chunk-H63XBYD3.js.map +7 -0
- package/dist/chunks/{chunk-4J65SMNI.js → chunk-I5ZKJNKL.js} +173 -71
- package/dist/chunks/chunk-I5ZKJNKL.js.map +7 -0
- package/dist/chunks/{chunk-YBNCEX5E.js → chunk-IH3JJDYL.js} +2 -2
- package/dist/chunks/{chunk-52JBWP2C.js → chunk-K5YUDXMV.js} +2 -2
- package/dist/chunks/{chunk-MQSRZC2O.js → chunk-MJOGV6CR.js} +2 -2
- package/dist/chunks/chunk-QW5LEEFV.js +130 -0
- package/dist/chunks/chunk-QW5LEEFV.js.map +7 -0
- package/dist/chunks/{chunk-LNP6JVCZ.js → chunk-QYDOKSGI.js} +5 -4
- package/dist/chunks/chunk-QYDOKSGI.js.map +7 -0
- package/dist/chunks/{chunk-P4GPBAQS.js → chunk-RT6AUW5S.js} +3 -3
- package/dist/chunks/{chunk-ZFMSJLQE.js → chunk-S3CWAHSO.js} +11 -1
- package/dist/chunks/chunk-S3CWAHSO.js.map +7 -0
- package/dist/compaction/hook.d.ts +1 -0
- package/dist/compaction/hook.d.ts.map +1 -1
- package/dist/compaction/hook.js +4 -4
- package/dist/compaction/lcm-addresses.d.ts.map +1 -1
- package/dist/compaction/lcm-maintenance.d.ts +2 -0
- package/dist/compaction/lcm-maintenance.d.ts.map +1 -1
- package/dist/compaction/lcm-model.d.ts +1 -0
- package/dist/compaction/lcm-model.d.ts.map +1 -1
- package/dist/compaction/lcm-recovery.d.ts +7 -0
- package/dist/compaction/lcm-recovery.d.ts.map +1 -0
- package/dist/compaction/lcm-runtime.d.ts +6 -1
- package/dist/compaction/lcm-runtime.d.ts.map +1 -1
- package/dist/compaction/lcm-runtime.js +281 -93
- package/dist/compaction/lcm-runtime.js.map +3 -3
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/core/action-registry.js +3 -3
- package/dist/core/omp-tools.d.ts +4 -0
- package/dist/core/omp-tools.d.ts.map +1 -1
- package/dist/core/system-guidance.d.ts.map +1 -1
- package/dist/core/tool-ownership.d.ts.map +1 -1
- package/dist/entropy/index.js +6 -6
- package/dist/fabric-exec-tool.d.ts.map +1 -1
- package/dist/fabric-runtime-state.js +15 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -18
- package/dist/index.js.map +3 -3
- package/dist/memory/discovery.js +5 -5
- package/dist/memory/lcm-adapter.d.ts +7 -2
- package/dist/memory/lcm-adapter.d.ts.map +1 -1
- package/dist/memory/normalize.js +4 -4
- package/dist/memory/search.js +5 -5
- package/dist/providers/memory-provider.d.ts +2 -1
- package/dist/providers/memory-provider.d.ts.map +1 -1
- package/dist/providers/memory-provider.js +8 -8
- package/dist/residency/host.js +10 -10
- package/dist/residency/omp-entry.js +10 -10
- package/dist/runtime/core-override-guest-types.js +1 -1
- package/dist/runtime/node-process-runtime.js +1 -1
- package/dist/storage/lcm-identity.d.ts +4 -0
- package/dist/storage/lcm-identity.d.ts.map +1 -1
- package/dist/storage/lcm-ledger.d.ts +11 -2
- package/dist/storage/lcm-ledger.d.ts.map +1 -1
- package/dist/storage/lcm-migration.d.ts.map +1 -1
- package/dist/ui/settings.d.ts.map +1 -1
- package/dist/ui/settings.js +22 -2
- package/dist/ui/settings.js.map +2 -2
- package/docs/compaction.md +16 -4
- package/docs/configuration.md +1 -1
- package/package.json +1 -1
- package/dist/chunks/chunk-4J65SMNI.js.map +0 -7
- package/dist/chunks/chunk-GUJNTA7Z.js.map +0 -7
- package/dist/chunks/chunk-LNP6JVCZ.js.map +0 -7
- package/dist/chunks/chunk-NX2G7BRO.js +0 -87
- package/dist/chunks/chunk-NX2G7BRO.js.map +0 -7
- package/dist/chunks/chunk-WCV26AOT.js.map +0 -7
- package/dist/chunks/chunk-ZFMSJLQE.js.map +0 -7
- /package/dist/chunks/{chunk-SDZXNTBF.js.map → chunk-2DY7NDAZ.js.map} +0 -0
- /package/dist/chunks/{chunk-DTG3DYVC.js.map → chunk-2HOIGAHB.js.map} +0 -0
- /package/dist/chunks/{chunk-3GMSYFLT.js.map → chunk-5EZWO4C5.js.map} +0 -0
- /package/dist/chunks/{chunk-BPMSLBX4.js.map → chunk-5LNZXB4X.js.map} +0 -0
- /package/dist/chunks/{chunk-RA2IB2QT.js.map → chunk-5P4VR2IZ.js.map} +0 -0
- /package/dist/chunks/{chunk-R4EFMCIY.js.map → chunk-6DJFJLM6.js.map} +0 -0
- /package/dist/chunks/{chunk-YBNCEX5E.js.map → chunk-IH3JJDYL.js.map} +0 -0
- /package/dist/chunks/{chunk-52JBWP2C.js.map → chunk-K5YUDXMV.js.map} +0 -0
- /package/dist/chunks/{chunk-MQSRZC2O.js.map → chunk-MJOGV6CR.js.map} +0 -0
- /package/dist/chunks/{chunk-P4GPBAQS.js.map → chunk-RT6AUW5S.js.map} +0 -0
|
@@ -10,24 +10,29 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
sessionDirNamesForCwd,
|
|
12
12
|
sessionsDirRoot
|
|
13
|
-
} from "../chunks/chunk-
|
|
14
|
-
import "../chunks/chunk-
|
|
13
|
+
} from "../chunks/chunk-MJOGV6CR.js";
|
|
14
|
+
import "../chunks/chunk-5LNZXB4X.js";
|
|
15
15
|
import {
|
|
16
|
+
DEFAULT_LCM_MAX_INPUT_CHARS,
|
|
16
17
|
LcmModelAdapter,
|
|
18
|
+
activeRequestBlock,
|
|
17
19
|
buildLcmPrompt,
|
|
18
|
-
emergencyReduce
|
|
19
|
-
|
|
20
|
+
emergencyReduce,
|
|
21
|
+
tailDigest
|
|
22
|
+
} from "../chunks/chunk-CBQOS3IW.js";
|
|
20
23
|
import {
|
|
24
|
+
canonicalLcmEntry,
|
|
21
25
|
canonicalLcmPayload,
|
|
22
26
|
canonicalProjectIdentity,
|
|
23
27
|
defaultLedgerPath,
|
|
24
28
|
defaultLedgerRoot,
|
|
25
29
|
hash,
|
|
26
30
|
hashLcmPayload,
|
|
31
|
+
isPrunedLcmEntry,
|
|
27
32
|
lcmSummaryAddress,
|
|
28
33
|
renderLcmChildAddresses,
|
|
29
34
|
renderLcmSourceAddresses
|
|
30
|
-
} from "../chunks/chunk-
|
|
35
|
+
} from "../chunks/chunk-QW5LEEFV.js";
|
|
31
36
|
import {
|
|
32
37
|
writeJsonAtomic
|
|
33
38
|
} from "../chunks/chunk-KAD7UWT2.js";
|
|
@@ -40,9 +45,9 @@ import {
|
|
|
40
45
|
clipUtf8,
|
|
41
46
|
utf8Bytes
|
|
42
47
|
} from "../chunks/chunk-3XPR2AZL.js";
|
|
43
|
-
import "../chunks/chunk-
|
|
44
|
-
import "../chunks/chunk-
|
|
45
|
-
import "../chunks/chunk-
|
|
48
|
+
import "../chunks/chunk-IH3JJDYL.js";
|
|
49
|
+
import "../chunks/chunk-K5YUDXMV.js";
|
|
50
|
+
import "../chunks/chunk-S3CWAHSO.js";
|
|
46
51
|
|
|
47
52
|
// src/compaction/lcm-runtime.ts
|
|
48
53
|
import fs4 from "node:fs";
|
|
@@ -141,6 +146,28 @@ var sweepLedgers = (rootDir, options) => {
|
|
|
141
146
|
// src/storage/lcm-ledger.ts
|
|
142
147
|
var LCM_LEDGER_WARNING_BYTES = 8 * 1024 ** 3;
|
|
143
148
|
var LCM_LEDGER_MAINTENANCE_BYTES = 10 * 1024 ** 3;
|
|
149
|
+
var rowFilter = (options, alias = "") => {
|
|
150
|
+
const column = (name) => alias ? `${alias}.${name}` : name;
|
|
151
|
+
const clauses = [];
|
|
152
|
+
const params = [];
|
|
153
|
+
const list = (values, operator) => {
|
|
154
|
+
if (!values || values.length === 0) return;
|
|
155
|
+
clauses.push(`${column("role")} ${operator} (${values.map(() => "?").join(",")})`);
|
|
156
|
+
params.push(...values);
|
|
157
|
+
};
|
|
158
|
+
list(options.roles, "IN");
|
|
159
|
+
list(options.excludeRoles, "NOT IN");
|
|
160
|
+
if (options.since !== void 0) {
|
|
161
|
+
clauses.push(`${column("created_at")}>=?`);
|
|
162
|
+
params.push(options.since);
|
|
163
|
+
}
|
|
164
|
+
if (options.until !== void 0) {
|
|
165
|
+
clauses.push(`${column("created_at")}<=?`);
|
|
166
|
+
params.push(options.until);
|
|
167
|
+
}
|
|
168
|
+
return { sql: clauses.map((clause) => ` AND ${clause}`).join(""), params };
|
|
169
|
+
};
|
|
170
|
+
var validSearchFilter = (options) => [options.roles, options.excludeRoles].every((values) => values === void 0 || Array.isArray(values) && values.every((value) => typeof value === "string" && value.length > 0)) && [options.since, options.until].every((value) => value === void 0 || Number.isFinite(value));
|
|
144
171
|
var LEDGER_SCHEMA_VERSION = 4;
|
|
145
172
|
var BACKUP_MANIFEST_VERSION = 3;
|
|
146
173
|
var DELETE_MANIFEST_VERSION = 2;
|
|
@@ -405,8 +432,8 @@ ALTER TABLE summary_node_revisions_next RENAME TO summary_node_revisions;`);
|
|
|
405
432
|
throw new Error("invalid payload JSON");
|
|
406
433
|
}
|
|
407
434
|
if (!payload || typeof payload !== "object" || typeof payload.type !== "string" || typeof payload.id !== "string" || payload.id !== entry.entryId) throw new Error("payload must be a full SessionEntry");
|
|
408
|
-
const payloadJson =
|
|
409
|
-
const contentHash =
|
|
435
|
+
const payloadJson = canonicalLcmEntry(payload);
|
|
436
|
+
const contentHash = hash(payloadJson);
|
|
410
437
|
const ownsTransaction = this.transactionDepth === 0;
|
|
411
438
|
if (ownsTransaction) this.db.exec("BEGIN IMMEDIATE");
|
|
412
439
|
try {
|
|
@@ -415,6 +442,11 @@ ALTER TABLE summary_node_revisions_next RENAME TO summary_node_revisions;`);
|
|
|
415
442
|
if (ownsTransaction) this.db.exec("COMMIT");
|
|
416
443
|
return { ...entry, payloadJson, payloadHash: contentHash, revision: existing.revision, contentHash, createdAt: existing.created_at };
|
|
417
444
|
}
|
|
445
|
+
const prior = this.priorRevision(entry.projectKey, entry.sessionId, entry.entryId, contentHash, isPrunedLcmEntry(payload));
|
|
446
|
+
if (prior) {
|
|
447
|
+
if (ownsTransaction) this.db.exec("COMMIT");
|
|
448
|
+
return prior;
|
|
449
|
+
}
|
|
418
450
|
const latest = this.db.prepare("SELECT COALESCE(MAX(revision),0) revision FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=?").get(entry.projectKey, entry.sessionId, entry.entryId);
|
|
419
451
|
const revision = latest.revision + 1;
|
|
420
452
|
const createdAt = entry.createdAt ?? this.now();
|
|
@@ -429,6 +461,28 @@ ALTER TABLE summary_node_revisions_next RENAME TO summary_node_revisions;`);
|
|
|
429
461
|
throw new LedgerDegradedError("ledger write failed", error);
|
|
430
462
|
}
|
|
431
463
|
}
|
|
464
|
+
identities = /* @__PURE__ */ new Map();
|
|
465
|
+
/** A stored revision the host only rewrote (pruned, re-signed, blob-externalized) stays the entry's source. */
|
|
466
|
+
priorRevision(projectKey, sessionId, entryId, identity, pruned) {
|
|
467
|
+
const rows = this.db.prepare("SELECT * FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? ORDER BY revision DESC").all(projectKey, sessionId, entryId);
|
|
468
|
+
if (rows.length === 0) return void 0;
|
|
469
|
+
if (pruned) return toRawEntry(rows[0]);
|
|
470
|
+
const match = rows.find((row) => this.identityOf(row) === identity);
|
|
471
|
+
return match ? toRawEntry(match) : void 0;
|
|
472
|
+
}
|
|
473
|
+
identityOf(row) {
|
|
474
|
+
const stored = row.content_hash;
|
|
475
|
+
const cached = this.identities.get(stored);
|
|
476
|
+
if (cached !== void 0) return cached;
|
|
477
|
+
let identity = stored;
|
|
478
|
+
try {
|
|
479
|
+
identity = hash(canonicalLcmEntry(JSON.parse(row.payload_json)));
|
|
480
|
+
} catch {
|
|
481
|
+
}
|
|
482
|
+
if (this.identities.size >= 5e4) this.identities.clear();
|
|
483
|
+
this.identities.set(stored, identity);
|
|
484
|
+
return identity;
|
|
485
|
+
}
|
|
432
486
|
readRaw(projectKey = this.project.key, sessionId) {
|
|
433
487
|
this.guard();
|
|
434
488
|
this.assertProjectKey(projectKey);
|
|
@@ -456,8 +510,9 @@ ALTER TABLE summary_node_revisions_next RENAME TO summary_node_revisions;`);
|
|
|
456
510
|
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 1) throw new Error("invalid raw page");
|
|
457
511
|
const scanLimit = options.scanLimit ?? LCM_SEARCH_SCAN_LIMIT;
|
|
458
512
|
if (!Number.isSafeInteger(scanLimit) || scanLimit < 1) throw new Error("invalid scan limit");
|
|
513
|
+
if (!validSearchFilter(options)) throw new Error("invalid raw search filter");
|
|
459
514
|
const query = options.query?.trim() ?? "";
|
|
460
|
-
if (query.length === 0) return this.recentPage(key, sessionId, offset, limit);
|
|
515
|
+
if (query.length === 0) return this.recentPage(key, sessionId, offset, limit, rowFilter(options));
|
|
461
516
|
if (options.mode === "regex") {
|
|
462
517
|
let pattern;
|
|
463
518
|
try {
|
|
@@ -465,40 +520,43 @@ ALTER TABLE summary_node_revisions_next RENAME TO summary_node_revisions;`);
|
|
|
465
520
|
} catch {
|
|
466
521
|
return { rows: [], total: 0, scanned: 0, complete: false };
|
|
467
522
|
}
|
|
468
|
-
return this.scanPage(key, sessionId, (content) => pattern.test(content), offset, limit, scanLimit, false);
|
|
523
|
+
return this.scanPage(key, sessionId, (content) => pattern.test(content), offset, limit, scanLimit, false, rowFilter(options));
|
|
469
524
|
}
|
|
470
525
|
const match = options.match ?? "any";
|
|
471
526
|
const phrases = searchPhrases(query, options.mode);
|
|
472
527
|
if (phrases.length === 0) return { rows: [], total: 0, scanned: 0, complete: true };
|
|
473
528
|
if (this.fts) {
|
|
474
529
|
try {
|
|
475
|
-
return this.indexPage(key, sessionId, ftsExpression(phrases, match), offset, limit);
|
|
530
|
+
return this.indexPage(key, sessionId, ftsExpression(phrases, match), offset, limit, rowFilter(options, "r"), options.order === "relevance");
|
|
476
531
|
} catch {
|
|
477
532
|
}
|
|
478
533
|
}
|
|
479
|
-
return this.scanPage(key, sessionId, phrasePredicate(phrases, match), offset, limit, scanLimit, true);
|
|
534
|
+
return this.scanPage(key, sessionId, phrasePredicate(phrases, match), offset, limit, scanLimit, true, rowFilter(options));
|
|
480
535
|
}
|
|
481
|
-
recentPage(key, sessionId, offset, limit) {
|
|
482
|
-
const
|
|
483
|
-
const
|
|
484
|
-
const
|
|
536
|
+
recentPage(key, sessionId, offset, limit, filter) {
|
|
537
|
+
const scope = `FROM raw_entries WHERE project_key=?${sessionId ? " AND session_id=?" : ""}${filter.sql}`;
|
|
538
|
+
const params = [key, ...sessionId ? [sessionId] : [], ...filter.params];
|
|
539
|
+
const total = Number(this.db.prepare(`SELECT count(*) n ${scope}`).get(...params).n);
|
|
540
|
+
const rows = this.db.prepare(`SELECT * ${scope} ORDER BY created_at DESC, revision DESC, rowid DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
485
541
|
return { rows: toRawEntries(rows), total, scanned: total, complete: true };
|
|
486
542
|
}
|
|
487
|
-
indexPage(key, sessionId, expression, offset, limit) {
|
|
488
|
-
const source = "FROM raw_entries_fts JOIN raw_entries r ON r.project_key=raw_entries_fts.project_key AND r.session_id=raw_entries_fts.session_id AND r.entry_id=raw_entries_fts.entry_id AND r.revision=CAST(raw_entries_fts.revision AS INTEGER) WHERE raw_entries_fts MATCH ? AND raw_entries_fts.project_key=?" + (sessionId ? " AND raw_entries_fts.session_id=?" : "");
|
|
489
|
-
const filters =
|
|
543
|
+
indexPage(key, sessionId, expression, offset, limit, filter, relevance) {
|
|
544
|
+
const source = "FROM raw_entries_fts JOIN raw_entries r ON r.project_key=raw_entries_fts.project_key AND r.session_id=raw_entries_fts.session_id AND r.entry_id=raw_entries_fts.entry_id AND r.revision=CAST(raw_entries_fts.revision AS INTEGER) WHERE raw_entries_fts MATCH ? AND raw_entries_fts.project_key=?" + (sessionId ? " AND raw_entries_fts.session_id=?" : "") + filter.sql;
|
|
545
|
+
const filters = [expression, key, ...sessionId ? [sessionId] : [], ...filter.params];
|
|
490
546
|
const total = Number(this.db.prepare(`SELECT count(*) n ${source}`).get(...filters).n);
|
|
491
|
-
const
|
|
547
|
+
const order = relevance ? "bm25(raw_entries_fts), r.created_at DESC, r.revision DESC, r.rowid DESC" : "r.created_at DESC, r.revision DESC, r.rowid DESC";
|
|
548
|
+
const rows = this.db.prepare(`SELECT r.* ${source} ORDER BY ${order} LIMIT ? OFFSET ?`).all(...filters, limit, offset);
|
|
492
549
|
return { rows: toRawEntries(rows), total, scanned: total, complete: true };
|
|
493
550
|
}
|
|
494
|
-
scanPage(key, sessionId, matches, offset, limit, scanLimit, degraded) {
|
|
495
|
-
const statement =
|
|
551
|
+
scanPage(key, sessionId, matches, offset, limit, scanLimit, degraded, filter) {
|
|
552
|
+
const statement = this.db.prepare(`SELECT session_id,entry_id,revision,content FROM raw_entries WHERE project_key=?${sessionId ? " AND session_id=?" : ""}${filter.sql} ORDER BY created_at DESC, revision DESC, rowid DESC LIMIT ? OFFSET ?`);
|
|
553
|
+
const scope = [key, ...sessionId ? [sessionId] : [], ...filter.params];
|
|
496
554
|
const found = [];
|
|
497
555
|
let scanned = 0;
|
|
498
556
|
let exhausted = false;
|
|
499
557
|
while (scanned < scanLimit) {
|
|
500
558
|
const size = Math.min(LCM_SCAN_BATCH, scanLimit - scanned);
|
|
501
|
-
const batch =
|
|
559
|
+
const batch = statement.all(...scope, size, scanned);
|
|
502
560
|
scanned += batch.length;
|
|
503
561
|
for (const row of batch) if (matches(row.content)) found.push({ sessionId: row.session_id, entryId: row.entry_id, revision: Number(row.revision) });
|
|
504
562
|
if (batch.length < size) {
|
|
@@ -954,13 +1012,13 @@ function migrateSessions(options) {
|
|
|
954
1012
|
}
|
|
955
1013
|
counts.eligible++;
|
|
956
1014
|
const payloadJson = canonicalLcmPayload(row);
|
|
957
|
-
const contentHash =
|
|
1015
|
+
const contentHash = hash2(canonicalLcmEntry(row));
|
|
958
1016
|
const identity = JSON.stringify([projectKey, sessionId, row.id, contentHash]);
|
|
959
1017
|
const progress = { projectKey, sessionPath: file, sourceHash, lineOrdinal: ordinal, entryId: row.id, contentHash };
|
|
960
1018
|
const duplicate = drySeen.has(identity) || database?.prepare("SELECT 1 FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? AND content_hash=?").get(projectKey, sessionId, row.id, contentHash) !== void 0;
|
|
961
1019
|
if (duplicate) counts.skippedDuplicate++;
|
|
962
1020
|
else if (ledger) {
|
|
963
|
-
ledger.appendRaw({
|
|
1021
|
+
const persisted = ledger.appendRaw({
|
|
964
1022
|
projectKey,
|
|
965
1023
|
sessionId,
|
|
966
1024
|
entryId: row.id,
|
|
@@ -972,7 +1030,8 @@ function migrateSessions(options) {
|
|
|
972
1030
|
...cwd ? { recordedCwd: cwd } : {},
|
|
973
1031
|
createdAt: at
|
|
974
1032
|
});
|
|
975
|
-
counts.imported++;
|
|
1033
|
+
if (persisted.contentHash === contentHash) counts.imported++;
|
|
1034
|
+
else counts.skippedDuplicate++;
|
|
976
1035
|
} else if (!duplicate) counts.imported++;
|
|
977
1036
|
drySeen.add(identity);
|
|
978
1037
|
if (ledger) database.prepare("INSERT OR IGNORE INTO migration_checkpoints(project_key,session_path,source_hash,line_ordinal,entry_id,content_hash) VALUES(?,?,?,?,?,?)").run(projectKey, file, sourceHash, ordinal, row.id, contentHash);
|
|
@@ -996,6 +1055,7 @@ function migrateSessions(options) {
|
|
|
996
1055
|
import crypto3 from "node:crypto";
|
|
997
1056
|
var DEFAULT_LCM_BUDGET = { calls: Number.POSITIVE_INFINITY, inputTokens: Number.POSITIVE_INFINITY, outputTokens: Number.POSITIVE_INFINITY, cost: Number.POSITIVE_INFINITY, wallMs: Number.POSITIVE_INFINITY, sessionCalls: Number.POSITIVE_INFINITY };
|
|
998
1057
|
var DEFAULT_LEAF_ENTRIES = 32;
|
|
1058
|
+
var EMPTY_TAINT = /* @__PURE__ */ new Set();
|
|
999
1059
|
var DEFAULT_MAINTENANCE_CONCURRENCY = 3;
|
|
1000
1060
|
var LEASE_MS = 3e4;
|
|
1001
1061
|
var LEASE_SWEEP_GRACE_MS = 6e4;
|
|
@@ -1133,8 +1193,26 @@ var LcmMaintenance = class {
|
|
|
1133
1193
|
});
|
|
1134
1194
|
}
|
|
1135
1195
|
/** Scoped by the live source set when given; drops a condensed child and a contained node. */
|
|
1196
|
+
/** The oversized deterministic leaves and every ancestor that inherited one. */
|
|
1197
|
+
tainted(nodes) {
|
|
1198
|
+
const oversized = nodes.filter((node) => node.kind === "leaf" && node.modelHash === "emergency" && node.sources.length > this.maxLeaf).map((node) => node.nodeId);
|
|
1199
|
+
if (oversized.length === 0) return EMPTY_TAINT;
|
|
1200
|
+
const parents = /* @__PURE__ */ new Map();
|
|
1201
|
+
for (const node of nodes) for (const child of node.children) parents.set(child, [...parents.get(child) ?? [], node.nodeId]);
|
|
1202
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
1203
|
+
const queue = [...oversized];
|
|
1204
|
+
while (queue.length > 0) {
|
|
1205
|
+
const nodeId = queue.pop();
|
|
1206
|
+
if (excluded.has(nodeId)) continue;
|
|
1207
|
+
excluded.add(nodeId);
|
|
1208
|
+
for (const parent of parents.get(nodeId) ?? []) queue.push(parent);
|
|
1209
|
+
}
|
|
1210
|
+
return excluded;
|
|
1211
|
+
}
|
|
1136
1212
|
getFrontier(sessionId, activeSources) {
|
|
1137
|
-
const
|
|
1213
|
+
const all = this.listNodes(1e5);
|
|
1214
|
+
const taint = this.tainted(all);
|
|
1215
|
+
const nodes = all.filter((n) => !taint.has(n.nodeId) && n.state === "ready" && n.policyHash === this.policyHash && (!sessionId || activeSources !== void 0 || n.sessionId === sessionId) && (!activeSources || n.sources.every((s) => activeSources.has(this.sourceKey(s)))));
|
|
1138
1216
|
const condensed = new Set(nodes.flatMap((n) => n.children));
|
|
1139
1217
|
const standing = nodes.filter((n) => !condensed.has(n.nodeId));
|
|
1140
1218
|
const ranked = [...standing].sort((a, b) => b.sources.length - a.sources.length || a.nodeId.localeCompare(b.nodeId));
|
|
@@ -1267,7 +1345,9 @@ var LcmMaintenance = class {
|
|
|
1267
1345
|
selectLeaf(entries = this.ledger.readRaw(this.projectKey), activeSources) {
|
|
1268
1346
|
const first = entries[0];
|
|
1269
1347
|
if (!first) return [];
|
|
1270
|
-
const
|
|
1348
|
+
const all = this.listNodes(1e5);
|
|
1349
|
+
const taint = this.tainted(all);
|
|
1350
|
+
const nodes = all.filter((n) => !taint.has(n.nodeId) && n.state !== "failed" && n.policyHash === this.policyHash && (n.sessionId === first.sessionId || activeSources !== void 0 && n.state === "ready") && (!activeSources || n.sources.every((s) => activeSources.has(this.sourceKey(s)))));
|
|
1271
1351
|
const covered = new Set(nodes.flatMap((n) => n.sources.map((s) => this.sourceKey(s))));
|
|
1272
1352
|
const candidates = entries.filter((e) => e.sessionId === first.sessionId && !covered.has(this.sourceKey(e)));
|
|
1273
1353
|
const picked = [];
|
|
@@ -1282,7 +1362,9 @@ var LcmMaintenance = class {
|
|
|
1282
1362
|
return picked;
|
|
1283
1363
|
}
|
|
1284
1364
|
selectCondensation(sessionId, activeSources) {
|
|
1285
|
-
const
|
|
1365
|
+
const all = this.listNodes(1e5);
|
|
1366
|
+
const taint = this.tainted(all);
|
|
1367
|
+
const nodes = all.filter((n) => !taint.has(n.nodeId) && n.state === "ready" && n.policyHash === this.policyHash && (!sessionId || n.sessionId === sessionId) && (!activeSources || n.sources.every((s) => activeSources.has(this.sourceKey(s)))));
|
|
1286
1368
|
const eligible = new Set(nodes.map((n) => n.nodeId));
|
|
1287
1369
|
const edges = this.ledger.readOnly((db) => db.prepare("SELECT e.parent_id,e.child_id FROM summary_edges e JOIN summary_nodes parent ON parent.node_id=e.parent_id AND parent.project_key=? JOIN summary_nodes child ON child.node_id=e.child_id AND child.project_key=parent.project_key WHERE parent.project_key=?").all(this.projectKey, this.projectKey));
|
|
1288
1370
|
const consumed = new Set(edges.filter((e) => eligible.has(e.parent_id)).map((e) => e.child_id));
|
|
@@ -1574,6 +1656,11 @@ var MAX_REPAIR_LOG = 8;
|
|
|
1574
1656
|
var CLAIMABLE_JOB_LIMIT = 256;
|
|
1575
1657
|
var FAILED_JOB_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
1576
1658
|
var NODE_ADDRESS_LABEL = "address: ";
|
|
1659
|
+
var TAIL_DIGEST_MAX_BYTES = Math.floor(MAX_SUMMARY_BYTES * 0.2);
|
|
1660
|
+
var FRONTIER_REQUEST_BYTES = 4096;
|
|
1661
|
+
var MIN_SHARED_BLOCK_BYTES = 512;
|
|
1662
|
+
var ACTIVE_REQUEST_SHARE = 0.4;
|
|
1663
|
+
var UPGRADE_CANDIDATES = 256;
|
|
1577
1664
|
var BLOCK_SEPARATOR = "\n\n";
|
|
1578
1665
|
var WITHHELD_EXPAND_LABEL = "; expand: ";
|
|
1579
1666
|
var withheldHeadline = (count) => `\u2026 withheld ${count} frontier node${count === 1 ? "" : "s"} that did not fit`;
|
|
@@ -1603,17 +1690,19 @@ ${NODE_ADDRESS_LABEL}${lcmSummaryAddress(node.nodeId)}`;
|
|
|
1603
1690
|
const parts = [...requestBlock ? [requestBlock] : [], head, ...notice ? [notice] : []];
|
|
1604
1691
|
return `${parts.join(BLOCK_SEPARATOR)}${footer}`;
|
|
1605
1692
|
};
|
|
1606
|
-
var renderAddressedFrontier = (frontier, requestLines = []) => {
|
|
1693
|
+
var renderAddressedFrontier = (frontier, requestLines = [], surround = {}) => {
|
|
1607
1694
|
const blocks = frontier.filter((node) => Boolean(node.text)).map((node) => ({ node, head: `${node.text ?? ""}
|
|
1608
1695
|
${NODE_ADDRESS_LABEL}${lcmSummaryAddress(node.nodeId)}` }));
|
|
1609
1696
|
if (blocks.length === 0) return "";
|
|
1610
|
-
const
|
|
1611
|
-
${requestLines.join("\n")}` : "";
|
|
1697
|
+
const leads = [requestLines.length > 0 ? `[Compaction Request]
|
|
1698
|
+
${requestLines.join("\n")}` : "", surround.lead ?? ""].filter((block) => block.length > 0);
|
|
1699
|
+
const trail = clipUtf8(surround.trail ?? "", TAIL_DIGEST_MAX_BYTES);
|
|
1700
|
+
const requestBlock = leads.join(BLOCK_SEPARATOR);
|
|
1612
1701
|
const footer = `
|
|
1613
1702
|
|
|
1614
1703
|
${LCM_RECOVERY_POINTER}`;
|
|
1615
1704
|
const separator = utf8Bytes(BLOCK_SEPARATOR);
|
|
1616
|
-
const footerBytes = utf8Bytes(footer);
|
|
1705
|
+
const footerBytes = utf8Bytes(footer) + (trail ? utf8Bytes(trail) + separator : 0);
|
|
1617
1706
|
const requestBytes = requestBlock ? utf8Bytes(requestBlock) + separator : 0;
|
|
1618
1707
|
const headBytes = blocks.map((block) => utf8Bytes(block.head));
|
|
1619
1708
|
const floorFor = (kept2) => kept2 === blocks.length ? 0 : separator + utf8Bytes(withheldHeadline(blocks.length - kept2));
|
|
@@ -1629,7 +1718,38 @@ ${LCM_RECOVERY_POINTER}`;
|
|
|
1629
1718
|
kept.push(index);
|
|
1630
1719
|
body = grown;
|
|
1631
1720
|
}
|
|
1632
|
-
if (
|
|
1721
|
+
if (withheld.length > 0) {
|
|
1722
|
+
const addressLines = blocks.map(({ node }) => `
|
|
1723
|
+
${NODE_ADDRESS_LABEL}${lcmSummaryAddress(node.nodeId)}`);
|
|
1724
|
+
let room2 = MAX_SUMMARY_BYTES - requestBytes - footerBytes - separator * (blocks.length - 1) - addressLines.reduce((total, line) => total + utf8Bytes(line), 0);
|
|
1725
|
+
const textBytes = blocks.map(({ node }) => utf8Bytes(node.text ?? ""));
|
|
1726
|
+
const budget = new Array(blocks.length).fill(0);
|
|
1727
|
+
let open = blocks.map((_unused, index) => index);
|
|
1728
|
+
while (open.length > 0 && room2 > 0) {
|
|
1729
|
+
const share = Math.floor(room2 / open.length);
|
|
1730
|
+
if (share < 1) break;
|
|
1731
|
+
const fitting = open.filter((index) => textBytes[index] <= share);
|
|
1732
|
+
if (fitting.length === 0) {
|
|
1733
|
+
for (const index of open) budget[index] = share;
|
|
1734
|
+
room2 -= share * open.length;
|
|
1735
|
+
break;
|
|
1736
|
+
}
|
|
1737
|
+
for (const index of fitting) {
|
|
1738
|
+
budget[index] = textBytes[index];
|
|
1739
|
+
room2 -= textBytes[index];
|
|
1740
|
+
}
|
|
1741
|
+
open = open.filter((index) => !fitting.includes(index));
|
|
1742
|
+
}
|
|
1743
|
+
if (budget.every((value, index) => value >= Math.min(textBytes[index], MIN_SHARED_BLOCK_BYTES))) {
|
|
1744
|
+
const shared = blocks.map(({ node }, index) => {
|
|
1745
|
+
const text = clipUtf8(node.text ?? "", budget[index]);
|
|
1746
|
+
return text ? `${text}${addressLines[index]}` : addressLines[index].slice(1);
|
|
1747
|
+
});
|
|
1748
|
+
const parts2 = [...requestBlock ? [requestBlock] : [], ...shared, ...trail ? [trail] : []];
|
|
1749
|
+
return `${parts2.join(BLOCK_SEPARATOR)}${footer}`;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
if (kept.length === 0) return clipOversized(blocks, headBytes, `${trail ? `${BLOCK_SEPARATOR}${trail}` : ""}${footer}`, separator, requestBlock);
|
|
1633
1753
|
const room = MAX_SUMMARY_BYTES - body - footerBytes - (withheld.length === 0 ? 0 : separator);
|
|
1634
1754
|
const notice = withheld.length === 0 ? "" : withheldNotice(withheld.map((index) => blocks[index].node.nodeId), Math.max(utf8Bytes(withheldHeadline(withheld.length)), Math.floor(room / 2)));
|
|
1635
1755
|
const perNode = Math.max(0, Math.floor((room - utf8Bytes(notice)) / kept.length) - 1);
|
|
@@ -1639,7 +1759,7 @@ ${LCM_RECOVERY_POINTER}`;
|
|
|
1639
1759
|
return addresses && utf8Bytes(addresses) <= perNode ? `${head}
|
|
1640
1760
|
${addresses}` : head;
|
|
1641
1761
|
});
|
|
1642
|
-
const parts = [...requestBlock ? [requestBlock] : [], ...rendered, ...notice ? [notice] : []];
|
|
1762
|
+
const parts = [...requestBlock ? [requestBlock] : [], ...rendered, ...notice ? [notice] : [], ...trail ? [trail] : []];
|
|
1643
1763
|
return `${parts.join(BLOCK_SEPARATOR)}${footer}`;
|
|
1644
1764
|
};
|
|
1645
1765
|
var LcmRuntime = class {
|
|
@@ -1650,6 +1770,8 @@ var LcmRuntime = class {
|
|
|
1650
1770
|
abort = new AbortController();
|
|
1651
1771
|
writePending = Promise.resolve();
|
|
1652
1772
|
maintenancePending = Promise.resolve();
|
|
1773
|
+
maintenanceRunning = false;
|
|
1774
|
+
maintenanceQueued = false;
|
|
1653
1775
|
closed = false;
|
|
1654
1776
|
dirty = false;
|
|
1655
1777
|
degradedError;
|
|
@@ -1788,7 +1910,7 @@ var LcmRuntime = class {
|
|
|
1788
1910
|
const pending = [];
|
|
1789
1911
|
for (const entry of entries) {
|
|
1790
1912
|
const payloadJson = canonicalLcmPayload(entry);
|
|
1791
|
-
const contentHash = hash(
|
|
1913
|
+
const contentHash = hash(canonicalLcmEntry(entry));
|
|
1792
1914
|
const known = this.persisted.get(entry);
|
|
1793
1915
|
if (known?.contentHash === contentHash) active.add(known.key);
|
|
1794
1916
|
else pending.push({ entry, payloadJson, contentHash });
|
|
@@ -1922,7 +2044,21 @@ var LcmRuntime = class {
|
|
|
1922
2044
|
}
|
|
1923
2045
|
scheduleMaintenance() {
|
|
1924
2046
|
if (this.closed) return;
|
|
1925
|
-
|
|
2047
|
+
if (this.maintenanceRunning) {
|
|
2048
|
+
this.maintenanceQueued = true;
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
this.maintenanceRunning = true;
|
|
2052
|
+
const run = this.maintenancePending.then(async () => {
|
|
2053
|
+
await this.runMaintenance();
|
|
2054
|
+
while (this.maintenanceQueued && !this.closed) {
|
|
2055
|
+
this.maintenanceQueued = false;
|
|
2056
|
+
await this.runMaintenance();
|
|
2057
|
+
}
|
|
2058
|
+
}).finally(() => {
|
|
2059
|
+
this.maintenanceRunning = false;
|
|
2060
|
+
this.maintenanceQueued = false;
|
|
2061
|
+
});
|
|
1926
2062
|
this.maintenancePending = run.catch((error) => {
|
|
1927
2063
|
this.degradedError = error;
|
|
1928
2064
|
});
|
|
@@ -2007,12 +2143,31 @@ var LcmRuntime = class {
|
|
|
2007
2143
|
await Promise.all(Array.from({ length: Math.min(this.maintenance.concurrencyLimit, items.length) }, (_unused, slot) => worker(slot)));
|
|
2008
2144
|
};
|
|
2009
2145
|
this.reclaimExpiredLeases();
|
|
2146
|
+
const inputBound = this.options.lcmMaxInputChars ?? DEFAULT_LCM_MAX_INPUT_CHARS;
|
|
2147
|
+
const fitsBound = (node) => {
|
|
2148
|
+
if (node.sources.length > (this.options.maxLeafEntries ?? DEFAULT_LEAF_ENTRIES) * fanIn) return false;
|
|
2149
|
+
try {
|
|
2150
|
+
return utf8Bytes(inputFor(node)) <= inputBound;
|
|
2151
|
+
} catch {
|
|
2152
|
+
return false;
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2010
2155
|
for (let pass = 0; pass < passes && !this.closed && Date.now() < runDeadline; pass += 1) {
|
|
2011
|
-
const
|
|
2012
|
-
const
|
|
2156
|
+
const leaves = [];
|
|
2157
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2158
|
+
while (leaves.length < this.maintenance.concurrencyLimit) {
|
|
2159
|
+
const rows = this.maintenance.selectLeaf(raw, this.activeSources);
|
|
2160
|
+
if (rows.length === 0) break;
|
|
2161
|
+
const created = this.maintenance.createLeaf(rows);
|
|
2162
|
+
const node = created ? this.maintenance.getNode(created.nodeId) : void 0;
|
|
2163
|
+
if (!node || seen.has(node.nodeId)) break;
|
|
2164
|
+
seen.add(node.nodeId);
|
|
2165
|
+
leaves.push(node);
|
|
2166
|
+
}
|
|
2013
2167
|
const batch = [];
|
|
2014
2168
|
const queued = /* @__PURE__ */ new Set();
|
|
2015
|
-
|
|
2169
|
+
for (const leaf of leaves) {
|
|
2170
|
+
if (leaf.state !== "pending") continue;
|
|
2016
2171
|
const job = this.maintenance.jobForNode(leaf.nodeId);
|
|
2017
2172
|
if (job && this.maintenance.isClaimable(job)) {
|
|
2018
2173
|
batch.push({ job, node: leaf });
|
|
@@ -2033,9 +2188,8 @@ var LcmRuntime = class {
|
|
|
2033
2188
|
if (job && this.maintenance.isClaimable(job)) await runJob(job, node);
|
|
2034
2189
|
}
|
|
2035
2190
|
}
|
|
2036
|
-
if (
|
|
2037
|
-
const
|
|
2038
|
-
const upgrade = upgrades[0];
|
|
2191
|
+
if (leaves.length === 0 && pending.length === 0 && children.length < fanIn) {
|
|
2192
|
+
const upgrade = this.maintenance.selectUpgrades(sessionId, this.activeSources, UPGRADE_CANDIDATES).find(fitsBound);
|
|
2039
2193
|
if (!upgrade) break;
|
|
2040
2194
|
const job = this.maintenance.reopen(upgrade.nodeId);
|
|
2041
2195
|
await runJob(job, upgrade);
|
|
@@ -2066,71 +2220,105 @@ var LcmRuntime = class {
|
|
|
2066
2220
|
payloadJson,
|
|
2067
2221
|
parentEntryId: entry.parentId ?? null
|
|
2068
2222
|
});
|
|
2069
|
-
persistedEntries.set(
|
|
2223
|
+
persistedEntries.set(entry.id, stored);
|
|
2070
2224
|
}
|
|
2071
2225
|
});
|
|
2072
2226
|
this.invalidateFrontier();
|
|
2073
2227
|
const firstKeptIndex = input.firstKeptEntryId ? input.branchEntries.findIndex((entry) => entry.id === input.firstKeptEntryId) : input.branchEntries.length;
|
|
2074
2228
|
const sourceEntries = input.branchEntries.slice(0, firstKeptIndex < 0 ? input.branchEntries.length : firstKeptIndex);
|
|
2075
2229
|
const selectedStored = sourceEntries.map((entry) => {
|
|
2076
|
-
const
|
|
2077
|
-
const stored = persistedEntries.get(`${entry.id}:${payloadHash}`);
|
|
2230
|
+
const stored = persistedEntries.get(entry.id);
|
|
2078
2231
|
if (!stored) throw new Error("compaction source was not persisted");
|
|
2079
2232
|
return stored;
|
|
2080
2233
|
});
|
|
2081
2234
|
const activeSources = new Set(selectedStored.map((entry) => this.sourceKey(entry)));
|
|
2082
2235
|
const frontier = this.maintenance.getFrontier(input.sessionId, activeSources);
|
|
2083
2236
|
const coveredSources = new Set(frontier.flatMap((node) => node.sources.map((source) => this.sourceKey(source))));
|
|
2084
|
-
const
|
|
2237
|
+
const keptEntry = firstKeptIndex >= 0 ? input.branchEntries[firstKeptIndex] : void 0;
|
|
2238
|
+
const keptRole = keptEntry?.type === "message" ? keptEntry.message.role : void 0;
|
|
2239
|
+
const splitTurn = input.isSplitTurn ?? keptRole !== "user";
|
|
2240
|
+
const requestFor = (maxBytes) => splitTurn ? activeRequestBlock(sourceEntries, selectedStored, maxBytes) : "";
|
|
2085
2241
|
const instructionDetails = instructions.ok && (instructions.requestLines.length > 0 || instructions.policy.preserveCount > 0) ? { instructionPolicy: instructions.policy } : void 0;
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2242
|
+
const uncovered = selectedStored.flatMap((stored, index) => coveredSources.has(this.sourceKey(stored)) ? [] : [index]);
|
|
2243
|
+
if (uncovered.length === 0) {
|
|
2244
|
+
const summary = renderAddressedFrontier(frontier, requestLines, { lead: requestFor(FRONTIER_REQUEST_BYTES) });
|
|
2245
|
+
if (summary) {
|
|
2246
|
+
return {
|
|
2247
|
+
summary,
|
|
2248
|
+
firstKeptEntryId: input.firstKeptEntryId,
|
|
2249
|
+
tokensBefore: input.tokensBefore,
|
|
2250
|
+
source: "ready-frontier",
|
|
2251
|
+
...instructionDetails ? { details: instructionDetails } : {}
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2094
2254
|
}
|
|
2095
2255
|
if (selectedStored.length === 0) throw new Error("LCM emergency fallback has no persisted sources");
|
|
2096
|
-
const
|
|
2097
|
-
const
|
|
2098
|
-
const
|
|
2099
|
-
const
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2256
|
+
const tailEntries = uncovered.length > 0 ? uncovered.map((index) => sourceEntries[index]) : sourceEntries;
|
|
2257
|
+
const tailStored = uncovered.length > 0 ? uncovered.map((index) => selectedStored[index]) : selectedStored;
|
|
2258
|
+
const outputBound = this.options.lcmMaxOutputChars ?? 4096;
|
|
2259
|
+
const nodeTextFor = (entries, stored) => {
|
|
2260
|
+
const digest = tailDigest(entries, stored, Math.min(TAIL_DIGEST_MAX_BYTES, outputBound));
|
|
2261
|
+
if (digest) return digest;
|
|
2262
|
+
const payloads = entries.map((entry) => canonicalLcmPayload(entry)).join("\n");
|
|
2263
|
+
return emergencyReduce(payloads, outputBound, stored.map((entry) => ({ sessionId: entry.sessionId, entryId: entry.entryId, revision: entry.revision, payloadHash: entry.payloadHash })), requestLines);
|
|
2264
|
+
};
|
|
2265
|
+
const fallback = nodeTextFor(tailEntries, tailStored);
|
|
2266
|
+
const compose = (nodeText) => {
|
|
2267
|
+
const partial = frontier.length > 0 ? renderAddressedFrontier(frontier, requestLines, { lead: requestFor(FRONTIER_REQUEST_BYTES), trail: nodeText }) : "";
|
|
2268
|
+
if (partial) return partial;
|
|
2269
|
+
const pointer = nodeText.includes(LCM_RECOVERY_POINTER) ? "" : LCM_RECOVERY_POINTER;
|
|
2270
|
+
const separator = utf8Bytes(BLOCK_SEPARATOR);
|
|
2271
|
+
const room = Math.max(0, outputBound - (pointer ? utf8Bytes(pointer) + separator : 0));
|
|
2272
|
+
const request = requestLines.length > 0 ? `[Compaction Request]
|
|
2273
|
+
${requestLines.join("\n")}` : "";
|
|
2274
|
+
const head = [request, requestFor(Math.floor(room * ACTIVE_REQUEST_SHARE))].filter((block) => block.length > 0);
|
|
2275
|
+
const headBytes = head.length > 0 ? utf8Bytes(head.join(BLOCK_SEPARATOR)) + separator : 0;
|
|
2276
|
+
const tailRoom = Math.max(0, room - headBytes);
|
|
2277
|
+
const tail = tailDigest(tailEntries, tailStored, Math.min(TAIL_DIGEST_MAX_BYTES, tailRoom)) || clipUtf8(nodeText, tailRoom);
|
|
2278
|
+
const body = clipUtf8([...head, tail].join(BLOCK_SEPARATOR), room);
|
|
2279
|
+
return pointer ? `${body}${BLOCK_SEPARATOR}${pointer}` : body;
|
|
2280
|
+
};
|
|
2281
|
+
const leafCap = Math.max(1, this.options.maxLeafEntries ?? DEFAULT_LEAF_ENTRIES);
|
|
2282
|
+
const inputCap = this.options.lcmMaxInputChars ?? DEFAULT_LCM_MAX_INPUT_CHARS;
|
|
2283
|
+
const spans = [];
|
|
2284
|
+
let span = [];
|
|
2285
|
+
let spanChars = 0;
|
|
2286
|
+
for (let index = 0; index < tailStored.length; index += 1) {
|
|
2287
|
+
const size = tailStored[index].payloadJson.length;
|
|
2288
|
+
if (span.length > 0 && (span.length >= leafCap || spanChars + size > inputCap)) {
|
|
2289
|
+
spans.push(span);
|
|
2290
|
+
span = [];
|
|
2291
|
+
spanChars = 0;
|
|
2292
|
+
}
|
|
2293
|
+
span.push(index);
|
|
2294
|
+
spanChars += size;
|
|
2112
2295
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
const
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
}
|
|
2124
|
-
|
|
2125
|
-
if (!
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
...instructionDetails ? { details: instructionDetails } : {}
|
|
2132
|
-
};
|
|
2296
|
+
if (span.length > 0) spans.push(span);
|
|
2297
|
+
for (const indices of spans) {
|
|
2298
|
+
const stored = indices.map((index) => tailStored[index]);
|
|
2299
|
+
const entries = indices.map((index) => tailEntries[index]);
|
|
2300
|
+
const leaf = this.maintenance.createLeaf(stored);
|
|
2301
|
+
if (!leaf) throw new Error("LCM emergency fallback node was not created");
|
|
2302
|
+
const persisted = this.maintenance.getNode(leaf.nodeId);
|
|
2303
|
+
if (persisted?.state === "ready") {
|
|
2304
|
+
if (!persisted.text) throw new Error("LCM emergency fallback text is missing");
|
|
2305
|
+
continue;
|
|
2306
|
+
}
|
|
2307
|
+
const job = this.maintenance.jobForNode(leaf.nodeId);
|
|
2308
|
+
if (!job) throw new Error("LCM emergency fallback job was not created");
|
|
2309
|
+
try {
|
|
2310
|
+
this.maintenance.completeEmergency(this.maintenance.claimEmergency(job.jobId), nodeTextFor(entries, stored));
|
|
2311
|
+
} catch (error) {
|
|
2312
|
+
if (!isLcmRejection(error)) throw error;
|
|
2313
|
+
}
|
|
2133
2314
|
}
|
|
2315
|
+
return {
|
|
2316
|
+
summary: compose(fallback),
|
|
2317
|
+
firstKeptEntryId: input.firstKeptEntryId,
|
|
2318
|
+
tokensBefore: input.tokensBefore,
|
|
2319
|
+
source: "emergency",
|
|
2320
|
+
...instructionDetails ? { details: instructionDetails } : {}
|
|
2321
|
+
};
|
|
2134
2322
|
}
|
|
2135
2323
|
report() {
|
|
2136
2324
|
const day = new Date(Date.now()).toISOString().slice(0, 10);
|