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:
@ -57,7 +57,7 @@ fn final_chunk(s: &str) -> ReplyChunk {
|
||||
fn chunk_from_event_maps_text_delta() {
|
||||
assert_eq!(
|
||||
chunk_from_event(ReplyEvent::TextDelta { text: "hi".into() }),
|
||||
ReplyChunk::TextDelta { text: "hi".into() }
|
||||
Some(ReplyChunk::TextDelta { text: "hi".into() })
|
||||
);
|
||||
}
|
||||
|
||||
@ -67,9 +67,9 @@ fn chunk_from_event_maps_tool_activity() {
|
||||
chunk_from_event(ReplyEvent::ToolActivity {
|
||||
label: "reads file".into()
|
||||
}),
|
||||
ReplyChunk::ToolActivity {
|
||||
Some(ReplyChunk::ToolActivity {
|
||||
label: "reads file".into()
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -79,12 +79,19 @@ fn chunk_from_event_maps_final() {
|
||||
chunk_from_event(ReplyEvent::Final {
|
||||
content: "done".into()
|
||||
}),
|
||||
ReplyChunk::Final {
|
||||
Some(ReplyChunk::Final {
|
||||
content: "done".into()
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunk_from_event_drops_heartbeat() {
|
||||
// A heartbeat is a non-terminal liveness proof with no chat content (lot 1) ⇒ no
|
||||
// wire chunk; the pump skips it while still draining to the Final.
|
||||
assert_eq!(chunk_from_event(ReplyEvent::Heartbeat), None);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// agent_send pump behaviour: deltas* then exactly one Final (zone 2)
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -94,7 +101,10 @@ fn chunk_from_event_maps_final() {
|
||||
/// end. This is the body of the spawned pump thread, run inline.
|
||||
fn pump_turn(bridge: &ChatBridge, session: &SessionId, gen: u64, events: Vec<ReplyEvent>) {
|
||||
for event in events {
|
||||
let _ = bridge.send_output(session, chunk_from_event(event));
|
||||
// Mirror the real pump: heartbeats map to no chunk and are skipped.
|
||||
if let Some(chunk) = chunk_from_event(event) {
|
||||
let _ = bridge.send_output(session, chunk);
|
||||
}
|
||||
}
|
||||
bridge.detach_if(session, gen);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user