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:
@ -191,6 +191,8 @@ fn single_leaf(node_id: NodeId) -> LayoutTree {
|
||||
id: node_id,
|
||||
session: None,
|
||||
agent: None,
|
||||
conversation_id: None,
|
||||
agent_was_running: false,
|
||||
})
|
||||
}
|
||||
|
||||
@ -638,6 +640,77 @@ async fn mutate_set_cell_agent_missing_leaf_is_not_found() {
|
||||
assert_eq!(err.code(), "NOT_FOUND", "got {err:?}");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SetCellConversation (T4b — persist the assigned CLI conversation id)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[tokio::test]
|
||||
async fn mutate_set_cell_conversation_persists_id_on_leaf() {
|
||||
let env = mut_env(pid(52)).await;
|
||||
// Record a conversation id on the single root leaf (nid(1)).
|
||||
env.mutate
|
||||
.execute(MutateLayoutInput {
|
||||
project_id: env.project_id,
|
||||
layout_id: None,
|
||||
operation: LayoutOperation::SetCellConversation {
|
||||
target: nid(1),
|
||||
conversation_id: Some("conv-42".to_owned()),
|
||||
},
|
||||
})
|
||||
.await
|
||||
.expect("set_cell_conversation records the id");
|
||||
|
||||
// The id must survive in the persisted JSON, so the next open resumes.
|
||||
let tree_json = active_tree_json(&env.fs);
|
||||
assert_eq!(
|
||||
tree_json["root"]["node"]["conversationId"], "conv-42",
|
||||
"conversation id must be persisted on the leaf"
|
||||
);
|
||||
|
||||
// Now clear it.
|
||||
let out = env
|
||||
.mutate
|
||||
.execute(MutateLayoutInput {
|
||||
project_id: env.project_id,
|
||||
layout_id: None,
|
||||
operation: LayoutOperation::SetCellConversation {
|
||||
target: nid(1),
|
||||
conversation_id: None,
|
||||
},
|
||||
})
|
||||
.await
|
||||
.expect("set_cell_conversation clears the id");
|
||||
|
||||
match &out.layout.root {
|
||||
LayoutNode::Leaf(l) => assert_eq!(l.conversation_id, None, "id must be cleared"),
|
||||
_ => panic!("expected leaf root"),
|
||||
}
|
||||
assert!(
|
||||
active_tree_json(&env.fs)["root"]["node"]
|
||||
.get("conversationId")
|
||||
.is_none(),
|
||||
"cleared id must not be serialised"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mutate_set_cell_conversation_missing_leaf_is_not_found() {
|
||||
let env = mut_env(pid(53)).await;
|
||||
let err = env
|
||||
.mutate
|
||||
.execute(MutateLayoutInput {
|
||||
project_id: env.project_id,
|
||||
layout_id: None,
|
||||
operation: LayoutOperation::SetCellConversation {
|
||||
target: nid(404),
|
||||
conversation_id: Some("x".to_owned()),
|
||||
},
|
||||
})
|
||||
.await
|
||||
.expect_err("unknown node rejected");
|
||||
assert_eq!(err.code(), "NOT_FOUND", "got {err:?}");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Named-layout management (#4)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user