nlm-memory 0.9.1 → 0.10.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 (69) hide show
  1. package/README.md +4 -0
  2. package/dist/cli/nlm.js +54 -0
  3. package/dist/cli/nlm.js.map +1 -1
  4. package/dist/core/dataset/build-dataset.d.ts +13 -0
  5. package/dist/core/dataset/build-dataset.js +31 -2
  6. package/dist/core/dataset/build-dataset.js.map +1 -1
  7. package/dist/core/integrity/check-invariants.d.ts +41 -0
  8. package/dist/core/integrity/check-invariants.js +349 -0
  9. package/dist/core/integrity/check-invariants.js.map +1 -0
  10. package/dist/core/scheduler/scan-once.d.ts +4 -1
  11. package/dist/core/scheduler/scan-once.js +53 -14
  12. package/dist/core/scheduler/scan-once.js.map +1 -1
  13. package/dist/core/scheduler/scheduler.js +64 -11
  14. package/dist/core/scheduler/scheduler.js.map +1 -1
  15. package/dist/core/storage/live-status.d.ts +5 -3
  16. package/dist/core/storage/live-status.js +6 -2
  17. package/dist/core/storage/live-status.js.map +1 -1
  18. package/dist/core/storage/migrate.d.ts +5 -0
  19. package/dist/core/storage/migrate.js +24 -8
  20. package/dist/core/storage/migrate.js.map +1 -1
  21. package/dist/core/storage/pg-session-store.d.ts +2 -2
  22. package/dist/core/storage/pg-session-store.js +31 -6
  23. package/dist/core/storage/pg-session-store.js.map +1 -1
  24. package/dist/core/storage/sqlite-fact-store.js +7 -15
  25. package/dist/core/storage/sqlite-fact-store.js.map +1 -1
  26. package/dist/core/storage/sqlite-session-store.d.ts +14 -2
  27. package/dist/core/storage/sqlite-session-store.js +61 -24
  28. package/dist/core/storage/sqlite-session-store.js.map +1 -1
  29. package/dist/hook/prompt-recall-hook.js +1 -1
  30. package/dist/hook/prompt-recall-hook.js.map +1 -1
  31. package/dist/hook/stop-hook.js +1 -1
  32. package/dist/hook/stop-hook.js.map +1 -1
  33. package/dist/http/app.js +3 -1
  34. package/dist/http/app.js.map +1 -1
  35. package/dist/shared/types.d.ts +24 -5
  36. package/dist/ui/assets/index-CJgVgxlM.js +11 -0
  37. package/dist/ui/assets/index-D5lKaksH.css +1 -0
  38. package/dist/ui/index.html +2 -2
  39. package/migrations/000_initial_schema.sql +174 -0
  40. package/migrations/001_entity_type_rename.sql +17 -0
  41. package/migrations/002_adapter_state_extend.sql +12 -0
  42. package/migrations/003_session_embeddings.sql +11 -0
  43. package/migrations/004_facts.sql +46 -0
  44. package/migrations/005_sources.sql +31 -0
  45. package/migrations/006_providers.sql +33 -0
  46. package/migrations/007_source_tokens.sql +17 -0
  47. package/migrations/008_fts_rebuild.sql +9 -0
  48. package/migrations/009_session_embedding_chunks.sql +46 -0
  49. package/migrations/010_sources_opencode.sql +30 -0
  50. package/migrations/011_sources_hermes_agent.sql +30 -0
  51. package/migrations/012_sources_aider.sql +30 -0
  52. package/migrations/013_adapter_state_failure_count.sql +12 -0
  53. package/migrations/014_sources_cursor.sql +30 -0
  54. package/migrations/015_sources_windsurf.sql +30 -0
  55. package/migrations/016_sources_codex.sql +30 -0
  56. package/migrations/017_signals.sql +31 -0
  57. package/migrations/018_repair_self_supersede.sql +21 -0
  58. package/migrations/019_split_replaces.sql +117 -0
  59. package/migrations/020_collapse_duplicate_active_facts.sql +45 -0
  60. package/migrations/pg/001_initial.sql +214 -0
  61. package/migrations/pg/018_repair_self_supersede.sql +16 -0
  62. package/migrations/pg/019_split_replaces.sql +32 -0
  63. package/migrations/pg/020_collapse_duplicate_active_facts.sql +46 -0
  64. package/nlm/index.js +1 -1
  65. package/package.json +2 -1
  66. package/plugin/scripts/prompt-recall-hook.mjs +1 -1
  67. package/plugin/scripts/stop-hook.mjs +1 -1
  68. package/dist/ui/assets/index-BTj60bAH.js +0 -11
  69. package/dist/ui/assets/index-Baw2qIKc.css +0 -1
@@ -0,0 +1,349 @@
1
+ /**
2
+ * check-invariants — pure integrity check definitions.
3
+ *
4
+ * Each check returns a typed violation describing the problem. A clean
5
+ * database returns an empty violations array. The SQL strings are backend-
6
+ * agnostic where possible; per-backend runners at the bottom execute them.
7
+ *
8
+ * Checks:
9
+ * I1 no self-loop edges in session_edges for any 'kind'
10
+ * I2 status matches incoming edge kind: status='superseded' has an incoming
11
+ * 'supersedes' edge; status='replaced' has an incoming 'replaces' edge
12
+ * I3 supersedence graph is acyclic over the union of 'supersedes' and
13
+ * 'replaces' edges (BFS, depth-capped at 100)
14
+ * I4 every session_edges endpoint exists in sessions
15
+ * I5 at most one active fact per (subject, predicate); every superseded_by
16
+ * references an existing facts.id
17
+ * I6 every non-null adapter_state.session_id references a sessions.id
18
+ */
19
+ // ─── SQL strings (portable) ──────────────────────────────────────────────────
20
+ const SQL_I1 = `
21
+ SELECT from_session AS bad_id
22
+ FROM session_edges
23
+ WHERE from_session = to_session
24
+ LIMIT 6
25
+ `;
26
+ const SQL_I2_ORPHANED = `
27
+ SELECT s.id AS bad_id
28
+ FROM sessions s
29
+ WHERE s.status = 'superseded'
30
+ AND NOT EXISTS (
31
+ SELECT 1 FROM session_edges e
32
+ WHERE e.to_session = s.id AND e.kind = 'supersedes'
33
+ )
34
+ LIMIT 6
35
+ `;
36
+ const SQL_I2_ORPHANED_REPLACED = `
37
+ SELECT s.id AS bad_id
38
+ FROM sessions s
39
+ WHERE s.status = 'replaced'
40
+ AND NOT EXISTS (
41
+ SELECT 1 FROM session_edges e
42
+ WHERE e.to_session = s.id AND e.kind = 'replaces'
43
+ )
44
+ LIMIT 6
45
+ `;
46
+ const SQL_I4_MISSING_FROM = `
47
+ SELECT e.from_session AS bad_id
48
+ FROM session_edges e
49
+ WHERE NOT EXISTS (SELECT 1 FROM sessions s WHERE s.id = e.from_session)
50
+ LIMIT 6
51
+ `;
52
+ const SQL_I4_MISSING_TO = `
53
+ SELECT e.to_session AS bad_id
54
+ FROM session_edges e
55
+ WHERE NOT EXISTS (SELECT 1 FROM sessions s WHERE s.id = e.to_session)
56
+ LIMIT 6
57
+ `;
58
+ // One row per conflicting (subject, predicate) group — the count is the number
59
+ // of conflicts to resolve, not the number of facts involved. MIN(id) keeps the
60
+ // projection aggregate-only so PostgreSQL's GROUP BY strictness accepts it.
61
+ const SQL_I5_DUPLICATE_FACTS = `
62
+ SELECT MIN(id) AS bad_id
63
+ FROM facts
64
+ WHERE superseded_by IS NULL
65
+ GROUP BY subject, predicate
66
+ HAVING COUNT(*) > 1
67
+ LIMIT 6
68
+ `;
69
+ const SQL_I5_DANGLING_SUPERSEDED_BY = `
70
+ SELECT f.id AS bad_id
71
+ FROM facts f
72
+ WHERE f.superseded_by IS NOT NULL
73
+ AND NOT EXISTS (SELECT 1 FROM facts f2 WHERE f2.id = f.superseded_by)
74
+ LIMIT 6
75
+ `;
76
+ const SQL_I6 = `
77
+ SELECT a.session_id AS bad_id
78
+ FROM adapter_state a
79
+ WHERE a.session_id IS NOT NULL
80
+ AND NOT EXISTS (SELECT 1 FROM sessions s WHERE s.id = a.session_id)
81
+ LIMIT 6
82
+ `;
83
+ const MAX_CYCLE_DEPTH = 100;
84
+ export function detectCycles(loadEdges) {
85
+ const edges = loadEdges();
86
+ const children = new Map();
87
+ for (const { from_session, to_session } of edges) {
88
+ const list = children.get(from_session);
89
+ if (list)
90
+ list.push(to_session);
91
+ else
92
+ children.set(from_session, [to_session]);
93
+ }
94
+ const cycleNodes = [];
95
+ const visited = new Set();
96
+ for (const start of children.keys()) {
97
+ if (visited.has(start))
98
+ continue;
99
+ const queue = [{ node: start, depth: 0 }];
100
+ const inPath = new Set();
101
+ while (queue.length > 0) {
102
+ const item = queue.shift();
103
+ if (item.depth > MAX_CYCLE_DEPTH)
104
+ break;
105
+ if (inPath.has(item.node)) {
106
+ if (cycleNodes.length < 5)
107
+ cycleNodes.push(item.node);
108
+ continue;
109
+ }
110
+ inPath.add(item.node);
111
+ visited.add(item.node);
112
+ for (const child of children.get(item.node) ?? []) {
113
+ queue.push({ node: child, depth: item.depth + 1 });
114
+ }
115
+ }
116
+ }
117
+ if (cycleNodes.length === 0)
118
+ return null;
119
+ return {
120
+ id: "I3",
121
+ description: "supersedence graph contains cycles",
122
+ count: cycleNodes.length,
123
+ sampleIds: cycleNodes.slice(0, 5),
124
+ };
125
+ }
126
+ // ─── SQLite runner ────────────────────────────────────────────────────────────
127
+ function sqliteRows(db, sql) {
128
+ return db
129
+ .prepare(sql)
130
+ .all()
131
+ .map((r) => r.bad_id);
132
+ }
133
+ function sqliteCount(db, sql) {
134
+ const countSql = `SELECT count(*) AS n FROM (${sql.replace(/LIMIT \d+/, "")})`;
135
+ return (db.prepare(countSql).get()?.n) ?? 0;
136
+ }
137
+ function buildViolation(id, description, samples, count) {
138
+ return { id, description, count, sampleIds: samples.slice(0, 5) };
139
+ }
140
+ export function runChecksOnSqlite(db) {
141
+ const violations = [];
142
+ // I1
143
+ const i1Samples = sqliteRows(db, SQL_I1);
144
+ if (i1Samples.length > 0) {
145
+ const count = sqliteCount(db, SQL_I1);
146
+ violations.push(buildViolation("I1", "self-loop edges in session_edges", i1Samples, count));
147
+ }
148
+ // I2
149
+ const i2Samples = sqliteRows(db, SQL_I2_ORPHANED);
150
+ if (i2Samples.length > 0) {
151
+ const count = sqliteCount(db, SQL_I2_ORPHANED);
152
+ violations.push(buildViolation("I2", "sessions marked superseded with no incoming supersedes edge", i2Samples, count));
153
+ }
154
+ const i2ReplacedSamples = sqliteRows(db, SQL_I2_ORPHANED_REPLACED);
155
+ if (i2ReplacedSamples.length > 0) {
156
+ const count = sqliteCount(db, SQL_I2_ORPHANED_REPLACED);
157
+ violations.push(buildViolation("I2r", "sessions marked replaced with no incoming replaces edge", i2ReplacedSamples, count));
158
+ }
159
+ // I3
160
+ const i3 = detectCycles(() => db
161
+ .prepare("SELECT from_session, to_session FROM session_edges WHERE kind IN ('supersedes', 'replaces')")
162
+ .all());
163
+ if (i3)
164
+ violations.push(i3);
165
+ // I4
166
+ const i4FromSamples = sqliteRows(db, SQL_I4_MISSING_FROM);
167
+ const i4ToSamples = sqliteRows(db, SQL_I4_MISSING_TO);
168
+ const i4All = [...new Set([...i4FromSamples, ...i4ToSamples])];
169
+ if (i4All.length > 0) {
170
+ const countFrom = sqliteCount(db, SQL_I4_MISSING_FROM);
171
+ const countTo = sqliteCount(db, SQL_I4_MISSING_TO);
172
+ violations.push(buildViolation("I4", "session_edges reference session ids not in sessions table", i4All, countFrom + countTo));
173
+ }
174
+ // I5
175
+ const i5DupSamples = sqliteRows(db, SQL_I5_DUPLICATE_FACTS);
176
+ if (i5DupSamples.length > 0) {
177
+ const count = sqliteCount(db, SQL_I5_DUPLICATE_FACTS);
178
+ violations.push(buildViolation("I5a", "multiple active facts for same (subject, predicate)", i5DupSamples, count));
179
+ }
180
+ const i5DangleSamples = sqliteRows(db, SQL_I5_DANGLING_SUPERSEDED_BY);
181
+ if (i5DangleSamples.length > 0) {
182
+ const count = sqliteCount(db, SQL_I5_DANGLING_SUPERSEDED_BY);
183
+ violations.push(buildViolation("I5b", "facts.superseded_by references non-existent facts.id", i5DangleSamples, count));
184
+ }
185
+ // I6
186
+ const i6Samples = sqliteRows(db, SQL_I6);
187
+ if (i6Samples.length > 0) {
188
+ const count = sqliteCount(db, SQL_I6);
189
+ violations.push(buildViolation("I6", "adapter_state.session_id references non-existent sessions.id", i6Samples, count));
190
+ }
191
+ return violations;
192
+ }
193
+ // ─── PostgreSQL runner ────────────────────────────────────────────────────────
194
+ async function pgRows(pool, sql) {
195
+ const stripped = sql.replace(/LIMIT \d+/, "LIMIT 6");
196
+ const result = await pool.query(stripped);
197
+ return result.rows.map((r) => r.bad_id);
198
+ }
199
+ async function pgCount(pool, sql) {
200
+ const countSql = `SELECT count(*) AS n FROM (${sql.replace(/LIMIT \d+/, "")}) AS sub`;
201
+ const result = await pool.query(countSql);
202
+ return Number.parseInt(result.rows[0]?.n ?? "0", 10);
203
+ }
204
+ export async function runChecksOnPg(pool) {
205
+ const violations = [];
206
+ // I1
207
+ const i1Samples = await pgRows(pool, SQL_I1);
208
+ if (i1Samples.length > 0) {
209
+ const count = await pgCount(pool, SQL_I1);
210
+ violations.push(buildViolation("I1", "self-loop edges in session_edges", i1Samples, count));
211
+ }
212
+ // I2
213
+ const i2Samples = await pgRows(pool, SQL_I2_ORPHANED);
214
+ if (i2Samples.length > 0) {
215
+ const count = await pgCount(pool, SQL_I2_ORPHANED);
216
+ violations.push(buildViolation("I2", "sessions marked superseded with no incoming supersedes edge", i2Samples, count));
217
+ }
218
+ const i2ReplacedSamples = await pgRows(pool, SQL_I2_ORPHANED_REPLACED);
219
+ if (i2ReplacedSamples.length > 0) {
220
+ const count = await pgCount(pool, SQL_I2_ORPHANED_REPLACED);
221
+ violations.push(buildViolation("I2r", "sessions marked replaced with no incoming replaces edge", i2ReplacedSamples, count));
222
+ }
223
+ // I3
224
+ const edgeResult = await pool.query("SELECT from_session, to_session FROM session_edges WHERE kind IN ('supersedes', 'replaces')");
225
+ const i3 = detectCycles(() => edgeResult.rows);
226
+ if (i3)
227
+ violations.push(i3);
228
+ // I4
229
+ const [i4FromSamples, i4ToSamples] = await Promise.all([
230
+ pgRows(pool, SQL_I4_MISSING_FROM),
231
+ pgRows(pool, SQL_I4_MISSING_TO),
232
+ ]);
233
+ const i4All = [...new Set([...i4FromSamples, ...i4ToSamples])];
234
+ if (i4All.length > 0) {
235
+ const [countFrom, countTo] = await Promise.all([
236
+ pgCount(pool, SQL_I4_MISSING_FROM),
237
+ pgCount(pool, SQL_I4_MISSING_TO),
238
+ ]);
239
+ violations.push(buildViolation("I4", "session_edges reference session ids not in sessions table", i4All, countFrom + countTo));
240
+ }
241
+ // I5
242
+ const [i5DupSamples, i5DangleSamples] = await Promise.all([
243
+ pgRows(pool, SQL_I5_DUPLICATE_FACTS),
244
+ pgRows(pool, SQL_I5_DANGLING_SUPERSEDED_BY),
245
+ ]);
246
+ if (i5DupSamples.length > 0) {
247
+ const count = await pgCount(pool, SQL_I5_DUPLICATE_FACTS);
248
+ violations.push(buildViolation("I5a", "multiple active facts for same (subject, predicate)", i5DupSamples, count));
249
+ }
250
+ if (i5DangleSamples.length > 0) {
251
+ const count = await pgCount(pool, SQL_I5_DANGLING_SUPERSEDED_BY);
252
+ violations.push(buildViolation("I5b", "facts.superseded_by references non-existent facts.id", i5DangleSamples, count));
253
+ }
254
+ // I6
255
+ const i6Samples = await pgRows(pool, SQL_I6);
256
+ if (i6Samples.length > 0) {
257
+ const count = await pgCount(pool, SQL_I6);
258
+ violations.push(buildViolation("I6", "adapter_state.session_id references non-existent sessions.id", i6Samples, count));
259
+ }
260
+ return violations;
261
+ }
262
+ // ─── Cheap subset (I1 + I2 + I6) for scheduled watchdog ─────────────────────
263
+ export function runCheapChecksOnSqlite(db) {
264
+ const violations = [];
265
+ const i1Samples = sqliteRows(db, SQL_I1);
266
+ if (i1Samples.length > 0) {
267
+ violations.push(buildViolation("I1", "self-loop edges in session_edges", i1Samples, sqliteCount(db, SQL_I1)));
268
+ }
269
+ const i2Samples = sqliteRows(db, SQL_I2_ORPHANED);
270
+ if (i2Samples.length > 0) {
271
+ violations.push(buildViolation("I2", "sessions marked superseded with no incoming supersedes edge", i2Samples, sqliteCount(db, SQL_I2_ORPHANED)));
272
+ }
273
+ const i2ReplacedSamples = sqliteRows(db, SQL_I2_ORPHANED_REPLACED);
274
+ if (i2ReplacedSamples.length > 0) {
275
+ violations.push(buildViolation("I2r", "sessions marked replaced with no incoming replaces edge", i2ReplacedSamples, sqliteCount(db, SQL_I2_ORPHANED_REPLACED)));
276
+ }
277
+ const i6Samples = sqliteRows(db, SQL_I6);
278
+ if (i6Samples.length > 0) {
279
+ violations.push(buildViolation("I6", "adapter_state.session_id references non-existent sessions.id", i6Samples, sqliteCount(db, SQL_I6)));
280
+ }
281
+ return violations;
282
+ }
283
+ export async function runCheapChecksOnPg(pool) {
284
+ const violations = [];
285
+ const [i1, i2, i2r, i6] = await Promise.all([
286
+ pgRows(pool, SQL_I1),
287
+ pgRows(pool, SQL_I2_ORPHANED),
288
+ pgRows(pool, SQL_I2_ORPHANED_REPLACED),
289
+ pgRows(pool, SQL_I6),
290
+ ]);
291
+ if (i1.length > 0) {
292
+ violations.push(buildViolation("I1", "self-loop edges in session_edges", i1, await pgCount(pool, SQL_I1)));
293
+ }
294
+ if (i2.length > 0) {
295
+ violations.push(buildViolation("I2", "sessions marked superseded with no incoming supersedes edge", i2, await pgCount(pool, SQL_I2_ORPHANED)));
296
+ }
297
+ if (i2r.length > 0) {
298
+ violations.push(buildViolation("I2r", "sessions marked replaced with no incoming replaces edge", i2r, await pgCount(pool, SQL_I2_ORPHANED_REPLACED)));
299
+ }
300
+ if (i6.length > 0) {
301
+ violations.push(buildViolation("I6", "adapter_state.session_id references non-existent sessions.id", i6, await pgCount(pool, SQL_I6)));
302
+ }
303
+ return violations;
304
+ }
305
+ export function applyFixOnSqlite(db) {
306
+ const deleteResult = db
307
+ .prepare("DELETE FROM session_edges WHERE from_session = to_session")
308
+ .run();
309
+ const updateSuperseded = db
310
+ .prepare(`
311
+ UPDATE sessions
312
+ SET status = 'closed', updated_at = datetime('now')
313
+ WHERE status = 'superseded'
314
+ AND id NOT IN (SELECT to_session FROM session_edges WHERE kind = 'supersedes')
315
+ `)
316
+ .run();
317
+ const updateReplaced = db
318
+ .prepare(`
319
+ UPDATE sessions
320
+ SET status = 'closed', updated_at = datetime('now')
321
+ WHERE status = 'replaced'
322
+ AND id NOT IN (SELECT to_session FROM session_edges WHERE kind = 'replaces')
323
+ `)
324
+ .run();
325
+ return {
326
+ deletedSelfLoops: deleteResult.changes,
327
+ restoredToClosed: updateSuperseded.changes + updateReplaced.changes,
328
+ };
329
+ }
330
+ export async function applyFixOnPg(pool) {
331
+ const deleteResult = await pool.query("DELETE FROM session_edges WHERE from_session = to_session");
332
+ const updateSuperseded = await pool.query(`
333
+ UPDATE sessions
334
+ SET status = 'closed', updated_at = NOW()
335
+ WHERE status = 'superseded'
336
+ AND id NOT IN (SELECT to_session FROM session_edges WHERE kind = 'supersedes')
337
+ `);
338
+ const updateReplaced = await pool.query(`
339
+ UPDATE sessions
340
+ SET status = 'closed', updated_at = NOW()
341
+ WHERE status = 'replaced'
342
+ AND id NOT IN (SELECT to_session FROM session_edges WHERE kind = 'replaces')
343
+ `);
344
+ return {
345
+ deletedSelfLoops: deleteResult.rowCount ?? 0,
346
+ restoredToClosed: (updateSuperseded.rowCount ?? 0) + (updateReplaced.rowCount ?? 0),
347
+ };
348
+ }
349
+ //# sourceMappingURL=check-invariants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-invariants.js","sourceRoot":"","sources":["../../../src/core/integrity/check-invariants.ts"],"names":[],"mappings":"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"}
@@ -33,4 +33,7 @@ export declare function recordClassified(db: Database.Database, adapterName: str
33
33
  export declare function recordFailed(db: Database.Database, adapterName: string, sourcePath: string): void;
34
34
  export declare function getFileSize(sourcePath: string): number | null;
35
35
  export declare function scanOncePg(adapter: TranscriptAdapter, idleMinutes: number, pool: Pool, now?: number): Promise<ScanResult[]>;
36
- export declare function recordFailedPg(pool: Pool, adapterName: string, sourcePath: string, fileSize: number | null): Promise<void>;
36
+ export declare function recordClassifiedPg(pool: Pool, adapterName: string, sourcePath: string, sessionId: string): Promise<void>;
37
+ export declare function recordSkippedLowConfidence(db: Database.Database, adapterName: string, sourcePath: string): void;
38
+ export declare function recordSkippedLowConfidencePg(pool: Pool, adapterName: string, sourcePath: string): Promise<void>;
39
+ export declare function recordFailedPg(pool: Pool, adapterName: string, sourcePath: string, fileSize: number | null): Promise<number>;
@@ -42,7 +42,6 @@ export async function scanOnce(adapter, idleMinutes, db, now = Date.now()) {
42
42
  if (age < idleMs)
43
43
  continue;
44
44
  const prior = byPath.get(path);
45
- let supersedes = null;
46
45
  if (prior) {
47
46
  const sizeUnchanged = (prior.file_size ?? 0) === st.size;
48
47
  if (sizeUnchanged) {
@@ -54,11 +53,11 @@ export async function scanOnce(adapter, idleMinutes, db, now = Date.now()) {
54
53
  if (prior.failure_count >= MAX_CLASSIFY_FAILURES) {
55
54
  db.prepare("UPDATE adapter_state SET failure_count = 0 WHERE adapter_name = ? AND source_path = ?").run(adapter.name, path);
56
55
  }
57
- supersedes = prior.session_id;
58
56
  }
59
57
  const chunk = await adapter.parseSession(path);
60
58
  if (!chunk)
61
59
  continue;
60
+ const supersedes = prior?.session_id && prior.session_id !== chunk.id ? prior.session_id : null;
62
61
  out.push({ chunk, supersedes });
63
62
  }
64
63
  return out;
@@ -109,9 +108,15 @@ export async function scanOncePg(adapter, idleMinutes, pool, now = Date.now()) {
109
108
  const idleMs = idleMinutes * 60 * 1000;
110
109
  const stateRows = await pool.query(`SELECT source_path, file_size, session_id, COALESCE(failure_count, 0) AS failure_count
111
110
  FROM adapter_state WHERE adapter_name = $1`, [adapter.name]);
111
+ // pg returns BIGINT (file_size) as a string; coerce to number so the
112
+ // unchanged-size check compares against statSync's numeric size.
112
113
  const stateMap = new Map(stateRows.rows.map((r) => [
113
114
  r.source_path,
114
- { fileSize: r.file_size, sessionId: r.session_id, failureCount: r.failure_count },
115
+ {
116
+ fileSize: r.file_size === null ? null : Number(r.file_size),
117
+ sessionId: r.session_id,
118
+ failureCount: r.failure_count,
119
+ },
115
120
  ]));
116
121
  const paths = await adapter.discover();
117
122
  const results = [];
@@ -140,25 +145,59 @@ export async function scanOncePg(adapter, idleMinutes, pool, now = Date.now()) {
140
145
  const chunk = await adapter.parseSession(sourcePath);
141
146
  if (!chunk)
142
147
  continue;
143
- const prior = stateMap.get(sourcePath);
144
- const supersedes = prior?.sessionId !== chunk.id ? (prior?.sessionId ?? null) : null;
148
+ const supersedes = state?.sessionId && state.sessionId !== chunk.id ? state.sessionId : null;
145
149
  results.push({ chunk, supersedes });
146
- await pool.query(`INSERT INTO adapter_state (adapter_name, source_path, last_offset, file_size, session_id, failure_count)
147
- VALUES ($1, $2, 0, $3, $4, 0)
148
- ON CONFLICT (adapter_name, source_path) DO UPDATE SET
149
- file_size = EXCLUDED.file_size,
150
- session_id = EXCLUDED.session_id,
151
- failure_count = 0,
152
- last_processed_at = NOW()`, [adapter.name, sourcePath, getFileSize(sourcePath), chunk.id]);
153
150
  }
154
151
  return results;
155
152
  }
153
+ export async function recordClassifiedPg(pool, adapterName, sourcePath, sessionId) {
154
+ const size = getFileSize(sourcePath);
155
+ if (size === null)
156
+ return;
157
+ await pool.query(`INSERT INTO adapter_state (adapter_name, source_path, last_offset, file_size, session_id, failure_count, last_processed_at)
158
+ VALUES ($1, $2, 0, $3, $4, 0, NOW())
159
+ ON CONFLICT (adapter_name, source_path) DO UPDATE SET
160
+ file_size = EXCLUDED.file_size,
161
+ session_id = EXCLUDED.session_id,
162
+ failure_count = 0,
163
+ last_processed_at = NOW()`, [adapterName, sourcePath, size, sessionId]);
164
+ }
165
+ export function recordSkippedLowConfidence(db, adapterName, sourcePath) {
166
+ let size = 0;
167
+ try {
168
+ size = statSync(sourcePath).size;
169
+ }
170
+ catch {
171
+ return;
172
+ }
173
+ db.prepare(`INSERT INTO adapter_state
174
+ (adapter_name, source_path, last_offset, file_size, session_id, failure_count, last_processed_at)
175
+ VALUES (?, ?, ?, ?, NULL, 0, datetime('now'))
176
+ ON CONFLICT(adapter_name, source_path) DO UPDATE SET
177
+ last_offset = excluded.last_offset,
178
+ file_size = excluded.file_size,
179
+ failure_count = 0,
180
+ last_processed_at = excluded.last_processed_at`).run(adapterName, sourcePath, size, size);
181
+ }
182
+ export async function recordSkippedLowConfidencePg(pool, adapterName, sourcePath) {
183
+ const size = getFileSize(sourcePath);
184
+ if (size === null)
185
+ return;
186
+ await pool.query(`INSERT INTO adapter_state (adapter_name, source_path, last_offset, file_size, session_id, failure_count, last_processed_at)
187
+ VALUES ($1, $2, 0, $3, NULL, 0, NOW())
188
+ ON CONFLICT (adapter_name, source_path) DO UPDATE SET
189
+ file_size = EXCLUDED.file_size,
190
+ failure_count = 0,
191
+ last_processed_at = NOW()`, [adapterName, sourcePath, size]);
192
+ }
156
193
  export async function recordFailedPg(pool, adapterName, sourcePath, fileSize) {
157
- await pool.query(`INSERT INTO adapter_state (adapter_name, source_path, last_offset, failure_count, file_size)
194
+ const res = await pool.query(`INSERT INTO adapter_state (adapter_name, source_path, last_offset, failure_count, file_size)
158
195
  VALUES ($1, $2, 0, 1, $3)
159
196
  ON CONFLICT (adapter_name, source_path) DO UPDATE SET
160
197
  failure_count = adapter_state.failure_count + 1,
161
198
  file_size = $3,
162
- last_processed_at = NOW()`, [adapterName, sourcePath, fileSize]);
199
+ last_processed_at = NOW()
200
+ RETURNING failure_count`, [adapterName, sourcePath, fileSize]);
201
+ return res.rows[0]?.failure_count ?? 1;
163
202
  }
164
203
  //# sourceMappingURL=scan-once.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scan-once.js","sourceRoot":"","sources":["../../../src/core/scheduler/scan-once.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scan-once.js","sourceRoot":"","sources":["../../../src/core/scheduler/scan-once.ts"],"names":[],"mappings":"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"}
@@ -16,14 +16,45 @@
16
16
  * Confidence floor of 0.3 mirrors Python: classifier outputs below that
17
17
  * are skipped rather than persisted as low-quality noise.
18
18
  */
19
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
20
+ import { dirname, join } from "node:path";
21
+ import { homedir } from "node:os";
19
22
  import { extractFacts } from "../facts/extract-facts.js";
20
23
  import { normalizeSignal } from "../signals/ingest-signal.js";
21
- import { MAX_CLASSIFY_FAILURES, getFileSize, recordClassified, recordFailed, recordFailedPg, scanOnce, scanOncePg } from "./scan-once.js";
24
+ import { MAX_CLASSIFY_FAILURES, getFileSize, recordClassified, recordClassifiedPg, recordFailed, recordFailedPg, recordSkippedLowConfidence, recordSkippedLowConfidencePg, scanOnce, scanOncePg } from "./scan-once.js";
25
+ import { runCheapChecksOnSqlite } from "../integrity/check-invariants.js";
22
26
  const DEFAULT_INTERVAL_MS = 30 * 60 * 1000; // 30 min, matches Python default
23
27
  const DEFAULT_CLASSIFY_TIMEOUT_MS = 120_000;
24
28
  const DEFAULT_CONFIDENCE_FLOOR = 0.3;
25
29
  const DEFAULT_IDLE_MINUTES = 15;
26
30
  const BODY_CAP = 200_000;
31
+ const INTEGRITY_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24h
32
+ function integrityCheckCachePath() {
33
+ return process.env["NLM_INTEGRITY_CACHE"] ?? join(homedir(), ".nlm", "integrity-check.json");
34
+ }
35
+ function shouldRunIntegrityCheck(now) {
36
+ try {
37
+ const raw = readFileSync(integrityCheckCachePath(), "utf8");
38
+ const parsed = JSON.parse(raw);
39
+ if (typeof parsed.checkedAt === "string") {
40
+ return now - Date.parse(parsed.checkedAt) >= INTEGRITY_CHECK_INTERVAL_MS;
41
+ }
42
+ }
43
+ catch {
44
+ // cache missing or corrupt — run the check
45
+ }
46
+ return true;
47
+ }
48
+ function markIntegrityCheckRan(now) {
49
+ try {
50
+ const path = integrityCheckCachePath();
51
+ mkdirSync(dirname(path), { recursive: true });
52
+ writeFileSync(path, JSON.stringify({ checkedAt: new Date(now).toISOString() }, null, 2), "utf8");
53
+ }
54
+ catch {
55
+ // cache write failure is non-fatal
56
+ }
57
+ }
27
58
  export class ScanScheduler {
28
59
  opts;
29
60
  stopped = true;
@@ -70,8 +101,21 @@ export class ScanScheduler {
70
101
  let classifyFailures = 0;
71
102
  let storageFailures = 0;
72
103
  let chunksSeen = 0;
104
+ const now = Date.now();
105
+ const _pgPool = this.opts.store.pgPool?.();
106
+ if (!_pgPool && shouldRunIntegrityCheck(now)) {
107
+ try {
108
+ const violations = runCheapChecksOnSqlite(this.opts.store.rawDb());
109
+ markIntegrityCheckRan(now);
110
+ for (const v of violations) {
111
+ this.opts.logger(`[integrity] FAIL ${v.id} count=${v.count} ${v.description} run \`nlm doctor\` to inspect or \`nlm doctor --fix\` to repair`);
112
+ }
113
+ }
114
+ catch (e) {
115
+ this.opts.logger(`[integrity] check error: ${e instanceof Error ? e.message : String(e)}`);
116
+ }
117
+ }
73
118
  for (const adapter of this.opts.adapters) {
74
- const _pgPool = this.opts.store.pgPool?.();
75
119
  let results;
76
120
  try {
77
121
  results = _pgPool
@@ -92,23 +136,29 @@ export class ScanScheduler {
92
136
  catch (e) {
93
137
  classifyFailures += 1;
94
138
  const reason = e instanceof TimeoutError ? "timed out" : `error: ${e instanceof Error ? e.message : String(e)}`;
139
+ let count;
95
140
  if (_pgPool) {
96
- void recordFailedPg(_pgPool, adapter.name, chunk.sourcePath, getFileSize(chunk.sourcePath));
141
+ count = await recordFailedPg(_pgPool, adapter.name, chunk.sourcePath, getFileSize(chunk.sourcePath));
97
142
  }
98
143
  else {
99
144
  recordFailed(this.opts.store.rawDb(), adapter.name, chunk.sourcePath);
100
- }
101
- const count = _pgPool
102
- ? 1
103
- : (this.opts.store.rawDb()
145
+ count = this.opts.store.rawDb()
104
146
  .prepare("SELECT COALESCE(failure_count, 0) AS failure_count FROM adapter_state WHERE adapter_name = ? AND source_path = ?")
105
- .get(adapter.name, chunk.sourcePath)?.failure_count ?? 1);
147
+ .get(adapter.name, chunk.sourcePath)?.failure_count ?? 1;
148
+ }
106
149
  const ceiling = count >= MAX_CLASSIFY_FAILURES ? ` (failure ${count}/${MAX_CLASSIFY_FAILURES} — will skip until file grows)` : ` (failure ${count}/${MAX_CLASSIFY_FAILURES})`;
107
150
  this.opts.logger(`[scheduler] classifier ${reason} for ${chunk.id}${ceiling}`);
108
151
  continue;
109
152
  }
110
153
  if (classification.confidence < this.opts.confidenceFloor) {
111
154
  skippedLowConfidence += 1;
155
+ if (_pgPool) {
156
+ await recordSkippedLowConfidencePg(_pgPool, adapter.name, chunk.sourcePath);
157
+ }
158
+ else {
159
+ recordSkippedLowConfidence(this.opts.store.rawDb(), adapter.name, chunk.sourcePath);
160
+ }
161
+ this.opts.logger(`[scheduler] low-confidence (${classification.confidence} < ${this.opts.confidenceFloor}) for ${chunk.id} - skipping until file grows`);
112
162
  continue;
113
163
  }
114
164
  const record = {
@@ -137,8 +187,11 @@ export class ScanScheduler {
137
187
  }
138
188
  : null;
139
189
  try {
140
- await this.opts.store.insertSession(record, this.opts.embedder, supersedes, factSink);
141
- if (!_pgPool) {
190
+ await this.opts.store.insertSession(record, this.opts.embedder, supersedes ? { priorSessionId: supersedes, kind: "replaces" } : null, factSink);
191
+ if (_pgPool) {
192
+ await recordClassifiedPg(_pgPool, adapter.name, chunk.sourcePath, chunk.id);
193
+ }
194
+ else {
142
195
  recordClassified(this.opts.store.rawDb(), adapter.name, chunk.sourcePath, chunk.id);
143
196
  }
144
197
  inserted += 1;
@@ -146,7 +199,7 @@ export class ScanScheduler {
146
199
  catch (e) {
147
200
  storageFailures += 1;
148
201
  if (_pgPool) {
149
- void recordFailedPg(_pgPool, adapter.name, chunk.sourcePath, getFileSize(chunk.sourcePath));
202
+ await recordFailedPg(_pgPool, adapter.name, chunk.sourcePath, getFileSize(chunk.sourcePath));
150
203
  }
151
204
  else {
152
205
  recordFailed(this.opts.store.rawDb(), adapter.name, chunk.sourcePath);