claude-memory-layer 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +14 -0
- package/.history/package_20260201114632.json +46 -0
- package/dist/cli/index.js +360 -154
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +337 -161
- package/dist/core/index.js.map +3 -3
- package/dist/hooks/session-end.js +320 -130
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +331 -138
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +320 -130
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +320 -130
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/services/memory-service.js +349 -128
- package/dist/services/memory-service.js.map +4 -4
- package/package.json +2 -1
- package/src/cli/index.ts +84 -23
- package/src/core/consolidated-store.ts +33 -18
- package/src/core/continuity-manager.ts +12 -7
- package/src/core/db-wrapper.ts +112 -0
- package/src/core/edge-repo.ts +22 -13
- package/src/core/entity-repo.ts +23 -14
- package/src/core/event-store.ts +98 -72
- package/src/core/task/blocker-resolver.ts +17 -9
- package/src/core/task/task-matcher.ts +8 -6
- package/src/core/task/task-projector.ts +29 -16
- package/src/core/task/task-resolver.ts +17 -9
- package/src/core/vector-outbox.ts +29 -16
- package/src/core/vector-store.ts +23 -12
- package/src/core/vector-worker.ts +7 -4
- package/src/core/working-set-store.ts +31 -18
- package/src/hooks/session-end.ts +3 -2
- package/src/hooks/session-start.ts +12 -8
- package/src/hooks/stop.ts +3 -2
- package/src/hooks/user-prompt-submit.ts +3 -2
- package/src/services/memory-service.ts +158 -6
package/src/core/event-store.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Principles: Append-only, Single Source of Truth, Idempotency
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Database } from 'duckdb';
|
|
7
6
|
import { randomUUID } from 'crypto';
|
|
8
7
|
import {
|
|
9
8
|
MemoryEvent,
|
|
@@ -13,13 +12,14 @@ import {
|
|
|
13
12
|
OutboxItem
|
|
14
13
|
} from './types.js';
|
|
15
14
|
import { makeCanonicalKey, makeDedupeKey } from './canonical-key.js';
|
|
15
|
+
import { createDatabase, dbRun, dbAll, dbClose, toDate, type Database } from './db-wrapper.js';
|
|
16
16
|
|
|
17
17
|
export class EventStore {
|
|
18
18
|
private db: Database;
|
|
19
19
|
private initialized = false;
|
|
20
20
|
|
|
21
21
|
constructor(private dbPath: string) {
|
|
22
|
-
this.db =
|
|
22
|
+
this.db = createDatabase(dbPath);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -29,7 +29,7 @@ export class EventStore {
|
|
|
29
29
|
if (this.initialized) return;
|
|
30
30
|
|
|
31
31
|
// L0 EventStore: Single Source of Truth (immutable, append-only)
|
|
32
|
-
await this.db
|
|
32
|
+
await dbRun(this.db, `
|
|
33
33
|
CREATE TABLE IF NOT EXISTS events (
|
|
34
34
|
id VARCHAR PRIMARY KEY,
|
|
35
35
|
event_type VARCHAR NOT NULL,
|
|
@@ -43,7 +43,7 @@ export class EventStore {
|
|
|
43
43
|
`);
|
|
44
44
|
|
|
45
45
|
// Dedup table for idempotency
|
|
46
|
-
await this.db
|
|
46
|
+
await dbRun(this.db, `
|
|
47
47
|
CREATE TABLE IF NOT EXISTS event_dedup (
|
|
48
48
|
dedupe_key VARCHAR PRIMARY KEY,
|
|
49
49
|
event_id VARCHAR NOT NULL,
|
|
@@ -52,7 +52,7 @@ export class EventStore {
|
|
|
52
52
|
`);
|
|
53
53
|
|
|
54
54
|
// Session metadata
|
|
55
|
-
await this.db
|
|
55
|
+
await dbRun(this.db, `
|
|
56
56
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
57
57
|
id VARCHAR PRIMARY KEY,
|
|
58
58
|
started_at TIMESTAMP NOT NULL,
|
|
@@ -64,7 +64,7 @@ export class EventStore {
|
|
|
64
64
|
`);
|
|
65
65
|
|
|
66
66
|
// Insights (derived data, rebuildable)
|
|
67
|
-
await this.db
|
|
67
|
+
await dbRun(this.db, `
|
|
68
68
|
CREATE TABLE IF NOT EXISTS insights (
|
|
69
69
|
id VARCHAR PRIMARY KEY,
|
|
70
70
|
insight_type VARCHAR NOT NULL,
|
|
@@ -78,7 +78,7 @@ export class EventStore {
|
|
|
78
78
|
`);
|
|
79
79
|
|
|
80
80
|
// Embedding Outbox (Single-Writer Pattern)
|
|
81
|
-
await this.db
|
|
81
|
+
await dbRun(this.db, `
|
|
82
82
|
CREATE TABLE IF NOT EXISTS embedding_outbox (
|
|
83
83
|
id VARCHAR PRIMARY KEY,
|
|
84
84
|
event_id VARCHAR NOT NULL,
|
|
@@ -92,7 +92,7 @@ export class EventStore {
|
|
|
92
92
|
`);
|
|
93
93
|
|
|
94
94
|
// Projection offset tracking
|
|
95
|
-
await this.db
|
|
95
|
+
await dbRun(this.db, `
|
|
96
96
|
CREATE TABLE IF NOT EXISTS projection_offsets (
|
|
97
97
|
projection_name VARCHAR PRIMARY KEY,
|
|
98
98
|
last_event_id VARCHAR,
|
|
@@ -102,7 +102,7 @@ export class EventStore {
|
|
|
102
102
|
`);
|
|
103
103
|
|
|
104
104
|
// Memory level tracking
|
|
105
|
-
await this.db
|
|
105
|
+
await dbRun(this.db, `
|
|
106
106
|
CREATE TABLE IF NOT EXISTS memory_levels (
|
|
107
107
|
event_id VARCHAR PRIMARY KEY,
|
|
108
108
|
level VARCHAR NOT NULL DEFAULT 'L0',
|
|
@@ -115,7 +115,7 @@ export class EventStore {
|
|
|
115
115
|
// ============================================================
|
|
116
116
|
|
|
117
117
|
// Entries (immutable memory units)
|
|
118
|
-
await this.db
|
|
118
|
+
await dbRun(this.db, `
|
|
119
119
|
CREATE TABLE IF NOT EXISTS entries (
|
|
120
120
|
entry_id VARCHAR PRIMARY KEY,
|
|
121
121
|
created_ts TIMESTAMP NOT NULL,
|
|
@@ -133,7 +133,7 @@ export class EventStore {
|
|
|
133
133
|
`);
|
|
134
134
|
|
|
135
135
|
// Entities (task/condition/artifact)
|
|
136
|
-
await this.db
|
|
136
|
+
await dbRun(this.db, `
|
|
137
137
|
CREATE TABLE IF NOT EXISTS entities (
|
|
138
138
|
entity_id VARCHAR PRIMARY KEY,
|
|
139
139
|
entity_type VARCHAR NOT NULL,
|
|
@@ -150,7 +150,7 @@ export class EventStore {
|
|
|
150
150
|
`);
|
|
151
151
|
|
|
152
152
|
// Entity aliases for canonical key lookup
|
|
153
|
-
await this.db
|
|
153
|
+
await dbRun(this.db, `
|
|
154
154
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
155
155
|
entity_type VARCHAR NOT NULL,
|
|
156
156
|
canonical_key VARCHAR NOT NULL,
|
|
@@ -162,7 +162,7 @@ export class EventStore {
|
|
|
162
162
|
`);
|
|
163
163
|
|
|
164
164
|
// Edges (relationships between entries/entities)
|
|
165
|
-
await this.db
|
|
165
|
+
await dbRun(this.db, `
|
|
166
166
|
CREATE TABLE IF NOT EXISTS edges (
|
|
167
167
|
edge_id VARCHAR PRIMARY KEY,
|
|
168
168
|
src_type VARCHAR NOT NULL,
|
|
@@ -179,7 +179,7 @@ export class EventStore {
|
|
|
179
179
|
// Vector Outbox V2 Table
|
|
180
180
|
// ============================================================
|
|
181
181
|
|
|
182
|
-
await this.db
|
|
182
|
+
await dbRun(this.db, `
|
|
183
183
|
CREATE TABLE IF NOT EXISTS vector_outbox (
|
|
184
184
|
job_id VARCHAR PRIMARY KEY,
|
|
185
185
|
item_kind VARCHAR NOT NULL,
|
|
@@ -198,7 +198,7 @@ export class EventStore {
|
|
|
198
198
|
// Build Runs & Metrics Tables
|
|
199
199
|
// ============================================================
|
|
200
200
|
|
|
201
|
-
await this.db
|
|
201
|
+
await dbRun(this.db, `
|
|
202
202
|
CREATE TABLE IF NOT EXISTS build_runs (
|
|
203
203
|
build_id VARCHAR PRIMARY KEY,
|
|
204
204
|
started_at TIMESTAMP NOT NULL,
|
|
@@ -214,7 +214,7 @@ export class EventStore {
|
|
|
214
214
|
)
|
|
215
215
|
`);
|
|
216
216
|
|
|
217
|
-
await this.db
|
|
217
|
+
await dbRun(this.db, `
|
|
218
218
|
CREATE TABLE IF NOT EXISTS pipeline_metrics (
|
|
219
219
|
id VARCHAR PRIMARY KEY,
|
|
220
220
|
ts TIMESTAMP NOT NULL,
|
|
@@ -231,7 +231,7 @@ export class EventStore {
|
|
|
231
231
|
// ============================================================
|
|
232
232
|
|
|
233
233
|
// Working Set table (active memory window)
|
|
234
|
-
await this.db
|
|
234
|
+
await dbRun(this.db, `
|
|
235
235
|
CREATE TABLE IF NOT EXISTS working_set (
|
|
236
236
|
id VARCHAR PRIMARY KEY,
|
|
237
237
|
event_id VARCHAR NOT NULL,
|
|
@@ -243,7 +243,7 @@ export class EventStore {
|
|
|
243
243
|
`);
|
|
244
244
|
|
|
245
245
|
// Consolidated Memories table (long-term integrated memories)
|
|
246
|
-
await this.db
|
|
246
|
+
await dbRun(this.db, `
|
|
247
247
|
CREATE TABLE IF NOT EXISTS consolidated_memories (
|
|
248
248
|
memory_id VARCHAR PRIMARY KEY,
|
|
249
249
|
summary TEXT NOT NULL,
|
|
@@ -257,7 +257,7 @@ export class EventStore {
|
|
|
257
257
|
`);
|
|
258
258
|
|
|
259
259
|
// Continuity Log table (tracks context transitions)
|
|
260
|
-
await this.db
|
|
260
|
+
await dbRun(this.db, `
|
|
261
261
|
CREATE TABLE IF NOT EXISTS continuity_log (
|
|
262
262
|
log_id VARCHAR PRIMARY KEY,
|
|
263
263
|
from_context_id VARCHAR,
|
|
@@ -269,7 +269,7 @@ export class EventStore {
|
|
|
269
269
|
`);
|
|
270
270
|
|
|
271
271
|
// Endless Mode Config table
|
|
272
|
-
await this.db
|
|
272
|
+
await dbRun(this.db, `
|
|
273
273
|
CREATE TABLE IF NOT EXISTS endless_config (
|
|
274
274
|
key VARCHAR PRIMARY KEY,
|
|
275
275
|
value JSON,
|
|
@@ -282,27 +282,27 @@ export class EventStore {
|
|
|
282
282
|
// ============================================================
|
|
283
283
|
|
|
284
284
|
// Entry indexes
|
|
285
|
-
await this.db
|
|
286
|
-
await this.db
|
|
287
|
-
await this.db
|
|
285
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_entries_type ON entries(entry_type)`);
|
|
286
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_entries_stage ON entries(stage)`);
|
|
287
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_entries_canonical ON entries(canonical_key)`);
|
|
288
288
|
|
|
289
289
|
// Entity indexes
|
|
290
|
-
await this.db
|
|
291
|
-
await this.db
|
|
290
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_entities_type_key ON entities(entity_type, canonical_key)`);
|
|
291
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_entities_status ON entities(status)`);
|
|
292
292
|
|
|
293
293
|
// Edge indexes
|
|
294
|
-
await this.db
|
|
295
|
-
await this.db
|
|
296
|
-
await this.db
|
|
294
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_edges_src ON edges(src_id, rel_type)`);
|
|
295
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_edges_dst ON edges(dst_id, rel_type)`);
|
|
296
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_edges_rel ON edges(rel_type)`);
|
|
297
297
|
|
|
298
298
|
// Outbox indexes
|
|
299
|
-
await this.db
|
|
299
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_outbox_status ON vector_outbox(status)`);
|
|
300
300
|
|
|
301
301
|
// Endless Mode indexes
|
|
302
|
-
await this.db
|
|
303
|
-
await this.db
|
|
304
|
-
await this.db
|
|
305
|
-
await this.db
|
|
302
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_working_set_expires ON working_set(expires_at)`);
|
|
303
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_working_set_relevance ON working_set(relevance_score DESC)`);
|
|
304
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_consolidated_confidence ON consolidated_memories(confidence DESC)`);
|
|
305
|
+
await dbRun(this.db, `CREATE INDEX IF NOT EXISTS idx_continuity_created ON continuity_log(created_at)`);
|
|
306
306
|
|
|
307
307
|
this.initialized = true;
|
|
308
308
|
}
|
|
@@ -318,7 +318,8 @@ export class EventStore {
|
|
|
318
318
|
const dedupeKey = makeDedupeKey(input.content, input.sessionId);
|
|
319
319
|
|
|
320
320
|
// Check for duplicate
|
|
321
|
-
const existing = await
|
|
321
|
+
const existing = await dbAll<{ event_id: string }>(
|
|
322
|
+
this.db,
|
|
322
323
|
`SELECT event_id FROM event_dedup WHERE dedupe_key = ?`,
|
|
323
324
|
[dedupeKey]
|
|
324
325
|
);
|
|
@@ -335,7 +336,8 @@ export class EventStore {
|
|
|
335
336
|
const timestamp = input.timestamp.toISOString();
|
|
336
337
|
|
|
337
338
|
try {
|
|
338
|
-
await
|
|
339
|
+
await dbRun(
|
|
340
|
+
this.db,
|
|
339
341
|
`INSERT INTO events (id, event_type, session_id, timestamp, content, canonical_key, dedupe_key, metadata)
|
|
340
342
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
341
343
|
[
|
|
@@ -350,13 +352,15 @@ export class EventStore {
|
|
|
350
352
|
]
|
|
351
353
|
);
|
|
352
354
|
|
|
353
|
-
await
|
|
355
|
+
await dbRun(
|
|
356
|
+
this.db,
|
|
354
357
|
`INSERT INTO event_dedup (dedupe_key, event_id) VALUES (?, ?)`,
|
|
355
358
|
[dedupeKey, id]
|
|
356
359
|
);
|
|
357
360
|
|
|
358
361
|
// Initialize at L0
|
|
359
|
-
await
|
|
362
|
+
await dbRun(
|
|
363
|
+
this.db,
|
|
360
364
|
`INSERT INTO memory_levels (event_id, level) VALUES (?, 'L0')`,
|
|
361
365
|
[id]
|
|
362
366
|
);
|
|
@@ -376,7 +380,8 @@ export class EventStore {
|
|
|
376
380
|
async getSessionEvents(sessionId: string): Promise<MemoryEvent[]> {
|
|
377
381
|
await this.initialize();
|
|
378
382
|
|
|
379
|
-
const rows = await
|
|
383
|
+
const rows = await dbAll<Record<string, unknown>>(
|
|
384
|
+
this.db,
|
|
380
385
|
`SELECT * FROM events WHERE session_id = ? ORDER BY timestamp ASC`,
|
|
381
386
|
[sessionId]
|
|
382
387
|
);
|
|
@@ -390,7 +395,8 @@ export class EventStore {
|
|
|
390
395
|
async getRecentEvents(limit: number = 100): Promise<MemoryEvent[]> {
|
|
391
396
|
await this.initialize();
|
|
392
397
|
|
|
393
|
-
const rows = await
|
|
398
|
+
const rows = await dbAll<Record<string, unknown>>(
|
|
399
|
+
this.db,
|
|
394
400
|
`SELECT * FROM events ORDER BY timestamp DESC LIMIT ?`,
|
|
395
401
|
[limit]
|
|
396
402
|
);
|
|
@@ -404,7 +410,8 @@ export class EventStore {
|
|
|
404
410
|
async getEvent(id: string): Promise<MemoryEvent | null> {
|
|
405
411
|
await this.initialize();
|
|
406
412
|
|
|
407
|
-
const rows = await
|
|
413
|
+
const rows = await dbAll<Record<string, unknown>>(
|
|
414
|
+
this.db,
|
|
408
415
|
`SELECT * FROM events WHERE id = ?`,
|
|
409
416
|
[id]
|
|
410
417
|
);
|
|
@@ -419,13 +426,15 @@ export class EventStore {
|
|
|
419
426
|
async upsertSession(session: Partial<Session> & { id: string }): Promise<void> {
|
|
420
427
|
await this.initialize();
|
|
421
428
|
|
|
422
|
-
const existing = await
|
|
429
|
+
const existing = await dbAll<{ id: string }>(
|
|
430
|
+
this.db,
|
|
423
431
|
`SELECT id FROM sessions WHERE id = ?`,
|
|
424
432
|
[session.id]
|
|
425
433
|
);
|
|
426
434
|
|
|
427
435
|
if (existing.length === 0) {
|
|
428
|
-
await
|
|
436
|
+
await dbRun(
|
|
437
|
+
this.db,
|
|
429
438
|
`INSERT INTO sessions (id, started_at, project_path, tags)
|
|
430
439
|
VALUES (?, ?, ?, ?)`,
|
|
431
440
|
[
|
|
@@ -454,7 +463,8 @@ export class EventStore {
|
|
|
454
463
|
|
|
455
464
|
if (updates.length > 0) {
|
|
456
465
|
values.push(session.id);
|
|
457
|
-
await
|
|
466
|
+
await dbRun(
|
|
467
|
+
this.db,
|
|
458
468
|
`UPDATE sessions SET ${updates.join(', ')} WHERE id = ?`,
|
|
459
469
|
values
|
|
460
470
|
);
|
|
@@ -468,7 +478,8 @@ export class EventStore {
|
|
|
468
478
|
async getSession(id: string): Promise<Session | null> {
|
|
469
479
|
await this.initialize();
|
|
470
480
|
|
|
471
|
-
const rows = await
|
|
481
|
+
const rows = await dbAll<Record<string, unknown>>(
|
|
482
|
+
this.db,
|
|
472
483
|
`SELECT * FROM sessions WHERE id = ?`,
|
|
473
484
|
[id]
|
|
474
485
|
);
|
|
@@ -478,8 +489,8 @@ export class EventStore {
|
|
|
478
489
|
const row = rows[0];
|
|
479
490
|
return {
|
|
480
491
|
id: row.id as string,
|
|
481
|
-
startedAt:
|
|
482
|
-
endedAt: row.ended_at ?
|
|
492
|
+
startedAt: toDate(row.started_at),
|
|
493
|
+
endedAt: row.ended_at ? toDate(row.ended_at) : undefined,
|
|
483
494
|
projectPath: row.project_path as string | undefined,
|
|
484
495
|
summary: row.summary as string | undefined,
|
|
485
496
|
tags: row.tags ? JSON.parse(row.tags as string) : undefined
|
|
@@ -493,7 +504,8 @@ export class EventStore {
|
|
|
493
504
|
await this.initialize();
|
|
494
505
|
|
|
495
506
|
const id = randomUUID();
|
|
496
|
-
await
|
|
507
|
+
await dbRun(
|
|
508
|
+
this.db,
|
|
497
509
|
`INSERT INTO embedding_outbox (id, event_id, content, status, retry_count)
|
|
498
510
|
VALUES (?, ?, ?, 'pending', 0)`,
|
|
499
511
|
[id, eventId, content]
|
|
@@ -508,27 +520,34 @@ export class EventStore {
|
|
|
508
520
|
async getPendingOutboxItems(limit: number = 32): Promise<OutboxItem[]> {
|
|
509
521
|
await this.initialize();
|
|
510
522
|
|
|
511
|
-
//
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
WHERE
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
ORDER BY created_at
|
|
519
|
-
LIMIT ?
|
|
520
|
-
)
|
|
521
|
-
RETURNING *`,
|
|
523
|
+
// First, get pending items
|
|
524
|
+
const pending = await dbAll<Record<string, unknown>>(
|
|
525
|
+
this.db,
|
|
526
|
+
`SELECT * FROM embedding_outbox
|
|
527
|
+
WHERE status = 'pending'
|
|
528
|
+
ORDER BY created_at
|
|
529
|
+
LIMIT ?`,
|
|
522
530
|
[limit]
|
|
523
531
|
);
|
|
524
532
|
|
|
525
|
-
|
|
533
|
+
if (pending.length === 0) return [];
|
|
534
|
+
|
|
535
|
+
// Update status to processing
|
|
536
|
+
const ids = pending.map(r => r.id as string);
|
|
537
|
+
const placeholders = ids.map(() => '?').join(',');
|
|
538
|
+
await dbRun(
|
|
539
|
+
this.db,
|
|
540
|
+
`UPDATE embedding_outbox SET status = 'processing' WHERE id IN (${placeholders})`,
|
|
541
|
+
ids
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
return pending.map(row => ({
|
|
526
545
|
id: row.id as string,
|
|
527
546
|
eventId: row.event_id as string,
|
|
528
547
|
content: row.content as string,
|
|
529
|
-
status:
|
|
548
|
+
status: 'processing' as const,
|
|
530
549
|
retryCount: row.retry_count as number,
|
|
531
|
-
createdAt:
|
|
550
|
+
createdAt: toDate(row.created_at),
|
|
532
551
|
errorMessage: row.error_message as string | undefined
|
|
533
552
|
}));
|
|
534
553
|
}
|
|
@@ -540,7 +559,8 @@ export class EventStore {
|
|
|
540
559
|
if (ids.length === 0) return;
|
|
541
560
|
|
|
542
561
|
const placeholders = ids.map(() => '?').join(',');
|
|
543
|
-
await
|
|
562
|
+
await dbRun(
|
|
563
|
+
this.db,
|
|
544
564
|
`DELETE FROM embedding_outbox WHERE id IN (${placeholders})`,
|
|
545
565
|
ids
|
|
546
566
|
);
|
|
@@ -553,7 +573,8 @@ export class EventStore {
|
|
|
553
573
|
if (ids.length === 0) return;
|
|
554
574
|
|
|
555
575
|
const placeholders = ids.map(() => '?').join(',');
|
|
556
|
-
await
|
|
576
|
+
await dbRun(
|
|
577
|
+
this.db,
|
|
557
578
|
`UPDATE embedding_outbox
|
|
558
579
|
SET status = CASE WHEN retry_count >= 3 THEN 'failed' ELSE 'pending' END,
|
|
559
580
|
retry_count = retry_count + 1,
|
|
@@ -569,7 +590,8 @@ export class EventStore {
|
|
|
569
590
|
async updateMemoryLevel(eventId: string, level: string): Promise<void> {
|
|
570
591
|
await this.initialize();
|
|
571
592
|
|
|
572
|
-
await
|
|
593
|
+
await dbRun(
|
|
594
|
+
this.db,
|
|
573
595
|
`UPDATE memory_levels SET level = ?, promoted_at = CURRENT_TIMESTAMP WHERE event_id = ?`,
|
|
574
596
|
[level, eventId]
|
|
575
597
|
);
|
|
@@ -581,7 +603,8 @@ export class EventStore {
|
|
|
581
603
|
async getLevelStats(): Promise<Array<{ level: string; count: number }>> {
|
|
582
604
|
await this.initialize();
|
|
583
605
|
|
|
584
|
-
const rows = await
|
|
606
|
+
const rows = await dbAll<{ level: string; count: number }>(
|
|
607
|
+
this.db,
|
|
585
608
|
`SELECT level, COUNT(*) as count FROM memory_levels GROUP BY level`
|
|
586
609
|
);
|
|
587
610
|
|
|
@@ -605,7 +628,8 @@ export class EventStore {
|
|
|
605
628
|
async getEndlessConfig(key: string): Promise<unknown | null> {
|
|
606
629
|
await this.initialize();
|
|
607
630
|
|
|
608
|
-
const rows = await
|
|
631
|
+
const rows = await dbAll<{ value: string }>(
|
|
632
|
+
this.db,
|
|
609
633
|
`SELECT value FROM endless_config WHERE key = ?`,
|
|
610
634
|
[key]
|
|
611
635
|
);
|
|
@@ -620,7 +644,8 @@ export class EventStore {
|
|
|
620
644
|
async setEndlessConfig(key: string, value: unknown): Promise<void> {
|
|
621
645
|
await this.initialize();
|
|
622
646
|
|
|
623
|
-
await
|
|
647
|
+
await dbRun(
|
|
648
|
+
this.db,
|
|
624
649
|
`INSERT OR REPLACE INTO endless_config (key, value, updated_at)
|
|
625
650
|
VALUES (?, ?, CURRENT_TIMESTAMP)`,
|
|
626
651
|
[key, JSON.stringify(value)]
|
|
@@ -633,14 +658,15 @@ export class EventStore {
|
|
|
633
658
|
async getAllSessions(): Promise<Session[]> {
|
|
634
659
|
await this.initialize();
|
|
635
660
|
|
|
636
|
-
const rows = await
|
|
661
|
+
const rows = await dbAll<Record<string, unknown>>(
|
|
662
|
+
this.db,
|
|
637
663
|
`SELECT * FROM sessions ORDER BY started_at DESC`
|
|
638
664
|
);
|
|
639
665
|
|
|
640
666
|
return rows.map(row => ({
|
|
641
667
|
id: row.id as string,
|
|
642
|
-
startedAt:
|
|
643
|
-
endedAt: row.ended_at ?
|
|
668
|
+
startedAt: toDate(row.started_at),
|
|
669
|
+
endedAt: row.ended_at ? toDate(row.ended_at) : undefined,
|
|
644
670
|
projectPath: row.project_path as string | undefined,
|
|
645
671
|
summary: row.summary as string | undefined,
|
|
646
672
|
tags: row.tags ? JSON.parse(row.tags as string) : undefined
|
|
@@ -651,7 +677,7 @@ export class EventStore {
|
|
|
651
677
|
* Close database connection
|
|
652
678
|
*/
|
|
653
679
|
async close(): Promise<void> {
|
|
654
|
-
await this.db
|
|
680
|
+
await dbClose(this.db);
|
|
655
681
|
}
|
|
656
682
|
|
|
657
683
|
/**
|
|
@@ -662,7 +688,7 @@ export class EventStore {
|
|
|
662
688
|
id: row.id as string,
|
|
663
689
|
eventType: row.event_type as 'user_prompt' | 'agent_response' | 'session_summary',
|
|
664
690
|
sessionId: row.session_id as string,
|
|
665
|
-
timestamp:
|
|
691
|
+
timestamp: toDate(row.timestamp),
|
|
666
692
|
content: row.content as string,
|
|
667
693
|
canonicalKey: row.canonical_key as string,
|
|
668
694
|
dedupeKey: row.dedupe_key as string,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* AXIOMMIND: No stub task creation, fallback to condition
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Database } from '
|
|
6
|
+
import { dbRun, dbAll, type Database } from '../db-wrapper.js';
|
|
7
7
|
import { randomUUID } from 'crypto';
|
|
8
8
|
import type { BlockerRef, BlockerKind, Entity } from '../types.js';
|
|
9
9
|
import { makeEntityCanonicalKey, makeArtifactKey } from '../canonical-key.js';
|
|
@@ -110,7 +110,8 @@ export class BlockerResolver {
|
|
|
110
110
|
const canonicalKey = makeArtifactKey(text);
|
|
111
111
|
|
|
112
112
|
// Find or create artifact
|
|
113
|
-
const existing = await
|
|
113
|
+
const existing = await dbAll<Record<string, unknown>>(
|
|
114
|
+
this.db,
|
|
114
115
|
`SELECT entity_id FROM entities
|
|
115
116
|
WHERE entity_type = 'artifact' AND canonical_key = ?`,
|
|
116
117
|
[canonicalKey]
|
|
@@ -138,7 +139,8 @@ export class BlockerResolver {
|
|
|
138
139
|
*/
|
|
139
140
|
private async tryResolveAsTaskId(taskId: string): Promise<BlockerRef | null> {
|
|
140
141
|
// taskId format: task:project:identifier
|
|
141
|
-
const rows = await
|
|
142
|
+
const rows = await dbAll<Record<string, unknown>>(
|
|
143
|
+
this.db,
|
|
142
144
|
`SELECT entity_id FROM entities
|
|
143
145
|
WHERE entity_type = 'task' AND canonical_key = ?
|
|
144
146
|
AND status = 'active'`,
|
|
@@ -169,7 +171,8 @@ export class BlockerResolver {
|
|
|
169
171
|
});
|
|
170
172
|
|
|
171
173
|
// Find existing condition
|
|
172
|
-
const existing = await
|
|
174
|
+
const existing = await dbAll<Record<string, unknown>>(
|
|
175
|
+
this.db,
|
|
173
176
|
`SELECT entity_id FROM entities
|
|
174
177
|
WHERE entity_type = 'condition' AND canonical_key = ?`,
|
|
175
178
|
[canonicalKey]
|
|
@@ -213,7 +216,8 @@ export class BlockerResolver {
|
|
|
213
216
|
}))
|
|
214
217
|
};
|
|
215
218
|
|
|
216
|
-
await
|
|
219
|
+
await dbRun(
|
|
220
|
+
this.db,
|
|
217
221
|
`INSERT INTO entities (
|
|
218
222
|
entity_id, entity_type, canonical_key, title, stage, status,
|
|
219
223
|
current_json, title_norm, search_text, created_at, updated_at
|
|
@@ -234,7 +238,8 @@ export class BlockerResolver {
|
|
|
234
238
|
);
|
|
235
239
|
|
|
236
240
|
// Create alias
|
|
237
|
-
await
|
|
241
|
+
await dbRun(
|
|
242
|
+
this.db,
|
|
238
243
|
`INSERT INTO entity_aliases (entity_type, canonical_key, entity_id, is_primary)
|
|
239
244
|
VALUES (?, ?, ?, TRUE)
|
|
240
245
|
ON CONFLICT (entity_type, canonical_key) DO NOTHING`,
|
|
@@ -269,7 +274,8 @@ export class BlockerResolver {
|
|
|
269
274
|
artifactType
|
|
270
275
|
};
|
|
271
276
|
|
|
272
|
-
await
|
|
277
|
+
await dbRun(
|
|
278
|
+
this.db,
|
|
273
279
|
`INSERT INTO entities (
|
|
274
280
|
entity_id, entity_type, canonical_key, title, stage, status,
|
|
275
281
|
current_json, title_norm, search_text, created_at, updated_at
|
|
@@ -290,7 +296,8 @@ export class BlockerResolver {
|
|
|
290
296
|
);
|
|
291
297
|
|
|
292
298
|
// Create alias
|
|
293
|
-
await
|
|
299
|
+
await dbRun(
|
|
300
|
+
this.db,
|
|
294
301
|
`INSERT INTO entity_aliases (entity_type, canonical_key, entity_id, is_primary)
|
|
295
302
|
VALUES (?, ?, ?, TRUE)
|
|
296
303
|
ON CONFLICT (entity_type, canonical_key) DO NOTHING`,
|
|
@@ -310,7 +317,8 @@ export class BlockerResolver {
|
|
|
310
317
|
const ref = await this.createConditionBlocker(text);
|
|
311
318
|
|
|
312
319
|
// Mark as auto placeholder
|
|
313
|
-
await
|
|
320
|
+
await dbRun(
|
|
321
|
+
this.db,
|
|
314
322
|
`UPDATE entities
|
|
315
323
|
SET current_json = json_set(current_json, '$.auto_placeholder', true)
|
|
316
324
|
WHERE entity_id = ?`,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* AXIOMMIND: strict matching (≥0.92, gap≥0.03)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Database } from '
|
|
6
|
+
import { dbAll, toDate, type Database } from '../db-wrapper.js';
|
|
7
7
|
import type { Entity, MatchConfidence } from '../types.js';
|
|
8
8
|
import { makeEntityCanonicalKey } from '../canonical-key.js';
|
|
9
9
|
import { MATCH_THRESHOLDS } from '../types.js';
|
|
@@ -46,7 +46,8 @@ export class TaskMatcher {
|
|
|
46
46
|
async findExact(title: string, project?: string): Promise<Entity | null> {
|
|
47
47
|
const canonicalKey = makeEntityCanonicalKey('task', title, { project });
|
|
48
48
|
|
|
49
|
-
const rows = await
|
|
49
|
+
const rows = await dbAll<Record<string, unknown>>(
|
|
50
|
+
this.db,
|
|
50
51
|
`SELECT * FROM entities
|
|
51
52
|
WHERE entity_type = 'task' AND canonical_key = ?
|
|
52
53
|
AND status = 'active'`,
|
|
@@ -63,7 +64,8 @@ export class TaskMatcher {
|
|
|
63
64
|
async findByAlias(title: string, project?: string): Promise<Entity | null> {
|
|
64
65
|
const canonicalKey = makeEntityCanonicalKey('task', title, { project });
|
|
65
66
|
|
|
66
|
-
const rows = await
|
|
67
|
+
const rows = await dbAll<Record<string, unknown>>(
|
|
68
|
+
this.db,
|
|
67
69
|
`SELECT e.* FROM entities e
|
|
68
70
|
JOIN entity_aliases a ON e.entity_id = a.entity_id
|
|
69
71
|
WHERE a.entity_type = 'task' AND a.canonical_key = ?
|
|
@@ -105,7 +107,7 @@ export class TaskMatcher {
|
|
|
105
107
|
sql += ` ORDER BY match_score DESC, updated_at DESC LIMIT ?`;
|
|
106
108
|
params.push(this.config.maxCandidates);
|
|
107
109
|
|
|
108
|
-
const rows = await
|
|
110
|
+
const rows = await dbAll<Record<string, unknown>>(this.db, sql, params);
|
|
109
111
|
|
|
110
112
|
return rows.map(row => ({
|
|
111
113
|
entity: this.rowToEntity(row),
|
|
@@ -231,8 +233,8 @@ export class TaskMatcher {
|
|
|
231
233
|
: row.current_json as Record<string, unknown>,
|
|
232
234
|
titleNorm: row.title_norm as string | undefined,
|
|
233
235
|
searchText: row.search_text as string | undefined,
|
|
234
|
-
createdAt:
|
|
235
|
-
updatedAt:
|
|
236
|
+
createdAt: toDate(row.created_at),
|
|
237
|
+
updatedAt: toDate(row.updated_at)
|
|
236
238
|
};
|
|
237
239
|
}
|
|
238
240
|
}
|