Skip to content

【胡淇华】feat(chat): enable native Codex steering when ACP advertises a safe idle contract #796

Description

@andrehqh

摘要:官方 #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:

  1. Injecting into a session that cannot steer mid-turn;
  2. Starting an unmanaged extra turn when the original turn already ended;
  3. 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)

  1. 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).
  2. While a turn is running: deliver steer via the native path (turn/steer) and report success (locally observed as injected).
  3. 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

  1. Treat Codex as native-steer capable only when the live adapter advertises the safety contract above.
  2. If a future adapter upgrade drops the marker, capability goes false — UI falls back to pull/queue; no blind enable.
  3. 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

  1. Closer parity with Claude native steering when the runtime can do it safely.
  2. Fewer false claims in UI: capability comes from the adapter, not a hardcoded agent list.
  3. Safe degradation: dropped metadata → pull/queue, not silent breakage or surprise turns.
  4. Composability: pull notes remain for agents without native steer; queue remains for intentional batching.

Open questions for maintainers

  1. Is native Codex steering desired in product, or is pull-only a long-term stance for Codex?
  2. Preferred first merge shape: capability + gate only (B) vs gate + default Enter steer (A) vs setting?
  3. Canonical capability field on Codex ACP: name, enum values, and whether codeg should also require an explicit nativeSteer bit beyond idle semantics.
  4. Public adapter package/repo path for Codex ACP so a PR can land in the right place.
  5. 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.


— 胡淇华

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions