agent-working-memory 0.10.0 → 0.11.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 +89 -19
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +5 -1
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +2 -1
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.js +29 -29
- package/dist/cli.js +82 -2
- package/dist/cli.js.map +1 -1
- package/dist/coordination/circuit-breaker.js +23 -23
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +50 -3
- package/dist/mcp.js.map +1 -1
- package/dist/onboard/index.d.ts +68 -0
- package/dist/onboard/index.d.ts.map +1 -0
- package/dist/onboard/index.js +265 -0
- package/dist/onboard/index.js.map +1 -0
- package/dist/storage/pglite-schema.js +143 -143
- package/dist/storage/postgres.js +138 -138
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +27 -0
- package/dist/version.js.map +1 -0
- package/package.json +9 -1
- package/src/adapters/common.ts +5 -1
- package/src/api/index.ts +3 -3
- package/src/api/routes.ts +2 -1
- package/src/cli/migrate.ts +307 -307
- package/src/cli.ts +77 -2
- package/src/coordination/circuit-breaker.ts +83 -83
- package/src/coordination/failure-modes.ts +50 -50
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +110 -110
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/ml-worker-entry.ts +194 -194
- package/src/core/ml-worker.ts +281 -281
- package/src/core/query-expander.ts +122 -122
- package/src/core/reranker.ts +119 -119
- package/src/engine/confidence.ts +120 -120
- package/src/engine/consolidation-scheduler.ts +242 -242
- package/src/engine/eval.ts +102 -102
- package/src/engine/eviction.ts +101 -101
- package/src/engine/index.ts +8 -8
- package/src/engine/retraction.ts +366 -366
- package/src/engine/staging.ts +74 -74
- package/src/index.ts +2 -1
- package/src/mcp.ts +62 -3
- package/src/onboard/index.ts +298 -0
- package/src/storage/index.ts +3 -3
- package/src/storage/pglite-schema.ts +166 -166
- package/src/storage/postgres.ts +1475 -1475
- package/src/storage/store.ts +80 -80
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/eval.ts +100 -100
- package/src/types/index.ts +6 -6
- package/src/version.ts +26 -0
package/src/core/embeddings.ts
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
/**
|
|
4
|
-
* Embedding Engine - vector embeddings via the ML worker pool.
|
|
5
|
-
*
|
|
6
|
-
* Default model: bge-small-en-v1.5 (384 dimensions, ~90MB, MTEB retrieval-optimized).
|
|
7
|
-
* Configurable via AWM_EMBED_MODEL env var.
|
|
8
|
-
*
|
|
9
|
-
* AWM 0.8.x: inference dispatches through ml-worker.ts. The worker_threads
|
|
10
|
-
* path was planned but reverted to in-process because onnxruntime-node's
|
|
11
|
-
* native bindings store V8 handles that don't cross isolate boundaries
|
|
12
|
-
* safely — see ml-worker.ts for the full status. The dispatch abstraction
|
|
13
|
-
* is preserved for a future child_process or HTTP sidecar pool.
|
|
14
|
-
* `AWM_ML_INPROCESS=1` is honored as a no-op (in-process is now the default).
|
|
15
|
-
*
|
|
16
|
-
* NOTE: Changing the model invalidates existing embeddings.
|
|
17
|
-
* Set AWM_EMBED_MODEL=Xenova/all-MiniLM-L6-v2 for backward compatibility.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { pipeline, type FeatureExtractionPipeline } from '@huggingface/transformers';
|
|
21
|
-
import { dispatchEmbed, registerInProcessHandlers } from './ml-worker.js';
|
|
22
|
-
|
|
23
|
-
const MODEL_ID = process.env.AWM_EMBED_MODEL ?? 'Xenova/bge-small-en-v1.5';
|
|
24
|
-
const DIMENSIONS = parseInt(process.env.AWM_EMBED_DIMS ?? '384', 10);
|
|
25
|
-
const POOLING = (process.env.AWM_EMBED_POOLING ?? 'mean') as 'cls' | 'mean';
|
|
26
|
-
|
|
27
|
-
// --- In-process fallback (used by tests and crash recovery) ---
|
|
28
|
-
|
|
29
|
-
let inProcessInstance: FeatureExtractionPipeline | null = null;
|
|
30
|
-
let inProcessInitPromise: Promise<FeatureExtractionPipeline> | null = null;
|
|
31
|
-
|
|
32
|
-
async function loadInProcess(): Promise<FeatureExtractionPipeline> {
|
|
33
|
-
if (inProcessInstance) return inProcessInstance;
|
|
34
|
-
if (inProcessInitPromise) return inProcessInitPromise;
|
|
35
|
-
inProcessInitPromise = pipeline('feature-extraction', MODEL_ID, { dtype: 'fp32' }).then(pipe => {
|
|
36
|
-
inProcessInstance = pipe;
|
|
37
|
-
console.log(`Embedding model loaded in-process: ${MODEL_ID} (${DIMENSIONS}d)`);
|
|
38
|
-
return pipe;
|
|
39
|
-
});
|
|
40
|
-
return inProcessInitPromise;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function inProcessEmbed(args: { texts: string[]; pooling: 'cls' | 'mean'; dimensions: number }): Promise<number[][]> {
|
|
44
|
-
const { texts, pooling, dimensions } = args;
|
|
45
|
-
if (texts.length === 0) return [];
|
|
46
|
-
const embedder = await loadInProcess();
|
|
47
|
-
const result = await embedder(texts, { pooling, normalize: true });
|
|
48
|
-
const data = result.data as Float32Array;
|
|
49
|
-
const vectors: number[][] = [];
|
|
50
|
-
for (let i = 0; i < texts.length; i++) {
|
|
51
|
-
vectors.push(Array.from(data.slice(i * dimensions, (i + 1) * dimensions)));
|
|
52
|
-
}
|
|
53
|
-
return vectors;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Register the in-process handler with the pool (used in test mode and as fallback)
|
|
57
|
-
registerInProcessHandlers({ embed: inProcessEmbed });
|
|
58
|
-
|
|
59
|
-
// --- Public API ---
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get or initialize the embedding pipeline (singleton).
|
|
63
|
-
* Kept for backwards compat — returns the in-process pipeline only.
|
|
64
|
-
* Most consumers should use embed() / embedBatch() which dispatch
|
|
65
|
-
* to the worker pool by default.
|
|
66
|
-
*/
|
|
67
|
-
export async function getEmbedder(): Promise<FeatureExtractionPipeline> {
|
|
68
|
-
return loadInProcess();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Generate an embedding vector for a text string.
|
|
73
|
-
* Dispatches to the worker pool (or in-process fallback).
|
|
74
|
-
*/
|
|
75
|
-
export async function embed(text: string): Promise<number[]> {
|
|
76
|
-
const vectors = await dispatchEmbed({ texts: [text], pooling: POOLING, dimensions: DIMENSIONS });
|
|
77
|
-
return vectors[0] ?? new Array(DIMENSIONS).fill(0);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Generate embeddings for multiple texts in a batch.
|
|
82
|
-
* More efficient than calling embed() in a loop — the worker batches the
|
|
83
|
-
* tokenization + forward pass.
|
|
84
|
-
*/
|
|
85
|
-
export async function embedBatch(texts: string[]): Promise<number[][]> {
|
|
86
|
-
if (texts.length === 0) return [];
|
|
87
|
-
return dispatchEmbed({ texts, pooling: POOLING, dimensions: DIMENSIONS });
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Get the current embedding model ID (for version tracking in stored embeddings) */
|
|
91
|
-
export function getModelId(): string {
|
|
92
|
-
return MODEL_ID;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Cosine similarity between two normalized vectors.
|
|
97
|
-
* Since vectors are pre-normalized, this is just the dot product.
|
|
98
|
-
*/
|
|
99
|
-
export function cosineSimilarity(a: number[], b: number[]): number {
|
|
100
|
-
if (a.length !== b.length || a.length === 0) return 0;
|
|
101
|
-
let dot = 0;
|
|
102
|
-
for (let i = 0; i < a.length; i++) {
|
|
103
|
-
dot += a[i] * b[i];
|
|
104
|
-
}
|
|
105
|
-
// Clamp to [-1, 1] to handle floating point drift
|
|
106
|
-
return Math.max(-1, Math.min(1, dot));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Vector dimensions for this model */
|
|
110
|
-
export const EMBEDDING_DIMENSIONS = DIMENSIONS;
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Embedding Engine - vector embeddings via the ML worker pool.
|
|
5
|
+
*
|
|
6
|
+
* Default model: bge-small-en-v1.5 (384 dimensions, ~90MB, MTEB retrieval-optimized).
|
|
7
|
+
* Configurable via AWM_EMBED_MODEL env var.
|
|
8
|
+
*
|
|
9
|
+
* AWM 0.8.x: inference dispatches through ml-worker.ts. The worker_threads
|
|
10
|
+
* path was planned but reverted to in-process because onnxruntime-node's
|
|
11
|
+
* native bindings store V8 handles that don't cross isolate boundaries
|
|
12
|
+
* safely — see ml-worker.ts for the full status. The dispatch abstraction
|
|
13
|
+
* is preserved for a future child_process or HTTP sidecar pool.
|
|
14
|
+
* `AWM_ML_INPROCESS=1` is honored as a no-op (in-process is now the default).
|
|
15
|
+
*
|
|
16
|
+
* NOTE: Changing the model invalidates existing embeddings.
|
|
17
|
+
* Set AWM_EMBED_MODEL=Xenova/all-MiniLM-L6-v2 for backward compatibility.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { pipeline, type FeatureExtractionPipeline } from '@huggingface/transformers';
|
|
21
|
+
import { dispatchEmbed, registerInProcessHandlers } from './ml-worker.js';
|
|
22
|
+
|
|
23
|
+
const MODEL_ID = process.env.AWM_EMBED_MODEL ?? 'Xenova/bge-small-en-v1.5';
|
|
24
|
+
const DIMENSIONS = parseInt(process.env.AWM_EMBED_DIMS ?? '384', 10);
|
|
25
|
+
const POOLING = (process.env.AWM_EMBED_POOLING ?? 'mean') as 'cls' | 'mean';
|
|
26
|
+
|
|
27
|
+
// --- In-process fallback (used by tests and crash recovery) ---
|
|
28
|
+
|
|
29
|
+
let inProcessInstance: FeatureExtractionPipeline | null = null;
|
|
30
|
+
let inProcessInitPromise: Promise<FeatureExtractionPipeline> | null = null;
|
|
31
|
+
|
|
32
|
+
async function loadInProcess(): Promise<FeatureExtractionPipeline> {
|
|
33
|
+
if (inProcessInstance) return inProcessInstance;
|
|
34
|
+
if (inProcessInitPromise) return inProcessInitPromise;
|
|
35
|
+
inProcessInitPromise = pipeline('feature-extraction', MODEL_ID, { dtype: 'fp32' }).then(pipe => {
|
|
36
|
+
inProcessInstance = pipe;
|
|
37
|
+
console.log(`Embedding model loaded in-process: ${MODEL_ID} (${DIMENSIONS}d)`);
|
|
38
|
+
return pipe;
|
|
39
|
+
});
|
|
40
|
+
return inProcessInitPromise;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function inProcessEmbed(args: { texts: string[]; pooling: 'cls' | 'mean'; dimensions: number }): Promise<number[][]> {
|
|
44
|
+
const { texts, pooling, dimensions } = args;
|
|
45
|
+
if (texts.length === 0) return [];
|
|
46
|
+
const embedder = await loadInProcess();
|
|
47
|
+
const result = await embedder(texts, { pooling, normalize: true });
|
|
48
|
+
const data = result.data as Float32Array;
|
|
49
|
+
const vectors: number[][] = [];
|
|
50
|
+
for (let i = 0; i < texts.length; i++) {
|
|
51
|
+
vectors.push(Array.from(data.slice(i * dimensions, (i + 1) * dimensions)));
|
|
52
|
+
}
|
|
53
|
+
return vectors;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Register the in-process handler with the pool (used in test mode and as fallback)
|
|
57
|
+
registerInProcessHandlers({ embed: inProcessEmbed });
|
|
58
|
+
|
|
59
|
+
// --- Public API ---
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get or initialize the embedding pipeline (singleton).
|
|
63
|
+
* Kept for backwards compat — returns the in-process pipeline only.
|
|
64
|
+
* Most consumers should use embed() / embedBatch() which dispatch
|
|
65
|
+
* to the worker pool by default.
|
|
66
|
+
*/
|
|
67
|
+
export async function getEmbedder(): Promise<FeatureExtractionPipeline> {
|
|
68
|
+
return loadInProcess();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generate an embedding vector for a text string.
|
|
73
|
+
* Dispatches to the worker pool (or in-process fallback).
|
|
74
|
+
*/
|
|
75
|
+
export async function embed(text: string): Promise<number[]> {
|
|
76
|
+
const vectors = await dispatchEmbed({ texts: [text], pooling: POOLING, dimensions: DIMENSIONS });
|
|
77
|
+
return vectors[0] ?? new Array(DIMENSIONS).fill(0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generate embeddings for multiple texts in a batch.
|
|
82
|
+
* More efficient than calling embed() in a loop — the worker batches the
|
|
83
|
+
* tokenization + forward pass.
|
|
84
|
+
*/
|
|
85
|
+
export async function embedBatch(texts: string[]): Promise<number[][]> {
|
|
86
|
+
if (texts.length === 0) return [];
|
|
87
|
+
return dispatchEmbed({ texts, pooling: POOLING, dimensions: DIMENSIONS });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Get the current embedding model ID (for version tracking in stored embeddings) */
|
|
91
|
+
export function getModelId(): string {
|
|
92
|
+
return MODEL_ID;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Cosine similarity between two normalized vectors.
|
|
97
|
+
* Since vectors are pre-normalized, this is just the dot product.
|
|
98
|
+
*/
|
|
99
|
+
export function cosineSimilarity(a: number[], b: number[]): number {
|
|
100
|
+
if (a.length !== b.length || a.length === 0) return 0;
|
|
101
|
+
let dot = 0;
|
|
102
|
+
for (let i = 0; i < a.length; i++) {
|
|
103
|
+
dot += a[i] * b[i];
|
|
104
|
+
}
|
|
105
|
+
// Clamp to [-1, 1] to handle floating point drift
|
|
106
|
+
return Math.max(-1, Math.min(1, dot));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Vector dimensions for this model */
|
|
110
|
+
export const EMBEDDING_DIMENSIONS = DIMENSIONS;
|
package/src/core/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
export * from './decay.js';
|
|
4
|
-
export * from './hebbian.js';
|
|
5
|
-
export * from './salience.js';
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
export * from './decay.js';
|
|
4
|
+
export * from './hebbian.js';
|
|
5
|
+
export * from './salience.js';
|
package/src/core/logger.ts
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
/**
|
|
4
|
-
* Simple file logger for AWM activity.
|
|
5
|
-
*
|
|
6
|
-
* Appends one line per event to data/awm.log (next to memory.db).
|
|
7
|
-
* Format: ISO timestamp | agent | event | detail
|
|
8
|
-
*
|
|
9
|
-
* Designed for dev pilot observability — know at a glance what's happening.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
13
|
-
import { dirname, resolve } from 'node:path';
|
|
14
|
-
|
|
15
|
-
let logPath: string | null = null;
|
|
16
|
-
|
|
17
|
-
export function initLogger(dbPath: string): void {
|
|
18
|
-
const dir = dirname(resolve(dbPath));
|
|
19
|
-
mkdirSync(dir, { recursive: true });
|
|
20
|
-
logPath = resolve(dir, 'awm.log');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function log(agentId: string, event: string, detail: string): void {
|
|
24
|
-
if (!logPath) return;
|
|
25
|
-
const ts = new Date().toISOString();
|
|
26
|
-
const line = `${ts} | ${agentId} | ${event} | ${detail}\n`;
|
|
27
|
-
try {
|
|
28
|
-
appendFileSync(logPath, line);
|
|
29
|
-
} catch {
|
|
30
|
-
// Logging should never crash the server
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function getLogPath(): string | null {
|
|
35
|
-
return logPath;
|
|
36
|
-
}
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Simple file logger for AWM activity.
|
|
5
|
+
*
|
|
6
|
+
* Appends one line per event to data/awm.log (next to memory.db).
|
|
7
|
+
* Format: ISO timestamp | agent | event | detail
|
|
8
|
+
*
|
|
9
|
+
* Designed for dev pilot observability — know at a glance what's happening.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
13
|
+
import { dirname, resolve } from 'node:path';
|
|
14
|
+
|
|
15
|
+
let logPath: string | null = null;
|
|
16
|
+
|
|
17
|
+
export function initLogger(dbPath: string): void {
|
|
18
|
+
const dir = dirname(resolve(dbPath));
|
|
19
|
+
mkdirSync(dir, { recursive: true });
|
|
20
|
+
logPath = resolve(dir, 'awm.log');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function log(agentId: string, event: string, detail: string): void {
|
|
24
|
+
if (!logPath) return;
|
|
25
|
+
const ts = new Date().toISOString();
|
|
26
|
+
const line = `${ts} | ${agentId} | ${event} | ${detail}\n`;
|
|
27
|
+
try {
|
|
28
|
+
appendFileSync(logPath, line);
|
|
29
|
+
} catch {
|
|
30
|
+
// Logging should never crash the server
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getLogPath(): string | null {
|
|
35
|
+
return logPath;
|
|
36
|
+
}
|