结论
协议级请求取消 $/cancel_request 与配套错误码 -32800 (Cancelled) 完全未实现。我们只有整轮取消 session/cancel。后果不对称:收不到无妨(规范允许忽略),但我们自己发出去的请求也没法取消。
证据链
1. 规范:这是 Protocol Level 的独立机制,与 session/cancel 不同层
v1 schema ## Protocol Level(/tmp/acp-v1-schema.md:1639)与 ### $/cancel_request(:1651):
Notifications whose methods start with '$/' are messages which are protocol implementation dependent and might not be implementable in all clients or agents. ... If an agent or client receives notifications starting with '$/' it is free to ignore the notification.
$/cancel_request
Cancels an ongoing request.
This is a notification sent by the side that sent a request to cancel that request.
Upon receiving this notification, the receiver:
- MAY cancel the corresponding request activity and all nested activities
- MAY send any pending notifications.
- MUST send one of these responses for the original request:
- Valid response with appropriate data (partial results or cancellation marker)
- Error response with code
-32800 (Cancelled)
CancelRequestNotification 只有一个必填字段 requestId: RequestId。
同一机制在 v2 保留(/tmp/acp-v2-schema.md:1110 同名节,属于 Protocol Level 段 :1098)。
2. 我们侧:方法、错误码、收发三处全无
$ grep -rn '32800' src/ | wc -l
0
$ grep -rn '\$/cancel\|cancel_request\|CancelRequest' src/SalmonEgg.Acp/ | wc -l
0
src/SalmonEgg.Acp/JsonRpc/JsonRpcErrorCode.cs 完整枚举了标准码(-32700/-32600/-32601/-32602/-32603)与 ACP 扩展码(-32000 AuthenticationRequired ~ -32005 CapabilityNotSupported),没有 -32800。而且:
public static bool IsStandardErrorCode(int code)
{
return code >= -32700 && code <= -32603;
}
public static bool IsAcpErrorCode(int code)
{
return code >= -32099 && code <= -32000;
}
-32800 落在这两个区间之外,GetErrorMessage(-32800) 会返回 "Unknown error (code: -32800)"。也就是说即便对端按规范回了 -32800,我们也只会把它当无名错误呈现。
3. 我们现有的只是整轮取消,粒度不同
src/SalmonEgg.Acp/Client/AcpClient.cs:690-694 发的是 session/cancel:
"session/cancel requires 'sessionId'.");
...
"session/cancel",
它按 sessionId 取消整个 prompt turn,无法取消单个在途请求。入站侧 HandleNotification(AcpClient.cs:1143-1154)只有一个 case:
switch (notification.Method)
{
case "session/update":
HandleSessionUpdate(notification);
break;
default:
// Unknown notification type.
break;
}
$/cancel_request 会静默落入 default——这一半符合规范(明文允许忽略 $/ 通知)。
4. 真正的缺陷在出站侧
我们发给 agent 的请求(session/prompt 之外的 session/list、session/load、terminal/* 等)一旦 CancellationToken 被触发,按规范应当发 $/cancel_request 通知对端;现在没有这条路,只能本地放弃等待,对端继续跑到底。这会浪费对端算力,并可能让副作用(文件写入、命令执行)在用户已取消后仍然发生。
影响
建议范围
JsonRpcErrorCode 增补 Cancelled = -32800,并修正 IsStandardErrorCode / IsAcpErrorCode 的区间判定或新增单独判定(注意 -32800 属 JSON-RPC 保留区但不在现有两段内,别硬塞进现有区间破坏语义);GetErrorMessage 补映射。
- 出站:
AcpClient 的请求发送路径在 CancellationToken 触发时发 $/cancel_request(携带原 requestId),随后仍需等待/清理对端的终态响应(规范要求对端 MUST 回一个响应,我们的 pending 表要能收得下 -32800)。
- 入站(可选,规范允许忽略):若要支持,需能取消我们正在处理的 client 侧请求(
fs/*、terminal/*、session/request_permission),并按规范回 -32800。现有 TrackPendingInboundRequest / RemovePendingInboundTracking 已有 pending 表可复用。
- 呈现层:
-32800 归类为「已取消」,不进故障面。
验证要求
结论
协议级请求取消
$/cancel_request与配套错误码-32800 (Cancelled)完全未实现。我们只有整轮取消session/cancel。后果不对称:收不到无妨(规范允许忽略),但我们自己发出去的请求也没法取消。证据链
1. 规范:这是 Protocol Level 的独立机制,与
session/cancel不同层v1 schema
## Protocol Level(/tmp/acp-v1-schema.md:1639)与### $/cancel_request(:1651):CancelRequestNotification只有一个必填字段requestId: RequestId。同一机制在 v2 保留(
/tmp/acp-v2-schema.md:1110同名节,属于 Protocol Level 段:1098)。2. 我们侧:方法、错误码、收发三处全无
src/SalmonEgg.Acp/JsonRpc/JsonRpcErrorCode.cs完整枚举了标准码(-32700/-32600/-32601/-32602/-32603)与 ACP 扩展码(-32000AuthenticationRequired ~-32005CapabilityNotSupported),没有-32800。而且:-32800落在这两个区间之外,GetErrorMessage(-32800)会返回"Unknown error (code: -32800)"。也就是说即便对端按规范回了-32800,我们也只会把它当无名错误呈现。3. 我们现有的只是整轮取消,粒度不同
src/SalmonEgg.Acp/Client/AcpClient.cs:690-694发的是session/cancel:它按
sessionId取消整个 prompt turn,无法取消单个在途请求。入站侧HandleNotification(AcpClient.cs:1143-1154)只有一个 case:$/cancel_request会静默落入 default——这一半符合规范(明文允许忽略$/通知)。4. 真正的缺陷在出站侧
我们发给 agent 的请求(
session/prompt之外的session/list、session/load、terminal/*等)一旦CancellationToken被触发,按规范应当发$/cancel_request通知对端;现在没有这条路,只能本地放弃等待,对端继续跑到底。这会浪费对端算力,并可能让副作用(文件写入、命令执行)在用户已取消后仍然发生。影响
terminal/create后续链路)无法及时止住。-32800时我们呈现为"Unknown error (code: -32800)",不可读、也无法据此区分「被取消」与「真失败」。按 AGENTS docs(config): align migration spec with additive-only reality #99 的口径,被取消不是故障,不应落在会保持到下次成功操作的故障面上。建议范围
JsonRpcErrorCode增补Cancelled = -32800,并修正IsStandardErrorCode/IsAcpErrorCode的区间判定或新增单独判定(注意-32800属 JSON-RPC 保留区但不在现有两段内,别硬塞进现有区间破坏语义);GetErrorMessage补映射。AcpClient的请求发送路径在CancellationToken触发时发$/cancel_request(携带原requestId),随后仍需等待/清理对端的终态响应(规范要求对端 MUST 回一个响应,我们的 pending 表要能收得下-32800)。fs/*、terminal/*、session/request_permission),并按规范回-32800。现有TrackPendingInboundRequest/RemovePendingInboundTracking已有 pending 表可复用。-32800归类为「已取消」,不进故障面。验证要求
$/cancel_request且requestId与原请求一致(断言报文,不只断言本地状态)。-32800时,不在 UI 形成滞留故障提示;GetErrorMessage(-32800)不再返回 Unknown。