mirror of
https://github.com/openai/codex.git
synced 2026-04-24 06:35:50 +00:00
docs: explain sub-agent role config inheritance
Document how sub-agent roles preserve the caller's active profile and model provider unless the role explicitly overrides them. Also explain that multi-agent spawn config is rebuilt from live turn state before role-specific config is layered on top. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
//! Applies agent-role configuration layers on top of an existing session config.
|
||||
//!
|
||||
//! Roles are selected at spawn time and are loaded with the same config machinery as
|
||||
//! `config.toml`. This module resolves built-in and user-defined role files, inserts the role as a
|
||||
//! high-precedence layer, and preserves the caller's current profile/provider unless the role
|
||||
//! explicitly takes ownership of model selection. It does not decide when to spawn a sub-agent or
|
||||
//! which role to use; the multi-agent tool handler owns that orchestration.
|
||||
|
||||
use crate::config::AgentRoleConfig;
|
||||
use crate::config::Config;
|
||||
use crate::config::ConfigOverrides;
|
||||
@@ -13,10 +21,18 @@ use std::path::Path;
|
||||
use std::sync::LazyLock;
|
||||
use toml::Value as TomlValue;
|
||||
|
||||
/// The role name used when a caller omits `agent_type`.
|
||||
pub const DEFAULT_ROLE_NAME: &str = "default";
|
||||
const AGENT_TYPE_UNAVAILABLE_ERROR: &str = "agent type is currently not available";
|
||||
|
||||
/// Applies a role config layer to a mutable config and preserves unspecified keys.
|
||||
/// Applies a named role layer to `config` while preserving caller-owned model selection.
|
||||
///
|
||||
/// The role layer is inserted at session-flag precedence so it can override persisted config, but
|
||||
/// the caller's current `profile` and `model_provider` remain sticky runtime choices unless the
|
||||
/// role explicitly sets `profile`, explicitly sets `model_provider`, or rewrites the active
|
||||
/// profile's `model_provider` in place. Rebuilding the config without those overrides would make a
|
||||
/// spawned agent silently fall back to the default provider, which is the bug this preservation
|
||||
/// logic avoids.
|
||||
pub(crate) async fn apply_role_to_config(
|
||||
config: &mut Config,
|
||||
role_name: Option<&str>,
|
||||
@@ -74,6 +90,8 @@ pub(crate) async fn apply_role_to_config(
|
||||
.map(|profile| profile.contains_key("model_provider"))
|
||||
})
|
||||
.unwrap_or(false);
|
||||
// A role that does not explicitly take ownership of model selection should inherit the
|
||||
// caller's current profile/provider choices across the config reload.
|
||||
let preserve_current_profile = !role_selects_provider && !role_selects_profile;
|
||||
let preserve_current_provider =
|
||||
preserve_current_profile && !role_updates_active_profile_provider;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//! Implements the collaboration tool surface for spawning and managing sub-agents.
|
||||
//!
|
||||
//! This handler translates model tool calls into `AgentControl` operations and keeps spawned
|
||||
//! agents aligned with the live turn that created them. Sub-agents start from the turn's effective
|
||||
//! config, inherit runtime-only state such as provider, approval policy, sandbox, and cwd, and
|
||||
//! then optionally layer role-specific config on top.
|
||||
|
||||
use crate::agent::AgentStatus;
|
||||
use crate::agent::exceeds_thread_spawn_depth_limit;
|
||||
use crate::codex::Session;
|
||||
@@ -35,6 +42,7 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Function-tool handler for the multi-agent collaboration API.
|
||||
pub struct MultiAgentHandler;
|
||||
|
||||
/// Minimum wait timeout to prevent tight polling loops from burning CPU.
|
||||
@@ -894,6 +902,13 @@ fn input_preview(items: &[UserInput]) -> String {
|
||||
parts.join("\n")
|
||||
}
|
||||
|
||||
/// Builds the base config snapshot for a newly spawned sub-agent.
|
||||
///
|
||||
/// The returned config starts from the parent's effective config and then refreshes the
|
||||
/// runtime-owned fields carried on `turn`, including model selection, reasoning settings,
|
||||
/// approval policy, sandbox, and cwd. Role-specific overrides are layered after this step;
|
||||
/// skipping this helper and cloning stale config state directly can send the child agent out with
|
||||
/// the wrong provider or runtime policy.
|
||||
pub(crate) fn build_agent_spawn_config(
|
||||
base_instructions: &BaseInstructions,
|
||||
turn: &TurnContext,
|
||||
@@ -928,6 +943,10 @@ fn build_agent_shared_config(turn: &TurnContext) -> Result<Config, FunctionCallE
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Copies runtime-only turn state onto a child config before it is handed to `AgentControl`.
|
||||
///
|
||||
/// These values are chosen by the live turn rather than persisted config, so leaving them stale
|
||||
/// can make a child agent disagree with its parent about approval policy, cwd, or sandboxing.
|
||||
fn apply_spawn_agent_runtime_overrides(
|
||||
config: &mut Config,
|
||||
turn: &TurnContext,
|
||||
|
||||
Reference in New Issue
Block a user