How an approval works
A gated tool call parks and shows the person a card with three things: the tool’s name, one plain sentence saying what will change (intent), and the
literal operation (detail) so the sentence can be checked against what will
run. For a spend, the engine writes the sentence from its own cost estimate;
the model does not get to describe the price.
Only a person can approve. Every other way a request settles is a denial, so
“nobody answered” is safe by construction. The timeout is five minutes by
default (
APPROVAL_TIMEOUT_MS; 0 denies at once). A pending request is
written to the transcript, so a dropped connection loses the view, not the
decision.
Turning approvals off. REQUIRE_WRITE_APPROVAL set to false, 0 or no
removes the approval channel from person chat. The effect differs by tool and
is listed below; most gated writes then refuse rather than run.
Unattended runs (coworkers, the goal agent, the reviewer, and since
2026-10-11 an inbound email turn) have no approval channel at all, whatever the
setting.
Source: src/engine/approval.ts (ApprovalBroker, approvalRequired, approvalTimeoutFromEnv)
Which calls ask a person
Not gated by an approval: memory notes, project files, AgentMail drafts,
content writer drafts, browser logins, and Contentsquare tools.
Source:
src/engine/session.ts (where each server is given or denied the approval channel), src/engine/memory.ts, src/engine/shopify.ts, src/engine/dataforseo.ts, src/engine/firecrawl.ts, src/engine/sheets.ts, src/engine/github.ts, src/engine/agentmail.ts, src/engine/cloudflare.ts, src/engine/railway.ts
Model spend
A limit in
agent.config.json that is zero, negative or not finite stops the
server from loading the config. A monthly ledger that cannot be read stops the
session before it starts, rather than reading as nothing spent. The session
checks the monthly figure before starting and after every turn.
On a gateway model the per-turn cost is the CLI’s estimate, not the provider’s
bill. The cap still fires on the estimate.
Source: src/engine/config.ts (loadConfig), src/engine/session.ts (monthly cap), src/engine/harness/registry.ts (harnessSessionLimits), src/server/registry.ts (worker overrides)
Goals
A daily figure of zero or below is refused. If today’s review spend cannot be
read, no review runs.
Source:
src/engine/goal-reviewer.ts, src/engine/goal-store.ts, src/engine/goal-agent.ts
Connector spend
The connector’s own ledger is separate from the model ledger, so buying data
never consumes the budget that keeps the session alive. The counter follows
the credential: brands sharing one account’s key share one monthly counter.
For both connectors,
0 is the strictest setting, never “no limit”. A
threshold of 0 asks on every call, a per-call ceiling of 0 refuses every
billed call, and a monthly budget of 0 refuses every billed call for the
month. Free calls, such as the account check, still run. Before PR #139
a monthly budget of 0 switched the monthly check off; that was a bug and
is fixed.Coworker allowances
A specialist that holds DataForSEO or Firecrawl must carry an allowance:
The per-run figure is checked inside the connector’s gate on every call,
counting what is spent plus what is held for calls still in flight. The
per-day figure is checked before a run starts. Both sit under the deployment’s
per-call ceiling and monthly budget, never above them.
Measured once on 2026-09-23 for Firecrawl: a scrape was charged 1 credit, then
a crawl that would have taken the run past a 3-credit allowance was refused
with nothing spent (
ALLOWANCE_CAPTURED). The DataForSEO allowance and the
per-day refusal have not been exercised (ALLOWANCE_UNVERIFIED).
Source: src/engine/dataforseo.ts, src/engine/firecrawl.ts, src/engine/allowance.ts, src/engine/coworkers.ts (specialistProblem)
Other caps
Source:
src/engine/googleads.ts, src/engine/microsoftads.ts, src/engine/agentmail.ts, src/engine/agentmail-inbound.ts, src/engine/browser-config.ts, src/engine/coworkers.ts