feat(agents): pont Codex inter-agents + readiness/heartbeat lot 1
Deux chantiers livrés au vert (workspace entier : domain+application+
infrastructure 42 + app-tauri --lib 128, 0 échec).
## Codex inter-agents
- domaine: McpConfigStrategy::TomlConfigHome { target, home_env } +
toml_config_home(...); AgentProfile::materializes_idea_bridge()
(whitelist Claude/ConfigFile + Codex/TomlConfigHome); McpServerWiring
+ encodeur TOML.
- application: lifecycle apply_mcp_config bras TomlConfigHome (écrit
{runDir}/<target>, pousse (home_env, parent) dans spec.env);
guard_mcp_bridge_supported ré-exprimée via materializes_idea_bridge();
catalogue Codex porte toml_config_home(".codex/config.toml","CODEX_HOME").
- app-tauri: is_codex_mcp_profile, migrate_codex_run_dir,
mcp_server_entry_toml.
- tests: matrice domaine TomlConfigHome + round-trip dual Claude/Codex
sur loopback réel (fakes, zéro token).
## Readiness/heartbeat lot 1
- domaine: readiness.rs — ReadinessPolicy::classify (Final => TurnEnded),
variantes ReplyEvent::Heartbeat / ToolActivity.
- application: drain_with_readiness consulte la policy et appelle
mark_idle sur le signal déterministe; branché dans ask_agent.
Corrige la cause racine: une cible qui ne renvoie qu'un Final (sans
idea_reply) débloque désormais sa file Busy.
- infrastructure: adapters de session émettent Heartbeat/ToolActivity.
- tests: drain_with_readiness_lot1 (points QA 5 & 6) verts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -80,8 +80,11 @@ pub fn reference_profiles() -> Vec<AgentProfile> {
|
||||
.expect("codex reference profile is valid")
|
||||
.with_structured_adapter(StructuredAdapter::Codex)
|
||||
.with_mcp(McpCapability::new(
|
||||
McpConfigStrategy::config_file(".mcp.json")
|
||||
.expect(".mcp.json is a valid relative MCP config target"),
|
||||
// Codex lit ses serveurs MCP dans `$CODEX_HOME/config.toml`, pas `.mcp.json` :
|
||||
// IdeA écrit ce TOML DANS le run dir et pointe `CODEX_HOME` dessus pour
|
||||
// isoler l'agent du `~/.codex` global (miroir du `.mcp.json` de Claude).
|
||||
McpConfigStrategy::toml_config_home(".codex/config.toml", "CODEX_HOME")
|
||||
.expect(".codex/config.toml + CODEX_HOME is a valid MCP config target"),
|
||||
McpTransport::Stdio,
|
||||
)),
|
||||
AgentProfile::new(
|
||||
@ -157,18 +160,36 @@ mod mcp_tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claude_and_codex_mcp_use_config_file_mcp_json() {
|
||||
for slug in ["claude", "codex"] {
|
||||
let mcp = profile(slug).mcp.expect("mcp present");
|
||||
assert_eq!(
|
||||
mcp.config,
|
||||
McpConfigStrategy::ConfigFile {
|
||||
target: ".mcp.json".to_owned()
|
||||
},
|
||||
"profile `{slug}` should declare `.mcp.json`"
|
||||
);
|
||||
assert_eq!(mcp.transport, McpTransport::Stdio);
|
||||
}
|
||||
fn claude_mcp_uses_config_file_mcp_json() {
|
||||
let mcp = profile("claude").mcp.expect("mcp present");
|
||||
assert_eq!(
|
||||
mcp.config,
|
||||
McpConfigStrategy::ConfigFile {
|
||||
target: ".mcp.json".to_owned()
|
||||
},
|
||||
"Claude should declare `.mcp.json`"
|
||||
);
|
||||
assert_eq!(mcp.transport, McpTransport::Stdio);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_mcp_uses_toml_config_home_codex() {
|
||||
// Codex lit `$CODEX_HOME/config.toml`, pas `.mcp.json` : le seed doit déclarer
|
||||
// la stratégie TOML isolée par `CODEX_HOME` (pendant Codex de Claude).
|
||||
let mcp = profile("codex").mcp.expect("mcp present");
|
||||
assert_eq!(
|
||||
mcp.config,
|
||||
McpConfigStrategy::TomlConfigHome {
|
||||
target: ".codex/config.toml".to_owned(),
|
||||
home_env: "CODEX_HOME".to_owned(),
|
||||
},
|
||||
"Codex should declare `.codex/config.toml` + CODEX_HOME"
|
||||
);
|
||||
assert_eq!(mcp.transport, McpTransport::Stdio);
|
||||
assert!(
|
||||
profile("codex").materializes_idea_bridge(),
|
||||
"the Codex seed must materialise the idea bridge"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user