Skip to content

subagent 工作树隔离:首版为软隔离,强制隔离待 V2 Location 作用域 #33

Open

Description

@lihaokun

agent-management(#23)首版给 subagent 的工作树隔离是软隔离:默认在自己的 worktree 里干活,但拦不住它用主 checkout 的绝对路径出去。Claude Code 的同名能力是强隔离,命令层有主动检查。本 issue 记录这个差距,并说明为什么它应当在 V2 的 Location 作用域就位后再收口。

Claude Code 的强度

isolation: worktree 之下:

  • subagent 得到一份从默认分支切出的 worktree(不是父会话的 HEAD)
  • Bash / PowerShell 命令在 worktree 内执行;「A command whose working directory resolves to your main checkout instead, for example because the worktree directory was removed while the subagent was running, fails with an error」
  • Bash 命令另有两项检查:工作目录必须解析到 worktree、命令不得把 git 重定向回主 checkout(且这些不是运行时计算的)
  • 无改动则自动清理

来源:https://code.claude.com/docs/en/sub-agents

首版能做到的强度

不改动任何文件系统解析逻辑,靠三件既有设施拼出来:

  1. 六个文件工具都接受绝对路径 —— tool/read.ts:235、write.ts:41、edit.ts:80、lsp.ts:48、glob.ts:39、grep.ts:51;glob/grep 还接受 path 参数换搜索根
  2. shell 有 cwd —— tool/shell.ts 的 cmd(shell, command, cwd, env)
  3. external_directory 权限是现成的强制点 —— tool/external-directory.ts:目标不在 instance 目录内就要过一次 ctx.ask;shell.ts 会扫命令涉及的目录逐个 ask

于是:建 worktree → 子 Session 权限里预置 external_directory: { "<worktree>/**": "allow" } → 初始 prompt 说明工作目录 → 无改动则清理。

这已经比纯提示词约定强:越界到 worktree 与主目录之外的任何地方都会被 external_directory 拦下,是代码强制而非模型自觉。

差距在哪

子 Agent 仍可用主 checkout 的绝对路径直接操作。 主 checkout 在 instance 目录之内,containsPath(full, ins) 为真,external_directory 按设计放行 —— 它防的是"出界",不是"串门"。

具体缺三项:

Claude Code 首版
命令 cwd 必须解析到 worktree,否则报错 无检查,cwd 传什么是什么
命令不得把 git 重定向回主 checkout 无检查
worktree 被删则命令失败 无检查,会静默落到别处

为什么现在不做强制

强制需要"这个 Session 的文件系统根是 X"这一事实在工具层可判定。opencode V1 没有这根轴:

  • 文件工具解析路径用的是 instance 级的目录(InstanceState.directory),不是 Session 的
  • InstanceState 本身以目录为 cache key(effect/instance-state.ts 的 get 用 ScopedCache.get(cache, yield* directory)),所以"换目录"等于换到另一份 instance 分片——SessionRunState、SessionStatus、BackgroundJob 全都会换一份,agent_list / agent_stop 随之失效

也就是说,在 V1 上做强制隔离要先把「文件系统根」与「instance 状态分片键」解耦,那是地基改造,且与 V2 正在建的东西重复。

为什么 V2 能收口

项目约束里已经写明方向:

Keep SessionRunner, model resolution, tool registry, permissions, and filesystem Location-scoped. Omitted Location.workspaceID means implicit-local placement; explicit workspace identity remains reserved for future placement semantics.

Location 正是那根缺失的轴:文件系统按 Location 走,执行调度不必跟着走。届时"这个 Agent 的根是哪个 worktree"在工具层可判定,上面三项检查才有落点。

收口时要做的

  1. 工具层从 Session/Location 解析文件系统根,而非 instance 目录
  2. shell 执行前断言 cwd 解析到该根之内,否则失败
  3. 命令扫描增加 git 重定向检查(--git-dir / --work-tree / GIT_DIR 等)
  4. 根目录消失时命令失败而非回落
  5. 与 external_directory 的关系厘清:前者防出界,新增的防串门,两者并存

关联

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions