@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/db.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import Database from "better-sqlite3";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
import { join, dirname } from "node:path";
|
|
5
|
-
import { mkdirSync } from "node:fs";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
|
|
8
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
|
|
10
|
-
export function resolveDbPath(cliDbPath?: string): string {
|
|
11
|
-
if (cliDbPath) return cliDbPath;
|
|
12
|
-
if (process.env.AO_MEMORY_DB) return process.env.AO_MEMORY_DB;
|
|
13
|
-
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
14
|
-
const aoDir = join(home, ".ao");
|
|
15
|
-
mkdirSync(aoDir, { recursive: true });
|
|
16
|
-
return join(aoDir, "memory.db");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function initDb(dbPath: string): Database.Database {
|
|
20
|
-
const db = new Database(dbPath);
|
|
21
|
-
const schema = readFileSync(join(__dirname, "schema.sql"), "utf-8");
|
|
22
|
-
db.exec(schema);
|
|
23
|
-
return db;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function contentHash(
|
|
27
|
-
entryType: string,
|
|
28
|
-
agentRole: string,
|
|
29
|
-
project: string,
|
|
30
|
-
title: string,
|
|
31
|
-
body: string
|
|
32
|
-
): string {
|
|
33
|
-
return createHash("sha256")
|
|
34
|
-
.update(`${entryType}\0${agentRole}\0${project}\0${title}\0${body}`)
|
|
35
|
-
.digest("hex");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function now(): string {
|
|
39
|
-
return new Date().toISOString();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function jsonResult(data: unknown) {
|
|
43
|
-
return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function errorResult(message: string) {
|
|
47
|
-
return { content: [{ type: "text" as const, text: JSON.stringify({ error: message }) }], isError: true };
|
|
48
|
-
}
|
package/src/server.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node --experimental-strip-types
|
|
2
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import {
|
|
5
|
-
ListToolsRequestSchema,
|
|
6
|
-
CallToolRequestSchema,
|
|
7
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
-
import { resolveDbPath, initDb, errorResult } from "./db.ts";
|
|
9
|
-
import { storeTools, handleStore } from "./tools/store.ts";
|
|
10
|
-
import { recallTools, handleRecall } from "./tools/recall.ts";
|
|
11
|
-
import { statsTools, handleStats } from "./tools/stats.ts";
|
|
12
|
-
import { contextTools, handleContext } from "./tools/context.ts";
|
|
13
|
-
import { summarizeTools, handleSummarize } from "./tools/summarize.ts";
|
|
14
|
-
import { patternTools, handlePatterns } from "./tools/patterns.ts";
|
|
15
|
-
|
|
16
|
-
// Parse CLI args
|
|
17
|
-
const args = process.argv.slice(2);
|
|
18
|
-
let dbPath: string | undefined;
|
|
19
|
-
for (let i = 0; i < args.length; i++) {
|
|
20
|
-
if (args[i] === "--db" && args[i + 1]) {
|
|
21
|
-
dbPath = args[++i];
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const db = initDb(resolveDbPath(dbPath));
|
|
26
|
-
|
|
27
|
-
const allTools = [
|
|
28
|
-
...storeTools,
|
|
29
|
-
...recallTools,
|
|
30
|
-
...statsTools,
|
|
31
|
-
...contextTools,
|
|
32
|
-
...summarizeTools,
|
|
33
|
-
...patternTools,
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
const handlers = [handleStore, handleRecall, handleStats, handleContext, handleSummarize, handlePatterns];
|
|
37
|
-
|
|
38
|
-
const server = new Server(
|
|
39
|
-
{ name: "ao-memory-mcp", version: "1.0.0" },
|
|
40
|
-
{ capabilities: { tools: {} } }
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
44
|
-
tools: allTools,
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
|
-
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
48
|
-
const { name, arguments: input } = req.params;
|
|
49
|
-
|
|
50
|
-
for (const handler of handlers) {
|
|
51
|
-
const result = handler(db, name, input || {});
|
|
52
|
-
if (result) return result;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return errorResult(`Unknown tool: ${name}`);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const transport = new StdioServerTransport();
|
|
59
|
-
await server.connect(transport);
|
package/src/tools/context.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type Database from "better-sqlite3";
|
|
2
|
-
import { jsonResult } from "../db.ts";
|
|
3
|
-
|
|
4
|
-
export const contextTools = [
|
|
5
|
-
{
|
|
6
|
-
name: "memory.context",
|
|
7
|
-
description:
|
|
8
|
-
"Agent boot tool — call at the start of each run to load relevant memory. Returns recent entries, active decisions, cross-project patterns, summaries, and a summarization_needed flag.",
|
|
9
|
-
inputSchema: {
|
|
10
|
-
type: "object" as const,
|
|
11
|
-
properties: {
|
|
12
|
-
agent_role: { type: "string", description: "Agent role requesting context" },
|
|
13
|
-
project: { type: "string", description: "Project the agent is working on" },
|
|
14
|
-
limit: { type: "number", description: "Max entries per section (default 10)" },
|
|
15
|
-
},
|
|
16
|
-
required: ["agent_role", "project"],
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
export function handleContext(db: Database.Database, name: string, args: any) {
|
|
22
|
-
if (name === "memory.context") return memoryContext(db, args);
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function memoryContext(db: Database.Database, args: any) {
|
|
27
|
-
const { agent_role, project } = args;
|
|
28
|
-
const limit = args.limit || 10;
|
|
29
|
-
|
|
30
|
-
const recentEntries = db.prepare(`
|
|
31
|
-
SELECT * FROM memory_entries
|
|
32
|
-
WHERE agent_role = ? AND project = ? AND status = 'active'
|
|
33
|
-
ORDER BY occurred_at DESC LIMIT ?
|
|
34
|
-
`).all(agent_role, project, limit);
|
|
35
|
-
|
|
36
|
-
const activeDecisions = db.prepare(`
|
|
37
|
-
SELECT * FROM memory_entries
|
|
38
|
-
WHERE project = ? AND entry_type = 'decision' AND status = 'active'
|
|
39
|
-
ORDER BY occurred_at DESC LIMIT ?
|
|
40
|
-
`).all(project, limit);
|
|
41
|
-
|
|
42
|
-
const activePatterns = db.prepare(`
|
|
43
|
-
SELECT * FROM memory_patterns
|
|
44
|
-
WHERE status = 'active'
|
|
45
|
-
AND EXISTS (SELECT 1 FROM json_each(projects) WHERE json_each.value = ?)
|
|
46
|
-
ORDER BY last_seen DESC LIMIT ?
|
|
47
|
-
`).all(project, limit);
|
|
48
|
-
|
|
49
|
-
const recentSummaries = db.prepare(`
|
|
50
|
-
SELECT * FROM memory_summaries
|
|
51
|
-
WHERE agent_role = ? AND project = ?
|
|
52
|
-
ORDER BY created_at DESC LIMIT 5
|
|
53
|
-
`).all(agent_role, project);
|
|
54
|
-
|
|
55
|
-
const crossProjectAlerts = db.prepare(`
|
|
56
|
-
SELECT * FROM memory_patterns
|
|
57
|
-
WHERE status = 'active' AND occurrence_count >= 3
|
|
58
|
-
ORDER BY last_seen DESC LIMIT 5
|
|
59
|
-
`).all();
|
|
60
|
-
|
|
61
|
-
// Check if summarization is needed: 20+ active entries older than 3 days
|
|
62
|
-
const threeDaysAgo = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString();
|
|
63
|
-
const staleCount = (db.prepare(`
|
|
64
|
-
SELECT COUNT(*) as count FROM memory_entries
|
|
65
|
-
WHERE agent_role = ? AND project = ? AND status = 'active' AND occurred_at < ?
|
|
66
|
-
`).get(agent_role, project, threeDaysAgo) as any).count;
|
|
67
|
-
|
|
68
|
-
return jsonResult({
|
|
69
|
-
recent_entries: recentEntries,
|
|
70
|
-
active_decisions: activeDecisions,
|
|
71
|
-
active_patterns: activePatterns,
|
|
72
|
-
recent_summaries: recentSummaries,
|
|
73
|
-
cross_project_alerts: crossProjectAlerts,
|
|
74
|
-
summarization_needed: staleCount >= 20,
|
|
75
|
-
stale_entry_count: staleCount,
|
|
76
|
-
});
|
|
77
|
-
}
|
package/src/tools/patterns.ts
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import type Database from "better-sqlite3";
|
|
2
|
-
import { now, jsonResult, errorResult } from "../db.ts";
|
|
3
|
-
|
|
4
|
-
export const patternTools = [
|
|
5
|
-
{
|
|
6
|
-
name: "memory.patterns.detect",
|
|
7
|
-
description:
|
|
8
|
-
"Scan for recurring patterns across projects. Finds entries with similar titles or matching tags that appear in multiple projects.",
|
|
9
|
-
inputSchema: {
|
|
10
|
-
type: "object" as const,
|
|
11
|
-
properties: {
|
|
12
|
-
min_occurrences: { type: "number", description: "Minimum projects to count as a pattern (default 2)" },
|
|
13
|
-
entry_type: { type: "string", description: "Restrict to entry type" },
|
|
14
|
-
limit: { type: "number", description: "Max patterns to return (default 10)" },
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
name: "memory.patterns.record",
|
|
20
|
-
description:
|
|
21
|
-
"Create or update a confirmed cross-project pattern.",
|
|
22
|
-
inputSchema: {
|
|
23
|
-
type: "object" as const,
|
|
24
|
-
properties: {
|
|
25
|
-
id: { type: "number", description: "Existing pattern ID to update (omit to create new)" },
|
|
26
|
-
pattern_type: { type: "string", description: "Type (e.g. bug_pattern, process_pattern, architectural_pattern, anti_pattern)" },
|
|
27
|
-
title: { type: "string", description: "Pattern name" },
|
|
28
|
-
description: { type: "string", description: "Full description" },
|
|
29
|
-
projects: { type: "array", items: { type: "string" }, description: "Projects where pattern appears" },
|
|
30
|
-
agent_roles: { type: "array", items: { type: "string" }, description: "Roles that reported it" },
|
|
31
|
-
entry_ids: { type: "array", items: { type: "number" }, description: "Memory entry IDs as evidence" },
|
|
32
|
-
status: { type: "string", enum: ["active", "resolved", "archived"], description: "Pattern status" },
|
|
33
|
-
},
|
|
34
|
-
required: ["pattern_type", "title", "description"],
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: "memory.patterns.list",
|
|
39
|
-
description: "List known cross-project patterns.",
|
|
40
|
-
inputSchema: {
|
|
41
|
-
type: "object" as const,
|
|
42
|
-
properties: {
|
|
43
|
-
status: { type: "string", enum: ["active", "resolved", "archived", "all"], description: "Filter by status (default: active)" },
|
|
44
|
-
pattern_type: { type: "string", description: "Filter by pattern type" },
|
|
45
|
-
project: { type: "string", description: "Filter patterns involving this project" },
|
|
46
|
-
limit: { type: "number", description: "Max results (default 20)" },
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
export function handlePatterns(db: Database.Database, name: string, args: any) {
|
|
53
|
-
if (name === "memory.patterns.detect") return patternsDetect(db, args);
|
|
54
|
-
if (name === "memory.patterns.record") return patternsRecord(db, args);
|
|
55
|
-
if (name === "memory.patterns.list") return patternsList(db, args);
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function patternsDetect(db: Database.Database, args: any) {
|
|
60
|
-
const minOccurrences = args.min_occurrences ?? 2;
|
|
61
|
-
const limit = args.limit ?? 10;
|
|
62
|
-
|
|
63
|
-
// Find tags that appear across multiple projects
|
|
64
|
-
const tagCondition = args.entry_type ? "AND e.entry_type = ?" : "";
|
|
65
|
-
const tagVals = args.entry_type ? [args.entry_type] : [];
|
|
66
|
-
|
|
67
|
-
const tagPatterns = db.prepare(`
|
|
68
|
-
SELECT t.value as tag, COUNT(DISTINCT e.project) as project_count,
|
|
69
|
-
GROUP_CONCAT(DISTINCT e.project) as projects,
|
|
70
|
-
COUNT(*) as total_entries
|
|
71
|
-
FROM memory_entries e, json_each(e.tags) t
|
|
72
|
-
WHERE e.status = 'active' ${tagCondition}
|
|
73
|
-
GROUP BY t.value
|
|
74
|
-
HAVING COUNT(DISTINCT e.project) >= ?
|
|
75
|
-
ORDER BY project_count DESC
|
|
76
|
-
LIMIT ?
|
|
77
|
-
`).all(...tagVals, minOccurrences, limit);
|
|
78
|
-
|
|
79
|
-
// Find similar titles across projects using FTS5
|
|
80
|
-
const titlePatterns = db.prepare(`
|
|
81
|
-
SELECT e1.title, COUNT(DISTINCT e1.project) as project_count,
|
|
82
|
-
GROUP_CONCAT(DISTINCT e1.project) as projects,
|
|
83
|
-
COUNT(*) as total_entries
|
|
84
|
-
FROM memory_entries e1
|
|
85
|
-
WHERE e1.status = 'active' ${tagCondition}
|
|
86
|
-
GROUP BY e1.title
|
|
87
|
-
HAVING COUNT(DISTINCT e1.project) >= ?
|
|
88
|
-
ORDER BY project_count DESC
|
|
89
|
-
LIMIT ?
|
|
90
|
-
`).all(...tagVals, minOccurrences, limit);
|
|
91
|
-
|
|
92
|
-
return jsonResult({
|
|
93
|
-
tag_patterns: tagPatterns,
|
|
94
|
-
title_patterns: titlePatterns,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function patternsRecord(db: Database.Database, args: any) {
|
|
99
|
-
const ts = now();
|
|
100
|
-
|
|
101
|
-
if (args.id) {
|
|
102
|
-
const existing = db.prepare("SELECT * FROM memory_patterns WHERE id = ?").get(args.id) as any;
|
|
103
|
-
if (!existing) return errorResult(`Pattern ${args.id} not found`);
|
|
104
|
-
|
|
105
|
-
const sets: string[] = [];
|
|
106
|
-
const vals: any[] = [];
|
|
107
|
-
|
|
108
|
-
if (args.pattern_type) { sets.push("pattern_type = ?"); vals.push(args.pattern_type); }
|
|
109
|
-
if (args.title) { sets.push("title = ?"); vals.push(args.title); }
|
|
110
|
-
if (args.description) { sets.push("description = ?"); vals.push(args.description); }
|
|
111
|
-
if (args.projects) { sets.push("projects = ?"); vals.push(JSON.stringify(args.projects)); }
|
|
112
|
-
if (args.agent_roles) { sets.push("agent_roles = ?"); vals.push(JSON.stringify(args.agent_roles)); }
|
|
113
|
-
if (args.entry_ids) { sets.push("entry_ids = ?"); vals.push(JSON.stringify(args.entry_ids)); }
|
|
114
|
-
if (args.status) {
|
|
115
|
-
sets.push("status = ?"); vals.push(args.status);
|
|
116
|
-
if (args.status === "resolved") { sets.push("resolved_at = ?"); vals.push(ts); }
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
sets.push("last_seen = ?"); vals.push(ts);
|
|
120
|
-
sets.push("updated_at = ?"); vals.push(ts);
|
|
121
|
-
sets.push("occurrence_count = occurrence_count + 1");
|
|
122
|
-
|
|
123
|
-
vals.push(args.id);
|
|
124
|
-
db.prepare(`UPDATE memory_patterns SET ${sets.join(", ")} WHERE id = ?`).run(...vals);
|
|
125
|
-
|
|
126
|
-
return jsonResult({ id: args.id, updated: true });
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const result = db.prepare(`
|
|
130
|
-
INSERT INTO memory_patterns (pattern_type, title, description, projects, agent_roles, entry_ids, occurrence_count, status, first_seen, last_seen, created_at, updated_at)
|
|
131
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, 'active', ?, ?, ?, ?)
|
|
132
|
-
`).run(
|
|
133
|
-
args.pattern_type,
|
|
134
|
-
args.title,
|
|
135
|
-
args.description,
|
|
136
|
-
JSON.stringify(args.projects || []),
|
|
137
|
-
JSON.stringify(args.agent_roles || []),
|
|
138
|
-
JSON.stringify(args.entry_ids || []),
|
|
139
|
-
ts, ts, ts, ts
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
return jsonResult({ id: result.lastInsertRowid, created: true });
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function patternsList(db: Database.Database, args: any) {
|
|
146
|
-
const conditions: string[] = [];
|
|
147
|
-
const vals: any[] = [];
|
|
148
|
-
const status = args.status || "active";
|
|
149
|
-
|
|
150
|
-
if (status !== "all") { conditions.push("status = ?"); vals.push(status); }
|
|
151
|
-
if (args.pattern_type) { conditions.push("pattern_type = ?"); vals.push(args.pattern_type); }
|
|
152
|
-
if (args.project) {
|
|
153
|
-
conditions.push("EXISTS (SELECT 1 FROM json_each(projects) WHERE json_each.value = ?)");
|
|
154
|
-
vals.push(args.project);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
158
|
-
const limit = args.limit || 20;
|
|
159
|
-
|
|
160
|
-
const rows = db.prepare(
|
|
161
|
-
`SELECT * FROM memory_patterns ${where} ORDER BY last_seen DESC LIMIT ?`
|
|
162
|
-
).all(...vals, limit);
|
|
163
|
-
|
|
164
|
-
return jsonResult({ patterns: rows, count: rows.length });
|
|
165
|
-
}
|
package/src/tools/recall.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import type Database from "better-sqlite3";
|
|
2
|
-
import { jsonResult, errorResult } from "../db.ts";
|
|
3
|
-
|
|
4
|
-
export const recallTools = [
|
|
5
|
-
{
|
|
6
|
-
name: "memory.recall",
|
|
7
|
-
description:
|
|
8
|
-
"Query memory entries with structured filters. Returns entries sorted by date. Default: 50 most recent active entries.",
|
|
9
|
-
inputSchema: {
|
|
10
|
-
type: "object" as const,
|
|
11
|
-
properties: {
|
|
12
|
-
agent_role: { type: "string", description: "Filter by agent role" },
|
|
13
|
-
project: { type: "string", description: "Filter by project" },
|
|
14
|
-
entry_type: { type: "string", description: "Filter by entry type" },
|
|
15
|
-
task_id: { type: "string", description: "Filter by task ID" },
|
|
16
|
-
status: { type: "string", enum: ["active", "summarized", "archived"], description: "Filter by status (default: active)" },
|
|
17
|
-
date_from: { type: "string", description: "Entries from this ISO date" },
|
|
18
|
-
date_to: { type: "string", description: "Entries up to this ISO date" },
|
|
19
|
-
tags: { type: "array", items: { type: "string" }, description: "Filter by tags (must have ALL specified)" },
|
|
20
|
-
limit: { type: "number", description: "Max results (default 50)" },
|
|
21
|
-
offset: { type: "number", description: "Pagination offset (default 0)" },
|
|
22
|
-
order: { type: "string", enum: ["newest", "oldest"], description: "Sort order (default: newest)" },
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: "memory.search",
|
|
28
|
-
description:
|
|
29
|
-
"Full-text search across all memory entries. Uses FTS5 for fast matching.",
|
|
30
|
-
inputSchema: {
|
|
31
|
-
type: "object" as const,
|
|
32
|
-
properties: {
|
|
33
|
-
query: { type: "string", description: "Search query (supports FTS5 syntax)" },
|
|
34
|
-
agent_role: { type: "string", description: "Restrict to agent role" },
|
|
35
|
-
project: { type: "string", description: "Restrict to project" },
|
|
36
|
-
entry_type: { type: "string", description: "Restrict to entry type" },
|
|
37
|
-
status: { type: "string", description: "Restrict to status" },
|
|
38
|
-
limit: { type: "number", description: "Max results (default 20)" },
|
|
39
|
-
},
|
|
40
|
-
required: ["query"],
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: "memory.get",
|
|
45
|
-
description: "Get a single memory entry by ID.",
|
|
46
|
-
inputSchema: {
|
|
47
|
-
type: "object" as const,
|
|
48
|
-
properties: {
|
|
49
|
-
id: { type: "number", description: "Memory entry ID" },
|
|
50
|
-
},
|
|
51
|
-
required: ["id"],
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
export function handleRecall(db: Database.Database, name: string, args: any) {
|
|
57
|
-
if (name === "memory.recall") return memoryRecall(db, args);
|
|
58
|
-
if (name === "memory.search") return memorySearch(db, args);
|
|
59
|
-
if (name === "memory.get") return memoryGet(db, args);
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function memoryRecall(db: Database.Database, args: any) {
|
|
64
|
-
const conditions: string[] = [];
|
|
65
|
-
const vals: any[] = [];
|
|
66
|
-
const status = args.status || "active";
|
|
67
|
-
|
|
68
|
-
conditions.push("status = ?"); vals.push(status);
|
|
69
|
-
if (args.agent_role) { conditions.push("agent_role = ?"); vals.push(args.agent_role); }
|
|
70
|
-
if (args.project) { conditions.push("project = ?"); vals.push(args.project); }
|
|
71
|
-
if (args.entry_type) { conditions.push("entry_type = ?"); vals.push(args.entry_type); }
|
|
72
|
-
if (args.task_id) { conditions.push("task_id = ?"); vals.push(args.task_id); }
|
|
73
|
-
if (args.date_from) { conditions.push("occurred_at >= ?"); vals.push(args.date_from); }
|
|
74
|
-
if (args.date_to) { conditions.push("occurred_at <= ?"); vals.push(args.date_to); }
|
|
75
|
-
|
|
76
|
-
if (args.tags?.length) {
|
|
77
|
-
for (const tag of args.tags) {
|
|
78
|
-
conditions.push("EXISTS (SELECT 1 FROM json_each(tags) WHERE json_each.value = ?)");
|
|
79
|
-
vals.push(tag);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const order = args.order === "oldest" ? "ASC" : "DESC";
|
|
84
|
-
const limit = args.limit || 50;
|
|
85
|
-
const offset = args.offset || 0;
|
|
86
|
-
|
|
87
|
-
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
88
|
-
const rows = db.prepare(
|
|
89
|
-
`SELECT * FROM memory_entries ${where} ORDER BY occurred_at ${order} LIMIT ? OFFSET ?`
|
|
90
|
-
).all(...vals, limit, offset);
|
|
91
|
-
|
|
92
|
-
return jsonResult({ entries: rows, count: rows.length });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function memorySearch(db: Database.Database, args: any) {
|
|
96
|
-
const conditions: string[] = [];
|
|
97
|
-
const vals: any[] = [];
|
|
98
|
-
|
|
99
|
-
if (args.agent_role) { conditions.push("e.agent_role = ?"); vals.push(args.agent_role); }
|
|
100
|
-
if (args.project) { conditions.push("e.project = ?"); vals.push(args.project); }
|
|
101
|
-
if (args.entry_type) { conditions.push("e.entry_type = ?"); vals.push(args.entry_type); }
|
|
102
|
-
if (args.status) { conditions.push("e.status = ?"); vals.push(args.status); }
|
|
103
|
-
|
|
104
|
-
const extraWhere = conditions.length ? `AND ${conditions.join(" AND ")}` : "";
|
|
105
|
-
const limit = args.limit || 20;
|
|
106
|
-
|
|
107
|
-
const rows = db.prepare(`
|
|
108
|
-
SELECT e.*, snippet(memory_fts, 0, '<mark>', '</mark>', '...', 32) as title_snippet,
|
|
109
|
-
snippet(memory_fts, 1, '<mark>', '</mark>', '...', 64) as body_snippet
|
|
110
|
-
FROM memory_fts f
|
|
111
|
-
JOIN memory_entries e ON e.id = f.rowid
|
|
112
|
-
WHERE memory_fts MATCH ? ${extraWhere}
|
|
113
|
-
ORDER BY rank
|
|
114
|
-
LIMIT ?
|
|
115
|
-
`).all(args.query, ...vals, limit);
|
|
116
|
-
|
|
117
|
-
return jsonResult({ entries: rows, count: rows.length });
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function memoryGet(db: Database.Database, args: any) {
|
|
121
|
-
const entry = db.prepare("SELECT * FROM memory_entries WHERE id = ?").get(args.id);
|
|
122
|
-
if (!entry) return errorResult(`Entry ${args.id} not found`);
|
|
123
|
-
return jsonResult(entry);
|
|
124
|
-
}
|
package/src/tools/stats.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import type Database from "better-sqlite3";
|
|
2
|
-
import { jsonResult } from "../db.ts";
|
|
3
|
-
|
|
4
|
-
export const statsTools = [
|
|
5
|
-
{
|
|
6
|
-
name: "memory.stats",
|
|
7
|
-
description:
|
|
8
|
-
"Get aggregate statistics about memory entries. Optionally filter by project or agent role.",
|
|
9
|
-
inputSchema: {
|
|
10
|
-
type: "object" as const,
|
|
11
|
-
properties: {
|
|
12
|
-
project: { type: "string", description: "Filter by project" },
|
|
13
|
-
agent_role: { type: "string", description: "Filter by agent role" },
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
export function handleStats(db: Database.Database, name: string, args: any) {
|
|
20
|
-
if (name === "memory.stats") return memoryStats(db, args);
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function memoryStats(db: Database.Database, args: any) {
|
|
25
|
-
const conditions: string[] = [];
|
|
26
|
-
const vals: any[] = [];
|
|
27
|
-
|
|
28
|
-
if (args.project) { conditions.push("project = ?"); vals.push(args.project); }
|
|
29
|
-
if (args.agent_role) { conditions.push("agent_role = ?"); vals.push(args.agent_role); }
|
|
30
|
-
|
|
31
|
-
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
32
|
-
|
|
33
|
-
const total = (db.prepare(`SELECT COUNT(*) as count FROM memory_entries ${where}`).get(...vals) as any).count;
|
|
34
|
-
|
|
35
|
-
const byType = db.prepare(
|
|
36
|
-
`SELECT entry_type, COUNT(*) as count FROM memory_entries ${where} GROUP BY entry_type ORDER BY count DESC`
|
|
37
|
-
).all(...vals);
|
|
38
|
-
|
|
39
|
-
const byStatus = db.prepare(
|
|
40
|
-
`SELECT status, COUNT(*) as count FROM memory_entries ${where} GROUP BY status ORDER BY count DESC`
|
|
41
|
-
).all(...vals);
|
|
42
|
-
|
|
43
|
-
const byRole = db.prepare(
|
|
44
|
-
`SELECT agent_role, COUNT(*) as count FROM memory_entries ${where} GROUP BY agent_role ORDER BY count DESC`
|
|
45
|
-
).all(...vals);
|
|
46
|
-
|
|
47
|
-
const byProject = db.prepare(
|
|
48
|
-
`SELECT project, COUNT(*) as count FROM memory_entries ${where} GROUP BY project ORDER BY count DESC`
|
|
49
|
-
).all(...vals);
|
|
50
|
-
|
|
51
|
-
const dateRange = db.prepare(
|
|
52
|
-
`SELECT MIN(occurred_at) as oldest, MAX(occurred_at) as newest FROM memory_entries ${where}`
|
|
53
|
-
).get(...vals) as any;
|
|
54
|
-
|
|
55
|
-
const summaryCount = (db.prepare(
|
|
56
|
-
`SELECT COUNT(*) as count FROM memory_summaries ${where.replace("entry_type", "entry_type")}`
|
|
57
|
-
).get(...vals) as any).count;
|
|
58
|
-
|
|
59
|
-
const patternCount = (db.prepare(
|
|
60
|
-
`SELECT COUNT(*) as count FROM memory_patterns WHERE status = 'active'`
|
|
61
|
-
).get() as any).count;
|
|
62
|
-
|
|
63
|
-
return jsonResult({
|
|
64
|
-
total_entries: total,
|
|
65
|
-
total_summaries: summaryCount,
|
|
66
|
-
active_patterns: patternCount,
|
|
67
|
-
oldest_entry: dateRange?.oldest,
|
|
68
|
-
newest_entry: dateRange?.newest,
|
|
69
|
-
by_type: byType,
|
|
70
|
-
by_status: byStatus,
|
|
71
|
-
by_role: byRole,
|
|
72
|
-
by_project: byProject,
|
|
73
|
-
});
|
|
74
|
-
}
|