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:
@ -14,10 +14,10 @@ use application::{
|
||||
CreateAgentFromTemplate, CreateLayout, CreateProject, CreateTemplate, DeleteAgent,
|
||||
DeleteLayout, DeleteProfile, DeleteTemplate, DetectAgentDrift, DetectProfiles, FirstRunState,
|
||||
GitBranches, GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage, GitStatus, GitUnstage,
|
||||
HealthUseCase, LaunchAgent, ListAgents, ListLayouts, ListProfiles, ListProjects, ListSkills,
|
||||
ListTemplates, LoadLayout, MoveTabToNewWindow, MutateLayout, OpenProject, OpenTerminal,
|
||||
OrchestratorService, ReadAgentContext, ReferenceProfiles, RenameLayout, ResizeTerminal,
|
||||
SaveProfile, SetActiveLayout,
|
||||
HealthUseCase, InspectConversation, LaunchAgent, ListAgents, ListLayouts, ListProfiles, ListProjects, ListSkills,
|
||||
ListTemplates, LiveAgentRegistry, LoadLayout, MoveTabToNewWindow, MutateLayout, OpenProject,
|
||||
OpenTerminal, OrchestratorService, ReadAgentContext, ReferenceProfiles, RenameLayout,
|
||||
ResizeTerminal, SaveProfile, SetActiveLayout, SnapshotRunningAgents,
|
||||
AssignSkillToAgent, CreateSkill, DeleteSkill, UnassignSkillFromAgent, UpdateSkill,
|
||||
SyncAgentWithTemplate, TerminalSessions, UpdateAgentContext, UpdateTemplate, WriteToTerminal,
|
||||
};
|
||||
@ -28,9 +28,10 @@ use domain::ports::{
|
||||
use domain::{DomainEvent, Project, ProjectId};
|
||||
|
||||
use infrastructure::{
|
||||
CliAgentRuntime, FsOrchestratorWatcher, FsProfileStore, FsProjectStore, FsSkillStore,
|
||||
FsTemplateStore, Git2Repository, IdeaiContextStore, LocalFileSystem, LocalProcessSpawner,
|
||||
OrchestratorWatchHandle, PortablePtyAdapter, SystemClock, TokioBroadcastEventBus, UuidGenerator,
|
||||
ClaudeTranscriptInspector, CliAgentRuntime, FsOrchestratorWatcher, FsProfileStore,
|
||||
FsProjectStore, FsSkillStore, FsTemplateStore, Git2Repository, IdeaiContextStore,
|
||||
LocalFileSystem, LocalProcessSpawner, OrchestratorWatchHandle, PortablePtyAdapter, SystemClock,
|
||||
TokioBroadcastEventBus, UuidGenerator,
|
||||
};
|
||||
|
||||
use crate::pty::PtyBridge;
|
||||
@ -76,6 +77,8 @@ pub struct AppState {
|
||||
pub delete_layout: Arc<DeleteLayout>,
|
||||
/// Set the active named layout (#4).
|
||||
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>,
|
||||
/// Detect which candidate profiles' CLIs are installed (first-run).
|
||||
pub detect_profiles: Arc<DetectProfiles>,
|
||||
/// List configured profiles.
|
||||
@ -112,6 +115,11 @@ pub struct AppState {
|
||||
pub delete_agent: Arc<DeleteAgent>,
|
||||
/// Launch an agent (spawn PTY, apply injection strategy).
|
||||
pub launch_agent: Arc<LaunchAgent>,
|
||||
/// Best-effort inspection of a conversation (last topic + token indicator)
|
||||
/// for the resume popup (T7). Optional/extensible: backed by a `Vec` of
|
||||
/// [`domain::ports::SessionInspector`]s; an empty/missing match yields empty
|
||||
/// details, never an error.
|
||||
pub inspect_conversation: Arc<InspectConversation>,
|
||||
/// Project registry — used by agent commands to resolve a `Project` from an id.
|
||||
pub project_store: Arc<dyn ProjectStore>,
|
||||
// --- Windows (L10) ---
|
||||
@ -282,6 +290,14 @@ impl AppState {
|
||||
Arc::clone(&fs_port),
|
||||
Arc::clone(&events_port),
|
||||
));
|
||||
// Close-time snapshot of running agents (T5). Shares the SAME live-session
|
||||
// registry as the terminal/agent use cases, so its liveness check reflects
|
||||
// the very PTYs the shutdown hook is about to kill — it must run *before*.
|
||||
let snapshot_running_agents = Arc::new(SnapshotRunningAgents::new(
|
||||
Arc::clone(&store_port),
|
||||
Arc::clone(&fs_port),
|
||||
Arc::clone(&terminal_sessions) as Arc<dyn LiveAgentRegistry>,
|
||||
));
|
||||
|
||||
// --- Profiles & AI runtime (L5) ---
|
||||
// One generic, profile-driven runtime adapter (Open/Closed): it holds the
|
||||
@ -347,6 +363,27 @@ impl AppState {
|
||||
Arc::clone(&skill_store_port),
|
||||
Arc::clone(&terminal_sessions),
|
||||
Arc::clone(&events_port),
|
||||
Arc::clone(&ids) as Arc<dyn IdGenerator>,
|
||||
));
|
||||
|
||||
// --- Conversation inspection (T7) ---
|
||||
// Best-effort, optional, extensible: a `Vec` of SessionInspectors routed
|
||||
// by profile. Adding an inspectable CLI = pushing one more adapter here.
|
||||
// The Claude inspector reads `<home>/.claude/projects/<cwd>/<id>.jsonl`;
|
||||
// `$HOME` is resolved from the environment (empty string if unset — the
|
||||
// inspector then simply finds nothing and yields empty details).
|
||||
let home_dir = std::env::var("HOME")
|
||||
.or_else(|_| std::env::var("USERPROFILE"))
|
||||
.unwrap_or_default();
|
||||
let inspectors: Vec<Arc<dyn domain::ports::SessionInspector>> =
|
||||
vec![Arc::new(ClaudeTranscriptInspector::new(
|
||||
Arc::clone(&fs_port),
|
||||
home_dir,
|
||||
))];
|
||||
let inspect_conversation = Arc::new(InspectConversation::new(
|
||||
Arc::clone(&contexts_port),
|
||||
Arc::clone(&profile_store_port),
|
||||
inspectors,
|
||||
));
|
||||
|
||||
let project_store = Arc::clone(&store_port);
|
||||
@ -461,6 +498,7 @@ impl AppState {
|
||||
rename_layout,
|
||||
delete_layout,
|
||||
set_active_layout,
|
||||
snapshot_running_agents,
|
||||
detect_profiles,
|
||||
list_profiles,
|
||||
save_profile,
|
||||
@ -478,6 +516,7 @@ impl AppState {
|
||||
update_agent_context,
|
||||
delete_agent,
|
||||
launch_agent,
|
||||
inspect_conversation,
|
||||
project_store,
|
||||
create_template,
|
||||
update_template,
|
||||
@ -535,6 +574,20 @@ impl AppState {
|
||||
watchers.insert(project.id, handle);
|
||||
}
|
||||
|
||||
/// Returns the ids of every currently-open project.
|
||||
///
|
||||
/// Derived from the orchestrator watcher registry, which holds exactly one
|
||||
/// entry per open project (started on open/create, dropped on close). Used by
|
||||
/// the shutdown hook to snapshot running agents across all open projects
|
||||
/// before the global PTY kill.
|
||||
#[must_use]
|
||||
pub fn open_project_ids(&self) -> Vec<ProjectId> {
|
||||
self.orchestrator_watchers
|
||||
.lock()
|
||||
.map(|w| w.keys().copied().collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Stops and removes the orchestrator watcher for `project_id`, if any.
|
||||
/// Called from `close_project` so a closed project stops consuming requests.
|
||||
pub fn stop_orchestrator_watch(&self, project_id: &ProjectId) {
|
||||
|
||||
Reference in New Issue
Block a user