universal-agent-memory 2.9.0 → 3.0.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/dist/bin/cli.js +27 -2
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/hooks.d.ts +7 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +127 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli/memory.d.ts +4 -1
- package/dist/cli/memory.d.ts.map +1 -1
- package/dist/cli/memory.js +169 -4
- package/dist/cli/memory.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/agent-scoped-memory.d.ts +67 -0
- package/dist/memory/agent-scoped-memory.d.ts.map +1 -0
- package/dist/memory/agent-scoped-memory.js +126 -0
- package/dist/memory/agent-scoped-memory.js.map +1 -0
- package/dist/memory/correction-propagator.d.ts +44 -0
- package/dist/memory/correction-propagator.d.ts.map +1 -0
- package/dist/memory/correction-propagator.js +156 -0
- package/dist/memory/correction-propagator.js.map +1 -0
- package/dist/memory/daily-log.d.ts +67 -0
- package/dist/memory/daily-log.d.ts.map +1 -0
- package/dist/memory/daily-log.js +143 -0
- package/dist/memory/daily-log.js.map +1 -0
- package/dist/memory/hierarchical-memory.d.ts +12 -0
- package/dist/memory/hierarchical-memory.d.ts.map +1 -1
- package/dist/memory/hierarchical-memory.js +68 -0
- package/dist/memory/hierarchical-memory.js.map +1 -1
- package/dist/memory/memory-maintenance.d.ts +38 -0
- package/dist/memory/memory-maintenance.d.ts.map +1 -0
- package/dist/memory/memory-maintenance.js +268 -0
- package/dist/memory/memory-maintenance.js.map +1 -0
- package/dist/memory/write-gate.d.ts +39 -0
- package/dist/memory/write-gate.d.ts.map +1 -0
- package/dist/memory/write-gate.js +190 -0
- package/dist/memory/write-gate.js.map +1 -0
- package/package.json +1 -1
- package/templates/SCHEMA.md +57 -0
- package/templates/hooks/pre-compact.sh +20 -0
- package/templates/hooks/session-start.sh +45 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automated Memory Maintenance
|
|
3
|
+
*
|
|
4
|
+
* Periodic maintenance tasks: verify stale claims, prune decayed entries,
|
|
5
|
+
* consolidate daily logs, and surface maintenance recommendations.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by Total Recall's maintenance cadences and Clawe's heartbeat system.
|
|
8
|
+
*/
|
|
9
|
+
import Database from 'better-sqlite3';
|
|
10
|
+
import { existsSync } from 'fs';
|
|
11
|
+
import { ensureDailyLogSchema } from './daily-log.js';
|
|
12
|
+
const DEFAULT_CONFIG = {
|
|
13
|
+
staleDaysThreshold: 14,
|
|
14
|
+
decayRate: 0.95,
|
|
15
|
+
minImportanceAfterDecay: 1,
|
|
16
|
+
archiveDaysOld: 30,
|
|
17
|
+
duplicateSimilarityThreshold: 0.92,
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Run full maintenance cycle on the memory database.
|
|
21
|
+
*/
|
|
22
|
+
export function runMaintenance(dbPath, config = {}) {
|
|
23
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
24
|
+
const result = {
|
|
25
|
+
staleEntriesPruned: 0,
|
|
26
|
+
decayedEntriesUpdated: 0,
|
|
27
|
+
dailyLogsArchived: 0,
|
|
28
|
+
duplicatesRemoved: 0,
|
|
29
|
+
recommendations: [],
|
|
30
|
+
};
|
|
31
|
+
if (!existsSync(dbPath)) {
|
|
32
|
+
result.recommendations.push('Database not found. Run `uam init` first.');
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
const db = new Database(dbPath);
|
|
36
|
+
try {
|
|
37
|
+
// 1. Apply importance decay to old memories
|
|
38
|
+
result.decayedEntriesUpdated = applyDecay(db, cfg);
|
|
39
|
+
// 2. Prune entries that decayed below minimum importance
|
|
40
|
+
result.staleEntriesPruned = pruneStale(db, cfg);
|
|
41
|
+
// 3. Archive old daily log entries
|
|
42
|
+
result.dailyLogsArchived = archiveDailyLogs(db, cfg);
|
|
43
|
+
// 4. Remove duplicates
|
|
44
|
+
result.duplicatesRemoved = removeDuplicates(db);
|
|
45
|
+
// 5. Generate recommendations
|
|
46
|
+
result.recommendations = generateRecommendations(db, cfg);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
db.close();
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
function applyDecay(db, cfg) {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
let updated = 0;
|
|
56
|
+
try {
|
|
57
|
+
const rows = db.prepare(`
|
|
58
|
+
SELECT id, importance, timestamp
|
|
59
|
+
FROM memories
|
|
60
|
+
WHERE importance > ?
|
|
61
|
+
`).all(cfg.minImportanceAfterDecay);
|
|
62
|
+
const updateStmt = db.prepare('UPDATE memories SET importance = ? WHERE id = ?');
|
|
63
|
+
for (const row of rows) {
|
|
64
|
+
const daysSince = (now - new Date(row.timestamp).getTime()) / (1000 * 60 * 60 * 24);
|
|
65
|
+
if (daysSince < 1)
|
|
66
|
+
continue; // Skip recent entries
|
|
67
|
+
const decayed = Math.round(row.importance * Math.pow(cfg.decayRate, daysSince));
|
|
68
|
+
if (decayed !== row.importance && decayed >= cfg.minImportanceAfterDecay) {
|
|
69
|
+
updateStmt.run(decayed, row.id);
|
|
70
|
+
updated++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Table might not exist
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const rows = db.prepare(`
|
|
79
|
+
SELECT id, importance, timestamp
|
|
80
|
+
FROM session_memories
|
|
81
|
+
WHERE importance > ?
|
|
82
|
+
`).all(cfg.minImportanceAfterDecay);
|
|
83
|
+
const updateStmt = db.prepare('UPDATE session_memories SET importance = ? WHERE id = ?');
|
|
84
|
+
for (const row of rows) {
|
|
85
|
+
const daysSince = (now - new Date(row.timestamp).getTime()) / (1000 * 60 * 60 * 24);
|
|
86
|
+
if (daysSince < 1)
|
|
87
|
+
continue;
|
|
88
|
+
const decayed = Math.round(row.importance * Math.pow(cfg.decayRate, daysSince));
|
|
89
|
+
if (decayed !== row.importance && decayed >= cfg.minImportanceAfterDecay) {
|
|
90
|
+
updateStmt.run(decayed, row.id);
|
|
91
|
+
updated++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Table might not exist
|
|
97
|
+
}
|
|
98
|
+
return updated;
|
|
99
|
+
}
|
|
100
|
+
function pruneStale(db, cfg) {
|
|
101
|
+
let pruned = 0;
|
|
102
|
+
const cutoffDate = new Date();
|
|
103
|
+
cutoffDate.setDate(cutoffDate.getDate() - cfg.staleDaysThreshold);
|
|
104
|
+
const cutoff = cutoffDate.toISOString();
|
|
105
|
+
try {
|
|
106
|
+
const result = db.prepare(`
|
|
107
|
+
DELETE FROM memories
|
|
108
|
+
WHERE importance <= ? AND timestamp < ?
|
|
109
|
+
`).run(cfg.minImportanceAfterDecay, cutoff);
|
|
110
|
+
pruned += result.changes;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Table might not exist
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const result = db.prepare(`
|
|
117
|
+
DELETE FROM session_memories
|
|
118
|
+
WHERE importance <= ? AND timestamp < ?
|
|
119
|
+
`).run(cfg.minImportanceAfterDecay, cutoff);
|
|
120
|
+
pruned += result.changes;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Table might not exist
|
|
124
|
+
}
|
|
125
|
+
return pruned;
|
|
126
|
+
}
|
|
127
|
+
function archiveDailyLogs(db, cfg) {
|
|
128
|
+
try {
|
|
129
|
+
ensureDailyLogSchema(db);
|
|
130
|
+
const cutoff = new Date();
|
|
131
|
+
cutoff.setDate(cutoff.getDate() - cfg.archiveDaysOld);
|
|
132
|
+
const cutoffDate = cutoff.toISOString().split('T')[0];
|
|
133
|
+
// Delete promoted entries older than threshold
|
|
134
|
+
const result = db.prepare(`
|
|
135
|
+
DELETE FROM daily_log WHERE date < ? AND promoted = 1
|
|
136
|
+
`).run(cutoffDate);
|
|
137
|
+
return result.changes;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function removeDuplicates(db) {
|
|
144
|
+
let removed = 0;
|
|
145
|
+
try {
|
|
146
|
+
// Remove exact duplicates in memories (keep lowest id)
|
|
147
|
+
const result = db.prepare(`
|
|
148
|
+
DELETE FROM memories
|
|
149
|
+
WHERE id NOT IN (
|
|
150
|
+
SELECT MIN(id) FROM memories GROUP BY content, project_id
|
|
151
|
+
)
|
|
152
|
+
`).run();
|
|
153
|
+
removed += result.changes;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Table might not exist
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
// Remove exact duplicates in session_memories
|
|
160
|
+
const result = db.prepare(`
|
|
161
|
+
DELETE FROM session_memories
|
|
162
|
+
WHERE id NOT IN (
|
|
163
|
+
SELECT MIN(id) FROM session_memories GROUP BY content, session_id
|
|
164
|
+
)
|
|
165
|
+
`).run();
|
|
166
|
+
removed += result.changes;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Table might not exist
|
|
170
|
+
}
|
|
171
|
+
return removed;
|
|
172
|
+
}
|
|
173
|
+
function generateRecommendations(db, cfg) {
|
|
174
|
+
const recs = [];
|
|
175
|
+
try {
|
|
176
|
+
// Check total memory count
|
|
177
|
+
const memCount = db.prepare('SELECT COUNT(*) as c FROM memories').get().c;
|
|
178
|
+
if (memCount > 45) {
|
|
179
|
+
recs.push(`Working memory near capacity (${memCount}/50). Consider promoting important entries and pruning old ones.`);
|
|
180
|
+
}
|
|
181
|
+
// Check for stale entries
|
|
182
|
+
const staleDate = new Date();
|
|
183
|
+
staleDate.setDate(staleDate.getDate() - cfg.staleDaysThreshold);
|
|
184
|
+
const staleCount = db.prepare(`
|
|
185
|
+
SELECT COUNT(*) as c FROM memories WHERE timestamp < ? AND importance <= 3
|
|
186
|
+
`).get(staleDate.toISOString()).c;
|
|
187
|
+
if (staleCount > 0) {
|
|
188
|
+
recs.push(`${staleCount} stale entries with low importance found. Run maintenance again or review manually.`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Table might not exist
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
// Check unpromoted daily log entries
|
|
196
|
+
ensureDailyLogSchema(db);
|
|
197
|
+
const unpromoted = db.prepare(`
|
|
198
|
+
SELECT COUNT(*) as c FROM daily_log WHERE promoted = 0
|
|
199
|
+
`).get().c;
|
|
200
|
+
if (unpromoted > 10) {
|
|
201
|
+
recs.push(`${unpromoted} unpromoted daily log entries. Run \`uam memory promote\` to review.`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Table might not exist
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
// Check for entries with very low importance
|
|
209
|
+
const lowImp = db.prepare(`
|
|
210
|
+
SELECT COUNT(*) as c FROM session_memories WHERE importance <= 2
|
|
211
|
+
`).get().c;
|
|
212
|
+
if (lowImp > 20) {
|
|
213
|
+
recs.push(`${lowImp} session memories with very low importance. These may be pruned on next maintenance.`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// Table might not exist
|
|
218
|
+
}
|
|
219
|
+
if (recs.length === 0) {
|
|
220
|
+
recs.push('Memory system is healthy. No action needed.');
|
|
221
|
+
}
|
|
222
|
+
return recs;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get a quick health summary without running full maintenance.
|
|
226
|
+
*/
|
|
227
|
+
export function getHealthSummary(dbPath) {
|
|
228
|
+
const summary = {
|
|
229
|
+
memoriesCount: 0,
|
|
230
|
+
sessionCount: 0,
|
|
231
|
+
dailyLogCount: 0,
|
|
232
|
+
staleCount: 0,
|
|
233
|
+
unpromotedCount: 0,
|
|
234
|
+
healthy: true,
|
|
235
|
+
};
|
|
236
|
+
if (!existsSync(dbPath))
|
|
237
|
+
return summary;
|
|
238
|
+
const db = new Database(dbPath, { readonly: true });
|
|
239
|
+
try {
|
|
240
|
+
try {
|
|
241
|
+
summary.memoriesCount = db.prepare('SELECT COUNT(*) as c FROM memories').get().c;
|
|
242
|
+
}
|
|
243
|
+
catch { /* table doesn't exist */ }
|
|
244
|
+
try {
|
|
245
|
+
summary.sessionCount = db.prepare('SELECT COUNT(*) as c FROM session_memories').get().c;
|
|
246
|
+
}
|
|
247
|
+
catch { /* table doesn't exist */ }
|
|
248
|
+
try {
|
|
249
|
+
summary.dailyLogCount = db.prepare('SELECT COUNT(*) as c FROM daily_log').get().c;
|
|
250
|
+
summary.unpromotedCount = db.prepare('SELECT COUNT(*) as c FROM daily_log WHERE promoted = 0').get().c;
|
|
251
|
+
}
|
|
252
|
+
catch { /* table doesn't exist */ }
|
|
253
|
+
const staleDate = new Date();
|
|
254
|
+
staleDate.setDate(staleDate.getDate() - 14);
|
|
255
|
+
try {
|
|
256
|
+
summary.staleCount = db.prepare(`
|
|
257
|
+
SELECT COUNT(*) as c FROM memories WHERE timestamp < ? AND importance <= 3
|
|
258
|
+
`).get(staleDate.toISOString()).c;
|
|
259
|
+
}
|
|
260
|
+
catch { /* table doesn't exist */ }
|
|
261
|
+
summary.healthy = summary.memoriesCount <= 45 && summary.staleCount < 10 && summary.unpromotedCount < 20;
|
|
262
|
+
}
|
|
263
|
+
finally {
|
|
264
|
+
db.close();
|
|
265
|
+
}
|
|
266
|
+
return summary;
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=memory-maintenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-maintenance.js","sourceRoot":"","sources":["../../src/memory/memory-maintenance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write Gate for UAM Memory System
|
|
3
|
+
*
|
|
4
|
+
* Evaluates candidate memories against 5 criteria before persisting.
|
|
5
|
+
* Inspired by Total Recall's write gate: "Does this change future behavior?"
|
|
6
|
+
*
|
|
7
|
+
* Gate Criteria:
|
|
8
|
+
* 1. Behavioral change - changes how the agent operates in future
|
|
9
|
+
* 2. Commitment with consequences - deadline, deliverable, follow-up
|
|
10
|
+
* 3. Decision with rationale - why X was chosen over Y
|
|
11
|
+
* 4. Stable recurring fact - not transient, will matter again
|
|
12
|
+
* 5. Explicit user request - user said "remember this"
|
|
13
|
+
*/
|
|
14
|
+
export interface WriteGateResult {
|
|
15
|
+
passed: boolean;
|
|
16
|
+
score: number;
|
|
17
|
+
criteria: GateCriteria[];
|
|
18
|
+
rejectionReason?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface GateCriteria {
|
|
21
|
+
name: string;
|
|
22
|
+
matched: boolean;
|
|
23
|
+
confidence: number;
|
|
24
|
+
evidence?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface WriteGateConfig {
|
|
27
|
+
minScore: number;
|
|
28
|
+
enableFuzzyMatching: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Evaluate whether a candidate memory should be persisted.
|
|
32
|
+
* Returns a WriteGateResult with score and matched criteria.
|
|
33
|
+
*/
|
|
34
|
+
export declare function evaluateWriteGate(content: string, config?: WriteGateConfig): WriteGateResult;
|
|
35
|
+
/**
|
|
36
|
+
* Format a human-readable summary of the write gate evaluation.
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatGateResult(result: WriteGateResult): string;
|
|
39
|
+
//# sourceMappingURL=write-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-gate.d.ts","sourceRoot":"","sources":["../../src/memory/write-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AA0DD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,eAAgC,GACvC,eAAe,CAqGjB;AAkBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAiBhE"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write Gate for UAM Memory System
|
|
3
|
+
*
|
|
4
|
+
* Evaluates candidate memories against 5 criteria before persisting.
|
|
5
|
+
* Inspired by Total Recall's write gate: "Does this change future behavior?"
|
|
6
|
+
*
|
|
7
|
+
* Gate Criteria:
|
|
8
|
+
* 1. Behavioral change - changes how the agent operates in future
|
|
9
|
+
* 2. Commitment with consequences - deadline, deliverable, follow-up
|
|
10
|
+
* 3. Decision with rationale - why X was chosen over Y
|
|
11
|
+
* 4. Stable recurring fact - not transient, will matter again
|
|
12
|
+
* 5. Explicit user request - user said "remember this"
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_CONFIG = {
|
|
15
|
+
minScore: 0.3,
|
|
16
|
+
enableFuzzyMatching: true,
|
|
17
|
+
};
|
|
18
|
+
const BEHAVIORAL_PATTERNS = [
|
|
19
|
+
/\b(prefer|always|never|don'?t|avoid|stop|instead)\b/i,
|
|
20
|
+
/\b(use|switch to|migrate to|adopt)\b.*\b(over|instead of|not)\b/i,
|
|
21
|
+
/\b(default|convention|standard|rule|policy)\b/i,
|
|
22
|
+
/\b(format|style|indent|naming|casing)\b/i,
|
|
23
|
+
/\b(timezone|locale|language|encoding)\b/i,
|
|
24
|
+
/\b(before|after|when|every time)\b.*\b(always|must|should)\b/i,
|
|
25
|
+
];
|
|
26
|
+
const COMMITMENT_PATTERNS = [
|
|
27
|
+
/\b(deadline|due|by|until|before)\b.*\b(\d{4}|\d{1,2}[\/\-]\d{1,2}|monday|tuesday|wednesday|thursday|friday|saturday|sunday|tomorrow|next week|end of)\b/i,
|
|
28
|
+
/\b(deliver|ship|release|deploy|submit|send)\b.*\b(by|before|on)\b/i,
|
|
29
|
+
/\b(follow up|check back|revisit|circle back|get back)\b/i,
|
|
30
|
+
/\b(waiting on|blocked by|depends on|need from)\b/i,
|
|
31
|
+
/\b(committed to|promised|agreed to|will do)\b/i,
|
|
32
|
+
/\b(TODO|FIXME|HACK)\b/,
|
|
33
|
+
];
|
|
34
|
+
const DECISION_PATTERNS = [
|
|
35
|
+
/\b(decided|chose|picked|selected|went with|opted for)\b/i,
|
|
36
|
+
/\b(because|reason|rationale|trade-?off|pros? and cons?)\b/i,
|
|
37
|
+
/\b(over|instead of|rather than|as opposed to)\b/i,
|
|
38
|
+
/\b(alternative|option|approach|strategy|architecture)\b/i,
|
|
39
|
+
/\b(evaluated|compared|benchmarked|tested)\b.*\b(and|vs\.?|versus)\b/i,
|
|
40
|
+
];
|
|
41
|
+
const STABLE_FACT_PATTERNS = [
|
|
42
|
+
/\b(api|endpoint|url|port|host|domain)\b.*\b(is|at|on)\b/i,
|
|
43
|
+
/\b(version|release|v\d+)\b/i,
|
|
44
|
+
/\b(password|secret|key|token|credential)\b.*\b(stored|located|in|at)\b/i,
|
|
45
|
+
/\b(schema|table|column|field|index)\b.*\b(is|has|named)\b/i,
|
|
46
|
+
/\b(environment|staging|production|dev)\b.*\b(uses?|runs?|on|at)\b/i,
|
|
47
|
+
/\b(rotates?|expires?|renews?)\b.*\b(every|monthly|weekly|daily)\b/i,
|
|
48
|
+
/\b(contact|email|phone|slack)\b.*\b(is|at)\b/i,
|
|
49
|
+
];
|
|
50
|
+
const EXPLICIT_REMEMBER_PATTERNS = [
|
|
51
|
+
/\b(remember|memorize|note|save|store|record|keep in mind)\b.*\b(this|that)\b/i,
|
|
52
|
+
/\b(important|critical|crucial|key|essential)\b.*\b(to (know|note|remember))\b/i,
|
|
53
|
+
/\b(don'?t forget|make sure to remember|for future reference)\b/i,
|
|
54
|
+
/\bremember\s*:/i,
|
|
55
|
+
];
|
|
56
|
+
const NOISE_PATTERNS = [
|
|
57
|
+
/^(thanks|thank you|ok|okay|got it|sounds good|great|perfect|nice|cool|lgtm)/i,
|
|
58
|
+
/^(yes|no|sure|right|correct|exactly|indeed|absolutely)$/i,
|
|
59
|
+
/\b(looks? good|works? for me|makes? sense)\b/i,
|
|
60
|
+
/^(can you|could you|please|would you)\b/i,
|
|
61
|
+
/\b(just ran|just tested|just checked|running now)\b/i,
|
|
62
|
+
];
|
|
63
|
+
/**
|
|
64
|
+
* Evaluate whether a candidate memory should be persisted.
|
|
65
|
+
* Returns a WriteGateResult with score and matched criteria.
|
|
66
|
+
*/
|
|
67
|
+
export function evaluateWriteGate(content, config = DEFAULT_CONFIG) {
|
|
68
|
+
if (!content || content.trim().length === 0) {
|
|
69
|
+
return {
|
|
70
|
+
passed: false,
|
|
71
|
+
score: 0,
|
|
72
|
+
criteria: [],
|
|
73
|
+
rejectionReason: 'Empty content',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const trimmed = content.trim();
|
|
77
|
+
// Short content is likely noise
|
|
78
|
+
if (trimmed.length < 10) {
|
|
79
|
+
return {
|
|
80
|
+
passed: false,
|
|
81
|
+
score: 0,
|
|
82
|
+
criteria: [],
|
|
83
|
+
rejectionReason: 'Content too short to be a meaningful memory',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Check for noise patterns first
|
|
87
|
+
for (const pattern of NOISE_PATTERNS) {
|
|
88
|
+
if (pattern.test(trimmed)) {
|
|
89
|
+
return {
|
|
90
|
+
passed: false,
|
|
91
|
+
score: 0,
|
|
92
|
+
criteria: [],
|
|
93
|
+
rejectionReason: 'Content matches noise pattern (acknowledgment, transient request)',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const criteria = [];
|
|
98
|
+
// Criterion 1: Behavioral change
|
|
99
|
+
const behavioralScore = matchPatterns(trimmed, BEHAVIORAL_PATTERNS);
|
|
100
|
+
criteria.push({
|
|
101
|
+
name: 'behavioral_change',
|
|
102
|
+
matched: behavioralScore > 0,
|
|
103
|
+
confidence: behavioralScore,
|
|
104
|
+
evidence: behavioralScore > 0 ? 'Changes how the agent should operate' : undefined,
|
|
105
|
+
});
|
|
106
|
+
// Criterion 2: Commitment with consequences
|
|
107
|
+
const commitmentScore = matchPatterns(trimmed, COMMITMENT_PATTERNS);
|
|
108
|
+
criteria.push({
|
|
109
|
+
name: 'commitment',
|
|
110
|
+
matched: commitmentScore > 0,
|
|
111
|
+
confidence: commitmentScore,
|
|
112
|
+
evidence: commitmentScore > 0 ? 'Contains deadline, deliverable, or follow-up' : undefined,
|
|
113
|
+
});
|
|
114
|
+
// Criterion 3: Decision with rationale
|
|
115
|
+
const decisionScore = matchPatterns(trimmed, DECISION_PATTERNS);
|
|
116
|
+
criteria.push({
|
|
117
|
+
name: 'decision_rationale',
|
|
118
|
+
matched: decisionScore > 0,
|
|
119
|
+
confidence: decisionScore,
|
|
120
|
+
evidence: decisionScore > 0 ? 'Records a decision and its reasoning' : undefined,
|
|
121
|
+
});
|
|
122
|
+
// Criterion 4: Stable recurring fact
|
|
123
|
+
const factScore = matchPatterns(trimmed, STABLE_FACT_PATTERNS);
|
|
124
|
+
criteria.push({
|
|
125
|
+
name: 'stable_fact',
|
|
126
|
+
matched: factScore > 0,
|
|
127
|
+
confidence: factScore,
|
|
128
|
+
evidence: factScore > 0 ? 'Durable fact that will be referenced again' : undefined,
|
|
129
|
+
});
|
|
130
|
+
// Criterion 5: Explicit user request
|
|
131
|
+
const explicitScore = matchPatterns(trimmed, EXPLICIT_REMEMBER_PATTERNS);
|
|
132
|
+
criteria.push({
|
|
133
|
+
name: 'explicit_request',
|
|
134
|
+
matched: explicitScore > 0,
|
|
135
|
+
confidence: explicitScore,
|
|
136
|
+
evidence: explicitScore > 0 ? 'User explicitly requested to remember' : undefined,
|
|
137
|
+
});
|
|
138
|
+
// Aggregate score: highest matching criterion wins, with bonus for multiple matches
|
|
139
|
+
const matchedCriteria = criteria.filter(c => c.matched);
|
|
140
|
+
const maxConfidence = Math.max(...criteria.map(c => c.confidence), 0);
|
|
141
|
+
const multiMatchBonus = matchedCriteria.length > 1 ? 0.1 * (matchedCriteria.length - 1) : 0;
|
|
142
|
+
const score = Math.min(1.0, maxConfidence + multiMatchBonus);
|
|
143
|
+
// High importance heuristic: long, structured content with technical terms
|
|
144
|
+
const lengthBonus = trimmed.length > 200 ? 0.15 : trimmed.length > 100 ? 0.05 : 0;
|
|
145
|
+
const finalScore = Math.min(1.0, score + lengthBonus);
|
|
146
|
+
const passed = finalScore >= config.minScore;
|
|
147
|
+
return {
|
|
148
|
+
passed,
|
|
149
|
+
score: finalScore,
|
|
150
|
+
criteria,
|
|
151
|
+
rejectionReason: passed
|
|
152
|
+
? undefined
|
|
153
|
+
: 'Content does not match any write gate criteria (behavioral change, commitment, decision, stable fact, or explicit request)',
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Match content against a set of regex patterns.
|
|
158
|
+
* Returns a confidence score 0-1 based on number and quality of matches.
|
|
159
|
+
*/
|
|
160
|
+
function matchPatterns(content, patterns) {
|
|
161
|
+
let matches = 0;
|
|
162
|
+
for (const pattern of patterns) {
|
|
163
|
+
if (pattern.test(content)) {
|
|
164
|
+
matches++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (matches === 0)
|
|
168
|
+
return 0;
|
|
169
|
+
// Score: 0.4 for first match, +0.15 for each additional, cap at 1.0
|
|
170
|
+
return Math.min(1.0, 0.4 + (matches - 1) * 0.15);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Format a human-readable summary of the write gate evaluation.
|
|
174
|
+
*/
|
|
175
|
+
export function formatGateResult(result) {
|
|
176
|
+
const status = result.passed ? 'PASSED' : 'REJECTED';
|
|
177
|
+
const lines = [`Write Gate: ${status} (score: ${result.score.toFixed(2)})`];
|
|
178
|
+
for (const criterion of result.criteria) {
|
|
179
|
+
const icon = criterion.matched ? '+' : '-';
|
|
180
|
+
lines.push(` ${icon} ${criterion.name}: ${criterion.matched ? `yes (${criterion.confidence.toFixed(2)})` : 'no'}`);
|
|
181
|
+
if (criterion.evidence) {
|
|
182
|
+
lines.push(` ${criterion.evidence}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (result.rejectionReason) {
|
|
186
|
+
lines.push(` Reason: ${result.rejectionReason}`);
|
|
187
|
+
}
|
|
188
|
+
return lines.join('\n');
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=write-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-gate.js","sourceRoot":"","sources":["../../src/memory/write-gate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-agent-memory",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Universal AI agent memory system - CLAUDE.md templates, memory, worktrees for Claude Code, Factory.AI, VSCode, OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# UAM Memory Schema
|
|
2
|
+
|
|
3
|
+
> Loaded every session alongside working memory. Teaches the agent how the memory system works.
|
|
4
|
+
|
|
5
|
+
## Memory Tiers
|
|
6
|
+
|
|
7
|
+
| Tier | Storage | Latency | Loaded |
|
|
8
|
+
|------|---------|---------|--------|
|
|
9
|
+
| L1 Working | SQLite `memories` | <1ms | Always (last 50) |
|
|
10
|
+
| L2 Session | SQLite `session_memories` | <5ms | Current session |
|
|
11
|
+
| L3 Semantic | Qdrant vectors | ~50ms | On-demand search |
|
|
12
|
+
| L4 Graph | SQLite `entities`/`relationships` | <20ms | On-demand |
|
|
13
|
+
| Daily Log | SQLite `daily_log` | <1ms | Today + yesterday |
|
|
14
|
+
|
|
15
|
+
## Write Rules
|
|
16
|
+
|
|
17
|
+
**Write Gate**: Before storing anything, it must pass at least one criterion:
|
|
18
|
+
1. Changes future behavior (preference, boundary, recurring pattern)
|
|
19
|
+
2. Commitment with consequences (deadline, deliverable, follow-up)
|
|
20
|
+
3. Decision with rationale (why X over Y)
|
|
21
|
+
4. Stable recurring fact (not transient, will matter again)
|
|
22
|
+
5. Explicit "remember this" request
|
|
23
|
+
|
|
24
|
+
**Default destination**: Daily log first. Promote to permanent memory later.
|
|
25
|
+
|
|
26
|
+
**Never**: silently overwrite. Mark old entries `[superseded]` with date and reason.
|
|
27
|
+
|
|
28
|
+
## Read Rules
|
|
29
|
+
|
|
30
|
+
- Working memory and this schema are always loaded
|
|
31
|
+
- Daily log checked for today and yesterday
|
|
32
|
+
- Registers/semantic memory searched on demand when topic is relevant
|
|
33
|
+
- Use `uam memory query` for anything older
|
|
34
|
+
|
|
35
|
+
## When to Write
|
|
36
|
+
|
|
37
|
+
| Trigger | Destination |
|
|
38
|
+
|---------|-------------|
|
|
39
|
+
| User says "remember" | Daily log + maybe working memory |
|
|
40
|
+
| User corrects you | Supersede old + write corrected across all tiers |
|
|
41
|
+
| Decision with rationale | Daily log, promote if durable |
|
|
42
|
+
| Preference expressed | Daily log, promote to working memory |
|
|
43
|
+
| Commitment/deadline | Daily log + working memory |
|
|
44
|
+
| Debugging details | **DISCARD** |
|
|
45
|
+
| Transient state | **DISCARD** |
|
|
46
|
+
| Acknowledgments | **DISCARD** |
|
|
47
|
+
|
|
48
|
+
## Correction Protocol
|
|
49
|
+
|
|
50
|
+
When corrected: (1) find original, (2) mark superseded with reason, (3) write corrected version to daily log + working memory + semantic memory, (4) verify next session.
|
|
51
|
+
|
|
52
|
+
## Maintenance
|
|
53
|
+
|
|
54
|
+
- Memories decay: `effective_importance = importance * (0.95 ^ days_since_access)`
|
|
55
|
+
- Consolidation triggers every 10 new entries
|
|
56
|
+
- Stale entries (>14 days unaccessed) auto-demote from hot to warm tier
|
|
57
|
+
- Run `uam memory maintain` periodically for health checks
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# UAM Pre-Compact Hook for Claude Code
|
|
3
|
+
# Writes a timestamp marker to the daily log before context compaction.
|
|
4
|
+
# Fails safely - never blocks the agent.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
8
|
+
DB_PATH="${PROJECT_DIR}/agents/data/memory/short_term.db"
|
|
9
|
+
|
|
10
|
+
if [ ! -f "$DB_PATH" ]; then
|
|
11
|
+
exit 0
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
15
|
+
|
|
16
|
+
# Record a compaction marker in memory so sessions can detect context resets
|
|
17
|
+
sqlite3 "$DB_PATH" "
|
|
18
|
+
INSERT OR IGNORE INTO memories (timestamp, type, content)
|
|
19
|
+
VALUES ('$TIMESTAMP', 'action', '[pre-compact] Context compaction at $TIMESTAMP');
|
|
20
|
+
" 2>/dev/null || true
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# UAM Session Start Hook for Claude Code
|
|
3
|
+
# Injects open loops and recent daily context at session start.
|
|
4
|
+
# Fails safely - never blocks the agent.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
8
|
+
DB_PATH="${PROJECT_DIR}/agents/data/memory/short_term.db"
|
|
9
|
+
|
|
10
|
+
if [ ! -f "$DB_PATH" ]; then
|
|
11
|
+
exit 0
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
output=""
|
|
15
|
+
|
|
16
|
+
# Recent memories (last 24h, high importance)
|
|
17
|
+
recent=$(sqlite3 "$DB_PATH" "
|
|
18
|
+
SELECT type, content FROM memories
|
|
19
|
+
WHERE timestamp >= datetime('now', '-1 day')
|
|
20
|
+
ORDER BY id DESC
|
|
21
|
+
LIMIT 10;
|
|
22
|
+
" 2>/dev/null || true)
|
|
23
|
+
|
|
24
|
+
if [ -n "$recent" ]; then
|
|
25
|
+
output+="## Recent Memory Context"$'\n'
|
|
26
|
+
output+="$recent"$'\n\n'
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Open loops from session memories
|
|
30
|
+
open_loops=$(sqlite3 "$DB_PATH" "
|
|
31
|
+
SELECT content FROM session_memories
|
|
32
|
+
WHERE type IN ('action','goal','decision')
|
|
33
|
+
AND importance >= 7
|
|
34
|
+
ORDER BY id DESC
|
|
35
|
+
LIMIT 5;
|
|
36
|
+
" 2>/dev/null || true)
|
|
37
|
+
|
|
38
|
+
if [ -n "$open_loops" ]; then
|
|
39
|
+
output+="## Open Loops"$'\n'
|
|
40
|
+
output+="$open_loops"$'\n'
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
if [ -n "$output" ]; then
|
|
44
|
+
echo "$output"
|
|
45
|
+
fi
|