agent-working-memory 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/migrate.js +29 -29
- package/dist/cli.js +324 -219
- package/dist/cli.js.map +1 -1
- package/dist/coordination/circuit-breaker.js +23 -23
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +10 -1
- package/dist/core/salience.js.map +1 -1
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +5 -1
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/storage/factory.d.ts +1 -1
- package/dist/storage/factory.d.ts.map +1 -1
- package/dist/storage/factory.js +16 -2
- package/dist/storage/factory.js.map +1 -1
- package/dist/storage/pglite-schema.js +143 -143
- package/dist/storage/pglite.d.ts.map +1 -1
- package/dist/storage/pglite.js +146 -138
- package/dist/storage/pglite.js.map +1 -1
- package/dist/storage/postgres.d.ts +228 -0
- package/dist/storage/postgres.d.ts.map +1 -0
- package/dist/storage/postgres.js +1221 -0
- package/dist/storage/postgres.js.map +1 -0
- package/package.json +3 -1
- package/src/api/index.ts +3 -3
- package/src/cli/migrate.ts +307 -307
- package/src/cli.ts +266 -268
- package/src/coordination/circuit-breaker.ts +83 -83
- package/src/coordination/failure-modes.ts +50 -50
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +110 -110
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/ml-worker-entry.ts +194 -194
- package/src/core/ml-worker.ts +281 -281
- package/src/core/query-expander.ts +122 -122
- package/src/core/reranker.ts +119 -119
- package/src/core/salience.ts +10 -1
- package/src/core/write-pipeline.ts +5 -1
- package/src/engine/confidence.ts +120 -120
- package/src/engine/consolidation-scheduler.ts +242 -242
- package/src/engine/eval.ts +102 -102
- package/src/engine/eviction.ts +101 -101
- package/src/engine/index.ts +8 -8
- package/src/engine/retraction.ts +366 -366
- package/src/engine/staging.ts +74 -74
- package/src/storage/factory.ts +159 -147
- package/src/storage/index.ts +3 -3
- package/src/storage/pglite-schema.ts +166 -166
- package/src/storage/pglite.ts +1372 -1363
- package/src/storage/postgres.ts +1475 -0
- package/src/storage/store.ts +80 -80
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/eval.ts +100 -100
- package/src/types/index.ts +6 -6
package/dist/storage/pglite.js
CHANGED
|
@@ -129,6 +129,7 @@ function extractTagValue(tags, prefix) {
|
|
|
129
129
|
}
|
|
130
130
|
return null;
|
|
131
131
|
}
|
|
132
|
+
let warnedNoCoordPglite = false; // one-time warning when workspace/hive recall falls back (coord_agents absent)
|
|
132
133
|
export class PGliteEngramStore {
|
|
133
134
|
db;
|
|
134
135
|
readyPromise;
|
|
@@ -184,7 +185,7 @@ export class PGliteEngramStore {
|
|
|
184
185
|
params.push(e.id, e.agentId, e.timestamp.toISOString(), e.context, e.resultsReturned, e.topScore, e.latencyMs, JSON.stringify(e.engramIds));
|
|
185
186
|
}
|
|
186
187
|
try {
|
|
187
|
-
await this.db.query(`INSERT INTO activation_events (id, agent_id, timestamp, context, results_returned, top_score, latency_ms, engram_ids)
|
|
188
|
+
await this.db.query(`INSERT INTO activation_events (id, agent_id, timestamp, context, results_returned, top_score, latency_ms, engram_ids)
|
|
188
189
|
VALUES ${values.join(',')}`, params);
|
|
189
190
|
}
|
|
190
191
|
catch {
|
|
@@ -222,18 +223,18 @@ export class PGliteEngramStore {
|
|
|
222
223
|
await this.readyPromise;
|
|
223
224
|
const id = input.id ?? randomUUID();
|
|
224
225
|
const now = new Date().toISOString();
|
|
225
|
-
await this.db.query(`INSERT INTO engrams (
|
|
226
|
-
id, agent_id, concept, content, embedding, embedding_model,
|
|
227
|
-
confidence, salience, access_count, last_accessed, created_at,
|
|
228
|
-
salience_features, reason_codes, stage, ttl, retracted,
|
|
229
|
-
tags, memory_type, memory_class, supersedes, episode_id,
|
|
230
|
-
task_status, task_priority, blocked_by, sequence, references_json
|
|
231
|
-
) VALUES (
|
|
232
|
-
$1, $2, $3, $4, $5::vector, $6,
|
|
233
|
-
$7, $8, 0, $9, $10,
|
|
234
|
-
$11, $12, 'active', $13, FALSE,
|
|
235
|
-
$14, $15, $16, $17, $18,
|
|
236
|
-
$19, $20, $21, $22, $23
|
|
226
|
+
await this.db.query(`INSERT INTO engrams (
|
|
227
|
+
id, agent_id, concept, content, embedding, embedding_model,
|
|
228
|
+
confidence, salience, access_count, last_accessed, created_at,
|
|
229
|
+
salience_features, reason_codes, stage, ttl, retracted,
|
|
230
|
+
tags, memory_type, memory_class, supersedes, episode_id,
|
|
231
|
+
task_status, task_priority, blocked_by, sequence, references_json
|
|
232
|
+
) VALUES (
|
|
233
|
+
$1, $2, $3, $4, $5::vector, $6,
|
|
234
|
+
$7, $8, 0, $9, $10,
|
|
235
|
+
$11, $12, 'active', $13, FALSE,
|
|
236
|
+
$14, $15, $16, $17, $18,
|
|
237
|
+
$19, $20, $21, $22, $23
|
|
237
238
|
)`, [
|
|
238
239
|
id,
|
|
239
240
|
input.agentId,
|
|
@@ -357,15 +358,22 @@ export class PGliteEngramStore {
|
|
|
357
358
|
return names;
|
|
358
359
|
}
|
|
359
360
|
catch {
|
|
361
|
+
// coord_agents isn't provisioned on PGlite — workspace/hive coordination is currently SQLite-only.
|
|
362
|
+
// Warn ONCE so this degrades VISIBLY (recall scoped to self) instead of silently.
|
|
363
|
+
if (!warnedNoCoordPglite) {
|
|
364
|
+
warnedNoCoordPglite = true;
|
|
365
|
+
console.warn('[awm:pglite] workspace/hive coordination is not available on the PGlite backend ' +
|
|
366
|
+
'(coord_agents not provisioned) — recall is scoped to THIS agent only. Hive coordination is SQLite-only for now.');
|
|
367
|
+
}
|
|
360
368
|
return [agentId];
|
|
361
369
|
}
|
|
362
370
|
}
|
|
363
371
|
async touchEngram(id) {
|
|
364
372
|
await this.readyPromise;
|
|
365
|
-
await this.db.query(`UPDATE engrams
|
|
366
|
-
SET access_count = access_count + 1,
|
|
367
|
-
last_accessed = $1,
|
|
368
|
-
confidence = LEAST(0.85, confidence + 0.02 / (1.0 + sqrt(access_count::float)))
|
|
373
|
+
await this.db.query(`UPDATE engrams
|
|
374
|
+
SET access_count = access_count + 1,
|
|
375
|
+
last_accessed = $1,
|
|
376
|
+
confidence = LEAST(0.85, confidence + 0.02 / (1.0 + sqrt(access_count::float)))
|
|
369
377
|
WHERE id = $2`, [new Date().toISOString(), id]);
|
|
370
378
|
}
|
|
371
379
|
async updateStage(id, stage) {
|
|
@@ -411,9 +419,9 @@ export class PGliteEngramStore {
|
|
|
411
419
|
async timeWarp(agentId, ms) {
|
|
412
420
|
await this.readyPromise;
|
|
413
421
|
const seconds = Math.round(ms / 1000);
|
|
414
|
-
const r1 = await this.db.query(`UPDATE engrams SET
|
|
415
|
-
created_at = to_char(($1::timestamptz - interval '1 second' * $2), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
|
|
416
|
-
last_accessed = to_char(($3::timestamptz - interval '1 second' * $2), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
422
|
+
const r1 = await this.db.query(`UPDATE engrams SET
|
|
423
|
+
created_at = to_char(($1::timestamptz - interval '1 second' * $2), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
|
|
424
|
+
last_accessed = to_char(($3::timestamptz - interval '1 second' * $2), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
417
425
|
WHERE agent_id = $4`, ['now', seconds, 'now', agentId]);
|
|
418
426
|
// Simpler: just update with relative arithmetic on stored ISO strings.
|
|
419
427
|
// PGlite doesn't support all date ops cleanly; fall through to JS-side calculation.
|
|
@@ -439,13 +447,13 @@ export class PGliteEngramStore {
|
|
|
439
447
|
// Restrict to active + fading. Faded engrams (Paper 1: storage degradation)
|
|
440
448
|
// retain their embedding so they still participate in semantic recall, even
|
|
441
449
|
// though their content has been trimmed. Excludes staging/consolidated/archived.
|
|
442
|
-
const result = await this.db.query(`SELECT *, (embedding <=> $2::vector) AS distance
|
|
443
|
-
FROM engrams
|
|
444
|
-
WHERE agent_id = $1
|
|
445
|
-
AND embedding IS NOT NULL
|
|
446
|
-
AND retracted = FALSE
|
|
447
|
-
AND stage IN ('active', 'fading')
|
|
448
|
-
ORDER BY distance ASC
|
|
450
|
+
const result = await this.db.query(`SELECT *, (embedding <=> $2::vector) AS distance
|
|
451
|
+
FROM engrams
|
|
452
|
+
WHERE agent_id = $1
|
|
453
|
+
AND embedding IS NOT NULL
|
|
454
|
+
AND retracted = FALSE
|
|
455
|
+
AND stage IN ('active', 'fading')
|
|
456
|
+
ORDER BY distance ASC
|
|
449
457
|
LIMIT $3`, [agentId, vectorToLiteral(vec), limit]);
|
|
450
458
|
return result.rows.map((r) => ({ engram: rowToEngram(r), distance: r.distance }));
|
|
451
459
|
}
|
|
@@ -464,11 +472,11 @@ export class PGliteEngramStore {
|
|
|
464
472
|
if (tokens.length === 0)
|
|
465
473
|
return [];
|
|
466
474
|
const websearchQuery = tokens.join(' OR ');
|
|
467
|
-
const result = await this.db.query(`SELECT *, ts_rank_cd(fts, websearch_to_tsquery('english', $2)) AS rank
|
|
468
|
-
FROM engrams
|
|
469
|
-
WHERE agent_id = $1 AND retracted = FALSE
|
|
470
|
-
AND fts @@ websearch_to_tsquery('english', $2)
|
|
471
|
-
ORDER BY rank DESC
|
|
475
|
+
const result = await this.db.query(`SELECT *, ts_rank_cd(fts, websearch_to_tsquery('english', $2)) AS rank
|
|
476
|
+
FROM engrams
|
|
477
|
+
WHERE agent_id = $1 AND retracted = FALSE
|
|
478
|
+
AND fts @@ websearch_to_tsquery('english', $2)
|
|
479
|
+
ORDER BY rank DESC
|
|
472
480
|
LIMIT $3`, [agentId, websearchQuery, limit]);
|
|
473
481
|
return result.rows.map((r) => ({ engram: rowToEngram(r), bm25Score: calibrateBm25(Number(r.rank)) }));
|
|
474
482
|
}
|
|
@@ -482,11 +490,11 @@ export class PGliteEngramStore {
|
|
|
482
490
|
if (tokens.length === 0)
|
|
483
491
|
return [];
|
|
484
492
|
const websearchQuery = tokens.join(' OR ');
|
|
485
|
-
const result = await this.db.query(`SELECT *, ts_rank_cd(fts, websearch_to_tsquery('english', $2)) AS rank
|
|
486
|
-
FROM engrams
|
|
487
|
-
WHERE agent_id = ANY($1::text[]) AND retracted = FALSE
|
|
488
|
-
AND fts @@ websearch_to_tsquery('english', $2)
|
|
489
|
-
ORDER BY rank DESC
|
|
493
|
+
const result = await this.db.query(`SELECT *, ts_rank_cd(fts, websearch_to_tsquery('english', $2)) AS rank
|
|
494
|
+
FROM engrams
|
|
495
|
+
WHERE agent_id = ANY($1::text[]) AND retracted = FALSE
|
|
496
|
+
AND fts @@ websearch_to_tsquery('english', $2)
|
|
497
|
+
ORDER BY rank DESC
|
|
490
498
|
LIMIT $3`, [agentIds, websearchQuery, limit]);
|
|
491
499
|
return result.rows.map((r) => ({ engram: rowToEngram(r), bm25Score: calibrateBm25(Number(r.rank)) }));
|
|
492
500
|
}
|
|
@@ -567,32 +575,32 @@ export class PGliteEngramStore {
|
|
|
567
575
|
sql += ` AND task_status = $${params.length + 1}`;
|
|
568
576
|
params.push(status);
|
|
569
577
|
}
|
|
570
|
-
sql += ` ORDER BY
|
|
571
|
-
CASE task_priority
|
|
572
|
-
WHEN 'urgent' THEN 0
|
|
573
|
-
WHEN 'high' THEN 1
|
|
574
|
-
WHEN 'medium' THEN 2
|
|
575
|
-
WHEN 'low' THEN 3
|
|
576
|
-
ELSE 4
|
|
577
|
-
END,
|
|
578
|
+
sql += ` ORDER BY
|
|
579
|
+
CASE task_priority
|
|
580
|
+
WHEN 'urgent' THEN 0
|
|
581
|
+
WHEN 'high' THEN 1
|
|
582
|
+
WHEN 'medium' THEN 2
|
|
583
|
+
WHEN 'low' THEN 3
|
|
584
|
+
ELSE 4
|
|
585
|
+
END,
|
|
578
586
|
created_at DESC`;
|
|
579
587
|
const result = await this.db.query(sql, params);
|
|
580
588
|
return result.rows.map(rowToEngram);
|
|
581
589
|
}
|
|
582
590
|
async getNextTask(agentId) {
|
|
583
591
|
await this.readyPromise;
|
|
584
|
-
const result = await this.db.query(`SELECT * FROM engrams
|
|
585
|
-
WHERE agent_id = $1 AND task_status IN ('open', 'in_progress') AND retracted = FALSE
|
|
586
|
-
ORDER BY
|
|
587
|
-
CASE task_status WHEN 'in_progress' THEN 0 ELSE 1 END,
|
|
588
|
-
CASE task_priority
|
|
589
|
-
WHEN 'urgent' THEN 0
|
|
590
|
-
WHEN 'high' THEN 1
|
|
591
|
-
WHEN 'medium' THEN 2
|
|
592
|
-
WHEN 'low' THEN 3
|
|
593
|
-
ELSE 4
|
|
594
|
-
END,
|
|
595
|
-
created_at ASC
|
|
592
|
+
const result = await this.db.query(`SELECT * FROM engrams
|
|
593
|
+
WHERE agent_id = $1 AND task_status IN ('open', 'in_progress') AND retracted = FALSE
|
|
594
|
+
ORDER BY
|
|
595
|
+
CASE task_status WHEN 'in_progress' THEN 0 ELSE 1 END,
|
|
596
|
+
CASE task_priority
|
|
597
|
+
WHEN 'urgent' THEN 0
|
|
598
|
+
WHEN 'high' THEN 1
|
|
599
|
+
WHEN 'medium' THEN 2
|
|
600
|
+
WHEN 'low' THEN 3
|
|
601
|
+
ELSE 4
|
|
602
|
+
END,
|
|
603
|
+
created_at ASC
|
|
596
604
|
LIMIT 1`, [agentId]);
|
|
597
605
|
return result.rows.length > 0 ? rowToEngram(result.rows[0]) : null;
|
|
598
606
|
}
|
|
@@ -606,11 +614,11 @@ export class PGliteEngramStore {
|
|
|
606
614
|
}
|
|
607
615
|
async findActiveMatchByConcept(agentId, concept, requiredTags) {
|
|
608
616
|
await this.readyPromise;
|
|
609
|
-
let sql = `SELECT * FROM engrams
|
|
610
|
-
WHERE agent_id = $1
|
|
611
|
-
AND LOWER(TRIM(concept)) = LOWER(TRIM($2))
|
|
612
|
-
AND stage = 'active'
|
|
613
|
-
AND retracted = FALSE
|
|
617
|
+
let sql = `SELECT * FROM engrams
|
|
618
|
+
WHERE agent_id = $1
|
|
619
|
+
AND LOWER(TRIM(concept)) = LOWER(TRIM($2))
|
|
620
|
+
AND stage = 'active'
|
|
621
|
+
AND retracted = FALSE
|
|
614
622
|
AND superseded_by IS NULL`;
|
|
615
623
|
const params = [agentId, concept];
|
|
616
624
|
if (requiredTags && requiredTags.length > 0) {
|
|
@@ -643,12 +651,12 @@ export class PGliteEngramStore {
|
|
|
643
651
|
await this.readyPromise;
|
|
644
652
|
const id = randomUUID();
|
|
645
653
|
const now = new Date().toISOString();
|
|
646
|
-
await this.db.query(`INSERT INTO associations (id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated)
|
|
647
|
-
VALUES ($1, $2, $3, $4, $5, $6, 0, $7, $7)
|
|
648
|
-
ON CONFLICT (from_engram_id, to_engram_id) DO UPDATE SET
|
|
649
|
-
weight = EXCLUDED.weight,
|
|
650
|
-
confidence = EXCLUDED.confidence,
|
|
651
|
-
last_activated = EXCLUDED.last_activated,
|
|
654
|
+
await this.db.query(`INSERT INTO associations (id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated)
|
|
655
|
+
VALUES ($1, $2, $3, $4, $5, $6, 0, $7, $7)
|
|
656
|
+
ON CONFLICT (from_engram_id, to_engram_id) DO UPDATE SET
|
|
657
|
+
weight = EXCLUDED.weight,
|
|
658
|
+
confidence = EXCLUDED.confidence,
|
|
659
|
+
last_activated = EXCLUDED.last_activated,
|
|
652
660
|
activation_count = associations.activation_count + 1`, [id, fromId, toId, weight, confidence, type, now]);
|
|
653
661
|
const assoc = await this.getAssociation(fromId, toId);
|
|
654
662
|
if (!assoc)
|
|
@@ -670,12 +678,12 @@ export class PGliteEngramStore {
|
|
|
670
678
|
if (engramIds.length === 0)
|
|
671
679
|
return result;
|
|
672
680
|
await this.readyPromise;
|
|
673
|
-
const r = await this.db.query(`SELECT id, SUM(cnt) AS count, SUM(sw) AS sum_weight FROM (
|
|
674
|
-
SELECT from_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE from_engram_id = ANY($1::text[])
|
|
675
|
-
UNION ALL
|
|
676
|
-
SELECT to_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE to_engram_id = ANY($1::text[])
|
|
677
|
-
) t
|
|
678
|
-
WHERE id = ANY($1::text[])
|
|
681
|
+
const r = await this.db.query(`SELECT id, SUM(cnt) AS count, SUM(sw) AS sum_weight FROM (
|
|
682
|
+
SELECT from_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE from_engram_id = ANY($1::text[])
|
|
683
|
+
UNION ALL
|
|
684
|
+
SELECT to_engram_id AS id, 1 AS cnt, weight AS sw FROM associations WHERE to_engram_id = ANY($1::text[])
|
|
685
|
+
) t
|
|
686
|
+
WHERE id = ANY($1::text[])
|
|
679
687
|
GROUP BY id`, [engramIds]);
|
|
680
688
|
for (const row of r.rows) {
|
|
681
689
|
result.set(row.id, { count: Number(row.count), sumWeight: Number(row.sum_weight) });
|
|
@@ -691,7 +699,7 @@ export class PGliteEngramStore {
|
|
|
691
699
|
if (engramIds.length === 0)
|
|
692
700
|
return result;
|
|
693
701
|
await this.readyPromise;
|
|
694
|
-
const r = await this.db.query(`SELECT * FROM associations
|
|
702
|
+
const r = await this.db.query(`SELECT * FROM associations
|
|
695
703
|
WHERE from_engram_id = ANY($1::text[]) OR to_engram_id = ANY($1::text[])`, [engramIds]);
|
|
696
704
|
for (const row of r.rows) {
|
|
697
705
|
const a = rowToAssociation(row);
|
|
@@ -731,8 +739,8 @@ export class PGliteEngramStore {
|
|
|
731
739
|
}
|
|
732
740
|
async getAllAssociations(agentId) {
|
|
733
741
|
await this.readyPromise;
|
|
734
|
-
const result = await this.db.query(`SELECT a.* FROM associations a
|
|
735
|
-
JOIN engrams e ON a.from_engram_id = e.id
|
|
742
|
+
const result = await this.db.query(`SELECT a.* FROM associations a
|
|
743
|
+
JOIN engrams e ON a.from_engram_id = e.id
|
|
736
744
|
WHERE e.agent_id = $1`, [agentId]);
|
|
737
745
|
return result.rows.map(rowToAssociation);
|
|
738
746
|
}
|
|
@@ -741,11 +749,11 @@ export class PGliteEngramStore {
|
|
|
741
749
|
// ============================================================
|
|
742
750
|
async getEvictionCandidates(agentId, limit) {
|
|
743
751
|
await this.readyPromise;
|
|
744
|
-
const result = await this.db.query(`SELECT * FROM engrams
|
|
745
|
-
WHERE agent_id = $1 AND stage = 'active' AND retracted = FALSE
|
|
746
|
-
ORDER BY (salience * 0.3 + confidence * 0.3
|
|
747
|
-
+ (access_count::float / (access_count + 5)) * 0.2
|
|
748
|
-
+ (1.0 / (1.0 + EXTRACT(EPOCH FROM (now() - last_accessed::timestamptz)) / 86400.0)) * 0.2) ASC
|
|
752
|
+
const result = await this.db.query(`SELECT * FROM engrams
|
|
753
|
+
WHERE agent_id = $1 AND stage = 'active' AND retracted = FALSE
|
|
754
|
+
ORDER BY (salience * 0.3 + confidence * 0.3
|
|
755
|
+
+ (access_count::float / (access_count + 5)) * 0.2
|
|
756
|
+
+ (1.0 / (1.0 + EXTRACT(EPOCH FROM (now() - last_accessed::timestamptz)) / 86400.0)) * 0.2) ASC
|
|
749
757
|
LIMIT $2`, [agentId, limit]);
|
|
750
758
|
return result.rows.map(rowToEngram);
|
|
751
759
|
}
|
|
@@ -780,7 +788,7 @@ export class PGliteEngramStore {
|
|
|
780
788
|
}
|
|
781
789
|
async logStagingEvent(event) {
|
|
782
790
|
await this.readyPromise;
|
|
783
|
-
await this.db.query(`INSERT INTO staging_events (engram_id, agent_id, action, resonance_score, timestamp, age_ms)
|
|
791
|
+
await this.db.query(`INSERT INTO staging_events (engram_id, agent_id, action, resonance_score, timestamp, age_ms)
|
|
784
792
|
VALUES ($1, $2, $3, $4, $5, $6)`, [
|
|
785
793
|
event.engramId, event.agentId, event.action,
|
|
786
794
|
event.resonanceScore, event.timestamp.toISOString(), event.ageMs,
|
|
@@ -788,18 +796,18 @@ export class PGliteEngramStore {
|
|
|
788
796
|
}
|
|
789
797
|
async logRetrievalFeedback(activationEventId, engramId, useful, context) {
|
|
790
798
|
await this.readyPromise;
|
|
791
|
-
await this.db.query(`INSERT INTO retrieval_feedback (id, activation_event_id, engram_id, useful, context, timestamp)
|
|
799
|
+
await this.db.query(`INSERT INTO retrieval_feedback (id, activation_event_id, engram_id, useful, context, timestamp)
|
|
792
800
|
VALUES ($1, $2, $3, $4, $5, $6)`, [randomUUID(), activationEventId, engramId, useful, context, new Date().toISOString()]);
|
|
793
801
|
}
|
|
794
802
|
async getRetrievalPrecision(agentId, windowHours = 24) {
|
|
795
803
|
await this.readyPromise;
|
|
796
804
|
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
797
|
-
const result = await this.db.query(`SELECT
|
|
798
|
-
COUNT(CASE WHEN useful = TRUE THEN 1 END) AS useful_count,
|
|
799
|
-
COUNT(*) AS total_count
|
|
800
|
-
FROM retrieval_feedback rf
|
|
801
|
-
LEFT JOIN activation_events ae ON rf.activation_event_id = ae.id
|
|
802
|
-
JOIN engrams e ON rf.engram_id = e.id
|
|
805
|
+
const result = await this.db.query(`SELECT
|
|
806
|
+
COUNT(CASE WHEN useful = TRUE THEN 1 END) AS useful_count,
|
|
807
|
+
COUNT(*) AS total_count
|
|
808
|
+
FROM retrieval_feedback rf
|
|
809
|
+
LEFT JOIN activation_events ae ON rf.activation_event_id = ae.id
|
|
810
|
+
JOIN engrams e ON rf.engram_id = e.id
|
|
803
811
|
WHERE e.agent_id = $1 AND rf.timestamp > $2`, [agentId, since]);
|
|
804
812
|
const row = result.rows[0];
|
|
805
813
|
const total = Number(row?.total_count ?? 0);
|
|
@@ -808,10 +816,10 @@ export class PGliteEngramStore {
|
|
|
808
816
|
}
|
|
809
817
|
async getStagingMetrics(agentId) {
|
|
810
818
|
await this.readyPromise;
|
|
811
|
-
const result = await this.db.query(`SELECT
|
|
812
|
-
COUNT(CASE WHEN action = 'promoted' THEN 1 END) AS promoted,
|
|
813
|
-
COUNT(CASE WHEN action = 'discarded' THEN 1 END) AS discarded,
|
|
814
|
-
COUNT(CASE WHEN action = 'expired' THEN 1 END) AS expired
|
|
819
|
+
const result = await this.db.query(`SELECT
|
|
820
|
+
COUNT(CASE WHEN action = 'promoted' THEN 1 END) AS promoted,
|
|
821
|
+
COUNT(CASE WHEN action = 'discarded' THEN 1 END) AS discarded,
|
|
822
|
+
COUNT(CASE WHEN action = 'expired' THEN 1 END) AS expired
|
|
815
823
|
FROM staging_events WHERE agent_id = $1`, [agentId]);
|
|
816
824
|
const row = result.rows[0] ?? { promoted: 0, discarded: 0, expired: 0 };
|
|
817
825
|
return {
|
|
@@ -825,8 +833,8 @@ export class PGliteEngramStore {
|
|
|
825
833
|
// Flush any buffered activation events so stats reflect the latest writes.
|
|
826
834
|
await this.flushActivationEvents();
|
|
827
835
|
const since = new Date(Date.now() - windowHours * 3600_000).toISOString();
|
|
828
|
-
const result = await this.db.query(`SELECT latency_ms FROM activation_events
|
|
829
|
-
WHERE agent_id = $1 AND timestamp > $2
|
|
836
|
+
const result = await this.db.query(`SELECT latency_ms FROM activation_events
|
|
837
|
+
WHERE agent_id = $1 AND timestamp > $2
|
|
830
838
|
ORDER BY latency_ms ASC`, [agentId, since]);
|
|
831
839
|
if (result.rows.length === 0)
|
|
832
840
|
return { count: 0, avgLatencyMs: 0, p95LatencyMs: 0 };
|
|
@@ -851,7 +859,7 @@ export class PGliteEngramStore {
|
|
|
851
859
|
await this.readyPromise;
|
|
852
860
|
const id = randomUUID();
|
|
853
861
|
const now = new Date().toISOString();
|
|
854
|
-
await this.db.query(`INSERT INTO episodes (id, agent_id, label, embedding, engram_count, start_time, end_time, created_at)
|
|
862
|
+
await this.db.query(`INSERT INTO episodes (id, agent_id, label, embedding, engram_count, start_time, end_time, created_at)
|
|
855
863
|
VALUES ($1, $2, $3, $4::vector, 0, $5, $5, $5)`, [id, input.agentId, input.label, vectorToLiteral(input.embedding ?? null), now]);
|
|
856
864
|
const ep = await this.getEpisode(id);
|
|
857
865
|
if (!ep)
|
|
@@ -877,9 +885,9 @@ export class PGliteEngramStore {
|
|
|
877
885
|
async addEngramToEpisode(engramId, episodeId) {
|
|
878
886
|
await this.readyPromise;
|
|
879
887
|
await this.db.query(`UPDATE engrams SET episode_id = $1 WHERE id = $2`, [episodeId, engramId]);
|
|
880
|
-
await this.db.query(`UPDATE episodes SET
|
|
881
|
-
engram_count = engram_count + 1,
|
|
882
|
-
end_time = GREATEST(end_time, $1)
|
|
888
|
+
await this.db.query(`UPDATE episodes SET
|
|
889
|
+
engram_count = engram_count + 1,
|
|
890
|
+
end_time = GREATEST(end_time, $1)
|
|
883
891
|
WHERE id = $2`, [new Date().toISOString(), episodeId]);
|
|
884
892
|
}
|
|
885
893
|
async getEngramsByEpisode(episodeId) {
|
|
@@ -918,44 +926,44 @@ export class PGliteEngramStore {
|
|
|
918
926
|
async updateAutoCheckpointWrite(agentId, engramId) {
|
|
919
927
|
await this.readyPromise;
|
|
920
928
|
const now = new Date().toISOString();
|
|
921
|
-
await this.db.query(`INSERT INTO conscious_state (agent_id, last_write_id, last_activity_at, write_count_since_consolidation, updated_at)
|
|
922
|
-
VALUES ($1, $2, $3, 1, $3)
|
|
923
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
924
|
-
last_write_id = EXCLUDED.last_write_id,
|
|
925
|
-
last_activity_at = EXCLUDED.last_activity_at,
|
|
926
|
-
write_count_since_consolidation = conscious_state.write_count_since_consolidation + 1,
|
|
929
|
+
await this.db.query(`INSERT INTO conscious_state (agent_id, last_write_id, last_activity_at, write_count_since_consolidation, updated_at)
|
|
930
|
+
VALUES ($1, $2, $3, 1, $3)
|
|
931
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
932
|
+
last_write_id = EXCLUDED.last_write_id,
|
|
933
|
+
last_activity_at = EXCLUDED.last_activity_at,
|
|
934
|
+
write_count_since_consolidation = conscious_state.write_count_since_consolidation + 1,
|
|
927
935
|
updated_at = EXCLUDED.updated_at`, [agentId, engramId, now]);
|
|
928
936
|
}
|
|
929
937
|
async updateAutoCheckpointRecall(agentId, context, engramIds) {
|
|
930
938
|
await this.readyPromise;
|
|
931
939
|
const now = new Date().toISOString();
|
|
932
|
-
await this.db.query(`INSERT INTO conscious_state (agent_id, last_recall_context, last_recall_ids, last_activity_at, recall_count_since_consolidation, updated_at)
|
|
933
|
-
VALUES ($1, $2, $3, $4, 1, $4)
|
|
934
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
935
|
-
last_recall_context = EXCLUDED.last_recall_context,
|
|
936
|
-
last_recall_ids = EXCLUDED.last_recall_ids,
|
|
937
|
-
last_activity_at = EXCLUDED.last_activity_at,
|
|
938
|
-
recall_count_since_consolidation = conscious_state.recall_count_since_consolidation + 1,
|
|
940
|
+
await this.db.query(`INSERT INTO conscious_state (agent_id, last_recall_context, last_recall_ids, last_activity_at, recall_count_since_consolidation, updated_at)
|
|
941
|
+
VALUES ($1, $2, $3, $4, 1, $4)
|
|
942
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
943
|
+
last_recall_context = EXCLUDED.last_recall_context,
|
|
944
|
+
last_recall_ids = EXCLUDED.last_recall_ids,
|
|
945
|
+
last_activity_at = EXCLUDED.last_activity_at,
|
|
946
|
+
recall_count_since_consolidation = conscious_state.recall_count_since_consolidation + 1,
|
|
939
947
|
updated_at = EXCLUDED.updated_at`, [agentId, context, JSON.stringify(engramIds), now]);
|
|
940
948
|
}
|
|
941
949
|
async touchActivity(agentId) {
|
|
942
950
|
await this.readyPromise;
|
|
943
951
|
const now = new Date().toISOString();
|
|
944
|
-
await this.db.query(`INSERT INTO conscious_state (agent_id, last_activity_at, updated_at)
|
|
945
|
-
VALUES ($1, $2, $2)
|
|
946
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
947
|
-
last_activity_at = EXCLUDED.last_activity_at,
|
|
952
|
+
await this.db.query(`INSERT INTO conscious_state (agent_id, last_activity_at, updated_at)
|
|
953
|
+
VALUES ($1, $2, $2)
|
|
954
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
955
|
+
last_activity_at = EXCLUDED.last_activity_at,
|
|
948
956
|
updated_at = EXCLUDED.updated_at`, [agentId, now]);
|
|
949
957
|
}
|
|
950
958
|
async saveCheckpoint(agentId, state) {
|
|
951
959
|
await this.readyPromise;
|
|
952
960
|
const now = new Date().toISOString();
|
|
953
|
-
await this.db.query(`INSERT INTO conscious_state (agent_id, execution_state, checkpoint_at, last_activity_at, updated_at)
|
|
954
|
-
VALUES ($1, $2, $3, $3, $3)
|
|
955
|
-
ON CONFLICT(agent_id) DO UPDATE SET
|
|
956
|
-
execution_state = EXCLUDED.execution_state,
|
|
957
|
-
checkpoint_at = EXCLUDED.checkpoint_at,
|
|
958
|
-
last_activity_at = EXCLUDED.last_activity_at,
|
|
961
|
+
await this.db.query(`INSERT INTO conscious_state (agent_id, execution_state, checkpoint_at, last_activity_at, updated_at)
|
|
962
|
+
VALUES ($1, $2, $3, $3, $3)
|
|
963
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
964
|
+
execution_state = EXCLUDED.execution_state,
|
|
965
|
+
checkpoint_at = EXCLUDED.checkpoint_at,
|
|
966
|
+
last_activity_at = EXCLUDED.last_activity_at,
|
|
959
967
|
updated_at = EXCLUDED.updated_at`, [agentId, JSON.stringify(state), now]);
|
|
960
968
|
}
|
|
961
969
|
async getCheckpoint(agentId) {
|
|
@@ -988,13 +996,13 @@ export class PGliteEngramStore {
|
|
|
988
996
|
await this.db.query(`UPDATE conscious_state SET last_mini_consolidation_at = $1, updated_at = $1 WHERE agent_id = $2`, [now, agentId]);
|
|
989
997
|
}
|
|
990
998
|
else {
|
|
991
|
-
await this.db.query(`UPDATE conscious_state SET
|
|
992
|
-
last_consolidation_at = $1,
|
|
993
|
-
last_mini_consolidation_at = $1,
|
|
994
|
-
write_count_since_consolidation = 0,
|
|
995
|
-
recall_count_since_consolidation = 0,
|
|
996
|
-
consolidation_cycle_count = consolidation_cycle_count + 1,
|
|
997
|
-
updated_at = $1
|
|
999
|
+
await this.db.query(`UPDATE conscious_state SET
|
|
1000
|
+
last_consolidation_at = $1,
|
|
1001
|
+
last_mini_consolidation_at = $1,
|
|
1002
|
+
write_count_since_consolidation = 0,
|
|
1003
|
+
recall_count_since_consolidation = 0,
|
|
1004
|
+
consolidation_cycle_count = consolidation_cycle_count + 1,
|
|
1005
|
+
updated_at = $1
|
|
998
1006
|
WHERE agent_id = $2`, [now, agentId]);
|
|
999
1007
|
}
|
|
1000
1008
|
}
|
|
@@ -1019,10 +1027,10 @@ export class PGliteEngramStore {
|
|
|
1019
1027
|
// ============================================================
|
|
1020
1028
|
async getLatestByTag(opts) {
|
|
1021
1029
|
await this.readyPromise;
|
|
1022
|
-
let sql = `SELECT * FROM engrams
|
|
1023
|
-
WHERE agent_id = $1
|
|
1024
|
-
AND retracted = $2
|
|
1025
|
-
AND stage = 'active'
|
|
1030
|
+
let sql = `SELECT * FROM engrams
|
|
1031
|
+
WHERE agent_id = $1
|
|
1032
|
+
AND retracted = $2
|
|
1033
|
+
AND stage = 'active'
|
|
1026
1034
|
AND tags LIKE $3`;
|
|
1027
1035
|
const params = [opts.agentId, opts.retracted ?? false, `%"${opts.tagKeyPrefix}%`];
|
|
1028
1036
|
if (opts.scopeTagsAll && opts.scopeTagsAll.length > 0) {
|
|
@@ -1049,10 +1057,10 @@ export class PGliteEngramStore {
|
|
|
1049
1057
|
}
|
|
1050
1058
|
async getTopBy(opts) {
|
|
1051
1059
|
await this.readyPromise;
|
|
1052
|
-
let sql = `SELECT * FROM engrams
|
|
1053
|
-
WHERE agent_id = $1
|
|
1054
|
-
AND retracted = $2
|
|
1055
|
-
AND stage = 'active'
|
|
1060
|
+
let sql = `SELECT * FROM engrams
|
|
1061
|
+
WHERE agent_id = $1
|
|
1062
|
+
AND retracted = $2
|
|
1063
|
+
AND stage = 'active'
|
|
1056
1064
|
AND tags LIKE $3`;
|
|
1057
1065
|
const params = [opts.agentId, opts.retracted ?? false, `%"${opts.sortField}%`];
|
|
1058
1066
|
if (opts.filterTagsAll && opts.filterTagsAll.length > 0) {
|