agent-working-memory 0.7.17 → 0.8.5
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 +156 -15
- package/dist/adapters/common.js +202 -202
- package/dist/adapters/http.js +22 -22
- package/dist/api/routes.d.ts +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +258 -79
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.d.ts +42 -0
- package/dist/cli/migrate.d.ts.map +1 -0
- package/dist/cli/migrate.js +268 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli.js +147 -103
- package/dist/cli.js.map +1 -1
- package/dist/coordination/circuit-breaker.d.ts +24 -0
- package/dist/coordination/circuit-breaker.d.ts.map +1 -0
- package/dist/coordination/circuit-breaker.js +70 -0
- package/dist/coordination/circuit-breaker.js.map +1 -0
- package/dist/coordination/failure-modes.d.ts +21 -0
- package/dist/coordination/failure-modes.d.ts.map +1 -0
- package/dist/coordination/failure-modes.js +49 -0
- package/dist/coordination/failure-modes.js.map +1 -0
- package/dist/coordination/peer-decisions.js +7 -7
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +44 -1
- package/dist/coordination/routes.js.map +1 -1
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +18 -0
- package/dist/coordination/schema.js.map +1 -1
- package/dist/coordination/schemas.d.ts +9 -9
- package/dist/coordination/stale.d.ts +9 -1
- package/dist/coordination/stale.d.ts.map +1 -1
- package/dist/coordination/stale.js +53 -9
- package/dist/coordination/stale.js.map +1 -1
- package/dist/core/embeddings.d.ts +16 -10
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +54 -38
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/ml-worker-entry.d.ts +17 -0
- package/dist/core/ml-worker-entry.d.ts.map +1 -0
- package/dist/core/ml-worker-entry.js +193 -0
- package/dist/core/ml-worker-entry.js.map +1 -0
- package/dist/core/ml-worker.d.ts +59 -0
- package/dist/core/ml-worker.d.ts.map +1 -0
- package/dist/core/ml-worker.js +253 -0
- package/dist/core/ml-worker.js.map +1 -0
- package/dist/core/query-expander.d.ts +13 -15
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +48 -54
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.d.ts +9 -19
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +33 -35
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts +32 -5
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +201 -28
- package/dist/core/salience.js.map +1 -1
- package/dist/core/write-pipeline.d.ts +10 -2
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +218 -49
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/engine/activation.d.ts +2 -2
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +238 -115
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/confidence.d.ts +62 -0
- package/dist/engine/confidence.d.ts.map +1 -0
- package/dist/engine/confidence.js +100 -0
- package/dist/engine/confidence.js.map +1 -0
- package/dist/engine/connections.d.ts +47 -8
- package/dist/engine/connections.d.ts.map +1 -1
- package/dist/engine/connections.js +83 -22
- package/dist/engine/connections.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts +46 -10
- package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
- package/dist/engine/consolidation-scheduler.js +170 -50
- package/dist/engine/consolidation-scheduler.js.map +1 -1
- package/dist/engine/consolidation.d.ts +16 -2
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +143 -44
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts +2 -2
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +9 -9
- package/dist/engine/eval.js.map +1 -1
- package/dist/engine/eviction.d.ts +4 -4
- package/dist/engine/eviction.d.ts.map +1 -1
- package/dist/engine/eviction.js +15 -15
- package/dist/engine/eviction.js.map +1 -1
- package/dist/engine/retraction.d.ts +93 -5
- package/dist/engine/retraction.d.ts.map +1 -1
- package/dist/engine/retraction.js +238 -19
- package/dist/engine/retraction.js.map +1 -1
- package/dist/engine/staging.d.ts +1 -1
- package/dist/engine/staging.d.ts.map +1 -1
- package/dist/engine/staging.js +3 -3
- package/dist/engine/staging.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +1 -1
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +4 -4
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +48 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +167 -141
- package/dist/mcp.js.map +1 -1
- package/dist/storage/factory.d.ts +55 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +135 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/pglite-schema.d.ts +20 -0
- package/dist/storage/pglite-schema.d.ts.map +1 -0
- package/dist/storage/pglite-schema.js +165 -0
- package/dist/storage/pglite-schema.js.map +1 -0
- package/dist/storage/pglite.d.ts +187 -0
- package/dist/storage/pglite.d.ts.map +1 -0
- package/dist/storage/pglite.js +1114 -0
- package/dist/storage/pglite.js.map +1 -0
- package/dist/storage/sqlite.d.ts +117 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +357 -8
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/storage/store.d.ts +63 -0
- package/dist/storage/store.d.ts.map +1 -0
- package/dist/storage/store.js +27 -0
- package/dist/storage/store.js.map +1 -0
- package/dist/types/engram.d.ts +92 -2
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +3 -1
- package/src/adapters/claude-code.ts +218 -218
- package/src/adapters/codex.ts +252 -252
- package/src/adapters/common.ts +411 -411
- package/src/adapters/cursor.ts +135 -135
- package/src/adapters/http.ts +93 -93
- package/src/adapters/index.ts +31 -31
- package/src/adapters/types.ts +75 -75
- package/src/api/index.ts +3 -3
- package/src/api/routes.ts +970 -714
- package/src/cli/migrate.ts +307 -0
- package/src/cli.ts +755 -719
- package/src/coordination/circuit-breaker.ts +83 -0
- package/src/coordination/events.ts +90 -90
- package/src/coordination/failure-modes.ts +50 -0
- package/src/coordination/peer-decisions.ts +105 -105
- package/src/coordination/plugin-loader.ts +60 -60
- package/src/coordination/plugin.ts +44 -44
- package/src/coordination/routes.ts +56 -1
- package/src/coordination/schema.ts +13 -0
- package/src/coordination/stale.ts +80 -11
- package/src/coordination/types.ts +311 -311
- package/src/coordination/write-mutex.ts +69 -69
- package/src/core/auto-tagger.ts +168 -168
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +110 -93
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/ml-worker-entry.ts +194 -0
- package/src/core/ml-worker.ts +281 -0
- package/src/core/query-expander.ts +122 -128
- package/src/core/reranker.ts +119 -125
- package/src/core/salience.ts +200 -33
- package/src/core/write-pipeline.ts +521 -343
- package/src/engine/activation.ts +235 -109
- package/src/engine/confidence.ts +120 -0
- package/src/engine/connections.ts +162 -103
- package/src/engine/consolidation-scheduler.ts +242 -125
- package/src/engine/consolidation.ts +138 -45
- 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 -116
- package/src/engine/staging.ts +74 -74
- package/src/hooks/sidecar.ts +5 -5
- package/src/index.ts +226 -212
- package/src/mcp.ts +1145 -1121
- package/src/storage/factory.ts +147 -0
- package/src/storage/index.ts +3 -3
- package/src/storage/pglite-schema.ts +166 -0
- package/src/storage/pglite.ts +1363 -0
- package/src/storage/sqlite.ts +402 -7
- package/src/storage/store.ts +80 -0
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/engram.ts +101 -3
- package/src/types/eval.ts +100 -100
- package/src/types/index.ts +6 -6
package/src/hooks/sidecar.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
|
|
20
20
|
import { readFileSync, existsSync } from 'node:fs';
|
|
21
|
-
import type { EngramStore } from '../storage/
|
|
21
|
+
import type { IEngramStore as EngramStore } from '../storage/store.js';
|
|
22
22
|
import type { ConsciousState } from '../types/checkpoint.js';
|
|
23
23
|
import { log, getLogPath } from '../core/logger.js';
|
|
24
24
|
|
|
@@ -211,7 +211,7 @@ export function startSidecar(deps: SidecarDeps): { close: () => void } {
|
|
|
211
211
|
episodeId: null,
|
|
212
212
|
};
|
|
213
213
|
|
|
214
|
-
store.saveCheckpoint(agentId, state);
|
|
214
|
+
await store.saveCheckpoint(agentId, state);
|
|
215
215
|
log(agentId, `hook:${event}`, `auto-checkpoint files=${state.activeFiles.length} task="${state.currentTask.slice(0, 80)}"`);
|
|
216
216
|
|
|
217
217
|
// On SessionEnd: run full consolidation (sleep cycle) before process dies
|
|
@@ -255,9 +255,9 @@ export function startSidecar(deps: SidecarDeps): { close: () => void } {
|
|
|
255
255
|
});
|
|
256
256
|
|
|
257
257
|
// --- Silent auto-checkpoint every 15 minutes ---
|
|
258
|
-
const autoCheckpointTimer = setInterval(() => {
|
|
258
|
+
const autoCheckpointTimer = setInterval(async () => {
|
|
259
259
|
try {
|
|
260
|
-
const checkpoint = store.getCheckpoint(agentId);
|
|
260
|
+
const checkpoint = await store.getCheckpoint(agentId);
|
|
261
261
|
if (!checkpoint) return; // No state to save yet
|
|
262
262
|
|
|
263
263
|
// Only checkpoint if there's been activity since last auto-checkpoint
|
|
@@ -275,7 +275,7 @@ export function startSidecar(deps: SidecarDeps): { close: () => void } {
|
|
|
275
275
|
episodeId: checkpoint.executionState?.episodeId ?? null,
|
|
276
276
|
};
|
|
277
277
|
|
|
278
|
-
store.saveCheckpoint(agentId, state);
|
|
278
|
+
await store.saveCheckpoint(agentId, state);
|
|
279
279
|
log(agentId, 'hook:timer', `auto-checkpoint (${Math.round(sinceActivity / 60_000)}min since activity)`);
|
|
280
280
|
} catch { /* timer failure is non-fatal */ }
|
|
281
281
|
}, AUTO_CHECKPOINT_INTERVAL_MS);
|
package/src/index.ts
CHANGED
|
@@ -1,212 +1,226 @@
|
|
|
1
|
-
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { readFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
|
|
4
|
-
import { resolve, dirname, basename } from 'node:path';
|
|
5
|
-
import Fastify from 'fastify';
|
|
6
|
-
|
|
7
|
-
// Load .env file if present (no external dependency)
|
|
8
|
-
try {
|
|
9
|
-
const envPath = resolve(process.cwd(), '.env');
|
|
10
|
-
const envContent = readFileSync(envPath, 'utf-8');
|
|
11
|
-
for (const line of envContent.split('\n')) {
|
|
12
|
-
const trimmed = line.trim();
|
|
13
|
-
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
14
|
-
const eqIdx = trimmed.indexOf('=');
|
|
15
|
-
if (eqIdx === -1) continue;
|
|
16
|
-
const key = trimmed.slice(0, eqIdx).trim();
|
|
17
|
-
const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, '');
|
|
18
|
-
if (!process.env[key]) process.env[key] = val; // Don't override existing env
|
|
19
|
-
}
|
|
20
|
-
} catch { /* No .env file — that's fine */ }
|
|
21
|
-
import { EngramStore } from './storage/sqlite.js';
|
|
22
|
-
import { ActivationEngine } from './engine/activation.js';
|
|
23
|
-
import { ConnectionEngine } from './engine/connections.js';
|
|
24
|
-
import { StagingBuffer } from './engine/staging.js';
|
|
25
|
-
import { EvictionEngine } from './engine/eviction.js';
|
|
26
|
-
import { RetractionEngine } from './engine/retraction.js';
|
|
27
|
-
import { EvalEngine } from './engine/eval.js';
|
|
28
|
-
import { ConsolidationEngine } from './engine/consolidation.js';
|
|
29
|
-
import { ConsolidationScheduler } from './engine/consolidation-scheduler.js';
|
|
30
|
-
import { registerRoutes } from './api/routes.js';
|
|
31
|
-
import { DEFAULT_AGENT_CONFIG } from './types/agent.js';
|
|
32
|
-
import { getEmbedder } from './core/embeddings.js';
|
|
33
|
-
import { getReranker } from './core/reranker.js';
|
|
34
|
-
import { getExpander } from './core/query-expander.js';
|
|
35
|
-
import { initLogger } from './core/logger.js';
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { readFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
|
|
4
|
+
import { resolve, dirname, basename } from 'node:path';
|
|
5
|
+
import Fastify from 'fastify';
|
|
6
|
+
|
|
7
|
+
// Load .env file if present (no external dependency)
|
|
8
|
+
try {
|
|
9
|
+
const envPath = resolve(process.cwd(), '.env');
|
|
10
|
+
const envContent = readFileSync(envPath, 'utf-8');
|
|
11
|
+
for (const line of envContent.split('\n')) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
14
|
+
const eqIdx = trimmed.indexOf('=');
|
|
15
|
+
if (eqIdx === -1) continue;
|
|
16
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
17
|
+
const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, '');
|
|
18
|
+
if (!process.env[key]) process.env[key] = val; // Don't override existing env
|
|
19
|
+
}
|
|
20
|
+
} catch { /* No .env file — that's fine */ }
|
|
21
|
+
import { EngramStore } from './storage/sqlite.js';
|
|
22
|
+
import { ActivationEngine } from './engine/activation.js';
|
|
23
|
+
import { ConnectionEngine } from './engine/connections.js';
|
|
24
|
+
import { StagingBuffer } from './engine/staging.js';
|
|
25
|
+
import { EvictionEngine } from './engine/eviction.js';
|
|
26
|
+
import { RetractionEngine } from './engine/retraction.js';
|
|
27
|
+
import { EvalEngine } from './engine/eval.js';
|
|
28
|
+
import { ConsolidationEngine } from './engine/consolidation.js';
|
|
29
|
+
import { ConsolidationScheduler } from './engine/consolidation-scheduler.js';
|
|
30
|
+
import { registerRoutes } from './api/routes.js';
|
|
31
|
+
import { DEFAULT_AGENT_CONFIG } from './types/agent.js';
|
|
32
|
+
import { getEmbedder } from './core/embeddings.js';
|
|
33
|
+
import { getReranker } from './core/reranker.js';
|
|
34
|
+
import { getExpander } from './core/query-expander.js';
|
|
35
|
+
import { initLogger } from './core/logger.js';
|
|
36
|
+
import { openStore, getConfiguredBackend, type StoreBackend } from './storage/factory.js';
|
|
37
|
+
import type { IEngramStore } from './storage/store.js';
|
|
38
|
+
|
|
39
|
+
const PORT = parseInt(process.env.AWM_PORT ?? '8400', 10);
|
|
40
|
+
const BACKEND: StoreBackend = getConfiguredBackend();
|
|
41
|
+
const DB_PATH = process.env.AWM_DB_PATH ?? (BACKEND === 'pglite' ? 'memory-pglite' : 'memory.db');
|
|
42
|
+
const API_KEY = process.env.AWM_API_KEY ?? null;
|
|
43
|
+
|
|
44
|
+
async function main() {
|
|
45
|
+
// Auto-backup: copy DB to backups/ on startup (SQLite-only; PGlite is a
|
|
46
|
+
// directory and is backed up by snapshot/replication at the OS level).
|
|
47
|
+
if (BACKEND === 'sqlite' && existsSync(DB_PATH)) {
|
|
48
|
+
const dbDir = dirname(resolve(DB_PATH));
|
|
49
|
+
const backupDir = resolve(dbDir, 'backups');
|
|
50
|
+
mkdirSync(backupDir, { recursive: true });
|
|
51
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
52
|
+
const backupPath = resolve(backupDir, `${basename(DB_PATH, '.db')}-${ts}.db`);
|
|
53
|
+
try {
|
|
54
|
+
copyFileSync(resolve(DB_PATH), backupPath);
|
|
55
|
+
console.log(`Backup: ${backupPath}`);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.log(`Backup skipped: ${(err as Error).message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Logger — write activity to awm.log alongside the DB
|
|
62
|
+
initLogger(DB_PATH);
|
|
63
|
+
|
|
64
|
+
// Storage — backend-agnostic open via the factory.
|
|
65
|
+
// Some routes (export, coordination health) are still SQLite-only and
|
|
66
|
+
// gracefully return 501 / skip when running on PGlite.
|
|
67
|
+
const { store: storeAny } = await openStore();
|
|
68
|
+
const store = storeAny as unknown as EngramStore;
|
|
69
|
+
|
|
70
|
+
// Integrity check — SQLite-only. PGlite has its own consistency model.
|
|
71
|
+
const integrity = BACKEND === 'sqlite'
|
|
72
|
+
? store.integrityCheck()
|
|
73
|
+
: { ok: true, result: 'pglite: skipped' };
|
|
74
|
+
if (!integrity.ok) {
|
|
75
|
+
console.error(`DB integrity check FAILED: ${integrity.result}`);
|
|
76
|
+
// Close corrupt DB, restore from backup, and exit for process manager to restart
|
|
77
|
+
store.close();
|
|
78
|
+
const dbDir = dirname(resolve(DB_PATH));
|
|
79
|
+
const backupDir = resolve(dbDir, 'backups');
|
|
80
|
+
if (existsSync(backupDir)) {
|
|
81
|
+
const backups = readdirSync(backupDir)
|
|
82
|
+
.filter(f => f.endsWith('.db'))
|
|
83
|
+
.sort()
|
|
84
|
+
.reverse();
|
|
85
|
+
if (backups.length > 0) {
|
|
86
|
+
const restorePath = resolve(backupDir, backups[0]);
|
|
87
|
+
console.error(`Attempting restore from: ${restorePath}`);
|
|
88
|
+
try {
|
|
89
|
+
copyFileSync(restorePath, resolve(DB_PATH));
|
|
90
|
+
console.error('Restore complete — exiting for restart with restored DB');
|
|
91
|
+
process.exit(1);
|
|
92
|
+
} catch (restoreErr) {
|
|
93
|
+
console.error(`Restore failed: ${(restoreErr as Error).message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
console.error('No backup available — continuing with potentially corrupt DB');
|
|
98
|
+
} else {
|
|
99
|
+
console.log(' DB integrity check: ok');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Engines
|
|
103
|
+
const activationEngine = new ActivationEngine(store);
|
|
104
|
+
const connectionEngine = new ConnectionEngine(store, activationEngine);
|
|
105
|
+
const stagingBuffer = new StagingBuffer(store, activationEngine);
|
|
106
|
+
const evictionEngine = new EvictionEngine(store);
|
|
107
|
+
const retractionEngine = new RetractionEngine(store);
|
|
108
|
+
const evalEngine = new EvalEngine(store);
|
|
109
|
+
const consolidationEngine = new ConsolidationEngine(store, connectionEngine);
|
|
110
|
+
const consolidationScheduler = new ConsolidationScheduler(store, consolidationEngine);
|
|
111
|
+
|
|
112
|
+
// API — disable Fastify's default request logging (too noisy for hive polling)
|
|
113
|
+
// bodyLimit: 512KB to prevent Content-Length mismatch errors with large task payloads
|
|
114
|
+
const app = Fastify({ logger: false, bodyLimit: 512_000 });
|
|
115
|
+
|
|
116
|
+
// Bearer token auth — only enforced when AWM_API_KEY is explicitly set and non-empty
|
|
117
|
+
if (API_KEY && API_KEY !== 'NONE' && API_KEY.length > 1) {
|
|
118
|
+
app.addHook('onRequest', async (req, reply) => {
|
|
119
|
+
if (req.url === '/health') return; // Health check is always public
|
|
120
|
+
const bearer = req.headers.authorization;
|
|
121
|
+
const xApiKey = req.headers['x-api-key'] as string | undefined;
|
|
122
|
+
if (bearer === `Bearer ${API_KEY}` || xApiKey === API_KEY) return;
|
|
123
|
+
reply.code(401).send({ error: 'Unauthorized' });
|
|
124
|
+
});
|
|
125
|
+
console.log('API key auth enabled (AWM_API_KEY set)');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
registerRoutes(app, {
|
|
129
|
+
store, activationEngine, connectionEngine,
|
|
130
|
+
evictionEngine, retractionEngine, evalEngine,
|
|
131
|
+
consolidationEngine, consolidationScheduler,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Coordination module (opt-in via AWM_COORDINATION=true) — SQLite-only.
|
|
135
|
+
const { isCoordinationEnabled, initCoordination, stopCoordinationCleanup } = await import('./coordination/index.js');
|
|
136
|
+
if (isCoordinationEnabled() && BACKEND === 'sqlite') {
|
|
137
|
+
initCoordination(app, store.getDb(), store);
|
|
138
|
+
} else if (isCoordinationEnabled()) {
|
|
139
|
+
console.log(` Coordination requested but disabled — requires SQLite backend (current: ${BACKEND})`);
|
|
140
|
+
} else {
|
|
141
|
+
console.log(' Coordination module disabled (set AWM_COORDINATION=true to enable)');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Background tasks
|
|
145
|
+
stagingBuffer.start(DEFAULT_AGENT_CONFIG.stagingTtlMs);
|
|
146
|
+
consolidationScheduler.start();
|
|
147
|
+
|
|
148
|
+
// Periodic hot backup every 10 minutes (keep last 6 = 1hr coverage)
|
|
149
|
+
const dbDir = dirname(resolve(DB_PATH));
|
|
150
|
+
const backupDir = resolve(dbDir, 'backups');
|
|
151
|
+
mkdirSync(backupDir, { recursive: true });
|
|
152
|
+
|
|
153
|
+
// Cleanup old backups on startup (older than 2 hours)
|
|
154
|
+
try {
|
|
155
|
+
const TWO_HOURS_MS = 2 * 60 * 60 * 1000;
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
for (const f of readdirSync(backupDir).filter(f => f.endsWith('.db'))) {
|
|
158
|
+
const match = f.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})/);
|
|
159
|
+
if (match) {
|
|
160
|
+
const fileDate = new Date(`${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}:${match[6]}Z`);
|
|
161
|
+
if (now - fileDate.getTime() > TWO_HOURS_MS) {
|
|
162
|
+
unlinkSync(resolve(backupDir, f));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch { /* cleanup is non-fatal */ }
|
|
167
|
+
|
|
168
|
+
// Periodic hot backup — SQLite-only. PGlite directory backups are an OS-level concern.
|
|
169
|
+
const backupTimer: ReturnType<typeof setInterval> | null = BACKEND === 'sqlite' ? setInterval(() => {
|
|
170
|
+
try {
|
|
171
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
172
|
+
const backupPath = resolve(backupDir, `${basename(DB_PATH, '.db')}-${ts}.db`);
|
|
173
|
+
store.backup(backupPath);
|
|
174
|
+
// Prune: keep only last 6 backups
|
|
175
|
+
const backups = readdirSync(backupDir).filter(f => f.endsWith('.db')).sort();
|
|
176
|
+
while (backups.length > 6) {
|
|
177
|
+
const old = backups.shift()!;
|
|
178
|
+
try { unlinkSync(resolve(backupDir, old)); } catch { /* non-fatal */ }
|
|
179
|
+
}
|
|
180
|
+
} catch (err) {
|
|
181
|
+
console.warn(`[backup] failed: ${(err as Error).message}`);
|
|
182
|
+
}
|
|
183
|
+
}, 10 * 60_000) : null;
|
|
184
|
+
|
|
185
|
+
// Pre-load ML models (downloads on first run: embeddings ~22MB, reranker ~22MB, expander ~80MB)
|
|
186
|
+
getEmbedder().catch(err => console.warn('Embedding model unavailable:', err.message));
|
|
187
|
+
getReranker().catch(err => console.warn('Reranker model unavailable:', err.message));
|
|
188
|
+
getExpander().catch(err => console.warn('Query expander model unavailable:', err.message));
|
|
189
|
+
|
|
190
|
+
if (BACKEND === 'sqlite') {
|
|
191
|
+
setImmediate(() => {
|
|
192
|
+
try {
|
|
193
|
+
const t0 = Date.now();
|
|
194
|
+
store.warmSlimCache();
|
|
195
|
+
const stats = store.getSlimCacheStats();
|
|
196
|
+
console.log(` Slim cache warmed: ${stats.size} entries in ${Date.now() - t0}ms`);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.warn(` Slim cache warm failed: ${(err as Error).message}`);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Start server
|
|
204
|
+
await app.listen({ port: PORT, host: '0.0.0.0' });
|
|
205
|
+
console.log(`AgentWorkingMemory v0.8.5 listening on port ${PORT}`);
|
|
206
|
+
|
|
207
|
+
// Graceful shutdown
|
|
208
|
+
const shutdown = async () => {
|
|
209
|
+
if (backupTimer) clearInterval(backupTimer);
|
|
210
|
+
await stopCoordinationCleanup();
|
|
211
|
+
consolidationScheduler.stop();
|
|
212
|
+
stagingBuffer.stop();
|
|
213
|
+
if (BACKEND === 'sqlite') {
|
|
214
|
+
try { store.walCheckpoint(); } catch { /* non-fatal */ }
|
|
215
|
+
}
|
|
216
|
+
try { await (store as any).close?.(); } catch { /* best-effort */ }
|
|
217
|
+
process.exit(0);
|
|
218
|
+
};
|
|
219
|
+
process.on('SIGINT', shutdown);
|
|
220
|
+
process.on('SIGTERM', shutdown);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
main().catch(err => {
|
|
224
|
+
console.error('Failed to start:', err);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
});
|