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:
@ -76,11 +76,54 @@ impl ContextInjection {
|
||||
}
|
||||
}
|
||||
|
||||
/// Declares how IdeA assigns and resumes a CLI conversation, without parsing
|
||||
/// the model's output (universal, declarative — see CONTEXT.md §9).
|
||||
///
|
||||
/// Optional on a profile: a profile without a `session` block behaves as today
|
||||
/// (no resume).
|
||||
///
|
||||
/// Invariants:
|
||||
/// - `resume_flag` is non-empty,
|
||||
/// - if `assign_flag` is `Some`, it is non-empty.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SessionStrategy {
|
||||
/// Flag passed on the FIRST launch with a UUID generated by IdeA
|
||||
/// (e.g. `"--session-id"`). `None` => degraded mode (resume without id).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub assign_flag: Option<String>,
|
||||
/// Resume flag (e.g. `"--resume"` or `"--continue"`).
|
||||
pub resume_flag: String,
|
||||
}
|
||||
|
||||
impl SessionStrategy {
|
||||
/// Builds a validated session strategy.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns [`DomainError::EmptyField`] if `resume_flag` is empty, or if
|
||||
/// `assign_flag` is `Some` but empty.
|
||||
pub fn new(
|
||||
assign_flag: Option<String>,
|
||||
resume_flag: impl Into<String>,
|
||||
) -> Result<Self, DomainError> {
|
||||
let resume_flag = resume_flag.into();
|
||||
crate::validation::non_empty(&resume_flag, "session.resumeFlag")?;
|
||||
if let Some(flag) = &assign_flag {
|
||||
crate::validation::non_empty(flag, "session.assignFlag")?;
|
||||
}
|
||||
Ok(Self {
|
||||
assign_flag,
|
||||
resume_flag,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Declarative runtime configuration for one AI CLI.
|
||||
///
|
||||
/// Invariants:
|
||||
/// - `name` and `command` non-empty,
|
||||
/// - `context_injection` is itself valid (guaranteed by its constructors).
|
||||
/// - `context_injection` is itself valid (guaranteed by its constructors),
|
||||
/// - `session` is itself valid (guaranteed by its constructor).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AgentProfile {
|
||||
@ -101,6 +144,10 @@ pub struct AgentProfile {
|
||||
/// **never** the project root, so that N agents of the same profile never
|
||||
/// collide on a single conventional file (`CLAUDE.md`, …) at the root.
|
||||
pub cwd_template: String,
|
||||
/// Optional conversation assign/resume strategy. Absent (legacy / no resume)
|
||||
/// keeps today's behaviour.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub session: Option<SessionStrategy>,
|
||||
}
|
||||
|
||||
impl AgentProfile {
|
||||
@ -117,6 +164,7 @@ impl AgentProfile {
|
||||
context_injection: ContextInjection,
|
||||
detect: Option<String>,
|
||||
cwd_template: impl Into<String>,
|
||||
session: Option<SessionStrategy>,
|
||||
) -> Result<Self, DomainError> {
|
||||
let name = name.into();
|
||||
let command = command.into();
|
||||
@ -131,6 +179,7 @@ impl AgentProfile {
|
||||
context_injection,
|
||||
detect,
|
||||
cwd_template,
|
||||
session,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user