# dsh-sandbox-escalation-fix(已支持 DSH 0.1.1-rc.1) [English](README.md) | 中文 > [!IMPORTANT] > 这是独立开发的社区插件,不是 DeepSeek 官方发布、维护或背书的插件。它不会修改 DeepSeek Harness 的核心代码。 > [!CAUTION] > DSH `0.1.0-rc8` & `0.1.1-rc.1` 已通过 `approval=never` 的运行时提示对错误升级问题做了部分改善,但 DSH `0.1.1-rc.1` 仍使用相同的静态升级 Schema 和执行期校验。**建议 `0.1.0-rc8` 与 `0.1.1-rc.1` 用户先观察原生行为,仅在实际遇到本文所列的同模式升级、空 justification 或反复重试问题后再安装本插件。** **dsh-sandbox-escalation-fix** 是一个零配置兼容插件,直接解决 GPT 等第三方模型在 DSH All Access 下调用 `bash`、`pwsh`、`write`、`edit` 等工具时,因为错误的沙箱升级参数提示而调用失败、反复重试的问题。 如果你遇到过下面这些错误,这个插件就是针对它们的: ```text Error: invalid justification: expected a non-empty sentence Error: sandbox escalation to "danger-full-access" is not strictly wider than this call's current "danger-full-access" mode Error: sandbox escalation to "workspace-write" is not strictly wider than this call's current "danger-full-access" mode ``` ## 目录 - [这是什么?](#这是什么) - [它能解决什么问题?](#它能解决什么问题) - [安装前后对比](#安装前后对比) - [为什么选择它?](#为什么选择它) - [与其他“仅执行期止血”方案的区别](#与其他仅执行期止血方案的区别) - [快速使用](#快速使用) - [Release 一键安装与卸载](#release-一键安装与卸载) - [升级插件](#升级插件) - [手动覆盖安装(Windows)](#手动覆盖安装windows) - [命令行安装](#命令行安装) - [验证修复](#验证修复) - [人工测试清单](#人工测试清单) - [验证证据](#验证证据) - [插件行为一览](#插件行为一览) - [与其他包装插件协作](#与其他包装插件协作) - [卸载](#卸载) - [故障排查](#故障排查) - [支持范围](#支持范围) - [开发验证](#开发验证) ## 这是什么? 一句话说明:**这个插件让 DeepSeek Harness 只把“当前会话真正能用”的沙箱升级选项展示给模型。** 在 `danger-full-access + approval=never` 这类 All Access 会话里,DSH 原版仍然会在 `bash`、`pwsh`、`write`、`edit` 工具中向模型展示 `sandbox_permissions` 和 `justification` 两个参数。但此时: - 当前权限已经是最高级,没有更宽的权限可以升; - 审批策略又是 `never`,所有审批请求都会被自动拒绝。 于是模型一旦填写这两个参数,调用就会在执行前失败,然后模型可能反复重试,陷入“报错 → 换参数 → 再报错”的循环。 本插件会根据**每个 Session 当前的 Sandbox Mode 和 Approval Policy**,动态调整模型看到的工具参数和说明;同时在执行前做一层最小兜底,把同模式的冗余请求安全地处理掉。 ## 它能解决什么问题? - 模型在 All Access 下仍能看到并填写 `sandbox_permissions` / `justification`,导致工具还没执行就报错。 - 模型在 `workspace-write` 下看到两个升级目标,但其实只有 `danger-full-access` 是真正更宽的选项。 - 模型在 `approval=never` 下仍然被提示“可以升级”,但所有升级都会被拒绝。 - 工具描述和错误结果里继续出现“升级可用”的提示,诱导模型反复重试。 - Native Tool Call 和 Code Mode SDK 展示不一致,一边修好了、另一边还在误导模型。 ### 问题成因 DSH 工具注册时会公开静态的升级字段,但真正可以请求的升级目标取决于每个 Session 当前的 Sandbox Mode 和 Approval Policy。原始模型可见 Schema 在构造请求前没有根据实时会话状态重新投影,因此即使当前权限已经最高或审批已被禁止,模型仍可能收到无法成功执行的升级参数。工具随后会在执行前拒绝这些请求,模型便可能进入反复修改参数并重试的循环。 ## 安装前后对比 未安装插件时,受影响的 All Access Session 可能在实际操作开始前反复失败。模型会在空白 `justification`、同模式 `danger-full-access` 请求,甚至会被 DSH 正确拒绝的降级请求之间循环。 ### 安装前:参数校验与升级错误反复出现 ![安装前:invalid justification 与非严格变宽的 Sandbox 升级错误反复出现](assets/before-errors-overview.png) ![安装前:Edit 与 Pwsh 在完成实际工作前反复失败](assets/before-repeated-errors.png) ### 安装后:工具可以连续完成工作流 安装插件后,同一模型可以连续执行 Edit、Read、Pwsh、格式化、测试、Lint 和 Type Check,不再进入无效升级循环。 ![安装后:Edit、Read 与 Pwsh 连续完成多步骤开发工作流](assets/after-successful-tools.png) ## 为什么选择它? ### 它解决的是根因,而不是只让报错消失 很多修复方案只在模型已经拿到错误 Schema 之后,于执行前把参数删掉。这样做调用虽然不报错了,但模型仍然会继续看到、继续发送这些无效参数。 本插件会按每个 Session 的实时权限状态,动态投影模型真正可见的工具参数: | 当前模式 | 审批策略 | 模型看到的结果 | |---|---|---| | `read-only` | `ask` | `workspace-write`、`danger-full-access` | | `workspace-write` | `ask` | 仅 `danger-full-access` | | `danger-full-access` | `ask` | 不展示升级参数 | | 任意模式 | `never` | 不展示升级参数 | 模型看不到当前不可能成功的参数,自然就不会去使用它。 ### 它不会只修 Native Tool Call,而漏掉 Code Mode 插件的参数投影发生在 Agent 精确作用域的工具定义上。原生工具调用和 Code Mode SDK 读取的是同一份投影结果: - 原生工具 Schema 里不会出现无效升级字段; - Code Mode 生成的 TypeScript/Python SDK 里同样不会出现; - 两种模式的行为始终一致。 不会出现“原生调用已经修好,模型走 `run_code` 仍然看到并误用旧参数”的半成品状态。 ### 它不会静默吞掉降级请求 执行兜底只做一件非常克制的事:当且仅当 `requestedMode === effectiveMode` 时,把冗余参数视为幂等请求并移除,然后按普通调用执行。 对于降级请求或非法值,插件会原样保留,交给 DSH 原有的安全逻辑处理: - `danger-full-access` 下请求 `danger-full-access` → 忽略,正常执行; - `danger-full-access` 下请求 `workspace-write` → 不会被擅自当成 Full access 执行; - `read-only` 下请求更宽模式 → 原样进入审批流程。 它不会为了“少报一个错”,就把调用者显式要求的更窄权限静默替换成更宽权限。 ### 它不会自动填充一个虚假的审批理由 对于真正需要升级的请求,插件不会自动编造或填充占位 justification。缺失、空白或非法的理由仍然由 DSH 原有校验逻辑处理,保证审批流程看到的信息真实、可审计。 ### 它不会一边说“不要升级”,一边又在结果里提示“升级可用” 当会话没有合法升级目标时,插件除了隐藏参数,还会同步处理模型看到的自然语言: - 工具描述中的升级引导段落会被裁剪; - Shell、文件工具和 `job_output` 结果中已不成立的 `escalation available` 提示会被清理。 模型看到的参数、描述和执行结果不会再互相矛盾。 ### 它不会修改或绕过 DSH 的安全核心 `approveEscalation()` 的严格变宽检查、审批流程、一次性授权语义都保持原样。插件只做模型可见面的投影和调用前的最小兼容处理: - 不删除严格变宽检查; - 不在 `approval=never` 时自动批准升级; - 不授予任何额外权限; - 不修改 DSH 安装目录或核心包。 ### 它不会把同一进程里的不同 Session 一刀切 插件按 Agent/Session 独立包装,不修改全局工具注册表。因此同一个进程中: ```text Session A = read-only + ask → 看到两个升级目标 Session B = danger-full-access + never → 看不到升级字段 ``` 各自互不影响。Session 中途切换权限后,下一次模型请求的工具 Schema 也会立即按新状态重新计算。 ### 它不会在 Agent 销毁或工具替换后留下残留包装 插件完整监听 Agent 创建、销毁、Preset 切换、动态限制和工具变更事件。动态 Preset 调用 `agent.ctx.tools.restrict()` 时,对应的 Agent Exact Scope 包装器会与被限制的父工具同步退出;限制解除后自动恢复投影包装。Agent 创建时不可见的目标工具以后重新出现,也会自动接入。每个 Agent 独立协调,Agent 销毁或插件卸载后恢复原始定义。 ### 它不会只支持单一 DSH 版本 插件同时支持 DSH `0.1.0-rc.5`、`0.1.0-rc.6`、`0.1.0-rc.7`、`0.1.0-rc.8` 和 `0.1.1-rc.1`,并在加载时校验 DSH 各包版本是否一致且受支持。遇到不兼容的工具定义会主动拒绝安装,而不是在运行中产生难以排查的诡异行为。 ### 它不会给你增加配置负担 零配置,安装到实际使用的 Profile 后即可生效。测试覆盖 28 项,包含真实 DSH 包的集成验证,覆盖 Schema 投影、Code Mode SDK、动态限制、多 Agent 隔离、Delegate 与协作包装器替换、内部超时预算透传、失败提示清理和插件卸载等关键路径。 ## 与其他“仅执行期止血”方案的区别 | 能力 | 本插件 | 仅执行期参数正规化 | |---|---|---| | 从 Native Tool Schema 隐藏不可执行升级字段 | 支持,按 Session 动态投影 | 不支持 | | 从 Code Mode SDK 隐藏相同字段 | 支持,与 Native 读取同一 Agent Exact Scope 定义 | 不支持 | | 只删除精确同模式的冗余请求 | 支持 | 取决于实现 | | 保留显式降级和非法请求给 DSH 校验 | 支持 | 不保证 | | 保留缺失或空白 `justification` 给 DSH 校验 | 支持 | 不保证 | | 清理描述与结果中的无效升级建议 | 覆盖 Shell、FS、Code Mode、`job_output` | 不支持 | | 响应 Agent、Preset 和工具生命周期变化 | 支持 | 取决于实现 | ## 快速使用 插件为零配置修复。安装到实际使用的 Profile 后,继续按原方式启动 DSH 即可: ```sh dsh --profile ``` 无需修改模型配置、Sandbox Mode、Approval Policy 或 Agent Preset。插件会按每个 Session 的当前权限状态动态决定模型可见参数。 ## Release 一键安装与卸载 `0.1.1-rc1` Release 包通过 DSH CLI 管理插件,不需要手动编辑 Profile Patch。原有 `v0.1.2` Release 保留,继续提供给 DSH rc8 用户。新版 Release ZIP 解压后包含以下四个文件: ```text dsh-sandbox-escalation-fix-0.1.1-rc1.tgz install-release.ps1 uninstall-release.ps1 RELEASE-USAGE.zh.md ``` 安装或卸载前先完全关闭 DSH。双击脚本前,请确认系统已将 `dsh` 命令加入 PATH,且当前 DSH 使用的是 rc5、rc6、rc7、rc8 或 `0.1.1-rc.1`。rc8 与 0.1.1-rc.1 用户建议先实际复现同类错误,再决定是否安装。 ### 安装到默认 Web Profile 在 Release 目录打开 PowerShell,执行: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File ".\install-release.ps1" ``` 脚本会定位同目录中唯一的 `.tgz` 文件,然后执行: ```powershell dsh plugin --profile web add ``` DSH CLI 会将插件安装到 `web` Profile,并在 pnpm 成功后自动把插件的 `cordis.patch.yml` 加入 Profile Bundle 层。安装完成后重启 DSH。 ### 安装到其他 Profile 例如安装到 `headless`: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File ".\install-release.ps1" -Profile headless ``` ### 从默认 Web Profile 卸载 在同一 Release 目录执行: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File ".\uninstall-release.ps1" ``` 脚本会执行: ```powershell dsh plugin --profile web remove dsh-sandbox-escalation-fix ``` DSH CLI 会移除 Profile 依赖,并同步移除对应 Bundle 层;完成后重启 DSH。 ### 从其他 Profile 卸载 例如从 `headless` 卸载: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File ".\uninstall-release.ps1" -Profile headless ``` ### 发布者构建 Release 目录 在源码根目录执行: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File ".\build-release.ps1" ``` 该脚本会先构建 `lib`,再执行 `npm pack` 生成 `.tgz`,最后在 `release` 目录生成 `dsh-sandbox-escalation-fix-0.1.1-rc1-release.zip`。ZIP 内含 tarball、两个一键脚本和简明中文使用说明;上传 GitHub Release 时只需上传该 ZIP。 ## 升级插件 升级前必须关闭 DSH。插件包名、Bundle ID 和 Profile Patch 配置行均未改变,已经安装旧版的用户不需要再次修改 `cordis.patch.yml`。 ### 通过 GitHub Commit 安装的用户 将原安装命令中的 Commit SHA 换成新的、已经审核过的 SHA,再执行同一条命令: ```sh dsh plugin --profile add github:/dsh-sandbox-escalation-fix# ``` 该命令会更新 Profile 依赖并重新构建插件。如果 pnpm 要求 `allowBuilds`,继续保留原有配置即可。安装完成后检查 `--dump-config`,然后重新启动 DSH。 ### 手动安装到 Web Profile 的用户 准备包含新版 `lib` 的仓库或发布包,在插件目录中打开 Windows PowerShell,然后执行: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File ".\deploy-web-profile.ps1" ``` 脚本优先使用 `DSH_HOME`;未设置时使用 `%USERPROFILE%\.dsh`。它只覆盖 8 个发布用 `lib` 文件并逐一比较 SHA-256;只有部署目录与新版构建完全一致时,最后一行才会显示: ```text Deployment verified. ``` 脚本不会修改 Profile Patch,也不会复制 `node_modules`。验证成功后重新启动 DSH。 ## 手动覆盖安装(Windows) 这是 Windows 下不使用插件安装命令的推荐方式。它只修改指定 Profile,不修改 DSH 安装目录或核心包。 需要更短的逐文件教程时,可查看 [奶龙也能看懂的食用说明.txt](奶龙也能看懂的食用说明.txt);其英文对应版为 [Tutorials that even Peppa Pig can understand](Tutorials%20that%20even%20Peppa%20Pig%20can%20understand)。 ### 开始前先确认 - 本教程中的“Profile 根目录”不是 `.dsh`,而是当前实际启动的 Profile 目录。 - 使用 Web Profile 时,Profile 根目录就是 `%USERPROFILE%\.dsh\profiles\web`。 - 要修改的是 `profiles\web\cordis.patch.yml`;`.dsh` 根目录下没有该文件并不异常。 - 可以直接复制整个 `dsh-sandbox-escalation-fix` 文件夹,但复制后必须删除插件目录内部的 `node_modules`。 - Profile Patch 使用 YAML 块列表;不要给配置加引号,也不要自行添加 `[` 或 `]`。 ### 1. 关闭 DSH 退出正在运行的 Web、桌面端或 Headless 进程,避免旧插件文件仍被 Node.js 占用。 ### 2. 打开 Profile 目录 默认 Harness Home 是: ```text C:\Users\<你的用户名>\.dsh ``` 目标 Profile 目录是: ```text C:\Users\<你的用户名>\.dsh\profiles\ ``` 例如 Web Profile 通常是: ```text C:\Users\<你的用户名>\.dsh\profiles\web ``` 可直接在 Windows 资源管理器地址栏输入: ```text %USERPROFILE%\.dsh\profiles\web ``` 如果配置过 `DSH_HOME`,请把上面的 `C:\Users\<你的用户名>\.dsh` 换成实际目录。若 `profiles\web` 尚不存在,先正常启动一次 DSH Web,让它初始化 Profile。 注意:这里说的是 Profile 根目录 `%USERPROFILE%\.dsh\profiles\web`,**不是** `%USERPROFILE%\.dsh`。看到 `.dsh` 根目录没有 `cordis.patch.yml` 是正常的,不需要创建;继续进入 `profiles\web` 修改其中已有的文件即可。 ### 3. 覆盖插件目录 在目标 Profile 中创建以下目录: ```text \node_modules\dsh-sandbox-escalation-fix ``` Web Profile 的默认目标目录是: ```text %USERPROFILE%\.dsh\profiles\web\node_modules\dsh-sandbox-escalation-fix ``` 如果该目录已经存在,先将整个目录改名为 `dsh-sandbox-escalation-fix.backup`。 然后从本项目目录复制以下内容到新的 `dsh-sandbox-escalation-fix` 目录: ```text package.json cordis.patch.yml README.md lib\ ``` 也可以直接把整个 `dsh-sandbox-escalation-fix` 文件夹复制到 Profile 的 `node_modules`,但复制完成后必须删除: ```text \node_modules\dsh-sandbox-escalation-fix\node_modules ``` 嵌套的 `node_modules` 可能携带另一套 Cordis 和 DSH 包,导致 Scope、Service 或错误类型来自不同实例。`src`、`tests`、`package-lock.json` 和 TypeScript 配置留在目标目录通常不会影响运行,只是没有必要。 复制完成后的结构应为: ```text \ ├── cordis.patch.yml ├── package.json └── node_modules\ └── dsh-sandbox-escalation-fix\ ├── package.json ├── cordis.patch.yml ├── README.md └── lib\ ├── index.mjs ├── index.d.mts ├── wrapper-protocol.mjs └── wrapper-protocol.d.mts ``` 注意: - 不要复制本项目的 `node_modules`。 - 不需要复制 `src`、`tests`、`tsconfig.json` 或构建工具配置。 - 手动安装包必须已经包含 `lib\index.mjs`;只有源码而没有 `lib` 的 ZIP 不能直接使用此方法。 - 不要把插件自己的 `cordis.patch.yml` 直接覆盖到 Profile 根目录;Profile 根目录中的同名文件可能包含其他用户配置,必须按下一步手动合并。 - 确认没有多套一层同名目录。 正确入口是: ```text \node_modules\dsh-sandbox-escalation-fix\package.json \node_modules\dsh-sandbox-escalation-fix\lib\index.mjs ``` 以下结构是错误的: ```text \node_modules\dsh-sandbox-escalation-fix\dsh-sandbox-escalation-fix\package.json ``` 更新已经安装的版本时,请直接按照[升级插件](#升级插件)操作,不要重复添加 Profile Patch。 ### 4. 修改 Profile Patch 用记事本或其他纯文本编辑器打开: ```text \cordis.patch.yml ``` 如果文件内容只有 `[]`,将其完整替换为: ```yaml - insert: - id: sandbox-escalation-fix name: dsh-sandbox-escalation-fix ``` 例如,初始文件为: ```yaml # Your patch layer for this dsh profile, applied after every bundle layer: # a top-level YAML array of loader patch entries (id-targeted config # overrides, disables, and insert lists; `!!js` expressions allowed). [] ``` 修改后的完整文件应为: ```yaml # Your patch layer for this dsh profile, applied after every bundle layer: # a top-level YAML array of loader patch entries (id-targeted config # overrides, disables, and insert lists; `!!js` expressions allowed). - insert: - id: sandbox-escalation-fix name: dsh-sandbox-escalation-fix ``` 顶部注释可以保留,只替换最后一行 `[]`。 以下两种写法都**不要**使用: ```yaml [- insert: - id: sandbox-escalation-fix name: dsh-sandbox-escalation-fix ``` ```yaml - insert: - id: sandbox-escalation-fix name: dsh-sandbox-escalation-fix] ``` 正确写法不带方括号、不带引号,且 `- insert:` 必须顶格。请使用空格缩进,不要使用 Tab。 如果文件已有其他配置,不要覆盖原内容。在文件末尾追加同一个顶层 Patch: ```yaml - insert: - id: sandbox-escalation-fix name: dsh-sandbox-escalation-fix ``` 同一个文件中只能保留一个 `id: sandbox-escalation-fix`,不要重复追加。此手动方式不要求修改 Profile 的 `package.json`,因为插件由 Profile Patch 直接加载。 ### 5. 重启并验证 按原来的方式启动该 Profile,新建 Session,然后在 All Access 下要求模型执行简单 Shell 命令或写入文件。 修复生效时,OAI 系列模型不再因为重复发送 `danger-full-access` 升级参数而导致工具调用失败。若仍看到旧行为,请确认修改的是当前实际启动的 Profile,并彻底退出 DSH 后重新启动。 ### 安装后检查表 - [ ] `profiles\web\cordis.patch.yml` 中只有一个 `id: sandbox-escalation-fix`。 - [ ] Patch 中没有 `[`、`]` 或包住整段配置的引号。 - [ ] 插件的 `package.json` 直接位于 `node_modules\dsh-sandbox-escalation-fix` 下。 - [ ] 插件目录包含 `lib\index.mjs`。 - [ ] 插件目录内部不存在第二个 `node_modules`。 - [ ] DSH 已完全退出并重启。 - [ ] 测试使用的是重启后新建的 Session。 ### 手动更新 关闭 DSH,将新的 `package.json`、`cordis.patch.yml`、`README.md` 和整个 `lib` 目录覆盖到 Profile 的插件目录即可。Profile Patch 已存在时不需要再次修改。 ### 手动回退 1. 关闭 DSH。 2. 从 `\cordis.patch.yml` 删除 `sandbox-escalation-fix` 的整个 `- insert:` 块。 3. 删除 `\node_modules\dsh-sandbox-escalation-fix`。 4. 如果保留了 `.backup`,将其改回原名;否则直接重启 DSH。 ## 命令行安装 建议从可信仓库锁定 Commit SHA 安装到指定 Profile: ```sh dsh plugin --profile add github:/dsh-sandbox-escalation-fix# ``` 在发布到 GitHub 前,也可以从本地目录安装: ```sh dsh plugin --profile add D:/deepseek-harness/plugins/dsh-sandbox-escalation-fix ``` Git 安装会运行本包的 `prepare` 构建脚本。pnpm 10 首次可能拒绝执行;按 DSH 输出提示,在该 Profile 的 `pnpm-workspace.yaml` 中加入: ```yaml allowBuilds: dsh-sandbox-escalation-fix: true ``` 然后重新执行安装命令,并检查最终组合: ```sh dsh --profile --dump-config ``` 输出中应包含 `dsh-sandbox-escalation-fix` 层和 `sandbox-escalation-fix` 行。 ## 验证修复 Web 正常启动只能证明 Profile 组合成功、插件加载时没有导致进程退出;动态限制行为仍需按本节后续步骤验证。使用 DSH 源码仓库时,可在仓库根目录运行: ```powershell node --import tsx/esm apps/cli/src/bin.ts web ``` 请先确保受支持的 Node.js 已加入 PATH。看到以下输出后再继续测试: ```text dsh web: http://127.0.0.1:3080 ``` 1. 使用安装插件的 Profile 启动 DSH。 2. 选择原先在 All Access 下受影响的 OAI 系列模型。 3. 将权限设置为 All Access,对应 `danger-full-access` 与 Approval Policy `never`。 4. 新建 Session,让 Agent Scope 在插件已加载的状态下创建。 5. 要求模型执行一条简单 Shell 命令或写入工作区文件。 6. 如果 Preset 使用 `agent.ctx.tools.restrict()`,进入限制状态并确认对应工具消失;解除限制后确认工具无需重建 Agent 即可恢复。 修复生效时: - 模型可见的 `bash`、`pwsh`、`write`、`edit` Schema 不再包含 `sandbox_permissions` 和 `justification`。 - 模型即使发送与当前模式相同的冗余升级参数,工具也会删除这对参数后正常委托。 - Sandbox 拒绝结果不会继续提示一个当前策略无法执行的升级动作。 修改插件安装状态、Profile 或 Preset 后,建议新建 Session 验证,避免把旧 Agent Scope 的行为误认为当前配置。 ## 人工测试清单 每项测试建议使用重启后新建的 Session。涉及文件写入的项目使用专门的临时目录,完成后删除测试文件。 ### Web 与会话生命周期 - [ ] 刷新 Web 后,已有会话仍显示在原工作区。 - [ ] 依次点击至少三个已有会话;每个会话都能打开,不会从列表临时消失。 - [ ] 在两个工作区之间来回切换;列表和当前工作区标题同步变化。 - [ ] 在工作区 A 新建会话;新会话保留在工作区 A,且可以正常发送第一条消息。 - [ ] 在工作区 B 新建会话;新会话保留在工作区 B,不会跳回工作区 A。 - [ ] 刷新 Web 后,刚创建的会话仍存在并能重新打开。 - [ ] 关闭并重新启动 DSH 后,再次打开旧会话和新会话均正常。 ### OAI 模型与 All Access 将模型切换为受影响的 OAI 系列模型,并将 Access Mode 设为 All Access。 - [ ] 发送“使用 pwsh 输出当前目录”;工具成功执行,没有 `sandbox_permissions` 参数错误。 - [ ] 发送“使用 pwsh 输出数字 12345”;结果包含 `12345`,且没有无效升级提示。 - [ ] 发送“读取测试目录中的 sample.txt”;读取工具返回文件内容。 - [ ] 发送“创建 plugin-smoke.txt,内容为 smoke-test”;写入工具成功。 - [ ] 发送“将 plugin-smoke.txt 中的 smoke-test 改为 smoke-test-updated”;编辑工具成功。 - [ ] 再次读取该文件;结果为 `smoke-test-updated`。 - [ ] 删除人工测试产生的文件,确认工作区没有残留。 ### 权限矩阵 - [ ] `danger-full-access` + `never`:Shell、write、edit 正常执行,不出现要求再次升级到 `danger-full-access` 的提示。 - [ ] `workspace-write` + `ask`:工作区内写入正常;需要更高权限的操作仍可请求 `danger-full-access`。 - [ ] `read-only` + `ask`:读取正常;写入操作仍按 DSH 原策略请求 `workspace-write` 或更高权限。 - [ ] 任意 Mode + `never`:不会向模型提供无法获批的升级路径。 ### Preset 与 Code Mode - [ ] 在可用的两个 Agent Preset 之间切换后,新建 Session,pwsh 与文件工具仍正常。 - [ ] 使用会调用 `agent.ctx.tools.restrict()` 的动态 Preset;进入限制状态后,被限制工具立即从 Native Schema 和 Code Mode SDK 消失。 - [ ] 解除动态限制后,不重建 Agent 也能恢复相应工具,且恢复后的 Schema 仍按当前权限隐藏无效升级字段。 - [ ] 两个 Agent 使用不同动态限制时,一个 Agent 隐藏工具不会影响另一个 Agent。 - [ ] 如果使用 Code Mode,程序内调用 pwsh、读取和写入工具均正常,错误消息不包含无法执行的升级建议。 - [ ] 切回 Native Tool 模式后,工具行为保持一致。 ### 冲突与稳定性 - [ ] 连续新建和关闭多个 Session,Web 不出现会话临时消失或工作区无法切换。 - [ ] 执行工具失败时,失败应显示为当前工具错误,不应使会话从列表消失。 - [ ] 浏览器刷新后,所有会话和工作区状态仍可恢复。 - [ ] DSH 启动日志中没有 `has no scope key`、`installation failed` 或同名工具注册错误。 全部通过后,可认为会话生命周期、OAI All Access、Native/Code Mode 和主要权限路径均已人工验收。 ## 验证证据 自动测试直接使用真实 DSH `SessionStore`、`ToolRuntime`、`AgentRegistry`、`SandboxPolicyService`、`ApprovalService` 和 `SystemPrompt` 包,而不是只测试隔离 Mock。28 项测试覆盖: - 权限矩阵; - 精确同模式正规化; - Native Schema 投影; - Code Mode SDK 生成; - 动态 Preset 限制、解除和初始受限后自动发现; - 多 Agent 限制隔离; - Delegate 替换; - 普通包装器与协作协议包装器动态切换; - 运行期不兼容定义的单工具隔离与恢复; - 包装协作与冲突拒绝; - Shell / FS / `job_output` 提示过滤; - 版本检查; - 插件卸载失效。 自动测试不能替代真实模型 Provider 的 E2E 证据。上方人工清单用于验证完整 Web 会话生命周期和受影响的 OAI All Access 工作流。 ## 插件行为一览 | 场景 | 插件行为 | |---|---| | `danger-full-access` 或 Approval Policy 为 `never` | 不向模型广告 `sandbox_permissions` 与 `justification` | | `workspace-write` 且允许审批 | 只广告 `danger-full-access` | | `read-only` 且允许审批 | 广告 `workspace-write` 与 `danger-full-access` | | 模型发送与当前模式完全相同的冗余升级参数 | 包装器删除这一对参数后委托原工具 | | 降级、未知目标、缺少配对参数和真实升级请求 | 仍交给原工具严格验证 | | 无合法升级目标 | 删除 Shell 描述尾部的升级说明,并清理与 Sandbox Denial Marker 同时出现的无效提示 | | 动态 Preset 限制目标工具 | 对应 Exact Scope 包装器在同一次同步变更中退出 | | 限制解除或 Provider 恢复 | 自动重新建立投影包装,无需重建 Agent | | 运行期替换为不兼容定义 | 只让对应 Agent 的对应工具暂时不包装,记录诊断并等待兼容定义恢复 | ## 与其他包装插件协作 插件会占用每个 Agent Exact Scope 中的 `bash`、`pwsh`、`write`、`edit` 名称。 另一个包装插件只有在同名工具暴露 `Symbol.for('dsh.tool-wrapper.v1')` 协议时才能与本插件链式协作。包装层按 `priority`、`owner` 稳定排序。如果同名工具没有实现协作协议,Agent 注册会明确失败,避免静默改变包装顺序或产生错误语义;此时必须由用户选择保留其中一个插件。 协议类型可从以下入口导入: ```ts import { TOOL_WRAPPER_PROTOCOL, type WrapperLayer, type ToolWrapperProtocolV1, } from 'dsh-sandbox-escalation-fix/wrapper-protocol' ``` ## 卸载 ```sh dsh plugin --profile remove dsh-sandbox-escalation-fix ``` 卸载会移除本插件创建的包装 Host、包装层和结果过滤器。显式协作层必须随其自身插件生命周期释放;本插件 Host 卸载时,这些层也会一并失效。 卸载后可再次检查组合: ```sh dsh --profile --dump-config ``` 输出中不应再出现 `dsh-sandbox-escalation-fix` 层。 ## 故障排查 | 问题 | 处理方式 | |---|---| | 安装后没有生效 | 确认安装和启动使用同一个 `--profile`,并通过 `--dump-config` 检查插件层 | | `.dsh` 根目录没有 `cordis.patch.yml` | 这是正常的;Web Profile 应修改 `.dsh\profiles\web\cordis.patch.yml` | | 不知道 `[]` 怎么改 | 保留注释,只把独占一行的 `[]` 替换为不带方括号的 `- insert:` YAML 块 | | 复制了整个开发文件夹 | 可以保留,但必须删除目标插件目录内部的 `node_modules`,并确认没有多套一层同名目录 | | YAML 启动报错 | 检查 `- insert:` 是否顶格、是否误加了方括号或引号,以及缩进是否只使用空格 | | Git 安装构建失败 | 确认 Profile 的 `pnpm-workspace.yaml` 已允许构建 `dsh-sandbox-escalation-fix`,然后重新安装 | | 启动时报版本错误 | 不要混装 rc.5、rc.6、rc.7、rc.8 与 0.1.1-rc.1 包;让 Profile 中关键 `@deepseek-ai/dsh-*` 包保持同一版本 | | Agent 注册时报同名工具冲突 | 另一个插件已在 Agent Exact Scope 注册 `bash`、`pwsh`、`write` 或 `edit`,且未实现协作协议;只能卸载其中一个 | | 动态 Preset 隐藏了部分工具 | 这是正常行为;插件会镜像 `tools.restrict()`,限制解除后自动恢复包装 | | 日志出现动态协调警告 | 检查警告中目标工具的替换定义;其他工具和 Agent 会继续工作,兼容定义出现后自动恢复 | | 升级字段仍然出现 | 确认查看的是安装后新建 Session 的 Schema,并确认没有后加载的插件替换同名工具 | ## 支持范围 - Node.js `^22.19.0` 或 `>=24.0.0` - `@deepseek-ai/dsh-*` `0.1.0-rc.5`、`0.1.0-rc.6`、`0.1.0-rc.7`、`0.1.0-rc.8`、`0.1.1-rc.1` - `@deepseek-ai/cordis` `4.0.1` 插件针对这些版本的公开 Scope、ToolRuntime、Sandbox Policy 与 Approval Service 契约构建。Agent 初次创建时,已可见的目标工具定义或同 Scope 包装协议不兼容会严格拒绝该 Agent 注册。 启动时会读取关键 `@deepseek-ai/dsh-*` 包的实际版本;rc.5/rc.6/rc.7/rc.8/0.1.1-rc.1 混装或未知版本会拒绝启动。目标工具同时省略两个升级字段时视为已经安全。运行期的 Preset 限制或 Provider 稳定删除会让包装器进入休眠;运行期替换为字段残缺或输出定义不兼容的工具时,只隔离对应 Agent 的对应工具并记录警告,不会终止 Host 进程,后续兼容定义出现时自动恢复。 ## 开发验证 ```sh npm install npm test npm run build ```