fix(agents): enforcer l'invariant « 1 session vivante par agent » (singleton)
Un agent ne peut tourner que dans une seule cellule à la fois. La garde dans LaunchAgent refuse le spawn si l'agent est déjà vivant dans un autre node (AGENT_ALREADY_RUNNING) ; idempotent sur le même node ; le chemin resume (agent mort) reste inchangé. Le node_id est désormais plombé jusqu'au use case. Corrige le reset asymétrique d'une cellule au changement d'onglet : deux leaves partageant le même agent id rendaient session_for_agent/is_agent_live/stop_agent ambigus (cible arbitraire). Le churn reset/reattach déclenchait aussi les accents mélangés (FIFO intact, non touché). - snapshot agentWasRunning calculé par node (is_node_live) et non par agent - commande list_live_agents + live_agents()/node_for_agent()/is_node_live() - UI : dropdown grise les agents déjà placés ailleurs ; 2e cellule en doublon affiche « disponible » au lieu d'une relance fantôme Tests : cargo test (application + app-tauri) vert ; tsc + vitest vert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -162,11 +162,19 @@ export function TerminalView({
|
||||
};
|
||||
|
||||
const onOpenError = (e: unknown) => {
|
||||
if (!disposed) {
|
||||
if (disposed) return;
|
||||
// The agent is already running in another cell (singleton invariant): this
|
||||
// is NOT an error — the cell is simply available. Show a calm, muted notice
|
||||
// (not the red failure line) so the user can pick another agent.
|
||||
if (errorCode(e) === "AGENT_ALREADY_RUNNING") {
|
||||
term.write(
|
||||
`\r\n\x1b[31mfailed to open terminal: ${describe(e)}\x1b[0m\r\n`,
|
||||
`\r\n\x1b[2mCellule disponible — l'agent est en cours dans une autre cellule.\x1b[0m\r\n`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
term.write(
|
||||
`\r\n\x1b[31mfailed to open terminal: ${describe(e)}\x1b[0m\r\n`,
|
||||
);
|
||||
};
|
||||
|
||||
// Re-attach to an existing live PTY when this cell already has a session;
|
||||
@ -265,3 +273,11 @@ function describe(e: unknown): string {
|
||||
}
|
||||
return String(e);
|
||||
}
|
||||
|
||||
/** Extracts a gateway error `code` when present (the Tauri/mock error shape). */
|
||||
function errorCode(e: unknown): string | undefined {
|
||||
if (e && typeof e === "object" && "code" in e) {
|
||||
return String((e as { code: unknown }).code);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user