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

@ -851,7 +851,7 @@ pub struct ProjectionContext<'a> {
/// Absolute isolated run dir of the agent (`.ideai/run/<agent-id>/`).
pub run_dir: &'a str,
/// Optional model selected by the agent profile. Orthogonal to permissions:
/// projectors may still materialise it even when `eff == None`.
/// projectors may still materialise or forward it when appropriate.
pub model: Option<&'a str>,
}

View File

@ -910,6 +910,10 @@ pub struct AgentProfile {
/// CLI. OpenCode garde ses champs dédiés.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
/// Effort de raisonnement Codex explicitement configuré par le profil. `None`
/// conserve le défaut natif de la CLI ; seules les sessions Codex le consomment.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model_reasoning_effort: Option<String>,
/// Capacité **MCP** (ARCHITECTURE §14.3, orchestration v3, Décision 1).
/// `None` ⇒ repli fichier `.ideai/requests` + prose (comportement actuel).
/// `Some(_)` ⇒ IdeA matérialise la config MCP de cette CLI au lancement et
@ -1117,6 +1121,7 @@ impl AgentProfile {
opencode: None,
opencode_provider: None,
model: None,
model_reasoning_effort: None,
mcp: None,
liveness: None,
rate_limit_pattern: None,
@ -1174,6 +1179,13 @@ impl AgentProfile {
self
}
/// Builder : fixe l'effort de raisonnement Codex direct (ticket #99 follow-up).
#[must_use]
pub fn with_model_reasoning_effort(mut self, effort: impl Into<String>) -> Self {
self.model_reasoning_effort = Some(effort.into());
self
}
/// Builder : fixe la [`McpCapability`] (§14.3, orchestration v3) et renvoie le
/// profil. Laisse [`AgentProfile::new`] stable (zéro régression d'appel) : les
/// profils sans MCP ne l'appellent simplement pas.
@ -1488,10 +1500,15 @@ mod mcp_tests {
fn profile_model_round_trips_without_codex_or_claude_provider_config() {
let profile = profile_without_mcp()
.with_structured_adapter(StructuredAdapter::Codex)
.with_model("gpt-5-codex");
.with_model("gpt-5-codex")
.with_model_reasoning_effort("medium");
let json = serde_json::to_string(&profile).expect("serialise");
assert!(json.contains("\"model\":\"gpt-5-codex\""), "got: {json}");
assert!(
json.contains("\"modelReasoningEffort\":\"medium\""),
"got: {json}"
);
assert!(!json.contains("codexProvider"), "got: {json}");
assert!(!json.contains("claudeProvider"), "got: {json}");
assert!(!json.contains("apiKeyRef"), "got: {json}");
@ -1499,6 +1516,7 @@ mod mcp_tests {
let back: AgentProfile = serde_json::from_str(&json).expect("deserialise");
assert_eq!(back.model.as_deref(), Some("gpt-5-codex"));
assert_eq!(back.model_reasoning_effort.as_deref(), Some("medium"));
}
#[test]