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

@ -141,6 +141,45 @@ describe("HttpAgentGateway WS round-trip (B6 frames)", () => {
});
});
it("restarts the exact cell runtime through the HTTP command", async () => {
const response = {
restartId: "restart-7",
status: "scheduled",
agentId: "agent-1",
nodeId: "node-2",
kind: "pty",
presentation: "tui",
layoutId: null,
};
const { gw, calls } = httpAgentGateway(async () => ({
ok: true,
status: 200,
json: async () => response,
text: async () => JSON.stringify(response),
}));
await expect(
gw.restartCellAgentRuntime({
projectId: "proj-1",
nodeId: "node-2",
agentId: "agent-1",
presentation: "tui",
}),
).resolves.toEqual(response);
const init = calls[0].init as { body: string };
expect(JSON.parse(init.body)).toEqual({
command: "restart_cell_agent_runtime",
args: {
request: {
projectId: "proj-1",
nodeId: "node-2",
agentId: "agent-1",
presentation: "tui",
},
},
});
});
it("updates the project orchestrator through the dedicated request DTO", async () => {
const response = { orchestratorAgentId: "agent-2" };
const { gw, calls } = httpAgentGateway(async () => ({