agent-working-memory 0.6.0 → 0.7.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/README.md +19 -9
- package/dist/adapters/claude-code.d.ts +4 -0
- package/dist/adapters/claude-code.d.ts.map +1 -0
- package/dist/adapters/claude-code.js +218 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +4 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +226 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/common.d.ts +34 -0
- package/dist/adapters/common.d.ts.map +1 -0
- package/dist/adapters/common.js +145 -0
- package/dist/adapters/common.js.map +1 -0
- package/dist/adapters/cursor.d.ts +4 -0
- package/dist/adapters/cursor.d.ts.map +1 -0
- package/dist/adapters/cursor.js +138 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/http.d.ts +4 -0
- package/dist/adapters/http.d.ts.map +1 -0
- package/dist/adapters/http.js +88 -0
- package/dist/adapters/http.js.map +1 -0
- package/dist/adapters/index.d.ts +7 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +21 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/types.d.ts +65 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +4 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +1 -0
- package/dist/api/routes.js.map +1 -1
- package/dist/cli.js +104 -230
- package/dist/cli.js.map +1 -1
- package/dist/coordination/events.d.ts +59 -0
- package/dist/coordination/events.d.ts.map +1 -0
- package/dist/coordination/events.js +28 -0
- package/dist/coordination/events.js.map +1 -0
- package/dist/coordination/index.d.ts +10 -1
- package/dist/coordination/index.d.ts.map +1 -1
- package/dist/coordination/index.js +87 -3
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/peer-decisions.d.ts +40 -0
- package/dist/coordination/peer-decisions.d.ts.map +1 -0
- package/dist/coordination/peer-decisions.js +82 -0
- package/dist/coordination/peer-decisions.js.map +1 -0
- package/dist/coordination/plugin-loader.d.ts +18 -0
- package/dist/coordination/plugin-loader.d.ts.map +1 -0
- package/dist/coordination/plugin-loader.js +55 -0
- package/dist/coordination/plugin-loader.js.map +1 -0
- package/dist/coordination/plugin.d.ts +40 -0
- package/dist/coordination/plugin.d.ts.map +1 -0
- package/dist/coordination/plugin.js +22 -0
- package/dist/coordination/plugin.js.map +1 -0
- package/dist/coordination/routes.d.ts +2 -1
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +899 -76
- package/dist/coordination/routes.js.map +1 -1
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +72 -14
- package/dist/coordination/schema.js.map +1 -1
- package/dist/coordination/schemas.d.ts +84 -3
- package/dist/coordination/schemas.d.ts.map +1 -1
- package/dist/coordination/schemas.js +71 -1
- package/dist/coordination/schemas.js.map +1 -1
- package/dist/coordination/stale.d.ts.map +1 -1
- package/dist/coordination/stale.js +2 -1
- package/dist/coordination/stale.js.map +1 -1
- package/dist/coordination/types.d.ts +252 -0
- package/dist/coordination/types.d.ts.map +1 -0
- package/dist/coordination/types.js +8 -0
- package/dist/coordination/types.js.map +1 -0
- package/dist/coordination/write-mutex.d.ts +26 -0
- package/dist/coordination/write-mutex.d.ts.map +1 -0
- package/dist/coordination/write-mutex.js +63 -0
- package/dist/coordination/write-mutex.js.map +1 -0
- package/dist/core/embeddings.d.ts +2 -0
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +4 -0
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/hebbian.d.ts +25 -1
- package/dist/core/hebbian.d.ts.map +1 -1
- package/dist/core/hebbian.js +71 -3
- package/dist/core/hebbian.js.map +1 -1
- package/dist/engine/activation.d.ts +22 -4
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +152 -76
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +15 -6
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/retraction.d.ts +3 -1
- package/dist/engine/retraction.d.ts.map +1 -1
- package/dist/engine/retraction.js +19 -6
- package/dist/engine/retraction.js.map +1 -1
- package/dist/index.js +6 -18
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +61 -4
- package/dist/mcp.js.map +1 -1
- package/dist/storage/sqlite.d.ts +23 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +112 -3
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +1 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapters/claude-code.ts +234 -0
- package/src/adapters/codex.ts +262 -0
- package/src/adapters/common.ts +172 -0
- package/src/adapters/cursor.ts +150 -0
- package/src/adapters/http.ts +100 -0
- package/src/adapters/index.ts +31 -0
- package/src/adapters/types.ts +75 -0
- package/src/api/routes.ts +2 -0
- package/src/cli.ts +107 -238
- package/src/coordination/events.ts +90 -0
- package/src/coordination/index.ts +102 -3
- package/src/coordination/peer-decisions.ts +105 -0
- package/src/coordination/plugin-loader.ts +60 -0
- package/src/coordination/plugin.ts +44 -0
- package/src/coordination/routes.ts +1176 -105
- package/src/coordination/schema.ts +67 -14
- package/src/coordination/schemas.ts +85 -1
- package/src/coordination/stale.ts +3 -2
- package/src/coordination/types.ts +311 -0
- package/src/coordination/write-mutex.ts +69 -0
- package/src/core/embeddings.ts +5 -0
- package/src/core/hebbian.ts +84 -3
- package/src/engine/activation.ts +149 -73
- package/src/engine/consolidation.ts +15 -6
- package/src/engine/retraction.ts +22 -6
- package/src/index.ts +6 -15
- package/src/mcp.ts +77 -4
- package/src/storage/sqlite.ts +116 -3
- package/src/types/engram.ts +1 -0
package/dist/core/hebbian.d.ts
CHANGED
|
@@ -15,7 +15,16 @@
|
|
|
15
15
|
export declare function strengthenAssociation(currentWeight: number, signal?: number, rate?: number): number;
|
|
16
16
|
/**
|
|
17
17
|
* Weaken an association weight due to lack of co-activation.
|
|
18
|
-
*
|
|
18
|
+
* Uses power-law decay (DASH model) instead of exponential.
|
|
19
|
+
* Power law has a longer tail — old but valuable associations
|
|
20
|
+
* don't vanish as aggressively as exponential decay.
|
|
21
|
+
*
|
|
22
|
+
* DASH: weight = initial × (1 + t/scale)^(-exponent)
|
|
23
|
+
* vs exponential: weight = initial × 0.5^(t/halfLife)
|
|
24
|
+
*
|
|
25
|
+
* At 7 days: power-law retains ~58% vs exponential 50%
|
|
26
|
+
* At 30 days: power-law retains ~32% vs exponential 6%
|
|
27
|
+
* At 90 days: power-law retains ~20% vs exponential 0.02%
|
|
19
28
|
*/
|
|
20
29
|
export declare function decayAssociation(currentWeight: number, daysSinceActivation: number, halfLife?: number): number;
|
|
21
30
|
/**
|
|
@@ -35,4 +44,19 @@ export declare class CoActivationBuffer {
|
|
|
35
44
|
getCoActivatedPairs(windowMs?: number): [string, string][];
|
|
36
45
|
clear(): void;
|
|
37
46
|
}
|
|
47
|
+
export declare class ValidationGatedBuffer {
|
|
48
|
+
private pending;
|
|
49
|
+
/** Record co-activated pairs as pending (awaiting feedback validation) */
|
|
50
|
+
addPending(engramIds: string[], pairs: [string, string][]): void;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve pending updates for an engram that received feedback.
|
|
53
|
+
* Returns pairs to strengthen (positive) or weaken (negative).
|
|
54
|
+
*/
|
|
55
|
+
resolveFeedback(engramId: string, useful: boolean): {
|
|
56
|
+
pairs: [string, string][];
|
|
57
|
+
signal: number;
|
|
58
|
+
};
|
|
59
|
+
/** Get count of pending updates (for stats) */
|
|
60
|
+
get pendingCount(): number;
|
|
61
|
+
}
|
|
38
62
|
//# sourceMappingURL=hebbian.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hebbian.d.ts","sourceRoot":"","sources":["../../src/core/hebbian.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAKH;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,EACrB,MAAM,GAAE,MAAY,EACpB,IAAI,GAAE,MAAa,GAClB,MAAM,CAIR;AAED
|
|
1
|
+
{"version":3,"file":"hebbian.d.ts","sourceRoot":"","sources":["../../src/core/hebbian.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAKH;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,EACrB,MAAM,GAAE,MAAY,EACpB,IAAI,GAAE,MAAa,GAClB,MAAM,CAIR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,GAAE,MAAY,GACrB,MAAM,CAKR;AAED;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAiD;IAC/D,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,GAAE,MAAW;IAIhC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAO5B,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAMpC;;OAEG;IACH,mBAAmB,CAAC,QAAQ,GAAE,MAAa,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;IAiBhE,KAAK,IAAI,IAAI;CAGd;AAsBD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,OAAO,CAA8B;IAE7C,0EAA0E;IAC1E,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI;IAOhE;;;OAGG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG;QAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IA+BjG,+CAA+C;IAC/C,IAAI,YAAY,IAAI,MAAM,CAAgC;CAC3D"}
|
package/dist/core/hebbian.js
CHANGED
|
@@ -23,11 +23,22 @@ export function strengthenAssociation(currentWeight, signal = 1.0, rate = 0.25)
|
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Weaken an association weight due to lack of co-activation.
|
|
26
|
-
*
|
|
26
|
+
* Uses power-law decay (DASH model) instead of exponential.
|
|
27
|
+
* Power law has a longer tail — old but valuable associations
|
|
28
|
+
* don't vanish as aggressively as exponential decay.
|
|
29
|
+
*
|
|
30
|
+
* DASH: weight = initial × (1 + t/scale)^(-exponent)
|
|
31
|
+
* vs exponential: weight = initial × 0.5^(t/halfLife)
|
|
32
|
+
*
|
|
33
|
+
* At 7 days: power-law retains ~58% vs exponential 50%
|
|
34
|
+
* At 30 days: power-law retains ~32% vs exponential 6%
|
|
35
|
+
* At 90 days: power-law retains ~20% vs exponential 0.02%
|
|
27
36
|
*/
|
|
28
|
-
export function decayAssociation(currentWeight, daysSinceActivation, halfLife = 7.0 // days
|
|
37
|
+
export function decayAssociation(currentWeight, daysSinceActivation, halfLife = 7.0 // scale parameter (days)
|
|
29
38
|
) {
|
|
30
|
-
|
|
39
|
+
// Power-law decay: (1 + t/scale)^(-exponent)
|
|
40
|
+
const exponent = 0.8; // Controls steepness — 0.8 gives a good balance
|
|
41
|
+
const decayFactor = Math.pow(1 + daysSinceActivation / halfLife, -exponent);
|
|
31
42
|
return Math.max(currentWeight * decayFactor, MIN_WEIGHT);
|
|
32
43
|
}
|
|
33
44
|
/**
|
|
@@ -73,4 +84,61 @@ export class CoActivationBuffer {
|
|
|
73
84
|
this.buffer = [];
|
|
74
85
|
}
|
|
75
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Validation-gated Hebbian buffer (Kairos-inspired).
|
|
89
|
+
*
|
|
90
|
+
* Instead of strengthening associations immediately on co-activation,
|
|
91
|
+
* pairs are held pending until feedback arrives. This prevents hub toxicity
|
|
92
|
+
* from noisy co-retrieval — edges only strengthen when the retrieval was
|
|
93
|
+
* actually useful.
|
|
94
|
+
*
|
|
95
|
+
* - Positive feedback → strengthen the pending pairs
|
|
96
|
+
* - Negative feedback → slightly weaken them
|
|
97
|
+
* - No feedback within GATE_TIMEOUT_MS → discard (neutral)
|
|
98
|
+
*/
|
|
99
|
+
const GATE_TIMEOUT_MS = 60_000; // 60 seconds to receive feedback
|
|
100
|
+
export class ValidationGatedBuffer {
|
|
101
|
+
pending = [];
|
|
102
|
+
/** Record co-activated pairs as pending (awaiting feedback validation) */
|
|
103
|
+
addPending(engramIds, pairs) {
|
|
104
|
+
this.pending.push({ pairs, engramIds, timestamp: Date.now() });
|
|
105
|
+
// Evict expired entries
|
|
106
|
+
const cutoff = Date.now() - GATE_TIMEOUT_MS;
|
|
107
|
+
this.pending = this.pending.filter(p => p.timestamp > cutoff);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolve pending updates for an engram that received feedback.
|
|
111
|
+
* Returns pairs to strengthen (positive) or weaken (negative).
|
|
112
|
+
*/
|
|
113
|
+
resolveFeedback(engramId, useful) {
|
|
114
|
+
const cutoff = Date.now() - GATE_TIMEOUT_MS;
|
|
115
|
+
const matching = [];
|
|
116
|
+
// Find all pending updates that include this engram and are still within the gate window
|
|
117
|
+
this.pending = this.pending.filter(p => {
|
|
118
|
+
if (p.timestamp < cutoff)
|
|
119
|
+
return false; // expired
|
|
120
|
+
if (p.engramIds.includes(engramId)) {
|
|
121
|
+
matching.push(...p.pairs);
|
|
122
|
+
return false; // consumed
|
|
123
|
+
}
|
|
124
|
+
return true; // keep
|
|
125
|
+
});
|
|
126
|
+
// Deduplicate pairs
|
|
127
|
+
const seen = new Set();
|
|
128
|
+
const unique = [];
|
|
129
|
+
for (const [a, b] of matching) {
|
|
130
|
+
const key = a < b ? `${a}:${b}` : `${b}:${a}`;
|
|
131
|
+
if (!seen.has(key)) {
|
|
132
|
+
seen.add(key);
|
|
133
|
+
unique.push([a, b]);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
pairs: unique,
|
|
138
|
+
signal: useful ? 1.0 : -0.3, // positive = full strengthen, negative = slight weaken
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** Get count of pending updates (for stats) */
|
|
142
|
+
get pendingCount() { return this.pending.length; }
|
|
143
|
+
}
|
|
76
144
|
//# sourceMappingURL=hebbian.js.map
|
package/dist/core/hebbian.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hebbian.js","sourceRoot":"","sources":["../../src/core/hebbian.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;;;GAUG;AAEH,MAAM,UAAU,GAAG,KAAK,CAAC;AACzB,MAAM,UAAU,GAAG,GAAG,CAAC,CAAE,2CAA2C;AAEpE;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,aAAqB,EACrB,SAAiB,GAAG,EACpB,OAAe,IAAI;IAEnB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"hebbian.js","sourceRoot":"","sources":["../../src/core/hebbian.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,23 +16,41 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Logs activation events for eval metrics.
|
|
18
18
|
*/
|
|
19
|
+
import { ValidationGatedBuffer } from '../core/hebbian.js';
|
|
19
20
|
import type { ActivationResult, ActivationQuery } from '../types/index.js';
|
|
20
21
|
import type { EngramStore } from '../storage/sqlite.js';
|
|
21
22
|
export declare class ActivationEngine {
|
|
22
23
|
private store;
|
|
23
24
|
private coActivationBuffer;
|
|
25
|
+
readonly validationGate: ValidationGatedBuffer;
|
|
24
26
|
constructor(store: EngramStore);
|
|
25
27
|
/**
|
|
26
28
|
* Activate — retrieve the most cognitively relevant engrams for a context.
|
|
27
29
|
*/
|
|
28
30
|
activate(query: ActivationQuery): Promise<ActivationResult[]>;
|
|
29
31
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
32
|
+
* Multi-graph traversal (MAGMA-inspired).
|
|
33
|
+
*
|
|
34
|
+
* Instead of one beam search over all edge types, runs independent traversals
|
|
35
|
+
* per graph type with specialized scoring, then fuses the boosts.
|
|
36
|
+
*
|
|
37
|
+
* Four sub-graphs:
|
|
38
|
+
* - Semantic (connection + hebbian edges) → standard weight-based walk
|
|
39
|
+
* - Temporal (temporal edges) → recency-weighted (favor recent connections)
|
|
40
|
+
* - Causal (causal edges) → full weight walk (causal links are high-value)
|
|
41
|
+
* - Entity (bridge edges) → entity-tag-weighted walk
|
|
42
|
+
*
|
|
43
|
+
* Each sub-graph contributes independently to the final graph boost,
|
|
44
|
+
* weighted by configurable per-graph weights.
|
|
33
45
|
*/
|
|
46
|
+
private static readonly GRAPH_WEIGHTS;
|
|
34
47
|
private graphWalk;
|
|
35
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Resolve validation-gated Hebbian update for a specific engram.
|
|
50
|
+
* Called by memory_feedback — only strengthens when retrieval was useful.
|
|
51
|
+
* This prevents hub toxicity from noisy co-retrieval (Kairos-inspired).
|
|
52
|
+
*/
|
|
53
|
+
resolveHebbianFeedback(engramId: string, useful: boolean): number;
|
|
36
54
|
private explain;
|
|
37
55
|
}
|
|
38
56
|
//# sourceMappingURL=activation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activation.d.ts","sourceRoot":"","sources":["../../src/engine/activation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;
|
|
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;IAifnE;;;;;;;;;;;;;;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"}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { randomUUID } from 'node:crypto';
|
|
22
22
|
import { baseLevelActivation, softplus } from '../core/decay.js';
|
|
23
|
-
import { strengthenAssociation, CoActivationBuffer } from '../core/hebbian.js';
|
|
23
|
+
import { strengthenAssociation, CoActivationBuffer, ValidationGatedBuffer } from '../core/hebbian.js';
|
|
24
24
|
import { embed, cosineSimilarity } from '../core/embeddings.js';
|
|
25
25
|
import { rerank } from '../core/reranker.js';
|
|
26
26
|
import { expandQuery } from '../core/query-expander.js';
|
|
@@ -150,9 +150,11 @@ function jaccard(a, b) {
|
|
|
150
150
|
export class ActivationEngine {
|
|
151
151
|
store;
|
|
152
152
|
coActivationBuffer;
|
|
153
|
+
validationGate;
|
|
153
154
|
constructor(store) {
|
|
154
155
|
this.store = store;
|
|
155
156
|
this.coActivationBuffer = new CoActivationBuffer(50);
|
|
157
|
+
this.validationGate = new ValidationGatedBuffer();
|
|
156
158
|
}
|
|
157
159
|
/**
|
|
158
160
|
* Activate — retrieve the most cognitively relevant engrams for a context.
|
|
@@ -165,14 +167,17 @@ export class ActivationEngine {
|
|
|
165
167
|
const useExpansion = query.useExpansion ?? true;
|
|
166
168
|
const abstentionThreshold = query.abstentionThreshold ?? 0;
|
|
167
169
|
const adaptive = resolveAdaptiveParams(query);
|
|
170
|
+
// Resolve workspace scope: if workspace is set, search across all agents in that workspace
|
|
171
|
+
const agentIds = query.workspace
|
|
172
|
+
? this.store.getWorkspaceAgentIds(query.agentId, query.workspace)
|
|
173
|
+
: [query.agentId];
|
|
174
|
+
const isWorkspaceScoped = agentIds.length > 1;
|
|
168
175
|
// Phase -1: Coref expansion — if query has pronouns, append recent entity names
|
|
169
|
-
// Helps conversational recall where "she/he/they/it" refers to a named entity.
|
|
170
176
|
let queryContext = query.context;
|
|
171
177
|
const pronounPattern = /\b(she|he|they|her|his|him|their|it|that|this|there)\b/i;
|
|
172
178
|
if (pronounPattern.test(queryContext)) {
|
|
173
|
-
// Pull recent entity names from the agent's most-accessed memories
|
|
174
179
|
try {
|
|
175
|
-
const recentEntities = this.store.
|
|
180
|
+
const recentEntities = this.store.getEngramsByAgents(agentIds, 'active')
|
|
176
181
|
.sort((a, b) => b.accessCount - a.accessCount)
|
|
177
182
|
.slice(0, 10)
|
|
178
183
|
.flatMap(e => e.tags.filter(t => t.length >= 3 && !/^(session-|low-|D\d)/.test(t)))
|
|
@@ -187,11 +192,19 @@ export class ActivationEngine {
|
|
|
187
192
|
// Phase 0: Query expansion — add related terms to improve BM25 recall
|
|
188
193
|
let searchContext = queryContext;
|
|
189
194
|
if (useExpansion) {
|
|
195
|
+
let timer;
|
|
190
196
|
try {
|
|
191
|
-
searchContext = await
|
|
197
|
+
searchContext = await Promise.race([
|
|
198
|
+
expandQuery(query.context),
|
|
199
|
+
new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('expansion timeout')), 5000); }),
|
|
200
|
+
]);
|
|
192
201
|
}
|
|
193
202
|
catch {
|
|
194
|
-
// Expansion unavailable — use original query
|
|
203
|
+
// Expansion unavailable or timed out — use original query
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
if (timer)
|
|
207
|
+
clearTimeout(timer);
|
|
195
208
|
}
|
|
196
209
|
}
|
|
197
210
|
// Phase 1: Embed query for vector similarity (uses coref-expanded context)
|
|
@@ -206,9 +219,9 @@ export class ActivationEngine {
|
|
|
206
219
|
// Two-pass BM25: (1) keyword-stripped query for precision, (2) expanded query for recall.
|
|
207
220
|
const keywordQuery = Array.from(tokenize(query.context)).join(' ');
|
|
208
221
|
const bm25Keyword = keywordQuery.length > 2
|
|
209
|
-
? this.store.
|
|
222
|
+
? this.store.searchBM25WithRankMultiAgent(agentIds, keywordQuery, limit * 3)
|
|
210
223
|
: [];
|
|
211
|
-
const bm25Expanded = this.store.
|
|
224
|
+
const bm25Expanded = this.store.searchBM25WithRankMultiAgent(agentIds, searchContext, limit * 3);
|
|
212
225
|
// Merge: take the best BM25 score per engram from either pass
|
|
213
226
|
const bm25ScoreMap = new Map();
|
|
214
227
|
const bm25EngramMap = new Map();
|
|
@@ -222,7 +235,7 @@ export class ActivationEngine {
|
|
|
222
235
|
const bm25Ranked = Array.from(bm25EngramMap.entries()).map(([id, engram]) => ({
|
|
223
236
|
engram, bm25Score: bm25ScoreMap.get(id) ?? 0,
|
|
224
237
|
}));
|
|
225
|
-
const allActive = this.store.
|
|
238
|
+
const allActive = this.store.getEngramsByAgents(agentIds, query.includeStaging ? undefined : 'active', query.includeRetracted ?? false);
|
|
226
239
|
// Merge candidates (deduplicate)
|
|
227
240
|
const candidateMap = new Map();
|
|
228
241
|
for (const r of bm25Ranked)
|
|
@@ -354,7 +367,7 @@ export class ActivationEngine {
|
|
|
354
367
|
// Take top 5 feedback terms and re-search
|
|
355
368
|
const extraTerms = Array.from(feedbackTerms).slice(0, 5).join(' ');
|
|
356
369
|
if (extraTerms) {
|
|
357
|
-
const feedbackBM25 = this.store.
|
|
370
|
+
const feedbackBM25 = this.store.searchBM25WithRankMultiAgent(agentIds, `${searchContext} ${extraTerms}`, limit * 2);
|
|
358
371
|
for (const r of feedbackBM25) {
|
|
359
372
|
if (!candidateMap.has(r.engram.id)) {
|
|
360
373
|
candidateMap.set(r.engram.id, r.engram);
|
|
@@ -472,7 +485,12 @@ export class ActivationEngine {
|
|
|
472
485
|
if (useReranker && rerankPool.length > 0) {
|
|
473
486
|
try {
|
|
474
487
|
const passages = rerankPool.map(r => `${r.engram.concept}: ${r.engram.content}`);
|
|
475
|
-
|
|
488
|
+
let rerankTimer;
|
|
489
|
+
const rerankResults = await Promise.race([
|
|
490
|
+
rerank(query.context, passages),
|
|
491
|
+
new Promise((_, reject) => { rerankTimer = setTimeout(() => reject(new Error('reranker timeout')), 10000); }),
|
|
492
|
+
]).finally(() => { if (rerankTimer)
|
|
493
|
+
clearTimeout(rerankTimer); });
|
|
476
494
|
// Adaptive reranker blend (Codex recommendation):
|
|
477
495
|
// When BM25/text signals are strong, trust them more; when weak, lean on reranker.
|
|
478
496
|
const bm25Max = Math.max(...rerankPool.map(r => r.phaseScores.textMatch));
|
|
@@ -558,13 +576,24 @@ export class ActivationEngine {
|
|
|
558
576
|
associations: r.associations,
|
|
559
577
|
}));
|
|
560
578
|
const activatedIds = results.map(r => r.engram.id);
|
|
561
|
-
// Side effects: touch, co-activate, Hebbian
|
|
579
|
+
// Side effects: touch, co-activate, defer Hebbian to validation gate (skip for internal/system calls)
|
|
562
580
|
if (!query.internal) {
|
|
563
581
|
for (const id of activatedIds) {
|
|
564
582
|
this.store.touchEngram(id);
|
|
565
583
|
}
|
|
566
584
|
this.coActivationBuffer.pushBatch(activatedIds);
|
|
567
|
-
|
|
585
|
+
// Validation-gated Hebbian: defer strengthening until feedback arrives
|
|
586
|
+
const pairs = this.coActivationBuffer.getCoActivatedPairs(10_000);
|
|
587
|
+
const seen = new Set();
|
|
588
|
+
const uniquePairs = [];
|
|
589
|
+
for (const [a, b] of pairs) {
|
|
590
|
+
const key = a < b ? `${a}:${b}` : `${b}:${a}`;
|
|
591
|
+
if (!seen.has(key)) {
|
|
592
|
+
seen.add(key);
|
|
593
|
+
uniquePairs.push([a, b]);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
this.validationGate.addPending(activatedIds, uniquePairs);
|
|
568
597
|
// Log activation event for eval
|
|
569
598
|
const latencyMs = performance.now() - startTime;
|
|
570
599
|
this.store.logActivationEvent({
|
|
@@ -581,83 +610,130 @@ export class ActivationEngine {
|
|
|
581
610
|
return results;
|
|
582
611
|
}
|
|
583
612
|
/**
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
613
|
+
* Multi-graph traversal (MAGMA-inspired).
|
|
614
|
+
*
|
|
615
|
+
* Instead of one beam search over all edge types, runs independent traversals
|
|
616
|
+
* per graph type with specialized scoring, then fuses the boosts.
|
|
617
|
+
*
|
|
618
|
+
* Four sub-graphs:
|
|
619
|
+
* - Semantic (connection + hebbian edges) → standard weight-based walk
|
|
620
|
+
* - Temporal (temporal edges) → recency-weighted (favor recent connections)
|
|
621
|
+
* - Causal (causal edges) → full weight walk (causal links are high-value)
|
|
622
|
+
* - Entity (bridge edges) → entity-tag-weighted walk
|
|
623
|
+
*
|
|
624
|
+
* Each sub-graph contributes independently to the final graph boost,
|
|
625
|
+
* weighted by configurable per-graph weights.
|
|
587
626
|
*/
|
|
627
|
+
static GRAPH_WEIGHTS = {
|
|
628
|
+
semantic: 0.40, // connection + hebbian
|
|
629
|
+
temporal: 0.20, // temporal edges
|
|
630
|
+
causal: 0.25, // causal edges (high-value signal)
|
|
631
|
+
entity: 0.15, // bridge edges
|
|
632
|
+
};
|
|
588
633
|
graphWalk(scored, maxDepth, hopPenalty, beamWidth = 15) {
|
|
589
634
|
const scoreMap = new Map(scored.map(s => [s.engram.id, s]));
|
|
590
635
|
const MAX_TOTAL_BOOST = 0.25;
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (explored.has(neighborId))
|
|
614
|
-
continue;
|
|
615
|
-
const neighbor = scoreMap.get(neighborId);
|
|
616
|
-
if (!neighbor)
|
|
617
|
-
continue;
|
|
618
|
-
// Query-dependent edge filtering: neighbor must have SOME relevance
|
|
619
|
-
// (textMatch > 0.05 for deeper hops, relaxed from 0.1)
|
|
620
|
-
const relevanceFloor = depth === 0 ? 0.1 : 0.05;
|
|
621
|
-
if (neighbor.phaseScores.textMatch < relevanceFloor)
|
|
622
|
-
continue;
|
|
623
|
-
// Skip if neighbor already at boost cap
|
|
624
|
-
if (neighbor.phaseScores.graphBoost >= MAX_TOTAL_BOOST)
|
|
636
|
+
// Define which edge types belong to each sub-graph
|
|
637
|
+
const graphTypes = {
|
|
638
|
+
semantic: ['connection', 'hebbian'],
|
|
639
|
+
temporal: ['temporal'],
|
|
640
|
+
causal: ['causal'],
|
|
641
|
+
entity: ['bridge'],
|
|
642
|
+
};
|
|
643
|
+
// Run independent traversals per sub-graph, accumulate boosts
|
|
644
|
+
const boostAccum = new Map(); // engramId → total boost
|
|
645
|
+
for (const [graphName, edgeTypes] of Object.entries(graphTypes)) {
|
|
646
|
+
const graphWeight = ActivationEngine.GRAPH_WEIGHTS[graphName];
|
|
647
|
+
const subBeamWidth = Math.max(3, Math.ceil(beamWidth * graphWeight));
|
|
648
|
+
// Seed beam
|
|
649
|
+
const beam = scored
|
|
650
|
+
.filter(item => item.phaseScores.textMatch >= 0.15)
|
|
651
|
+
.sort((a, b) => b.score - a.score)
|
|
652
|
+
.slice(0, subBeamWidth);
|
|
653
|
+
const explored = new Set();
|
|
654
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
655
|
+
const nextBeam = [];
|
|
656
|
+
for (const item of beam) {
|
|
657
|
+
if (explored.has(item.engram.id))
|
|
625
658
|
continue;
|
|
626
|
-
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
659
|
+
explored.add(item.engram.id);
|
|
660
|
+
const associations = item.associations.length > 0
|
|
661
|
+
? item.associations
|
|
662
|
+
: this.store.getAssociationsFor(item.engram.id);
|
|
663
|
+
// Filter to only edges of this sub-graph type
|
|
664
|
+
const relevantEdges = associations.filter(a => edgeTypes.includes(a.type));
|
|
665
|
+
for (const assoc of relevantEdges) {
|
|
666
|
+
const neighborId = assoc.fromEngramId === item.engram.id
|
|
667
|
+
? assoc.toEngramId
|
|
668
|
+
: assoc.fromEngramId;
|
|
669
|
+
if (explored.has(neighborId))
|
|
670
|
+
continue;
|
|
671
|
+
const neighbor = scoreMap.get(neighborId);
|
|
672
|
+
if (!neighbor)
|
|
673
|
+
continue;
|
|
674
|
+
const relevanceFloor = depth === 0 ? 0.1 : 0.05;
|
|
675
|
+
if (neighbor.phaseScores.textMatch < relevanceFloor)
|
|
676
|
+
continue;
|
|
677
|
+
// Path score with graph-type-specific weighting
|
|
678
|
+
const normalizedWeight = Math.min(assoc.weight, 5.0) / 5.0;
|
|
679
|
+
let pathScore = item.score * normalizedWeight * Math.pow(hopPenalty, depth + 1);
|
|
680
|
+
// Causal edges get a 2x boost — they represent verified reasoning chains
|
|
681
|
+
if (graphName === 'causal')
|
|
682
|
+
pathScore *= 2.0;
|
|
683
|
+
// Weight by sub-graph importance
|
|
684
|
+
const boost = Math.min(pathScore * graphWeight, 0.15);
|
|
685
|
+
if (boost > 0.001) {
|
|
686
|
+
boostAccum.set(neighborId, (boostAccum.get(neighborId) ?? 0) + boost);
|
|
687
|
+
nextBeam.push(neighbor);
|
|
688
|
+
}
|
|
634
689
|
}
|
|
635
690
|
}
|
|
691
|
+
if (nextBeam.length === 0)
|
|
692
|
+
break;
|
|
693
|
+
beam.length = 0;
|
|
694
|
+
beam.push(...nextBeam
|
|
695
|
+
.sort((a, b) => b.score - a.score)
|
|
696
|
+
.slice(0, subBeamWidth));
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
// Apply fused boosts to scored items
|
|
700
|
+
for (const [engramId, totalBoost] of boostAccum) {
|
|
701
|
+
const item = scoreMap.get(engramId);
|
|
702
|
+
if (!item)
|
|
703
|
+
continue;
|
|
704
|
+
const capped = Math.min(totalBoost, MAX_TOTAL_BOOST - item.phaseScores.graphBoost);
|
|
705
|
+
if (capped > 0.001) {
|
|
706
|
+
item.score += capped;
|
|
707
|
+
item.phaseScores.graphBoost += capped;
|
|
636
708
|
}
|
|
637
|
-
// Prune beam for next depth level
|
|
638
|
-
if (nextBeam.length === 0)
|
|
639
|
-
break;
|
|
640
|
-
beam.length = 0;
|
|
641
|
-
beam.push(...nextBeam
|
|
642
|
-
.sort((a, b) => b.score - a.score)
|
|
643
|
-
.slice(0, BEAM_WIDTH));
|
|
644
709
|
}
|
|
645
710
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
711
|
+
/**
|
|
712
|
+
* Resolve validation-gated Hebbian update for a specific engram.
|
|
713
|
+
* Called by memory_feedback — only strengthens when retrieval was useful.
|
|
714
|
+
* This prevents hub toxicity from noisy co-retrieval (Kairos-inspired).
|
|
715
|
+
*/
|
|
716
|
+
resolveHebbianFeedback(engramId, useful) {
|
|
717
|
+
const { pairs, signal } = this.validationGate.resolveFeedback(engramId, useful);
|
|
718
|
+
let updated = 0;
|
|
650
719
|
for (const [a, b] of pairs) {
|
|
651
|
-
const key = a < b ? `${a}:${b}` : `${b}:${a}`;
|
|
652
|
-
if (seen.has(key))
|
|
653
|
-
continue;
|
|
654
|
-
seen.add(key);
|
|
655
720
|
const existing = this.store.getAssociation(a, b) ?? this.store.getAssociation(b, a);
|
|
656
721
|
const currentWeight = existing?.weight ?? 0.1;
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
722
|
+
if (signal > 0) {
|
|
723
|
+
// Positive feedback → strengthen
|
|
724
|
+
const newWeight = strengthenAssociation(currentWeight, signal);
|
|
725
|
+
this.store.upsertAssociation(a, b, newWeight, 'hebbian');
|
|
726
|
+
this.store.upsertAssociation(b, a, newWeight, 'hebbian');
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
// Negative feedback → slight weakening (decay by signal magnitude)
|
|
730
|
+
const newWeight = Math.max(0.001, currentWeight * (1 + signal)); // signal is -0.3
|
|
731
|
+
this.store.upsertAssociation(a, b, newWeight, 'hebbian');
|
|
732
|
+
this.store.upsertAssociation(b, a, newWeight, 'hebbian');
|
|
733
|
+
}
|
|
734
|
+
updated++;
|
|
660
735
|
}
|
|
736
|
+
return updated;
|
|
661
737
|
}
|
|
662
738
|
explain(phases, engram, associations) {
|
|
663
739
|
const parts = [];
|