diff --git a/docs.json b/docs.json
index 5a3d4dde0..54821448f 100644
--- a/docs.json
+++ b/docs.json
@@ -4642,6 +4642,13 @@
{
"group": "Migrations",
"pages": [
+ {
+ "group": "Agno v3 Migration",
+ "pages": [
+ "other/v3-migration",
+ "other/v3-changelog"
+ ]
+ },
{
"group": "Agno v2 Migration",
"pages": [
diff --git a/other/v3-changelog.mdx b/other/v3-changelog.mdx
new file mode 100644
index 000000000..9460b35bd
--- /dev/null
+++ b/other/v3-changelog.mdx
@@ -0,0 +1,152 @@
+---
+title: Agno v3.0 Changelog
+sidebarTitle: Agno v3.0 Changelog
+description: "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
+
+
+ - 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()` (SQL) or
+ `db.cleanup_legacy_runs_field()` (document/KV adapters) after verifying the
+ migration.
+
+
+
+ - `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.
+
+
+## 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. 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.
+ - 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
+
+
+ - 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=`
+ explicitly.
+ - `continue_run` / `acontinue_run`: `updated_tools` removed. Pass
+ `requirements` (a list of `RunRequirement` from the paused run output).
+
+
+
+ - `agent.run()` executes async tools automatically. The v2 guard that raised
+ and required `arun()` is 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](/knowledge/overview) for shared cross-user information.
+
+
+## Teams & Workflows
+
+
+ - `Team` and `Workflow` constructors no longer accept positional arguments:
+ `Team(members=[...])`, `Workflow(name=..., steps=[...])`.
+
+
+
+ - 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 constructor parameters drop the `enable_` prefix
+ (e.g. `SlackTools(send_message=True)`). The old names still work and log a
+ deprecation warning.
+ - `MCPToolbox`: `auth_tokens` and `auth_headers` removed. Use
+ `auth_token_getters`.
+ - Toolkits have an `id`, used by AgentOS to reference tools stably.
+
diff --git a/other/v3-migration.mdx b/other/v3-migration.mdx
new file mode 100644
index 000000000..37910b489
--- /dev/null
+++ b/other/v3-migration.mdx
@@ -0,0 +1,397 @@
+---
+title: Migrating to Agno v3.0
+sidebarTitle: Agno v3 Migration
+description: Guide to migrate your Agno applications from v2 to v3.
+---
+
+If you have questions during your migration, we can help! See [Get Help](/get-help) for more information.
+
+
+ Reference the [v3.0 Changelog](/other/v3-changelog) for the full list of
+ changes.
+
+
+
+ Want to migrate automatically? Jump to [Migrate with AI](#migrate-with-ai) for
+ a prompt you can paste into Claude, Cursor or any coding agent.
+
+
+## Installing Agno v3
+
+If you are already using Agno, you can upgrade to v3 by running:
+
+```bash
+pip install -U agno
+```
+
+## Migrating your Agno DB
+
+The built-in migration makes two schema changes:
+
+1. **Session runs move to their own table.** In v2, every session row held its
+ full run history as a single JSON blob in the `runs` column. In v3, each run
+ is its own row in a dedicated runs table (`agno_runs` by default), which
+ removes the write amplification and unbounded row growth of the blob design.
+2. **A `user_id` column (with index) is added** to the evals, components,
+ knowledge, schedules, schedule-runs and metrics tables, for
+ [user isolation](#6-user-isolation-user-id-across-the-platform). The metrics
+ unique key changes from `(date, aggregation_period)` to include `user_id`.
+
+One command applies both:
+
+```python migrate_to_v3.py
+import asyncio
+
+from agno.db.postgres import PostgresDb # or SqliteDb, MongoDb, RedisDb, ...
+from agno.db.migrations.manager import MigrationManager
+
+db = PostgresDb(db_url="postgresql+psycopg://...")
+
+# Step 1: run all v3 migrations (runs table + user_id columns)
+asyncio.run(MigrationManager(db).up())
+
+# Step 2: VERIFY the runs actually landed before any cleanup
+runs = db.get_runs(limit=5)
+assert len(runs) > 0, "Migration copied nothing - do NOT run cleanup"
+
+# Step 3 (optional, after verifying): reclaim the legacy blob storage
+db.cleanup_legacy_runs_column() # SQL adapters
+# db.cleanup_legacy_runs_field() # Mongo / Redis / Valkey / Firestore / Dynamo / JSON adapters
+```
+
+Vector databases are migrated separately. If you use per-user knowledge with a
+vector table created before v3, run the matching script from
+[`libs/agno/migrations/v2_to_v3`](https://github.com/agno-agi/agno/tree/main/libs/agno/migrations/v2_to_v3)
+(`migrate_sql_vectordbs.py`, `migrate_field_vectordbs.py` or
+`migrate_sentinel_vectordbs.py`, depending on your vector store) to add
+`user_id` scoping to existing collections. Un-migrated tables raise a
+`ValueError` on user-scoped searches instead of returning empty results.
+
+Notes:
+
+- The migration is **non-destructive and idempotent**: the legacy `runs` column
+ is preserved as a backup, and re-running the migration never duplicates runs.
+- Reads keep working before, during and after the migration. Sessions merge the
+ runs table with any legacy blob, so an un-migrated session still shows its
+ history.
+- `cleanup_legacy_runs_column()` refuses to run while legacy data is present
+ unless you pass `force=True`. **Only pass `force=True` after Step 2 passes.**
+ Cleanup permanently deletes the blob, which is the only copy of your history
+ if the migration did not actually copy it.
+- Supported everywhere sessions are stored: Postgres, MySQL, SQLite,
+ SingleStore (+ async variants), MongoDB, Redis, Valkey, Firestore, DynamoDB,
+ SurrealDB, JSON, and GCS JSON.
+
+For the full storage design and per-adapter details, see the
+[v3 storage migration guide](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/db/migrations/V3_MIGRATION_GUIDE.md)
+in the repository.
+
+## Migrating your Agno code
+
+Each section covers one breaking change, with before and after examples.
+
+### 1. Sessions and runs (denormalization)
+
+Reading sessions is unchanged. `session.runs` is still populated, now from the
+runs table:
+
+```python v3_sessions.py
+session = agent.get_session(session_id="s1")
+session.runs # still works, loaded from the runs table
+
+# New: fetch runs directly, without loading the whole session
+runs = db.get_runs(session_id="s1")
+run = db.get_run(run_id="...")
+```
+
+If you queried the `runs` column of the sessions table directly (SQL, dashboards,
+exports), point those queries at the runs table instead. After cleanup the
+column no longer exists:
+
+```sql
+SELECT run_id, run_data FROM agno_runs WHERE session_id = 's1' ORDER BY run_index;
+```
+
+### 2. Workflow HITL: flat kwargs → `HumanReview`
+
+Workflow primitives no longer accept flat HITL kwargs. All human-in-the-loop
+configuration lives in one `HumanReview` object.
+
+This is how it looked in v2:
+
+```python v2_hitl.py
+from agno.workflow.step import Step
+
+step = Step(
+ name="deploy",
+ executor=deploy,
+ requires_confirmation=True,
+ confirmation_message="Deploy to production?",
+)
+```
+
+This is how it looks in v3:
+
+```python v3_hitl.py
+from agno.workflow.step import Step
+from agno.workflow.types import HumanReview
+
+step = Step(
+ name="deploy",
+ executor=deploy,
+ human_review=HumanReview(
+ requires_confirmation=True,
+ confirmation_message="Deploy to production?",
+ ),
+)
+```
+
+Field mapping: every flat kwarg keeps its name inside `HumanReview`, except
+`hitl_max_retries` → `max_retries` and `hitl_timeout` → `timeout`. This applies
+to `Step`, `Steps`, `Loop`, `Condition` and `Router`.
+
+### 3. Removed and renamed parameters
+
+These deprecated parameters have been removed. Update them to their v3 names:
+
+**`Agent` and `Team` constructors:**
+
+| v2 (removed) | v3 |
+|---|---|
+| `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` |
+
+```python v3_agent_params.py
+agent = Agent(
+ update_memory_on_run=True,
+ search_past_sessions=True,
+ num_past_sessions_to_search=3,
+)
+```
+
+**`continue_run` / `acontinue_run`:** the `updated_tools` parameter is removed.
+Pass `requirements` (a list of `RunRequirement`, available on the paused run
+output) instead of a modified `ToolExecution` list:
+
+```python v3_continue_run.py
+run = agent.run("...") # pauses for confirmation
+for requirement in run.requirements:
+ requirement.confirm()
+agent.continue_run(run_id=run.run_id, requirements=run.requirements)
+```
+
+**JWT middleware and `authorization_config`:** `secret_key` is removed. Use
+`verification_keys`, which takes a list:
+
+```python v3_jwt.py
+JWTMiddleware(verification_keys=["your-key"]) # was: secret_key="your-key"
+```
+
+**`MCPToolbox`:** `auth_tokens` and `auth_headers` are removed. Use
+`auth_token_getters` (same shape: a mapping of auth source names to token
+callables).
+
+### 4. Reasoning requires an explicit model
+
+The `reasoning=True` shortcut has been removed. Pass a native reasoning model
+explicitly:
+
+```python v2_reasoning.py
+agent = Agent(model=OpenAIResponses(id="gpt-5.5"), reasoning=True)
+```
+
+```python v3_reasoning.py
+agent = Agent(
+ model=OpenAIResponses(id="gpt-5.5"),
+ reasoning_model=OpenAIResponses(id="gpt-5.5"),
+)
+```
+
+### 5. `Team` and `Workflow` constructors are keyword-only
+
+Positional arguments are no longer accepted:
+
+```python v2_team.py
+team = Team([agent_1, agent_2])
+workflow = Workflow("my-workflow", steps=[...])
+```
+
+```python v3_team.py
+team = Team(members=[agent_1, agent_2])
+workflow = Workflow(name="my-workflow", steps=[...])
+```
+
+### 6. User isolation: `user_id` across the platform
+
+With `user_isolation` enabled on AgentOS, data is now scoped per user across
+**memories, knowledge, evals, metrics, schedules and vector databases**, in
+addition to sessions. What this means for your code and data:
+
+- `user_id` columns were added to the schedules, schedule-runs and evals tables;
+ the built-in migration handles this.
+- Metrics aggregate **per user**: the unique key changed from
+ `(date, aggregation_period)` to `(user_id, date, aggregation_period)`.
+ Deployments without isolation see the same single-row-per-date shape as
+ before; sessions without a `user_id` aggregate into a shared bucket.
+- Vector database collections created before v3 have no per-user scoping. When
+ isolation is on, searching them with a `user_id` raises a `ValueError` telling
+ you to run the vector database migration. This is deliberate: an un-migrated
+ table fails loudly instead of silently returning empty results.
+
+### 7. Background execution and durable queues
+
+`background=True` on AgentOS is rebuilt around a durable job queue. In v2 it
+spawned an unbounded `asyncio.create_task`, and a process death silently lost
+every waiting and in-flight run. In v3:
+
+- Accepted requests are **committed rows** that survive crashes, restarts and
+ deploys; any replica's worker can execute them.
+- Runs are **bounded** by a concurrency cap; excess submissions wait in the
+ queue in `pending` status instead of overloading the process.
+- Every run can be watched (`stream=true` tails), 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.
+
+Breaking implications: background execution requires a `db` on the agent
+(enforced with a 400), run status now transitions `pending → running →
+completed` (poll `GET /agents/{id}/runs/{run_id}` for the terminal state), and
+external framework agents (LangGraph, Claude, etc.) stream inline, so their
+runs are not resumable.
+
+### 8. Culture feature removed
+
+The experimental culture feature (`enable_agentic_culture`,
+`add_culture_to_context`, `CulturalKnowledge`, the `agno_culture` table) has
+been removed. Remove any references; if you need shared knowledge across users,
+use [Knowledge](/knowledge/overview) instead.
+
+### 9. Smaller changes
+
+- **Async tools run in sync runs**: v2's `agent.run()` raised when the agent
+ had async tools, forcing `arun()`. v3 executes them automatically; the guard
+ and its error are gone.
+- **Toolkit parameters**: `enable_*` prefixes are dropped
+ (e.g. `SlackTools(enable_send_message=True)` → `SlackTools(send_message=True)`).
+ v2 names still work with a deprecation warning.
+- **AgentOS metadata routes**: `GET /models` was removed (its data moved into
+ `GET /config` under `available_models`), and `GET /` is now a minimal landing
+ response. `GET /info` is the single unauthenticated metadata endpoint.
+- **Toolkits have an `id`**, used by AgentOS to reference tools stably.
+
+## Migrate with a Coding Agent
+
+Paste the prompt below into Claude, Cursor, or any coding agent with access to
+your repository. It applies the mechanical changes and flags everything that
+needs your judgment.
+
+````markdown Copy this prompt expandable
+You are migrating a codebase from Agno v2 to Agno v3. Apply the following
+changes carefully. Make the mechanical edits directly; for anything marked
+JUDGMENT, report it to me instead of guessing.
+
+## 1. Renamed parameters (mechanical)
+
+Rename these constructor parameters wherever Agent(...) or Team(...) is called:
+- 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
+
+Rename these too, wherever they appear:
+- JWTMiddleware / authorization_config: secret_key="k" -> verification_keys=["k"]
+ (note the list wrapping)
+- MCPToolbox: auth_tokens= or auth_headers= -> auth_token_getters= (same value)
+
+## 1b. continue_run updated_tools (JUDGMENT)
+
+Agent/Team continue_run and acontinue_run no longer accept updated_tools
+(List[ToolExecution]). The v3 path is requirements=. This is a structural change to HITL continue code, not
+a rename: find every call site passing updated_tools and report it.
+
+## 2. Workflow HITL config (mechanical)
+
+Step, Steps, Loop, Condition and Router no longer accept flat HITL kwargs.
+Collect any of these kwargs from their constructors:
+ 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
+and move them into a single human_review=HumanReview(...) argument
+(import: from agno.workflow.types import HumanReview).
+Rename while moving: hitl_max_retries -> max_retries, hitl_timeout -> timeout.
+All other names are unchanged inside HumanReview.
+
+## 3. Reasoning (JUDGMENT)
+
+Agent(reasoning=True) no longer exists. Comment the argument out with a
+`# TODO(agno-v3):` marker so the file stays importable, and report every
+occurrence: the fix is to set reasoning_model=, and I need to choose which model.
+
+## 4. Keyword-only constructors (mechanical)
+
+Team and Workflow constructors are keyword-only. Convert positional arguments:
+ Team([a, b]) -> Team(members=[a, b])
+ Workflow("name", ...) -> Workflow(name="name", ...)
+
+## 5. Culture feature (JUDGMENT)
+
+The culture feature was removed. Find any use of: enable_agentic_culture,
+add_culture_to_context, CulturalKnowledge, update_cultural_knowledge, or
+imports from agno.culture. Comment constructor arguments out with a
+`# TODO(agno-v3):` marker so files stay importable; leave other usages in
+place. Report every occurrence.
+
+## 6. Toolkit parameters (mechanical, optional)
+
+Toolkit constructor params dropped their enable_ prefix (old names still work
+but warn). Where obvious, rename e.g. enable_send_message -> send_message.
+
+## 7. Direct SQL against sessions (JUDGMENT)
+
+Search for SQL, dashboard queries or exports reading the `runs` column of the
+agno_sessions table. In v3 runs live in the agno_runs table
+(run_id, session_id, run_type, run_index, run_data, ...). Report every hit.
+
+## 8. AgentOS API consumers (JUDGMENT)
+
+If this codebase calls the AgentOS HTTP API: GET /models was removed (use
+GET /config -> available_models), and GET / returns a minimal landing payload.
+Report any client code using those routes.
+
+## 9. Database migration (do NOT automate the destructive step)
+
+Write (but do not execute) a migration script for me with exactly this shape:
+
+ import asyncio
+ from agno.db.migrations.manager import MigrationManager
+ # build db exactly as the app does
+ asyncio.run(MigrationManager(db).up())
+ runs = db.get_runs(limit=5)
+ assert len(runs) > 0, "Migration copied nothing - do NOT run cleanup"
+ print("Migration verified. Run db.cleanup_legacy_runs_column() manually "
+ "once you have confirmed history is intact in the UI.")
+
+Never call cleanup_legacy_runs_column / cleanup_legacy_runs_field yourself,
+and never pass force=True on my behalf: cleanup permanently deletes the legacy
+run history, and must only happen after the verification assert passes AND I
+have confirmed the migrated history looks right.
+
+## Output
+
+When done: list every file you changed with a one-line summary, then a
+JUDGMENT section listing every finding from steps 3, 5, 7 and 8 that needs my
+decision. If the repo pins agno in requirements/pyproject, update it to >=3.0.
+````
+
+
+ The prompt deliberately refuses to run the destructive cleanup step. Keep it
+ that way: verify your migrated history in the AgentOS UI before reclaiming
+ the legacy storage.
+