fix: project model and reasoning_effort into Codex CLI args, stop rewriting .codex/config.toml

- Add model_reasoning_effort field to AgentProfile (domain layer)
- Remove model parameter from codex_config_toml, stop rewriting model in TOML
- Pass model and model_reasoning_effort via Codex CLI -c overrides on every exec
- Update CodexExecSession with new_with_policy_and_overrides factory
- Cover new conversation and resume flows with tests
This commit is contained in:
2026-08-02 17:19:26 +02:00
parent b730e356aa
commit dcc7a6f216
9 changed files with 363 additions and 91 deletions

View File

@ -160,6 +160,10 @@ pub struct CodexExecSession {
writable_roots: Vec<String>,
/// Structured policy projection of Codex workspace-write sandbox network access.
network_access: Option<bool>,
/// Profile-selected model forwarded as a Codex config override on every exec turn.
model: Option<String>,
/// Profile-selected reasoning effort forwarded as a Codex config override.
model_reasoning_effort: Option<String>,
/// Variables d'environnement préparées au lancement (ex. `CODEX_HOME` isolé).
env: Vec<(String, String)>,
/// Id de conversation **du moteur** Codex, capté au premier tour, `None` avant.
@ -188,7 +192,7 @@ impl CodexExecSession {
sandbox: Option<SandboxPlan>,
sandbox_enforcer: Option<Arc<dyn SandboxEnforcer>>,
) -> Self {
Self::new_with_policy(
Self::new_with_policy_and_overrides(
id,
command,
cwd,
@ -196,6 +200,8 @@ impl CodexExecSession {
"workspace-write",
writable_roots,
None,
None,
None,
env,
sandbox,
sandbox_enforcer,
@ -217,6 +223,39 @@ impl CodexExecSession {
env: Vec<(String, String)>,
sandbox: Option<SandboxPlan>,
sandbox_enforcer: Option<Arc<dyn SandboxEnforcer>>,
) -> Self {
Self::new_with_policy_and_overrides(
id,
command,
cwd,
seed_conversation_id,
sandbox_mode,
writable_roots,
network_access,
None,
None,
env,
sandbox,
sandbox_enforcer,
)
}
/// Construit l'adapter avec politique + overrides de config issus du profil IdeA.
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn new_with_policy_and_overrides(
id: SessionId,
command: impl Into<String>,
cwd: impl Into<String>,
seed_conversation_id: Option<String>,
sandbox_mode: impl Into<String>,
writable_roots: Vec<String>,
network_access: Option<bool>,
model: Option<String>,
model_reasoning_effort: Option<String>,
env: Vec<(String, String)>,
sandbox: Option<SandboxPlan>,
sandbox_enforcer: Option<Arc<dyn SandboxEnforcer>>,
) -> Self {
Self {
id,
@ -225,6 +264,8 @@ impl CodexExecSession {
sandbox_mode: sandbox_mode.into(),
writable_roots,
network_access,
model,
model_reasoning_effort,
env,
conversation_id: Mutex::new(seed_conversation_id),
sandbox,
@ -269,6 +310,25 @@ impl CodexExecSession {
"sandbox_workspace_write.network_access={network_access}"
));
}
if let Some(model) = self
.model
.as_deref()
.filter(|model| !model.trim().is_empty())
{
args.push("-c".to_owned());
args.push(format!("model={}", codex_toml_string(model)));
}
if let Some(effort) = self
.model_reasoning_effort
.as_deref()
.filter(|effort| !effort.trim().is_empty())
{
args.push("-c".to_owned());
args.push(format!(
"model_reasoning_effort={}",
codex_toml_string(effort)
));
}
if let Some(id) = conversation_id {
args.push("resume".to_owned());
args.push(id);
@ -293,6 +353,10 @@ impl CodexExecSession {
}
}
fn codex_toml_string(value: &str) -> String {
serde_json::to_string(value).expect("string serialization cannot fail")
}
fn upsert_env(env: &mut Vec<(String, String)>, key: &str, value: &str) {
if let Some((_, existing)) = env.iter_mut().find(|(k, _)| k == key) {
*existing = value.to_owned();