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 adds opt-in durable AgentOS background execution.
The major changes are:
- Session runs are stored one row per run in a dedicated runs table.
user_idscoping extends to metrics, schedules, evals, knowledge and vector databases.- With
QueueConfig(durable=True)and a supported queue database, eligible AgentOS background requests are committed to a durable job queue. Durability defaults to off. - 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_runsby default) withrun_id,session_id,run_type,run_index,user_id,statusandrun_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.runsis 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)anddb.get_runs(session_id=..., user_id=..., status=..., limit=...). - The v2 -> v3 migration preserves the legacy
runscolumn as a backup. Reclaim it withdb.cleanup_legacy_runs_column(force=True)(SQL) ordb.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.
SqliteDbandAsyncSqliteDbissuePRAGMA journal_mode=WALon 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
-waland-shmsidecar files next to it. Use SQLite's backup API for a live database, or take a consistent copy after all users of the database are stopped. Independently copying live files can produce an inconsistent backup; see SQLite backup guidance.
await MigrationManager(db).up()(orasyncio.run(MigrationManager(db).up())outside an event loop) 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 stop further work. Each successfully completed table is stamped before the next one begins, so an error can leave earlier tables migrated. Inspect the failure and rerun after resolving it.
Knowledge
Knowledge.add_content->insert()Knowledge.add_content_async->ainsert()Knowledge.add_contents_async->ainsert_many()
LanceDb:use_tantivyis removed; passing it now raises aTypeError.- On schema-based stores, searching a pre-v3 vector table with a
user_idraises aValueErrordirecting 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_idcolumns added to the schedules, schedule-runs and evals tables. All user-facing read and write methods acceptuser_id.- Metrics aggregate per user. The unique key changed from
(date, aggregation_period)to(user_id, date, aggregation_period). Sessions without auser_idaggregate into a shared bucket thatget_metricsmaps back toNone. - 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_idraises aValueErrordirecting 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 singlemcp=parameter, which takes a bool or anMCPConfig. The earliermcp_server=andMCPServerConfigspellings remain deprecated compatibility aliases.
- Passing
pagewithoutlimit, or apagebelow 1, now raises aValueErrorinstead of being silently ignored. Pages are 1-indexed.
- Background runs are capped at 32 per replica by default; excess submissions
wait as
PENDING. Configure withQueueConfig(max_concurrency=...)orAGNO_BACKGROUND_MAX_CONCURRENCY. QueueConfig(durable=True)with a supported queue database: accepted eligible requests are committed job rows that survive crashes, restarts and deploys. Any replica's worker can claim and execute them. EnablesIdempotency-Keydeduplication and the/queueoperations 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.- For external framework agents (LangGraph, Claude, DSPy, etc.), the
background=trueplus streaming path executes inline and still requires an agent database. These runs do not gain durable resumption.
secret_keyremoved fromJWTMiddlewareandauthorization_config. Useverification_keys, which takes a list of keys.
GET /modelsremoved. Model data moved intoGET /configunderavailable_models.GET /returns a minimal landing response linking to/docs,/infoand/health.GET /infois the single unauthenticated metadata endpoint.
Agents
enable_user_memories->update_memory_on_runsearch_session_history->search_past_sessionsnum_history_sessions->num_past_sessions_to_searchnum_past_session_runs->num_past_session_runs_in_searchreasoning=Trueremoved. Setreasoning_model=<native reasoning model>explicitly.continue_run/acontinue_run:updated_toolsremoved. Passrequirements(a list ofRunRequirementfrom the paused run output).
- The experimental culture feature is removed:
enable_agentic_culture,add_culture_to_context,CulturalKnowledge, the culture tools and theagno_culturetable. - Use Knowledge for shared cross-user information.
Teams & Workflows
- The
Workflowconstructor no longer accepts positional arguments:Workflow(name=..., steps=[...]). Teamstill acceptsTeam([agent_1, agent_2]); the keyword formTeam(members=[...])is preferred but not required.
- Flat HITL kwargs on
Step,Steps,Loop,ConditionandRouterare 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 fromagno.workflow.types). Field names are unchanged excepthitl_max_retries->max_retriesandhitl_timeout->timeout.
Tools
MCPToolbox:auth_tokensandauth_headersremoved. Useauth_token_getters.- Toolkits have an
id, used by AgentOS to reference tools stably.
-
Workspacenow 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:
Workspace(".", allow_paths=["config/credentials.json"]) -
Committed templates go the other way and become readable (
.env.example,.env.sample,.env.template,.env.dist). -
credentials.*andsecrets.*are deliberately absent from the list: they would also refuse ordinary source such ascredentials.py. -
Known limit: a hard link to an excluded file bypasses the boundary. Symlinks are caught.
MultiMCPToolsis removed, along with itsallow_partial_failureparameter. Use oneMCPToolsper server.- The flat Google tool modules are removed:
agno.tools.gmail,agno.tools.googlesheets,agno.tools.googlecalendar,agno.tools.google_maps,agno.tools.google_driveandagno.tools.google_bigquery. Import fromagno.tools.google.*instead. - Google toolkits:
creds_path->credentials_path,auth_port->oauth_port. SeltzTools:max_documents->max_results. OlderseltzSDKs still work through a fallback;seltz>=1.2.0is needed for thescope, domain and date filters.BrandfetchTools: theasync_toolsparameter is removed.StudioTool->StudioTools.GDriveContextProvider->GoogleDriveContextProvider.
DuckDuckGoTools.duckduckgo_search->web_search, andDuckDuckGoTools.duckduckgo_news->search_news. The toolkit now builds onWebSearchTools, which supplies both methods.FileTools.check_escape->Toolkit._check_path.LocalFileSystemToolskeeps its owncheck_escape, which is unaffected.BrightDataTools.get_screenshot: the unusedoutput_pathparameter is removed.PgVector.enable_prefix_matchingis 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_idanddisabled_reason.managed_byandtarget_idare 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
NULLprovenance; 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_schedulecan only writename,description,method,endpoint,payload,cron_expr,timezone,timeout_seconds,max_retries,retry_delay_seconds,enabled,next_run_atanddisabled_reason. Any other key raises aValueErrornaming 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: theeval_idparameter is renamed torun_id.{eval_id}is no longer accepted infile_path_to_save_resultstemplates. Use{run_id}.POST /eval-runsnow returns the id the row was actually stored under (run_id), instead of the eval object'seval_id. Every eval run gets its ownrun_id.- The eval classes no longer carry
eval_id; results carry a per-runrun_id, so re-runs no longer overwrite each other's stored results.
Models
- The
agno.models.metricsmodule and itsMetricsalias are removed. Useagno.metricsandRunMetrics. Model.classify_error->ModelProviderError.classify(error).
- The
mistralaiv1 compatibility layer is removed.agno[mistral]now requiresmistralai>=2.0.0. agno[mistral]is included in themodelsextra again.
CerebrasandCerebrasOpenAIdefault togpt-oss-120b, replacingllama-4-scout-17b-16e-instruct.- OpenAI
reasoning_effort,reasoning_summary,service_tierandverbosityaccept the full set of API values (includingnone,xhigh,max,scale,fast,ultrafast) and any future string. This widens the accepted types; no existing call breaks.
Learning
EntityMemoryStorewithnamespace="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 theuser_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_learningsdirectly. The migration'sdown()refuses to reverse the re-key, since the old key collides users by design. EntityMemoryStore.delete/adeletetake a keyword-onlyuser_idand refusenamespace="user"deletes without it.get/agetnow require auser_idin that namespace instead of returning an arbitrary user's row.
enable_agentic_memoryandmemory_manager_idare removed from every Studio create/edit form (sync and async). Studio components declare memory throughLearningMachineinstead:learning_namebinds a registry-declared machine, orenable_learning=Truebuilds the default one.- The
AgentandTeamconstructor parameters are unchanged, as areRegistry.memory_managersandresolve_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) clearsenable_agentic_memoryandmemory_manager: both register a tool namedupdate_user_memory, and the legacy one silently shadowed the store's. Setting them toFalse/""leaves the legacy pair alone.
MemoriesConfig->UserMemoryConfigMemoriesStore->UserMemoryStoreDecision->DecisionLog
Packaging
agno[postgres]installedpsycopg-binaryonly, which ships the C accelerator but no importablepsycopgand no engine layer, soPostgresDbraisedModuleNotFoundError: No module named 'sqlalchemy'on a clean install. The extra now installspsycopg,psycopg-binaryandsqlalchemy.- If you worked around this by installing
psycopgorsqlalchemyyourself, you can drop those pins.
Errors
- SQL adapters raised
Table <name> has an invalid schemawith 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())orPOST /databases/all/migrateon AgentOS.