Skip to content

feat(chat): 为 Pi 启用原生引导 —— pi-acp 已可满足 promptRequired 安全门控(附端到端实测) #872

Open

Description

@djt889

摘要

Pi 目前被 registry.rs 的 steering_prompt_required_min_version() 排除在原生引导之外(_ => None),同时 pull 通道对它也不可用,导致 Pi 用户在 codeg 里完全没有中途干预手段。

但现状已经变了:pi-acp 社区已有一个实现完整安全契约的 PR(svkozak/pi-acp#115),我按同一契约独立实现并做了端到端验证——三个 outcome 全部按 codeg 要求的行为,且不产生 #934 那类"游离回合"。

因此建议:待 pi-acp 上游合并后,把 Pi 加入门控;或先落地与 #796 相同的通用能力门控机制。


一、现状(codeg v0.32.4,与上游 main 一致)

Pi 会话的 session/new 快照(实测):

{
  "connection_id": "ff933fd2-680a-42e1-b536-d63d7947b056",
  "status": "connected",
  "feedback_tool_available":   false,
  "native_steering_available": false
}

两条通道都是 false,UI 上运行回合时输入区只剩「取消」按钮,没有任何插话入口。

即使适配器主动宣称能力,codeg 仍然拒绝启用——补丁后日志:

[ACP][Pi] steering: advertised=true, agent_version=Some("0.0.34"), native=false
                          ↑ 适配器已宣称      ↑ 被策略表拦下

策略表(registry.rs,含 steering_prompt_required_min_version_gates_claude_only 测试锁定):

match agent_type {
    AgentType::ClaudeCode => Some("0.65.0"),
    _ => None,          // ← Pi / Codex / 其它
}

二、pull 通道对 Pi 同样不可用

codeg 的降级方案是注入 codeg-mcp 反馈工具(check_user_feedback),但 pi-acp 接收后直接丢弃 mcpServers:

constructor(opts) {
  this.mcpServers = opts.mcpServers;   // 第 855 行赋值
  // ...整个 3102 行文件里再无任何引用
}

所以 feedback_tool_available: false。这不是 codeg 的问题(上游 PR svkozak/pi-acp#145 正在修),但结果是:Pi 用户既没有 native 也没有 pull,只能等回合跑完。

三、Pi 已经能满足 codeg 的安全契约

registry.rs 的注释列了两个条件,pi-acp 侧都能满足:

条件 1:尊重 _meta.steering.idleBehavior = "promptRequired" opt-in

svkozak/pi-acp#115(davidhs26, 2026-08-20, open)实现的正是这个契约:

  • initialize() 上报 _meta.steering.supported = true
  • _session/steering 三种 outcome:injected / promptRequired / startedNewTurn
  • 空闲 + opt-in → promptRequired,不消费内容

条件 2(注释里的 ACTIVE 顾虑):steered work 期间保持 owning session/prompt in flight

这一点 _session/steering 形态天然满足,而且比 #91 的方案更稳妥:

方案 机制 与 codeg 回合生命周期的关系
#91 把并发 session/prompt 路由到 steer 需要对齐 concurrent prompt 的 resolve 时机——正是 #934 出问题的地方
#115 / _session/steering 独立扩展请求,立即返回 {outcome:"injected"} 完全不参与 prompt 生命周期;原 session/prompt 照常等到 agent_settled,不会被 steer 提前 settle

也就是说:steer 请求立即返回、不产生新回合、不影响原 prompt 的终端事件——不存在"宿主请求不拥有的游离回合"。这正是 codeg 想避免的场景。

四、本地端到端验证

我按上述契约做了一份等价实现(pi-acp 0.0.34 + 该补丁),用原生 ACP stdio 驱动,结果:

空闲态(带 opt-in)

→ _session/steering  { sessionId, prompt:[...], _meta:{steering:{idleBehavior:"promptRequired"}} }
← { "outcome": "promptRequired", "reason": "noRunningTurn" }

→ 不偷开新回合 ✅

运行中(prompt 强制跑 sleep 30 的 bash 调用,在它执行期间插入)

← { "outcome": "injected", "disposition": "queued" }

并且 agent 在同一回合内确实收到了注入内容——它在后续回复里输出了我要求的标记词。这是硬证据:消息进了模型上下文,不只是协议层回了句话。

未破坏原有行为

→ _session/nonexistent
← -32601 "Method not found"

methodNotFound 语义保留 ✅

验证方法可复现:起一个 pi-acp 子进程 → ACP initialize(检查 _meta.steering.supported)→ session/new → 发一个带 sleep 的 prompt → 中途发 _session/steering → 断言返回 injected 且 agent 回复含注入内容。无需 LLM 也可验证前两项(idle + methodNotFound)。

五、请求

  1. 待 svkozak/pi-acp#115 上游合并后,把 Pi 加入 steering_prompt_required_min_version(版本取首个带该契约的发布版)。
  2. 或者,如果 【胡淇华】feat(chat): enable native Codex steering when ACP advertises a safe idle contract #796 的通用能力门控机制先落地("live adapter 宣称 + 版本证明"而非静态白名单),Pi 应能随之自动生效——建议门控判定里显式包含 Pi 的适配器版本证明路径。
  3. 在适配器尚未就绪期间,是否可以在 UI 上把 Pi 的中途发送语义说明得明确一些?现在用户看到的是"发了没反应",容易误判为 bug(对比 ACP "redirect" fabricates empty ~1ms turn_completes when the agent advertises steering=false, wedging the conversation (cancelled/pending_review) and swallowing the send #590 那类"看起来卡死"的体验)。

六、补充说明

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions