feat(backend,application,frontend,domain,web-server,app-tauri): bouton Redémarrer l'agent dans la barre supérieure des cellules — #317 (QA verte)

La cellule expose un bouton Redémarrer l'agent valable pour les trois
modes de présentation (headless, TUI native, CLI custom), sans jamais
prendre la présentation pour vérité runtime.

- Application: use case RestartCellAgentRuntime validant le binding
  exact cellule/runtime via les snapshots live (RestartRuntimeNotFound,
  ConflictAgentVisibleElsewhere, AgentNotBoundToCell) puis déléguant au
  scheduler self-restart existant (schedule_cell); CellAgentPresentation
  purement informationnelle.
- Backend/app-tauri/web-server: commande restart_cell_agent sur contrat
  DTO identique Tauri/HTTP, stop borné par timeout typé
  (RestartStopTimeout), relance préservant routage TUI natif, géométrie
  de cellule (node/rows/cols) et conversation; évenements domaine
  enrichis.
- Frontend: bouton dans la barre de cellule avec confirmation,
  overlay d'état Redémarrage…/échec (libellés par code d'erreur, Fermer/
  Réessayer), gardes d'identité de cellule et de restarts externes,
  gateway/mock/http adapters et ports alignés.
- Tests: dto_agents, workstate_actions, contrats web-server,
  LayoutGrid.cellControlsLayering, adapters agent/mock/http.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-11 15:00:43 +02:00
parent 46449bdf46
commit 7fb9d8bb86
26 changed files with 1404 additions and 58 deletions

View File

@ -12,13 +12,14 @@ use serde_json::Value;
use application::{
AgentBackgroundTaskState, AgentTicketState, AppError, AppExitWorkGuardDetail,
AppExitWorkGuardState, AttachLiveAgentOutput, BackgroundTaskKindLabel,
AppExitWorkGuardState, AttachLiveAgentOutput, BackgroundTaskKindLabel, CellAgentPresentation,
ConversationPreviewStatus, ConversationTurnWorkPreview, ConversationWorkSummary,
CreateProjectInput, CreateProjectOutput, ExecuteSlashCommandOutput, GitGraphOutput,
HealthInput, HealthReport, LayoutKind, ListProjectsOutput, ListSlashCommandsOutput,
LiveRuntimeState, LiveSessionKind, LiveSessionSnapshot, OpenProjectOutput, ProjectWorkState,
RestoreOpenProjectFailure, RestoreOpenProjectsOutput, SlashCommandEffect, StopLiveAgentOutput,
TicketWorkSource, TicketWorkStatus, TurnPage, TurnSource, TurnView,
RestartCellAgentRuntimeOutput, RestoreOpenProjectFailure, RestoreOpenProjectsOutput,
SlashCommandEffect, StopLiveAgentOutput, TicketWorkSource, TicketWorkStatus, TurnPage,
TurnSource, TurnView,
};
use domain::ports::{ReplyProgress, ReplyProgressKind, ReplyProgressSource, ReplyProgressStage};
use domain::{
@ -4611,6 +4612,98 @@ pub struct StopLiveAgentRequestDto {
pub reason: Option<String>,
}
/// Informational presentation supplied by the cell that requests a restart.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum CellAgentPresentationDto {
/// Background/headless orchestration surface.
Headless,
/// Native interactive provider terminal.
Tui,
/// IdeA structured/custom CLI chat.
CustomCli,
/// Presentation cannot be determined by the caller.
Unknown,
}
impl From<CellAgentPresentationDto> for CellAgentPresentation {
fn from(value: CellAgentPresentationDto) -> Self {
match value {
CellAgentPresentationDto::Headless => Self::Headless,
CellAgentPresentationDto::Tui => Self::Tui,
CellAgentPresentationDto::CustomCli => Self::CustomCli,
CellAgentPresentationDto::Unknown => Self::Unknown,
}
}
}
impl From<CellAgentPresentation> for CellAgentPresentationDto {
fn from(value: CellAgentPresentation) -> Self {
match value {
CellAgentPresentation::Headless => Self::Headless,
CellAgentPresentation::Tui => Self::Tui,
CellAgentPresentation::CustomCli => Self::CustomCli,
CellAgentPresentation::Unknown => Self::Unknown,
}
}
}
/// Request for an atomic restart of the runtime bound to one agent cell.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RestartCellAgentRuntimeRequestDto {
/// Owning project registry id.
pub project_id: String,
/// Exact hosting layout leaf.
pub node_id: String,
/// Agent expected to be bound to the leaf.
pub agent_id: String,
/// Informational presentation; the backend resolves the real runtime.
#[serde(default = "unknown_cell_agent_presentation")]
pub presentation: CellAgentPresentationDto,
/// Optional named layout id when already known by the caller.
#[serde(default)]
pub layout_id: Option<String>,
}
fn unknown_cell_agent_presentation() -> CellAgentPresentationDto {
CellAgentPresentationDto::Unknown
}
/// Immediate acknowledgement for an accepted cell restart.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RestartCellAgentRuntimeResponseDto {
/// Correlation id used by `agentRestart*` events.
pub restart_id: String,
/// Stable acknowledgement state.
pub status: &'static str,
/// Validated agent id.
pub agent_id: String,
/// Validated node id.
pub node_id: String,
/// Actual live runtime family (`pty` or `structured`).
pub kind: LiveWorkSessionKindDto,
/// Informational presentation echoed to the caller.
pub presentation: CellAgentPresentationDto,
/// Optional layout id echoed to the caller.
pub layout_id: Option<String>,
}
impl From<RestartCellAgentRuntimeOutput> for RestartCellAgentRuntimeResponseDto {
fn from(out: RestartCellAgentRuntimeOutput) -> Self {
Self {
restart_id: out.restart_id,
status: "scheduled",
agent_id: out.agent_id.to_string(),
node_id: out.node_id.to_string(),
kind: out.kind.into(),
presentation: out.presentation.into(),
layout_id: out.layout_id.map(|id| id.to_string()),
}
}
}
/// Response DTO for `stop_live_agent`: the torn-down session's coordinates.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]