Expand the dedicated Editor surface with safe rendered preview mode, ACE-backed source editing, browser-style tabs, toolbar/file actions, preview search, and richer Markdown rendering for code blocks, task lists, images, tables, math, local links, and footnotes.
Keep open Markdown files synchronized with the active context and saved tool edits, including live refresh for document_artifact and text_editor results without routing Markdown through Desktop/Office.
Add inline preview-page editing, clickable preview task-list checkboxes, source editor rehydration after preview-mode refreshes, and regression coverage for the new editor wiring and sync behavior.
Add a builtin _editor plugin that owns Markdown API/WebSocket sessions, canvas and modal UI, live refresh, tabs, prompt Extras for active-context open files, inline close confirmation, and Close All handling.
Route Markdown document artifacts to Editor while keeping Office/Desktop focused on LibreOffice formats, and update Desktop/Office prompts, menus, compatibility shims, and regression coverage.
Make file creation opt-in through document_artifact, move document file cards to final responses, and keep the tool payload as a quiet execution record.
Deduplicate response cards by file identity, refresh open Desktop canvas sessions after saved edits, and harden document_artifact edit input normalization for common append/update shapes.
Update prompts, skills, styles, and regression coverage for response-only file actions and explicit-only canvas opening.
- Replace the two-step settings nav with a sticky accordion that tracks active sections
- Restyle the settings rail with opacity-based active state and hash-aware opening
- Reinitialize and clean up API example Ace editors across modal reopen cycles
- Preserve modal html classes and center settings loading/error states across the full modal body
Introduce the shared surfaces frontend service and stylesheet so Browser and Desktop can register docked or floating live UI without special cases in modals.js. Update Browser and right-canvas integration to preserve active viewers across canvas/modal switches and avoid creating blank tabs unless explicitly requested.
Remove Time Travel's floating modal behavior so it uses the standard centered modal shell and shared backdrop like Settings. Keep the modal-only sizing classes on the component document and stop exempting Time Travel from backdrop rendering.
Keep Browser modal activation passive when switching from Desktop by reusing existing Browser sessions instead of creating a blank tab on viewer subscribe.
Add a Focus mode control to the Browser modal header matching Desktop's fullscreen/restore behavior.
Cover the passive subscribe path and Browser modal focus button in regression tests.
Remove the fixed right-canvas width limits so the panel can shrink to zero and grow across the available workspace.
Add Browser/Desktop surface-switch buttons to modal headers using the same registered surface metadata as the canvas controls, while preserving modal-mode preference and dock-to-canvas behavior.
Add regression coverage for the unlimited canvas sizing and modal surface switcher controls.
Restart the canvas screencast after page-changing commands and remount viewport metrics when starting or resizing streams so canvas scrolling stays smooth across first mount, new tabs, and navigation.
Move Browser JS off Alpine global store lookups and onto direct store imports, tighten modal/canvas handoff state, and keep annotations aligned with accepted viewport frames.
Improve Browser tab close ergonomics, allow Chromium native error pages to render without blocking the UI, include right-canvas tab polish, and expand regression coverage for these paths.
Restyle Settings and standard modals around a streamlined left-rail layout, clearer section hierarchy, advanced settings disclosures, and stronger update states.
Add persistent update visibility with quieter once-daily update notifications, plus Remote Link and Space Agent actions in the canvas rail. Refresh the tunnel experience as a normal Remote Link modal with clearer copy, QR/mobile affordances, and safer state handling.
Add the _time_travel core plugin with Agent Zero-owned shadow Git snapshots, history/diff/preview/travel/revert APIs, capture hooks, and canvas plus floating window UI surfaces for /a0/usr workspaces.
Wire generic file-browser mutation hooks for UI edits, update modal backdrop handling, remove the legacy _diff_viewer plugin, and replace Diff Viewer tests with focused Time Travel coverage.
Inspired by Space Agent :-)
Adds the core _diff_viewer plugin for viewing staged, unstaged, and untracked working-tree changes in the right canvas and window modal.
Includes context-aware workspace resolution, safe read-only Git collection, zero-line .gitkeep filtering, unified diff rendering, and focused diff collection tests.
Introduce the new built-in Browser plugin for Agent Zero, replacing the legacy
browser-use-based browser agent with a direct Playwright-powered browser tool,
live WebUI viewer, browser session controls, status APIs, configuration, and
extension-management support.
Add browser-specific modal behavior so the browser can run as a floating,
resizable, no-backdrop window, including modal focus, toggle, and idempotent
open helpers for richer WebUI surfaces.
Remove the old `_browser_agent` core plugin and the `browser-use` dependency,
then clean up stale browser-model wiring and references across agent code,
model configuration docs, setup guides, troubleshooting docs, skills, and
Agent Zero knowledge.
Update regression and WebUI extension-surface coverage for the new browser
architecture and modal behavior.
The legacy browser-use implementation has been extracted from core so it can
continue separately as a community plugin published through the A0 Plugin Index for any user or professional that were relying on it for workflow.
Add a shared safe markdown pipeline for plugin READMEs and docs.
- vendor DOMPurify and introduce a shared safe-markdown helper
- centralize GitHub README link/image rebasing, including repo routes like `releases`
- sanitize rendered HTML before all plugin-related x-html sinks
- apply the shared renderer to Plugin Hub README, installed plugin README, and markdown modal docs
- preserve target/rel handling for external links
- Fix Memory Leaks: Resolved SID retention in _known_sids after disconnection and cleaned up unreferenced broadcast tasks in _schedule_lifecycle_broadcast.
- Unify Dispatching Paths: Unified client and server event dispatching through the process_client_event() method to ensure diagnostic consistency.
- Optimization & Cleanup: Expanded the _OPTION_KEYS whitelist, removed dead code (iter_event_types), and deleted unused websocket exports.
- Robustness: Added handling for None responses in process_client_event to prevent cluttering responses with empty results.
- Testing: Added test cases to verify SID TTL expiration and stale SID cleanup on disconnect.
- Change update check API endpoint from api.agent-zero.ai to tapi.agent-zero.ai
- Move self-update modal opening logic from inline handler to store method
- Add openModal method to self-update store for centralized modal management
- Import self-update store in backup settings component
- Replace closeModal window method call with imported closeModal function
- Add SELF_UPDATE_MODAL_PATH constant for consistent
- Rename presets: "Efficiency" → "Max Power", "Intelligence" → "Balance", add "Cost Efficient"
- Update preset models: use Claude Opus 4.6, Sonnet 4.6, Kimi K2.5, GPT-5.4-mini/nano, Gemini 3.1 Flash Lite
- Adjust context lengths and vision capabilities across presets
- Pin chardet<6 to avoid import warnings from requests when unstructured is installed
- Replace settings-updated listener with modal-closed event
Replace the restrictive `get_tool_message_badge` extension hook with a generic `get_tool_message_handler` hook. This allows plugins to provide a full handler function for their specific `type === "tool"` rows.
Plugins can now completely take over rendering of their tool rows, or they can import and delegate to `drawMessageToolSimple` if they simply want the standard UI with a custom badge. Updated the `_browser_agent` plugin to use the new hook and documented the change in `AGENTS.plugins.md`.
feat: add get_tool_message_badge plugin hook
Add a WebUI extension point `get_tool_message_badge` so plugins can set short tool-row badge codes (`code`) and optional `displayKvps` after core handling for built-in tools (skills, vision, search, memory). Core `drawMessageTool` is now async and calls `callJsExtensions("get_tool_message_badge", …)` on the fallback path.
Remove the hardcoded `browser_agent` → `WWW` branch from `messages.js`. The `_browser_agent` plugin registers `WWW` via `extensions/webui/get_tool_message_badge/browser-tool-badge.js`.
Document the hook in `docs/agents/AGENTS.plugins.md`. With the plugin disabled, `browser_agent` tool rows no longer get a `WWW` badge from core (generic tool row).
Move the Browser Agent/browser-use stack into a tracked built-in
`_browser_agent` plugin while preserving the current model/config flow.
Changes:
- add built-in `_browser_agent` plugin with tool, helpers, prompts, assets,
status API, and WebUI message/status surfaces
- move browser-use wrapper and monkeypatch ownership out of `models.py`
into plugin helper code
- keep browser model resolution on the `_model_config` path and continue
using the effective Main Model / chat model config
- remove stale Browser HTTP Headers UI and outdated browser-model wording
- make Playwright runtime resolution bootstrap-only and stop installing
browser binaries on demand
- switch browser log rendering to plugin-owned WebUI extension handling
with core fallback compatibility for old logs
- delete obsolete core browser tool/helpers/prompts/assets after plugin
resolution is in place
- update docs to reflect built-in plugin ownership and Main Model browser
behavior
feat(browser-agent): improve lifecycle reliability, LLM compatibility, and local-dev bootstrap
- **Lifecycle Reliability**: Implemented stale Chromium lock file cleanup and one-shot ephemeral profile fallback to prevent CDP connection hangs and profile-locked crashes.
- **LLM Compatibility**: Added OpenRouter/Gemini shims including schema compaction, system instruction injection, and JSON output normalization for `browser-use` validation.
- **Cache Alignment**: Reverted Playwright browser cache to `tmp/playwright` for parity with core behavior and updated Docker scripts and documentation accordingly.
- **Local-Dev Bootstrap**: Added a plugin `install()` hook to automate Python dependency setup and transitioned bootstrap feedback to UI notifications.
- **Plugin Config**: Set `always_enabled: false` to allow users to opt-out or bring their own browser providers.
- **Testing**: Expanded test suite to cover lifecycle edge cases, LLM normalization, and bootstrap logic.
rework clean up browser agent sessions on reset/removal
Force browser-agent teardown to use BrowserSession.kill() so keep_alive sessions do not leave Chromium running and keep the profile locked. Add centralized browser state cleanup for reset/context removal and regression tests to cover the new teardown path and prevent SingletonLock fallbacks from stale sessions.
add browser agent thumbnail
restore local dev for Playwright in browser agent via hook
- Introduced a new hook to bootstrap Playwright for local development, ensuring the Chromium headless shell is installed when in development mode.
- Updated the Playwright helper to call the new hook if the binary is not found during the binary check.
- Added tests to verify the correct installation and behavior of the Playwright binary in local development scenarios.
proper install notifications for Playwright in local dev
- Added notification management for Playwright bootstrap process, including info, success, and error notifications.
- Enhanced the `bootstrap_local_dev_playwright` function to notify users about the installation status of the Playwright runtime.
- Updated tests to verify the correct notifications are sent during the Playwright installation process.
rework browser state cleanup extensions for agent context removal and reset
- Added `CleanupBrowserStateOnRemove` and `CleanupBrowserStateOnReset` extensions to handle browser state cleanup when an agent context is removed or reset.
- Updated `cleanup_browser_agent_state` function to utilize new protocols for better type safety and clarity.
- Enhanced the `State` class to ensure proper cleanup of browser sessions and user data directories.
- Introduced tests to validate the cleanup functionality and ensure browser sessions are correctly managed during agent lifecycle events.
enhance Playwright cache handling
- Updated Playwright helper to include support for a new cache directory at `.cache/ms-playwright`, aligning with standard cache locations.
- Modified the `get_playwright_search_dirs` function to incorporate the new cache path.
- Added a new test to verify the retrieval of the Playwright binary from the repository's `.cache/ms-playwright` directory, ensuring proper functionality across different cache locations.
- Updated existing tests to reflect changes in cache path handling.
update docs and README.md
restore local dev bootstrap and async-safe teardown
Keep Browser Agent bootstrap local in development, matching pre-extraction behavior, by removing the RFC filesystem hop from Playwright cache setup. Restore on-demand local-dev bootstrap through plugin hooks before browser-use import, so missing browser packages and runtime can be installed on first use.
Rework browser session teardown to be async-safe during reset and cleanup, avoiding nested event loop shutdown failures while still terminating the browser worker cleanly. Also remove redundant browser-use patch application and add regression tests covering local bootstrap, reset-time async shutdown, and lifecycle cleanup.
playwright bootstrap via plugin hooks
[Fixed][P1] The hook-based restore was architecturally in the right place, but it was too late to recover a missing local browser_use / playwright install. The browser tool imports browser_use at module load time, and tool loading silently skips modules that fail to import, so the plugin install() hook could never rescue the first browser invocation if those packages were absent. I fixed that by calling the plugin install hook before importing browser_use in local development: browser_use.py, browser_use.py, agent.py, test_browser_agent_playwright_bootstrap.py.
[Fixed][P3] The Browser Agent UI and runtime error text still claimed on-demand install did not exist, which contradicted the intended local-dev bootstrap path. I corrected both messages so they now describe the real behavior: main.html, playwright.py.
rm tests
restore old browser use logic files
restore old browser working behaviour
browser: migrate cleanup hooks to new extensible paths
Update Browser Agent cleanup extensions to the new deep @extensible path layout introduced in 7e1d9ad2a4, so AgentContext reset/remove hooks fire again after the framework migration.
`This restores browser state cleanup when chats are reset or deleted, preventing keep-alive browser sessions from surviving context removal and leaving chats stuck until restart. The change also keeps the recent browser regressions covered with focused tests for Anthropic/OpenRouter action normalization, keep-alive session teardown via kill(), and extension discovery under the new _functions/... path structure.`
Update messages.js
update docs
- Add wildcard pattern matching for websocket events (*, eventPattern compilation)
- Update WebSocketClient.on() to handle wildcard subscriptions with onAny/offAny
- Add send_data helper and set_shared_websocket_manager for global websocket access
- Broadcast cache clear events to frontend via websocket
- Subscribe to wildcard events in sync-store and route to extensions
- Move get_default_value import to function scope in
Body nodes (containing Alpine x-data directives) were appended to the
DOM immediately during the import loop, while <script type="module">
imports were awaited afterward. Alpine's MutationObserver would evaluate
x-data expressions before the module had registered its store, causing
"Cannot convert undefined or null to object" errors.
Collect body nodes into a buffer and append them only after
Promise.all(loadPromises) resolves, ensuring stores are registered
before Alpine processes the DOM.
Add @extension.extensible decorators to all plugin API handler methods and core plugin functions to enable extension points. Implement plugin hooks system allowing plugins to define custom behavior via hooks.py file. Add call_plugin_hook function to execute plugin-specific hooks for events like uninstall, save_plugin_config, and get_plugin_config. Introduce uninstall_plugin function that calls uninstall hook before deletion. Move circular
SameSite=Strict cookies are not sent with WebSocket upgrade requests on
Chromium-based browsers (Brave confirmed), causing the CSRF cookie
check at connect time to fail with 'csrf cookie mismatch'. This breaks
the state_sync namespace, preventing the UI from loading chats.
Change SameSite from Strict to Lax for both the Flask session cookie
and the JavaScript-set CSRF token cookie. Lax still prevents cross-site
POST CSRF while allowing same-origin WebSocket upgrades to include
cookies.
Fixes#1237
Introduce validate_tool_request() extensible method in agent.py to validate tool request structure (dict with tool_name string and tool_args dict fields) before processing. Add after_plugin_change() helper in helpers/plugins.py that clears cache and sends a frontend reload notification (throttled to display_time interval) with a reload button. Update plugin installer install/delete flows to call after_plugin_change(). Extend notification
Refactor plugin settings to use a per-modal prototype/context instead of binding directly to the global store. Introduces pluginSettingsPrototype (renamed export) and a lightweight instantiate helper (Alpine.magic('instantiate')) to clone the prototype into a modal-local context. Plugin config HTMLs (code_execution, infection_check, memory, text_editor) now bind UI fields to config.* and use context.* for modal-level state and actions; plugin settings components (plugin-settings.html, plugin-configs.html, plugin-settings-store.js, pluginListStore.js) were updated to create and consume the local context and to pass context into nested modals. Documentation and the SKILL guide were updated to describe the new modal contract and usage. This change scopes settings UI state to modal instances, enabling safer local state and easier integration with core-setting wrappers (use context.saveMode = 'core').
Add robust plugin name derivation and clean up API helper code.
- helpers/git.py: add giturlparse dependency and extract_author_repo(url) to reliably extract owner/repo from git URLs (strips auth and validates).
- plugins/plugin_installer/helpers/install.py: replace the old sanitize function with two derivation helpers: _derive_git_plugin_name (normalizes owner/repo into a safe plugin ID) and _derive_zip_plugin_name (determine name from zip contents or uploaded filename). Import regex and use extract_author_repo; switch import to clear_plugin_cache and remove redundant cache clears on intermediate failures.
- requirements.txt: add giturlparse==0.14.0.
- webui/js/api.js: deduplicate and move extensions/URL normalization helpers, add redirect(response) helper to centralize login redirect handling, normalize CSRF cookie secure flag formatting, and minor whitespace/logic cleanup.
These changes improve reliability of plugin ID inference from git URLs/archives and simplify/centralize client-side API helper logic.
Login redirect responses are followed without validating the target
origin. An attacker who can influence the redirect URL could send
users to a malicious domain.
Add origin validation before following the redirect in both
fetchApi() and getCsrfToken() — only follow if the redirect URL
origin matches window.location.origin.
Severity: Medium
Add a generic JS extension point for the shared confirm dialog so plugins can
augment the warning body and footer without hardcoding scanner logic in the
installer.
- webui/js/confirmDialog.js: call callJsExtensions('confirm_dialog_after_render')
with dialog/body/footer nodes and optional extensionContext; defer show until
extensions run; add isClosed guard and Enter key handling for extension buttons.
- plugins/plugin_installer: pass extensionContext only from installFromIndex()
(kind: marketplace_plugin_install_warning, gitUrl, etc.); ZIP/Git install
flows unchanged.
- plugins/plugin_scan: new extension confirm_dialog_after_render that, for
marketplace install warnings, appends recommendation text and a "Scan with A0"
button that closes the dialog and opens the scanner modal with the repo URL.
- webui/css/modals.css: margin for .confirm-dialog-extension-note.
- docs: document confirm_dialog_after_render and when to use JS hooks vs HTML
breakpoints (AGENTS.plugins.md, developer/plugins.md).
Add extension hooks and improve plugin cache handling. Changes include:
- Add new JSON API extension points and a cache reset handler (extensions/webui/json_api_call_before, json_api_call_after/cache_reset.js) to clear frontend cache when backend cache_reset runs.
- Rename webui extension hook directories to use fetch_api_call_* naming.
- Refactor webui/js/api.js to normalize API URLs, lazily import ./extensions.js, and call extension hooks for json_api_call_before/error/after and fetch_api_call_before/after using a ctx object.
- Rename invalidate_plugin_cache to clear_plugin_cache and call it after plugin install/delete/toggle/config save to keep plugin cache consistent.
- Harden plugin name sanitization to replace non-alphanumeric chars with underscores.
- Include extensions/**/*.js in jsconfig.json for editor tooling.
These changes improve extensibility for API lifecycle events and ensure plugin-related cache is cleared when plugin state changes.
Redesign extension handling to support explicit async/sync execution. helpers/extension.py rewrites the extensible decorator, adds call_extensions_async / call_extensions_sync, and a helper to gather extension classes; caching flag adjusted. Updated call sites across the codebase (agent, APIs, plugins, tools, settings, extensions) to use extension.extensible and the new call_extensions_async/sync API, and converted several extension handlers from async to sync. Also small frontend tweaks (use globalThis.runtimeInfo) and minor import updates (csrf_protect in run_ui). This centralizes extension discovery/execution and avoids previously scattered asyncio.run usage.
Introduce a reusable frontend cache (webui/js/cache.js) with area-based storage, glob clearing, and enable toggles. Integrate the cache into webui/js/extensions.js (add/get/clear cache usage, define JS/HTML cache area keys, make imported JS extension defaults variadic) and adjust the HTML import/loading flow and MutationObserver callback wiring. Update webui/js/messages.js to support async message handlers (typedef allowing Promise results), make getMessageHandler async and consult extensions for custom handlers, and await setMessage/setMessage handler results. Also remove an unused DockerContainerManager import from python/tools/code_execution_tool.py.
The CSRF token cookie is set without the Secure flag. On HTTPS
deployments the cookie could be transmitted over plain HTTP if a
mixed-content scenario occurs.
Conditionally add the Secure flag when running on HTTPS
(window.location.protocol === 'https:'). No impact on HTTP-only
deployments.
Severity: Low-Medium
Enable stricter type checking (checkJs, allowJs, strict, ES2022/ESNext) in jsconfig.json and add comprehensive JSDoc typedefs and return types across the web UI message code. Key changes:
- simple-action-buttons: add JSDoc for createActionButton, handle missing icons gracefully (use text fallback), and compute feedback flag safely.
- initFw.js: ignore TS on importing Alpine and expose Alpine from globalThis.
- messages.js: introduce typedefs (MessageHandlerArgs, MessageHandlerResult, SetMessageResult, ProcessStepArgs), return richer handler results, make setMessage return { args, result }, tighten null/undefined handling, annotate scroller and other locals, and update many drawMessage/drawProcessStep functions to return consistent objects.
- Several safety/refactor fixes: use globalThis.katex for rendering, safer DOM helpers (ensureChild typed), avoid early null returns, and build actionButtons arrays incrementally instead of relying on filtering nulls.
Overall these changes improve type safety, null-safety, and clarity of message handler contracts to support better tooling and fewer runtime errors.
Expose plugin config management and improve UI/UX. Backend: add plugins API actions list_configs and delete_config (with validation and file deletion), and refine find_plugin_assets to infer project/agent from paths when wildcards are used. Frontend: update download endpoints to /api/download_work_dir_file, redesign plugin-configs list layout, add delete confirmation and show action, and adjust styles. Enhance plugin-settings-store with unsaved-changes detection, scope-change confirmation, config listing and deletion via the new API, and settings snapshot lifecycle. Also update message path replacement to use the /api prefix.
Major refactor of plugin and project helper APIs and add a plugin management UI.
Key changes:
- Rename project meta helpers from get_project_meta_folder -> get_project_meta and update callers across many modules (projects, memory, skills_import, secrets, subagents, skills).
- Overhaul python/helpers/plugins.py: introduce PluginMetadata and PluginListItem (Pydantic), new get_plugins_list/get_enhanced_plugins_list, support for reading plugin config (config.json), enable/disable logic (.enabled/.disabled), get_enabled_plugin_paths(), and helpers to find/read/save plugin assets. Plugin discovery now supports webui/main/config detection.
- Use enabled-plugin-aware lookups in subagents, extension loading, and other places (plugins.get_enabled_plugin_paths / get_enhanced_plugins_list used instead of previous list_plugins/get_plugin_paths where appropriate).
- Add API endpoint python/api/plugins_list.py to return JSON plugin lists.
- Add frontend plugin management UI: web components webui/components/plugins/list/plugin-list.html and pluginListStore.js; wire quick-actions button to open the plugins modal.
- Add new webui assets and config pages for example_agent and memory plugins; add plugins/plugin.json metadata for example_agent and memory.
- Memory plugin fixes: updated calls to use get_project_meta and adjusted memory path helpers to use project meta layout.
- File helper: add read_file_json to read JSON files directly.
- run_ui: tightened plugin asset serving security (only serve from plugin webui or plugin extensions/webui), added unified _serve_plugin_asset helper, and load plugin API handlers using enhanced plugin list.
- Small fixes: adjustments to parse_prompt/read_prompt/tool lookup to use updated subagents.get_paths signature, extension loader now uses enabled plugin paths, and web UI extensions JS updated to expect string paths.
These changes centralize project meta handling, improve plugin discovery and enable/disable behavior, and add a basic plugin management UI and API.