oh-memos-mcp 3.1.2 → 3.1.4

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.
@@ -69,8 +69,19 @@ export declare function renderVerbatimSections(verbatim: string | null, siblings
69
69
  /**
70
70
  * 在候选集里找与 target 同源的其他碎片。
71
71
  *
72
- * 排除 target 自身,也排除没有原文的节点。顺序按候选集原顺序,由调用方决定
73
- * 候选集怎么排(后端返回顺序通常已按 created_at)。
72
+ * 排除三类:target 自身、没有原文的节点、**scheduler 管理的短期副本**。
73
+ *
74
+ * ## 为什么必须滤层级
75
+ *
76
+ * 后端对每条抽取结果写两个节点:一个 `WorkingMemory` 短期副本 + 一个
77
+ * `LongTermMemory`(或 `UserMemory`)持久节点,`key` 与 `created_at` 逐字相同。
78
+ * 不滤的话同源列表里每个 key 成对出现 —— 实测一条被切成 6 段的记忆列出了 12 条同源。
79
+ *
80
+ * 这不是新决策,是 3.1.0 已经做过的决定(`memory-tier.ts`)。`search` 与
81
+ * `list_v2` 早已在滤,`findSiblings` 是 3.1.2 新增的路径 —— 又漏了一处。
82
+ * **新写的检索路径必须继承既有的分层过滤决定**,这是同一形态的第五次。
83
+ *
84
+ * 顺序按候选集原顺序,由调用方决定候选集怎么排(后端通常已按 updated_at 倒序)。
74
85
  */
75
- export declare function findSiblings(target: MemoryNode | undefined | null, candidates: readonly (MemoryNode | undefined | null)[], limit?: number): SiblingRef[];
86
+ export declare function findSiblings(target: MemoryNode | undefined | null, candidates: readonly (MemoryNode | undefined | null)[], limit?: number, env?: NodeJS.ProcessEnv): SiblingRef[];
76
87
  //# sourceMappingURL=verbatim-source.d.ts.map
@@ -34,6 +34,40 @@
34
34
  *
35
35
  * 设计讨论见 docs/design/memory-retrieval-optimization.md。
36
36
  */
37
+ import { isEphemeralTier, showsEphemeralTier } from "./memory-tier.js";
38
+ /**
39
+ * 取一条 source 的 content —— **同一个字段有两种线上形态**。
40
+ *
41
+ * 实测(3.1.2 上线后才暴露):
42
+ * `GET /memories/{cube}/{id}`(单条)→ `sources[0]` 是**对象** `{type,role,content}`
43
+ * `GET /memories`(列表) → `sources[0]` 是**JSON 字符串** `'{"type":...}'`
44
+ *
45
+ * 初版只处理对象那种,于是同源查找在列表数据上永远匹配不到 —— 原文能返回,
46
+ * 但「同源碎片」区块从不出现。单测用的 fixture 也只造了对象形态,所以测试全绿。
47
+ *
48
+ * 解析失败按无 content 处理,不抛:这是展示层,坏数据不该让 `memos_get` 失败。
49
+ */
50
+ function contentOf(entry) {
51
+ if (typeof entry === "string") {
52
+ // 列表端点的形态。不是 JSON 就当没有 —— 不去猜它是不是裸原文,
53
+ // 否则任何字符串都会被当成原文,把无关记忆归成同源。
54
+ if (!entry.trimStart().startsWith("{"))
55
+ return null;
56
+ try {
57
+ const parsed = JSON.parse(entry);
58
+ const c = parsed?.content;
59
+ return typeof c === "string" && c.trim() !== "" ? c : null;
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ }
65
+ if (entry && typeof entry === "object") {
66
+ const c = entry.content;
67
+ return typeof c === "string" && c.trim() !== "" ? c : null;
68
+ }
69
+ return null;
70
+ }
37
71
  /**
38
72
  * 取出逐字原文。
39
73
  *
@@ -48,11 +82,7 @@ export function verbatimOf(node) {
48
82
  ?.sources;
49
83
  if (!Array.isArray(sources) || sources.length === 0)
50
84
  return null;
51
- const first = sources[0];
52
- const content = first?.content;
53
- if (typeof content !== "string" || content.trim() === "")
54
- return null;
55
- return content;
85
+ return contentOf(sources[0]);
56
86
  }
57
87
  /**
58
88
  * 同源指纹。同一次写入切出的碎片共享它。
@@ -90,13 +120,25 @@ export function renderVerbatimSections(verbatim, siblings, summary) {
90
120
  /**
91
121
  * 在候选集里找与 target 同源的其他碎片。
92
122
  *
93
- * 排除 target 自身,也排除没有原文的节点。顺序按候选集原顺序,由调用方决定
94
- * 候选集怎么排(后端返回顺序通常已按 created_at)。
123
+ * 排除三类:target 自身、没有原文的节点、**scheduler 管理的短期副本**。
124
+ *
125
+ * ## 为什么必须滤层级
126
+ *
127
+ * 后端对每条抽取结果写两个节点:一个 `WorkingMemory` 短期副本 + 一个
128
+ * `LongTermMemory`(或 `UserMemory`)持久节点,`key` 与 `created_at` 逐字相同。
129
+ * 不滤的话同源列表里每个 key 成对出现 —— 实测一条被切成 6 段的记忆列出了 12 条同源。
130
+ *
131
+ * 这不是新决策,是 3.1.0 已经做过的决定(`memory-tier.ts`)。`search` 与
132
+ * `list_v2` 早已在滤,`findSiblings` 是 3.1.2 新增的路径 —— 又漏了一处。
133
+ * **新写的检索路径必须继承既有的分层过滤决定**,这是同一形态的第五次。
134
+ *
135
+ * 顺序按候选集原顺序,由调用方决定候选集怎么排(后端通常已按 updated_at 倒序)。
95
136
  */
96
- export function findSiblings(target, candidates, limit = 12) {
137
+ export function findSiblings(target, candidates, limit = 12, env = process.env) {
97
138
  const fp = sourceFingerprint(target);
98
139
  if (fp === null)
99
140
  return [];
141
+ const showEphemeral = showsEphemeralTier(env);
100
142
  const targetId = String(target?.id ?? "");
101
143
  const out = [];
102
144
  const seen = new Set();
@@ -108,6 +150,9 @@ export function findSiblings(target, candidates, limit = 12) {
108
150
  continue;
109
151
  if (sourceFingerprint(node) !== fp)
110
152
  continue;
153
+ // 逃生开关打开时不滤 —— 与 filterEphemeralTier 的语义保持一致。
154
+ if (!showEphemeral && isEphemeralTier(node?.metadata))
155
+ continue;
111
156
  seen.add(id);
112
157
  const meta = (node?.metadata ?? {});
113
158
  const key = String(meta.key ?? "").trim();
package/package.json CHANGED
@@ -1,68 +1,68 @@
1
- {
2
- "name": "oh-memos-mcp",
3
- "version": "3.1.2",
4
- "description": "MCP Server for oh-memos — Intelligent Memory Management (Node.js, no Python required)",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "bin": {
8
- "oh-memos-mcp": "dist/index.js"
9
- },
10
- "files": [
11
- "dist/**/*.js",
12
- "dist/**/*.d.ts",
13
- "README.md",
14
- "CHANGELOG.md",
15
- ".env.example"
16
- ],
17
- "scripts": {
18
- "build": "tsc",
19
- "dev": "tsx src/index.ts",
20
- "test": "vitest run",
21
- "test:protocol": "npm run build && node scripts/protocol-smoke.mjs && node scripts/protocol-v2-smoke.mjs",
22
- "test:protocol:v2": "npm run build && node scripts/protocol-v2-smoke.mjs",
23
- "test:lite-smoke": "npm run build && node scripts/lite-smoke-test.mjs",
24
- "test:spread-smoke": "npm run build && node scripts/spread-smoke-test.mjs",
25
- "test:host-env-smoke": "npm run build && node scripts/host-env-smoke.mjs",
26
- "schema:semantic": "npm run build && node scripts/schema-semantic-snapshot.mjs --check",
27
- "schema:semantic:freeze": "npm run build && node scripts/schema-semantic-snapshot.mjs --write",
28
- "test:pack": "node scripts/pack-contract.mjs",
29
- "test:watch": "vitest",
30
- "schema:budget": "npm run build && node scripts/schema-budget.mjs --check",
31
- "schema:budget:freeze": "npm run build && node scripts/schema-budget.mjs --write",
32
- "prepublishOnly": "npm run build"
33
- },
34
- "engines": {
35
- "node": ">=20.0.0"
36
- },
37
- "keywords": [
38
- "mcp",
39
- "memory",
40
- "memos",
41
- "claude",
42
- "ai",
43
- "knowledge-graph"
44
- ],
45
- "author": "xigou",
46
- "license": "MIT",
47
- "repository": {
48
- "type": "git",
49
- "url": "https://github.com/lsg1103275794/oh-memos.git",
50
- "directory": "mcp-server-node"
51
- },
52
- "bugs": {
53
- "url": "https://github.com/lsg1103275794/oh-memos/issues"
54
- },
55
- "homepage": "https://github.com/lsg1103275794/oh-memos/tree/main/mcp-server-node",
56
- "dependencies": {
57
- "@modelcontextprotocol/server": "^2.0.0",
58
- "dotenv": "^16.0.0",
59
- "zod": "^4.2.0"
60
- },
61
- "devDependencies": {
62
- "@modelcontextprotocol/client": "^2.0.0",
63
- "@types/node": "^22.0.0",
64
- "tsx": "^4.23.12",
65
- "typescript": "^5.5.0",
66
- "vitest": "3.2.7"
67
- }
68
- }
1
+ {
2
+ "name": "oh-memos-mcp",
3
+ "version": "3.1.4",
4
+ "description": "MCP Server for oh-memos — Intelligent Memory Management (Node.js, no Python required)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "oh-memos-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/**/*.js",
12
+ "dist/**/*.d.ts",
13
+ "README.md",
14
+ "CHANGELOG.md",
15
+ ".env.example"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "dev": "tsx src/index.ts",
20
+ "test": "vitest run",
21
+ "test:protocol": "npm run build && node scripts/protocol-smoke.mjs && node scripts/protocol-v2-smoke.mjs",
22
+ "test:protocol:v2": "npm run build && node scripts/protocol-v2-smoke.mjs",
23
+ "test:lite-smoke": "npm run build && node scripts/lite-smoke-test.mjs",
24
+ "test:spread-smoke": "npm run build && node scripts/spread-smoke-test.mjs",
25
+ "test:host-env-smoke": "npm run build && node scripts/host-env-smoke.mjs",
26
+ "schema:semantic": "npm run build && node scripts/schema-semantic-snapshot.mjs --check",
27
+ "schema:semantic:freeze": "npm run build && node scripts/schema-semantic-snapshot.mjs --write",
28
+ "test:pack": "node scripts/pack-contract.mjs",
29
+ "test:watch": "vitest",
30
+ "schema:budget": "npm run build && node scripts/schema-budget.mjs --check",
31
+ "schema:budget:freeze": "npm run build && node scripts/schema-budget.mjs --write",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+ "engines": {
35
+ "node": ">=20.0.0"
36
+ },
37
+ "keywords": [
38
+ "mcp",
39
+ "memory",
40
+ "memos",
41
+ "claude",
42
+ "ai",
43
+ "knowledge-graph"
44
+ ],
45
+ "author": "xigou",
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/lsg1103275794/oh-memos.git",
50
+ "directory": "mcp-server-node"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/lsg1103275794/oh-memos/issues"
54
+ },
55
+ "homepage": "https://github.com/lsg1103275794/oh-memos/tree/main/mcp-server-node",
56
+ "dependencies": {
57
+ "@modelcontextprotocol/server": "^2.0.0",
58
+ "dotenv": "^16.0.0",
59
+ "zod": "^4.2.0"
60
+ },
61
+ "devDependencies": {
62
+ "@modelcontextprotocol/client": "^2.0.0",
63
+ "@types/node": "^22.0.0",
64
+ "tsx": "^4.23.12",
65
+ "typescript": "^5.5.0",
66
+ "vitest": "3.2.7"
67
+ }
68
+ }