agent-working-memory 0.17.0 → 0.18.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/README.md +321 -319
- package/dist/adapters/common.d.ts +34 -0
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +528 -446
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +19 -0
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +31 -29
- package/dist/cli/migrate.js.map +1 -1
- package/dist/core/asked-value.d.ts +37 -0
- package/dist/core/asked-value.d.ts.map +1 -0
- package/dist/core/asked-value.js +77 -0
- package/dist/core/asked-value.js.map +1 -0
- package/dist/core/conflicts.d.ts +89 -0
- package/dist/core/conflicts.d.ts.map +1 -0
- package/dist/core/conflicts.js +267 -0
- package/dist/core/conflicts.js.map +1 -0
- package/dist/core/embeddings.d.ts +14 -2
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +32 -10
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/format-recall.d.ts +21 -1
- package/dist/core/format-recall.d.ts.map +1 -1
- package/dist/core/format-recall.js +60 -5
- package/dist/core/format-recall.js.map +1 -1
- package/dist/core/ml-worker.d.ts +24 -1
- package/dist/core/ml-worker.d.ts.map +1 -1
- package/dist/core/ml-worker.js +165 -4
- package/dist/core/ml-worker.js.map +1 -1
- package/dist/core/model-daemon-client.d.ts +77 -0
- package/dist/core/model-daemon-client.d.ts.map +1 -0
- package/dist/core/model-daemon-client.js +383 -0
- package/dist/core/model-daemon-client.js.map +1 -0
- package/dist/core/model-daemon-server.d.ts +71 -0
- package/dist/core/model-daemon-server.d.ts.map +1 -0
- package/dist/core/model-daemon-server.js +363 -0
- package/dist/core/model-daemon-server.js.map +1 -0
- package/dist/core/model-daemon.d.ts +137 -0
- package/dist/core/model-daemon.d.ts.map +1 -0
- package/dist/core/model-daemon.js +231 -0
- package/dist/core/model-daemon.js.map +1 -0
- package/dist/core/query-expander.d.ts +5 -0
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +3 -2
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/recall-config.d.ts +1 -1
- package/dist/core/recall-config.d.ts.map +1 -1
- package/dist/core/recall-config.js +5 -0
- package/dist/core/recall-config.js.map +1 -1
- package/dist/core/rerank-window.d.ts +7 -2
- package/dist/core/rerank-window.d.ts.map +1 -1
- package/dist/core/rerank-window.js +9 -4
- package/dist/core/rerank-window.js.map +1 -1
- package/dist/core/rerank2.d.ts +52 -2
- package/dist/core/rerank2.d.ts.map +1 -1
- package/dist/core/rerank2.js +60 -3
- package/dist/core/rerank2.js.map +1 -1
- package/dist/core/reranker.d.ts +19 -0
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +76 -12
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/trust.d.ts +32 -0
- package/dist/core/trust.d.ts.map +1 -0
- package/dist/core/trust.js +66 -0
- package/dist/core/trust.js.map +1 -0
- package/dist/core/validity.d.ts +21 -0
- package/dist/core/validity.d.ts.map +1 -0
- package/dist/core/validity.js +41 -0
- package/dist/core/validity.js.map +1 -0
- package/dist/core/whoami.d.ts +3 -0
- package/dist/core/whoami.d.ts.map +1 -1
- package/dist/core/whoami.js +14 -0
- package/dist/core/whoami.js.map +1 -1
- package/dist/core/write-pipeline.d.ts +48 -0
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +80 -3
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +27 -4
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +53 -4
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/hooks/prime-query.d.ts +57 -0
- package/dist/hooks/prime-query.d.ts.map +1 -0
- package/dist/hooks/prime-query.js +388 -0
- package/dist/hooks/prime-query.js.map +1 -0
- package/dist/hooks/prime.d.ts +8 -0
- package/dist/hooks/prime.d.ts.map +1 -1
- package/dist/hooks/prime.js +21 -2
- package/dist/hooks/prime.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +7 -0
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +10 -2
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +148 -103
- package/dist/mcp.js.map +1 -1
- package/dist/model-daemon.d.ts +8 -0
- package/dist/model-daemon.d.ts.map +1 -0
- package/dist/model-daemon.js +45 -0
- package/dist/model-daemon.js.map +1 -0
- package/dist/storage/factory.d.ts.map +1 -1
- package/dist/storage/factory.js +5 -0
- package/dist/storage/factory.js.map +1 -1
- package/dist/storage/pglite-schema.d.ts.map +1 -1
- package/dist/storage/pglite-schema.js +176 -170
- 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 +165 -153
- 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 +165 -153
- package/dist/storage/postgres.js.map +1 -1
- package/dist/storage/sqlite.d.ts +22 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +400 -363
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +33 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/README.md +1 -1
- package/plugin/bin/awm-mcp-launcher.cjs +2 -2
- package/plugin/skills/awm-memory/SKILL.md +11 -2
- package/src/adapters/common.ts +881 -802
- package/src/api/routes.ts +20 -0
- package/src/cli/migrate.ts +309 -307
- package/src/core/asked-value.ts +74 -0
- package/src/core/conflicts.ts +280 -0
- package/src/core/embeddings.ts +35 -10
- package/src/core/format-recall.ts +83 -25
- package/src/core/ml-worker.ts +458 -281
- package/src/core/model-daemon-client.ts +374 -0
- package/src/core/model-daemon-server.ts +344 -0
- package/src/core/model-daemon.ts +243 -0
- package/src/core/query-expander.ts +127 -126
- package/src/core/recall-config.ts +5 -0
- package/src/core/rerank-window.ts +164 -159
- package/src/core/rerank2.ts +143 -82
- package/src/core/reranker.ts +78 -11
- package/src/core/trust.ts +65 -0
- package/src/core/validity.ts +47 -0
- package/src/core/whoami.ts +136 -120
- package/src/core/write-pipeline.ts +119 -3
- package/src/engine/activation.ts +27 -4
- package/src/engine/consolidation.ts +950 -899
- package/src/hooks/prime-query.ts +375 -0
- package/src/hooks/prime.ts +25 -2
- package/src/hooks/sidecar.ts +13 -3
- package/src/index.ts +255 -248
- package/src/mcp.ts +1521 -1475
- package/src/model-daemon.ts +48 -0
- package/src/storage/factory.ts +25 -19
- package/src/storage/pglite-schema.ts +199 -193
- package/src/storage/pglite.ts +1513 -1498
- package/src/storage/postgres.ts +1613 -1598
- package/src/storage/sqlite.ts +2056 -2019
- package/src/types/engram.ts +30 -0
package/dist/storage/sqlite.js
CHANGED
|
@@ -154,123 +154,123 @@ 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
|
-
-- Serves getTopAccessedTags: the recall path's pronoun branch needs the ten
|
|
185
|
-
-- most-accessed engrams, and without this it filters then sorts the whole
|
|
186
|
-
-- agent partition on every query containing "it", "this" or "that".
|
|
187
|
-
CREATE INDEX IF NOT EXISTS idx_engrams_access ON engrams(agent_id, stage, access_count DESC);
|
|
188
|
-
|
|
189
|
-
CREATE TABLE IF NOT EXISTS associations (
|
|
190
|
-
id TEXT PRIMARY KEY,
|
|
191
|
-
from_engram_id TEXT NOT NULL REFERENCES engrams(id) ON DELETE CASCADE,
|
|
192
|
-
to_engram_id TEXT NOT NULL REFERENCES engrams(id) ON DELETE CASCADE,
|
|
193
|
-
weight REAL NOT NULL DEFAULT 0.1,
|
|
194
|
-
confidence REAL NOT NULL DEFAULT 0.5,
|
|
195
|
-
type TEXT NOT NULL DEFAULT 'hebbian',
|
|
196
|
-
activation_count INTEGER NOT NULL DEFAULT 0,
|
|
197
|
-
created_at TEXT NOT NULL,
|
|
198
|
-
last_activated TEXT NOT NULL
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
CREATE INDEX IF NOT EXISTS idx_assoc_from ON associations(from_engram_id);
|
|
202
|
-
CREATE INDEX IF NOT EXISTS idx_assoc_to ON associations(to_engram_id);
|
|
203
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_assoc_pair ON associations(from_engram_id, to_engram_id);
|
|
204
|
-
|
|
205
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
206
|
-
id TEXT PRIMARY KEY,
|
|
207
|
-
name TEXT NOT NULL,
|
|
208
|
-
created_at TEXT NOT NULL,
|
|
209
|
-
config TEXT NOT NULL DEFAULT '{}'
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
-- FTS5 for full-text search (BM25 ranking built in)
|
|
213
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS engrams_fts USING fts5(
|
|
214
|
-
concept, content, tags,
|
|
215
|
-
content=engrams,
|
|
216
|
-
content_rowid=rowid
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
-- Triggers to keep FTS in sync
|
|
220
|
-
CREATE TRIGGER IF NOT EXISTS engrams_ai AFTER INSERT ON engrams BEGIN
|
|
221
|
-
INSERT INTO engrams_fts(rowid, concept, content, tags) VALUES (new.rowid, new.concept, new.content, new.tags);
|
|
222
|
-
END;
|
|
223
|
-
CREATE TRIGGER IF NOT EXISTS engrams_ad AFTER DELETE ON engrams BEGIN
|
|
224
|
-
INSERT INTO engrams_fts(engrams_fts, rowid, concept, content, tags) VALUES('delete', old.rowid, old.concept, old.content, old.tags);
|
|
225
|
-
END;
|
|
226
|
-
CREATE TRIGGER IF NOT EXISTS engrams_au AFTER UPDATE ON engrams BEGIN
|
|
227
|
-
INSERT INTO engrams_fts(engrams_fts, rowid, concept, content, tags) VALUES('delete', old.rowid, old.concept, old.content, old.tags);
|
|
228
|
-
INSERT INTO engrams_fts(rowid, concept, content, tags) VALUES (new.rowid, new.concept, new.content, new.tags);
|
|
229
|
-
END;
|
|
230
|
-
|
|
231
|
-
-- Eval event logs
|
|
232
|
-
CREATE TABLE IF NOT EXISTS activation_events (
|
|
233
|
-
id TEXT PRIMARY KEY,
|
|
234
|
-
agent_id TEXT NOT NULL,
|
|
235
|
-
timestamp TEXT NOT NULL,
|
|
236
|
-
context TEXT NOT NULL,
|
|
237
|
-
results_returned INTEGER NOT NULL,
|
|
238
|
-
top_score REAL,
|
|
239
|
-
latency_ms REAL NOT NULL,
|
|
240
|
-
engram_ids TEXT NOT NULL DEFAULT '[]'
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
CREATE TABLE IF NOT EXISTS staging_events (
|
|
244
|
-
engram_id TEXT NOT NULL,
|
|
245
|
-
agent_id TEXT NOT NULL,
|
|
246
|
-
action TEXT NOT NULL,
|
|
247
|
-
resonance_score REAL,
|
|
248
|
-
timestamp TEXT NOT NULL,
|
|
249
|
-
age_ms INTEGER NOT NULL
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
CREATE TABLE IF NOT EXISTS retrieval_feedback (
|
|
253
|
-
id TEXT PRIMARY KEY,
|
|
254
|
-
activation_event_id TEXT,
|
|
255
|
-
engram_id TEXT NOT NULL,
|
|
256
|
-
useful INTEGER NOT NULL,
|
|
257
|
-
context TEXT,
|
|
258
|
-
timestamp TEXT NOT NULL
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
CREATE TABLE IF NOT EXISTS episodes (
|
|
262
|
-
id TEXT PRIMARY KEY,
|
|
263
|
-
agent_id TEXT NOT NULL,
|
|
264
|
-
label TEXT NOT NULL,
|
|
265
|
-
embedding BLOB,
|
|
266
|
-
engram_count INTEGER NOT NULL DEFAULT 0,
|
|
267
|
-
start_time TEXT NOT NULL,
|
|
268
|
-
end_time TEXT NOT NULL,
|
|
269
|
-
created_at TEXT NOT NULL
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
CREATE INDEX IF NOT EXISTS idx_episodes_agent ON episodes(agent_id);
|
|
273
|
-
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
|
+
-- Serves getTopAccessedTags: the recall path's pronoun branch needs the ten
|
|
185
|
+
-- most-accessed engrams, and without this it filters then sorts the whole
|
|
186
|
+
-- agent partition on every query containing "it", "this" or "that".
|
|
187
|
+
CREATE INDEX IF NOT EXISTS idx_engrams_access ON engrams(agent_id, stage, access_count DESC);
|
|
188
|
+
|
|
189
|
+
CREATE TABLE IF NOT EXISTS associations (
|
|
190
|
+
id TEXT PRIMARY KEY,
|
|
191
|
+
from_engram_id TEXT NOT NULL REFERENCES engrams(id) ON DELETE CASCADE,
|
|
192
|
+
to_engram_id TEXT NOT NULL REFERENCES engrams(id) ON DELETE CASCADE,
|
|
193
|
+
weight REAL NOT NULL DEFAULT 0.1,
|
|
194
|
+
confidence REAL NOT NULL DEFAULT 0.5,
|
|
195
|
+
type TEXT NOT NULL DEFAULT 'hebbian',
|
|
196
|
+
activation_count INTEGER NOT NULL DEFAULT 0,
|
|
197
|
+
created_at TEXT NOT NULL,
|
|
198
|
+
last_activated TEXT NOT NULL
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
CREATE INDEX IF NOT EXISTS idx_assoc_from ON associations(from_engram_id);
|
|
202
|
+
CREATE INDEX IF NOT EXISTS idx_assoc_to ON associations(to_engram_id);
|
|
203
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_assoc_pair ON associations(from_engram_id, to_engram_id);
|
|
204
|
+
|
|
205
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
206
|
+
id TEXT PRIMARY KEY,
|
|
207
|
+
name TEXT NOT NULL,
|
|
208
|
+
created_at TEXT NOT NULL,
|
|
209
|
+
config TEXT NOT NULL DEFAULT '{}'
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
-- FTS5 for full-text search (BM25 ranking built in)
|
|
213
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS engrams_fts USING fts5(
|
|
214
|
+
concept, content, tags,
|
|
215
|
+
content=engrams,
|
|
216
|
+
content_rowid=rowid
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
-- Triggers to keep FTS in sync
|
|
220
|
+
CREATE TRIGGER IF NOT EXISTS engrams_ai AFTER INSERT ON engrams BEGIN
|
|
221
|
+
INSERT INTO engrams_fts(rowid, concept, content, tags) VALUES (new.rowid, new.concept, new.content, new.tags);
|
|
222
|
+
END;
|
|
223
|
+
CREATE TRIGGER IF NOT EXISTS engrams_ad AFTER DELETE ON engrams BEGIN
|
|
224
|
+
INSERT INTO engrams_fts(engrams_fts, rowid, concept, content, tags) VALUES('delete', old.rowid, old.concept, old.content, old.tags);
|
|
225
|
+
END;
|
|
226
|
+
CREATE TRIGGER IF NOT EXISTS engrams_au AFTER UPDATE ON engrams BEGIN
|
|
227
|
+
INSERT INTO engrams_fts(engrams_fts, rowid, concept, content, tags) VALUES('delete', old.rowid, old.concept, old.content, old.tags);
|
|
228
|
+
INSERT INTO engrams_fts(rowid, concept, content, tags) VALUES (new.rowid, new.concept, new.content, new.tags);
|
|
229
|
+
END;
|
|
230
|
+
|
|
231
|
+
-- Eval event logs
|
|
232
|
+
CREATE TABLE IF NOT EXISTS activation_events (
|
|
233
|
+
id TEXT PRIMARY KEY,
|
|
234
|
+
agent_id TEXT NOT NULL,
|
|
235
|
+
timestamp TEXT NOT NULL,
|
|
236
|
+
context TEXT NOT NULL,
|
|
237
|
+
results_returned INTEGER NOT NULL,
|
|
238
|
+
top_score REAL,
|
|
239
|
+
latency_ms REAL NOT NULL,
|
|
240
|
+
engram_ids TEXT NOT NULL DEFAULT '[]'
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
CREATE TABLE IF NOT EXISTS staging_events (
|
|
244
|
+
engram_id TEXT NOT NULL,
|
|
245
|
+
agent_id TEXT NOT NULL,
|
|
246
|
+
action TEXT NOT NULL,
|
|
247
|
+
resonance_score REAL,
|
|
248
|
+
timestamp TEXT NOT NULL,
|
|
249
|
+
age_ms INTEGER NOT NULL
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
CREATE TABLE IF NOT EXISTS retrieval_feedback (
|
|
253
|
+
id TEXT PRIMARY KEY,
|
|
254
|
+
activation_event_id TEXT,
|
|
255
|
+
engram_id TEXT NOT NULL,
|
|
256
|
+
useful INTEGER NOT NULL,
|
|
257
|
+
context TEXT,
|
|
258
|
+
timestamp TEXT NOT NULL
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
CREATE TABLE IF NOT EXISTS episodes (
|
|
262
|
+
id TEXT PRIMARY KEY,
|
|
263
|
+
agent_id TEXT NOT NULL,
|
|
264
|
+
label TEXT NOT NULL,
|
|
265
|
+
embedding BLOB,
|
|
266
|
+
engram_count INTEGER NOT NULL DEFAULT 0,
|
|
267
|
+
start_time TEXT NOT NULL,
|
|
268
|
+
end_time TEXT NOT NULL,
|
|
269
|
+
created_at TEXT NOT NULL
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_agent ON episodes(agent_id);
|
|
273
|
+
CREATE INDEX IF NOT EXISTS idx_episodes_time ON episodes(agent_id, end_time);
|
|
274
274
|
`);
|
|
275
275
|
// Migration: add episode_id column if missing
|
|
276
276
|
try {
|
|
@@ -284,10 +284,10 @@ export class EngramStore {
|
|
|
284
284
|
this.db.prepare('SELECT task_status FROM engrams LIMIT 0').get();
|
|
285
285
|
}
|
|
286
286
|
catch {
|
|
287
|
-
this.db.exec(`
|
|
288
|
-
ALTER TABLE engrams ADD COLUMN task_status TEXT;
|
|
289
|
-
ALTER TABLE engrams ADD COLUMN task_priority TEXT;
|
|
290
|
-
ALTER TABLE engrams ADD COLUMN blocked_by TEXT;
|
|
287
|
+
this.db.exec(`
|
|
288
|
+
ALTER TABLE engrams ADD COLUMN task_status TEXT;
|
|
289
|
+
ALTER TABLE engrams ADD COLUMN task_priority TEXT;
|
|
290
|
+
ALTER TABLE engrams ADD COLUMN blocked_by TEXT;
|
|
291
291
|
`);
|
|
292
292
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_engrams_task ON engrams(agent_id, task_status)');
|
|
293
293
|
}
|
|
@@ -296,10 +296,10 @@ export class EngramStore {
|
|
|
296
296
|
this.db.prepare('SELECT memory_class FROM engrams LIMIT 0').get();
|
|
297
297
|
}
|
|
298
298
|
catch {
|
|
299
|
-
this.db.exec(`
|
|
300
|
-
ALTER TABLE engrams ADD COLUMN memory_class TEXT NOT NULL DEFAULT 'working';
|
|
301
|
-
ALTER TABLE engrams ADD COLUMN superseded_by TEXT;
|
|
302
|
-
ALTER TABLE engrams ADD COLUMN supersedes TEXT;
|
|
299
|
+
this.db.exec(`
|
|
300
|
+
ALTER TABLE engrams ADD COLUMN memory_class TEXT NOT NULL DEFAULT 'working';
|
|
301
|
+
ALTER TABLE engrams ADD COLUMN superseded_by TEXT;
|
|
302
|
+
ALTER TABLE engrams ADD COLUMN supersedes TEXT;
|
|
303
303
|
`);
|
|
304
304
|
}
|
|
305
305
|
// Migration: add embedding_model for version tracking (prevents drift on model change)
|
|
@@ -311,49 +311,57 @@ export class EngramStore {
|
|
|
311
311
|
}
|
|
312
312
|
// Entity inverted index + alias table (D9, 2026-07-30) — write-time
|
|
313
313
|
// bookkeeping only; retrieval unchanged until D11. Idempotent CREATEs.
|
|
314
|
-
this.db.exec(`
|
|
315
|
-
CREATE TABLE IF NOT EXISTS entity_mentions (
|
|
316
|
-
entity TEXT NOT NULL,
|
|
317
|
-
engram_id TEXT NOT NULL,
|
|
318
|
-
agent_id TEXT NOT NULL,
|
|
319
|
-
PRIMARY KEY (entity, engram_id)
|
|
320
|
-
);
|
|
321
|
-
CREATE INDEX IF NOT EXISTS idx_entity_mentions_agent ON entity_mentions(agent_id, entity);
|
|
322
|
-
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
323
|
-
alias TEXT PRIMARY KEY,
|
|
324
|
-
entity TEXT NOT NULL
|
|
325
|
-
);
|
|
314
|
+
this.db.exec(`
|
|
315
|
+
CREATE TABLE IF NOT EXISTS entity_mentions (
|
|
316
|
+
entity TEXT NOT NULL,
|
|
317
|
+
engram_id TEXT NOT NULL,
|
|
318
|
+
agent_id TEXT NOT NULL,
|
|
319
|
+
PRIMARY KEY (entity, engram_id)
|
|
320
|
+
);
|
|
321
|
+
CREATE INDEX IF NOT EXISTS idx_entity_mentions_agent ON entity_mentions(agent_id, entity);
|
|
322
|
+
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
323
|
+
alias TEXT PRIMARY KEY,
|
|
324
|
+
entity TEXT NOT NULL
|
|
325
|
+
);
|
|
326
326
|
`);
|
|
327
327
|
// Migration: memory-spine provenance + temporal validity (D5/D8, 2026-07-30)
|
|
328
328
|
try {
|
|
329
329
|
this.db.prepare('SELECT origin_class FROM engrams LIMIT 0').get();
|
|
330
330
|
}
|
|
331
331
|
catch {
|
|
332
|
-
this.db.exec(`
|
|
333
|
-
ALTER TABLE engrams ADD COLUMN origin_class TEXT;
|
|
334
|
-
ALTER TABLE engrams ADD COLUMN writer_session TEXT;
|
|
335
|
-
ALTER TABLE engrams ADD COLUMN recipe_id TEXT;
|
|
336
|
-
ALTER TABLE engrams ADD COLUMN valid_from TEXT;
|
|
337
|
-
ALTER TABLE engrams ADD COLUMN valid_to TEXT;
|
|
332
|
+
this.db.exec(`
|
|
333
|
+
ALTER TABLE engrams ADD COLUMN origin_class TEXT;
|
|
334
|
+
ALTER TABLE engrams ADD COLUMN writer_session TEXT;
|
|
335
|
+
ALTER TABLE engrams ADD COLUMN recipe_id TEXT;
|
|
336
|
+
ALTER TABLE engrams ADD COLUMN valid_from TEXT;
|
|
337
|
+
ALTER TABLE engrams ADD COLUMN valid_to TEXT;
|
|
338
338
|
`);
|
|
339
339
|
}
|
|
340
|
+
// Migration: stage-transition timestamp (A-4, 2026-10-12). Additive, nullable,
|
|
341
|
+
// no backfill — null means "never transitioned", so readers fall back to created_at.
|
|
342
|
+
try {
|
|
343
|
+
this.db.prepare('SELECT stage_changed_at FROM engrams LIMIT 0').get();
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
this.db.exec(`ALTER TABLE engrams ADD COLUMN stage_changed_at TEXT`);
|
|
347
|
+
}
|
|
340
348
|
// Migration: add conscious_state table for checkpointing
|
|
341
|
-
this.db.exec(`
|
|
342
|
-
CREATE TABLE IF NOT EXISTS conscious_state (
|
|
343
|
-
agent_id TEXT PRIMARY KEY,
|
|
344
|
-
last_write_id TEXT,
|
|
345
|
-
last_recall_context TEXT,
|
|
346
|
-
last_recall_ids TEXT NOT NULL DEFAULT '[]',
|
|
347
|
-
last_activity_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
348
|
-
write_count_since_consolidation INTEGER NOT NULL DEFAULT 0,
|
|
349
|
-
recall_count_since_consolidation INTEGER NOT NULL DEFAULT 0,
|
|
350
|
-
execution_state TEXT,
|
|
351
|
-
checkpoint_at TEXT,
|
|
352
|
-
last_consolidation_at TEXT,
|
|
353
|
-
last_mini_consolidation_at TEXT,
|
|
354
|
-
consolidation_cycle_count INTEGER NOT NULL DEFAULT 0,
|
|
355
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
356
|
-
)
|
|
349
|
+
this.db.exec(`
|
|
350
|
+
CREATE TABLE IF NOT EXISTS conscious_state (
|
|
351
|
+
agent_id TEXT PRIMARY KEY,
|
|
352
|
+
last_write_id TEXT,
|
|
353
|
+
last_recall_context TEXT,
|
|
354
|
+
last_recall_ids TEXT NOT NULL DEFAULT '[]',
|
|
355
|
+
last_activity_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
356
|
+
write_count_since_consolidation INTEGER NOT NULL DEFAULT 0,
|
|
357
|
+
recall_count_since_consolidation INTEGER NOT NULL DEFAULT 0,
|
|
358
|
+
execution_state TEXT,
|
|
359
|
+
checkpoint_at TEXT,
|
|
360
|
+
last_consolidation_at TEXT,
|
|
361
|
+
last_mini_consolidation_at TEXT,
|
|
362
|
+
consolidation_cycle_count INTEGER NOT NULL DEFAULT 0,
|
|
363
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
364
|
+
)
|
|
357
365
|
`);
|
|
358
366
|
// Migration: add consolidation_cycle_count if missing (existing DBs)
|
|
359
367
|
try {
|
|
@@ -374,7 +382,7 @@ export class EngramStore {
|
|
|
374
382
|
}
|
|
375
383
|
catch {
|
|
376
384
|
this.db.exec(`ALTER TABLE engrams ADD COLUMN sequence INTEGER`);
|
|
377
|
-
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_engrams_agent_sequence
|
|
385
|
+
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_engrams_agent_sequence
|
|
378
386
|
ON engrams(agent_id, sequence) WHERE sequence IS NOT NULL`);
|
|
379
387
|
}
|
|
380
388
|
// Migration (0.8 Cluster A): references_json column for typed cross-record
|
|
@@ -393,12 +401,12 @@ export class EngramStore {
|
|
|
393
401
|
const embeddingBlob = input.embedding
|
|
394
402
|
? Buffer.from(new Float32Array(input.embedding).buffer)
|
|
395
403
|
: null;
|
|
396
|
-
this.db.prepare(`
|
|
397
|
-
INSERT INTO engrams (id, agent_id, concept, content, embedding, confidence, salience,
|
|
398
|
-
access_count, last_accessed, created_at, salience_features, reason_codes, stage, tags, episode_id,
|
|
399
|
-
ttl, memory_class, supersedes, task_status, task_priority, blocked_by, memory_type,
|
|
400
|
-
sequence, references_json, origin_class, writer_session, recipe_id, valid_from, valid_to)
|
|
401
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
404
|
+
this.db.prepare(`
|
|
405
|
+
INSERT INTO engrams (id, agent_id, concept, content, embedding, confidence, salience,
|
|
406
|
+
access_count, last_accessed, created_at, salience_features, reason_codes, stage, tags, episode_id,
|
|
407
|
+
ttl, memory_class, supersedes, task_status, task_priority, blocked_by, memory_type,
|
|
408
|
+
sequence, references_json, origin_class, writer_session, recipe_id, valid_from, valid_to)
|
|
409
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
402
410
|
`).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
|
|
403
411
|
? JSON.stringify(input.references) : null, input.originClass ?? null, input.writerSession ?? null, input.recipeId ?? null, input.validFrom ?? null, input.validTo ?? null);
|
|
404
412
|
// Add to slim cache (skip if not yet populated — first slim fetch will load it)
|
|
@@ -578,11 +586,11 @@ export class EngramStore {
|
|
|
578
586
|
getTopAccessedTags(agentIds, topN, stage = 'active') {
|
|
579
587
|
if (agentIds.length === 0 || topN <= 0)
|
|
580
588
|
return [];
|
|
581
|
-
const stmt = this.db.prepare(`
|
|
582
|
-
SELECT tags, access_count AS accessCount, id FROM engrams
|
|
583
|
-
WHERE agent_id = ? AND stage = ? AND retracted = 0
|
|
584
|
-
ORDER BY access_count DESC, id
|
|
585
|
-
LIMIT ?
|
|
589
|
+
const stmt = this.db.prepare(`
|
|
590
|
+
SELECT tags, access_count AS accessCount, id FROM engrams
|
|
591
|
+
WHERE agent_id = ? AND stage = ? AND retracted = 0
|
|
592
|
+
ORDER BY access_count DESC, id
|
|
593
|
+
LIMIT ?
|
|
586
594
|
`);
|
|
587
595
|
const rows = [];
|
|
588
596
|
for (const agentId of agentIds) {
|
|
@@ -620,15 +628,15 @@ export class EngramStore {
|
|
|
620
628
|
try {
|
|
621
629
|
const placeholders = agentIds.map(() => '?').join(',');
|
|
622
630
|
const innerLimit = Math.max(limit * 5, 50);
|
|
623
|
-
const rows = this.db.prepare(`
|
|
624
|
-
WITH top_fts AS (
|
|
625
|
-
SELECT rowid, rank FROM engrams_fts WHERE engrams_fts MATCH ? ORDER BY rank LIMIT ?
|
|
626
|
-
)
|
|
627
|
-
SELECT e.*, top_fts.rank FROM top_fts
|
|
628
|
-
JOIN engrams e ON e.rowid = top_fts.rowid
|
|
629
|
-
WHERE e.agent_id IN (${placeholders}) AND e.retracted = 0
|
|
630
|
-
ORDER BY top_fts.rank
|
|
631
|
-
LIMIT ?
|
|
631
|
+
const rows = this.db.prepare(`
|
|
632
|
+
WITH top_fts AS (
|
|
633
|
+
SELECT rowid, rank FROM engrams_fts WHERE engrams_fts MATCH ? ORDER BY rank LIMIT ?
|
|
634
|
+
)
|
|
635
|
+
SELECT e.*, top_fts.rank FROM top_fts
|
|
636
|
+
JOIN engrams e ON e.rowid = top_fts.rowid
|
|
637
|
+
WHERE e.agent_id IN (${placeholders}) AND e.retracted = 0
|
|
638
|
+
ORDER BY top_fts.rank
|
|
639
|
+
LIMIT ?
|
|
632
640
|
`).all(sanitized, innerLimit, ...agentIds, limit);
|
|
633
641
|
return rows.map(r => ({
|
|
634
642
|
engram: this.rowToEngram(r),
|
|
@@ -667,16 +675,22 @@ export class EngramStore {
|
|
|
667
675
|
* Boost: +0.02 per access, scaled by 1/sqrt(accessCount+1), capped at 0.85.
|
|
668
676
|
*/
|
|
669
677
|
touchEngram(id) {
|
|
670
|
-
this.db.prepare(`
|
|
671
|
-
UPDATE engrams
|
|
672
|
-
SET access_count = access_count + 1,
|
|
673
|
-
last_accessed = ?,
|
|
674
|
-
confidence = MIN(0.85, confidence + 0.02 / (1.0 + sqrt(access_count)))
|
|
675
|
-
WHERE id = ?
|
|
678
|
+
this.db.prepare(`
|
|
679
|
+
UPDATE engrams
|
|
680
|
+
SET access_count = access_count + 1,
|
|
681
|
+
last_accessed = ?,
|
|
682
|
+
confidence = MIN(0.85, confidence + 0.02 / (1.0 + sqrt(access_count)))
|
|
683
|
+
WHERE id = ?
|
|
676
684
|
`).run(new Date().toISOString(), id);
|
|
677
685
|
}
|
|
686
|
+
/**
|
|
687
|
+
* Move an engram to a new stage, recording WHEN. The timestamp is written here
|
|
688
|
+
* rather than at the six consolidation call sites so a future transition cannot
|
|
689
|
+
* forget it; see `Engram.stageChangedAt` for why the column exists.
|
|
690
|
+
*/
|
|
678
691
|
updateStage(id, stage) {
|
|
679
|
-
this.db.prepare('UPDATE engrams SET stage = ? WHERE id = ?')
|
|
692
|
+
this.db.prepare('UPDATE engrams SET stage = ?, stage_changed_at = ? WHERE id = ?')
|
|
693
|
+
.run(stage, new Date().toISOString(), id);
|
|
680
694
|
this.cacheUpdateStage(id, stage);
|
|
681
695
|
}
|
|
682
696
|
/**
|
|
@@ -702,13 +716,35 @@ export class EngramStore {
|
|
|
702
716
|
this.cacheUpdateEmbedding(id, embedding);
|
|
703
717
|
}
|
|
704
718
|
retractEngram(id, retractedBy) {
|
|
705
|
-
this.db.prepare(`
|
|
706
|
-
UPDATE engrams SET retracted = 1, retracted_by = ?, retracted_at = ? WHERE id = ?
|
|
719
|
+
this.db.prepare(`
|
|
720
|
+
UPDATE engrams SET retracted = 1, retracted_by = ?, retracted_at = ? WHERE id = ?
|
|
707
721
|
`).run(retractedBy, new Date().toISOString(), id);
|
|
708
722
|
this.cacheRetract(id);
|
|
709
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* Delete an engram and everything that points at it.
|
|
726
|
+
*
|
|
727
|
+
* `associations` cascades (its FKs declare ON DELETE CASCADE), but
|
|
728
|
+
* `retrieval_feedback`, `entity_mentions` and `staging_events` carry a bare
|
|
729
|
+
* `engram_id TEXT` with no foreign key, so deleting an engram used to strand their
|
|
730
|
+
* rows. Measured on the live store 2026-10-12: **571 orphaned retrieval_feedback
|
|
731
|
+
* rows and 3 orphaned entity_mentions**, present in the pre-purge backup too, so
|
|
732
|
+
* they accumulated over time rather than in one incident. Inert for recall, but
|
|
733
|
+
* they corrupt any join that assumes referential integrity — including the
|
|
734
|
+
* feedback-weighted scoring channel's own counts.
|
|
735
|
+
*
|
|
736
|
+
* One transaction, so a crash cannot leave the half-deleted state this is fixing.
|
|
737
|
+
* `activation_events` is deliberately NOT touched: its `engram_ids` is a JSON array
|
|
738
|
+
* describing a recall that really happened, and editing history to remove one id
|
|
739
|
+
* would falsify the event rather than tidy it.
|
|
740
|
+
*/
|
|
710
741
|
deleteEngram(id) {
|
|
711
|
-
this.db.
|
|
742
|
+
this.db.transaction(() => {
|
|
743
|
+
this.db.prepare('DELETE FROM retrieval_feedback WHERE engram_id = ?').run(id);
|
|
744
|
+
this.db.prepare('DELETE FROM entity_mentions WHERE engram_id = ?').run(id);
|
|
745
|
+
this.db.prepare('DELETE FROM staging_events WHERE engram_id = ?').run(id);
|
|
746
|
+
this.db.prepare('DELETE FROM engrams WHERE id = ?').run(id);
|
|
747
|
+
})();
|
|
712
748
|
this.cacheRemove(id);
|
|
713
749
|
}
|
|
714
750
|
/**
|
|
@@ -720,20 +756,20 @@ export class EngramStore {
|
|
|
720
756
|
let count = 0;
|
|
721
757
|
const shiftSec = Math.round(ms / 1000);
|
|
722
758
|
// Shift engram timestamps
|
|
723
|
-
const r1 = this.db.prepare(`
|
|
724
|
-
UPDATE engrams SET
|
|
725
|
-
created_at = datetime(created_at, '-${shiftSec} seconds'),
|
|
726
|
-
last_accessed = datetime(last_accessed, '-${shiftSec} seconds')
|
|
727
|
-
WHERE agent_id = ?
|
|
759
|
+
const r1 = this.db.prepare(`
|
|
760
|
+
UPDATE engrams SET
|
|
761
|
+
created_at = datetime(created_at, '-${shiftSec} seconds'),
|
|
762
|
+
last_accessed = datetime(last_accessed, '-${shiftSec} seconds')
|
|
763
|
+
WHERE agent_id = ?
|
|
728
764
|
`).run(agentId);
|
|
729
765
|
count += r1.changes;
|
|
730
766
|
// Shift association timestamps
|
|
731
|
-
const r2 = this.db.prepare(`
|
|
732
|
-
UPDATE associations SET
|
|
733
|
-
created_at = datetime(created_at, '-${shiftSec} seconds'),
|
|
734
|
-
last_activated = datetime(last_activated, '-${shiftSec} seconds')
|
|
735
|
-
WHERE from_engram_id IN (SELECT id FROM engrams WHERE agent_id = ?)
|
|
736
|
-
OR to_engram_id IN (SELECT id FROM engrams WHERE agent_id = ?)
|
|
767
|
+
const r2 = this.db.prepare(`
|
|
768
|
+
UPDATE associations SET
|
|
769
|
+
created_at = datetime(created_at, '-${shiftSec} seconds'),
|
|
770
|
+
last_activated = datetime(last_activated, '-${shiftSec} seconds')
|
|
771
|
+
WHERE from_engram_id IN (SELECT id FROM engrams WHERE agent_id = ?)
|
|
772
|
+
OR to_engram_id IN (SELECT id FROM engrams WHERE agent_id = ?)
|
|
737
773
|
`).run(agentId, agentId);
|
|
738
774
|
count += r2.changes;
|
|
739
775
|
return count;
|
|
@@ -812,15 +848,15 @@ export class EngramStore {
|
|
|
812
848
|
// dominant, low retracted rate).
|
|
813
849
|
try {
|
|
814
850
|
const innerLimit = Math.max(limit * 5, 50);
|
|
815
|
-
const rows = this.db.prepare(`
|
|
816
|
-
WITH top_fts AS (
|
|
817
|
-
SELECT rowid, rank FROM engrams_fts WHERE engrams_fts MATCH ? ORDER BY rank LIMIT ?
|
|
818
|
-
)
|
|
819
|
-
SELECT e.*, top_fts.rank FROM top_fts
|
|
820
|
-
JOIN engrams e ON e.rowid = top_fts.rowid
|
|
821
|
-
WHERE e.agent_id = ? AND e.retracted = 0
|
|
822
|
-
ORDER BY top_fts.rank
|
|
823
|
-
LIMIT ?
|
|
851
|
+
const rows = this.db.prepare(`
|
|
852
|
+
WITH top_fts AS (
|
|
853
|
+
SELECT rowid, rank FROM engrams_fts WHERE engrams_fts MATCH ? ORDER BY rank LIMIT ?
|
|
854
|
+
)
|
|
855
|
+
SELECT e.*, top_fts.rank FROM top_fts
|
|
856
|
+
JOIN engrams e ON e.rowid = top_fts.rowid
|
|
857
|
+
WHERE e.agent_id = ? AND e.retracted = 0
|
|
858
|
+
ORDER BY top_fts.rank
|
|
859
|
+
LIMIT ?
|
|
824
860
|
`).all(sanitized, innerLimit, agentId, limit);
|
|
825
861
|
return rows.map(r => ({
|
|
826
862
|
engram: this.rowToEngram(r),
|
|
@@ -940,14 +976,14 @@ export class EngramStore {
|
|
|
940
976
|
sql += ' AND task_status = ?';
|
|
941
977
|
params.push(status);
|
|
942
978
|
}
|
|
943
|
-
sql += ` ORDER BY
|
|
944
|
-
CASE task_priority
|
|
945
|
-
WHEN 'urgent' THEN 0
|
|
946
|
-
WHEN 'high' THEN 1
|
|
947
|
-
WHEN 'medium' THEN 2
|
|
948
|
-
WHEN 'low' THEN 3
|
|
949
|
-
ELSE 4
|
|
950
|
-
END,
|
|
979
|
+
sql += ` ORDER BY
|
|
980
|
+
CASE task_priority
|
|
981
|
+
WHEN 'urgent' THEN 0
|
|
982
|
+
WHEN 'high' THEN 1
|
|
983
|
+
WHEN 'medium' THEN 2
|
|
984
|
+
WHEN 'low' THEN 3
|
|
985
|
+
ELSE 4
|
|
986
|
+
END,
|
|
951
987
|
created_at DESC`;
|
|
952
988
|
return this.db.prepare(sql).all(...params).map(r => this.rowToEngram(r));
|
|
953
989
|
}
|
|
@@ -955,20 +991,20 @@ export class EngramStore {
|
|
|
955
991
|
* Get the next actionable task — highest priority that's not blocked or done.
|
|
956
992
|
*/
|
|
957
993
|
getNextTask(agentId) {
|
|
958
|
-
const row = this.db.prepare(`
|
|
959
|
-
SELECT * FROM engrams
|
|
960
|
-
WHERE agent_id = ? AND task_status IN ('open', 'in_progress') AND retracted = 0
|
|
961
|
-
ORDER BY
|
|
962
|
-
CASE task_status WHEN 'in_progress' THEN 0 ELSE 1 END,
|
|
963
|
-
CASE task_priority
|
|
964
|
-
WHEN 'urgent' THEN 0
|
|
965
|
-
WHEN 'high' THEN 1
|
|
966
|
-
WHEN 'medium' THEN 2
|
|
967
|
-
WHEN 'low' THEN 3
|
|
968
|
-
ELSE 4
|
|
969
|
-
END,
|
|
970
|
-
created_at ASC
|
|
971
|
-
LIMIT 1
|
|
994
|
+
const row = this.db.prepare(`
|
|
995
|
+
SELECT * FROM engrams
|
|
996
|
+
WHERE agent_id = ? AND task_status IN ('open', 'in_progress') AND retracted = 0
|
|
997
|
+
ORDER BY
|
|
998
|
+
CASE task_status WHEN 'in_progress' THEN 0 ELSE 1 END,
|
|
999
|
+
CASE task_priority
|
|
1000
|
+
WHEN 'urgent' THEN 0
|
|
1001
|
+
WHEN 'high' THEN 1
|
|
1002
|
+
WHEN 'medium' THEN 2
|
|
1003
|
+
WHEN 'low' THEN 3
|
|
1004
|
+
ELSE 4
|
|
1005
|
+
END,
|
|
1006
|
+
created_at ASC
|
|
1007
|
+
LIMIT 1
|
|
972
1008
|
`).get(agentId);
|
|
973
1009
|
return row ? this.rowToEngram(row) : null;
|
|
974
1010
|
}
|
|
@@ -990,11 +1026,11 @@ export class EngramStore {
|
|
|
990
1026
|
* Returns null if no match.
|
|
991
1027
|
*/
|
|
992
1028
|
findActiveMatchByConcept(agentId, concept, requiredTags) {
|
|
993
|
-
let sql = `SELECT * FROM engrams
|
|
994
|
-
WHERE agent_id = ?
|
|
995
|
-
AND LOWER(TRIM(concept)) = LOWER(TRIM(?))
|
|
996
|
-
AND stage = 'active'
|
|
997
|
-
AND retracted = 0
|
|
1029
|
+
let sql = `SELECT * FROM engrams
|
|
1030
|
+
WHERE agent_id = ?
|
|
1031
|
+
AND LOWER(TRIM(concept)) = LOWER(TRIM(?))
|
|
1032
|
+
AND stage = 'active'
|
|
1033
|
+
AND retracted = 0
|
|
998
1034
|
AND superseded_by IS NULL`;
|
|
999
1035
|
const params = [agentId, concept];
|
|
1000
1036
|
if (requiredTags && requiredTags.length > 0) {
|
|
@@ -1029,11 +1065,11 @@ export class EngramStore {
|
|
|
1029
1065
|
upsertAssociation(fromId, toId, weight, type = 'hebbian', confidence = 0.5) {
|
|
1030
1066
|
const now = new Date().toISOString();
|
|
1031
1067
|
const id = randomUUID();
|
|
1032
|
-
this.db.prepare(`
|
|
1033
|
-
INSERT INTO associations (id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated)
|
|
1034
|
-
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)
|
|
1035
|
-
ON CONFLICT(from_engram_id, to_engram_id) DO UPDATE SET
|
|
1036
|
-
weight = ?, confidence = ?, last_activated = ?, activation_count = activation_count + 1
|
|
1068
|
+
this.db.prepare(`
|
|
1069
|
+
INSERT INTO associations (id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated)
|
|
1070
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)
|
|
1071
|
+
ON CONFLICT(from_engram_id, to_engram_id) DO UPDATE SET
|
|
1072
|
+
weight = ?, confidence = ?, last_activated = ?, activation_count = activation_count + 1
|
|
1037
1073
|
`).run(id, fromId, toId, weight, confidence, type, now, now, weight, confidence, now);
|
|
1038
1074
|
return this.getAssociation(fromId, toId);
|
|
1039
1075
|
}
|
|
@@ -1073,12 +1109,12 @@ export class EngramStore {
|
|
|
1073
1109
|
// associations under both their from and to engram. Self-loops would be
|
|
1074
1110
|
// double-counted, but they're rare in practice and the prior code handled them
|
|
1075
1111
|
// identically.
|
|
1076
|
-
const rows = this.db.prepare(`SELECT id, SUM(cnt) AS count, SUM(sw) AS sum_weight FROM (
|
|
1077
|
-
SELECT from_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE from_engram_id IN (${placeholders})
|
|
1078
|
-
UNION ALL
|
|
1079
|
-
SELECT to_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE to_engram_id IN (${placeholders})
|
|
1080
|
-
)
|
|
1081
|
-
WHERE id IN (${placeholders})
|
|
1112
|
+
const rows = this.db.prepare(`SELECT id, SUM(cnt) AS count, SUM(sw) AS sum_weight FROM (
|
|
1113
|
+
SELECT from_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE from_engram_id IN (${placeholders})
|
|
1114
|
+
UNION ALL
|
|
1115
|
+
SELECT to_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE to_engram_id IN (${placeholders})
|
|
1116
|
+
)
|
|
1117
|
+
WHERE id IN (${placeholders})
|
|
1082
1118
|
GROUP BY id`).all(...chunk, ...chunk, ...chunk);
|
|
1083
1119
|
for (const r of rows) {
|
|
1084
1120
|
result.set(r.id, { count: r.count, sumWeight: r.sum_weight });
|
|
@@ -1109,7 +1145,7 @@ export class EngramStore {
|
|
|
1109
1145
|
for (let i = 0; i < engramIds.length; i += CHUNK) {
|
|
1110
1146
|
const chunk = engramIds.slice(i, i + CHUNK);
|
|
1111
1147
|
const placeholders = chunk.map(() => '?').join(',');
|
|
1112
|
-
const rows = this.db.prepare(`SELECT * FROM associations
|
|
1148
|
+
const rows = this.db.prepare(`SELECT * FROM associations
|
|
1113
1149
|
WHERE from_engram_id IN (${placeholders}) OR to_engram_id IN (${placeholders})`).all(...chunk, ...chunk);
|
|
1114
1150
|
for (const r of rows) {
|
|
1115
1151
|
const a = this.rowToAssociation(r);
|
|
@@ -1148,22 +1184,22 @@ export class EngramStore {
|
|
|
1148
1184
|
this.db.prepare('DELETE FROM associations WHERE id = ?').run(id);
|
|
1149
1185
|
}
|
|
1150
1186
|
getAllAssociations(agentId) {
|
|
1151
|
-
const rows = this.db.prepare(`
|
|
1152
|
-
SELECT a.* FROM associations a
|
|
1153
|
-
JOIN engrams e ON a.from_engram_id = e.id
|
|
1154
|
-
WHERE e.agent_id = ?
|
|
1187
|
+
const rows = this.db.prepare(`
|
|
1188
|
+
SELECT a.* FROM associations a
|
|
1189
|
+
JOIN engrams e ON a.from_engram_id = e.id
|
|
1190
|
+
WHERE e.agent_id = ?
|
|
1155
1191
|
`).all(agentId);
|
|
1156
1192
|
return rows.map(r => this.rowToAssociation(r));
|
|
1157
1193
|
}
|
|
1158
1194
|
// --- Eviction ---
|
|
1159
1195
|
getEvictionCandidates(agentId, limit) {
|
|
1160
1196
|
// Lowest combined score: low salience + low access + low confidence + oldest
|
|
1161
|
-
const rows = this.db.prepare(`
|
|
1162
|
-
SELECT * FROM engrams
|
|
1163
|
-
WHERE agent_id = ? AND stage = 'active' AND retracted = 0
|
|
1164
|
-
ORDER BY (salience * 0.3 + confidence * 0.3 + (CAST(access_count AS REAL) / (access_count + 5)) * 0.2 +
|
|
1165
|
-
(1.0 / (1.0 + (julianday('now') - julianday(last_accessed)))) * 0.2) ASC
|
|
1166
|
-
LIMIT ?
|
|
1197
|
+
const rows = this.db.prepare(`
|
|
1198
|
+
SELECT * FROM engrams
|
|
1199
|
+
WHERE agent_id = ? AND stage = 'active' AND retracted = 0
|
|
1200
|
+
ORDER BY (salience * 0.3 + confidence * 0.3 + (CAST(access_count AS REAL) / (access_count + 5)) * 0.2 +
|
|
1201
|
+
(1.0 / (1.0 + (julianday('now') - julianday(last_accessed)))) * 0.2) ASC
|
|
1202
|
+
LIMIT ?
|
|
1167
1203
|
`).all(agentId, limit);
|
|
1168
1204
|
return rows.map(r => this.rowToEngram(r));
|
|
1169
1205
|
}
|
|
@@ -1178,8 +1214,8 @@ export class EngramStore {
|
|
|
1178
1214
|
// --- Staging buffer ---
|
|
1179
1215
|
getExpiredStaging() {
|
|
1180
1216
|
const now = Date.now();
|
|
1181
|
-
const rows = this.db.prepare(`
|
|
1182
|
-
SELECT * FROM engrams WHERE stage = 'staging' AND ttl IS NOT NULL
|
|
1217
|
+
const rows = this.db.prepare(`
|
|
1218
|
+
SELECT * FROM engrams WHERE stage = 'staging' AND ttl IS NOT NULL
|
|
1183
1219
|
`).all();
|
|
1184
1220
|
return rows
|
|
1185
1221
|
.map(r => this.rowToEngram(r))
|
|
@@ -1187,53 +1223,53 @@ export class EngramStore {
|
|
|
1187
1223
|
}
|
|
1188
1224
|
// --- Eval event logging ---
|
|
1189
1225
|
logActivationEvent(event) {
|
|
1190
|
-
this.db.prepare(`
|
|
1191
|
-
INSERT INTO activation_events (id, agent_id, timestamp, context, results_returned, top_score, latency_ms, engram_ids)
|
|
1192
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1226
|
+
this.db.prepare(`
|
|
1227
|
+
INSERT INTO activation_events (id, agent_id, timestamp, context, results_returned, top_score, latency_ms, engram_ids)
|
|
1228
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1193
1229
|
`).run(event.id, event.agentId, event.timestamp.toISOString(), event.context, event.resultsReturned, event.topScore, event.latencyMs, JSON.stringify(event.engramIds));
|
|
1194
1230
|
}
|
|
1195
1231
|
logStagingEvent(event) {
|
|
1196
|
-
this.db.prepare(`
|
|
1197
|
-
INSERT INTO staging_events (engram_id, agent_id, action, resonance_score, timestamp, age_ms)
|
|
1198
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
1232
|
+
this.db.prepare(`
|
|
1233
|
+
INSERT INTO staging_events (engram_id, agent_id, action, resonance_score, timestamp, age_ms)
|
|
1234
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1199
1235
|
`).run(event.engramId, event.agentId, event.action, event.resonanceScore, event.timestamp.toISOString(), event.ageMs);
|
|
1200
1236
|
}
|
|
1201
1237
|
logRetrievalFeedback(activationEventId, engramId, useful, context) {
|
|
1202
|
-
this.db.prepare(`
|
|
1203
|
-
INSERT INTO retrieval_feedback (id, activation_event_id, engram_id, useful, context, timestamp)
|
|
1204
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
1238
|
+
this.db.prepare(`
|
|
1239
|
+
INSERT INTO retrieval_feedback (id, activation_event_id, engram_id, useful, context, timestamp)
|
|
1240
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1205
1241
|
`).run(randomUUID(), activationEventId, engramId, useful ? 1 : 0, context, new Date().toISOString());
|
|
1206
1242
|
}
|
|
1207
1243
|
// --- Eval metrics queries ---
|
|
1208
1244
|
getRetrievalPrecision(agentId, windowHours = 24) {
|
|
1209
1245
|
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1210
|
-
const row = this.db.prepare(`
|
|
1211
|
-
SELECT
|
|
1212
|
-
COUNT(CASE WHEN useful = 1 THEN 1 END) as useful_count,
|
|
1213
|
-
COUNT(*) as total_count
|
|
1214
|
-
FROM retrieval_feedback rf
|
|
1215
|
-
LEFT JOIN activation_events ae ON rf.activation_event_id = ae.id
|
|
1216
|
-
JOIN engrams e ON rf.engram_id = e.id
|
|
1217
|
-
WHERE e.agent_id = ? AND rf.timestamp > ?
|
|
1246
|
+
const row = this.db.prepare(`
|
|
1247
|
+
SELECT
|
|
1248
|
+
COUNT(CASE WHEN useful = 1 THEN 1 END) as useful_count,
|
|
1249
|
+
COUNT(*) as total_count
|
|
1250
|
+
FROM retrieval_feedback rf
|
|
1251
|
+
LEFT JOIN activation_events ae ON rf.activation_event_id = ae.id
|
|
1252
|
+
JOIN engrams e ON rf.engram_id = e.id
|
|
1253
|
+
WHERE e.agent_id = ? AND rf.timestamp > ?
|
|
1218
1254
|
`).get(agentId, since);
|
|
1219
1255
|
return row.total_count > 0 ? row.useful_count / row.total_count : 0;
|
|
1220
1256
|
}
|
|
1221
1257
|
getStagingMetrics(agentId) {
|
|
1222
|
-
const row = this.db.prepare(`
|
|
1223
|
-
SELECT
|
|
1224
|
-
COUNT(CASE WHEN action = 'promoted' THEN 1 END) as promoted,
|
|
1225
|
-
COUNT(CASE WHEN action = 'discarded' THEN 1 END) as discarded,
|
|
1226
|
-
COUNT(CASE WHEN action = 'expired' THEN 1 END) as expired
|
|
1227
|
-
FROM staging_events WHERE agent_id = ?
|
|
1258
|
+
const row = this.db.prepare(`
|
|
1259
|
+
SELECT
|
|
1260
|
+
COUNT(CASE WHEN action = 'promoted' THEN 1 END) as promoted,
|
|
1261
|
+
COUNT(CASE WHEN action = 'discarded' THEN 1 END) as discarded,
|
|
1262
|
+
COUNT(CASE WHEN action = 'expired' THEN 1 END) as expired
|
|
1263
|
+
FROM staging_events WHERE agent_id = ?
|
|
1228
1264
|
`).get(agentId);
|
|
1229
1265
|
return { promoted: row.promoted, discarded: row.discarded, expired: row.expired };
|
|
1230
1266
|
}
|
|
1231
1267
|
getActivationStats(agentId, windowHours = 24) {
|
|
1232
1268
|
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1233
|
-
const rows = this.db.prepare(`
|
|
1234
|
-
SELECT latency_ms FROM activation_events
|
|
1235
|
-
WHERE agent_id = ? AND timestamp > ?
|
|
1236
|
-
ORDER BY latency_ms ASC
|
|
1269
|
+
const rows = this.db.prepare(`
|
|
1270
|
+
SELECT latency_ms FROM activation_events
|
|
1271
|
+
WHERE agent_id = ? AND timestamp > ?
|
|
1272
|
+
ORDER BY latency_ms ASC
|
|
1237
1273
|
`).all(agentId, since);
|
|
1238
1274
|
if (rows.length === 0)
|
|
1239
1275
|
return { count: 0, avgLatencyMs: 0, p50LatencyMs: 0, p90LatencyMs: 0, p95LatencyMs: 0 };
|
|
@@ -1258,11 +1294,11 @@ export class EngramStore {
|
|
|
1258
1294
|
*/
|
|
1259
1295
|
getLinkedFeedbackStats(agentId, windowHours = 24 * 7) {
|
|
1260
1296
|
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
1261
|
-
const row = this.db.prepare(`
|
|
1262
|
-
SELECT COUNT(*) AS total, COUNT(CASE WHEN rf.useful = 1 THEN 1 END) AS useful
|
|
1263
|
-
FROM retrieval_feedback rf
|
|
1264
|
-
JOIN activation_events ae ON ae.id = rf.activation_event_id
|
|
1265
|
-
WHERE ae.agent_id = ? AND rf.timestamp > ?
|
|
1297
|
+
const row = this.db.prepare(`
|
|
1298
|
+
SELECT COUNT(*) AS total, COUNT(CASE WHEN rf.useful = 1 THEN 1 END) AS useful
|
|
1299
|
+
FROM retrieval_feedback rf
|
|
1300
|
+
JOIN activation_events ae ON ae.id = rf.activation_event_id
|
|
1301
|
+
WHERE ae.agent_id = ? AND rf.timestamp > ?
|
|
1266
1302
|
`).get(agentId, since);
|
|
1267
1303
|
return { total: row.total, useful: row.useful };
|
|
1268
1304
|
}
|
|
@@ -1308,6 +1344,7 @@ export class EngramStore {
|
|
|
1308
1344
|
recipeId: row.recipe_id ?? null,
|
|
1309
1345
|
validFrom: row.valid_from ?? null,
|
|
1310
1346
|
validTo: row.valid_to ?? null,
|
|
1347
|
+
stageChangedAt: row.stage_changed_at ?? null,
|
|
1311
1348
|
};
|
|
1312
1349
|
}
|
|
1313
1350
|
rowToAssociation(row) {
|
|
@@ -1330,9 +1367,9 @@ export class EngramStore {
|
|
|
1330
1367
|
const embeddingBlob = input.embedding
|
|
1331
1368
|
? Buffer.from(new Float32Array(input.embedding).buffer)
|
|
1332
1369
|
: null;
|
|
1333
|
-
this.db.prepare(`
|
|
1334
|
-
INSERT INTO episodes (id, agent_id, label, embedding, engram_count, start_time, end_time, created_at)
|
|
1335
|
-
VALUES (?, ?, ?, ?, 0, ?, ?, ?)
|
|
1370
|
+
this.db.prepare(`
|
|
1371
|
+
INSERT INTO episodes (id, agent_id, label, embedding, engram_count, start_time, end_time, created_at)
|
|
1372
|
+
VALUES (?, ?, ?, ?, 0, ?, ?, ?)
|
|
1336
1373
|
`).run(id, input.agentId, input.label, embeddingBlob, now, now, now);
|
|
1337
1374
|
return this.getEpisode(id);
|
|
1338
1375
|
}
|
|
@@ -1347,19 +1384,19 @@ export class EngramStore {
|
|
|
1347
1384
|
getActiveEpisode(agentId, windowMs = 3600_000) {
|
|
1348
1385
|
// Find most recent episode that ended within the time window
|
|
1349
1386
|
const cutoff = new Date(Date.now() - windowMs).toISOString();
|
|
1350
|
-
const row = this.db.prepare(`
|
|
1351
|
-
SELECT * FROM episodes WHERE agent_id = ? AND end_time > ?
|
|
1352
|
-
ORDER BY end_time DESC LIMIT 1
|
|
1387
|
+
const row = this.db.prepare(`
|
|
1388
|
+
SELECT * FROM episodes WHERE agent_id = ? AND end_time > ?
|
|
1389
|
+
ORDER BY end_time DESC LIMIT 1
|
|
1353
1390
|
`).get(agentId, cutoff);
|
|
1354
1391
|
return row ? this.rowToEpisode(row) : null;
|
|
1355
1392
|
}
|
|
1356
1393
|
addEngramToEpisode(engramId, episodeId) {
|
|
1357
1394
|
this.db.prepare('UPDATE engrams SET episode_id = ? WHERE id = ?').run(episodeId, engramId);
|
|
1358
|
-
this.db.prepare(`
|
|
1359
|
-
UPDATE episodes SET
|
|
1360
|
-
engram_count = engram_count + 1,
|
|
1361
|
-
end_time = MAX(end_time, ?)
|
|
1362
|
-
WHERE id = ?
|
|
1395
|
+
this.db.prepare(`
|
|
1396
|
+
UPDATE episodes SET
|
|
1397
|
+
engram_count = engram_count + 1,
|
|
1398
|
+
end_time = MAX(end_time, ?)
|
|
1399
|
+
WHERE id = ?
|
|
1363
1400
|
`).run(new Date().toISOString(), episodeId);
|
|
1364
1401
|
}
|
|
1365
1402
|
getEngramsByEpisode(episodeId) {
|
|
@@ -1410,49 +1447,49 @@ export class EngramStore {
|
|
|
1410
1447
|
// --- Checkpointing ---
|
|
1411
1448
|
updateAutoCheckpointWrite(agentId, engramId) {
|
|
1412
1449
|
const now = new Date().toISOString();
|
|
1413
|
-
this.db.prepare(`
|
|
1414
|
-
INSERT INTO conscious_state (agent_id, last_write_id, last_activity_at, write_count_since_consolidation, updated_at)
|
|
1415
|
-
VALUES (?, ?, ?, 1, ?)
|
|
1416
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1417
|
-
last_write_id = excluded.last_write_id,
|
|
1418
|
-
last_activity_at = excluded.last_activity_at,
|
|
1419
|
-
write_count_since_consolidation = write_count_since_consolidation + 1,
|
|
1420
|
-
updated_at = excluded.updated_at
|
|
1450
|
+
this.db.prepare(`
|
|
1451
|
+
INSERT INTO conscious_state (agent_id, last_write_id, last_activity_at, write_count_since_consolidation, updated_at)
|
|
1452
|
+
VALUES (?, ?, ?, 1, ?)
|
|
1453
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1454
|
+
last_write_id = excluded.last_write_id,
|
|
1455
|
+
last_activity_at = excluded.last_activity_at,
|
|
1456
|
+
write_count_since_consolidation = write_count_since_consolidation + 1,
|
|
1457
|
+
updated_at = excluded.updated_at
|
|
1421
1458
|
`).run(agentId, engramId, now, now);
|
|
1422
1459
|
}
|
|
1423
1460
|
updateAutoCheckpointRecall(agentId, context, engramIds) {
|
|
1424
1461
|
const now = new Date().toISOString();
|
|
1425
|
-
this.db.prepare(`
|
|
1426
|
-
INSERT INTO conscious_state (agent_id, last_recall_context, last_recall_ids, last_activity_at, recall_count_since_consolidation, updated_at)
|
|
1427
|
-
VALUES (?, ?, ?, ?, 1, ?)
|
|
1428
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1429
|
-
last_recall_context = excluded.last_recall_context,
|
|
1430
|
-
last_recall_ids = excluded.last_recall_ids,
|
|
1431
|
-
last_activity_at = excluded.last_activity_at,
|
|
1432
|
-
recall_count_since_consolidation = recall_count_since_consolidation + 1,
|
|
1433
|
-
updated_at = excluded.updated_at
|
|
1462
|
+
this.db.prepare(`
|
|
1463
|
+
INSERT INTO conscious_state (agent_id, last_recall_context, last_recall_ids, last_activity_at, recall_count_since_consolidation, updated_at)
|
|
1464
|
+
VALUES (?, ?, ?, ?, 1, ?)
|
|
1465
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1466
|
+
last_recall_context = excluded.last_recall_context,
|
|
1467
|
+
last_recall_ids = excluded.last_recall_ids,
|
|
1468
|
+
last_activity_at = excluded.last_activity_at,
|
|
1469
|
+
recall_count_since_consolidation = recall_count_since_consolidation + 1,
|
|
1470
|
+
updated_at = excluded.updated_at
|
|
1434
1471
|
`).run(agentId, context, JSON.stringify(engramIds), now, now);
|
|
1435
1472
|
}
|
|
1436
1473
|
touchActivity(agentId) {
|
|
1437
1474
|
const now = new Date().toISOString();
|
|
1438
|
-
this.db.prepare(`
|
|
1439
|
-
INSERT INTO conscious_state (agent_id, last_activity_at, updated_at)
|
|
1440
|
-
VALUES (?, ?, ?)
|
|
1441
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1442
|
-
last_activity_at = excluded.last_activity_at,
|
|
1443
|
-
updated_at = excluded.updated_at
|
|
1475
|
+
this.db.prepare(`
|
|
1476
|
+
INSERT INTO conscious_state (agent_id, last_activity_at, updated_at)
|
|
1477
|
+
VALUES (?, ?, ?)
|
|
1478
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1479
|
+
last_activity_at = excluded.last_activity_at,
|
|
1480
|
+
updated_at = excluded.updated_at
|
|
1444
1481
|
`).run(agentId, now, now);
|
|
1445
1482
|
}
|
|
1446
1483
|
saveCheckpoint(agentId, state) {
|
|
1447
1484
|
const now = new Date().toISOString();
|
|
1448
|
-
this.db.prepare(`
|
|
1449
|
-
INSERT INTO conscious_state (agent_id, execution_state, checkpoint_at, last_activity_at, updated_at)
|
|
1450
|
-
VALUES (?, ?, ?, ?, ?)
|
|
1451
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1452
|
-
execution_state = excluded.execution_state,
|
|
1453
|
-
checkpoint_at = excluded.checkpoint_at,
|
|
1454
|
-
last_activity_at = excluded.last_activity_at,
|
|
1455
|
-
updated_at = excluded.updated_at
|
|
1485
|
+
this.db.prepare(`
|
|
1486
|
+
INSERT INTO conscious_state (agent_id, execution_state, checkpoint_at, last_activity_at, updated_at)
|
|
1487
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1488
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
1489
|
+
execution_state = excluded.execution_state,
|
|
1490
|
+
checkpoint_at = excluded.checkpoint_at,
|
|
1491
|
+
last_activity_at = excluded.last_activity_at,
|
|
1492
|
+
updated_at = excluded.updated_at
|
|
1456
1493
|
`).run(agentId, JSON.stringify(state), now, now, now);
|
|
1457
1494
|
}
|
|
1458
1495
|
/** Record entity mentions for an engram (D9 inverted index; idempotent). */
|
|
@@ -1469,10 +1506,10 @@ export class EngramStore {
|
|
|
1469
1506
|
const t = term.toLowerCase().replace(/[%_]/g, '');
|
|
1470
1507
|
if (!t)
|
|
1471
1508
|
return [];
|
|
1472
|
-
const rows = this.db.prepare(`
|
|
1473
|
-
SELECT DISTINCT entity FROM (
|
|
1474
|
-
SELECT entity FROM entity_mentions WHERE entity LIKE ?
|
|
1475
|
-
UNION SELECT entity FROM entity_aliases WHERE alias LIKE ?
|
|
1509
|
+
const rows = this.db.prepare(`
|
|
1510
|
+
SELECT DISTINCT entity FROM (
|
|
1511
|
+
SELECT entity FROM entity_mentions WHERE entity LIKE ?
|
|
1512
|
+
UNION SELECT entity FROM entity_aliases WHERE alias LIKE ?
|
|
1476
1513
|
) LIMIT ?`).all(`%${t}%`, `%${t}%`, limit);
|
|
1477
1514
|
return rows.map(r => r.entity);
|
|
1478
1515
|
}
|
|
@@ -1514,20 +1551,20 @@ export class EngramStore {
|
|
|
1514
1551
|
markConsolidation(agentId, mini) {
|
|
1515
1552
|
const now = new Date().toISOString();
|
|
1516
1553
|
if (mini) {
|
|
1517
|
-
this.db.prepare(`
|
|
1518
|
-
UPDATE conscious_state SET last_mini_consolidation_at = ?, updated_at = ? WHERE agent_id = ?
|
|
1554
|
+
this.db.prepare(`
|
|
1555
|
+
UPDATE conscious_state SET last_mini_consolidation_at = ?, updated_at = ? WHERE agent_id = ?
|
|
1519
1556
|
`).run(now, now, agentId);
|
|
1520
1557
|
}
|
|
1521
1558
|
else {
|
|
1522
|
-
this.db.prepare(`
|
|
1523
|
-
UPDATE conscious_state SET
|
|
1524
|
-
last_consolidation_at = ?,
|
|
1525
|
-
last_mini_consolidation_at = ?,
|
|
1526
|
-
write_count_since_consolidation = 0,
|
|
1527
|
-
recall_count_since_consolidation = 0,
|
|
1528
|
-
consolidation_cycle_count = consolidation_cycle_count + 1,
|
|
1529
|
-
updated_at = ?
|
|
1530
|
-
WHERE agent_id = ?
|
|
1559
|
+
this.db.prepare(`
|
|
1560
|
+
UPDATE conscious_state SET
|
|
1561
|
+
last_consolidation_at = ?,
|
|
1562
|
+
last_mini_consolidation_at = ?,
|
|
1563
|
+
write_count_since_consolidation = 0,
|
|
1564
|
+
recall_count_since_consolidation = 0,
|
|
1565
|
+
consolidation_cycle_count = consolidation_cycle_count + 1,
|
|
1566
|
+
updated_at = ?
|
|
1567
|
+
WHERE agent_id = ?
|
|
1531
1568
|
`).run(now, now, now, agentId);
|
|
1532
1569
|
}
|
|
1533
1570
|
}
|
|
@@ -1598,10 +1635,10 @@ export class EngramStore {
|
|
|
1598
1635
|
* sequence are excluded (they have no story-time anchor).
|
|
1599
1636
|
*/
|
|
1600
1637
|
getLatestByTag(opts) {
|
|
1601
|
-
let sql = `SELECT * FROM engrams
|
|
1602
|
-
WHERE agent_id = ?
|
|
1603
|
-
AND retracted = ?
|
|
1604
|
-
AND stage = 'active'
|
|
1638
|
+
let sql = `SELECT * FROM engrams
|
|
1639
|
+
WHERE agent_id = ?
|
|
1640
|
+
AND retracted = ?
|
|
1641
|
+
AND stage = 'active'
|
|
1605
1642
|
AND tags LIKE ?`;
|
|
1606
1643
|
const params = [
|
|
1607
1644
|
opts.agentId,
|
|
@@ -1641,10 +1678,10 @@ export class EngramStore {
|
|
|
1641
1678
|
*/
|
|
1642
1679
|
getTopBy(opts) {
|
|
1643
1680
|
// Build the base query inline (mirrors search() but adds the topBy sort).
|
|
1644
|
-
let sql = `SELECT * FROM engrams
|
|
1645
|
-
WHERE agent_id = ?
|
|
1646
|
-
AND retracted = ?
|
|
1647
|
-
AND stage = 'active'
|
|
1681
|
+
let sql = `SELECT * FROM engrams
|
|
1682
|
+
WHERE agent_id = ?
|
|
1683
|
+
AND retracted = ?
|
|
1684
|
+
AND stage = 'active'
|
|
1648
1685
|
AND tags LIKE ?`;
|
|
1649
1686
|
const params = [
|
|
1650
1687
|
opts.agentId,
|
|
@@ -1712,11 +1749,11 @@ export class EngramStore {
|
|
|
1712
1749
|
// Find candidate referencing engrams. LIKE-filter first to narrow,
|
|
1713
1750
|
// then JSON.parse each candidate's references_json to verify the
|
|
1714
1751
|
// matchEngramId and type. Cheap enough at NovelForge's scale.
|
|
1715
|
-
const candidates = this.db.prepare(`SELECT id, references_json, created_at FROM engrams
|
|
1716
|
-
WHERE agent_id = ?
|
|
1717
|
-
AND retracted = 0
|
|
1718
|
-
AND stage = 'active'
|
|
1719
|
-
AND references_json IS NOT NULL
|
|
1752
|
+
const candidates = this.db.prepare(`SELECT id, references_json, created_at FROM engrams
|
|
1753
|
+
WHERE agent_id = ?
|
|
1754
|
+
AND retracted = 0
|
|
1755
|
+
AND stage = 'active'
|
|
1756
|
+
AND references_json IS NOT NULL
|
|
1720
1757
|
AND references_json LIKE ?`).all(target.agentId, `%"${targetEngramId}"%`);
|
|
1721
1758
|
const terminalTypes = new Set(['resolves', 'subverts', 'abandons']);
|
|
1722
1759
|
const resolvingEvents = [];
|