fix(application,domain,infrastructure,web-server,app-tauri,frontend): arrêt d'agent fiable avec cascade sur l'arbre de propriété et statut cohérent — #320 (QA verte)
Symptôme: stopper un agent (ex Main sur UnityPlugin) laissait des travaux poursuivre, la cascade n'atteignait pas les agents lancés et la pastille restait orange pendant que l'agent continuait à en appeler d'autres. - barrière d'admission par génération d'exécution (ExecutionControl): un stop ferme l'admission de tout le sous-arbre propriété/attente AVANT toute annulation asynchrone — plus aucune nouvelle action inter-agents après stop - arbre de propriété durable en-process (launched_by) + dépendances d'attente actives: la cascade couvre transitivement agents et travaux lancés - commandes d'un agent arrêté rejetées (AppError::AgentStopped); stop lui-même admis pour rester idempotent; réactivation sur relance humaine (génération +1) et enregistrement du propriétaire au spawn - admission exposée aux adaptateurs hors commandes cœur (tickets, templates, plugins) via ensure_agent_execution_active; MCP server/tools, web-server et commandes Tauri alignés - cohérence live-state: statut Idle avec intent « Arrêté » ou « Arrêt incomplet — Certains travaux sont encore actifs » selon l'issue de la cascade; workstate et surfaces UI (AgentsPanel, LayoutGrid, ProjectWorkStatePanel) reflètent l'état réel, plus de pastille orange fantôme - tests: orchestrator_service (cascade/générations/idempotence), workstate_actions, background_task_cancel, error_codes, agents, layout, workstate QA verte sur fix/320-agent-stop-cascade. Aucun push.
This commit is contained in:
@ -145,7 +145,7 @@ export interface AgentsViewModel {
|
||||
onData: (bytes: Uint8Array) => void,
|
||||
) => Promise<TerminalHandle>;
|
||||
/** Stops an agent PTY explicitly by closing its live session id. */
|
||||
stopAgent: (agentId?: string) => Promise<void>;
|
||||
stopAgent: (agentId?: string) => Promise<boolean>;
|
||||
/**
|
||||
* Cancels the auto-resume armed for a rate-limited agent (ARCHITECTURE §21):
|
||||
* the "Annuler la reprise" action. Optimistically drops the countdown; the
|
||||
@ -513,20 +513,23 @@ export function useAgents(projectId: string): AgentsViewModel {
|
||||
);
|
||||
|
||||
const stopAgent = useCallback(
|
||||
async (agentId?: string) => {
|
||||
async (agentId?: string): Promise<boolean> => {
|
||||
const targetAgentId = agentId ?? runningAgentId;
|
||||
if (!targetAgentId) return;
|
||||
setRunningAgentId((current) =>
|
||||
current === targetAgentId || !targetAgentId ? null : current,
|
||||
);
|
||||
if (!targetAgentId) return false;
|
||||
setError(null);
|
||||
try {
|
||||
await agent.stopLiveAgent(projectId, targetAgentId, "Arrêt demandé depuis Agents");
|
||||
setRunningAgentId((current) =>
|
||||
current === targetAgentId ? null : current,
|
||||
);
|
||||
setLiveAgents((current) =>
|
||||
current.filter((candidate) => candidate.agentId !== targetAgentId),
|
||||
);
|
||||
} catch (e) {
|
||||
setError(describe(e));
|
||||
return true;
|
||||
} catch {
|
||||
setError("Arrêt incomplet — Certains travaux sont encore actifs.");
|
||||
await refreshLiveAgents();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[agent, projectId, refreshLiveAgents, runningAgentId],
|
||||
|
||||
Reference in New Issue
Block a user