@modusensus/dsh-mneme 0.11.0 → 0.12.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 +10 -6
- package/docs/CONFIGURATION.md +2 -0
- package/lib/api.js +172 -6
- package/lib/client.js +619 -18
- package/lib/config.js +32 -1
- package/lib/document.js +9 -3
- package/lib/dream/clustering.js +6 -1
- package/lib/index.js +40 -4
- package/lib/inject.js +4 -2
- package/lib/lang.js +28 -0
- package/lib/profile-facts.js +356 -0
- package/lib/service.js +109 -4
- package/lib/settings.js +5 -0
- package/lib/store.js +483 -34
- package/lib/tools.js +140 -23
- package/lib/user-settings.js +63 -7
- package/lib/wiki/queries.js +149 -9
- package/package.json +1 -1
- package/scripts/sync-test-badge.mjs +1 -1
- package/scripts/test-count-sync.mjs +9 -5
- package/src/api.js +172 -6
- package/src/config.js +32 -1
- package/src/document.js +9 -3
- package/src/dream/clustering.js +6 -1
- package/src/index.js +40 -4
- package/src/inject.js +4 -2
- package/src/lang.js +28 -0
- package/src/profile-facts.js +356 -0
- package/src/service.js +109 -4
- package/src/settings.js +5 -0
- package/src/store.js +483 -34
- package/src/tools.js +140 -23
- package/src/user-settings.js +63 -7
- package/src/wiki/queries.js +149 -9
- package/test/api.test.js +3 -1
- package/test/client.test.js +147 -2
- package/test/config.test.js +79 -0
- package/test/document.test.js +32 -0
- package/test/embedding-cache-f32.test.js +276 -0
- package/test/inject-pin-explicit.test.js +300 -0
- package/test/inject-pin-vector-crowd.test.js +225 -0
- package/test/persona-settings.test.js +31 -0
- package/test/profile-facts.test.js +492 -0
- package/test/session-gate.test.js +1 -1
- package/test/test-count-sync.test.js +14 -3
- package/test/tool-state-ignore.test.js +65 -0
- package/test/tools-dto-schema-matrix.test.js +23 -2
- package/test/tools.test.js +144 -7
- package/test/wiki-api.test.js +121 -1
- package/test/wiki-queries.test.js +84 -1
- package/test/wiki-tools.test.js +131 -76
package/test/wiki-tools.test.js
CHANGED
|
@@ -1,41 +1,45 @@
|
|
|
1
|
-
//
|
|
2
|
-
// 1. 默认配置下三个 choro_* 工具根本不注册(模型连名字都看不到,与 memory_organize
|
|
3
|
-
// 同款反向语义)——tools.test.js 的 11 工具清单锁管默认面,这里管开启面;
|
|
4
|
-
// 2. chorographyEnabled 开 + queries 装配 → 三工具在场;
|
|
5
|
-
// 3. 开了开关但 queries 缺装配(null)→ 同样不在场(「开了没装配」与「没开」
|
|
6
|
-
// 对模型都不可见,可区分性由面板路由的 409/503 负责);
|
|
7
|
-
// 4. execute 的错误形状:多仓歧义带回可选清单、未知 slug 报 page-not-found。
|
|
1
|
+
// #579:从宿主的工具执行入口验证,绕过 schema 校验直调 execute 会漏掉原故障。
|
|
8
2
|
import test from "node:test";
|
|
9
3
|
import assert from "node:assert/strict";
|
|
10
|
-
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
|
|
4
|
+
import { mkdtempSync, rmSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
|
|
11
5
|
import { tmpdir } from "node:os";
|
|
12
6
|
import { join } from "node:path";
|
|
13
7
|
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
9
|
+
import SystemPrompt from "@deepseek-ai/dsh-system-prompt";
|
|
10
|
+
import ToolRuntime from "@deepseek-ai/dsh-tools";
|
|
14
11
|
import { createStore } from "../src/store.js";
|
|
15
12
|
import { createService } from "../src/service.js";
|
|
16
13
|
import { createTools } from "../src/tools.js";
|
|
17
14
|
import { createChoroQueries } from "../src/wiki/queries.js";
|
|
18
15
|
import { runChoroT0 } from "../src/wiki/t0.js";
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
const choroArgs = {
|
|
18
|
+
choro_module_map: {},
|
|
19
|
+
choro_read_page: { slug: "src" },
|
|
20
|
+
choro_search: { query: "b.js" }
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
async function setup(t, config = {}, withChoro = false) {
|
|
21
24
|
const store = createStore(":memory:");
|
|
25
|
+
t.after(() => store.close());
|
|
26
|
+
const ctx = new Context();
|
|
27
|
+
const prompt = await ctx.plugin(SystemPrompt);
|
|
28
|
+
const runtime = await ctx.plugin(ToolRuntime);
|
|
29
|
+
t.after(() => { runtime.dispose(); prompt.dispose(); });
|
|
30
|
+
const tools = ctx.get("tools");
|
|
22
31
|
const service = createService({ store, mirror: null, config: {} });
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return () => {};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
createTools(ctx, service, config, null, null, choro);
|
|
33
|
-
return { registered, store };
|
|
32
|
+
createTools({ tools }, service, config, null, null, withChoro ? createChoroQueries({ store }) : null);
|
|
33
|
+
const call = (name, args = {}) => tools.execute({
|
|
34
|
+
name, callId: name, arguments: args, signal: new AbortController().signal
|
|
35
|
+
});
|
|
36
|
+
return { tools, store, call };
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
function makeRepo(store) {
|
|
39
|
+
function makeRepo(t, store, name = "toolfix") {
|
|
37
40
|
const root = mkdtempSync(join(tmpdir(), "choro-tools-"));
|
|
38
|
-
|
|
41
|
+
t.after(() => rmTempBestEffort(root));
|
|
42
|
+
writeFileSync(join(root, "package.json"), JSON.stringify({ name, scripts: { test: "node --test" } }, null, 2) + "\n", "utf8");
|
|
39
43
|
mkdirSync(join(root, "src"), { recursive: true });
|
|
40
44
|
writeFileSync(join(root, "src", "a.js"), 'import { b } from "./b.js";\nexport const a = () => b();\n', "utf8");
|
|
41
45
|
writeFileSync(join(root, "src", "b.js"), "export const b = () => 1;\n", "utf8");
|
|
@@ -55,67 +59,118 @@ function rmTempBestEffort(dir) {
|
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
function expectToolError(result, pattern) {
|
|
63
|
+
const message = result.content[0]?.text ?? "";
|
|
64
|
+
assert.equal(result.isError, true, message);
|
|
65
|
+
assert.match(message, pattern);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
test("默认配置下三个 choro 工具不注册(模型看不到就不会调)", async (t) => {
|
|
69
|
+
const { tools } = await setup(t, {}, true);
|
|
70
|
+
for (const name of Object.keys(choroArgs)) {
|
|
71
|
+
assert.ok(!tools.get(name), `${name} 默认缺席`);
|
|
63
72
|
}
|
|
64
73
|
});
|
|
65
74
|
|
|
66
|
-
test("
|
|
67
|
-
const {
|
|
68
|
-
const
|
|
69
|
-
assert.ok(!names.includes("choro_search"));
|
|
75
|
+
test("开关开但 queries 缺装配 → 同样缺席(可区分性归路由层)", async (t) => {
|
|
76
|
+
const { tools } = await setup(t, { chorographyEnabled: true });
|
|
77
|
+
for (const name of Object.keys(choroArgs)) assert.ok(!tools.get(name));
|
|
70
78
|
});
|
|
71
79
|
|
|
72
|
-
test("
|
|
73
|
-
const store =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
rmTempBestEffort(root);
|
|
80
|
+
test("开启后三个只读工具的成功结果通过宿主校验", async (t) => {
|
|
81
|
+
const { call, store } = await setup(t, { chorographyEnabled: true }, true);
|
|
82
|
+
makeRepo(t, store);
|
|
83
|
+
const map = await call("choro_module_map");
|
|
84
|
+
assert.equal(map.isError, false);
|
|
85
|
+
assert.equal(map.value.repo, "toolfix");
|
|
86
|
+
assert.ok(map.value.modules.length >= 2);
|
|
87
|
+
const page = await call("choro_read_page", { slug: "src" });
|
|
88
|
+
assert.equal(page.isError, false);
|
|
89
|
+
assert.equal(page.value.slug, "src");
|
|
90
|
+
assert.match(page.value.content, /## 文件清单/);
|
|
91
|
+
const search = await call("choro_search", { query: "b.js" });
|
|
92
|
+
assert.equal(search.isError, false);
|
|
93
|
+
assert.ok(search.value.hits.length >= 1);
|
|
94
|
+
assert.equal(search.value.unreadablePages, undefined);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("未登记或找不到指定仓库时,三个工具都返回可操作的错误", async (t) => {
|
|
98
|
+
const { call, store } = await setup(t, { chorographyEnabled: true }, true);
|
|
99
|
+
for (const [name, args] of Object.entries(choroArgs)) {
|
|
100
|
+
expectToolError(await call(name, args), /No repository is registered.*dsh-mneme-choro/);
|
|
101
|
+
}
|
|
102
|
+
makeRepo(t, store);
|
|
103
|
+
const [{ id }] = createChoroQueries({ store }).listRepos();
|
|
104
|
+
const unknownId = String(id + 1);
|
|
105
|
+
for (const [name, args] of Object.entries(choroArgs)) {
|
|
106
|
+
expectToolError(await call(name, { ...args, repo: unknownId }), /Repository "\d+" is not registered/);
|
|
100
107
|
}
|
|
101
108
|
});
|
|
102
109
|
|
|
103
|
-
test("
|
|
104
|
-
const store =
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
assert.equal(result.error, "repo-ambiguous");
|
|
113
|
-
assert.equal(result.repos.length, 2);
|
|
114
|
-
const named = await map.execute({ repo: "toolfix" });
|
|
115
|
-
assert.ok(named.modules, "显式给名后解析成功(fixture 同名仓按 id 序取先者)");
|
|
116
|
-
} finally {
|
|
117
|
-
store.close();
|
|
118
|
-
rmTempBestEffort(root1);
|
|
119
|
-
rmTempBestEffort(root2);
|
|
110
|
+
test("多仓歧义提示可选 id,按 id 重试可读到对应仓库", async (t) => {
|
|
111
|
+
const { call, store } = await setup(t, { chorographyEnabled: true }, true);
|
|
112
|
+
makeRepo(t, store);
|
|
113
|
+
makeRepo(t, store, "other");
|
|
114
|
+
const repos = createChoroQueries({ store }).listRepos();
|
|
115
|
+
for (const [name, args] of Object.entries(choroArgs)) {
|
|
116
|
+
const result = await call(name, args);
|
|
117
|
+
expectToolError(result, /More than one repository is registered/);
|
|
118
|
+
for (const { id, name } of repos) assert.ok(result.content[0].text.includes(`${id} (${name})`));
|
|
120
119
|
}
|
|
120
|
+
const selected = await call("choro_module_map", { repo: String(repos[1].id) });
|
|
121
|
+
assert.equal(selected.isError, false);
|
|
122
|
+
assert.equal(selected.value.repo, "other");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("读页错误区分未知 slug、缺失文件和不可读文件", async (t) => {
|
|
126
|
+
const { call, store } = await setup(t, { chorographyEnabled: true }, true);
|
|
127
|
+
const root = makeRepo(t, store);
|
|
128
|
+
expectToolError(await call("choro_read_page", { slug: "nope" }), /Page "nope" was not found.*choro_module_map/);
|
|
129
|
+
const path = join(root, ".chorography", "modules", "src.md");
|
|
130
|
+
unlinkSync(path);
|
|
131
|
+
expectToolError(await call("choro_read_page", { slug: "src" }), /Page "src" is missing from disk.*dsh-mneme-choro/);
|
|
132
|
+
mkdirSync(path);
|
|
133
|
+
const unreadable = await call("choro_read_page", { slug: "src" });
|
|
134
|
+
expectToolError(unreadable, /Page "src" could not be read.*permissions/);
|
|
135
|
+
assert.ok(!unreadable.content[0].text.includes(root), "工具错误不暴露仓库绝对路径");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("共享库中本机不可达的仓库仍有模块图,读页和检索报告文件访问失败", async (t) => {
|
|
139
|
+
const { call, store } = await setup(t, { chorographyEnabled: true }, true);
|
|
140
|
+
const root = makeRepo(t, store);
|
|
141
|
+
const [{ id }] = createChoroQueries({ store }).listRepos();
|
|
142
|
+
store.db.prepare("UPDATE choro_repos SET root_path = ? WHERE id = ?")
|
|
143
|
+
.run(join(root, "unavailable-checkout"), id);
|
|
144
|
+
const repo = String(id);
|
|
145
|
+
|
|
146
|
+
const map = await call("choro_module_map", { repo });
|
|
147
|
+
assert.equal(map.isError, false);
|
|
148
|
+
assert.equal(map.value.repo, "toolfix");
|
|
149
|
+
assert.ok(map.value.modules.some(({ slug }) => slug === "src"));
|
|
150
|
+
expectToolError(await call("choro_read_page", { repo, slug: "src" }), /missing from disk.*local checkout/);
|
|
151
|
+
expectToolError(await call("choro_search", { repo, query: "b.js" }), /Search was incomplete.*src.*local checkout/);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("检索报告不可读的页,保留已读到的命中", async (t) => {
|
|
155
|
+
const { call, store } = await setup(t, { chorographyEnabled: true }, true);
|
|
156
|
+
const root = makeRepo(t, store);
|
|
157
|
+
const rootPage = join(root, ".chorography", "modules", "root.md");
|
|
158
|
+
unlinkSync(rootPage);
|
|
159
|
+
const partial = await call("choro_search", { query: "b.js" });
|
|
160
|
+
assert.equal(partial.isError, false);
|
|
161
|
+
assert.ok(partial.value.hits.length >= 1);
|
|
162
|
+
assert.deepEqual(partial.value.unreadablePages, ["root"]);
|
|
163
|
+
assert.match(partial.content[0].text, /Search was incomplete.*root/);
|
|
164
|
+
|
|
165
|
+
const empty = await call("choro_search", { query: "not present", module: "src" });
|
|
166
|
+
assert.equal(empty.isError, false);
|
|
167
|
+
assert.deepEqual(empty.value, { hits: [] });
|
|
168
|
+
assert.equal(empty.content[0].text, "0 hits.");
|
|
169
|
+
|
|
170
|
+
const incomplete = await call("choro_search", { query: "not present" });
|
|
171
|
+
expectToolError(incomplete, /Search was incomplete.*root/);
|
|
172
|
+
const srcPage = join(root, ".chorography", "modules", "src.md");
|
|
173
|
+
unlinkSync(srcPage);
|
|
174
|
+
mkdirSync(srcPage);
|
|
175
|
+
expectToolError(await call("choro_search", { query: "b.js" }), /Search was incomplete.*root.*src/);
|
|
121
176
|
});
|