跳转至

独立审查 B:工具、扩展、MCP、Code Mode、接口与 Durable

结论:FAIL,须修订后复验。 本结论来自语义与源码契约核对,不以网页构建或示例通过代替。没有启动子代理,没有调用真实模型、没有使用真实 API 凭据,没有修改课程或示例。

基线与范围

  • 发布基线:pi-1.0/,v1.0.0,a13d35a742c6ef8462812a28fbe1d8c8b7431c32。
  • latest main:upstream/,9fba660cf1caca0ade5bea72269352416e595a19;稳定依赖仍为 1.0.0。
  • 完整阅读课程第 08、09、10、11 章及 labs/12;阅读对应固定版官方 MCP、Code Mode、SDK、Prompt、Protocol、Chord、Delta 文档。已读 upstream/AGENTS.md。
  • 核查完整工具 read/edit/file-mutation-queue、SDK、Codemode host、Durable ToolTask、NestedToolCallRunner、MCP tools/config/runtime、RPC client/JSONL 函数;MCP index 和 Extension runner、AgentSession 为逐段阅读相关完整函数上下文。未声称完整仓库逐行人工审计。

阻断问题

B-01:RPC disposition 枚举值错误(中)

  • 文档:docs/source/10-interfaces.md §10.4,使用 accepted / queued / handled。
  • 证据:packages/coding-agent/src/core/agent-session.ts:294–295:PromptDisposition = "handled" | "queued" | "started";modes/rpc/rpc-mode.ts:394–410 原样返回 preflightResult disposition。
  • 影响:读者按 accepted 写分支将不能正确识别普通 prompt 已启动。
  • 建议:使用 started / queued / handled,补 {"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}} 的真实结构(带 id);说明 started/queued 不等于业务完成。监听完成事件应在发送前注册,避免短任务的竞态。RpcClient.promptAndWait() 本身未处理 handled,应提示不要对可能被扩展直接消费的输入无条件调用它。

B-02:扩展分支状态恢复遗漏同会话 tree 导航(中)

  • 文档:docs/source/08-tools-extensions.md:127–131 仅给 session_start 重放方式,同时指出切分支不能沿用 Map,却没有实际刷新入口。
  • 证据:core/agent-session.ts:4039–4102 完成 leaf 切换后发送 session_tree(4091–4098),没有发送 session_start;extensions/codemode/execute.ts:391–394 每次执行从 getBranch() 重新计算 store。
  • 影响:读者照 session_start + appendEntry 方案实现有状态扩展,/tree 后会继续使用旧分支缓存。
  • 建议:补 session_tree 重建(以及 session_start 所涵盖的 resume/new/fork/reload),或者示范每次使用时从当前 branch 重算。补树图 showing A branch / B branch 的 custom entry 不互相传播。

B-03:tool_call 可变参数无需重校验的边界未说明(中)

  • 文档:docs/source/08-tools-extensions.md:96 将 tool_call 概括为「已校验参数的授权拦截」,第 08 章没有进一步说明改参契约。
  • 证据:core/extensions/types.ts:1198–1203 明确 input 可原地修改、后续 handler 可见 earlier mutations、No re-validation is performed after mutation;Core agent-loop.ts:725–775 schema validate 位于 before hook 前,execute 使用该可变对象。
  • 影响:与第 11 章 Durable「beforeTool 后二次验证」并读,容易以为所有 Pi 工具 hooks 都会自动重新验证。改参权限扩展可能送入无效/未授权参数。
  • 建议:明确区分 Core/SDK 和 Durable;扩展若原地改参,应自己验证最终值,关键业务工具也在 execute 边界检查业务权限。补 prepare→validate→hook→execute 图,并标「无自动 second validate」。

B-04:Durable failure 与 isError 的取消语义混用(中)

  • 文档:docs/source/11-durable.md:86「父调用 abort / failure 时会被取消」。
  • 证据:durable/src/harness/tool.ts:242–256 execute 或环境创建 throw 才设 ending failed;tool.ts:390–394 Ending 注释明确 result with isError still completes(386 行返回 completed);execute 恢复分支 101–107 的 unsafe interrupted 才 failed。durable/README.md Abort and Subagents 同样具体限定 throws / unsafe interrupted。
  • 影响:若开发者返回 {isError:true} 想取消 owned child,会得到 completed task,孩子仍按 ownership 完成。
  • 建议:列出 returned isError / thrown error / aborted / unsafe crash 四种 outcome,说明前三种非等价。图里外部 effect 和最终 commit 之间的第二个 crash 窗口也应标出,safe replay 可能重复外部动作,仍需业务幂等。

B-05:OAuth issuer 检查无条件表述不准确(中)

  • 文档:docs/source/09-mcp-codemode.md:152「授权响应 issuer 要匹配」。
  • 证据:mcp/src/oauth/flow.ts:339–344 仅在 metadata 可用、且响应含 iss 或服务端声明 authorization_response_iss_parameter_supported 时才比较。
  • 建议:准确列出条件:有 iss 必须匹配;服务端声明支持时缺 iss 也拒绝;未声明且未返回 iss 不走此 check。不能把新 main CIMD 防混淆流程写成 1.0 保证。

B-06:latest main 更新说明不足,版本表须刷新(中)

  • 文档:docs/01-version-roadmap.md 当前 main 为旧 1387af...,只举一项 main MCP override 区别;需在更新版本章保留此次最新 commit 与采集时间。
  • 证据:git diff v1.0.0..9fba660... 的 codemode/src/runtime/prelude-source.ts 新增 16,777,216 output chars + 100,000 output items 硬限制(新 text/image/console 输出累计保护);v1.0 无此限制。coding-agent/src/core/mcp-servers.ts 增 oauth.clientRegistration dcr/cimd,main MCP OAuth flow 亦变动。项目 settings-only override 支持 enabled/exposure/toolExposure 三项,v1.0 则需 command/url 完整配置后整 entry 替换。
  • 建议:增加单独 main 差异矩阵,用 main 固定 commit 的源码链接;课程主文 1.0 内容仍然正确标注 release,切勿直接替换所有源码链接为 main。

必须补充的教学边界

B-07:Durable storage 单进程所有权(中,缺少关键前提)

  • 文档:docs/source/11-durable.md:72–82 提到考虑并发 writer,却没有明确禁止共同拥有。
  • 证据:durable/README.md:527:One process owns a storage at a time; there is no cross-process locking。
  • 建议:明确不允许多个 Agent worker 同时 open 同一 Durable storage,即使 SQLite 有文件锁也不代表 Harness 所有权契约可共享。复验不要求额外真实故障测试,但应标明原课程仅机制研究。

B-08:MCP Code Mode 结果 isError 不 reject(中,易写错业务逻辑)

  • 文档:§9.7 已区分 read string / MCP CallToolResult / bash object,但没有明确 MCP error Promise resolve 的例外;不能把 allSettled fulfilled 当业务成功。
  • 证据:extensions/mcp/tools.ts:232–257 将 scriptResult 放在 structuredContent 并保留 isError;extensions/codemode/execute.ts:305–311 有 outputSchema + structuredContent 时先 return,后面才判断 outcome.isError;MCP tools.ts:117–134 每个工具都声明结果 schema。
  • 建议:补 MCP 脚本示例 if (result.isError) ...,区分权限阻断/invalid args 等无 structured value 会 reject,而服务端 MCP {isError:true} 会 resolve;bash exit_code 也必须检查。注意 _meta 被 drop,别承诺完整原始 _meta 透传。

B-09:MCP 重试存在 session-expired 例外(低,建议深化)

  • 文档:§9.11 已说明工具不盲重发,措辞目前不算绝对错误。
  • 证据:extensions/mcp/runtime.ts:290–313 readOnly transient 重试一次;但 McpSessionExpiredError 第一次会建新 session 重试,连 tool call 也如此,因为旧 session 已被 server 拒绝且请求未执行。isTransientError 排除 501,不能无条件写所有 5xx。
  • 建议:补连接重试、resource read 重试、tool 业务重试三层表,并标 session-expired special case。

已核对且暂未发现错误的内容

  • v1.0 read 文本默认 2000 行/50 KiB、offset 1 基、图片不等于纯文本;edit 匹配原文件、非增量 edits、BOM/CRLF 恢复、同进程 mutation queue 的限定均与源码相符。
  • CLI 内建 extensions 与 SDK 不自动加载的区分;SDK §9.10 imports、defaultTools 增量、bindExtensions 与连接 session_start 顺序与固定版 SDK 文档一致。
  • MCP direct/codemode/deferred/hidden 主表方向正确,但两种 indirect mechanism 实际都可以触达 codemode/deferred,建议表后明确区别主要是自动激活哪个 discovery tool。
  • QuickJS 没有 Node/network/timer;每 script 新 worker;CLI memory256 MiB/无限默认 deadline 与 standalone host300000 ms 已正确区分;取消已完成外部作用不回滚;successful storeWrites 才提交,当前分支读取正确。
  • JSONL RPC 与实验 CBOR protocol8 区分正确;CBOR length prefix、16 MiB、64 levels、strictJSON opaque payload 与无 peer authentication 均匹配固定版 README。
  • Durable saved/current replay 必须都 safe;存储 fsync与SQLite synchronous NORMAL 保证相符;Chord 独立包、provider先激活、逆序清理、trusted ownership而不freeze、公共subscription合并 vs remote reset delta 区分正确。
  • labs/12 prompt substitutions 与 package manifest、registerTool/Command 分工匹配源码和课程实物。只拦内建名称不是完整 OS 沙箱的限定正确。

复验门槛

B-01 至 B-08 解决且版本标识与源码固定链接一致后才可 PASS;B-09 建议补入加深讲解。当前 FAIL。待父代理通知修订完成,重新独立阅读修订和对应函数,核对图示,不以“文字已改”代替语义复验。