Expose thread-level multi-agent mode (#28792)

## Why

Once multi-agent mode can be selected per turn, clients also need to
choose the initial selection when creating a thread and observe that
selection through lifecycle and settings APIs.

The selected value is intentionally distinct from the effective
model-visible value: no client selection is represented as `null`, even
though an eligible multi-agent v2 turn derives `explicitRequestOnly` as
its effective default.

## What changed

- Add the optional experimental `thread/start.multiAgentMode` parameter
and pass it through thread creation.
- Preserve an omitted initial value as an unset selection rather than
eagerly storing `explicitRequestOnly`.
- Apply an explicit `thread/start` selection to the first turn through
the session configuration established at thread creation.
- Restore the latest persisted effective mode as the selected baseline
on cold resume when rollout history contains one.
- Inherit the optional selected mode from a loaded parent when creating
related runtime threads.
- Return the current selected `multiAgentMode` from `thread/start`,
`thread/resume`, `thread/fork`, and thread settings, using `null` when
no mode is selected.
- Keep lifecycle reporting independent from model capability and feature
eligibility; core turn construction remains responsible for calculating
and persisting the effective mode.

## Not covered

- Clearing an existing loaded-session selection back to unset through
`turn/start`; omitted or `null` currently retains the session's
selection.
- A TUI control, slash command, or `config.toml` preference.

## Verification

- `CARGO_INCREMENTAL=0 just test -p codex-app-server-protocol`
- `CARGO_INCREMENTAL=0 just test -p codex-app-server multi_agent_mode`

The focused app-server coverage verifies explicit `thread/start`
initialization, first-turn prompting, nullable reporting for an omitted
selection, and retention of selections that are not currently
runtime-eligible.

## Stack

Stacked on #28685. This PR contains only the thread initialization and
lifecycle/settings API layer.
This commit is contained in:
Shijie Rao
2026-06-19 10:50:44 +02:00
committed by GitHub
parent fc8c6b7384
commit 7abfcf220b
46 changed files with 620 additions and 36 deletions
+1
View File
@@ -641,6 +641,7 @@ impl TestCodexBuilder {
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
multi_agent_mode: None,
parent_trace: None,
environments,
thread_extension_init: Default::default(),
+2
View File
@@ -441,6 +441,7 @@ async fn loads_user_instructions_without_a_primary_environment() -> Result<()> {
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
multi_agent_mode: None,
parent_trace: None,
environments: Vec::new(),
thread_extension_init: Default::default(),
@@ -648,6 +649,7 @@ async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapsho
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
multi_agent_mode: None,
parent_trace: None,
environments: vec![
TurnEnvironmentSelection {
@@ -751,6 +751,7 @@ async fn subagent_stop_replaces_stop_and_skips_internal_subagents() -> Result<()
thread_source: None,
dynamic_tools: Vec::new(),
metrics_service_name: None,
multi_agent_mode: None,
parent_trace: None,
environments: Vec::new(),
thread_extension_init: Default::default(),