agent-working-memory 0.8.6 → 0.8.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.
Files changed (53) hide show
  1. package/README.md +4 -2
  2. package/dist/adapters/common.d.ts.map +1 -1
  3. package/dist/adapters/common.js +13 -0
  4. package/dist/adapters/common.js.map +1 -1
  5. package/dist/api/routes.js +1 -1
  6. package/dist/cli/migrate.js +29 -29
  7. package/dist/cli.js +1 -1
  8. package/dist/coordination/circuit-breaker.js +23 -23
  9. package/dist/core/lite-compress.d.ts +26 -0
  10. package/dist/core/lite-compress.d.ts.map +1 -0
  11. package/dist/core/lite-compress.js +105 -0
  12. package/dist/core/lite-compress.js.map +1 -0
  13. package/dist/mcp.d.ts +5 -1
  14. package/dist/mcp.d.ts.map +1 -1
  15. package/dist/mcp.js +58 -4
  16. package/dist/mcp.js.map +1 -1
  17. package/dist/storage/pglite-schema.js +143 -143
  18. package/dist/storage/pglite.js +138 -138
  19. package/package.json +4 -3
  20. package/src/adapters/common.ts +13 -0
  21. package/src/api/index.ts +3 -3
  22. package/src/api/routes.ts +1 -1
  23. package/src/cli/migrate.ts +307 -307
  24. package/src/cli.ts +1 -1
  25. package/src/coordination/circuit-breaker.ts +83 -83
  26. package/src/coordination/failure-modes.ts +50 -50
  27. package/src/core/decay.ts +63 -63
  28. package/src/core/embeddings.ts +110 -110
  29. package/src/core/index.ts +5 -5
  30. package/src/core/lite-compress.ts +129 -0
  31. package/src/core/logger.ts +36 -36
  32. package/src/core/ml-worker-entry.ts +194 -194
  33. package/src/core/ml-worker.ts +281 -281
  34. package/src/core/query-expander.ts +122 -122
  35. package/src/core/reranker.ts +119 -119
  36. package/src/engine/confidence.ts +120 -120
  37. package/src/engine/connections.ts +162 -162
  38. package/src/engine/consolidation-scheduler.ts +242 -242
  39. package/src/engine/eval.ts +102 -102
  40. package/src/engine/eviction.ts +101 -101
  41. package/src/engine/index.ts +8 -8
  42. package/src/engine/retraction.ts +366 -366
  43. package/src/engine/staging.ts +74 -74
  44. package/src/mcp.ts +70 -4
  45. package/src/storage/factory.ts +147 -147
  46. package/src/storage/index.ts +3 -3
  47. package/src/storage/pglite-schema.ts +166 -166
  48. package/src/storage/pglite.ts +1363 -1363
  49. package/src/storage/store.ts +80 -80
  50. package/src/types/agent.ts +67 -67
  51. package/src/types/checkpoint.ts +46 -46
  52. package/src/types/eval.ts +100 -100
  53. package/src/types/index.ts +6 -6
@@ -1,122 +1,122 @@
1
- // Copyright 2026 Robert Winter / Complete Ideas
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Query Expander - rewrites queries with synonyms and related terms.
5
- *
6
- * Uses Xenova/flan-t5-small (~80MB ONNX) to expand search queries with
7
- * related terms that improve BM25 recall.
8
- *
9
- * AWM 0.8.x: inference dispatches through ml-worker.ts (currently in-process
10
- * — worker_threads reverted because onnxruntime-node bindings cross isolate
11
- * boundaries unsafely; see ml-worker.ts). The dispatch abstraction is
12
- * preserved for a future child_process / HTTP sidecar pool.
13
- *
14
- * The LRU cache + skip heuristic stay on the main thread — they're pure
15
- * filter/lookup logic that shouldn't pay IPC cost.
16
- */
17
-
18
- import { pipeline, type Text2TextGenerationPipeline } from '@huggingface/transformers';
19
- import { dispatchExpand, registerInProcessHandlers } from './ml-worker.js';
20
-
21
- const MODEL_ID = 'Xenova/flan-t5-small';
22
-
23
- // --- In-process fallback ---
24
-
25
- let inProcessInstance: Text2TextGenerationPipeline | null = null;
26
- let inProcessInitPromise: Promise<Text2TextGenerationPipeline> | null = null;
27
-
28
- async function loadInProcess(): Promise<Text2TextGenerationPipeline> {
29
- if (inProcessInstance) return inProcessInstance;
30
- if (inProcessInitPromise) return inProcessInitPromise;
31
- inProcessInitPromise = pipeline('text2text-generation', MODEL_ID, { dtype: 'fp32' }).then(pipe => {
32
- inProcessInstance = pipe as Text2TextGenerationPipeline;
33
- console.log(`Query expander loaded in-process: ${MODEL_ID}`);
34
- return inProcessInstance;
35
- });
36
- return inProcessInitPromise;
37
- }
38
-
39
- async function inProcessExpand(args: { prompt: string; maxNewTokens: number; noRepeatNgramSize: number }): Promise<string> {
40
- const expander = await loadInProcess();
41
- const result = await expander(args.prompt, {
42
- max_new_tokens: args.maxNewTokens,
43
- no_repeat_ngram_size: args.noRepeatNgramSize,
44
- });
45
- const text = Array.isArray(result) ? (result[0] as any)?.generated_text ?? '' : '';
46
- return String(text).trim();
47
- }
48
-
49
- registerInProcessHandlers({ expand: inProcessExpand });
50
-
51
- // --- Public API ---
52
-
53
- /** Kept for backwards compat. */
54
- export async function getExpander(): Promise<Text2TextGenerationPipeline> {
55
- return loadInProcess();
56
- }
57
-
58
- /**
59
- * LRU cache of normalized-query → expanded-query mappings.
60
- * Lives on the main thread — cache hits skip the worker IPC entirely.
61
- */
62
- const expansionCache = new Map<string, string>();
63
- const EXPANSION_CACHE_LIMIT = 500;
64
-
65
- /**
66
- * Skip expansion when the query is already specific (long or many tokens).
67
- */
68
- function shouldSkipExpansion(normalized: string): boolean {
69
- if (normalized.length === 0) return true;
70
- if (normalized.length > 50) return true;
71
- const tokens = new Set(normalized.split(/\s+/).filter(t => t.length > 2));
72
- return tokens.size >= 5;
73
- }
74
-
75
- /**
76
- * Expand a query with related terms and synonyms.
77
- * Returns the original query + generated expansion terms.
78
- * Falls back to the original query on any error.
79
- *
80
- * Dispatches inference to the worker pool. Cache + skip heuristic stay
81
- * on the main thread.
82
- */
83
- export async function expandQuery(originalQuery: string): Promise<string> {
84
- const normalized = originalQuery.toLowerCase().trim();
85
- const optimizationsEnabled = process.env.AWM_DISABLE_EXPANSION_CACHE !== '1';
86
-
87
- if (optimizationsEnabled) {
88
- if (shouldSkipExpansion(normalized)) return originalQuery;
89
- const cached = expansionCache.get(normalized);
90
- if (cached !== undefined) {
91
- // LRU touch
92
- expansionCache.delete(normalized);
93
- expansionCache.set(normalized, cached);
94
- return cached;
95
- }
96
- }
97
-
98
- try {
99
- const prompt = `Expand this search query with synonyms and related terms. Only output the additional terms, not the original query. Query: ${originalQuery}. Additional terms:`;
100
- const expansion = await dispatchExpand({ prompt, maxNewTokens: 25, noRepeatNgramSize: 2 });
101
- const finalQuery = expansion && expansion.length > 2
102
- ? `${originalQuery} ${expansion}`
103
- : originalQuery;
104
-
105
- if (optimizationsEnabled) {
106
- if (expansionCache.size >= EXPANSION_CACHE_LIMIT) {
107
- const oldestKey = expansionCache.keys().next().value;
108
- if (oldestKey !== undefined) expansionCache.delete(oldestKey);
109
- }
110
- expansionCache.set(normalized, finalQuery);
111
- }
112
-
113
- return finalQuery;
114
- } catch {
115
- return originalQuery;
116
- }
117
- }
118
-
119
- /** Clear the expansion cache (used by tests + cache invalidation). */
120
- export function clearExpansionCache(): void {
121
- expansionCache.clear();
122
- }
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Query Expander - rewrites queries with synonyms and related terms.
5
+ *
6
+ * Uses Xenova/flan-t5-small (~80MB ONNX) to expand search queries with
7
+ * related terms that improve BM25 recall.
8
+ *
9
+ * AWM 0.8.x: inference dispatches through ml-worker.ts (currently in-process
10
+ * — worker_threads reverted because onnxruntime-node bindings cross isolate
11
+ * boundaries unsafely; see ml-worker.ts). The dispatch abstraction is
12
+ * preserved for a future child_process / HTTP sidecar pool.
13
+ *
14
+ * The LRU cache + skip heuristic stay on the main thread — they're pure
15
+ * filter/lookup logic that shouldn't pay IPC cost.
16
+ */
17
+
18
+ import { pipeline, type Text2TextGenerationPipeline } from '@huggingface/transformers';
19
+ import { dispatchExpand, registerInProcessHandlers } from './ml-worker.js';
20
+
21
+ const MODEL_ID = 'Xenova/flan-t5-small';
22
+
23
+ // --- In-process fallback ---
24
+
25
+ let inProcessInstance: Text2TextGenerationPipeline | null = null;
26
+ let inProcessInitPromise: Promise<Text2TextGenerationPipeline> | null = null;
27
+
28
+ async function loadInProcess(): Promise<Text2TextGenerationPipeline> {
29
+ if (inProcessInstance) return inProcessInstance;
30
+ if (inProcessInitPromise) return inProcessInitPromise;
31
+ inProcessInitPromise = pipeline('text2text-generation', MODEL_ID, { dtype: 'fp32' }).then(pipe => {
32
+ inProcessInstance = pipe as Text2TextGenerationPipeline;
33
+ console.log(`Query expander loaded in-process: ${MODEL_ID}`);
34
+ return inProcessInstance;
35
+ });
36
+ return inProcessInitPromise;
37
+ }
38
+
39
+ async function inProcessExpand(args: { prompt: string; maxNewTokens: number; noRepeatNgramSize: number }): Promise<string> {
40
+ const expander = await loadInProcess();
41
+ const result = await expander(args.prompt, {
42
+ max_new_tokens: args.maxNewTokens,
43
+ no_repeat_ngram_size: args.noRepeatNgramSize,
44
+ });
45
+ const text = Array.isArray(result) ? (result[0] as any)?.generated_text ?? '' : '';
46
+ return String(text).trim();
47
+ }
48
+
49
+ registerInProcessHandlers({ expand: inProcessExpand });
50
+
51
+ // --- Public API ---
52
+
53
+ /** Kept for backwards compat. */
54
+ export async function getExpander(): Promise<Text2TextGenerationPipeline> {
55
+ return loadInProcess();
56
+ }
57
+
58
+ /**
59
+ * LRU cache of normalized-query → expanded-query mappings.
60
+ * Lives on the main thread — cache hits skip the worker IPC entirely.
61
+ */
62
+ const expansionCache = new Map<string, string>();
63
+ const EXPANSION_CACHE_LIMIT = 500;
64
+
65
+ /**
66
+ * Skip expansion when the query is already specific (long or many tokens).
67
+ */
68
+ function shouldSkipExpansion(normalized: string): boolean {
69
+ if (normalized.length === 0) return true;
70
+ if (normalized.length > 50) return true;
71
+ const tokens = new Set(normalized.split(/\s+/).filter(t => t.length > 2));
72
+ return tokens.size >= 5;
73
+ }
74
+
75
+ /**
76
+ * Expand a query with related terms and synonyms.
77
+ * Returns the original query + generated expansion terms.
78
+ * Falls back to the original query on any error.
79
+ *
80
+ * Dispatches inference to the worker pool. Cache + skip heuristic stay
81
+ * on the main thread.
82
+ */
83
+ export async function expandQuery(originalQuery: string): Promise<string> {
84
+ const normalized = originalQuery.toLowerCase().trim();
85
+ const optimizationsEnabled = process.env.AWM_DISABLE_EXPANSION_CACHE !== '1';
86
+
87
+ if (optimizationsEnabled) {
88
+ if (shouldSkipExpansion(normalized)) return originalQuery;
89
+ const cached = expansionCache.get(normalized);
90
+ if (cached !== undefined) {
91
+ // LRU touch
92
+ expansionCache.delete(normalized);
93
+ expansionCache.set(normalized, cached);
94
+ return cached;
95
+ }
96
+ }
97
+
98
+ try {
99
+ const prompt = `Expand this search query with synonyms and related terms. Only output the additional terms, not the original query. Query: ${originalQuery}. Additional terms:`;
100
+ const expansion = await dispatchExpand({ prompt, maxNewTokens: 25, noRepeatNgramSize: 2 });
101
+ const finalQuery = expansion && expansion.length > 2
102
+ ? `${originalQuery} ${expansion}`
103
+ : originalQuery;
104
+
105
+ if (optimizationsEnabled) {
106
+ if (expansionCache.size >= EXPANSION_CACHE_LIMIT) {
107
+ const oldestKey = expansionCache.keys().next().value;
108
+ if (oldestKey !== undefined) expansionCache.delete(oldestKey);
109
+ }
110
+ expansionCache.set(normalized, finalQuery);
111
+ }
112
+
113
+ return finalQuery;
114
+ } catch {
115
+ return originalQuery;
116
+ }
117
+ }
118
+
119
+ /** Clear the expansion cache (used by tests + cache invalidation). */
120
+ export function clearExpansionCache(): void {
121
+ expansionCache.clear();
122
+ }
@@ -1,119 +1,119 @@
1
- // Copyright 2026 Robert Winter / Complete Ideas
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Cross-Encoder Re-Ranker - scores (query, passage) pairs for relevance.
5
- *
6
- * Uses Xenova/ms-marco-MiniLM-L-6-v2 (~22MB ONNX) trained on MS-MARCO
7
- * passage ranking. Unlike bi-encoders, cross-encoders see both query and
8
- * passage together via full attention - much better at judging if a
9
- * passage actually answers a question.
10
- *
11
- * AWM 0.8.x: inference dispatches through ml-worker.ts (currently in-process
12
- * — see ml-worker.ts for the worker_threads → in-process revert rationale).
13
- */
14
-
15
- import {
16
- AutoTokenizer,
17
- AutoModelForSequenceClassification,
18
- type PreTrainedTokenizer,
19
- type PreTrainedModel,
20
- } from '@huggingface/transformers';
21
- import { dispatchRerank, registerInProcessHandlers } from './ml-worker.js';
22
-
23
- const DEFAULT_MODEL = 'Xenova/ms-marco-MiniLM-L-6-v2';
24
- const MODEL_ID = process.env.AWM_RERANKER_MODEL || DEFAULT_MODEL;
25
-
26
- // --- In-process fallback ---
27
-
28
- let tokenizer: PreTrainedTokenizer | null = null;
29
- let model: PreTrainedModel | null = null;
30
- let initPromise: Promise<void> | null = null;
31
-
32
- async function ensureLoaded(): Promise<void> {
33
- if (tokenizer && model) return;
34
- if (initPromise) return initPromise;
35
- initPromise = (async () => {
36
- tokenizer = await AutoTokenizer.from_pretrained(MODEL_ID);
37
- model = await AutoModelForSequenceClassification.from_pretrained(MODEL_ID, { dtype: 'fp32' });
38
- console.log(`Re-ranker model loaded in-process: ${MODEL_ID}`);
39
- })();
40
- return initPromise;
41
- }
42
-
43
- function sigmoid(x: number): number {
44
- return 1 / (1 + Math.exp(-x));
45
- }
46
-
47
- async function inProcessRerank(args: { query: string; passages: string[] }): Promise<Array<{ index: number; score: number }>> {
48
- const { query, passages } = args;
49
- if (passages.length === 0) return [];
50
- await ensureLoaded();
51
-
52
- // Batch path
53
- try {
54
- const queries = passages.map(() => query);
55
- const inputs = tokenizer!(queries, {
56
- text_pair: passages,
57
- padding: true,
58
- truncation: true,
59
- return_tensors: 'pt',
60
- });
61
- const output = await model!(inputs);
62
- const logits = output.logits ?? output.last_hidden_state;
63
- const data = logits.data as Float32Array | number[];
64
- const results: Array<{ index: number; score: number }> = [];
65
- for (let i = 0; i < passages.length; i++) {
66
- const rawLogit = Number(data[i] ?? 0);
67
- results.push({ index: i, score: sigmoid(rawLogit) });
68
- }
69
- results.sort((a, b) => b.score - a.score);
70
- return results;
71
- } catch {
72
- // Per-passage fallback (the original 0.7.13 path)
73
- const results: Array<{ index: number; score: number }> = [];
74
- for (let i = 0; i < passages.length; i++) {
75
- try {
76
- const inputs = tokenizer!(query, {
77
- text_pair: passages[i],
78
- padding: true,
79
- truncation: true,
80
- return_tensors: 'pt',
81
- });
82
- const output = await model!(inputs);
83
- const logits = output.logits ?? output.last_hidden_state;
84
- const rawLogit = logits.data[0] as number;
85
- results.push({ index: i, score: sigmoid(rawLogit) });
86
- } catch {
87
- results.push({ index: i, score: 0 });
88
- }
89
- }
90
- results.sort((a, b) => b.score - a.score);
91
- return results;
92
- }
93
- }
94
-
95
- // Register the in-process handler with the pool
96
- registerInProcessHandlers({ rerank: inProcessRerank });
97
-
98
- // --- Public API ---
99
-
100
- /** Kept for backwards compat. */
101
- export async function getReranker(): Promise<any> {
102
- await ensureLoaded();
103
- return model;
104
- }
105
-
106
- export interface RerankResult {
107
- index: number;
108
- score: number; // sigmoid-normalized relevance (0-1)
109
- }
110
-
111
- /**
112
- * Re-rank candidate passages against a query using the cross-encoder.
113
- * Returns results sorted by relevance score (descending).
114
- * Dispatches to the worker pool (or in-process fallback).
115
- */
116
- export async function rerank(query: string, passages: string[]): Promise<RerankResult[]> {
117
- if (passages.length === 0) return [];
118
- return dispatchRerank({ query, passages });
119
- }
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Cross-Encoder Re-Ranker - scores (query, passage) pairs for relevance.
5
+ *
6
+ * Uses Xenova/ms-marco-MiniLM-L-6-v2 (~22MB ONNX) trained on MS-MARCO
7
+ * passage ranking. Unlike bi-encoders, cross-encoders see both query and
8
+ * passage together via full attention - much better at judging if a
9
+ * passage actually answers a question.
10
+ *
11
+ * AWM 0.8.x: inference dispatches through ml-worker.ts (currently in-process
12
+ * — see ml-worker.ts for the worker_threads → in-process revert rationale).
13
+ */
14
+
15
+ import {
16
+ AutoTokenizer,
17
+ AutoModelForSequenceClassification,
18
+ type PreTrainedTokenizer,
19
+ type PreTrainedModel,
20
+ } from '@huggingface/transformers';
21
+ import { dispatchRerank, registerInProcessHandlers } from './ml-worker.js';
22
+
23
+ const DEFAULT_MODEL = 'Xenova/ms-marco-MiniLM-L-6-v2';
24
+ const MODEL_ID = process.env.AWM_RERANKER_MODEL || DEFAULT_MODEL;
25
+
26
+ // --- In-process fallback ---
27
+
28
+ let tokenizer: PreTrainedTokenizer | null = null;
29
+ let model: PreTrainedModel | null = null;
30
+ let initPromise: Promise<void> | null = null;
31
+
32
+ async function ensureLoaded(): Promise<void> {
33
+ if (tokenizer && model) return;
34
+ if (initPromise) return initPromise;
35
+ initPromise = (async () => {
36
+ tokenizer = await AutoTokenizer.from_pretrained(MODEL_ID);
37
+ model = await AutoModelForSequenceClassification.from_pretrained(MODEL_ID, { dtype: 'fp32' });
38
+ console.log(`Re-ranker model loaded in-process: ${MODEL_ID}`);
39
+ })();
40
+ return initPromise;
41
+ }
42
+
43
+ function sigmoid(x: number): number {
44
+ return 1 / (1 + Math.exp(-x));
45
+ }
46
+
47
+ async function inProcessRerank(args: { query: string; passages: string[] }): Promise<Array<{ index: number; score: number }>> {
48
+ const { query, passages } = args;
49
+ if (passages.length === 0) return [];
50
+ await ensureLoaded();
51
+
52
+ // Batch path
53
+ try {
54
+ const queries = passages.map(() => query);
55
+ const inputs = tokenizer!(queries, {
56
+ text_pair: passages,
57
+ padding: true,
58
+ truncation: true,
59
+ return_tensors: 'pt',
60
+ });
61
+ const output = await model!(inputs);
62
+ const logits = output.logits ?? output.last_hidden_state;
63
+ const data = logits.data as Float32Array | number[];
64
+ const results: Array<{ index: number; score: number }> = [];
65
+ for (let i = 0; i < passages.length; i++) {
66
+ const rawLogit = Number(data[i] ?? 0);
67
+ results.push({ index: i, score: sigmoid(rawLogit) });
68
+ }
69
+ results.sort((a, b) => b.score - a.score);
70
+ return results;
71
+ } catch {
72
+ // Per-passage fallback (the original 0.7.13 path)
73
+ const results: Array<{ index: number; score: number }> = [];
74
+ for (let i = 0; i < passages.length; i++) {
75
+ try {
76
+ const inputs = tokenizer!(query, {
77
+ text_pair: passages[i],
78
+ padding: true,
79
+ truncation: true,
80
+ return_tensors: 'pt',
81
+ });
82
+ const output = await model!(inputs);
83
+ const logits = output.logits ?? output.last_hidden_state;
84
+ const rawLogit = logits.data[0] as number;
85
+ results.push({ index: i, score: sigmoid(rawLogit) });
86
+ } catch {
87
+ results.push({ index: i, score: 0 });
88
+ }
89
+ }
90
+ results.sort((a, b) => b.score - a.score);
91
+ return results;
92
+ }
93
+ }
94
+
95
+ // Register the in-process handler with the pool
96
+ registerInProcessHandlers({ rerank: inProcessRerank });
97
+
98
+ // --- Public API ---
99
+
100
+ /** Kept for backwards compat. */
101
+ export async function getReranker(): Promise<any> {
102
+ await ensureLoaded();
103
+ return model;
104
+ }
105
+
106
+ export interface RerankResult {
107
+ index: number;
108
+ score: number; // sigmoid-normalized relevance (0-1)
109
+ }
110
+
111
+ /**
112
+ * Re-rank candidate passages against a query using the cross-encoder.
113
+ * Returns results sorted by relevance score (descending).
114
+ * Dispatches to the worker pool (or in-process fallback).
115
+ */
116
+ export async function rerank(query: string, passages: string[]): Promise<RerankResult[]> {
117
+ if (passages.length === 0) return [];
118
+ return dispatchRerank({ query, passages });
119
+ }