@cup319/mmpl 2.7.1 → 2.8.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/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  ## 核心特性
9
9
 
10
- - **8 个精简工具**:write、search、manage、evolve、compress、palace、symbol、stats
10
+ - **9 个精简工具**:write、search、list、manage、evolve、compress、palace、symbol、stats
11
11
  - **模型自动下载**:首次启动自动从 HuggingFace 下载 ONNX 模型(默认 all-MiniLM-L6-v2,90MB,384维)
12
12
  - **跨平台支持**:Windows + Linux,自动适配路径和信号处理
13
13
  - **记忆生命周期**:Active → Infrequent → Archived → Permanent(含衰减、强化、SM-2 间隔复习)
@@ -140,7 +140,7 @@ curl http://127.0.0.1:7680/
140
140
  { "type": "summary" }
141
141
  ```
142
142
 
143
- 到这里你已经跑通了完整闭环。8 个工具的完整说明见下文「工具一览」。
143
+ 到这里你已经跑通了完整闭环。9 个工具的完整说明见下文「工具一览」。
144
144
 
145
145
  > **常驻保活**:HTTP server 自身不会自我守护。Windows 推荐 `nssm install MMPL node D:\path\to\dist\index.js`,Linux/macOS 推荐 `systemd` 或 `pm2 start dist/index.js --name mmpl`。
146
146
 
@@ -231,6 +231,7 @@ v2.5.1 之前默认走 stdio,每个 client 各 spawn 一个进程。该模式
231
231
  |------|------|
232
232
  | `memory_write` | 写入记忆(encode 编码 + capture 捕获) |
233
233
  | `memory_search` | 检索记忆(quick/deep/hybrid/keyword + drill 钻取) |
234
+ | `memory_list` | 列出记忆(分页 + 状态/类型/强度/关键词过滤 + 排序) |
234
235
  | `memory_manage` | 管理生命周期(forget/reinforce/consolidate/reclaim/reembed) |
235
236
  | `memory_evolve` | 自动进化(extract 提取 + persona 画像更新) |
236
237
  | `memory_compress` | 压缩记忆(compress/decompress/offload) |
package/dist/index.js CHANGED
@@ -943,6 +943,40 @@ var Storage = class _Storage {
943
943
  `);
944
944
  return rows.map((r) => this.rowToMemory(r));
945
945
  }
946
+ /**
947
+ * 分页列出记忆,支持按 status / type / permanent / 强度区间 / 关键词过滤,
948
+ * 支持白名单字段排序。返回 { items, total } —— total 是过滤后总条数
949
+ * (不受分页影响),用于客户端计算 has_more / 总页数。
950
+ *
951
+ * 与 getActiveMemories/getMemoriesByStatus 的区别:本方法做确定性分页浏览,
952
+ * 不依赖语义检索,专供 memory_list 工具浏览全库使用。
953
+ */
954
+ async listMemories(opts) {
955
+ const conditions = [];
956
+ if (opts.status) conditions.push(`status = '${this.escape(opts.status)}'`);
957
+ if (opts.type) conditions.push(`type = '${this.escape(opts.type)}'`);
958
+ if (opts.permanent !== void 0) conditions.push(`permanent = ${opts.permanent ? "true" : "false"}`);
959
+ if (opts.minStrength !== void 0) conditions.push(`strength >= ${Number(opts.minStrength)}`);
960
+ if (opts.maxStrength !== void 0) conditions.push(`strength <= ${Number(opts.maxStrength)}`);
961
+ if (opts.q) conditions.push(`content LIKE '%${this.escape(opts.q)}%'`);
962
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
963
+ const validOrderBy = /* @__PURE__ */ new Set([
964
+ "created_at",
965
+ "strength",
966
+ "importance",
967
+ "last_accessed",
968
+ "access_count"
969
+ ]);
970
+ const orderBy = validOrderBy.has(opts.orderBy ?? "") ? opts.orderBy : "created_at";
971
+ const order = opts.order === "asc" ? "ASC" : "DESC";
972
+ const limit = Math.min(Math.max(opts.limit ?? 20, 1), 500);
973
+ const offset = Math.max(opts.offset ?? 0, 0);
974
+ const total = this.N((await this.get(`SELECT COUNT(*) as c FROM memories ${where}`))?.c) ?? 0;
975
+ const rows = await this.all(
976
+ `SELECT * FROM memories ${where} ORDER BY ${orderBy} ${order} LIMIT ${limit} OFFSET ${offset}`
977
+ );
978
+ return { items: rows.map((r) => this.rowToMemory(r)), total };
979
+ }
946
980
  async updateMemoryBatch(ids, updates) {
947
981
  for (const id of ids) {
948
982
  await this.updateMemory(id, updates);
@@ -21662,6 +21696,111 @@ async function handleMetrics(deps) {
21662
21696
  return { content: [{ type: "text", text: JSON.stringify({ metrics, report }, null, 2) }] };
21663
21697
  }
21664
21698
 
21699
+ // src/tools/memory-list.ts
21700
+ var TRUNC_LIGHT = 100;
21701
+ function serializeLight(m, fullContent) {
21702
+ const item = {
21703
+ memory_id: m.id,
21704
+ type: m.type,
21705
+ status: m.status,
21706
+ strength: Number(m.strength?.toFixed(3)),
21707
+ permanent: m.permanent
21708
+ };
21709
+ if (fullContent || m.content.length <= TRUNC_LIGHT) {
21710
+ item.content = m.content;
21711
+ item.content_length = m.content.length;
21712
+ } else {
21713
+ item.content = m.content.slice(0, TRUNC_LIGHT) + "...";
21714
+ item.content_length = m.content.length;
21715
+ item.content_truncated = true;
21716
+ }
21717
+ item.created_at_iso = m.created_at ? new Date(m.created_at).toISOString() : null;
21718
+ return item;
21719
+ }
21720
+ function serializeDetail(m, fullContent) {
21721
+ const TRUNC_DETAIL = 300;
21722
+ const item = {
21723
+ memory_id: m.id,
21724
+ type: m.type,
21725
+ status: m.status,
21726
+ strength: Number(m.strength?.toFixed(3)),
21727
+ importance: Number(m.importance?.toFixed(3)),
21728
+ permanent: m.permanent,
21729
+ tags: m.tags,
21730
+ access_count: m.access_count,
21731
+ created_at: m.created_at,
21732
+ created_at_iso: m.created_at ? new Date(m.created_at).toISOString() : null,
21733
+ last_accessed: m.last_accessed,
21734
+ last_accessed_iso: m.last_accessed ? new Date(m.last_accessed).toISOString() : null,
21735
+ anchor_id: m.anchor_id,
21736
+ compressed: m.compressed
21737
+ };
21738
+ if (fullContent || m.content.length <= TRUNC_DETAIL) {
21739
+ item.content = m.content;
21740
+ item.content_length = m.content.length;
21741
+ } else {
21742
+ item.content = m.content.slice(0, TRUNC_DETAIL) + "...";
21743
+ item.content_length = m.content.length;
21744
+ item.content_truncated = true;
21745
+ }
21746
+ return item;
21747
+ }
21748
+ function registerMemoryList(server, deps) {
21749
+ server.tool(
21750
+ "memory_list",
21751
+ "\u5217\u51FA\u8BB0\u5FC6\uFF1A\u5206\u9875\u67E5\u8BE2\u3001\u6309\u7C7B\u578B/\u72B6\u6001/\u5F3A\u5EA6/\u5173\u952E\u8BCD\u8FC7\u6EE4\u3001\u652F\u6301\u6392\u5E8F",
21752
+ {
21753
+ status: external_exports.enum(["active", "infrequent", "archived"]).optional().describe("\u72B6\u6001\u8FC7\u6EE4\uFF1B\u4E0D\u4F20=\u5168\u90E8"),
21754
+ type: external_exports.enum(["fact", "preference", "instruction", "event", "skill"]).optional().describe("\u7C7B\u578B\u8FC7\u6EE4"),
21755
+ permanent: external_exports.boolean().optional().describe("\u53EA\u770B\u6C38\u4E45\u8BB0\u5FC6\uFF08true\uFF09/\u53EA\u770B\u975E\u6C38\u4E45\uFF08false\uFF09\uFF1B\u4E0D\u4F20=\u5168\u90E8"),
21756
+ min_strength: external_exports.number().optional().describe("\u6700\u4F4E\u5F3A\u5EA6\u8FC7\u6EE4\uFF080-1\uFF09"),
21757
+ max_strength: external_exports.number().optional().describe("\u6700\u9AD8\u5F3A\u5EA6\u8FC7\u6EE4\uFF080-1\uFF09"),
21758
+ q: external_exports.string().optional().describe("\u5173\u952E\u8BCD\u6A21\u7CCA\u5339\u914D\uFF08LIKE\uFF09\uFF0C\u53EF\u9009"),
21759
+ order_by: external_exports.enum(["created_at", "strength", "importance", "last_accessed", "access_count"]).optional().describe("\u6392\u5E8F\u5B57\u6BB5\uFF0C\u9ED8\u8BA4 created_at"),
21760
+ order: external_exports.enum(["asc", "desc"]).optional().describe("\u6392\u5E8F\u65B9\u5411\uFF0C\u9ED8\u8BA4 desc"),
21761
+ limit: external_exports.number().optional().describe("\u6BCF\u9875\u6570\u91CF\uFF0C\u9ED8\u8BA4 20\uFF0C\u786C\u4E0A\u9650 500"),
21762
+ offset: external_exports.number().optional().describe("\u504F\u79FB\u91CF\uFF0C\u9ED8\u8BA4 0"),
21763
+ detail: external_exports.boolean().optional().describe("\u8FD4\u56DE\u5168\u5B57\u6BB5\uFF08importance/tags/access_count \u7B49\uFF09\uFF1B\u9ED8\u8BA4 false \u53EA\u8FD4\u56DE\u6838\u5FC3\u5B57\u6BB5"),
21764
+ full_content: external_exports.boolean().optional().describe("\u8FD4\u56DE\u5B8C\u6574 content\uFF08\u9ED8\u8BA4 light \u622A\u65AD\u5230 100 \u5B57\u7B26\uFF0Cdetail \u622A\u65AD\u5230 300\uFF09")
21765
+ },
21766
+ async (params) => {
21767
+ const { storage } = deps;
21768
+ const limit = Math.min(Math.max(params.limit ?? 20, 1), 500);
21769
+ const offset = Math.max(params.offset ?? 0, 0);
21770
+ const fullContent = params.full_content ?? false;
21771
+ const detail = params.detail ?? false;
21772
+ const { items, total } = await storage.listMemories({
21773
+ status: params.status,
21774
+ type: params.type,
21775
+ permanent: params.permanent,
21776
+ minStrength: params.min_strength,
21777
+ maxStrength: params.max_strength,
21778
+ q: params.q,
21779
+ orderBy: params.order_by,
21780
+ order: params.order,
21781
+ limit,
21782
+ offset
21783
+ });
21784
+ const serialized = items.map(
21785
+ (m) => detail ? serializeDetail(m, fullContent) : serializeLight(m, fullContent)
21786
+ );
21787
+ return {
21788
+ content: [{
21789
+ type: "text",
21790
+ text: JSON.stringify({
21791
+ items: serialized,
21792
+ total,
21793
+ limit,
21794
+ offset,
21795
+ mode: detail ? "detail" : "light",
21796
+ has_more: offset + items.length < total
21797
+ }, null, 2)
21798
+ }]
21799
+ };
21800
+ }
21801
+ );
21802
+ }
21803
+
21665
21804
  // src/index.ts
21666
21805
  BigInt.prototype.toJSON = function() {
21667
21806
  return Number(this);
@@ -21748,7 +21887,7 @@ async function main() {
21748
21887
  const createMcpServer = () => {
21749
21888
  const server = new McpServer({
21750
21889
  name: "MMPL",
21751
- version: "2.7.1"
21890
+ version: "2.8.0"
21752
21891
  });
21753
21892
  registerMemoryWrite(server, deps);
21754
21893
  registerMemorySearch(server, deps);
@@ -21758,6 +21897,7 @@ async function main() {
21758
21897
  registerMemoryPalace(server, deps);
21759
21898
  registerMemorySymbol(server, deps);
21760
21899
  registerMemoryStats(server, deps);
21900
+ registerMemoryList(server, deps);
21761
21901
  return server;
21762
21902
  };
21763
21903
  const httpHost = process.env.MCP_HTTP_HOST ?? "127.0.0.1";
@@ -21771,7 +21911,7 @@ async function main() {
21771
21911
  void embeddingService.ready().catch((err) => {
21772
21912
  console.error("[startup] embedding service init failed:", err);
21773
21913
  });
21774
- console.error(`MMPL (Memory Palace) MCP Server v2.7.1 listening on http://${httpHost}:${httpPort}${httpPath}`);
21914
+ console.error(`MMPL (Memory Palace) MCP Server v2.8.0 listening on http://${httpHost}:${httpPort}${httpPath}`);
21775
21915
  const gracefulShutdown = async (signal) => {
21776
21916
  console.error(`
21777
21917
  [shutdown] Received ${signal}, gracefully shutting down...`);