agent-working-memory 0.11.0 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -297
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +63 -3
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +358 -306
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +29 -7
- package/dist/api/routes.js.map +1 -1
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +174 -170
- package/dist/coordination/routes.js.map +1 -1
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +4 -1
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/entity-extract.d.ts +3 -0
- package/dist/core/entity-extract.d.ts.map +1 -0
- package/dist/core/entity-extract.js +47 -0
- package/dist/core/entity-extract.js.map +1 -0
- package/dist/core/format-recall.d.ts +16 -0
- package/dist/core/format-recall.d.ts.map +1 -0
- package/dist/core/format-recall.js +24 -0
- package/dist/core/format-recall.js.map +1 -0
- package/dist/core/query-expander.js +1 -1
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.js +1 -1
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +14 -2
- package/dist/core/salience.js.map +1 -1
- package/dist/core/whoami.d.ts +24 -0
- package/dist/core/whoami.d.ts.map +1 -0
- package/dist/core/whoami.js +66 -0
- package/dist/core/whoami.js.map +1 -0
- package/dist/core/write-pipeline.d.ts +9 -0
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +109 -68
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/core/write-telemetry.d.ts +33 -0
- package/dist/core/write-telemetry.d.ts.map +1 -0
- package/dist/core/write-telemetry.js +110 -0
- package/dist/core/write-telemetry.js.map +1 -0
- package/dist/engine/activation.d.ts +22 -12
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +133 -17
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts +1 -1
- package/dist/engine/consolidation-scheduler.js +1 -1
- package/dist/engine/consolidation.d.ts +1 -0
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +18 -0
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +5 -1
- package/dist/engine/eval.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +26 -0
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +30 -0
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +222 -108
- package/dist/mcp.js.map +1 -1
- package/dist/recipes/index.d.ts +57 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +81 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/storage/pglite-schema.d.ts.map +1 -1
- package/dist/storage/pglite-schema.js +27 -0
- package/dist/storage/pglite-schema.js.map +1 -1
- package/dist/storage/pglite.d.ts +5 -0
- package/dist/storage/pglite.d.ts.map +1 -1
- package/dist/storage/pglite.js +180 -138
- package/dist/storage/pglite.js.map +1 -1
- package/dist/storage/postgres.d.ts +5 -0
- package/dist/storage/postgres.d.ts.map +1 -1
- package/dist/storage/postgres.js +180 -138
- package/dist/storage/postgres.js.map +1 -1
- package/dist/storage/sqlite.d.ts +9 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +394 -326
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +14 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/claude-code.ts +66 -3
- package/src/adapters/common.ts +567 -515
- package/src/api/routes.ts +999 -971
- package/src/coordination/routes.ts +2155 -2150
- package/src/core/embeddings.ts +4 -1
- package/src/core/entity-extract.ts +47 -0
- package/src/core/format-recall.ts +25 -0
- package/src/core/query-expander.ts +1 -1
- package/src/core/reranker.ts +1 -1
- package/src/core/salience.ts +529 -514
- package/src/core/whoami.ts +92 -0
- package/src/core/write-pipeline.ts +60 -8
- package/src/core/write-telemetry.ts +131 -0
- package/src/engine/activation.ts +1468 -1369
- package/src/engine/consolidation-scheduler.ts +1 -1
- package/src/engine/consolidation.ts +887 -869
- package/src/engine/eval.ts +6 -1
- package/src/hooks/sidecar.ts +55 -0
- package/src/index.ts +248 -227
- package/src/mcp.ts +1387 -1270
- package/src/recipes/index.ts +125 -0
- package/src/storage/pglite-schema.ts +27 -0
- package/src/storage/pglite.ts +1420 -1372
- package/src/storage/postgres.ts +1523 -1475
- package/src/storage/sqlite.ts +1936 -1861
- package/src/types/engram.ts +22 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Write-path telemetry (D1, 2026-07-30).
|
|
5
|
+
*
|
|
6
|
+
* Purpose: prove the mechanism behind slow writes before building the
|
|
7
|
+
* serialized write service. Captures, per write:
|
|
8
|
+
* - phase wall times (embed / novelty / persist)
|
|
9
|
+
* - event-loop lag at write completion (was this process's loop blocked?)
|
|
10
|
+
* - whether a consolidation cycle was running IN THIS PROCESS
|
|
11
|
+
* - whether SQLITE_BUSY was hit
|
|
12
|
+
* - embed-model cold-load duration (first inference)
|
|
13
|
+
*
|
|
14
|
+
* Always-on slow-write warning: any write slower than AWM_SLOW_WRITE_MS
|
|
15
|
+
* (default 250ms; set 0 to disable) emits ONE structured stderr line.
|
|
16
|
+
* AWM_PROFILE_WRITE=1 continues to log every write as before.
|
|
17
|
+
*
|
|
18
|
+
* Design notes: additive module — no existing export changes (MWA vendors
|
|
19
|
+
* dist internals; nothing here alters existing paths). All timers are
|
|
20
|
+
* unref'd so they never hold the process open.
|
|
21
|
+
*/
|
|
22
|
+
const SLOW_WRITE_MS = (() => {
|
|
23
|
+
const v = Number(process.env.AWM_SLOW_WRITE_MS ?? '250');
|
|
24
|
+
return Number.isFinite(v) ? v : 250;
|
|
25
|
+
})();
|
|
26
|
+
// ---------- Event-loop lag monitor ----------
|
|
27
|
+
const TICK_MS = 500;
|
|
28
|
+
let lastTick = 0;
|
|
29
|
+
let maxLagSinceRead = 0;
|
|
30
|
+
let monitorStarted = false;
|
|
31
|
+
/** Start the loop-lag heartbeat. Idempotent; call once at process boot. */
|
|
32
|
+
export function startLoopLagMonitor() {
|
|
33
|
+
if (monitorStarted)
|
|
34
|
+
return;
|
|
35
|
+
monitorStarted = true;
|
|
36
|
+
lastTick = performance.now();
|
|
37
|
+
const t = setInterval(() => {
|
|
38
|
+
const now = performance.now();
|
|
39
|
+
const lag = now - lastTick - TICK_MS;
|
|
40
|
+
if (lag > maxLagSinceRead)
|
|
41
|
+
maxLagSinceRead = lag;
|
|
42
|
+
lastTick = now;
|
|
43
|
+
}, TICK_MS);
|
|
44
|
+
if (typeof t.unref === 'function')
|
|
45
|
+
t.unref();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Max observed loop lag (ms beyond the expected tick) since the last read.
|
|
49
|
+
* Reading resets the max. A large value means this process's event loop was
|
|
50
|
+
* blocked (in-process ML, consolidation, sync SQL) during the window.
|
|
51
|
+
*/
|
|
52
|
+
export function readMaxLoopLag() {
|
|
53
|
+
const v = maxLagSinceRead;
|
|
54
|
+
maxLagSinceRead = 0;
|
|
55
|
+
return Math.max(0, Math.round(v));
|
|
56
|
+
}
|
|
57
|
+
// ---------- In-process consolidation state ----------
|
|
58
|
+
let consolidationActive = false;
|
|
59
|
+
let consolidationAgent = null;
|
|
60
|
+
let consolidationStartedAt = 0;
|
|
61
|
+
export function setConsolidationActive(active, agentId) {
|
|
62
|
+
consolidationActive = active;
|
|
63
|
+
consolidationAgent = active ? (agentId ?? null) : null;
|
|
64
|
+
consolidationStartedAt = active ? performance.now() : 0;
|
|
65
|
+
}
|
|
66
|
+
export function getConsolidationState() {
|
|
67
|
+
return {
|
|
68
|
+
active: consolidationActive,
|
|
69
|
+
agentId: consolidationAgent,
|
|
70
|
+
runningMs: consolidationActive ? Math.round(performance.now() - consolidationStartedAt) : 0,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// ---------- Model cold-load record ----------
|
|
74
|
+
let modelColdLoadMs = null;
|
|
75
|
+
export function noteModelLoad(ms) {
|
|
76
|
+
if (modelColdLoadMs === null) {
|
|
77
|
+
modelColdLoadMs = Math.round(ms);
|
|
78
|
+
process.stderr.write(`[awm] embed model cold load: ${modelColdLoadMs}ms\n`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function getModelColdLoadMs() {
|
|
82
|
+
return modelColdLoadMs;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Report a completed write. Emits one structured stderr line when the write
|
|
86
|
+
* exceeded the slow threshold (always-on) — including loop lag and in-process
|
|
87
|
+
* consolidation state, the two signals that distinguish "DB lock wait" from
|
|
88
|
+
* "my own event loop was blocked".
|
|
89
|
+
*/
|
|
90
|
+
export function reportWrite(t) {
|
|
91
|
+
if (SLOW_WRITE_MS <= 0)
|
|
92
|
+
return;
|
|
93
|
+
if (t.totalMs < SLOW_WRITE_MS)
|
|
94
|
+
return;
|
|
95
|
+
const consol = getConsolidationState();
|
|
96
|
+
const parts = [
|
|
97
|
+
`[awm] SLOW WRITE ${Math.round(t.totalMs)}ms`,
|
|
98
|
+
`agent=${t.agentId}`,
|
|
99
|
+
`action=${t.action}`,
|
|
100
|
+
`embed=${Math.round(t.embedMs)}ms`,
|
|
101
|
+
`novelty=${Math.round(t.noveltyMs)}ms`,
|
|
102
|
+
`persist=${Math.round(t.persistMs)}ms`,
|
|
103
|
+
`loopLagMax=${readMaxLoopLag()}ms`,
|
|
104
|
+
`consolidating=${consol.active ? `yes(${consol.agentId ?? '?'},${consol.runningMs}ms)` : 'no'}`,
|
|
105
|
+
`busy=${t.busyHit ? 'yes' : 'no'}`,
|
|
106
|
+
modelColdLoadMs !== null ? `modelColdLoad=${modelColdLoadMs}ms` : 'modelColdLoad=none',
|
|
107
|
+
];
|
|
108
|
+
process.stderr.write(parts.join(' ') + '\n');
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=write-telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-telemetry.js","sourceRoot":"","sources":["../../src/core/write-telemetry.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AAEtC;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE;IAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,KAAK,CAAC,CAAC;IACzD,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACtC,CAAC,CAAC,EAAE,CAAC;AAEL,+CAA+C;AAE/C,MAAM,OAAO,GAAG,GAAG,CAAC;AACpB,IAAI,QAAQ,GAAG,CAAC,CAAC;AACjB,IAAI,eAAe,GAAG,CAAC,CAAC;AACxB,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,2EAA2E;AAC3E,MAAM,UAAU,mBAAmB;IACjC,IAAI,cAAc;QAAE,OAAO;IAC3B,cAAc,GAAG,IAAI,CAAC;IACtB,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE;QACzB,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,GAAG,GAAG,QAAQ,GAAG,OAAO,CAAC;QACrC,IAAI,GAAG,GAAG,eAAe;YAAE,eAAe,GAAG,GAAG,CAAC;QACjD,QAAQ,GAAG,GAAG,CAAC;IACjB,CAAC,EAAE,OAAO,CAAC,CAAC;IACZ,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,UAAU;QAAE,CAAC,CAAC,KAAK,EAAE,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,CAAC,GAAG,eAAe,CAAC;IAC1B,eAAe,GAAG,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,uDAAuD;AAEvD,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAChC,IAAI,kBAAkB,GAAkB,IAAI,CAAC;AAC7C,IAAI,sBAAsB,GAAG,CAAC,CAAC;AAE/B,MAAM,UAAU,sBAAsB,CAAC,MAAe,EAAE,OAAgB;IACtE,mBAAmB,GAAG,MAAM,CAAC;IAC7B,kBAAkB,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,sBAAsB,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO;QACL,MAAM,EAAE,mBAAmB;QAC3B,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;KAC5F,CAAC;AACJ,CAAC;AAED,+CAA+C;AAE/C,IAAI,eAAe,GAAkB,IAAI,CAAC;AAE1C,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;QAC7B,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,eAAe,MAAM,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,eAAe,CAAC;AACzB,CAAC;AAcD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,CAAe;IACzC,IAAI,aAAa,IAAI,CAAC;QAAE,OAAO;IAC/B,IAAI,CAAC,CAAC,OAAO,GAAG,aAAa;QAAE,OAAO;IACtC,MAAM,MAAM,GAAG,qBAAqB,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG;QACZ,oBAAoB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI;QAC7C,SAAS,CAAC,CAAC,OAAO,EAAE;QACpB,UAAU,CAAC,CAAC,MAAM,EAAE;QACpB,SAAS,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI;QAClC,WAAW,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI;QACtC,WAAW,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI;QACtC,cAAc,cAAc,EAAE,IAAI;QAClC,iBAAiB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,OAAO,IAAI,GAAG,IAAI,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QAC/F,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QAClC,eAAe,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB,eAAe,IAAI,CAAC,CAAC,CAAC,oBAAoB;KACvF,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Activation Pipeline — the core retrieval engine.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 1.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
4
|
+
* Cognitive retrieval pipeline — phases as shipped (D4 honesty pass 2026-07-30;
|
|
5
|
+
* default-OFF phases are marked, since operators tune from this header):
|
|
6
|
+
* -1. Coreference expansion (conditional: query contains pronouns)
|
|
7
|
+
* 0. Query expansion (flan-t5-small; caller-gated, MCP default ON)
|
|
8
|
+
* 1. Vector embedding (bge-small 384d)
|
|
9
|
+
* 2. Parallel retrieval (dual FTS5/BM25 + native vector top-K)
|
|
10
|
+
* 3. Per-candidate scoring (BM25, Jaccard, cosine floor, ACT-R decay,
|
|
11
|
+
* Hebbian boost, confidence gate — computed together in phase 3b)
|
|
12
|
+
* 3.5 Rocchio pseudo-relevance feedback (conditional on BM25 signal)
|
|
13
|
+
* 3.7 Entity-bridge boost (default ON; AWM_DISABLE_ENTITY_BRIDGE=1)
|
|
14
|
+
* 3.5 Entity-index candidate injection (DEFAULT OFF; AWM_ENTITY_INDEX_FETCH=1 —
|
|
15
|
+
* D11 2026-07-30: D9 inverted-index lookup of query-named entities; injected
|
|
16
|
+
* candidates get no boost but a guaranteed rerank audition)
|
|
17
|
+
* 3.75 Query-conditioned entity bridge (DEFAULT OFF; AWM_QUERY_BRIDGE=1)
|
|
18
|
+
* 4/5 Spreading-activation graph walk (DEFAULT OFF; AWM_SPREAD=1 — parked
|
|
19
|
+
* after displacing-gold regressions; see design-proposals D11)
|
|
20
|
+
* 6. Filter + sort into rerank pool (wide pool since 0.9.0)
|
|
21
|
+
* 7. Cross-encoder rerank (ms-marco; clear-winner skip unless
|
|
22
|
+
* AWM_DISABLE_RERANK_SKIP=1)
|
|
23
|
+
* 8. Multi-channel OOD detection + agreement gate; supersession penalty;
|
|
24
|
+
* abstention enforced only when caller passes require_confidence
|
|
25
|
+
* 9. Final sort, granularity, confidence attach
|
|
16
26
|
*
|
|
17
27
|
* Logs activation events for eval metrics.
|
|
18
28
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activation.d.ts","sourceRoot":"","sources":["../../src/engine/activation.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"activation.d.ts","sourceRoot":"","sources":["../../src/engine/activation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,EAA6C,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAKtG,OAAO,KAAK,EACF,gBAAgB,EAAE,eAAe,EAC1C,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAoIvE,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;IA66BnE;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAKnC;YAEY,SAAS;IA+FvB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;YACW,gBAAgB;IAwJ9B;;;;OAIG;IACG,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBhF,OAAO,CAAC,OAAO;CAchB"}
|
|
@@ -3,18 +3,28 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Activation Pipeline — the core retrieval engine.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* 1.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
6
|
+
* Cognitive retrieval pipeline — phases as shipped (D4 honesty pass 2026-07-30;
|
|
7
|
+
* default-OFF phases are marked, since operators tune from this header):
|
|
8
|
+
* -1. Coreference expansion (conditional: query contains pronouns)
|
|
9
|
+
* 0. Query expansion (flan-t5-small; caller-gated, MCP default ON)
|
|
10
|
+
* 1. Vector embedding (bge-small 384d)
|
|
11
|
+
* 2. Parallel retrieval (dual FTS5/BM25 + native vector top-K)
|
|
12
|
+
* 3. Per-candidate scoring (BM25, Jaccard, cosine floor, ACT-R decay,
|
|
13
|
+
* Hebbian boost, confidence gate — computed together in phase 3b)
|
|
14
|
+
* 3.5 Rocchio pseudo-relevance feedback (conditional on BM25 signal)
|
|
15
|
+
* 3.7 Entity-bridge boost (default ON; AWM_DISABLE_ENTITY_BRIDGE=1)
|
|
16
|
+
* 3.5 Entity-index candidate injection (DEFAULT OFF; AWM_ENTITY_INDEX_FETCH=1 —
|
|
17
|
+
* D11 2026-07-30: D9 inverted-index lookup of query-named entities; injected
|
|
18
|
+
* candidates get no boost but a guaranteed rerank audition)
|
|
19
|
+
* 3.75 Query-conditioned entity bridge (DEFAULT OFF; AWM_QUERY_BRIDGE=1)
|
|
20
|
+
* 4/5 Spreading-activation graph walk (DEFAULT OFF; AWM_SPREAD=1 — parked
|
|
21
|
+
* after displacing-gold regressions; see design-proposals D11)
|
|
22
|
+
* 6. Filter + sort into rerank pool (wide pool since 0.9.0)
|
|
23
|
+
* 7. Cross-encoder rerank (ms-marco; clear-winner skip unless
|
|
24
|
+
* AWM_DISABLE_RERANK_SKIP=1)
|
|
25
|
+
* 8. Multi-channel OOD detection + agreement gate; supersession penalty;
|
|
26
|
+
* abstention enforced only when caller passes require_confidence
|
|
27
|
+
* 9. Final sort, granularity, confidence attach
|
|
18
28
|
*
|
|
19
29
|
* Logs activation events for eval metrics.
|
|
20
30
|
*/
|
|
@@ -255,7 +265,12 @@ export class ActivationEngine {
|
|
|
255
265
|
// its slim cache + JS cosine (same as before but encapsulated). Z-score
|
|
256
266
|
// normalization is replaced with a mode-adaptive raw-cosine floor —
|
|
257
267
|
// simpler, faster, model-tuned for BGE-small embeddings.
|
|
258
|
-
|
|
268
|
+
// D4 (2026-07-30): AWM_DISABLE_POOL_FILTER=1 was documented since 0.7.x but
|
|
269
|
+
// never implemented after the 0.8.x native-vector refactor absorbed the
|
|
270
|
+
// pool pre-filter. Honor its documented semantics: score ALL active
|
|
271
|
+
// candidates (no top-K cut, no similarity floor).
|
|
272
|
+
const POOL_FILTER_DISABLED = process.env.AWM_DISABLE_POOL_FILTER === '1';
|
|
273
|
+
const VECTOR_TOP_K = POOL_FILTER_DISABLED ? Number.MAX_SAFE_INTEGER : Math.max(50, limit * 5);
|
|
259
274
|
// Tokenize query once (used by scoring)
|
|
260
275
|
const queryTokens = tokenize(query.context);
|
|
261
276
|
// Phase 3a: native vector search across agents — top-K by cosine.
|
|
@@ -271,9 +286,9 @@ export class ActivationEngine {
|
|
|
271
286
|
// entirely. The vectorMatch scoring floor (0.50 targeted / 0.35 exploratory)
|
|
272
287
|
// still suppresses low-confidence matches in the final score.
|
|
273
288
|
// Env override: AWM_SIM_CANDIDATE_FLOOR_TARGETED, AWM_SIM_CANDIDATE_FLOOR_EXPLORATORY.
|
|
274
|
-
const SIM_CANDIDATE_FLOOR = adaptive.zScoreGate > 0.5
|
|
289
|
+
const SIM_CANDIDATE_FLOOR = POOL_FILTER_DISABLED ? -1 : (adaptive.zScoreGate > 0.5
|
|
275
290
|
? Number(process.env.AWM_SIM_CANDIDATE_FLOOR_TARGETED ?? 0.40)
|
|
276
|
-
: Number(process.env.AWM_SIM_CANDIDATE_FLOOR_EXPLORATORY ?? 0.30);
|
|
291
|
+
: Number(process.env.AWM_SIM_CANDIDATE_FLOOR_EXPLORATORY ?? 0.30));
|
|
277
292
|
const rawCosineSims = new Map();
|
|
278
293
|
const vectorHits = [];
|
|
279
294
|
if (queryEmbedding) {
|
|
@@ -376,6 +391,75 @@ export class ActivationEngine {
|
|
|
376
391
|
catch { /* best-effort: entity fetch never breaks recall */ }
|
|
377
392
|
}
|
|
378
393
|
}
|
|
394
|
+
// ── D11 (2026-07-30): ENTITY-INDEX CANDIDATE INJECTION (default-OFF, AWM_ENTITY_INDEX_FETCH=1) ──
|
|
395
|
+
// The D9 inverted index resolves query-NAMED entities ("Seetha", "ticket 18999") to every
|
|
396
|
+
// engram indexed under them — deterministic exact lookup, immune to embedding/BM25 vocabulary
|
|
397
|
+
// mismatch. Injected candidates get NO score boost; instead they are GUARANTEED a rerank
|
|
398
|
+
// audition (exempt from the topN cut, the minScore pool filter, the rerank-pool slice, and
|
|
399
|
+
// the rerank-skip heuristic) and the cross-encoder alone decides whether they surface.
|
|
400
|
+
// This is the guarded successor to AWM_ENTITY_FETCH above, whose failure mode was measured:
|
|
401
|
+
// injected gold entered the pool but was cut before the reranker ever scored it. Bounded
|
|
402
|
+
// (AWM_ENTITY_INDEX_CAP, default 12) so the audition costs at most one extra rerank batch.
|
|
403
|
+
const injectedIds = new Set();
|
|
404
|
+
if (process.env.AWM_ENTITY_INDEX_FETCH === '1') {
|
|
405
|
+
try {
|
|
406
|
+
const IDX_CAP = Number(process.env.AWM_ENTITY_INDEX_CAP ?? 12);
|
|
407
|
+
const IDX_QSTOP = new Set(['what', 'who', 'when', 'where', 'why', 'how', 'which', 'whose',
|
|
408
|
+
'the', 'this', 'that', 'and', 'but', 'for', 'did', 'does', 'is', 'are', 'was', 'were',
|
|
409
|
+
'tell', 'about', 'they', 'them', 'write', 'reply', 'list', 'please', 'remember', 'confirm']);
|
|
410
|
+
const terms = new Set();
|
|
411
|
+
for (const m of query.context.matchAll(/\b[A-Z][A-Za-z]{2,}\b/g)) {
|
|
412
|
+
const w = m[0].toLowerCase();
|
|
413
|
+
if (!IDX_QSTOP.has(w))
|
|
414
|
+
terms.add(w);
|
|
415
|
+
}
|
|
416
|
+
for (const m of query.context.matchAll(/\b\d{4,}\b/g))
|
|
417
|
+
terms.add(m[0]); // bare ids: tickets, members, events
|
|
418
|
+
let added = 0;
|
|
419
|
+
for (const term of Array.from(terms).slice(0, 4)) {
|
|
420
|
+
if (added >= IDX_CAP)
|
|
421
|
+
break;
|
|
422
|
+
const entities = await this.store.searchEntities(term, 6);
|
|
423
|
+
for (const entity of entities) {
|
|
424
|
+
if (added >= IDX_CAP)
|
|
425
|
+
break;
|
|
426
|
+
for (const agentId of agentIds) {
|
|
427
|
+
if (added >= IDX_CAP)
|
|
428
|
+
break;
|
|
429
|
+
for (const id of await this.store.getEngramIdsByEntity(entity, agentId)) {
|
|
430
|
+
if (added >= IDX_CAP)
|
|
431
|
+
break;
|
|
432
|
+
if (injectedIds.has(id))
|
|
433
|
+
continue;
|
|
434
|
+
// Already a candidate via BM25/vector? Still VOUCH for it: a weak in-pool
|
|
435
|
+
// candidate the index confirms would otherwise die at the minScore/topN
|
|
436
|
+
// cuts unguarded. The audition guarantee is about the index match, not
|
|
437
|
+
// about how the candidate entered the pool.
|
|
438
|
+
const inPool = candidateMap.get(id);
|
|
439
|
+
if (inPool) {
|
|
440
|
+
if (inPool.stage === 'active' && !inPool.retracted && !inPool.supersededBy) {
|
|
441
|
+
injectedIds.add(id);
|
|
442
|
+
added++;
|
|
443
|
+
}
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
const n = await this.store.getEngram(id);
|
|
447
|
+
if (!n || n.stage !== 'active' || n.retracted || n.supersededBy)
|
|
448
|
+
continue;
|
|
449
|
+
if (query.memoryType && n.memoryType !== query.memoryType)
|
|
450
|
+
continue;
|
|
451
|
+
candidateMap.set(id, n);
|
|
452
|
+
injectedIds.add(id);
|
|
453
|
+
added++;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (added > 0)
|
|
459
|
+
candidates = Array.from(candidateMap.values());
|
|
460
|
+
}
|
|
461
|
+
catch { /* index injection never breaks recall */ }
|
|
462
|
+
}
|
|
379
463
|
if (candidates.length === 0)
|
|
380
464
|
return [];
|
|
381
465
|
// Phase 3b: Score each candidate with per-phase breakdown
|
|
@@ -692,6 +776,14 @@ export class ActivationEngine {
|
|
|
692
776
|
// Tunable via AWM_TOPN_MULT.
|
|
693
777
|
const topNMult = Number(process.env.AWM_TOPN_MULT ?? 8);
|
|
694
778
|
const topN = sorted.slice(0, limit * topNMult);
|
|
779
|
+
// D11 guard 1/4: injected entity-index candidates ride into the graph/rerank stages even
|
|
780
|
+
// when their (boost-free) composite fell below the topN cut.
|
|
781
|
+
if (injectedIds.size > 0) {
|
|
782
|
+
for (const item of sorted.slice(limit * topNMult)) {
|
|
783
|
+
if (injectedIds.has(item.engram.id))
|
|
784
|
+
topN.push(item);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
695
787
|
if (process.env.AWM_SPREAD === '1' && query.spread !== false) {
|
|
696
788
|
await this.spreadActivation(topN);
|
|
697
789
|
}
|
|
@@ -699,8 +791,9 @@ export class ActivationEngine {
|
|
|
699
791
|
await this.graphWalk(topN, 2, adaptive.hopPenalty, adaptive.beamWidth);
|
|
700
792
|
}
|
|
701
793
|
// Phase 6: Initial filter and sort for re-ranking pool
|
|
794
|
+
// (D11 guard 2/4: injected entity-index candidates are exempt from the minScore floor.)
|
|
702
795
|
const pool = topN
|
|
703
|
-
.filter(r => r.score >= minScore)
|
|
796
|
+
.filter(r => r.score >= minScore || injectedIds.has(r.engram.id))
|
|
704
797
|
.sort((a, b) => b.score - a.score);
|
|
705
798
|
// Phase 7: Cross-encoder re-ranking — scores (query, passage) pairs directly
|
|
706
799
|
// Widens the pool to find relevant results that keyword matching missed.
|
|
@@ -713,6 +806,13 @@ export class ActivationEngine {
|
|
|
713
806
|
// the reranker does discrimination on a wide pool. Tunable via AWM_RERANK_POOL.
|
|
714
807
|
const rerankPoolSize = Number(process.env.AWM_RERANK_POOL ?? Math.max(limit * 4, 40));
|
|
715
808
|
const rerankPool = pool.slice(0, rerankPoolSize);
|
|
809
|
+
// D11 guard 3/4: injected entity-index candidates always reach the cross-encoder.
|
|
810
|
+
if (injectedIds.size > 0) {
|
|
811
|
+
for (const item of pool.slice(rerankPoolSize)) {
|
|
812
|
+
if (injectedIds.has(item.engram.id))
|
|
813
|
+
rerankPool.push(item);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
716
816
|
// Reranker skip heuristic (0.7.10+): if BM25 already has a clear winner with
|
|
717
817
|
// strong absolute score AND a meaningful gap to the runner-up, the cross-encoder
|
|
718
818
|
// is unlikely to change the top result. Skipping saves ~300ms of wall-clock per
|
|
@@ -726,7 +826,9 @@ export class ActivationEngine {
|
|
|
726
826
|
// Ambiguous queries (close BM25 scores, weak top-1, large pool) still go through
|
|
727
827
|
// the reranker. Disable this heuristic via AWM_DISABLE_RERANK_SKIP=1.
|
|
728
828
|
let rerankSkipped = false;
|
|
729
|
-
|
|
829
|
+
// (D11 guard 4/4: never skip the reranker when entity-index candidates were injected —
|
|
830
|
+
// the audition IS the rerank; skipping would return them unjudged or drop them.)
|
|
831
|
+
if (useReranker && rerankPool.length >= 2 && injectedIds.size === 0 && process.env.AWM_DISABLE_RERANK_SKIP !== '1') {
|
|
730
832
|
const top1 = rerankPool[0];
|
|
731
833
|
const top2 = rerankPool[1];
|
|
732
834
|
const t1Text = top1.phaseScores.textMatch;
|
|
@@ -1186,6 +1288,20 @@ export class ActivationEngine {
|
|
|
1186
1288
|
inflow.set(v, (inflow.get(v) ?? 0) + au * share);
|
|
1187
1289
|
}
|
|
1188
1290
|
}
|
|
1291
|
+
// D11: SYNAPSE-style lateral inhibition (divisive normalization) — competing
|
|
1292
|
+
// receivers suppress each other WITHIN an iteration: a node keeps its inflow in
|
|
1293
|
+
// proportion to how much of the iteration's total it earned, so a few strongly-
|
|
1294
|
+
// reached nodes stay strong while diffuse spread mass is crushed. This is the
|
|
1295
|
+
// published fix for the displacing-gold regression that parked AWM_SPREAD.
|
|
1296
|
+
// λ=0 (default) disables; enable for the re-test with AWM_SPREAD_INHIBIT=0.3.
|
|
1297
|
+
const INHIBIT = Number(process.env.AWM_SPREAD_INHIBIT ?? 0);
|
|
1298
|
+
if (INHIBIT > 0 && inflow.size > 1) {
|
|
1299
|
+
let total = 0;
|
|
1300
|
+
for (const f of inflow.values())
|
|
1301
|
+
total += f;
|
|
1302
|
+
for (const [v, f] of inflow)
|
|
1303
|
+
inflow.set(v, f * (f / (f + INHIBIT * (total - f))));
|
|
1304
|
+
}
|
|
1189
1305
|
for (const [v, f] of inflow)
|
|
1190
1306
|
graphActivation.set(v, (graphActivation.get(v) ?? 0) + f);
|
|
1191
1307
|
// Restart (PPR): blend propagated inflow with the original seed vector.
|