feat(subagent): 支持配置原生子代理运行时策略 - #1144
Conversation
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
|
感谢这个 PR,设计和文档都很扎实——IPC 鉴权(域分离 HMAC、nonce 重放、时间窗、响应 proof、有界读取)、 以下为自动评审的初步意见,最终以维护者审阅为准。 🔴 1(建议修改):沙箱里 MCP gateway 的 shim 覆盖被误伤,
|
| 环境 | 结果 |
|---|---|
| canonical origin/master | test/plugin-mcp-sandbox.test.ts 3 passed |
| 本 PR 合并树(本地) | 2 failed / 1 passed(McpError: Connection closed) |
本 PR 的 CI build 腿 |
同样这 2 条 failed |
直接对比两棵树产出的 bwrap argv,差异恰好是少了这 3 个参数:
--ro-bind
<home>/.botmux/data/sandboxes/<sid>/shimbin/botmux
<home>/.botmux/bin/botmux
注意紧邻的既有守卫 if (selfExec !== undefined && resolvedTarget === selfExec) continue; 已经覆盖了注释里描述的"编译版二进制自己"那种场景,且只在该路径就是当前运行的可执行文件时才跳过;新加的这条是无条件按路径匹配,范围明显更宽。
建议:把排除条件收窄到真正需要豁免的情形(例如仅在 isStandaloneBinary() / 该路径确实等于当前执行体时跳过),或反过来让 native hook 走一个不与 gateway 入口冲突的路径;无论哪种,都请让 plugin-mcp-sandbox 恢复绿。
另外
resolveStableBotmuxWrapperPath(process.env, ...)直接读process.env,而prepareDirectSandbox本身已经有显式的home参数(sandbox.ts:663,且847行用它设置子进程 HOME)。同一函数里两个 home 来源不一致,建议统一从opts.home推导,stableBotmuxWrapperPath测试缝可以保留。
🟠 2(建议补测):hook 的 tool_name 匹配没有被真实覆盖
src/cli.ts 里这行是"只改写 spawn_agent"的唯一保证:
if (payload.hook_event_name !== 'PreToolUse' || payload.tool_name !== 'spawn_agent') return;把 || payload.tool_name !== 'spawn_agent' 删掉后,PR 新增的 21 个测试文件全绿。
现有的否定用例(native-subagent-runtime-hook.test.ts:252,tool_name: 'Bash')是用 startServer: false 跑的——此时 hook 在拿到策略前就 fail-open 返回了,所以它无论匹配器在不在都会通过。
我另写了一个探针确认这个变异不是惰性的:接上真实 policy server 后,删掉匹配器会让 Bash 调用被注入 model / model_provider:
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow",
"updatedInput":{"command":"ls","model_provider":"trae","model":"POLICY-MODEL"}}}同一探针的阳性对照(spawn_agent 走同一条 live server 路径确实被改写)是通过的,所以这不是探针接线问题。建议把那条 Bash 否定用例改成挂真实 server 跑,断言 stdout 为空。
🟡 3(小建议):源码文本断言可以被注释满足
test/read-isolation.test.ts:216 用 expect(workerSource).toContain('[canonical(originDirectory), canonical(attestationDirectory)]') 来守 Seatbelt 授权。我把 src/worker.ts 里真实那行注释掉、只让注释里保留同样的字面量,测试依然全绿(88 passed)。建议断言前先剥注释,或改成断言 buildSeatbeltProfile 的实际入参。
✅ 已验证没问题的部分
- bwrap 分组绑定确实是必要修复:我用真实 bwrap 实测,旧写法(同一 parent 两次
--tmpfs)第二次会把第一个 bind 抹掉(capability 文件读不到);新写法两个目录都可读、兄弟会话目录不可见、只读生效。 managedTurnOrigin在 turn terminal 后保留 policy 授权不削弱鉴权:占位capability是新的随机值,没有任何一方持有,所有按 capability 比对的路径仍然失败;测试也显式断言了它 ≠ 被吊销的那个。- daemon boot id 从
randomUUID()改为 43 字符 base64url:pane marker 把它当不透明字符串,无格式校验;且与B64URL_32_BYTES_RE一致。 - 反变异:对核心守卫打了 10 个变异,9 个转红(响应鉴权 fail-closed、capability 签名校验、重放保护、loopback、策略 mode 校验、429 fail-closed、wrapper 排除、invalid 状态追踪、transcript 类型守卫)。
- worker 侧
!remoteWsUrl判断看似无覆盖,但实为惰性变异——traex.ts的buildArgs在 remote 分支提前 return,根本读不到该参数,属于冗余纵深防御,不是缺口。 bunx tsc --noEmit通过;bun run build通过;PR 涉及的 21 个测试文件在最新 master 合并树上 1360 passed / 1 skipped。- 相邻回归面(vc-meeting / managed-origin / worker / session / codex-app / current-actor / daemon-ipc / workflow-daemon 共 127 文件)在
dist/构建后 1999 passed,与 master 逐项一致。
CI 里另一条红 mojo-close-worker-journal 与本 PR 无关(已知异步 IPC 竞态 flake,见 issue #1151),本 PR 未触碰任何 mojo 文件。
以上为自动评审的初步意见,最终以维护者审阅为准。第 1 条建议先修,其余可按需处理。
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
b46dd13 to
12deaac
Compare
…me-policy Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
|
再次感谢快速跟进。这轮( 以下为自动评审的初步意见,最终以维护者审阅为准。 ✅ 三条旧意见逐条复验(都在干净 checkout里做的,见文末说明)
独立入口这个修法我额外验了两点:
✅ 这轮新增逻辑(白话)前两轮只解决"hook 怎么被安全地执行",这轮解决的是"策略查询权限该活多久": CLI 崩溃后 botmux 会在同一个 Node worker 内自动重启 CLI。之前 CLI 一退出就把 policy capability 一起吊销了,于是重启后的第一个子代理查不到策略。这轮把生命周期精确化成:live 发送权限跟着 backend 世代走(一退就废),policy 查询权限跟着 Node worker 世代走——只在这个 worker 还真有资格原地重启时才保留(排除 codex-app 活跃写入方、shared-adopt、远端后端、worker 已 killed),崩溃循环放弃重启时立即清掉。revoke 消息也拆成两条独立通道,各自精确 token 匹配,避免一个陈旧 token 挡掉另一个的正常吊销。 另外补上了 Dashboard 侧校验:选了"自定义"却留空不再允许保存(这正是我上一轮提到的空值路径)。 🔬 新增逻辑的反变异(9 枪,8 红)转红(守卫有效):崩溃退出的资格判定、崩溃循环放弃时清权限、revoke 的 live/policy 两侧 token 精确匹配、
唯一转绿(🟡 建议补测,非阻断): 我确认它不是惰性代码:入口处 ✅ 其它已验证
|
…me-policy Co-authored-by: TRAE CLI <traecli@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
…me-policy Co-authored-by: TRAE CLI <traecli@bytedance.com>
…me-policy Co-authored-by: TRAE CLI <traecli@bytedance.com>
|
已按本轮反馈完成并推送修正:
最新分支已合入
完整最终审查未发现 Critical / Important / Minor 问题。 |
改了什么
spawn_agent增加 Bot 级运行时策略;模型与思考强度可独立选择“透传请求”或固定为自定义值。bots.json文档。当前协议不提供inherit;旧mode: "inherit"作为无效配置整段丢弃。PreToolUsehook 在调用时读取当前策略,只改写原生子代理请求,不改变父代理。为什么
Bot 的父代理与原生子代理可能需要不同的模型和思考强度。此前原生子代理完全依赖单次
spawn_agent请求,Bot 管理员无法统一固定其运行时;子代理元数据也可能污染父会话卡片展示。本改动提供显式、可热更新的 Bot 级约束,同时让未配置维度继续保持现有透传行为。
影响面
spawn_agent。model_provider=trae。测试
bunx vitest run --project unit <16 个相关测试文件>:837 passed,1 skipped。origin/master后:bunx tsc --noEmit --pretty false:通过。bun run build:通过。bun run verify:binary:compiled binary smoke 全部通过。git diff --check:通过。bun run test执行到 20,305 passed、19 skipped;剩余 9 个测试 / 1 个 suite 失败均为当前宿主环境问题(/home→/data00/home路径别名、/proccomm、bwrap 临时目录清理/MCP/DSH sandbox),相关功能测试单独运行通过。实机验证
使用 fresh Trae 进程和真实原生
spawn_agent验证:collab_agent_spawn_end与 childturn_context都记录GPT-5.4 / high,覆盖了请求中的另一组 runtime。DeepSeek-V4-Flash / low,child 正常完成。UI
Dashboard 的 Trae Bot「Agent 配置」中增加“原生子代理运行时”区块,模型和思考强度各自提供“透传子代理请求 / 自定义”两态;非 Trae Bot 不显示该区块。