agent-working-memory 0.11.0 → 0.12.3
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/README.md +71 -297
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +63 -3
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +358 -306
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +29 -7
- package/dist/api/routes.js.map +1 -1
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +174 -170
- package/dist/coordination/routes.js.map +1 -1
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +4 -1
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/entity-extract.d.ts +3 -0
- package/dist/core/entity-extract.d.ts.map +1 -0
- package/dist/core/entity-extract.js +47 -0
- package/dist/core/entity-extract.js.map +1 -0
- package/dist/core/format-recall.d.ts +16 -0
- package/dist/core/format-recall.d.ts.map +1 -0
- package/dist/core/format-recall.js +24 -0
- package/dist/core/format-recall.js.map +1 -0
- package/dist/core/query-expander.js +1 -1
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.js +1 -1
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +14 -2
- package/dist/core/salience.js.map +1 -1
- package/dist/core/whoami.d.ts +24 -0
- package/dist/core/whoami.d.ts.map +1 -0
- package/dist/core/whoami.js +66 -0
- package/dist/core/whoami.js.map +1 -0
- package/dist/core/write-pipeline.d.ts +9 -0
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +109 -68
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/core/write-telemetry.d.ts +33 -0
- package/dist/core/write-telemetry.d.ts.map +1 -0
- package/dist/core/write-telemetry.js +110 -0
- package/dist/core/write-telemetry.js.map +1 -0
- package/dist/engine/activation.d.ts +22 -12
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +133 -17
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts +1 -1
- package/dist/engine/consolidation-scheduler.js +1 -1
- package/dist/engine/consolidation.d.ts +1 -0
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +18 -0
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +5 -1
- package/dist/engine/eval.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +26 -0
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +30 -0
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +222 -108
- package/dist/mcp.js.map +1 -1
- package/dist/recipes/index.d.ts +57 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +81 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/storage/pglite-schema.d.ts.map +1 -1
- package/dist/storage/pglite-schema.js +27 -0
- package/dist/storage/pglite-schema.js.map +1 -1
- package/dist/storage/pglite.d.ts +5 -0
- package/dist/storage/pglite.d.ts.map +1 -1
- package/dist/storage/pglite.js +180 -138
- package/dist/storage/pglite.js.map +1 -1
- package/dist/storage/postgres.d.ts +5 -0
- package/dist/storage/postgres.d.ts.map +1 -1
- package/dist/storage/postgres.js +180 -138
- package/dist/storage/postgres.js.map +1 -1
- package/dist/storage/sqlite.d.ts +9 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +394 -326
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +14 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/claude-code.ts +66 -3
- package/src/adapters/common.ts +567 -515
- package/src/api/routes.ts +999 -971
- package/src/coordination/routes.ts +2155 -2150
- package/src/core/embeddings.ts +4 -1
- package/src/core/entity-extract.ts +47 -0
- package/src/core/format-recall.ts +25 -0
- package/src/core/query-expander.ts +1 -1
- package/src/core/reranker.ts +1 -1
- package/src/core/salience.ts +529 -514
- package/src/core/whoami.ts +92 -0
- package/src/core/write-pipeline.ts +60 -8
- package/src/core/write-telemetry.ts +131 -0
- package/src/engine/activation.ts +1468 -1369
- package/src/engine/consolidation-scheduler.ts +1 -1
- package/src/engine/consolidation.ts +887 -869
- package/src/engine/eval.ts +6 -1
- package/src/hooks/sidecar.ts +55 -0
- package/src/index.ts +248 -227
- package/src/mcp.ts +1387 -1270
- package/src/recipes/index.ts +125 -0
- package/src/storage/pglite-schema.ts +27 -0
- package/src/storage/pglite.ts +1420 -1372
- package/src/storage/postgres.ts +1523 -1475
- package/src/storage/sqlite.ts +1936 -1861
- package/src/types/engram.ts +22 -0
package/dist/storage/sqlite.js
CHANGED
|
@@ -154,119 +154,119 @@ export class EngramStore {
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
init() {
|
|
157
|
-
this.db.exec(`
|
|
158
|
-
CREATE TABLE IF NOT EXISTS engrams (
|
|
159
|
-
id TEXT PRIMARY KEY,
|
|
160
|
-
agent_id TEXT NOT NULL,
|
|
161
|
-
concept TEXT NOT NULL,
|
|
162
|
-
content TEXT NOT NULL,
|
|
163
|
-
embedding BLOB,
|
|
164
|
-
confidence REAL NOT NULL DEFAULT 0.5,
|
|
165
|
-
salience REAL NOT NULL DEFAULT 0.5,
|
|
166
|
-
access_count INTEGER NOT NULL DEFAULT 0,
|
|
167
|
-
last_accessed TEXT NOT NULL,
|
|
168
|
-
created_at TEXT NOT NULL,
|
|
169
|
-
salience_features TEXT NOT NULL DEFAULT '{}',
|
|
170
|
-
reason_codes TEXT NOT NULL DEFAULT '[]',
|
|
171
|
-
stage TEXT NOT NULL DEFAULT 'active',
|
|
172
|
-
ttl INTEGER,
|
|
173
|
-
retracted INTEGER NOT NULL DEFAULT 0,
|
|
174
|
-
retracted_by TEXT,
|
|
175
|
-
retracted_at TEXT,
|
|
176
|
-
tags TEXT NOT NULL DEFAULT '[]',
|
|
177
|
-
memory_type TEXT NOT NULL DEFAULT 'unclassified'
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
CREATE INDEX IF NOT EXISTS idx_engrams_agent ON engrams(agent_id);
|
|
181
|
-
CREATE INDEX IF NOT EXISTS idx_engrams_stage ON engrams(agent_id, stage);
|
|
182
|
-
CREATE INDEX IF NOT EXISTS idx_engrams_concept ON engrams(concept);
|
|
183
|
-
CREATE INDEX IF NOT EXISTS idx_engrams_retracted ON engrams(agent_id, retracted);
|
|
184
|
-
|
|
185
|
-
CREATE TABLE IF NOT EXISTS associations (
|
|
186
|
-
id TEXT PRIMARY KEY,
|
|
187
|
-
from_engram_id TEXT NOT NULL REFERENCES engrams(id) ON DELETE CASCADE,
|
|
188
|
-
to_engram_id TEXT NOT NULL REFERENCES engrams(id) ON DELETE CASCADE,
|
|
189
|
-
weight REAL NOT NULL DEFAULT 0.1,
|
|
190
|
-
confidence REAL NOT NULL DEFAULT 0.5,
|
|
191
|
-
type TEXT NOT NULL DEFAULT 'hebbian',
|
|
192
|
-
activation_count INTEGER NOT NULL DEFAULT 0,
|
|
193
|
-
created_at TEXT NOT NULL,
|
|
194
|
-
last_activated TEXT NOT NULL
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
CREATE INDEX IF NOT EXISTS idx_assoc_from ON associations(from_engram_id);
|
|
198
|
-
CREATE INDEX IF NOT EXISTS idx_assoc_to ON associations(to_engram_id);
|
|
199
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_assoc_pair ON associations(from_engram_id, to_engram_id);
|
|
200
|
-
|
|
201
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
202
|
-
id TEXT PRIMARY KEY,
|
|
203
|
-
name TEXT NOT NULL,
|
|
204
|
-
created_at TEXT NOT NULL,
|
|
205
|
-
config TEXT NOT NULL DEFAULT '{}'
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
-- FTS5 for full-text search (BM25 ranking built in)
|
|
209
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS engrams_fts USING fts5(
|
|
210
|
-
concept, content, tags,
|
|
211
|
-
content=engrams,
|
|
212
|
-
content_rowid=rowid
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
-- Triggers to keep FTS in sync
|
|
216
|
-
CREATE TRIGGER IF NOT EXISTS engrams_ai AFTER INSERT ON engrams BEGIN
|
|
217
|
-
INSERT INTO engrams_fts(rowid, concept, content, tags) VALUES (new.rowid, new.concept, new.content, new.tags);
|
|
218
|
-
END;
|
|
219
|
-
CREATE TRIGGER IF NOT EXISTS engrams_ad AFTER DELETE ON engrams BEGIN
|
|
220
|
-
INSERT INTO engrams_fts(engrams_fts, rowid, concept, content, tags) VALUES('delete', old.rowid, old.concept, old.content, old.tags);
|
|
221
|
-
END;
|
|
222
|
-
CREATE TRIGGER IF NOT EXISTS engrams_au AFTER UPDATE ON engrams BEGIN
|
|
223
|
-
INSERT INTO engrams_fts(engrams_fts, rowid, concept, content, tags) VALUES('delete', old.rowid, old.concept, old.content, old.tags);
|
|
224
|
-
INSERT INTO engrams_fts(rowid, concept, content, tags) VALUES (new.rowid, new.concept, new.content, new.tags);
|
|
225
|
-
END;
|
|
226
|
-
|
|
227
|
-
-- Eval event logs
|
|
228
|
-
CREATE TABLE IF NOT EXISTS activation_events (
|
|
229
|
-
id TEXT PRIMARY KEY,
|
|
230
|
-
agent_id TEXT NOT NULL,
|
|
231
|
-
timestamp TEXT NOT NULL,
|
|
232
|
-
context TEXT NOT NULL,
|
|
233
|
-
results_returned INTEGER NOT NULL,
|
|
234
|
-
top_score REAL,
|
|
235
|
-
latency_ms REAL NOT NULL,
|
|
236
|
-
engram_ids TEXT NOT NULL DEFAULT '[]'
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
CREATE TABLE IF NOT EXISTS staging_events (
|
|
240
|
-
engram_id TEXT NOT NULL,
|
|
241
|
-
agent_id TEXT NOT NULL,
|
|
242
|
-
action TEXT NOT NULL,
|
|
243
|
-
resonance_score REAL,
|
|
244
|
-
timestamp TEXT NOT NULL,
|
|
245
|
-
age_ms INTEGER NOT NULL
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
CREATE TABLE IF NOT EXISTS retrieval_feedback (
|
|
249
|
-
id TEXT PRIMARY KEY,
|
|
250
|
-
activation_event_id TEXT,
|
|
251
|
-
engram_id TEXT NOT NULL,
|
|
252
|
-
useful INTEGER NOT NULL,
|
|
253
|
-
context TEXT,
|
|
254
|
-
timestamp TEXT NOT NULL
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
CREATE TABLE IF NOT EXISTS episodes (
|
|
258
|
-
id TEXT PRIMARY KEY,
|
|
259
|
-
agent_id TEXT NOT NULL,
|
|
260
|
-
label TEXT NOT NULL,
|
|
261
|
-
embedding BLOB,
|
|
262
|
-
engram_count INTEGER NOT NULL DEFAULT 0,
|
|
263
|
-
start_time TEXT NOT NULL,
|
|
264
|
-
end_time TEXT NOT NULL,
|
|
265
|
-
created_at TEXT NOT NULL
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
CREATE INDEX IF NOT EXISTS idx_episodes_agent ON episodes(agent_id);
|
|
269
|
-
CREATE INDEX IF NOT EXISTS idx_episodes_time ON episodes(agent_id, end_time);
|
|
157
|
+
this.db.exec(`
|
|
158
|
+
CREATE TABLE IF NOT EXISTS engrams (
|
|
159
|
+
id TEXT PRIMARY KEY,
|
|
160
|
+
agent_id TEXT NOT NULL,
|
|
161
|
+
concept TEXT NOT NULL,
|
|
162
|
+
content TEXT NOT NULL,
|
|
163
|
+
embedding BLOB,
|
|
164
|
+
confidence REAL NOT NULL DEFAULT 0.5,
|
|
165
|
+
salience REAL NOT NULL DEFAULT 0.5,
|
|
166
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
167
|
+
last_accessed TEXT NOT NULL,
|
|
168
|
+
created_at TEXT NOT NULL,
|
|
169
|
+
salience_features TEXT NOT NULL DEFAULT '{}',
|
|
170
|
+
reason_codes TEXT NOT NULL DEFAULT '[]',
|
|
171
|
+
stage TEXT NOT NULL DEFAULT 'active',
|
|
172
|
+
ttl INTEGER,
|
|
173
|
+
retracted INTEGER NOT NULL DEFAULT 0,
|
|
174
|
+
retracted_by TEXT,
|
|
175
|
+
retracted_at TEXT,
|
|
176
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
177
|
+
memory_type TEXT NOT NULL DEFAULT 'unclassified'
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
CREATE INDEX IF NOT EXISTS idx_engrams_agent ON engrams(agent_id);
|
|
181
|
+
CREATE INDEX IF NOT EXISTS idx_engrams_stage ON engrams(agent_id, stage);
|
|
182
|
+
CREATE INDEX IF NOT EXISTS idx_engrams_concept ON engrams(concept);
|
|
183
|
+
CREATE INDEX IF NOT EXISTS idx_engrams_retracted ON engrams(agent_id, retracted);
|
|
184
|
+
|
|
185
|
+
CREATE TABLE IF NOT EXISTS associations (
|
|
186
|
+
id TEXT PRIMARY KEY,
|
|
187
|
+
from_engram_id TEXT NOT NULL REFERENCES engrams(id) ON DELETE CASCADE,
|
|
188
|
+
to_engram_id TEXT NOT NULL REFERENCES engrams(id) ON DELETE CASCADE,
|
|
189
|
+
weight REAL NOT NULL DEFAULT 0.1,
|
|
190
|
+
confidence REAL NOT NULL DEFAULT 0.5,
|
|
191
|
+
type TEXT NOT NULL DEFAULT 'hebbian',
|
|
192
|
+
activation_count INTEGER NOT NULL DEFAULT 0,
|
|
193
|
+
created_at TEXT NOT NULL,
|
|
194
|
+
last_activated TEXT NOT NULL
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
CREATE INDEX IF NOT EXISTS idx_assoc_from ON associations(from_engram_id);
|
|
198
|
+
CREATE INDEX IF NOT EXISTS idx_assoc_to ON associations(to_engram_id);
|
|
199
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_assoc_pair ON associations(from_engram_id, to_engram_id);
|
|
200
|
+
|
|
201
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
202
|
+
id TEXT PRIMARY KEY,
|
|
203
|
+
name TEXT NOT NULL,
|
|
204
|
+
created_at TEXT NOT NULL,
|
|
205
|
+
config TEXT NOT NULL DEFAULT '{}'
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
-- FTS5 for full-text search (BM25 ranking built in)
|
|
209
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS engrams_fts USING fts5(
|
|
210
|
+
concept, content, tags,
|
|
211
|
+
content=engrams,
|
|
212
|
+
content_rowid=rowid
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
-- Triggers to keep FTS in sync
|
|
216
|
+
CREATE TRIGGER IF NOT EXISTS engrams_ai AFTER INSERT ON engrams BEGIN
|
|
217
|
+
INSERT INTO engrams_fts(rowid, concept, content, tags) VALUES (new.rowid, new.concept, new.content, new.tags);
|
|
218
|
+
END;
|
|
219
|
+
CREATE TRIGGER IF NOT EXISTS engrams_ad AFTER DELETE ON engrams BEGIN
|
|
220
|
+
INSERT INTO engrams_fts(engrams_fts, rowid, concept, content, tags) VALUES('delete', old.rowid, old.concept, old.content, old.tags);
|
|
221
|
+
END;
|
|
222
|
+
CREATE TRIGGER IF NOT EXISTS engrams_au AFTER UPDATE ON engrams BEGIN
|
|
223
|
+
INSERT INTO engrams_fts(engrams_fts, rowid, concept, content, tags) VALUES('delete', old.rowid, old.concept, old.content, old.tags);
|
|
224
|
+
INSERT INTO engrams_fts(rowid, concept, content, tags) VALUES (new.rowid, new.concept, new.content, new.tags);
|
|
225
|
+
END;
|
|
226
|
+
|
|
227
|
+
-- Eval event logs
|
|
228
|
+
CREATE TABLE IF NOT EXISTS activation_events (
|
|
229
|
+
id TEXT PRIMARY KEY,
|
|
230
|
+
agent_id TEXT NOT NULL,
|
|
231
|
+
timestamp TEXT NOT NULL,
|
|
232
|
+
context TEXT NOT NULL,
|
|
233
|
+
results_returned INTEGER NOT NULL,
|
|
234
|
+
top_score REAL,
|
|
235
|
+
latency_ms REAL NOT NULL,
|
|
236
|
+
engram_ids TEXT NOT NULL DEFAULT '[]'
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
CREATE TABLE IF NOT EXISTS staging_events (
|
|
240
|
+
engram_id TEXT NOT NULL,
|
|
241
|
+
agent_id TEXT NOT NULL,
|
|
242
|
+
action TEXT NOT NULL,
|
|
243
|
+
resonance_score REAL,
|
|
244
|
+
timestamp TEXT NOT NULL,
|
|
245
|
+
age_ms INTEGER NOT NULL
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
CREATE TABLE IF NOT EXISTS retrieval_feedback (
|
|
249
|
+
id TEXT PRIMARY KEY,
|
|
250
|
+
activation_event_id TEXT,
|
|
251
|
+
engram_id TEXT NOT NULL,
|
|
252
|
+
useful INTEGER NOT NULL,
|
|
253
|
+
context TEXT,
|
|
254
|
+
timestamp TEXT NOT NULL
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
CREATE TABLE IF NOT EXISTS episodes (
|
|
258
|
+
id TEXT PRIMARY KEY,
|
|
259
|
+
agent_id TEXT NOT NULL,
|
|
260
|
+
label TEXT NOT NULL,
|
|
261
|
+
embedding BLOB,
|
|
262
|
+
engram_count INTEGER NOT NULL DEFAULT 0,
|
|
263
|
+
start_time TEXT NOT NULL,
|
|
264
|
+
end_time TEXT NOT NULL,
|
|
265
|
+
created_at TEXT NOT NULL
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_agent ON episodes(agent_id);
|
|
269
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_time ON episodes(agent_id, end_time);
|
|
270
270
|
`);
|
|
271
271
|
// Migration: add episode_id column if missing
|
|
272
272
|
try {
|
|
@@ -280,10 +280,10 @@ export class EngramStore {
|
|
|
280
280
|
this.db.prepare('SELECT task_status FROM engrams LIMIT 0').get();
|
|
281
281
|
}
|
|
282
282
|
catch {
|
|
283
|
-
this.db.exec(`
|
|
284
|
-
ALTER TABLE engrams ADD COLUMN task_status TEXT;
|
|
285
|
-
ALTER TABLE engrams ADD COLUMN task_priority TEXT;
|
|
286
|
-
ALTER TABLE engrams ADD COLUMN blocked_by TEXT;
|
|
283
|
+
this.db.exec(`
|
|
284
|
+
ALTER TABLE engrams ADD COLUMN task_status TEXT;
|
|
285
|
+
ALTER TABLE engrams ADD COLUMN task_priority TEXT;
|
|
286
|
+
ALTER TABLE engrams ADD COLUMN blocked_by TEXT;
|
|
287
287
|
`);
|
|
288
288
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_engrams_task ON engrams(agent_id, task_status)');
|
|
289
289
|
}
|
|
@@ -292,10 +292,10 @@ export class EngramStore {
|
|
|
292
292
|
this.db.prepare('SELECT memory_class FROM engrams LIMIT 0').get();
|
|
293
293
|
}
|
|
294
294
|
catch {
|
|
295
|
-
this.db.exec(`
|
|
296
|
-
ALTER TABLE engrams ADD COLUMN memory_class TEXT NOT NULL DEFAULT 'working';
|
|
297
|
-
ALTER TABLE engrams ADD COLUMN superseded_by TEXT;
|
|
298
|
-
ALTER TABLE engrams ADD COLUMN supersedes TEXT;
|
|
295
|
+
this.db.exec(`
|
|
296
|
+
ALTER TABLE engrams ADD COLUMN memory_class TEXT NOT NULL DEFAULT 'working';
|
|
297
|
+
ALTER TABLE engrams ADD COLUMN superseded_by TEXT;
|
|
298
|
+
ALTER TABLE engrams ADD COLUMN supersedes TEXT;
|
|
299
299
|
`);
|
|
300
300
|
}
|
|
301
301
|
// Migration: add embedding_model for version tracking (prevents drift on model change)
|
|
@@ -305,23 +305,51 @@ export class EngramStore {
|
|
|
305
305
|
catch {
|
|
306
306
|
this.db.exec(`ALTER TABLE engrams ADD COLUMN embedding_model TEXT`);
|
|
307
307
|
}
|
|
308
|
+
// Entity inverted index + alias table (D9, 2026-07-30) — write-time
|
|
309
|
+
// bookkeeping only; retrieval unchanged until D11. Idempotent CREATEs.
|
|
310
|
+
this.db.exec(`
|
|
311
|
+
CREATE TABLE IF NOT EXISTS entity_mentions (
|
|
312
|
+
entity TEXT NOT NULL,
|
|
313
|
+
engram_id TEXT NOT NULL,
|
|
314
|
+
agent_id TEXT NOT NULL,
|
|
315
|
+
PRIMARY KEY (entity, engram_id)
|
|
316
|
+
);
|
|
317
|
+
CREATE INDEX IF NOT EXISTS idx_entity_mentions_agent ON entity_mentions(agent_id, entity);
|
|
318
|
+
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
319
|
+
alias TEXT PRIMARY KEY,
|
|
320
|
+
entity TEXT NOT NULL
|
|
321
|
+
);
|
|
322
|
+
`);
|
|
323
|
+
// Migration: memory-spine provenance + temporal validity (D5/D8, 2026-07-30)
|
|
324
|
+
try {
|
|
325
|
+
this.db.prepare('SELECT origin_class FROM engrams LIMIT 0').get();
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
this.db.exec(`
|
|
329
|
+
ALTER TABLE engrams ADD COLUMN origin_class TEXT;
|
|
330
|
+
ALTER TABLE engrams ADD COLUMN writer_session TEXT;
|
|
331
|
+
ALTER TABLE engrams ADD COLUMN recipe_id TEXT;
|
|
332
|
+
ALTER TABLE engrams ADD COLUMN valid_from TEXT;
|
|
333
|
+
ALTER TABLE engrams ADD COLUMN valid_to TEXT;
|
|
334
|
+
`);
|
|
335
|
+
}
|
|
308
336
|
// Migration: add conscious_state table for checkpointing
|
|
309
|
-
this.db.exec(`
|
|
310
|
-
CREATE TABLE IF NOT EXISTS conscious_state (
|
|
311
|
-
agent_id TEXT PRIMARY KEY,
|
|
312
|
-
last_write_id TEXT,
|
|
313
|
-
last_recall_context TEXT,
|
|
314
|
-
last_recall_ids TEXT NOT NULL DEFAULT '[]',
|
|
315
|
-
last_activity_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
316
|
-
write_count_since_consolidation INTEGER NOT NULL DEFAULT 0,
|
|
317
|
-
recall_count_since_consolidation INTEGER NOT NULL DEFAULT 0,
|
|
318
|
-
execution_state TEXT,
|
|
319
|
-
checkpoint_at TEXT,
|
|
320
|
-
last_consolidation_at TEXT,
|
|
321
|
-
last_mini_consolidation_at TEXT,
|
|
322
|
-
consolidation_cycle_count INTEGER NOT NULL DEFAULT 0,
|
|
323
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
324
|
-
)
|
|
337
|
+
this.db.exec(`
|
|
338
|
+
CREATE TABLE IF NOT EXISTS conscious_state (
|
|
339
|
+
agent_id TEXT PRIMARY KEY,
|
|
340
|
+
last_write_id TEXT,
|
|
341
|
+
last_recall_context TEXT,
|
|
342
|
+
last_recall_ids TEXT NOT NULL DEFAULT '[]',
|
|
343
|
+
last_activity_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
344
|
+
write_count_since_consolidation INTEGER NOT NULL DEFAULT 0,
|
|
345
|
+
recall_count_since_consolidation INTEGER NOT NULL DEFAULT 0,
|
|
346
|
+
execution_state TEXT,
|
|
347
|
+
checkpoint_at TEXT,
|
|
348
|
+
last_consolidation_at TEXT,
|
|
349
|
+
last_mini_consolidation_at TEXT,
|
|
350
|
+
consolidation_cycle_count INTEGER NOT NULL DEFAULT 0,
|
|
351
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
352
|
+
)
|
|
325
353
|
`);
|
|
326
354
|
// Migration: add consolidation_cycle_count if missing (existing DBs)
|
|
327
355
|
try {
|
|
@@ -342,7 +370,7 @@ export class EngramStore {
|
|
|
342
370
|
}
|
|
343
371
|
catch {
|
|
344
372
|
this.db.exec(`ALTER TABLE engrams ADD COLUMN sequence INTEGER`);
|
|
345
|
-
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_engrams_agent_sequence
|
|
373
|
+
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_engrams_agent_sequence
|
|
346
374
|
ON engrams(agent_id, sequence) WHERE sequence IS NOT NULL`);
|
|
347
375
|
}
|
|
348
376
|
// Migration (0.8 Cluster A): references_json column for typed cross-record
|
|
@@ -361,14 +389,14 @@ export class EngramStore {
|
|
|
361
389
|
const embeddingBlob = input.embedding
|
|
362
390
|
? Buffer.from(new Float32Array(input.embedding).buffer)
|
|
363
391
|
: null;
|
|
364
|
-
this.db.prepare(`
|
|
365
|
-
INSERT INTO engrams (id, agent_id, concept, content, embedding, confidence, salience,
|
|
366
|
-
access_count, last_accessed, created_at, salience_features, reason_codes, stage, tags, episode_id,
|
|
367
|
-
ttl, memory_class, supersedes, task_status, task_priority, blocked_by, memory_type,
|
|
368
|
-
sequence, references_json)
|
|
369
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
392
|
+
this.db.prepare(`
|
|
393
|
+
INSERT INTO engrams (id, agent_id, concept, content, embedding, confidence, salience,
|
|
394
|
+
access_count, last_accessed, created_at, salience_features, reason_codes, stage, tags, episode_id,
|
|
395
|
+
ttl, memory_class, supersedes, task_status, task_priority, blocked_by, memory_type,
|
|
396
|
+
sequence, references_json, origin_class, writer_session, recipe_id, valid_from, valid_to)
|
|
397
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
370
398
|
`).run(id, input.agentId, input.concept, input.content, embeddingBlob, input.confidence ?? 0.5, input.salience ?? 0.5, now, now, JSON.stringify(input.salienceFeatures ?? DEFAULT_SALIENCE_FEATURES), JSON.stringify(input.reasonCodes ?? []), JSON.stringify(input.tags ?? []), input.episodeId ?? null, input.ttl ?? null, input.memoryClass ?? 'working', input.supersedes ?? null, input.taskStatus ?? null, input.taskPriority ?? null, input.blockedBy ?? null, input.memoryType ?? 'unclassified', input.sequence ?? null, input.references && input.references.length > 0
|
|
371
|
-
? JSON.stringify(input.references) : null);
|
|
399
|
+
? JSON.stringify(input.references) : null, input.originClass ?? null, input.writerSession ?? null, input.recipeId ?? null, input.validFrom ?? null, input.validTo ?? null);
|
|
372
400
|
// Add to slim cache (skip if not yet populated — first slim fetch will load it)
|
|
373
401
|
if (this.slimCachePopulated) {
|
|
374
402
|
this.cacheAdd({
|
|
@@ -536,15 +564,15 @@ export class EngramStore {
|
|
|
536
564
|
try {
|
|
537
565
|
const placeholders = agentIds.map(() => '?').join(',');
|
|
538
566
|
const innerLimit = Math.max(limit * 5, 50);
|
|
539
|
-
const rows = this.db.prepare(`
|
|
540
|
-
WITH top_fts AS (
|
|
541
|
-
SELECT rowid, rank FROM engrams_fts WHERE engrams_fts MATCH ? ORDER BY rank LIMIT ?
|
|
542
|
-
)
|
|
543
|
-
SELECT e.*, top_fts.rank FROM top_fts
|
|
544
|
-
JOIN engrams e ON e.rowid = top_fts.rowid
|
|
545
|
-
WHERE e.agent_id IN (${placeholders}) AND e.retracted = 0
|
|
546
|
-
ORDER BY top_fts.rank
|
|
547
|
-
LIMIT ?
|
|
567
|
+
const rows = this.db.prepare(`
|
|
568
|
+
WITH top_fts AS (
|
|
569
|
+
SELECT rowid, rank FROM engrams_fts WHERE engrams_fts MATCH ? ORDER BY rank LIMIT ?
|
|
570
|
+
)
|
|
571
|
+
SELECT e.*, top_fts.rank FROM top_fts
|
|
572
|
+
JOIN engrams e ON e.rowid = top_fts.rowid
|
|
573
|
+
WHERE e.agent_id IN (${placeholders}) AND e.retracted = 0
|
|
574
|
+
ORDER BY top_fts.rank
|
|
575
|
+
LIMIT ?
|
|
548
576
|
`).all(sanitized, innerLimit, ...agentIds, limit);
|
|
549
577
|
return rows.map(r => ({
|
|
550
578
|
engram: this.rowToEngram(r),
|
|
@@ -583,12 +611,12 @@ export class EngramStore {
|
|
|
583
611
|
* Boost: +0.02 per access, scaled by 1/sqrt(accessCount+1), capped at 0.85.
|
|
584
612
|
*/
|
|
585
613
|
touchEngram(id) {
|
|
586
|
-
this.db.prepare(`
|
|
587
|
-
UPDATE engrams
|
|
588
|
-
SET access_count = access_count + 1,
|
|
589
|
-
last_accessed = ?,
|
|
590
|
-
confidence = MIN(0.85, confidence + 0.02 / (1.0 + sqrt(access_count)))
|
|
591
|
-
WHERE id = ?
|
|
614
|
+
this.db.prepare(`
|
|
615
|
+
UPDATE engrams
|
|
616
|
+
SET access_count = access_count + 1,
|
|
617
|
+
last_accessed = ?,
|
|
618
|
+
confidence = MIN(0.85, confidence + 0.02 / (1.0 + sqrt(access_count)))
|
|
619
|
+
WHERE id = ?
|
|
592
620
|
`).run(new Date().toISOString(), id);
|
|
593
621
|
}
|
|
594
622
|
updateStage(id, stage) {
|
|
@@ -618,8 +646,8 @@ export class EngramStore {
|
|
|
618
646
|
this.cacheUpdateEmbedding(id, embedding);
|
|
619
647
|
}
|
|
620
648
|
retractEngram(id, retractedBy) {
|
|
621
|
-
this.db.prepare(`
|
|
622
|
-
UPDATE engrams SET retracted = 1, retracted_by = ?, retracted_at = ? WHERE id = ?
|
|
649
|
+
this.db.prepare(`
|
|
650
|
+
UPDATE engrams SET retracted = 1, retracted_by = ?, retracted_at = ? WHERE id = ?
|
|
623
651
|
`).run(retractedBy, new Date().toISOString(), id);
|
|
624
652
|
this.cacheRetract(id);
|
|
625
653
|
}
|
|
@@ -636,20 +664,20 @@ export class EngramStore {
|
|
|
636
664
|
let count = 0;
|
|
637
665
|
const shiftSec = Math.round(ms / 1000);
|
|
638
666
|
// Shift engram timestamps
|
|
639
|
-
const r1 = this.db.prepare(`
|
|
640
|
-
UPDATE engrams SET
|
|
641
|
-
created_at = datetime(created_at, '-${shiftSec} seconds'),
|
|
642
|
-
last_accessed = datetime(last_accessed, '-${shiftSec} seconds')
|
|
643
|
-
WHERE agent_id = ?
|
|
667
|
+
const r1 = this.db.prepare(`
|
|
668
|
+
UPDATE engrams SET
|
|
669
|
+
created_at = datetime(created_at, '-${shiftSec} seconds'),
|
|
670
|
+
last_accessed = datetime(last_accessed, '-${shiftSec} seconds')
|
|
671
|
+
WHERE agent_id = ?
|
|
644
672
|
`).run(agentId);
|
|
645
673
|
count += r1.changes;
|
|
646
674
|
// Shift association timestamps
|
|
647
|
-
const r2 = this.db.prepare(`
|
|
648
|
-
UPDATE associations SET
|
|
649
|
-
created_at = datetime(created_at, '-${shiftSec} seconds'),
|
|
650
|
-
last_activated = datetime(last_activated, '-${shiftSec} seconds')
|
|
651
|
-
WHERE from_engram_id IN (SELECT id FROM engrams WHERE agent_id = ?)
|
|
652
|
-
OR to_engram_id IN (SELECT id FROM engrams WHERE agent_id = ?)
|
|
675
|
+
const r2 = this.db.prepare(`
|
|
676
|
+
UPDATE associations SET
|
|
677
|
+
created_at = datetime(created_at, '-${shiftSec} seconds'),
|
|
678
|
+
last_activated = datetime(last_activated, '-${shiftSec} seconds')
|
|
679
|
+
WHERE from_engram_id IN (SELECT id FROM engrams WHERE agent_id = ?)
|
|
680
|
+
OR to_engram_id IN (SELECT id FROM engrams WHERE agent_id = ?)
|
|
653
681
|
`).run(agentId, agentId);
|
|
654
682
|
count += r2.changes;
|
|
655
683
|
return count;
|
|
@@ -728,15 +756,15 @@ export class EngramStore {
|
|
|
728
756
|
// dominant, low retracted rate).
|
|
729
757
|
try {
|
|
730
758
|
const innerLimit = Math.max(limit * 5, 50);
|
|
731
|
-
const rows = this.db.prepare(`
|
|
732
|
-
WITH top_fts AS (
|
|
733
|
-
SELECT rowid, rank FROM engrams_fts WHERE engrams_fts MATCH ? ORDER BY rank LIMIT ?
|
|
734
|
-
)
|
|
735
|
-
SELECT e.*, top_fts.rank FROM top_fts
|
|
736
|
-
JOIN engrams e ON e.rowid = top_fts.rowid
|
|
737
|
-
WHERE e.agent_id = ? AND e.retracted = 0
|
|
738
|
-
ORDER BY top_fts.rank
|
|
739
|
-
LIMIT ?
|
|
759
|
+
const rows = this.db.prepare(`
|
|
760
|
+
WITH top_fts AS (
|
|
761
|
+
SELECT rowid, rank FROM engrams_fts WHERE engrams_fts MATCH ? ORDER BY rank LIMIT ?
|
|
762
|
+
)
|
|
763
|
+
SELECT e.*, top_fts.rank FROM top_fts
|
|
764
|
+
JOIN engrams e ON e.rowid = top_fts.rowid
|
|
765
|
+
WHERE e.agent_id = ? AND e.retracted = 0
|
|
766
|
+
ORDER BY top_fts.rank
|
|
767
|
+
LIMIT ?
|
|
740
768
|
`).all(sanitized, innerLimit, agentId, limit);
|
|
741
769
|
return rows.map(r => ({
|
|
742
770
|
engram: this.rowToEngram(r),
|
|
@@ -851,14 +879,14 @@ export class EngramStore {
|
|
|
851
879
|
sql += ' AND task_status = ?';
|
|
852
880
|
params.push(status);
|
|
853
881
|
}
|
|
854
|
-
sql += ` ORDER BY
|
|
855
|
-
CASE task_priority
|
|
856
|
-
WHEN 'urgent' THEN 0
|
|
857
|
-
WHEN 'high' THEN 1
|
|
858
|
-
WHEN 'medium' THEN 2
|
|
859
|
-
WHEN 'low' THEN 3
|
|
860
|
-
ELSE 4
|
|
861
|
-
END,
|
|
882
|
+
sql += ` ORDER BY
|
|
883
|
+
CASE task_priority
|
|
884
|
+
WHEN 'urgent' THEN 0
|
|
885
|
+
WHEN 'high' THEN 1
|
|
886
|
+
WHEN 'medium' THEN 2
|
|
887
|
+
WHEN 'low' THEN 3
|
|
888
|
+
ELSE 4
|
|
889
|
+
END,
|
|
862
890
|
created_at DESC`;
|
|
863
891
|
return this.db.prepare(sql).all(...params).map(r => this.rowToEngram(r));
|
|
864
892
|
}
|
|
@@ -866,20 +894,20 @@ export class EngramStore {
|
|
|
866
894
|
* Get the next actionable task — highest priority that's not blocked or done.
|
|
867
895
|
*/
|
|
868
896
|
getNextTask(agentId) {
|
|
869
|
-
const row = this.db.prepare(`
|
|
870
|
-
SELECT * FROM engrams
|
|
871
|
-
WHERE agent_id = ? AND task_status IN ('open', 'in_progress') AND retracted = 0
|
|
872
|
-
ORDER BY
|
|
873
|
-
CASE task_status WHEN 'in_progress' THEN 0 ELSE 1 END,
|
|
874
|
-
CASE task_priority
|
|
875
|
-
WHEN 'urgent' THEN 0
|
|
876
|
-
WHEN 'high' THEN 1
|
|
877
|
-
WHEN 'medium' THEN 2
|
|
878
|
-
WHEN 'low' THEN 3
|
|
879
|
-
ELSE 4
|
|
880
|
-
END,
|
|
881
|
-
created_at ASC
|
|
882
|
-
LIMIT 1
|
|
897
|
+
const row = this.db.prepare(`
|
|
898
|
+
SELECT * FROM engrams
|
|
899
|
+
WHERE agent_id = ? AND task_status IN ('open', 'in_progress') AND retracted = 0
|
|
900
|
+
ORDER BY
|
|
901
|
+
CASE task_status WHEN 'in_progress' THEN 0 ELSE 1 END,
|
|
902
|
+
CASE task_priority
|
|
903
|
+
WHEN 'urgent' THEN 0
|
|
904
|
+
WHEN 'high' THEN 1
|
|
905
|
+
WHEN 'medium' THEN 2
|
|
906
|
+
WHEN 'low' THEN 3
|
|
907
|
+
ELSE 4
|
|
908
|
+
END,
|
|
909
|
+
created_at ASC
|
|
910
|
+
LIMIT 1
|
|
883
911
|
`).get(agentId);
|
|
884
912
|
return row ? this.rowToEngram(row) : null;
|
|
885
913
|
}
|
|
@@ -901,11 +929,11 @@ export class EngramStore {
|
|
|
901
929
|
* Returns null if no match.
|
|
902
930
|
*/
|
|
903
931
|
findActiveMatchByConcept(agentId, concept, requiredTags) {
|
|
904
|
-
let sql = `SELECT * FROM engrams
|
|
905
|
-
WHERE agent_id = ?
|
|
906
|
-
AND LOWER(TRIM(concept)) = LOWER(TRIM(?))
|
|
907
|
-
AND stage = 'active'
|
|
908
|
-
AND retracted = 0
|
|
932
|
+
let sql = `SELECT * FROM engrams
|
|
933
|
+
WHERE agent_id = ?
|
|
934
|
+
AND LOWER(TRIM(concept)) = LOWER(TRIM(?))
|
|
935
|
+
AND stage = 'active'
|
|
936
|
+
AND retracted = 0
|
|
909
937
|
AND superseded_by IS NULL`;
|
|
910
938
|
const params = [agentId, concept];
|
|
911
939
|
if (requiredTags && requiredTags.length > 0) {
|
|
@@ -935,11 +963,11 @@ export class EngramStore {
|
|
|
935
963
|
upsertAssociation(fromId, toId, weight, type = 'hebbian', confidence = 0.5) {
|
|
936
964
|
const now = new Date().toISOString();
|
|
937
965
|
const id = randomUUID();
|
|
938
|
-
this.db.prepare(`
|
|
939
|
-
INSERT INTO associations (id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated)
|
|
940
|
-
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)
|
|
941
|
-
ON CONFLICT(from_engram_id, to_engram_id) DO UPDATE SET
|
|
942
|
-
weight = ?, confidence = ?, last_activated = ?, activation_count = activation_count + 1
|
|
966
|
+
this.db.prepare(`
|
|
967
|
+
INSERT INTO associations (id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated)
|
|
968
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)
|
|
969
|
+
ON CONFLICT(from_engram_id, to_engram_id) DO UPDATE SET
|
|
970
|
+
weight = ?, confidence = ?, last_activated = ?, activation_count = activation_count + 1
|
|
943
971
|
`).run(id, fromId, toId, weight, confidence, type, now, now, weight, confidence, now);
|
|
944
972
|
return this.getAssociation(fromId, toId);
|
|
945
973
|
}
|
|
@@ -979,12 +1007,12 @@ export class EngramStore {
|
|
|
979
1007
|
// associations under both their from and to engram. Self-loops would be
|
|
980
1008
|
// double-counted, but they're rare in practice and the prior code handled them
|
|
981
1009
|
// identically.
|
|
982
|
-
const rows = this.db.prepare(`SELECT id, SUM(cnt) AS count, SUM(sw) AS sum_weight FROM (
|
|
983
|
-
SELECT from_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE from_engram_id IN (${placeholders})
|
|
984
|
-
UNION ALL
|
|
985
|
-
SELECT to_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE to_engram_id IN (${placeholders})
|
|
986
|
-
)
|
|
987
|
-
WHERE id IN (${placeholders})
|
|
1010
|
+
const rows = this.db.prepare(`SELECT id, SUM(cnt) AS count, SUM(sw) AS sum_weight FROM (
|
|
1011
|
+
SELECT from_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE from_engram_id IN (${placeholders})
|
|
1012
|
+
UNION ALL
|
|
1013
|
+
SELECT to_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE to_engram_id IN (${placeholders})
|
|
1014
|
+
)
|
|
1015
|
+
WHERE id IN (${placeholders})
|
|
988
1016
|
GROUP BY id`).all(...chunk, ...chunk, ...chunk);
|
|
989
1017
|
for (const r of rows) {
|
|
990
1018
|
result.set(r.id, { count: r.count, sumWeight: r.sum_weight });
|
|
@@ -1015,7 +1043,7 @@ export class EngramStore {
|
|
|
1015
1043
|
for (let i = 0; i < engramIds.length; i += CHUNK) {
|
|
1016
1044
|
const chunk = engramIds.slice(i, i + CHUNK);
|
|
1017
1045
|
const placeholders = chunk.map(() => '?').join(',');
|
|
1018
|
-
const rows = this.db.prepare(`SELECT * FROM associations
|
|
1046
|
+
const rows = this.db.prepare(`SELECT * FROM associations
|
|
1019
1047
|
WHERE from_engram_id IN (${placeholders}) OR to_engram_id IN (${placeholders})`).all(...chunk, ...chunk);
|
|
1020
1048
|
for (const r of rows) {
|
|
1021
1049
|
const a = this.rowToAssociation(r);
|
|
@@ -1054,22 +1082,22 @@ export class EngramStore {
|
|
|
1054
1082
|
this.db.prepare('DELETE FROM associations WHERE id = ?').run(id);
|
|
1055
1083
|
}
|
|
1056
1084
|
getAllAssociations(agentId) {
|
|
1057
|
-
const rows = this.db.prepare(`
|
|
1058
|
-
SELECT a.* FROM associations a
|
|
1059
|
-
JOIN engrams e ON a.from_engram_id = e.id
|
|
1060
|
-
WHERE e.agent_id = ?
|
|
1085
|
+
const rows = this.db.prepare(`
|
|
1086
|
+
SELECT a.* FROM associations a
|
|
1087
|
+
JOIN engrams e ON a.from_engram_id = e.id
|
|
1088
|
+
WHERE e.agent_id = ?
|
|
1061
1089
|
`).all(agentId);
|
|
1062
1090
|
return rows.map(r => this.rowToAssociation(r));
|
|
1063
1091
|
}
|
|
1064
1092
|
// --- Eviction ---
|
|
1065
1093
|
getEvictionCandidates(agentId, limit) {
|
|
1066
1094
|
// Lowest combined score: low salience + low access + low confidence + oldest
|
|
1067
|
-
const rows = this.db.prepare(`
|
|
1068
|
-
SELECT * FROM engrams
|
|
1069
|
-
WHERE agent_id = ? AND stage = 'active' AND retracted = 0
|
|
1070
|
-
ORDER BY (salience * 0.3 + confidence * 0.3 + (CAST(access_count AS REAL) / (access_count + 5)) * 0.2 +
|
|
1071
|
-
(1.0 / (1.0 + (julianday('now') - julianday(last_accessed)))) * 0.2) ASC
|
|
1072
|
-
LIMIT ?
|
|
1095
|
+
const rows = this.db.prepare(`
|
|
1096
|
+
SELECT * FROM engrams
|
|
1097
|
+
WHERE agent_id = ? AND stage = 'active' AND retracted = 0
|
|
1098
|
+
ORDER BY (salience * 0.3 + confidence * 0.3 + (CAST(access_count AS REAL) / (access_count + 5)) * 0.2 +
|
|
1099
|
+
(1.0 / (1.0 + (julianday('now') - julianday(last_accessed)))) * 0.2) ASC
|
|
1100
|
+
LIMIT ?
|
|
1073
1101
|
`).all(agentId, limit);
|
|
1074
1102
|
return rows.map(r => this.rowToEngram(r));
|
|
1075
1103
|
}
|
|
@@ -1084,8 +1112,8 @@ export class EngramStore {
|
|
|
1084
1112
|
// --- Staging buffer ---
|
|
1085
1113
|
getExpiredStaging() {
|
|
1086
1114
|
const now = Date.now();
|
|
1087
|
-
const rows = this.db.prepare(`
|
|
1088
|
-
SELECT * FROM engrams WHERE stage = 'staging' AND ttl IS NOT NULL
|
|
1115
|
+
const rows = this.db.prepare(`
|
|
1116
|
+
SELECT * FROM engrams WHERE stage = 'staging' AND ttl IS NOT NULL
|
|
1089
1117
|
`).all();
|
|
1090
1118
|
return rows
|
|
1091
1119
|
.map(r => this.rowToEngram(r))
|
|
@@ -1093,53 +1121,53 @@ export class EngramStore {
|
|
|
1093
1121
|
}
|
|
1094
1122
|
// --- Eval event logging ---
|
|
1095
1123
|
logActivationEvent(event) {
|
|
1096
|
-
this.db.prepare(`
|
|
1097
|
-
INSERT INTO activation_events (id, agent_id, timestamp, context, results_returned, top_score, latency_ms, engram_ids)
|
|
1098
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1124
|
+
this.db.prepare(`
|
|
1125
|
+
INSERT INTO activation_events (id, agent_id, timestamp, context, results_returned, top_score, latency_ms, engram_ids)
|
|
1126
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1099
1127
|
`).run(event.id, event.agentId, event.timestamp.toISOString(), event.context, event.resultsReturned, event.topScore, event.latencyMs, JSON.stringify(event.engramIds));
|
|
1100
1128
|
}
|
|
1101
1129
|
logStagingEvent(event) {
|
|
1102
|
-
this.db.prepare(`
|
|
1103
|
-
INSERT INTO staging_events (engram_id, agent_id, action, resonance_score, timestamp, age_ms)
|
|
1104
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
1130
|
+
this.db.prepare(`
|
|
1131
|
+
INSERT INTO staging_events (engram_id, agent_id, action, resonance_score, timestamp, age_ms)
|
|
1132
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1105
1133
|
`).run(event.engramId, event.agentId, event.action, event.resonanceScore, event.timestamp.toISOString(), event.ageMs);
|
|
1106
1134
|
}
|
|
1107
1135
|
logRetrievalFeedback(activationEventId, engramId, useful, context) {
|
|
1108
|
-
this.db.prepare(`
|
|
1109
|
-
INSERT INTO retrieval_feedback (id, activation_event_id, engram_id, useful, context, timestamp)
|
|
1110
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
1136
|
+
this.db.prepare(`
|
|
1137
|
+
INSERT INTO retrieval_feedback (id, activation_event_id, engram_id, useful, context, timestamp)
|
|
1138
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1111
1139
|
`).run(randomUUID(), activationEventId, engramId, useful ? 1 : 0, context, new Date().toISOString());
|
|
1112
1140
|
}
|
|
1113
1141
|
// --- Eval metrics queries ---
|
|
1114
1142
|
getRetrievalPrecision(agentId, windowHours = 24) {
|
|
1115
1143
|
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1116
|
-
const row = this.db.prepare(`
|
|
1117
|
-
SELECT
|
|
1118
|
-
COUNT(CASE WHEN useful = 1 THEN 1 END) as useful_count,
|
|
1119
|
-
COUNT(*) as total_count
|
|
1120
|
-
FROM retrieval_feedback rf
|
|
1121
|
-
LEFT JOIN activation_events ae ON rf.activation_event_id = ae.id
|
|
1122
|
-
JOIN engrams e ON rf.engram_id = e.id
|
|
1123
|
-
WHERE e.agent_id = ? AND rf.timestamp > ?
|
|
1144
|
+
const row = this.db.prepare(`
|
|
1145
|
+
SELECT
|
|
1146
|
+
COUNT(CASE WHEN useful = 1 THEN 1 END) as useful_count,
|
|
1147
|
+
COUNT(*) as total_count
|
|
1148
|
+
FROM retrieval_feedback rf
|
|
1149
|
+
LEFT JOIN activation_events ae ON rf.activation_event_id = ae.id
|
|
1150
|
+
JOIN engrams e ON rf.engram_id = e.id
|
|
1151
|
+
WHERE e.agent_id = ? AND rf.timestamp > ?
|
|
1124
1152
|
`).get(agentId, since);
|
|
1125
1153
|
return row.total_count > 0 ? row.useful_count / row.total_count : 0;
|
|
1126
1154
|
}
|
|
1127
1155
|
getStagingMetrics(agentId) {
|
|
1128
|
-
const row = this.db.prepare(`
|
|
1129
|
-
SELECT
|
|
1130
|
-
COUNT(CASE WHEN action = 'promoted' THEN 1 END) as promoted,
|
|
1131
|
-
COUNT(CASE WHEN action = 'discarded' THEN 1 END) as discarded,
|
|
1132
|
-
COUNT(CASE WHEN action = 'expired' THEN 1 END) as expired
|
|
1133
|
-
FROM staging_events WHERE agent_id = ?
|
|
1156
|
+
const row = this.db.prepare(`
|
|
1157
|
+
SELECT
|
|
1158
|
+
COUNT(CASE WHEN action = 'promoted' THEN 1 END) as promoted,
|
|
1159
|
+
COUNT(CASE WHEN action = 'discarded' THEN 1 END) as discarded,
|
|
1160
|
+
COUNT(CASE WHEN action = 'expired' THEN 1 END) as expired
|
|
1161
|
+
FROM staging_events WHERE agent_id = ?
|
|
1134
1162
|
`).get(agentId);
|
|
1135
1163
|
return { promoted: row.promoted, discarded: row.discarded, expired: row.expired };
|
|
1136
1164
|
}
|
|
1137
1165
|
getActivationStats(agentId, windowHours = 24) {
|
|
1138
1166
|
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1139
|
-
const rows = this.db.prepare(`
|
|
1140
|
-
SELECT latency_ms FROM activation_events
|
|
1141
|
-
WHERE agent_id = ? AND timestamp > ?
|
|
1142
|
-
ORDER BY latency_ms ASC
|
|
1167
|
+
const rows = this.db.prepare(`
|
|
1168
|
+
SELECT latency_ms FROM activation_events
|
|
1169
|
+
WHERE agent_id = ? AND timestamp > ?
|
|
1170
|
+
ORDER BY latency_ms ASC
|
|
1143
1171
|
`).all(agentId, since);
|
|
1144
1172
|
if (rows.length === 0)
|
|
1145
1173
|
return { count: 0, avgLatencyMs: 0, p95LatencyMs: 0 };
|
|
@@ -1188,6 +1216,11 @@ export class EngramStore {
|
|
|
1188
1216
|
blockedBy: row.blocked_by ?? null,
|
|
1189
1217
|
sequence: row.sequence == null ? null : Number(row.sequence),
|
|
1190
1218
|
references: row.references_json ? JSON.parse(row.references_json) : null,
|
|
1219
|
+
originClass: row.origin_class ?? null,
|
|
1220
|
+
writerSession: row.writer_session ?? null,
|
|
1221
|
+
recipeId: row.recipe_id ?? null,
|
|
1222
|
+
validFrom: row.valid_from ?? null,
|
|
1223
|
+
validTo: row.valid_to ?? null,
|
|
1191
1224
|
};
|
|
1192
1225
|
}
|
|
1193
1226
|
rowToAssociation(row) {
|
|
@@ -1210,9 +1243,9 @@ export class EngramStore {
|
|
|
1210
1243
|
const embeddingBlob = input.embedding
|
|
1211
1244
|
? Buffer.from(new Float32Array(input.embedding).buffer)
|
|
1212
1245
|
: null;
|
|
1213
|
-
this.db.prepare(`
|
|
1214
|
-
INSERT INTO episodes (id, agent_id, label, embedding, engram_count, start_time, end_time, created_at)
|
|
1215
|
-
VALUES (?, ?, ?, ?, 0, ?, ?, ?)
|
|
1246
|
+
this.db.prepare(`
|
|
1247
|
+
INSERT INTO episodes (id, agent_id, label, embedding, engram_count, start_time, end_time, created_at)
|
|
1248
|
+
VALUES (?, ?, ?, ?, 0, ?, ?, ?)
|
|
1216
1249
|
`).run(id, input.agentId, input.label, embeddingBlob, now, now, now);
|
|
1217
1250
|
return this.getEpisode(id);
|
|
1218
1251
|
}
|
|
@@ -1227,19 +1260,19 @@ export class EngramStore {
|
|
|
1227
1260
|
getActiveEpisode(agentId, windowMs = 3600_000) {
|
|
1228
1261
|
// Find most recent episode that ended within the time window
|
|
1229
1262
|
const cutoff = new Date(Date.now() - windowMs).toISOString();
|
|
1230
|
-
const row = this.db.prepare(`
|
|
1231
|
-
SELECT * FROM episodes WHERE agent_id = ? AND end_time > ?
|
|
1232
|
-
ORDER BY end_time DESC LIMIT 1
|
|
1263
|
+
const row = this.db.prepare(`
|
|
1264
|
+
SELECT * FROM episodes WHERE agent_id = ? AND end_time > ?
|
|
1265
|
+
ORDER BY end_time DESC LIMIT 1
|
|
1233
1266
|
`).get(agentId, cutoff);
|
|
1234
1267
|
return row ? this.rowToEpisode(row) : null;
|
|
1235
1268
|
}
|
|
1236
1269
|
addEngramToEpisode(engramId, episodeId) {
|
|
1237
1270
|
this.db.prepare('UPDATE engrams SET episode_id = ? WHERE id = ?').run(episodeId, engramId);
|
|
1238
|
-
this.db.prepare(`
|
|
1239
|
-
UPDATE episodes SET
|
|
1240
|
-
engram_count = engram_count + 1,
|
|
1241
|
-
end_time = MAX(end_time, ?)
|
|
1242
|
-
WHERE id = ?
|
|
1271
|
+
this.db.prepare(`
|
|
1272
|
+
UPDATE episodes SET
|
|
1273
|
+
engram_count = engram_count + 1,
|
|
1274
|
+
end_time = MAX(end_time, ?)
|
|
1275
|
+
WHERE id = ?
|
|
1243
1276
|
`).run(new Date().toISOString(), episodeId);
|
|
1244
1277
|
}
|
|
1245
1278
|
getEngramsByEpisode(episodeId) {
|
|
@@ -1290,51 +1323,86 @@ export class EngramStore {
|
|
|
1290
1323
|
// --- Checkpointing ---
|
|
1291
1324
|
updateAutoCheckpointWrite(agentId, engramId) {
|
|
1292
1325
|
const now = new Date().toISOString();
|
|
1293
|
-
this.db.prepare(`
|
|
1294
|
-
INSERT INTO conscious_state (agent_id, last_write_id, last_activity_at, write_count_since_consolidation, updated_at)
|
|
1295
|
-
VALUES (?, ?, ?, 1, ?)
|
|
1296
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1297
|
-
last_write_id = excluded.last_write_id,
|
|
1298
|
-
last_activity_at = excluded.last_activity_at,
|
|
1299
|
-
write_count_since_consolidation = write_count_since_consolidation + 1,
|
|
1300
|
-
updated_at = excluded.updated_at
|
|
1326
|
+
this.db.prepare(`
|
|
1327
|
+
INSERT INTO conscious_state (agent_id, last_write_id, last_activity_at, write_count_since_consolidation, updated_at)
|
|
1328
|
+
VALUES (?, ?, ?, 1, ?)
|
|
1329
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1330
|
+
last_write_id = excluded.last_write_id,
|
|
1331
|
+
last_activity_at = excluded.last_activity_at,
|
|
1332
|
+
write_count_since_consolidation = write_count_since_consolidation + 1,
|
|
1333
|
+
updated_at = excluded.updated_at
|
|
1301
1334
|
`).run(agentId, engramId, now, now);
|
|
1302
1335
|
}
|
|
1303
1336
|
updateAutoCheckpointRecall(agentId, context, engramIds) {
|
|
1304
1337
|
const now = new Date().toISOString();
|
|
1305
|
-
this.db.prepare(`
|
|
1306
|
-
INSERT INTO conscious_state (agent_id, last_recall_context, last_recall_ids, last_activity_at, recall_count_since_consolidation, updated_at)
|
|
1307
|
-
VALUES (?, ?, ?, ?, 1, ?)
|
|
1308
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1309
|
-
last_recall_context = excluded.last_recall_context,
|
|
1310
|
-
last_recall_ids = excluded.last_recall_ids,
|
|
1311
|
-
last_activity_at = excluded.last_activity_at,
|
|
1312
|
-
recall_count_since_consolidation = recall_count_since_consolidation + 1,
|
|
1313
|
-
updated_at = excluded.updated_at
|
|
1338
|
+
this.db.prepare(`
|
|
1339
|
+
INSERT INTO conscious_state (agent_id, last_recall_context, last_recall_ids, last_activity_at, recall_count_since_consolidation, updated_at)
|
|
1340
|
+
VALUES (?, ?, ?, ?, 1, ?)
|
|
1341
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1342
|
+
last_recall_context = excluded.last_recall_context,
|
|
1343
|
+
last_recall_ids = excluded.last_recall_ids,
|
|
1344
|
+
last_activity_at = excluded.last_activity_at,
|
|
1345
|
+
recall_count_since_consolidation = recall_count_since_consolidation + 1,
|
|
1346
|
+
updated_at = excluded.updated_at
|
|
1314
1347
|
`).run(agentId, context, JSON.stringify(engramIds), now, now);
|
|
1315
1348
|
}
|
|
1316
1349
|
touchActivity(agentId) {
|
|
1317
1350
|
const now = new Date().toISOString();
|
|
1318
|
-
this.db.prepare(`
|
|
1319
|
-
INSERT INTO conscious_state (agent_id, last_activity_at, updated_at)
|
|
1320
|
-
VALUES (?, ?, ?)
|
|
1321
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1322
|
-
last_activity_at = excluded.last_activity_at,
|
|
1323
|
-
updated_at = excluded.updated_at
|
|
1351
|
+
this.db.prepare(`
|
|
1352
|
+
INSERT INTO conscious_state (agent_id, last_activity_at, updated_at)
|
|
1353
|
+
VALUES (?, ?, ?)
|
|
1354
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1355
|
+
last_activity_at = excluded.last_activity_at,
|
|
1356
|
+
updated_at = excluded.updated_at
|
|
1324
1357
|
`).run(agentId, now, now);
|
|
1325
1358
|
}
|
|
1326
1359
|
saveCheckpoint(agentId, state) {
|
|
1327
1360
|
const now = new Date().toISOString();
|
|
1328
|
-
this.db.prepare(`
|
|
1329
|
-
INSERT INTO conscious_state (agent_id, execution_state, checkpoint_at, last_activity_at, updated_at)
|
|
1330
|
-
VALUES (?, ?, ?, ?, ?)
|
|
1331
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1332
|
-
execution_state = excluded.execution_state,
|
|
1333
|
-
checkpoint_at = excluded.checkpoint_at,
|
|
1334
|
-
last_activity_at = excluded.last_activity_at,
|
|
1335
|
-
updated_at = excluded.updated_at
|
|
1361
|
+
this.db.prepare(`
|
|
1362
|
+
INSERT INTO conscious_state (agent_id, execution_state, checkpoint_at, last_activity_at, updated_at)
|
|
1363
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1364
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1365
|
+
execution_state = excluded.execution_state,
|
|
1366
|
+
checkpoint_at = excluded.checkpoint_at,
|
|
1367
|
+
last_activity_at = excluded.last_activity_at,
|
|
1368
|
+
updated_at = excluded.updated_at
|
|
1336
1369
|
`).run(agentId, JSON.stringify(state), now, now, now);
|
|
1337
1370
|
}
|
|
1371
|
+
/** Record entity mentions for an engram (D9 inverted index; idempotent). */
|
|
1372
|
+
recordEntityMentions(engramId, agentId, entities) {
|
|
1373
|
+
if (!entities || entities.length === 0)
|
|
1374
|
+
return;
|
|
1375
|
+
const stmt = this.db.prepare('INSERT OR IGNORE INTO entity_mentions (entity, engram_id, agent_id) VALUES (?, ?, ?)');
|
|
1376
|
+
for (const entity of entities)
|
|
1377
|
+
stmt.run(entity, engramId, agentId);
|
|
1378
|
+
}
|
|
1379
|
+
/** Indexed entities whose normalized key:value — or a registered alias — contains the term
|
|
1380
|
+
* (D11 query-side lookup). Alias matches resolve to their target entity. */
|
|
1381
|
+
searchEntities(term, limit = 8) {
|
|
1382
|
+
const t = term.toLowerCase().replace(/[%_]/g, '');
|
|
1383
|
+
if (!t)
|
|
1384
|
+
return [];
|
|
1385
|
+
const rows = this.db.prepare(`
|
|
1386
|
+
SELECT DISTINCT entity FROM (
|
|
1387
|
+
SELECT entity FROM entity_mentions WHERE entity LIKE ?
|
|
1388
|
+
UNION SELECT entity FROM entity_aliases WHERE alias LIKE ?
|
|
1389
|
+
) LIMIT ?`).all(`%${t}%`, `%${t}%`, limit);
|
|
1390
|
+
return rows.map(r => r.entity);
|
|
1391
|
+
}
|
|
1392
|
+
/** Engram ids mentioning an entity (alias-resolved), optionally agent-scoped. */
|
|
1393
|
+
getEngramIdsByEntity(entity, agentId) {
|
|
1394
|
+
const resolved = this.db.prepare('SELECT entity FROM entity_aliases WHERE alias = ?')
|
|
1395
|
+
.get(entity.toLowerCase())?.entity ?? entity.toLowerCase();
|
|
1396
|
+
const rows = agentId
|
|
1397
|
+
? this.db.prepare('SELECT engram_id FROM entity_mentions WHERE entity = ? AND agent_id = ?').all(resolved, agentId)
|
|
1398
|
+
: this.db.prepare('SELECT engram_id FROM entity_mentions WHERE entity = ?').all(resolved);
|
|
1399
|
+
return rows.map(r => r.engram_id);
|
|
1400
|
+
}
|
|
1401
|
+
/** Distinct agent ids present in this store (D3 whoami; includes all stages). */
|
|
1402
|
+
listAgentIds() {
|
|
1403
|
+
const rows = this.db.prepare('SELECT DISTINCT agent_id FROM engrams ORDER BY agent_id').all();
|
|
1404
|
+
return rows.map(r => r.agent_id);
|
|
1405
|
+
}
|
|
1338
1406
|
getCheckpoint(agentId) {
|
|
1339
1407
|
const row = this.db.prepare('SELECT * FROM conscious_state WHERE agent_id = ?').get(agentId);
|
|
1340
1408
|
if (!row)
|
|
@@ -1359,20 +1427,20 @@ export class EngramStore {
|
|
|
1359
1427
|
markConsolidation(agentId, mini) {
|
|
1360
1428
|
const now = new Date().toISOString();
|
|
1361
1429
|
if (mini) {
|
|
1362
|
-
this.db.prepare(`
|
|
1363
|
-
UPDATE conscious_state SET last_mini_consolidation_at = ?, updated_at = ? WHERE agent_id = ?
|
|
1430
|
+
this.db.prepare(`
|
|
1431
|
+
UPDATE conscious_state SET last_mini_consolidation_at = ?, updated_at = ? WHERE agent_id = ?
|
|
1364
1432
|
`).run(now, now, agentId);
|
|
1365
1433
|
}
|
|
1366
1434
|
else {
|
|
1367
|
-
this.db.prepare(`
|
|
1368
|
-
UPDATE conscious_state SET
|
|
1369
|
-
last_consolidation_at = ?,
|
|
1370
|
-
last_mini_consolidation_at = ?,
|
|
1371
|
-
write_count_since_consolidation = 0,
|
|
1372
|
-
recall_count_since_consolidation = 0,
|
|
1373
|
-
consolidation_cycle_count = consolidation_cycle_count + 1,
|
|
1374
|
-
updated_at = ?
|
|
1375
|
-
WHERE agent_id = ?
|
|
1435
|
+
this.db.prepare(`
|
|
1436
|
+
UPDATE conscious_state SET
|
|
1437
|
+
last_consolidation_at = ?,
|
|
1438
|
+
last_mini_consolidation_at = ?,
|
|
1439
|
+
write_count_since_consolidation = 0,
|
|
1440
|
+
recall_count_since_consolidation = 0,
|
|
1441
|
+
consolidation_cycle_count = consolidation_cycle_count + 1,
|
|
1442
|
+
updated_at = ?
|
|
1443
|
+
WHERE agent_id = ?
|
|
1376
1444
|
`).run(now, now, now, agentId);
|
|
1377
1445
|
}
|
|
1378
1446
|
}
|
|
@@ -1443,10 +1511,10 @@ export class EngramStore {
|
|
|
1443
1511
|
* sequence are excluded (they have no story-time anchor).
|
|
1444
1512
|
*/
|
|
1445
1513
|
getLatestByTag(opts) {
|
|
1446
|
-
let sql = `SELECT * FROM engrams
|
|
1447
|
-
WHERE agent_id = ?
|
|
1448
|
-
AND retracted = ?
|
|
1449
|
-
AND stage = 'active'
|
|
1514
|
+
let sql = `SELECT * FROM engrams
|
|
1515
|
+
WHERE agent_id = ?
|
|
1516
|
+
AND retracted = ?
|
|
1517
|
+
AND stage = 'active'
|
|
1450
1518
|
AND tags LIKE ?`;
|
|
1451
1519
|
const params = [
|
|
1452
1520
|
opts.agentId,
|
|
@@ -1486,10 +1554,10 @@ export class EngramStore {
|
|
|
1486
1554
|
*/
|
|
1487
1555
|
getTopBy(opts) {
|
|
1488
1556
|
// Build the base query inline (mirrors search() but adds the topBy sort).
|
|
1489
|
-
let sql = `SELECT * FROM engrams
|
|
1490
|
-
WHERE agent_id = ?
|
|
1491
|
-
AND retracted = ?
|
|
1492
|
-
AND stage = 'active'
|
|
1557
|
+
let sql = `SELECT * FROM engrams
|
|
1558
|
+
WHERE agent_id = ?
|
|
1559
|
+
AND retracted = ?
|
|
1560
|
+
AND stage = 'active'
|
|
1493
1561
|
AND tags LIKE ?`;
|
|
1494
1562
|
const params = [
|
|
1495
1563
|
opts.agentId,
|
|
@@ -1557,11 +1625,11 @@ export class EngramStore {
|
|
|
1557
1625
|
// Find candidate referencing engrams. LIKE-filter first to narrow,
|
|
1558
1626
|
// then JSON.parse each candidate's references_json to verify the
|
|
1559
1627
|
// matchEngramId and type. Cheap enough at NovelForge's scale.
|
|
1560
|
-
const candidates = this.db.prepare(`SELECT id, references_json, created_at FROM engrams
|
|
1561
|
-
WHERE agent_id = ?
|
|
1562
|
-
AND retracted = 0
|
|
1563
|
-
AND stage = 'active'
|
|
1564
|
-
AND references_json IS NOT NULL
|
|
1628
|
+
const candidates = this.db.prepare(`SELECT id, references_json, created_at FROM engrams
|
|
1629
|
+
WHERE agent_id = ?
|
|
1630
|
+
AND retracted = 0
|
|
1631
|
+
AND stage = 'active'
|
|
1632
|
+
AND references_json IS NOT NULL
|
|
1565
1633
|
AND references_json LIKE ?`).all(target.agentId, `%"${targetEngramId}"%`);
|
|
1566
1634
|
const terminalTypes = new Set(['resolves', 'subverts', 'abandons']);
|
|
1567
1635
|
const resolvingEvents = [];
|