feat(agent): robustesse routage ask — fix registre session + concurrence (R0+A0) — §14.3/§15

Stabilise le routage de `ask` avant le bind transport MCP (v5). Invariant
« 1 agent = 1 employé » durci ; un agent traite un tour à la fois.

- R0a garde LaunchAgent : lève AgentAlreadyRunning pour un lancement neuf
  ciblant un agent déjà vivant sur un autre node (PTY + structuré) ; rebind
  seulement même-node ou réattache explicite (conversation_id). Idem spawn_agent.
- R0b list_live_agents agrège PTY + structuré (LiveSessions) + dédup.
- R0c réconciliation des layouts.json à doublons à l'ouverture (host déterministe,
  idempotent) — corrige « une cellule reset au retour d'onglet ».
- R0d UI : option agent désactivée si vivant ailleurs + « aller à la cellule »,
  mapping AGENT_ALREADY_RUNNING.
- A0 sérialisation FIFO des tours par agent_id dans ask_agent (verrou tokio par
  agent ; agents différents en parallèle ; timeout tour 300s, cap attente 600s).

Cadrage : .ideai/briefs/orchestration-v5-transport-bind-cadrage.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 17:34:57 +02:00
parent 37e72747d3
commit 6ca519b815
21 changed files with 2402 additions and 85 deletions

View File

@ -22,7 +22,8 @@ use application::{
ListTemplates,
LiveAgentRegistry, LoadLayout, MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject,
OpenTerminal, OrchestratorService, ReadAgentContext, ReadMemoryIndex, ReadProjectContext,
RecallMemory, ReferenceProfiles, RenameLayout, ResizeTerminal, ResolveMemoryLinks,
RecallMemory, ReconcileLayouts, ReferenceProfiles, RenameLayout, ResizeTerminal,
ResolveMemoryLinks,
SaveEmbedderProfile, SaveProfile, SetActiveLayout, SnapshotRunningAgents, StructuredSessions,
SuggestedThisSession,
SyncAgentWithTemplate, TerminalSessions, UnassignSkillFromAgent, UpdateAgentContext,
@ -98,6 +99,9 @@ pub struct AppState {
pub set_active_layout: Arc<SetActiveLayout>,
/// Freeze `agent_was_running` on every agent leaf before a PTY kill (T5).
pub snapshot_running_agents: Arc<SnapshotRunningAgents>,
/// Dé-doublonne, à l'ouverture, les feuilles d'agent en double d'un même
/// agent dans `layouts.json` (R0c). Idempotent : no-op sans doublon.
pub reconcile_layouts: Arc<ReconcileLayouts>,
/// Detect which candidate profiles' CLIs are installed (first-run).
pub detect_profiles: Arc<DetectProfiles>,
/// List configured profiles.
@ -380,6 +384,14 @@ impl AppState {
Arc::clone(&terminal_sessions) as Arc<dyn LiveAgentRegistry>,
));
// Twin of the snapshot above, but at *open* time: dé-doublonne les
// `layouts.json` portant plusieurs feuilles sur le même agent (R0c, §3.4
// « Trou C »). Idempotent : aucun doublon ⇒ aucune écriture.
let reconcile_layouts = Arc::new(ReconcileLayouts::new(
Arc::clone(&store_port),
Arc::clone(&fs_port),
));
// --- Profiles & AI runtime (L5) ---
// One generic, profile-driven runtime adapter (Open/Closed): it holds the
// process spawner used for detection. The profile store persists
@ -761,6 +773,7 @@ impl AppState {
delete_layout,
set_active_layout,
snapshot_running_agents,
reconcile_layouts,
detect_profiles,
list_profiles,
save_profile,