Supervisors & Auto-Approval
Supervised agents ask before every tool call. That is the right default, but it means an agent is useless while you are asleep — a scheduled job that needs one tool call will sit waiting until you tap a button.
Denkeeper offers two ways to answer that prompt without you: auto-approve rules, which are standing decisions about specific tools, and a supervisor agent, which is a second LLM that reviews each call on its merits.
The approval chain
For a supervised agent, each tool call runs this gauntlet in order, stopping at the first thing that decides:
- Auto-approve rules — a matching rule executes immediately
- Supervisor agent — if configured, reviews and returns APPROVE, DENY, or ESCALATE
- Human approval — the Approve/Deny buttons
Anything not decided by 1 or 2 reaches you. Nothing skips the chain.
Auto-approve rules
A rule says “this tool never needs asking about again”. There are three scopes, checked in this order:
| Scope | Lives in | Created by | Lifetime |
|---|---|---|---|
config | TOML | Operator, in the config file | Until config changes |
session | Memory | The Auto (session) button, dashboard, or REST | 15 minutes, conversation-scoped |
permanent | SQLite | The Auto (always) button, dashboard, or REST | Until deleted |
Declare config rules per agent:
[[agents]]
name = "default"
session_tier = "supervised"
auto_approve_tools = ["web_search", "kv_get"]
All three scopes answer the same question, so the ordering cannot change an approve/deny outcome — it fixes attribution, keeping scope=config stable across sessions, and skips a database lookup for the hottest calls.
config rules cannot be weakened at runtime. POST /api/v1/auto-approve rejects scope: "config" with a 400, and config rules are listed with an empty id so there is nothing for a DELETE to address. Removing one means editing the TOML file.
Names that do not match any advertised tool are warned about and kept, never dropped — a remote MCP server that connects late must not silently weaken your policy in the window before it appears.
Supervisor agents
A supervisor is an ordinary agent pointed at the job of reviewing another agent’s tool calls.
[[agents]]
name = "default"
session_tier = "supervised"
supervisor = "argus"
supervisor_timeout = "30s"
supervisor_context_messages = 5
[[agents]]
name = "argus"
session_tier = "autonomous"
llm_model = "claude-haiku-4-5"
When a tool call reaches the supervisor, it gets a one-shot LLM call — the tool name and arguments, its description, and the last few conversation messages — and answers with one of:
| Decision | Result |
|---|---|
| APPROVE | The call executes |
| DENY | The call is blocked, and the supervisor’s reason is fed back to the agent’s LLM so it can adapt |
| ESCALATE | Falls through to human approval |
A timeout or an error also falls through to human approval. The supervisor fails open to you, never open to the tool.
Constraints
Validation rejects configurations that would deadlock or recurse:
- The supervisor must exist
- It must not itself be supervised — no chains
- It must not use the
supervisedtier, which would deadlock supervisoris only meaningful on a supervised agent- An agent that is referenced as a supervisor cannot be deleted
The review is a single call with no storage, no skills, and no tool loop of its own, which is what keeps it cheap enough to sit in front of every tool call. A small fast model is usually the right choice.
Observability
Every decision emits a supervisor audit event, preserving the raw response alongside the parsed decision and reason. Tool-approval events carry a matching status — supervisor_approved, supervisor_denied, supervisor_escalated, or supervisor_error — which surfaces in the dashboard’s Chat view and as a filter chip in the Audit Log.
The post-turn reviewer
Distinct from the supervisor, and easy to confuse with it. The supervisor gates tool calls before they run; the reviewer looks at a turn after it finishes, to maintain the agent’s memory and suggest skill improvements.
[[agents]]
name = "default"
reviewer_model = "claude-haiku-4-5"
nudge_memory_interval = 10
nudge_skill_interval = 25
Leaving reviewer_model empty disables it. It runs fire-and-forget through a no-op sender, so it cannot message you.
The reviewer is capability-reduced, not supervised — a deliberate design choice. Approval gating exists only at the engine tier, and the reviewer sends through a no-op, so it could never be meaningfully supervised. Instead it is handed a deliberately small tool set: skill_list, skill_get, skill_read_file, persona_get, and append-only persona_memory_manage. It can read skills and report improvements as text, but it cannot rewrite them.
It runs at the supervised tier rather than restricted, because restricted grants read-only tools only and persona_memory_manage is a write — the one call the reviewer exists to make.
See the configuration reference for every supervisor and reviewer knob.