> ## Documentation Index
> Fetch the complete documentation index at: https://agno-v2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Agno v3.0 Changelog

> Full list of storage, parameter, and behavior changes introduced in Agno v3.0.

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

> **Denormalized session 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.

> **SQLite runs in WAL journal mode**
>
> * `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.

> **Built-in migrations and schema versioning**
>
> * `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

> **Insert API replaces add\_content**
>
> * `Knowledge.add_content` -> `insert()`
> * `Knowledge.add_content_async` -> `ainsert()`
> * `Knowledge.add_contents_async` -> `ainsert_many()`

> **Vector database changes**
>
> * `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

> **Per-user scoping across the platform**
>
> * `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

> **MCP server configuration**
>
> * `AgentOS(enable_mcp_server=..., mcp_config=...)` is replaced by a single
>   `mcp=` parameter, which takes a bool or an `MCPConfig`. The earlier
>   `mcp_server=` and `MCPServerConfig` spellings remain deprecated compatibility
>   aliases.

> **Pagination is validated**
>
> * Passing `page` without `limit`, or a `page` below 1, now raises a
>   `ValueError` instead of being silently ignored. Pages are 1-indexed.

> **Durable background execution**
>
> * Background runs are capped at 32 per replica by default; excess submissions
>   wait as `PENDING`. Configure with `QueueConfig(max_concurrency=...)` or
>   `AGNO_BACKGROUND_MAX_CONCURRENCY`.
> * `QueueConfig(durable=True)`: accepted requests are committed job rows that
>   survive crashes, restarts and deploys. Any replica's worker can claim and
>   execute them. Enables `Idempotency-Key` deduplication and the `/queue`
>   operations endpoints.
> * `QueueConfig(redis=...)`: live event streams and cross-replica cancellation
>   from one setting, so runs can be tailed, resumed and cancelled from any
>   replica. Redis is coordination, never truth. A Redis fault degrades the live
>   view; it cannot lose or corrupt a run.
> * External framework agents (LangGraph, Claude, DSPy, etc.) stream inline
>   when `background=true` is requested; their runs are not resumable.

> **JWT middleware**
>
> * `secret_key` removed from `JWTMiddleware` and `authorization_config`. Use
>   `verification_keys`, which takes a list of keys.

> **Metadata route consolidation**
>
> * `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

> **Removed parameters**
>
> * `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).

> **Culture feature removed**
>
> * The experimental culture feature is removed: `enable_agentic_culture`,
>   `add_culture_to_context`, `CulturalKnowledge`, the culture tools and the
>   `agno_culture` table.
> * Use [Knowledge](https://agno-v2.mintlify.site/knowledge/overview.md) for shared cross-user information.

## Teams & Workflows

> **Keyword-only Workflow constructor**
>
> * 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.

> **HITL configuration unified into HumanReview**
>
> * 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

> **Toolkit updates**
>
> * `MCPToolbox`: `auth_tokens` and `auth_headers` removed. Use
>   `auth_token_getters`.
> * Toolkits have an `id`, used by AgentOS to reference tools stably.

> **Workspace refuses credential files by default**
>
> * `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:
>
>   ```python theme={null}
>   Workspace(".", allow_paths=["config/credentials.json"])
>   ```
>
> * 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.

> **Removed and renamed toolkits**
>
> * `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`.

> **Removed toolkit methods**
>
> * `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

> **Schedule provenance columns**
>
> * 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.

> **Schedule names are unique per user**
>
> * 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 column allow-list**
>
> * `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

> **Result file placeholder renamed**
>
> * `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

> **Removed model APIs**
>
> * The `agno.models.metrics` module and its `Metrics` alias are removed. Use
>   `agno.metrics` and `RunMetrics`.
> * `Model.classify_error` -> `ModelProviderError.classify(error)`.

> **Mistral requires mistralai v2**
>
> * The `mistralai` v1 compatibility layer is removed. `agno[mistral]` now
>   requires `mistralai>=2.0.0`.
> * `agno[mistral]` is included in the `models` extra again.

> **Default model updates**
>
> * `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

> **Entity memory is isolated per user**
>
> * `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.

> **Studio authors learning, not legacy memory**
>
> * `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.

> **Removed learn aliases**
>
> * `MemoriesConfig` -> `UserMemoryConfig`
> * `MemoriesStore` -> `UserMemoryStore`
> * `Decision` -> `DecisionLog`

## Packaging

> **Postgres extra installs a working driver**
>
> * `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

> **Clearer schema mismatch 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.