agent-working-memory 0.3.2 → 0.4.1
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 +56 -7
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +3 -1
- package/dist/api/routes.js.map +1 -1
- package/dist/cli.js +94 -23
- package/dist/cli.js.map +1 -1
- package/dist/core/logger.d.ts +12 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +32 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/salience.d.ts +14 -0
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +51 -5
- package/dist/core/salience.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +27 -0
- package/dist/hooks/sidecar.d.ts.map +1 -0
- package/dist/hooks/sidecar.js +220 -0
- package/dist/hooks/sidecar.js.map +1 -0
- package/dist/mcp.js +631 -417
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
- package/src/api/routes.ts +4 -1
- package/src/cli.ts +98 -23
- package/src/core/logger.ts +34 -0
- package/src/core/salience.ts +51 -5
- package/src/hooks/sidecar.ts +258 -0
- package/src/mcp.ts +282 -33
package/dist/core/salience.js
CHANGED
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
11
|
* Weights for the salience scoring formula.
|
|
12
|
+
* Novelty is the strongest signal — new information should always be stored.
|
|
13
|
+
* Duplicates get filtered aggressively.
|
|
12
14
|
*/
|
|
13
15
|
const WEIGHTS = {
|
|
14
|
-
surprise: 0.
|
|
15
|
-
decision: 0.
|
|
16
|
-
causalDepth: 0.
|
|
17
|
-
resolutionEffort: 0.
|
|
16
|
+
surprise: 0.15,
|
|
17
|
+
decision: 0.15,
|
|
18
|
+
causalDepth: 0.15,
|
|
19
|
+
resolutionEffort: 0.1,
|
|
20
|
+
novelty: 0.45,
|
|
18
21
|
};
|
|
19
22
|
/**
|
|
20
23
|
* Rule-based salience scorer with full audit trail.
|
|
@@ -28,11 +31,15 @@ export function evaluateSalience(input, activeThreshold = 0.4, stagingThreshold
|
|
|
28
31
|
eventType: input.eventType ?? 'observation',
|
|
29
32
|
};
|
|
30
33
|
const reasonCodes = [];
|
|
34
|
+
// Novelty: 1.0 = completely new info, 0 = exact duplicate exists
|
|
35
|
+
// Default to 0.8 (assume mostly novel) when caller doesn't check
|
|
36
|
+
const novelty = input.novelty ?? 0.8;
|
|
31
37
|
// Score components
|
|
32
38
|
const surpriseScore = WEIGHTS.surprise * features.surprise;
|
|
33
39
|
const decisionScore = WEIGHTS.decision * (features.decisionMade ? 1.0 : 0);
|
|
34
40
|
const causalScore = WEIGHTS.causalDepth * features.causalDepth;
|
|
35
41
|
const effortScore = WEIGHTS.resolutionEffort * features.resolutionEffort;
|
|
42
|
+
const noveltyScore = WEIGHTS.novelty * novelty;
|
|
36
43
|
if (features.surprise > 0.5)
|
|
37
44
|
reasonCodes.push('high_surprise');
|
|
38
45
|
if (features.decisionMade)
|
|
@@ -41,6 +48,10 @@ export function evaluateSalience(input, activeThreshold = 0.4, stagingThreshold
|
|
|
41
48
|
reasonCodes.push('causal_insight');
|
|
42
49
|
if (features.resolutionEffort > 0.5)
|
|
43
50
|
reasonCodes.push('high_effort_resolution');
|
|
51
|
+
if (novelty > 0.7)
|
|
52
|
+
reasonCodes.push('novel_information');
|
|
53
|
+
if (novelty < 0.3)
|
|
54
|
+
reasonCodes.push('redundant_information');
|
|
44
55
|
// Event type bonus
|
|
45
56
|
let typeBonus = 0;
|
|
46
57
|
switch (features.eventType) {
|
|
@@ -62,7 +73,7 @@ export function evaluateSalience(input, activeThreshold = 0.4, stagingThreshold
|
|
|
62
73
|
break;
|
|
63
74
|
case 'observation': break;
|
|
64
75
|
}
|
|
65
|
-
const score = Math.min(surpriseScore + decisionScore + causalScore + effortScore + typeBonus, 1.0);
|
|
76
|
+
const score = Math.min(surpriseScore + decisionScore + causalScore + effortScore + noveltyScore + typeBonus, 1.0);
|
|
66
77
|
let disposition;
|
|
67
78
|
if (score >= activeThreshold) {
|
|
68
79
|
disposition = 'active';
|
|
@@ -78,4 +89,39 @@ export function evaluateSalience(input, activeThreshold = 0.4, stagingThreshold
|
|
|
78
89
|
}
|
|
79
90
|
return { score, disposition, features, reasonCodes };
|
|
80
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Compute novelty score by checking how similar the content is to existing memories.
|
|
94
|
+
* Uses BM25 (synchronous, fast) to find the closest existing memory.
|
|
95
|
+
*
|
|
96
|
+
* Returns 0..1 where:
|
|
97
|
+
* 1.0 = nothing similar exists (completely novel)
|
|
98
|
+
* 0.0 = near-exact duplicate exists
|
|
99
|
+
*
|
|
100
|
+
* The check is cheap (~1ms) because BM25 is synchronous SQLite FTS5.
|
|
101
|
+
*/
|
|
102
|
+
export function computeNovelty(store, agentId, concept, content) {
|
|
103
|
+
// Search using concept + first 100 chars of content (enough to detect duplicates, fast)
|
|
104
|
+
const searchText = `${concept} ${content.slice(0, 100)}`;
|
|
105
|
+
try {
|
|
106
|
+
const results = store.searchBM25WithRank(agentId, searchText, 3);
|
|
107
|
+
if (results.length === 0)
|
|
108
|
+
return 1.0; // Nothing similar — fully novel
|
|
109
|
+
// BM25 scores are unbounded. Normalize the top score relative to a "strong match" threshold.
|
|
110
|
+
// A BM25 score > 15 typically indicates very high overlap. > 25 is near-duplicate.
|
|
111
|
+
const topScore = results[0].bm25Score;
|
|
112
|
+
if (topScore > 25)
|
|
113
|
+
return 0.1; // Near-duplicate
|
|
114
|
+
if (topScore > 15)
|
|
115
|
+
return 0.3; // High overlap
|
|
116
|
+
if (topScore > 8)
|
|
117
|
+
return 0.5; // Moderate overlap
|
|
118
|
+
if (topScore > 3)
|
|
119
|
+
return 0.7; // Some overlap
|
|
120
|
+
return 0.9; // Minimal overlap — mostly novel
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// If BM25 search fails (e.g., FTS not ready), assume novel
|
|
124
|
+
return 0.8;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
81
127
|
//# sourceMappingURL=salience.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"salience.js","sourceRoot":"","sources":["../../src/core/salience.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"salience.js","sourceRoot":"","sources":["../../src/core/salience.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Sidecar — lightweight HTTP server that runs alongside the MCP process.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code hooks (PreCompact, SessionEnd, etc.) send POST requests here.
|
|
5
|
+
* Since we share the same process as the MCP server, there's zero SQLite
|
|
6
|
+
* contention — we use the same store/engines directly.
|
|
7
|
+
*
|
|
8
|
+
* Endpoints:
|
|
9
|
+
* POST /hooks/checkpoint — auto-checkpoint (called by PreCompact, SessionEnd hooks)
|
|
10
|
+
* GET /health — health check
|
|
11
|
+
*
|
|
12
|
+
* Security:
|
|
13
|
+
* - Binds to 127.0.0.1 only (localhost)
|
|
14
|
+
* - Bearer token auth via AWM_HOOK_SECRET
|
|
15
|
+
*/
|
|
16
|
+
import type { EngramStore } from '../storage/sqlite.js';
|
|
17
|
+
export interface SidecarDeps {
|
|
18
|
+
store: EngramStore;
|
|
19
|
+
agentId: string;
|
|
20
|
+
secret: string | null;
|
|
21
|
+
port: number;
|
|
22
|
+
onConsolidate?: (agentId: string, reason: string) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function startSidecar(deps: SidecarDeps): {
|
|
25
|
+
close: () => void;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=sidecar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../../src/hooks/sidecar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAwGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,IAAI,CAAA;CAAE,CA6HrE"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Sidecar — lightweight HTTP server that runs alongside the MCP process.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code hooks (PreCompact, SessionEnd, etc.) send POST requests here.
|
|
5
|
+
* Since we share the same process as the MCP server, there's zero SQLite
|
|
6
|
+
* contention — we use the same store/engines directly.
|
|
7
|
+
*
|
|
8
|
+
* Endpoints:
|
|
9
|
+
* POST /hooks/checkpoint — auto-checkpoint (called by PreCompact, SessionEnd hooks)
|
|
10
|
+
* GET /health — health check
|
|
11
|
+
*
|
|
12
|
+
* Security:
|
|
13
|
+
* - Binds to 127.0.0.1 only (localhost)
|
|
14
|
+
* - Bearer token auth via AWM_HOOK_SECRET
|
|
15
|
+
*/
|
|
16
|
+
import { createServer } from 'node:http';
|
|
17
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
18
|
+
import { log, getLogPath } from '../core/logger.js';
|
|
19
|
+
/**
|
|
20
|
+
* Parse the Claude Code transcript to extract context for auto-checkpointing.
|
|
21
|
+
* Reads the JSONL transcript file and extracts recent tool calls, files, and user messages.
|
|
22
|
+
*/
|
|
23
|
+
function parseTranscript(transcriptPath) {
|
|
24
|
+
const ctx = {
|
|
25
|
+
currentTask: '',
|
|
26
|
+
activeFiles: [],
|
|
27
|
+
recentTools: [],
|
|
28
|
+
lastUserMessage: '',
|
|
29
|
+
};
|
|
30
|
+
try {
|
|
31
|
+
const raw = readFileSync(transcriptPath, 'utf-8');
|
|
32
|
+
const lines = raw.trim().split('\n').filter(Boolean);
|
|
33
|
+
const files = new Set();
|
|
34
|
+
const tools = [];
|
|
35
|
+
let lastUserMsg = '';
|
|
36
|
+
// Parse last 100 lines max to avoid huge transcripts
|
|
37
|
+
const recent = lines.slice(-100);
|
|
38
|
+
for (const line of recent) {
|
|
39
|
+
try {
|
|
40
|
+
const entry = JSON.parse(line);
|
|
41
|
+
// Extract user messages
|
|
42
|
+
if (entry.role === 'user' && typeof entry.content === 'string') {
|
|
43
|
+
lastUserMsg = entry.content.slice(0, 200);
|
|
44
|
+
}
|
|
45
|
+
if (entry.role === 'user' && Array.isArray(entry.content)) {
|
|
46
|
+
for (const part of entry.content) {
|
|
47
|
+
if (part.type === 'text' && typeof part.text === 'string') {
|
|
48
|
+
lastUserMsg = part.text.slice(0, 200);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Extract tool uses
|
|
53
|
+
if (entry.role === 'assistant' && Array.isArray(entry.content)) {
|
|
54
|
+
for (const part of entry.content) {
|
|
55
|
+
if (part.type === 'tool_use') {
|
|
56
|
+
tools.push(part.name);
|
|
57
|
+
// Extract file paths from tool inputs
|
|
58
|
+
const input = part.input;
|
|
59
|
+
if (input?.file_path)
|
|
60
|
+
files.add(String(input.file_path));
|
|
61
|
+
if (input?.path)
|
|
62
|
+
files.add(String(input.path));
|
|
63
|
+
if (input?.command && typeof input.command === 'string') {
|
|
64
|
+
// Try to extract file paths from bash commands
|
|
65
|
+
const pathMatch = input.command.match(/["']?([A-Z]:[/\\][^"'\s]+|\/[^\s"']+\.\w+)["']?/g);
|
|
66
|
+
if (pathMatch)
|
|
67
|
+
pathMatch.forEach((p) => files.add(p.replace(/["']/g, '')));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Skip malformed lines
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
ctx.lastUserMessage = lastUserMsg;
|
|
78
|
+
ctx.activeFiles = [...files].slice(-20); // Last 20 unique files
|
|
79
|
+
ctx.recentTools = tools.slice(-30); // Last 30 tool calls
|
|
80
|
+
ctx.currentTask = lastUserMsg || 'Unknown task (auto-checkpoint)';
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
ctx.currentTask = 'Auto-checkpoint (transcript unavailable)';
|
|
84
|
+
}
|
|
85
|
+
return ctx;
|
|
86
|
+
}
|
|
87
|
+
function readBody(req) {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const chunks = [];
|
|
90
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
91
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString()));
|
|
92
|
+
req.on('error', reject);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function json(res, status, body) {
|
|
96
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
97
|
+
res.end(JSON.stringify(body));
|
|
98
|
+
}
|
|
99
|
+
export function startSidecar(deps) {
|
|
100
|
+
const { store, agentId, secret, port, onConsolidate } = deps;
|
|
101
|
+
const server = createServer(async (req, res) => {
|
|
102
|
+
// CORS preflight
|
|
103
|
+
if (req.method === 'OPTIONS') {
|
|
104
|
+
res.writeHead(204);
|
|
105
|
+
res.end();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Health check — no auth required
|
|
109
|
+
if (req.url === '/health' && req.method === 'GET') {
|
|
110
|
+
json(res, 200, { status: 'ok', sidecar: true, agentId });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Auth check
|
|
114
|
+
if (secret) {
|
|
115
|
+
const auth = req.headers.authorization;
|
|
116
|
+
if (auth !== `Bearer ${secret}`) {
|
|
117
|
+
json(res, 401, { error: 'Unauthorized' });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// GET /stats — daily activity counts from the log (no auth required)
|
|
122
|
+
if (req.url === '/stats' && req.method === 'GET') {
|
|
123
|
+
try {
|
|
124
|
+
const lp = getLogPath();
|
|
125
|
+
if (!lp || !existsSync(lp)) {
|
|
126
|
+
json(res, 200, { error: 'No log file', writes: 0, recalls: 0, restores: 0, hooks: 0, total: 0 });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const raw = readFileSync(lp, 'utf-8');
|
|
130
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
131
|
+
const todayLines = raw.split('\n').filter(l => l.startsWith(today));
|
|
132
|
+
let writes = 0, recalls = 0, restores = 0, hooks = 0, checkpoints = 0;
|
|
133
|
+
for (const line of todayLines) {
|
|
134
|
+
if (line.includes('| write:'))
|
|
135
|
+
writes++;
|
|
136
|
+
else if (line.includes('| recall'))
|
|
137
|
+
recalls++;
|
|
138
|
+
else if (line.includes('| restore'))
|
|
139
|
+
restores++;
|
|
140
|
+
else if (line.includes('| hook:'))
|
|
141
|
+
hooks++;
|
|
142
|
+
else if (line.includes('| checkpoint'))
|
|
143
|
+
checkpoints++;
|
|
144
|
+
}
|
|
145
|
+
const total = writes + recalls + restores + hooks + checkpoints;
|
|
146
|
+
json(res, 200, { date: today, agentId, writes, recalls, restores, hooks, checkpoints, total });
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
json(res, 500, { error: 'Failed to read log' });
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// POST /hooks/checkpoint — auto-checkpoint from hook events
|
|
154
|
+
if (req.url === '/hooks/checkpoint' && req.method === 'POST') {
|
|
155
|
+
try {
|
|
156
|
+
const body = await readBody(req);
|
|
157
|
+
const hookInput = body ? JSON.parse(body) : {};
|
|
158
|
+
const event = hookInput.hook_event_name ?? 'unknown';
|
|
159
|
+
// Parse transcript for rich context
|
|
160
|
+
let ctx = null;
|
|
161
|
+
if (hookInput.transcript_path) {
|
|
162
|
+
ctx = parseTranscript(hookInput.transcript_path);
|
|
163
|
+
}
|
|
164
|
+
// Build checkpoint state
|
|
165
|
+
const state = {
|
|
166
|
+
currentTask: ctx?.currentTask ?? `Auto-checkpoint (${event})`,
|
|
167
|
+
decisions: [],
|
|
168
|
+
activeFiles: ctx?.activeFiles ?? [],
|
|
169
|
+
nextSteps: [],
|
|
170
|
+
relatedMemoryIds: [],
|
|
171
|
+
notes: `Auto-saved by ${event} hook.${ctx?.recentTools.length ? ` Recent tools: ${[...new Set(ctx.recentTools)].join(', ')}` : ''}`,
|
|
172
|
+
episodeId: null,
|
|
173
|
+
};
|
|
174
|
+
store.saveCheckpoint(agentId, state);
|
|
175
|
+
log(agentId, `hook:${event}`, `auto-checkpoint files=${state.activeFiles.length} task="${state.currentTask.slice(0, 80)}"`);
|
|
176
|
+
// On SessionEnd: run full consolidation (sleep cycle) before process dies
|
|
177
|
+
let consolidated = false;
|
|
178
|
+
if (event === 'SessionEnd' && onConsolidate) {
|
|
179
|
+
try {
|
|
180
|
+
onConsolidate(agentId, `SessionEnd hook (graceful exit)`);
|
|
181
|
+
consolidated = true;
|
|
182
|
+
log(agentId, 'consolidation', 'full sleep cycle on graceful exit');
|
|
183
|
+
}
|
|
184
|
+
catch { /* consolidation failure is non-fatal */ }
|
|
185
|
+
}
|
|
186
|
+
// Return context for Claude (stdout from hooks is visible)
|
|
187
|
+
json(res, 200, {
|
|
188
|
+
status: 'checkpointed',
|
|
189
|
+
event,
|
|
190
|
+
task: state.currentTask,
|
|
191
|
+
files: state.activeFiles.length,
|
|
192
|
+
consolidated,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
json(res, 500, { error: 'Checkpoint failed', detail: String(err) });
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// 404
|
|
201
|
+
json(res, 404, { error: 'Not found' });
|
|
202
|
+
});
|
|
203
|
+
server.listen(port, '127.0.0.1', () => {
|
|
204
|
+
console.error(`AWM hook sidecar listening on 127.0.0.1:${port}`);
|
|
205
|
+
});
|
|
206
|
+
server.on('error', (err) => {
|
|
207
|
+
if (err.code === 'EADDRINUSE') {
|
|
208
|
+
console.error(`AWM hook sidecar: port ${port} in use, hooks disabled`);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
console.error('AWM hook sidecar error:', err.message);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
close: () => {
|
|
216
|
+
server.close();
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=sidecar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../../src/hooks/sidecar.ts"],"names":[],"mappings":"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"}
|