@anionex/dsh-tool-search 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.
Files changed (62) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +160 -0
  4. package/README.zh.md +160 -0
  5. package/cordis.patch.yml +3 -0
  6. package/docs/benchmark-results.json +51 -0
  7. package/lib/bm25.js +83 -0
  8. package/lib/bm25.js.map +1 -0
  9. package/lib/catalog.js +53 -0
  10. package/lib/catalog.js.map +1 -0
  11. package/lib/client.js +388 -0
  12. package/lib/client.js.map +1 -0
  13. package/lib/index.js +24 -0
  14. package/lib/index.js.map +1 -0
  15. package/lib/runtime.js +286 -0
  16. package/lib/runtime.js.map +1 -0
  17. package/lib/settings.js +50 -0
  18. package/lib/settings.js.map +1 -0
  19. package/lib/shared.js +40 -0
  20. package/lib/shared.js.map +1 -0
  21. package/lib/types/bm25.d.ts +20 -0
  22. package/lib/types/bm25.d.ts.map +1 -0
  23. package/lib/types/catalog.d.ts +26 -0
  24. package/lib/types/catalog.d.ts.map +1 -0
  25. package/lib/types/client/index.d.ts +65 -0
  26. package/lib/types/client/index.d.ts.map +1 -0
  27. package/lib/types/index.d.ts +14 -0
  28. package/lib/types/index.d.ts.map +1 -0
  29. package/lib/types/runtime.d.ts +37 -0
  30. package/lib/types/runtime.d.ts.map +1 -0
  31. package/lib/types/settings.d.ts +16 -0
  32. package/lib/types/settings.d.ts.map +1 -0
  33. package/lib/types/shared.d.ts +31 -0
  34. package/lib/types/shared.d.ts.map +1 -0
  35. package/lib/types/web.d.ts +5 -0
  36. package/lib/types/web.d.ts.map +1 -0
  37. package/lib/web.js +28 -0
  38. package/lib/web.js.map +1 -0
  39. package/package.json +149 -0
  40. package/pnpm-workspace.yaml +11 -0
  41. package/scripts/benchmark.mjs +124 -0
  42. package/scripts/profile-e2e.mjs +366 -0
  43. package/scripts/validate.mjs +170 -0
  44. package/src/bm25.ts +104 -0
  45. package/src/catalog.ts +72 -0
  46. package/src/client/index.tsx +344 -0
  47. package/src/index.ts +32 -0
  48. package/src/runtime.ts +326 -0
  49. package/src/settings.ts +63 -0
  50. package/src/shared.ts +63 -0
  51. package/src/web.ts +34 -0
  52. package/src/wink-porter2-stemmer.d.ts +3 -0
  53. package/tests/bm25.spec.ts +75 -0
  54. package/tests/catalog.spec.ts +63 -0
  55. package/tests/client.spec.tsx +39 -0
  56. package/tests/execution-regression.spec.ts +200 -0
  57. package/tests/runtime.spec.ts +271 -0
  58. package/tests/settings.spec.ts +33 -0
  59. package/tsconfig.client.json +31 -0
  60. package/tsconfig.json +30 -0
  61. package/tsconfig.test.json +23 -0
  62. package/tsdown.config.mjs +37 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-09-05
4
+
5
+ - Add deterministic local BM25 discovery for deferred DSH tools.
6
+ - Add per-Agent monotonic selection with resume and fork recovery.
7
+ - Commit selections only after a successful final result and persist the same canonical recovery payload.
8
+ - Keep the real registry and approval, guard, event, and execution semantics intact.
9
+ - Keep maintained DSH control-plane tools, including `report`, visible by default and reject unsupported code/PTC presentation modes.
10
+ - Add live Web settings for Top-K, always-visible names, and never-search names.
11
+ - Add responsive container-based settings layout and compatibility with DSH through `0.1.2-rc.1`.
12
+ - Add Recall@1/Recall@5, lifecycle, pipeline, UI, package, token, and real Agent E2E acceptance.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anionex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # dsh-tool-search
2
+
3
+ Codex-style deferred tool loading for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/DeepSeek-Harness).
4
+
5
+ `dsh-tool-search` keeps the real DSH tool registry intact while reducing the model-visible tool surface. A new Agent initially sees the maintained core tool set plus `tool_search`. Calling `tool_search` runs deterministic local BM25 over deferred metadata, adds the Top-K matches to that Agent's selected set, and exposes their complete native schemas on the next model call. The model then calls each tool by its real name through the normal DSH execution pipeline.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ dsh plugin --profile web add @anionex/dsh-tool-search
11
+ ```
12
+
13
+ Use `--profile headless` or another DSH profile name when appropriate. Restart a running profile and create a new session after installation.
14
+
15
+ On affected Node.js 24+ standalone DSH launchers, a bare external Profile package can fail to resolve. If startup reports `Cannot find package '@anionex/dsh-tool-search'`, use `node --expose-internals "$(command -v dsh)" --profile <name> ...`; this is an upstream DSH loader limitation rather than a plugin resolution fallback.
16
+
17
+ Requirements:
18
+
19
+ - DSH `0.1.1-rc.1` through the current `0.1.2-rc.1` prerelease line
20
+ - Node.js `^22.19.0 || >=24.0.0`
21
+ - Native tool presentation mode
22
+
23
+ ## Behavior
24
+
25
+ The initial native schema surface contains:
26
+
27
+ - `tool_search`
28
+ - `apply_patch`
29
+ - DSH core tools maintained in `DEFAULT_CORE_TOOLS`
30
+ - exact names added to `alwaysVisible`
31
+
32
+ Version `0.1.0` treats these names as core:
33
+
34
+ ```text
35
+ apply_patch, ask_user_question, bash, create_goal, dsh_im_return_file,
36
+ exit_plan_mode, get_goal, interrupt_agent, job_kill, job_list, job_output,
37
+ list_agents, ralph, report, send_message, skill, subagent, subagent_fork,
38
+ todo_write, update_goal, workflow
39
+ ```
40
+
41
+ Every other registered tool is searchable but initially omitted. `neverSearch` hides a name from both the initial surface and the search corpus; it wins a conflict with `alwaysVisible`. `tool_search` itself cannot be blacklisted, preventing a configuration dead end.
42
+
43
+ The selected set is:
44
+
45
+ - isolated per live Agent;
46
+ - monotonic during that Agent lifecycle;
47
+ - reconstructed from successful `tool_search` results on resume;
48
+ - reconstructed from the copied history prefix on fork;
49
+ - re-evaluated against live settings without deleting its history.
50
+
51
+ Tool and prompt registry changes invalidate every cached catalog. The next assembly or search rebuilds from current scoped schemas.
52
+
53
+ ## Settings
54
+
55
+ The DSH Web settings page exposes:
56
+
57
+ - default result count, `1-20` and `5` by default;
58
+ - an exact-name always-visible allowlist;
59
+ - an exact-name never-search denylist;
60
+ - a live, filterable registry table with per-tool policy controls.
61
+
62
+ Equivalent settings:
63
+
64
+ ```yaml
65
+ - id: dsh-tool-search
66
+ config:
67
+ defaultLimit: 5
68
+ alwaysVisible:
69
+ - my_frequent_tool
70
+ neverSearch:
71
+ - internal_debug_tool
72
+ ```
73
+
74
+ ## Search Contract
75
+
76
+ Each function search document contains, in order:
77
+
78
+ 1. `tool.name`
79
+ 2. `tool.name.replaceAll("_", " ")`
80
+ 3. `tool.description`
81
+ 4. a recursive schema traversal that emits each node description, then each sorted property name immediately before that property's nested descriptions; `items` and `anyOf` follow
82
+
83
+ The local index uses BM25 with `k1=1.2` and `b=0.75`, English stop-word removal, and Porter2 stemming. Exact raw-name matches are always ranked first. Remaining results use descending BM25 score, then code-point lexical tool name as a stable tie-break. Search results contain only names, short descriptions, and scores; they never duplicate parameter schemas.
84
+
85
+ ## Codex Alignment
86
+
87
+ The implementation was checked against `openai/codex` commit [`3fde89f`](https://github.com/openai/codex/commit/3fde89f628281b9b049376fb0cec4d1577bfeaec), the latest observed `main` commit during the source review. Codex's generic function document builder is the source for the field order and recursive schema traversal. Codex also uses `bm25` `2.3.2` with its English tokenizer and the same BM25 defaults.
88
+
89
+ Intentional DSH adaptations:
90
+
91
+ | Area | Codex | This plugin |
92
+ | --- | --- | --- |
93
+ | Default Top-K | 8 in current upstream | 5 |
94
+ | Loading protocol | Responses API `tool_search_output` carries loadable definitions in history | ordinary DSH `tool_search` returns summaries; native schemas appear in the next request |
95
+ | Loaded state | definitions remain usable while retained in model history | per-Agent monotonic selected set recovered from DSH session events |
96
+ | Equal-score order | upstream candidate order can be unstable | lexical name tie-break |
97
+ | Exact name | ordinary BM25 ranking | explicit Recall@1 guarantee |
98
+ | Transliteration | Rust `deunicode` | Unicode NFKD diacritic folding |
99
+ | Result payload | definitions, no scores | summaries and scores, no schemas |
100
+
101
+ These differences are deliberate. DSH has no Responses API `tool_search_output` history item, while the requested behavior requires stable selected-set recovery and schema injection on the next model turn.
102
+
103
+ ## Execution Semantics
104
+
105
+ This plugin filters only `PromptAssembly.tools` and matching `tool:<name>` guidance sections. It does not unregister tools and does not call `ctx.tools.restrict()`.
106
+
107
+ After discovery, a real tool call still follows DSH's standard chain:
108
+
109
+ ```text
110
+ tool/call
111
+ -> tools/pre-execute
112
+ -> user approval
113
+ -> guards
114
+ -> tools/execute
115
+ -> ToolDefinition.execute
116
+ -> tools/post-execute
117
+ -> finalizer
118
+ -> tools/result
119
+ -> tool/result
120
+ ```
121
+
122
+ Deferred visibility is a context and token optimization, not an authorization boundary. A model or caller that already knows a hidden real name can still attempt it, and approval, guards, sandboxing, and tool-owned validation remain responsible for enforcement.
123
+
124
+ Selection is committed only from a successful final `tools/result`, after post-execute policy. The finalizer persists the same compact, schema-free JSON result that drove the live commit, so resume and fork replay cannot diverge from the in-memory selected set.
125
+
126
+ ## Token Benchmark
127
+
128
+ `pnpm benchmark` tokenizes deterministic nested OpenAI function-tool wire schemas using `gpt-tokenizer`'s default `o200k_base` encoder:
129
+
130
+ | Deferred tools | Full initial schemas | `tool_search` initial | Initial reduction | After Top-5 |
131
+ | ---: | ---: | ---: | ---: | ---: |
132
+ | 10 | 1,822 | 103 | 94.3% | 1,013 |
133
+ | 30 | 5,462 | 103 | 98.1% | 1,013 |
134
+ | 50 | 9,102 | 103 | 98.9% | 1,013 |
135
+ | 100 | 18,202 | 103 | 99.4% | 1,013 |
136
+
137
+ The shipped generator and exact fixture construction are in [`scripts/benchmark.mjs`](scripts/benchmark.mjs). [`docs/benchmark-results.json`](docs/benchmark-results.json) records the package version, generator and corpus SHA-256 hashes, fixture sizes, tokenizer, and machine-readable results. Application totals also include core schemas and system prompt tokens, so production percentage savings depend on the installed tool mix.
138
+
139
+ ## Verification
140
+
141
+ ```bash
142
+ pnpm install --frozen-lockfile
143
+ pnpm validate
144
+ pnpm run e2e:profile
145
+ ```
146
+
147
+ The test suite covers document construction, exact-name Recall@1, capability Recall@5, stable ties, per-Agent isolation, monotonic growth, registry refresh, result shape, final-success commit, post-execute rejection and rewrite, one-turn loading, resume/fork recovery, live settings, Web controls, and the real DSH approval/guard/event/execution pipeline. The profile E2E installs the packed plugin into a clean `DSH_HOME` and drives a scripted real Agent through search, next-turn schema exposure, and a real-name tool call. Release validation runs this E2E on DSH `0.1.1-rc.1` and `0.1.2-rc.1`.
148
+
149
+ ## Known Limits
150
+
151
+ - Native mode is required. DSH code/PTC and `both` modes independently render a `tools:sdk` prompt section; the stable plugin API does not expose a filtered SDK renderer. The plugin rejects those assemblies with a clear error instead of leaving search unavailable while leaking the unfiltered SDK.
152
+ - On some Node.js 24+ builds, the DSH `0.1.2-rc.1` standalone launcher cannot access Node's internal ESM loader and therefore cannot resolve any bare external Profile package. If startup reports `Cannot find package '@anionex/dsh-tool-search'`, launch with `node --expose-internals "$(command -v dsh)" --profile <name> ...`. CI requires standard bare-package loading on Node 22; the Profile E2E records whether a local run needed this launcher workaround.
153
+ - Tool definitions do not expose package provenance. Newly introduced DSH core names default to deferred until this package updates `DEFAULT_CORE_TOOLS` or the user adds an allowlist entry.
154
+ - Exact `tool:<name>` guidance is filtered with a hidden schema. Arbitrary third-party prompt sections have no tool provenance and cannot be safely rewritten.
155
+ - English stemming and NFKD folding are not semantic or multilingual retrieval. There is no reranker or embedding fallback.
156
+ - Cooperative prompt waterfalls are not a hostile-plugin security boundary.
157
+
158
+ ## License
159
+
160
+ MIT
package/README.zh.md ADDED
@@ -0,0 +1,160 @@
1
+ # dsh-tool-search
2
+
3
+ 面向 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/DeepSeek-Harness) 的 Codex 风格延迟工具加载插件。
4
+
5
+ `dsh-tool-search` 保留真实的 DSH 工具注册表,只缩小模型可见的工具集合。新 Agent 起初只看到维护中的核心工具和 `tool_search`。调用 `tool_search` 后,插件在本地延迟工具元数据上运行确定性 BM25,将 Top-K 结果加入该 Agent 的已选集合,并在下一次模型调用中暴露它们的完整原生 schema。模型随后用真实工具名调用,仍经过 DSH 原有执行链。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ dsh plugin --profile web add @anionex/dsh-tool-search
11
+ ```
12
+
13
+ 需要时可把 `web` 换成 `headless` 或其他 DSH profile。安装后应重启运行中的 profile,并新建会话。
14
+
15
+ 部分 Node.js 24+ 版本的 DSH 独立启动器无法解析 Profile 中的裸包名。若启动时报 `Cannot find package '@anionex/dsh-tool-search'`,请使用 `node --expose-internals "$(command -v dsh)" --profile <name> ...`;这是 DSH 上游 loader 的限制,不是插件通过其他路径加载。
16
+
17
+ 要求:
18
+
19
+ - DSH `0.1.1-rc.1` 至当前 `0.1.2-rc.1` 预发布版本线
20
+ - Node.js `^22.19.0 || >=24.0.0`
21
+ - 原生工具展示模式
22
+
23
+ ## 行为
24
+
25
+ 初始原生 schema 集合包含:
26
+
27
+ - `tool_search`
28
+ - `apply_patch`
29
+ - `DEFAULT_CORE_TOOLS` 中维护的 DSH 核心工具
30
+ - `alwaysVisible` 中配置的精确名称
31
+
32
+ `0.1.0` 把以下名称视为核心工具:
33
+
34
+ ```text
35
+ apply_patch, ask_user_question, bash, create_goal, dsh_im_return_file,
36
+ exit_plan_mode, get_goal, interrupt_agent, job_kill, job_list, job_output,
37
+ list_agents, ralph, report, send_message, skill, subagent, subagent_fork,
38
+ todo_write, update_goal, workflow
39
+ ```
40
+
41
+ 其他已注册工具都可搜索,但默认不显示。`neverSearch` 同时从初始集合和搜索语料中隐藏名称,并在与 `alwaysVisible` 冲突时优先。`tool_search` 自身不能被拉黑,避免配置后无工具可发现。
42
+
43
+ 已选集合具备以下语义:
44
+
45
+ - 每个活跃 Agent 相互隔离;
46
+ - 一个 Agent 生命周期内只增长;
47
+ - resume 时从成功的 `tool_search` 结果恢复;
48
+ - fork 时从复制的历史前缀恢复;
49
+ - 设置变化只改变当前有效可见性,不删除已选历史。
50
+
51
+ 工具或提示词注册表变化会让全部缓存目录失效。下一次 assembly 或搜索按当前作用域 schema 重建索引。
52
+
53
+ ## 设置
54
+
55
+ DSH Web 设置页提供:
56
+
57
+ - 默认结果数,范围 `1-20`,默认 `5`;
58
+ - 精确工具名常驻白名单;
59
+ - 精确工具名搜索黑名单;
60
+ - 可筛选的实时工具目录及逐工具策略选择。
61
+
62
+ 等价配置:
63
+
64
+ ```yaml
65
+ - id: dsh-tool-search
66
+ config:
67
+ defaultLimit: 5
68
+ alwaysVisible:
69
+ - my_frequent_tool
70
+ neverSearch:
71
+ - internal_debug_tool
72
+ ```
73
+
74
+ ## 搜索合同
75
+
76
+ 每个函数工具的搜索文档按顺序包含:
77
+
78
+ 1. `tool.name`
79
+ 2. `tool.name.replaceAll("_", " ")`
80
+ 3. `tool.description`
81
+ 4. 递归遍历 schema:先输出当前节点描述,再按排序后的属性名逐项输出“属性名 + 该属性的嵌套描述”;随后处理 `items` 和 `anyOf`
82
+
83
+ 本地索引使用 `k1=1.2`、`b=0.75` 的 BM25,包含英语停用词移除和 Porter2 词干化。精确原始名称命中固定排第一;其他结果先按 BM25 分数降序,再按工具名 Unicode 码点字典序稳定处理同分。搜索结果只有名称、短描述和分数,不重复参数 schema。
84
+
85
+ ## Codex 对齐
86
+
87
+ 实现对照了源码调研期间 `openai/codex` 最新 `main` 提交 [`3fde89f`](https://github.com/openai/codex/commit/3fde89f628281b9b049376fb0cec4d1577bfeaec)。Codex 的通用函数文档构造器是字段顺序和递归 schema 遍历的依据;Codex 同样使用 `bm25` `2.3.2` 的英语 tokenizer 与相同 BM25 默认参数。
88
+
89
+ DSH 适配差异:
90
+
91
+ | 范围 | Codex | 本插件 |
92
+ | --- | --- | --- |
93
+ | 默认 Top-K | 当前上游为 8 | 5 |
94
+ | 加载协议 | Responses API 的 `tool_search_output` 在历史中携带可加载定义 | 普通 DSH `tool_search` 返回摘要,下一请求加入原生 schema |
95
+ | 已加载状态 | 定义保留在模型历史时可用 | 每 Agent 单调已选集合,并从 DSH session 事件恢复 |
96
+ | 同分排序 | 上游候选顺序可能不稳定 | 工具名字典序 tie-break |
97
+ | 精确名称 | 普通 BM25 排名 | 显式保证 Recall@1 |
98
+ | 字符转写 | Rust `deunicode` | Unicode NFKD 变音符号折叠 |
99
+ | 结果载荷 | 定义,无分数 | 摘要和分数,无 schema |
100
+
101
+ 这些差异是有意设计。DSH 没有 Responses API 的 `tool_search_output` 历史类型,而需求要求下一模型轮注入 schema、稳定恢复已选集合。
102
+
103
+ ## 执行语义
104
+
105
+ 插件只过滤 `PromptAssembly.tools` 和匹配的 `tool:<name>` 指引段,不注销工具,也不调用 `ctx.tools.restrict()`。
106
+
107
+ 发现后的真实工具调用仍经过 DSH 标准链:
108
+
109
+ ```text
110
+ tool/call
111
+ -> tools/pre-execute
112
+ -> 用户审批
113
+ -> guards
114
+ -> tools/execute
115
+ -> ToolDefinition.execute
116
+ -> tools/post-execute
117
+ -> finalizer
118
+ -> tools/result
119
+ -> tool/result
120
+ ```
121
+
122
+ 延迟可见性是上下文与 token 优化,不是授权边界。模型或调用方若已知隐藏工具的真实名称,仍可尝试调用;审批、guard、sandbox 和工具自身校验继续承担安全职责。
123
+
124
+ 已选集合只在 post-execute 策略之后、最终 `tools/result` 成功时提交。finalizer 会持久化驱动实时提交的同一份紧凑、无 schema JSON 结果,因此 resume/fork 重放不会与内存中的已选集合分叉。
125
+
126
+ ## Token 基准
127
+
128
+ `pnpm benchmark` 使用 `gpt-tokenizer` 默认的 `o200k_base` 编码器,对确定性的嵌套 OpenAI function-tool wire schema 计数:
129
+
130
+ | 延迟工具数 | 完整初始 schema | 仅 `tool_search` | 初始减少 | 加载 Top-5 后 |
131
+ | ---: | ---: | ---: | ---: | ---: |
132
+ | 10 | 1,822 | 103 | 94.3% | 1,013 |
133
+ | 30 | 5,462 | 103 | 98.1% | 1,013 |
134
+ | 50 | 9,102 | 103 | 98.9% | 1,013 |
135
+ | 100 | 18,202 | 103 | 99.4% | 1,013 |
136
+
137
+ 随包发布的生成器和完整 fixture 构造位于 [`scripts/benchmark.mjs`](scripts/benchmark.mjs)。[`docs/benchmark-results.json`](docs/benchmark-results.json) 记录包版本、生成器与语料 SHA-256、fixture 规模、tokenizer 和机器可读结果。应用总量还包含核心 schema 和系统提示词 token,生产环境的百分比取决于实际安装工具集合。
138
+
139
+ ## 验证
140
+
141
+ ```bash
142
+ pnpm install --frozen-lockfile
143
+ pnpm validate
144
+ pnpm run e2e:profile
145
+ ```
146
+
147
+ 测试覆盖文档构造、精确名称 Recall@1、能力 Recall@5、同分稳定性、Agent 隔离、集合单调增长、注册表刷新、结果形状、最终成功后提交、post-execute 拒绝与改写、只多一轮加载、resume/fork 恢复、实时设置、Web 控件,以及真实 DSH approval/guard/event/execute 链。Profile E2E 会把打包产物安装到全新 `DSH_HOME`,再驱动一个脚本化真实 Agent 完成搜索、下一轮 schema 可见和真实名称调用。发布验收会在 DSH `0.1.1-rc.1` 和 `0.1.2-rc.1` 上分别运行该 E2E。
148
+
149
+ ## 已知限制
150
+
151
+ - 必须使用原生模式。DSH code/PTC 和 `both` 模式会独立渲染 `tools:sdk` 提示词段;稳定插件 API 没有过滤 SDK 的渲染钩子。插件会用明确错误拒绝这些 assembly,避免搜索不可用时仍泄露未过滤 SDK。
152
+ - 部分 Node.js 24+ 版本下,DSH `0.1.2-rc.1` 独立启动器无法访问 Node 内部 ESM loader,因而不能解析任何 Profile 中以裸包名安装的外部插件。若启动时报 `Cannot find package '@anionex/dsh-tool-search'`,请改用 `node --expose-internals "$(command -v dsh)" --profile <name> ...`。CI 会在 Node 22 上强制验证标准裸包名加载;Profile E2E 也会记录本地运行是否用到了这一启动器兼容方案。
153
+ - 工具定义不提供包来源。新加入的 DSH 核心工具默认延迟,直到本包更新 `DEFAULT_CORE_TOOLS`,或用户加入白名单。
154
+ - 插件会随隐藏 schema 一起过滤精确 `tool:<name>` 指引。任意第三方提示词段没有工具来源,无法可靠改写。
155
+ - 英语词干化和 NFKD 折叠不是语义或多语言检索;没有 reranker 或 embedding fallback。
156
+ - 协作式 prompt waterfall 不是抵御恶意插件的安全边界。
157
+
158
+ ## 许可证
159
+
160
+ MIT
@@ -0,0 +1,3 @@
1
+ - insert:
2
+ - id: dsh-tool-search
3
+ name: '@anionex/dsh-tool-search'
@@ -0,0 +1,51 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "packageVersion": "0.1.0",
4
+ "tokenizer": "gpt-tokenizer 4.0.0 (o200k_base default encode)",
5
+ "fixture": {
6
+ "description": "Deterministic nested function schemas; full surface versus tool_search-only initial surface.",
7
+ "generator": "scripts/benchmark.mjs",
8
+ "generatorSha256": "146898e3a92876fb35d9531c65950e940b664e7d77fa72e9a9c31169337adec1",
9
+ "corpusSha256": "2a7e824f3891f37b4633f09f3a814abd5d071c28a421b75a41580df9528745e2",
10
+ "sizes": [
11
+ 10,
12
+ 30,
13
+ 50,
14
+ 100
15
+ ]
16
+ },
17
+ "rows": [
18
+ {
19
+ "tools": 10,
20
+ "fullTokens": 1822,
21
+ "deferredInitialTokens": 103,
22
+ "initialTokensSaved": 1719,
23
+ "initialReductionPercent": 94.3,
24
+ "afterTop5Tokens": 1013
25
+ },
26
+ {
27
+ "tools": 30,
28
+ "fullTokens": 5462,
29
+ "deferredInitialTokens": 103,
30
+ "initialTokensSaved": 5359,
31
+ "initialReductionPercent": 98.1,
32
+ "afterTop5Tokens": 1013
33
+ },
34
+ {
35
+ "tools": 50,
36
+ "fullTokens": 9102,
37
+ "deferredInitialTokens": 103,
38
+ "initialTokensSaved": 8999,
39
+ "initialReductionPercent": 98.9,
40
+ "afterTop5Tokens": 1013
41
+ },
42
+ {
43
+ "tools": 100,
44
+ "fullTokens": 18202,
45
+ "deferredInitialTokens": 103,
46
+ "initialTokensSaved": 18099,
47
+ "initialReductionPercent": 99.4,
48
+ "afterTop5Tokens": 1013
49
+ }
50
+ ]
51
+ }
package/lib/bm25.js ADDED
@@ -0,0 +1,83 @@
1
+ /** Deterministic local BM25, aligned with Codex's bm25 2.3.2 defaults. */
2
+ import stem from 'wink-porter2-stemmer';
3
+ const K1 = 1.2;
4
+ const B = 0.75;
5
+ // stop-words 0.9.0 uses the NLTK English list before stemming.
6
+ const ENGLISH_STOP_WORDS = new Set([
7
+ 'a', 'about', 'above', 'after', 'again', 'against', 'ain', 'all', 'am', 'an', 'and', 'any', 'are',
8
+ 'aren', "aren't", 'as', 'at', 'be', 'because', 'been', 'before', 'being', 'below', 'between', 'both',
9
+ 'but', 'by', 'can', 'couldn', "couldn't", 'd', 'did', 'didn', "didn't", 'do', 'does', 'doesn',
10
+ "doesn't", 'doing', 'don', "don't", 'down', 'during', 'each', 'few', 'for', 'from', 'further', 'had',
11
+ 'hadn', "hadn't", 'has', 'hasn', "hasn't", 'have', 'haven', "haven't", 'having', 'he', 'her', 'here',
12
+ 'hers', 'herself', 'him', 'himself', 'his', 'how', 'i', 'if', 'in', 'into', 'is', 'isn', "isn't",
13
+ 'it', "it's", 'its', 'itself', 'just', 'll', 'm', 'ma', 'me', 'mightn', "mightn't", 'more', 'most',
14
+ 'mustn', "mustn't", 'my', 'myself', 'needn', "needn't", 'no', 'nor', 'not', 'now', 'o', 'of', 'off',
15
+ 'on', 'once', 'only', 'or', 'other', 'our', 'ours', 'ourselves', 'out', 'over', 'own', 're', 's',
16
+ 'same', 'shan', "shan't", 'she', "she's", 'should', "should've", 'shouldn', "shouldn't", 'so', 'some',
17
+ 'such', 't', 'than', 'that', "that'll", 'the', 'their', 'theirs', 'them', 'themselves', 'then',
18
+ 'there', 'these', 'they', 'this', 'those', 'through', 'to', 'too', 'under', 'until', 'up', 've',
19
+ 'very', 'was', 'wasn', "wasn't", 'we', 'were', 'weren', "weren't", 'what', 'when', 'where', 'which',
20
+ 'while', 'who', 'whom', 'why', 'will', 'with', 'won', "won't", 'wouldn', "wouldn't", 'y', 'you',
21
+ "you'd", "you'll", "you're", "you've", 'your', 'yours', 'yourself', 'yourselves',
22
+ ]);
23
+ function asciiFold(value) {
24
+ return value.normalize('NFKD').replace(/\p{M}+/gu, '');
25
+ }
26
+ export function tokenize(value) {
27
+ const matches = asciiFold(value).toLowerCase().match(/[\p{L}\p{N}]+(?:['.][\p{L}\p{N}]+)*/gu) ?? [];
28
+ return matches.filter(token => !ENGLISH_STOP_WORDS.has(token)).map(token => stem(token));
29
+ }
30
+ export class Bm25Index {
31
+ documents;
32
+ frequencies = new Map();
33
+ averageLength;
34
+ constructor(documents) {
35
+ this.documents = documents.map((document, order) => {
36
+ const tokens = tokenize(document.text);
37
+ const counts = new Map();
38
+ for (const token of tokens)
39
+ counts.set(token, (counts.get(token) ?? 0) + 1);
40
+ for (const token of counts.keys())
41
+ this.frequencies.set(token, (this.frequencies.get(token) ?? 0) + 1);
42
+ return { ...document, tokens, counts, order };
43
+ });
44
+ this.averageLength = this.documents.length === 0
45
+ ? 256
46
+ : this.documents.reduce((total, document) => total + document.tokens.length, 0) / this.documents.length;
47
+ }
48
+ search(query, limit) {
49
+ if (limit <= 0 || this.documents.length === 0)
50
+ return [];
51
+ const queryTokens = tokenize(query);
52
+ const normalizedQuery = query.trim();
53
+ const matches = [];
54
+ for (const document of this.documents) {
55
+ let score = 0;
56
+ for (const token of queryTokens) {
57
+ const termFrequency = document.counts.get(token) ?? 0;
58
+ if (termFrequency === 0)
59
+ continue;
60
+ const documentFrequency = this.frequencies.get(token) ?? 0;
61
+ const inverseDocumentFrequency = Math.log1p((this.documents.length - documentFrequency + 0.5) / (documentFrequency + 0.5));
62
+ const lengthRatio = document.tokens.length / this.averageLength;
63
+ const weight = (termFrequency * (K1 + 1))
64
+ / (termFrequency + K1 * (1 - B + B * lengthRatio));
65
+ score += inverseDocumentFrequency * weight;
66
+ }
67
+ const exact = document.name === normalizedQuery;
68
+ if (score > 0 || exact)
69
+ matches.push({ id: document.id, name: document.name, score, exact, order: document.order });
70
+ }
71
+ matches.sort((left, right) => {
72
+ if (left.exact !== right.exact)
73
+ return left.exact ? -1 : 1;
74
+ if (left.score !== right.score)
75
+ return right.score - left.score;
76
+ if (left.name !== right.name)
77
+ return left.name < right.name ? -1 : 1;
78
+ return left.order - right.order;
79
+ });
80
+ return matches.slice(0, limit).map(({ id, name, score }) => ({ id, name, score }));
81
+ }
82
+ }
83
+ //# sourceMappingURL=bm25.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bm25.js","sourceRoot":"","sources":["../src/bm25.ts"],"names":[],"mappings":"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"}
package/lib/catalog.js ADDED
@@ -0,0 +1,53 @@
1
+ /** Codex-compatible tool search document construction. */
2
+ function pushPart(parts, value) {
3
+ if (typeof value !== 'string')
4
+ return;
5
+ const part = value.trim();
6
+ if (part.length > 0)
7
+ parts.push(part);
8
+ }
9
+ /**
10
+ * Append descriptions and property names in the same recursive shape used by
11
+ * Codex: description, sorted object properties, array items, then anyOf.
12
+ */
13
+ export function appendSchemaSearchText(schema, parts) {
14
+ if (typeof schema !== 'object' || schema === null || Array.isArray(schema))
15
+ return;
16
+ const node = schema;
17
+ pushPart(parts, node.description);
18
+ if (typeof node.properties === 'object' && node.properties !== null && !Array.isArray(node.properties)) {
19
+ const properties = node.properties;
20
+ for (const name of Object.keys(properties).sort()) {
21
+ pushPart(parts, name);
22
+ appendSchemaSearchText(properties[name], parts);
23
+ }
24
+ }
25
+ appendSchemaSearchText(node.items, parts);
26
+ if (Array.isArray(node.anyOf)) {
27
+ for (const variant of node.anyOf)
28
+ appendSchemaSearchText(variant, parts);
29
+ }
30
+ }
31
+ export function toolSearchText(tool) {
32
+ const parts = [];
33
+ pushPart(parts, tool.name);
34
+ pushPart(parts, tool.name.replaceAll('_', ' '));
35
+ pushPart(parts, tool.description);
36
+ appendSchemaSearchText(tool.parameters, parts);
37
+ return parts.join(' ');
38
+ }
39
+ export function buildCatalog(tools) {
40
+ const byName = new Map();
41
+ for (const tool of tools) {
42
+ if (byName.has(tool.name))
43
+ continue;
44
+ byName.set(tool.name, {
45
+ name: tool.name,
46
+ description: tool.description?.trim() ?? '',
47
+ parameters: structuredClone(tool.parameters),
48
+ searchText: toolSearchText(tool),
49
+ });
50
+ }
51
+ return [...byName.values()].sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
52
+ }
53
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}