6.0 KiB
status, created, owner
| status | created | owner |
|---|---|---|
| shipped | 2026-04-28 | cfl |
Agent Creation Governance
Why
Any agent with can_create_agents: true (CEO by default) can call POST /workspaces/:wsId/agents and create unlimited subordinate agents that immediately become idle and start receiving wakeups. The approval infrastructure already exists — ApprovalTypeHireAgent in models.go, AgentStatusPendingApproval in the status enum, pending_approval → idle in allowedTransitions — but nothing wires them into the creation path.
Without governance, a misbehaving or compromised CEO agent can silently expand the agent roster, consume budget, and trigger downstream wakeups before the human operator notices. The infrastructure is present; it just isn't connected.
What
Workspace setting: require_approval_for_new_agents
A boolean workspace setting stored in office_workspace_settings (new table, keyed by workspace ID). Default: true.
The setting is readable and writable via GET/PATCH /workspaces/:wsId/settings. It appears in the workspace settings UI under a "Governance" section.
Creation path changes
The createAgent handler currently sets agent.Status = models.AgentStatusIdle. After this change:
- The handler reads
require_approval_for_new_agentsfor the workspace. - If the caller is an authenticated agent (JWT present) and the setting is
true:- Agent is persisted with
status = pending_approval. - An
ApprovalTypeHireAgentapproval record is created viaApprovalService.CreateApprovalWithActivity. The payload includes: agent name, role, requested permissions JSON, creator agent ID, and the optionalreasonfield from the request body. - Response is
201 Createdwith the agent inpending_approvalstatus.
- Agent is persisted with
- If the caller is a human (no JWT) or the setting is
false:- Agent is persisted with
status = idle— unchanged from current behavior.
- Agent is persisted with
A pending_approval agent cannot receive wakeups. The scheduler already skips non-idle agents; no scheduler changes are needed.
Approval resolution
ApprovalService.applyApprovalSideEffects currently calls queueApprovalWakeup for ApprovalTypeHireAgent. This changes:
- Approved: call
agentWriter.UpdateAgentStatusFields(ctx, agentInstanceID, "idle", "")to activate the agent, then queue the approval wakeup so the creating agent is notified. - Rejected: call
agentWriter.UpdateAgentStatusFields(ctx, agentInstanceID, "stopped", "rejected by human")to prevent activation, then queue the wakeup.
ApprovalService needs an AgentWriter dependency (the same shared.AgentWriter interface already implemented by AgentService).
The Approval.Payload must include agent_instance_id so the service can look up which agent to activate or stop.
Request body addition
CreateAgentRequest gains an optional reason string field. When the caller is an agent, this is included in the approval payload so the human reviewer sees why the agent is being hired.
Workspace settings model and API
New table office_workspace_settings:
CREATE TABLE IF NOT EXISTS office_workspace_settings (
workspace_id TEXT PRIMARY KEY,
require_approval_for_new_agents INTEGER NOT NULL DEFAULT 1,
updated_at DATETIME NOT NULL DEFAULT (datetime('now'))
);
New repository methods: GetWorkspaceSettings, UpsertWorkspaceSettings.
New handler (mounted under existing config or a new settings group):
GET /workspaces/:wsId/settings— returns current settings, with defaults if no row exists.PATCH /workspaces/:wsId/settings— updates one or more fields.
UX
Inbox
Pending agent hire approvals appear in the inbox as items with type hire_agent. Each item shows:
- Title: "New agent request: {agent name}"
- Body: role, requested permissions (human-readable), creator agent name, reason (if provided)
- Actions: Approve / Reject buttons (call
POST /approvals/:id/decide)
Agent list
Agents with status = pending_approval appear in the agent list with:
- A "Pending approval" badge (amber, not green)
- Dimmed appearance
- No "Assign task" button
Workspace settings
Under Settings > Governance (or equivalent section in the office settings page):
- Toggle: "Require approval for new agents created by agents" (default: on)
- Help text: "When enabled, agents created by other agents must be approved before they can receive tasks. Agents created directly from the UI are always created immediately."
Scenarios
-
GIVEN
require_approval_for_new_agents = true, WHEN the CEO agent callsPOST /workspaces/:wsId/agents, THEN the new agent is persisted withstatus = pending_approval, anApprovalTypeHireAgentrecord is created, and the API returns the agent withpending_approvalstatus. -
GIVEN a
pending_approvalagent with an open hire approval, WHEN the human approves viaPOST /approvals/:id/decidewithstatus = approved, THEN the agent's status changes toidleand the CEO receives aapproval_resolvedwakeup. -
GIVEN a
pending_approvalagent, WHEN the human rejects, THEN the agent's status changes tostoppedand the CEO receives aapproval_resolvedwakeup withstatus = rejected. -
GIVEN a human user creates an agent via the UI (no JWT), WHEN
POST /workspaces/:wsId/agentsis called, THEN the agent is created asidleregardless of the governance setting. -
GIVEN
require_approval_for_new_agents = false, WHEN the CEO creates an agent, THEN the agent is created asidleimmediately with no approval record.
Out of scope
- Board-level revision requests (
revision_requested→pendingstate); kandev uses a simpler two-outcome approve/reject model for now. - Limiting which human users can approve (all authenticated users can decide approvals today; role-based approval gating is a separate feature).
- CEO agents creating other CEO agents (already blocked by
ErrAgentCEOAlreadyExists; governance adds no new constraint here). - Retroactively gate-checking agents already at
idlestatus.