DeepSeek Harness 的文件搜索:打包 ripgrep 与 unconfined spawn

2026-08-16

deepseek-harness 里读沙箱那一层,很容易产生一个印象:这个仓库把「在本机执行东西」拆得非常细,ctx.sandbox 有三种模式、四个平台后端,可写根有唯一定义处,ctx.fs 之上还挂了一道围栏。读到这里你大概会默认:模型能碰文件系统的所有路径都被这套词汇罩住了。

然后你翻到 packages/fs/tool-fs-search/,会看到模型每天用得最多的两个工具 —— globgrep —— 根本不在这套词汇里。它们不走 ctx.fs,也不注入 sandbox。README 自己给这条执行路径起的名字,原文就是 the unconfined spawnpackages/fs/tool-fs-search/README.md:5)。

先说清限定:以下全部来自 2026-08-16 我们对快照 47f9438 的静态阅读。这个仓库建立于 2026-08-13,版本是 0.1.0-rc.5,GitHub 上一个 Release 都没有,README 自述处于开发者预览阶段并明写未来会有破坏兼容性的变更。下面提到的每一个字段名、默认值、行号都可能在后续版本里变。我们没有安装过 dsh,没有跑过任何命令,没有起过任何一个沙箱。

一行 inject 决定了这两个工具的位置

packages/fs/ 下截至 2026-08-16 有 7 个包:-fs(seam)、-fs-local-fs-observation-policy-fs-sandbox-tool-fs-tool-fs-search-tool-str-replace-editorglobgrep 属于其中倒数第二个。

关键的一行在 packages/fs/tool-fs-search/src/index.ts:70

export const inject = ['tools', 'systemPrompt', 'subprocess']

三个依赖,没有 fs,也没有 sandbox。对照着看另外那半边:packages/fs/fs-sandbox/ 提供的围栏是 read-only 拒绝一切变更、workspace-write 只允许目标规范化后落在 writableRoots() 里、danger-full-access 直接放行(packages/fs/fs-sandbox/README.md:15-17),而读在任何模式下都放行(同 README :5)。也就是说,即使 glob/grep 真的接在 ctx.fs 上,作为读操作它们也不会被这道围栏拦住;而现在它们连接都没接。

这两件事要分开记:一是「围栏管不管读」,二是「这条路径压根不经过围栏」。前者是围栏自己的语义,后者是装配上的事实。

「unconfined」不等于「什么约束都没有」

glob/grep 背后是 随包发布的 ripgrep 二进制@vscode/ripgrep),不是系统里的 rg。每次调用通过 ctx.subprocess 用一组固定的 argv 向量 spawn 这个二进制,argv 前置 --no-config;README 给出的理由是防止宿主环境里的 RIPGREP_CONFIG_PATH 往这条 spawn 里注入 --pre 预处理器(packages/fs/tool-fs-search/README.md:5)。这个二进制的具体版本号我们没有核实 —— 没有读它对应的 package.json,所以这里不写。

那这条路径上还剩什么?答案是 ctx.subprocess 那一层自己的规矩,它们和沙箱是两套东西:

  • argv 永不被 shell 解释docs/subsystems/subprocess.md:101)。这条对搜索工具尤其关键,因为 pattern 是模型给的。
  • 环境变量按名字启发式清洗scrubbedParentEnv()process.env 里剔除命中 SENSITIVE_ENV_PATTERN = /KEY|PASSWORD|SECRET|TOKEN/i 的名字,以及所有 DSH_ 前缀的名字(packages/subprocess/subprocess/src/index.ts:44:60-66)。JSDoc 明说 PATHHOME、locale、proxy 变量会活下来。packages/subprocess/subprocess-local/README.md:32 自述这只是名字启发式,像 *PASSPHRASE* 这样名字不同的 secret 会继续传下去。
  • seam 本身不提供任何默认值:「this seam applies no defaults: every disposition, limit, and directory is explicit」(docs/subsystems/subprocess.md:91)。

所以 README 里那个 unconfined,指的是不受 ctx.sandbox 那套文件效果约束,而不是这条 spawn 裸奔。这个区别值得写清楚,因为两种误读的方向正好相反:一种以为搜索也被沙箱兜着,另一种以为搜索完全没有任何处理。

顺带一提,沙箱那套词汇本身的边界也是写在明面上的:docs/subsystems/sandbox.md:11 原文说「Network and process visibility are outside this vocabulary.」—— 网络和进程可见性从一开始就不在这套词汇里。

四个上限,以及唯一那个必须你自己选的

搜索侧一共有几个上限项,值得逐个记住确切名字与它写在哪一层(截至 2026-08-16 快照):

配置项默认值写在哪一层
sampleOverCapGlobResults必填,无回退配置 schema,部署方必须显式给
globMaxResults100包 README 的配置表
grepMaxMatches250包 README 的配置表
grepMaxLineBytes2000包 README 的配置表
SEARCH_TIMEOUT_MS(搜索超时)30_000常量在 packages/fs/tool-fs-search/src/search-core.ts:42,同一个值又作为 schema 默认写在 src/index.ts:106

第一行是这张表里最特别的:sampleOverCapGlobResults 没有回退值,装插件时不给就过不去。其余几项都有默认,超时那一项还出现在两个位置 —— 一处是常量、一处是配置默认,两处的值一致。

这些默认值该不该改?我们不给建议。它们是配置里的默认值,不是你用起来会怎样的保证,仓库也没有给出通用取值指导,具体调成多少取决于你的用法。

超时这一块还有个相邻事实,我只并列陈述,不推断二者的调用关系:packages/guard/timeout-policy/(包名 @deepseek-ai/dsh-tool-call-timeout-policy)是零配置的,只读工具自己声明的 ToolDefinition.timeoutMs;它的 README 有一节标题就叫「Cooperative, not a hard kill」(:30-32),原文说派生的信号只做通知,终止仍属于工具本身,「a tool that ignores the signal will not stop on timeout」。同 README :56-57 记了一条限制:没有全局预算,只有声明了 timeoutMs 的工具才有 deadline,而 bash/read/write/edit 故意都不声明。这两处的具体链路我们没有在卡内材料里核到底,就到此为止。

「不是安全边界」这句话,仓库自己写了五处

这不是我们的评价,是原文集中在一起的样子:

  1. packages/code-runtime/code-runtime-worker-thread/README.md:5:「Containment, not a security boundary」,并写「trust posture is bash-equivalent by design」。
  2. docs/subsystems/code-runtime.md:161isolation 字段是「a diagnostic label, not a security claim」。
  3. packages/fs/fs-sandbox/README.md:19 的小节标题:「Threat model: a policy fence, not a kernel boundary」;同 README 还把围栏描述为「a check in TRUSTED code over a MODEL-CONTROLLED path」,并明说残留的 TOCTOU 被收窄但没有消除、并且是被接受的(:21:43)。
  4. packages/extensions/tool-cordis/README.md:23:「The sandbox isolates globals but is not a security boundary.」
  5. packages/extensions/cordis-host-runner/README.md:32:同源表述。

把这五处和 tool-fs-search 的 unconfined 放在一起看,这些原文都是在划定边界的适用范围,而不是在承诺隔离。至于这样的姿态合不合适你的场景,我们不给结论;也请不要由这些原文反推「所以安全」或「所以不安全」。

顺着 ctx.fs 那一侧再补两条对照事实,能把「搜索」和「读写文件」的处置差别标得更清楚。其一,文件 IO 这边没有超时read/write/edit 不收 timeoutMs,provider 契约不设 deadline,docs/subsystems/filesystem.md:272 给的理由是本地 syscall 至多是 best-effort 可中止,超时没法让进行中的 fsync/rename 停下来 —— 而搜索那边有一个 30_000 的常量。其二,ctx.fs 的错误码是封闭枚举、共 13 个(docs/subsystems/filesystem.md:254-267),其中 FS_SANDBOX_DENIED策略拒绝,与宿主内核拒绝的 FS_PERMISSION_DENIED 被明确区分开(:270)。这层区分只存在于走 ctx.fs 的路径上,而 glob/grep 不在这条路径上。

Windows 侧的一条并列记载

packages/sandbox/sandbox-windows-acl/README.md:93-102 记了若干已知限制,其中一条是:受限进程内 spawn(..., { stdio: 'pipe' })EPERM,因此「A confined process therefore cannot capture a grandchild’s output through a pipe; tools that must capture output cannot run confined.」

这条记载和 tool-fs-search 不注入 sandbox两处独立的仓库事实,我们只把它们并列摆出来,不推断谁导致谁、也不推断作者的取舍。你如果关心 Windows 上的实际行为,请以仓库最新的这两份 README 与代码为准。

另外,Windows 侧的 windows-acl 后端在未探测时声明的 enforcement 是 partialpackages/sandbox/sandbox-local/src/index.ts:177-187),其余三个后端 bwrap/landlock/seatbelt 声明的是 full。而 SandboxEnforcement 在 seam 文档里被定性为「a reported fact」,partial 意味着后端或较老内核 ABI 只能管住一部分文件效果(docs/subsystems/sandbox.md:30)。这些同样只是文件里写的话,不是运行观测。

你可以自己去核的三个动作

  • 打开 packages/fs/tool-fs-search/src/index.ts:70,确认那行 inject 里有没有 fssandbox
  • 打开 packages/fs/fs-sandbox/README.md:5:15-23,把「读全放行」和威胁模型小节连起来读一遍。
  • 对比两个示例组合的装配:examples/acp-agent/cordis.yml 里装的是 @deepseek-ai/dsh-sandbox-local:25)、dsh-bash-sandbox:38)与 dsh-fs-sandbox:166);而 examples/headless-agent/cordis.yml 装的是 @deepseek-ai/dsh-bash-local:39)与 @deepseek-ai/dsh-fs-local:157),我们在这个文件里 grep sandbox 一条都没有匹配到。同一个仓库里两个示例的隔离姿态不一样,这一点只陈述,不延伸。

最后重复一遍开头的限定:这些行号、字段名与默认值都对应 2026-08-16 的快照 47f9438,版本 0.1.0-rc.5,README 自述开发者预览并明写会有破坏兼容性的变更。你读到本文时,上面任何一处都可能已经不是这样了 —— 请以仓库最新内容为准。

延伸阅读


本文依据 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?报名体系课或加入会员,照着学、照着用。