DeepSeek Harness 的 bash 工具参数:目录五个,源码再展开两个

2026-08-16

先把前提摆清楚:deepseek-ai/deepseek-harness 这个仓库建立于 2026-08-13,我们采集事实的时间锚点是 2026-08-16,前后只差三天;根 package.json 里的版本是 0.1.0-rc.5,GitHub 上一个 Release 都没有,README 自述处于开发者预览阶段并明写未来会出现破坏兼容性的变更。下面提到的每一个参数名、每一处行号,都可能在你读到时已经变了,请以仓库当前内容为准。我们没有安装、没有运行、没有构建过这个项目,所有说法都来自仓库里的文档与源码文本。

现象:同一个工具,两处口径给出的参数个数不一样

仓库里有一份 docs/tool-catalog.md,1873 行,是工具目录。里面 bash 这一节从第 180 行开始,贴出的 JSON Schema 里 properties 只有五个键:commanddescriptiontimeoutMsworkdirrun_in_backgrounddocs/tool-catalog.md:184-213),required 里是 commanddescription 两个。

但源码 packages/shell/tool-bash/src/index.ts:258-271 处还有一段展开:当 escalationModes.length > 0 时,schema 里会再多出 sandbox_permissionsjustification 两个参数。也就是说,同一个工具,一处是五个参数,另一处在特定条件下是七个。

两处的具体位置都摆在上面了:一处是 docs/tool-catalog.md:184-213,一处是 packages/shell/tool-bash/src/index.ts:258-271。我们只陈述这处差异,不去推断哪一处更「对」,也不推断原因。

目录是生成物,而且它是「启动出来」的

理解这处差异,得先知道 docs/tool-catalog.md 是怎么来的。文件头两行原文标着 <!-- Generated by scripts/gen-tool-catalog.ts — do not edit by hand.Run \pnpm run gen-tool-catalog` to regenerate. —>docs/tool-catalog.md:1-2),另有 pnpm run verify-tool-catalog用于校验,属于doc-sync`。

关键在于它的生成方式和别的目录不一样。文件里自己写了原文(docs/tool-catalog.md:8):与 cordis catalog 那种纯源码 AST 扫描不同,这个生成器会在一个真实 context 上把每个工具插件启动起来,然后读 ctx.tools.schemas()。给的理由有四条:运行时展开的枚举、拼接出来的描述文本、由配置决定的名字、原始 JSON Schema 形式的 MCP 工具。同一行还写了完整性守卫:生成器会 glob packages/*/tool-*,任何包不在启动清单里就直接失败。

紧接着 :10 写明收录范围:packages/*/tool-* 下的已发布产品工具,每个都以它的 DEFAULT config 启动;如果某个 Config 字段是必填且没有默认值,生成器必须替它挑一支分支,并在该包备注里记录选了哪支。examples/ 下的演示工具(例如 echo)被排除在外。

看到这里,五个参数这件事就有了落点:目录里的那份 schema,是在某一组默认配置下启动后读出来的快照,而不是源码里所有可能分支的并集。

源码里那两个参数的展开条件

回到 packages/shell/tool-bash/src/index.tsescalationModes 的取值在 :192-193:只有当 ctx.shell.sandboxMode !== undefined 时,它才取 ESCALATION_TARGETS;否则是空数组。

ESCALATION_TARGETS 是一张硬表,定义在 packages/sandbox/sandbox/src/escalation.ts:41,值是 ['workspace-write', 'danger-full-access']。同文件 :28-30 还有一张升级阶梯表:'read-only' 可以升到 ['workspace-write','danger-full-access']'workspace-write' 只能升到 ['danger-full-access'];注释写明 read-only 是地板,没有东西能升级到它(:33-40)。

沙箱模式本身是三档,定义在 packages/sandbox/sandbox/src/index.ts:29'read-only' | 'workspace-write' | 'danger-full-access'。其 JSDoc 原文说明(:23-27):read-only 只放行必需的 sink(例如 /dev/null),workspace-write 另外放行工作区与后端定义的临时区,danger-full-access 绕过限制;并明确写了网络与进程可见性不在这套词表里。

所以那句「限制型执行器」是有确切含义的:挂载的 bash 执行器有没有 sandboxMode,直接决定 bash 这个工具让模型看到五个还是七个参数。

描述文本也是同一个分支的两个结果

参数个数不是唯一受影响的东西。bashDescription()packages/shell/tool-bash/src/index.ts:74-82 拼出一段 base 文本,:83-92 则是另一段升级指引。

目录里 bash 那段描述(docs/tool-catalog.md:182恰好等于源码在 escalationModes.length === 0 时返回的这个 base,不含 :83-92 那段升级指引。这与它自己在 :10 声明的「以 DEFAULT config 启动」是同一件事的两个侧面。

顺带把两条模型可见的标记字符串也记下来,它们出现在目录的描述里:[exit code: N][sandbox: file access denied under <mode> mode]docs/tool-catalog.md:182)。后者在源码侧的产出点是 escalation.ts:71-73。另外还有一条只在展开分支下才会用到的提示,原文在 escalation.ts:84-86[sandbox: escalation available — retry this exact ${subject} once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]

这两个参数带出来的那条链

sandbox_permissionsjustification 不是两个孤立的可选参数,它们绑着一条固定顺序的链。

先是配对校验 validateEscalationArgspackages/sandbox/sandbox/src/escalation.ts:51-61):两个参数必须同时出现,且 justification 去掉空格后不能为空,三种违规各有固定错误文案。

然后是 approveEscalation()escalation.ts:157-189),顺序写得很死:

  1. 先查请求的模式是不是严格更宽,不加宽就直接抛错,原文是 sandbox escalation to "X" is not strictly wider than this call's current "Y" mode:162-163);这一步的注释原文写着「A non-widening request never prompts a human.」(:150);
  2. 没有组合审批服务,抛 ... requires approval, but no approval service is composed:165-166);
  3. 没有 agent 可路由,抛 ... requires approval, but the call has no agent to route it through:168-169);
  4. 到这一步才真正发出审批请求,reason 拼成 escalate sandbox to ${mode}: ${justification}:177);
  5. 四种结果分别处置:allowed-once 返回该模式(:183),rejected / cancelled / unavailable 各抛不同文案(:184-186)。

审批那一侧的词表是封闭且 fail-closed 的四值:'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'docs/subsystems/approval.md:28)。文档 :21 原文写「allowed-once grants only the asked-about action」,缺席、不拥有、抛异常或返回值不合规的应答者一律归成 unavailable 而不是放行。packages/interaction/user-approval/README.md 的 Known Limitations 也写明:目前只有一次性授权,词表里有 allowed-once没有 allow-always、没有记住的规则、没有撤销、没有授权存储;会话策略只有 ask / never 两档。

需要如实说明的是:dsh 的这套工具会在本机执行命令、起子进程。写清这条链不等于说它「安全」——上面这些都是配置与代码里的字面约定,不是运行时表现的保证。

你自己怎么判断「我这套组合会看到几个参数」

给几个可执行的核对动作,都不需要运行这个项目:

第一步,看目录那一节。 打开 docs/tool-catalog.md,跳到 bash 小节(我们采集时在 :180),数 properties 里的键。同时注意目录里 bash 这个名字出现了两次::180 属于 @deepseek-ai/dsh-tool-bash:506 属于 @deepseek-ai/dsh-tool-bash-persistent。整份目录里 ## 级包小节 24 个、### 级工具小节 52 个,去重后 51 个不同的模型可见工具名,重复的那一个正是 bash。别把两节看串。

第二步,回源码看分支条件。packages/shell/tool-bash/src/index.ts 里定位 escalationModes:192-193),确认它依赖的是 ctx.shell.sandboxMode;再到 :258-271 看展开的位置。

第三步,看你这套组合里沙箱模式是怎么配的。 这里也有两处不同的字面值:SandboxPolicyService 的 Config schema 默认是 mode: ....default('read-only')packages/sandbox/sandbox-policy/src/index.ts:94),而 packages/bundle/base/cordis.patch.yml:175 把它配成 process.env.DSH_PERMISSION_MODE ?? 'workspace-write'。两处不一致,我们只记录差异,不推断原因。

第四步,注意还有一个参数是配置开关。 enableRunInBackground 默认 true,关掉时 run_in_background 整个参数被移除docs/tool-catalog.md:218)。也就是说五个参数这个数本身也不是恒定的。

什么情况说明不是这个原因。 如果你看到的差异发生在参数的描述文字而不是参数个数上,那可能落在别处;如果差异出现在别的工具上(例如 ask_user_question 的模型可见参数名是 multi_select,而 seam 类型 AskUserQuestionItem 用的是 multiSelect,见 docs/tool-catalog.md:95docs/subsystems/user-questions.md:65),那是另一条线索,与沙箱条件展开无关。还有一种情况:你手头的目录文件可能不是最新一次 pnpm run gen-tool-catalog 的产物——它是生成物,需要有人跑生成命令才会更新。

这处差异真正值得记住的一点

工具的 JSON Schema 在这个仓库里不是一份静态清单。它可以随载入期配置变形:enableRunInBackground 会移除参数,ctx.shell.sandboxMode 会追加参数,bashDescription() 会换掉一整段描述文本。目录之所以要「启动每个插件再读 schema」,与这种可变形的设计是一致的;也正因为如此,一份目录只能代表一组默认配置下的样子。

至于该把沙箱模式配成哪一档、该不该关掉后台执行,这取决于你的用法与你所在环境的要求,仓库里没有给出通用值,我们也不给。

延伸阅读


本文依据 DeepSeek Harness 官方仓库(github.com/deepseek-ai/deepseek-harness)的 README、docs/ 下的 架构与子系统文档、以及 packages/ 下的源码整理,核对日 2026-08-16,对应仓库快照 47f9438(版本 0.1.0-rc.5)。 本文内容为仓库源码与文档口径,我们没有安装、也没有运行过这个项目, 因此不涉及界面外观、操作手感与运行速度的任何描述。 该仓库建立于 2026-08-13,README 自述处于开发者预览阶段并明确说明未来会有破坏兼容性的变更, 文中出现的命令、配置与默认值随时可能变动,请以仓库最新内容为准。 安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。