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/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