mirror of
https://github.com/openai/codex.git
synced 2026-04-25 00:41:46 +03:00
Compare commits
3 Commits
latest-alp
...
baumann-oa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa9af900e1 | ||
|
|
7014bebfed | ||
|
|
8b873c0ba8 |
@@ -6,6 +6,19 @@
|
||||
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
|
||||
"type": "string"
|
||||
},
|
||||
"AgentNicknameCandidatesToml": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"AgentRoleToml": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -53,6 +66,26 @@
|
||||
"format": "uint",
|
||||
"minimum": 1.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"nickname_candidates": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AgentNicknameCandidatesToml"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Global fallback nickname candidates for spawned agent threads.\n\nExample: ```toml [agents] nickname_candidates = [\"Scout\", \"Builder\", \"Reviewer\"]\n\n# Or select a named pack: nickname_candidates = \"succession\"\n\n[agents.nickname_packs] succession = [\"Shiv\", \"Roman\", \"Kendall\"] ```"
|
||||
},
|
||||
"nickname_packs": {
|
||||
"additionalProperties": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"default": {},
|
||||
"description": "Named global fallback nickname candidate packs keyed by pack name.",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
||||
@@ -54,6 +54,10 @@ fn agent_nickname_candidates(
|
||||
return candidates;
|
||||
}
|
||||
|
||||
if !config.agent_nickname_candidates.is_empty() {
|
||||
return config.agent_nickname_candidates.clone();
|
||||
}
|
||||
|
||||
default_agent_nickname_list()
|
||||
.into_iter()
|
||||
.map(ToOwned::to_owned)
|
||||
|
||||
@@ -961,6 +961,94 @@ async fn spawn_thread_subagent_uses_role_specific_nickname_candidates() {
|
||||
assert_eq!(agent_nickname, Some("Atlas".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn spawn_thread_subagent_uses_global_nickname_candidates_when_role_has_no_candidates() {
|
||||
let mut harness = AgentControlHarness::new().await;
|
||||
harness.config.agent_nickname_candidates = vec!["Scout".to_string()];
|
||||
harness.config.agent_roles.insert(
|
||||
"researcher".to_string(),
|
||||
AgentRoleConfig {
|
||||
description: Some("Research role".to_string()),
|
||||
config_file: None,
|
||||
nickname_candidates: None,
|
||||
},
|
||||
);
|
||||
let (parent_thread_id, _parent_thread) = harness.start_thread().await;
|
||||
|
||||
let child_thread_id = harness
|
||||
.control
|
||||
.spawn_agent(
|
||||
harness.config.clone(),
|
||||
text_input("hello child"),
|
||||
Some(SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
|
||||
parent_thread_id,
|
||||
depth: 1,
|
||||
agent_nickname: None,
|
||||
agent_role: Some("researcher".to_string()),
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect("child spawn should succeed");
|
||||
|
||||
let child_thread = harness
|
||||
.manager
|
||||
.get_thread(child_thread_id)
|
||||
.await
|
||||
.expect("child thread should be registered");
|
||||
let snapshot = child_thread.config_snapshot().await;
|
||||
|
||||
let SessionSource::SubAgent(SubAgentSource::ThreadSpawn { agent_nickname, .. }) =
|
||||
snapshot.session_source
|
||||
else {
|
||||
panic!("expected thread-spawn sub-agent source");
|
||||
};
|
||||
assert_eq!(agent_nickname, Some("Scout".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn spawn_thread_subagent_prefers_role_candidates_over_global_nickname_candidates() {
|
||||
let mut harness = AgentControlHarness::new().await;
|
||||
harness.config.agent_nickname_candidates = vec!["Scout".to_string()];
|
||||
harness.config.agent_roles.insert(
|
||||
"researcher".to_string(),
|
||||
AgentRoleConfig {
|
||||
description: Some("Research role".to_string()),
|
||||
config_file: None,
|
||||
nickname_candidates: Some(vec!["Atlas".to_string()]),
|
||||
},
|
||||
);
|
||||
let (parent_thread_id, _parent_thread) = harness.start_thread().await;
|
||||
|
||||
let child_thread_id = harness
|
||||
.control
|
||||
.spawn_agent(
|
||||
harness.config.clone(),
|
||||
text_input("hello child"),
|
||||
Some(SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
|
||||
parent_thread_id,
|
||||
depth: 1,
|
||||
agent_nickname: None,
|
||||
agent_role: Some("researcher".to_string()),
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect("child spawn should succeed");
|
||||
|
||||
let child_thread = harness
|
||||
.manager
|
||||
.get_thread(child_thread_id)
|
||||
.await
|
||||
.expect("child thread should be registered");
|
||||
let snapshot = child_thread.config_snapshot().await;
|
||||
|
||||
let SessionSource::SubAgent(SubAgentSource::ThreadSpawn { agent_nickname, .. }) =
|
||||
snapshot.session_source
|
||||
else {
|
||||
panic!("expected thread-spawn sub-agent source");
|
||||
};
|
||||
assert_eq!(agent_nickname, Some("Atlas".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resume_thread_subagent_restores_stored_nickname_and_role() {
|
||||
let (home, mut config) = test_config().await;
|
||||
|
||||
@@ -139,7 +139,7 @@ impl Guards {
|
||||
active_agents.nickname_reset_count += 1;
|
||||
if let Some(metrics) = codex_otel::metrics::global() {
|
||||
let _ = metrics.counter(
|
||||
"codex.multi_agent.nickname_pool_reset",
|
||||
"codex.multi_agent.nickname_candidates_reset",
|
||||
/*inc*/ 1,
|
||||
&[],
|
||||
);
|
||||
|
||||
@@ -441,6 +441,47 @@ fn normalize_agent_role_nickname_candidates(
|
||||
Ok(Some(normalized_candidates))
|
||||
}
|
||||
|
||||
pub(crate) fn normalize_global_agent_nickname_candidates(
|
||||
field_label: &str,
|
||||
nickname_candidates: Option<&[String]>,
|
||||
) -> std::io::Result<Option<Vec<String>>> {
|
||||
let Some(nickname_candidates) = nickname_candidates else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut normalized_candidates = Vec::with_capacity(nickname_candidates.len());
|
||||
let mut seen_candidates = BTreeSet::new();
|
||||
|
||||
for nickname in nickname_candidates {
|
||||
let normalized_nickname = nickname.trim();
|
||||
if normalized_nickname.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !normalized_nickname
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || matches!(c, ' ' | '-' | '_'))
|
||||
{
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!(
|
||||
"{field_label} may only contain ASCII letters, digits, spaces, hyphens, and underscores"
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
if seen_candidates.insert(normalized_nickname.to_owned()) {
|
||||
normalized_candidates.push(normalized_nickname.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
if normalized_candidates.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(normalized_candidates))
|
||||
}
|
||||
}
|
||||
|
||||
fn discover_agent_roles_in_dir(
|
||||
agents_dir: &Path,
|
||||
declared_role_files: &BTreeSet<PathBuf>,
|
||||
|
||||
@@ -107,6 +107,24 @@ persistence = "none"
|
||||
history_no_persistence_cfg.history
|
||||
);
|
||||
|
||||
let agents_with_nickname_candidates = r#"
|
||||
[agents]
|
||||
nickname_candidates = ["Scout", "Builder", "Reviewer"]
|
||||
"#;
|
||||
let agents_with_nickname_candidates_cfg =
|
||||
toml::from_str::<ConfigToml>(agents_with_nickname_candidates)
|
||||
.expect("TOML deserialization should succeed");
|
||||
assert_eq!(
|
||||
agents_with_nickname_candidates_cfg
|
||||
.agents
|
||||
.and_then(|agents| agents.nickname_candidates),
|
||||
Some(AgentNicknameCandidatesToml::Candidates(vec![
|
||||
"Scout".to_string(),
|
||||
"Builder".to_string(),
|
||||
"Reviewer".to_string()
|
||||
]))
|
||||
);
|
||||
|
||||
let memories = r#"
|
||||
[memories]
|
||||
no_memories_if_mcp_or_web_search = true
|
||||
@@ -1579,6 +1597,153 @@ profile = "project"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_agents_nickname_candidates_are_used_without_project_override() -> std::io::Result<()>
|
||||
{
|
||||
let codex_home = TempDir::new()?;
|
||||
let workspace = TempDir::new()?;
|
||||
let workspace_key = workspace.path().to_string_lossy().replace('\\', "\\\\");
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
format!(
|
||||
r#"
|
||||
[agents]
|
||||
nickname_candidates = [" Scout ", "", "Builder", "Scout", " ", "Reviewer"]
|
||||
|
||||
[projects."{workspace_key}"]
|
||||
trust_level = "trusted"
|
||||
"#,
|
||||
),
|
||||
)?;
|
||||
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(workspace.path().to_path_buf()))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
config.agent_nickname_candidates,
|
||||
vec![
|
||||
"Scout".to_string(),
|
||||
"Builder".to_string(),
|
||||
"Reviewer".to_string()
|
||||
]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn project_agents_nickname_candidates_override_user_candidates() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let workspace = TempDir::new()?;
|
||||
let workspace_key = workspace.path().to_string_lossy().replace('\\', "\\\\");
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
format!(
|
||||
r#"
|
||||
[agents]
|
||||
nickname_candidates = ["Scout", "Builder"]
|
||||
|
||||
[projects."{workspace_key}"]
|
||||
trust_level = "trusted"
|
||||
"#,
|
||||
),
|
||||
)?;
|
||||
let project_config_dir = workspace.path().join(".codex");
|
||||
std::fs::create_dir_all(&project_config_dir)?;
|
||||
std::fs::write(
|
||||
project_config_dir.join(CONFIG_TOML_FILE),
|
||||
r#"
|
||||
[agents]
|
||||
nickname_candidates = ["Reviewer"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(workspace.path().to_path_buf()))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
config.agent_nickname_candidates,
|
||||
vec!["Reviewer".to_string()]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn empty_agents_nickname_candidates_preserve_default_behavior() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let workspace = TempDir::new()?;
|
||||
let workspace_key = workspace.path().to_string_lossy().replace('\\', "\\\\");
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
format!(
|
||||
r#"
|
||||
[agents]
|
||||
nickname_candidates = ["", " "]
|
||||
|
||||
[projects."{workspace_key}"]
|
||||
trust_level = "trusted"
|
||||
"#,
|
||||
),
|
||||
)?;
|
||||
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(workspace.path().to_path_buf()))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
assert!(config.agent_nickname_candidates.is_empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn selected_agents_nickname_pack_is_used() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let workspace = TempDir::new()?;
|
||||
let workspace_key = workspace.path().to_string_lossy().replace('\\', "\\\\");
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
format!(
|
||||
r#"
|
||||
[agents]
|
||||
nickname_candidates = "succession"
|
||||
|
||||
[agents.nickname_packs]
|
||||
succession = ["Shiv", " Roman ", "", "Kendall", "Shiv"]
|
||||
the_office = ["Pam", "Jim"]
|
||||
|
||||
[projects."{workspace_key}"]
|
||||
trust_level = "trusted"
|
||||
"#
|
||||
),
|
||||
)?;
|
||||
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(workspace.path().to_path_buf()))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
config.agent_nickname_candidates,
|
||||
vec![
|
||||
"Shiv".to_string(),
|
||||
"Roman".to_string(),
|
||||
"Kendall".to_string()
|
||||
]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn profile_sandbox_mode_overrides_base() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
@@ -3064,6 +3229,8 @@ fn load_config_rejects_missing_agent_role_config_file() -> std::io::Result<()> {
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -3929,6 +4096,8 @@ fn load_config_normalizes_agent_role_nickname_candidates() -> std::io::Result<()
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -3970,6 +4139,8 @@ fn load_config_rejects_empty_agent_role_nickname_candidates() -> std::io::Result
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4005,6 +4176,8 @@ fn load_config_rejects_duplicate_agent_role_nickname_candidates() -> std::io::Re
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4040,6 +4213,8 @@ fn load_config_rejects_unsafe_agent_role_nickname_candidates() -> std::io::Resul
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4284,6 +4459,7 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
tool_output_token_limit: None,
|
||||
agent_max_threads: DEFAULT_AGENT_MAX_THREADS,
|
||||
agent_max_depth: DEFAULT_AGENT_MAX_DEPTH,
|
||||
agent_nickname_candidates: Vec::new(),
|
||||
agent_roles: BTreeMap::new(),
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
@@ -4425,6 +4601,7 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
tool_output_token_limit: None,
|
||||
agent_max_threads: DEFAULT_AGENT_MAX_THREADS,
|
||||
agent_max_depth: DEFAULT_AGENT_MAX_DEPTH,
|
||||
agent_nickname_candidates: Vec::new(),
|
||||
agent_roles: BTreeMap::new(),
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
@@ -4564,6 +4741,7 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
tool_output_token_limit: None,
|
||||
agent_max_threads: DEFAULT_AGENT_MAX_THREADS,
|
||||
agent_max_depth: DEFAULT_AGENT_MAX_DEPTH,
|
||||
agent_nickname_candidates: Vec::new(),
|
||||
agent_roles: BTreeMap::new(),
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
@@ -4689,6 +4867,7 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
tool_output_token_limit: None,
|
||||
agent_max_threads: DEFAULT_AGENT_MAX_THREADS,
|
||||
agent_max_depth: DEFAULT_AGENT_MAX_DEPTH,
|
||||
agent_nickname_candidates: Vec::new(),
|
||||
agent_roles: BTreeMap::new(),
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
|
||||
@@ -417,6 +417,9 @@ pub struct Config {
|
||||
/// Maximum nesting depth allowed for spawned agent threads.
|
||||
pub agent_max_depth: i32,
|
||||
|
||||
/// Global fallback nickname candidates for spawned agent threads.
|
||||
pub agent_nickname_candidates: Vec<String>,
|
||||
|
||||
/// User-defined role declarations keyed by role name.
|
||||
pub agent_roles: BTreeMap<String, AgentRoleConfig>,
|
||||
|
||||
@@ -1666,6 +1669,29 @@ pub struct AgentsToml {
|
||||
#[schemars(range(min = 1))]
|
||||
pub job_max_runtime_seconds: Option<u64>,
|
||||
|
||||
/// Global fallback nickname candidates for spawned agent threads.
|
||||
///
|
||||
/// Example:
|
||||
/// ```toml
|
||||
/// [agents]
|
||||
/// nickname_candidates = ["Scout", "Builder", "Reviewer"]
|
||||
///
|
||||
/// # Or select a named pack:
|
||||
/// nickname_candidates = "succession"
|
||||
///
|
||||
/// [agents.nickname_packs]
|
||||
/// succession = ["Shiv", "Roman", "Kendall"]
|
||||
/// ```
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "deserialize_optional_agent_nickname_candidates"
|
||||
)]
|
||||
pub nickname_candidates: Option<AgentNicknameCandidatesToml>,
|
||||
|
||||
/// Named global fallback nickname candidate packs keyed by pack name.
|
||||
#[serde(default)]
|
||||
pub nickname_packs: BTreeMap<String, Vec<String>>,
|
||||
|
||||
/// User-defined role declarations keyed by role name.
|
||||
///
|
||||
/// Example:
|
||||
@@ -1679,6 +1705,22 @@ pub struct AgentsToml {
|
||||
pub roles: BTreeMap<String, AgentRoleToml>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(untagged)]
|
||||
pub enum AgentNicknameCandidatesToml {
|
||||
Candidates(Vec<String>),
|
||||
Pack(String),
|
||||
}
|
||||
|
||||
fn deserialize_optional_agent_nickname_candidates<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<AgentNicknameCandidatesToml>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Option::<AgentNicknameCandidatesToml>::deserialize(deserializer)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct AgentRoleConfig {
|
||||
/// Human-facing role documentation used in spawn tool guidance.
|
||||
@@ -2442,6 +2484,42 @@ impl Config {
|
||||
"agents.job_max_runtime_seconds must fit within a 64-bit signed integer",
|
||||
));
|
||||
}
|
||||
let agent_nickname_candidates = {
|
||||
let candidates = match cfg.agents.as_ref().and_then(|agents| {
|
||||
agents.nickname_candidates.as_ref().map(|nickname_candidates| {
|
||||
(nickname_candidates, &agents.nickname_packs)
|
||||
})
|
||||
}) {
|
||||
Some((AgentNicknameCandidatesToml::Candidates(candidates), _)) => {
|
||||
Some(candidates.as_slice())
|
||||
}
|
||||
Some((AgentNicknameCandidatesToml::Pack(pack), nickname_packs)) => {
|
||||
let selected_pack = pack.trim();
|
||||
if selected_pack.is_empty() {
|
||||
None
|
||||
} else {
|
||||
match nickname_packs.get(selected_pack) {
|
||||
Some(candidates) => Some(candidates.as_slice()),
|
||||
None => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!(
|
||||
"agents.nickname_candidates references unknown nickname pack `{selected_pack}`"
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
agent_roles::normalize_global_agent_nickname_candidates(
|
||||
"agents.nickname_candidates",
|
||||
candidates,
|
||||
)?
|
||||
}
|
||||
.unwrap_or_default();
|
||||
let background_terminal_max_timeout = cfg
|
||||
.background_terminal_max_timeout
|
||||
.unwrap_or(DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS)
|
||||
@@ -2707,6 +2785,7 @@ impl Config {
|
||||
tool_output_token_limit: cfg.tool_output_token_limit,
|
||||
agent_max_threads,
|
||||
agent_max_depth,
|
||||
agent_nickname_candidates,
|
||||
agent_roles,
|
||||
memories: cfg.memories.unwrap_or_default().into(),
|
||||
agent_job_max_runtime_seconds,
|
||||
|
||||
Reference in New Issue
Block a user