agent-working-memory 0.3.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/LICENSE +21 -0
- package/README.md +311 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +2 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/routes.d.ts +53 -0
- package/dist/api/routes.d.ts.map +1 -0
- package/dist/api/routes.js +388 -0
- package/dist/api/routes.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +245 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/decay.d.ts +36 -0
- package/dist/core/decay.d.ts.map +1 -0
- package/dist/core/decay.js +38 -0
- package/dist/core/decay.js.map +1 -0
- package/dist/core/embeddings.d.ts +33 -0
- package/dist/core/embeddings.d.ts.map +1 -0
- package/dist/core/embeddings.js +76 -0
- package/dist/core/embeddings.js.map +1 -0
- package/dist/core/hebbian.d.ts +38 -0
- package/dist/core/hebbian.d.ts.map +1 -0
- package/dist/core/hebbian.js +74 -0
- package/dist/core/hebbian.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/query-expander.d.ts +24 -0
- package/dist/core/query-expander.d.ts.map +1 -0
- package/dist/core/query-expander.js +58 -0
- package/dist/core/query-expander.js.map +1 -0
- package/dist/core/reranker.d.ts +25 -0
- package/dist/core/reranker.d.ts.map +1 -0
- package/dist/core/reranker.js +75 -0
- package/dist/core/reranker.js.map +1 -0
- package/dist/core/salience.d.ts +30 -0
- package/dist/core/salience.d.ts.map +1 -0
- package/dist/core/salience.js +81 -0
- package/dist/core/salience.js.map +1 -0
- package/dist/engine/activation.d.ts +38 -0
- package/dist/engine/activation.d.ts.map +1 -0
- package/dist/engine/activation.js +516 -0
- package/dist/engine/activation.js.map +1 -0
- package/dist/engine/connections.d.ts +31 -0
- package/dist/engine/connections.d.ts.map +1 -0
- package/dist/engine/connections.js +74 -0
- package/dist/engine/connections.js.map +1 -0
- package/dist/engine/consolidation-scheduler.d.ts +31 -0
- package/dist/engine/consolidation-scheduler.d.ts.map +1 -0
- package/dist/engine/consolidation-scheduler.js +115 -0
- package/dist/engine/consolidation-scheduler.js.map +1 -0
- package/dist/engine/consolidation.d.ts +62 -0
- package/dist/engine/consolidation.d.ts.map +1 -0
- package/dist/engine/consolidation.js +368 -0
- package/dist/engine/consolidation.js.map +1 -0
- package/dist/engine/eval.d.ts +22 -0
- package/dist/engine/eval.d.ts.map +1 -0
- package/dist/engine/eval.js +79 -0
- package/dist/engine/eval.js.map +1 -0
- package/dist/engine/eviction.d.ts +29 -0
- package/dist/engine/eviction.d.ts.map +1 -0
- package/dist/engine/eviction.js +86 -0
- package/dist/engine/eviction.js.map +1 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +7 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/retraction.d.ts +32 -0
- package/dist/engine/retraction.d.ts.map +1 -0
- package/dist/engine/retraction.js +77 -0
- package/dist/engine/retraction.js.map +1 -0
- package/dist/engine/staging.d.ts +33 -0
- package/dist/engine/staging.d.ts.map +1 -0
- package/dist/engine/staging.js +63 -0
- package/dist/engine/staging.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +24 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +532 -0
- package/dist/mcp.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/sqlite.d.ts +116 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +750 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/types/agent.d.ts +30 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +23 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/checkpoint.d.ts +50 -0
- package/dist/types/checkpoint.d.ts.map +1 -0
- package/dist/types/checkpoint.js +8 -0
- package/dist/types/checkpoint.js.map +1 -0
- package/dist/types/engram.d.ts +165 -0
- package/dist/types/engram.d.ts.map +1 -0
- package/dist/types/engram.js +8 -0
- package/dist/types/engram.js.map +1 -0
- package/dist/types/eval.d.ts +84 -0
- package/dist/types/eval.d.ts.map +1 -0
- package/dist/types/eval.js +11 -0
- package/dist/types/eval.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +55 -0
- package/src/api/index.ts +1 -0
- package/src/api/routes.ts +528 -0
- package/src/cli.ts +260 -0
- package/src/core/decay.ts +61 -0
- package/src/core/embeddings.ts +82 -0
- package/src/core/hebbian.ts +91 -0
- package/src/core/index.ts +3 -0
- package/src/core/query-expander.ts +64 -0
- package/src/core/reranker.ts +99 -0
- package/src/core/salience.ts +95 -0
- package/src/engine/activation.ts +577 -0
- package/src/engine/connections.ts +101 -0
- package/src/engine/consolidation-scheduler.ts +123 -0
- package/src/engine/consolidation.ts +443 -0
- package/src/engine/eval.ts +100 -0
- package/src/engine/eviction.ts +99 -0
- package/src/engine/index.ts +6 -0
- package/src/engine/retraction.ts +98 -0
- package/src/engine/staging.ts +72 -0
- package/src/index.ts +100 -0
- package/src/mcp.ts +635 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/sqlite.ts +893 -0
- package/src/types/agent.ts +65 -0
- package/src/types/checkpoint.ts +44 -0
- package/src/types/engram.ts +194 -0
- package/src/types/eval.ts +98 -0
- package/src/types/index.ts +4 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Routes — the black box interface agents interact with.
|
|
3
|
+
*
|
|
4
|
+
* Core (agent-facing):
|
|
5
|
+
* POST /memory/write — write a memory (salience filter decides disposition)
|
|
6
|
+
* POST /memory/activate — retrieve by context activation
|
|
7
|
+
* POST /memory/feedback — report whether a memory was useful
|
|
8
|
+
* POST /memory/retract — invalidate a wrong memory
|
|
9
|
+
*
|
|
10
|
+
* Checkpointing:
|
|
11
|
+
* POST /memory/checkpoint — save explicit execution state
|
|
12
|
+
* GET /memory/restore/:agentId — restore state + targeted recall + async mini-consolidation
|
|
13
|
+
*
|
|
14
|
+
* Task management:
|
|
15
|
+
* POST /task/create — create a prioritized task
|
|
16
|
+
* POST /task/update — update status, priority, or blocking
|
|
17
|
+
* GET /task/list/:agentId — list tasks (filtered by status)
|
|
18
|
+
* GET /task/next/:agentId — get highest-priority actionable task
|
|
19
|
+
*
|
|
20
|
+
* Diagnostic (debugging/eval):
|
|
21
|
+
* POST /memory/search — deterministic search (not cognitive)
|
|
22
|
+
* GET /memory/:id — get a specific engram
|
|
23
|
+
* GET /agent/:id/stats — memory stats for an agent
|
|
24
|
+
* GET /agent/:id/metrics — eval metrics
|
|
25
|
+
* POST /agent/register — register a new agent
|
|
26
|
+
*
|
|
27
|
+
* System:
|
|
28
|
+
* POST /system/evict — trigger eviction check
|
|
29
|
+
* POST /system/decay — trigger edge decay
|
|
30
|
+
* POST /system/consolidate — run sleep cycle (strengthen, decay, sweep)
|
|
31
|
+
* GET /health — health check
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import type { FastifyInstance } from 'fastify';
|
|
35
|
+
import type { EngramStore } from '../storage/sqlite.js';
|
|
36
|
+
import type { ActivationEngine } from '../engine/activation.js';
|
|
37
|
+
import type { ConnectionEngine } from '../engine/connections.js';
|
|
38
|
+
import type { EvictionEngine } from '../engine/eviction.js';
|
|
39
|
+
import type { RetractionEngine } from '../engine/retraction.js';
|
|
40
|
+
import type { EvalEngine } from '../engine/eval.js';
|
|
41
|
+
import type { ConsolidationEngine } from '../engine/consolidation.js';
|
|
42
|
+
import type { ConsolidationScheduler } from '../engine/consolidation-scheduler.js';
|
|
43
|
+
import { evaluateSalience } from '../core/salience.js';
|
|
44
|
+
import type { SalienceEventType } from '../core/salience.js';
|
|
45
|
+
import type { TaskStatus, TaskPriority } from '../types/engram.js';
|
|
46
|
+
import type { ConsciousState } from '../types/checkpoint.js';
|
|
47
|
+
import { DEFAULT_AGENT_CONFIG } from '../types/agent.js';
|
|
48
|
+
import { embed } from '../core/embeddings.js';
|
|
49
|
+
|
|
50
|
+
export interface MemoryDeps {
|
|
51
|
+
store: EngramStore;
|
|
52
|
+
activationEngine: ActivationEngine;
|
|
53
|
+
connectionEngine: ConnectionEngine;
|
|
54
|
+
evictionEngine: EvictionEngine;
|
|
55
|
+
retractionEngine: RetractionEngine;
|
|
56
|
+
evalEngine: EvalEngine;
|
|
57
|
+
consolidationEngine: ConsolidationEngine;
|
|
58
|
+
consolidationScheduler: ConsolidationScheduler;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function registerRoutes(app: FastifyInstance, deps: MemoryDeps): void {
|
|
62
|
+
const { store, activationEngine, connectionEngine, evictionEngine, retractionEngine, evalEngine, consolidationEngine, consolidationScheduler } = deps;
|
|
63
|
+
|
|
64
|
+
// ============================================================
|
|
65
|
+
// CORE — Agent-facing endpoints
|
|
66
|
+
// ============================================================
|
|
67
|
+
|
|
68
|
+
app.post('/memory/write', async (req, reply) => {
|
|
69
|
+
const body = req.body as {
|
|
70
|
+
agentId: string;
|
|
71
|
+
concept: string;
|
|
72
|
+
content: string;
|
|
73
|
+
tags?: string[];
|
|
74
|
+
eventType?: SalienceEventType;
|
|
75
|
+
surprise?: number;
|
|
76
|
+
decisionMade?: boolean;
|
|
77
|
+
causalDepth?: number;
|
|
78
|
+
resolutionEffort?: number;
|
|
79
|
+
confidence?: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const salience = evaluateSalience({
|
|
83
|
+
content: body.content,
|
|
84
|
+
eventType: body.eventType,
|
|
85
|
+
surprise: body.surprise,
|
|
86
|
+
decisionMade: body.decisionMade,
|
|
87
|
+
causalDepth: body.causalDepth,
|
|
88
|
+
resolutionEffort: body.resolutionEffort,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (salience.disposition === 'discard') {
|
|
92
|
+
return reply.code(200).send({
|
|
93
|
+
stored: false,
|
|
94
|
+
disposition: 'discard',
|
|
95
|
+
salience: salience.score,
|
|
96
|
+
reasonCodes: salience.reasonCodes,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const engram = store.createEngram({
|
|
101
|
+
agentId: body.agentId,
|
|
102
|
+
concept: body.concept,
|
|
103
|
+
content: body.content,
|
|
104
|
+
tags: body.tags,
|
|
105
|
+
salience: salience.score,
|
|
106
|
+
confidence: body.confidence,
|
|
107
|
+
salienceFeatures: salience.features,
|
|
108
|
+
reasonCodes: salience.reasonCodes,
|
|
109
|
+
ttl: salience.disposition === 'staging' ? DEFAULT_AGENT_CONFIG.stagingTtlMs : undefined,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (salience.disposition === 'staging') {
|
|
113
|
+
store.updateStage(engram.id, 'staging');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Create temporal adjacency edge to previous memory (conversation thread graph)
|
|
117
|
+
// This enables multi-hop graph walk through conversation sequences
|
|
118
|
+
try {
|
|
119
|
+
const prev = store.getLatestEngram(body.agentId, engram.id);
|
|
120
|
+
if (prev) {
|
|
121
|
+
store.upsertAssociation(prev.id, engram.id, 0.3, 'temporal', 0.8);
|
|
122
|
+
}
|
|
123
|
+
} catch { /* Temporal edge creation is non-fatal */ }
|
|
124
|
+
|
|
125
|
+
if (salience.disposition === 'active') {
|
|
126
|
+
connectionEngine.enqueue(engram.id);
|
|
127
|
+
|
|
128
|
+
// Auto-assign to episode (1-hour window per agent)
|
|
129
|
+
try {
|
|
130
|
+
let episode = store.getActiveEpisode(body.agentId, 3600_000);
|
|
131
|
+
if (!episode) {
|
|
132
|
+
episode = store.createEpisode({ agentId: body.agentId, label: body.concept });
|
|
133
|
+
}
|
|
134
|
+
store.addEngramToEpisode(engram.id, episode.id);
|
|
135
|
+
} catch { /* Episode assignment is non-fatal */ }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Generate embedding asynchronously (don't block response)
|
|
139
|
+
embed(`${body.concept} ${body.content}`).then(vec => {
|
|
140
|
+
store.updateEmbedding(engram.id, vec);
|
|
141
|
+
}).catch(() => {}); // Embedding failure is non-fatal
|
|
142
|
+
|
|
143
|
+
// Auto-checkpoint: track write for consolidation scheduling
|
|
144
|
+
try { store.updateAutoCheckpointWrite(body.agentId, engram.id); } catch { /* non-fatal */ }
|
|
145
|
+
|
|
146
|
+
return reply.code(201).send({
|
|
147
|
+
stored: true,
|
|
148
|
+
disposition: salience.disposition,
|
|
149
|
+
salience: salience.score,
|
|
150
|
+
reasonCodes: salience.reasonCodes,
|
|
151
|
+
engram,
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
app.post('/memory/activate', async (req, reply) => {
|
|
156
|
+
const body = req.body as {
|
|
157
|
+
agentId: string;
|
|
158
|
+
context: string;
|
|
159
|
+
limit?: number;
|
|
160
|
+
minScore?: number;
|
|
161
|
+
includeStaging?: boolean;
|
|
162
|
+
useReranker?: boolean;
|
|
163
|
+
useExpansion?: boolean;
|
|
164
|
+
abstentionThreshold?: number;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const results = await activationEngine.activate({
|
|
168
|
+
agentId: body.agentId,
|
|
169
|
+
context: body.context,
|
|
170
|
+
limit: body.limit,
|
|
171
|
+
minScore: body.minScore,
|
|
172
|
+
includeStaging: body.includeStaging,
|
|
173
|
+
useReranker: body.useReranker,
|
|
174
|
+
useExpansion: body.useExpansion,
|
|
175
|
+
abstentionThreshold: body.abstentionThreshold,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Auto-checkpoint: track recall for consolidation scheduling
|
|
179
|
+
try {
|
|
180
|
+
const ids = results.map(r => r.engram.id);
|
|
181
|
+
store.updateAutoCheckpointRecall(body.agentId, body.context, ids);
|
|
182
|
+
} catch { /* non-fatal */ }
|
|
183
|
+
|
|
184
|
+
return reply.send({ results });
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
app.post('/memory/feedback', async (req, reply) => {
|
|
188
|
+
const body = req.body as {
|
|
189
|
+
activationEventId?: string;
|
|
190
|
+
engramId: string;
|
|
191
|
+
useful: boolean;
|
|
192
|
+
context?: string;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
store.logRetrievalFeedback(
|
|
196
|
+
body.activationEventId ?? null,
|
|
197
|
+
body.engramId,
|
|
198
|
+
body.useful,
|
|
199
|
+
body.context ?? ''
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// Update engram confidence based on feedback
|
|
203
|
+
const engram = store.getEngram(body.engramId);
|
|
204
|
+
if (engram) {
|
|
205
|
+
const config = DEFAULT_AGENT_CONFIG;
|
|
206
|
+
const delta = body.useful
|
|
207
|
+
? config.feedbackPositiveBoost
|
|
208
|
+
: -config.feedbackNegativePenalty;
|
|
209
|
+
store.updateConfidence(engram.id, engram.confidence + delta);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Touch activity for consolidation scheduling
|
|
213
|
+
if (engram) {
|
|
214
|
+
try { store.touchActivity(engram.agentId); } catch { /* non-fatal */ }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return reply.send({ recorded: true });
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
app.post('/memory/retract', async (req, reply) => {
|
|
221
|
+
const body = req.body as {
|
|
222
|
+
agentId: string;
|
|
223
|
+
targetEngramId: string;
|
|
224
|
+
reason: string;
|
|
225
|
+
counterContent?: string;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const result = retractionEngine.retract({
|
|
229
|
+
agentId: body.agentId,
|
|
230
|
+
targetEngramId: body.targetEngramId,
|
|
231
|
+
reason: body.reason,
|
|
232
|
+
counterContent: body.counterContent,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Touch activity for consolidation scheduling
|
|
236
|
+
try { store.touchActivity(body.agentId); } catch { /* non-fatal */ }
|
|
237
|
+
|
|
238
|
+
return reply.send(result);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ============================================================
|
|
242
|
+
// DIAGNOSTIC — Debugging and inspection
|
|
243
|
+
// ============================================================
|
|
244
|
+
|
|
245
|
+
app.post('/memory/search', async (req, reply) => {
|
|
246
|
+
const body = req.body as {
|
|
247
|
+
agentId: string;
|
|
248
|
+
text?: string;
|
|
249
|
+
concept?: string;
|
|
250
|
+
tags?: string[];
|
|
251
|
+
stage?: string;
|
|
252
|
+
retracted?: boolean;
|
|
253
|
+
limit?: number;
|
|
254
|
+
offset?: number;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const results = store.search({
|
|
258
|
+
agentId: body.agentId,
|
|
259
|
+
text: body.text,
|
|
260
|
+
concept: body.concept,
|
|
261
|
+
tags: body.tags,
|
|
262
|
+
stage: body.stage as any,
|
|
263
|
+
retracted: body.retracted,
|
|
264
|
+
limit: body.limit,
|
|
265
|
+
offset: body.offset,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
return reply.send({ results, count: results.length });
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
app.get('/memory/:id', async (req, reply) => {
|
|
272
|
+
const { id } = req.params as { id: string };
|
|
273
|
+
const engram = store.getEngram(id);
|
|
274
|
+
if (!engram) return reply.code(404).send({ error: 'Not found' });
|
|
275
|
+
|
|
276
|
+
const associations = store.getAssociationsFor(id);
|
|
277
|
+
return reply.send({ engram, associations });
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
app.get('/agent/:id/stats', async (req, reply) => {
|
|
281
|
+
const { id } = req.params as { id: string };
|
|
282
|
+
const active = store.getEngramsByAgent(id, 'active');
|
|
283
|
+
const staging = store.getEngramsByAgent(id, 'staging');
|
|
284
|
+
const retracted = store.getEngramsByAgent(id, undefined, true).filter(e => e.retracted);
|
|
285
|
+
const associations = store.getAllAssociations(id);
|
|
286
|
+
|
|
287
|
+
return reply.send({
|
|
288
|
+
agentId: id,
|
|
289
|
+
engrams: {
|
|
290
|
+
active: active.length,
|
|
291
|
+
staging: staging.length,
|
|
292
|
+
retracted: retracted.length,
|
|
293
|
+
total: active.length + staging.length + retracted.length,
|
|
294
|
+
},
|
|
295
|
+
associations: associations.length,
|
|
296
|
+
avgConfidence: active.length > 0
|
|
297
|
+
? +(active.reduce((s, e) => s + e.confidence, 0) / active.length).toFixed(3)
|
|
298
|
+
: 0,
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
app.get('/agent/:id/metrics', async (req, reply) => {
|
|
303
|
+
const { id } = req.params as { id: string };
|
|
304
|
+
const windowHours = parseInt((req.query as any).window ?? '24', 10);
|
|
305
|
+
const metrics = evalEngine.computeMetrics(id, windowHours);
|
|
306
|
+
return reply.send({ metrics });
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
app.post('/agent/register', async (req, reply) => {
|
|
310
|
+
const body = req.body as { name: string };
|
|
311
|
+
const id = crypto.randomUUID();
|
|
312
|
+
return reply.code(201).send({
|
|
313
|
+
id,
|
|
314
|
+
name: body.name,
|
|
315
|
+
config: DEFAULT_AGENT_CONFIG,
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// ============================================================
|
|
320
|
+
// SYSTEM — Maintenance operations
|
|
321
|
+
// ============================================================
|
|
322
|
+
|
|
323
|
+
app.post('/system/evict', async (req, reply) => {
|
|
324
|
+
const body = req.body as { agentId: string };
|
|
325
|
+
const result = evictionEngine.enforceCapacity(body.agentId, DEFAULT_AGENT_CONFIG);
|
|
326
|
+
return reply.send(result);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
app.post('/system/decay', async (req, reply) => {
|
|
330
|
+
const body = req.body as { agentId: string; halfLifeDays?: number };
|
|
331
|
+
const decayed = evictionEngine.decayEdges(body.agentId, body.halfLifeDays);
|
|
332
|
+
return reply.send({ edgesDecayed: decayed });
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
app.post('/system/consolidate', async (req, reply) => {
|
|
336
|
+
const body = req.body as { agentId: string };
|
|
337
|
+
const result = consolidationEngine.consolidate(body.agentId);
|
|
338
|
+
return reply.send(result);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// ============================================================
|
|
342
|
+
// CHECKPOINTING — Conscious state preservation
|
|
343
|
+
// ============================================================
|
|
344
|
+
|
|
345
|
+
app.post('/memory/checkpoint', async (req, reply) => {
|
|
346
|
+
const body = req.body as {
|
|
347
|
+
agentId: string;
|
|
348
|
+
currentTask: string;
|
|
349
|
+
decisions?: string[];
|
|
350
|
+
activeFiles?: string[];
|
|
351
|
+
nextSteps?: string[];
|
|
352
|
+
relatedMemoryIds?: string[];
|
|
353
|
+
notes?: string;
|
|
354
|
+
episodeId?: string | null;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const state: ConsciousState = {
|
|
358
|
+
currentTask: body.currentTask,
|
|
359
|
+
decisions: body.decisions ?? [],
|
|
360
|
+
activeFiles: body.activeFiles ?? [],
|
|
361
|
+
nextSteps: body.nextSteps ?? [],
|
|
362
|
+
relatedMemoryIds: body.relatedMemoryIds ?? [],
|
|
363
|
+
notes: body.notes ?? '',
|
|
364
|
+
episodeId: body.episodeId ?? null,
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
store.saveCheckpoint(body.agentId, state);
|
|
368
|
+
return reply.send({ saved: true, agentId: body.agentId });
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
app.get('/memory/restore/:agentId', async (req, reply) => {
|
|
372
|
+
const { agentId } = req.params as { agentId: string };
|
|
373
|
+
const checkpoint = store.getCheckpoint(agentId);
|
|
374
|
+
|
|
375
|
+
const now = Date.now();
|
|
376
|
+
const idleMs = checkpoint
|
|
377
|
+
? now - checkpoint.auto.lastActivityAt.getTime()
|
|
378
|
+
: 0;
|
|
379
|
+
|
|
380
|
+
// Get last written engram for context
|
|
381
|
+
let lastWrite: { id: string; concept: string; content: string } | null = null;
|
|
382
|
+
if (checkpoint?.auto.lastWriteId) {
|
|
383
|
+
const engram = store.getEngram(checkpoint.auto.lastWriteId);
|
|
384
|
+
if (engram) {
|
|
385
|
+
lastWrite = { id: engram.id, concept: engram.concept, content: engram.content };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Recall memories using last context (if available)
|
|
390
|
+
let recalledMemories: Array<{ id: string; concept: string; content: string; score: number }> = [];
|
|
391
|
+
const recallContext = checkpoint?.auto.lastRecallContext
|
|
392
|
+
?? checkpoint?.executionState?.currentTask
|
|
393
|
+
?? null;
|
|
394
|
+
|
|
395
|
+
if (recallContext) {
|
|
396
|
+
try {
|
|
397
|
+
const results = await activationEngine.activate({
|
|
398
|
+
agentId,
|
|
399
|
+
context: recallContext,
|
|
400
|
+
limit: 5,
|
|
401
|
+
minScore: 0.05,
|
|
402
|
+
useReranker: true,
|
|
403
|
+
useExpansion: true,
|
|
404
|
+
});
|
|
405
|
+
recalledMemories = results.map(r => ({
|
|
406
|
+
id: r.engram.id,
|
|
407
|
+
concept: r.engram.concept,
|
|
408
|
+
content: r.engram.content,
|
|
409
|
+
score: r.score,
|
|
410
|
+
}));
|
|
411
|
+
} catch { /* recall failure is non-fatal */ }
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Trigger mini-consolidation if idle >5min (async, fire-and-forget)
|
|
415
|
+
const MINI_CONSOLIDATION_IDLE_MS = 5 * 60_000;
|
|
416
|
+
let miniConsolidationTriggered = false;
|
|
417
|
+
if (idleMs > MINI_CONSOLIDATION_IDLE_MS) {
|
|
418
|
+
miniConsolidationTriggered = true;
|
|
419
|
+
consolidationScheduler.runMiniConsolidation(agentId).catch(() => {});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return reply.send({
|
|
423
|
+
executionState: checkpoint?.executionState ?? null,
|
|
424
|
+
checkpointAt: checkpoint?.checkpointAt ?? null,
|
|
425
|
+
recalledMemories,
|
|
426
|
+
lastWrite,
|
|
427
|
+
idleMs,
|
|
428
|
+
miniConsolidationTriggered,
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// ============================================================
|
|
433
|
+
// TASK MANAGEMENT
|
|
434
|
+
// ============================================================
|
|
435
|
+
|
|
436
|
+
app.post('/task/create', async (req, reply) => {
|
|
437
|
+
const body = req.body as {
|
|
438
|
+
agentId: string;
|
|
439
|
+
concept: string;
|
|
440
|
+
content: string;
|
|
441
|
+
tags?: string[];
|
|
442
|
+
priority?: TaskPriority;
|
|
443
|
+
blockedBy?: string;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const engram = store.createEngram({
|
|
447
|
+
agentId: body.agentId,
|
|
448
|
+
concept: body.concept,
|
|
449
|
+
content: body.content,
|
|
450
|
+
tags: [...(body.tags ?? []), 'task'],
|
|
451
|
+
salience: 0.9,
|
|
452
|
+
confidence: 0.8,
|
|
453
|
+
salienceFeatures: {
|
|
454
|
+
surprise: 0.5, decisionMade: true, causalDepth: 0.5,
|
|
455
|
+
resolutionEffort: 0.5, eventType: 'decision',
|
|
456
|
+
},
|
|
457
|
+
reasonCodes: ['task-created'],
|
|
458
|
+
taskStatus: body.blockedBy ? 'blocked' : 'open',
|
|
459
|
+
taskPriority: body.priority ?? 'medium',
|
|
460
|
+
blockedBy: body.blockedBy,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
connectionEngine.enqueue(engram.id);
|
|
464
|
+
embed(`${body.concept} ${body.content}`).then(vec => {
|
|
465
|
+
store.updateEmbedding(engram.id, vec);
|
|
466
|
+
}).catch(() => {});
|
|
467
|
+
|
|
468
|
+
return reply.send(engram);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
app.post('/task/update', async (req, reply) => {
|
|
472
|
+
const body = req.body as {
|
|
473
|
+
taskId: string;
|
|
474
|
+
status?: TaskStatus;
|
|
475
|
+
priority?: TaskPriority;
|
|
476
|
+
blockedBy?: string | null;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
const engram = store.getEngram(body.taskId);
|
|
480
|
+
if (!engram || !engram.taskStatus) {
|
|
481
|
+
return reply.code(404).send({ error: 'Task not found' });
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (body.blockedBy !== undefined) {
|
|
485
|
+
store.updateBlockedBy(body.taskId, body.blockedBy);
|
|
486
|
+
}
|
|
487
|
+
if (body.status) {
|
|
488
|
+
store.updateTaskStatus(body.taskId, body.status);
|
|
489
|
+
}
|
|
490
|
+
if (body.priority) {
|
|
491
|
+
store.updateTaskPriority(body.taskId, body.priority);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return reply.send(store.getEngram(body.taskId));
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
app.get('/task/list/:agentId', async (req, reply) => {
|
|
498
|
+
const { agentId } = req.params as { agentId: string };
|
|
499
|
+
const { status, includeDone } = req.query as { status?: TaskStatus; includeDone?: string };
|
|
500
|
+
|
|
501
|
+
let tasks = store.getTasks(agentId, status);
|
|
502
|
+
if (includeDone !== 'true' && !status) {
|
|
503
|
+
tasks = tasks.filter(t => t.taskStatus !== 'done');
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return reply.send({ tasks, count: tasks.length });
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
app.get('/task/next/:agentId', async (req, reply) => {
|
|
510
|
+
const { agentId } = req.params as { agentId: string };
|
|
511
|
+
const next = store.getNextTask(agentId);
|
|
512
|
+
return reply.send(next ? { task: next } : { task: null, message: 'No actionable tasks' });
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// Time warp — shift all timestamps backward by N days (for testing)
|
|
516
|
+
app.post('/system/time-warp', async (req, reply) => {
|
|
517
|
+
const body = req.body as { agentId: string; days: number };
|
|
518
|
+
const ms = body.days * 24 * 60 * 60 * 1000;
|
|
519
|
+
const shifted = store.timeWarp(body.agentId, ms);
|
|
520
|
+
return reply.send({ shifted, days: body.days });
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
app.get('/health', async () => ({
|
|
524
|
+
status: 'ok',
|
|
525
|
+
timestamp: new Date().toISOString(),
|
|
526
|
+
version: '0.3.0',
|
|
527
|
+
}));
|
|
528
|
+
}
|