agent-working-memory 0.7.0 → 0.7.2
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 +20 -5
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +9 -1
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +107 -10
- package/dist/api/routes.js.map +1 -1
- package/dist/cli.js +103 -103
- package/dist/core/auto-tagger.d.ts +29 -0
- package/dist/core/auto-tagger.d.ts.map +1 -0
- package/dist/core/auto-tagger.js +139 -0
- package/dist/core/auto-tagger.js.map +1 -0
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js.map +1 -1
- package/dist/engine/consolidation.d.ts +1 -0
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +149 -9
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp.js +114 -83
- package/dist/mcp.js.map +1 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +6 -5
- 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 +57 -57
- package/src/adapters/common.ts +9 -1
- package/src/api/routes.ts +723 -602
- package/src/cli.ts +719 -719
- package/src/core/auto-tagger.ts +168 -0
- package/src/core/query-expander.ts +0 -1
- package/src/core/reranker.ts +0 -1
- package/src/engine/consolidation.ts +165 -9
- package/src/index.ts +199 -199
- package/src/mcp.ts +1192 -1166
- package/src/storage/sqlite.ts +6 -5
- package/src/types/engram.ts +1 -0
package/src/api/routes.ts
CHANGED
|
@@ -1,602 +1,723 @@
|
|
|
1
|
-
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
/**
|
|
4
|
-
* API Routes — the black box interface agents interact with.
|
|
5
|
-
*
|
|
6
|
-
* Core (agent-facing):
|
|
7
|
-
* POST /memory/write — write a memory (salience filter decides disposition)
|
|
8
|
-
* POST /memory/activate — retrieve by context activation
|
|
9
|
-
* POST /memory/feedback — report whether a memory was useful
|
|
10
|
-
* POST /memory/retract — invalidate a wrong memory
|
|
11
|
-
*
|
|
12
|
-
* Checkpointing:
|
|
13
|
-
* POST /memory/checkpoint — save explicit execution state
|
|
14
|
-
* GET /memory/restore/:agentId — restore state + targeted recall + async mini-consolidation
|
|
15
|
-
*
|
|
16
|
-
* Task management:
|
|
17
|
-
* POST /task/create — create a prioritized task
|
|
18
|
-
* POST /task/update — update status, priority, or blocking
|
|
19
|
-
* GET /task/list/:agentId — list tasks (filtered by status)
|
|
20
|
-
* GET /task/next/:agentId — get highest-priority actionable task
|
|
21
|
-
*
|
|
22
|
-
* Diagnostic (debugging/eval):
|
|
23
|
-
* POST /memory/search — deterministic search (not cognitive)
|
|
24
|
-
* GET /memory/:id — get a specific engram
|
|
25
|
-
* GET /agent/:id/stats — memory stats for an agent
|
|
26
|
-
* GET /agent/:id/metrics — eval metrics
|
|
27
|
-
* POST /agent/register — register a new agent
|
|
28
|
-
*
|
|
29
|
-
* System:
|
|
30
|
-
* POST /system/evict — trigger eviction check
|
|
31
|
-
* POST /system/decay — trigger edge decay
|
|
32
|
-
* POST /system/consolidate — run sleep cycle (strengthen, decay, sweep)
|
|
33
|
-
* GET /health — health check
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
import type { FastifyInstance } from 'fastify';
|
|
37
|
-
import type { EngramStore } from '../storage/sqlite.js';
|
|
38
|
-
import type { ActivationEngine } from '../engine/activation.js';
|
|
39
|
-
import type { ConnectionEngine } from '../engine/connections.js';
|
|
40
|
-
import type { EvictionEngine } from '../engine/eviction.js';
|
|
41
|
-
import type { RetractionEngine } from '../engine/retraction.js';
|
|
42
|
-
import type { EvalEngine } from '../engine/eval.js';
|
|
43
|
-
import type { ConsolidationEngine } from '../engine/consolidation.js';
|
|
44
|
-
import type { ConsolidationScheduler } from '../engine/consolidation-scheduler.js';
|
|
45
|
-
import { evaluateSalience, computeNovelty } from '../core/salience.js';
|
|
46
|
-
import type { SalienceEventType } from '../core/salience.js';
|
|
47
|
-
import type { TaskStatus, TaskPriority } from '../types/engram.js';
|
|
48
|
-
import type { ConsciousState } from '../types/checkpoint.js';
|
|
49
|
-
import { DEFAULT_AGENT_CONFIG } from '../types/agent.js';
|
|
50
|
-
import { embed } from '../core/embeddings.js';
|
|
51
|
-
|
|
52
|
-
export interface MemoryDeps {
|
|
53
|
-
store: EngramStore;
|
|
54
|
-
activationEngine: ActivationEngine;
|
|
55
|
-
connectionEngine: ConnectionEngine;
|
|
56
|
-
evictionEngine: EvictionEngine;
|
|
57
|
-
retractionEngine: RetractionEngine;
|
|
58
|
-
evalEngine: EvalEngine;
|
|
59
|
-
consolidationEngine: ConsolidationEngine;
|
|
60
|
-
consolidationScheduler: ConsolidationScheduler;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function registerRoutes(app: FastifyInstance, deps: MemoryDeps): void {
|
|
64
|
-
const { store, activationEngine, connectionEngine, evictionEngine, retractionEngine, evalEngine, consolidationEngine, consolidationScheduler } = deps;
|
|
65
|
-
|
|
66
|
-
// ============================================================
|
|
67
|
-
// CORE — Agent-facing endpoints
|
|
68
|
-
// ============================================================
|
|
69
|
-
|
|
70
|
-
app.post('/memory/write', async (req, reply) => {
|
|
71
|
-
const body = req.body as {
|
|
72
|
-
agentId: string;
|
|
73
|
-
concept: string;
|
|
74
|
-
content: string;
|
|
75
|
-
tags?: string[];
|
|
76
|
-
eventType?: SalienceEventType;
|
|
77
|
-
surprise?: number;
|
|
78
|
-
decisionMade?: boolean;
|
|
79
|
-
causalDepth?: number;
|
|
80
|
-
resolutionEffort?: number;
|
|
81
|
-
confidence?: number;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
if (
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
if (
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* API Routes — the black box interface agents interact with.
|
|
5
|
+
*
|
|
6
|
+
* Core (agent-facing):
|
|
7
|
+
* POST /memory/write — write a memory (salience filter decides disposition)
|
|
8
|
+
* POST /memory/activate — retrieve by context activation
|
|
9
|
+
* POST /memory/feedback — report whether a memory was useful
|
|
10
|
+
* POST /memory/retract — invalidate a wrong memory
|
|
11
|
+
*
|
|
12
|
+
* Checkpointing:
|
|
13
|
+
* POST /memory/checkpoint — save explicit execution state
|
|
14
|
+
* GET /memory/restore/:agentId — restore state + targeted recall + async mini-consolidation
|
|
15
|
+
*
|
|
16
|
+
* Task management:
|
|
17
|
+
* POST /task/create — create a prioritized task
|
|
18
|
+
* POST /task/update — update status, priority, or blocking
|
|
19
|
+
* GET /task/list/:agentId — list tasks (filtered by status)
|
|
20
|
+
* GET /task/next/:agentId — get highest-priority actionable task
|
|
21
|
+
*
|
|
22
|
+
* Diagnostic (debugging/eval):
|
|
23
|
+
* POST /memory/search — deterministic search (not cognitive)
|
|
24
|
+
* GET /memory/:id — get a specific engram
|
|
25
|
+
* GET /agent/:id/stats — memory stats for an agent
|
|
26
|
+
* GET /agent/:id/metrics — eval metrics
|
|
27
|
+
* POST /agent/register — register a new agent
|
|
28
|
+
*
|
|
29
|
+
* System:
|
|
30
|
+
* POST /system/evict — trigger eviction check
|
|
31
|
+
* POST /system/decay — trigger edge decay
|
|
32
|
+
* POST /system/consolidate — run sleep cycle (strengthen, decay, sweep)
|
|
33
|
+
* GET /health — health check
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import type { FastifyInstance } from 'fastify';
|
|
37
|
+
import type { EngramStore } from '../storage/sqlite.js';
|
|
38
|
+
import type { ActivationEngine } from '../engine/activation.js';
|
|
39
|
+
import type { ConnectionEngine } from '../engine/connections.js';
|
|
40
|
+
import type { EvictionEngine } from '../engine/eviction.js';
|
|
41
|
+
import type { RetractionEngine } from '../engine/retraction.js';
|
|
42
|
+
import type { EvalEngine } from '../engine/eval.js';
|
|
43
|
+
import type { ConsolidationEngine } from '../engine/consolidation.js';
|
|
44
|
+
import type { ConsolidationScheduler } from '../engine/consolidation-scheduler.js';
|
|
45
|
+
import { evaluateSalience, computeNovelty } from '../core/salience.js';
|
|
46
|
+
import type { SalienceEventType } from '../core/salience.js';
|
|
47
|
+
import type { TaskStatus, TaskPriority } from '../types/engram.js';
|
|
48
|
+
import type { ConsciousState } from '../types/checkpoint.js';
|
|
49
|
+
import { DEFAULT_AGENT_CONFIG } from '../types/agent.js';
|
|
50
|
+
import { embed, embedBatch } from '../core/embeddings.js';
|
|
51
|
+
|
|
52
|
+
export interface MemoryDeps {
|
|
53
|
+
store: EngramStore;
|
|
54
|
+
activationEngine: ActivationEngine;
|
|
55
|
+
connectionEngine: ConnectionEngine;
|
|
56
|
+
evictionEngine: EvictionEngine;
|
|
57
|
+
retractionEngine: RetractionEngine;
|
|
58
|
+
evalEngine: EvalEngine;
|
|
59
|
+
consolidationEngine: ConsolidationEngine;
|
|
60
|
+
consolidationScheduler: ConsolidationScheduler;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function registerRoutes(app: FastifyInstance, deps: MemoryDeps): void {
|
|
64
|
+
const { store, activationEngine, connectionEngine, evictionEngine, retractionEngine, evalEngine, consolidationEngine, consolidationScheduler } = deps;
|
|
65
|
+
|
|
66
|
+
// ============================================================
|
|
67
|
+
// CORE — Agent-facing endpoints
|
|
68
|
+
// ============================================================
|
|
69
|
+
|
|
70
|
+
app.post('/memory/write', async (req, reply) => {
|
|
71
|
+
const body = req.body as {
|
|
72
|
+
agentId: string;
|
|
73
|
+
concept: string;
|
|
74
|
+
content: string;
|
|
75
|
+
tags?: string[];
|
|
76
|
+
eventType?: SalienceEventType;
|
|
77
|
+
surprise?: number;
|
|
78
|
+
decisionMade?: boolean;
|
|
79
|
+
causalDepth?: number;
|
|
80
|
+
resolutionEffort?: number;
|
|
81
|
+
confidence?: number;
|
|
82
|
+
// Agent-provided metadata (stored as searchable tags)
|
|
83
|
+
project?: string;
|
|
84
|
+
topic?: string;
|
|
85
|
+
source?: string;
|
|
86
|
+
confidenceLevel?: string;
|
|
87
|
+
sessionId?: string;
|
|
88
|
+
intent?: string;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (!body.agentId || typeof body.agentId !== 'string' ||
|
|
92
|
+
!body.concept || typeof body.concept !== 'string' ||
|
|
93
|
+
!body.content || typeof body.content !== 'string') {
|
|
94
|
+
return reply.status(400).send({ error: 'agentId, concept, and content are required strings' });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const novelty = computeNovelty(store, body.agentId, body.concept, body.content);
|
|
98
|
+
|
|
99
|
+
const salience = evaluateSalience({
|
|
100
|
+
content: body.content,
|
|
101
|
+
eventType: body.eventType,
|
|
102
|
+
surprise: body.surprise,
|
|
103
|
+
decisionMade: body.decisionMade,
|
|
104
|
+
causalDepth: body.causalDepth,
|
|
105
|
+
resolutionEffort: body.resolutionEffort,
|
|
106
|
+
novelty,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// v0.5.4: No longer discard — store with low confidence for ranking.
|
|
110
|
+
const isLowSalience = salience.disposition === 'discard';
|
|
111
|
+
const confidence = isLowSalience
|
|
112
|
+
? 0.25
|
|
113
|
+
: body.confidence ?? (salience.disposition === 'staging' ? 0.40 : 0.50);
|
|
114
|
+
|
|
115
|
+
// Assemble tags: user-provided + agent metadata
|
|
116
|
+
const userTags = body.tags ?? [];
|
|
117
|
+
const metaTags: string[] = [];
|
|
118
|
+
if (body.project) metaTags.push(`proj=${body.project}`);
|
|
119
|
+
if (body.topic) metaTags.push(`topic=${body.topic}`);
|
|
120
|
+
if (body.source) metaTags.push(`src=${body.source}`);
|
|
121
|
+
if (body.confidenceLevel) metaTags.push(`conf=${body.confidenceLevel}`);
|
|
122
|
+
if (body.sessionId) metaTags.push(`sid=${body.sessionId}`);
|
|
123
|
+
if (body.intent) metaTags.push(`intent=${body.intent}`);
|
|
124
|
+
const allTags = isLowSalience
|
|
125
|
+
? [...userTags, ...metaTags, 'low-salience']
|
|
126
|
+
: [...userTags, ...metaTags];
|
|
127
|
+
|
|
128
|
+
const engram = store.createEngram({
|
|
129
|
+
agentId: body.agentId,
|
|
130
|
+
concept: body.concept,
|
|
131
|
+
content: body.content,
|
|
132
|
+
tags: allTags,
|
|
133
|
+
salience: salience.score,
|
|
134
|
+
confidence,
|
|
135
|
+
salienceFeatures: salience.features,
|
|
136
|
+
reasonCodes: salience.reasonCodes,
|
|
137
|
+
ttl: salience.disposition === 'staging' ? DEFAULT_AGENT_CONFIG.stagingTtlMs : undefined,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (salience.disposition === 'staging') {
|
|
141
|
+
store.updateStage(engram.id, 'staging');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Create temporal adjacency edge to previous memory (conversation thread graph)
|
|
145
|
+
// This enables multi-hop graph walk through conversation sequences
|
|
146
|
+
try {
|
|
147
|
+
const prev = store.getLatestEngram(body.agentId, engram.id);
|
|
148
|
+
if (prev) {
|
|
149
|
+
store.upsertAssociation(prev.id, engram.id, 0.3, 'temporal', 0.8);
|
|
150
|
+
}
|
|
151
|
+
} catch { /* Temporal edge creation is non-fatal */ }
|
|
152
|
+
|
|
153
|
+
if (salience.disposition === 'active' || isLowSalience) {
|
|
154
|
+
connectionEngine.enqueue(engram.id);
|
|
155
|
+
|
|
156
|
+
// Auto-assign to episode (1-hour window per agent)
|
|
157
|
+
try {
|
|
158
|
+
let episode = store.getActiveEpisode(body.agentId, 3600_000);
|
|
159
|
+
if (!episode) {
|
|
160
|
+
episode = store.createEpisode({ agentId: body.agentId, label: body.concept });
|
|
161
|
+
}
|
|
162
|
+
store.addEngramToEpisode(engram.id, episode.id);
|
|
163
|
+
} catch { /* Episode assignment is non-fatal */ }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Generate embedding asynchronously (don't block response)
|
|
167
|
+
embed(`${body.concept} ${body.content}`).then(vec => {
|
|
168
|
+
store.updateEmbedding(engram.id, vec);
|
|
169
|
+
}).catch(() => {}); // Embedding failure is non-fatal
|
|
170
|
+
|
|
171
|
+
// Auto-checkpoint: track write for consolidation scheduling
|
|
172
|
+
try { store.updateAutoCheckpointWrite(body.agentId, engram.id); } catch { /* non-fatal */ }
|
|
173
|
+
|
|
174
|
+
return reply.code(201).send({
|
|
175
|
+
stored: true,
|
|
176
|
+
disposition: isLowSalience ? 'low-salience' : salience.disposition,
|
|
177
|
+
salience: salience.score,
|
|
178
|
+
reasonCodes: salience.reasonCodes,
|
|
179
|
+
engram,
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Bulk write — accepts many facts in one request.
|
|
185
|
+
* Creates engrams in a single transaction, embeds in batch.
|
|
186
|
+
* Returns all IDs for downstream supersession calls.
|
|
187
|
+
*/
|
|
188
|
+
app.post('/memory/write-batch', async (req, reply) => {
|
|
189
|
+
const body = req.body as {
|
|
190
|
+
agentId: string;
|
|
191
|
+
sessionId?: string; // Shared session ID for all memories in this batch
|
|
192
|
+
memories: Array<{
|
|
193
|
+
concept: string;
|
|
194
|
+
content: string;
|
|
195
|
+
tags?: string[];
|
|
196
|
+
supersedes?: string;
|
|
197
|
+
sessionId?: string; // Per-memory session override
|
|
198
|
+
}>;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
if (!body.agentId || !body.memories || body.memories.length === 0) {
|
|
202
|
+
return reply.code(400).send({ error: 'agentId and non-empty memories array required' });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const results: Array<{ id: string; concept: string; disposition: string }> = [];
|
|
206
|
+
|
|
207
|
+
for (const mem of body.memories) {
|
|
208
|
+
// Add session ID tag if provided (batch-level or per-memory)
|
|
209
|
+
const sid = mem.sessionId ?? body.sessionId;
|
|
210
|
+
const memTags = [...(mem.tags ?? [])];
|
|
211
|
+
if (sid) memTags.push(`sid=${sid}`);
|
|
212
|
+
|
|
213
|
+
const engram = store.createEngram({
|
|
214
|
+
agentId: body.agentId,
|
|
215
|
+
concept: mem.concept,
|
|
216
|
+
content: mem.content,
|
|
217
|
+
tags: memTags,
|
|
218
|
+
salience: 0.5,
|
|
219
|
+
confidence: 0.5,
|
|
220
|
+
supersedes: mem.supersedes ?? undefined,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Handle supersession inline — archive superseded memory to remove from active pool
|
|
224
|
+
if (mem.supersedes) {
|
|
225
|
+
store.supersedeEngram(mem.supersedes, engram.id);
|
|
226
|
+
store.updateConfidence(mem.supersedes, 0.1);
|
|
227
|
+
store.updateStage(mem.supersedes, 'archived'); // Remove from active search pool
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
results.push({ id: engram.id, concept: mem.concept, disposition: 'active' });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Batch embed synchronously — ensures embeddings are ready before queries hit
|
|
234
|
+
const texts = body.memories.map((m, i) => `${m.concept} ${m.content}`);
|
|
235
|
+
try {
|
|
236
|
+
const vecs = await embedBatch(texts);
|
|
237
|
+
for (let i = 0; i < vecs.length; i++) {
|
|
238
|
+
if (results[i]) {
|
|
239
|
+
store.updateEmbedding(results[i].id, vecs[i]);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch { /* Embedding failure is non-fatal */ }
|
|
243
|
+
|
|
244
|
+
try { store.updateAutoCheckpointWrite(body.agentId, results[results.length - 1]?.id ?? ''); } catch {}
|
|
245
|
+
|
|
246
|
+
return reply.code(201).send({
|
|
247
|
+
stored: results.length,
|
|
248
|
+
results,
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
app.post('/memory/activate', async (req, reply) => {
|
|
253
|
+
const body = req.body as {
|
|
254
|
+
agentId: string;
|
|
255
|
+
context: string;
|
|
256
|
+
limit?: number;
|
|
257
|
+
minScore?: number;
|
|
258
|
+
includeStaging?: boolean;
|
|
259
|
+
useReranker?: boolean;
|
|
260
|
+
useExpansion?: boolean;
|
|
261
|
+
abstentionThreshold?: number;
|
|
262
|
+
workspace?: string;
|
|
263
|
+
bm25Only?: boolean;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const results = await activationEngine.activate({
|
|
267
|
+
agentId: body.agentId,
|
|
268
|
+
context: body.context,
|
|
269
|
+
limit: body.limit,
|
|
270
|
+
minScore: body.minScore,
|
|
271
|
+
includeStaging: body.includeStaging,
|
|
272
|
+
useReranker: body.useReranker,
|
|
273
|
+
useExpansion: body.useExpansion,
|
|
274
|
+
abstentionThreshold: body.abstentionThreshold,
|
|
275
|
+
workspace: body.workspace,
|
|
276
|
+
bm25Only: body.bm25Only,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Auto-checkpoint: track recall for consolidation scheduling
|
|
280
|
+
try {
|
|
281
|
+
const ids = results.map(r => r.engram.id);
|
|
282
|
+
store.updateAutoCheckpointRecall(body.agentId, body.context, ids);
|
|
283
|
+
} catch { /* non-fatal */ }
|
|
284
|
+
|
|
285
|
+
return reply.send({ results });
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
app.post('/memory/feedback', async (req, reply) => {
|
|
289
|
+
const body = req.body as {
|
|
290
|
+
activationEventId?: string;
|
|
291
|
+
engramId: string;
|
|
292
|
+
useful: boolean;
|
|
293
|
+
context?: string;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
store.logRetrievalFeedback(
|
|
297
|
+
body.activationEventId ?? null,
|
|
298
|
+
body.engramId,
|
|
299
|
+
body.useful,
|
|
300
|
+
body.context ?? ''
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// Update engram confidence based on feedback
|
|
304
|
+
const engram = store.getEngram(body.engramId);
|
|
305
|
+
if (engram) {
|
|
306
|
+
const config = DEFAULT_AGENT_CONFIG;
|
|
307
|
+
const delta = body.useful
|
|
308
|
+
? config.feedbackPositiveBoost
|
|
309
|
+
: -config.feedbackNegativePenalty;
|
|
310
|
+
store.updateConfidence(engram.id, engram.confidence + delta);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Touch activity for consolidation scheduling
|
|
314
|
+
if (engram) {
|
|
315
|
+
try { store.touchActivity(engram.agentId); } catch { /* non-fatal */ }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return reply.send({ recorded: true });
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
app.post('/memory/retract', async (req, reply) => {
|
|
322
|
+
const body = req.body as {
|
|
323
|
+
agentId: string;
|
|
324
|
+
targetEngramId: string;
|
|
325
|
+
reason: string;
|
|
326
|
+
counterContent?: string;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const result = retractionEngine.retract({
|
|
330
|
+
agentId: body.agentId,
|
|
331
|
+
targetEngramId: body.targetEngramId,
|
|
332
|
+
reason: body.reason,
|
|
333
|
+
counterContent: body.counterContent,
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Touch activity for consolidation scheduling
|
|
337
|
+
try { store.touchActivity(body.agentId); } catch { /* non-fatal */ }
|
|
338
|
+
|
|
339
|
+
return reply.send(result);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
app.post('/memory/supersede', async (req, reply) => {
|
|
343
|
+
const body = req.body as {
|
|
344
|
+
oldEngramId: string;
|
|
345
|
+
newEngramId: string;
|
|
346
|
+
reason?: string;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const oldEngram = store.getEngram(body.oldEngramId);
|
|
350
|
+
const newEngram = store.getEngram(body.newEngramId);
|
|
351
|
+
if (!oldEngram) return reply.code(404).send({ error: `Old engram ${body.oldEngramId} not found` });
|
|
352
|
+
if (!newEngram) return reply.code(404).send({ error: `New engram ${body.newEngramId} not found` });
|
|
353
|
+
|
|
354
|
+
// Create causal association (new → old)
|
|
355
|
+
store.upsertAssociation(body.newEngramId, body.oldEngramId, 0.8, 'causal', 1.0);
|
|
356
|
+
|
|
357
|
+
// Reduce old engram confidence to 20% (keep for historical reference)
|
|
358
|
+
store.updateConfidence(body.oldEngramId, oldEngram.confidence * 0.2);
|
|
359
|
+
|
|
360
|
+
// Mark supersession via store method
|
|
361
|
+
store.supersedeEngram(body.oldEngramId, body.newEngramId);
|
|
362
|
+
|
|
363
|
+
try { store.touchActivity(oldEngram.agentId); } catch { /* non-fatal */ }
|
|
364
|
+
|
|
365
|
+
return reply.send({
|
|
366
|
+
superseded: body.oldEngramId,
|
|
367
|
+
supersededBy: body.newEngramId,
|
|
368
|
+
reason: body.reason ?? 'outdated',
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// ============================================================
|
|
373
|
+
// DIAGNOSTIC — Debugging and inspection
|
|
374
|
+
// ============================================================
|
|
375
|
+
|
|
376
|
+
app.post('/memory/search', async (req, reply) => {
|
|
377
|
+
const body = req.body as {
|
|
378
|
+
agentId: string;
|
|
379
|
+
text?: string;
|
|
380
|
+
concept?: string;
|
|
381
|
+
tags?: string[];
|
|
382
|
+
stage?: string;
|
|
383
|
+
retracted?: boolean;
|
|
384
|
+
limit?: number;
|
|
385
|
+
offset?: number;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const results = store.search({
|
|
389
|
+
agentId: body.agentId,
|
|
390
|
+
text: body.text,
|
|
391
|
+
concept: body.concept,
|
|
392
|
+
tags: body.tags,
|
|
393
|
+
stage: body.stage as any,
|
|
394
|
+
retracted: body.retracted,
|
|
395
|
+
limit: body.limit,
|
|
396
|
+
offset: body.offset,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
return reply.send({ results, count: results.length });
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
app.get('/memory/:id', async (req, reply) => {
|
|
403
|
+
const { id } = req.params as { id: string };
|
|
404
|
+
const engram = store.getEngram(id);
|
|
405
|
+
if (!engram) return reply.code(404).send({ error: 'Not found' });
|
|
406
|
+
|
|
407
|
+
const associations = store.getAssociationsFor(id);
|
|
408
|
+
return reply.send({ engram, associations });
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
app.get('/agent/:id/stats', async (req, reply) => {
|
|
412
|
+
const { id } = req.params as { id: string };
|
|
413
|
+
const active = store.getEngramsByAgent(id, 'active');
|
|
414
|
+
const staging = store.getEngramsByAgent(id, 'staging');
|
|
415
|
+
const retracted = store.getEngramsByAgent(id, undefined, true).filter(e => e.retracted);
|
|
416
|
+
const associations = store.getAllAssociations(id);
|
|
417
|
+
|
|
418
|
+
return reply.send({
|
|
419
|
+
agentId: id,
|
|
420
|
+
engrams: {
|
|
421
|
+
active: active.length,
|
|
422
|
+
staging: staging.length,
|
|
423
|
+
retracted: retracted.length,
|
|
424
|
+
total: active.length + staging.length + retracted.length,
|
|
425
|
+
},
|
|
426
|
+
associations: associations.length,
|
|
427
|
+
avgConfidence: active.length > 0
|
|
428
|
+
? +(active.reduce((s, e) => s + e.confidence, 0) / active.length).toFixed(3)
|
|
429
|
+
: 0,
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
app.get('/agent/:id/metrics', async (req, reply) => {
|
|
434
|
+
const { id } = req.params as { id: string };
|
|
435
|
+
const windowHours = parseInt((req.query as any).window ?? '24', 10);
|
|
436
|
+
const metrics = evalEngine.computeMetrics(id, windowHours);
|
|
437
|
+
return reply.send({ metrics });
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
app.post('/agent/register', async (req, reply) => {
|
|
441
|
+
const body = req.body as { name: string };
|
|
442
|
+
const id = crypto.randomUUID();
|
|
443
|
+
return reply.code(201).send({
|
|
444
|
+
id,
|
|
445
|
+
name: body.name,
|
|
446
|
+
config: DEFAULT_AGENT_CONFIG,
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// ============================================================
|
|
451
|
+
// SYSTEM — Maintenance operations
|
|
452
|
+
// ============================================================
|
|
453
|
+
|
|
454
|
+
app.post('/system/evict', async (req, reply) => {
|
|
455
|
+
const body = req.body as { agentId: string };
|
|
456
|
+
const result = evictionEngine.enforceCapacity(body.agentId, DEFAULT_AGENT_CONFIG);
|
|
457
|
+
return reply.send(result);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
app.post('/system/decay', async (req, reply) => {
|
|
461
|
+
const body = req.body as { agentId: string; halfLifeDays?: number };
|
|
462
|
+
const decayed = evictionEngine.decayEdges(body.agentId, body.halfLifeDays);
|
|
463
|
+
return reply.send({ edgesDecayed: decayed });
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
app.post('/system/consolidate', async (req, reply) => {
|
|
467
|
+
const body = req.body as { agentId: string };
|
|
468
|
+
const result = await consolidationEngine.consolidate(body.agentId);
|
|
469
|
+
return reply.send(result);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// ============================================================
|
|
473
|
+
// CHECKPOINTING — Conscious state preservation
|
|
474
|
+
// ============================================================
|
|
475
|
+
|
|
476
|
+
app.post('/memory/checkpoint', async (req, reply) => {
|
|
477
|
+
const body = req.body as {
|
|
478
|
+
agentId: string;
|
|
479
|
+
currentTask: string;
|
|
480
|
+
decisions?: string[];
|
|
481
|
+
activeFiles?: string[];
|
|
482
|
+
nextSteps?: string[];
|
|
483
|
+
relatedMemoryIds?: string[];
|
|
484
|
+
notes?: string;
|
|
485
|
+
episodeId?: string | null;
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const state: ConsciousState = {
|
|
489
|
+
currentTask: body.currentTask,
|
|
490
|
+
decisions: body.decisions ?? [],
|
|
491
|
+
activeFiles: body.activeFiles ?? [],
|
|
492
|
+
nextSteps: body.nextSteps ?? [],
|
|
493
|
+
relatedMemoryIds: body.relatedMemoryIds ?? [],
|
|
494
|
+
notes: body.notes ?? '',
|
|
495
|
+
episodeId: body.episodeId ?? null,
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
store.saveCheckpoint(body.agentId, state);
|
|
499
|
+
return reply.send({ saved: true, agentId: body.agentId });
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
app.get('/memory/restore/:agentId', async (req, reply) => {
|
|
503
|
+
const { agentId } = req.params as { agentId: string };
|
|
504
|
+
const checkpoint = store.getCheckpoint(agentId);
|
|
505
|
+
|
|
506
|
+
const now = Date.now();
|
|
507
|
+
const idleMs = checkpoint
|
|
508
|
+
? now - checkpoint.auto.lastActivityAt.getTime()
|
|
509
|
+
: 0;
|
|
510
|
+
|
|
511
|
+
// Get last written engram for context
|
|
512
|
+
let lastWrite: { id: string; concept: string; content: string } | null = null;
|
|
513
|
+
if (checkpoint?.auto.lastWriteId) {
|
|
514
|
+
const engram = store.getEngram(checkpoint.auto.lastWriteId);
|
|
515
|
+
if (engram) {
|
|
516
|
+
lastWrite = { id: engram.id, concept: engram.concept, content: engram.content };
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Recall memories using last context (if available)
|
|
521
|
+
let recalledMemories: Array<{ id: string; concept: string; content: string; score: number }> = [];
|
|
522
|
+
const recallContext = checkpoint?.auto.lastRecallContext
|
|
523
|
+
?? checkpoint?.executionState?.currentTask
|
|
524
|
+
?? null;
|
|
525
|
+
|
|
526
|
+
if (recallContext) {
|
|
527
|
+
try {
|
|
528
|
+
const results = await activationEngine.activate({
|
|
529
|
+
agentId,
|
|
530
|
+
context: recallContext,
|
|
531
|
+
limit: 5,
|
|
532
|
+
minScore: 0.05,
|
|
533
|
+
useReranker: true,
|
|
534
|
+
useExpansion: true,
|
|
535
|
+
});
|
|
536
|
+
recalledMemories = results.map(r => ({
|
|
537
|
+
id: r.engram.id,
|
|
538
|
+
concept: r.engram.concept,
|
|
539
|
+
content: r.engram.content,
|
|
540
|
+
score: r.score,
|
|
541
|
+
}));
|
|
542
|
+
} catch { /* recall failure is non-fatal */ }
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Trigger mini-consolidation if idle >5min (async, fire-and-forget)
|
|
546
|
+
const MINI_CONSOLIDATION_IDLE_MS = 5 * 60_000;
|
|
547
|
+
let miniConsolidationTriggered = false;
|
|
548
|
+
if (idleMs > MINI_CONSOLIDATION_IDLE_MS) {
|
|
549
|
+
miniConsolidationTriggered = true;
|
|
550
|
+
consolidationScheduler.runMiniConsolidation(agentId).catch(() => {});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return reply.send({
|
|
554
|
+
executionState: checkpoint?.executionState ?? null,
|
|
555
|
+
checkpointAt: checkpoint?.checkpointAt ?? null,
|
|
556
|
+
recalledMemories,
|
|
557
|
+
lastWrite,
|
|
558
|
+
idleMs,
|
|
559
|
+
miniConsolidationTriggered,
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// ============================================================
|
|
564
|
+
// TASK MANAGEMENT
|
|
565
|
+
// ============================================================
|
|
566
|
+
|
|
567
|
+
app.post('/task/create', async (req, reply) => {
|
|
568
|
+
const body = req.body as {
|
|
569
|
+
agentId: string;
|
|
570
|
+
concept: string;
|
|
571
|
+
content: string;
|
|
572
|
+
tags?: string[];
|
|
573
|
+
priority?: TaskPriority;
|
|
574
|
+
blockedBy?: string;
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const engram = store.createEngram({
|
|
578
|
+
agentId: body.agentId,
|
|
579
|
+
concept: body.concept,
|
|
580
|
+
content: body.content,
|
|
581
|
+
tags: [...(body.tags ?? []), 'task'],
|
|
582
|
+
salience: 0.9,
|
|
583
|
+
confidence: 0.8,
|
|
584
|
+
salienceFeatures: {
|
|
585
|
+
surprise: 0.5, decisionMade: true, causalDepth: 0.5,
|
|
586
|
+
resolutionEffort: 0.5, eventType: 'decision',
|
|
587
|
+
},
|
|
588
|
+
reasonCodes: ['task-created'],
|
|
589
|
+
taskStatus: body.blockedBy ? 'blocked' : 'open',
|
|
590
|
+
taskPriority: body.priority ?? 'medium',
|
|
591
|
+
blockedBy: body.blockedBy,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
connectionEngine.enqueue(engram.id);
|
|
595
|
+
embed(`${body.concept} ${body.content}`).then(vec => {
|
|
596
|
+
store.updateEmbedding(engram.id, vec);
|
|
597
|
+
}).catch(() => {});
|
|
598
|
+
|
|
599
|
+
return reply.send(engram);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
app.post('/task/update', async (req, reply) => {
|
|
603
|
+
const body = req.body as {
|
|
604
|
+
taskId: string;
|
|
605
|
+
status?: TaskStatus;
|
|
606
|
+
priority?: TaskPriority;
|
|
607
|
+
blockedBy?: string | null;
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
const engram = store.getEngram(body.taskId);
|
|
611
|
+
if (!engram || !engram.taskStatus) {
|
|
612
|
+
return reply.code(404).send({ error: 'Task not found' });
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (body.blockedBy !== undefined) {
|
|
616
|
+
store.updateBlockedBy(body.taskId, body.blockedBy);
|
|
617
|
+
}
|
|
618
|
+
if (body.status) {
|
|
619
|
+
store.updateTaskStatus(body.taskId, body.status);
|
|
620
|
+
}
|
|
621
|
+
if (body.priority) {
|
|
622
|
+
store.updateTaskPriority(body.taskId, body.priority);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return reply.send(store.getEngram(body.taskId));
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
app.get('/task/list/:agentId', async (req, reply) => {
|
|
629
|
+
const { agentId } = req.params as { agentId: string };
|
|
630
|
+
const { status, includeDone } = req.query as { status?: TaskStatus; includeDone?: string };
|
|
631
|
+
|
|
632
|
+
let tasks = store.getTasks(agentId, status);
|
|
633
|
+
if (includeDone !== 'true' && !status) {
|
|
634
|
+
tasks = tasks.filter(t => t.taskStatus !== 'done');
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return reply.send({ tasks, count: tasks.length });
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
app.get('/task/next/:agentId', async (req, reply) => {
|
|
641
|
+
const { agentId } = req.params as { agentId: string };
|
|
642
|
+
const next = store.getNextTask(agentId);
|
|
643
|
+
return reply.send(next ? { task: next } : { task: null, message: 'No actionable tasks' });
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// Time warp — shift all timestamps backward by N days (for testing)
|
|
647
|
+
app.post('/system/time-warp', async (req, reply) => {
|
|
648
|
+
const body = req.body as { agentId: string; days: number };
|
|
649
|
+
const ms = body.days * 24 * 60 * 60 * 1000;
|
|
650
|
+
const shifted = store.timeWarp(body.agentId, ms);
|
|
651
|
+
return reply.send({ shifted, days: body.days });
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
// ─── Export ─────────────────────────────────────────────────────────────
|
|
655
|
+
|
|
656
|
+
app.get('/memory/export', async (req, reply) => {
|
|
657
|
+
const { agentId, all } = req.query as { agentId?: string; all?: string };
|
|
658
|
+
const includeAll = all === 'true';
|
|
659
|
+
const db = store.getDb();
|
|
660
|
+
|
|
661
|
+
let engramSql = `SELECT id, agent_id, concept, content, confidence, salience, access_count,
|
|
662
|
+
last_accessed, created_at, salience_features, reason_codes, stage, ttl,
|
|
663
|
+
retracted, retracted_by, retracted_at, tags
|
|
664
|
+
FROM engrams`;
|
|
665
|
+
const conditions: string[] = [];
|
|
666
|
+
const params: string[] = [];
|
|
667
|
+
|
|
668
|
+
if (agentId) {
|
|
669
|
+
conditions.push('agent_id = ?');
|
|
670
|
+
params.push(agentId);
|
|
671
|
+
}
|
|
672
|
+
if (!includeAll) {
|
|
673
|
+
conditions.push('retracted = 0');
|
|
674
|
+
conditions.push("stage = 'active'");
|
|
675
|
+
}
|
|
676
|
+
if (conditions.length > 0) {
|
|
677
|
+
engramSql += ' WHERE ' + conditions.join(' AND ');
|
|
678
|
+
}
|
|
679
|
+
engramSql += ' ORDER BY created_at ASC';
|
|
680
|
+
|
|
681
|
+
const engrams = db.prepare(engramSql).all(...params) as { id: string }[];
|
|
682
|
+
|
|
683
|
+
const engramIds = new Set(engrams.map(e => e.id));
|
|
684
|
+
const allAssocs = db.prepare(
|
|
685
|
+
`SELECT id, from_engram_id, to_engram_id, weight, confidence, type, activation_count, created_at, last_activated
|
|
686
|
+
FROM associations`
|
|
687
|
+
).all() as { from_engram_id: string; to_engram_id: string }[];
|
|
688
|
+
const associations = allAssocs.filter(a => engramIds.has(a.from_engram_id) && engramIds.has(a.to_engram_id));
|
|
689
|
+
|
|
690
|
+
return reply.send({
|
|
691
|
+
exported_at: new Date().toISOString(),
|
|
692
|
+
agent_id: agentId ?? null,
|
|
693
|
+
include_all: includeAll,
|
|
694
|
+
engrams_count: engrams.length,
|
|
695
|
+
associations_count: associations.length,
|
|
696
|
+
engrams,
|
|
697
|
+
associations,
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
// ─── Health ─────────────────────────────────────────────────────────────
|
|
702
|
+
|
|
703
|
+
app.get('/health', async () => {
|
|
704
|
+
const coordEnabled = process.env.AWM_COORDINATION === 'true' || process.env.AWM_COORDINATION === '1';
|
|
705
|
+
const base: Record<string, unknown> = {
|
|
706
|
+
status: 'ok',
|
|
707
|
+
timestamp: new Date().toISOString(),
|
|
708
|
+
version: '0.7.2',
|
|
709
|
+
coordination: coordEnabled,
|
|
710
|
+
};
|
|
711
|
+
if (coordEnabled) {
|
|
712
|
+
try {
|
|
713
|
+
const db = deps.store.getDb();
|
|
714
|
+
const stats = db.prepare(`SELECT
|
|
715
|
+
(SELECT COUNT(*) FROM coord_agents WHERE status != 'dead') AS agents_alive,
|
|
716
|
+
(SELECT COUNT(*) FROM coord_assignments WHERE status = 'pending') AS pending_tasks,
|
|
717
|
+
(SELECT COUNT(*) FROM coord_locks) AS active_locks`).get() as { agents_alive: number; pending_tasks: number; active_locks: number };
|
|
718
|
+
Object.assign(base, stats);
|
|
719
|
+
} catch { /* tables may not exist yet */ }
|
|
720
|
+
}
|
|
721
|
+
return base;
|
|
722
|
+
});
|
|
723
|
+
}
|