github agno-agi/agno v3.1.4

5 hours ago

Changelog

New Features:

  • Sandboxed Harness Agents: ClaudeAgent and CodexAgent take a sandbox= parameter and run the whole harness process inside an isolated, per-session computer instead of on the host, with session state that survives losing the container, the host or the replica that ran the last turn. No new classes to hold: ClaudeAgent(id="coder", db=PostgresDb(...), sandbox=DockerSandbox("agno-sandbox:local", network="isolated")) is the whole setup, and the agent builds its coordinator from its PostgresDb on first use. Each turn claims the session through a lease row in the agent's Postgres (<session_table>_sandbox_bindings, created on first use), starts compute restored from the session's last committed checkpoint (workspace plus the harness's own conversation files), runs the turn, then captures and verifies a new checkpoint and publishes the run row and recovery point in one transaction. A cancelled or failed turn publishes nothing and the previous checkpoint stays the recovery point. The turn refuses to continue on state it cannot verify: it passes the native conversation id (Claude session id, Codex thread id) committed with the last checkpoint, and a restore that lost it fails with native_state_missing before the model runs instead of silently starting a new conversation. Typed harness settings travel to the runtime as JSON; host-only settings (cwd, retries, env, native option objects) are rejected by name at construction. require_native_resume and strict_persistence are new options on BaseExternalAgent, and agno[claude] / agno[codex] install extras cover the harness SDKs. See cookbook. (#11028, #11043, #11056, #11057)
  • Docker Sandbox: DockerSandbox is the customer-operated provider. Containers run as UID 1000 with a read-only image filesystem, dropped capabilities, no privilege escalation, CPU, memory and process limits, bounded tmpfs for /workspace and /runtime, no host or Docker socket mounts, and only explicitly configured environment values plus the harness's model API key. Networking defaults to none; network="isolated" gives a managed bridge with inter-container traffic off, and subnet= picks its range. Deterministic session and generation identity reconciles creation races and lost responses; commands are never retried automatically. pause / resume are provided through the kernel freezer, and DockerCheckpointTransfer moves checkpoints in and out. cookbook/frameworks/sandbox/build_image.py builds the runtime image. See cookbook. (#10975, #11028)
  • Amazon Bedrock AgentCore Sandbox: AgentCoreSandbox implements the same contract on AgentCore Runtime, with AgentCoreCheckpointTransfer next to the Docker transfer. Each sandbox generation is one AgentCore runtime session in a Firecracker microVM; create is idempotent through a session id derived from the namespace, agent, session and generation. A root server on port 8080 claims the microVM, starts the runtime as UID 1000, fences every later operation with the claimed configuration and a random incarnation so a stale handle can never act on a later claim, forwards start and poll calls, and runs commands behind a guard that drops to UID 1000 in /workspace. Checkpoints stop UID 1000 processes during capture and restore and move through InvokeAgentRuntime in chunks, up to 2 GiB. Pause is not supported and AgentCore session storage is not used. The provider packages moved to agno.sandbox.docker and agno.sandbox.agentcore; DockerCheckpointTransfer now imports from agno.sandbox.docker. The runtime also gained two Codex fixes that apply to Docker as well: it creates CODEX_HOME, and it logs Codex in from OPENAI_API_KEY when no stored login matches, since checkpoints exclude auth.json. cookbook/frameworks/sandbox/agentcore/agentcore_setup.py builds, pushes and deploys the runtime. See cookbook. (#11050)
  • Warm Sandbox Turns: The coordinator keeps a session's computer for warm_computer_seconds (default 300) after a turn instead of destroying it: paused on Docker, left idle on AgentCore. The next turn inside that window resumes it and restores nothing; otherwise it retires the old computer and takes the cold path on a fresh generation restored from the checkpoint. The checkpoint is still captured after every turn, so a warm computer is only a shortcut to the state the checkpoint already holds. metadata.sandbox.resumed on every completed run says which path the turn took, and retire_computer / sweep_warm on the coordinator remove computers that sessions never came back for. Live on Docker a warm Claude turn took 4.6 s against 8.2 s cold; on AgentCore 3.3 s against 8.2 s. See cookbook. (#11088)
  • Sandbox Through AgentOS: POST /agents/{agent_id}/runs for a sandboxed agent submits foreground, streaming and background requests to the durable queue with no new endpoints. An Idempotency-Key retry returns the same run, while a different session, input, options or attachment bytes returns 409. Clients reconnect with /runs/{run_id}/resume (last_event_index) and cancel with /runs/{run_id}/cancel, including across replicas: a cancel settled on another replica now records the run as cancelled and keeps the user's turn on the run row. AgentOS fails at startup if a sandboxed agent has no Postgres to coordinate on, refuses the host-local default checkpoint store once a queue worker is active, and validates each sandboxed agent's bindings table at startup and before a submission, naming the remedy for a table from an earlier build. See cookbook. (#11043, #11105)
  • Attachments for Sandboxed Agents: run / arun on a sandboxed ClaudeAgent or CodexAgent accept images= and files=, and the AgentOS run route accepts them as uploads. Only bytes cross into the sandbox: each attachment is validated and encoded before any durable write, staged under /workspace/.agno/uploads/<run_id>/ by the harness's own staging, and kept in the turn's checkpoint so later turns on a new computer can still read it. A request carries at most 16 attachments and 512 KiB in total; URLs, media references, server file paths, audio and video are refused. Queued runs store the attachments with the submission, so a retry sends the same bytes. See cookbook. (#10990, #11091)
  • Images and PDFs Inline for Claude Code: ClaudeAgent now sends attached images (PNG, JPEG, GIF, WEBP) and PDFs up to 4 MiB inside the message as content blocks, the form the Agent SDK's streaming input takes, instead of only writing them to disk for Claude's Read tool. Claude sees the picture directly and never reads the file back, which used to return the whole file as base64 and overflow the SDK's 1 MiB message buffer and the sandbox's 256 KiB event frame for images of a few hundred KB. Whether a file goes inline is decided from its bytes, so a mislabelled file stays on disk for the file tools; other files and larger images keep the Read path. See cookbook. (#11115)
  • Queue Per Session: Durable background runs on one session now execute at most one at a time, in submission order, for every component. Each ticket carries a store-assigned session_seq; a ticket is claimed only while no sibling in its session is running or paused and no earlier sibling is still queued, so waiting tickets spend no attempt and no worker slot. Different sessions stay concurrent within max_concurrency. A paused run holds its session, a continued or retried run keeps its place, and a PENDING run poll carries waiting_on with the run it is blocked behind. QueueConfig.queue_per_session (default on) controls it, external agents are serialized regardless, and GET /config reports the effective setting. See cookbook. (#9587)
  • Instant Durable Submissions: The replica that accepts a durable submission wakes its own queue worker after the ticket commits, so the run starts in milliseconds instead of waiting up to poll_interval for the next poll. A saturated replica wakes on slot release, and peers still pick up jobs on their own poll. Durability is untouched: the row is committed before anyone is woken. (#9667)
  • ChartTools: New toolkit with a single create_chart tool that validates a chart payload (bar, line, area, pie, bar-horizontal, bar-stacked, area-stacked) and returns a ready fenced chart block for Agno OS to render, replacing the schema prompt users had to carry. Validation failures tell the model what to fix, and the toolkit ships its own instructions, so tools=[ChartTools()] is all an agent needs. See cookbook. (#8295)

Improvements:

  • Durable Queue for Db-Backed and Factory Components: Background runs on components created through the components API, on version-pinned runs, and on AgentFactory / TeamFactory / WorkflowFactory submissions now ride the durable queue instead of dropping to a detached task on the accepting replica. The accepting endpoint stamps a resolution scope on the ticket (source, owner, version, and for factories the trusted claims and factory input), and the worker replays that resolution at claim time. A factory that reads ctx.request on the worker fails the run with a message naming the fix, since the live request cannot ride the ticket. The run read routes for factory runs no longer answer 400 on every poll. See cookbook. (#9796, #11045)
  • Codex Permission Policy Naming: CodexAgent(sandbox="read-only") is renamed sandbox_mode, matching Codex's own config key, since sandbox= now takes a provider. The old string form still works with a DeprecationWarning. (#11043)
  • Sandbox Cookbooks: cookbook/frameworks/sandbox/ is split into docker/ and agentcore/ folders, each with its own README and test log, covering the provider, checkpoints, the coordinator, recovery, cancellation, attachments, warm turns and AgentOS. build_image.py loads the built tag into the image store and verifies it is there. (#11050, #11081, #11105)
  • Gemini Cookbooks: Updated to the current models and API. (#11038)

Bug Fixes:

  • Sandbox Checkpoint Restore: Every second turn in a sandbox session failed with getent unable to find entry "1000:1000" in passwd database, because docker cp --archive asks the daemon to resolve the container's user as a name. Restore now copies without archive mode and pins every restored entry to 1000:1000 itself. (#11094)
  • Sandbox Store and Coordinator: A queue cancel racing a worker claim of the same run could deadlock on lock order; the stop path now takes the session, run and binding in the same order as every other store transaction. Sync calls through DockerSandbox leaked an HTTP client per call. Cancelled sandbox runs now record the EXECUTING cancellation stage the queue expects. A run marked finished between cleanup and commit goes through the stop path instead of leaving the binding in finalizing, and long provider calls extend their lease. (#11057, #11092)
  • Sandbox Submissions: Submitting with a user_id to a session created without one, or the reverse, answers 404 before any ticket exists instead of a 500 with a cancelled job left behind. A foreground request whose job failed before its run row existed returns the ticket's error as a 500 instead of a 503 asking the client to reconnect. (#11105)
  • WebSocket Tails: With a durable queue, a chat client that sent a second message while a workflow run was streaming over the WebSocket stopped getting events for the first run. Tail pumps are now keyed per (socket, run), a re-subscribe replaces only that run's pump, and a socket may attach up to 32 runs. (#11039)
  • API Docs Order: The FileSystem routes are registered after the Database routes on setup and resync, so the generated API docs list them in order. (#11099)
  • Tests: The WebSocket factory workflow tests pin the inline path they were written for, after factory submissions started riding the durable queue. (#11106)

What's Changed

Full Changelog: v3.1.3...v3.1.4

Don't miss a new agno release

NewReleases is sending notifications on new releases.