Add agent restart lifecycle command
Extends the existing CheckIn RPC with a one-shot AgentCommand (restart only -- stop/uninstall need real per-platform OS service-manager integration and stay deliberately out of scope), delivered at-most-once: cleared the instant it's handed to the agent in a response, since a restarting agent's process is gone before it could ever confirm receipt. On restart, the agent flushes whatever's buffered, aborts its source task, and exits cleanly, relying entirely on the host's own service manager to bring it back up. Issuing a command is gated at RoleAdmin (stricter than config editing's RoleEditor) and logged into the same audit_log table Phase 7's AI interactions use, via a new agent_command event type. A real bug was found and fixed during live verification: the first implementation tried to atomically read-and-clear pending_command in a single INSERT...ON CONFLICT statement using a sibling CTE referenced only from RETURNING, on the assumption that Postgres evaluates every part of a WITH query against one pre-statement snapshot. That's wrong specifically for FOR UPDATE, which always reads the latest row version including one written earlier in the same statement -- confirmed empirically (a restart command was always coming back empty even when genuinely pending, so the agent never received it). Fixed by splitting into two real, ordered statements inside one explicit transaction. See /docs/agent-management-design.md's "Lifecycle commands" section.
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
-- Agent lifecycle commands (Phase: agent management punch-list item 1,
|
||||
-- see /docs/agent-management-design.md). A one-shot action, not a
|
||||
-- persistent desired state like desired_override -- pending_command is
|
||||
-- cleared atomically by ingest/internal/agentregistry.Registry.CheckIn
|
||||
-- the moment it's handed to the agent in a response, not once the agent
|
||||
-- confirms execution (a restarting agent's process is gone before it
|
||||
-- could send that confirmation). issued_at/issued_by are kept even
|
||||
-- after the command clears, as the last-issued record for the web UI
|
||||
-- and as a lightweight trail alongside the real audit_log entry
|
||||
-- (event_type = 'agent_command', see enterprise/internal/audit).
|
||||
ALTER TABLE agents
|
||||
ADD COLUMN pending_command TEXT,
|
||||
ADD COLUMN command_issued_at TIMESTAMPTZ,
|
||||
ADD COLUMN command_issued_by TEXT;
|
||||
|
||||
ALTER TABLE agents
|
||||
ADD CONSTRAINT agents_pending_command_check
|
||||
CHECK (pending_command IS NULL OR pending_command IN ('restart'));
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Agent lifecycle commands are audited through the same append-only,
|
||||
-- hash-chained audit_log table every other privileged action already
|
||||
-- uses -- same extension shape as 0036_add_ai_interaction_event_type.sql.
|
||||
-- Postgres has no ALTER CHECK, so drop and recreate.
|
||||
ALTER TABLE audit_log DROP CONSTRAINT audit_log_event_type_check;
|
||||
|
||||
ALTER TABLE audit_log
|
||||
ADD CONSTRAINT audit_log_event_type_check
|
||||
CHECK (event_type IN ('query', 'role_change', 'grant_change', 'sso_config_change', 'secret_reveal', 'ai_interaction', 'agent_command'));
|
||||
Reference in New Issue
Block a user