Completed OpenCode requests left their event streams open across repeated and parallel conversations. Each request now closes its own subscription on exit without cancelling peer sessions, and cancellation preserves its original cause. Direct-model Execute commands no longer restrict cwd to the workspace. Absolute paths, parent-relative paths, and symlinks to external directories are accepted. The workspace remains the default and relative-path base; Ask stays read-only, and local execution binding, directory validation, and process cleanup remain. Add real OpenCode lifecycle and real Shell tool-loop regressions, plus matching feature documentation. Focused checks: 182 passed, 1 skipped; typecheck and lint passed. Full shared-worktree suite: 3542 passed, 57 skipped, 1 unrelated sidebar heading failure, reproduced separately and documented. Release note: 修复 OpenCode 连续及并行对话结束后事件连接未释放的问题;直连模型 Execute 命令现在可直接指定工作区外的绝对路径、相对路径或符号链接目录,Ask 只读边界保持不变。
8.2 KiB
8.2 KiB
直连模型 Agent 能力功能逻辑设计
文档信息
| 项目 | 内容 |
|---|---|
| 状态 | 已实施 |
| 版本 | 0.3 |
| 日期 | 2026-09-03 |
| 关联 PRD | PRD |
| 关联故事 | User Stories |
1. 权威不变量
process_execute和subagent_delegate只属于直连文本模型。- Ask 永远不能启动命令、写入工作区或创建 Execute Subagent。
- 子级的项目、执行空间、工作模式、模型连接和能力范围只能继承或缩小。
- 子级工具清单不包含
subagent_delegate,最大深度固定为 1。 - 命令必须在父请求绑定的执行空间运行,禁止跨设备静默回退。
- 一个
process_execute调用只拥有自己创建的前台进程树,不能管理无关进程。 - 非零退出码是命令结果;启动失败、输入无效和后端不可用才是工具失败。
- 子任务是父请求内部 Job/Subjob,不创建顶层 Task 或独立 Conversation。
- 父请求取消、Runtime 释放或应用退出会传播到全部活动子级和进程。
- OpenCode、Continue 和 DeepSeek Harness 的原生能力不被本功能替换或包装。
- 直连模型只在尚未显示文本或推理内容时自动重试瞬时网络错误或请求超时,最多重试 3 次;用户取消和已显示的部分输出禁止重放。
2. 状态维度
| 维度 | 值 |
|---|---|
| Runtime | model、opencode、continue、deepseek-harness |
| 工作模式 | ask、execute |
| 执行空间 | local、ssh |
| 本机 Shell | available、unavailable |
| Subagent 深度 | 0、1 |
| 父请求 | active、cancelling、terminal |
| 模型请求重试 | initial、retry 1..3、terminal |
3. 工具可见性决策表
| Runtime | 模式 | 执行空间 | process_execute |
subagent_delegate |
|---|---|---|---|---|
model |
Ask | 本机 | 隐藏 | 提供,只读继承 |
model |
Execute | 本机且 Shell 可用 | 提供 | 提供,Execute 继承 |
model |
Execute | 本机但 Shell 不可用 | 隐藏并报告诊断 | 提供,但子级同样没有进程工具 |
model |
Ask/Execute | SSH,远端后端未实现 | 隐藏 | 当前远端直连模型产品入口不开放 |
| 其他 Runtime | 任意 | 任意 | 隐藏 | 隐藏 |
工具清单隐藏不是最终授权边界。callTool 仍须重新验证 Runtime、工作模式、执行空间和父请求
是否活动,以拒绝旧快照或伪造调用。
4. 进程状态机
validating
├─ invalid → failed
└─ valid → starting
├─ spawn_failed → failed
└─ running
├─ exit 0 → completed
├─ exit nonzero → completed_nonzero
├─ timeout → terminating → timed_out
├─ parent cancel → terminating → cancelled
└─ output/lifecycle failure → terminating → failed
规则:
completed_nonzero仍产生正常工具结果。- 超时和取消结果包含已经取得的有界输出。
- 进入
terminating后不再接受新输入,也不启动替代命令。 - 终止只针对本次调用记录的进程树。
- 迟到的
close只用于清理,不得覆盖已提交的取消或超时原因。
5. 工作目录决策
| 输入 | 结果 |
|---|---|
| 未提供 | 当前工作区根目录 |
. 或相对路径 |
以当前工作区为基准解析,可指向工作区外目录 |
| 绝对路径 | 使用指定目录 |
.. 或指向工作区外的符号链接 |
解析到实际目标目录,按当前用户权限执行 |
| 不存在或不是目录 | 拒绝 |
| 当前工作区身份在验证期间变化 | 拒绝本次调用 |
工作区提供默认目录和相对路径基准,不是 Execute 命令的文件系统边界。单次命令选择其他
工作目录不会修改父请求绑定的项目或工作区,也不会影响后续未指定 cwd 的命令。
6. Shell 选择
| 平台 | 顺序 | 不可用结果 |
|---|---|---|
| Windows | pwsh → powershell.exe |
进程工具不注册,设置诊断显示 PowerShell 不可用 |
| macOS | /bin/bash → /bin/sh |
两者都不可用时不注册 |
| Linux | /bin/bash → /bin/sh |
两者都不可用时不注册 |
一次父请求冻结实际 Shell 描述。PATH 或工具环境设置变化只影响新创建的 Runtime/执行服务, 不在运行中的命令或 Subagent 中途切换。
7. Subagent 状态机
validating
├─ invalid/depth_exceeded → failed
└─ queued
├─ parent_cancelled → cancelled
└─ running
├─ model done → completed
├─ child failure → failed_with_partial_output
├─ timeout → cancelled
└─ parent cancel → cancelled
父模型收到结构化终态:
type SubagentDelegateResult = {
status: 'completed' | 'failed' | 'cancelled'
output: string
error?: string
modelUsage?: {
inputTokens?: number
outputTokens?: number
}
}
失败和取消是父模型可以解释的子任务结果。只有契约损坏、未知子任务身份或服务已经释放等 内部不变量失败才终止父工具循环。
8. 权限与能力继承
| 父请求 | 子级模式 | 子级能力 |
|---|---|---|
| Ask | Ask | 父级只读工具的子集,不含进程、写入和再次委派 |
| Execute | Execute | 父级已启用工具的子集,包含本机进程能力,不含再次委派 |
- 子级不能请求更高模式。
- 子级使用父请求开始时的工具与能力快照;运行中设置变化不重写快照。
- 子级调用 MCP、浏览器或知识能力时继续经过这些能力已有的作用域和生命周期边界。
- Execute 子级不弹出额外的“Subagent 执行授权”,也不获得父级没有的 MCP 分配。
9. 并发和顺序
- 全局编程 Subagent 最大并发为 3,额外请求进入有界队列。
- 单个父模型的工具调用仍按直连模型现有顺序结算,不为本功能改写全部工具并发语义。
- 不同父请求的 Subagent 可以并发;共享工作区的并发写入属于用户已授权的 Execute 行为, 活动记录必须保留实际顺序和结果。
- 首版不自动创建工作树、锁文件或回滚层。出现真实冲突时由父模型读取当前工作区事实并处理。
10. 结果优先级
当多个终止原因竞争时,使用以下优先级:
用户/父请求取消 > 工具超时 > 输出或协议边界 > 进程退出 > 普通工具错误
已经提交的正常终态不被稍后到达的取消覆盖。父请求进入终态后,迟到的子级文本和工具事件 只用于资源清理,不再写入会话。
11. 模型请求重试
attempt
├─ success → completed
├─ cancel → cancelled
├─ non-retryable failure → failed
├─ transient failure after visible output → failed_with_partial_output
└─ transient failure before visible output
├─ retry count < 3 → backoff → attempt
└─ retry count = 3 → failed
- 瞬时失败包括常见连接重置、连接/网络不可达、DNS 临时失败、连接管道中断和请求超时。
- 三次重试分别等待 500 ms、1 s 和 2 s;每次尝试重新建立请求并使用独立请求超时。
- 普通问答、工具模型轮次和上下文摘要使用同一规则。
- 重试不重复已经执行的工具;工具只会在模型完整返回一轮工具调用后开始执行。
- 退避等待绑定父请求取消信号,取消优先于启动下一次尝试。
12. 逻辑完整性评估
| 领域 | 结论 |
|---|---|
| Runtime 隔离 | 完整,工具只注入 model |
| Ask/Execute | 完整,清单过滤和调用边界双重检查 |
| 本机三平台 | 完整,统一契约和平台 Shell 决策已定义 |
| Subagent 递归 | 完整,深度固定为 1 |
| 取消与失败 | 完整,父子和进程传播及优先级已定义 |
| 模型网络恢复 | 完整,有限重试、输出重放和取消边界已定义 |
| 产品对象 | 完整,遵循 Task/Job 权威模型 |
| SSH 远端执行 | 明确不在首版;未来必须增加 Agent 后端后才能开放 |
当前没有需要在实施前由用户补充的产品决策。