agent-working-memory 0.7.10 → 0.7.11

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 CHANGED
@@ -393,6 +393,7 @@ npm run test:locomo # LoCoMo industry benchmark (28.2%)
393
393
  | `AWM_DISABLE_POOL_FILTER` | *(unset)* | Set to `1` to disable the candidate pool reduction (0.7.7+). Reverts recall to scoring all active candidates — slower but useful for A/B testing if a recall regression appears |
394
394
  | `AWM_DISABLE_SLIM_CACHE` | *(unset)* | Set to `1` to disable the in-memory slim cache (0.7.10+). Reverts to per-recall SQL fetch — slower but useful if cache invariants are suspected of drift |
395
395
  | `AWM_DISABLE_RERANK_SKIP` | *(unset)* | Set to `1` to disable the reranker skip on clear-winner queries (0.7.10+). Forces every recall through the cross-encoder |
396
+ | `AWM_DISABLE_EXPANSION_CACHE` | *(unset)* | Set to `1` to disable the query expansion skip heuristic + LRU cache (0.7.11+). Forces every recall through the flan-t5-small expander |
396
397
  | `AWM_WORKSPACE` | *(unset)* | Default workspace for cross-agent recall in hive setups |
397
398
 
398
399
  ## Tech Stack
@@ -412,6 +413,10 @@ npm run test:locomo # LoCoMo industry benchmark (28.2%)
412
413
 
413
414
  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.
414
415
 
416
+ ## What's New in v0.7.11
417
+
418
+ - **Query expansion skip + LRU cache** — flan-t5-small was 164ms per recall (18% of post-0.7.10 floor). Two fixes in `core/query-expander.ts`: (1) skip heuristic for long/specific queries (>50 chars OR ≥5 distinct meaningful tokens), and (2) 500-entry LRU cache for repeated queries. ~30% of typical agent recalls hit the skip; repeated recalls hit the cache. Avg savings: ~100-150ms per recall. Recall quality A/B: 8/8 top-1, 4.63/5 top-5. Disable via `AWM_DISABLE_EXPANSION_CACHE=1`.
419
+
415
420
  ## What's New in v0.7.10
416
421
 
417
422
  - **Recall latency 1.4s → 0.9s median (~35% on top of 0.7.9)** — two more fixes after phase-breakdown showed the slim fetch was still 310ms (Buffer→Float32Array on every recall) and the reranker was 354ms (40% of remaining cost):
@@ -482,7 +487,7 @@ See [CHANGELOG.md](CHANGELOG.md) for full details.
482
487
 
483
488
  ## Project Status
484
489
 
485
- AWM is in active development (v0.7.10). The core memory pipeline, consolidation system, multi-agent coordination, and MCP integration are stable and used daily in production coding workflows.
490
+ AWM is in active development (v0.7.11). The core memory pipeline, consolidation system, multi-agent coordination, and MCP integration are stable and used daily in production coding workflows.
486
491
 
487
492
  - Core retrieval and consolidation: **stable**
488
493
  - MCP tools and Claude Code integration: **stable**
@@ -527,7 +527,7 @@ export function registerRoutes(app, deps) {
527
527
  const base = {
528
528
  status: 'ok',
529
529
  timestamp: new Date().toISOString(),
530
- version: '0.7.10',
530
+ version: '0.7.11',
531
531
  coordination: coordEnabled,
532
532
  };
533
533
  if (coordEnabled) {
package/dist/cli.js CHANGED
@@ -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.10',
310
+ version: '0.7.11',
311
311
  exported_at: new Date().toISOString(),
312
312
  source_db: dbPath,
313
313
  agent_filter: agentFilter,
@@ -19,6 +19,13 @@ export declare function getExpander(): Promise<Text2TextGenerationPipeline>;
19
19
  * Expand a query with related terms and synonyms.
20
20
  * Returns the original query + generated expansion terms.
21
21
  * Falls back to the original query on any error.
22
+ *
23
+ * Optimization (0.7.11+):
24
+ * - Skip heuristic for long/specific queries (~30% of typical agent recalls)
25
+ * - LRU cache for repeated queries (cache hit ≈ 0ms vs 164ms cold)
26
+ * - Disable both via AWM_DISABLE_EXPANSION_CACHE=1
22
27
  */
23
28
  export declare function expandQuery(originalQuery: string): Promise<string>;
29
+ /** Clear the expansion cache (used by tests + cache invalidation if needed). */
30
+ export declare function clearExpansionCache(): void;
24
31
  //# sourceMappingURL=query-expander.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query-expander.d.ts","sourceRoot":"","sources":["../../src/core/query-expander.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,OAAO,EAAY,KAAK,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAMvF;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,2BAA2B,CAAC,CAaxE;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBxE"}
1
+ {"version":3,"file":"query-expander.d.ts","sourceRoot":"","sources":["../../src/core/query-expander.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,OAAO,EAAY,KAAK,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAMvF;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,2BAA2B,CAAC,CAaxE;AA6BD;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8CxE;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
@@ -33,12 +33,58 @@ export async function getExpander() {
33
33
  });
34
34
  return initPromise;
35
35
  }
36
+ /**
37
+ * LRU cache of normalized-query → expanded-query mappings.
38
+ * Map preserves insertion order — re-set on hit moves entry to the end (most-recent),
39
+ * delete-first when over capacity drops the least-recent. ~500 entries × ~200 chars
40
+ * each ≈ 100KB, negligible memory cost.
41
+ *
42
+ * Why: phase-breakdown spike (2026-05-08) showed expandQuery at ~164ms per call.
43
+ * Agent recall patterns repeat — cache hits are common.
44
+ */
45
+ const expansionCache = new Map();
46
+ const EXPANSION_CACHE_LIMIT = 500;
47
+ /**
48
+ * Heuristic: skip expansion when the query is already specific.
49
+ * Long, multi-token queries don't benefit from synonyms — they're already
50
+ * narrow enough that flan-t5's general-vocabulary expansion adds noise more
51
+ * than recall. Exact thresholds are conservative; false-skips would only
52
+ * affect candidates that BM25 catches anyway.
53
+ */
54
+ function shouldSkipExpansion(normalized) {
55
+ if (normalized.length === 0)
56
+ return true;
57
+ if (normalized.length > 50)
58
+ return true;
59
+ // ≥5 distinct meaningful tokens = already specific
60
+ const tokens = new Set(normalized.split(/\s+/).filter(t => t.length > 2));
61
+ return tokens.size >= 5;
62
+ }
36
63
  /**
37
64
  * Expand a query with related terms and synonyms.
38
65
  * Returns the original query + generated expansion terms.
39
66
  * Falls back to the original query on any error.
67
+ *
68
+ * Optimization (0.7.11+):
69
+ * - Skip heuristic for long/specific queries (~30% of typical agent recalls)
70
+ * - LRU cache for repeated queries (cache hit ≈ 0ms vs 164ms cold)
71
+ * - Disable both via AWM_DISABLE_EXPANSION_CACHE=1
40
72
  */
41
73
  export async function expandQuery(originalQuery) {
74
+ const normalized = originalQuery.toLowerCase().trim();
75
+ const optimizationsEnabled = process.env.AWM_DISABLE_EXPANSION_CACHE !== '1';
76
+ if (optimizationsEnabled) {
77
+ if (shouldSkipExpansion(normalized)) {
78
+ return originalQuery;
79
+ }
80
+ const cached = expansionCache.get(normalized);
81
+ if (cached !== undefined) {
82
+ // Move to end (most-recent) for LRU semantics
83
+ expansionCache.delete(normalized);
84
+ expansionCache.set(normalized, cached);
85
+ return cached;
86
+ }
87
+ }
42
88
  try {
43
89
  const expander = await getExpander();
44
90
  const prompt = `Expand this search query with synonyms and related terms. Only output the additional terms, not the original query. Query: ${originalQuery}. Additional terms:`;
@@ -48,13 +94,26 @@ export async function expandQuery(originalQuery) {
48
94
  });
49
95
  const expanded = Array.isArray(result) ? result[0]?.generated_text ?? '' : '';
50
96
  const cleanExpanded = expanded.trim();
51
- if (cleanExpanded && cleanExpanded.length > 2) {
52
- return `${originalQuery} ${cleanExpanded}`;
97
+ const finalQuery = cleanExpanded && cleanExpanded.length > 2
98
+ ? `${originalQuery} ${cleanExpanded}`
99
+ : originalQuery;
100
+ // Cache the result (LRU eviction when over capacity)
101
+ if (optimizationsEnabled) {
102
+ if (expansionCache.size >= EXPANSION_CACHE_LIMIT) {
103
+ const oldestKey = expansionCache.keys().next().value;
104
+ if (oldestKey !== undefined)
105
+ expansionCache.delete(oldestKey);
106
+ }
107
+ expansionCache.set(normalized, finalQuery);
53
108
  }
54
- return originalQuery;
109
+ return finalQuery;
55
110
  }
56
111
  catch {
57
112
  return originalQuery;
58
113
  }
59
114
  }
115
+ /** Clear the expansion cache (used by tests + cache invalidation if needed). */
116
+ export function clearExpansionCache() {
117
+ expansionCache.clear();
118
+ }
60
119
  //# sourceMappingURL=query-expander.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"query-expander.js","sourceRoot":"","sources":["../../src/core/query-expander.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAoC,MAAM,2BAA2B,CAAC;AAEvF,MAAM,QAAQ,GAAG,sBAAsB,CAAC;AACxC,IAAI,QAAQ,GAAuC,IAAI,CAAC;AACxD,IAAI,WAAW,GAAgD,IAAI,CAAC;AAEpE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,WAAW,GAAG,QAAQ,CAAC,sBAAsB,EAAE,QAAQ,EAAE;QACvD,KAAK,EAAE,MAAM;KACd,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACb,QAAQ,GAAG,IAAmC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;QAClD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,aAAqB;IACrD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,8HAA8H,aAAa,qBAAqB,CAAC;QAEhL,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE;YACpC,cAAc,EAAE,EAAE;YAClB,oBAAoB,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,CAAC,CAAS,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEtC,IAAI,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,OAAO,GAAG,aAAa,IAAI,aAAa,EAAE,CAAC;QAC7C,CAAC;QACD,OAAO,aAAa,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,aAAa,CAAC;IACvB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"query-expander.js","sourceRoot":"","sources":["../../src/core/query-expander.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -175,7 +175,7 @@ async function main() {
175
175
  getExpander().catch(err => console.warn('Query expander model unavailable:', err.message));
176
176
  // Start server
177
177
  await app.listen({ port: PORT, host: '0.0.0.0' });
178
- console.log(`AgentWorkingMemory v0.7.10 listening on port ${PORT}`);
178
+ console.log(`AgentWorkingMemory v0.7.11 listening on port ${PORT}`);
179
179
  // Graceful shutdown
180
180
  const shutdown = async () => {
181
181
  clearInterval(backupTimer);
package/dist/mcp.js CHANGED
@@ -71,7 +71,7 @@ import { queryPeerDecisions, formatPeerDecisions } from './coordination/peer-dec
71
71
  const INCOGNITO = process.env.AWM_INCOGNITO === '1' || process.env.AWM_INCOGNITO === 'true';
72
72
  if (INCOGNITO) {
73
73
  console.error('AWM: incognito mode — all memory tools disabled, nothing will be recorded');
74
- const server = new McpServer({ name: 'agent-working-memory', version: '0.7.10' });
74
+ const server = new McpServer({ name: 'agent-working-memory', version: '0.7.11' });
75
75
  const transport = new StdioServerTransport();
76
76
  server.connect(transport).catch(err => {
77
77
  console.error('MCP server failed:', err);
@@ -102,7 +102,7 @@ else {
102
102
  let coordDb = null;
103
103
  const server = new McpServer({
104
104
  name: 'agent-working-memory',
105
- version: '0.7.10',
105
+ version: '0.7.11',
106
106
  });
107
107
  server.registerResource('awm-overview', 'awm://server/overview', {
108
108
  title: 'AWM Overview',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-working-memory",
3
- "version": "0.7.10",
3
+ "version": "0.7.11",
4
4
  "description": "Cognitive memory layer for AI agents — activation-based retrieval, salience filtering, associative connections",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/api/routes.ts CHANGED
@@ -705,7 +705,7 @@ export function registerRoutes(app: FastifyInstance, deps: MemoryDeps): void {
705
705
  const base: Record<string, unknown> = {
706
706
  status: 'ok',
707
707
  timestamp: new Date().toISOString(),
708
- version: '0.7.10',
708
+ version: '0.7.11',
709
709
  coordination: coordEnabled,
710
710
  };
711
711
  if (coordEnabled) {
package/src/cli.ts CHANGED
@@ -334,7 +334,7 @@ async function exportMemories() {
334
334
  const agents = [...new Set(memories.map((m: any) => m.agent_id))];
335
335
 
336
336
  const exportData = {
337
- version: '0.7.10',
337
+ version: '0.7.11',
338
338
  exported_at: new Date().toISOString(),
339
339
  source_db: dbPath,
340
340
  agent_filter: agentFilter,
@@ -37,12 +37,60 @@ export async function getExpander(): Promise<Text2TextGenerationPipeline> {
37
37
  return initPromise;
38
38
  }
39
39
 
40
+ /**
41
+ * LRU cache of normalized-query → expanded-query mappings.
42
+ * Map preserves insertion order — re-set on hit moves entry to the end (most-recent),
43
+ * delete-first when over capacity drops the least-recent. ~500 entries × ~200 chars
44
+ * each ≈ 100KB, negligible memory cost.
45
+ *
46
+ * Why: phase-breakdown spike (2026-05-08) showed expandQuery at ~164ms per call.
47
+ * Agent recall patterns repeat — cache hits are common.
48
+ */
49
+ const expansionCache = new Map<string, string>();
50
+ const EXPANSION_CACHE_LIMIT = 500;
51
+
52
+ /**
53
+ * Heuristic: skip expansion when the query is already specific.
54
+ * Long, multi-token queries don't benefit from synonyms — they're already
55
+ * narrow enough that flan-t5's general-vocabulary expansion adds noise more
56
+ * than recall. Exact thresholds are conservative; false-skips would only
57
+ * affect candidates that BM25 catches anyway.
58
+ */
59
+ function shouldSkipExpansion(normalized: string): boolean {
60
+ if (normalized.length === 0) return true;
61
+ if (normalized.length > 50) return true;
62
+ // ≥5 distinct meaningful tokens = already specific
63
+ const tokens = new Set(normalized.split(/\s+/).filter(t => t.length > 2));
64
+ return tokens.size >= 5;
65
+ }
66
+
40
67
  /**
41
68
  * Expand a query with related terms and synonyms.
42
69
  * Returns the original query + generated expansion terms.
43
70
  * Falls back to the original query on any error.
71
+ *
72
+ * Optimization (0.7.11+):
73
+ * - Skip heuristic for long/specific queries (~30% of typical agent recalls)
74
+ * - LRU cache for repeated queries (cache hit ≈ 0ms vs 164ms cold)
75
+ * - Disable both via AWM_DISABLE_EXPANSION_CACHE=1
44
76
  */
45
77
  export async function expandQuery(originalQuery: string): Promise<string> {
78
+ const normalized = originalQuery.toLowerCase().trim();
79
+ const optimizationsEnabled = process.env.AWM_DISABLE_EXPANSION_CACHE !== '1';
80
+
81
+ if (optimizationsEnabled) {
82
+ if (shouldSkipExpansion(normalized)) {
83
+ return originalQuery;
84
+ }
85
+ const cached = expansionCache.get(normalized);
86
+ if (cached !== undefined) {
87
+ // Move to end (most-recent) for LRU semantics
88
+ expansionCache.delete(normalized);
89
+ expansionCache.set(normalized, cached);
90
+ return cached;
91
+ }
92
+ }
93
+
46
94
  try {
47
95
  const expander = await getExpander();
48
96
  const prompt = `Expand this search query with synonyms and related terms. Only output the additional terms, not the original query. Query: ${originalQuery}. Additional terms:`;
@@ -55,11 +103,26 @@ export async function expandQuery(originalQuery: string): Promise<string> {
55
103
  const expanded = Array.isArray(result) ? (result[0] as any)?.generated_text ?? '' : '';
56
104
  const cleanExpanded = expanded.trim();
57
105
 
58
- if (cleanExpanded && cleanExpanded.length > 2) {
59
- return `${originalQuery} ${cleanExpanded}`;
106
+ const finalQuery = cleanExpanded && cleanExpanded.length > 2
107
+ ? `${originalQuery} ${cleanExpanded}`
108
+ : originalQuery;
109
+
110
+ // Cache the result (LRU eviction when over capacity)
111
+ if (optimizationsEnabled) {
112
+ if (expansionCache.size >= EXPANSION_CACHE_LIMIT) {
113
+ const oldestKey = expansionCache.keys().next().value;
114
+ if (oldestKey !== undefined) expansionCache.delete(oldestKey);
115
+ }
116
+ expansionCache.set(normalized, finalQuery);
60
117
  }
61
- return originalQuery;
118
+
119
+ return finalQuery;
62
120
  } catch {
63
121
  return originalQuery;
64
122
  }
65
123
  }
124
+
125
+ /** Clear the expansion cache (used by tests + cache invalidation if needed). */
126
+ export function clearExpansionCache(): void {
127
+ expansionCache.clear();
128
+ }
package/src/index.ts CHANGED
@@ -177,7 +177,7 @@ async function main() {
177
177
 
178
178
  // Start server
179
179
  await app.listen({ port: PORT, host: '0.0.0.0' });
180
- console.log(`AgentWorkingMemory v0.7.10 listening on port ${PORT}`);
180
+ console.log(`AgentWorkingMemory v0.7.11 listening on port ${PORT}`);
181
181
 
182
182
  // Graceful shutdown
183
183
  const shutdown = async () => {
package/src/mcp.ts CHANGED
@@ -78,7 +78,7 @@ const INCOGNITO = process.env.AWM_INCOGNITO === '1' || process.env.AWM_INCOGNITO
78
78
 
79
79
  if (INCOGNITO) {
80
80
  console.error('AWM: incognito mode — all memory tools disabled, nothing will be recorded');
81
- const server = new McpServer({ name: 'agent-working-memory', version: '0.7.10' });
81
+ const server = new McpServer({ name: 'agent-working-memory', version: '0.7.11' });
82
82
  const transport = new StdioServerTransport();
83
83
  server.connect(transport).catch(err => {
84
84
  console.error('MCP server failed:', err);
@@ -115,7 +115,7 @@ let coordDb: import('better-sqlite3').Database | null = null;
115
115
 
116
116
  const server = new McpServer({
117
117
  name: 'agent-working-memory',
118
- version: '0.7.10',
118
+ version: '0.7.11',
119
119
  });
120
120
 
121
121
  server.registerResource(