agent-working-memory 0.7.4 → 0.7.7
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 +10 -1
- package/dist/api/routes.js +8 -8
- package/dist/cli.js +103 -103
- package/dist/core/salience.d.ts +2 -0
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +51 -0
- package/dist/core/salience.js.map +1 -1
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +59 -3
- package/dist/engine/activation.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp.js +82 -82
- package/dist/storage/sqlite.d.ts +9 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +75 -10
- package/dist/storage/sqlite.js.map +1 -1
- package/package.json +57 -57
- package/src/api/routes.ts +723 -723
- package/src/cli.ts +719 -719
- package/src/core/salience.ts +48 -0
- package/src/engine/activation.ts +55 -3
- package/src/index.ts +199 -199
- package/src/mcp.ts +1192 -1192
- package/src/storage/sqlite.ts +77 -10
package/README.md
CHANGED
|
@@ -407,6 +407,15 @@ npm run test:locomo # LoCoMo industry benchmark (28.2%)
|
|
|
407
407
|
|
|
408
408
|
All three ML models run locally via ONNX. No external API calls for retrieval. The entire system is a single SQLite file + a Node.js process.
|
|
409
409
|
|
|
410
|
+
## What's New in v0.7.7
|
|
411
|
+
|
|
412
|
+
- **Recall latency 2.5s → 1.0s end-to-end (~50% on top of 0.7.6)** — phase-breakdown spike showed that after the 0.7.6 BM25 fix, the new bottleneck was `getAssociationsForBatch` over all ~10K candidates (68% of recall latency). Added a cheap pre-filter before deep scoring: candidates survive only if they have a BM25 hit, a cosine z-score above the gate, or concept-token overlap with the query. From ~10K candidates → typically 100-300 survivors. Graph-walk correctness preserved (it only boosts neighbors with `textMatch >= 0.05`, which would also pass this filter). Recall quality A/B verified: 8/8 top-1 matches, 90% top-5 overlap, 94% top-10 overlap on diverse queries. Set `AWM_DISABLE_POOL_FILTER=1` to revert. **Cumulative since 0.7.4 baseline: 11-23s → 0.9-1.6s (~10-15× faster).**
|
|
413
|
+
|
|
414
|
+
## What's New in v0.7.6
|
|
415
|
+
|
|
416
|
+
- **Recall latency 11-23s → 2.5s end-to-end (~5× faster)** — measurement spike found the slow path was a SQLite query-plan trap, not vector search. The BM25 query `JOIN engrams_fts ON e.rowid + WHERE MATCH + ORDER BY rank LIMIT N` materialized all matching rows (with 1.5KB embedding blobs) before the LIMIT applied. CTE prefilter forces FTS5 LIMIT first, then joins only the top-K rowids. Same SQLite, same data, same results — 567× faster for wide OR queries (3682ms → 6.5ms verified). Also added `getAssociationsForBatch` to replace the per-candidate N+1 in the activation scoring loop. Top-K results are byte-identical to the old query (verified by the equivalence test in `spike/`).
|
|
417
|
+
- **Salience filter — auto-promote verified operational records** — operational batch summaries (e.g., "Submitted 6 events 2026-05-07 — IDs 18969, 18971…") were being discarded at salience 0.14 because BM25 novelty couldn't distinguish "useful new operational record" from "duplicate observation" when topic terminology repeated. New `detectVerifiedFinding()` pattern detector parallel to `detectUserFeedback()`: requires action-verb header (Submitted/Finalized/Completed/Reconciled/Triaged/etc.) plus ≥2 concrete identifiers (ISO date or contextual numeric ID). Matched memories get a 0.45 salience floor (active disposition, not canonical). 7 new tests, 23 salience tests pass.
|
|
418
|
+
|
|
410
419
|
## What's New in v0.7.4
|
|
411
420
|
|
|
412
421
|
- **Channel push telemetry** — new `GET /telemetry/channels` JSON endpoint and Prometheus counters (`coord_channel_push_attempts_total`, `..._delivered_total`, `..._failed_total{reason}`, `..._no_session_total`, `..._fallback_mailbox_total`, `..._session_disconnects_total`). Surfaces real delivery rate so coordination reliability can be measured rather than guessed.
|
|
@@ -452,7 +461,7 @@ See [CHANGELOG.md](CHANGELOG.md) for full details.
|
|
|
452
461
|
|
|
453
462
|
## Project Status
|
|
454
463
|
|
|
455
|
-
AWM is in active development (v0.7.
|
|
464
|
+
AWM is in active development (v0.7.7). The core memory pipeline, consolidation system, multi-agent coordination, and MCP integration are stable and used daily in production coding workflows.
|
|
456
465
|
|
|
457
466
|
- Core retrieval and consolidation: **stable**
|
|
458
467
|
- MCP tools and Claude Code integration: **stable**
|
package/dist/api/routes.js
CHANGED
|
@@ -488,9 +488,9 @@ export function registerRoutes(app, deps) {
|
|
|
488
488
|
const { agentId, all } = req.query;
|
|
489
489
|
const includeAll = all === 'true';
|
|
490
490
|
const db = store.getDb();
|
|
491
|
-
let engramSql = `SELECT id, agent_id, concept, content, confidence, salience, access_count,
|
|
492
|
-
last_accessed, created_at, salience_features, reason_codes, stage, ttl,
|
|
493
|
-
retracted, retracted_by, retracted_at, tags
|
|
491
|
+
let engramSql = `SELECT id, agent_id, concept, content, confidence, salience, access_count,
|
|
492
|
+
last_accessed, created_at, salience_features, reason_codes, stage, ttl,
|
|
493
|
+
retracted, retracted_by, retracted_at, tags
|
|
494
494
|
FROM engrams`;
|
|
495
495
|
const conditions = [];
|
|
496
496
|
const params = [];
|
|
@@ -508,7 +508,7 @@ export function registerRoutes(app, deps) {
|
|
|
508
508
|
engramSql += ' ORDER BY created_at ASC';
|
|
509
509
|
const engrams = db.prepare(engramSql).all(...params);
|
|
510
510
|
const engramIds = new Set(engrams.map(e => e.id));
|
|
511
|
-
const allAssocs = db.prepare(`SELECT id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated
|
|
511
|
+
const allAssocs = db.prepare(`SELECT id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated
|
|
512
512
|
FROM associations`).all();
|
|
513
513
|
const associations = allAssocs.filter(a => engramIds.has(a.from_engram_id) && engramIds.has(a.to_engram_id));
|
|
514
514
|
return reply.send({
|
|
@@ -527,15 +527,15 @@ export function registerRoutes(app, deps) {
|
|
|
527
527
|
const base = {
|
|
528
528
|
status: 'ok',
|
|
529
529
|
timestamp: new Date().toISOString(),
|
|
530
|
-
version: '0.7.
|
|
530
|
+
version: '0.7.7',
|
|
531
531
|
coordination: coordEnabled,
|
|
532
532
|
};
|
|
533
533
|
if (coordEnabled) {
|
|
534
534
|
try {
|
|
535
535
|
const db = deps.store.getDb();
|
|
536
|
-
const stats = db.prepare(`SELECT
|
|
537
|
-
(SELECT COUNT(*) FROM coord_agents WHERE status != 'dead') AS agents_alive,
|
|
538
|
-
(SELECT COUNT(*) FROM coord_assignments WHERE status = 'pending') AS pending_tasks,
|
|
536
|
+
const stats = db.prepare(`SELECT
|
|
537
|
+
(SELECT COUNT(*) FROM coord_agents WHERE status != 'dead') AS agents_alive,
|
|
538
|
+
(SELECT COUNT(*) FROM coord_assignments WHERE status = 'pending') AS pending_tasks,
|
|
539
539
|
(SELECT COUNT(*) FROM coord_locks) AS active_locks`).get();
|
|
540
540
|
Object.assign(base, stats);
|
|
541
541
|
}
|
package/dist/cli.js
CHANGED
|
@@ -38,44 +38,44 @@ catch { /* No .env file */ }
|
|
|
38
38
|
const args = process.argv.slice(2);
|
|
39
39
|
const command = args[0];
|
|
40
40
|
function printUsage() {
|
|
41
|
-
console.log(`
|
|
42
|
-
AgentWorkingMemory — Cognitive memory for AI agents
|
|
43
|
-
|
|
44
|
-
Usage:
|
|
45
|
-
awm setup [target] [options] Configure AWM for an AI CLI
|
|
46
|
-
awm doctor [target|--all] Validate AWM integrations
|
|
47
|
-
awm mcp Start MCP server (stdio)
|
|
48
|
-
awm serve [--port <port>] Start HTTP API server
|
|
49
|
-
awm health [--port <port>] Check server health
|
|
50
|
-
awm export --db <path> [--agent <id>] [--output <file>] [--active-only]
|
|
51
|
-
Export memories to JSON
|
|
52
|
-
awm import <file> --db <path> [--remap-agent <id>] [--dedupe] [--dry-run]
|
|
53
|
-
Import memories from JSON
|
|
54
|
-
awm merge --target <db> --source <db> [--source ...]
|
|
55
|
-
[--remap uuid=name] [--remap-all-uuids <name>]
|
|
56
|
-
[--dedupe] [--dry-run] Merge multiple memory DBs
|
|
57
|
-
|
|
58
|
-
Setup targets:
|
|
59
|
-
claude-code (default) .mcp.json + CLAUDE.md + hooks
|
|
60
|
-
codex ~/.codex/config.toml + AGENTS.md
|
|
61
|
-
cursor .cursor/mcp.json + .cursorrules
|
|
62
|
-
http Connection info for HTTP API
|
|
63
|
-
|
|
64
|
-
Setup options:
|
|
65
|
-
--global Use global scope (recommended for claude-code)
|
|
66
|
-
--agent-id <id> Agent identifier (default: project name)
|
|
67
|
-
--db-path <path> Database path (default: <awm>/data/memory.db)
|
|
68
|
-
--no-instructions Skip instruction file (CLAUDE.md, AGENTS.md, etc.)
|
|
69
|
-
--no-claude-md Alias for --no-instructions
|
|
70
|
-
--no-hooks Skip hook installation
|
|
71
|
-
--hook-port PORT Sidecar port for hooks (default: 8401)
|
|
72
|
-
|
|
73
|
-
Examples:
|
|
74
|
-
awm setup --global Claude Code, global (recommended)
|
|
75
|
-
awm setup codex Codex CLI
|
|
76
|
-
awm setup cursor Cursor IDE
|
|
77
|
-
awm setup http Generic HTTP integration
|
|
78
|
-
awm doctor --all Check all configured targets
|
|
41
|
+
console.log(`
|
|
42
|
+
AgentWorkingMemory — Cognitive memory for AI agents
|
|
43
|
+
|
|
44
|
+
Usage:
|
|
45
|
+
awm setup [target] [options] Configure AWM for an AI CLI
|
|
46
|
+
awm doctor [target|--all] Validate AWM integrations
|
|
47
|
+
awm mcp Start MCP server (stdio)
|
|
48
|
+
awm serve [--port <port>] Start HTTP API server
|
|
49
|
+
awm health [--port <port>] Check server health
|
|
50
|
+
awm export --db <path> [--agent <id>] [--output <file>] [--active-only]
|
|
51
|
+
Export memories to JSON
|
|
52
|
+
awm import <file> --db <path> [--remap-agent <id>] [--dedupe] [--dry-run]
|
|
53
|
+
Import memories from JSON
|
|
54
|
+
awm merge --target <db> --source <db> [--source ...]
|
|
55
|
+
[--remap uuid=name] [--remap-all-uuids <name>]
|
|
56
|
+
[--dedupe] [--dry-run] Merge multiple memory DBs
|
|
57
|
+
|
|
58
|
+
Setup targets:
|
|
59
|
+
claude-code (default) .mcp.json + CLAUDE.md + hooks
|
|
60
|
+
codex ~/.codex/config.toml + AGENTS.md
|
|
61
|
+
cursor .cursor/mcp.json + .cursorrules
|
|
62
|
+
http Connection info for HTTP API
|
|
63
|
+
|
|
64
|
+
Setup options:
|
|
65
|
+
--global Use global scope (recommended for claude-code)
|
|
66
|
+
--agent-id <id> Agent identifier (default: project name)
|
|
67
|
+
--db-path <path> Database path (default: <awm>/data/memory.db)
|
|
68
|
+
--no-instructions Skip instruction file (CLAUDE.md, AGENTS.md, etc.)
|
|
69
|
+
--no-claude-md Alias for --no-instructions
|
|
70
|
+
--no-hooks Skip hook installation
|
|
71
|
+
--hook-port PORT Sidecar port for hooks (default: 8401)
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
awm setup --global Claude Code, global (recommended)
|
|
75
|
+
awm setup codex Codex CLI
|
|
76
|
+
awm setup cursor Cursor IDE
|
|
77
|
+
awm setup http Generic HTTP integration
|
|
78
|
+
awm doctor --all Check all configured targets
|
|
79
79
|
`.trim());
|
|
80
80
|
}
|
|
81
81
|
// ─── SETUP ──────────────────────────────────────
|
|
@@ -133,18 +133,18 @@ async function setup() {
|
|
|
133
133
|
const configAction = adapter.writeMcpConfig(ctx);
|
|
134
134
|
const instructionsAction = adapter.writeInstructions(ctx, skipInstructions);
|
|
135
135
|
const hooksAction = adapter.writeHooks(ctx, skipHooks);
|
|
136
|
-
console.log(`
|
|
137
|
-
AWM configured for ${adapter.name}${isGlobal ? ' (global)' : ''}
|
|
138
|
-
|
|
139
|
-
Agent ID: ${ctx.agentId}
|
|
140
|
-
DB path: ${ctx.dbPath}
|
|
141
|
-
${configAction}
|
|
142
|
-
${instructionsAction}
|
|
143
|
-
${hooksAction}
|
|
144
|
-
|
|
145
|
-
Next steps:
|
|
146
|
-
1. Restart ${adapter.name} to pick up the MCP server
|
|
147
|
-
2. Memory tools will appear automatically${adapter.id === 'codex' ? ' (verify with /mcp)' : ''}
|
|
136
|
+
console.log(`
|
|
137
|
+
AWM configured for ${adapter.name}${isGlobal ? ' (global)' : ''}
|
|
138
|
+
|
|
139
|
+
Agent ID: ${ctx.agentId}
|
|
140
|
+
DB path: ${ctx.dbPath}
|
|
141
|
+
${configAction}
|
|
142
|
+
${instructionsAction}
|
|
143
|
+
${hooksAction}
|
|
144
|
+
|
|
145
|
+
Next steps:
|
|
146
|
+
1. Restart ${adapter.name} to pick up the MCP server
|
|
147
|
+
2. Memory tools will appear automatically${adapter.id === 'codex' ? ' (verify with /mcp)' : ''}
|
|
148
148
|
`.trim());
|
|
149
149
|
}
|
|
150
150
|
// ─── DOCTOR ──────────────────────────────────────
|
|
@@ -307,7 +307,7 @@ async function exportMemories() {
|
|
|
307
307
|
// Collect unique agents
|
|
308
308
|
const agents = [...new Set(memories.map((m) => m.agent_id))];
|
|
309
309
|
const exportData = {
|
|
310
|
-
version: '0.7.
|
|
310
|
+
version: '0.7.7',
|
|
311
311
|
exported_at: new Date().toISOString(),
|
|
312
312
|
source_db: dbPath,
|
|
313
313
|
agent_filter: agentFilter,
|
|
@@ -372,23 +372,23 @@ async function importMemories() {
|
|
|
372
372
|
const Database = (await import('better-sqlite3')).default;
|
|
373
373
|
const db = new Database(dbPath);
|
|
374
374
|
// Ensure tables exist in target
|
|
375
|
-
db.exec(`
|
|
376
|
-
CREATE TABLE IF NOT EXISTS engrams (
|
|
377
|
-
id TEXT PRIMARY KEY, agent_id TEXT NOT NULL, concept TEXT NOT NULL, content TEXT NOT NULL,
|
|
378
|
-
embedding BLOB, confidence REAL NOT NULL DEFAULT 0.5, salience REAL NOT NULL DEFAULT 0.5,
|
|
379
|
-
access_count INTEGER NOT NULL DEFAULT 0, last_accessed TEXT NOT NULL, created_at TEXT NOT NULL,
|
|
380
|
-
salience_features TEXT NOT NULL DEFAULT '{}', reason_codes TEXT NOT NULL DEFAULT '[]',
|
|
381
|
-
stage TEXT NOT NULL DEFAULT 'active', ttl INTEGER, retracted INTEGER NOT NULL DEFAULT 0,
|
|
382
|
-
retracted_by TEXT, retracted_at TEXT, tags TEXT NOT NULL DEFAULT '[]',
|
|
383
|
-
episode_id TEXT, task_status TEXT, task_priority TEXT, blocked_by TEXT,
|
|
384
|
-
memory_class TEXT NOT NULL DEFAULT 'working', superseded_by TEXT, supersedes TEXT
|
|
385
|
-
);
|
|
386
|
-
CREATE TABLE IF NOT EXISTS associations (
|
|
387
|
-
id TEXT PRIMARY KEY, from_engram_id TEXT NOT NULL, to_engram_id TEXT NOT NULL,
|
|
388
|
-
weight REAL NOT NULL DEFAULT 0.1, confidence REAL NOT NULL DEFAULT 0.5,
|
|
389
|
-
type TEXT NOT NULL DEFAULT 'hebbian', activation_count INTEGER NOT NULL DEFAULT 0,
|
|
390
|
-
created_at TEXT NOT NULL, last_activated TEXT
|
|
391
|
-
);
|
|
375
|
+
db.exec(`
|
|
376
|
+
CREATE TABLE IF NOT EXISTS engrams (
|
|
377
|
+
id TEXT PRIMARY KEY, agent_id TEXT NOT NULL, concept TEXT NOT NULL, content TEXT NOT NULL,
|
|
378
|
+
embedding BLOB, confidence REAL NOT NULL DEFAULT 0.5, salience REAL NOT NULL DEFAULT 0.5,
|
|
379
|
+
access_count INTEGER NOT NULL DEFAULT 0, last_accessed TEXT NOT NULL, created_at TEXT NOT NULL,
|
|
380
|
+
salience_features TEXT NOT NULL DEFAULT '{}', reason_codes TEXT NOT NULL DEFAULT '[]',
|
|
381
|
+
stage TEXT NOT NULL DEFAULT 'active', ttl INTEGER, retracted INTEGER NOT NULL DEFAULT 0,
|
|
382
|
+
retracted_by TEXT, retracted_at TEXT, tags TEXT NOT NULL DEFAULT '[]',
|
|
383
|
+
episode_id TEXT, task_status TEXT, task_priority TEXT, blocked_by TEXT,
|
|
384
|
+
memory_class TEXT NOT NULL DEFAULT 'working', superseded_by TEXT, supersedes TEXT
|
|
385
|
+
);
|
|
386
|
+
CREATE TABLE IF NOT EXISTS associations (
|
|
387
|
+
id TEXT PRIMARY KEY, from_engram_id TEXT NOT NULL, to_engram_id TEXT NOT NULL,
|
|
388
|
+
weight REAL NOT NULL DEFAULT 0.1, confidence REAL NOT NULL DEFAULT 0.5,
|
|
389
|
+
type TEXT NOT NULL DEFAULT 'hebbian', activation_count INTEGER NOT NULL DEFAULT 0,
|
|
390
|
+
created_at TEXT NOT NULL, last_activated TEXT
|
|
391
|
+
);
|
|
392
392
|
`);
|
|
393
393
|
// Build dedup set if needed
|
|
394
394
|
const existingHashes = new Set();
|
|
@@ -403,15 +403,15 @@ async function importMemories() {
|
|
|
403
403
|
let imported = 0;
|
|
404
404
|
let skippedDupes = 0;
|
|
405
405
|
let skippedRetracted = 0;
|
|
406
|
-
const insertMem = db.prepare(`
|
|
407
|
-
INSERT INTO engrams (id, agent_id, concept, content, confidence, salience,
|
|
408
|
-
access_count, last_accessed, created_at, stage, tags, memory_class,
|
|
409
|
-
episode_id, task_status, task_priority, supersedes, superseded_by, retracted)
|
|
410
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
406
|
+
const insertMem = db.prepare(`
|
|
407
|
+
INSERT INTO engrams (id, agent_id, concept, content, confidence, salience,
|
|
408
|
+
access_count, last_accessed, created_at, stage, tags, memory_class,
|
|
409
|
+
episode_id, task_status, task_priority, supersedes, superseded_by, retracted)
|
|
410
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
411
411
|
`);
|
|
412
|
-
const insertAssoc = db.prepare(`
|
|
413
|
-
INSERT INTO associations (id, from_engram_id, to_engram_id, weight, type, activation_count, created_at)
|
|
414
|
-
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
|
|
412
|
+
const insertAssoc = db.prepare(`
|
|
413
|
+
INSERT INTO associations (id, from_engram_id, to_engram_id, weight, type, activation_count, created_at)
|
|
414
|
+
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
|
|
415
415
|
`);
|
|
416
416
|
const importTx = db.transaction(() => {
|
|
417
417
|
// Import memories
|
|
@@ -514,21 +514,21 @@ async function mergeMemories() {
|
|
|
514
514
|
targetDb.pragma('journal_mode = WAL');
|
|
515
515
|
targetDb.pragma('foreign_keys = ON');
|
|
516
516
|
// Ensure tables exist in target
|
|
517
|
-
targetDb.exec(`
|
|
518
|
-
CREATE TABLE IF NOT EXISTS engrams (
|
|
519
|
-
id TEXT PRIMARY KEY, agent_id TEXT NOT NULL, concept TEXT NOT NULL, content TEXT NOT NULL,
|
|
520
|
-
embedding BLOB, confidence REAL NOT NULL DEFAULT 0.5, salience REAL NOT NULL DEFAULT 0.5,
|
|
521
|
-
access_count INTEGER NOT NULL DEFAULT 0, last_accessed TEXT NOT NULL, created_at TEXT NOT NULL,
|
|
522
|
-
salience_features TEXT NOT NULL DEFAULT '{}', reason_codes TEXT NOT NULL DEFAULT '[]',
|
|
523
|
-
stage TEXT NOT NULL DEFAULT 'active', ttl INTEGER, retracted INTEGER NOT NULL DEFAULT 0,
|
|
524
|
-
retracted_by TEXT, retracted_at TEXT, tags TEXT NOT NULL DEFAULT '[]'
|
|
525
|
-
);
|
|
526
|
-
CREATE TABLE IF NOT EXISTS associations (
|
|
527
|
-
id TEXT PRIMARY KEY, from_engram_id TEXT NOT NULL, to_engram_id TEXT NOT NULL,
|
|
528
|
-
weight REAL NOT NULL DEFAULT 0.1, confidence REAL NOT NULL DEFAULT 0.5,
|
|
529
|
-
type TEXT NOT NULL DEFAULT 'hebbian', activation_count INTEGER NOT NULL DEFAULT 0,
|
|
530
|
-
created_at TEXT NOT NULL, last_activated TEXT NOT NULL
|
|
531
|
-
);
|
|
517
|
+
targetDb.exec(`
|
|
518
|
+
CREATE TABLE IF NOT EXISTS engrams (
|
|
519
|
+
id TEXT PRIMARY KEY, agent_id TEXT NOT NULL, concept TEXT NOT NULL, content TEXT NOT NULL,
|
|
520
|
+
embedding BLOB, confidence REAL NOT NULL DEFAULT 0.5, salience REAL NOT NULL DEFAULT 0.5,
|
|
521
|
+
access_count INTEGER NOT NULL DEFAULT 0, last_accessed TEXT NOT NULL, created_at TEXT NOT NULL,
|
|
522
|
+
salience_features TEXT NOT NULL DEFAULT '{}', reason_codes TEXT NOT NULL DEFAULT '[]',
|
|
523
|
+
stage TEXT NOT NULL DEFAULT 'active', ttl INTEGER, retracted INTEGER NOT NULL DEFAULT 0,
|
|
524
|
+
retracted_by TEXT, retracted_at TEXT, tags TEXT NOT NULL DEFAULT '[]'
|
|
525
|
+
);
|
|
526
|
+
CREATE TABLE IF NOT EXISTS associations (
|
|
527
|
+
id TEXT PRIMARY KEY, from_engram_id TEXT NOT NULL, to_engram_id TEXT NOT NULL,
|
|
528
|
+
weight REAL NOT NULL DEFAULT 0.1, confidence REAL NOT NULL DEFAULT 0.5,
|
|
529
|
+
type TEXT NOT NULL DEFAULT 'hebbian', activation_count INTEGER NOT NULL DEFAULT 0,
|
|
530
|
+
created_at TEXT NOT NULL, last_activated TEXT NOT NULL
|
|
531
|
+
);
|
|
532
532
|
`);
|
|
533
533
|
// Build dedupe hash set from existing target memories
|
|
534
534
|
const existingHashes = new Set();
|
|
@@ -538,16 +538,16 @@ async function mergeMemories() {
|
|
|
538
538
|
existingHashes.add(contentHash(row.concept, row.content));
|
|
539
539
|
console.log(`Target has ${existingHashes.size} unique memories (for dedupe)\n`);
|
|
540
540
|
}
|
|
541
|
-
const insertEngram = targetDb.prepare(`
|
|
542
|
-
INSERT OR IGNORE INTO engrams (id, agent_id, concept, content, confidence, salience, access_count,
|
|
543
|
-
last_accessed, created_at, salience_features, reason_codes, stage, ttl,
|
|
544
|
-
retracted, retracted_by, retracted_at, tags)
|
|
545
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
541
|
+
const insertEngram = targetDb.prepare(`
|
|
542
|
+
INSERT OR IGNORE INTO engrams (id, agent_id, concept, content, confidence, salience, access_count,
|
|
543
|
+
last_accessed, created_at, salience_features, reason_codes, stage, ttl,
|
|
544
|
+
retracted, retracted_by, retracted_at, tags)
|
|
545
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
546
546
|
`);
|
|
547
|
-
const insertAssoc = targetDb.prepare(`
|
|
548
|
-
INSERT OR IGNORE INTO associations (id, from_engram_id, to_engram_id, weight, confidence, type,
|
|
549
|
-
activation_count, created_at, last_activated)
|
|
550
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
547
|
+
const insertAssoc = targetDb.prepare(`
|
|
548
|
+
INSERT OR IGNORE INTO associations (id, from_engram_id, to_engram_id, weight, confidence, type,
|
|
549
|
+
activation_count, created_at, last_activated)
|
|
550
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
551
551
|
`);
|
|
552
552
|
let totalMemories = 0, totalAssociations = 0, totalSkipped = 0;
|
|
553
553
|
for (const sourcePath of sources) {
|
|
@@ -556,10 +556,10 @@ async function mergeMemories() {
|
|
|
556
556
|
continue;
|
|
557
557
|
}
|
|
558
558
|
const sourceDb = new Database(sourcePath, { readonly: true });
|
|
559
|
-
const engrams = sourceDb.prepare(`SELECT id, agent_id, concept, content, confidence, salience, access_count,
|
|
560
|
-
last_accessed, created_at, salience_features, reason_codes, stage, ttl,
|
|
559
|
+
const engrams = sourceDb.prepare(`SELECT id, agent_id, concept, content, confidence, salience, access_count,
|
|
560
|
+
last_accessed, created_at, salience_features, reason_codes, stage, ttl,
|
|
561
561
|
retracted, retracted_by, retracted_at, tags FROM engrams`).all();
|
|
562
|
-
const assocs = sourceDb.prepare(`SELECT id, from_engram_id, to_engram_id, weight, confidence, type,
|
|
562
|
+
const assocs = sourceDb.prepare(`SELECT id, from_engram_id, to_engram_id, weight, confidence, type,
|
|
563
563
|
activation_count, created_at, last_activated FROM associations`).all();
|
|
564
564
|
const idMap = new Map();
|
|
565
565
|
const skippedIds = new Set();
|
package/dist/core/salience.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ import type { EngramStore } from '../storage/sqlite.js';
|
|
|
12
12
|
export type SalienceEventType = 'decision' | 'friction' | 'surprise' | 'causal' | 'observation' | 'user_feedback';
|
|
13
13
|
/** Returns true if the content looks like direct user feedback that should auto-promote to canonical. */
|
|
14
14
|
export declare function detectUserFeedback(content: string): boolean;
|
|
15
|
+
/** Returns true if the content looks like a verified operational/batch record that should auto-bump salience. */
|
|
16
|
+
export declare function detectVerifiedFinding(content: string): boolean;
|
|
15
17
|
export interface SalienceInput {
|
|
16
18
|
content: string;
|
|
17
19
|
eventType?: SalienceEventType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"salience.d.ts","sourceRoot":"","sources":["../../src/core/salience.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,aAAa,GAAG,eAAe,CAAC;AAsBlH,yGAAyG;AACzG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sGAAsG;IACtG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAC9C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAeD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EACpB,eAAe,GAAE,MAAY,EAC7B,gBAAgB,GAAE,MAAY,GAC7B,cAAc,
|
|
1
|
+
{"version":3,"file":"salience.d.ts","sourceRoot":"","sources":["../../src/core/salience.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,aAAa,GAAG,eAAe,CAAC;AAsBlH,yGAAyG;AACzG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG3D;AA2BD,iHAAiH;AACjH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAO9D;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sGAAsG;IACtG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAC9C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAeD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EACpB,eAAe,GAAE,MAAY,EAC7B,gBAAgB,GAAE,MAAY,GAC7B,cAAc,CAgGhB;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CA+C5G;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EACrE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB,aAAa,CA6Cf"}
|
package/dist/core/salience.js
CHANGED
|
@@ -34,6 +34,41 @@ export function detectUserFeedback(content) {
|
|
|
34
34
|
return false;
|
|
35
35
|
return USER_FEEDBACK_PATTERN.test(content.trim());
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Auto-detect verified operational findings: batch records, completion summaries,
|
|
39
|
+
* incident reconciliations. These have low BM25 novelty (terminology repeats across
|
|
40
|
+
* runs — "USEF results submission", "Freshdesk triage batch") but the SPECIFIC
|
|
41
|
+
* event/ticket IDs, dates, and counts make each one uniquely valuable for future
|
|
42
|
+
* recall.
|
|
43
|
+
*
|
|
44
|
+
* Why this exists: the salience filter discarded a 6-event USEF batch summary at
|
|
45
|
+
* 0.14 (verified in activity log 2026-05-07T18:44:14) because the topic words
|
|
46
|
+
* collided with the long-running USEF history. The procedural memory beside it
|
|
47
|
+
* scored 0.70 — same topic, different content shape. The novelty signal alone
|
|
48
|
+
* can't distinguish a useful operational record from a duplicate observation.
|
|
49
|
+
*
|
|
50
|
+
* Pattern requires BOTH:
|
|
51
|
+
* 1. An action-verb header (Submitted/Finalized/Completed/Reconciled/Triaged/Posted/Resolved/Stamped)
|
|
52
|
+
* 2. At least 2 concrete identifiers — absolute dates (YYYY-MM-DD) OR numeric IDs
|
|
53
|
+
* with context (event \d+, ticket #\d+, USEF \d+, USEA \d+).
|
|
54
|
+
*
|
|
55
|
+
* Matched memories get a salience floor of 0.45 (active, but below canonical
|
|
56
|
+
* 0.7) — preserves the record without claiming source-of-truth status.
|
|
57
|
+
*/
|
|
58
|
+
const OPERATIONAL_VERB_PATTERN = /\b(Submitted|Finalized|Completed|Reconciled|Triaged|Posted|Resolved|Stamped|Pushed|Deployed|Migrated|Imported|Exported|Backfilled)\b/i;
|
|
59
|
+
const ISO_DATE_PATTERN = /\b\d{4}-\d{2}-\d{2}\b/g;
|
|
60
|
+
const CONCRETE_ID_PATTERN = /\b(?:events?|tickets?|comps?|comp_id|usef|usea|classes|class|cases?|orders?|payments?|member_id|horse_id|user_id|orgs?|#)\s*[#:]?\s*\d{3,}/gi;
|
|
61
|
+
/** Returns true if the content looks like a verified operational/batch record that should auto-bump salience. */
|
|
62
|
+
export function detectVerifiedFinding(content) {
|
|
63
|
+
if (typeof content !== 'string' || content.length === 0)
|
|
64
|
+
return false;
|
|
65
|
+
const text = content.trim();
|
|
66
|
+
if (!OPERATIONAL_VERB_PATTERN.test(text))
|
|
67
|
+
return false;
|
|
68
|
+
const dateCount = (text.match(ISO_DATE_PATTERN) || []).length;
|
|
69
|
+
const idCount = (text.match(CONCRETE_ID_PATTERN) || []).length;
|
|
70
|
+
return dateCount + idCount >= 2;
|
|
71
|
+
}
|
|
37
72
|
/**
|
|
38
73
|
* Weights for the salience scoring formula.
|
|
39
74
|
* Novelty is the strongest signal — new information should always be stored.
|
|
@@ -56,11 +91,21 @@ export function evaluateSalience(input, activeThreshold = 0.4, stagingThreshold
|
|
|
56
91
|
let resolvedEventType = input.eventType ?? 'observation';
|
|
57
92
|
let resolvedMemoryClass = input.memoryClass ?? 'working';
|
|
58
93
|
let autoPromoted = false;
|
|
94
|
+
let verifiedFindingFloor = false;
|
|
59
95
|
if (detectUserFeedback(input.content)) {
|
|
60
96
|
resolvedEventType = 'user_feedback';
|
|
61
97
|
resolvedMemoryClass = 'canonical';
|
|
62
98
|
autoPromoted = true;
|
|
63
99
|
}
|
|
100
|
+
else if (detectVerifiedFinding(input.content)) {
|
|
101
|
+
// Operational record: bump eventType to 'decision' (typeBonus +0.15) and
|
|
102
|
+
// remember to apply a 0.45 salience floor below. Do NOT promote to canonical
|
|
103
|
+
// — these records are verified, not source-of-truth.
|
|
104
|
+
if (resolvedEventType === 'observation') {
|
|
105
|
+
resolvedEventType = 'decision';
|
|
106
|
+
}
|
|
107
|
+
verifiedFindingFloor = true;
|
|
108
|
+
}
|
|
64
109
|
const features = {
|
|
65
110
|
surprise: input.surprise ?? 0,
|
|
66
111
|
decisionMade: input.decisionMade ?? false,
|
|
@@ -71,6 +116,8 @@ export function evaluateSalience(input, activeThreshold = 0.4, stagingThreshold
|
|
|
71
116
|
const reasonCodes = [];
|
|
72
117
|
if (autoPromoted)
|
|
73
118
|
reasonCodes.push('auto:user_feedback');
|
|
119
|
+
if (verifiedFindingFloor)
|
|
120
|
+
reasonCodes.push('auto:verified_finding');
|
|
74
121
|
// Novelty: 1.0 = completely new info, 0 = exact duplicate exists
|
|
75
122
|
// Default to 0.8 (assume mostly novel) when caller doesn't check
|
|
76
123
|
const novelty = input.novelty ?? 0.8;
|
|
@@ -128,6 +175,10 @@ export function evaluateSalience(input, activeThreshold = 0.4, stagingThreshold
|
|
|
128
175
|
else if (memoryClass === 'ephemeral') {
|
|
129
176
|
reasonCodes.push('class:ephemeral');
|
|
130
177
|
}
|
|
178
|
+
else if (verifiedFindingFloor) {
|
|
179
|
+
// Verified operational record: 0.45 floor — keeps it active without canonical promotion
|
|
180
|
+
score = Math.max(score, 0.45);
|
|
181
|
+
}
|
|
131
182
|
let disposition;
|
|
132
183
|
if (memoryClass === 'canonical') {
|
|
133
184
|
// Canonical always goes active — they represent current truth
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"salience.js","sourceRoot":"","sources":["../../src/core/salience.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;GAQG;AAOH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,qBAAqB,GAAG,qRAAqR,CAAC;AAEpT,yGAAyG;AACzG,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,OAAO,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACpD,CAAC;AAsBD;;;;GAIG;AACH,MAAM,OAAO,GAAG;IACd,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,IAAI;IACjB,gBAAgB,EAAE,GAAG;IACrB,OAAO,EAAE,IAAI;CACd,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAoB,EACpB,kBAA0B,GAAG,EAC7B,mBAA2B,GAAG;IAE9B,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,IAAI,iBAAiB,GAAsB,KAAK,CAAC,SAAS,IAAI,aAAa,CAAC;IAC5E,IAAI,mBAAmB,GAAgB,KAAK,CAAC,WAAW,IAAI,SAAS,CAAC;IACtE,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,iBAAiB,GAAG,eAAe,CAAC;QACpC,mBAAmB,GAAG,WAAW,CAAC;QAClC,YAAY,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,MAAM,QAAQ,GAAqB;QACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC;QAC7B,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,KAAK;QACzC,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,CAAC;QACnC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,CAAC;QAC7C,SAAS,EAAE,iBAAiB;KAC7B,CAAC;IAEF,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,YAAY;QAAE,WAAW,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"salience.js","sourceRoot":"","sources":["../../src/core/salience.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activation.d.ts","sourceRoot":"","sources":["../../src/engine/activation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAA6C,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAItG,OAAO,KAAK,EACF,gBAAgB,EAAE,eAAe,EAC1C,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAoIxD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC;gBAEnC,KAAK,EAAE,WAAW;IAM9B;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"activation.d.ts","sourceRoot":"","sources":["../../src/engine/activation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAA6C,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAItG,OAAO,KAAK,EACF,gBAAgB,EAAE,eAAe,EAC1C,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAoIxD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC;gBAEnC,KAAK,EAAE,WAAW;IAM9B;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqiBnE;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAKnC;IAEF,OAAO,CAAC,SAAS;IA+FjB;;;;OAIG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM;IAwBjE,OAAO,CAAC,OAAO;CAchB"}
|
|
@@ -268,10 +268,66 @@ export class ActivationEngine {
|
|
|
268
268
|
? Math.sqrt(simValues.reduce((sum, s) => sum + (s - simMean) ** 2, 0) / simValues.length) : 0.15;
|
|
269
269
|
// Floor stddev at 0.10 to prevent z-score inflation with small candidate pools
|
|
270
270
|
const simStdDev = Math.max(rawStdDev, 0.10);
|
|
271
|
-
// Phase
|
|
272
|
-
|
|
271
|
+
// Phase 3a': Cheap pre-filter — deep scoring (with associations + tokenize-content
|
|
272
|
+
// + Hebbian/centrality) only runs for candidates that have a non-trivial signal
|
|
273
|
+
// we can verify cheaply. Anything that fails this filter would score below the
|
|
274
|
+
// relevance gate (textMatch > 0.1) anyway and produce a near-zero composite.
|
|
275
|
+
//
|
|
276
|
+
// Why: phase-breakdown spike (2026-05-08) showed assocBatch over 10K candidates
|
|
277
|
+
// costs 1500ms and represents 68% of recall latency. Most candidates have zero
|
|
278
|
+
// BM25 hit, low cosine similarity, and no concept-jaccard match — so fetching
|
|
279
|
+
// their associations and tokenizing their full content is wasted work.
|
|
280
|
+
//
|
|
281
|
+
// Survival criteria (any one):
|
|
282
|
+
// 1. BM25 hit (bm25Score > 0)
|
|
283
|
+
// 2. Cosine z-score above the gate (would produce non-zero vectorMatch)
|
|
284
|
+
// 3. Concept-token overlap with query (cheap to compute — concept is short)
|
|
285
|
+
//
|
|
286
|
+
// Graph walk preserves correctness: it only boosts neighbors whose own textMatch
|
|
287
|
+
// already passes the relevance floor (0.05-0.1), and any candidate meeting that
|
|
288
|
+
// floor would also pass this filter (they'd have either BM25, cosine, or concept
|
|
289
|
+
// jaccard match). So no graph-walkable target is lost.
|
|
290
|
+
//
|
|
291
|
+
// Disable via AWM_DISABLE_POOL_FILTER=1 (for A/B testing or if a regression appears)
|
|
292
|
+
const poolFilterEnabled = process.env.AWM_DISABLE_POOL_FILTER !== '1';
|
|
293
|
+
let filteredCandidates;
|
|
294
|
+
if (poolFilterEnabled) {
|
|
295
|
+
filteredCandidates = [];
|
|
296
|
+
for (const engram of candidates) {
|
|
297
|
+
const bm25 = bm25ScoreMap.get(engram.id) ?? 0;
|
|
298
|
+
if (bm25 > 0) {
|
|
299
|
+
filteredCandidates.push(engram);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const sim = rawCosineSims.get(engram.id);
|
|
303
|
+
if (sim !== undefined) {
|
|
304
|
+
const z = (sim - simMean) / simStdDev;
|
|
305
|
+
if (z > adaptive.zScoreGate) {
|
|
306
|
+
filteredCandidates.push(engram);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Cheap concept jaccard — concepts are short (typically 3-8 tokens)
|
|
311
|
+
const conceptTokens = tokenize(engram.concept);
|
|
312
|
+
if (conceptTokens.size === 0)
|
|
313
|
+
continue;
|
|
314
|
+
let conceptOverlap = 0;
|
|
315
|
+
for (const w of conceptTokens)
|
|
316
|
+
if (queryTokens.has(w))
|
|
317
|
+
conceptOverlap++;
|
|
318
|
+
if (conceptOverlap > 0)
|
|
319
|
+
filteredCandidates.push(engram);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
filteredCandidates = candidates;
|
|
324
|
+
}
|
|
325
|
+
// Phase 3b: Score each filtered candidate with per-phase breakdown
|
|
326
|
+
// Batch-fetch associations only for survivors (was N+1 over 10K, now 1 query over ~200)
|
|
327
|
+
const associationsByEngram = this.store.getAssociationsForBatch(filteredCandidates.map(e => e.id));
|
|
328
|
+
const scored = filteredCandidates.map(engram => {
|
|
273
329
|
const ageDays = (Date.now() - engram.createdAt.getTime()) / (1000 * 60 * 60 * 24);
|
|
274
|
-
const associations =
|
|
330
|
+
const associations = associationsByEngram.get(engram.id) ?? [];
|
|
275
331
|
// --- Text relevance (keyword signals) ---
|
|
276
332
|
// Signal 1: BM25 continuous score (0-1, from FTS5 rank)
|
|
277
333
|
const bm25Score = bm25ScoreMap.get(engram.id) ?? 0;
|