docs/agents/AGENTS.plugins.md
This guide covers the Python Backend and Frontend WebUI plugin architecture. Use this as the definitive reference for building and extending Agent Zero.
Agent Zero uses a convention-over-configuration plugin model where runtime capabilities are discovered from the directory structure.
extensions/python/<point>/ and implicit @extensible hooks from extensions/python/_functions/<module>/<qualname>/<start|end>/.Each plugin lives in usr/plugins/<plugin_name>/.
usr/plugins/<plugin_name>/
├── plugin.yaml # Required: Title, version, settings + activation metadata
├── execute.py # Optional: user-triggered plugin script
├── hooks.py # Optional: runtime hook functions callable by the framework
├── default_config.yaml # Optional: fallback settings defaults
├── README.md # Optional locally; strongly recommended for community plugins (Plugin Hub)
├── LICENSE # Optional locally (shown in Plugin List UI when present); required at repo root for Plugin Index submission
├── conf/
│ └── model_providers.yaml # Optional: add or override model providers
├── api/ # API handlers (ApiHandler subclasses)
├── tools/ # Agent tools (Tool subclasses)
├── helpers/ # Shared Python logic
├── prompts/ # Prompt templates
├── agents/ # Agent profiles (agents/<profile>/agent.yaml)
├── extensions/
│ ├── python/<point>/ # Named backend lifecycle hooks
│ ├── python/_functions/<module>/<qualname>/<start|end>/ # Implicit @extensible hooks
│ └── webui/<point>/ # UI HTML/JS contributions
└── webui/
├── config.html # Optional: Plugin settings UI
└── ... # Full plugin pages/components
Use one of these backend extension layouts, depending on what you are extending:
extensions/python/<extension_point>/ for named lifecycle hooks such as system_prompt, monologue_start, or tool_execute_beforeextensions/python/_functions/<module>/<qualname>/<start|end>/ for implicit @extensible call sitesThe _functions layout preserves every module segment and every nested __qualname__ segment. Do not use the retired flattened form extensions/python/<module>_<qualname>_<start|end>/; those folder names no longer match the runtime lookup logic.
For plugin-local Python code in usr/plugins/<plugin_name>/, import through the
fully qualified usr.plugins.<plugin_name>... package path.
Good (DO):
from usr.plugins.my_plugin.helpers.runtime import do_work
import usr.plugins.my_plugin.helpers.state as state
Avoid (DON'T):
# sys.path hacks
sys.path.insert(0, ...)
from helpers.runtime import do_work
# persistent symlink-based imports
from plugins.my_plugin.helpers.runtime import do_work
Why:
usr.plugins... works without renaming helpers/sys.path mutation for plugin-local imports/a0/plugins/This is the manifest file that lives inside your plugin directory and drives runtime behavior. It is distinct from the index manifest (index.yaml) used when publishing to the Plugin Index (see Section 7).
name: my_plugin # required for community plugins (^[a-z0-9_]+$, must match dir name)
title: My Plugin
description: What this plugin does.
version: 1.0.0
settings_sections:
- agent
per_project_config: false
per_agent_config: false
# Optional: lock plugin permanently ON in UI/back-end
always_enabled: false
Field reference:
name: Plugin identifier. Required by CI when submitting to the Plugin Index. Must be ^[a-z0-9_]+$ and match the index folder name exactly.title: UI display namedescription: Short plugin summaryversion: Plugin version stringsettings_sections: Which Settings tabs show a subsection for this plugin. Valid values: agent, external, mcp, developer, backup. Use [] for no subsection.per_project_config: Enables project-scoped settings and toggle rulesper_agent_config: Enables agent-profile-scoped settings and toggle rulesalways_enabled: Forces ON and disables toggle controls in the UI (reserved for framework use)Plugins can include an optional execute.py file at the plugin root for user-triggered work such as setup, post-install steps, maintenance, migrations, repair flows, or resource refreshes. It is started manually from the Plugins UI, never automatically, and should print progress while returning 0 on success.
Design guidance:
execute.py for manual operations the user may need to run again laterhooks.py or lifecycle extensionsPlugins can include an optional hooks.py file at the plugin root. Agent Zero loads this module on demand and calls exported functions by name through helpers.plugins.call_plugin_hook(...).
hooks.py runs inside the Agent Zero framework runtime and Python environment, not the separate agent execution environment.Current built-in usage:
install() from hooks.py after a plugin is copied into placepre_update() from hooks.py immediately before pulling new plugin code into placehooks.py installs Python packages with sys.executable -m pip, those packages are installed into the same Python environment that runs Agent Zero itself.If your plugin needs to install packages or binaries for the agent execution environment instead of the framework runtime, launch a subprocess that explicitly activates or targets that other environment first. In practice this means invoking the correct interpreter or shell for that environment rather than relying on the current process environment. For example:
pipIn Docker deployments, this distinction is especially important:
/opt/venv-a0/opt/venvSo a hooks.py install step affects /opt/venv-a0 unless you intentionally switch to /opt/venv (or another target) inside your subprocess.
Core UI defines insertion points like <x-extension id="sidebar-quick-actions-main-start"></x-extension>. To contribute:
Place *.js files in extensions/webui/<extension_point>/ and export a default async function. They are called via callJsExtensions("<point>", context).
Core JS hooks can also expose runtime UI surfaces when static HTML breakpoints are not a fit. For example, confirm_dialog_after_render runs after the shared confirm dialog is built and receives the rendered dialog/body/footer nodes plus any caller-provided extensionContext.
For tool chat rows (type === "tool"), after built-in badge rules, core calls get_tool_message_handler with a mutable object containing tool_name, kvps, and handler. Plugins can set handler to entirely take over rendering for their _tool_name. A plugin can import drawMessageToolSimple and call it internally (e.g., passing { ...args, code: "WWW" }) if it just wants standard tool row styling with a custom badge.
Plugin UI must use the A0 notification system for errors, success, and warnings. Do not render dedicated error/success boxes (e.g. a red block bound to store.error). Use the notification store so toasts and notification history stay consistent across the app.
toastFrontendError, toastFrontendSuccess, toastFrontendWarning, toastFrontendInfo from /components/notifications/notification-store.js, or call $store.notificationStore.frontendError(message, title) etc.AgentNotification.error(...), AgentNotification.success(...) from helpers.notification.See Notifications for the full API.
Plugins can add or override model providers by placing a conf/model_providers.yaml inside their plugin directory. The file follows the same format as the main conf/model_providers.yaml.
At startup (and whenever a plugin is enabled/disabled), the system:
conf/model_providers.yaml.conf/model_providers.yaml from all enabled plugins.Example plugin provider file (usr/plugins/my_plugin/conf/model_providers.yaml):
chat:
my_custom_provider:
name: My Custom LLM
litellm_provider: openai
kwargs:
api_base: https://my-llm.example.com/v1
embedding:
my_custom_embed:
name: My Embeddings
litellm_provider: openai
kwargs:
api_base: https://my-embed.example.com/v1
.toggle-1 (ON) and .toggle-0 (OFF).ON, OFF, and Advanced (shown when any project/profile-specific override exists).always_enabled: true in plugin.yaml forces ON and disables toggle controls in the UI.| Route | Purpose |
|---|---|
| GET /plugins/<name>/<path> | Serve static assets |
| POST /api/plugins/<name>/<handler> | Call plugin API |
| POST /api/plugins | Management (actions: get_config, save_config, list_configs, delete_config, toggle_plugin, get_doc) |
The Plugin Index is a community-maintained repository at https://github.com/agent0ai/a0-plugins that lists plugins available to the Agent Zero community. Plugins listed there can be discovered and installed by other users.
There are two completely different manifest files used at different stages. They must not be confused:
Runtime manifest (plugin.yaml, inside your plugin repo/directory — drives Agent Zero behavior):
name: my_plugin # REQUIRED for index submission; must match index folder name
title: My Plugin
description: What this plugin does.
version: 1.0.0
settings_sections:
- agent
per_project_config: false
per_agent_config: false
always_enabled: false
Index manifest (index.yaml, submitted to the a0-plugins repo under plugins/<your_plugin_name>/ — drives discoverability only):
title: My Plugin
description: What this plugin does.
github: https://github.com/yourname/your-plugin-repo
tags:
- tools
- example
screenshots: # optional, up to 5 full image URLs
- https://raw.githubusercontent.com/yourname/your-plugin-repo/main/docs/screen.png
The index manifest is named index.yaml (not plugin.yaml). Required fields: title, description, github. Optional: tags (up to 5), screenshots (up to 5 URLs). The github field must point to the root of a GitHub repository that contains a runtime plugin.yaml at the repository root, and that plugin.yaml must include a name field matching the index folder name exactly. That repository must also include a LICENSE file at its root so community users have explicit terms of use.
When creating a plugin intended for the community, the plugin should be a standalone GitHub repository where the plugin directory contents live at the repo root:
your-plugin-repo/ ← GitHub repository root
├── plugin.yaml ← runtime manifest (title, description, version, ...)
├── default_config.yaml
├── README.md
├── LICENSE ← required for Plugin Index (community) plugins
├── api/
├── tools/
├── extensions/
└── webui/
Users install it locally by cloning (or downloading) the repo contents into /a0/usr/plugins/<plugin_name>/.
plugin.yaml (including the name field) at the repo root.https://github.com/agent0ai/a0-plugins.plugins/<your_plugin_name>/ containing only an index.yaml (and optionally a square thumbnail image ≤ 20 KB).Index submission rules:
^[a-z0-9_]+$ (lowercase, numbers, underscores — no hyphens)name field in your remote plugin.yaml_ are reserved for internal usegithub must point to a public repo that contains plugin.yaml at its root with a matching name fieldLICENSE at its root (community contribution requirement)title max 50 characters, description max 500 charactersindex.yaml total max 2000 characterstags: optional, up to 5, use recommended tags from https://github.com/agent0ai/a0-plugins/blob/main/TAGS.mdscreenshots: optional, up to 5 full image URLs (png/jpg/webp, each ≤ 2 MB)The Plugin Hub is provided by the always-enabled _plugin_installer plugin. Users can reach it from the Plugins dialog in two ways:
webui/components/plugins/list/plugin-list.htmlplugins/_plugin_installer/extensions/webui/plugins-list-header-buttons/install-buttons.html, which opens plugins/_plugin_installer/webui/main.html on its own Browse tabBoth routes surface Plugin Index entries inside Agent Zero. The Plugin Hub supports search, filtering, sorting, and a detail view with README content and installation actions.
docs/developer/plugins.md for the developer-facing plugin lifecycle and publishing guideplugins/README.md for the bundled-vs-user plugin directory overview and quick linksskills/a0-plugin-router/SKILL.md for the agent-facing entry point that routes plugin tasks to the right specialist skillAGENTS.md for the main framework guide