refund tool is declared with requires_confirmation=True. When the model calls it, Agno pauses the run, AgentOS asks a person, and the run continues with the approval, the edited arguments or the rejection, in whichever container answers the resume.
The SDK’s agno adapter does the work: it keeps the paused RunOutput in run state, asks one hold for every call waiting for confirmation, and calls continue_run with the decisions on the resume. See Agent frameworks.
Tested with
agno 3.1.3 and Python 3.13, using the conformance kit: 22 checks passed, including an approved, an edited and a rejected refund.The bundle
vendor/ folder and is installed with its agno extra; see SDKs.
How the model is wired
agno.model(inv) is an OpenAILike model for config.models.main: the gateway’s base URL, the run token in the x-agentos-run-token header, and OpenAI clients that never send the token anywhere else. The model name reaches the gateway exactly as written in agentos.yaml (anthropic/claude-sonnet-4-5). Pass a role for another model: agno.model(inv, "judge").
The agent is built inside the handler, on every invocation, because the token belongs to one invocation. telemetry=False keeps Agno from calling its telemetry service, which an agent can’t reach anyway unless you allow it (see Network access).
Agno doesn’t raise when a model call fails. A guardrail block ends the Agno run with status ERROR and the gateway’s message as its content, so the handler checks output.status and fails the AgentOS run with that message instead of returning it as an answer. See Guardrails.
How the approval works
1
Agno pauses the run
The model calls
refund. Because the tool has requires_confirmation=True, Agno pauses instead of running it. agno.run keeps the paused RunOutput and asks one hold: one item per waiting call, its id the tool call id, its data the call’s arguments, with approve, edit and reject.2
A person decides
The run is held until someone decides in the Review queue or through the Holds API. An edit changes the call’s arguments, for example a smaller amount.
3
The run continues
On the resume the adapter restores the
RunOutput, confirms or rejects each call (with the edited arguments for an edit), and Agno’s continue_run goes on. A rejected call gets the reviewer’s comment (or “A person rejected this tool call.”) as its note, and the model answers the customer.Run it
1
Zip and deploy
Put the SDK wheel in
vendor/ (see SDKs), set config.models.main to a model from your Models page, then zip the folder’s contents and upload them:2
Ask for a refund
GET …/holds/h_12). An edit carries the item’s full data:Test it before you deploy
The conformance kit builds the bundle and runs it against a mock platform whose model followscall:<tool> {arguments} in a message, so these cases drive the tools and every decision without a real model:
conformance.yaml
Things to know
- Only confirmations are mapped. The adapter turns
requires_confirmation=Trueinto a hold. Agno’s other pauses (asking the user for input, external execution) aren’t mapped; use the core SDK’sinv.askfor those. - The
RunOutputtravels in run state. A value there can be at most 1 MiB, and theRunOutputgrows with the conversation.
Next steps
Human approvals
Everything an ask can carry: several items, details, files, an expiry.
More frameworks
The same agent in other frameworks and languages.

