dsh-agentic-tool-catalog 0.0.0-stage → 0.3.1
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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +167 -2
- package/README.zh.md +134 -0
- package/cordis.patch.yml +29 -0
- package/docs/assets/plugin-page.png +0 -0
- package/docs/assets/ui-dock.png +0 -0
- package/docs/assets/ui-panel.png +0 -0
- package/docs/benchmark.md +54 -0
- package/docs/data-card.md +96 -0
- package/docs/detail-benchmark.svg +30 -0
- package/docs/detail-hero.svg +57 -0
- package/docs/detail.md +33 -0
- package/docs/detail.zh.md +30 -0
- package/icon.svg +15 -0
- package/lib/catalog.js +208 -0
- package/lib/client.js +368 -0
- package/lib/generation.js +306 -0
- package/lib/index.js +781 -0
- package/lib/store.js +108 -0
- package/locale/en.json +7 -0
- package/locale/zh.json +7 -0
- package/package.json +89 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.1 — 2026-11-11
|
|
4
|
+
|
|
5
|
+
Documentation-only release: the plugin page was rewritten around the user's questions (what it
|
|
6
|
+
does, what it saves, what to know), the product hero and benchmark charts were added, and the
|
|
7
|
+
in-session screenshot section / implementation jargon were removed. No runtime behavior change.
|
|
8
|
+
|
|
9
|
+
## 0.3.0 — 2026-11-11
|
|
10
|
+
|
|
11
|
+
Renamed from `dsh-tool-discovery` / Tool Discovery; public identity is now **Agentic Tool Catalog**.
|
|
12
|
+
|
|
13
|
+
- **Name/identity**: `@local/dsh-agentic-tool-catalog`, row id `agentic-tool-catalog`,
|
|
14
|
+
display "Agentic Tool Catalog / Agent 工具目录"; tools renamed `discovery_status` → `catalog_status`;
|
|
15
|
+
prompt section `catalog:note`; config `agentGroup*` → `catalog*`; internal `policy.js` → `catalog.js`,
|
|
16
|
+
`agent-groups.js` → `generation.js`.
|
|
17
|
+
- **Persistence**: moved the catalog cache from a self-managed JSON file to official DSH Storage
|
|
18
|
+
(`ctx.storageDomain`, domain `agentic_tool_catalog`, schema-validated). No second file backend;
|
|
19
|
+
profiles without a storage service use memory-only caching and report `storage.mode = "memory"`.
|
|
20
|
+
- **Status contract**: `catalog_status` returns fixed sections `catalog` / `session` / `generation` /
|
|
21
|
+
`storage` / `warnings`.
|
|
22
|
+
- **Migration fix**: when replacing the old `dsh-tool-discovery` bundle, release restrictions left in the
|
|
23
|
+
legacy global registry so a renamed plugin does not inherit an orphaned deny set.
|
|
24
|
+
- **Empty catalog fix**: with nothing deferred (e.g. every tool resident) the plugin no longer spawns a
|
|
25
|
+
grouping subagent that can only fail `TOO_FEW_GROUPS`; it emits an empty catalog directly. The group floor
|
|
26
|
+
now scales down for catalogs with fewer than 6 tools, and validation accepts exactly `0` groups for `0` tools.
|
|
27
|
+
- **Docs**: bilingual README (`README.md` / `README.zh.md`), rewritten product detail pages, MIT LICENSE.
|
|
28
|
+
|
|
29
|
+
## 0.2.0 — 2026-11-10
|
|
30
|
+
|
|
31
|
+
- Agent-curated catalog only (all legacy/rules/BM25/heuristic strategies removed).
|
|
32
|
+
- Lazy/pre-warm generation, incremental regeneration, official freshness/origin reporting,
|
|
33
|
+
last-known-good continuation with explicit `catalog-stale` warnings.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek Harness plugins contributors
|
|
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
CHANGED
|
@@ -1,3 +1,168 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Agentic Tool Catalog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**An agent builds and maintains the tool catalog: schemas stay out of context, tools load on demand.**
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
[English](README.md) | [中文](README.zh.md)
|
|
8
|
+
|
|
9
|
+
DeepSeek Harness normally sends every visible tool schema on every request. With many MCP servers and
|
|
10
|
+
extension plugins that gets expensive: context bloat, slower requests, and degraded tool selection once the
|
|
11
|
+
catalog grows past a few dozen tools.
|
|
12
|
+
|
|
13
|
+
Agentic Tool Catalog keeps a **directory** of deferred tools instead. A real DSH sub-agent reads the current
|
|
14
|
+
tool inventory, groups tools by intent, self-checks coverage, and returns a catalog. The plugin validates it,
|
|
15
|
+
caches it in **official DSH Storage**, and only ever sends a few lines of directory text — tools are loaded
|
|
16
|
+
by `search_tools` when a task actually needs them.
|
|
17
|
+
|
|
18
|
+
## What you get
|
|
19
|
+
|
|
20
|
+
- **Smaller requests**: deferred tool schemas never enter the context until loaded (measured 91 deferred
|
|
21
|
+
schemas ≈ 109.6 KB ≈ ~27k tokens replaced by a 443-character directory note in this profile).
|
|
22
|
+
- **Agent-curated grouping**: groups are generated by an agent (not by name prefixes, not by a manual list):
|
|
23
|
+
browser tools split by intent, Google tools split by product, cross-server capabilities merged.
|
|
24
|
+
- **Zero cooperation from other plugins**: no declarations, no metadata, no code changes. Any installed tool
|
|
25
|
+
is discovered automatically.
|
|
26
|
+
- **Local-first**: the generator runs on your DSH model; no external service is required.
|
|
27
|
+
- **Reliable by design**: strict validation, one retry with feedback, single-flight generation, persistent
|
|
28
|
+
cache (official `ctx.storageDomain`), incremental regeneration on catalog changes, and a visible
|
|
29
|
+
"stale / refreshing" state if generation cannot complete. It always keeps working with the last successful
|
|
30
|
+
catalog or a flat bootstrap directory — and never silently hides the failure.
|
|
31
|
+
- **Observable**: `catalog_status` exposes catalog freshness/origin, groups, loaded tools, generation and
|
|
32
|
+
storage health.
|
|
33
|
+
|
|
34
|
+
## How it works
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
agent needs tools
|
|
38
|
+
→ tool catalog fingerprint (tool names + cache namespace)
|
|
39
|
+
→ hot cache / official Storage hit? ── yes → use catalog
|
|
40
|
+
→ no: generate with a DSH sub-agent (incremental if a previous catalog exists)
|
|
41
|
+
→ validate (100% coverage, no duplicates, sane groups)
|
|
42
|
+
→ cache in official Storage + apply catalog (restriction releases tools on demand)
|
|
43
|
+
→ failure: retry once with error feedback; still failing → keep last successful grouping
|
|
44
|
+
(status = stale, explicit warning + backoff retry) or flat bootstrap directory
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- **New sessions** start generation immediately (pre-warm); **restored sessions** generate on their first
|
|
48
|
+
user message/search; **catalog changes** regenerate immediately (incremental first).
|
|
49
|
+
- **Generation is keyed by the tool set, not by time**: one generation per unique tool fingerprint; the result is
|
|
50
|
+
cached in official Storage and reused across sessions/restarts. There is no periodic refresh — the timestamp
|
|
51
|
+
only moves when the tool set actually changes (install/remove a plugin or MCP server, preset change, etc.).
|
|
52
|
+
- Tools are hidden from the model with `agent.ctx.tools.restrict({ deny })`; `search_tools` releases them
|
|
53
|
+
through DSH's native mid-conversation tool-addition path.
|
|
54
|
+
- The catalog cache lives in official DSH Storage (`ctx.storageDomain`, domain `agentic_tool_catalog`).
|
|
55
|
+
If a profile has no storage service, the plugin falls back to memory-only caching (still functional;
|
|
56
|
+
one regeneration after restart) and reports `storage.mode = "memory"`.
|
|
57
|
+
|
|
58
|
+
## Model-facing tools
|
|
59
|
+
|
|
60
|
+
| Tool | Parameters | Behavior |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `search_tools` | `query: string`, `limit?: number` | Search the catalog by intent/keywords (name, description, group id/description). Exact group-name queries load the whole group (up to `groupMaxResults`). Loaded tools become callable for the rest of the session. |
|
|
63
|
+
| `catalog_status` | — | Read-only JSON: `catalog` (source/freshness/origin/total/groups/others), `session` (loaded/pending/restriction), `generation` (runs, cache hits, failures, retries, dedupe, stale uses, incremental runs, last error/ok, instance), `storage` (mode/state), `warnings`. |
|
|
64
|
+
|
|
65
|
+
A short `catalog:note` system-prompt section lists deferred groups and counts; during bootstrap/stale states it
|
|
66
|
+
adds one explicit line explaining that the catalog is initializing/refreshing.
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
- id: agentic-tool-catalog
|
|
72
|
+
name: 'dsh-agentic-tool-catalog'
|
|
73
|
+
config:
|
|
74
|
+
maxResults: 6 # tools loaded by one generic search
|
|
75
|
+
groupMaxResults: 30 # tools loaded by an exact group-name search
|
|
76
|
+
promptNote: true # inject the catalog:note system-prompt section
|
|
77
|
+
resident: [] # override the built-in resident list (tool names/globs)
|
|
78
|
+
extraKeepResident: [] # append globs that must always stay visible
|
|
79
|
+
extraDeferred: [] # force globs to stay deferred
|
|
80
|
+
catalogProvider: spawn # DSH subagent provider used for generation
|
|
81
|
+
catalogTimeoutMs: 120000 # per-generation timeout
|
|
82
|
+
catalogResetNonce: '' # cache namespace; change it to force regeneration
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Most profiles need no configuration. New plugins/MCP servers are picked up automatically on the next catalog
|
|
86
|
+
change; if a tool must always be visible, add it to `extraKeepResident`.
|
|
87
|
+
|
|
88
|
+
## Install
|
|
89
|
+
|
|
90
|
+
Local development (this workspace):
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# via the DSH plugin manager
|
|
94
|
+
dsh plugin --profile web add dsh-agentic-tool-catalog
|
|
95
|
+
# local development (this repository)
|
|
96
|
+
dsh plugin --profile web add /path/to/dsh-agentic-tool-catalog
|
|
97
|
+
# or, from a shell in the DSH checkout
|
|
98
|
+
dsh plugin add /path/to/dsh-agentic-tool-catalog
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Published package: `dsh-agentic-tool-catalog` (npm). Requires DSH `0.2.2-rc.1` or later.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
dsh plugin --profile web add dsh-agentic-tool-catalog
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Performance (A/B: with vs without)
|
|
108
|
+
|
|
109
|
+
Measured on 5 tasks × 2 independent sessions per arm, same model and tool surface (122 tools incl. Google
|
|
110
|
+
Workspace MCP), DSH 0.2.2-rc.1, `deepseek-flash`:
|
|
111
|
+
|
|
112
|
+
| Metric | without (all tools resident) | with this plugin | Change |
|
|
113
|
+
|---|---:|---:|---:|
|
|
114
|
+
| Task pass rate | 100% (10/10) | 100% (10/10) | equal |
|
|
115
|
+
| First-request tool schema (payload) | 122 tools / 131,390 chars | 31 tools / 21,877 chars | **−83.3%** |
|
|
116
|
+
| **First-request real prompt tokens** (usage) | **49,504** | **18,536** | **−62.6% / −30,968 per request** |
|
|
117
|
+
| Avg total tokens / session | 452,463 | 268,551 | **−40.6%** |
|
|
118
|
+
| Avg steps | 7.40 | 7.90 | +6.8% |
|
|
119
|
+
| Avg cost / session | $0.0112 | $0.0116 | +3.7% (≈flat) |
|
|
120
|
+
| Avg wall time | 37.0s | 33.3s | −9.9% |
|
|
121
|
+
| Avg `search_tools` calls | 0.10 | 1.30 | +1.2/session |
|
|
122
|
+
|
|
123
|
+
One-time catalog generation (background, cached in official DSH Storage) is excluded from these task metrics.
|
|
124
|
+
Full methodology and caveats: [`docs/benchmark.md`](docs/benchmark.md) · full data card: [`docs/data-card.md`](docs/data-card.md).
|
|
125
|
+
|
|
126
|
+
## UI (minimal, read-only)
|
|
127
|
+
|
|
128
|
+
An icon + a small on-demand-tool count sits in the official composer dock (hover shows the catalog summary and
|
|
129
|
+
uses the same hover treatment as the official context meter). Click it for a read-only panel modeled on the
|
|
130
|
+
official ContextMeter: a segmented bar plus three rows (always available / loaded / on demand), a status dot and
|
|
131
|
+
freshness word, and one meta line (groups · storage · updated). There is no directory dump, no configuration, and
|
|
132
|
+
no long explanatory copy. It uses official slots/theme tokens and follows the light/dark theme.
|
|
133
|
+
|
|
134
|
+
## Verification
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
node scripts/verify-agentic-tool-catalog.mjs # module/regression tests
|
|
138
|
+
node scripts/verify-agentic-tool-catalog-schema.mjs # real dsh-tools/schemastery/storage-domain compile
|
|
139
|
+
node scripts/verify-presentation.mjs # presentation files (locale/icon/detail)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Benchmark methodology and data live in [`benchmark/`](../../benchmark/discovery/) and are summarized in the
|
|
143
|
+
plugin page / release data card (compare **with vs without** the plugin on the same tasks).
|
|
144
|
+
|
|
145
|
+
## Safety & privacy
|
|
146
|
+
|
|
147
|
+
- Everything runs locally: the catalog is generated by the configured DSH sub-agent; no external service is required.
|
|
148
|
+
- The cache contains only tool names/group descriptions and lives in official DSH Storage (`ctx.storageDomain`). It never
|
|
149
|
+
contains credentials, prompts, message content or tool results; it is derived data that can be regenerated at any time.
|
|
150
|
+
- The UI status panel is read-only, same-origin, and scoped to the current session; it exposes no secrets.
|
|
151
|
+
- The plugin does not modify other plugins or install anything outside DSH's plugin system.
|
|
152
|
+
|
|
153
|
+
## Known limits
|
|
154
|
+
|
|
155
|
+
- The cache is derived data. Losing it costs one regeneration (tens of seconds once per catalog); it is not
|
|
156
|
+
user data and is not migrated between storage backends.
|
|
157
|
+
- Cold start: the very first run has no catalog yet and starts with a flat bootstrap directory while the agent
|
|
158
|
+
generates one in the background. Subsequent runs, restarts and catalog changes use the persisted/incremental path.
|
|
159
|
+
- Generation quality depends on the configured model; the plugin validates strictly and retries/falls back
|
|
160
|
+
with a visible status, but it cannot make a bad grouping "correct" silently.
|
|
161
|
+
- No UI dashboard yet (`catalog_status` is the observable interface); tools only (skills are not part of the
|
|
162
|
+
catalog). Scale beyond a few hundred tools is not benchmarked yet.
|
|
163
|
+
- Uses the public `tools.definitions/restrict` surface and the official `storageDomain` API; both are
|
|
164
|
+
DSH-plugin-facing APIs subject to the usual `0.2.x` stability caveats.
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Agentic Tool Catalog(Agent 工具目录)
|
|
2
|
+
|
|
3
|
+
**由 Agent 自动生成并维护工具目录:工具 schema 默认不占上下文,按需加载。**
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [中文](README.zh.md)
|
|
6
|
+
|
|
7
|
+
DeepSeek Harness 默认把**每个可见工具的完整 schema** 放进**每一次**模型请求。接的 MCP/扩展插件一多,
|
|
8
|
+
上下文就会膨胀、请求变慢,工具超过几十个后模型选择准确率也会下降。
|
|
9
|
+
|
|
10
|
+
Agentic Tool Catalog 换一种做法:上下文里只保留一份**目录**。插件启动一个**真正的 DSH 子代理**,
|
|
11
|
+
读取当前工具清单、按用户意图分组、自检覆盖率,交回一份目录;插件严格校验后缓存到**官方 DSH Storage**,
|
|
12
|
+
每次请求只发几行目录文字,具体工具由 `search_tools` 在需要时加载。
|
|
13
|
+
|
|
14
|
+
## 你能得到什么
|
|
15
|
+
|
|
16
|
+
- **更小的请求**:未加载工具 schema 不进上下文(本机实测:91 个延迟工具 schema 合计约 109.6 KB ≈ 2.7 万 tokens,
|
|
17
|
+
被 443 字符的目录提示替代);
|
|
18
|
+
- **Agent 生成的语义分组**:按意图拆组(浏览器拆成操作/观察/判断;Google 按邮件/日历/登录拆;跨 server 能力合并),
|
|
19
|
+
而不是按名字前缀;
|
|
20
|
+
- **零插件配合**:无需其他插件声明字段、元数据或改代码;装了就会被自动发现;
|
|
21
|
+
- **本地优先**:生成使用本机 DSH 模型,不需要外部服务;
|
|
22
|
+
- **可靠性**:严格校验、失败带反馈重试一次、同指纹单飞、官方 Storage 持久化、目录变化增量重生成;
|
|
23
|
+
生成没完成时状态会显式标 `stale`(或使用扁平引导目录),**不会静默失败**;
|
|
24
|
+
- **可观测**:`catalog_status` 提供目录新鲜度/来源、分组、已加载、生成与存储健康状态。
|
|
25
|
+
|
|
26
|
+
## 工作机制
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
需要工具
|
|
30
|
+
→ 目录指纹(工具名 + 缓存命名空间)
|
|
31
|
+
→ 内存/官方 Storage 命中? ── 是 → 直接使用
|
|
32
|
+
→ 否:用 DSH 子代理生成(有上次目录时优先增量)
|
|
33
|
+
→ 校验(100% 覆盖、无重复、组合理)
|
|
34
|
+
→ 写入官方 Storage 并应用目录(按需释放工具)
|
|
35
|
+
→ 失败:带错误反馈重试一次;仍失败 → 沿用最近一次成功分组
|
|
36
|
+
(状态 stale + 显式告警 + 退避重试)或扁平引导目录
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- **新会话**创建时立即预热生成;**恢复的旧会话**在第一条用户消息/搜索时才生成;**目录变化**立即重新生成(优先增量);
|
|
40
|
+
- **生成由工具集决定,不是按时间**:同一份工具指纹只生成一次,结果缓存进官方 Storage,跨会话/重启复用;
|
|
41
|
+
**没有定时刷新**——只有工具集真的变了(装/卸插件、MCP server 上下线、preset 变化等)才会重新生成。
|
|
42
|
+
- 工具用 `agent.ctx.tools.restrict({ deny })` 从模型请求中隐藏,`search_tools` 命中后通过 DSH 原生
|
|
43
|
+
tool-addition 机制在会话中途释放;
|
|
44
|
+
- 缓存走官方 `ctx.storageDomain`(domain `agentic_tool_catalog`);若 profile 没有 storage 服务,
|
|
45
|
+
降级为纯内存缓存(功能可用、重启后重新生成一次),并在 `storage.mode` 明确报告 `memory`。
|
|
46
|
+
|
|
47
|
+
## 模型可见工具
|
|
48
|
+
|
|
49
|
+
| 工具 | 参数 | 行为 |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `search_tools` | `query: string`,`limit?: number` | 按意图/关键词搜索目录(名字、描述、组名/组描述);query 精确等于组名时整组加载(≤`groupMaxResults`);加载后本会话可直接调用 |
|
|
52
|
+
| `catalog_status` | — | 只读 JSON:`catalog`(来源/新鲜度/来源类型/总数/分组/others)、`session`(已加载/待加载/restriction)、`generation`(次数、缓存命中、失败、重试、去重、stale 次数、增量次数、last error/ok、实例)、`storage`(模式/状态)、`warnings` |
|
|
53
|
+
|
|
54
|
+
系统提示里有一个 `catalog:note` 段:列出延迟分组和数量;初始化中/刷新中会额外写明一行状态。
|
|
55
|
+
|
|
56
|
+
## 配置
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
- id: agentic-tool-catalog
|
|
60
|
+
name: 'dsh-agentic-tool-catalog'
|
|
61
|
+
config:
|
|
62
|
+
maxResults: 6 # 一次普通搜索最多加载的工具数
|
|
63
|
+
groupMaxResults: 30 # 按组名整组加载的上限
|
|
64
|
+
promptNote: true # 是否注入 catalog:note 提示
|
|
65
|
+
resident: [] # 覆盖内置常驻清单(工具名/glob)
|
|
66
|
+
extraKeepResident: [] # 追加"必须常驻"的 glob
|
|
67
|
+
extraDeferred: [] # 强制按需的 glob
|
|
68
|
+
catalogProvider: spawn # 生成用子代理 provider
|
|
69
|
+
catalogTimeoutMs: 120000 # 单次生成超时
|
|
70
|
+
catalogResetNonce: '' # 缓存命名空间;改它可强制重新生成
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
大多数情况下**不需要任何配置**;新装插件/MCP 在目录变化时自动收编;只有"某工具必须永远可见"时才需要
|
|
74
|
+
`extraKeepResident`。
|
|
75
|
+
|
|
76
|
+
## 安装
|
|
77
|
+
|
|
78
|
+
本地开发(本工作区):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
dsh plugin --profile web add dsh-agentic-tool-catalog
|
|
82
|
+
# 本地开发(本仓库)
|
|
83
|
+
dsh plugin --profile web add /path/to/dsh-agentic-tool-catalog
|
|
84
|
+
# 或在 DSH checkout 里
|
|
85
|
+
dsh plugin add /path/to/dsh-agentic-tool-catalog
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
发布包:`dsh-agentic-tool-catalog`(npm);要求 DSH `0.2.2-rc.1` 及以上。
|
|
89
|
+
|
|
90
|
+
## 性能(A/B:用 vs 不用)
|
|
91
|
+
|
|
92
|
+
5 个任务 × 每臂 2 轮独立会话,同模型、同工具面(122 tools,含 Google Workspace MCP),DSH 0.2.2-rc.1、`deepseek-flash`:
|
|
93
|
+
|
|
94
|
+
| 指标 | 不用(全量常驻) | 用本插件 | 变化 |
|
|
95
|
+
|---|---:|---:|---:|
|
|
96
|
+
| 任务通过率 | 100% (10/10) | 100% (10/10) | 持平 |
|
|
97
|
+
| 首请求工具 schema(载荷) | 122 tools / 131,390 chars | 31 tools / 21,877 chars | **−83.3%** |
|
|
98
|
+
| **首请求真实 prompt tokens**(usage) | **49,504** | **18,536** | **−62.6% / 每请求 −30,968** |
|
|
99
|
+
| 平均总 tokens/会话 | 452,463 | 268,551 | **−40.6%** |
|
|
100
|
+
| 平均步数 | 7.40 | 7.90 | +6.8% |
|
|
101
|
+
| 平均成本/会话 | $0.0112 | $0.0116 | +3.7%(≈持平) |
|
|
102
|
+
| 平均墙钟 | 37.0s | 33.3s | −9.9% |
|
|
103
|
+
| 平均 `search_tools` 调用 | 0.10 | 1.30 | +1.2/会话 |
|
|
104
|
+
|
|
105
|
+
一次性目录生成(后台、缓存到官方 DSH Storage)不计入上表任务指标;完整方法与边界见 [`docs/benchmark.md`](docs/benchmark.md) · 完整数据卡:[`docs/data-card.md`](docs/data-card.md)。
|
|
106
|
+
|
|
107
|
+
## 最小 UI(只读)
|
|
108
|
+
|
|
109
|
+
官方输入框下方是 **图标 + 一个极小的按需工具数**(hover 有摘要,hover/展开样式与官方 ContextMeter 一致)。
|
|
110
|
+
点击展开只读面板(照官方 ContextMeter 的尺寸/token 设计):常驻/已加载/待加载分段条 + 三行图例 +
|
|
111
|
+
状态点/新鲜度 + 一行“分组 · 存储 · 更新时间”。没有目录名堆叠、没有配置项、没有长说明;
|
|
112
|
+
使用官方 slot/主题变量,自动跟随明暗主题。
|
|
113
|
+
|
|
114
|
+
## 验证
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
node scripts/verify-agentic-tool-catalog.mjs # 模块/回归测试
|
|
118
|
+
node scripts/verify-agentic-tool-catalog-schema.mjs # 真实 dsh-tools/schemastery/storage-domain 编译
|
|
119
|
+
node scripts/verify-presentation.mjs # 展示文件检查(locale/icon/detail)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Benchmark 方法/数据见 [`benchmark/`](../../benchmark/discovery/);发布数据卡按**用 vs 不用插件**对照。
|
|
123
|
+
|
|
124
|
+
## 已知限制
|
|
125
|
+
|
|
126
|
+
- 缓存是**派生数据**:丢了最多重新生成一次(每个目录一次);它不是用户数据,不做跨介质迁移;
|
|
127
|
+
- 冷启动首次没有目录时,会先用扁平引导目录,后台生成完成后自动替换;之后重启/目录变化走持久化/增量路径;
|
|
128
|
+
- 生成质量取决于配置的模型:插件会严格校验、重试并在状态里显式标注,但不会"偷偷把坏分组当成正常";
|
|
129
|
+
- 暂无 UI 仪表盘(`catalog_status` 是观测入口);目前只处理 tools(不含 skills);数百个工具以上的规模尚未基准测试;
|
|
130
|
+
- 依赖公开的 `tools.definitions/restrict` 与官方 `storageDomain` API,遵循 DSH `0.2.x` 的稳定性边界。
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# dsh-agentic-tool-catalog —— 按需发现工具(tool search / 延迟加载)
|
|
2
|
+
#
|
|
3
|
+
# 本 bundle 只持有自己的插件行。MCP 能力由各自集成 bundle 持有
|
|
4
|
+
# (谁集成、谁持有自己的组件):
|
|
5
|
+
# - Cloudflare API MCP(OAuth)→ plugins/dsh-cloudflare
|
|
6
|
+
# - Google Workspace MCP → plugins/gws-mcp
|
|
7
|
+
# 之前放在这里的 `mcp-cfapi` / `mcp-cfdocs` 两行已于 2026-11-10 移除
|
|
8
|
+
# (Cloudflare 集成迁入 dsh-cloudflare 插件;cfdocs 已被 API MCP 的 docs 工具覆盖)。
|
|
9
|
+
#
|
|
10
|
+
# 注意:只有 `insert` 会新增行;带 `id` 的顶层条目是"覆盖已有行"语义。
|
|
11
|
+
- insert:
|
|
12
|
+
- id: agentic-tool-catalog
|
|
13
|
+
name: 'dsh-agentic-tool-catalog'
|
|
14
|
+
config:
|
|
15
|
+
# 分组是插件运行时按策略自动推导的(默认 rules):
|
|
16
|
+
# mcp__<server>__* → mcp-<server>;其余按工具名自动聚类,归不进组的进 others。
|
|
17
|
+
# discovery 源码里没有任何插件工具名;新增 MCP/插件工具不需要改这里。
|
|
18
|
+
#
|
|
19
|
+
# 唯一可能需要改的:某个新工具必须"永远可见、不能按需"时,往这里追加一行
|
|
20
|
+
# (内置常驻白名单:bash/fs/jobs/skill/ask/todo/web/present/plan/delegation/
|
|
21
|
+
# mcp-resources/Agent Teams 等):
|
|
22
|
+
# extraKeepResident:
|
|
23
|
+
# - some_new_tool
|
|
24
|
+
#
|
|
25
|
+
# 一次 search 最多加载 6 个工具(避免一次拉回 21 个 workspace 工具)。
|
|
26
|
+
maxResults: 6
|
|
27
|
+
# 生产:唯一策略 = 真 agent 自动分组(目录变化时后台生成一次、按目录指纹缓存、
|
|
28
|
+
# 跨重启磁盘缓存、last-known-good 降级、无外部模型依赖;旧策略已全部移除)。
|
|
29
|
+
# 详见 notes/2026-11-10-agent-strategy-production.md;benchmark 见 benchmark/discovery/。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Performance: with vs without Agentic Tool Catalog
|
|
2
|
+
|
|
3
|
+
Measured 2026-11-11 on DSH `0.2.2-rc.1` (desktop profile), model `deepseek-flash`, Google Workspace MCP online.
|
|
4
|
+
Same 5 tasks × 2 independent sessions per arm; verified independently.
|
|
5
|
+
|
|
6
|
+
- **baseline (without on-demand catalog)**: all 122 tools resident, every request carries every schema.
|
|
7
|
+
- **treatment (this plugin)**: 31 resident tools + a short catalog note; 92 deferred tools loaded on demand.
|
|
8
|
+
|
|
9
|
+
| Metric | baseline | treatment | Change |
|
|
10
|
+
|---|---:|---:|---:|
|
|
11
|
+
| Task pass rate | 100% (10/10) | 100% (10/10) | equal |
|
|
12
|
+
| First-request tools | 122 | 31 | −74.6% |
|
|
13
|
+
| First-request tool-schema chars (payload) | 131,390 | 21,877 | **−83.3%** |
|
|
14
|
+
| **First-request real prompt tokens** (usage, not estimate) | **49,504** | **18,536** | **−62.6% (−30,968 tokens/request)** |
|
|
15
|
+
| Max request schema chars | 131,390 | 34,785 | −73.5% |
|
|
16
|
+
| Avg total tokens / session | 452,463 | 268,551 | **−40.6%** |
|
|
17
|
+
| Avg steps | 7.40 | 7.90 | +6.8% |
|
|
18
|
+
| Avg cost / session | $0.0112 | $0.0116 | +3.7% (≈flat) |
|
|
19
|
+
| Avg wall time | 37.0s | 33.3s | −9.9% |
|
|
20
|
+
| Avg `search_tools` calls | 0.10 | 1.30 | +1.2/session |
|
|
21
|
+
|
|
22
|
+
**Headline**: same task success, **−83.3% tool-schema payload, −62.6% real first-request prompt tokens
|
|
23
|
+
(−30,968 tokens/request)**, and **−41% total session tokens**; the cost is ~1.2 extra catalog searches per session and +7% steps.
|
|
24
|
+
Cost/wall differences are within run-to-run noise.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Quality (same runs, from the discovery output):
|
|
28
|
+
|
|
29
|
+
| Metric | baseline | treatment |
|
|
30
|
+
|---|---:|---:|
|
|
31
|
+
| Target capability hit | 10/10 | 10/10 |
|
|
32
|
+
| missedCapability | 0 | 0 |
|
|
33
|
+
| Searches / session | 0.10 | 1.30 |
|
|
34
|
+
| Setup searches before target call | 0 | 1.20 |
|
|
35
|
+
| Tools loaded / search | — | 9.2 |
|
|
36
|
+
| Loaded tools actually used | — | 1.9 |
|
|
37
|
+
| loadedUsedRatio | — | **0.289** |
|
|
38
|
+
|
|
39
|
+
Honest read: the catalog trades "see a little more" for "carry much less"; search currently loads ~9 tools and
|
|
40
|
+
uses ~2, so the loaded-used ratio is a tuning target (tighter `maxResults`/ranking), not something hidden here.
|
|
41
|
+
|
|
42
|
+
Notes:
|
|
43
|
+
- One-time generation (per catalog version, background) is not included in task metrics; it is cached in official
|
|
44
|
+
DSH Storage and reused across sessions/restarts, with incremental regeneration on catalog changes.
|
|
45
|
+
- This card does **not** claim cost savings: cost is ~flat (+3.7%); it saves context/tokens. Sample is 5 tasks ×
|
|
46
|
+
2 runs, one model (`deepseek-flash`), one profile — not a cross-model claim.
|
|
47
|
+
- Catalog generation is a one-time background cost and is **not** included in the task metrics; its exact
|
|
48
|
+
token/cost instrumentation is future work (no precise number is claimed).
|
|
49
|
+
- Tasks: `host-ops-session-list`, `docs-workers-limit`, `terminal-echo`, `goal-set`, `docs-d1-storage`.
|
|
50
|
+
`workspace-calendar-count` is excluded from the main table because it depends on MCP availability.
|
|
51
|
+
- First-request token figures come from real session usage (`inputTokens + cacheReadTokens`); schema chars are a
|
|
52
|
+
payload-size metric, **not** a chars/4 token estimate.
|
|
53
|
+
- Full methodology and raw results: `benchmark/discovery/data-card.md` and `benchmark/discovery/results/` in the
|
|
54
|
+
source repository.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# 性能数据卡:Agentic Tool Catalog(用 vs 不用)
|
|
2
|
+
|
|
3
|
+
> 目标:用**同一批任务、同一模型、同一工具面**回答一个问题——
|
|
4
|
+
> **装上 Agentic Tool Catalog,和"所有工具常驻上下文"相比,付多少、得多少?**
|
|
5
|
+
|
|
6
|
+
## 1. 对照设置
|
|
7
|
+
|
|
8
|
+
| | baseline(不用插件) | treatment(用插件) |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| 上下文形态 | 所有工具常驻:每次请求带全部 122 个工具 schema | 31 个常驻工具 + 一小段目录提示;延迟工具 schema 不进请求 |
|
|
11
|
+
| 配置 | 插件 `resident: ['*']`(目录为空,不搜索) | 生产配置(目录由 agent 生成并缓存,`fresh`) |
|
|
12
|
+
| 工具面 | 122 tools(含 Google Workspace MCP 22 个) | deferred 92 tools / 19 组,`catalog_status.storage=ready` |
|
|
13
|
+
| 模型 / 环境 | `deepseek-flash`(provider `deepseek-account`)、DSH `0.2.2-rc.1`、同一桌面 profile | 同左 |
|
|
14
|
+
|
|
15
|
+
- 任务集(5 个,每任务 2 轮独立会话,共 10+10 run):
|
|
16
|
+
`host-ops-session-list`、`docs-workers-limit`、`terminal-echo`、`goal-set`、`docs-d1-storage`;
|
|
17
|
+
- 所有任务由 `benchmark/discovery/run.mjs` 记录,`verify` 独立核验文件/结果;
|
|
18
|
+
- `workspace-calendar-count` 未纳入主表:它依赖 Google Workspace MCP 的可用性,属环境敏感任务;
|
|
19
|
+
- 指标来自 DSH 会话日志:首请求工具 schema 字符(官方 `metrics.firstRequestTools`)、tokens、成本、步数、墙钟、
|
|
20
|
+
`search_tools` 调用次数。
|
|
21
|
+
|
|
22
|
+
## 2. 结果(10 + 10 run,全部有效)
|
|
23
|
+
|
|
24
|
+
| 指标 | baseline(全量常驻) | treatment(按需目录) | 变化 |
|
|
25
|
+
|---|---:|---:|---:|
|
|
26
|
+
| 任务通过率 | **100%** (10/10) | **100%** (10/10) | 持平 |
|
|
27
|
+
| 首请求工具数 | 122 | 31 | **−74.6%** |
|
|
28
|
+
| 首请求工具 schema 字符(纯 schema 载荷) | 131,390 | 21,877 | **−83.3%** |
|
|
29
|
+
| **首请求真实 prompt tokens**(会话日志 usage,非估算) | **49,504** | **18,536** | **−62.6%(−30,968 tokens/请求)** |
|
|
30
|
+
| 最大请求 schema 字符(任务全程) | 131,390 | 34,785 | **−73.5%** |
|
|
31
|
+
| 平均总 tokens / 会话 | 452,463 | 268,551 | **−40.6%** |
|
|
32
|
+
| 平均步数 | 7.40 | 7.90 | +6.8% |
|
|
33
|
+
| 平均成本 / 会话 | $0.0112 | $0.0116 | +3.7%(≈持平) |
|
|
34
|
+
| 平均墙钟 | 37.0s | 33.3s | −9.9% |
|
|
35
|
+
| 平均 `search_tools` 调用 | 0.10 | 1.30 | +1.2 次/会话 |
|
|
36
|
+
|
|
37
|
+
**一句话**:任务成功率不变,**首请求工具 schema 载荷降 83.3%,首请求真实 prompt tokens 从 49,504 降到 18,536(−62.6%,约 −31k tokens/请求)**,会话总 tokens 降约 41%;
|
|
38
|
+
代价是每会话多约 1.2 次目录搜索、步数 +7%;成本与墙钟在噪声范围内(成本 +3.7%、墙钟 −9.9%)。
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## 3. 质量指标(同一批结果的 discovery 输出)
|
|
42
|
+
|
|
43
|
+
| 指标 | baseline(全量常驻) | treatment(按需目录) |
|
|
44
|
+
|---|---:|---:|
|
|
45
|
+
| 目标能力命中(targetToolCalled) | 10/10 | 10/10 |
|
|
46
|
+
| missedCapability | 0 | 0 |
|
|
47
|
+
| 搜索次数 / 会话 | 0.10 | 1.30 |
|
|
48
|
+
| 目标工具调用前的 setup 搜索 | 0 | 1.20 |
|
|
49
|
+
| 每次搜索平均加载工具数 | — | 9.2 |
|
|
50
|
+
| 加载后实际使用工具数 | — | 1.9 |
|
|
51
|
+
| loadedUsedRatio(加载后使用率) | — | **0.289** |
|
|
52
|
+
| 工具调用失败(环境摩擦,非插件失败) | 6 | 2 |
|
|
53
|
+
|
|
54
|
+
诚实解读:目录让模型"多看少带"——每次搜索会加载约 9 个工具、实际用到约 2 个;
|
|
55
|
+
**加载后使用率 0.289 说明搜索返回偏宽**,这是下一步可优化项(收紧 maxResults/排序或按组加载),
|
|
56
|
+
不是数据卡要掩盖的点。目标能力 10/10 命中、0 missed,说明"该找的工具都找到了"。
|
|
57
|
+
|
|
58
|
+
### 逐任务首请求 prompt tokens(真实 usage)
|
|
59
|
+
|
|
60
|
+
| 任务 | baseline | treatment | 变化 |
|
|
61
|
+
|---|---:|---:|---:|
|
|
62
|
+
| host-ops-session-list | 49,511 | 18,543 | −62.5% |
|
|
63
|
+
| docs-workers-limit | 49,516 | 18,548 | −62.5% |
|
|
64
|
+
| terminal-echo | 49,483 | 18,515 | −62.6% |
|
|
65
|
+
| goal-set | 49,477 | 18,509 | −62.6% |
|
|
66
|
+
| docs-d1-storage | 49,533 | 18,565 | −62.5% |
|
|
67
|
+
|
|
68
|
+
## 4. 一次性成本与持久化(不混入上表)
|
|
69
|
+
|
|
70
|
+
- 目录生成由 DSH 子代理完成,**每个目录版本一次**(后台,不阻塞任务);本机 92 工具目录的实测生成约 60–75s、
|
|
71
|
+
约 6–7 万 tokens 量级(≈ 一次生成),完成后按目录指纹缓存到**官方 DSH Storage**;
|
|
72
|
+
- 之后的会话/重启走缓存(`catalogOrigin=storage`),**零生成**;目录变化时增量重生成;
|
|
73
|
+
- 数据卡上表不含生成成本;生成失败不会静默:状态标 `stale`/`refreshing`,自动带反馈重试并从上次成功分组继续。
|
|
74
|
+
|
|
75
|
+
## 5. 可靠性与观测(同期验证)
|
|
76
|
+
|
|
77
|
+
- A/B 的 10 个 treatment run:首请求全部 `31 tools / 21,877 chars`,无 bootstrap/error/stale;
|
|
78
|
+
- 目录与缓存:`catalog_status` 显示 `fresh`、`origin=storage`、`storage.state=ready`;
|
|
79
|
+
- 一次真实故障演练(开发期 Google Workspace MCP 抖动)中,生成校验失败 4 次、按预期走重试/
|
|
80
|
+
最近一次成功分组(`staleUses=5`),**没有静默隐藏工具**;MCP 恢复后目录继续 `fresh`。
|
|
81
|
+
|
|
82
|
+
## 6. 边界与复现
|
|
83
|
+
|
|
84
|
+
- 收益主要来自"延迟工具 schema 不进每次请求";工具越多、MCP/扩展越多,收益越大;工具很少(<10)时收益有限;
|
|
85
|
+
- 对照依赖同一工具面:需要 Google Workspace MCP 在线(本卡 122 工具面);MCP 掉线会改变首请求工具数,
|
|
86
|
+
本卡使用的是工具面一致的 10–4x 批次;
|
|
87
|
+
- 首请求 tokens 来自会话日志真实 usage(非 chars/4 估算);schema 字符是载荷指标。成本/墙钟受模型与网络影响,应看多轮重复;
|
|
88
|
+
- 复现:`node benchmark/discovery/card.mjs --baseline baseline-resident --treatment agent-catalog --tasks host-ops-session-list,docs-workers-limit,terminal-echo,goal-set,docs-d1-storage --markdown`
|
|
89
|
+
原始结果在 [`results/`](results/),方法说明 [`benchmark/discovery/README.md`](README.md)。
|
|
90
|
+
|
|
91
|
+
## 7. 和竞品怎么比
|
|
92
|
+
|
|
93
|
+
- 竞品(`dsh-tool-search`、`dsh-tool-activator`、`dsh-capability-catalog`、`dsh-mcp-lens` 等)多数以
|
|
94
|
+
**搜索/手动激活/人工登记**组织工具;本插件差异是 **agent 自动生成并维护语义目录**,
|
|
95
|
+
本卡只回答"用 vs 不用",不做无法公平对齐的跨插件数字对比;定性差异与竞品调研见
|
|
96
|
+
[`notes/2026-11-11-plugin-naming-interface.md`](../../notes/2026-11-11-plugin-naming-interface.md)。
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<svg width="760" height="330" viewBox="0 0 760 330" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="760" height="330" rx="20" fill="#F5F7FB"/>
|
|
3
|
+
<text x="32" y="48" font-family="system-ui, -apple-system, sans-serif" font-size="16" font-weight="700" fill="#1F2329">With vs without Agentic Tool Catalog</text>
|
|
4
|
+
<text x="32" y="70" font-family="system-ui, -apple-system, sans-serif" font-size="11.5" fill="#6B7280">Same tasks · same model · same tool surface · verified independently · 10 runs per arm</text>
|
|
5
|
+
|
|
6
|
+
<!-- schema payload -->
|
|
7
|
+
<text x="32" y="112" font-family="system-ui, -apple-system, sans-serif" font-size="12" fill="#1F2329">First-request tool-schema payload</text>
|
|
8
|
+
<rect x="290" y="100" width="300" height="14" rx="7" fill="#D8DEE7"/>
|
|
9
|
+
<text x="600" y="112" font-family="system-ui, -apple-system, sans-serif" font-size="11.5" fill="#6B7280">131,390 chars</text>
|
|
10
|
+
<rect x="290" y="120" width="50" height="14" rx="7" fill="#145AF3"/>
|
|
11
|
+
<text x="350" y="132" font-family="system-ui, -apple-system, sans-serif" font-size="11.5" fill="#145AF3">21,877 chars · −83.3%</text>
|
|
12
|
+
|
|
13
|
+
<!-- prompt tokens -->
|
|
14
|
+
<text x="32" y="172" font-family="system-ui, -apple-system, sans-serif" font-size="12" fill="#1F2329">First-request prompt tokens (real usage)</text>
|
|
15
|
+
<rect x="290" y="160" width="300" height="14" rx="7" fill="#D8DEE7"/>
|
|
16
|
+
<text x="600" y="172" font-family="system-ui, -apple-system, sans-serif" font-size="11.5" fill="#6B7280">49,504</text>
|
|
17
|
+
<rect x="290" y="180" width="112" height="14" rx="7" fill="#145AF3"/>
|
|
18
|
+
<text x="412" y="192" font-family="system-ui, -apple-system, sans-serif" font-size="11.5" fill="#145AF3">18,536 · −62.6%</text>
|
|
19
|
+
|
|
20
|
+
<!-- total tokens -->
|
|
21
|
+
<text x="32" y="232" font-family="system-ui, -apple-system, sans-serif" font-size="12" fill="#1F2329">Total tokens per session</text>
|
|
22
|
+
<rect x="290" y="220" width="300" height="14" rx="7" fill="#D8DEE7"/>
|
|
23
|
+
<text x="600" y="232" font-family="system-ui, -apple-system, sans-serif" font-size="11.5" fill="#6B7280">452,463</text>
|
|
24
|
+
<rect x="290" y="240" width="178" height="14" rx="7" fill="#145AF3"/>
|
|
25
|
+
<text x="478" y="252" font-family="system-ui, -apple-system, sans-serif" font-size="11.5" fill="#145AF3">268,551 · −40.6%</text>
|
|
26
|
+
|
|
27
|
+
<rect x="32" y="280" width="696" height="28" rx="8" fill="#E9EFFB"/>
|
|
28
|
+
<text x="46" y="295" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#33507A">Task success: 100% both arms · cost per session ≈ flat (+3.7%)</text>
|
|
29
|
+
<text x="46" y="309" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#33507A">One-time background catalog generation is not included in these task figures.</text>
|
|
30
|
+
</svg>
|