main
md 365 lines 13.6 KB
Rendered Raw
1 ---
2 name: a0-manage-plugin
3 description: "Manage Agent Zero plugins lifecycle: browse the Plugin Hub, scan for security, install from Git/ZIP/Plugin Hub, update, uninstall, enable, disable, debug, and troubleshoot. Use when asked to install, update, uninstall, remove, scan, find, search, enable, disable, debug, or troubleshoot a plugin."
4 version: 1.2.0
5 tags: ["plugins", "install", "uninstall", "update", "scan", "security", "debug", "troubleshoot", "plugin-hub", "manage"]
6 trigger_patterns:
7 - "install plugin"
8 - "uninstall plugin"
9 - "remove plugin"
10 - "delete plugin"
11 - "update plugin"
12 - "scan plugin"
13 - "debug plugin"
14 - "troubleshoot plugin"
15 - "browse plugins"
16 - "search plugins"
17 - "plugin not working"
18 - "enable plugin"
19 - "disable plugin"
20 - "plugin hub"
21 - "list plugins"
22 ---
23
24 # Agent Zero Plugin Management
25
26 ## Action Routing
27
28 Identify what the user needs and jump to the relevant section:
29
30 | User need | Section |
31 |---|---|
32 | Find / search / browse available plugins | [Browse Plugin Hub](#browse-plugin-hub) |
33 | Scan a plugin for security issues | [Security Scan](#security-scan) |
34 | Install a plugin | [Install a Plugin](#install-a-plugin) |
35 | Update an installed plugin | [Update a Plugin](#update-a-plugin) |
36 | Uninstall / remove a plugin | [Uninstall a Plugin](#uninstall-a-plugin) |
37 | Enable or disable a plugin | [Activation](#activation) |
38 | Plugin not loading / crashing / missing | Read `/a0/skills/a0-debug-plugin/SKILL.md` |
39 | Explain how plugin discovery works | Read `/a0/skills/a0-debug-plugin/SKILL.md` |
40
41 ---
42
43 ## Browse Plugin Hub
44
45 Fetch the current community index:
46
47 ```python
48 import urllib.request, json
49
50 url = "https://github.com/agent0ai/a0-plugins/releases/download/generated-index/index.json"
51 with urllib.request.urlopen(url, timeout=30) as resp:
52 index = json.loads(resp.read())
53
54 plugins = index.get("plugins", {})
55 ```
56
57 Each entry in `plugins` is keyed by plugin name with fields: `title`, `description`, `github`, `tags`, `thumbnail` (URL if available).
58
59 **To search**: filter by keyword in `title`, `description`, or `tags`.
60
61 **To list installed plugins** locally:
62 ```bash
63 ls /a0/usr/plugins/
64 ```
65
66 **Alternatively via UI**: Open the Plugins dialog in Agent Zero and switch to the **Browse** tab (or click **Install** in the toolbar to open the Plugin Hub).
67
68 ---
69
70 ## Security Scan
71
72 The `_plugin_scan` plugin provides an LLM-driven security scanner that clones the repository, reads all files, and produces a structured markdown report covering 6 checks: structure match, static code review, agent manipulation detection, remote communication, secrets access, and obfuscation.
73
74 ### Pre-install scan protocol (follow this for every install)
75
76 **Always offer to scan before installing.** If the user hasn't explicitly declined, say:
77
78 > "Before installing, I strongly recommend running a security scan on this plugin. Third-party plugins execute code inside your Agent Zero environment. Should I scan it first? The scan typically takes 2-4 minutes."
79
80 If the user declines, acknowledge but warn once:
81
82 > "Understood, skipping the scan. Note that installing unscanned third-party code carries security risks. Proceeding with installation."
83
84 Then proceed to install. **Do not ask again** after the user declines.
85
86 ### Running a scan
87
88 ```python
89 # (after authentication setup - see Install section)
90 resp = s.post(
91 f"{BASE}/api/plugins/_plugin_scan/plugin_scan_run",
92 json={
93 "git_url": "https://github.com/<user>/<plugin-repo>",
94 "checks": ["structure", "codeReview", "agentManipulation", "remoteComms", "secrets", "obfuscation"],
95 },
96 headers={"X-CSRF-Token": token, "Origin": ORIGIN},
97 timeout=600, # set generously - the scan clones the repo, reads all files, runs 6 LLM checks
98 )
99 data = resp.json()
100 ```
101
102 Always pass the full `checks` list explicitly - omitting it has been observed to cause intermittent failures. To run a subset, remove unwanted keys from the list.
103
104 ### Interpreting the result
105
106 ```python
107 report = data["report"] # full markdown security report
108 ```
109
110 Present the full `report` to the user. Read the Summary section to determine the overall verdict (**Safe** / **Caution** / **Dangerous**) and act accordingly:
111
112 | Verdict | Action |
113 |---|---|
114 | **Safe** | Offer to proceed with installation |
115 | **Caution** | Show findings, warn that issues were found, ask for explicit confirmation: "Some warnings were found. Do you still want to install?" |
116 | **Dangerous** | Show findings, **strongly advise against installing**: "The scanner flagged serious security issues. I strongly recommend NOT installing this plugin. Do you want to proceed anyway?" Only install if user explicitly confirms. |
117
118 If the scan times out or errors (500), inform the user and ask whether to proceed without a scan.
119
120 ---
121
122 ## Install a Plugin
123
124 > **Always use the HTTP API or UI.** Never import Agent Zero framework modules directly from `code_execution_tool` - the agent runs in a separate Python runtime (`/opt/venv`) that does not have the framework's dependencies. All programmatic installs must go through HTTP.
125
126 ### How installed state works
127
128 The Plugin Hub marks a plugin as **Installed** by cross-referencing Plugin Hub keys against `usr/plugins/` directory names at request time. To appear installed:
129 - The plugin directory must exist at `usr/plugins/<name>/` with a valid `plugin.yaml`
130 - If the plugin ships extensions, the framework will register both named extension points under `extensions/python/<point>/` and implicit `@extensible` hooks under `extensions/python/_functions/<module>/<qualname>/<start|end>/` after the plugin cache is refreshed
131 - The framework plugin cache must be cleared (the API handles this automatically)
132 - Re-fetching the Plugin Hub index will then show it as installed
133
134 ### API authentication (required for all HTTP calls)
135
136 The Agent Zero API uses CSRF protection. The `Origin` header is **always required** - without it the CSRF endpoint returns `ok: false` even when login is disabled.
137
138 **Step 1: Set the base URL.** Agent Zero listens on port 80 inside Docker (the standard deployment):
139
140 ```python
141 import requests
142
143 BASE = "http://localhost" # port 80 inside Docker
144 # If running outside Docker (dev mode), check: os.environ.get("WEB_UI_PORT", "5000")
145 ```
146
147 **Step 2: Bootstrap the session and get the CSRF token:**
148
149 ```python
150 s = requests.Session()
151 ORIGIN = BASE # Origin must match a localhost pattern
152
153 r = s.get(f"{BASE}/api/csrf_token", headers={"Origin": ORIGIN}, timeout=10)
154 data = r.json()
155
156 if not data.get("ok"):
157 raise RuntimeError(f"CSRF bootstrap failed: {data.get('error')}")
158
159 token = data["token"]
160 runtime_id = data["runtime_id"]
161
162 # Set the CSRF cookie (required alongside the header)
163 s.cookies.set(f"csrf_token_{runtime_id}", token)
164 ```
165
166 Reuse `s`, `BASE`, `ORIGIN`, and `token` for all subsequent API calls. Always include `headers={"X-CSRF-Token": token, "Origin": ORIGIN}` on every request.
167
168 ### Method 1: From a Git URL (via HTTP API) - preferred for programmatic use
169
170 ```python
171 # (after authentication setup above)
172 resp = s.post(
173 f"{BASE}/api/plugins/_plugin_installer/plugin_install",
174 json={
175 "action": "install_git",
176 "git_url": "https://github.com/<user>/<plugin-repo>",
177 # "git_token": "<token>", # optional, for private repos
178 # "plugin_name": "override" # optional, override directory name
179 },
180 headers={"X-CSRF-Token": token, "Origin": ORIGIN},
181 timeout=120,
182 )
183 print(resp.json())
184 ```
185
186 This runs the full pipeline in the framework runtime: clone → validate → place in `usr/plugins/` → run `install` hook → clear plugin cache → notify frontend. The Plugin Hub will show the plugin as installed on the next index fetch.
187
188 ### Method 2: From the Plugin Hub (UI) - preferred for interactive use
189
190 1. Open the Plugins dialog
191 2. Go to the **Browse** tab (or click **Install**)
192 3. Find the plugin, click it, click **Install**
193
194 The UI handles everything including marking the plugin as installed in the Plugin Hub view.
195
196 ### Method 3: From a ZIP file (via HTTP API)
197
198 ```python
199 # (after authentication setup above)
200 with open("plugin.zip", "rb") as f:
201 resp = s.post(
202 f"{BASE}/api/plugins/_plugin_installer/plugin_install",
203 data={"action": "install_zip"},
204 files={"plugin_file": f},
205 headers={"X-CSRF-Token": token, "Origin": ORIGIN},
206 )
207 print(resp.json())
208 ```
209
210 Or via UI: Plugins dialog -> Install -> ZIP tab -> upload file.
211
212 ### Manual install (last resort only)
213
214 Only use this if the HTTP API is genuinely unavailable (not because of import errors - those mean you must use the HTTP API instead).
215
216 ```bash
217 git clone https://github.com/<user>/<repo> /a0/usr/plugins/<plugin_name>
218 ```
219
220 After cloning, the plugin is on disk but the framework doesn't know about it. Clear the cache and notify the frontend by calling the toggle API (off then on) which triggers `after_plugin_change()` internally:
221
222 ```python
223 # (after authentication setup above)
224 for state in [False, True]:
225 s.post(
226 f"{BASE}/api/plugins",
227 json={"action": "toggle_plugin", "plugin_name": "<plugin_name>", "enabled": state},
228 headers={"X-CSRF-Token": token, "Origin": ORIGIN},
229 )
230 ```
231
232 Or simply restart Agent Zero - on startup it re-scans `usr/plugins/` fresh.
233
234 ---
235
236 ## Update a Plugin
237
238 > The framework update flow now calls `pre_update()` from `hooks.py` immediately before pulling new plugin code into place, then re-runs `install()` after the update if that hook exists.
239
240 ### Checking for updates
241
242 **Do not compare version strings.** Contributors often forget to bump the version, so a matching version does not mean the plugin is current. Check for new commits instead:
243
244 ```bash
245 # Is the plugin a git repo?
246 git -C /a0/usr/plugins/<name> rev-parse --is-inside-work-tree 2>/dev/null
247
248 # Compare local HEAD with remote HEAD (no fetch required)
249 LOCAL=$(git -C /a0/usr/plugins/<name> rev-parse HEAD)
250 REMOTE=$(git -C /a0/usr/plugins/<name> ls-remote origin HEAD | awk '{print $1}')
251 echo "Local: $LOCAL"
252 echo "Remote: $REMOTE"
253 [ "$LOCAL" = "$REMOTE" ] && echo "Up to date" || echo "Update available"
254 ```
255
256 If they differ, new commits exist on the remote - report this to the user as "update available" regardless of whether the version field changed.
257
258 ### Applying the update
259
260 If installed via Git:
261
262 ```bash
263 cd /a0/usr/plugins/<name>
264 git pull origin main
265 ```
266
267 Then refresh the framework cache via the toggle API (see [API authentication](#api-authentication-required-for-all-http-calls) for session setup):
268
269 ```python
270 # (after authentication setup)
271 for state in [False, True]:
272 s.post(
273 f"{BASE}/api/plugins",
274 json={"action": "toggle_plugin", "plugin_name": "<name>", "enabled": state},
275 headers={"X-CSRF-Token": token, "Origin": ORIGIN},
276 )
277 ```
278
279 If not a git repo: uninstall via the API (see [Uninstall a Plugin](#uninstall-a-plugin)), then reinstall via the Git method above.
280
281 ---
282
283 ## Uninstall a Plugin
284
285 > **Safety rules - read before proceeding**:
286 > - **Core plugins** (in `plugins/`, not `usr/plugins/`) cannot be uninstalled via the API - the framework blocks it. Disable them instead (see [Activation](#activation)).
287 > - **Always ask for explicit user confirmation** before uninstalling: "Are you sure you want to uninstall `<name>`? This will delete all plugin files and cannot be undone."
288 > - Uninstalling does NOT delete plugin config files stored in `usr/agents/` or project scopes.
289
290 ### Standard uninstall (via API)
291
292 Uses the framework's `uninstall_plugin` which calls the plugin's `uninstall` hook (if defined) before deleting. Requires an authenticated session (see [API authentication](#api-authentication-required-for-all-http-calls) in the Install section):
293
294 ```python
295 # (after authentication setup from the Install section)
296 resp = s.post(
297 f"{BASE}/api/plugins",
298 json={
299 "action": "delete_plugin",
300 "plugin_name": "<name>",
301 },
302 headers={"X-CSRF-Token": token, "Origin": ORIGIN},
303 )
304 print(resp.json())
305 ```
306
307 This is the preferred method. The framework will:
308 1. Call `uninstall()` from `hooks.py` (if present) - runs cleanup
309 2. Delete the `usr/plugins/<name>/` directory
310 3. Notify the frontend to refresh the plugin list
311
312 **Via UI**: Plugins dialog -> find the plugin -> click the delete (trash) icon -> confirm.
313
314 ### Fallback: direct folder removal
315
316 Use this only if the standard uninstall fails (e.g., broken `uninstall` hook that crashes or hangs):
317
318 ```bash
319 # Confirm the plugin is a custom one (usr/plugins/) - NEVER delete from plugins/
320 ls /a0/usr/plugins/<name>/
321
322 # Remove it
323 rm -rf /a0/usr/plugins/<name>/
324 ```
325
326 After manual removal, refresh the plugin list via the UI or restart Agent Zero.
327
328 ---
329
330 ## Activation
331
332 Plugins are enabled/disabled via toggle files:
333 - `.toggle-1` = explicitly ON
334 - `.toggle-0` = explicitly OFF
335 - No file = default (enabled for most plugins)
336
337 **Enable a plugin**:
338 ```bash
339 rm -f /a0/usr/plugins/<name>/.toggle-0
340 touch /a0/usr/plugins/<name>/.toggle-1
341 ```
342
343 **Disable a plugin**:
344 ```bash
345 rm -f /a0/usr/plugins/<name>/.toggle-1
346 touch /a0/usr/plugins/<name>/.toggle-0
347 ```
348
349 Via UI: Plugins dialog -> find the plugin -> use the toggle switch.
350
351 Plugins with `always_enabled: true` in `plugin.yaml` cannot be toggled (framework core plugins only).
352
353 **Scoped toggles** (when `per_project_config` or `per_agent_config` is true): use the "Switch" modal in the UI, or place toggle files in the appropriate scoped path:
354 - Project scope: `project/.a0proj/plugins/<name>/.toggle-1`
355 - Agent profile scope: `usr/agents/<profile>/plugins/<name>/.toggle-1`
356
357 ---
358
359 ## References
360
361 - Plugin architecture: `/a0/plugins/AGENTS.md`
362 - Developer lifecycle guide: `/a0/docs/developer/plugins.md`
363 - Debug a broken plugin: read `/a0/skills/a0-debug-plugin/SKILL.md`
364 - Create a new plugin: read `/a0/skills/a0-create-plugin/SKILL.md`
365 - Review a plugin: read `/a0/skills/a0-review-plugin/SKILL.md`