memory-pulse-mcp-server 0.1.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/LICENSE +26 -0
- package/README.md +65 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2051 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2051 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import {
|
|
8
|
+
CallToolRequestSchema,
|
|
9
|
+
ListToolsRequestSchema,
|
|
10
|
+
ListResourcesRequestSchema,
|
|
11
|
+
ReadResourceRequestSchema,
|
|
12
|
+
ListPromptsRequestSchema,
|
|
13
|
+
GetPromptRequestSchema
|
|
14
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
15
|
+
|
|
16
|
+
// ../storage/dist/sqlite-storage.js
|
|
17
|
+
import Database from "better-sqlite3";
|
|
18
|
+
import { nanoid } from "nanoid";
|
|
19
|
+
|
|
20
|
+
// ../core/dist/types.js
|
|
21
|
+
var MemoryType;
|
|
22
|
+
(function(MemoryType2) {
|
|
23
|
+
MemoryType2["DECISION"] = "decision";
|
|
24
|
+
MemoryType2["SOLUTION"] = "solution";
|
|
25
|
+
MemoryType2["CONFIG"] = "config";
|
|
26
|
+
MemoryType2["CODE"] = "code";
|
|
27
|
+
MemoryType2["ERROR"] = "error";
|
|
28
|
+
MemoryType2["SESSION"] = "session";
|
|
29
|
+
})(MemoryType || (MemoryType = {}));
|
|
30
|
+
var SearchStrategy;
|
|
31
|
+
(function(SearchStrategy2) {
|
|
32
|
+
SearchStrategy2["EXACT"] = "exact";
|
|
33
|
+
SearchStrategy2["FULLTEXT"] = "fulltext";
|
|
34
|
+
SearchStrategy2["SEMANTIC"] = "semantic";
|
|
35
|
+
SearchStrategy2["AUTO"] = "auto";
|
|
36
|
+
})(SearchStrategy || (SearchStrategy = {}));
|
|
37
|
+
|
|
38
|
+
// ../storage/dist/schema.js
|
|
39
|
+
function initSchema(db) {
|
|
40
|
+
db.exec(`
|
|
41
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
42
|
+
id TEXT PRIMARY KEY,
|
|
43
|
+
projectId TEXT NOT NULL,
|
|
44
|
+
sessionId TEXT,
|
|
45
|
+
timestamp TEXT NOT NULL,
|
|
46
|
+
type TEXT NOT NULL,
|
|
47
|
+
tags TEXT, -- JSON\u6570\u7EC4: ["tag1", "tag2"]
|
|
48
|
+
version INTEGER DEFAULT 1,
|
|
49
|
+
|
|
50
|
+
-- content
|
|
51
|
+
summary TEXT NOT NULL,
|
|
52
|
+
data TEXT, -- JSON\u5BF9\u8C61
|
|
53
|
+
|
|
54
|
+
-- relations
|
|
55
|
+
replaces TEXT, -- JSON\u6570\u7EC4
|
|
56
|
+
relatedTo TEXT, -- JSON\u6570\u7EC4
|
|
57
|
+
impacts TEXT, -- JSON\u6570\u7EC4
|
|
58
|
+
derivedFrom TEXT,
|
|
59
|
+
|
|
60
|
+
-- context\uFF08\u7279\u5B9A\u7C7B\u578B\u7684\u4E0A\u4E0B\u6587\u6570\u636E\uFF09
|
|
61
|
+
context TEXT, -- JSON\u5BF9\u8C61
|
|
62
|
+
|
|
63
|
+
-- searchable
|
|
64
|
+
keywords TEXT, -- JSON\u6570\u7EC4
|
|
65
|
+
fullText TEXT,
|
|
66
|
+
|
|
67
|
+
-- \u65F6\u95F4\u6233
|
|
68
|
+
createdAt TEXT NOT NULL,
|
|
69
|
+
updatedAt TEXT NOT NULL,
|
|
70
|
+
|
|
71
|
+
-- \u7D22\u5F15\u5B57\u6BB5
|
|
72
|
+
UNIQUE(id)
|
|
73
|
+
);
|
|
74
|
+
`);
|
|
75
|
+
db.exec(`
|
|
76
|
+
-- \u9879\u76EEID\u7D22\u5F15\uFF08\u9891\u7E41\u6309\u9879\u76EE\u67E5\u8BE2\uFF09
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_memories_projectId ON memories(projectId);
|
|
78
|
+
|
|
79
|
+
-- \u4F1A\u8BDDID\u7D22\u5F15
|
|
80
|
+
CREATE INDEX IF NOT EXISTS idx_memories_sessionId ON memories(sessionId);
|
|
81
|
+
|
|
82
|
+
-- \u7C7B\u578B\u7D22\u5F15
|
|
83
|
+
CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
|
|
84
|
+
|
|
85
|
+
-- \u65F6\u95F4\u6233\u7D22\u5F15\uFF08\u7528\u4E8E\u65F6\u95F4\u7EBF\u67E5\u8BE2\uFF09
|
|
86
|
+
CREATE INDEX IF NOT EXISTS idx_memories_timestamp ON memories(timestamp DESC);
|
|
87
|
+
|
|
88
|
+
-- \u590D\u5408\u7D22\u5F15\uFF08\u9879\u76EE+\u7C7B\u578B\uFF09- \u7528\u4E8E\u6309\u9879\u76EE\u548C\u7C7B\u578B\u8FC7\u6EE4
|
|
89
|
+
CREATE INDEX IF NOT EXISTS idx_memories_project_type ON memories(projectId, type);
|
|
90
|
+
|
|
91
|
+
-- \u590D\u5408\u7D22\u5F15\uFF08\u9879\u76EE+\u65F6\u95F4\uFF09- \u7528\u4E8E\u9879\u76EE\u65F6\u95F4\u7EBF\u67E5\u8BE2\uFF0C\u6027\u80FD\u63D0\u5347\u663E\u8457
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_memories_project_timestamp ON memories(projectId, timestamp DESC);
|
|
93
|
+
|
|
94
|
+
-- \u590D\u5408\u7D22\u5F15\uFF08\u9879\u76EE+\u7C7B\u578B+\u65F6\u95F4\uFF09- \u7528\u4E8E\u6309\u9879\u76EE\u548C\u7C7B\u578B\u8FC7\u6EE4\u7684\u65F6\u95F4\u7EBF
|
|
95
|
+
CREATE INDEX IF NOT EXISTS idx_memories_project_type_timestamp ON memories(projectId, type, timestamp DESC);
|
|
96
|
+
|
|
97
|
+
-- \u90E8\u5206\u7D22\u5F15\uFF08\u4EC5\u7D22\u5F15\u6709\u4F1A\u8BDD\u7684\u8BB0\u5FC6\uFF09- \u51CF\u5C11\u7D22\u5F15\u5927\u5C0F
|
|
98
|
+
CREATE INDEX IF NOT EXISTS idx_memories_session_active ON memories(sessionId, timestamp DESC)
|
|
99
|
+
WHERE sessionId IS NOT NULL;
|
|
100
|
+
`);
|
|
101
|
+
db.exec(`
|
|
102
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
103
|
+
summary,
|
|
104
|
+
fullText,
|
|
105
|
+
tokenize = 'unicode61'
|
|
106
|
+
);
|
|
107
|
+
`);
|
|
108
|
+
db.exec(`
|
|
109
|
+
-- \u63D2\u5165\u89E6\u53D1\u5668
|
|
110
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_insert AFTER INSERT ON memories BEGIN
|
|
111
|
+
INSERT INTO memories_fts(rowid, summary, fullText)
|
|
112
|
+
VALUES (new.rowid, new.summary, new.fullText);
|
|
113
|
+
END;
|
|
114
|
+
|
|
115
|
+
-- \u66F4\u65B0\u89E6\u53D1\u5668
|
|
116
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_update AFTER UPDATE ON memories BEGIN
|
|
117
|
+
UPDATE memories_fts
|
|
118
|
+
SET summary = new.summary, fullText = new.fullText
|
|
119
|
+
WHERE rowid = new.rowid;
|
|
120
|
+
END;
|
|
121
|
+
|
|
122
|
+
-- \u5220\u9664\u89E6\u53D1\u5668
|
|
123
|
+
CREATE TRIGGER IF NOT EXISTS memories_fts_delete AFTER DELETE ON memories BEGIN
|
|
124
|
+
DELETE FROM memories_fts WHERE rowid = old.rowid;
|
|
125
|
+
END;
|
|
126
|
+
`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ../storage/dist/cache.js
|
|
130
|
+
var LRUCache = class {
|
|
131
|
+
capacity;
|
|
132
|
+
cache;
|
|
133
|
+
head;
|
|
134
|
+
tail;
|
|
135
|
+
hits;
|
|
136
|
+
misses;
|
|
137
|
+
constructor(capacity = 100) {
|
|
138
|
+
this.capacity = capacity;
|
|
139
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
140
|
+
this.head = null;
|
|
141
|
+
this.tail = null;
|
|
142
|
+
this.hits = 0;
|
|
143
|
+
this.misses = 0;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 生成缓存 key
|
|
147
|
+
*/
|
|
148
|
+
static generateKey(filters) {
|
|
149
|
+
const parts = [
|
|
150
|
+
filters.query || "",
|
|
151
|
+
filters.projectId || "",
|
|
152
|
+
filters.type || "",
|
|
153
|
+
filters.sessionId || "",
|
|
154
|
+
filters.strategy || "",
|
|
155
|
+
filters.limit || "",
|
|
156
|
+
filters.offset || "",
|
|
157
|
+
(filters.tags || []).sort().join(",")
|
|
158
|
+
];
|
|
159
|
+
return parts.join("|");
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 获取缓存
|
|
163
|
+
*/
|
|
164
|
+
get(key) {
|
|
165
|
+
const node = this.cache.get(key);
|
|
166
|
+
if (!node) {
|
|
167
|
+
this.misses++;
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
this.moveToHead(node);
|
|
171
|
+
this.hits++;
|
|
172
|
+
return {
|
|
173
|
+
...node.value,
|
|
174
|
+
metrics: {
|
|
175
|
+
dbTime: node.value.metrics?.dbTime || 0,
|
|
176
|
+
parseTime: node.value.metrics?.parseTime || 0,
|
|
177
|
+
strategyTime: node.value.metrics?.strategyTime,
|
|
178
|
+
cacheHit: true
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 设置缓存
|
|
184
|
+
*/
|
|
185
|
+
set(key, value) {
|
|
186
|
+
let node = this.cache.get(key);
|
|
187
|
+
if (node) {
|
|
188
|
+
node.value = value;
|
|
189
|
+
this.moveToHead(node);
|
|
190
|
+
} else {
|
|
191
|
+
node = {
|
|
192
|
+
key,
|
|
193
|
+
value,
|
|
194
|
+
prev: null,
|
|
195
|
+
next: null
|
|
196
|
+
};
|
|
197
|
+
this.cache.set(key, node);
|
|
198
|
+
this.addToHead(node);
|
|
199
|
+
if (this.cache.size > this.capacity) {
|
|
200
|
+
const removed = this.removeTail();
|
|
201
|
+
if (removed) {
|
|
202
|
+
this.cache.delete(removed.key);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* 清空缓存
|
|
209
|
+
*/
|
|
210
|
+
clear() {
|
|
211
|
+
this.cache.clear();
|
|
212
|
+
this.head = null;
|
|
213
|
+
this.tail = null;
|
|
214
|
+
this.hits = 0;
|
|
215
|
+
this.misses = 0;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* 失效指定项目的所有缓存
|
|
219
|
+
*/
|
|
220
|
+
invalidateProject(projectId) {
|
|
221
|
+
const keysToDelete = [];
|
|
222
|
+
for (const [key, _] of this.cache) {
|
|
223
|
+
if (key.includes(`|${projectId}|`)) {
|
|
224
|
+
keysToDelete.push(key);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
for (const key of keysToDelete) {
|
|
228
|
+
const node = this.cache.get(key);
|
|
229
|
+
if (node) {
|
|
230
|
+
this.removeNode(node);
|
|
231
|
+
this.cache.delete(key);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 获取缓存统计
|
|
237
|
+
*/
|
|
238
|
+
getStats() {
|
|
239
|
+
const total = this.hits + this.misses;
|
|
240
|
+
return {
|
|
241
|
+
size: this.cache.size,
|
|
242
|
+
capacity: this.capacity,
|
|
243
|
+
hits: this.hits,
|
|
244
|
+
misses: this.misses,
|
|
245
|
+
hitRate: total > 0 ? this.hits / total : 0
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
// ========== 双向链表操作 ==========
|
|
249
|
+
addToHead(node) {
|
|
250
|
+
node.next = this.head;
|
|
251
|
+
node.prev = null;
|
|
252
|
+
if (this.head) {
|
|
253
|
+
this.head.prev = node;
|
|
254
|
+
}
|
|
255
|
+
this.head = node;
|
|
256
|
+
if (!this.tail) {
|
|
257
|
+
this.tail = node;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
removeNode(node) {
|
|
261
|
+
if (node.prev) {
|
|
262
|
+
node.prev.next = node.next;
|
|
263
|
+
} else {
|
|
264
|
+
this.head = node.next;
|
|
265
|
+
}
|
|
266
|
+
if (node.next) {
|
|
267
|
+
node.next.prev = node.prev;
|
|
268
|
+
} else {
|
|
269
|
+
this.tail = node.prev;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
moveToHead(node) {
|
|
273
|
+
this.removeNode(node);
|
|
274
|
+
this.addToHead(node);
|
|
275
|
+
}
|
|
276
|
+
removeTail() {
|
|
277
|
+
const node = this.tail;
|
|
278
|
+
if (node) {
|
|
279
|
+
this.removeNode(node);
|
|
280
|
+
}
|
|
281
|
+
return node;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// ../storage/dist/sqlite-storage.js
|
|
286
|
+
var SQLiteStorage = class {
|
|
287
|
+
db;
|
|
288
|
+
cache;
|
|
289
|
+
constructor(dbPath = ":memory:", options) {
|
|
290
|
+
this.db = new Database(dbPath);
|
|
291
|
+
this.db.pragma("journal_mode = WAL");
|
|
292
|
+
this.db.pragma("foreign_keys = ON");
|
|
293
|
+
initSchema(this.db);
|
|
294
|
+
this.cache = options?.enableCache !== false ? new LRUCache(options?.cacheSize || 100) : null;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* 通用存储方法
|
|
298
|
+
*/
|
|
299
|
+
async store(params) {
|
|
300
|
+
const id = `mem_${nanoid()}`;
|
|
301
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
302
|
+
const memoryType = params.type || MemoryType.CODE;
|
|
303
|
+
const stmt = this.db.prepare(`
|
|
304
|
+
INSERT INTO memories (
|
|
305
|
+
id, projectId, sessionId, timestamp, type, tags,
|
|
306
|
+
summary, data, replaces, relatedTo, impacts, derivedFrom,
|
|
307
|
+
context, keywords, fullText, createdAt, updatedAt
|
|
308
|
+
) VALUES (
|
|
309
|
+
?, ?, ?, ?, ?, ?,
|
|
310
|
+
?, ?, ?, ?, ?, ?,
|
|
311
|
+
?, ?, ?, ?, ?
|
|
312
|
+
)
|
|
313
|
+
`);
|
|
314
|
+
stmt.run(
|
|
315
|
+
id,
|
|
316
|
+
params.projectId,
|
|
317
|
+
params.sessionId || null,
|
|
318
|
+
timestamp,
|
|
319
|
+
memoryType,
|
|
320
|
+
params.tags ? JSON.stringify(params.tags) : null,
|
|
321
|
+
params.content,
|
|
322
|
+
// summary
|
|
323
|
+
JSON.stringify(params.rawContext),
|
|
324
|
+
// data
|
|
325
|
+
params.relations?.replaces ? JSON.stringify(params.relations.replaces) : null,
|
|
326
|
+
params.relations?.relatedTo ? JSON.stringify(params.relations.relatedTo) : null,
|
|
327
|
+
params.relations?.impacts ? JSON.stringify(params.relations.impacts) : null,
|
|
328
|
+
params.relations?.derivedFrom || null,
|
|
329
|
+
JSON.stringify(params.rawContext),
|
|
330
|
+
// context
|
|
331
|
+
params.tags ? JSON.stringify(params.tags) : null,
|
|
332
|
+
// keywords
|
|
333
|
+
params.content,
|
|
334
|
+
// fullText
|
|
335
|
+
timestamp,
|
|
336
|
+
timestamp
|
|
337
|
+
);
|
|
338
|
+
if (this.cache) {
|
|
339
|
+
this.cache.invalidateProject(params.projectId);
|
|
340
|
+
}
|
|
341
|
+
return { id, success: true };
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* 存储决策记忆
|
|
345
|
+
*/
|
|
346
|
+
async storeDecision(params) {
|
|
347
|
+
const id = `mem_${nanoid()}`;
|
|
348
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
349
|
+
const summary = `[\u51B3\u7B56] ${params.question}`;
|
|
350
|
+
const fullText = `${params.question} ${params.options.map((o) => o.name).join(" ")} ${params.reason}`;
|
|
351
|
+
const keywords = [...params.tags || [], ...params.options.map((o) => o.name), params.chosen];
|
|
352
|
+
const stmt = this.db.prepare(`
|
|
353
|
+
INSERT INTO memories (
|
|
354
|
+
id, projectId, sessionId, timestamp, type, tags,
|
|
355
|
+
summary, data, replaces, relatedTo, impacts, derivedFrom,
|
|
356
|
+
context, keywords, fullText, createdAt, updatedAt
|
|
357
|
+
) VALUES (
|
|
358
|
+
?, ?, ?, ?, ?, ?,
|
|
359
|
+
?, ?, ?, ?, ?, ?,
|
|
360
|
+
?, ?, ?, ?, ?
|
|
361
|
+
)
|
|
362
|
+
`);
|
|
363
|
+
stmt.run(
|
|
364
|
+
id,
|
|
365
|
+
params.projectId,
|
|
366
|
+
params.sessionId || null,
|
|
367
|
+
timestamp,
|
|
368
|
+
MemoryType.DECISION,
|
|
369
|
+
params.tags ? JSON.stringify(params.tags) : null,
|
|
370
|
+
summary,
|
|
371
|
+
JSON.stringify({}),
|
|
372
|
+
// data为空
|
|
373
|
+
params.relations?.replaces ? JSON.stringify(params.relations.replaces) : null,
|
|
374
|
+
params.relations?.relatedTo ? JSON.stringify(params.relations.relatedTo) : null,
|
|
375
|
+
params.relations?.impacts ? JSON.stringify(params.relations.impacts) : null,
|
|
376
|
+
params.relations?.derivedFrom || null,
|
|
377
|
+
JSON.stringify({
|
|
378
|
+
question: params.question,
|
|
379
|
+
options: params.options,
|
|
380
|
+
chosen: params.chosen,
|
|
381
|
+
reason: params.reason
|
|
382
|
+
}),
|
|
383
|
+
JSON.stringify(keywords),
|
|
384
|
+
fullText,
|
|
385
|
+
timestamp,
|
|
386
|
+
timestamp
|
|
387
|
+
);
|
|
388
|
+
if (this.cache) {
|
|
389
|
+
this.cache.invalidateProject(params.projectId);
|
|
390
|
+
}
|
|
391
|
+
return { id, success: true };
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* 存储解决方案记忆
|
|
395
|
+
*/
|
|
396
|
+
async storeSolution(params) {
|
|
397
|
+
const id = `mem_${nanoid()}`;
|
|
398
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
399
|
+
const summary = `[\u65B9\u6848] ${params.problem}`;
|
|
400
|
+
const fullText = `${params.problem} ${params.rootCause} ${params.solution} ${params.prevention || ""} ${params.relatedIssues?.join(" ") || ""}`;
|
|
401
|
+
const keywords = [...params.tags || [], ...params.relatedIssues || []];
|
|
402
|
+
const stmt = this.db.prepare(`
|
|
403
|
+
INSERT INTO memories (
|
|
404
|
+
id, projectId, sessionId, timestamp, type, tags,
|
|
405
|
+
summary, data, replaces, relatedTo, impacts, derivedFrom,
|
|
406
|
+
context, keywords, fullText, createdAt, updatedAt
|
|
407
|
+
) VALUES (
|
|
408
|
+
?, ?, ?, ?, ?, ?,
|
|
409
|
+
?, ?, ?, ?, ?, ?,
|
|
410
|
+
?, ?, ?, ?, ?
|
|
411
|
+
)
|
|
412
|
+
`);
|
|
413
|
+
stmt.run(id, params.projectId, params.sessionId || null, timestamp, MemoryType.SOLUTION, params.tags ? JSON.stringify(params.tags) : null, summary, JSON.stringify(params.artifacts || {}), params.relations?.replaces ? JSON.stringify(params.relations.replaces) : null, params.relations?.relatedTo ? JSON.stringify(params.relations.relatedTo) : null, params.relations?.impacts ? JSON.stringify(params.relations.impacts) : null, params.relations?.derivedFrom || null, JSON.stringify({
|
|
414
|
+
problem: params.problem,
|
|
415
|
+
rootCause: params.rootCause,
|
|
416
|
+
solution: params.solution,
|
|
417
|
+
prevention: params.prevention,
|
|
418
|
+
relatedIssues: params.relatedIssues
|
|
419
|
+
}), JSON.stringify(keywords), fullText, timestamp, timestamp);
|
|
420
|
+
if (this.cache) {
|
|
421
|
+
this.cache.invalidateProject(params.projectId);
|
|
422
|
+
}
|
|
423
|
+
return { id, success: true };
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* 存储会话记忆
|
|
427
|
+
*/
|
|
428
|
+
async storeSession(params) {
|
|
429
|
+
const id = `mem_${nanoid()}`;
|
|
430
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
431
|
+
const summary = `[\u4F1A\u8BDD] ${params.summary}`;
|
|
432
|
+
const fullText = `${params.summary} ${params.decisions?.join(" ") || ""} ${params.unfinishedTasks?.join(" ") || ""} ${params.nextSteps?.join(" ") || ""}`;
|
|
433
|
+
const keywords = [...params.decisions || [], ...params.unfinishedTasks || []];
|
|
434
|
+
const stmt = this.db.prepare(`
|
|
435
|
+
INSERT INTO memories (
|
|
436
|
+
id, projectId, sessionId, timestamp, type, tags,
|
|
437
|
+
summary, data, replaces, relatedTo, impacts, derivedFrom,
|
|
438
|
+
context, keywords, fullText, createdAt, updatedAt
|
|
439
|
+
) VALUES (
|
|
440
|
+
?, ?, ?, ?, ?, ?,
|
|
441
|
+
?, ?, ?, ?, ?, ?,
|
|
442
|
+
?, ?, ?, ?, ?
|
|
443
|
+
)
|
|
444
|
+
`);
|
|
445
|
+
stmt.run(
|
|
446
|
+
id,
|
|
447
|
+
params.projectId,
|
|
448
|
+
params.sessionId || id,
|
|
449
|
+
// 使用当前id作为sessionId
|
|
450
|
+
timestamp,
|
|
451
|
+
MemoryType.SESSION,
|
|
452
|
+
null,
|
|
453
|
+
// tags
|
|
454
|
+
summary,
|
|
455
|
+
JSON.stringify({}),
|
|
456
|
+
null,
|
|
457
|
+
// replaces
|
|
458
|
+
null,
|
|
459
|
+
// relatedTo
|
|
460
|
+
null,
|
|
461
|
+
// impacts
|
|
462
|
+
null,
|
|
463
|
+
// derivedFrom
|
|
464
|
+
JSON.stringify({
|
|
465
|
+
summary: params.summary,
|
|
466
|
+
decisions: params.decisions,
|
|
467
|
+
unfinishedTasks: params.unfinishedTasks,
|
|
468
|
+
nextSteps: params.nextSteps
|
|
469
|
+
}),
|
|
470
|
+
JSON.stringify(keywords),
|
|
471
|
+
fullText,
|
|
472
|
+
timestamp,
|
|
473
|
+
timestamp
|
|
474
|
+
);
|
|
475
|
+
if (this.cache) {
|
|
476
|
+
this.cache.invalidateProject(params.projectId);
|
|
477
|
+
}
|
|
478
|
+
return { id, success: true };
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* 检索记忆
|
|
482
|
+
*/
|
|
483
|
+
async recall(filters) {
|
|
484
|
+
const startTime = Date.now();
|
|
485
|
+
if (this.cache) {
|
|
486
|
+
const cacheKey = LRUCache.generateKey(filters);
|
|
487
|
+
const cached = this.cache.get(cacheKey);
|
|
488
|
+
if (cached) {
|
|
489
|
+
return cached;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const strategy = filters.strategy || SearchStrategy.AUTO;
|
|
493
|
+
const limit = filters.limit || 10;
|
|
494
|
+
const offset = filters.offset || 0;
|
|
495
|
+
let actualStrategy;
|
|
496
|
+
let results;
|
|
497
|
+
let dbStartTime;
|
|
498
|
+
let dbEndTime;
|
|
499
|
+
let parseStartTime;
|
|
500
|
+
const strategyStartTime = Date.now();
|
|
501
|
+
if (strategy === SearchStrategy.EXACT || strategy === SearchStrategy.AUTO) {
|
|
502
|
+
dbStartTime = Date.now();
|
|
503
|
+
results = this.exactSearch(filters, limit, offset);
|
|
504
|
+
dbEndTime = Date.now();
|
|
505
|
+
actualStrategy = SearchStrategy.EXACT;
|
|
506
|
+
if (results.length === 0 && strategy === SearchStrategy.AUTO) {
|
|
507
|
+
dbStartTime = Date.now();
|
|
508
|
+
results = this.fulltextSearch(filters, limit, offset);
|
|
509
|
+
dbEndTime = Date.now();
|
|
510
|
+
actualStrategy = SearchStrategy.FULLTEXT;
|
|
511
|
+
}
|
|
512
|
+
} else if (strategy === SearchStrategy.FULLTEXT) {
|
|
513
|
+
dbStartTime = Date.now();
|
|
514
|
+
results = this.fulltextSearch(filters, limit, offset);
|
|
515
|
+
dbEndTime = Date.now();
|
|
516
|
+
actualStrategy = SearchStrategy.FULLTEXT;
|
|
517
|
+
} else {
|
|
518
|
+
dbStartTime = Date.now();
|
|
519
|
+
results = this.fulltextSearch(filters, limit, offset);
|
|
520
|
+
dbEndTime = Date.now();
|
|
521
|
+
actualStrategy = SearchStrategy.FULLTEXT;
|
|
522
|
+
}
|
|
523
|
+
const strategyTime = Date.now() - strategyStartTime;
|
|
524
|
+
const took = Date.now() - startTime;
|
|
525
|
+
const dbTime = dbEndTime - dbStartTime;
|
|
526
|
+
const parseTime = took - dbTime - strategyTime;
|
|
527
|
+
const result = {
|
|
528
|
+
memories: results,
|
|
529
|
+
total: results.length,
|
|
530
|
+
strategy: actualStrategy,
|
|
531
|
+
took,
|
|
532
|
+
metrics: {
|
|
533
|
+
dbTime,
|
|
534
|
+
parseTime: Math.max(0, parseTime),
|
|
535
|
+
// 确保非负
|
|
536
|
+
strategyTime,
|
|
537
|
+
cacheHit: false
|
|
538
|
+
// 数据库查询,标记为未命中
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
if (this.cache) {
|
|
542
|
+
const cacheKey = LRUCache.generateKey(filters);
|
|
543
|
+
this.cache.set(cacheKey, result);
|
|
544
|
+
}
|
|
545
|
+
return result;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* L1: 精确匹配检索
|
|
549
|
+
*/
|
|
550
|
+
exactSearch(filters, limit, offset) {
|
|
551
|
+
let sql = "SELECT * FROM memories WHERE 1=1";
|
|
552
|
+
const params = [];
|
|
553
|
+
if (filters.projectId) {
|
|
554
|
+
sql += " AND projectId = ?";
|
|
555
|
+
params.push(filters.projectId);
|
|
556
|
+
}
|
|
557
|
+
if (filters.type) {
|
|
558
|
+
sql += " AND type = ?";
|
|
559
|
+
params.push(filters.type);
|
|
560
|
+
}
|
|
561
|
+
if (filters.sessionId) {
|
|
562
|
+
sql += " AND sessionId = ?";
|
|
563
|
+
params.push(filters.sessionId);
|
|
564
|
+
}
|
|
565
|
+
if (filters.query) {
|
|
566
|
+
sql += " AND (summary LIKE ? OR fullText LIKE ?)";
|
|
567
|
+
params.push(`%${filters.query}%`, `%${filters.query}%`);
|
|
568
|
+
}
|
|
569
|
+
sql += " ORDER BY timestamp DESC LIMIT ? OFFSET ?";
|
|
570
|
+
params.push(limit, offset);
|
|
571
|
+
const stmt = this.db.prepare(sql);
|
|
572
|
+
const rows = stmt.all(...params);
|
|
573
|
+
return rows.map(this.rowToMemory);
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* L2: 全文搜索(FTS5)
|
|
577
|
+
*/
|
|
578
|
+
fulltextSearch(filters, limit, offset) {
|
|
579
|
+
let sql = "SELECT * FROM memories WHERE 1=1";
|
|
580
|
+
const params = [];
|
|
581
|
+
if (filters.projectId) {
|
|
582
|
+
sql += " AND projectId = ?";
|
|
583
|
+
params.push(filters.projectId);
|
|
584
|
+
}
|
|
585
|
+
if (filters.type) {
|
|
586
|
+
sql += " AND type = ?";
|
|
587
|
+
params.push(filters.type);
|
|
588
|
+
}
|
|
589
|
+
if (filters.sessionId) {
|
|
590
|
+
sql += " AND sessionId = ?";
|
|
591
|
+
params.push(filters.sessionId);
|
|
592
|
+
}
|
|
593
|
+
if (filters.query) {
|
|
594
|
+
sql += " AND (summary LIKE ? OR fullText LIKE ?)";
|
|
595
|
+
params.push(`%${filters.query}%`, `%${filters.query}%`);
|
|
596
|
+
}
|
|
597
|
+
sql += " ORDER BY timestamp DESC LIMIT ? OFFSET ?";
|
|
598
|
+
params.push(limit, offset);
|
|
599
|
+
const stmt = this.db.prepare(sql);
|
|
600
|
+
const rows = stmt.all(...params);
|
|
601
|
+
return rows.map(this.rowToMemory);
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* 获取时间线
|
|
605
|
+
*/
|
|
606
|
+
async getTimeline(options) {
|
|
607
|
+
let sql = "SELECT * FROM memories WHERE projectId = ?";
|
|
608
|
+
const params = [options.projectId];
|
|
609
|
+
if (options.type) {
|
|
610
|
+
sql += " AND type = ?";
|
|
611
|
+
params.push(options.type);
|
|
612
|
+
}
|
|
613
|
+
if (options.dateRange) {
|
|
614
|
+
sql += " AND timestamp >= ? AND timestamp <= ?";
|
|
615
|
+
params.push(options.dateRange[0], options.dateRange[1]);
|
|
616
|
+
}
|
|
617
|
+
sql += " ORDER BY timestamp DESC";
|
|
618
|
+
if (options.limit) {
|
|
619
|
+
sql += " LIMIT ?";
|
|
620
|
+
params.push(options.limit);
|
|
621
|
+
}
|
|
622
|
+
if (options.offset) {
|
|
623
|
+
sql += " OFFSET ?";
|
|
624
|
+
params.push(options.offset);
|
|
625
|
+
}
|
|
626
|
+
const stmt = this.db.prepare(sql);
|
|
627
|
+
const rows = stmt.all(...params);
|
|
628
|
+
const memories = rows.map(this.rowToMemory);
|
|
629
|
+
const entries = memories.map((memory, index) => ({
|
|
630
|
+
memory,
|
|
631
|
+
prevMemoryId: index > 0 ? memories[index - 1].meta.id : void 0,
|
|
632
|
+
nextMemoryId: index < memories.length - 1 ? memories[index + 1].meta.id : void 0
|
|
633
|
+
}));
|
|
634
|
+
return {
|
|
635
|
+
entries,
|
|
636
|
+
total: entries.length
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* 获取关系链
|
|
641
|
+
*/
|
|
642
|
+
async getRelations(options) {
|
|
643
|
+
const memory = await this.getById(options.memoryId);
|
|
644
|
+
if (!memory) {
|
|
645
|
+
throw new Error(`Memory ${options.memoryId} not found`);
|
|
646
|
+
}
|
|
647
|
+
const depth = options.depth || 1;
|
|
648
|
+
const relatedNodes = [];
|
|
649
|
+
if (depth > 0) {
|
|
650
|
+
const relatedIds = [
|
|
651
|
+
...memory.relations.replaces || [],
|
|
652
|
+
...memory.relations.relatedTo || [],
|
|
653
|
+
...memory.relations.impacts || []
|
|
654
|
+
];
|
|
655
|
+
if (memory.relations.derivedFrom) {
|
|
656
|
+
relatedIds.push(memory.relations.derivedFrom);
|
|
657
|
+
}
|
|
658
|
+
for (const relatedId of relatedIds) {
|
|
659
|
+
const relatedMemory = await this.getById(relatedId);
|
|
660
|
+
if (relatedMemory) {
|
|
661
|
+
let nestedRelated = void 0;
|
|
662
|
+
if (depth > 1) {
|
|
663
|
+
const nestedResult = await this.getRelations({
|
|
664
|
+
memoryId: relatedId,
|
|
665
|
+
depth: depth - 1
|
|
666
|
+
});
|
|
667
|
+
nestedRelated = nestedResult.related;
|
|
668
|
+
}
|
|
669
|
+
relatedNodes.push({
|
|
670
|
+
memory: relatedMemory,
|
|
671
|
+
related: nestedRelated
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return {
|
|
677
|
+
memory,
|
|
678
|
+
related: relatedNodes.length > 0 ? relatedNodes : void 0
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* 删除记忆
|
|
683
|
+
*/
|
|
684
|
+
async delete(memoryId) {
|
|
685
|
+
if (this.cache) {
|
|
686
|
+
const memory = this.db.prepare("SELECT projectId FROM memories WHERE id = ?").get(memoryId);
|
|
687
|
+
if (memory) {
|
|
688
|
+
this.cache.invalidateProject(memory.projectId);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
const stmt = this.db.prepare("DELETE FROM memories WHERE id = ?");
|
|
692
|
+
const result = stmt.run(memoryId);
|
|
693
|
+
return { success: result.changes > 0 };
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* 更新记忆
|
|
697
|
+
*/
|
|
698
|
+
async update(memoryId, updates) {
|
|
699
|
+
const fields = [];
|
|
700
|
+
const params = [];
|
|
701
|
+
if (updates.content) {
|
|
702
|
+
if (updates.content.summary) {
|
|
703
|
+
fields.push("summary = ?");
|
|
704
|
+
params.push(updates.content.summary);
|
|
705
|
+
}
|
|
706
|
+
if (updates.content.data !== void 0) {
|
|
707
|
+
fields.push("data = ?");
|
|
708
|
+
params.push(JSON.stringify(updates.content.data));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (updates.meta?.tags) {
|
|
712
|
+
fields.push("tags = ?");
|
|
713
|
+
params.push(JSON.stringify(updates.meta.tags));
|
|
714
|
+
}
|
|
715
|
+
if (updates.relations) {
|
|
716
|
+
if (updates.relations.replaces) {
|
|
717
|
+
fields.push("replaces = ?");
|
|
718
|
+
params.push(JSON.stringify(updates.relations.replaces));
|
|
719
|
+
}
|
|
720
|
+
if (updates.relations.relatedTo) {
|
|
721
|
+
fields.push("relatedTo = ?");
|
|
722
|
+
params.push(JSON.stringify(updates.relations.relatedTo));
|
|
723
|
+
}
|
|
724
|
+
if (updates.relations.impacts) {
|
|
725
|
+
fields.push("impacts = ?");
|
|
726
|
+
params.push(JSON.stringify(updates.relations.impacts));
|
|
727
|
+
}
|
|
728
|
+
if (updates.relations.derivedFrom) {
|
|
729
|
+
fields.push("derivedFrom = ?");
|
|
730
|
+
params.push(updates.relations.derivedFrom);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (fields.length === 0) {
|
|
734
|
+
return { success: false };
|
|
735
|
+
}
|
|
736
|
+
fields.push("updatedAt = ?");
|
|
737
|
+
params.push((/* @__PURE__ */ new Date()).toISOString());
|
|
738
|
+
params.push(memoryId);
|
|
739
|
+
const sql = `UPDATE memories SET ${fields.join(", ")} WHERE id = ?`;
|
|
740
|
+
const stmt = this.db.prepare(sql);
|
|
741
|
+
const result = stmt.run(...params);
|
|
742
|
+
if (this.cache && result.changes > 0) {
|
|
743
|
+
const memory = this.db.prepare("SELECT projectId FROM memories WHERE id = ?").get(memoryId);
|
|
744
|
+
if (memory) {
|
|
745
|
+
this.cache.invalidateProject(memory.projectId);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return { success: result.changes > 0 };
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* 根据 ID 获取单个记忆
|
|
752
|
+
*/
|
|
753
|
+
async getById(id) {
|
|
754
|
+
const stmt = this.db.prepare("SELECT * FROM memories WHERE id = ?");
|
|
755
|
+
const row = stmt.get(id);
|
|
756
|
+
return row ? this.rowToMemory(row) : null;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* 将数据库行转换为Memory对象
|
|
760
|
+
*/
|
|
761
|
+
rowToMemory(row) {
|
|
762
|
+
return {
|
|
763
|
+
meta: {
|
|
764
|
+
id: row.id,
|
|
765
|
+
projectId: row.projectId,
|
|
766
|
+
sessionId: row.sessionId,
|
|
767
|
+
timestamp: row.timestamp,
|
|
768
|
+
type: row.type,
|
|
769
|
+
tags: row.tags ? JSON.parse(row.tags) : [],
|
|
770
|
+
version: row.version
|
|
771
|
+
},
|
|
772
|
+
content: {
|
|
773
|
+
summary: row.summary,
|
|
774
|
+
data: row.data ? JSON.parse(row.data) : {}
|
|
775
|
+
},
|
|
776
|
+
relations: {
|
|
777
|
+
replaces: row.replaces ? JSON.parse(row.replaces) : void 0,
|
|
778
|
+
relatedTo: row.relatedTo ? JSON.parse(row.relatedTo) : void 0,
|
|
779
|
+
impacts: row.impacts ? JSON.parse(row.impacts) : void 0,
|
|
780
|
+
derivedFrom: row.derivedFrom || void 0
|
|
781
|
+
},
|
|
782
|
+
searchable: {
|
|
783
|
+
keywords: row.keywords ? JSON.parse(row.keywords) : [],
|
|
784
|
+
fullText: row.fullText
|
|
785
|
+
},
|
|
786
|
+
createdAt: row.createdAt,
|
|
787
|
+
updatedAt: row.updatedAt
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* 关闭数据库连接
|
|
792
|
+
*/
|
|
793
|
+
close() {
|
|
794
|
+
this.db.close();
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
// ../storage/dist/postgresql-storage.js
|
|
799
|
+
import { PrismaClient } from "@prisma/client";
|
|
800
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
801
|
+
var PostgreSQLStorage = class {
|
|
802
|
+
prisma;
|
|
803
|
+
cache;
|
|
804
|
+
constructor(databaseUrl, options) {
|
|
805
|
+
this.prisma = new PrismaClient({
|
|
806
|
+
datasources: databaseUrl ? {
|
|
807
|
+
db: { url: databaseUrl }
|
|
808
|
+
} : void 0
|
|
809
|
+
});
|
|
810
|
+
this.cache = options?.enableCache !== false ? new LRUCache(options?.cacheSize || 100) : null;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* 通用存储方法
|
|
814
|
+
*/
|
|
815
|
+
async store(params) {
|
|
816
|
+
const id = `mem_${nanoid2()}`;
|
|
817
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
818
|
+
const memoryType = params.type || MemoryType.CODE;
|
|
819
|
+
await this.prisma.memory.create({
|
|
820
|
+
data: {
|
|
821
|
+
id,
|
|
822
|
+
projectId: params.projectId,
|
|
823
|
+
sessionId: params.sessionId || null,
|
|
824
|
+
timestamp,
|
|
825
|
+
type: memoryType,
|
|
826
|
+
tags: params.tags || [],
|
|
827
|
+
summary: params.content,
|
|
828
|
+
data: params.rawContext,
|
|
829
|
+
replaces: params.relations?.replaces || [],
|
|
830
|
+
relatedTo: params.relations?.relatedTo || [],
|
|
831
|
+
impacts: params.relations?.impacts || [],
|
|
832
|
+
derivedFrom: params.relations?.derivedFrom || null,
|
|
833
|
+
context: params.rawContext,
|
|
834
|
+
keywords: params.tags || [],
|
|
835
|
+
fullText: params.content
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
if (this.cache) {
|
|
839
|
+
this.cache.invalidateProject(params.projectId);
|
|
840
|
+
}
|
|
841
|
+
return { id, success: true };
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* 存储决策记忆
|
|
845
|
+
*/
|
|
846
|
+
async storeDecision(params) {
|
|
847
|
+
const id = `mem_${nanoid2()}`;
|
|
848
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
849
|
+
const summary = `[\u51B3\u7B56] ${params.question}`;
|
|
850
|
+
const fullText = `${params.question} ${params.options.map((o) => o.name).join(" ")} ${params.reason}`;
|
|
851
|
+
const keywords = [...params.tags || [], ...params.options.map((o) => o.name), params.chosen];
|
|
852
|
+
await this.prisma.memory.create({
|
|
853
|
+
data: {
|
|
854
|
+
id,
|
|
855
|
+
projectId: params.projectId,
|
|
856
|
+
sessionId: params.sessionId || null,
|
|
857
|
+
timestamp,
|
|
858
|
+
type: MemoryType.DECISION,
|
|
859
|
+
tags: params.tags || [],
|
|
860
|
+
summary,
|
|
861
|
+
data: {},
|
|
862
|
+
replaces: params.relations?.replaces || [],
|
|
863
|
+
relatedTo: params.relations?.relatedTo || [],
|
|
864
|
+
impacts: params.relations?.impacts || [],
|
|
865
|
+
derivedFrom: params.relations?.derivedFrom || null,
|
|
866
|
+
context: {
|
|
867
|
+
question: params.question,
|
|
868
|
+
analysis: params.analysis,
|
|
869
|
+
options: params.options,
|
|
870
|
+
chosen: params.chosen,
|
|
871
|
+
reason: params.reason
|
|
872
|
+
},
|
|
873
|
+
keywords,
|
|
874
|
+
fullText
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
if (this.cache) {
|
|
878
|
+
this.cache.invalidateProject(params.projectId);
|
|
879
|
+
}
|
|
880
|
+
return { id, success: true };
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* 存储解决方案记忆
|
|
884
|
+
*/
|
|
885
|
+
async storeSolution(params) {
|
|
886
|
+
const id = `mem_${nanoid2()}`;
|
|
887
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
888
|
+
const summary = `[\u65B9\u6848] ${params.problem}`;
|
|
889
|
+
const fullText = `${params.problem} ${params.rootCause} ${params.solution} ${params.prevention || ""} ${params.relatedIssues?.join(" ") || ""}`;
|
|
890
|
+
const keywords = [...params.tags || [], ...params.relatedIssues || []];
|
|
891
|
+
await this.prisma.memory.create({
|
|
892
|
+
data: {
|
|
893
|
+
id,
|
|
894
|
+
projectId: params.projectId,
|
|
895
|
+
sessionId: params.sessionId || null,
|
|
896
|
+
timestamp,
|
|
897
|
+
type: MemoryType.SOLUTION,
|
|
898
|
+
tags: params.tags || [],
|
|
899
|
+
summary,
|
|
900
|
+
data: params.artifacts || {},
|
|
901
|
+
replaces: params.relations?.replaces || [],
|
|
902
|
+
relatedTo: params.relations?.relatedTo || [],
|
|
903
|
+
impacts: params.relations?.impacts || [],
|
|
904
|
+
derivedFrom: params.relations?.derivedFrom || null,
|
|
905
|
+
context: {
|
|
906
|
+
problem: params.problem,
|
|
907
|
+
rootCause: params.rootCause,
|
|
908
|
+
solution: params.solution,
|
|
909
|
+
prevention: params.prevention,
|
|
910
|
+
relatedIssues: params.relatedIssues
|
|
911
|
+
},
|
|
912
|
+
keywords,
|
|
913
|
+
fullText
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
if (this.cache) {
|
|
917
|
+
this.cache.invalidateProject(params.projectId);
|
|
918
|
+
}
|
|
919
|
+
return { id, success: true };
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* 存储会话记忆
|
|
923
|
+
*/
|
|
924
|
+
async storeSession(params) {
|
|
925
|
+
const id = `mem_${nanoid2()}`;
|
|
926
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
927
|
+
const summary = `[\u4F1A\u8BDD] ${params.summary}`;
|
|
928
|
+
const fullText = `${params.summary} ${params.decisions?.join(" ") || ""} ${params.unfinishedTasks?.join(" ") || ""} ${params.nextSteps?.join(" ") || ""}`;
|
|
929
|
+
const keywords = [...params.decisions || [], ...params.unfinishedTasks || []];
|
|
930
|
+
await this.prisma.memory.create({
|
|
931
|
+
data: {
|
|
932
|
+
id,
|
|
933
|
+
projectId: params.projectId,
|
|
934
|
+
sessionId: params.sessionId || id,
|
|
935
|
+
timestamp,
|
|
936
|
+
type: MemoryType.SESSION,
|
|
937
|
+
tags: [],
|
|
938
|
+
summary,
|
|
939
|
+
data: {},
|
|
940
|
+
replaces: [],
|
|
941
|
+
relatedTo: [],
|
|
942
|
+
impacts: [],
|
|
943
|
+
derivedFrom: null,
|
|
944
|
+
context: {
|
|
945
|
+
summary: params.summary,
|
|
946
|
+
decisions: params.decisions,
|
|
947
|
+
unfinishedTasks: params.unfinishedTasks,
|
|
948
|
+
nextSteps: params.nextSteps
|
|
949
|
+
},
|
|
950
|
+
keywords,
|
|
951
|
+
fullText
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
if (this.cache) {
|
|
955
|
+
this.cache.invalidateProject(params.projectId);
|
|
956
|
+
}
|
|
957
|
+
return { id, success: true };
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* 检索记忆
|
|
961
|
+
*/
|
|
962
|
+
async recall(filters) {
|
|
963
|
+
const startTime = Date.now();
|
|
964
|
+
if (this.cache) {
|
|
965
|
+
const cacheKey = LRUCache.generateKey(filters);
|
|
966
|
+
const cached = this.cache.get(cacheKey);
|
|
967
|
+
if (cached) {
|
|
968
|
+
return cached;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
const strategy = filters.strategy || SearchStrategy.AUTO;
|
|
972
|
+
const limit = filters.limit || 10;
|
|
973
|
+
const offset = filters.offset || 0;
|
|
974
|
+
let actualStrategy;
|
|
975
|
+
let results;
|
|
976
|
+
let dbStartTime;
|
|
977
|
+
let dbEndTime;
|
|
978
|
+
const strategyStartTime = Date.now();
|
|
979
|
+
if (strategy === SearchStrategy.EXACT || strategy === SearchStrategy.AUTO) {
|
|
980
|
+
dbStartTime = Date.now();
|
|
981
|
+
results = await this.exactSearch(filters, limit, offset);
|
|
982
|
+
dbEndTime = Date.now();
|
|
983
|
+
actualStrategy = SearchStrategy.EXACT;
|
|
984
|
+
if (results.length === 0 && strategy === SearchStrategy.AUTO) {
|
|
985
|
+
dbStartTime = Date.now();
|
|
986
|
+
results = await this.fulltextSearch(filters, limit, offset);
|
|
987
|
+
dbEndTime = Date.now();
|
|
988
|
+
actualStrategy = SearchStrategy.FULLTEXT;
|
|
989
|
+
}
|
|
990
|
+
} else if (strategy === SearchStrategy.FULLTEXT) {
|
|
991
|
+
dbStartTime = Date.now();
|
|
992
|
+
results = await this.fulltextSearch(filters, limit, offset);
|
|
993
|
+
dbEndTime = Date.now();
|
|
994
|
+
actualStrategy = SearchStrategy.FULLTEXT;
|
|
995
|
+
} else {
|
|
996
|
+
dbStartTime = Date.now();
|
|
997
|
+
results = await this.fulltextSearch(filters, limit, offset);
|
|
998
|
+
dbEndTime = Date.now();
|
|
999
|
+
actualStrategy = SearchStrategy.FULLTEXT;
|
|
1000
|
+
}
|
|
1001
|
+
const strategyTime = Date.now() - strategyStartTime;
|
|
1002
|
+
const took = Date.now() - startTime;
|
|
1003
|
+
const dbTime = dbEndTime - dbStartTime;
|
|
1004
|
+
const parseTime = took - dbTime - strategyTime;
|
|
1005
|
+
const result = {
|
|
1006
|
+
memories: results,
|
|
1007
|
+
total: results.length,
|
|
1008
|
+
strategy: actualStrategy,
|
|
1009
|
+
took,
|
|
1010
|
+
metrics: {
|
|
1011
|
+
dbTime,
|
|
1012
|
+
parseTime: Math.max(0, parseTime),
|
|
1013
|
+
// 确保非负
|
|
1014
|
+
strategyTime,
|
|
1015
|
+
cacheHit: false
|
|
1016
|
+
// 数据库查询,标记为未命中
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
if (this.cache) {
|
|
1020
|
+
const cacheKey = LRUCache.generateKey(filters);
|
|
1021
|
+
this.cache.set(cacheKey, result);
|
|
1022
|
+
}
|
|
1023
|
+
return result;
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* L1: 精确匹配检索
|
|
1027
|
+
*/
|
|
1028
|
+
async exactSearch(filters, limit, offset) {
|
|
1029
|
+
const where = {};
|
|
1030
|
+
if (filters.projectId) {
|
|
1031
|
+
where.projectId = filters.projectId;
|
|
1032
|
+
}
|
|
1033
|
+
if (filters.type) {
|
|
1034
|
+
where.type = filters.type;
|
|
1035
|
+
}
|
|
1036
|
+
if (filters.sessionId) {
|
|
1037
|
+
where.sessionId = filters.sessionId;
|
|
1038
|
+
}
|
|
1039
|
+
if (filters.query) {
|
|
1040
|
+
where.OR = [
|
|
1041
|
+
{ summary: { contains: filters.query, mode: "insensitive" } },
|
|
1042
|
+
{ fullText: { contains: filters.query, mode: "insensitive" } }
|
|
1043
|
+
];
|
|
1044
|
+
}
|
|
1045
|
+
const rows = await this.prisma.memory.findMany({
|
|
1046
|
+
where,
|
|
1047
|
+
orderBy: { timestamp: "desc" },
|
|
1048
|
+
take: limit,
|
|
1049
|
+
skip: offset
|
|
1050
|
+
});
|
|
1051
|
+
return rows.map(this.rowToMemory);
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* L2: 全文搜索(PostgreSQL)
|
|
1055
|
+
*/
|
|
1056
|
+
async fulltextSearch(filters, limit, offset) {
|
|
1057
|
+
const where = {};
|
|
1058
|
+
if (filters.projectId) {
|
|
1059
|
+
where.projectId = filters.projectId;
|
|
1060
|
+
}
|
|
1061
|
+
if (filters.type) {
|
|
1062
|
+
where.type = filters.type;
|
|
1063
|
+
}
|
|
1064
|
+
if (filters.sessionId) {
|
|
1065
|
+
where.sessionId = filters.sessionId;
|
|
1066
|
+
}
|
|
1067
|
+
if (filters.query) {
|
|
1068
|
+
where.OR = [
|
|
1069
|
+
{ summary: { contains: filters.query, mode: "insensitive" } },
|
|
1070
|
+
{ fullText: { contains: filters.query, mode: "insensitive" } }
|
|
1071
|
+
];
|
|
1072
|
+
}
|
|
1073
|
+
const rows = await this.prisma.memory.findMany({
|
|
1074
|
+
where,
|
|
1075
|
+
orderBy: { timestamp: "desc" },
|
|
1076
|
+
take: limit,
|
|
1077
|
+
skip: offset
|
|
1078
|
+
});
|
|
1079
|
+
return rows.map(this.rowToMemory);
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* 获取时间线
|
|
1083
|
+
*/
|
|
1084
|
+
async getTimeline(options) {
|
|
1085
|
+
const where = {
|
|
1086
|
+
projectId: options.projectId
|
|
1087
|
+
};
|
|
1088
|
+
if (options.type) {
|
|
1089
|
+
where.type = options.type;
|
|
1090
|
+
}
|
|
1091
|
+
if (options.dateRange) {
|
|
1092
|
+
where.timestamp = {
|
|
1093
|
+
gte: new Date(options.dateRange[0]),
|
|
1094
|
+
lte: new Date(options.dateRange[1])
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
const memories = await this.prisma.memory.findMany({
|
|
1098
|
+
where,
|
|
1099
|
+
orderBy: { timestamp: "desc" },
|
|
1100
|
+
take: options.limit,
|
|
1101
|
+
skip: options.offset
|
|
1102
|
+
});
|
|
1103
|
+
const converted = memories.map(this.rowToMemory);
|
|
1104
|
+
const entries = converted.map((memory, index) => ({
|
|
1105
|
+
memory,
|
|
1106
|
+
prevMemoryId: index > 0 ? converted[index - 1].meta.id : void 0,
|
|
1107
|
+
nextMemoryId: index < converted.length - 1 ? converted[index + 1].meta.id : void 0
|
|
1108
|
+
}));
|
|
1109
|
+
return {
|
|
1110
|
+
entries,
|
|
1111
|
+
total: entries.length
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* 获取关系链
|
|
1116
|
+
*/
|
|
1117
|
+
async getRelations(options) {
|
|
1118
|
+
const memory = await this.getMemoryById(options.memoryId);
|
|
1119
|
+
if (!memory) {
|
|
1120
|
+
throw new Error(`Memory ${options.memoryId} not found`);
|
|
1121
|
+
}
|
|
1122
|
+
const depth = options.depth || 1;
|
|
1123
|
+
const relatedNodes = [];
|
|
1124
|
+
if (depth > 0) {
|
|
1125
|
+
const relatedIds = [
|
|
1126
|
+
...memory.relations.replaces || [],
|
|
1127
|
+
...memory.relations.relatedTo || [],
|
|
1128
|
+
...memory.relations.impacts || []
|
|
1129
|
+
];
|
|
1130
|
+
if (memory.relations.derivedFrom) {
|
|
1131
|
+
relatedIds.push(memory.relations.derivedFrom);
|
|
1132
|
+
}
|
|
1133
|
+
for (const relatedId of relatedIds) {
|
|
1134
|
+
const relatedMemory = await this.getMemoryById(relatedId);
|
|
1135
|
+
if (relatedMemory) {
|
|
1136
|
+
let nestedRelated = void 0;
|
|
1137
|
+
if (depth > 1) {
|
|
1138
|
+
const nestedResult = await this.getRelations({
|
|
1139
|
+
memoryId: relatedId,
|
|
1140
|
+
depth: depth - 1
|
|
1141
|
+
});
|
|
1142
|
+
nestedRelated = nestedResult.related;
|
|
1143
|
+
}
|
|
1144
|
+
relatedNodes.push({
|
|
1145
|
+
memory: relatedMemory,
|
|
1146
|
+
related: nestedRelated
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
return {
|
|
1152
|
+
memory,
|
|
1153
|
+
related: relatedNodes.length > 0 ? relatedNodes : void 0
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* 根据 ID 获取单个记忆
|
|
1158
|
+
*/
|
|
1159
|
+
async getById(id) {
|
|
1160
|
+
const row = await this.prisma.memory.findUnique({
|
|
1161
|
+
where: { id }
|
|
1162
|
+
});
|
|
1163
|
+
return row ? this.rowToMemory(row) : null;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* 删除记忆
|
|
1167
|
+
*/
|
|
1168
|
+
async delete(memoryId) {
|
|
1169
|
+
try {
|
|
1170
|
+
if (this.cache) {
|
|
1171
|
+
const memory = await this.prisma.memory.findUnique({
|
|
1172
|
+
where: { id: memoryId },
|
|
1173
|
+
select: { projectId: true }
|
|
1174
|
+
});
|
|
1175
|
+
if (memory) {
|
|
1176
|
+
this.cache.invalidateProject(memory.projectId);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
await this.prisma.memory.delete({
|
|
1180
|
+
where: { id: memoryId }
|
|
1181
|
+
});
|
|
1182
|
+
return { success: true };
|
|
1183
|
+
} catch (error) {
|
|
1184
|
+
return { success: false };
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* 更新记忆
|
|
1189
|
+
*/
|
|
1190
|
+
async update(memoryId, updates) {
|
|
1191
|
+
try {
|
|
1192
|
+
const data = {};
|
|
1193
|
+
if (updates.content) {
|
|
1194
|
+
if (updates.content.summary) {
|
|
1195
|
+
data.summary = updates.content.summary;
|
|
1196
|
+
}
|
|
1197
|
+
if (updates.content.data !== void 0) {
|
|
1198
|
+
data.data = updates.content.data;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (updates.meta?.tags) {
|
|
1202
|
+
data.tags = updates.meta.tags;
|
|
1203
|
+
}
|
|
1204
|
+
if (updates.relations) {
|
|
1205
|
+
if (updates.relations.replaces) {
|
|
1206
|
+
data.replaces = updates.relations.replaces;
|
|
1207
|
+
}
|
|
1208
|
+
if (updates.relations.relatedTo) {
|
|
1209
|
+
data.relatedTo = updates.relations.relatedTo;
|
|
1210
|
+
}
|
|
1211
|
+
if (updates.relations.impacts) {
|
|
1212
|
+
data.impacts = updates.relations.impacts;
|
|
1213
|
+
}
|
|
1214
|
+
if (updates.relations.derivedFrom) {
|
|
1215
|
+
data.derivedFrom = updates.relations.derivedFrom;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (Object.keys(data).length === 0) {
|
|
1219
|
+
return { success: false };
|
|
1220
|
+
}
|
|
1221
|
+
await this.prisma.memory.update({
|
|
1222
|
+
where: { id: memoryId },
|
|
1223
|
+
data
|
|
1224
|
+
});
|
|
1225
|
+
if (this.cache) {
|
|
1226
|
+
const memory = await this.prisma.memory.findUnique({
|
|
1227
|
+
where: { id: memoryId },
|
|
1228
|
+
select: { projectId: true }
|
|
1229
|
+
});
|
|
1230
|
+
if (memory) {
|
|
1231
|
+
this.cache.invalidateProject(memory.projectId);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return { success: true };
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
return { success: false };
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* 根据ID获取记忆
|
|
1241
|
+
*/
|
|
1242
|
+
async getMemoryById(id) {
|
|
1243
|
+
const row = await this.prisma.memory.findUnique({
|
|
1244
|
+
where: { id }
|
|
1245
|
+
});
|
|
1246
|
+
return row ? this.rowToMemory(row) : null;
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* 将Prisma行转换为Memory对象
|
|
1250
|
+
*/
|
|
1251
|
+
rowToMemory(row) {
|
|
1252
|
+
return {
|
|
1253
|
+
meta: {
|
|
1254
|
+
id: row.id,
|
|
1255
|
+
projectId: row.projectId,
|
|
1256
|
+
sessionId: row.sessionId,
|
|
1257
|
+
timestamp: row.timestamp.toISOString(),
|
|
1258
|
+
type: row.type,
|
|
1259
|
+
tags: row.tags || [],
|
|
1260
|
+
version: row.version
|
|
1261
|
+
},
|
|
1262
|
+
content: {
|
|
1263
|
+
summary: row.summary,
|
|
1264
|
+
data: row.data || {}
|
|
1265
|
+
},
|
|
1266
|
+
relations: {
|
|
1267
|
+
replaces: row.replaces || void 0,
|
|
1268
|
+
relatedTo: row.relatedTo || void 0,
|
|
1269
|
+
impacts: row.impacts || void 0,
|
|
1270
|
+
derivedFrom: row.derivedFrom || void 0
|
|
1271
|
+
},
|
|
1272
|
+
searchable: {
|
|
1273
|
+
keywords: row.keywords || [],
|
|
1274
|
+
fullText: row.fullText
|
|
1275
|
+
},
|
|
1276
|
+
createdAt: row.createdAt.toISOString(),
|
|
1277
|
+
updatedAt: row.updatedAt.toISOString()
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* 关闭数据库连接
|
|
1282
|
+
*/
|
|
1283
|
+
async close() {
|
|
1284
|
+
await this.prisma.$disconnect();
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
// src/index.ts
|
|
1289
|
+
var server = new Server(
|
|
1290
|
+
{
|
|
1291
|
+
name: "elb-memory-pulse",
|
|
1292
|
+
version: "0.1.0"
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
capabilities: {
|
|
1296
|
+
tools: {},
|
|
1297
|
+
resources: {},
|
|
1298
|
+
prompts: {}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
);
|
|
1302
|
+
var storageType = process.env.MEMORY_STORAGE || "sqlite";
|
|
1303
|
+
function createStorage() {
|
|
1304
|
+
if (storageType === "postgresql") {
|
|
1305
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
1306
|
+
if (!databaseUrl) {
|
|
1307
|
+
console.error("\u9519\u8BEF: \u4F7F\u7528 PostgreSQL \u5B58\u50A8\u9700\u8981\u8BBE\u7F6E DATABASE_URL \u73AF\u5883\u53D8\u91CF");
|
|
1308
|
+
process.exit(1);
|
|
1309
|
+
}
|
|
1310
|
+
console.error(`Memory Pulse \u4F7F\u7528 PostgreSQL \u5B58\u50A8: ${databaseUrl.replace(/:[^:@]+@/, ":****@")}`);
|
|
1311
|
+
return new PostgreSQLStorage(databaseUrl);
|
|
1312
|
+
}
|
|
1313
|
+
const dbPath = process.env.MEMORY_DB_PATH || "./memory.db";
|
|
1314
|
+
console.error(`Memory Pulse \u4F7F\u7528 SQLite \u5B58\u50A8: ${dbPath}`);
|
|
1315
|
+
return new SQLiteStorage(dbPath);
|
|
1316
|
+
}
|
|
1317
|
+
var storage = createStorage();
|
|
1318
|
+
var tools = [
|
|
1319
|
+
{
|
|
1320
|
+
name: "mpulse_store",
|
|
1321
|
+
description: "\u667A\u80FD\u5B58\u50A8\u8BB0\u5FC6\uFF0CAI \u81EA\u52A8\u5206\u7C7B\u548C\u7ED3\u6784\u5316",
|
|
1322
|
+
inputSchema: {
|
|
1323
|
+
type: "object",
|
|
1324
|
+
properties: {
|
|
1325
|
+
content: {
|
|
1326
|
+
type: "string",
|
|
1327
|
+
description: "AI \u603B\u7ED3\u7684\u5185\u5BB9"
|
|
1328
|
+
},
|
|
1329
|
+
rawContext: {
|
|
1330
|
+
type: "object",
|
|
1331
|
+
description: "\u5B8C\u6574\u539F\u59CB\u6570\u636E\uFF08\u4E0D\u538B\u7F29\uFF09"
|
|
1332
|
+
},
|
|
1333
|
+
projectId: {
|
|
1334
|
+
type: "string",
|
|
1335
|
+
description: "\u9879\u76EE ID"
|
|
1336
|
+
},
|
|
1337
|
+
type: {
|
|
1338
|
+
type: "string",
|
|
1339
|
+
enum: ["decision", "solution", "config", "code", "error", "session"],
|
|
1340
|
+
description: "\u8BB0\u5FC6\u7C7B\u578B\uFF08\u53EF\u9009\uFF0C\u4E0D\u586B\u5219\u9ED8\u8BA4\u4E3A code\uFF09"
|
|
1341
|
+
},
|
|
1342
|
+
tags: {
|
|
1343
|
+
type: "array",
|
|
1344
|
+
items: { type: "string" },
|
|
1345
|
+
description: "\u6807\u7B7E"
|
|
1346
|
+
},
|
|
1347
|
+
sessionId: {
|
|
1348
|
+
type: "string",
|
|
1349
|
+
description: "\u4F1A\u8BDD ID\uFF08\u53EF\u9009\uFF09"
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
required: ["content", "rawContext", "projectId"]
|
|
1353
|
+
}
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
name: "mpulse_store_decision",
|
|
1357
|
+
description: "\u5B58\u50A8\u67B6\u6784\u51B3\u7B56\uFF08\u5F3A\u5236\u5B57\u6BB5\uFF0C\u9632\u6B62 AI \u5077\u61D2\uFF09",
|
|
1358
|
+
inputSchema: {
|
|
1359
|
+
type: "object",
|
|
1360
|
+
properties: {
|
|
1361
|
+
question: {
|
|
1362
|
+
type: "string",
|
|
1363
|
+
description: "\u51B3\u7B56\u95EE\u9898"
|
|
1364
|
+
},
|
|
1365
|
+
options: {
|
|
1366
|
+
type: "array",
|
|
1367
|
+
items: {
|
|
1368
|
+
type: "object",
|
|
1369
|
+
properties: {
|
|
1370
|
+
name: { type: "string" },
|
|
1371
|
+
pros: { type: "array", items: { type: "string" } },
|
|
1372
|
+
cons: { type: "array", items: { type: "string" } }
|
|
1373
|
+
},
|
|
1374
|
+
required: ["name", "pros", "cons"]
|
|
1375
|
+
},
|
|
1376
|
+
description: "\u8003\u8651\u7684\u9009\u9879"
|
|
1377
|
+
},
|
|
1378
|
+
chosen: {
|
|
1379
|
+
type: "string",
|
|
1380
|
+
description: "\u9009\u62E9\u7684\u65B9\u6848"
|
|
1381
|
+
},
|
|
1382
|
+
reason: {
|
|
1383
|
+
type: "string",
|
|
1384
|
+
description: "\u9009\u62E9\u7406\u7531"
|
|
1385
|
+
},
|
|
1386
|
+
projectId: {
|
|
1387
|
+
type: "string",
|
|
1388
|
+
description: "\u9879\u76EE ID"
|
|
1389
|
+
},
|
|
1390
|
+
tags: {
|
|
1391
|
+
type: "array",
|
|
1392
|
+
items: { type: "string" },
|
|
1393
|
+
description: "\u6807\u7B7E"
|
|
1394
|
+
},
|
|
1395
|
+
sessionId: {
|
|
1396
|
+
type: "string",
|
|
1397
|
+
description: "\u4F1A\u8BDD ID\uFF08\u53EF\u9009\uFF09"
|
|
1398
|
+
}
|
|
1399
|
+
},
|
|
1400
|
+
required: ["question", "options", "chosen", "reason", "projectId"]
|
|
1401
|
+
}
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
name: "mpulse_store_solution",
|
|
1405
|
+
description: "\u5B58\u50A8\u95EE\u9898\u89E3\u51B3\u65B9\u6848",
|
|
1406
|
+
inputSchema: {
|
|
1407
|
+
type: "object",
|
|
1408
|
+
properties: {
|
|
1409
|
+
problem: {
|
|
1410
|
+
type: "string",
|
|
1411
|
+
description: "\u95EE\u9898\u63CF\u8FF0"
|
|
1412
|
+
},
|
|
1413
|
+
rootCause: {
|
|
1414
|
+
type: "string",
|
|
1415
|
+
description: "\u6839\u56E0\u5206\u6790"
|
|
1416
|
+
},
|
|
1417
|
+
solution: {
|
|
1418
|
+
type: "string",
|
|
1419
|
+
description: "\u89E3\u51B3\u65B9\u6848"
|
|
1420
|
+
},
|
|
1421
|
+
prevention: {
|
|
1422
|
+
type: "string",
|
|
1423
|
+
description: "\u5982\u4F55\u9884\u9632"
|
|
1424
|
+
},
|
|
1425
|
+
relatedIssues: {
|
|
1426
|
+
type: "array",
|
|
1427
|
+
items: { type: "string" },
|
|
1428
|
+
description: "\u5173\u8054\u95EE\u9898"
|
|
1429
|
+
},
|
|
1430
|
+
projectId: {
|
|
1431
|
+
type: "string",
|
|
1432
|
+
description: "\u9879\u76EE ID"
|
|
1433
|
+
},
|
|
1434
|
+
tags: {
|
|
1435
|
+
type: "array",
|
|
1436
|
+
items: { type: "string" },
|
|
1437
|
+
description: "\u6807\u7B7E"
|
|
1438
|
+
},
|
|
1439
|
+
sessionId: {
|
|
1440
|
+
type: "string",
|
|
1441
|
+
description: "\u4F1A\u8BDD ID\uFF08\u53EF\u9009\uFF09"
|
|
1442
|
+
},
|
|
1443
|
+
artifacts: {
|
|
1444
|
+
type: "object",
|
|
1445
|
+
description: "\u76F8\u5173\u6587\u4EF6\uFF08\u4EE3\u7801\u7247\u6BB5\u7B49\uFF09"
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
required: ["problem", "rootCause", "solution", "projectId"]
|
|
1449
|
+
}
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
name: "mpulse_store_session",
|
|
1453
|
+
description: "\u5B58\u50A8\u4F1A\u8BDD\u603B\u7ED3\uFF08\u4F1A\u8BDD\u7ED3\u675F\u65F6\u8C03\u7528\uFF09",
|
|
1454
|
+
inputSchema: {
|
|
1455
|
+
type: "object",
|
|
1456
|
+
properties: {
|
|
1457
|
+
summary: {
|
|
1458
|
+
type: "string",
|
|
1459
|
+
description: "\u672C\u6B21\u4F1A\u8BDD\u603B\u7ED3"
|
|
1460
|
+
},
|
|
1461
|
+
decisions: {
|
|
1462
|
+
type: "array",
|
|
1463
|
+
items: { type: "string" },
|
|
1464
|
+
description: "\u672C\u6B21\u505A\u51FA\u7684\u51B3\u7B56"
|
|
1465
|
+
},
|
|
1466
|
+
unfinishedTasks: {
|
|
1467
|
+
type: "array",
|
|
1468
|
+
items: { type: "string" },
|
|
1469
|
+
description: "\u672A\u5B8C\u6210\u7684\u4EFB\u52A1"
|
|
1470
|
+
},
|
|
1471
|
+
nextSteps: {
|
|
1472
|
+
type: "array",
|
|
1473
|
+
items: { type: "string" },
|
|
1474
|
+
description: "\u4E0B\u6B21\u4ECE\u54EA\u7EE7\u7EED"
|
|
1475
|
+
},
|
|
1476
|
+
projectId: {
|
|
1477
|
+
type: "string",
|
|
1478
|
+
description: "\u9879\u76EE ID"
|
|
1479
|
+
},
|
|
1480
|
+
sessionId: {
|
|
1481
|
+
type: "string",
|
|
1482
|
+
description: "\u4F1A\u8BDD ID"
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
required: ["summary", "projectId", "sessionId"]
|
|
1486
|
+
}
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
name: "mpulse_recall",
|
|
1490
|
+
description: "\u68C0\u7D22\u8BB0\u5FC6\uFF08\u591A\u7B56\u7565\uFF1A\u7CBE\u786E\u3001\u5168\u6587\u3001\u8BED\u4E49\uFF09",
|
|
1491
|
+
inputSchema: {
|
|
1492
|
+
type: "object",
|
|
1493
|
+
properties: {
|
|
1494
|
+
query: {
|
|
1495
|
+
type: "string",
|
|
1496
|
+
description: "\u67E5\u8BE2\u5185\u5BB9"
|
|
1497
|
+
},
|
|
1498
|
+
projectId: {
|
|
1499
|
+
type: "string",
|
|
1500
|
+
description: "\u9879\u76EE ID"
|
|
1501
|
+
},
|
|
1502
|
+
type: {
|
|
1503
|
+
type: "string",
|
|
1504
|
+
enum: ["decision", "solution", "config", "code", "error", "session"],
|
|
1505
|
+
description: "\u8BB0\u5FC6\u7C7B\u578B"
|
|
1506
|
+
},
|
|
1507
|
+
tags: {
|
|
1508
|
+
type: "array",
|
|
1509
|
+
items: { type: "string" },
|
|
1510
|
+
description: "\u6807\u7B7E\u8FC7\u6EE4"
|
|
1511
|
+
},
|
|
1512
|
+
strategy: {
|
|
1513
|
+
type: "string",
|
|
1514
|
+
enum: ["exact", "fulltext", "semantic"],
|
|
1515
|
+
description: "\u68C0\u7D22\u7B56\u7565"
|
|
1516
|
+
},
|
|
1517
|
+
limit: {
|
|
1518
|
+
type: "number",
|
|
1519
|
+
description: "\u8FD4\u56DE\u6570\u91CF"
|
|
1520
|
+
}
|
|
1521
|
+
},
|
|
1522
|
+
required: ["query"]
|
|
1523
|
+
}
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
name: "mpulse_timeline",
|
|
1527
|
+
description: "\u67E5\u770B\u9879\u76EE\u7684\u65F6\u95F4\u7EBF\u89C6\u56FE",
|
|
1528
|
+
inputSchema: {
|
|
1529
|
+
type: "object",
|
|
1530
|
+
properties: {
|
|
1531
|
+
projectId: {
|
|
1532
|
+
type: "string",
|
|
1533
|
+
description: "\u9879\u76EE ID"
|
|
1534
|
+
},
|
|
1535
|
+
dateRange: {
|
|
1536
|
+
type: "array",
|
|
1537
|
+
items: { type: "string" },
|
|
1538
|
+
description: "\u65E5\u671F\u8303\u56F4 [start, end]"
|
|
1539
|
+
},
|
|
1540
|
+
type: {
|
|
1541
|
+
type: "string",
|
|
1542
|
+
enum: ["decision", "solution", "config", "code", "error", "session"],
|
|
1543
|
+
description: "\u8BB0\u5FC6\u7C7B\u578B"
|
|
1544
|
+
},
|
|
1545
|
+
limit: {
|
|
1546
|
+
type: "number",
|
|
1547
|
+
description: "\u8FD4\u56DE\u6570\u91CF"
|
|
1548
|
+
},
|
|
1549
|
+
offset: {
|
|
1550
|
+
type: "number",
|
|
1551
|
+
description: "\u5206\u9875\u504F\u79FB"
|
|
1552
|
+
}
|
|
1553
|
+
},
|
|
1554
|
+
required: ["projectId"]
|
|
1555
|
+
}
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
name: "mpulse_relations",
|
|
1559
|
+
description: "\u67E5\u8BE2\u8BB0\u5FC6\u7684\u5173\u7CFB\u94FE",
|
|
1560
|
+
inputSchema: {
|
|
1561
|
+
type: "object",
|
|
1562
|
+
properties: {
|
|
1563
|
+
memoryId: {
|
|
1564
|
+
type: "string",
|
|
1565
|
+
description: "\u8BB0\u5FC6 ID"
|
|
1566
|
+
},
|
|
1567
|
+
depth: {
|
|
1568
|
+
type: "number",
|
|
1569
|
+
description: "\u9012\u5F52\u6DF1\u5EA6"
|
|
1570
|
+
}
|
|
1571
|
+
},
|
|
1572
|
+
required: ["memoryId"]
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
];
|
|
1576
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
1577
|
+
tools
|
|
1578
|
+
}));
|
|
1579
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
1580
|
+
resources: [
|
|
1581
|
+
{
|
|
1582
|
+
uri: "memory://projects",
|
|
1583
|
+
name: "\u6240\u6709\u9879\u76EE\u8BB0\u5FC6",
|
|
1584
|
+
description: "\u5217\u51FA\u6240\u6709\u9879\u76EE ID",
|
|
1585
|
+
mimeType: "application/json"
|
|
1586
|
+
}
|
|
1587
|
+
]
|
|
1588
|
+
}));
|
|
1589
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1590
|
+
const uri = request.params.uri;
|
|
1591
|
+
try {
|
|
1592
|
+
if (uri === "memory://projects") {
|
|
1593
|
+
const allMemories = await storage.recall({ query: "", limit: 1e4 });
|
|
1594
|
+
const projects = new Set(allMemories.memories.map((m) => m.meta.projectId));
|
|
1595
|
+
return {
|
|
1596
|
+
contents: [
|
|
1597
|
+
{
|
|
1598
|
+
uri,
|
|
1599
|
+
mimeType: "application/json",
|
|
1600
|
+
text: JSON.stringify(
|
|
1601
|
+
{
|
|
1602
|
+
projects: Array.from(projects),
|
|
1603
|
+
total: projects.size
|
|
1604
|
+
},
|
|
1605
|
+
null,
|
|
1606
|
+
2
|
|
1607
|
+
)
|
|
1608
|
+
}
|
|
1609
|
+
]
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
const projectMatch = uri.match(/^memory:\/\/project\/(.+)$/);
|
|
1613
|
+
if (projectMatch) {
|
|
1614
|
+
const projectId = decodeURIComponent(projectMatch[1]);
|
|
1615
|
+
const result = await storage.recall({ query: "", projectId, limit: 1e3 });
|
|
1616
|
+
return {
|
|
1617
|
+
contents: [
|
|
1618
|
+
{
|
|
1619
|
+
uri,
|
|
1620
|
+
mimeType: "application/json",
|
|
1621
|
+
text: JSON.stringify(
|
|
1622
|
+
{
|
|
1623
|
+
projectId,
|
|
1624
|
+
memories: result.memories,
|
|
1625
|
+
total: result.total
|
|
1626
|
+
},
|
|
1627
|
+
null,
|
|
1628
|
+
2
|
|
1629
|
+
)
|
|
1630
|
+
}
|
|
1631
|
+
]
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
const memoryMatch = uri.match(/^memory:\/\/memory\/(.+)$/);
|
|
1635
|
+
if (memoryMatch) {
|
|
1636
|
+
const memoryId = decodeURIComponent(memoryMatch[1]);
|
|
1637
|
+
const result = await storage.getRelations({ memoryId, depth: 0 });
|
|
1638
|
+
return {
|
|
1639
|
+
contents: [
|
|
1640
|
+
{
|
|
1641
|
+
uri,
|
|
1642
|
+
mimeType: "application/json",
|
|
1643
|
+
text: JSON.stringify(result.memory, null, 2)
|
|
1644
|
+
}
|
|
1645
|
+
]
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
const sessionMatch = uri.match(/^memory:\/\/session\/(.+)$/);
|
|
1649
|
+
if (sessionMatch) {
|
|
1650
|
+
const sessionId = decodeURIComponent(sessionMatch[1]);
|
|
1651
|
+
const result = await storage.recall({ query: "", sessionId, limit: 1e3 });
|
|
1652
|
+
return {
|
|
1653
|
+
contents: [
|
|
1654
|
+
{
|
|
1655
|
+
uri,
|
|
1656
|
+
mimeType: "application/json",
|
|
1657
|
+
text: JSON.stringify(
|
|
1658
|
+
{
|
|
1659
|
+
sessionId,
|
|
1660
|
+
memories: result.memories,
|
|
1661
|
+
total: result.total
|
|
1662
|
+
},
|
|
1663
|
+
null,
|
|
1664
|
+
2
|
|
1665
|
+
)
|
|
1666
|
+
}
|
|
1667
|
+
]
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
throw new Error(
|
|
1673
|
+
`Failed to read resource ${uri}: ${error instanceof Error ? error.message : String(error)}`
|
|
1674
|
+
);
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
1678
|
+
prompts: [
|
|
1679
|
+
{
|
|
1680
|
+
name: "analyze-decision",
|
|
1681
|
+
description: "\u5206\u6790\u9879\u76EE\u4E2D\u7684\u67B6\u6784\u51B3\u7B56",
|
|
1682
|
+
arguments: [
|
|
1683
|
+
{
|
|
1684
|
+
name: "projectId",
|
|
1685
|
+
description: "\u9879\u76EE ID",
|
|
1686
|
+
required: true
|
|
1687
|
+
}
|
|
1688
|
+
]
|
|
1689
|
+
},
|
|
1690
|
+
{
|
|
1691
|
+
name: "summarize-session",
|
|
1692
|
+
description: "\u603B\u7ED3\u4F1A\u8BDD\u4E2D\u7684\u5DE5\u4F5C\u5185\u5BB9",
|
|
1693
|
+
arguments: [
|
|
1694
|
+
{
|
|
1695
|
+
name: "sessionId",
|
|
1696
|
+
description: "\u4F1A\u8BDD ID",
|
|
1697
|
+
required: true
|
|
1698
|
+
}
|
|
1699
|
+
]
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
name: "find-related",
|
|
1703
|
+
description: "\u67E5\u627E\u4E0E\u7279\u5B9A\u4E3B\u9898\u76F8\u5173\u7684\u8BB0\u5FC6",
|
|
1704
|
+
arguments: [
|
|
1705
|
+
{
|
|
1706
|
+
name: "topic",
|
|
1707
|
+
description: "\u4E3B\u9898\u5173\u952E\u8BCD",
|
|
1708
|
+
required: true
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
name: "projectId",
|
|
1712
|
+
description: "\u9879\u76EE ID\uFF08\u53EF\u9009\uFF09",
|
|
1713
|
+
required: false
|
|
1714
|
+
}
|
|
1715
|
+
]
|
|
1716
|
+
},
|
|
1717
|
+
{
|
|
1718
|
+
name: "review-project",
|
|
1719
|
+
description: "\u56DE\u987E\u9879\u76EE\u7684\u53D1\u5C55\u5386\u7A0B",
|
|
1720
|
+
arguments: [
|
|
1721
|
+
{
|
|
1722
|
+
name: "projectId",
|
|
1723
|
+
description: "\u9879\u76EE ID",
|
|
1724
|
+
required: true
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
name: "type",
|
|
1728
|
+
description: "\u8BB0\u5FC6\u7C7B\u578B\uFF08\u53EF\u9009\uFF1Adecision, solution, session\uFF09",
|
|
1729
|
+
required: false
|
|
1730
|
+
}
|
|
1731
|
+
]
|
|
1732
|
+
}
|
|
1733
|
+
]
|
|
1734
|
+
}));
|
|
1735
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1736
|
+
const { name, arguments: args } = request.params;
|
|
1737
|
+
if (!args) {
|
|
1738
|
+
throw new Error("Missing required arguments");
|
|
1739
|
+
}
|
|
1740
|
+
try {
|
|
1741
|
+
switch (name) {
|
|
1742
|
+
case "analyze-decision": {
|
|
1743
|
+
const projectId = args.projectId;
|
|
1744
|
+
const decisions = await storage.recall({
|
|
1745
|
+
query: "",
|
|
1746
|
+
projectId,
|
|
1747
|
+
type: "decision",
|
|
1748
|
+
limit: 100
|
|
1749
|
+
});
|
|
1750
|
+
return {
|
|
1751
|
+
messages: [
|
|
1752
|
+
{
|
|
1753
|
+
role: "user",
|
|
1754
|
+
content: {
|
|
1755
|
+
type: "text",
|
|
1756
|
+
text: `\u8BF7\u5206\u6790\u9879\u76EE "${projectId}" \u4E2D\u7684\u67B6\u6784\u51B3\u7B56\u3002
|
|
1757
|
+
|
|
1758
|
+
\u627E\u5230\u4E86 ${decisions.total} \u4E2A\u51B3\u7B56\u8BB0\u5F55\uFF1A
|
|
1759
|
+
|
|
1760
|
+
${decisions.memories.map((m, idx) => {
|
|
1761
|
+
const data = m.content.data;
|
|
1762
|
+
return `
|
|
1763
|
+
${idx + 1}. ${data.question || m.content.summary}
|
|
1764
|
+
\u9009\u62E9\uFF1A${data.chosen || "\u672A\u77E5"}
|
|
1765
|
+
\u7406\u7531\uFF1A${data.reason || "\u672A\u63D0\u4F9B"}
|
|
1766
|
+
\u65F6\u95F4\uFF1A${m.meta.timestamp}
|
|
1767
|
+
`;
|
|
1768
|
+
}).join("\n")}
|
|
1769
|
+
|
|
1770
|
+
\u8BF7\u603B\u7ED3\uFF1A
|
|
1771
|
+
1. \u4E3B\u8981\u7684\u6280\u672F\u9009\u578B\u6709\u54EA\u4E9B\uFF1F
|
|
1772
|
+
2. \u51B3\u7B56\u7684\u6F14\u8FDB\u8D8B\u52BF\u5982\u4F55\uFF1F
|
|
1773
|
+
3. \u662F\u5426\u5B58\u5728\u53EF\u80FD\u7684\u6280\u672F\u503A\u52A1\uFF1F
|
|
1774
|
+
4. \u6709\u54EA\u4E9B\u503C\u5F97\u8BB0\u5F55\u7684\u7ECF\u9A8C\u6559\u8BAD\uFF1F`
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
]
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
case "summarize-session": {
|
|
1781
|
+
const sessionId = args.sessionId;
|
|
1782
|
+
const memories = await storage.recall({
|
|
1783
|
+
query: "",
|
|
1784
|
+
sessionId,
|
|
1785
|
+
limit: 100
|
|
1786
|
+
});
|
|
1787
|
+
return {
|
|
1788
|
+
messages: [
|
|
1789
|
+
{
|
|
1790
|
+
role: "user",
|
|
1791
|
+
content: {
|
|
1792
|
+
type: "text",
|
|
1793
|
+
text: `\u8BF7\u603B\u7ED3\u4F1A\u8BDD "${sessionId}" \u7684\u5DE5\u4F5C\u5185\u5BB9\u3002
|
|
1794
|
+
|
|
1795
|
+
\u4F1A\u8BDD\u5305\u542B ${memories.total} \u6761\u8BB0\u5FC6\uFF1A
|
|
1796
|
+
|
|
1797
|
+
${memories.memories.map((m, idx) => `${idx + 1}. [${m.meta.type}] ${m.content.summary}`).join("\n")}
|
|
1798
|
+
|
|
1799
|
+
\u8BF7\u751F\u6210\u4E00\u4E2A\u7B80\u6D01\u7684\u5DE5\u4F5C\u603B\u7ED3\uFF0C\u5305\u62EC\uFF1A
|
|
1800
|
+
1. \u4E3B\u8981\u5B8C\u6210\u7684\u4EFB\u52A1
|
|
1801
|
+
2. \u505A\u51FA\u7684\u91CD\u8981\u51B3\u7B56
|
|
1802
|
+
3. \u9047\u5230\u7684\u95EE\u9898\u548C\u89E3\u51B3\u65B9\u6848
|
|
1803
|
+
4. \u672A\u5B8C\u6210\u7684\u4EFB\u52A1\uFF08\u5982\u6709\uFF09
|
|
1804
|
+
5. \u4E0B\u4E00\u6B65\u8BA1\u5212\u5EFA\u8BAE`
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
]
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
case "find-related": {
|
|
1811
|
+
const topic = args.topic;
|
|
1812
|
+
const projectId = args.projectId;
|
|
1813
|
+
const related = await storage.recall({
|
|
1814
|
+
query: topic,
|
|
1815
|
+
projectId,
|
|
1816
|
+
limit: 20
|
|
1817
|
+
});
|
|
1818
|
+
return {
|
|
1819
|
+
messages: [
|
|
1820
|
+
{
|
|
1821
|
+
role: "user",
|
|
1822
|
+
content: {
|
|
1823
|
+
type: "text",
|
|
1824
|
+
text: `\u67E5\u627E\u4E0E "${topic}" \u76F8\u5173\u7684\u8BB0\u5FC6${projectId ? `\uFF08\u9879\u76EE\uFF1A${projectId}\uFF09` : ""}\u3002
|
|
1825
|
+
|
|
1826
|
+
\u627E\u5230 ${related.total} \u6761\u76F8\u5173\u8BB0\u5FC6\uFF1A
|
|
1827
|
+
|
|
1828
|
+
${related.memories.map(
|
|
1829
|
+
(m, idx) => `
|
|
1830
|
+
${idx + 1}. ${m.content.summary}
|
|
1831
|
+
\u7C7B\u578B\uFF1A${m.meta.type}
|
|
1832
|
+
\u9879\u76EE\uFF1A${m.meta.projectId}
|
|
1833
|
+
\u65F6\u95F4\uFF1A${m.meta.timestamp}
|
|
1834
|
+
`
|
|
1835
|
+
).join("\n")}
|
|
1836
|
+
|
|
1837
|
+
\u8BF7\u5206\u6790\u8FD9\u4E9B\u8BB0\u5FC6\u4E4B\u95F4\u7684\u5173\u8054\u6027\uFF0C\u5E76\u63D0\u4F9B\uFF1A
|
|
1838
|
+
1. \u8FD9\u4E9B\u8BB0\u5FC6\u56F4\u7ED5\u4EC0\u4E48\u4E3B\u9898\uFF1F
|
|
1839
|
+
2. \u662F\u5426\u5B58\u5728\u6F14\u8FDB\u5173\u7CFB\uFF1F
|
|
1840
|
+
3. \u6709\u54EA\u4E9B\u6709\u4EF7\u503C\u7684\u7ECF\u9A8C\u53EF\u4EE5\u590D\u7528\uFF1F`
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
]
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
case "review-project": {
|
|
1847
|
+
const projectId = args.projectId;
|
|
1848
|
+
const type = args.type;
|
|
1849
|
+
const timeline = await storage.getTimeline({
|
|
1850
|
+
projectId,
|
|
1851
|
+
type,
|
|
1852
|
+
limit: 100
|
|
1853
|
+
});
|
|
1854
|
+
return {
|
|
1855
|
+
messages: [
|
|
1856
|
+
{
|
|
1857
|
+
role: "user",
|
|
1858
|
+
content: {
|
|
1859
|
+
type: "text",
|
|
1860
|
+
text: `\u56DE\u987E\u9879\u76EE "${projectId}" \u7684\u53D1\u5C55\u5386\u7A0B${type ? `\uFF08\u7C7B\u578B\uFF1A${type}\uFF09` : ""}\u3002
|
|
1861
|
+
|
|
1862
|
+
\u65F6\u95F4\u7EBF\u5305\u542B ${timeline.total} \u6761\u8BB0\u5F55\uFF1A
|
|
1863
|
+
|
|
1864
|
+
${timeline.entries.map((entry, idx) => {
|
|
1865
|
+
const m = entry.memory;
|
|
1866
|
+
return `${idx + 1}. [${new Date(m.meta.timestamp).toLocaleDateString()}] ${m.content.summary}`;
|
|
1867
|
+
}).join("\n")}
|
|
1868
|
+
|
|
1869
|
+
\u8BF7\u751F\u6210\u9879\u76EE\u56DE\u987E\u62A5\u544A\uFF0C\u5305\u62EC\uFF1A
|
|
1870
|
+
1. \u9879\u76EE\u8D77\u6E90\u548C\u521D\u59CB\u76EE\u6807
|
|
1871
|
+
2. \u5173\u952E\u91CC\u7A0B\u7891\u548C\u51B3\u7B56\u70B9
|
|
1872
|
+
3. \u9047\u5230\u7684\u6311\u6218\u548C\u89E3\u51B3\u65B9\u6848
|
|
1873
|
+
4. \u5F53\u524D\u72B6\u6001\u548C\u672A\u6765\u65B9\u5411
|
|
1874
|
+
5. \u7ECF\u9A8C\u603B\u7ED3\u548C\u6539\u8FDB\u5EFA\u8BAE`
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
]
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
default:
|
|
1881
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
1882
|
+
}
|
|
1883
|
+
} catch (error) {
|
|
1884
|
+
throw new Error(
|
|
1885
|
+
`Failed to generate prompt ${name}: ${error instanceof Error ? error.message : String(error)}`
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
});
|
|
1889
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1890
|
+
const { name, arguments: args } = request.params;
|
|
1891
|
+
if (!args) {
|
|
1892
|
+
throw new Error("Missing required arguments");
|
|
1893
|
+
}
|
|
1894
|
+
try {
|
|
1895
|
+
switch (name) {
|
|
1896
|
+
case "mpulse_store": {
|
|
1897
|
+
const result = await storage.store({
|
|
1898
|
+
content: args.content,
|
|
1899
|
+
rawContext: args.rawContext,
|
|
1900
|
+
projectId: args.projectId,
|
|
1901
|
+
type: args.type,
|
|
1902
|
+
tags: args.tags,
|
|
1903
|
+
sessionId: args.sessionId
|
|
1904
|
+
});
|
|
1905
|
+
return {
|
|
1906
|
+
content: [
|
|
1907
|
+
{
|
|
1908
|
+
type: "text",
|
|
1909
|
+
text: JSON.stringify(result, null, 2)
|
|
1910
|
+
}
|
|
1911
|
+
]
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
case "mpulse_store_decision": {
|
|
1915
|
+
const decisionParams = {
|
|
1916
|
+
question: args.question,
|
|
1917
|
+
options: args.options,
|
|
1918
|
+
chosen: args.chosen,
|
|
1919
|
+
reason: args.reason,
|
|
1920
|
+
projectId: args.projectId,
|
|
1921
|
+
tags: args.tags,
|
|
1922
|
+
sessionId: args.sessionId
|
|
1923
|
+
};
|
|
1924
|
+
const result = await storage.storeDecision(decisionParams);
|
|
1925
|
+
return {
|
|
1926
|
+
content: [
|
|
1927
|
+
{
|
|
1928
|
+
type: "text",
|
|
1929
|
+
text: JSON.stringify(result, null, 2)
|
|
1930
|
+
}
|
|
1931
|
+
]
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
case "mpulse_store_solution": {
|
|
1935
|
+
const solutionParams = {
|
|
1936
|
+
problem: args.problem,
|
|
1937
|
+
rootCause: args.rootCause,
|
|
1938
|
+
solution: args.solution,
|
|
1939
|
+
prevention: args.prevention,
|
|
1940
|
+
relatedIssues: args.relatedIssues,
|
|
1941
|
+
projectId: args.projectId,
|
|
1942
|
+
tags: args.tags,
|
|
1943
|
+
sessionId: args.sessionId,
|
|
1944
|
+
artifacts: args.artifacts
|
|
1945
|
+
};
|
|
1946
|
+
const result = await storage.storeSolution(solutionParams);
|
|
1947
|
+
return {
|
|
1948
|
+
content: [
|
|
1949
|
+
{
|
|
1950
|
+
type: "text",
|
|
1951
|
+
text: JSON.stringify(result, null, 2)
|
|
1952
|
+
}
|
|
1953
|
+
]
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
case "mpulse_store_session": {
|
|
1957
|
+
const sessionParams = {
|
|
1958
|
+
summary: args.summary,
|
|
1959
|
+
decisions: args.decisions,
|
|
1960
|
+
unfinishedTasks: args.unfinishedTasks,
|
|
1961
|
+
nextSteps: args.nextSteps,
|
|
1962
|
+
projectId: args.projectId,
|
|
1963
|
+
sessionId: args.sessionId
|
|
1964
|
+
};
|
|
1965
|
+
const result = await storage.storeSession(sessionParams);
|
|
1966
|
+
return {
|
|
1967
|
+
content: [
|
|
1968
|
+
{
|
|
1969
|
+
type: "text",
|
|
1970
|
+
text: JSON.stringify(result, null, 2)
|
|
1971
|
+
}
|
|
1972
|
+
]
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
case "mpulse_recall": {
|
|
1976
|
+
const filters = {
|
|
1977
|
+
query: args.query,
|
|
1978
|
+
projectId: args.projectId,
|
|
1979
|
+
type: args.type,
|
|
1980
|
+
tags: args.tags,
|
|
1981
|
+
strategy: args.strategy,
|
|
1982
|
+
limit: args.limit
|
|
1983
|
+
};
|
|
1984
|
+
const result = await storage.recall(filters);
|
|
1985
|
+
return {
|
|
1986
|
+
content: [
|
|
1987
|
+
{
|
|
1988
|
+
type: "text",
|
|
1989
|
+
text: JSON.stringify(result, null, 2)
|
|
1990
|
+
}
|
|
1991
|
+
]
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
case "mpulse_timeline": {
|
|
1995
|
+
const options = {
|
|
1996
|
+
projectId: args.projectId,
|
|
1997
|
+
dateRange: args.dateRange,
|
|
1998
|
+
type: args.type,
|
|
1999
|
+
limit: args.limit,
|
|
2000
|
+
offset: args.offset
|
|
2001
|
+
};
|
|
2002
|
+
const result = await storage.getTimeline(options);
|
|
2003
|
+
return {
|
|
2004
|
+
content: [
|
|
2005
|
+
{
|
|
2006
|
+
type: "text",
|
|
2007
|
+
text: JSON.stringify(result, null, 2)
|
|
2008
|
+
}
|
|
2009
|
+
]
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
case "mpulse_relations": {
|
|
2013
|
+
const options = {
|
|
2014
|
+
memoryId: args.memoryId,
|
|
2015
|
+
depth: args.depth
|
|
2016
|
+
};
|
|
2017
|
+
const result = await storage.getRelations(options);
|
|
2018
|
+
return {
|
|
2019
|
+
content: [
|
|
2020
|
+
{
|
|
2021
|
+
type: "text",
|
|
2022
|
+
text: JSON.stringify(result, null, 2)
|
|
2023
|
+
}
|
|
2024
|
+
]
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
default:
|
|
2028
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
2029
|
+
}
|
|
2030
|
+
} catch (error) {
|
|
2031
|
+
return {
|
|
2032
|
+
content: [
|
|
2033
|
+
{
|
|
2034
|
+
type: "text",
|
|
2035
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
2036
|
+
}
|
|
2037
|
+
],
|
|
2038
|
+
isError: true
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
});
|
|
2042
|
+
async function main() {
|
|
2043
|
+
const transport = new StdioServerTransport();
|
|
2044
|
+
await server.connect(transport);
|
|
2045
|
+
console.error("Memory Pulse (\u8BB0\u5FC6\u8109\u640F) MCP Server running on stdio");
|
|
2046
|
+
}
|
|
2047
|
+
main().catch((error) => {
|
|
2048
|
+
console.error("Fatal error:", error);
|
|
2049
|
+
process.exit(1);
|
|
2050
|
+
});
|
|
2051
|
+
//# sourceMappingURL=index.js.map
|