Skip to main content
This example is a support agent built with Agno. It looks orders up with one tool, and its 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

The SDK wheel goes in the bundle’s 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

The run is held: the refund waits in the Review queue. Decide it there, or through the Holds API. The item’s id is the model’s tool call id: read it from the hold (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 follows call:<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=True into a hold. Agno’s other pauses (asking the user for input, external execution) aren’t mapped; use the core SDK’s inv.ask for those.
  • The RunOutput travels in run state. A value there can be at most 1 MiB, and the RunOutput grows 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.