<!-- DOC_VERSION: final -->
# dsh 标准工具插件（Standard ToolKit）设计文档 v2.3-final

> 定位：全局智能工具管家。暂存区隔离 + 按需加载 + 会话级生命周期。
> 定稿依据：三轮评审（架构/API/简化）闭环；引擎硬约束均已源码级验证。
> final 修订：修正自动匹配候选范围（预置工具可被自动加载）；恢复 session/disposed 触发时机 spike。

## 1. 核心设计原则

- **暂存区**：工具默认存入内存仓库（`Map<id@version, Definition>`），不进入 dsh 主注册表，零 Schema 开销。
- **可见性**：Allow 白名单（`restrict`）。默认仅暴露核心 4 工具（bash/fs/todo/subagent）。
- **隔离**：动态工具注册在 agent 层（`agent.ctx.tools.register`），随会话销毁，天然隔离，不污染全局。
- **加载**：仅两种途径——自动匹配（pre-step）或模型调用元工具（load_tool）。
- **候选范围（final 修正）**：自动匹配的候选 = 暂存区工具 ∪ **全局预置扩展工具**（cordis.yml 已注册但被 allow 遮罩的）− 本会话已加载。预置工具不重复入库，仅进匹配索引。

## 2. 架构骨架

- **分类树**：扫描 `toolbox/` 一级子目录动态生成（无 manifest.json），默认兜底：core/file/network/media/system/custom。
- **暂存区**：`Map<id@version, {schema, executor, metadata}>`。仅存储外部发现 + register_new_tool 产物。
- **雷达**：启动时 `schemas()` 快照 + 监听 `tools/change`（无 payload → re-schemas diff）。
- **元工具（模型可见，仅 2 个）**：`load_tool(name)`、`register_new_tool(def)`。

## 3. 引擎硬约束（源码验证）

1. **restrict 必须挂 agent.ctx**（全局调用抛异常，index.ts:1073-1075）→ 过滤器天然会话隔离。
2. **restrict 仅接受全局注册名**（restrictableNames，index.ts:1088-1092；未知名抛异常）→ 全局工具才能被遮罩；**agent 层工具（local）不受 restrict 管理，但天然仅本 agent 可见**。
3. **restrict 为 AND 叠加**（index.ts:740-741）→ 更新可见性必须 **dispose 旧过滤器 → 注册新过滤器**（单过滤器替换）。

**两层工具模型**：

| 层 | 注册方式 | 可见性 | restrict | 生命周期 |
|---|---|---|---|---|
| 全局层 | cordis.yml 预置 | 所有会话可见（可被 allow 遮罩） | ✅ 可管理 | 进程级 |
| agent 层 | register_new_tool 产物 / 现场加载 | 天然仅本 agent 可见 | ❌ 不需要 | 随会话销毁 |

## 4. 关键流程（tryRegister）

输入：工具名 / 定义。顺序（引擎强制，不可颠倒）：

1. **预检**：执行工具声明的 `check(ctx)`（依赖/版本/显存/端口）。失败 → 返回结构化错误，不加载。
2. **注册分流**：
   - 全局层（cordis.yml 预置）：跳过注册，直接进入步骤 3。
   - agent 层（动态/新造）：`agent.ctx.tools.register(def)`（随会话销毁）。
3. **可见性控制**：
   - 全局层工具：dispose 旧会话过滤器 → `restrict({ allow: [core4, ...新工具] })`。
   - agent 层工具：无需操作（引擎保证本 agent 可见）。
4. **冒烟**（P2 完善）：失败则 disposer() 回滚注册 + Allow 回退。
5. **全异常捕获** → 归一化返回 `{ ok, reason }`，绝不冒泡崩进程。

## 5. 自动匹配（pre-step）

- 取**最后一条用户消息**。
- 候选范围（final 修正）：**暂存区 ∪ 全局预置扩展工具 − 本会话已加载**（已加载缓存 `Map<sessionId, Set>`，P1 实现）。
- 匹配规则：关键词命中（triggerKeywords 命中≥2 或强关键词≥1）+ 置信度阈值。
- 命中 → 调用 tryRegister。未命中 → 跳过（零开销）。
- P2 优化：关键词倒排索引。

## 6. 安全与可靠性

- **register_new_tool**：Node vm 沙箱执行。注入受限门面 `{ fs, web, bash }`（非完整 ctx），禁止 process/require/global；`this` 显式绑定门面。
- **依赖声明**：元数据支持 `dependencies`、`resources`、`permissions`（挂载 sandbox-policy）。
- **状态接力**：`Map<sessionId, Map<key, value>>`，会话清理时销毁。
- **版本策略**：P1 单版本优先（id@version 键，别名指向最新）。P2 处理兼容性与升级通知。

## 7. 生命周期与清理

- **创建**：`ctx.agents.create({sessionId})` → 独立 agent 实例（server.ts:223）。
- **卸载**：监听 `session/disposed`（源码已存在，agent-loop:442-443）。agent 层注册的工具与过滤器随 agent 自动销毁；插件钩子负责清理状态仓与资源释放（如显存）。
- **持久化**：暂存区、分类树、目录索引跨会话保留。

## 8. 安装与配置

```text
standard-toolkit/
├── package.json          # postinstall 系统依赖
├── index.js              # apply(ctx, opts)
└── toolbox/<category>/   # 工具定义文件（schema/executor/metadata/check）
```

```yaml
- id: standard-toolkit
  name: './runtime/standard-toolkit'
  config:
    mode: auto            # auto | manual（模型不可见）
```

## 9. 阶段计划

- **P1（核心）**：分类树 + 暂存区 + allow 遮罩 + pre-step 匹配（已加载缓存）+ 依赖检查 + load_tool + agent 层注册验证。
- **P2（管家）**：外部扫描器 + 自动分类器 + 倒排索引 + 状态接力 + 完整回滚 + 版本管理。
- **P3（治理）**：显存释放 + Worker 沙箱 + 热重载。

## 10. 启动前 Spike（P1 必做）

1. **agent 层注册的工具是否对模型天然可见？**（决定 register_new_tool 是否需要手动处理可见性）
2. **pre-step 的 messages 结构**（提取最后一条用户文本）。
3. **agents.create 返回对象 → 获取 agent.ctx 的实际路径**（register/restrict 调用点）。
4. **session/disposed 在 jsonrpc-demo 的触发时机**（final 恢复：shutdown 全清 vs 会话结束即触发——决定"会话结束即卸载"是否成立、状态仓清理挂点）。
