@launchapp-dev/ao-memory-mcp 1.0.0 → 2.0.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/dist/db.d.ts +25 -0
- package/dist/db.js +90 -0
- package/dist/embeddings.d.ts +14 -0
- package/dist/embeddings.js +72 -0
- package/dist/schema.sql +194 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +56 -0
- package/dist/tools/context.d.ts +29 -0
- package/dist/tools/context.js +88 -0
- package/dist/tools/documents.d.ts +142 -0
- package/dist/tools/documents.js +201 -0
- package/dist/tools/episodes.d.ts +112 -0
- package/dist/tools/episodes.js +98 -0
- package/dist/tools/knowledge.d.ts +177 -0
- package/dist/tools/knowledge.js +235 -0
- package/dist/tools/recall.d.ts +153 -0
- package/dist/tools/recall.js +180 -0
- package/dist/tools/stats.d.ts +24 -0
- package/dist/tools/stats.js +50 -0
- package/dist/tools/store.d.ts +180 -0
- package/dist/tools/store.js +176 -0
- package/dist/tools/summarize.d.ts +74 -0
- package/dist/tools/summarize.js +92 -0
- package/package.json +12 -6
- package/src/schema.sql +173 -92
- package/migrate.ts +0 -250
- package/src/db.ts +0 -48
- package/src/server.ts +0 -59
- package/src/tools/context.ts +0 -77
- package/src/tools/patterns.ts +0 -165
- package/src/tools/recall.ts +0 -124
- package/src/tools/stats.ts +0 -74
- package/src/tools/store.ts +0 -160
- package/src/tools/summarize.ts +0 -140
- package/tsconfig.json +0 -12
package/src/tools/store.ts
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import type Database from "better-sqlite3";
|
|
2
|
-
import { contentHash, now, jsonResult, errorResult } from "../db.ts";
|
|
3
|
-
|
|
4
|
-
export const storeTools = [
|
|
5
|
-
{
|
|
6
|
-
name: "memory.store",
|
|
7
|
-
description:
|
|
8
|
-
"Store a new memory entry. Deduplicates via content hash — returns existing entry if duplicate detected.",
|
|
9
|
-
inputSchema: {
|
|
10
|
-
type: "object" as const,
|
|
11
|
-
properties: {
|
|
12
|
-
entry_type: { type: "string", description: "Type of memory (e.g. decision, observation, task_dispatch, test_result, review, action)" },
|
|
13
|
-
agent_role: { type: "string", description: "Agent role that produced this memory (e.g. planner, reviewer, qa-tester)" },
|
|
14
|
-
project: { type: "string", description: "Project/repo name" },
|
|
15
|
-
title: { type: "string", description: "Short summary line" },
|
|
16
|
-
body: { type: "string", description: "Full markdown content" },
|
|
17
|
-
task_id: { type: "string", description: "Task ID reference (e.g. TASK-051)" },
|
|
18
|
-
pr_number: { type: "number", description: "PR number if applicable" },
|
|
19
|
-
run_id: { type: "string", description: "Run identifier (e.g. run 51)" },
|
|
20
|
-
tags: { type: "array", items: { type: "string" }, description: "Tags for categorization" },
|
|
21
|
-
metadata: { type: "object", description: "Entry-type-specific metadata" },
|
|
22
|
-
occurred_at: { type: "string", description: "ISO 8601 date when event occurred (defaults to now)" },
|
|
23
|
-
},
|
|
24
|
-
required: ["entry_type", "agent_role", "project", "title", "body"],
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: "memory.update",
|
|
29
|
-
description: "Update an existing memory entry by ID.",
|
|
30
|
-
inputSchema: {
|
|
31
|
-
type: "object" as const,
|
|
32
|
-
properties: {
|
|
33
|
-
id: { type: "number", description: "Memory entry ID" },
|
|
34
|
-
title: { type: "string", description: "New title" },
|
|
35
|
-
body: { type: "string", description: "New body" },
|
|
36
|
-
status: { type: "string", enum: ["active", "summarized", "archived"], description: "New status" },
|
|
37
|
-
tags: { type: "array", items: { type: "string" }, description: "New tags" },
|
|
38
|
-
metadata: { type: "object", description: "Metadata to merge" },
|
|
39
|
-
},
|
|
40
|
-
required: ["id"],
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: "memory.archive",
|
|
45
|
-
description: "Bulk archive entries by filter. At least one filter required.",
|
|
46
|
-
inputSchema: {
|
|
47
|
-
type: "object" as const,
|
|
48
|
-
properties: {
|
|
49
|
-
ids: { type: "array", items: { type: "number" }, description: "Specific entry IDs to archive" },
|
|
50
|
-
agent_role: { type: "string", description: "Archive all active entries for this role" },
|
|
51
|
-
project: { type: "string", description: "Archive all active entries for this project" },
|
|
52
|
-
before: { type: "string", description: "Archive entries with occurred_at before this ISO date" },
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
export function handleStore(db: Database.Database, name: string, args: any) {
|
|
59
|
-
if (name === "memory.store") return memoryStore(db, args);
|
|
60
|
-
if (name === "memory.update") return memoryUpdate(db, args);
|
|
61
|
-
if (name === "memory.archive") return memoryArchive(db, args);
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function memoryStore(db: Database.Database, args: any) {
|
|
66
|
-
const { entry_type, agent_role, project, title, body } = args;
|
|
67
|
-
const hash = contentHash(entry_type, agent_role, project, title, body);
|
|
68
|
-
|
|
69
|
-
const existing = db.prepare("SELECT id FROM memory_entries WHERE content_hash = ?").get(hash) as any;
|
|
70
|
-
if (existing) {
|
|
71
|
-
return jsonResult({ duplicate: true, existing_id: existing.id });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const ts = now();
|
|
75
|
-
const result = db.prepare(`
|
|
76
|
-
INSERT INTO memory_entries (entry_type, agent_role, project, title, body, task_id, pr_number, run_id, status, tags, metadata, created_at, occurred_at, updated_at, content_hash)
|
|
77
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?, ?)
|
|
78
|
-
`).run(
|
|
79
|
-
entry_type,
|
|
80
|
-
agent_role,
|
|
81
|
-
project,
|
|
82
|
-
title,
|
|
83
|
-
body,
|
|
84
|
-
args.task_id || null,
|
|
85
|
-
args.pr_number || null,
|
|
86
|
-
args.run_id || null,
|
|
87
|
-
JSON.stringify(args.tags || []),
|
|
88
|
-
JSON.stringify(args.metadata || {}),
|
|
89
|
-
ts,
|
|
90
|
-
args.occurred_at || ts,
|
|
91
|
-
ts,
|
|
92
|
-
hash
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
return jsonResult({ id: result.lastInsertRowid, created: true });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function memoryUpdate(db: Database.Database, args: any) {
|
|
99
|
-
const { id, ...updates } = args;
|
|
100
|
-
|
|
101
|
-
const entry = db.prepare("SELECT * FROM memory_entries WHERE id = ?").get(id) as any;
|
|
102
|
-
if (!entry) return errorResult(`Entry ${id} not found`);
|
|
103
|
-
|
|
104
|
-
const sets: string[] = [];
|
|
105
|
-
const vals: any[] = [];
|
|
106
|
-
|
|
107
|
-
if (updates.title !== undefined) { sets.push("title = ?"); vals.push(updates.title); }
|
|
108
|
-
if (updates.body !== undefined) { sets.push("body = ?"); vals.push(updates.body); }
|
|
109
|
-
if (updates.status !== undefined) {
|
|
110
|
-
sets.push("status = ?"); vals.push(updates.status);
|
|
111
|
-
if (updates.status === "archived") { sets.push("archived_at = ?"); vals.push(now()); }
|
|
112
|
-
}
|
|
113
|
-
if (updates.tags !== undefined) { sets.push("tags = ?"); vals.push(JSON.stringify(updates.tags)); }
|
|
114
|
-
if (updates.metadata !== undefined) {
|
|
115
|
-
const merged = { ...JSON.parse(entry.metadata), ...updates.metadata };
|
|
116
|
-
sets.push("metadata = ?"); vals.push(JSON.stringify(merged));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (sets.length === 0) return errorResult("No fields to update");
|
|
120
|
-
|
|
121
|
-
sets.push("updated_at = ?"); vals.push(now());
|
|
122
|
-
|
|
123
|
-
if (updates.title !== undefined || updates.body !== undefined) {
|
|
124
|
-
const newTitle = updates.title || entry.title;
|
|
125
|
-
const newBody = updates.body || entry.body;
|
|
126
|
-
const hash = contentHash(entry.entry_type, entry.agent_role, entry.project, newTitle, newBody);
|
|
127
|
-
sets.push("content_hash = ?"); vals.push(hash);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
vals.push(id);
|
|
131
|
-
db.prepare(`UPDATE memory_entries SET ${sets.join(", ")} WHERE id = ?`).run(...vals);
|
|
132
|
-
|
|
133
|
-
return jsonResult({ id, updated: true });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function memoryArchive(db: Database.Database, args: any) {
|
|
137
|
-
const { ids, agent_role, project, before } = args;
|
|
138
|
-
|
|
139
|
-
if (!ids && !agent_role && !project && !before) {
|
|
140
|
-
return errorResult("At least one filter required");
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const conditions: string[] = ["status = 'active'"];
|
|
144
|
-
const vals: any[] = [];
|
|
145
|
-
|
|
146
|
-
if (ids?.length) {
|
|
147
|
-
conditions.push(`id IN (${ids.map(() => "?").join(",")})`);
|
|
148
|
-
vals.push(...ids);
|
|
149
|
-
}
|
|
150
|
-
if (agent_role) { conditions.push("agent_role = ?"); vals.push(agent_role); }
|
|
151
|
-
if (project) { conditions.push("project = ?"); vals.push(project); }
|
|
152
|
-
if (before) { conditions.push("occurred_at < ?"); vals.push(before); }
|
|
153
|
-
|
|
154
|
-
const ts = now();
|
|
155
|
-
const result = db.prepare(
|
|
156
|
-
`UPDATE memory_entries SET status = 'archived', archived_at = ?, updated_at = ? WHERE ${conditions.join(" AND ")}`
|
|
157
|
-
).run(ts, ts, ...vals);
|
|
158
|
-
|
|
159
|
-
return jsonResult({ archived_count: result.changes });
|
|
160
|
-
}
|
package/src/tools/summarize.ts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import type Database from "better-sqlite3";
|
|
2
|
-
import { now, jsonResult, errorResult } from "../db.ts";
|
|
3
|
-
|
|
4
|
-
export const summarizeTools = [
|
|
5
|
-
{
|
|
6
|
-
name: "memory.summarize",
|
|
7
|
-
description:
|
|
8
|
-
"Create a summary of memory entries. The calling agent provides the summary text. The server creates the summary record and transitions matching entries to 'summarized' status.",
|
|
9
|
-
inputSchema: {
|
|
10
|
-
type: "object" as const,
|
|
11
|
-
properties: {
|
|
12
|
-
agent_role: { type: "string", description: "Agent role being summarized" },
|
|
13
|
-
project: { type: "string", description: "Project being summarized" },
|
|
14
|
-
entry_type: { type: "string", description: "Entry type filter (omit for mixed)" },
|
|
15
|
-
summary_title: { type: "string", description: "Title for the summary" },
|
|
16
|
-
summary_body: { type: "string", description: "The summary text (markdown)" },
|
|
17
|
-
before: { type: "string", description: "Summarize entries before this ISO date (default: 3 days ago)" },
|
|
18
|
-
entry_ids: { type: "array", items: { type: "number" }, description: "Specific entry IDs to summarize (overrides date filter)" },
|
|
19
|
-
},
|
|
20
|
-
required: ["agent_role", "project", "summary_title", "summary_body"],
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
name: "memory.cleanup",
|
|
25
|
-
description:
|
|
26
|
-
"Identify entries needing summarization or archive old summarized entries. Use dry_run to preview without changes.",
|
|
27
|
-
inputSchema: {
|
|
28
|
-
type: "object" as const,
|
|
29
|
-
properties: {
|
|
30
|
-
older_than_days: { type: "number", description: "Entries older than N days (default 7)" },
|
|
31
|
-
min_entries: { type: "number", description: "Minimum entries per role+project to trigger (default 10)" },
|
|
32
|
-
dry_run: { type: "boolean", description: "If true, just report what would happen (default true)" },
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
export function handleSummarize(db: Database.Database, name: string, args: any) {
|
|
39
|
-
if (name === "memory.summarize") return memorySummarize(db, args);
|
|
40
|
-
if (name === "memory.cleanup") return memoryCleanup(db, args);
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function memorySummarize(db: Database.Database, args: any) {
|
|
45
|
-
const { agent_role, project, entry_type, summary_title, summary_body } = args;
|
|
46
|
-
|
|
47
|
-
const summarize = db.transaction(() => {
|
|
48
|
-
let entryIds: number[];
|
|
49
|
-
|
|
50
|
-
if (args.entry_ids?.length) {
|
|
51
|
-
entryIds = args.entry_ids;
|
|
52
|
-
} else {
|
|
53
|
-
const cutoff = args.before || new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString();
|
|
54
|
-
const conditions = ["agent_role = ?", "project = ?", "status = 'active'", "occurred_at < ?"];
|
|
55
|
-
const vals = [agent_role, project, cutoff];
|
|
56
|
-
if (entry_type) { conditions.push("entry_type = ?"); vals.push(entry_type); }
|
|
57
|
-
|
|
58
|
-
const rows = db.prepare(
|
|
59
|
-
`SELECT id FROM memory_entries WHERE ${conditions.join(" AND ")} ORDER BY occurred_at ASC`
|
|
60
|
-
).all(...vals) as any[];
|
|
61
|
-
entryIds = rows.map(r => r.id);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (entryIds.length === 0) {
|
|
65
|
-
return { error: "No entries to summarize" };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const entries = db.prepare(
|
|
69
|
-
`SELECT MIN(occurred_at) as date_from, MAX(occurred_at) as date_to FROM memory_entries WHERE id IN (${entryIds.map(() => "?").join(",")})`
|
|
70
|
-
).get(...entryIds) as any;
|
|
71
|
-
|
|
72
|
-
const ts = now();
|
|
73
|
-
const result = db.prepare(`
|
|
74
|
-
INSERT INTO memory_summaries (agent_role, project, entry_type, title, body, entry_count, date_from, date_to, entry_ids, created_at)
|
|
75
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
76
|
-
`).run(
|
|
77
|
-
agent_role, project, entry_type || null,
|
|
78
|
-
summary_title, summary_body,
|
|
79
|
-
entryIds.length, entries.date_from, entries.date_to,
|
|
80
|
-
JSON.stringify(entryIds), ts
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const placeholders = entryIds.map(() => "?").join(",");
|
|
84
|
-
db.prepare(
|
|
85
|
-
`UPDATE memory_entries SET status = 'summarized', updated_at = ? WHERE id IN (${placeholders})`
|
|
86
|
-
).run(ts, ...entryIds);
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
summary_id: result.lastInsertRowid,
|
|
90
|
-
entries_summarized: entryIds.length,
|
|
91
|
-
date_from: entries.date_from,
|
|
92
|
-
date_to: entries.date_to,
|
|
93
|
-
};
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const result = summarize();
|
|
97
|
-
if ((result as any).error) return errorResult((result as any).error);
|
|
98
|
-
return jsonResult(result);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function memoryCleanup(db: Database.Database, args: any) {
|
|
102
|
-
const olderThanDays = args.older_than_days ?? 7;
|
|
103
|
-
const minEntries = args.min_entries ?? 10;
|
|
104
|
-
const dryRun = args.dry_run ?? true;
|
|
105
|
-
|
|
106
|
-
const cutoff = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000).toISOString();
|
|
107
|
-
|
|
108
|
-
// Find scopes that need summarization
|
|
109
|
-
const needsSummarization = db.prepare(`
|
|
110
|
-
SELECT agent_role, project, COUNT(*) as entry_count,
|
|
111
|
-
MIN(occurred_at) as date_from, MAX(occurred_at) as date_to
|
|
112
|
-
FROM memory_entries
|
|
113
|
-
WHERE status = 'active' AND occurred_at < ?
|
|
114
|
-
GROUP BY agent_role, project
|
|
115
|
-
HAVING COUNT(*) >= ?
|
|
116
|
-
ORDER BY entry_count DESC
|
|
117
|
-
`).all(cutoff, minEntries);
|
|
118
|
-
|
|
119
|
-
// Find old summarized entries eligible for archival
|
|
120
|
-
const archivalCutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
121
|
-
const needsArchival = (db.prepare(`
|
|
122
|
-
SELECT COUNT(*) as count FROM memory_entries
|
|
123
|
-
WHERE status = 'summarized' AND updated_at < ?
|
|
124
|
-
`).get(archivalCutoff) as any).count;
|
|
125
|
-
|
|
126
|
-
if (!dryRun && needsArchival > 0) {
|
|
127
|
-
const ts = now();
|
|
128
|
-
db.prepare(`
|
|
129
|
-
UPDATE memory_entries SET status = 'archived', archived_at = ?, updated_at = ?
|
|
130
|
-
WHERE status = 'summarized' AND updated_at < ?
|
|
131
|
-
`).run(ts, ts, archivalCutoff);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return jsonResult({
|
|
135
|
-
needs_summarization: needsSummarization,
|
|
136
|
-
needs_archival: needsArchival,
|
|
137
|
-
archived: dryRun ? 0 : needsArchival,
|
|
138
|
-
dry_run: dryRun,
|
|
139
|
-
});
|
|
140
|
-
}
|
package/tsconfig.json
DELETED