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:
@ -245,3 +245,66 @@ fn set_cell_agent_op_deserialises_with_absent_agent_defaults_to_none() {
|
||||
_ => panic!("expected SetCellAgent with None agent"),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// setCellConversation operation deserialisation (T4b)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn set_cell_conversation_op_deserialises_with_id() {
|
||||
let target = nid(1);
|
||||
let raw = json!({
|
||||
"type": "setCellConversation",
|
||||
"target": target.to_string(),
|
||||
"conversationId": "conv-42"
|
||||
});
|
||||
let dto: LayoutOperationDto = serde_json::from_value(raw).unwrap();
|
||||
let op = dto.into_operation().unwrap();
|
||||
match op {
|
||||
application::LayoutOperation::SetCellConversation {
|
||||
target: t,
|
||||
conversation_id: Some(id),
|
||||
} => {
|
||||
assert_eq!(t, target);
|
||||
assert_eq!(id, "conv-42");
|
||||
}
|
||||
_ => panic!("expected SetCellConversation with id"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_cell_conversation_op_deserialises_with_null_id() {
|
||||
let target = nid(2);
|
||||
let raw = json!({
|
||||
"type": "setCellConversation",
|
||||
"target": target.to_string(),
|
||||
"conversationId": null
|
||||
});
|
||||
let dto: LayoutOperationDto = serde_json::from_value(raw).unwrap();
|
||||
let op = dto.into_operation().unwrap();
|
||||
match op {
|
||||
application::LayoutOperation::SetCellConversation {
|
||||
target: t,
|
||||
conversation_id: None,
|
||||
} => assert_eq!(t, target),
|
||||
_ => panic!("expected SetCellConversation with None id"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_cell_conversation_op_deserialises_with_absent_id_defaults_to_none() {
|
||||
let target = nid(3);
|
||||
let raw = json!({
|
||||
"type": "setCellConversation",
|
||||
"target": target.to_string()
|
||||
});
|
||||
let dto: LayoutOperationDto = serde_json::from_value(raw).unwrap();
|
||||
let op = dto.into_operation().unwrap();
|
||||
match op {
|
||||
application::LayoutOperation::SetCellConversation {
|
||||
conversation_id: None,
|
||||
..
|
||||
} => {}
|
||||
_ => panic!("expected SetCellConversation with None id"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user