fix(agent): rendre explicite le routage structuré vs PTY dans LaunchAgent (#33)

Un profil structuré rencontrant une factory de session non câblée retombait
silencieusement sur pty.spawn (fallthrough du `if let`), masquant une erreur de
configuration au lieu de la signaler.

Remplace ce fallthrough par une intention explicite :
- nouvel enum StructuredRoutingMode { HumanPtyFallback, RequireStructured } sur
  LaunchAgent, posé via le builder with_structured_routing_mode ;
- le `if let` devient un `match` explicite (agent/lifecycle.rs) ; en mode
  RequireStructured, un profil structuré sans factory câblée retourne
  AppError::Process("structured profile requires structured session factory")
  au lieu de tomber sur pty.spawn.

Composition root (app-tauri/src/state.rs) : launcher humain = HumanPtyFallback,
launcher orchestrateur = RequireStructured, wake background rebranché sur
orchestrator_launch_agent.

Tests : agent_lifecycle.rs (4 branches de routage) + non-régression
agent_wake/structured_launch_d3. Crate application verte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 19:08:23 +02:00
parent e24bb5f1f4
commit 67101ba607
5 changed files with 268 additions and 56 deletions

View File

@ -36,10 +36,10 @@ use application::{
ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask, RotateConversationLog,
SaveEmbedderProfile, SaveModelServer, SaveProfile, SessionLimitService, SetActiveLayout,
SnapshotOpenWindows, SnapshotRunningAgents, SpawnBackgroundCommand, StopLiveAgent,
StructuredSessions, SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions,
UnassignSkillFromAgent, UnassignTicketFromSprint, UnlinkIssues, UpdateAgentContext,
UpdateAgentPermissions, UpdateIssue, UpdateIssueCarnet, UpdateLiveState, UpdateMemory,
UpdateProjectContext, UpdateProjectPermissions, UpdateSkill, UpdateTemplate,
StructuredRoutingMode, StructuredSessions, SuggestedThisSession, SyncAgentWithTemplate,
TerminalSessions, UnassignSkillFromAgent, UnassignTicketFromSprint, UnlinkIssues,
UpdateAgentContext, UpdateAgentPermissions, UpdateIssue, UpdateIssueCarnet, UpdateLiveState,
UpdateMemory, UpdateProjectContext, UpdateProjectPermissions, UpdateSkill, UpdateTemplate,
WakeSessionProvider, WriteMemory, WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
};
use async_trait::async_trait;
@ -1568,6 +1568,7 @@ impl AppState {
Arc::clone(&memory_recall_port),
Some(Arc::clone(&check_embedder_suggestion)),
)
.with_structured_routing_mode(StructuredRoutingMode::HumanPtyFallback)
.with_permission_store(Arc::clone(&permission_store_port))
// Reprise conversationnelle (lot P7) : à chaque (re)lancement, si la cellule
// porte une conversation et qu'un handoff existe (`<root>/.ideai/conversations/`),
@ -1613,6 +1614,7 @@ impl AppState {
Arc::clone(&memory_recall_port),
Some(Arc::clone(&check_embedder_suggestion)),
)
.with_structured_routing_mode(StructuredRoutingMode::RequireStructured)
.with_permission_store(Arc::clone(&permission_store_port))
.with_handoff_provider(
Arc::new(AppHandoffProvider) as Arc<dyn application::HandoffProvider>
@ -1910,7 +1912,7 @@ impl AppState {
Arc::clone(&mailbox),
Arc::clone(&background_tasks_port),
Arc::new(AppWakeSessionProvider {
launch_agent: Arc::clone(&launch_agent),
launch_agent: Arc::clone(&orchestrator_launch_agent),
structured_sessions: Arc::clone(&structured_sessions),
}),
Some(Arc::clone(&events_port)),