mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Resolve relative agent role config paths from layers (#19261)
Fixes #19257. ## Summary Agent roles declared in config layers can set `config_file` to a relative path, but deserializing the layer-local `[agents.*]` table happened without an `AbsolutePathBuf` base path. That caused configs like `config_file = "agents/my-role.toml"` to fail with `AbsolutePathBuf deserialized without a base path`. This updates agent role layer loading to deserialize `[agents.*]` while the layer config folder is active as the path base, matching the behavior documented for `AgentRoleToml.config_file`. It also adds coverage for a user config layer with a relative agent role `config_file`.
This commit is contained in:
committed by
GitHub
Unverified
parent
4816b89204
commit
d87d918716
@@ -33,7 +33,8 @@ pub(crate) async fn load_agent_roles(
|
||||
for layer in layers {
|
||||
let mut layer_roles: BTreeMap<String, AgentRoleConfig> = BTreeMap::new();
|
||||
let mut declared_role_files = BTreeSet::new();
|
||||
let agents_toml = match agents_toml_from_layer(&layer.config) {
|
||||
let config_folder = layer.config_folder();
|
||||
let agents_toml = match agents_toml_from_layer(&layer.config, config_folder.as_deref()) {
|
||||
Ok(agents_toml) => agents_toml,
|
||||
Err(err) => {
|
||||
push_agent_role_warning(startup_warnings, err);
|
||||
@@ -169,11 +170,16 @@ fn merge_missing_role_fields(role: &mut AgentRoleConfig, fallback: &AgentRoleCon
|
||||
.or(fallback.nickname_candidates.clone());
|
||||
}
|
||||
|
||||
fn agents_toml_from_layer(layer_toml: &TomlValue) -> std::io::Result<Option<AgentsToml>> {
|
||||
fn agents_toml_from_layer(
|
||||
layer_toml: &TomlValue,
|
||||
config_base_dir: Option<&Path>,
|
||||
) -> std::io::Result<Option<AgentsToml>> {
|
||||
let Some(agents_toml) = layer_toml.get("agents") else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// AbsolutePathBufGuard resolves relative paths while it remains in scope.
|
||||
let _guard = config_base_dir.map(AbsolutePathBufGuard::new);
|
||||
agents_toml
|
||||
.clone()
|
||||
.try_into()
|
||||
|
||||
@@ -4017,6 +4017,63 @@ nickname_candidates = ["Hypatia", "Noether"]
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn agent_role_relative_config_file_resolves_from_config_layer() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let role_config_path = codex_home.path().join("agents").join("researcher.toml");
|
||||
tokio::fs::create_dir_all(
|
||||
role_config_path
|
||||
.parent()
|
||||
.expect("role config should have a parent directory"),
|
||||
)
|
||||
.await?;
|
||||
tokio::fs::write(
|
||||
&role_config_path,
|
||||
"developer_instructions = \"Research carefully\"\nmodel = \"gpt-5\"",
|
||||
)
|
||||
.await?;
|
||||
let layer_config = toml::from_str(
|
||||
r#"[agents.researcher]
|
||||
description = "Research role"
|
||||
config_file = "./agents/researcher.toml"
|
||||
"#,
|
||||
)
|
||||
.expect("agent role layer config should parse");
|
||||
let config_layer_stack = crate::config_loader::ConfigLayerStack::new(
|
||||
vec![crate::config_loader::ConfigLayerEntry::new(
|
||||
codex_app_server_protocol::ConfigLayerSource::User {
|
||||
file: codex_home.path().join(CONFIG_TOML_FILE).abs(),
|
||||
},
|
||||
layer_config,
|
||||
)],
|
||||
Default::default(),
|
||||
crate::config_loader::ConfigRequirementsToml::default(),
|
||||
)
|
||||
.map_err(std::io::Error::other)?;
|
||||
|
||||
let config = Config::load_config_with_layer_stack(
|
||||
LOCAL_FS.as_ref(),
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides {
|
||||
cwd: Some(codex_home.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.abs(),
|
||||
config_layer_stack,
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
config
|
||||
.agent_roles
|
||||
.get("researcher")
|
||||
.and_then(|role| role.config_file.as_ref()),
|
||||
Some(&role_config_path)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn agent_role_file_metadata_overrides_config_toml_metadata() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
Reference in New Issue
Block a user