remove agent-callable restart tool (blast radius in multi-user model)
Nightly Build / build (push) Successful in 6m37s
Nightly Build / build (push) Successful in 6m37s
This commit is contained in:
@@ -231,7 +231,6 @@ impl ApprovalManager {
|
||||
|
||||
let defaults: &[(&str, &str)] = &[
|
||||
(tn::EXECUTE_CMD, "require"),
|
||||
(tn::RESTART, "require"),
|
||||
// Opening a mobile pairing window emits a secret (the QR) into chat:
|
||||
// it must be a deliberate human action, not LLM-triggerable (plugin.md §11).
|
||||
("mobile_start_pairing", "require"),
|
||||
|
||||
@@ -41,13 +41,6 @@ impl ChatSessionHandler {
|
||||
} else if tool_name == tn::EXECUTE_CMD {
|
||||
let cmd = arguments["command"].as_str().unwrap_or("");
|
||||
em.pending_write(request_id, tool_call_id, "$ execute_cmd".to_string(), None, format!("$ {cmd}")).await;
|
||||
} else if tool_name == tn::RESTART {
|
||||
em.pending_write(
|
||||
request_id, tool_call_id,
|
||||
"$ restart".to_string(),
|
||||
None,
|
||||
"Riavvia il processo (exit -1 → supervisor ricompila e rilancia)".to_string(),
|
||||
).await;
|
||||
} else {
|
||||
em.approval_required(request_id, tool_call_id, tool_name.to_string(), arguments.clone()).await;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, info, trace};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::tools::tool_names as tn;
|
||||
use crate::chat_event_bus::ToolCallEvent;
|
||||
use crate::chatbot::{LlmTurn, ToolCall};
|
||||
use crate::db::{chat_history, chat_llm_tools};
|
||||
@@ -264,18 +263,6 @@ impl ChatSessionHandler {
|
||||
|
||||
debug!(session_id = self.session_id, tool = %call.name, tool_call_id, "dispatching");
|
||||
|
||||
// `restart` calls process::exit — mark the call done in the DB first so it
|
||||
// doesn't reappear as `pending` after the supervisor relaunches.
|
||||
if call.name == tn::RESTART {
|
||||
info!(session_id = self.session_id, tool_call_id, "restart approved — marking done then exiting");
|
||||
chat_llm_tools::complete(pool, tool_call_id, "Riavvio avviato.", "string").await?;
|
||||
em.tool_done(tool_call_id, "Riavvio avviato.".to_string(), "string".to_string(), None, None).await;
|
||||
// Use _exit() to skip C atexit handlers (e.g. Metal GPU cleanup in
|
||||
// whisper-rs/ggml, which aborts with SIGABRT and yields exit code 134
|
||||
// instead of 255 — breaking the run.sh restart supervisor).
|
||||
unsafe { libc::_exit(-1) }
|
||||
}
|
||||
|
||||
// Route the approved call to its executor. `AbortPending` means the
|
||||
// clarification WS channel closed — end the turn and leave the tool
|
||||
// `pending` for resume to re-ask.
|
||||
|
||||
@@ -358,17 +358,6 @@ impl ChatSessionHandler {
|
||||
GateOutcome::ChannelClosed => return Ok(true), // pending still, WS disconnected
|
||||
}
|
||||
|
||||
// `restart` calls process::exit and never returns — mark done first.
|
||||
if tc.name == tn::RESTART {
|
||||
info!(session_id = self.session_id, tool_call_id = tc.id, "restart approved (resume) — marking done then exiting");
|
||||
chat_llm_tools::complete(pool, tc.id, "Riavvio avviato.", "string").await?;
|
||||
em.tool_done(tc.id, "Riavvio avviato.".to_string(), "string".to_string(), None, None).await;
|
||||
// Use _exit() to skip C atexit handlers (e.g. Metal GPU cleanup in
|
||||
// whisper-rs/ggml, which aborts with SIGABRT and yields exit code 134
|
||||
// instead of 255 — breaking the run.sh restart supervisor).
|
||||
unsafe { libc::_exit(-1) }
|
||||
}
|
||||
|
||||
// Re-run the persisted intent through the SAME dispatcher as a live turn
|
||||
// (`execute_tool_call`), not the flat `build_execution`. This routes
|
||||
// sub-agent tools (`execute_task` mode=sync, `execute_subtask`,
|
||||
|
||||
@@ -211,7 +211,6 @@ impl Tools {
|
||||
tool_registry.register(crate::tools::ast_outline::AstOutline::new());
|
||||
tool_registry.register(crate::tools::exec::ExecuteCmd);
|
||||
tool_registry.register(crate::tools::read_notification::ReadNotification);
|
||||
tool_registry.register(crate::tools::restart::Restart);
|
||||
// Unified listing / toggling across plugins, cron (+ agents for list). MCP
|
||||
// is no longer agent-managed (blueprint §14): connectors are curated by the
|
||||
// admin and activated by the user via the Connectors UI/API, not tools.
|
||||
|
||||
@@ -43,7 +43,6 @@ pub mod list_secrets;
|
||||
pub mod notify;
|
||||
pub mod set_secret;
|
||||
pub mod read_notification;
|
||||
pub mod restart;
|
||||
pub mod show_file;
|
||||
pub mod toggle_item;
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde_json::{Value, json};
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::tools::{Tool, ToolDescriptionLength};
|
||||
|
||||
/// How to restart, when exiting for a supervisor is not the answer.
|
||||
///
|
||||
/// A shell with no supervisor watching its exit code would need to tear itself
|
||||
/// down and respawn on its own. That is knowledge about the process shell, which
|
||||
/// the core does not have — so such a shell installs it here. The default server
|
||||
/// shell has a supervisor (`run.sh`) and installs no handler, so `restart` falls
|
||||
/// back to the supervisor protocol below.
|
||||
///
|
||||
/// Returns only on failure; a successful handler never comes back.
|
||||
pub type RestartHandler = Box<dyn Fn() -> Result<()> + Send + Sync>;
|
||||
|
||||
static HANDLER: OnceLock<RestartHandler> = OnceLock::new();
|
||||
|
||||
/// Called once by the process shell during startup, before any tool can run.
|
||||
pub fn set_restart_handler(handler: RestartHandler) {
|
||||
if HANDLER.set(handler).is_err() {
|
||||
warn!("a restart handler is already installed — ignoring this one");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Restart;
|
||||
|
||||
impl Tool for Restart {
|
||||
fn name(&self) -> &str { crate::tools::tool_names::RESTART }
|
||||
fn category(&self) -> crate::tools::ToolCategory { crate::tools::ToolCategory::Shell }
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Restart the skald process. Nothing is recompiled: the same binary is re-executed, \
|
||||
so this applies config.yml and database changes, which are only read at startup. \
|
||||
To load new code, build first (./build.sh), then restart. \
|
||||
Requires user approval."
|
||||
}
|
||||
|
||||
fn parameters_schema(&self) -> Value {
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
})
|
||||
}
|
||||
|
||||
fn describe(&self, _args: &Value, _length: ToolDescriptionLength) -> String {
|
||||
"restart skald".to_string()
|
||||
}
|
||||
|
||||
fn execute(&self, _args: Value) -> Result<String> {
|
||||
// A shell that installed its own teardown-and-respawn handles it here.
|
||||
// Normally this never returns.
|
||||
if let Some(handler) = HANDLER.get() {
|
||||
info!("restart requested — delegating to the installed handler");
|
||||
handler()?;
|
||||
warn!("the restart handler returned without restarting — falling back");
|
||||
}
|
||||
|
||||
// Headless: exit with code -1 (= 255 on Unix), which `run.sh` reads as
|
||||
// "re-execute me". `_exit()` rather than `exit()` skips C atexit handlers
|
||||
// — whisper-rs's Metal GPU cleanup aborts with SIGABRT, turning the exit
|
||||
// code into 134 and stopping the supervisor instead of restarting it.
|
||||
info!("restart requested — exit(-1) → supervisor re-executes the binary");
|
||||
unsafe { libc::_exit(-1) }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
pub const EXECUTE_TASK: &str = "execute_task";
|
||||
pub const EXECUTE_SUBTASK: &str = "execute_subtask";
|
||||
pub const RESTART: &str = "restart";
|
||||
pub const UPDATE_SCRATCHPAD: &str = "update_scratchpad";
|
||||
pub const WRITE_TODOS: &str = "write_todos";
|
||||
pub const ASK_USER_CLARIFICATION: &str = "ask_user_clarification";
|
||||
|
||||
Reference in New Issue
Block a user