@memtensor/memos-local-openclaw-plugin 0.1.2 → 0.1.4
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/.env.example +13 -5
- package/README.md +180 -68
- package/dist/capture/index.d.ts +5 -7
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +72 -43
- package/dist/capture/index.js.map +1 -1
- package/dist/ingest/providers/anthropic.d.ts +2 -0
- package/dist/ingest/providers/anthropic.d.ts.map +1 -1
- package/dist/ingest/providers/anthropic.js +110 -1
- package/dist/ingest/providers/anthropic.js.map +1 -1
- package/dist/ingest/providers/bedrock.d.ts +2 -5
- package/dist/ingest/providers/bedrock.d.ts.map +1 -1
- package/dist/ingest/providers/bedrock.js +110 -6
- package/dist/ingest/providers/bedrock.js.map +1 -1
- package/dist/ingest/providers/gemini.d.ts +2 -0
- package/dist/ingest/providers/gemini.d.ts.map +1 -1
- package/dist/ingest/providers/gemini.js +106 -1
- package/dist/ingest/providers/gemini.js.map +1 -1
- package/dist/ingest/providers/index.d.ts +9 -0
- package/dist/ingest/providers/index.d.ts.map +1 -1
- package/dist/ingest/providers/index.js +66 -4
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/ingest/providers/openai.d.ts +2 -0
- package/dist/ingest/providers/openai.d.ts.map +1 -1
- package/dist/ingest/providers/openai.js +112 -1
- package/dist/ingest/providers/openai.js.map +1 -1
- package/dist/ingest/task-processor.d.ts +63 -0
- package/dist/ingest/task-processor.d.ts.map +1 -0
- package/dist/ingest/task-processor.js +339 -0
- package/dist/ingest/task-processor.js.map +1 -0
- package/dist/ingest/worker.d.ts +1 -1
- package/dist/ingest/worker.d.ts.map +1 -1
- package/dist/ingest/worker.js +18 -13
- package/dist/ingest/worker.js.map +1 -1
- package/dist/recall/engine.d.ts +1 -0
- package/dist/recall/engine.d.ts.map +1 -1
- package/dist/recall/engine.js +21 -11
- package/dist/recall/engine.js.map +1 -1
- package/dist/recall/mmr.d.ts.map +1 -1
- package/dist/recall/mmr.js +3 -1
- package/dist/recall/mmr.js.map +1 -1
- package/dist/storage/sqlite.d.ts +67 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +251 -5
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +955 -115
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +3 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +59 -1
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +221 -45
- package/openclaw.plugin.json +20 -45
- package/package.json +3 -4
- package/skill/SKILL.md +59 -0
- package/src/capture/index.ts +85 -45
- package/src/ingest/providers/anthropic.ts +128 -1
- package/src/ingest/providers/bedrock.ts +130 -6
- package/src/ingest/providers/gemini.ts +128 -1
- package/src/ingest/providers/index.ts +74 -8
- package/src/ingest/providers/openai.ts +130 -1
- package/src/ingest/task-processor.ts +380 -0
- package/src/ingest/worker.ts +21 -15
- package/src/recall/engine.ts +22 -12
- package/src/recall/mmr.ts +3 -1
- package/src/storage/sqlite.ts +298 -5
- package/src/types.ts +19 -0
- package/src/viewer/html.ts +955 -115
- package/src/viewer/server.ts +63 -1
- package/SKILL.md +0 -43
- package/www/index.html +0 -606
package/src/viewer/server.ts
CHANGED
|
@@ -155,7 +155,10 @@ export class ViewerServer {
|
|
|
155
155
|
|
|
156
156
|
if (p === "/api/memories" && req.method === "GET") this.serveMemories(res, url);
|
|
157
157
|
else if (p === "/api/stats") this.serveStats(res);
|
|
158
|
+
else if (p === "/api/metrics") this.serveMetrics(res, url);
|
|
158
159
|
else if (p === "/api/search") this.serveSearch(req, res, url);
|
|
160
|
+
else if (p === "/api/tasks" && req.method === "GET") this.serveTasks(res, url);
|
|
161
|
+
else if (p.startsWith("/api/task/") && req.method === "GET") this.serveTaskDetail(res, p);
|
|
159
162
|
else if (p === "/api/memory" && req.method === "POST") this.handleCreate(req, res);
|
|
160
163
|
else if (p.startsWith("/api/memory/") && req.method === "PUT") this.handleUpdate(req, res, p);
|
|
161
164
|
else if (p.startsWith("/api/memory/") && req.method === "DELETE") this.handleDelete(res, p);
|
|
@@ -302,12 +305,70 @@ export class ViewerServer {
|
|
|
302
305
|
const totalRow = db.prepare("SELECT COUNT(*) as count FROM chunks" + where).get(...params) as any;
|
|
303
306
|
const memories = db.prepare("SELECT * FROM chunks" + where + ` ORDER BY created_at ${sortBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
304
307
|
|
|
308
|
+
this.store.recordViewerEvent("list");
|
|
305
309
|
this.jsonResponse(res, {
|
|
306
310
|
memories, page, limit, total: totalRow.count,
|
|
307
311
|
totalPages: Math.ceil(totalRow.count / limit),
|
|
308
312
|
});
|
|
309
313
|
}
|
|
310
314
|
|
|
315
|
+
private serveMetrics(res: http.ServerResponse, url: URL): void {
|
|
316
|
+
const days = Math.min(90, Math.max(7, Number(url.searchParams.get("days")) || 30));
|
|
317
|
+
const data = this.store.getMetrics(days);
|
|
318
|
+
this.jsonResponse(res, data);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private serveTasks(res: http.ServerResponse, url: URL): void {
|
|
322
|
+
this.store.recordViewerEvent("tasks_list");
|
|
323
|
+
const status = url.searchParams.get("status") ?? undefined;
|
|
324
|
+
const limit = Math.min(100, Math.max(1, Number(url.searchParams.get("limit")) || 50));
|
|
325
|
+
const offset = Math.max(0, Number(url.searchParams.get("offset")) || 0);
|
|
326
|
+
const { tasks, total } = this.store.listTasks({ status, limit, offset });
|
|
327
|
+
|
|
328
|
+
const items = tasks.map((t) => ({
|
|
329
|
+
id: t.id,
|
|
330
|
+
sessionKey: t.sessionKey,
|
|
331
|
+
title: t.title,
|
|
332
|
+
summary: t.summary ? (t.summary.length > 300 ? t.summary.slice(0, 297) + "..." : t.summary) : "",
|
|
333
|
+
status: t.status,
|
|
334
|
+
startedAt: t.startedAt,
|
|
335
|
+
endedAt: t.endedAt,
|
|
336
|
+
chunkCount: this.store.countChunksByTask(t.id),
|
|
337
|
+
}));
|
|
338
|
+
|
|
339
|
+
this.jsonResponse(res, { tasks: items, total, limit, offset });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private serveTaskDetail(res: http.ServerResponse, urlPath: string): void {
|
|
343
|
+
const taskId = urlPath.replace("/api/task/", "");
|
|
344
|
+
const task = this.store.getTask(taskId);
|
|
345
|
+
if (!task) {
|
|
346
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
347
|
+
res.end(JSON.stringify({ error: "Task not found" }));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const chunks = this.store.getChunksByTask(taskId);
|
|
352
|
+
const chunkItems = chunks.map((c) => ({
|
|
353
|
+
id: c.id,
|
|
354
|
+
role: c.role,
|
|
355
|
+
content: c.content.length > 500 ? c.content.slice(0, 497) + "..." : c.content,
|
|
356
|
+
summary: c.summary,
|
|
357
|
+
createdAt: c.createdAt,
|
|
358
|
+
}));
|
|
359
|
+
|
|
360
|
+
this.jsonResponse(res, {
|
|
361
|
+
id: task.id,
|
|
362
|
+
sessionKey: task.sessionKey,
|
|
363
|
+
title: task.title,
|
|
364
|
+
summary: task.summary,
|
|
365
|
+
status: task.status,
|
|
366
|
+
startedAt: task.startedAt,
|
|
367
|
+
endedAt: task.endedAt,
|
|
368
|
+
chunks: chunkItems,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
311
372
|
private serveStats(res: http.ServerResponse): void {
|
|
312
373
|
const db = (this.store as any).db;
|
|
313
374
|
const total = db.prepare("SELECT COUNT(*) as count FROM chunks").get() as any;
|
|
@@ -385,6 +446,7 @@ export class ViewerServer {
|
|
|
385
446
|
if (!seenIds.has(r.id)) { seenIds.add(r.id); merged.push(r); }
|
|
386
447
|
}
|
|
387
448
|
|
|
449
|
+
this.store.recordViewerEvent("search");
|
|
388
450
|
this.jsonResponse(res, {
|
|
389
451
|
results: merged,
|
|
390
452
|
query: q,
|
|
@@ -407,7 +469,7 @@ export class ViewerServer {
|
|
|
407
469
|
id, sessionKey: data.session_key || "manual", turnId: `manual-${now}`, seq: 0,
|
|
408
470
|
role: data.role || "user", content: data.content || "", kind: data.kind || "paragraph",
|
|
409
471
|
summary: data.summary || data.content?.slice(0, 100) || "",
|
|
410
|
-
createdAt: now, updatedAt: now, embedding: null,
|
|
472
|
+
taskId: null, createdAt: now, updatedAt: now, embedding: null,
|
|
411
473
|
});
|
|
412
474
|
this.jsonResponse(res, { ok: true, id, message: "Memory created" });
|
|
413
475
|
} catch (err) {
|
package/SKILL.md
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# Memory Recall Skill — memos-local
|
|
2
|
-
|
|
3
|
-
You have access to long-term conversation memory through three tools:
|
|
4
|
-
`memory_search`, `memory_timeline`, `memory_get`.
|
|
5
|
-
|
|
6
|
-
## When to Search
|
|
7
|
-
|
|
8
|
-
- **DO search** when the user asks about a previous conversation, a specific detail (command, error, parameter, decision) you don't have in the current context, or when the topic clearly references past interactions.
|
|
9
|
-
- **DO NOT search** when the current context already contains enough information to answer accurately.
|
|
10
|
-
|
|
11
|
-
## Progressive Recall Chain
|
|
12
|
-
|
|
13
|
-
Follow this 3-step chain. Stop as soon as you have sufficient evidence.
|
|
14
|
-
|
|
15
|
-
### Step 1 — `memory_search`
|
|
16
|
-
|
|
17
|
-
Start with the default call (no extra parameters → top 6 results, minScore 0.45).
|
|
18
|
-
|
|
19
|
-
- If results are insufficient, **refine the query first** (add entity names, exact commands, error keywords).
|
|
20
|
-
- If still insufficient, increase `maxResults` to 12, then 20.
|
|
21
|
-
- As a last resort, lower `minScore` to 0.35.
|
|
22
|
-
- **Never repeat the exact same query with the same parameters** — vary query wording or adjust parameters.
|
|
23
|
-
|
|
24
|
-
### Step 2 — `memory_timeline`
|
|
25
|
-
|
|
26
|
-
When a search hit looks relevant but the `original_excerpt` is too short to confirm, call `memory_timeline` with the hit's `ref` to get surrounding context (±2 turns by default).
|
|
27
|
-
|
|
28
|
-
### Step 3 — `memory_get`
|
|
29
|
-
|
|
30
|
-
When you need the exact original text (to verify a command, a code snippet, an error stack), call `memory_get` with the `ref` and request up to 2000 characters (max 8000).
|
|
31
|
-
|
|
32
|
-
## How to Answer
|
|
33
|
-
|
|
34
|
-
1. **Evidence-based**: Only state facts backed by `original_excerpt`, timeline entries, or `memory_get` content. Do not fabricate details.
|
|
35
|
-
2. **Cite sources**: When referencing a memory, mention the approximate time and context (e.g., "In our conversation about deploying the API…").
|
|
36
|
-
3. **Acknowledge gaps**: If memory search returns no relevant results, say so honestly rather than guessing.
|
|
37
|
-
|
|
38
|
-
## Anti-Patterns to Avoid
|
|
39
|
-
|
|
40
|
-
- Searching on every single turn (only search when needed).
|
|
41
|
-
- Repeating the same failed query without modification.
|
|
42
|
-
- Ignoring `original_excerpt` and only using `summary` — the excerpt is the primary evidence.
|
|
43
|
-
- Making claims based solely on `summary` without checking the original text when details matter.
|