mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
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:
committed by
GitHub
Unverified
parent
fc8c6b7384
commit
7abfcf220b
@@ -178,6 +178,7 @@ async fn thread_settings_applied_event(sess: &Session) -> EventMsg {
|
||||
reasoning_summary: snapshot.reasoning_summary,
|
||||
personality: snapshot.personality,
|
||||
collaboration_mode: snapshot.collaboration_mode,
|
||||
multi_agent_mode: snapshot.multi_agent_mode,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -445,6 +445,7 @@ pub(crate) struct CodexSpawnArgs {
|
||||
pub(crate) attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
||||
pub(crate) external_time_provider: Option<Arc<dyn TimeProvider>>,
|
||||
pub(crate) inherited_multi_agent_version: Option<MultiAgentVersion>,
|
||||
pub(crate) initial_multi_agent_mode: Option<MultiAgentMode>,
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_multi_agent_version(
|
||||
@@ -529,6 +530,7 @@ impl Codex {
|
||||
attestation_provider,
|
||||
external_time_provider,
|
||||
inherited_multi_agent_version,
|
||||
initial_multi_agent_mode,
|
||||
} = args;
|
||||
let (tx_sub, rx_sub) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY);
|
||||
let (tx_event, rx_event) = async_channel::unbounded();
|
||||
@@ -583,7 +585,7 @@ impl Codex {
|
||||
.await;
|
||||
let multi_agent_version =
|
||||
resolve_multi_agent_version(&conversation_history, inherited_multi_agent_version);
|
||||
let multi_agent_mode = conversation_history.get_multi_agent_mode();
|
||||
let multi_agent_mode = initial_multi_agent_mode;
|
||||
config
|
||||
.validate_multi_agent_v2_config()
|
||||
.map_err(|err| CodexErr::InvalidRequest(err.to_string()))?;
|
||||
@@ -3249,7 +3251,10 @@ impl Session {
|
||||
&turn_context.config.multi_agent_v2,
|
||||
&session_source,
|
||||
turn_context.multi_agent_mode,
|
||||
turn_context.features.enabled(Feature::MultiAgentMode),
|
||||
turn_context
|
||||
.config
|
||||
.features
|
||||
.enabled(Feature::MultiAgentMode),
|
||||
) {
|
||||
items.push(ContextualUserFragment::into(
|
||||
MultiAgentModeInstructions::new(multi_agent_mode),
|
||||
|
||||
@@ -738,6 +738,7 @@ async fn guardian_subagent_does_not_inherit_parent_exec_policy_rules() {
|
||||
attestation_provider: None,
|
||||
external_time_provider: None,
|
||||
inherited_multi_agent_version: None,
|
||||
initial_multi_agent_mode: None,
|
||||
})
|
||||
.await
|
||||
.expect("spawn guardian subagent");
|
||||
|
||||
@@ -388,7 +388,7 @@ impl TurnContext {
|
||||
&self.config.multi_agent_v2,
|
||||
&self.session_source,
|
||||
self.multi_agent_mode,
|
||||
self.features.enabled(Feature::MultiAgentMode),
|
||||
self.config.features.enabled(Feature::MultiAgentMode),
|
||||
),
|
||||
realtime_active: Some(self.realtime_active),
|
||||
effort: self.reasoning_effort.clone(),
|
||||
|
||||
Reference in New Issue
Block a user