Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 75 additions & 1 deletion docs/rfc/codex-backend-phase2-feasibility.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Codex Backend — Phase 2 Feature 可行性分析

> **状态**: 可行性分析(含 codex 0.141.0 实测探针,2026-06-21);**embedded_context 已实现**(§6)
> **状态**: 可行性分析(含 codex 0.141.0 实测探针,2026-06-21);**embedded_context 已实现**(§6);**§7/§8 二次实测后结论翻转**
> **前置**: `codex-backend.md` v2 + `codex-backend-validation.md`(Phase 1 已交付 PR #2216)
> **范围**: 三个 claude 专属 UX Feature 在 codex backend 的落地评估。
>
> **⚠️ 结论翻转(2026-06-21 二次深度实测)**:初判 passthrough=可行/askuser=阻塞。实测后**对调**——passthrough 的 `turn/steer` 在 Bedrock gpt-5.5 上 ACK 成功但模型不采纳(silent drop,比 Collect 更差)→**推迟**;askuser 的 `requestUserInput` schema 已完整捕获→**可实现**。详见 §7、§8。

---

Expand Down Expand Up @@ -126,3 +128,75 @@ Phase 1 后,codex 与 kiro 的 Feature map **逐位相同**(7 项),三
| naozhi 侧代码 | 纯透传 | 纯透传(零文件读取,零新增安全面) |

弱于 claude 的静态保证,但匹配 dashboard 契约且零安全面。若未来要 claude 式强保证,可走方案 B(naozhi 服务端内联),但会引入路径穿越/大小限制/workspace confine 安全面,留作独立 RFC。

---

## 7. passthrough — **推迟(实测否定)**

§2 初判"原语齐备、适配器可行"。但 §2 只验证了 `clientUserMessageId` round-trip 与 `turn/steer` 的 **ACK**,没验证 steer 对模型输出的**实际效果**。2026-06-21 补做 3 组效果探针(Bedrock gpt-5.5):

| 探针 | turn 内容 | steer 内容 | 结果 |
|---|---|---|---|
| 1 | "10 段罗马帝国史"(长输出) | "IGNORE…只输出 PIVOT 并停" | steer ACK 成功;**输出未 pivot**,继续写罗马史,35s 未完成 |
| 2 | "慢数 1-8" | "数完后说 PIVOT" | 单 turn 5.5s 完成;**无 PIVOT**,无第二 turn |
| 3 | "跑 sleep 8 的 bash 再报告"(tool 占用 9s) | turn 刚 started 即 steer "末尾加 PIVOT" | steer ACK 成功 `{turnId}`;turn 9.2s 完成;**无 PIVOT** |

**结论**:`turn/steer` 在 Bedrock gpt-5.5 上**返回成功但模型不采纳 steered 输入**——是 silent no-op。naozhi 若据此开 passthrough,会出现:用户发 `/urgent` 或并发消息 → naozhi ACK、round-trip slot、显示已发 → 但 codex 实际丢弃。**比现有 Collect 模式更差**(Collect 可靠排队+下一 turn 投递,不丢消息)。

**决策:不 ship。** `passthrough` 保持 false,codex 继续走 Collect 模式(可靠)。

未决/可能复活的条件:① 非-Bedrock(OpenAI 直连 gpt-5.x-codex)下 steer 是否生效未测——若生效可做 per-provider 门控;② codex 后续版本明确 steer 语义。届时重测再议。

> 教训:协议原语的"ACK 成功"≠"语义生效"。验证可行性必须测**端到端效果**,不能止于 RPC 返回码。

---

## 8. askuser — **可实现(schema 已捕获)**

§3 初判"BLOCKED:requestUserInput schema 未验证"。2026-06-21 live 捕获补全(codex 0.141,`experimentalFeature/list` + 实跑):

### 8.1 触发前提
- 反向请求方法名确认为 **`item/tool/requestUserInput`**(`ServerRequest.json` 注册表 index[2])。
- **默认关闭**,必须 `--enable default_mode_request_user_input`(`-c features.default_mode_request_user_input=true`)。RFC 旧猜名 `request_user_input` 错误(server 启动即 `Unknown feature flag` 退出)。
- 另有 stable 且默认开的 `mcpServer/elicitation/request`(gated `tool_call_mcp_elicitation`),form/url schema 不同,非本卡片路径。

### 8.2 请求 schema(实测样本)
```json
{ "method":"item/tool/requestUserInput", "id":0, "params":{
"threadId":"...", "turnId":"...", "itemId":"call_0",
"questions":[{ "id":"deploy_target", "header":"Deploy",
"question":"What do you want to deploy?", "isOther":true, "isSecret":false,
"options":[{"label":"API server (Recommended)","description":"..."}, ...] }],
"autoResolutionMs":null }}
```
- question 必填 `id`/`header`/`question`;可选 `isOther`(允许自由文本)/`isSecret`(掩码)/`options`(null⇒自由文本题)。
- option 只有 `label`+`description`,**label 即回传值**(无独立 value)。`questions` 是数组(一卡多问)。

### 8.3 响应 schema(实测解锁 turn)
```json
{ "id":0, "result":{ "answers":{ "<questionId>":{ "answers":["<选中的 label>"] } } } }
```
- `result.answers` 是**按 question id 键控的对象**(非数组);值 `{answers:[label,...]}`(数组支持多选)。
- **校验宽松不报错**:错误/空 `result` 不会 JSON-RPC error,而是被当"用户未答"→模型**重发同一 requestUserInput**(观测到 id=1 重试)。故必须回**正确的 answers 键控结构**,否则死循环。

### 8.4 阻塞性(实测确认)
- `turn/start` 立即返回 result,但 **turn 不 completed** 直到反向请求被回答。挂起 12s 无 `turn/completed`;回答后才出 `item/agentMessage/delta` + `turn/completed`,并伴随 `serverRequest/resolved {requestId}`。
- requestUserInput **不**带自己的 item/started/completed,只作为 reverse RPC(keyed by itemId=call_0)出现。

### 8.5 与 claude askuser 的根本差异(实现难点)
claude 在 `-p` 下 **fire-and-forget**(~3ms 自注 `is_error:true` tool_result,turn 正常结束,答案作**下一轮 user 消息**回流)。`askuser-question.md` §"推翻的原设计假设"**明确删掉了** pendingQuestion 表/TTL/阻塞等待。

codex **真阻塞**——这些机制要**全部加回来**:
- ReadEvent 解析 `item/tool/requestUserInput` → `cli.AskQuestion`(itemId=ToolUseID,questions→Items,options.label→Opt)。
- pending 表持有 `requestId`+`itemId`+TTL(autoResolutionMs 可驱动)。
- 用户答案**不是发新 turn**,而是回 `{result:{answers:{qid:{answers:[label]}}}}` 到那条阻塞 RPC(HandleEvent 或新写回路径)。
- 用户不答 → 必须 TTL 自动回一个 decline-shaped answer 防 turn 永挂 + 模型重发死循环。

### 8.6 落地估时 / 风险
- ~2-3 天。中风险:`default_mode_request_user_input` 标 `underDevelopment`(schema 可能变);阻塞语义要求 naozhi dispatch 持 turn 开(与 Collect/passthrough 的 turn 生命周期假设不同)。
- 建议:独立 PR;先在 BuildArgs 加 flag + ReadEvent 解析 + pending 表 + 回写路径,TTL 兜底必须有。

### 8.7 推荐顺序更新(取代 §4)
1. ✅ embedded_context(已交付 PR #2219)
2. **askuser**(可实现,schema 齐备,~2-3d,独立 PR)
3. ~~passthrough~~ → **推迟**(§7,Bedrock steer 实测无效)
Loading