目标
让 Pi 对任何会改变外部状态的任务先定义完成态,再执行并以工具证据确认完成。避免将单个动作成功误报为整个任务完成。
本设计不继续扩充 Local Coding Protocol 的场景规则,也不引入额外模型、自动路由或多代理。
适用边界
- 覆盖本地模型 managed session。
- 允许所有只读探索工具直接执行:
read、grep、find、ls。 - 对状态变更工具建立统一 gate:
edit、write与 Bash 中的写操作。 - 不解析或预置具体业务流程;任务的目标、范围、完成条件和验证证据由模型在结构化契约中声明。
任务契约
新增 task_contract 工具。模型在首次状态变更前调用,参数为:
intent:用户想达成的最终结果。scope:可触碰的资源及明确不碰的资源。doneWhen:一组可验证的最终状态。verificationPlan:每条完成条件的验证方式。unresolved:无法从用户输入或现有证据确定的事项。
若 unresolved 非空,模型必须向用户澄清,不能继续状态变更。
契约不要求固定任务类别。例如“完整卸载应用”可定义命令、包、专属配置均不存在;“修复缺陷”可定义行为、测试和 diff 状态。
执行与证据
控制器维护内存态 TaskCompletionLedger:
- 当前契约;
- 变更工具调用;
- 成功工具结果作为验证证据;
- 尚未确认的完成条件;
- 一次性未完成提醒状态。
tool_call 判断工具是否会改变状态。未建立契约时阻止该调用,并向模型注入 steer,要求先调用 task_contract。task_contract 和 task_complete 本身始终放行,避免 gate 死锁。这是按读写性质的统一判断,不按卸载、提交、部署等任务类型分支。
模型在每次成功的只读验证后调用 task_verify(condition)。控制器自动把该条件绑定到最近一次、发生在最后一次状态变更之后的成功只读工具结果;不要求本地模型复述不透明 toolCallId。task_complete 仅在所有 doneWhen 均已验证时接受完成。这样模型不能在未观察最终状态时声明完成。
结束 Gate
agent settled 时:
- 没有发生状态变更:不干预。
- 已发生状态变更且没有有效
task_complete:使用sendUserMessage(..., { deliverAs: "followUp" })自动要求补全缺失验证。 - 已完成:持久化任务结果,不再续跑。
/local-report 扩展显示任务意图、变更数、完成状态与缺失完成条件。该命令不调用模型。
Bash 写操作判定
首版使用保守命令分类:已知只读命令允许,其余 Bash 调用视为可能改变状态,要求任务契约。该策略宁可让模型先定义任务,也不让其无契约执行删除、安装、提交、迁移或服务操作。
用户用 ! / !! 直接执行的 Bash 不受此 gate 影响。
验证
- 核心单测:契约校验、写操作判定、验证结果关联、未完成状态和报告格式。
- 扩展 bundle:Bun build。
- Pi RPC:确认
/local-report注册,并以工具调用验证task_contract、task_verify、task_complete可用。 - 单个 ThinkingCap 无副作用请求:请求执行
touch /tmp/pi-task-contract-probe,确认无契约时被阻止且目标文件没有创建;再验证task_contract -> pwd -> task_verify -> task_complete闭环;不切换或加载 35B。
非目标
- 不依赖模型自行总结或自动猜测用户意图。
- 不自动删除文件、安装包或执行恢复操作。
- 不强制读操作、纯问答或用户直接 Bash 进入任务契约。
- 不持久化跨 Pi 进程的 task ledger;首版只约束当前 agent session。
实施状态
已实施并运行时验证。核心账本测试覆盖任务契约、保守读写判定、条件验证和完成 gate;ThinkingCap 已验证无契约 touch 被阻止且未创建文件,并完成 task_contract -> pwd -> task_verify -> task_complete 无副作用闭环。