Skip to main content

All posts

Human-in-the-loop approvals for autonomous agents

Alex Cinovojupdated 26 July 2026

A gate of black pillars with a gold light beam, filtering a dense network down to a few paths
Security

Human-in-the-loop approval is a pause placed in front of a side effect, where a person approves or rejects the specific action before the agent continues. In Swarm 357 it is ApprovalGate, it produces a durable ApprovalRecord, and the current target is Bash commands under SWARM_HITL_BASH.

The reason this exists is not compliance theatre. It is that autonomy and reversibility are different axes, and most agent frameworks conflate them. An agent that drafts an email autonomously is fine, because the draft is reversible. An agent that sends the email autonomously is a different product with a different risk profile, and pretending otherwise is how teams end up apologising to customers.

Approvals must be durable, or they are decoration

The version that does not work: hold the pending request in memory, block the coroutine, wait for input.

It breaks the first time the process restarts. The pending request evaporates, the operator's approval lands on nothing, and the run is stuck in a state nobody can inspect. I have debugged that. It is worse than no approval flow, because the team believes the control exists.

A durable record fixes it. The request is written before the agent blocks, so:

  • A restart does not lose the request.
  • The operator can list pending requests from a different process.
  • The decision, the decider, and the timestamp are all recoverable afterwards.
  • The audit question "who approved this?" has an answer that does not depend on chat history.

The control plane

Two surfaces, same records.

swarm inspect            # what is pending, and what is each run waiting on
swarm approve <id>
swarm reject <id>

The HTTP API exposes the same operations, documented in approvals and execution, so approval can live in whatever tool your team already watches instead of forcing everyone into a terminal. In practice that is the difference between a control that gets used and a control that gets bypassed.

Timeouts reject

This is the single most important default in the whole feature.

An approval request that nobody answered is not an approval. If a timeout resolved to approve, the entire mechanism would be a delay rather than a gate, and it would fail exactly when your team is busiest, which is exactly when you most want the gate.

So timeouts reject, the run stops, and the operator retries deliberately. That is more annoying and it is correct.

What to gate, and what to leave alone

Gate the things you cannot take back.

ActionGate itWhy
Shell commandYesUnrecoverable, and the current HITL target
Write outside the workspaceBlocked entirelyConfinement handles this before approval matters
External send, payment, publishYes, in your own integrationThird-party side effects have no undo
File write inside the workspaceNoGit is your undo
Read, search, draftNoReversible by definition

The failure mode I see most often is gating everything. It feels safe for a week. Then approval requests pile up, someone starts approving without reading, and you have trained your team to rubber-stamp. A gate that is always green is worse than no gate, because it produces an audit trail that looks like oversight.

Gate less, and mean it.

How it composes with the other controls

Approvals are the last layer, not the only one.

  1. Deny by default. Server and production modes refuse Bash unless SWARM_ALLOW_BASH=1. Most deployments should stop here.
  2. Policy gate. If Bash is enabled, BashSecurityGate validates the command at the argv level against thirteen patterns. See why your agent needs a bash policy gate.
  3. Filesystem confinement. Read and Write stay inside the workspace root regardless of what any gate decides.
  4. Human approval. For what survives, a person decides.

Each layer assumes the one before it may fail. That is the point of layering, and it is why I do not describe any single one of them as "the security model".

HITL approvals are Beta on the status page. The Bash gate underneath is Stable.

When not to use HITL

  • High-frequency automation. If a workflow runs hundreds of times a day, a human gate is a queue, and queues get drained carelessly. Constrain the tools instead.
  • Nobody owns the queue. An approval flow with no named owner and no alerting is a way to stall runs, not to supervise them. Decide who watches it before you turn it on.
  • The action is already reversible. Approving a git commit on a feature branch is ceremony. Save the attention for the irreversible steps.

Get started

pip install techtide-swarm
swarm init
swarm demo

Then read human-in-the-loop approvals for configuration, ApprovalGate for the API, approve and reject for the CLI, and the security model for the full layered picture.

Frequently asked questions

What is a human-in-the-loop approval for an AI agent?
A pause inserted before a side effect, where a named person approves or rejects the specific action. The agent cannot proceed until the decision is recorded.
What happens if nobody responds to an approval request?
It rejects on timeout. An unanswered request is not consent, so the safe default is to refuse and let the operator retry deliberately.
Does HITL make agents too slow to be useful?
Only if you gate everything. Gate side effects with real blast radius, such as shell commands, and let reads and drafts run unattended.
  • Security
  • Operations
  • Architecture