feat(agent): fondation exécution structurée des agents IA (D0+D1) — §17

Pivot orchestration : agents IA pilotés via leur mode programmatique/JSON
(capture déterministe), au lieu du TUI brut + self-report. §16 (idea/MCP)
marquée remplacée comme voie principale.

- D0 (domaine) : port AgentSession + AgentSessionFactory, types ReplyEvent
  /ReplyStream/AgentSessionError, champ AgentProfile.structured_adapter
  (Option<StructuredAdapter{Claude,Codex}>, skip si None ⇒ zéro régression),
  catalogue Claude/Codex annotés.
- D1 (application) : registre StructuredSessions (jumeau de TerminalSessions),
  agrégateur LiveSessions{pty,structured} derrière LiveAgentRegistry (vivant si
  PTY OU structuré, surface du trait inchangée), helper send_blocking (draine le
  ReplyStream jusqu'au Final, Timeout sans tuer la session).

Tests : domaine 16+2 ; application registre 11 + send_blocking 9 ; workspace 0 échec.
A/B intacts. Aucun adapter concret (D2), pas de Tauri/front.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 17:54:48 +02:00
parent 7375f706da
commit 5e10b5eb42
17 changed files with 2084 additions and 11 deletions

View File

@ -324,3 +324,39 @@ fn catalogue_ids_are_stable_across_calls() {
// And match the slug-derived id helper.
assert_eq!(first[0].id, reference_profile_id("claude"));
}
// ---------------------------------------------------------------------------
// LOT D0 (§17.3) — structured_adapter sur les profils de référence
// ---------------------------------------------------------------------------
#[test]
fn catalogue_claude_and_codex_carry_their_structured_adapter() {
use domain::profile::StructuredAdapter;
let profiles = reference_profiles();
let by_command: HashMap<&str, &AgentProfile> =
profiles.iter().map(|p| (p.command.as_str(), p)).collect();
// Claude / Codex sont pilotés en mode structuré (cellule chat + AgentSession).
assert_eq!(
by_command["claude"].structured_adapter,
Some(StructuredAdapter::Claude),
"Claude reference profile must declare the Claude structured adapter"
);
assert_eq!(
by_command["codex"].structured_adapter,
Some(StructuredAdapter::Codex),
"Codex reference profile must declare the Codex structured adapter"
);
}
#[test]
fn catalogue_gemini_and_aider_stay_pty_without_adapter() {
// §17.3 : les profils non encore couverts restent TUI/PTY (pas d'adapter).
let profiles = reference_profiles();
let by_command: HashMap<&str, &AgentProfile> =
profiles.iter().map(|p| (p.command.as_str(), p)).collect();
assert_eq!(by_command["gemini"].structured_adapter, None);
assert_eq!(by_command["aider"].structured_adapter, None);
}