摘要:官方 #637 将 Codex 等会话定为 pull 留言;本提案建议在 Codex ACP 显式声明安全 idle 契约 时才启用原生插话(turn/steer),空闲/回合结束安全回落,不偷跑新回合。本地已在 Codeg v0.31.0 + Codex ACP v1.12.0 上完成源码级原型与真实 ACP 验证。希望讨论是否接受该能力门控,以及主按钮/Enter 默认行为的产品取舍。
Motivation
After #637, composer mid-turn sending is honest per delivery channel:
Pull notes and queue are valuable, but they are not the same as native steering (steer inside the running turn). Users who want Codex to “handle this now in the same turn” still cannot.
Codex’s ACP surface can support mid-turn steering if the host can trust idle/ended-turn semantics. Without a capability signal, blindly offering native steer risks:
- Injecting into a session that cannot steer mid-turn;
- Starting an unmanaged extra turn when the original turn already ended;
- UI that claims “inserted into current turn” when the agent only queued/pulled.
We prototyped a capability-gated path that enables native Codex steering only when the adapter advertises a specific safety contract, and verified real same-turn injection on Codex ACP 1.12.0.
Current official behavior (as we understand it)
| Surface |
Behavior today (from #637 and related PRs) |
| Claude |
Native insert into current turn available |
| Codex |
Mid-turn affordance follows delivery channel; native insert not treated as available; pull note + queue |
| Queue row |
Can insert via existing channel (#751, #774); copy distinguishes native ⚡ vs pull 🕐 |
| Composer default mid-turn (Codex) |
Official tree still routes main button / Enter toward queue; insert lives in the menu |
Related history (not duplicates of this proposal):
We did not find an existing issue that proposes native Codex steering behind an adapter safety contract, nor a PR that flips Codex native capability + composer default.
Prior contribution on this repo (unrelated): #766 (Claude /clear transcript pointer).
Proposed capability contract
Adapter (Codex ACP package)
- On initialize / capability advertisement, declare steering metadata used by codeg, e.g.
_meta.steering.idleBehavior = "promptRequired"
(exact field name should be agreed with maintainers; the meaning is what matters).
- While a turn is running: deliver steer via the native path (
turn/steer) and report success (locally observed as injected).
- If the turn already ended / agent is idle: do not open a new turn. Return a host-visible result meaning “no running turn; handle as a normal prompt” (locally:
promptRequired / noRunningTurn).
Codeg backend
- Treat Codex as native-steer capable only when the live adapter advertises the safety contract above.
- If a future adapter upgrade drops the marker, capability goes false — UI falls back to pull/queue; no blind enable.
- Other agents unchanged unless they also advertise an equivalent contract.
Composer UX (product choice — please advise)
Two acceptable upstream shapes; we are not insisting on (A) as a hard default in the first PR:
(A) Default when capable (implemented in the local prototype)
- Mid-turn draft: main button shows ⚡; click / Enter → native steer
- Overflow menu keeps Queue
- Idle: normal send; editing a queued message still saves edit
- Race (turn ends mid-request): fall back to queue; draft not cleared on other failures; no double-submit while steer in flight
- Claude and non-capable agents: unchanged official defaults
(B) Capability without changing the default
- Native insert remains menu-first
- Main button / Enter stay queue-first for Codex
- Capability gate still ships so channel copy can say native insert when true
We recommend (B) for the first merged capability PR, then a follow-up for default UX or a setting, unless maintainers prefer (A) immediately.
Local evidence (prototype)
Environment (public version tags only):
- Codeg v0.31.0
- Codex ACP v1.12.0
- Formal desktop rebuild used Tauri production entry (
--features tauri/custom-protocol --release); no binaries will be attached to GitHub
Behavior observed after the patch:
- Codeg enables Codex native path only when the adapter advertises the safety metadata; otherwise capability stays false
- Real ACP smoke (isolated test session, existing Codex login on the contributor machine): after the agent began reading a test file, a steer request returned
injected; the model answered the injected instruction in the same turn
- Idle / post-turn: host-handled normal send path; no extra rogue turn from the adapter
Tests held locally (will be rebased for any real PR):
- Composer / queue / feedback-related suites (~131): lightning on capable Codex only, Enter vs menu queue, in-flight double-submit guard, end-of-turn queue fallback, failed send keeps draft, idle send, queue edit save
- Adapter suites (~21): initialize metadata, steer-while-running, idle/promptRequired
Why this is product-relevant
- Closer parity with Claude native steering when the runtime can do it safely.
- Fewer false claims in UI: capability comes from the adapter, not a hardcoded agent list.
- Safe degradation: dropped metadata → pull/queue, not silent breakage or surprise turns.
- Composability: pull notes remain for agents without native steer; queue remains for intentional batching.
Open questions for maintainers
- Is native Codex steering desired in product, or is pull-only a long-term stance for Codex?
- Preferred first merge shape: capability + gate only (B) vs gate + default Enter steer (A) vs setting?
- Canonical capability field on Codex ACP: name, enum values, and whether codeg should also require an explicit
nativeSteer bit beyond idle semantics.
- Public adapter package/repo path for Codex ACP so a PR can land in the right place.
- Should Claude’s existing native path be refactored onto the same capability interface in the same effort, or kept separate?
Scope / non-goals (follow-up PR if accepted)
In scope for a “capability layer” PR:
- Codex ACP safety metadata +
turn/steer + idle fallback
- Codeg backend capability detection for Codex
- Tests pinning gate-on / gate-off
- Docs: channel table gains “Codex native (when advertised)”
Explicitly deferred:
- Changing global composer defaults for all users (unless maintainers choose A)
- Other harnesses without their own safety contracts
- Timeline rendering fixes already tracked in open PRs
- Shipping local installers or binaries
Proposed next step
If maintainers want this in-tree, we can open a fork PR limited to the capability contract + backend gate + tests (shape B), and leave default Enter behavior to a separate product decision.
— 胡淇华
Motivation
After #637, composer mid-turn sending is honest per delivery channel:
check_user_feedback) — Codex / Grok / etc.; the agent reads it on the next check.Pull notes and queue are valuable, but they are not the same as native steering (steer inside the running turn). Users who want Codex to “handle this now in the same turn” still cannot.
Codex’s ACP surface can support mid-turn steering if the host can trust idle/ended-turn semantics. Without a capability signal, blindly offering native steer risks:
We prototyped a capability-gated path that enables native Codex steering only when the adapter advertises a specific safety contract, and verified real same-turn injection on Codex ACP 1.12.0.
Current official behavior (as we understand it)
Related history (not duplicates of this proposal):
We did not find an existing issue that proposes native Codex steering behind an adapter safety contract, nor a PR that flips Codex native capability + composer default.
Prior contribution on this repo (unrelated): #766 (Claude
/cleartranscript pointer).Proposed capability contract
Adapter (Codex ACP package)
_meta.steering.idleBehavior = "promptRequired"(exact field name should be agreed with maintainers; the meaning is what matters).
turn/steer) and report success (locally observed asinjected).promptRequired/noRunningTurn).Codeg backend
Composer UX (product choice — please advise)
Two acceptable upstream shapes; we are not insisting on (A) as a hard default in the first PR:
(A) Default when capable (implemented in the local prototype)
(B) Capability without changing the default
We recommend (B) for the first merged capability PR, then a follow-up for default UX or a setting, unless maintainers prefer (A) immediately.
Local evidence (prototype)
Environment (public version tags only):
--features tauri/custom-protocol --release); no binaries will be attached to GitHubBehavior observed after the patch:
injected; the model answered the injected instruction in the same turnTests held locally (will be rebased for any real PR):
Why this is product-relevant
Open questions for maintainers
nativeSteerbit beyond idle semantics.Scope / non-goals (follow-up PR if accepted)
In scope for a “capability layer” PR:
turn/steer+ idle fallbackExplicitly deferred:
Proposed next step
If maintainers want this in-tree, we can open a fork PR limited to the capability contract + backend gate + tests (shape B), and leave default Enter behavior to a separate product decision.
— 胡淇华