omp-fabric 1.3.3 → 1.4.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/agents/handoff.d.ts.map +1 -1
  3. package/dist/chunks/{chunk-WZAH7BO2.js → chunk-2J2GOB4A.js} +30 -11
  4. package/dist/chunks/chunk-2J2GOB4A.js.map +7 -0
  5. package/dist/chunks/{chunk-2WDZT27Q.js → chunk-3H5LZWAG.js} +253 -4
  6. package/dist/chunks/chunk-3H5LZWAG.js.map +7 -0
  7. package/dist/chunks/{chunk-PYXBHMSL.js → chunk-IMSGR6OW.js} +3 -3
  8. package/dist/chunks/chunk-IY3IQPRQ.js +308 -0
  9. package/dist/chunks/chunk-IY3IQPRQ.js.map +7 -0
  10. package/dist/chunks/{chunk-VSSC4IDU.js → chunk-RTDJFHGH.js} +2 -2
  11. package/dist/chunks/{chunk-45URY66G.js → chunk-S4I7RMR5.js} +3 -3
  12. package/dist/chunks/{chunk-ZP3S74LW.js → chunk-SG4GVSWX.js} +49 -26
  13. package/dist/chunks/chunk-SG4GVSWX.js.map +7 -0
  14. package/dist/chunks/{chunk-JOWMM3EX.js → chunk-WPU4PKXQ.js} +135 -24
  15. package/dist/chunks/chunk-WPU4PKXQ.js.map +7 -0
  16. package/dist/chunks/{chunk-Z5AP4M6K.js → chunk-YOAZ5SO7.js} +12 -2
  17. package/dist/chunks/{chunk-Z5AP4M6K.js.map → chunk-YOAZ5SO7.js.map} +2 -2
  18. package/dist/compaction/branch-summary.d.ts +1 -1
  19. package/dist/compaction/branch-summary.d.ts.map +1 -1
  20. package/dist/compaction/hook.d.ts +21 -1
  21. package/dist/compaction/hook.d.ts.map +1 -1
  22. package/dist/compaction/hook.js +1 -1
  23. package/dist/compaction/lcm-maintenance.d.ts +103 -0
  24. package/dist/compaction/lcm-maintenance.d.ts.map +1 -0
  25. package/dist/compaction/lcm-model.d.ts +41 -0
  26. package/dist/compaction/lcm-model.d.ts.map +1 -0
  27. package/dist/compaction/lcm-runtime.d.ts +106 -0
  28. package/dist/compaction/lcm-runtime.d.ts.map +1 -0
  29. package/dist/config-migrations.d.ts +1 -1
  30. package/dist/config-migrations.d.ts.map +1 -1
  31. package/dist/config.d.ts +7 -1
  32. package/dist/config.d.ts.map +1 -1
  33. package/dist/fabric-runtime-state.d.ts +2 -0
  34. package/dist/fabric-runtime-state.d.ts.map +1 -1
  35. package/dist/fabric-runtime-state.js +12 -7
  36. package/dist/fabric-runtime-state.js.map +2 -2
  37. package/dist/fabric-state.d.ts +2 -1
  38. package/dist/fabric-state.d.ts.map +1 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1030 -108
  41. package/dist/index.js.map +4 -4
  42. package/dist/memory/lcm-adapter.d.ts +97 -0
  43. package/dist/memory/lcm-adapter.d.ts.map +1 -0
  44. package/dist/providers/memory-provider.d.ts +8 -0
  45. package/dist/providers/memory-provider.d.ts.map +1 -1
  46. package/dist/providers/memory-provider.js +2 -1
  47. package/dist/residency/host.js +6 -5
  48. package/dist/residency/omp-entry.js +6 -5
  49. package/dist/residency/omp-entry.js.map +1 -1
  50. package/dist/runtime/core-override-guest-types.js +1 -1
  51. package/dist/runtime/guest-types.d.ts +1 -1
  52. package/dist/runtime/guest-types.d.ts.map +1 -1
  53. package/dist/runtime/guest-types.js +1 -1
  54. package/dist/storage/lcm-ledger.d.ts +107 -0
  55. package/dist/storage/lcm-ledger.d.ts.map +1 -0
  56. package/dist/storage/lcm-migration.d.ts +67 -0
  57. package/dist/storage/lcm-migration.d.ts.map +1 -0
  58. package/dist/ui/settings.js +5 -5
  59. package/dist/ui/settings.js.map +2 -2
  60. package/docs/adr/0001-lcm-durable-context-ledger.md +6 -0
  61. package/docs/agents/domain.md +25 -0
  62. package/docs/agents/issue-tracker.md +25 -0
  63. package/docs/agents/triage-labels.md +11 -0
  64. package/docs/compaction.md +78 -312
  65. package/docs/configuration.md +3 -3
  66. package/package.json +1 -1
  67. package/dist/chunks/chunk-2WDZT27Q.js.map +0 -7
  68. package/dist/chunks/chunk-JOWMM3EX.js.map +0 -7
  69. package/dist/chunks/chunk-WZAH7BO2.js.map +0 -7
  70. package/dist/chunks/chunk-ZP3S74LW.js.map +0 -7
  71. /package/dist/chunks/{chunk-PYXBHMSL.js.map → chunk-IMSGR6OW.js.map} +0 -0
  72. /package/dist/chunks/{chunk-VSSC4IDU.js.map → chunk-RTDJFHGH.js.map} +0 -0
  73. /package/dist/chunks/{chunk-45URY66G.js.map → chunk-S4I7RMR5.js.map} +0 -0
@@ -0,0 +1,308 @@
1
+ // src/storage/lcm-ledger.ts
2
+ import { DatabaseSync } from "node:sqlite";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import crypto from "node:crypto";
6
+ var LCM_LEDGER_WARNING_BYTES = 8 * 1024 ** 3;
7
+ var LCM_LEDGER_MAINTENANCE_BYTES = 10 * 1024 ** 3;
8
+ function canonicalLcmPayload(value) {
9
+ const normalized = JSON.parse(JSON.stringify(value));
10
+ const encode = (item) => {
11
+ if (Array.isArray(item)) return `[${item.map(encode).join(",")}]`;
12
+ if (item !== null && typeof item === "object") return `{${Object.keys(item).sort().map((key) => `${JSON.stringify(key)}:${encode(item[key])}`).join(",")}}`;
13
+ return JSON.stringify(item);
14
+ };
15
+ return encode(normalized);
16
+ }
17
+ function hashLcmPayload(value) {
18
+ return crypto.createHash("sha256").update(canonicalLcmPayload(value), "utf8").digest("hex");
19
+ }
20
+ var SCHEMA = `
21
+ CREATE TABLE IF NOT EXISTS schema_metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL);
22
+ CREATE TABLE IF NOT EXISTS projects (project_key TEXT PRIMARY KEY, identity_json TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL);
23
+ CREATE TABLE IF NOT EXISTS project_aliases (alias TEXT PRIMARY KEY, project_key TEXT NOT NULL REFERENCES projects(project_key));
24
+ CREATE TABLE IF NOT EXISTS sessions (project_key TEXT NOT NULL, session_id TEXT NOT NULL, created_at INTEGER NOT NULL, PRIMARY KEY(project_key, session_id));
25
+ CREATE TABLE IF NOT EXISTS raw_entries (project_key TEXT NOT NULL, session_id TEXT NOT NULL, entry_id TEXT NOT NULL, revision INTEGER NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, content_hash TEXT NOT NULL, payload_json TEXT NOT NULL, parent_entry_id TEXT, branch TEXT, created_at INTEGER NOT NULL, PRIMARY KEY(project_key, session_id, entry_id, revision), UNIQUE(project_key, session_id, entry_id, content_hash));
26
+ CREATE TABLE IF NOT EXISTS summary_nodes (node_id TEXT PRIMARY KEY, project_key TEXT NOT NULL, payload TEXT NOT NULL, created_at INTEGER NOT NULL);
27
+ CREATE TABLE IF NOT EXISTS summary_edges (parent_id TEXT NOT NULL, child_id TEXT NOT NULL, PRIMARY KEY(parent_id, child_id));
28
+ CREATE TABLE IF NOT EXISTS frontiers (project_key TEXT NOT NULL, frontier_id TEXT NOT NULL, node_id TEXT NOT NULL, created_at INTEGER NOT NULL, PRIMARY KEY(project_key, frontier_id, node_id));
29
+ CREATE TABLE IF NOT EXISTS maintenance_jobs (job_id TEXT PRIMARY KEY, project_key TEXT NOT NULL, status TEXT NOT NULL, payload TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL);
30
+ CREATE TABLE IF NOT EXISTS maintenance_usage (project_key TEXT NOT NULL, day TEXT NOT NULL, session_id TEXT NOT NULL, calls INTEGER NOT NULL, input_tokens INTEGER NOT NULL, output_tokens INTEGER NOT NULL, cost REAL NOT NULL, wall_ms INTEGER NOT NULL, PRIMARY KEY(project_key,day,session_id));
31
+ CREATE INDEX IF NOT EXISTS raw_entries_lookup ON raw_entries(project_key, session_id, entry_id, revision);
32
+ `;
33
+ var hash = (s) => crypto.createHash("sha256").update(s).digest("hex");
34
+ var fileHash = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
35
+ var snapshotHash = (db) => {
36
+ const digest = crypto.createHash("sha256");
37
+ for (const [table, order] of [["schema_metadata", "key"], ["projects", "project_key"], ["project_aliases", "alias"], ["sessions", "project_key,session_id"], ["raw_entries", "project_key,session_id,entry_id,revision"], ["summary_nodes", "node_id"], ["summary_edges", "parent_id,child_id"], ["frontiers", "project_key,frontier_id,node_id"], ["maintenance_jobs", "job_id"], ["maintenance_usage", "project_key,day,session_id"]]) {
38
+ digest.update(`${table}\0`);
39
+ for (const row of db.prepare(`SELECT * FROM ${table} ORDER BY ${order}`).all()) digest.update(`${JSON.stringify(row)}\0`);
40
+ }
41
+ return digest.digest("hex");
42
+ };
43
+ var normalize = (p) => path.normalize(path.resolve(p));
44
+ var statIdentity = (p) => {
45
+ try {
46
+ const s = fs.statSync(p);
47
+ return { device: Number(s.dev), inode: Number(s.ino) };
48
+ } catch {
49
+ return {};
50
+ }
51
+ };
52
+ function canonicalProjectIdentity(input) {
53
+ const source = input.recordedCwd?.trim() || input.liveCwd?.trim();
54
+ if (!source) throw new Error("project cwd is required");
55
+ const normalized = normalize(source);
56
+ let canonicalPath = normalized;
57
+ try {
58
+ canonicalPath = fs.realpathSync.native(normalized);
59
+ } catch {
60
+ }
61
+ const ids = statIdentity(canonicalPath);
62
+ const key = ids.device !== void 0 && ids.inode !== void 0 ? `v1:devino:${ids.device}:${ids.inode}` : `v1:path:${canonicalPath}`;
63
+ return { version: 1, key, canonicalPath, ...ids, aliases: [normalized, canonicalPath] };
64
+ }
65
+ function defaultLedgerPath(rootDir = path.join(process.env.XDG_STATE_HOME || path.join(process.env.HOME || ".", ".local", "state"), "omp-fabric", "lcm"), projectKey = "default") {
66
+ return path.join(rootDir, `${hash(projectKey).slice(0, 24)}.sqlite`);
67
+ }
68
+ var LedgerDegradedError = class extends Error {
69
+ constructor(message, cause) {
70
+ super(message);
71
+ this.cause = cause;
72
+ this.name = "LedgerDegradedError";
73
+ }
74
+ };
75
+ var LcmLedger = class {
76
+ db;
77
+ project;
78
+ degraded = false;
79
+ dbPath;
80
+ now;
81
+ warningBytes;
82
+ maintenanceBytes;
83
+ writeChain = Promise.resolve();
84
+ transactionDepth = 0;
85
+ constructor(options = {}) {
86
+ this.now = options.now ?? Date.now;
87
+ this.warningBytes = options.warningBytes ?? LCM_LEDGER_WARNING_BYTES;
88
+ this.maintenanceBytes = options.maintenanceBytes ?? LCM_LEDGER_MAINTENANCE_BYTES;
89
+ if (!Number.isSafeInteger(this.warningBytes) || !Number.isSafeInteger(this.maintenanceBytes) || this.warningBytes < 0 || this.maintenanceBytes < this.warningBytes) {
90
+ throw new Error("invalid ledger size thresholds");
91
+ }
92
+ this.project = canonicalProjectIdentity(options.project ?? { liveCwd: process.cwd() });
93
+ const dbPath = options.dbPath ?? defaultLedgerPath(options.rootDir, this.project.key);
94
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
95
+ this.dbPath = dbPath;
96
+ this.db = new DatabaseSync(dbPath);
97
+ try {
98
+ this.db.exec("PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA busy_timeout=5000; PRAGMA wal_autocheckpoint=1000;");
99
+ this.db.exec("BEGIN IMMEDIATE;" + SCHEMA + "INSERT INTO schema_metadata(key,value) VALUES ('version','1') ON CONFLICT(key) DO UPDATE SET value='1'; COMMIT;");
100
+ const columns = this.db.prepare("PRAGMA table_info(raw_entries)").all();
101
+ if (!columns.some((column) => column.name === "payload_json")) {
102
+ if (Number(this.db.prepare("SELECT count(*) n FROM raw_entries").get().n)) throw new Error("raw payloads missing; reimport authoritative session entries");
103
+ this.db.exec("ALTER TABLE raw_entries ADD COLUMN payload_json TEXT NOT NULL DEFAULT ''");
104
+ }
105
+ const result = this.db.prepare("PRAGMA integrity_check").get();
106
+ if (result.integrity_check !== "ok") throw new Error(String(result.integrity_check));
107
+ this.registerProject(this.project);
108
+ } catch (error) {
109
+ this.degraded = true;
110
+ throw new LedgerDegradedError("ledger startup failed", error);
111
+ }
112
+ }
113
+ get isDegraded() {
114
+ return this.degraded;
115
+ }
116
+ get operationalState() {
117
+ if (this.degraded) return "degraded";
118
+ try {
119
+ const size = fs.statSync(this.dbPath).size;
120
+ if (size >= this.maintenanceBytes) return "maintenance";
121
+ if (size >= this.warningBytes) return "warning";
122
+ } catch {
123
+ }
124
+ return "healthy";
125
+ }
126
+ markDegraded(error) {
127
+ this.degraded = true;
128
+ return new LedgerDegradedError("ledger is degraded", error);
129
+ }
130
+ registerProject(identity) {
131
+ const now = this.now();
132
+ this.db.prepare("INSERT INTO projects(project_key,identity_json,created_at,updated_at) VALUES(?,?,?,?) ON CONFLICT(project_key) DO UPDATE SET identity_json=excluded.identity_json,updated_at=excluded.updated_at").run(identity.key, JSON.stringify(identity), now, now);
133
+ for (const alias of identity.aliases) {
134
+ const existing = this.db.prepare("SELECT project_key FROM project_aliases WHERE alias=?").get(alias);
135
+ if (existing && existing.project_key !== identity.key) throw new LedgerDegradedError(`ambiguous project alias: ${alias}`);
136
+ this.db.prepare("INSERT OR IGNORE INTO project_aliases(alias,project_key) VALUES(?,?)").run(alias, identity.key);
137
+ }
138
+ }
139
+ guard() {
140
+ if (this.degraded) throw new LedgerDegradedError("ledger is degraded");
141
+ }
142
+ assertProjectKey(projectKey) {
143
+ if (projectKey !== this.project.key) throw new Error("project key does not match ledger project");
144
+ }
145
+ async serialize(operation) {
146
+ const previous = this.writeChain;
147
+ let release;
148
+ this.writeChain = new Promise((resolve) => {
149
+ release = resolve;
150
+ });
151
+ await previous;
152
+ try {
153
+ this.guard();
154
+ return operation();
155
+ } finally {
156
+ release();
157
+ }
158
+ }
159
+ appendRaw(entry) {
160
+ this.guard();
161
+ if (entry.projectKey !== this.project.key) throw new Error("entry project does not match ledger project");
162
+ if (entry.entryId.trim().length === 0) throw new Error("entry ID is required");
163
+ if (typeof entry.payloadJson !== "string" || !entry.payloadJson.trim()) throw new Error("full payload JSON is required");
164
+ let payload;
165
+ try {
166
+ payload = JSON.parse(entry.payloadJson);
167
+ } catch {
168
+ throw new Error("invalid payload JSON");
169
+ }
170
+ 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");
171
+ const payloadJson = canonicalLcmPayload(payload);
172
+ const contentHash = hashLcmPayload(payload);
173
+ const ownsTransaction = this.transactionDepth === 0;
174
+ if (ownsTransaction) this.db.exec("BEGIN IMMEDIATE");
175
+ try {
176
+ const existing = this.db.prepare("SELECT revision,created_at FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? AND content_hash=?").get(entry.projectKey, entry.sessionId, entry.entryId, contentHash);
177
+ if (existing) {
178
+ if (ownsTransaction) this.db.exec("COMMIT");
179
+ return { ...entry, payloadJson, payloadHash: contentHash, revision: existing.revision, contentHash, createdAt: existing.created_at };
180
+ }
181
+ 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);
182
+ const revision = latest.revision + 1;
183
+ const createdAt = entry.createdAt ?? this.now();
184
+ this.db.prepare("INSERT OR IGNORE INTO sessions(project_key,session_id,created_at) VALUES(?,?,?)").run(entry.projectKey, entry.sessionId, createdAt);
185
+ this.db.prepare("INSERT INTO raw_entries(project_key,session_id,entry_id,revision,role,content,content_hash,payload_json,parent_entry_id,branch,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)").run(entry.projectKey, entry.sessionId, entry.entryId, revision, entry.role, entry.content, contentHash, payloadJson, entry.parentEntryId ?? null, entry.branch ?? null, createdAt);
186
+ if (ownsTransaction) this.db.exec("COMMIT");
187
+ return { ...entry, payloadJson, payloadHash: contentHash, revision, contentHash, createdAt };
188
+ } catch (error) {
189
+ if (ownsTransaction) this.db.exec("ROLLBACK");
190
+ const code = error.code;
191
+ if (code === "ENOSPC" || code === "SQLITE_FULL" || String(error).includes("database or disk is full")) this.degraded = true;
192
+ throw new LedgerDegradedError("ledger write failed", error);
193
+ }
194
+ }
195
+ readRaw(projectKey = this.project.key, sessionId) {
196
+ this.guard();
197
+ this.assertProjectKey(projectKey);
198
+ const rows = sessionId ? this.db.prepare("SELECT * FROM raw_entries WHERE project_key=? AND session_id=? ORDER BY created_at,revision").all(projectKey, sessionId) : this.db.prepare("SELECT * FROM raw_entries WHERE project_key=? ORDER BY created_at,session_id,entry_id,revision").all(projectKey);
199
+ return rows.map((row) => ({ projectKey: row.project_key, sessionId: row.session_id, entryId: row.entry_id, revision: row.revision, role: row.role, content: row.content, contentHash: row.content_hash, payloadHash: row.content_hash, payloadJson: row.payload_json, parentEntryId: row.parent_entry_id, branch: row.branch, createdAt: row.created_at }));
200
+ }
201
+ readRawPage(projectKey = this.project.key, sessionId, offset = 0, limit = 100) {
202
+ if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 1) throw new Error("invalid raw page");
203
+ this.guard();
204
+ this.assertProjectKey(projectKey);
205
+ const rows = sessionId ? this.db.prepare("SELECT * FROM raw_entries WHERE project_key=? AND session_id=? ORDER BY created_at,revision LIMIT ? OFFSET ?").all(projectKey, sessionId, limit, offset) : this.db.prepare("SELECT * FROM raw_entries WHERE project_key=? ORDER BY created_at,session_id,entry_id,revision LIMIT ? OFFSET ?").all(projectKey, limit, offset);
206
+ return rows.map((row) => ({ projectKey: row.project_key, sessionId: row.session_id, entryId: row.entry_id, revision: row.revision, role: row.role, content: row.content, contentHash: row.content_hash, payloadHash: row.content_hash, payloadJson: row.payload_json, parentEntryId: row.parent_entry_id, branch: row.branch, createdAt: row.created_at }));
207
+ }
208
+ readRawEntry(projectKey, sessionId, entryId, revision) {
209
+ this.guard();
210
+ this.assertProjectKey(projectKey);
211
+ const row = this.db.prepare("SELECT * FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? AND revision=?").get(projectKey, sessionId, entryId, revision);
212
+ return row ? { projectKey: row.project_key, sessionId: row.session_id, entryId: row.entry_id, revision: row.revision, role: row.role, content: row.content, contentHash: row.content_hash, payloadHash: row.content_hash, payloadJson: row.payload_json, parentEntryId: row.parent_entry_id, branch: row.branch, createdAt: row.created_at } : void 0;
213
+ }
214
+ readOnly(fn) {
215
+ this.guard();
216
+ return fn(this.db);
217
+ }
218
+ transaction(fn) {
219
+ this.guard();
220
+ if (this.transactionDepth > 0) return fn(this.db);
221
+ this.db.exec("BEGIN IMMEDIATE");
222
+ this.transactionDepth = 1;
223
+ try {
224
+ const result = fn(this.db);
225
+ this.db.exec("COMMIT");
226
+ return result;
227
+ } catch (error) {
228
+ this.db.exec("ROLLBACK");
229
+ throw error;
230
+ } finally {
231
+ this.transactionDepth = 0;
232
+ }
233
+ }
234
+ checkpoint(mode = "passive") {
235
+ const row = this.db.prepare(`PRAGMA wal_checkpoint(${mode.toUpperCase()})`).get();
236
+ const busy = row.busy ?? 0;
237
+ const logPages = row.log ?? 0;
238
+ return { mode, busy, logPages, checkpointedPages: row.checkpointed ?? 0, truncated: mode === "truncate" && busy === 0 && logPages === 0 };
239
+ }
240
+ backup(destination) {
241
+ this.guard();
242
+ return this.serializeSync(() => {
243
+ const target = path.resolve(destination);
244
+ fs.mkdirSync(path.dirname(target), { recursive: true });
245
+ if (fs.existsSync(target)) throw new Error("backup destination exists");
246
+ this.db.exec(`VACUUM INTO '${target.replace(/'/g, "''")}'`);
247
+ fs.chmodSync(target, 384);
248
+ const copy = new DatabaseSync(target);
249
+ let integrity = "unknown";
250
+ let backupStateSha256 = "";
251
+ const rowCounts = {};
252
+ try {
253
+ integrity = copy.prepare("PRAGMA integrity_check").get().integrity_check ?? "unknown";
254
+ for (const table of ["projects", "sessions", "raw_entries", "summary_nodes", "summary_edges", "frontiers", "maintenance_jobs", "maintenance_usage"]) {
255
+ rowCounts[table] = Number(copy.prepare(`SELECT count(*) n FROM ${table}`).get().n);
256
+ }
257
+ backupStateSha256 = snapshotHash(copy);
258
+ } finally {
259
+ copy.close();
260
+ }
261
+ const manifest = { format: "lcm-ledger-backup", version: 1, source: this.dbPath, destination: target, sourceSha256: fileHash(this.dbPath), backupSha256: fileHash(target), sourceStateSha256: backupStateSha256, rowCounts, integrity, createdAt: this.now() };
262
+ fs.writeFileSync(`${target}.manifest.json`, JSON.stringify(manifest), { mode: 384 });
263
+ fs.chmodSync(`${target}.manifest.json`, 384);
264
+ return manifest;
265
+ });
266
+ }
267
+ exportBackup(destination) {
268
+ return this.backup(destination);
269
+ }
270
+ deleteProject(token, backupManifest) {
271
+ this.guard();
272
+ if (token.__brand !== "DeleteConfirmationToken" || token.projectKey !== this.project.key || token.value !== hash(`delete:${this.project.key}`)) throw new Error("invalid delete confirmation token");
273
+ const backupDb = fs.existsSync(backupManifest.destination) ? new DatabaseSync(backupManifest.destination) : void 0;
274
+ let backupStateSha256;
275
+ try {
276
+ if (backupDb) backupStateSha256 = snapshotHash(backupDb);
277
+ } finally {
278
+ backupDb?.close();
279
+ }
280
+ if (backupManifest.integrity !== "ok" || backupManifest.source !== this.dbPath || !backupManifest.backupSha256 || backupManifest.backupSha256 !== fileHash(backupManifest.destination) || !backupManifest.sourceStateSha256 || backupManifest.sourceStateSha256 !== backupStateSha256 || backupManifest.sourceSha256 !== fileHash(this.dbPath) || backupManifest.sourceStateSha256 !== snapshotHash(this.db)) throw new Error("backup verification failed");
281
+ this.transaction((db) => {
282
+ db.prepare("DELETE FROM summary_edges WHERE parent_id IN (SELECT node_id FROM summary_nodes WHERE project_key=?) OR child_id IN (SELECT node_id FROM summary_nodes WHERE project_key=?)").run(this.project.key, this.project.key);
283
+ for (const table of ["raw_entries", "sessions", "frontiers", "summary_nodes", "maintenance_jobs", "maintenance_usage"]) db.prepare(`DELETE FROM ${table} WHERE project_key=?`).run(this.project.key);
284
+ db.prepare("DELETE FROM project_aliases WHERE project_key=?").run(this.project.key);
285
+ db.prepare("DELETE FROM projects WHERE project_key=?").run(this.project.key);
286
+ });
287
+ const integrity = this.db.prepare("PRAGMA integrity_check").get().integrity_check;
288
+ if (integrity !== "ok") throw new Error(`post-delete integrity failed: ${integrity}`);
289
+ const audit = { format: "lcm-ledger-delete", version: 1, projectKey: this.project.key, deletedAt: this.now(), integrity, remaining: Number(this.db.prepare("SELECT count(*) n FROM projects WHERE project_key=?").get(this.project.key).n) };
290
+ fs.writeFileSync(`${backupManifest.destination}.delete-manifest.json`, JSON.stringify(audit), { mode: 384 });
291
+ }
292
+ serializeSync(operation) {
293
+ this.guard();
294
+ return operation();
295
+ }
296
+ close() {
297
+ this.db.close();
298
+ }
299
+ };
300
+
301
+ export {
302
+ canonicalLcmPayload,
303
+ hashLcmPayload,
304
+ canonicalProjectIdentity,
305
+ defaultLedgerPath,
306
+ LcmLedger
307
+ };
308
+ //# sourceMappingURL=chunk-IY3IQPRQ.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/storage/lcm-ledger.ts"],
4
+ "sourcesContent": ["import { DatabaseSync } from \"node:sqlite\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\n\nexport interface ProjectIdentityInput { recordedCwd?: string; liveCwd?: string }\nexport interface ProjectIdentity { version: 1; key: string; canonicalPath?: string; device?: number; inode?: number; aliases: string[] }\nconst LCM_LEDGER_WARNING_BYTES = 8 * 1024 ** 3;\nconst LCM_LEDGER_MAINTENANCE_BYTES = 10 * 1024 ** 3;\nexport interface LedgerOptions { dbPath?: string; rootDir?: string; project?: ProjectIdentityInput; now?: () => number; warningBytes?: number; maintenanceBytes?: number }\nexport type OperationalState = \"healthy\" | \"warning\" | \"maintenance\" | \"degraded\";\nexport interface CheckpointMetrics { mode: \"passive\" | \"truncate\"; busy: number; logPages: number; checkpointedPages: number; truncated: boolean }\nexport interface BackupManifest { format: \"lcm-ledger-backup\"; version: 1; source: string; destination: string; sourceSha256: string; backupSha256: string; sourceStateSha256: string; rowCounts: Record<string, number>; integrity: \"ok\" | string; createdAt: number }\nexport interface DeleteConfirmationToken { readonly projectKey: string; readonly value: string; readonly __brand: \"DeleteConfirmationToken\" }\nexport function createDeleteConfirmationToken(projectKey: string): DeleteConfirmationToken { return { projectKey, value: hash(`delete:${projectKey}`), __brand: \"DeleteConfirmationToken\" }; }\nexport interface SessionEntry { projectKey: string; sessionId: string; entryId: string; revision?: number; role: string; content: string; payloadJson: string; parentEntryId?: string | null; branch?: string | null; recordedCwd?: string; createdAt?: number }\nexport interface RawEntry extends SessionEntry { revision: number; contentHash: string; payloadHash: string; createdAt: number }\n\nexport function canonicalLcmPayload(value: unknown): string {\n const normalized: unknown = JSON.parse(JSON.stringify(value));\n const encode = (item: unknown): string => {\n if (Array.isArray(item)) return `[${item.map(encode).join(\",\")}]`;\n if (item !== null && typeof item === \"object\") return `{${Object.keys(item).sort().map(key => `${JSON.stringify(key)}:${encode((item as Record<string, unknown>)[key])}`).join(\",\")}}`;\n return JSON.stringify(item);\n };\n return encode(normalized);\n}\nexport function hashLcmPayload(value: unknown): string { return crypto.createHash(\"sha256\").update(canonicalLcmPayload(value), \"utf8\").digest(\"hex\"); }\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS schema_metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL);\nCREATE TABLE IF NOT EXISTS projects (project_key TEXT PRIMARY KEY, identity_json TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL);\nCREATE TABLE IF NOT EXISTS project_aliases (alias TEXT PRIMARY KEY, project_key TEXT NOT NULL REFERENCES projects(project_key));\nCREATE TABLE IF NOT EXISTS sessions (project_key TEXT NOT NULL, session_id TEXT NOT NULL, created_at INTEGER NOT NULL, PRIMARY KEY(project_key, session_id));\nCREATE TABLE IF NOT EXISTS raw_entries (project_key TEXT NOT NULL, session_id TEXT NOT NULL, entry_id TEXT NOT NULL, revision INTEGER NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, content_hash TEXT NOT NULL, payload_json TEXT NOT NULL, parent_entry_id TEXT, branch TEXT, created_at INTEGER NOT NULL, PRIMARY KEY(project_key, session_id, entry_id, revision), UNIQUE(project_key, session_id, entry_id, content_hash));\nCREATE TABLE IF NOT EXISTS summary_nodes (node_id TEXT PRIMARY KEY, project_key TEXT NOT NULL, payload TEXT NOT NULL, created_at INTEGER NOT NULL);\nCREATE TABLE IF NOT EXISTS summary_edges (parent_id TEXT NOT NULL, child_id TEXT NOT NULL, PRIMARY KEY(parent_id, child_id));\nCREATE TABLE IF NOT EXISTS frontiers (project_key TEXT NOT NULL, frontier_id TEXT NOT NULL, node_id TEXT NOT NULL, created_at INTEGER NOT NULL, PRIMARY KEY(project_key, frontier_id, node_id));\nCREATE TABLE IF NOT EXISTS maintenance_jobs (job_id TEXT PRIMARY KEY, project_key TEXT NOT NULL, status TEXT NOT NULL, payload TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL);\nCREATE TABLE IF NOT EXISTS maintenance_usage (project_key TEXT NOT NULL, day TEXT NOT NULL, session_id TEXT NOT NULL, calls INTEGER NOT NULL, input_tokens INTEGER NOT NULL, output_tokens INTEGER NOT NULL, cost REAL NOT NULL, wall_ms INTEGER NOT NULL, PRIMARY KEY(project_key,day,session_id));\nCREATE INDEX IF NOT EXISTS raw_entries_lookup ON raw_entries(project_key, session_id, entry_id, revision);\n`;\n\nconst hash = (s: string) => crypto.createHash(\"sha256\").update(s).digest(\"hex\");\nconst fileHash = (file: string) => crypto.createHash(\"sha256\").update(fs.readFileSync(file)).digest(\"hex\");\nconst snapshotHash = (db: DatabaseSync): string => {\n const digest = crypto.createHash(\"sha256\");\n for (const [table, order] of [[\"schema_metadata\", \"key\"], [\"projects\", \"project_key\"], [\"project_aliases\", \"alias\"], [\"sessions\", \"project_key,session_id\"], [\"raw_entries\", \"project_key,session_id,entry_id,revision\"], [\"summary_nodes\", \"node_id\"], [\"summary_edges\", \"parent_id,child_id\"], [\"frontiers\", \"project_key,frontier_id,node_id\"], [\"maintenance_jobs\", \"job_id\"], [\"maintenance_usage\", \"project_key,day,session_id\"]] as const) {\n digest.update(`${table}\\0`);\n for (const row of db.prepare(`SELECT * FROM ${table} ORDER BY ${order}`).all()) digest.update(`${JSON.stringify(row)}\\0`);\n }\n return digest.digest(\"hex\");\n};\nconst normalize = (p: string) => path.normalize(path.resolve(p));\nconst statIdentity = (p: string) => { try { const s = fs.statSync(p); return { device: Number(s.dev), inode: Number(s.ino) }; } catch { return {}; } };\n\nexport function canonicalProjectIdentity(input: ProjectIdentityInput): ProjectIdentity {\n const source = input.recordedCwd?.trim() || input.liveCwd?.trim();\n if (!source) throw new Error(\"project cwd is required\");\n const normalized = normalize(source);\n let canonicalPath = normalized;\n try { canonicalPath = fs.realpathSync.native(normalized); } catch {}\n const ids = statIdentity(canonicalPath);\n const key = ids.device !== undefined && ids.inode !== undefined ? `v1:devino:${ids.device}:${ids.inode}` : `v1:path:${canonicalPath}`;\n return { version: 1, key, canonicalPath, ...ids, aliases: [normalized, canonicalPath] };\n}\n\nexport function defaultLedgerPath(rootDir = path.join(process.env.XDG_STATE_HOME || path.join(process.env.HOME || \".\", \".local\", \"state\"), \"omp-fabric\", \"lcm\"), projectKey = \"default\"): string {\n return path.join(rootDir, `${hash(projectKey).slice(0, 24)}.sqlite`);\n}\n\nclass LedgerDegradedError extends Error { constructor(message: string, public readonly cause?: unknown) { super(message); this.name = \"LedgerDegradedError\"; } }\n\nexport class LcmLedger {\n readonly db: DatabaseSync;\n readonly project: ProjectIdentity;\n private degraded = false;\n private readonly dbPath: string;\n private readonly now: () => number;\n private readonly warningBytes: number;\n private readonly maintenanceBytes: number;\n private writeChain: Promise<void> = Promise.resolve();\n private transactionDepth = 0;\n constructor(options: LedgerOptions = {}) {\n this.now = options.now ?? Date.now;\n this.warningBytes = options.warningBytes ?? LCM_LEDGER_WARNING_BYTES;\n this.maintenanceBytes = options.maintenanceBytes ?? LCM_LEDGER_MAINTENANCE_BYTES;\n if (!Number.isSafeInteger(this.warningBytes) || !Number.isSafeInteger(this.maintenanceBytes) || this.warningBytes < 0 || this.maintenanceBytes < this.warningBytes) {\n throw new Error(\"invalid ledger size thresholds\");\n }\n this.project = canonicalProjectIdentity(options.project ?? { liveCwd: process.cwd() });\n const dbPath = options.dbPath ?? defaultLedgerPath(options.rootDir, this.project.key);\n fs.mkdirSync(path.dirname(dbPath), { recursive: true });\n this.dbPath = dbPath;\n this.db = new DatabaseSync(dbPath);\n try {\n this.db.exec(\"PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA busy_timeout=5000; PRAGMA wal_autocheckpoint=1000;\");\n this.db.exec(\"BEGIN IMMEDIATE;\" + SCHEMA + \"INSERT INTO schema_metadata(key,value) VALUES ('version','1') ON CONFLICT(key) DO UPDATE SET value='1'; COMMIT;\");\n const columns = this.db.prepare(\"PRAGMA table_info(raw_entries)\").all() as Array<{ name: string }>;\n if (!columns.some(column => column.name === \"payload_json\")) {\n if (Number((this.db.prepare(\"SELECT count(*) n FROM raw_entries\").get() as { n: number }).n)) throw new Error(\"raw payloads missing; reimport authoritative session entries\");\n this.db.exec(\"ALTER TABLE raw_entries ADD COLUMN payload_json TEXT NOT NULL DEFAULT ''\");\n }\n const result = this.db.prepare(\"PRAGMA integrity_check\").get() as { integrity_check?: string };\n if (result.integrity_check !== \"ok\") throw new Error(String(result.integrity_check));\n this.registerProject(this.project);\n } catch (error) { this.degraded = true; throw new LedgerDegradedError(\"ledger startup failed\", error); }\n }\n get isDegraded() { return this.degraded; }\n get operationalState(): OperationalState {\n if (this.degraded) return \"degraded\";\n try { const size = fs.statSync(this.dbPath).size; if (size >= this.maintenanceBytes) return \"maintenance\"; if (size >= this.warningBytes) return \"warning\"; } catch {}\n return \"healthy\";\n }\n markDegraded(error?: unknown) { this.degraded = true; return new LedgerDegradedError(\"ledger is degraded\", error); }\n private registerProject(identity: ProjectIdentity) {\n const now = this.now();\n this.db.prepare(\"INSERT INTO projects(project_key,identity_json,created_at,updated_at) VALUES(?,?,?,?) ON CONFLICT(project_key) DO UPDATE SET identity_json=excluded.identity_json,updated_at=excluded.updated_at\").run(identity.key, JSON.stringify(identity), now, now);\n for (const alias of identity.aliases) {\n const existing = this.db.prepare(\"SELECT project_key FROM project_aliases WHERE alias=?\").get(alias) as { project_key?: string } | undefined;\n if (existing && existing.project_key !== identity.key) throw new LedgerDegradedError(`ambiguous project alias: ${alias}`);\n this.db.prepare(\"INSERT OR IGNORE INTO project_aliases(alias,project_key) VALUES(?,?)\").run(alias, identity.key);\n }\n }\n private guard() { if (this.degraded) throw new LedgerDegradedError(\"ledger is degraded\"); }\n private assertProjectKey(projectKey: string) { if (projectKey !== this.project.key) throw new Error(\"project key does not match ledger project\"); }\n async serialize<T>(operation: () => T): Promise<T> { const previous = this.writeChain; let release!: () => void; this.writeChain = new Promise<void>(resolve => { release = resolve }); await previous; try { this.guard(); return operation(); } finally { release(); } }\n appendRaw(entry: SessionEntry): RawEntry {\n this.guard();\n if (entry.projectKey !== this.project.key) throw new Error(\"entry project does not match ledger project\");\n if (entry.entryId.trim().length === 0) throw new Error(\"entry ID is required\");\n if (typeof entry.payloadJson !== \"string\" || !entry.payloadJson.trim()) throw new Error(\"full payload JSON is required\");\n let payload: Record<string, unknown>;\n try { payload = JSON.parse(entry.payloadJson) as Record<string, unknown>; } catch { throw new Error(\"invalid payload JSON\"); }\n 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\");\n const payloadJson = canonicalLcmPayload(payload);\n const contentHash = hashLcmPayload(payload);\n const ownsTransaction = this.transactionDepth === 0;\n if (ownsTransaction) this.db.exec(\"BEGIN IMMEDIATE\");\n try {\n const existing = this.db.prepare(\"SELECT revision,created_at FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? AND content_hash=?\").get(entry.projectKey, entry.sessionId, entry.entryId, contentHash) as { revision: number; created_at: number } | undefined;\n if (existing) { if (ownsTransaction) this.db.exec(\"COMMIT\"); return { ...entry, payloadJson, payloadHash: contentHash, revision: existing.revision, contentHash, createdAt: existing.created_at }; }\n 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) as { revision: number };\n const revision = latest.revision + 1; const createdAt = entry.createdAt ?? this.now();\n this.db.prepare(\"INSERT OR IGNORE INTO sessions(project_key,session_id,created_at) VALUES(?,?,?)\").run(entry.projectKey, entry.sessionId, createdAt);\n this.db.prepare(\"INSERT INTO raw_entries(project_key,session_id,entry_id,revision,role,content,content_hash,payload_json,parent_entry_id,branch,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)\").run(entry.projectKey,entry.sessionId,entry.entryId,revision,entry.role,entry.content,contentHash,payloadJson,entry.parentEntryId ?? null,entry.branch ?? null,createdAt);\n if (ownsTransaction) this.db.exec(\"COMMIT\");\n return { ...entry, payloadJson, payloadHash: contentHash, revision, contentHash, createdAt };\n } catch (error) {\n if (ownsTransaction) this.db.exec(\"ROLLBACK\");\n const code = (error as NodeJS.ErrnoException).code; if (code === \"ENOSPC\" || code === \"SQLITE_FULL\" || String(error).includes(\"database or disk is full\")) this.degraded = true; throw new LedgerDegradedError(\"ledger write failed\", error);\n }\n }\n readRaw(projectKey = this.project.key, sessionId?: string): RawEntry[] { this.guard(); this.assertProjectKey(projectKey); const rows = sessionId ? this.db.prepare(\"SELECT * FROM raw_entries WHERE project_key=? AND session_id=? ORDER BY created_at,revision\").all(projectKey,sessionId) : this.db.prepare(\"SELECT * FROM raw_entries WHERE project_key=? ORDER BY created_at,session_id,entry_id,revision\").all(projectKey); return (rows as Array<Record<string, unknown>>).map(row => ({ projectKey: row.project_key as string, sessionId: row.session_id as string, entryId: row.entry_id as string, revision: row.revision as number, role: row.role as string, content: row.content as string, contentHash: row.content_hash as string, payloadHash: row.content_hash as string, payloadJson: row.payload_json as string, parentEntryId: row.parent_entry_id as string | null, branch: row.branch as string | null, createdAt: row.created_at as number })); }\n readRawPage(projectKey = this.project.key, sessionId?: string, offset = 0, limit = 100): RawEntry[] { if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 1) throw new Error(\"invalid raw page\"); this.guard(); this.assertProjectKey(projectKey); const rows = sessionId ? this.db.prepare(\"SELECT * FROM raw_entries WHERE project_key=? AND session_id=? ORDER BY created_at,revision LIMIT ? OFFSET ?\").all(projectKey,sessionId,limit,offset) : this.db.prepare(\"SELECT * FROM raw_entries WHERE project_key=? ORDER BY created_at,session_id,entry_id,revision LIMIT ? OFFSET ?\").all(projectKey,limit,offset); return (rows as Array<Record<string, unknown>>).map(row => ({ projectKey: row.project_key as string, sessionId: row.session_id as string, entryId: row.entry_id as string, revision: row.revision as number, role: row.role as string, content: row.content as string, contentHash: row.content_hash as string, payloadHash: row.content_hash as string, payloadJson: row.payload_json as string, parentEntryId: row.parent_entry_id as string | null, branch: row.branch as string | null, createdAt: row.created_at as number })); }\n readRawEntry(projectKey: string, sessionId: string, entryId: string, revision: number): RawEntry | undefined { this.guard(); this.assertProjectKey(projectKey); const row = this.db.prepare(\"SELECT * FROM raw_entries WHERE project_key=? AND session_id=? AND entry_id=? AND revision=?\").get(projectKey,sessionId,entryId,revision) as Record<string, unknown> | undefined; return row ? { projectKey: row.project_key as string, sessionId: row.session_id as string, entryId: row.entry_id as string, revision: row.revision as number, role: row.role as string, content: row.content as string, contentHash: row.content_hash as string, payloadHash: row.content_hash as string, payloadJson: row.payload_json as string, parentEntryId: row.parent_entry_id as string | null, branch: row.branch as string | null, createdAt: row.created_at as number } : undefined; }\n readOnly<T>(fn: (db: DatabaseSync) => T): T { this.guard(); return fn(this.db); }\n transaction<T>(fn: (db: DatabaseSync) => T): T { this.guard(); if (this.transactionDepth > 0) return fn(this.db); this.db.exec(\"BEGIN IMMEDIATE\"); this.transactionDepth = 1; try { const result = fn(this.db); this.db.exec(\"COMMIT\"); return result; } catch (error) { this.db.exec(\"ROLLBACK\"); throw error; } finally { this.transactionDepth = 0; } }\n checkpoint(mode: \"passive\" | \"truncate\" = \"passive\"): CheckpointMetrics {\n const row = this.db.prepare(`PRAGMA wal_checkpoint(${mode.toUpperCase()})`).get() as { busy?: number; log?: number; checkpointed?: number };\n const busy = row.busy ?? 0;\n const logPages = row.log ?? 0;\n return { mode, busy, logPages, checkpointedPages: row.checkpointed ?? 0, truncated: mode === \"truncate\" && busy === 0 && logPages === 0 };\n }\n backup(destination: string): BackupManifest {\n this.guard();\n return this.serializeSync(() => {\n const target = path.resolve(destination);\n fs.mkdirSync(path.dirname(target), { recursive: true });\n if (fs.existsSync(target)) throw new Error(\"backup destination exists\");\n this.db.exec(`VACUUM INTO '${target.replace(/'/g, \"''\")}'`);\n fs.chmodSync(target, 0o600);\n const copy = new DatabaseSync(target);\n let integrity = \"unknown\";\n let backupStateSha256 = \"\";\n const rowCounts: Record<string, number> = {};\n try {\n integrity = (copy.prepare(\"PRAGMA integrity_check\").get() as { integrity_check?: string }).integrity_check ?? \"unknown\";\n for (const table of [\"projects\", \"sessions\", \"raw_entries\", \"summary_nodes\", \"summary_edges\", \"frontiers\", \"maintenance_jobs\", \"maintenance_usage\"]) {\n rowCounts[table] = Number((copy.prepare(`SELECT count(*) n FROM ${table}`).get() as { n: number }).n);\n }\n backupStateSha256 = snapshotHash(copy);\n } finally {\n copy.close();\n }\n const manifest: BackupManifest = { format: \"lcm-ledger-backup\", version: 1, source: this.dbPath, destination: target, sourceSha256: fileHash(this.dbPath), backupSha256: fileHash(target), sourceStateSha256: backupStateSha256, rowCounts, integrity, createdAt: this.now() };\n fs.writeFileSync(`${target}.manifest.json`, JSON.stringify(manifest), { mode: 0o600 });\n fs.chmodSync(`${target}.manifest.json`, 0o600);\n return manifest;\n });\n }\n exportBackup(destination: string): BackupManifest { return this.backup(destination); }\n deleteProject(token: DeleteConfirmationToken, backupManifest: BackupManifest): void {\n this.guard();\n if (token.__brand !== \"DeleteConfirmationToken\" || token.projectKey !== this.project.key || token.value !== hash(`delete:${this.project.key}`)) throw new Error(\"invalid delete confirmation token\");\n const backupDb = fs.existsSync(backupManifest.destination) ? new DatabaseSync(backupManifest.destination) : undefined;\n let backupStateSha256: string | undefined;\n try {\n if (backupDb) backupStateSha256 = snapshotHash(backupDb);\n } finally {\n backupDb?.close();\n }\n if (backupManifest.integrity !== \"ok\" || backupManifest.source !== this.dbPath || !backupManifest.backupSha256 || backupManifest.backupSha256 !== fileHash(backupManifest.destination) || !backupManifest.sourceStateSha256 || backupManifest.sourceStateSha256 !== backupStateSha256 || backupManifest.sourceSha256 !== fileHash(this.dbPath) || backupManifest.sourceStateSha256 !== snapshotHash(this.db)) throw new Error(\"backup verification failed\");\n this.transaction(db => {\n db.prepare(\"DELETE FROM summary_edges WHERE parent_id IN (SELECT node_id FROM summary_nodes WHERE project_key=?) OR child_id IN (SELECT node_id FROM summary_nodes WHERE project_key=?)\").run(this.project.key, this.project.key);\n for (const table of [\"raw_entries\", \"sessions\", \"frontiers\", \"summary_nodes\", \"maintenance_jobs\", \"maintenance_usage\"]) db.prepare(`DELETE FROM ${table} WHERE project_key=?`).run(this.project.key);\n db.prepare(\"DELETE FROM project_aliases WHERE project_key=?\").run(this.project.key);\n db.prepare(\"DELETE FROM projects WHERE project_key=?\").run(this.project.key);\n });\n const integrity = (this.db.prepare(\"PRAGMA integrity_check\").get() as { integrity_check?: string }).integrity_check;\n if (integrity !== \"ok\") throw new Error(`post-delete integrity failed: ${integrity}`);\n const audit = { format: \"lcm-ledger-delete\", version: 1, projectKey: this.project.key, deletedAt: this.now(), integrity, remaining: Number((this.db.prepare(\"SELECT count(*) n FROM projects WHERE project_key=?\").get(this.project.key) as { n: number }).n) };\n fs.writeFileSync(`${backupManifest.destination}.delete-manifest.json`, JSON.stringify(audit), { mode: 0o600 });\n }\n private serializeSync<T>(operation: () => T): T { this.guard(); return operation(); }\n close() { this.db.close(); }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -12,7 +12,7 @@ import {
12
12
  buildThinkingDigest,
13
13
  snapshotHandoffSession,
14
14
  thinkingTransferPolicy
15
- } from "./chunk-JOWMM3EX.js";
15
+ } from "./chunk-WPU4PKXQ.js";
16
16
  import {
17
17
  isFabricThinking
18
18
  } from "./chunk-JSBTB3P6.js";
@@ -2803,4 +2803,4 @@ export {
2803
2803
  resolveFabricIdentity,
2804
2804
  MainAgentController
2805
2805
  };
2806
- //# sourceMappingURL=chunk-VSSC4IDU.js.map
2806
+ //# sourceMappingURL=chunk-RTDJFHGH.js.map
@@ -21,13 +21,13 @@ import {
21
21
  import {
22
22
  peerLabelPrefix,
23
23
  writeHandoffSession
24
- } from "./chunk-JOWMM3EX.js";
24
+ } from "./chunk-WPU4PKXQ.js";
25
25
  import {
26
26
  DEFAULT_FABRIC_CONFIG,
27
27
  MAX_AGENT_TIMEOUT_MS,
28
28
  MIN_AGENT_TIMEOUT_MS,
29
29
  resolveAgentDir
30
- } from "./chunk-WZAH7BO2.js";
30
+ } from "./chunk-2J2GOB4A.js";
31
31
  import {
32
32
  FABRIC_ACTOR_HOST_EVENTS,
33
33
  readJsonlPage
@@ -6635,4 +6635,4 @@ export {
6635
6635
  agentParticipantRecords,
6636
6636
  actorParticipantRecord
6637
6637
  };
6638
- //# sourceMappingURL=chunk-45URY66G.js.map
6638
+ //# sourceMappingURL=chunk-S4I7RMR5.js.map
@@ -17,11 +17,6 @@ import "@oh-my-pi/pi-coding-agent";
17
17
 
18
18
  // src/core/token-math.ts
19
19
  var ESTIMATED_IMAGE_CHARS = 4800;
20
- var DEFAULT_COMPACTION_SETTINGS = {
21
- enabled: true,
22
- reserveTokens: 16384,
23
- keepRecentTokens: 2e4
24
- };
25
20
  var contentParts = (content) => {
26
21
  if (typeof content !== "string") return content ?? [];
27
22
  return [];
@@ -1643,7 +1638,7 @@ var boundedDetails = (sourceEntries, facts, sections, request, oldLeafId) => {
1643
1638
  }
1644
1639
  return details;
1645
1640
  };
1646
- var compileFabricBranchSummary = (entriesToSummarize, customInstructions, enrichers = NO_BUILTIN_ENRICHERS, oldLeafId = null) => {
1641
+ var compileLcmBranchSummary = (entriesToSummarize, customInstructions, enrichers = NO_BUILTIN_ENRICHERS, oldLeafId = null) => {
1647
1642
  const instructions = decodeCompactionInstructions(customInstructions);
1648
1643
  if (!instructions.ok) return void 0;
1649
1644
  const events = normalizeEntries(entriesToSummarize);
@@ -2163,28 +2158,55 @@ var notifyInstructionError = (context, error) => {
2163
2158
  };
2164
2159
  var registerCompactionHook = (omp, options) => {
2165
2160
  omp.on("session_before_compact", (event, context) => {
2166
- if (options.getEngine() !== "fabric") return;
2167
- const { preparation, branchEntries } = event;
2168
- const contextWindow = context?.model?.contextWindow;
2169
- const targetContextRatio = options.getTargetContextRatio?.();
2170
- const settings = preparation.settings ?? DEFAULT_COMPACTION_SETTINGS;
2171
- const budget = typeof contextWindow === "number" && Number.isFinite(contextWindow) && contextWindow > 0 && typeof targetContextRatio === "number" && Number.isFinite(targetContextRatio) ? { contextWindow, targetContextRatio, reserveTokens: settings.reserveTokens ?? DEFAULT_COMPACTION_SETTINGS.reserveTokens, keepRecentTokens: settings.keepRecentTokens } : void 0;
2172
- const result = compileFabricSummary(branchEntries ?? [], preparation.tokensBefore, options.enrichers, event.customInstructions, budget);
2173
- if ("cancel" in result) {
2174
- if (result.instructionError) {
2175
- notifyInstructionError(context, result.instructionError);
2176
- return { cancel: true };
2177
- }
2161
+ if (options.getEngine() === "omp") return;
2162
+ if (!options.lcm) return { cancel: true };
2163
+ const instructions = decodeCompactionInstructions(event.customInstructions);
2164
+ if (!instructions.ok) {
2165
+ notifyInstructionError(context, instructions.error);
2166
+ return { cancel: true };
2167
+ }
2168
+ const branchEntries = event.branchEntries ?? [];
2169
+ const sessionManager = context?.sessionManager;
2170
+ const sessionId = sessionManager?.getSessionId?.() ?? "";
2171
+ const branch = sessionManager?.getLeafId?.() ?? null;
2172
+ let output;
2173
+ try {
2174
+ output = options.lcm.compact({
2175
+ branchEntries,
2176
+ sessionId,
2177
+ branch,
2178
+ firstKeptEntryId: event.preparation.firstKeptEntryId,
2179
+ tokensBefore: event.preparation.tokensBefore,
2180
+ ...event.customInstructions === void 0 ? {} : { customInstructions: event.customInstructions }
2181
+ });
2182
+ } catch {
2183
+ return { cancel: true };
2184
+ }
2185
+ if (!output || typeof output.summary !== "string" || output.summary.length === 0 || output.source !== "ready-frontier" && output.source !== "emergency" || output.tokensBefore !== event.preparation.tokensBefore || output.branch !== branch || typeof output.firstKeptEntryId !== "string" || branchEntries.length === 0 || output.firstKeptEntryId.length > 0 && !branchEntries.some((entry) => entry.id === output.firstKeptEntryId)) {
2178
2186
  return { cancel: true };
2179
2187
  }
2180
- event._fabricCompaction = true;
2181
- return { compaction: result.compaction };
2188
+ return {
2189
+ compaction: {
2190
+ summary: output.summary,
2191
+ firstKeptEntryId: output.firstKeptEntryId,
2192
+ tokensBefore: output.tokensBefore,
2193
+ details: {
2194
+ compactor: "lcm",
2195
+ source: output.source,
2196
+ branch,
2197
+ ...output.details && typeof output.details === "object" ? output.details : {}
2198
+ }
2199
+ }
2200
+ };
2182
2201
  });
2183
- omp.on("session_before_tree", (event, context) => {
2184
- if (options.getEngine() !== "fabric") return;
2185
- const { preparation } = event;
2186
- if (!preparation.userWantsSummary) return;
2187
- const compiled = compileFabricBranchSummary(preparation.entriesToSummarize, void 0, options.enrichers, preparation.oldLeafId);
2202
+ omp.on("session_before_tree", (event) => {
2203
+ if (options.getEngine() !== "lcm" || !event.preparation.userWantsSummary) return;
2204
+ const compiled = compileLcmBranchSummary(
2205
+ event.preparation.entriesToSummarize,
2206
+ void 0,
2207
+ options.enrichers,
2208
+ event.preparation.oldLeafId
2209
+ );
2188
2210
  if (!compiled) return;
2189
2211
  return { summary: compiled };
2190
2212
  });
@@ -2192,6 +2214,7 @@ var registerCompactionHook = (omp, options) => {
2192
2214
 
2193
2215
  export {
2194
2216
  estimateTokens,
2217
+ clipUtf8,
2195
2218
  compactAtConfiguredThreshold,
2196
2219
  MAX_COMPACTION_INSTRUCTIONS_CHARS,
2197
2220
  MAX_PRESERVE_ITEMS,
@@ -2206,4 +2229,4 @@ export {
2206
2229
  compileFabricSummary,
2207
2230
  registerCompactionHook
2208
2231
  };
2209
- //# sourceMappingURL=chunk-ZP3S74LW.js.map
2232
+ //# sourceMappingURL=chunk-SG4GVSWX.js.map