openclaw-mem 1.0.4 → 1.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/HOOK.md +125 -0
- package/LICENSE +1 -1
- package/MCP.json +11 -0
- package/README.md +158 -167
- package/backfill-embeddings.js +79 -0
- package/context-builder.js +703 -0
- package/database.js +625 -0
- package/debug-logger.js +280 -0
- package/extractor.js +268 -0
- package/gateway-llm.js +250 -0
- package/handler.js +941 -0
- package/mcp-http-api.js +424 -0
- package/mcp-server.js +605 -0
- package/mem-get.sh +24 -0
- package/mem-search.sh +17 -0
- package/monitor.js +112 -0
- package/package.json +58 -30
- package/realtime-monitor.js +371 -0
- package/session-watcher.js +192 -0
- package/setup.js +114 -0
- package/sync-recent.js +63 -0
- package/README_CN.md +0 -201
- package/bin/openclaw-mem.js +0 -117
- package/docs/locales/README_AR.md +0 -35
- package/docs/locales/README_DE.md +0 -35
- package/docs/locales/README_ES.md +0 -35
- package/docs/locales/README_FR.md +0 -35
- package/docs/locales/README_HE.md +0 -35
- package/docs/locales/README_HI.md +0 -35
- package/docs/locales/README_ID.md +0 -35
- package/docs/locales/README_IT.md +0 -35
- package/docs/locales/README_JA.md +0 -57
- package/docs/locales/README_KO.md +0 -35
- package/docs/locales/README_NL.md +0 -35
- package/docs/locales/README_PL.md +0 -35
- package/docs/locales/README_PT.md +0 -35
- package/docs/locales/README_RU.md +0 -35
- package/docs/locales/README_TH.md +0 -35
- package/docs/locales/README_TR.md +0 -35
- package/docs/locales/README_UK.md +0 -35
- package/docs/locales/README_VI.md +0 -35
- package/docs/logo.svg +0 -32
- package/lib/context-builder.js +0 -415
- package/lib/database.js +0 -309
- package/lib/handler.js +0 -494
- package/scripts/commands.js +0 -141
- package/scripts/init.js +0 -248
package/lib/database.js
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenClaw-Mem Database Module
|
|
3
|
-
* SQLite-based storage for observations, sessions, and summaries
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from 'node:fs';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import os from 'node:os';
|
|
9
|
-
import Database from 'better-sqlite3';
|
|
10
|
-
|
|
11
|
-
const DATA_DIR = path.join(os.homedir(), '.openclaw-mem');
|
|
12
|
-
const DB_PATH = path.join(DATA_DIR, 'memory.db');
|
|
13
|
-
|
|
14
|
-
// Ensure data directory exists
|
|
15
|
-
if (!fs.existsSync(DATA_DIR)) {
|
|
16
|
-
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Initialize database
|
|
20
|
-
const db = new Database(DB_PATH);
|
|
21
|
-
db.pragma('journal_mode = WAL');
|
|
22
|
-
|
|
23
|
-
// Create tables
|
|
24
|
-
db.exec(`
|
|
25
|
-
-- Sessions table
|
|
26
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
27
|
-
id TEXT PRIMARY KEY,
|
|
28
|
-
project_path TEXT,
|
|
29
|
-
session_key TEXT,
|
|
30
|
-
started_at TEXT DEFAULT (datetime('now')),
|
|
31
|
-
ended_at TEXT,
|
|
32
|
-
status TEXT DEFAULT 'active',
|
|
33
|
-
source TEXT
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
-- Observations table (tool calls)
|
|
37
|
-
CREATE TABLE IF NOT EXISTS observations (
|
|
38
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
39
|
-
session_id TEXT,
|
|
40
|
-
timestamp TEXT DEFAULT (datetime('now')),
|
|
41
|
-
tool_name TEXT NOT NULL,
|
|
42
|
-
tool_input TEXT,
|
|
43
|
-
tool_response TEXT,
|
|
44
|
-
summary TEXT,
|
|
45
|
-
concepts TEXT,
|
|
46
|
-
tokens_discovery INTEGER DEFAULT 0,
|
|
47
|
-
tokens_read INTEGER DEFAULT 0,
|
|
48
|
-
FOREIGN KEY (session_id) REFERENCES sessions(id)
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
-- Summaries table
|
|
52
|
-
CREATE TABLE IF NOT EXISTS summaries (
|
|
53
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
54
|
-
session_id TEXT,
|
|
55
|
-
content TEXT,
|
|
56
|
-
request TEXT,
|
|
57
|
-
completed TEXT,
|
|
58
|
-
next_steps TEXT,
|
|
59
|
-
created_at TEXT DEFAULT (datetime('now')),
|
|
60
|
-
FOREIGN KEY (session_id) REFERENCES sessions(id)
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
-- Full-text search index
|
|
64
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
|
|
65
|
-
tool_name,
|
|
66
|
-
summary,
|
|
67
|
-
concepts,
|
|
68
|
-
content='observations',
|
|
69
|
-
content_rowid='id'
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
-- Triggers for FTS sync
|
|
73
|
-
CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN
|
|
74
|
-
INSERT INTO observations_fts(rowid, tool_name, summary, concepts)
|
|
75
|
-
VALUES (new.id, new.tool_name, new.summary, new.concepts);
|
|
76
|
-
END;
|
|
77
|
-
|
|
78
|
-
CREATE TRIGGER IF NOT EXISTS observations_ad AFTER DELETE ON observations BEGIN
|
|
79
|
-
INSERT INTO observations_fts(observations_fts, rowid, tool_name, summary, concepts)
|
|
80
|
-
VALUES ('delete', old.id, old.tool_name, old.summary, old.concepts);
|
|
81
|
-
END;
|
|
82
|
-
|
|
83
|
-
CREATE TRIGGER IF NOT EXISTS observations_au AFTER UPDATE ON observations BEGIN
|
|
84
|
-
INSERT INTO observations_fts(observations_fts, rowid, tool_name, summary, concepts)
|
|
85
|
-
VALUES ('delete', old.id, old.tool_name, old.summary, old.concepts);
|
|
86
|
-
INSERT INTO observations_fts(rowid, tool_name, summary, concepts)
|
|
87
|
-
VALUES (new.id, new.tool_name, new.summary, new.concepts);
|
|
88
|
-
END;
|
|
89
|
-
|
|
90
|
-
-- Indexes
|
|
91
|
-
CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
|
|
92
|
-
CREATE INDEX IF NOT EXISTS idx_observations_timestamp ON observations(timestamp DESC);
|
|
93
|
-
CREATE INDEX IF NOT EXISTS idx_observations_tool ON observations(tool_name);
|
|
94
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_path);
|
|
95
|
-
`);
|
|
96
|
-
|
|
97
|
-
// Prepared statements
|
|
98
|
-
const stmts = {
|
|
99
|
-
// Sessions
|
|
100
|
-
createSession: db.prepare(`
|
|
101
|
-
INSERT INTO sessions (id, project_path, session_key, source)
|
|
102
|
-
VALUES (?, ?, ?, ?)
|
|
103
|
-
`),
|
|
104
|
-
|
|
105
|
-
getSession: db.prepare(`
|
|
106
|
-
SELECT * FROM sessions WHERE id = ?
|
|
107
|
-
`),
|
|
108
|
-
|
|
109
|
-
endSession: db.prepare(`
|
|
110
|
-
UPDATE sessions SET ended_at = datetime('now'), status = 'completed'
|
|
111
|
-
WHERE id = ?
|
|
112
|
-
`),
|
|
113
|
-
|
|
114
|
-
getActiveSession: db.prepare(`
|
|
115
|
-
SELECT * FROM sessions WHERE session_key = ? AND status = 'active'
|
|
116
|
-
ORDER BY started_at DESC LIMIT 1
|
|
117
|
-
`),
|
|
118
|
-
|
|
119
|
-
// Observations
|
|
120
|
-
saveObservation: db.prepare(`
|
|
121
|
-
INSERT INTO observations (session_id, tool_name, tool_input, tool_response, summary, concepts, tokens_discovery, tokens_read)
|
|
122
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
123
|
-
`),
|
|
124
|
-
|
|
125
|
-
getObservation: db.prepare(`
|
|
126
|
-
SELECT * FROM observations WHERE id = ?
|
|
127
|
-
`),
|
|
128
|
-
|
|
129
|
-
getObservations: db.prepare(`
|
|
130
|
-
SELECT * FROM observations WHERE id IN (SELECT value FROM json_each(?))
|
|
131
|
-
`),
|
|
132
|
-
|
|
133
|
-
updateObservationSummary: db.prepare(`
|
|
134
|
-
UPDATE observations SET summary = ?, concepts = ?, tokens_read = ?
|
|
135
|
-
WHERE id = ?
|
|
136
|
-
`),
|
|
137
|
-
|
|
138
|
-
getRecentObservations: db.prepare(`
|
|
139
|
-
SELECT o.*, s.project_path
|
|
140
|
-
FROM observations o
|
|
141
|
-
JOIN sessions s ON o.session_id = s.id
|
|
142
|
-
WHERE s.project_path = ?
|
|
143
|
-
ORDER BY o.timestamp DESC
|
|
144
|
-
LIMIT ?
|
|
145
|
-
`),
|
|
146
|
-
|
|
147
|
-
getRecentObservationsAll: db.prepare(`
|
|
148
|
-
SELECT o.*, s.project_path
|
|
149
|
-
FROM observations o
|
|
150
|
-
JOIN sessions s ON o.session_id = s.id
|
|
151
|
-
ORDER BY o.timestamp DESC
|
|
152
|
-
LIMIT ?
|
|
153
|
-
`),
|
|
154
|
-
|
|
155
|
-
searchObservations: db.prepare(`
|
|
156
|
-
SELECT o.*, s.project_path,
|
|
157
|
-
highlight(observations_fts, 1, '<mark>', '</mark>') as summary_highlight
|
|
158
|
-
FROM observations_fts fts
|
|
159
|
-
JOIN observations o ON fts.rowid = o.id
|
|
160
|
-
JOIN sessions s ON o.session_id = s.id
|
|
161
|
-
WHERE observations_fts MATCH ?
|
|
162
|
-
ORDER BY rank
|
|
163
|
-
LIMIT ?
|
|
164
|
-
`),
|
|
165
|
-
|
|
166
|
-
// Summaries
|
|
167
|
-
saveSummary: db.prepare(`
|
|
168
|
-
INSERT INTO summaries (session_id, content, request, completed, next_steps)
|
|
169
|
-
VALUES (?, ?, ?, ?, ?)
|
|
170
|
-
`),
|
|
171
|
-
|
|
172
|
-
getRecentSummaries: db.prepare(`
|
|
173
|
-
SELECT su.*, s.project_path
|
|
174
|
-
FROM summaries su
|
|
175
|
-
JOIN sessions s ON su.session_id = s.id
|
|
176
|
-
WHERE s.project_path = ?
|
|
177
|
-
ORDER BY su.created_at DESC
|
|
178
|
-
LIMIT ?
|
|
179
|
-
`),
|
|
180
|
-
|
|
181
|
-
// Stats
|
|
182
|
-
getStats: db.prepare(`
|
|
183
|
-
SELECT
|
|
184
|
-
(SELECT COUNT(*) FROM sessions) as total_sessions,
|
|
185
|
-
(SELECT COUNT(*) FROM observations) as total_observations,
|
|
186
|
-
(SELECT COUNT(*) FROM summaries) as total_summaries,
|
|
187
|
-
(SELECT SUM(tokens_discovery) FROM observations) as total_discovery_tokens,
|
|
188
|
-
(SELECT SUM(tokens_read) FROM observations) as total_read_tokens
|
|
189
|
-
`)
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
// Database API
|
|
193
|
-
export const database = {
|
|
194
|
-
// Session operations
|
|
195
|
-
createSession(id, projectPath, sessionKey, source = 'unknown') {
|
|
196
|
-
try {
|
|
197
|
-
stmts.createSession.run(id, projectPath, sessionKey, source);
|
|
198
|
-
return { success: true, id };
|
|
199
|
-
} catch (err) {
|
|
200
|
-
// Session might already exist
|
|
201
|
-
return { success: false, error: err.message };
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
|
|
205
|
-
getSession(id) {
|
|
206
|
-
return stmts.getSession.get(id);
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
getActiveSession(sessionKey) {
|
|
210
|
-
return stmts.getActiveSession.get(sessionKey);
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
endSession(id) {
|
|
214
|
-
stmts.endSession.run(id);
|
|
215
|
-
},
|
|
216
|
-
|
|
217
|
-
// Observation operations
|
|
218
|
-
saveObservation(sessionId, toolName, toolInput, toolResponse, options = {}) {
|
|
219
|
-
const {
|
|
220
|
-
summary = null,
|
|
221
|
-
concepts = null,
|
|
222
|
-
tokensDiscovery = 0,
|
|
223
|
-
tokensRead = 0
|
|
224
|
-
} = options;
|
|
225
|
-
|
|
226
|
-
const result = stmts.saveObservation.run(
|
|
227
|
-
sessionId,
|
|
228
|
-
toolName,
|
|
229
|
-
JSON.stringify(toolInput),
|
|
230
|
-
JSON.stringify(toolResponse),
|
|
231
|
-
summary,
|
|
232
|
-
concepts,
|
|
233
|
-
tokensDiscovery,
|
|
234
|
-
tokensRead
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
return { success: true, id: result.lastInsertRowid };
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
getObservation(id) {
|
|
241
|
-
const row = stmts.getObservation.get(id);
|
|
242
|
-
if (row) {
|
|
243
|
-
row.tool_input = JSON.parse(row.tool_input || '{}');
|
|
244
|
-
row.tool_response = JSON.parse(row.tool_response || '{}');
|
|
245
|
-
}
|
|
246
|
-
return row;
|
|
247
|
-
},
|
|
248
|
-
|
|
249
|
-
getObservations(ids) {
|
|
250
|
-
const rows = stmts.getObservations.all(JSON.stringify(ids));
|
|
251
|
-
return rows.map(row => ({
|
|
252
|
-
...row,
|
|
253
|
-
tool_input: JSON.parse(row.tool_input || '{}'),
|
|
254
|
-
tool_response: JSON.parse(row.tool_response || '{}')
|
|
255
|
-
}));
|
|
256
|
-
},
|
|
257
|
-
|
|
258
|
-
updateObservationSummary(id, summary, concepts, tokensRead) {
|
|
259
|
-
stmts.updateObservationSummary.run(summary, concepts, tokensRead, id);
|
|
260
|
-
},
|
|
261
|
-
|
|
262
|
-
getRecentObservations(projectPath, limit = 50) {
|
|
263
|
-
const rows = projectPath
|
|
264
|
-
? stmts.getRecentObservations.all(projectPath, limit)
|
|
265
|
-
: stmts.getRecentObservationsAll.all(limit);
|
|
266
|
-
|
|
267
|
-
return rows.map(row => ({
|
|
268
|
-
...row,
|
|
269
|
-
tool_input: JSON.parse(row.tool_input || '{}'),
|
|
270
|
-
tool_response: JSON.parse(row.tool_response || '{}')
|
|
271
|
-
}));
|
|
272
|
-
},
|
|
273
|
-
|
|
274
|
-
searchObservations(query, limit = 20) {
|
|
275
|
-
try {
|
|
276
|
-
const rows = stmts.searchObservations.all(query, limit);
|
|
277
|
-
return rows.map(row => ({
|
|
278
|
-
...row,
|
|
279
|
-
tool_input: JSON.parse(row.tool_input || '{}'),
|
|
280
|
-
tool_response: JSON.parse(row.tool_response || '{}')
|
|
281
|
-
}));
|
|
282
|
-
} catch (err) {
|
|
283
|
-
console.error('[openclaw-mem] Search error:', err.message);
|
|
284
|
-
return [];
|
|
285
|
-
}
|
|
286
|
-
},
|
|
287
|
-
|
|
288
|
-
// Summary operations
|
|
289
|
-
saveSummary(sessionId, content, request = null, completed = null, nextSteps = null) {
|
|
290
|
-
const result = stmts.saveSummary.run(sessionId, content, request, completed, nextSteps);
|
|
291
|
-
return { success: true, id: result.lastInsertRowid };
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
getRecentSummaries(projectPath, limit = 5) {
|
|
295
|
-
return stmts.getRecentSummaries.all(projectPath, limit);
|
|
296
|
-
},
|
|
297
|
-
|
|
298
|
-
// Stats
|
|
299
|
-
getStats() {
|
|
300
|
-
return stmts.getStats.get();
|
|
301
|
-
},
|
|
302
|
-
|
|
303
|
-
// Close database
|
|
304
|
-
close() {
|
|
305
|
-
db.close();
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
export default database;
|