mirror of
https://github.com/openai/codex.git
synced 2026-10-25 18:28:38 +00:00
Add named agent nickname packs
This commit is contained in:
@@ -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": {
|
||||
@@ -55,11 +68,24 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"nickname_candidates": {
|
||||
"description": "Global fallback nickname candidates for spawned agent threads.\n\nExample: ```toml [agents] nickname_candidates = [\"Scout\", \"Builder\", \"Reviewer\"] ```",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"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"
|
||||
},
|
||||
"type": "array"
|
||||
"default": {},
|
||||
"description": "Named global fallback nickname candidate packs keyed by pack name.",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
||||
@@ -118,11 +118,11 @@ nickname_candidates = ["Scout", "Builder", "Reviewer"]
|
||||
agents_with_nickname_candidates_cfg
|
||||
.agents
|
||||
.and_then(|agents| agents.nickname_candidates),
|
||||
Some(vec![
|
||||
Some(AgentNicknameCandidatesToml::Candidates(vec![
|
||||
"Scout".to_string(),
|
||||
"Builder".to_string(),
|
||||
"Reviewer".to_string()
|
||||
])
|
||||
]))
|
||||
);
|
||||
|
||||
let memories = r#"
|
||||
@@ -1704,6 +1704,46 @@ trust_level = "trusted"
|
||||
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()?;
|
||||
@@ -3190,6 +3230,7 @@ fn load_config_rejects_missing_agent_role_config_file() -> std::io::Result<()> {
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4056,6 +4097,7 @@ fn load_config_normalizes_agent_role_nickname_candidates() -> std::io::Result<()
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4098,6 +4140,7 @@ fn load_config_rejects_empty_agent_role_nickname_candidates() -> std::io::Result
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4134,6 +4177,7 @@ fn load_config_rejects_duplicate_agent_role_nickname_candidates() -> std::io::Re
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4170,6 +4214,7 @@ fn load_config_rejects_unsafe_agent_role_nickname_candidates() -> std::io::Resul
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
nickname_candidates: None,
|
||||
nickname_packs: BTreeMap::new(),
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
|
||||
@@ -1675,8 +1675,22 @@ pub struct AgentsToml {
|
||||
/// ```toml
|
||||
/// [agents]
|
||||
/// nickname_candidates = ["Scout", "Builder", "Reviewer"]
|
||||
///
|
||||
/// # Or select a named pack:
|
||||
/// nickname_candidates = "succession"
|
||||
///
|
||||
/// [agents.nickname_packs]
|
||||
/// succession = ["Shiv", "Roman", "Kendall"]
|
||||
/// ```
|
||||
pub nickname_candidates: Option<Vec<String>>,
|
||||
#[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.
|
||||
///
|
||||
@@ -1691,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.
|
||||
@@ -2454,12 +2484,41 @@ impl Config {
|
||||
"agents.job_max_runtime_seconds must fit within a 64-bit signed integer",
|
||||
));
|
||||
}
|
||||
let agent_nickname_candidates = agent_roles::normalize_global_agent_nickname_candidates(
|
||||
"agents.nickname_candidates",
|
||||
cfg.agents
|
||||
.as_ref()
|
||||
.and_then(|agents| agents.nickname_candidates.as_deref()),
|
||||
)?
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user