feat(agent): reprise des sessions au redémarrage (B2) — commande + ResumeProjectPanel
- Tauri : commande list_resumable_agents (projectId) + ResumableAgentListDto
{ resumable } / ResumableAgentDto (camelCase, conversationId omis si None),
câblage state.rs (réutilise stores + ProfileStore, aucun nouveau port).
- Front : gateway listResumableAgents, ResumeProjectPanel monté à l'ouverture
de projet (opt-in, FR) : Reprendre (launch_agent nodeId+conversationId),
Nouvelle conversation (setCellConversation(null) puis launch), Ignorer,
Tout reprendre/ignorer. resumeSupported=false ⇒ « relance à neuf ».
- doc : §15.2 coquille agents→resumable (alignée use case/back/front).
Tests : app-tauri dto 9/9 ; vitest 324 (+10) ; workspace Rust 0 échec. 0 régression.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1305,6 +1305,72 @@ pub fn parse_agent_id(raw: &str) -> Result<AgentId, ErrorDto> {
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Resumable agents (§15.2 — Chantier B2)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
use application::{ListResumableAgentsOutput, ResumableAgent};
|
||||
|
||||
/// One resumable agent cell, as seen by the frontend (§15.2).
|
||||
///
|
||||
/// Mirrors [`ResumableAgent`]: the agent's identity + its host cell, the CLI
|
||||
/// conversation id to resume (absent ⇒ fresh relaunch), the `was_running` flag
|
||||
/// frozen at close, and whether the agent's profile can resume a CLI
|
||||
/// conversation. `conversationId` is **omitted from the wire when `None`**
|
||||
/// (`skip_serializing_if`), so the TypeScript side sees an absent key — not
|
||||
/// `null`.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResumableAgentDto {
|
||||
/// The resumable agent's id (UUID string).
|
||||
pub agent_id: String,
|
||||
/// The agent's display name (resolved from the manifest).
|
||||
pub name: String,
|
||||
/// The host layout leaf where the agent is relaunched/resumed (UUID string).
|
||||
pub node_id: String,
|
||||
/// Persistent CLI conversation id carried by the cell. Absent ⇒ fresh
|
||||
/// relaunch (no history to resume).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub conversation_id: Option<String>,
|
||||
/// The `agent_was_running` flag frozen at the cell's close.
|
||||
pub was_running: bool,
|
||||
/// `true` when the agent's profile carries a usable resume strategy.
|
||||
pub resume_supported: bool,
|
||||
}
|
||||
|
||||
impl From<ResumableAgent> for ResumableAgentDto {
|
||||
fn from(r: ResumableAgent) -> Self {
|
||||
Self {
|
||||
agent_id: r.agent_id.to_string(),
|
||||
name: r.name,
|
||||
node_id: r.node_id.to_string(),
|
||||
conversation_id: r.conversation_id,
|
||||
was_running: r.was_running,
|
||||
resume_supported: r.resume_supported,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Response DTO for `list_resumable_agents` (§15.2).
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResumableAgentListDto {
|
||||
/// The resumable agent cells, in layout-traversal order.
|
||||
pub resumable: Vec<ResumableAgentDto>,
|
||||
}
|
||||
|
||||
impl From<ListResumableAgentsOutput> for ResumableAgentListDto {
|
||||
fn from(out: ListResumableAgentsOutput) -> Self {
|
||||
Self {
|
||||
resumable: out
|
||||
.resumable
|
||||
.into_iter()
|
||||
.map(ResumableAgentDto::from)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Templates & sync (L7)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user