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.
@@ -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.3,
15
- decision: 0.25,
16
- causalDepth: 0.25,
17
- resolutionEffort: 0.2,
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":"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"}
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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"}