feat(agent): routage LaunchAgent structuré vs PTY + réconciliation A/B (D3) — §17

- LaunchAgent route sur profile.structured_adapter : Some ⇒ AgentSession via
  factory + enregistrement StructuredSessions (aucun pty.spawn) ; None ⇒ PTY
  inchangé. Dépendances structurées injectées par builders additifs
  with_structured (signatures publiques inchangées ⇒ A/B legacy verts).
- LaunchAgentOutput étendu d'un champ optionnel structured (non cassant).
- A (ChangeAgentProfile) : kill polymorphe — session structurée ⇒ shutdown(),
  PTY ⇒ kill ; détection sur les deux registres.
- B : resume_supported vrai pour profil structuré ; resolve_session_plan ⇒
  Resume{conversation_id} (Claude --resume / Codex exec resume).
- Codex : build_spawn_line porte --sandbox workspace-write --ask-for-approval
  never (autonomie d'écriture ; à terme piloté par les permissions).

Tests : structured_launch 8 + codex flags 2 ; application/infrastructure 0 échec.

Reste D4 : ChatBridge (Channel) + commandes agent_send/reattach + StructuredSessions
dans AppState + DTO cellKind/ReplyChunk.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 18:48:25 +02:00
parent f104682477
commit 56913b9053
6 changed files with 1524 additions and 52 deletions

View File

@ -161,13 +161,21 @@ impl ListResumableAgents {
};
let name = entry.name.clone();
// `resume_supported` : le profil de l'agent porte-t-il une
// SessionStrategy exploitable (resume_flag présent) ?
// `resume_supported` : le profil de l'agent sait-il reprendre une
// conversation ? Vrai si :
// - il porte une `SessionStrategy` (TUI/PTY avec `resume_flag`,
// sémantique §15), **ou**
// - il est **structuré** (`structured_adapter`, §17) : l'adapter
// Claude (`--resume`) / Codex (`exec resume`) passe le flag de
// reprise du moteur via `SessionPlan::Resume`, donc la reprise est
// intrinsèquement supportée.
let resume_supported = entry
.to_agent()
.ok()
.and_then(|agent| profiles.iter().find(|p| p.id == agent.profile_id))
.is_some_and(|profile| profile.session.is_some());
.is_some_and(|profile| {
profile.session.is_some() || profile.structured_adapter.is_some()
});
resumable.push(ResumableAgent {
agent_id,