feat(terminals): reprise de conversation par cellule + fix ordre d'écriture

Permet de recharger la conversation CLI précédente de chaque cellule à la
réouverture du projet, de façon universelle (indépendant du modèle/CLI).

- profil AgentRuntime: bloc déclaratif optionnel `session { assignFlag, resumeFlag }`
- LeafCell: `conversationId` (persistant, distinct du SessionId PTY) + `agentWasRunning`
- runtime: SessionPlan (None/Assign/Resume) + composition pure des args
- LaunchAgent: décide Assign vs Resume, génère l'UUID, remonte l'id assigné
  (persistance par l'appelant via setCellConversation — découplage SRP)
- close: SnapshotRunningAgents fige `agentWasRunning` avant le kill-all
  (statut clot/en cours universel, sans parsing CLI)
- SessionInspector: port optionnel best-effort + adapter ClaudeTranscriptInspector
- popup de reprise par cellule (statut + sujet/tokens si dispo), intercalée
  avant le Resume auto, jamais sur le chemin reattach

fix(terminals): sérialise les écritures PTY (file FIFO par handle) — corrige
les caractères mélangés/accents dus au réordonnancement des invoke Tauri concurrents

fix(layout): l'opération `move` préservait mal les champs du leaf (perdait `agent`)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 22:27:08 +02:00
parent d11eaaa8c0
commit 3ed0f6b45f
61 changed files with 5098 additions and 98 deletions

View File

@ -28,7 +28,7 @@
mod registry;
mod usecases;
pub use registry::TerminalSessions;
pub use registry::{LiveAgentRegistry, TerminalSessions};
pub use usecases::{
CloseTerminal, CloseTerminalInput, CloseTerminalOutput, OpenTerminal, OpenTerminalInput,
OpenTerminalOutput, ResizeTerminal, ResizeTerminalInput, WriteToTerminal, WriteToTerminalInput,

View File

@ -19,12 +19,30 @@ struct Entry {
session: TerminalSession,
}
/// Read-only liveness query over the agents that currently own a live PTY.
///
/// Abstracted as a trait so use cases that only need to ask "is this agent
/// running right now?" (e.g. the close-time snapshot of running agents) depend
/// on the *capability*, not on the whole [`TerminalSessions`] registry — and can
/// be tested against a trivial fake. [`TerminalSessions`] is the production
/// implementation.
pub trait LiveAgentRegistry: Send + Sync {
/// Whether `agent_id` currently has a live session in the registry.
fn is_agent_live(&self, agent_id: &AgentId) -> bool;
}
/// In-memory registry of active terminal sessions.
#[derive(Default)]
pub struct TerminalSessions {
entries: Mutex<HashMap<SessionId, Entry>>,
}
impl LiveAgentRegistry for TerminalSessions {
fn is_agent_live(&self, agent_id: &AgentId) -> bool {
self.session_for_agent(agent_id).is_some()
}
}
impl TerminalSessions {
/// Creates an empty registry.
#[must_use]