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:
@ -26,11 +26,12 @@ use application::{
|
||||
ReadAgentContextInput, ReadConversationPageInput, ReadMcpToolPermissionsInput,
|
||||
ReadMemoryIndexInput, ReadProjectContextInput, RecallMemoryInput, RenameDeviceInput,
|
||||
RenameLayoutInput, ResolveAgentPermissionsInput, ResolveAgentSystemPermissionsInput,
|
||||
ResolveMemoryLinksInput, RevokeDeviceInput, RotateConversationLogInput, SaveChatDraftInput,
|
||||
SetActiveLayoutInput, SetAgentMcpToolAllowedInput, SlashCommandEffect,
|
||||
SnapshotRunningAgentsInput, StopLiveAgentInput, SyncAgentWithTemplateInput,
|
||||
UnassignSkillFromAgentInput, UpdateAgentContactableInput, UpdateAgentContextInput,
|
||||
UpdateAgentEffortInput, UpdateAgentMcpToolPermissionsInput, UpdateAgentPermissionsInput,
|
||||
ResolveMemoryLinksInput, RestartCellAgentRuntimeInput, RevokeDeviceInput,
|
||||
RotateConversationLogInput, SaveChatDraftInput, SetActiveLayoutInput,
|
||||
SetAgentMcpToolAllowedInput, SlashCommandEffect, SnapshotRunningAgentsInput,
|
||||
StopLiveAgentInput, SyncAgentWithTemplateInput, UnassignSkillFromAgentInput,
|
||||
UpdateAgentContactableInput, UpdateAgentContextInput, UpdateAgentEffortInput,
|
||||
UpdateAgentMcpToolPermissionsInput, UpdateAgentPermissionsInput,
|
||||
UpdateAgentSystemPermissionsInput, UpdateMemoryInput, UpdateProjectContextInput,
|
||||
UpdateProjectMcpToolPermissionsInput, UpdateProjectOrchestratorInput,
|
||||
UpdateProjectPermissionsInput, UpdateProjectSystemPermissionsInput, UpdateSkillInput,
|
||||
@ -73,6 +74,7 @@ use crate::dto::{
|
||||
RemoveProjectResponseDto, RenameLayoutRequestDto, ReplyChunk, ResizeTerminalRequestDto,
|
||||
ResolveAgentPermissionsRequestDto, ResolveAgentPermissionsResponseDto,
|
||||
ResolveAgentSystemPermissionsRequestDto, ResolvedAgentSystemPermissionsDto,
|
||||
RestartCellAgentRuntimeRequestDto, RestartCellAgentRuntimeResponseDto,
|
||||
RestoreOpenProjectsOutputDto, ResumableAgentListDto, RetryOpenProjectRestoreRequestDto,
|
||||
SaveChatDraftRequestDto, SaveEmbedderProfileRequestDto, SaveModelServerRequestDto,
|
||||
SaveOpenCodeProviderProfileRequestDto, SaveOpenProjectRestoreOrderRequestDto,
|
||||
@ -2268,6 +2270,32 @@ pub async fn stop_live_agent(
|
||||
.map_err(ErrorDto::from)
|
||||
}
|
||||
|
||||
/// Atomically restarts the runtime bound to an exact visible agent cell.
|
||||
///
|
||||
/// The presentation hint is retained for correlation only. Runtime selection,
|
||||
/// stop ordering and relaunch are owned by the backend restart coordinator.
|
||||
#[tauri::command]
|
||||
pub async fn restart_cell_agent_runtime(
|
||||
request: RestartCellAgentRuntimeRequestDto,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<RestartCellAgentRuntimeResponseDto, ErrorDto> {
|
||||
let project = resolve_project(&request.project_id, &state).await?;
|
||||
let output = state
|
||||
.restart_cell_agent_runtime
|
||||
.execute(RestartCellAgentRuntimeInput {
|
||||
project,
|
||||
node_id: parse_node_id(&request.node_id)?,
|
||||
agent_id: parse_agent_id(&request.agent_id)?,
|
||||
presentation: request.presentation.into(),
|
||||
layout_id: request
|
||||
.layout_id
|
||||
.as_deref()
|
||||
.map(parse_layout_id)
|
||||
.transpose()?,
|
||||
})?;
|
||||
Ok(output.into())
|
||||
}
|
||||
|
||||
/// `read_agent_context` — read an agent's Markdown context.
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
@ -351,6 +351,7 @@ pub fn run() {
|
||||
commands::list_live_agents,
|
||||
commands::attach_live_agent,
|
||||
commands::stop_live_agent,
|
||||
commands::restart_cell_agent_runtime,
|
||||
commands::read_agent_context,
|
||||
commands::update_agent_context,
|
||||
commands::delete_agent,
|
||||
|
||||
@ -741,3 +741,53 @@ fn stop_live_agent_response_serialises_camelcase_with_kind() {
|
||||
assert_eq!(v["alreadyStopped"], false);
|
||||
assert!(v.get("session_id").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restart_cell_agent_request_deserialises_all_presentations() {
|
||||
use app_tauri_lib::dto::{CellAgentPresentationDto, RestartCellAgentRuntimeRequestDto};
|
||||
|
||||
for (wire, expected) in [
|
||||
("headless", CellAgentPresentationDto::Headless),
|
||||
("tui", CellAgentPresentationDto::Tui),
|
||||
("customCli", CellAgentPresentationDto::CustomCli),
|
||||
("unknown", CellAgentPresentationDto::Unknown),
|
||||
] {
|
||||
let raw = json!({
|
||||
"projectId": Uuid::from_u128(1).to_string(),
|
||||
"nodeId": Uuid::from_u128(2).to_string(),
|
||||
"agentId": Uuid::from_u128(3).to_string(),
|
||||
"presentation": wire,
|
||||
"layoutId": Uuid::from_u128(4).to_string()
|
||||
});
|
||||
let dto: RestartCellAgentRuntimeRequestDto = serde_json::from_value(raw).unwrap();
|
||||
assert_eq!(dto.presentation, expected);
|
||||
assert_eq!(dto.layout_id, Some(Uuid::from_u128(4).to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restart_cell_agent_response_uses_restart_event_correlation_contract() {
|
||||
use app_tauri_lib::dto::RestartCellAgentRuntimeResponseDto;
|
||||
use application::{CellAgentPresentation, LiveSessionKind, RestartCellAgentRuntimeOutput};
|
||||
|
||||
let agent = AgentId::from_uuid(Uuid::from_u128(11));
|
||||
let node = NodeId::from_uuid(Uuid::from_u128(12));
|
||||
let layout = domain::LayoutId::from_uuid(Uuid::from_u128(13));
|
||||
let dto = RestartCellAgentRuntimeResponseDto::from(RestartCellAgentRuntimeOutput {
|
||||
restart_id: "restart-317".to_owned(),
|
||||
agent_id: agent,
|
||||
node_id: node,
|
||||
kind: LiveSessionKind::Structured,
|
||||
presentation: CellAgentPresentation::CustomCli,
|
||||
layout_id: Some(layout),
|
||||
});
|
||||
|
||||
let value = serde_json::to_value(dto).unwrap();
|
||||
assert_eq!(value["restartId"], "restart-317");
|
||||
assert_eq!(value["status"], "scheduled");
|
||||
assert_eq!(value["agentId"], agent.to_string());
|
||||
assert_eq!(value["nodeId"], node.to_string());
|
||||
assert_eq!(value["kind"], "structured");
|
||||
assert_eq!(value["presentation"], "customCli");
|
||||
assert_eq!(value["layoutId"], layout.to_string());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user