feat(mcp,orchestrator,frontend): tool idea_restart_self, redemarrage autonome d'un agent — #288 (tool core self-only via requester MCP handshake, retour immediat {restartId, status: scheduled} puis orchestration detachee: snapshot reprise, stop PTY/structured, liberation Busy/mailbox, relance LaunchAgent meme conversation_id fresh_start=false, injection interne invisible; events domain/wire agentRestartRequested/PhaseChanged/Completed/Failed phases stopping/releasingInput/launching/resuming; CLI custom: separateurs exacts redemarrage de l'agent + etat termine/echoue, deduplication (restartId, event), verrouillage composer; TUI native: overlay AgentRestartOverlay sans injection xterm, progress/annonces projetes jusqu'a idle; QA verte: mcp_server restart_self 1/1, restart-self 3/3, backend self_restart_contract + events wire 2/2, frontend 3 suites 92/92, npm build, cargo check --workspace, fmt --check, diff --check; reserves hors perimetre: web-server 103/110 fixture PTY preexistante, 34 echecs npm test globaux listProjectSettings/web hors #288; scripts/ hors perimetre)

This commit is contained in:
2026-09-08 22:04:18 +02:00
parent ee259dc93a
commit 63537572b8
23 changed files with 1207 additions and 37 deletions

View File

@ -165,10 +165,11 @@ pub use model_server::{
};
pub use orchestrator::{
resolve_rendezvous_ceiling, resolve_rendezvous_window, run_inactivity_watchdog,
AgentWakeService, AskLivenessProbe, ContextGuardUseCases, LiveStateProvider,
LiveStateReadProvider, McpRuntimeProvider, OrchestratorOutcome, OrchestratorService,
ProposeContext, ReadContext, ReadMemory, RecordTurnProvider, WakeSessionProvider,
WatchdogOutcome, WriteMemory, DEFAULT_RENDEZVOUS_CEILING, DEFAULT_RENDEZVOUS_WINDOW,
AgentRestartScheduled, AgentSelfRestartScheduler, AgentWakeService, AskLivenessProbe,
ContextGuardUseCases, LiveStateProvider, LiveStateReadProvider, McpRuntimeProvider,
OrchestratorOutcome, OrchestratorService, ProposeContext, ReadContext, ReadMemory,
RecordTurnProvider, WakeSessionProvider, WatchdogOutcome, WriteMemory,
DEFAULT_RENDEZVOUS_CEILING, DEFAULT_RENDEZVOUS_WINDOW,
};
pub use permission::{
GetProjectPermissions, GetProjectPermissionsInput, GetProjectPermissionsOutput,

View File

@ -19,7 +19,8 @@ pub use rendezvous::{
WatchdogOutcome, DEFAULT_RENDEZVOUS_CEILING, DEFAULT_RENDEZVOUS_WINDOW,
};
pub use service::{
AskLivenessProbe, ContextGuardUseCases, LiveStateProvider, LiveStateReadProvider,
McpRuntimeProvider, OrchestratorOutcome, OrchestratorService, RecordTurnProvider,
AgentRestartScheduled, AgentSelfRestartScheduler, AskLivenessProbe, ContextGuardUseCases,
LiveStateProvider, LiveStateReadProvider, McpRuntimeProvider, OrchestratorOutcome,
OrchestratorService, RecordTurnProvider,
};
pub use wake::{AgentWakeService, WakeSessionProvider};

View File

@ -63,6 +63,25 @@ use crate::workstate::{
GetLiveStateLean, StopAgentExecutionCascade, UpdateLiveState, UpdateLiveStateInput,
};
/// Immediate acknowledgement returned after a self-restart has been accepted.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AgentRestartScheduled {
/// Correlation id emitted on all subsequent restart events.
pub restart_id: String,
}
/// Composition-root owned scheduler for the destructive, detached restart flow.
/// The application orchestrator only authenticates and dispatches the intent.
pub trait AgentSelfRestartScheduler: Send + Sync {
/// Validates liveness/concurrency synchronously, then owns the detached work.
fn schedule(
&self,
project: &Project,
agent: AgentId,
reason: Option<String>,
) -> Result<AgentRestartScheduled, AppError>;
}
/// Default terminal geometry for an orchestrator-launched agent cell. The UI
/// resizes the PTY to the real cell size on attach; these are sane starting rows
/// /cols so the spawn never fails on a zero-sized terminal.
@ -685,6 +704,8 @@ pub struct OrchestratorService {
/// Use case B8 qui produit une tâche de fond command-backed depuis la surface
/// agent MCP `idea_run_in_background`. `None` ⇒ outil non configuré.
spawn_background_command: Option<Arc<SpawnBackgroundCommand>>,
/// Detached self-restart coordinator, wired at the composition root.
self_restart: Option<Arc<dyn AgentSelfRestartScheduler>>,
}
/// Bundle des quatre use cases C7 sous [`domain::fileguard::FileGuard`], injectés
@ -784,6 +805,7 @@ impl OrchestratorService {
background_runner: None,
session_limits: None,
spawn_background_command: None,
self_restart: None,
}
}
@ -808,6 +830,13 @@ impl OrchestratorService {
self
}
/// Wires the requester-scoped detached self-restart coordinator.
#[must_use]
pub fn with_self_restart(mut self, restart: Arc<dyn AgentSelfRestartScheduler>) -> Self {
self.self_restart = Some(restart);
self
}
/// Branche le store des tâches de fond pour tracer les rendez-vous inter-agents
/// comme [`BackgroundTaskKind::HeadlessRendezvous`] (B6).
///
@ -1456,6 +1485,25 @@ impl OrchestratorService {
command: OrchestratorCommand,
) -> Result<OrchestratorOutcome, AppError> {
match command {
OrchestratorCommand::RestartSelf { agent, reason } => {
let scheduled = self
.self_restart
.as_ref()
.ok_or_else(|| {
AppError::Invalid("idea_restart_self not configured".to_owned())
})?
.schedule(project, agent, reason)?;
let reply = serde_json::json!({
"restartId": scheduled.restart_id,
"status": "scheduled",
"message": "Agent restart scheduled."
})
.to_string();
Ok(OrchestratorOutcome {
detail: "agent restart scheduled".to_owned(),
reply: Some(reply),
})
}
OrchestratorCommand::SpawnAgent {
name,
profile,