# Agent Note: 工具可达能力 seam 中的必填取消

Status: proposed

[English](2026-07-19-required-cancellation-through-tool-capability-seams.md) | 中文

## 问题

已经实现的[工具注册表取消契约](../../implemented/architecture/2026-07-19-cooperative-tool-cancellation.md)让每个工具主体中的 `exec.signal` 成为必填值，但许多由工具主体调用的异步能力接口仍接受可选信号。因此，工具可以满足自身类型，却在下一次同进程调用时意外丢失取消。

这项缺口会沿调用链传递。文件系统工具可能调用路径解析和 I/O，Web 工具可能调用提供方，Bash 工具可能调用执行器，组合工具可能启动或等待任务、subagent 或工作流。只要某个控制工具所持有工作的、且会被工具等待的操作允许省略信号，TypeScript 就无法证明取消仍能到达拥有副作用的边界。

要求仓库中所有异步函数都携带信号会过度扩张。有些操作无法从工具到达，有些同步查询无法等待或持有持续工作，而明确分离的工作在刻意交接后已经拥有新的所有者。

## 提议

所有能从工具主体到达、且在工具仍持有或等待该操作期间执行的异步同进程能力操作，都必须接收 `AbortSignal`。根据所属 seam 的既有形态，这项要求可以表现为位置参数，也可以表现为必填的只读请求字段，但省略信号必须导致 TypeScript 编译失败。

每个直接调用方提供自己持有的信号，或从自身必填的操作上下文继续传递信号。实现可以派生子截止时间或取消作用域，但派生信号在委托期间仍须与上游信号关联。能力实现不得生成永不中止信号、使用环境式异步本地取消，也不得仅为重复类型化同进程契约而在运行时校验 `AbortSignal`。

迁移首先从每个第一方 `ToolDefinition.execute()` 出发，清点其等待的能力调用；随后按内聚的接口／实现／消费方 seam，将测试与生成的 API 文档一并修改。文件系统、Bash 与任务、Web 与提供方、工作流与 subagent、代码运行时等能力族可以通过独立 PR（Pull Request）迁移，以保持每项变更可审查；但根据仓库的预发布原则，已经迁移的接口不得保留可选兼容重载。

### 范围边界

本提议包含完成或取消仍属于当前工具生命周期的异步能力操作，包括所有权交接前的启动操作、前台执行、读写、提供方请求、等待，以及工具会等待的清理或释放操作。

本提议不包含同步注册表查询、可用性检查、schema 渲染、参数分类，以及其他无法保留异步工作的操作。明确交接所有权后的分离工作也不在范围内：任务、工作流、worker 或 subagent 成功发布给新的生命周期所有者后，其分离生命周期由新所有者的控制器管理。发起启动的操作在交接提交前仍须接收调用方信号；之后若另一次工具调用等待该分离工作，则必须使用该次调用自己的信号。

若外部协议本身允许省略取消，解析器、配置、模型与工具 JSON、持久化与文件格式、worker、进程或协议输入仍可保留可选取消。所属边界必须先把该输入解析为必填的同进程信号，再调用已经迁移的能力 seam。

## 考虑过的替代方案

**因为工具主体已经收到信号，所以继续让下游信号保持可选。** 不予采纳，因为外层回调中存在信号并不能让传递过程具备类型安全；每个可选能力调用仍可合法省略它。

**通过 lint 规则或回调检查强制传递。** 不予采纳，因为语法检查无法可靠识别所有权、派生信号、抽象层或正确的完全停稳行为。必填接口参数可以在 TypeScript 能检查每个调用方的位置表达契约。

**把 `ToolRunContext` 传入所有能力。** 不予采纳，因为能力需要的是取消，而不是工具身份、agent（智能体）状态或上下文延后功能。传递更大的上下文会让可复用服务耦合到工具注册表，也会掩盖狭窄的 seam。

**使用环境式异步本地信号。** 不予采纳，因为隐藏传递会让所有权和分离交接难以审计，使测试复杂化，并可能让调用静默绑定到错误的生命周期。

**在能力实现中加入默认或永不中止信号。** 不予采纳，因为默认值会抹去缺失的所有者，而不是在编译期暴露问题。

**在已经实现的工具注册表变更中迁移所有能力。** 不予采纳，因为传递性的接口修改横跨独立能力族。单独保留这项提议既能维持已实现的注册表决策，也能让每个深层 seam 通过聚焦测试完成迁移。

## 验收标准

- 清单把每个第一方工具主体映射到所有权交接前可以到达的异步能力操作。
- 每个范围内的能力接口都要求 `AbortSignal`，并由编译期契约测试证明省略信号会失败。
- 接口、实现、直接消费方、测试辅助函数、示例和生成的 API 引用必须一起迁移，不保留兼容重载或生产环境永不中止哨兵。
- 派生截止时间和包装层作用域仍与调用方信号关联，集成测试证明取消到达副作用所有者，且等待的工作完全停稳。
- 同步查询和明确交接后的分离工作不受这项要求约束；存在歧义时，需要记录并测试所有权转换。
- 只有真实的无类型边界才添加运行时校验，不得重复校验 TypeScript 已要求的字段或参数。
- 每次内聚迁移后，顶层类型检查、覆盖率、快照、文档、模块图、构建、hygiene、演示和构建产物门禁全部通过。

## 风险

**传递性影响范围较大。** 一个必填参数可能同时暴露大量直接调用方。应按内聚能力族迁移，并把类型检查失败作为完整的调用方清单。

**错误划分分离工作。** 过早排除启动操作可能在发布提交前就让工作脱离控制；永久要求父信号又可能让已完成工具取消合法分离的工作。每次交接都需要明确提交点、新所有者、回滚行为和完全停稳的失败路径。

**信号所有权混淆。** 能力若在委托生命周期之外保存借用信号，可能让工作绑定到过期调用方。接口和测试必须区分借用的操作信号与长生命周期服务所持有的控制器。

**只有机械合规而没有协作行为。** 必填参数只能证明信号可用，不能证明实现会观察或转发它。进程、worker、套接字、提供方和任务边界仍需集成测试证明实际行为。

**把同步或无关 API 纳入范围。** 在不存在异步工作的地方要求取消只会增加噪声，并削弱契约的辨识度。修改前，清单需要记录每项操作为何可由工具到达并承载其生命周期。
