Skip to main content
This release rebuilds the storage layer around a normalized runs table, extends per-user isolation across the platform, and makes AgentOS background execution durable. The major changes are:
  • Session runs are stored one row per run in a dedicated runs table.
  • user_id scoping extends to metrics, schedules, evals, knowledge and vector databases.
  • background=True on AgentOS is backed by a durable job queue that survives crashes and deploys.
  • Database migrations run through the built-in MigrationManager, with schema versions tracked on every adapter.

Storage

  • Runs are no longer stored as a JSON blob in the sessions table. Each run is a row in the runs table (agno_runs by default) with run_id, session_id, run_type, run_index, user_id, status and run_data.
  • Saving a run writes one row instead of rewriting the whole session history. This removes the quadratic write amplification and unbounded row growth of the blob design.
  • session.runs is still populated on read: sessions merge the runs table with any legacy blob, so un-migrated sessions keep working.
  • New direct accessors: db.get_run(run_id) and db.get_runs(session_id=..., user_id=..., status=..., limit=...).
  • The v2 -> v3 migration preserves the legacy runs column as a backup. Reclaim it with db.cleanup_legacy_runs_column(force=True) (SQL) or db.cleanup_legacy_runs_field(force=True) (document/KV adapters) after verifying the migration — the migration keeps every legacy blob as a backup, so the unforced call refuses by design.
  • SqliteDb and AsyncSqliteDb issue PRAGMA journal_mode=WAL on connect, replacing SQLite’s default DELETE journal (a journal create, double fsync and delete on every commit).
  • WAL is persistent on the database file and produces -wal and -shm sidecar files next to it. Copy or back up all three together.
  • MigrationManager(db).up() walks all registered migrations for every table and stamps the resulting schema version.
  • Schema versions are tracked on every adapter, including the document and key-value stores (MongoDB, Redis, Valkey, Firestore, DynamoDB, SurrealDB, JSON, GCS JSON, in-memory). An unstamped database is treated as pre-v3 and migrated.
  • Migrations are idempotent and non-destructive. Failures raise and abort before any version stamp is written.

Knowledge

  • Knowledge.add_content -> insert()
  • Knowledge.add_content_async -> ainsert()
  • Knowledge.add_contents_async -> ainsert_many()
  • LanceDb: use_tantivy is removed; passing it now raises a TypeError.
  • On schema-based stores, searching a pre-v3 vector table with a user_id raises a ValueError directing you to the vector database migration, instead of silently returning empty results. Schemaless stores (Qdrant, Pinecone, Upstash, Chroma, MongoDB, OpenSearch, SurrealDB) treat pre-v3 documents as shared.

User Isolation

  • user_id columns added to the schedules, schedule-runs and evals tables. All user-facing read and write methods accept user_id.
  • Metrics aggregate per user. The unique key changed from (date, aggregation_period) to (user_id, date, aggregation_period). Sessions without a user_id aggregate into a shared bucket that get_metrics maps back to None.
  • Knowledge and vector database contents are scoped per user when isolation is enabled. On schema-based stores, searching a pre-v3 vector table with a user_id raises a ValueError directing you to the vector database migration; schemaless stores treat pre-v3 documents as shared.
  • Schedule polling (claim_due_schedule / release_schedule) stays unscoped so background execution fires across all users; each schedule run records the owner denormalized from its parent schedule.

AgentOS

  • AgentOS(enable_mcp_server=..., mcp_config=...) is replaced by a single mcp_server= parameter, which takes a bool or an MCPServerConfig.
  • Passing page without limit, or a page below 1, now raises a ValueError instead of being silently ignored. Pages are 1-indexed.
  • Accepted background=True requests are committed job rows that survive crashes, restarts and deploys. Any replica’s worker can claim and execute them.
  • Concurrency is bounded. Excess submissions wait in pending status instead of overloading the process.
  • Runs can be tailed (stream=true), resumed after a disconnect (/resume) and cancelled from any replica.
  • Idempotency-Key headers deduplicate resubmissions.
  • Redis is optional coordination (live event streams, cross-replica cancellation), never truth. A Redis fault degrades the live view; it cannot lose or corrupt a run.
  • Background execution requires a db on the component and returns a 400 without one.
  • External framework agents (LangGraph, Claude, DSPy, etc.) stream inline when background=true is requested; their runs are not resumable.
  • secret_key removed from JWTMiddleware and authorization_config. Use verification_keys, which takes a list of keys.
  • GET /models removed. Model data moved into GET /config under available_models.
  • GET / returns a minimal landing response linking to /docs, /info and /health.
  • GET /info is the single unauthenticated metadata endpoint.

Agents

  • enable_user_memories -> update_memory_on_run
  • search_session_history -> search_past_sessions
  • num_history_sessions -> num_past_sessions_to_search
  • num_past_session_runs -> num_past_session_runs_in_search
  • reasoning=True removed. Set reasoning_model=<native reasoning model> explicitly.
  • continue_run / acontinue_run: updated_tools removed. Pass requirements (a list of RunRequirement from the paused run output).
  • The experimental culture feature is removed: enable_agentic_culture, add_culture_to_context, CulturalKnowledge, the culture tools and the agno_culture table.
  • Use Knowledge for shared cross-user information.

Teams & Workflows

  • The Workflow constructor no longer accepts positional arguments: Workflow(name=..., steps=[...]).
  • Team still accepts Team([agent_1, agent_2]); the keyword form Team(members=[...]) is preferred but not required.
  • Flat HITL kwargs on Step, Steps, Loop, Condition and Router are removed: requires_confirmation, confirmation_message, on_reject, requires_user_input, user_input_message, user_input_schema, requires_output_review, output_review_message, requires_iteration_review, iteration_review_message, on_error, hitl_max_retries, hitl_timeout, on_timeout.
  • Pass human_review=HumanReview(...) instead (import from agno.workflow.types). Field names are unchanged except hitl_max_retries -> max_retries and hitl_timeout -> timeout.

Tools

  • MCPToolbox: auth_tokens and auth_headers removed. Use auth_token_getters.
  • Toolkits have an id, used by AgentOS to reference tools stably.
  • Workspace now excludes env files (.env*, *.env) and conventional credential files — private keys and keystores (*.pem, *.key, id_rsa*), credential directories (.ssh, .aws, .kube), registry and host tokens (.npmrc, .netrc, .git-credentials), credential data files (credentials.json, secrets.yaml, service_account*.json) and Terraform inputs (*.tfvars).
  • An agent that reads one of these today starts getting a refusal. Re-allow a specific path explicitly:
  • Committed templates go the other way and become readable (.env.example, .env.sample, .env.template, .env.dist).
  • credentials.* and secrets.* are deliberately absent from the list: they would also refuse ordinary source such as credentials.py.
  • Known limit: a hard link to an excluded file bypasses the boundary. Symlinks are caught.
  • MultiMCPTools is removed, along with its allow_partial_failure parameter. Use one MCPTools per server.
  • The flat Google tool modules are removed: agno.tools.gmail, agno.tools.googlesheets, agno.tools.googlecalendar, agno.tools.google_maps, agno.tools.google_drive and agno.tools.google_bigquery. Import from agno.tools.google.* instead.
  • Google toolkits: creds_path -> credentials_path, auth_port -> oauth_port.
  • SeltzTools: max_documents -> max_results. Older seltz SDKs still work through a fallback; seltz>=1.2.0 is needed for the scope, domain and date filters.
  • BrandfetchTools: the async_tools parameter is removed.
  • StudioTool -> StudioTools.
  • GDriveContextProvider -> GoogleDriveContextProvider.
  • DuckDuckGoTools.duckduckgo_search -> web_search, and DuckDuckGoTools.duckduckgo_news -> search_news. The toolkit now builds on WebSearchTools, which supplies both methods.
  • FileTools.check_escape -> Toolkit._check_path. LocalFileSystemTools keeps its own check_escape, which is unaffected.
  • BrightDataTools.get_screenshot: the unused output_path parameter is removed.
  • PgVector.enable_prefix_matching is removed. It was a dead helper with no effect on search.

Scheduler

  • The schedules table gains eight nullable columns recording where a schedule came from and who last touched it: managed_by, target_type, target_id, created_by_run_id, created_by_session_id, updated_by_run_id, updated_by_session_id and disabled_reason. managed_by and target_id are indexed.
  • The v3.0.0 migration adds the columns and indexes on SQLite and PostgreSQL (sync and async). Existing rows are left as-is with NULL provenance; no data is rewritten. MongoDB needs no schema change.
  • The schedules unique key becomes (user_id, name).
  • If duplicate schedule names already exist, the v3.0.0 migration aborts rather than stamping itself as done. Resolve the duplicates and re-run.
  • update_schedule can only write name, description, method, endpoint, payload, cron_expr, timezone, timeout_seconds, max_retries, retry_delay_seconds, enabled, next_run_at and disabled_reason. Any other key raises a ValueError naming the rejected columns.
  • Ownership, provenance and lock state are no longer writable through the generic update path, so a name-keyed upsert cannot repoint who a schedule belongs to or what it targets.

Evals

  • store_result_in_file: the eval_id parameter is renamed to run_id.
  • {eval_id} is no longer accepted in file_path_to_save_results templates. Use {run_id}.
  • POST /eval-runs now returns the id the row was actually stored under (run_id), instead of the eval object’s eval_id. Every eval run gets its own run_id.
  • The eval classes no longer carry eval_id; results carry a per-run run_id, so re-runs no longer overwrite each other’s stored results.

Models

  • The agno.models.metrics module and its Metrics alias are removed. Use agno.metrics and RunMetrics.
  • Model.classify_error -> ModelProviderError.classify(error).
  • The mistralai v1 compatibility layer is removed. agno[mistral] now requires mistralai>=2.0.0.
  • agno[mistral] is included in the models extra again.
  • Cerebras and CerebrasOpenAI default to gpt-oss-120b, replacing llama-4-scout-17b-16e-instruct.
  • OpenAI reasoning_effort, reasoning_summary, service_tier and verbosity accept the full set of API values (including none, xhigh, max, scale, fast, ultrafast) and any future string. This widens the accepted types; no existing call breaks.

Learning

  • EntityMemoryStore with namespace="user" did not isolate users: the row key carried no user component, so two users recording the same entity name and type shared one row. One user’s facts overwrote the other’s and leaked into their prompt context.
  • Row keys under namespace="user" now embed a digest of the user_id. Global and custom namespaces are unchanged and do not re-key.
  • Pre-v3 rows are re-keyed by the v3.0.0 migration, not at runtime. Run it with the rest of your migrations, or call agno.learn.migrations.rekey_user_entity_learnings directly. The migration’s down() refuses to reverse the re-key, since the old key collides users by design.
  • EntityMemoryStore.delete / adelete take a keyword-only user_id and refuse namespace="user" deletes without it. get / aget now require a user_id in that namespace instead of returning an arbitrary user’s row.
  • enable_agentic_memory and memory_manager_id are removed from every Studio create/edit form (sync and async). Studio components declare memory through LearningMachine instead: learning_name binds a registry-declared machine, or enable_learning=True builds the default one.
  • The Agent and Team constructor parameters are unchanged, as are Registry.memory_managers and resolve_memory_manager_reference, so configs stored with the legacy fields keep rehydrating. Only the Studio authoring surface dropped them.
  • Enabling learning on a component (a learning machine actually configured via learning_name / enable_learning) clears enable_agentic_memory and memory_manager: both register a tool named update_user_memory, and the legacy one silently shadowed the store’s. Setting them to False/"" leaves the legacy pair alone.
  • MemoriesConfig -> UserMemoryConfig
  • MemoriesStore -> UserMemoryStore
  • Decision -> DecisionLog

Packaging

  • agno[postgres] installed psycopg-binary only, which ships the C accelerator but no importable psycopg and no engine layer, so PostgresDb raised ModuleNotFoundError: No module named 'sqlalchemy' on a clean install. The extra now installs psycopg, psycopg-binary and sqlalchemy.
  • If you worked around this by installing psycopg or sqlalchemy yourself, you can drop those pins.

Errors

  • SQL adapters raised Table <name> has an invalid schema with no next step. The error now names the likely cause (a database created by an older Agno version) and points at both fixes: asyncio.run(MigrationManager(db).up()) or POST /databases/all/migrate on AgentOS.