diff --git a/AGENTS.md b/AGENTS.md
index 00a1570f9..31ab6a620 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -3,8 +3,8 @@
[Generated using reconnaissance on 2026-02-22]
## Quick Reference
-Tech Stack: Python 3.12+ | Flask | Alpine.js | LiteLLM | WebSocket (Socket.io)
-Dev Server: python run_ui.py (runs on http://localhost:50001 by default)
+Tech Stack: Framework Python 3.12+ | Agent execution Python 3.13 in Docker | Flask | Alpine.js | LiteLLM | WebSocket (Socket.io)
+Dev Server: python run_ui.py (discover host/port from startup output or runtime configuration; do not assume a default port)
Run Tests: pytest (standard) or pytest tests/test_name.py (file-scoped)
Documentation: README.md | docs/
Frontend & Plugin DOX: [WebUI](webui/AGENTS.md) | [Components](webui/components/AGENTS.md) | [Frontend JS](webui/js/AGENTS.md) | [Plugins](plugins/AGENTS.md)
@@ -41,25 +41,26 @@ Primary Language(s): Python, JavaScript (ES Modules)
Do not combine these commands; run them individually:
```bash
pip install -r requirements.txt
-pip install -r requirements2.txt
```
- Start WebUI: python run_ui.py
+- Discover the WebUI URL from startup output, launcher/Docker port mappings, or explicit `--host`/`--port`/`WEB_UI_PORT` configuration; do not hardcode a default port.
---
## Docker Environment
-When running in Docker, Agent Zero uses two distinct Python runtimes to isolate the framework from the code being executed:
+When running in Docker, Agent Zero uses two distinct Python runtimes to isolate the framework itself from code executed on behalf of the agent:
### 1. Framework Runtime (/opt/venv-a0)
- Version: Python 3.12.4
-- Purpose: Runs the Agent Zero backend, API, and core logic.
-- Packages: Contains all dependencies from requirements.txt.
+- Purpose: Runs the Agent Zero framework itself: WebUI backend, API, core loop, scheduler, framework imports, and plugin hooks/tools that execute inside the framework process.
+- Packages: Contains framework dependencies from requirements.txt.
+- Verification: Use this runtime for framework/backend import checks, WebUI startup checks, and plugin hook behavior unless the code explicitly switches environments.
-### 2. Execution Runtime (/opt/venv)
+### 2. Agent Execution Runtime (/opt/venv)
- Version: Python 3.13
-- Purpose: Default environment for the interactive terminal and the agent's code execution tool.
-- Behavior: This is the environment active when you docker exec into the container. Packages installed by the agent via pip install during a task are stored here.
+- Purpose: Default Python environment for the agent's terminal/code-execution tasks and user code run by the agent.
+- Behavior: Packages installed by the agent during a task belong here so task dependencies do not pollute or prove the framework runtime. Do not use this runtime as evidence that framework imports, WebUI startup, or plugin hooks work.
---
@@ -87,9 +88,9 @@ When running in Docker, Agent Zero uses two distinct Python runtimes to isolate
├── agents/ # Agent profiles (prompts and config)
├── prompts/ # System and message prompt templates
├── knowledge/
-│ └── main/about/ # Agent self-knowledge (indexed into vector DB for runtime recall)
+│ └── main/about/ # Agent self-knowledge reference material
│ ├── identity.md # Philosophy, principles, project context
-│ ├── architecture.md # Agent loop, memory pipeline, multi-agent, extensions
+│ ├── architecture.md # Agent loop, multi-agent coordination, extensions
│ ├── capabilities.md # Detailed capabilities and limitations
│ ├── configuration.md # LLM roles, providers, profiles, plugins, settings
│ └── setup-and-deployment.md # Docker deployment, updates, troubleshooting
@@ -97,12 +98,13 @@ When running in Docker, Agent Zero uses two distinct Python runtimes to isolate
```
Key Files:
-- agent.py: Defines AgentContext and the main Agent class.
+- agent.py: Defines AgentContext, LoopData virtual prompt areas (Protocol before history and Extras after history), and the main Agent class.
- helpers/plugins.py: Plugin discovery and configuration logic.
- webui/js/AlpineStore.js: Store factory for reactive frontend state.
- helpers/api.py: Base class for all API endpoints.
+- models.py: LLM provider configuration and LiteLLM wrappers; framework LiteLLM defaults such as `drop_params=True` are merged with `litellm_global_kwargs`, configured values override framework defaults, documented module-level switches such as `drop_params` are applied to LiteLLM, and merged kwargs are passed per call.
- scripts/openrouter_release_notes_system_prompt.md: Editable system prompt used to generate GitHub release notes during Docker publishing.
-- knowledge/main/about/: Agent self-knowledge files, indexed into the vector DB for runtime recall. Not user-facing docs - written for the agent's internal reference.
+- knowledge/main/about/: Agent self-knowledge files. Not user-facing docs - written for the agent's internal reference.
- webui/components/AGENTS.md: DOX contract for Alpine component architecture.
- webui/js/AGENTS.md: DOX contract for frontend infrastructure, modal stack, API helpers, and extension loading.
- plugins/AGENTS.md: DOX contract for bundled and custom plugin architecture; `usr/plugins/` remains ignored user state.
@@ -240,7 +242,6 @@ If pip install fails, try running in a clean virtual environment:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
-pip install -r requirements2.txt
```
### WebSocket Connection Failures
@@ -273,7 +274,7 @@ pip install -r requirements2.txt
6. Use the nearest AGENTS.md as the local contract and parent docs for repo-wide rules
7. If docs conflict, the closer doc controls local work details, but no child doc may weaken DOX
-Do not rely on memory. Re-read the applicable DOX chain in the current session before editing.
+Do not rely on prior context. Re-read the applicable DOX chain in the current session before editing.
## Update After Editing
diff --git a/README.md b/README.md
index 9b1dc4acf..45efd23d1 100644
--- a/README.md
+++ b/README.md
@@ -3,36 +3,62 @@
# Agent Zero
-### A full Linux system for your AI agent.
+### Give your agent a full Linux computer.
-Agent Zero is an open, dynamic, organic agentic framework. One Docker container ships a full Linux system with a desktop and a plugin hub that the agent can extend using Skills.
+Agent Zero is an open agent framework for work that needs more than chat: a Dockerized Linux desktop, a browser with DOM annotation, live document cowork, projects, skills, plugins, and a bridge back to your host machine.
[](https://agent-zero.ai)
[](./docs/)
[](https://discord.gg/B8KZKNsPpj)
[](https://github.com/sponsors/agent0ai)
-
-[Install](#how-to-install) |
-[What's Different](#what-makes-agent-zero-different) |
-[A0 CLI](#a0-cli-connector-extend-onto-your-host-machine) |
-[Docs](#documentation)
-
[](https://deepwiki.com/agent0ai/agent-zero)
-[Ask ChatGPT](https://chatgpt.com/?q=Analyze%20this%3A%20https%3A%2F%2Fgithub.com%2Fagent0ai%2Fagent-zero) |
-[Ask Claude](https://claude.ai/new?q=Analyze%20this%3A%20https%3A%2F%2Fgithub.com%2Fagent0ai%2Fagent-zero)
+[Quick Start](#quick-start) |
+[Why Agent Zero](#why-agent-zero) |
+[Try These First](#try-these-first) |
+[Deep Dives](#deep-dives) |
+[Docs](#documentation)
-
@@ -228,17 +263,6 @@ It is not a replacement for Git or backups. It is a practical safety layer for t
- **Client/project isolation:** keep memory, secrets, instructions, files, and model choices separated by project.
- **Scheduled operations:** run recurring checks and monitoring tasks with project-scoped context and credentials.
-## Safety Model
-
-Agent Zero is powerful because it can use a real environment.
-
-- Keep it running inside Docker or another isolated environment.
-- Do not mount your entire home directory unless you understand the risk.
-- Grant A0 CLI Read+Write access and remote code execution only for machines and workspaces you trust.
-- Store credentials in project secrets or settings, not in prompts or public files.
-- Review actions that touch accounts, money, production systems, or private data.
-- Keep backups for important workspaces.
-
## Documentation
| I want to... | Start here |
@@ -274,3 +298,16 @@ You can help by improving docs, creating skills, publishing plugins, testing mod
- [YouTube](https://www.youtube.com/@AgentZeroFW) for demos and tutorials.
- [X](https://x.com/Agent0ai), [LinkedIn](https://www.linkedin.com/company/109758317), and [Warpcast](https://warpcast.com/agent-zero) for updates.
- [GitHub Issues](https://github.com/agent0ai/agent-zero/issues) for bugs and feature requests.
+
+[Space Agent](https://github.com/agent0ai/space-agent) is the related, more polished product direction for the agent-shaped workspace. Agent Zero remains the open framework and Linux-powered workbench.
+
+## Safety Model
+
+Agent Zero is powerful because it can use a real environment.
+
+- Keep it running inside Docker or another isolated environment.
+- Do not mount your entire home directory unless you understand the risk.
+- Grant A0 CLI Read+Write access and remote code execution only for machines and workspaces you trust.
+- Store credentials in project secrets or settings, not in prompts or public files.
+- Review actions that touch accounts, money, production systems, or private data.
+- Keep backups for important workspaces.
diff --git a/agent.py b/agent.py
index 149c900e3..f8c902da7 100644
--- a/agent.py
+++ b/agent.py
@@ -1,4 +1,4 @@
-import asyncio, random, string, threading
+import asyncio, json, random, re, string, threading
from collections import OrderedDict
from dataclasses import dataclass, field
@@ -32,6 +32,15 @@ from typing import Callable
from helpers.localization import Localization
from helpers import extension
from helpers.errors import RepairableException, InterventionException, HandledException
+from helpers.llm_result import (
+ LLMResult,
+ RESPONSE_METADATA_KEY,
+ function_call_output_item,
+ metadata_from_llm_result,
+ result_from_metadata,
+)
+from helpers.litellm_transport import ResponsesTransport
+from helpers.responses_tools import build_responses_function_tools, original_tool_name
class AgentContextType(Enum):
USER = "user"
@@ -329,6 +338,8 @@ class LoopData:
self.system = []
self.user_message: history.Message | None = None
self.history_output: list[history.OutputMessage] = []
+ self.protocol_temporary: OrderedDict[str, history.MessageContent] = OrderedDict()
+ self.protocol_persistent: OrderedDict[str, history.MessageContent] = OrderedDict()
self.extras_temporary: OrderedDict[str, history.MessageContent] = OrderedDict()
self.extras_persistent: OrderedDict[str, history.MessageContent] = OrderedDict()
self.last_response = ""
@@ -346,6 +357,9 @@ class Agent:
DATA_NAME_SUPERIOR = "_superior"
DATA_NAME_SUBORDINATE = "_subordinate"
DATA_NAME_CTX_WINDOW = "ctx_window"
+ DATA_NAME_RESPONSES_STATE = "responses_state"
+ DATA_NAME_RESPONSES_TOOL_NAME_MAP = "responses_tool_name_map"
+ DATA_NAME_RESPONSES_COMPUTER_SESSION = "responses_computer_session_id"
@extension.extensible
def __init__(
@@ -468,11 +482,12 @@ class Agent:
return stop_response
# call main LLM
- agent_response, _reasoning = await self.call_chat_model(
+ llm_result = await self.call_chat_model_turn(
messages=prompt,
response_callback=stream_callback,
reasoning_callback=reasoning_callback,
)
+ agent_response = llm_result.response
await self.handle_intervention(agent_response)
# Notify extensions to finalize their stream filters
@@ -492,7 +507,12 @@ class Agent:
): # if assistant_response is the same as last message in history, let him know
# Append the assistant's response to the history
log_item = self.loop_data.params_temporary.get("log_item_generating")
- self.hist_add_ai_response(agent_response, id=log_item.id if log_item else "")
+ assistant_message = self.hist_add_ai_response(
+ agent_response,
+ id=log_item.id if log_item else "",
+ llm_result=llm_result,
+ )
+ self._remember_llm_result_state(llm_result, assistant_message)
# Append warning message to the history
warning_msg = self.read_prompt("fw.msg_repeat.md")
wmsg = self.hist_add_warning(message=warning_msg)
@@ -504,9 +524,16 @@ class Agent:
else: # otherwise proceed with tool
# Append the assistant's response to the history
log_item = self.loop_data.params_temporary.get("log_item_generating")
- self.hist_add_ai_response(agent_response, id=log_item.id if log_item else "")
+ assistant_message = self.hist_add_ai_response(
+ agent_response,
+ id=log_item.id if log_item else "",
+ llm_result=llm_result,
+ )
+ self._remember_llm_result_state(llm_result, assistant_message)
# process tools requested in agent message
- tools_result = await self.process_tools(agent_response)
+ tools_result = await self.process_llm_result_tools(
+ llm_result
+ )
if tools_result: # final response of message loop available
return tools_result # break the execution if the task is done
@@ -555,24 +582,28 @@ class Agent:
# concatenate system prompt
system_text = "\n\n".join(loop_data.system)
- # join extras
- extras = history.Message( # type: ignore[abstract]
- False,
- content=self.read_prompt(
- "agent.context.extras.md",
- extras=dirty_json.stringify(
- {**loop_data.extras_persistent, **loop_data.extras_temporary}
- ),
- ),
- ).output()
+ # join protocol and extras
+ protocol = self._build_context_message(
+ "agent.context.protocol.md",
+ "protocol",
+ {**loop_data.protocol_persistent, **loop_data.protocol_temporary},
+ include_empty=False,
+ )
+ extras = self._build_context_message(
+ "agent.context.extras.md",
+ "extras",
+ {**loop_data.extras_persistent, **loop_data.extras_temporary},
+ include_empty=True,
+ )
+ loop_data.protocol_temporary.clear()
loop_data.extras_temporary.clear()
- # convert history + extras to LLM format
+ # convert protocol + history + extras to LLM format
history_langchain: list[BaseMessage] = history.output_langchain(
- loop_data.history_output + extras
+ protocol + loop_data.history_output + extras
)
- # build full prompt from system prompt, message history and extrS
+ # build full prompt from system prompt, protocol, message history and extras
full_prompt: list[BaseMessage] = [
SystemMessage(content=system_text),
*history_langchain,
@@ -590,6 +621,24 @@ class Agent:
return full_prompt
+ def _build_context_message(
+ self,
+ prompt_file: str,
+ variable_name: str,
+ values: dict[str, history.MessageContent],
+ include_empty: bool,
+ ) -> list[history.OutputMessage]:
+ if not include_empty and not values:
+ return []
+
+ return history.Message( # type: ignore[abstract]
+ False,
+ content=self.read_prompt(
+ prompt_file,
+ **{variable_name: dirty_json.stringify(values)},
+ ),
+ ).output()
+
@extension.extensible
async def handle_exception(self, location: str, exception: Exception):
if exception:
@@ -664,7 +713,12 @@ class Agent:
@extension.extensible
def hist_add_message(
- self, ai: bool, content: history.MessageContent, tokens: int = 0, id: str = ""
+ self,
+ ai: bool,
+ content: history.MessageContent,
+ tokens: int = 0,
+ id: str = "",
+ metadata: dict[str, Any] | None = None,
):
self.last_message = Localization.get().now()
# Allow extensions to process content before adding to history
@@ -673,7 +727,11 @@ class Agent:
"hist_add_before", self, content_data=content_data, ai=ai
)
return self.history.add_message(
- ai=ai, content=content_data["content"], tokens=tokens, id=id
+ ai=ai,
+ content=content_data["content"],
+ tokens=tokens,
+ id=id,
+ metadata=metadata,
)
@extension.extensible
@@ -706,10 +764,17 @@ class Agent:
return msg
@extension.extensible
- def hist_add_ai_response(self, message: str, id: str = ""):
+ def hist_add_ai_response(
+ self, message: str, id: str = "", llm_result: LLMResult | None = None
+ ):
self.loop_data.last_response = message
content = self.parse_prompt("fw.ai_response.md", message=message)
- return self.hist_add_message(True, content=content, id=id)
+ return self.hist_add_message(
+ True,
+ content=content,
+ id=id,
+ metadata=metadata_from_llm_result(llm_result),
+ )
@extension.extensible
def hist_add_warning(self, message: history.MessageContent, id: str = ""):
@@ -719,13 +784,28 @@ class Agent:
@extension.extensible
def hist_add_tool_result(self, tool_name: str, tool_result: str, **kwargs):
msg_id = kwargs.pop("id", "")
+ responses_item = kwargs.pop("_responses_output_item", None) or kwargs.pop(
+ "responses_item", None
+ )
+ metadata = (
+ {
+ RESPONSE_METADATA_KEY: {
+ "input_items": [responses_item],
+ "output_items": [],
+ "mode": "responses",
+ "state": "provider",
+ }
+ }
+ if isinstance(responses_item, dict)
+ else None
+ )
data = {
"tool_name": tool_name,
"tool_result": tool_result,
**kwargs,
}
extension.call_extensions_sync("hist_add_tool_result", self, data=data)
- return self.hist_add_message(False, content=data, id=msg_id)
+ return self.hist_add_message(False, content=data, id=msg_id, metadata=metadata)
def concat_messages(
self, messages
@@ -830,6 +910,164 @@ class Agent:
return response, reasoning
+ @extension.extensible
+ async def call_chat_model_turn(
+ self,
+ messages: list[BaseMessage],
+ response_callback: Callable[[str, str], Awaitable[str | None]] | None = None,
+ reasoning_callback: Callable[[str, str], Awaitable[None]] | None = None,
+ background: bool = False,
+ explicit_caching: bool = True,
+ ) -> LLMResult:
+ model = self.get_chat_model()
+ model_kwargs = getattr(model, "kwargs", {}) if model else {}
+ if isinstance(model_kwargs, dict) and model_kwargs.get("responses_delete_on_chat_delete") is False:
+ self.set_data("responses_delete_on_chat_delete", False)
+ response_tools, name_map = build_responses_function_tools(self)
+ self.set_data(Agent.DATA_NAME_RESPONSES_TOOL_NAME_MAP, name_map)
+
+ call_data = {
+ "model": model,
+ "messages": messages,
+ "response_callback": response_callback,
+ "reasoning_callback": reasoning_callback,
+ "background": background,
+ "explicit_caching": explicit_caching,
+ "a0_responses_function_tools": response_tools,
+ }
+
+ previous_state = self._responses_state_for_model(model)
+ if previous_state:
+ history_counter = int(previous_state.get("history_counter", 0) or 0)
+ call_data["previous_response_id"] = previous_state.get("response_id", "")
+ call_data["responses_input_items"] = self._responses_input_items_since(
+ model,
+ history_counter,
+ )
+ call_data["responses_local_input_items"] = self._responses_prompt_input_items(
+ model,
+ messages,
+ )
+
+ await extension.call_extensions_async(
+ "chat_model_call_before", self, call_data=call_data
+ )
+
+ turn_kwargs = {
+ "a0_responses_function_tools": call_data.get(
+ "a0_responses_function_tools"
+ ),
+ "responses_local_input_items": call_data.get(
+ "responses_local_input_items"
+ ),
+ }
+ for key in (
+ "responses_builtin_tools",
+ "responses_state",
+ "previous_response_id",
+ "responses_input_items",
+ ):
+ if call_data.get(key) is not None:
+ turn_kwargs[key] = call_data.get(key)
+
+ llm_result = await call_data["model"].unified_turn(
+ messages=call_data["messages"],
+ reasoning_callback=call_data["reasoning_callback"],
+ response_callback=call_data["response_callback"],
+ rate_limiter_callback=(
+ self.rate_limiter_callback if not call_data["background"] else None
+ ),
+ explicit_caching=call_data["explicit_caching"],
+ **turn_kwargs,
+ )
+
+ downgraded = llm_result.capability.get("builtin_tool_downgrades")
+ if downgraded:
+ self.context.log.log(
+ type="info",
+ heading="Responses capability downgrade",
+ content=(
+ "Provider rejected Responses built-in tool(s); omitted: "
+ + ", ".join(str(item) for item in downgraded)
+ ),
+ )
+
+ await extension.call_extensions_async(
+ "chat_model_call_after",
+ self,
+ call_data=call_data,
+ response=llm_result.response,
+ reasoning=llm_result.reasoning,
+ )
+
+ return llm_result
+
+ def _responses_state_for_model(self, model: Any) -> dict[str, Any]:
+ state = self.get_data(Agent.DATA_NAME_RESPONSES_STATE)
+ if not isinstance(state, dict):
+ return {}
+ provider_model_key = str(getattr(model, "model_name", "") or "")
+ if state.get("provider_model_key") != provider_model_key:
+ return {}
+ if not state.get("response_id"):
+ return {}
+ return state
+
+ def _responses_input_items_since(
+ self, model: Any, sequence: int
+ ) -> list[dict[str, Any]]:
+ items: list[dict[str, Any]] = []
+ for message in self.history.messages_since(sequence):
+ items.extend(self._responses_input_items_for_message(model, message))
+ return items
+
+ def _responses_input_items_for_message(
+ self, model: Any, message: history.Message
+ ) -> list[dict[str, Any]]:
+ result = result_from_metadata(message.metadata)
+ if result:
+ if message.ai and result.output_items:
+ return [item.to_dict() for item in result.output_items]
+ if not message.ai and result.input_items:
+ return [dict(item) for item in result.input_items]
+
+ output = message.output()
+ langchain_messages = history.output_langchain(output)
+ if hasattr(model, "_convert_messages"):
+ converted = model._convert_messages(langchain_messages)
+ return ResponsesTransport.input_from_messages(converted)
+ return []
+
+ def _responses_prompt_input_items(
+ self, model: Any, messages: list[BaseMessage]
+ ) -> list[dict[str, Any]]:
+ if not hasattr(model, "_convert_messages"):
+ return []
+ converted = model._convert_messages(messages)
+ return ResponsesTransport.input_from_messages(converted)
+
+ def _remember_llm_result_state(
+ self, llm_result: LLMResult, history_message: history.Message
+ ) -> None:
+ if not llm_result.response_id:
+ return
+ current = self.get_data(Agent.DATA_NAME_RESPONSES_STATE)
+ response_ids = []
+ if isinstance(current, dict) and isinstance(current.get("response_ids"), list):
+ response_ids = [str(item) for item in current["response_ids"] if item]
+ if llm_result.response_id not in response_ids:
+ response_ids.append(llm_result.response_id)
+ self.set_data(
+ Agent.DATA_NAME_RESPONSES_STATE,
+ {
+ "response_id": llm_result.response_id,
+ "previous_response_id": llm_result.previous_response_id,
+ "provider_model_key": llm_result.provider_model_key,
+ "history_counter": history_message.sequence,
+ "response_ids": response_ids,
+ },
+ )
+
@extension.extensible
async def rate_limiter_callback(
self, message: str, key: str, total: int, limit: int
@@ -863,6 +1101,310 @@ class Agent:
while self.context.paused:
await asyncio.sleep(0.1)
+ async def process_llm_result_tools(self, llm_result: LLMResult):
+ await self._log_response_builtin_items(llm_result)
+ if llm_result.function_calls:
+ for function_call in llm_result.function_calls:
+ name_map = self.get_data(Agent.DATA_NAME_RESPONSES_TOOL_NAME_MAP)
+ tool_name = original_tool_name(function_call.name, name_map)
+ response_item_factory = lambda response, call=function_call: function_call_output_item(
+ call.call_id,
+ response.message,
+ )
+ result = await self._execute_tool_request(
+ tool_name=tool_name,
+ tool_args=function_call.arguments,
+ message=llm_result.response,
+ raw_tool_name=tool_name,
+ responses_item_factory=response_item_factory,
+ )
+ if result:
+ return result
+ return None
+ if llm_result.builtin_items and not llm_result.response:
+ return None
+ if (
+ llm_result.mode == "responses"
+ and llm_result.response
+ and extract_tools.json_parse_dirty(llm_result.response) is None
+ ):
+ return llm_result.response
+ return await self.process_tools(llm_result.response)
+
+ async def _execute_tool_request(
+ self,
+ tool_name: str,
+ tool_args: dict,
+ message: str,
+ raw_tool_name: str = "",
+ responses_item_factory: Callable[[Any], dict[str, Any]] | None = None,
+ ):
+ raw_tool_name = raw_tool_name or tool_name
+ tool_method = None
+ tool = None
+
+ try:
+ import helpers.mcp_handler as mcp_helper
+
+ mcp_tool_candidate = mcp_helper.MCPConfig.get_instance().get_tool(
+ self, tool_name
+ )
+ if mcp_tool_candidate:
+ tool = mcp_tool_candidate
+ except ImportError:
+ PrintStyle(
+ background_color="black", font_color="yellow", padding=True
+ ).print("MCP helper module not found. Skipping MCP tool lookup.")
+ except Exception as e:
+ PrintStyle(background_color="black", font_color="red", padding=True).print(
+ f"Failed to get MCP tool '{tool_name}': {e}"
+ )
+
+ if not tool:
+ tool = self.get_tool(
+ name=tool_name,
+ method=tool_method,
+ args=tool_args,
+ message=message,
+ loop_data=self.loop_data,
+ )
+
+ if not tool:
+ error_detail = (
+ f"Tool '{raw_tool_name}' not found or could not be initialized."
+ )
+ wmsg = self.hist_add_warning(error_detail)
+ PrintStyle(font_color="red", padding=True).print(error_detail)
+ self.context.log.log(
+ type="warning",
+ content=f"{self.agent_name}: {error_detail}",
+ id=wmsg.id,
+ )
+ return None
+
+ self.loop_data.current_tool = tool # type: ignore
+ try:
+ await self.handle_intervention()
+
+ await tool.before_execution(**tool_args)
+ await self.handle_intervention()
+
+ await extension.call_extensions_async(
+ "tool_execute_before",
+ self,
+ tool_args=tool_args or {},
+ tool_name=tool_name,
+ )
+
+ response = await tool.execute(**tool_args)
+ await self.handle_intervention()
+
+ await extension.call_extensions_async(
+ "tool_execute_after",
+ self,
+ response=response,
+ tool_name=tool_name,
+ )
+
+ if responses_item_factory:
+ response.additional = {
+ **(response.additional or {}),
+ "_responses_output_item": responses_item_factory(response),
+ }
+
+ await tool.after_execution(response)
+ await self.handle_intervention()
+
+ if response.break_loop:
+ self._clear_responses_pending_state()
+ return response.message
+ finally:
+ self.loop_data.current_tool = None
+ return None
+
+ async def _log_response_builtin_items(self, llm_result: LLMResult) -> None:
+ for item in llm_result.builtin_items:
+ if item.type == "computer_call":
+ await self._handle_responses_computer_call(item.data)
+ continue
+ if item.type == "mcp_approval_request":
+ self._handle_responses_mcp_approval_request(item.data)
+ continue
+ self.context.log.log(
+ type="info",
+ heading=f"Responses tool item: {item.type}",
+ content=json.dumps(item.data, ensure_ascii=False, default=str),
+ )
+
+ async def _handle_responses_computer_call(self, item: dict[str, Any]) -> None:
+ safety_checks = item.get("pending_safety_checks") or item.get("safety_checks")
+ if safety_checks:
+ message = (
+ "Responses computer_call requested safety-check acknowledgement. "
+ "Agent Zero requires explicit user acknowledgement before executing it."
+ )
+ output_item = {
+ "type": "computer_call_output",
+ "call_id": str(item.get("call_id") or item.get("id") or ""),
+ "output": {"type": "input_text", "text": message},
+ }
+ self.hist_add_tool_result(
+ "computer_call",
+ message,
+ responses_item=output_item,
+ )
+ self.context.log.log(type="warning", content=message)
+ return
+
+ args = self._computer_call_args(item)
+ if not args:
+ message = "Responses computer_call action is unsupported by Agent Zero."
+ output_item = {
+ "type": "computer_call_output",
+ "call_id": str(item.get("call_id") or item.get("id") or ""),
+ "output": {"type": "input_text", "text": message},
+ }
+ self.hist_add_tool_result(
+ "computer_call",
+ message,
+ responses_item=output_item,
+ )
+ self.context.log.log(type="warning", content=message)
+ return
+
+ if args.get("action") != "start_session" and not args.get("session_id"):
+ session_id = str(
+ self.get_data(Agent.DATA_NAME_RESPONSES_COMPUTER_SESSION) or ""
+ )
+ if session_id:
+ args["session_id"] = session_id
+
+ response_item_factory = lambda response: self._computer_call_output_item(
+ item,
+ response,
+ )
+ result = await self._execute_tool_request(
+ tool_name="computer_use_remote",
+ tool_args=args,
+ message=json.dumps(item, ensure_ascii=False, default=str),
+ raw_tool_name="computer_call",
+ responses_item_factory=response_item_factory,
+ )
+ _ = result
+
+ def _handle_responses_mcp_approval_request(self, item: dict[str, Any]) -> None:
+ request_id = str(
+ item.get("approval_request_id") or item.get("id") or item.get("call_id") or ""
+ )
+ message = (
+ "Responses MCP approval request received. Agent Zero denied it because "
+ "provider-hosted MCP approval requires explicit user approval."
+ )
+ output_item = {
+ "type": "mcp_approval_response",
+ "approval_request_id": request_id,
+ "approve": False,
+ }
+ self.hist_add_tool_result(
+ "mcp_approval_request",
+ message,
+ responses_item=output_item,
+ )
+ self.context.log.log(
+ type="warning",
+ heading="Responses MCP approval required",
+ content=message,
+ )
+
+ def _computer_call_args(self, item: dict[str, Any]) -> dict[str, Any]:
+ action = item.get("action")
+ action_data = dict(action) if isinstance(action, dict) else {}
+ action_type = str(
+ action_data.get("type")
+ or action_data.get("action")
+ or item.get("action_type")
+ or ""
+ ).strip().lower()
+ args: dict[str, Any] = {}
+
+ if action_type in {"screenshot", "capture"}:
+ args["action"] = "capture"
+ elif action_type in {"move", "mousemove"}:
+ args.update({"action": "move", "x": action_data.get("x"), "y": action_data.get("y")})
+ elif action_type in {"click", "double_click"}:
+ args.update(
+ {
+ "action": "click",
+ "x": action_data.get("x"),
+ "y": action_data.get("y"),
+ "button": action_data.get("button", "left"),
+ "count": 2 if action_type == "double_click" else action_data.get("count", 1),
+ }
+ )
+ elif action_type == "scroll":
+ args.update(
+ {
+ "action": "scroll",
+ "dx": action_data.get("dx", action_data.get("scroll_x", 0)),
+ "dy": action_data.get("dy", action_data.get("scroll_y", 0)),
+ }
+ )
+ elif action_type in {"keypress", "key"}:
+ args.update(
+ {
+ "action": "key",
+ "keys": action_data.get("keys") or action_data.get("key"),
+ }
+ )
+ elif action_type in {"type", "input_text"}:
+ args.update({"action": "type", "text": action_data.get("text", "")})
+ else:
+ return {}
+
+ session_id = item.get("session_id") or action_data.get("session_id")
+ if session_id:
+ args["session_id"] = session_id
+ return args
+
+ def _computer_call_output_item(
+ self, source_item: dict[str, Any], response: Any
+ ) -> dict[str, Any]:
+ output: dict[str, Any] = {
+ "type": "input_text",
+ "text": str(getattr(response, "message", "") or ""),
+ }
+ additional = getattr(response, "additional", None)
+ raw_content = additional.get("raw_content") if isinstance(additional, dict) else None
+ if isinstance(raw_content, list):
+ for content in raw_content:
+ if not isinstance(content, dict):
+ continue
+ if content.get("type") != "image_url":
+ continue
+ image_url = content.get("image_url")
+ url = image_url.get("url") if isinstance(image_url, dict) else image_url
+ if url:
+ output = {"type": "input_image", "image_url": url}
+ break
+
+ session_id_match = re_search_session_id(str(getattr(response, "message", "") or ""))
+ if session_id_match:
+ self.set_data(Agent.DATA_NAME_RESPONSES_COMPUTER_SESSION, session_id_match)
+
+ return {
+ "type": "computer_call_output",
+ "call_id": str(source_item.get("call_id") or source_item.get("id") or ""),
+ "output": output,
+ }
+
+ def _clear_responses_pending_state(self) -> None:
+ state = self.get_data(Agent.DATA_NAME_RESPONSES_STATE)
+ if isinstance(state, dict):
+ state = dict(state)
+ state.pop("response_id", None)
+ state.pop("previous_response_id", None)
+ self.set_data(Agent.DATA_NAME_RESPONSES_STATE, state)
+
@extension.extensible
async def process_tools(self, msg: str):
# search for tool usage requests in agent message
@@ -1037,3 +1579,8 @@ class Agent:
loop_data=loop_data,
**kwargs,
)
+
+
+def re_search_session_id(text: str) -> str:
+ match = re.search(r"session_id=([A-Za-z0-9_.:-]+)", text or "")
+ return match.group(1) if match else ""
diff --git a/agents/AGENTS.md b/agents/AGENTS.md
index cce3cbc96..ae6bda3c5 100644
--- a/agents/AGENTS.md
+++ b/agents/AGENTS.md
@@ -31,4 +31,14 @@
## Child DOX Index
-No child DOX files.
+Direct child DOX files:
+
+| Child | Scope |
+| --- | --- |
+| [_example/AGENTS.md](_example/AGENTS.md) | Reference profile demonstrating profile-local prompts, tools, and extensions. |
+| [agent0/AGENTS.md](agent0/AGENTS.md) | Main user-facing Agent Zero profile metadata. |
+| [default/AGENTS.md](default/AGENTS.md) | Base profile metadata and inherited prompt specifics. |
+| [developer/AGENTS.md](developer/AGENTS.md) | Software development specialist profile. |
+| [hacker/AGENTS.md](hacker/AGENTS.md) | Cyber security and penetration testing specialist profile. |
+| [researcher/AGENTS.md](researcher/AGENTS.md) | Research, data analysis, and reporting specialist profile. |
+| [tiny-local/AGENTS.md](tiny-local/AGENTS.md) | Small/local model profile with an action-first communication prompt. |
diff --git a/agents/_example/AGENTS.md b/agents/_example/AGENTS.md
new file mode 100644
index 000000000..555428ee6
--- /dev/null
+++ b/agents/_example/AGENTS.md
@@ -0,0 +1,33 @@
+# Example Agent Profile DOX
+
+## Purpose
+
+- Own the reference profile used to demonstrate bundled profile layout.
+- Show how profile-local prompts, tools, and extensions fit beside `agent.yaml`.
+
+## Ownership
+
+- `agent.yaml` owns the example profile metadata.
+- `prompts/` owns prompt override examples.
+- `tools/` owns profile-local tool examples.
+- `extensions/` owns profile-local lifecycle extension examples.
+
+## Local Contracts
+
+- Keep this profile generic, minimal, and safe to copy into user or plugin profile work.
+- Do not add product behavior here that should live in a real bundled profile.
+- Profile-local tools and extensions must follow the same contracts as root tools and extensions.
+
+## Work Guidance
+
+- Prefer simple examples that illustrate structure over complex behavior.
+- Update related skill guidance when the example profile layout changes.
+
+## Verification
+
+- Manually inspect YAML and prompt filenames after edits.
+- Run profile-loading tests when changing discovery or profile schema assumptions.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/agents/agent0/AGENTS.md b/agents/agent0/AGENTS.md
new file mode 100644
index 000000000..65528190a
--- /dev/null
+++ b/agents/agent0/AGENTS.md
@@ -0,0 +1,31 @@
+# Agent 0 Profile DOX
+
+## Purpose
+
+- Own the main user-facing Agent Zero profile metadata.
+- Keep the primary assistant profile discoverable and distinct from subordinate specialist profiles.
+
+## Ownership
+
+- `agent.yaml` owns the profile title, description, and delegation context.
+- Prompt behavior is inherited from the default profile unless a local prompt override is added.
+
+## Local Contracts
+
+- Keep `Agent 0` suitable as the direct conversation agent for the system.
+- Do not add narrow specialist behavior that belongs in `developer/`, `researcher/`, `hacker/`, or a custom user profile.
+- Do not store user-specific preferences, provider settings, or secrets in this profile.
+
+## Work Guidance
+
+- Keep metadata concise because it appears in profile selection and delegation contexts.
+- Coordinate substantial behavior changes with default prompts and WebUI profile selection.
+
+## Verification
+
+- Manually inspect `agent.yaml` for valid YAML after edits.
+- Run profile-loading tests when changing schema or discovery behavior.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/agents/default/AGENTS.md b/agents/default/AGENTS.md
new file mode 100644
index 000000000..130d84cc1
--- /dev/null
+++ b/agents/default/AGENTS.md
@@ -0,0 +1,32 @@
+# Default Agent Profile DOX
+
+## Purpose
+
+- Own base profile metadata and default prompt specifics inherited by specialized profiles.
+- Provide the shared behavior layer for bundled and custom profiles.
+
+## Ownership
+
+- `agent.yaml` owns default profile metadata.
+- `agent.system.main.specifics.md` owns default profile-specific system prompt content.
+- Additional prompt overrides under this directory become shared defaults unless a child profile overrides them.
+
+## Local Contracts
+
+- Keep default behavior broad, framework-compatible, and safe for inheritance.
+- Avoid role-specific instructions that belong in specialist profiles.
+- Prompt filenames must match the framework prompt override names they target.
+
+## Work Guidance
+
+- Prefer small, explicit prompt changes with clear inheritance impact.
+- Check bundled specialist profiles after changing default behavior.
+
+## Verification
+
+- Manually inspect YAML and prompt rendering assumptions after edits.
+- Run prompt/profile tests when changing inherited prompt behavior.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/agents/developer/AGENTS.md b/agents/developer/AGENTS.md
new file mode 100644
index 000000000..b2d189fff
--- /dev/null
+++ b/agents/developer/AGENTS.md
@@ -0,0 +1,32 @@
+# Developer Agent Profile DOX
+
+## Purpose
+
+- Own the bundled software development specialist profile.
+- Keep development, debugging, refactoring, and architecture behavior separate from general agent defaults.
+
+## Ownership
+
+- `agent.yaml` owns title, description, and delegation context for software development work.
+- `prompts/` owns developer-specific prompt overrides when present.
+- `extensions/` owns developer-specific lifecycle hooks when present.
+
+## Local Contracts
+
+- Keep this profile focused on software engineering tasks.
+- Do not hardcode repository-local credentials, paths, or project-specific conventions.
+- Prompt overrides must preserve the framework tool-call and response contracts.
+
+## Work Guidance
+
+- Align developer behavior with the root engineering and tool contracts.
+- Prefer profile prompt edits over core prompt edits when the behavior is specific to development tasks.
+
+## Verification
+
+- Manually inspect `agent.yaml` for valid YAML after edits.
+- Run prompt/profile tests when changing profile loading or developer prompt behavior.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/agents/hacker/AGENTS.md b/agents/hacker/AGENTS.md
new file mode 100644
index 000000000..9daeaed00
--- /dev/null
+++ b/agents/hacker/AGENTS.md
@@ -0,0 +1,31 @@
+# Hacker Agent Profile DOX
+
+## Purpose
+
+- Own the bundled cyber security and penetration testing specialist profile.
+- Keep security-audit behavior scoped to this profile instead of default agent behavior.
+
+## Ownership
+
+- `agent.yaml` owns title, description, and delegation context for security work.
+- `prompts/` owns security-specific prompt overrides when present.
+
+## Local Contracts
+
+- Keep the profile focused on authorized security analysis, vulnerability research, and defensive audit tasks.
+- Do not add secrets, target-specific credentials, or local environment assumptions.
+- Preserve the framework tool-call contract and safety expectations.
+
+## Work Guidance
+
+- Keep security instructions operational and bounded to legitimate testing contexts.
+- Coordinate broad safety changes with core prompts and relevant tests.
+
+## Verification
+
+- Manually inspect `agent.yaml` for valid YAML after edits.
+- Run prompt/profile tests when changing profile discovery or security prompt behavior.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/agents/researcher/AGENTS.md b/agents/researcher/AGENTS.md
new file mode 100644
index 000000000..dd7e41def
--- /dev/null
+++ b/agents/researcher/AGENTS.md
@@ -0,0 +1,31 @@
+# Researcher Agent Profile DOX
+
+## Purpose
+
+- Own the bundled research, data analysis, and reporting specialist profile.
+- Keep evidence-gathering and report-oriented behavior separate from general defaults.
+
+## Ownership
+
+- `agent.yaml` owns title, description, and delegation context for research work.
+- `prompts/` owns researcher-specific prompt overrides when present.
+
+## Local Contracts
+
+- Keep this profile focused on information gathering, analysis, synthesis, and reporting.
+- Do not bake in project-specific sources, credentials, or local paths.
+- Preserve the framework tool-call and response contracts.
+
+## Work Guidance
+
+- Prefer prompt changes that improve citation, evidence handling, and analysis quality for research tasks.
+- Coordinate broad research behavior changes with document or browser plugin contracts when relevant.
+
+## Verification
+
+- Manually inspect `agent.yaml` for valid YAML after edits.
+- Run prompt/profile tests when changing discovery or researcher prompt behavior.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/agents/tiny-local/AGENTS.md b/agents/tiny-local/AGENTS.md
new file mode 100644
index 000000000..86715f4dc
--- /dev/null
+++ b/agents/tiny-local/AGENTS.md
@@ -0,0 +1,38 @@
+# Tiny Local Agent Profile DOX
+
+## Purpose
+
+- Own the bundled Tiny Local profile for small/local chat models.
+- Keep local-model behavior prompt-only and isolated from core framework execution.
+
+## Ownership
+
+- `agent.yaml` owns profile metadata for discovery and profile switching.
+- `prompts/agent.system.main.communication.md` owns the local-model communication contract.
+- `prompts/agent.system.main.solving.md` owns the local-model problem-solving contract and suppresses inherited visible reasoning requirements.
+- `prompts/fw.msg_repeat.md` owns Tiny Local's profile-specific recovery instructions when the framework rejects a duplicate assistant message.
+- `prompts/agent.system.tools.md` owns the Tiny Local tools wrapper and final output-shape reminder after tool listing.
+- `prompts/agent.system.tool.*.md` files own Tiny Local-specific tool examples that avoid inherited reasoning fields and repeated writes.
+
+## Local Contracts
+
+- Preserve the normal Agent Zero tool-call shape: `tool_name` plus `tool_args`.
+- Do not add parser repair, duplicate suppression runtime, model transport, or text-editor runtime behavior here.
+- Duplicate-message handling may be tightened through profile prompts only.
+- Keep prompt text short enough for small local models to follow.
+- Treat continuation requests such as `proceed` or `continue` as commands to execute the next unfinished step, not as prompts for another status response.
+- Do not include user-specific provider names, API keys, local paths, or secrets.
+
+## Work Guidance
+
+- Prefer prompt wording changes over new files when tightening this profile, except when replacing inherited tool examples for local-model compliance.
+- Keep this profile suitable for Ollama, LM Studio, Qwen, and comparable local models.
+
+## Verification
+
+- Render the `tiny-local` system prompt after communication prompt changes.
+- Run `pytest tests/test_default_prompt_budget.py` for prompt and profile regressions.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/agents/tiny-local/agent.yaml b/agents/tiny-local/agent.yaml
new file mode 100644
index 000000000..413dcbfbf
--- /dev/null
+++ b/agents/tiny-local/agent.yaml
@@ -0,0 +1,3 @@
+title: Tiny Local
+description: Action-first profile for small local models that need a minimal tool-call contract.
+context: Use this agent when running small local chat models through Ollama, LM Studio, or similar providers and the model tends to explain actions instead of calling tools.
diff --git a/agents/tiny-local/prompts/agent.system.main.communication.md b/agents/tiny-local/prompts/agent.system.main.communication.md
new file mode 100644
index 000000000..65f79edef
--- /dev/null
+++ b/agents/tiny-local/prompts/agent.system.main.communication.md
@@ -0,0 +1,31 @@
+## Communication
+
+You are Agent Zero. Act on the user's behalf.
+
+When the user asks you to do something, do it directly. Do not explain how the user could do it themselves.
+
+Your visible assistant message must be exactly one valid JSON object.
+
+Use exactly these top-level fields: `"tool_name"` and `"tool_args"`.
+
+Do not include markdown fences, prose before the JSON, prose after the JSON, hidden reasoning, analysis, thoughts, or headlines.
+
+Choose a tool from the tools listed in this system prompt. Do not invent tool names, action names, or generic names such as `read`, `write`, `terminal`, or `multi`.
+
+For a final user-facing answer, use the `response` tool.
+
+Use `response` only when the work is complete, blocked, or the user is only acknowledging completed work.
+
+If the user says "proceed", "continue", "go ahead", "do it", "excellent proceed", or similar after you named a next step or there is unfinished work, do not answer with a promise or status update. Call the next appropriate tool.
+
+Final-answer shape:
+
+`{"tool_name":"response","tool_args":{"text":"Answer briefly."}}`
+
+For work that requires a command, file action, browser action, or any other available tool, call the appropriate tool immediately. Do not explain what command the user could run manually.
+
+If the framework warns that your prior message was malformed, repeated, or reasoning-only, output a corrected JSON tool request immediately without explaining the warning.
+
+When the warning says you sent the same message again, do not resend the same JSON. Change the tool, action, arguments, or final answer so the next message is meaningfully different.
+
+{{ include "agent.system.main.communication_additions.md" }}
diff --git a/agents/tiny-local/prompts/agent.system.main.solving.md b/agents/tiny-local/prompts/agent.system.main.solving.md
new file mode 100644
index 000000000..2e410be4a
--- /dev/null
+++ b/agents/tiny-local/prompts/agent.system.main.solving.md
@@ -0,0 +1,18 @@
+## Problem Solving
+
+Act directly and keep hidden reasoning out of the visible JSON.
+
+For simple questions, answer with the `response` tool.
+
+Continuation words such as "proceed", "continue", "go ahead", "do it", and "excellent proceed" mean execute the next unfinished step. Do not respond by saying you will begin, continue, start, proceed, or investigate. Use a real tool call unless the task is already complete or blocked.
+
+For tasks that need shell commands, files, browser actions, or other capabilities:
+- choose the appropriate listed tool immediately
+- keep one tool call per turn unless the `parallel` tool is listed and truly useful
+- inspect outputs before deciding the next tool call
+- never claim success from timeout output or a still-running command
+- after a successful tool result, do not repeat the same exact tool call
+- after a repeated-message warning, do not repeat the same status response or exact tool request; choose the next different executable action or report a blocker
+- when finished, use the `response` tool with a brief result
+
+Do not include `thoughts`, `headline`, analysis, plans, or prose outside the JSON object.
diff --git a/agents/tiny-local/prompts/agent.system.tool.code_exe.md b/agents/tiny-local/prompts/agent.system.tool.code_exe.md
new file mode 100644
index 000000000..a42a3314f
--- /dev/null
+++ b/agents/tiny-local/prompts/agent.system.tool.code_exe.md
@@ -0,0 +1,24 @@
+### code_execution_tool
+Run terminal, Python, or Node.js commands.
+
+Arguments in `tool_args`:
+- `runtime`: `terminal`, `python`, `nodejs`, or `output`
+- `code`: command or script code
+- `session`: terminal session id; default `0`
+- `reset`: kill a session before running; `true` or `false`
+
+Rules:
+- Put the command or script in `code`.
+- Use `runtime=output` to poll running work.
+- Use `input` for interactive terminal prompts.
+- If a session is stuck, call this tool again with the same `session` and `reset=true`.
+- Do not claim success from timeout output or a still-running command.
+- When counting files, prefer `find` over `ls` so hidden files and type filters are handled.
+
+Examples:
+
+`{"tool_name":"code_execution_tool","tool_args":{"runtime":"terminal","session":0,"reset":false,"code":"ls -1 /tmp | wc -l"}}`
+
+`{"tool_name":"code_execution_tool","tool_args":{"runtime":"python","session":0,"reset":false,"code":"import os\nprint(os.getcwd())"}}`
+
+`{"tool_name":"code_execution_tool","tool_args":{"runtime":"output","session":0}}`
diff --git a/agents/tiny-local/prompts/agent.system.tool.response.md b/agents/tiny-local/prompts/agent.system.tool.response.md
new file mode 100644
index 000000000..de0709179
--- /dev/null
+++ b/agents/tiny-local/prompts/agent.system.tool.response.md
@@ -0,0 +1,13 @@
+### response
+Final answer to the user.
+
+Use this tool only when the task is done, blocked, or no tool is needed.
+
+Do not use this tool for "proceed", "continue", "go ahead", or similar continuation requests when there is an unfinished next step. Call a real tool instead.
+
+Arguments in `tool_args`:
+- `text`: concise final answer text
+
+Example:
+
+`{"tool_name":"response","tool_args":{"text":"There are 24 files in /tmp."}}`
diff --git a/agents/tiny-local/prompts/agent.system.tool.text_editor.md b/agents/tiny-local/prompts/agent.system.tool.text_editor.md
new file mode 100644
index 000000000..fdf7ad141
--- /dev/null
+++ b/agents/tiny-local/prompts/agent.system.tool.text_editor.md
@@ -0,0 +1,26 @@
+### text_editor
+Read, write, or patch Markdown and plain text files.
+
+Actions in `tool_args.action`:
+- `read`: read a file
+- `write`: create or overwrite a file
+- `patch`: edit an existing file
+
+Common arguments:
+- `path`: absolute file path
+- `content`: full file content for `write`
+- `open_in_canvas`: set `true` when the user explicitly asks to open a Markdown file in the Canvas or Editor
+
+Rules:
+- Use this tool for `.md` and plain text files.
+- Use `write` to create a new Markdown file.
+- If the user asks to open the file in the Canvas or Editor, include `"open_in_canvas": true` in the same `write` or `patch` call.
+- After a successful write or patch result, do not repeat the same tool call. Use the `response` tool unless a different action is needed.
+
+Examples:
+
+`{"tool_name":"text_editor","tool_args":{"action":"write","path":"/a0/usr/workdir/TODO.md","content":"# TODO\n- [ ] First item\n","open_in_canvas":true}}`
+
+`{"tool_name":"text_editor","tool_args":{"action":"read","path":"/a0/usr/workdir/TODO.md"}}`
+
+`{"tool_name":"text_editor","tool_args":{"action":"patch","path":"/a0/usr/workdir/TODO.md","old_text":"- [ ] First item","new_text":"- [x] First item"}}`
diff --git a/agents/tiny-local/prompts/agent.system.tools.md b/agents/tiny-local/prompts/agent.system.tools.md
new file mode 100644
index 000000000..7c50f33b0
--- /dev/null
+++ b/agents/tiny-local/prompts/agent.system.tools.md
@@ -0,0 +1,17 @@
+## Available Tools
+
+Use only the tools listed below. Match tool names exactly.
+
+Every tool request must be exactly one JSON object with only these top-level fields:
+- `tool_name`
+- `tool_args`
+
+Action names are not tool names. Do not invent top-level `multi`, `read`, `write`, `terminal`, or generic batch tools.
+
+{{tools}}
+
+## Tiny Local Output Rule
+
+Some inherited tool examples may show `thoughts` or `headline`. Ignore that shape for this profile.
+
+Do not include `thoughts`, `headline`, analysis, markdown fences, or prose outside the JSON object.
diff --git a/agents/tiny-local/prompts/fw.msg_repeat.md b/agents/tiny-local/prompts/fw.msg_repeat.md
new file mode 100644
index 000000000..10602a558
--- /dev/null
+++ b/agents/tiny-local/prompts/fw.msg_repeat.md
@@ -0,0 +1,13 @@
+You have sent the same message again. You have to do something else.
+
+Your repeated JSON was recorded, but it did not execute another tool. Do not send the same JSON object again.
+
+Choose one different action now:
+- If work is unfinished, call a real tool for the next unfinished step.
+- If your previous JSON used `response` while work remains, replace it with the next real tool call.
+- If a file write or patch already succeeded, read that file or answer with the observed result.
+- If a command already ran, inspect its output or run a different next command.
+- If the user only said "proceed" or "continue", continue with the next real tool call.
+- If no different action is possible, use `response` with a brief blocker.
+
+Output exactly one JSON object with `tool_name` and `tool_args`. No prose or markdown.
diff --git a/api/AGENTS.md b/api/AGENTS.md
index 7e471b399..02344d435 100644
--- a/api/AGENTS.md
+++ b/api/AGENTS.md
@@ -19,17 +19,23 @@
- Keep CSRF and authentication protections intact for browser-facing state-changing endpoints.
- WebSocket handlers must derive from `helpers.ws.WsHandler` and validate event data before using it.
- Do not return secrets, raw environment values, private files, or unfiltered exception details to clients.
+- This directory is a file-documented DOX profile: every direct `*.py` endpoint or WebSocket module must have a same-directory `*.py.dox.md` file named by appending `.dox.md` to the full Python filename.
+- The `*.py.dox.md` file owns endpoint purpose, request/response concepts, auth/CSRF/API-key/loopback assumptions, side effects, important helper dependencies, and verification guidance.
+- When a Python endpoint is added, removed, renamed, or behaviorally changed, update its matching `*.py.dox.md` in the same change.
+- Do not leave stale file-level DOX after endpoint deletion or rename.
## Work Guidance
- Use helpers for shared behavior instead of duplicating persistence, auth, file, project, plugin, or notification logic in endpoints.
- Keep request and response payloads stable; update frontend callers and tests together when payloads change.
- Prefer `Response` for files, redirects, status codes, and plain-text errors; return dictionaries for JSON success payloads.
+- During the DOX pass, verify that every direct `*.py` file has a matching `*.py.dox.md` and that changed endpoint behavior is described there.
## Verification
- Run targeted `pytest tests/test_*api*.py`, endpoint-specific tests, or WebSocket tests after changing handler behavior.
- For auth, CSRF, upload/download, tunnel, or file endpoints, run the nearest security regression tests.
+- Check file-level documentation coverage with a script or shell loop that verifies each `api/*.py` has a matching `api/*.py.dox.md`.
## Child DOX Index
diff --git a/api/agent_profile_set.py b/api/agent_profile_set.py
index 331e21fd0..bec0b2ef1 100644
--- a/api/agent_profile_set.py
+++ b/api/agent_profile_set.py
@@ -1,4 +1,4 @@
-from agent import Agent, AgentContext
+from agent import AgentContext
from helpers import subagents
from helpers.api import ApiHandler, Request, Response
from helpers.persist_chat import save_tmp_chat
@@ -39,11 +39,7 @@ class SetAgentProfile(ApiHandler):
config = initialize_agent(override_settings={"agent_profile": profile})
context.config = config
-
- agent = context.agent0
- while agent:
- agent.config = config
- agent = agent.get_data(Agent.DATA_NAME_SUBORDINATE)
+ context.agent0.config = config
save_tmp_chat(context)
mark_dirty_for_context(context.id, reason="agent_profile_change")
diff --git a/api/agent_profile_set.py.dox.md b/api/agent_profile_set.py.dox.md
new file mode 100644
index 000000000..8e891085d
--- /dev/null
+++ b/api/agent_profile_set.py.dox.md
@@ -0,0 +1,48 @@
+# agent_profile_set.py DOX
+
+## Purpose
+
+- Own the `agent_profile_set.py` API endpoint.
+- This module sets the active agent profile for a chat context and returns profile label metadata.
+- Keep this file-level DOX profile synchronized with `agent_profile_set.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `agent_profile_set.py` owns the runtime implementation.
+- `agent_profile_set.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SetAgentProfile` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+- Top-level functions:
+- `_agent_profile_labels() -> dict[str, str]`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SetAgentProfile` is an `ApiHandler`.
+- `SetAgentProfile` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, settings/state persistence.
+- Switching a chat profile updates the context and top-level agent profile only; existing subordinate agents keep their own profile configs.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`, `helpers.persist_chat`, `helpers.state_monitor_integration`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `str.strip`, `context.is_running`, `_agent_profile_labels`, `initialize_agent`, `context.agent0.config`, `save_tmp_chat`, `mark_dirty_for_context`, `subagents.get_all_agents_list`, `Response`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_subagent_profiles.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/agents.py.dox.md b/api/agents.py.dox.md
new file mode 100644
index 000000000..e7f6895ac
--- /dev/null
+++ b/api/agents.py.dox.md
@@ -0,0 +1,48 @@
+# agents.py DOX
+
+## Purpose
+
+- Own the `agents.py` API endpoint.
+- This module lists available agent profiles for selection and delegation UI flows.
+- Keep this file-level DOX profile synchronized with `agents.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `agents.py` owns the runtime implementation.
+- `agents.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Agents` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Agents` is an `ApiHandler`.
+- `Agents` defines `process(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `subagents.get_all_agents_list`, `Exception`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_default_prompt_budget.py`
+ - `tests/test_office_document_store.py`
+ - `tests/test_projects.py`
+ - `tests/test_skills_runtime.py`
+ - `tests/test_time_travel.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/api_files_get.py.dox.md b/api/api_files_get.py.dox.md
new file mode 100644
index 000000000..c14bf9f13
--- /dev/null
+++ b/api/api_files_get.py.dox.md
@@ -0,0 +1,52 @@
+# api_files_get.py DOX
+
+## Purpose
+
+- Own the `api_files_get.py` API endpoint.
+- This module returns downloadable or inspectable files exposed through the external API surface.
+- Keep this file-level DOX profile synchronized with `api_files_get.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `api_files_get.py` owns the runtime implementation.
+- `api_files_get.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `ApiFilesGet` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_csrf(cls) -> bool`
+ - `requires_api_key(cls) -> bool`
+ - `get_methods(cls) -> list[str]`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `ApiFilesGet` is an `ApiHandler`.
+- `ApiFilesGet` defines `process(...)`.
+- `ApiFilesGet` defines `get_methods(...)`.
+- `ApiFilesGet` defines `requires_auth(...)`.
+- `ApiFilesGet` defines `requires_csrf(...)`.
+- `ApiFilesGet` defines `requires_api_key(...)`.
+- Observed side-effect areas: filesystem reads, filesystem writes, settings/state persistence.
+- Imported dependency areas include: `base64`, `helpers`, `helpers.api`, `helpers.print_style`, `json`, `os`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `Response`, `PrintStyle.error`, `path.startswith`, `PrintStyle`, `json.dumps`, `path.replace`, `files.get_abs_path`, `os.path.basename`, `os.path.exists`, `PrintStyle.warning`, `f.read`, `base64.b64encode.decode`, `base64.b64encode`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/api_log_get.py.dox.md b/api/api_log_get.py.dox.md
new file mode 100644
index 000000000..043a9c9cf
--- /dev/null
+++ b/api/api_log_get.py.dox.md
@@ -0,0 +1,52 @@
+# api_log_get.py DOX
+
+## Purpose
+
+- Own the `api_log_get.py` API endpoint.
+- This module returns API/chat log data for external API clients.
+- Keep this file-level DOX profile synchronized with `api_log_get.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `api_log_get.py` owns the runtime implementation.
+- `api_log_get.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `ApiLogGet` (`ApiHandler`)
+ - `get_methods(cls) -> list[str]`
+ - `requires_auth(cls) -> bool`
+ - `requires_csrf(cls) -> bool`
+ - `requires_api_key(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `ApiLogGet` is an `ApiHandler`.
+- `ApiLogGet` defines `process(...)`.
+- `ApiLogGet` defines `get_methods(...)`.
+- `ApiLogGet` defines `requires_auth(...)`.
+- `ApiLogGet` defines `requires_csrf(...)`.
+- `ApiLogGet` defines `requires_api_key(...)`.
+- Observed side-effect areas: settings/state persistence, secret handling.
+- Imported dependency areas include: `agent`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `AgentContext.use`, `Response`, `context.log.output`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/api_message.py.dox.md b/api/api_message.py.dox.md
new file mode 100644
index 000000000..646939cad
--- /dev/null
+++ b/api/api_message.py.dox.md
@@ -0,0 +1,51 @@
+# api_message.py DOX
+
+## Purpose
+
+- Own the `api_message.py` API endpoint.
+- This module accepts external API messages and dispatches them into Agent Zero chat processing.
+- Keep this file-level DOX profile synchronized with `api_message.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `api_message.py` owns the runtime implementation.
+- `api_message.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `ApiMessage` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_csrf(cls) -> bool`
+ - `requires_api_key(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `ApiMessage` is an `ApiHandler`.
+- `ApiMessage` defines `process(...)`.
+- `ApiMessage` defines `requires_auth(...)`.
+- `ApiMessage` defines `requires_csrf(...)`.
+- `ApiMessage` defines `requires_api_key(...)`.
+- Observed side-effect areas: filesystem reads, filesystem writes, settings/state persistence, secret handling, scheduler state.
+- Imported dependency areas include: `agent`, `base64`, `datetime`, `helpers`, `helpers.api`, `helpers.print_style`, `helpers.projects`, `helpers.security`, `initialize`, `os`, `uuid`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `context.set_data`, `datetime.now`, `Response`, `files.get_abs_path`, `os.makedirs`, `AgentContext.use`, `context.get_data`, `initialize_agent`, `AgentContext`, `context.log.log`, `context.communicate`, `ValueError`, `uuid.uuid4`, `UserMessage`, `task.result`, `PrintStyle.error`, `safe_filename`, `base64.b64decode`, `os.path.join`, `activate_project`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_api_chat_lifetime.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/api_reset_chat.py.dox.md b/api/api_reset_chat.py.dox.md
new file mode 100644
index 000000000..5120f46cd
--- /dev/null
+++ b/api/api_reset_chat.py.dox.md
@@ -0,0 +1,52 @@
+# api_reset_chat.py DOX
+
+## Purpose
+
+- Own the `api_reset_chat.py` API endpoint.
+- This module resets an API-created chat context.
+- Keep this file-level DOX profile synchronized with `api_reset_chat.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `api_reset_chat.py` owns the runtime implementation.
+- `api_reset_chat.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `ApiResetChat` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_csrf(cls) -> bool`
+ - `requires_api_key(cls) -> bool`
+ - `get_methods(cls) -> list[str]`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `ApiResetChat` is an `ApiHandler`.
+- `ApiResetChat` defines `process(...)`.
+- `ApiResetChat` defines `get_methods(...)`.
+- `ApiResetChat` defines `requires_auth(...)`.
+- `ApiResetChat` defines `requires_csrf(...)`.
+- `ApiResetChat` defines `requires_api_key(...)`.
+- Observed side-effect areas: filesystem writes, filesystem deletion, settings/state persistence.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`, `helpers.print_style`, `json`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `AgentContext.use`, `context.reset`, `persist_chat.save_tmp_chat`, `persist_chat.remove_msg_files`, `Response`, `PrintStyle.error`, `PrintStyle`, `json.dumps`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/api_terminate_chat.py.dox.md b/api/api_terminate_chat.py.dox.md
new file mode 100644
index 000000000..541a6d007
--- /dev/null
+++ b/api/api_terminate_chat.py.dox.md
@@ -0,0 +1,52 @@
+# api_terminate_chat.py DOX
+
+## Purpose
+
+- Own the `api_terminate_chat.py` API endpoint.
+- This module terminates an API-created chat context.
+- Keep this file-level DOX profile synchronized with `api_terminate_chat.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `api_terminate_chat.py` owns the runtime implementation.
+- `api_terminate_chat.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `ApiTerminateChat` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_csrf(cls) -> bool`
+ - `requires_api_key(cls) -> bool`
+ - `get_methods(cls) -> list[str]`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `ApiTerminateChat` is an `ApiHandler`.
+- `ApiTerminateChat` defines `process(...)`.
+- `ApiTerminateChat` defines `get_methods(...)`.
+- `ApiTerminateChat` defines `requires_auth(...)`.
+- `ApiTerminateChat` defines `requires_csrf(...)`.
+- `ApiTerminateChat` defines `requires_api_key(...)`.
+- Observed side-effect areas: filesystem writes, filesystem deletion, settings/state persistence.
+- Imported dependency areas include: `agent`, `helpers.api`, `helpers.persist_chat`, `helpers.print_style`, `json`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `AgentContext.use`, `AgentContext.remove`, `remove_chat`, `Response`, `PrintStyle.error`, `PrintStyle`, `json.dumps`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/backup_create.py.dox.md b/api/backup_create.py.dox.md
new file mode 100644
index 000000000..be6076326
--- /dev/null
+++ b/api/backup_create.py.dox.md
@@ -0,0 +1,49 @@
+# backup_create.py DOX
+
+## Purpose
+
+- Own the `backup_create.py` API endpoint.
+- This module handles backup create requests.
+- Keep this file-level DOX profile synchronized with `backup_create.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `backup_create.py` owns the runtime implementation.
+- `backup_create.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `BackupCreate` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_loopback(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `BackupCreate` is an `ApiHandler`.
+- `BackupCreate` defines `process(...)`.
+- `BackupCreate` defines `requires_auth(...)`.
+- `BackupCreate` defines `requires_loopback(...)`.
+- Observed side-effect areas: filesystem writes.
+- Imported dependency areas include: `helpers.api`, `helpers.backup`, `helpers.persist_chat`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `save_tmp_chats`, `BackupService`, `send_file`, `backup_service.create_backup`, `line.strip`, `line.startswith`, `patterns_string.split`, `line.strip.startswith`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_download_toast_regressions.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/backup_get_defaults.py.dox.md b/api/backup_get_defaults.py.dox.md
new file mode 100644
index 000000000..8f2c1966d
--- /dev/null
+++ b/api/backup_get_defaults.py.dox.md
@@ -0,0 +1,47 @@
+# backup_get_defaults.py DOX
+
+## Purpose
+
+- Own the `backup_get_defaults.py` API endpoint.
+- This module handles backup get defaults requests.
+- Keep this file-level DOX profile synchronized with `backup_get_defaults.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `backup_get_defaults.py` owns the runtime implementation.
+- `backup_get_defaults.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `BackupGetDefaults` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_loopback(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `BackupGetDefaults` is an `ApiHandler`.
+- `BackupGetDefaults` defines `process(...)`.
+- `BackupGetDefaults` defines `requires_auth(...)`.
+- `BackupGetDefaults` defines `requires_loopback(...)`.
+- Imported dependency areas include: `helpers.api`, `helpers.backup`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `BackupService`, `backup_service.get_default_backup_metadata`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/backup_inspect.py.dox.md b/api/backup_inspect.py.dox.md
new file mode 100644
index 000000000..363dd63dc
--- /dev/null
+++ b/api/backup_inspect.py.dox.md
@@ -0,0 +1,47 @@
+# backup_inspect.py DOX
+
+## Purpose
+
+- Own the `backup_inspect.py` API endpoint.
+- This module handles backup inspect requests.
+- Keep this file-level DOX profile synchronized with `backup_inspect.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `backup_inspect.py` owns the runtime implementation.
+- `backup_inspect.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `BackupInspect` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_loopback(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `BackupInspect` is an `ApiHandler`.
+- `BackupInspect` defines `process(...)`.
+- `BackupInspect` defines `requires_auth(...)`.
+- `BackupInspect` defines `requires_loopback(...)`.
+- Imported dependency areas include: `helpers.api`, `helpers.backup`, `werkzeug.datastructures`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `BackupService`, `backup_service.inspect_backup`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/backup_preview_grouped.py.dox.md b/api/backup_preview_grouped.py.dox.md
new file mode 100644
index 000000000..24e075981
--- /dev/null
+++ b/api/backup_preview_grouped.py.dox.md
@@ -0,0 +1,47 @@
+# backup_preview_grouped.py DOX
+
+## Purpose
+
+- Own the `backup_preview_grouped.py` API endpoint.
+- This module handles backup preview grouped requests.
+- Keep this file-level DOX profile synchronized with `backup_preview_grouped.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `backup_preview_grouped.py` owns the runtime implementation.
+- `backup_preview_grouped.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `BackupPreviewGrouped` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_loopback(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `BackupPreviewGrouped` is an `ApiHandler`.
+- `BackupPreviewGrouped` defines `process(...)`.
+- `BackupPreviewGrouped` defines `requires_auth(...)`.
+- `BackupPreviewGrouped` defines `requires_loopback(...)`.
+- Imported dependency areas include: `helpers.api`, `helpers.backup`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `BackupService`, `search_filter.strip`, `backup_service.test_patterns`, `search_filter.lower`, `path.strip.split`, `line.strip`, `line.startswith`, `groups.add`, `patterns_string.split`, `path.strip`, `join`, `f.lower`, `line.strip.startswith`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/backup_restore.py.dox.md b/api/backup_restore.py.dox.md
new file mode 100644
index 000000000..78e74737d
--- /dev/null
+++ b/api/backup_restore.py.dox.md
@@ -0,0 +1,49 @@
+# backup_restore.py DOX
+
+## Purpose
+
+- Own the `backup_restore.py` API endpoint.
+- This module handles backup restore requests.
+- Keep this file-level DOX profile synchronized with `backup_restore.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `backup_restore.py` owns the runtime implementation.
+- `backup_restore.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `BackupRestore` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_loopback(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `BackupRestore` is an `ApiHandler`.
+- `BackupRestore` defines `process(...)`.
+- `BackupRestore` defines `requires_auth(...)`.
+- `BackupRestore` defines `requires_loopback(...)`.
+- Observed side-effect areas: filesystem writes, filesystem deletion, settings/state persistence.
+- Imported dependency areas include: `helpers.api`, `helpers.backup`, `helpers.persist_chat`, `json`, `werkzeug.datastructures`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `request.form.get.lower`, `json.loads`, `BackupService`, `load_tmp_chats`, `backup_service.restore_backup`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_self_update_tag_filter.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/backup_restore_preview.py.dox.md b/api/backup_restore_preview.py.dox.md
new file mode 100644
index 000000000..572473a0a
--- /dev/null
+++ b/api/backup_restore_preview.py.dox.md
@@ -0,0 +1,48 @@
+# backup_restore_preview.py DOX
+
+## Purpose
+
+- Own the `backup_restore_preview.py` API endpoint.
+- This module handles backup restore preview requests.
+- Keep this file-level DOX profile synchronized with `backup_restore_preview.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `backup_restore_preview.py` owns the runtime implementation.
+- `backup_restore_preview.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `BackupRestorePreview` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_loopback(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `BackupRestorePreview` is an `ApiHandler`.
+- `BackupRestorePreview` defines `process(...)`.
+- `BackupRestorePreview` defines `requires_auth(...)`.
+- `BackupRestorePreview` defines `requires_loopback(...)`.
+- Observed side-effect areas: filesystem deletion, settings/state persistence.
+- Imported dependency areas include: `helpers.api`, `helpers.backup`, `json`, `werkzeug.datastructures`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `request.form.get.lower`, `json.loads`, `BackupService`, `backup_service.preview_restore`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/backup_test.py b/api/backup_test.py
index b5b43eac8..c7a748ebd 100644
--- a/api/backup_test.py
+++ b/api/backup_test.py
@@ -47,12 +47,13 @@ class BackupTest(ApiHandler):
backup_service = BackupService()
matched_files = await backup_service.test_patterns(metadata, max_files=max_files)
+ truncated = max_files is not None and len(matched_files) >= max_files
return {
"success": True,
"files": matched_files,
"total_count": len(matched_files),
- "truncated": len(matched_files) >= max_files
+ "truncated": truncated
}
except Exception as e:
diff --git a/api/backup_test.py.dox.md b/api/backup_test.py.dox.md
new file mode 100644
index 000000000..a8e06cc6b
--- /dev/null
+++ b/api/backup_test.py.dox.md
@@ -0,0 +1,48 @@
+# backup_test.py DOX
+
+## Purpose
+
+- Own the `backup_test.py` API endpoint.
+- This module handles backup test requests.
+- Keep this file-level DOX profile synchronized with `backup_test.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `backup_test.py` owns the runtime implementation.
+- `backup_test.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `BackupTest` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_loopback(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `BackupTest` is an `ApiHandler`.
+- `BackupTest` defines `process(...)`.
+- `BackupTest` defines `requires_auth(...)`.
+- `BackupTest` defines `requires_loopback(...)`.
+- Imported dependency areas include: `helpers.api`, `helpers.backup`.
+- The `truncated` response flag is true only when a finite `max_files` limit is supplied and the result reaches that limit.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `BackupService`, `backup_service.test_patterns`, `line.strip`, `line.startswith`, `patterns_string.split`, `line.strip.startswith`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/banners.py.dox.md b/api/banners.py.dox.md
new file mode 100644
index 000000000..9d007e7d5
--- /dev/null
+++ b/api/banners.py.dox.md
@@ -0,0 +1,46 @@
+# banners.py DOX
+
+## Purpose
+
+- Own the `banners.py` API endpoint.
+- This module collects alert banners and discovery cards from backend extensions.
+- Keep this file-level DOX profile synchronized with `banners.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `banners.py` owns the runtime implementation.
+- `banners.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `GetBanners` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `GetBanners` is an `ApiHandler`.
+- `GetBanners` defines `process(...)`.
+- Imported dependency areas include: `helpers.api`, `helpers.extension`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `call_extensions_async`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_model_config_api_keys.py`
+ - `tests/test_oauth_static.py`
+ - `tests/test_webui_extension_surfaces.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/cache_reset.py.dox.md b/api/cache_reset.py.dox.md
new file mode 100644
index 000000000..a7164d5d5
--- /dev/null
+++ b/api/cache_reset.py.dox.md
@@ -0,0 +1,53 @@
+# cache_reset.py DOX
+
+## Purpose
+
+- Own the `cache_reset.py` API endpoint.
+- This module handles cache reset API requests.
+- Keep this file-level DOX profile synchronized with `cache_reset.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `cache_reset.py` owns the runtime implementation.
+- `cache_reset.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `CacheReset` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_csrf(cls) -> bool`
+ - `requires_api_key(cls) -> bool`
+ - `requires_loopback(cls) -> bool`
+ - `get_methods(cls) -> list[str]`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `CacheReset` is an `ApiHandler`.
+- `CacheReset` defines `process(...)`.
+- `CacheReset` defines `get_methods(...)`.
+- `CacheReset` defines `requires_auth(...)`.
+- `CacheReset` defines `requires_csrf(...)`.
+- `CacheReset` defines `requires_api_key(...)`.
+- `CacheReset` defines `requires_loopback(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `cache.clear_all`, `cache.clear`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/chat_create.py.dox.md b/api/chat_create.py.dox.md
new file mode 100644
index 000000000..3546f4389
--- /dev/null
+++ b/api/chat_create.py.dox.md
@@ -0,0 +1,45 @@
+# chat_create.py DOX
+
+## Purpose
+
+- Own the `chat_create.py` API endpoint.
+- This module handles chat create requests.
+- Keep this file-level DOX profile synchronized with `chat_create.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `chat_create.py` owns the runtime implementation.
+- `chat_create.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `CreateChat` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `CreateChat` is an `ApiHandler`.
+- `CreateChat` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, model calls, plugin state, settings/state persistence.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`, `mark_dirty_all`, `guids.generate_id`, `current_context.get_data`, `current_context.get_output_data`, `new_context.set_data`, `new_context.set_output_data`, `is_chat_override_allowed`, `settings.get_settings`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_browser_agent_regressions.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/chat_export.py.dox.md b/api/chat_export.py.dox.md
new file mode 100644
index 000000000..03b5a0c91
--- /dev/null
+++ b/api/chat_export.py.dox.md
@@ -0,0 +1,44 @@
+# chat_export.py DOX
+
+## Purpose
+
+- Own the `chat_export.py` API endpoint.
+- This module handles chat export requests.
+- Keep this file-level DOX profile synchronized with `chat_export.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `chat_export.py` owns the runtime implementation.
+- `chat_export.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `ExportChat` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `ExportChat` is an `ApiHandler`.
+- `ExportChat` defines `process(...)`.
+- Observed side-effect areas: filesystem writes.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`, `persist_chat.export_json_chat`, `Exception`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/chat_files_path_get.py.dox.md b/api/chat_files_path_get.py.dox.md
new file mode 100644
index 000000000..63f0a2b6e
--- /dev/null
+++ b/api/chat_files_path_get.py.dox.md
@@ -0,0 +1,44 @@
+# chat_files_path_get.py DOX
+
+## Purpose
+
+- Own the `chat_files_path_get.py` API endpoint.
+- This module handles chat files path get requests.
+- Keep this file-level DOX profile synchronized with `chat_files_path_get.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `chat_files_path_get.py` owns the runtime implementation.
+- `chat_files_path_get.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `GetChatFilesPath` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `GetChatFilesPath` is an `ApiHandler`.
+- `GetChatFilesPath` defines `process(...)`.
+- Observed side-effect areas: settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`, `projects.get_context_project_name`, `Exception`, `files.normalize_a0_path`, `projects.get_project_folder`, `settings.get_settings`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/chat_load.py.dox.md b/api/chat_load.py.dox.md
new file mode 100644
index 000000000..4718a9fc6
--- /dev/null
+++ b/api/chat_load.py.dox.md
@@ -0,0 +1,44 @@
+# chat_load.py DOX
+
+## Purpose
+
+- Own the `chat_load.py` API endpoint.
+- This module handles chat load requests.
+- Keep this file-level DOX profile synchronized with `chat_load.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `chat_load.py` owns the runtime implementation.
+- `chat_load.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `LoadChats` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `LoadChats` is an `ApiHandler`.
+- `LoadChats` defines `process(...)`.
+- Observed side-effect areas: filesystem writes.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `persist_chat.load_json_chats`, `Exception`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/chat_remove.py.dox.md b/api/chat_remove.py.dox.md
new file mode 100644
index 000000000..ac1ffc0f8
--- /dev/null
+++ b/api/chat_remove.py.dox.md
@@ -0,0 +1,44 @@
+# chat_remove.py DOX
+
+## Purpose
+
+- Own the `chat_remove.py` API endpoint.
+- This module handles chat remove requests.
+- Keep this file-level DOX profile synchronized with `chat_remove.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `chat_remove.py` owns the runtime implementation.
+- `chat_remove.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `RemoveChat` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `RemoveChat` is an `ApiHandler`.
+- `RemoveChat` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, filesystem deletion, settings/state persistence, scheduler state.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`, `helpers.task_scheduler`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `scheduler.cancel_tasks_by_context`, `AgentContext.use`, `AgentContext.remove`, `persist_chat.remove_chat`, `scheduler.get_tasks_by_context_id`, `mark_dirty_all`, `context.reset`, `scheduler.reload`, `scheduler.remove_task_by_uuid`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/chat_reset.py.dox.md b/api/chat_reset.py.dox.md
new file mode 100644
index 000000000..39a886a53
--- /dev/null
+++ b/api/chat_reset.py.dox.md
@@ -0,0 +1,44 @@
+# chat_reset.py DOX
+
+## Purpose
+
+- Own the `chat_reset.py` API endpoint.
+- This module handles chat reset requests.
+- Keep this file-level DOX profile synchronized with `chat_reset.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `chat_reset.py` owns the runtime implementation.
+- `chat_reset.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Reset` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Reset` is an `ApiHandler`.
+- `Reset` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, filesystem deletion, settings/state persistence, scheduler state.
+- Imported dependency areas include: `helpers`, `helpers.api`, `helpers.task_scheduler`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `TaskScheduler.get.cancel_tasks_by_context`, `self.use_context`, `context.reset`, `persist_chat.save_tmp_chat`, `persist_chat.remove_msg_files`, `mark_dirty_all`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/csrf_token.py.dox.md b/api/csrf_token.py.dox.md
new file mode 100644
index 000000000..3570d2471
--- /dev/null
+++ b/api/csrf_token.py.dox.md
@@ -0,0 +1,57 @@
+# csrf_token.py DOX
+
+## Purpose
+
+- Own the `csrf_token.py` API endpoint.
+- This module issues or refreshes CSRF tokens for browser API clients.
+- Keep this file-level DOX profile synchronized with `csrf_token.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `csrf_token.py` owns the runtime implementation.
+- `csrf_token.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `GetCsrfToken` (`ApiHandler`)
+ - `get_methods(cls) -> list[str]`
+ - `requires_csrf(cls) -> bool`
+ - `async process(self, input: Input, request: Request) -> Output`
+ - `async check_allowed_origin(self, request: Request)`
+ - `async is_allowed_origin(self, request: Request)`
+ - `get_origin_from_request(self, request: Request)`
+ - `async get_allowed_origins(self) -> list[str]`
+ - `get_default_allowed_origins(self) -> list[str]`
+- Notable constants/configuration names: `ALLOWED_ORIGINS_KEY`.
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `GetCsrfToken` is an `ApiHandler`.
+- `GetCsrfToken` defines `process(...)`.
+- `GetCsrfToken` defines `get_methods(...)`.
+- `GetCsrfToken` defines `requires_csrf(...)`.
+- Observed side-effect areas: filesystem writes, network calls, secret handling, tunnel state.
+- Imported dependency areas include: `fnmatch`, `helpers`, `helpers.api`, `secrets`, `urllib.parse`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `login.is_login_required`, `self.initialize_allowed_origins`, `self.get_origin_from_request`, `urlparse`, `dotenv.get_dotenv_value`, `self.get_default_allowed_origins`, `dotenv.save_dotenv_value`, `self.check_allowed_origin`, `secrets.token_urlsafe`, `runtime.get_runtime_id`, `self.is_allowed_origin`, `self.get_allowed_origins`, `origin.strip`, `join`, `fnmatch.fnmatch`, `split`, `tunnel_api_process`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_http_auth_csrf.py`
+ - `tests/test_self_update_tag_filter.py`
+ - `tests/test_ws_security.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/ctx_window_get.py.dox.md b/api/ctx_window_get.py.dox.md
new file mode 100644
index 000000000..2de01bf37
--- /dev/null
+++ b/api/ctx_window_get.py.dox.md
@@ -0,0 +1,44 @@
+# ctx_window_get.py DOX
+
+## Purpose
+
+- Own the `ctx_window_get.py` API endpoint.
+- This module handles ctx window get API requests.
+- Keep this file-level DOX profile synchronized with `ctx_window_get.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `ctx_window_get.py` owns the runtime implementation.
+- `ctx_window_get.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `GetCtxWindow` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `GetCtxWindow` is an `ApiHandler`.
+- `GetCtxWindow` defines `process(...)`.
+- Observed side-effect areas: secret handling.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`, `agent.get_data`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/delete_work_dir_file.py.dox.md b/api/delete_work_dir_file.py.dox.md
new file mode 100644
index 000000000..536b70fcb
--- /dev/null
+++ b/api/delete_work_dir_file.py.dox.md
@@ -0,0 +1,46 @@
+# delete_work_dir_file.py DOX
+
+## Purpose
+
+- Own the `delete_work_dir_file.py` API endpoint.
+- This module handles workdir file operations for delete work dir file.
+- Keep this file-level DOX profile synchronized with `delete_work_dir_file.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `delete_work_dir_file.py` owns the runtime implementation.
+- `delete_work_dir_file.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `DeleteWorkDirFile` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+- Top-level functions:
+- `async delete_file(file_path: str)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `DeleteWorkDirFile` is an `ApiHandler`.
+- `DeleteWorkDirFile` defines `process(...)`.
+- Observed side-effect areas: filesystem deletion.
+- Imported dependency areas include: `api`, `helpers`, `helpers.api`, `helpers.file_browser`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `FileBrowser`, `browser.delete_file`, `file_path.startswith`, `runtime.call_development_function`, `extension.call_extensions_async`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/delete_work_dir_files.py.dox.md b/api/delete_work_dir_files.py.dox.md
new file mode 100644
index 000000000..2c68d3ca2
--- /dev/null
+++ b/api/delete_work_dir_files.py.dox.md
@@ -0,0 +1,47 @@
+# delete_work_dir_files.py DOX
+
+## Purpose
+
+- Own the `delete_work_dir_files.py` API endpoint.
+- This module handles workdir file operations for delete work dir files.
+- Keep this file-level DOX profile synchronized with `delete_work_dir_files.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `delete_work_dir_files.py` owns the runtime implementation.
+- `delete_work_dir_files.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `DeleteWorkDirFiles` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+- Top-level functions:
+- `async delete_files(paths: list[str]) -> dict`
+- `collapse_nested_paths(paths: list[str]) -> list[str]`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `DeleteWorkDirFiles` is an `ApiHandler`.
+- `DeleteWorkDirFiles` defines `process(...)`.
+- Observed side-effect areas: filesystem deletion.
+- Imported dependency areas include: `api`, `api.download_work_dir_files`, `helpers`, `helpers.api`, `helpers.file_browser`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `FileBrowser`, `collapse_nested_paths`, `browser.delete_file`, `normalize_paths`, `runtime.call_development_function`, `path.strip`, `extension.call_extensions_async`, `item.count`, `clean_path.startswith`, `parent.rstrip`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/download_work_dir_file.py.dox.md b/api/download_work_dir_file.py.dox.md
new file mode 100644
index 000000000..87085765a
--- /dev/null
+++ b/api/download_work_dir_file.py.dox.md
@@ -0,0 +1,53 @@
+# download_work_dir_file.py DOX
+
+## Purpose
+
+- Own the `download_work_dir_file.py` API endpoint.
+- This module handles workdir file operations for download work dir file.
+- Keep this file-level DOX profile synchronized with `download_work_dir_file.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `download_work_dir_file.py` owns the runtime implementation.
+- `download_work_dir_file.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `DownloadFile` (`ApiHandler`)
+ - `get_methods(cls)`
+ - `async process(self, input: Input, request: Request) -> Output`
+- Top-level functions:
+- `stream_file_download(file_source, download_name, chunk_size=...)`: Create a streaming response for file downloads that shows progress in browser.
+- `make_disposition(download_name: str) -> str`
+- `resolve_download_path(path: str) -> str`: Resolve a requested download path and keep it within the runtime base dir.
+- `async fetch_file(path)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `DownloadFile` is an `ApiHandler`.
+- `DownloadFile` defines `process(...)`.
+- `DownloadFile` defines `get_methods(...)`.
+- Observed side-effect areas: filesystem reads, network calls.
+- Imported dependency areas include: `api`, `base64`, `flask`, `helpers`, `helpers.api`, `io`, `mimetypes`, `os`, `pathlib`, `urllib.parse`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `mimetypes.guess_type`, `Response`, `quote`, `Path.resolve`, `Path`, `candidate.is_absolute`, `os.path.getsize`, `generate`, `download_name.encode.decode`, `candidate.resolve`, `resolve`, `resolved.relative_to`, `Exception`, `file.read`, `base64.b64encode.decode`, `file_source.tell`, `file_source.seek`, `ValueError`, `file_path.startswith`, `runtime.call_development_function`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_download_toast_regressions.py`
+ - `tests/test_office_canvas_setup.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/download_work_dir_files.py.dox.md b/api/download_work_dir_files.py.dox.md
new file mode 100644
index 000000000..478669fa8
--- /dev/null
+++ b/api/download_work_dir_files.py.dox.md
@@ -0,0 +1,54 @@
+# download_work_dir_files.py DOX
+
+## Purpose
+
+- Own the `download_work_dir_files.py` API endpoint.
+- This module handles workdir file operations for download work dir files.
+- Keep this file-level DOX profile synchronized with `download_work_dir_files.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `download_work_dir_files.py` owns the runtime implementation.
+- `download_work_dir_files.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `DownloadFiles` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+- Top-level functions:
+- `normalize_paths(paths) -> list[str]`
+- `selected_archive_name(count: int) -> str`
+- `create_selected_zip(paths: list[str], current_path: str=...) -> str`
+- `resolve_download_path(path: str, base_dir: Path) -> Path`
+- `collapse_nested_paths(paths: list[Path]) -> list[Path]`
+- `archive_root_name(source_path: Path, current_dir: Path | None, base_dir: Path) -> str`
+- `unique_archive_name(name: str, used_names: set[str]) -> str`
+- `write_zip_entry(zip_file: zipfile.ZipFile, source_path: Path, arc_root: str) -> None`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `DownloadFiles` is an `ApiHandler`.
+- `DownloadFiles` defines `process(...)`.
+- Observed side-effect areas: filesystem reads, filesystem writes, filesystem deletion.
+- Imported dependency areas include: `api.download_work_dir_file`, `base64`, `flask`, `helpers`, `helpers.api`, `helpers.localization`, `io`, `os`, `pathlib`, `tempfile`, `zipfile`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `Localization.get.now.strftime`, `Path.resolve`, `normalize_paths`, `collapse_nested_paths`, `Path`, `os.path.splitext`, `source_path.is_dir`, `zip_file.write`, `selected_archive_name`, `runtime.is_development`, `stream_file_download`, `ValueError`, `raw_path.strip`, `resolve_download_path`, `current_dir.is_file`, `resolved.exists`, `FileNotFoundError`, `tempfile.NamedTemporaryFile`, `zipfile.ZipFile`, `candidate.is_absolute`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_download_toast_regressions.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/edit_work_dir_file.py.dox.md b/api/edit_work_dir_file.py.dox.md
new file mode 100644
index 000000000..e5615517b
--- /dev/null
+++ b/api/edit_work_dir_file.py.dox.md
@@ -0,0 +1,50 @@
+# edit_work_dir_file.py DOX
+
+## Purpose
+
+- Own the `edit_work_dir_file.py` API endpoint.
+- This module handles workdir file operations for edit work dir file.
+- Keep this file-level DOX profile synchronized with `edit_work_dir_file.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `edit_work_dir_file.py` owns the runtime implementation.
+- `edit_work_dir_file.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `EditWorkDirFile` (`ApiHandler`)
+ - `get_methods(cls)`
+ - `async process(self, input: Input, request: Request) -> Output`
+- Top-level functions:
+- `async load_file(file_path: str) -> dict`
+- `save_file(file_path: str, content: str) -> bool`
+- Notable constants/configuration names: `MAX_EDIT_FILE_SIZE`, `BINARY_SAMPLE_SIZE`.
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `EditWorkDirFile` is an `ApiHandler`.
+- `EditWorkDirFile` defines `process(...)`.
+- `EditWorkDirFile` defines `get_methods(...)`.
+- Observed side-effect areas: filesystem reads, filesystem writes.
+- Imported dependency areas include: `helpers`, `helpers.api`, `helpers.file_browser`, `mimetypes`, `os`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `FileBrowser`, `browser.get_full_path`, `os.path.isdir`, `os.path.getsize`, `files.is_probably_binary_file`, `mimetypes.guess_type`, `browser.save_text_file`, `error_str.strip`, `Exception`, `os.path.basename`, `error_str.split`, `file.read`, `line.split.strip`, `file_path.startswith`, `content.encode`, `runtime.call_development_function`, `extension.call_extensions_async`, `self._extract_error_message`, `line.split`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/file_info.py.dox.md b/api/file_info.py.dox.md
new file mode 100644
index 000000000..e3612589d
--- /dev/null
+++ b/api/file_info.py.dox.md
@@ -0,0 +1,47 @@
+# file_info.py DOX
+
+## Purpose
+
+- Own the `file_info.py` API endpoint.
+- This module handles file info API requests.
+- Keep this file-level DOX profile synchronized with `file_info.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `file_info.py` owns the runtime implementation.
+- `file_info.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `FileInfoApi` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+- `FileInfo` (`TypedDict`)
+- Top-level functions:
+- `async get_file_info(path: str) -> FileInfo`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `FileInfoApi` is an `ApiHandler`.
+- `FileInfoApi` defines `process(...)`.
+- Observed side-effect areas: filesystem reads.
+- Imported dependency areas include: `helpers`, `helpers.api`, `os`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `files.get_abs_path`, `os.path.exists`, `os.path.dirname`, `os.path.basename`, `runtime.call_development_function`, `os.path.isdir`, `os.path.isfile`, `os.path.islink`, `os.path.getsize`, `os.path.getmtime`, `os.path.getctime`, `os.path.splitext`, `os.stat`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/get_work_dir_files.py b/api/get_work_dir_files.py
index 7e8e99c7f..68aee4990 100644
--- a/api/get_work_dir_files.py
+++ b/api/get_work_dir_files.py
@@ -9,7 +9,7 @@ class GetWorkDirFiles(ApiHandler):
return ["GET"]
async def process(self, input: dict, request: Request) -> dict | Response:
- current_path = request.args.get("path", "")
+ current_path = request.args.get("path", "") or "$WORK_DIR"
if current_path == "$WORK_DIR":
# if runtime.is_development():
# current_path = "work_dir"
diff --git a/api/get_work_dir_files.py.dox.md b/api/get_work_dir_files.py.dox.md
new file mode 100644
index 000000000..f5ff3e005
--- /dev/null
+++ b/api/get_work_dir_files.py.dox.md
@@ -0,0 +1,48 @@
+# get_work_dir_files.py DOX
+
+## Purpose
+
+- Own the `get_work_dir_files.py` API endpoint.
+- This module handles workdir file operations for get work dir files.
+- Keep this file-level DOX profile synchronized with `get_work_dir_files.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `get_work_dir_files.py` owns the runtime implementation.
+- `get_work_dir_files.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `GetWorkDirFiles` (`ApiHandler`)
+ - `get_methods(cls)`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+- Top-level functions:
+- `async get_files(path)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `GetWorkDirFiles` is an `ApiHandler`.
+- `GetWorkDirFiles` defines `process(...)`.
+- `GetWorkDirFiles` defines `get_methods(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`, `helpers.file_browser`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `FileBrowser`, `browser.get_files`, `runtime.call_development_function`.
+- Empty `path` requests and explicit `$WORK_DIR` requests resolve to the default workdir path before `FileBrowser` is called, so the WebUI never receives an empty startup path for the default file browser view.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/health.py.dox.md b/api/health.py.dox.md
new file mode 100644
index 000000000..236812f56
--- /dev/null
+++ b/api/health.py.dox.md
@@ -0,0 +1,52 @@
+# health.py DOX
+
+## Purpose
+
+- Own the `health.py` API endpoint.
+- This module reports process health for probes and startup checks.
+- Keep this file-level DOX profile synchronized with `health.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `health.py` owns the runtime implementation.
+- `health.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `HealthCheck` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `requires_csrf(cls) -> bool`
+ - `get_methods(cls) -> list[str]`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `HealthCheck` is an `ApiHandler`.
+- `HealthCheck` defines `process(...)`.
+- `HealthCheck` defines `get_methods(...)`.
+- `HealthCheck` defines `requires_auth(...)`.
+- `HealthCheck` defines `requires_csrf(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `git.get_git_info`, `errors.error_text`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_oauth_providers.py`
+ - `tests/test_office_document_store.py`
+ - `tests/test_self_update_tag_filter.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/history_get.py.dox.md b/api/history_get.py.dox.md
new file mode 100644
index 000000000..8f2f1ddca
--- /dev/null
+++ b/api/history_get.py.dox.md
@@ -0,0 +1,44 @@
+# history_get.py DOX
+
+## Purpose
+
+- Own the `history_get.py` API endpoint.
+- This module handles history get API requests.
+- Keep this file-level DOX profile synchronized with `history_get.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `history_get.py` owns the runtime implementation.
+- `history_get.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `GetHistory` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `GetHistory` is an `ApiHandler`.
+- `GetHistory` defines `process(...)`.
+- Observed side-effect areas: secret handling.
+- Imported dependency areas include: `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`, `agent.history.output_text`, `agent.history.get_tokens`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/image_get.py.dox.md b/api/image_get.py.dox.md
new file mode 100644
index 000000000..b9b0da35b
--- /dev/null
+++ b/api/image_get.py.dox.md
@@ -0,0 +1,53 @@
+# image_get.py DOX
+
+## Purpose
+
+- Own the `image_get.py` API endpoint.
+- This module serves allowed image references and fallback file-type icons.
+- Keep this file-level DOX profile synchronized with `image_get.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `image_get.py` owns the runtime implementation.
+- `image_get.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `ImageGet` (`ApiHandler`)
+ - `get_methods(cls) -> list[str]`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+- Top-level functions:
+- `_resolve_allowed_image_path(path: str) -> str`: Resolve a requested image path and keep it inside Agent Zero's base dir.
+- `_set_image_headers(response: Response, filename: str, file_ext: str) -> None`
+- `_send_file_type_icon(file_ext, filename=...)`: Return appropriate icon for file type
+- `_send_fallback_icon(icon_name)`: Return fallback icon from public directory
+- Notable constants/configuration names: `IMAGE_EXTENSIONS`, `SVG_EXTENSIONS`, `SVG_CONTENT_SECURITY_POLICY`.
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `ImageGet` is an `ApiHandler`.
+- `ImageGet` defines `process(...)`.
+- `ImageGet` defines `get_methods(...)`.
+- Observed side-effect areas: filesystem reads, network calls, subprocess/runtime control, settings/state persistence.
+- Imported dependency areas include: `base64`, `helpers`, `helpers.api`, `io`, `mimetypes`, `os`, `pathlib`, `urllib.parse`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `runtime.is_development`, `Path.resolve`, `candidate.resolve`, `quote`, `_send_fallback_icon`, `files.get_abs_path`, `send_file`, `os.path.splitext.lower`, `os.path.basename`, `Path`, `candidate.is_absolute`, `resolved.relative_to`, `os.path.exists`, `ValueError`, `_set_image_headers`, `_send_file_type_icon`, `files.fix_dev_path`, `_resolve_allowed_image_path`, `files.exists`, `files.get_base_dir`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_image_get_security.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/load_webui_extensions.py.dox.md b/api/load_webui_extensions.py.dox.md
new file mode 100644
index 000000000..c2d41e06a
--- /dev/null
+++ b/api/load_webui_extensions.py.dox.md
@@ -0,0 +1,44 @@
+# load_webui_extensions.py DOX
+
+## Purpose
+
+- Own the `load_webui_extensions.py` API endpoint.
+- This module returns frontend extension manifests/files for a WebUI extension point.
+- Keep this file-level DOX profile synchronized with `load_webui_extensions.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `load_webui_extensions.py` owns the runtime implementation.
+- `load_webui_extensions.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `LoadWebuiExtensions` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `LoadWebuiExtensions` is an `ApiHandler`.
+- `LoadWebuiExtensions` defines `process(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `extension.get_webui_extensions`, `Response`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_webui_extension_surfaces.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/logout.py.dox.md b/api/logout.py.dox.md
new file mode 100644
index 000000000..e125a43e7
--- /dev/null
+++ b/api/logout.py.dox.md
@@ -0,0 +1,47 @@
+# logout.py DOX
+
+## Purpose
+
+- Own the `logout.py` API endpoint.
+- This module clears login/session state for the current client.
+- Keep this file-level DOX profile synchronized with `logout.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `logout.py` owns the runtime implementation.
+- `logout.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `ApiLogout` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `ApiLogout` is an `ApiHandler`.
+- `ApiLogout` defines `process(...)`.
+- `ApiLogout` defines `requires_auth(...)`.
+- Observed side-effect areas: secret handling.
+- Imported dependency areas include: `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `session.clear`, `session.pop`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_office_document_store.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/mcp_server_get_detail.py b/api/mcp_server_get_detail.py
index bc5552c4d..25f865a41 100644
--- a/api/mcp_server_get_detail.py
+++ b/api/mcp_server_get_detail.py
@@ -9,9 +9,11 @@ class McpServerGetDetail(ApiHandler):
# try:
server_name = input.get("server_name")
+ project_name = str(input.get("project_name", "") or "").strip()
if not server_name:
return {"success": False, "error": "Missing server_name"}
- detail = MCPConfig.get_instance().get_server_detail(server_name)
+ config = MCPConfig.get_project_instance(project_name) if project_name else MCPConfig.get_instance()
+ detail = config.get_server_detail(server_name)
return {"success": True, "detail": detail}
# except Exception as e:
# return {"success": False, "error": str(e)}
diff --git a/api/mcp_server_get_detail.py.dox.md b/api/mcp_server_get_detail.py.dox.md
new file mode 100644
index 000000000..d71ed2551
--- /dev/null
+++ b/api/mcp_server_get_detail.py.dox.md
@@ -0,0 +1,45 @@
+# mcp_server_get_detail.py DOX
+
+## Purpose
+
+- Own the `mcp_server_get_detail.py` API endpoint.
+- This module handles MCP server detail requests for global or project scope.
+- Keep this file-level DOX profile synchronized with `mcp_server_get_detail.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `mcp_server_get_detail.py` owns the runtime implementation.
+- `mcp_server_get_detail.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `McpServerGetDetail` (`ApiHandler`)
+ - `async process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- The request accepts `server_name` and optional `project_name`; when `project_name` is present, detail resolves through the project-scoped MCP configuration.
+- Detail responses include the server tools visible to the manager UI. Tools disabled through a server `disabled_tools` config list remain present in this detail list with a `disabled` flag so the UI can re-enable them.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `McpServerGetDetail` is an `ApiHandler`.
+- `McpServerGetDetail` defines `process(...)`.
+- Imported dependency areas include: `helpers.api`, `helpers.mcp_handler`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `MCPConfig.get_instance.get_server_detail`, `MCPConfig.get_project_instance`, `MCPConfig.get_instance`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/mcp_server_get_log.py b/api/mcp_server_get_log.py
index 3305430ea..89b53b58f 100644
--- a/api/mcp_server_get_log.py
+++ b/api/mcp_server_get_log.py
@@ -9,9 +9,11 @@ class McpServerGetLog(ApiHandler):
# try:
server_name = input.get("server_name")
+ project_name = str(input.get("project_name", "") or "").strip()
if not server_name:
return {"success": False, "error": "Missing server_name"}
- log = MCPConfig.get_instance().get_server_log(server_name)
+ config = MCPConfig.get_project_instance(project_name) if project_name else MCPConfig.get_instance()
+ log = config.get_server_log(server_name)
return {"success": True, "log": log}
# except Exception as e:
# return {"success": False, "error": str(e)}
diff --git a/api/mcp_server_get_log.py.dox.md b/api/mcp_server_get_log.py.dox.md
new file mode 100644
index 000000000..0937274a4
--- /dev/null
+++ b/api/mcp_server_get_log.py.dox.md
@@ -0,0 +1,44 @@
+# mcp_server_get_log.py DOX
+
+## Purpose
+
+- Own the `mcp_server_get_log.py` API endpoint.
+- This module handles MCP server log requests for global or project scope.
+- Keep this file-level DOX profile synchronized with `mcp_server_get_log.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `mcp_server_get_log.py` owns the runtime implementation.
+- `mcp_server_get_log.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `McpServerGetLog` (`ApiHandler`)
+ - `async process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- The request accepts `server_name` and optional `project_name`; when `project_name` is present, logs resolve through the project-scoped MCP configuration.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `McpServerGetLog` is an `ApiHandler`.
+- `McpServerGetLog` defines `process(...)`.
+- Imported dependency areas include: `helpers.api`, `helpers.mcp_handler`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `MCPConfig.get_instance.get_server_log`, `MCPConfig.get_project_instance`, `MCPConfig.get_instance`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/mcp_server_scan.py b/api/mcp_server_scan.py
new file mode 100644
index 000000000..4639426d3
--- /dev/null
+++ b/api/mcp_server_scan.py
@@ -0,0 +1,232 @@
+import asyncio
+from shutil import which
+from typing import Any
+from urllib.parse import urlparse
+
+from helpers.api import ApiHandler, Request, Response
+from helpers.mcp_handler import MCPConfig, normalize_name
+
+
+_PROMPT_INJECTION_MARKERS = (
+ "ignore previous",
+ "ignore all previous",
+ "system prompt",
+ "developer message",
+ "hidden instruction",
+ "exfiltrate",
+ "leak secret",
+ "credential",
+)
+
+
+class McpServerScan(ApiHandler):
+ async def process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response:
+ server = dict(input.get("server") or {})
+ allow_local_execution = bool(input.get("allow_local_execution", False))
+ allow_remote_network = bool(input.get("allow_remote_network", False))
+ inspect_runtime = input.get("inspect_runtime", True) is not False
+
+ server = self._normalize_server(server)
+ warnings = self._static_warnings(server)
+ is_local = not (server.get("url") or server.get("serverUrl"))
+ has_static_errors = any(warning.get("level") == "error" for warning in warnings)
+
+ runtime_status: list[dict[str, Any]] = []
+ runtime_detail: dict[str, Any] = {}
+ runtime_error = ""
+
+ should_inspect_runtime = (
+ inspect_runtime
+ and not has_static_errors
+ and ((is_local and allow_local_execution) or (not is_local and allow_remote_network))
+ )
+
+ if should_inspect_runtime:
+ try:
+ scan_config = await asyncio.to_thread(
+ lambda: MCPConfig(servers_list=[server], config_scope="scan")
+ )
+ runtime_status = scan_config.get_servers_status()
+ runtime_detail = scan_config.get_server_detail(server.get("name", ""))
+ warnings.extend(self._tool_warnings(runtime_detail.get("tools", [])))
+ except Exception as exc:
+ runtime_error = str(exc)
+ warnings.append(
+ {
+ "level": "error",
+ "title": "Runtime inspection failed",
+ "message": runtime_error,
+ }
+ )
+ elif is_local and inspect_runtime:
+ warnings.append(
+ {
+ "level": "warning",
+ "title": "Local command not executed",
+ "message": "Local stdio MCP inspection requires explicit trust because it runs the configured command.",
+ }
+ )
+ elif not is_local and inspect_runtime and has_static_errors:
+ warnings.append(
+ {
+ "level": "info",
+ "title": "Runtime inspection skipped",
+ "message": "Fix static scan errors before attempting runtime MCP inspection.",
+ }
+ )
+ elif not is_local and inspect_runtime:
+ warnings.append(
+ {
+ "level": "info",
+ "title": "Remote runtime inspection skipped",
+ "message": "Enable trusted remote inspection to contact the MCP URL and list exposed tools.",
+ }
+ )
+
+ return {
+ "success": True,
+ "server": self._redact_server(server),
+ "risk_level": self._risk_level(warnings),
+ "warnings": warnings,
+ "status": runtime_status,
+ "detail": runtime_detail,
+ "runtime_error": runtime_error,
+ }
+
+ def _normalize_server(self, server: dict[str, Any]) -> dict[str, Any]:
+ name = str(server.get("name") or "").strip()
+ url = str(server.get("url") or server.get("serverUrl") or "").strip()
+ command = str(server.get("command") or "").strip()
+
+ if not name:
+ name = self._derive_name(url, command)
+ server["name"] = normalize_name(name or "mcp_server")
+
+ if url:
+ server["url"] = url
+ server.setdefault("type", "streamable-http")
+ elif command:
+ server["command"] = command
+ server["type"] = "stdio"
+
+ return server
+
+ def _derive_name(self, url: str, command: str) -> str:
+ if url:
+ parsed = urlparse(url)
+ parts = [part for part in parsed.path.split("/") if part]
+ return parts[-1] if parts else parsed.hostname or "remote_mcp"
+ if command:
+ return command.rsplit("/", 1)[-1]
+ return "mcp_server"
+
+ def _static_warnings(self, server: dict[str, Any]) -> list[dict[str, str]]:
+ warnings: list[dict[str, str]] = []
+ url = str(server.get("url") or "").strip()
+ command = str(server.get("command") or "").strip()
+
+ if url:
+ parsed = urlparse(url)
+ if parsed.scheme not in {"http", "https"}:
+ warnings.append(
+ {
+ "level": "error",
+ "title": "Unsupported URL scheme",
+ "message": "Remote MCP URLs should use http or https.",
+ }
+ )
+ elif parsed.scheme == "http" and parsed.hostname not in {"localhost", "127.0.0.1", "::1"}:
+ warnings.append(
+ {
+ "level": "warning",
+ "title": "Unencrypted remote URL",
+ "message": "Prefer HTTPS for remote MCP servers outside localhost.",
+ }
+ )
+ if not parsed.netloc:
+ warnings.append(
+ {
+ "level": "error",
+ "title": "Invalid remote URL",
+ "message": "The remote MCP URL is missing a host.",
+ }
+ )
+ elif command:
+ if which(command) is None:
+ warnings.append(
+ {
+ "level": "warning",
+ "title": "Command not found",
+ "message": f"'{command}' is not currently available on PATH.",
+ }
+ )
+ if command in {"bash", "sh", "zsh", "fish", "python", "python3", "node"}:
+ warnings.append(
+ {
+ "level": "warning",
+ "title": "General-purpose interpreter",
+ "message": "Review the command and arguments carefully before running this local MCP server.",
+ }
+ )
+ else:
+ warnings.append(
+ {
+ "level": "error",
+ "title": "Missing connection target",
+ "message": "Provide either a remote URL or a local command.",
+ }
+ )
+
+ if isinstance(server.get("headers"), dict) and server["headers"]:
+ warnings.append(
+ {
+ "level": "info",
+ "title": "Headers configured",
+ "message": "Header values are redacted in scan output. Keep tokens in trusted settings only.",
+ }
+ )
+
+ if isinstance(server.get("env"), dict) and server["env"]:
+ warnings.append(
+ {
+ "level": "info",
+ "title": "Environment configured",
+ "message": "Environment values are redacted in scan output. Avoid hardcoding secrets in MCP configs.",
+ }
+ )
+
+ return warnings
+
+ def _tool_warnings(self, tools: Any) -> list[dict[str, str]]:
+ warnings: list[dict[str, str]] = []
+ if not isinstance(tools, list):
+ return warnings
+
+ for tool in tools:
+ if not isinstance(tool, dict):
+ continue
+ haystack = f"{tool.get('name', '')}\n{tool.get('description', '')}".lower()
+ if any(marker in haystack for marker in _PROMPT_INJECTION_MARKERS):
+ warnings.append(
+ {
+ "level": "warning",
+ "title": "Suspicious tool description",
+ "message": f"Review tool '{tool.get('name', 'unknown')}' for prompt-injection style language.",
+ }
+ )
+ return warnings
+
+ def _redact_server(self, server: dict[str, Any]) -> dict[str, Any]:
+ redacted = dict(server)
+ for key in ("headers", "env"):
+ if isinstance(redacted.get(key), dict):
+ redacted[key] = {name: "***" for name in redacted[key]}
+ return redacted
+
+ def _risk_level(self, warnings: list[dict[str, str]]) -> str:
+ levels = {warning.get("level", "info") for warning in warnings}
+ if "error" in levels:
+ return "error"
+ if "warning" in levels:
+ return "warning"
+ return "ok"
diff --git a/api/mcp_server_scan.py.dox.md b/api/mcp_server_scan.py.dox.md
new file mode 100644
index 000000000..5487106aa
--- /dev/null
+++ b/api/mcp_server_scan.py.dox.md
@@ -0,0 +1,45 @@
+# mcp_server_scan.py DOX
+
+## Purpose
+
+- Own the `mcp_server_scan.py` API endpoint.
+- Provide static and optional runtime inspection for a single MCP server draft before it is added to global or project MCP config.
+- Keep this file-level DOX profile synchronized with `mcp_server_scan.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `mcp_server_scan.py` owns the runtime implementation.
+- `mcp_server_scan.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `McpServerScan` (`ApiHandler`)
+ - `async process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- The request accepts a `server` draft object, `inspect_runtime`, `allow_remote_network`, and `allow_local_execution`.
+- Remote runtime inspection may contact the configured MCP URL to list tools only when `allow_remote_network` is true and static checks have no errors.
+- Local stdio runtime inspection must not execute unless `allow_local_execution` is true.
+- Response data redacts `headers` and `env` values.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- Imported dependency areas include: `asyncio`, `helpers.api`, `helpers.mcp_handler`, `shutil`, `typing`, `urllib.parse`.
+
+## Key Concepts
+
+- Static checks report invalid URLs, non-HTTPS remote URLs, missing local commands, interpreter-style local commands, headers/env presence, and obvious prompt-injection markers in inspected tool descriptions.
+- Static errors skip runtime inspection; remote network inspection and local command execution both require explicit trust flags.
+- Runtime inspection creates a temporary `MCPConfig` in a worker thread so stdio/remote tool listing does not call `asyncio.run()` inside the request event loop.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Do not return secret values, raw environment values, or private files.
+- Keep scanner warnings explicit about local command execution risk.
+
+## Verification
+
+- Run endpoint-specific or MCP helper tests for changed behavior; smoke-test remote URL and local-command scan paths when practical.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/mcp_servers_apply.py b/api/mcp_servers_apply.py
index 7ea5275db..36877807a 100644
--- a/api/mcp_servers_apply.py
+++ b/api/mcp_servers_apply.py
@@ -5,20 +5,27 @@ from typing import Any
from helpers.mcp_handler import MCPConfig
from helpers.settings import set_settings_delta
+from helpers import projects
class McpServersApply(ApiHandler):
async def process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response:
mcp_servers = input["mcp_servers"]
+ project_name = str(input.get("project_name", "") or "").strip()
try:
- # MCPConfig.update(mcp_servers) # done in settings automatically
- set_settings_delta({"mcp_servers": "[]"}) # to force reinitialization
- set_settings_delta({"mcp_servers": mcp_servers})
+ if project_name:
+ projects.save_project_mcp_servers(project_name, mcp_servers)
+ config = MCPConfig.refresh_project(project_name)
+ else:
+ # MCPConfig.update(mcp_servers) # done in settings automatically
+ set_settings_delta({"mcp_servers": "[]"}) # to force reinitialization
+ set_settings_delta({"mcp_servers": mcp_servers})
- time.sleep(1) # wait at least a second
- # MCPConfig.wait_for_lock() # wait until config lock is released
- status = MCPConfig.get_instance().get_servers_status()
- return {"success": True, "status": status}
+ time.sleep(1) # wait at least a second
+ # MCPConfig.wait_for_lock() # wait until config lock is released
+ config = MCPConfig.get_instance()
+ status = config.get_servers_status()
+ return {"success": True, "status": status, "mcp_servers": mcp_servers, "project_name": project_name}
except Exception as e:
return {"success": False, "error": str(e)}
diff --git a/api/mcp_servers_apply.py.dox.md b/api/mcp_servers_apply.py.dox.md
new file mode 100644
index 000000000..361c331b2
--- /dev/null
+++ b/api/mcp_servers_apply.py.dox.md
@@ -0,0 +1,47 @@
+# mcp_servers_apply.py DOX
+
+## Purpose
+
+- Own the `mcp_servers_apply.py` API endpoint.
+- This module handles MCP servers apply requests for global or project scope.
+- Keep this file-level DOX profile synchronized with `mcp_servers_apply.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `mcp_servers_apply.py` owns the runtime implementation.
+- `mcp_servers_apply.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `McpServersApply` (`ApiHandler`)
+ - `async process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- The request accepts `config` and optional `project_name`.
+- Without `project_name`, the endpoint persists global `mcp_servers_config` through settings and refreshes the global `MCPConfig`.
+- With `project_name`, the endpoint saves `.a0proj/mcp_servers.json` through `helpers.projects.save_project_mcp_servers(...)` and refreshes that project's merged MCP config.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `McpServersApply` is an `ApiHandler`.
+- `McpServersApply` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, settings/state persistence.
+- Imported dependency areas include: `helpers.api`, `helpers.mcp_handler`, `helpers.projects`, `helpers.settings`, `time`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `set_settings_delta`, `projects.save_project_mcp_servers`, `MCPConfig.refresh_project`, `time.sleep`, `MCPConfig.get_instance.get_servers_status`, `MCPConfig.get_instance`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/mcp_servers_status.py b/api/mcp_servers_status.py
index 20f8a64c8..01afff421 100644
--- a/api/mcp_servers_status.py
+++ b/api/mcp_servers_status.py
@@ -9,7 +9,9 @@ class McpServersStatuss(ApiHandler):
async def process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response:
# try:
- status = MCPConfig.get_instance().get_servers_status()
+ project_name = (input or {}).get("project_name") if isinstance(input, dict) else None
+ config = MCPConfig.get_project_instance(project_name) if project_name else MCPConfig.get_instance()
+ status = config.get_servers_status()
return {"success": True, "status": status}
# except Exception as e:
# return {"success": False, "error": str(e)}
diff --git a/api/mcp_servers_status.py.dox.md b/api/mcp_servers_status.py.dox.md
new file mode 100644
index 000000000..90b4065be
--- /dev/null
+++ b/api/mcp_servers_status.py.dox.md
@@ -0,0 +1,45 @@
+# mcp_servers_status.py DOX
+
+## Purpose
+
+- Own the `mcp_servers_status.py` API endpoint.
+- This module handles MCP servers status requests for global or project scope.
+- Keep this file-level DOX profile synchronized with `mcp_servers_status.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `mcp_servers_status.py` owns the runtime implementation.
+- `mcp_servers_status.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `McpServersStatuss` (`ApiHandler`)
+ - `async process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- The request accepts optional `project_name`; when present, status resolves through the merged project-scoped MCP configuration.
+- `tool_count` reports enabled MCP tools only; tools disabled by a server `disabled_tools` list stay hidden from agent-facing status counts.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `McpServersStatuss` is an `ApiHandler`.
+- `McpServersStatuss` defines `process(...)`.
+- Imported dependency areas include: `helpers.api`, `helpers.mcp_handler`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `MCPConfig.get_instance.get_servers_status`, `MCPConfig.get_project_instance`, `MCPConfig.get_instance`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/message.py.dox.md b/api/message.py.dox.md
new file mode 100644
index 000000000..9ec63fc71
--- /dev/null
+++ b/api/message.py.dox.md
@@ -0,0 +1,54 @@
+# message.py DOX
+
+## Purpose
+
+- Own the `message.py` API endpoint.
+- This module submits a user message and runs agent processing synchronously through the UI API.
+- Keep this file-level DOX profile synchronized with `message.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `message.py` owns the runtime implementation.
+- `message.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Message` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+ - `async respond(self, task: DeferredTask, context: AgentContext)`
+ - `async communicate(self, input: dict, request: Request)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Message` is an `ApiHandler`.
+- `Message` defines `process(...)`.
+- Observed side-effect areas: filesystem reads, filesystem writes, settings/state persistence, scheduler state.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`, `helpers.defer`, `helpers.security`, `os`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `request.content_type.startswith`, `self.use_context`, `mq.log_user_message`, `self.communicate`, `self.respond`, `task.result`, `request.files.getlist`, `files.get_abs_path`, `request.get_json`, `extension.call_extensions_async`, `context.communicate`, `os.makedirs`, `UserMessage`, `safe_filename`, `attachment.save`, `context.get_agent`, `os.path.join`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/email_parser_test.py`
+ - `tests/rate_limiter_test.py`
+ - `tests/test_api_chat_lifetime.py`
+ - `tests/test_browser_agent_regressions.py`
+ - `tests/test_chat_compaction.py`
+ - `tests/test_docker_release_plan.py`
+ - `tests/test_document_query_fallback.py`
+ - `tests/test_download_toast_regressions.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/message_async.py.dox.md b/api/message_async.py.dox.md
new file mode 100644
index 000000000..6a6ba0093
--- /dev/null
+++ b/api/message_async.py.dox.md
@@ -0,0 +1,42 @@
+# message_async.py DOX
+
+## Purpose
+
+- Own the `message_async.py` API endpoint.
+- This module submits a user message for asynchronous agent processing.
+- Keep this file-level DOX profile synchronized with `message_async.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `message_async.py` owns the runtime implementation.
+- `message_async.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `MessageAsync` (`Message`)
+ - `async respond(self, task: DeferredTask, context: AgentContext)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- Observed side-effect areas: scheduler state.
+- Imported dependency areas include: `agent`, `api.message`, `helpers.defer`.
+
+## Key Concepts
+
+- This module is primarily declarative or delegates behavior through classes/imported objects.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/message_queue_add.py.dox.md b/api/message_queue_add.py.dox.md
new file mode 100644
index 000000000..f727a70f1
--- /dev/null
+++ b/api/message_queue_add.py.dox.md
@@ -0,0 +1,44 @@
+# message_queue_add.py DOX
+
+## Purpose
+
+- Own the `message_queue_add.py` API endpoint.
+- This module handles message queue add API requests.
+- Keep this file-level DOX profile synchronized with `message_queue_add.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `message_queue_add.py` owns the runtime implementation.
+- `message_queue_add.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `MessageQueueAdd` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `MessageQueueAdd` is an `ApiHandler`.
+- `MessageQueueAdd` defines `process(...)`.
+- Observed side-effect areas: settings/state persistence.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`, `helpers.state_monitor_integration`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `input.get.strip`, `mq.add`, `mark_dirty_for_context`, `Response`, `mq.get_queue`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/message_queue_remove.py.dox.md b/api/message_queue_remove.py.dox.md
new file mode 100644
index 000000000..8bf876b05
--- /dev/null
+++ b/api/message_queue_remove.py.dox.md
@@ -0,0 +1,44 @@
+# message_queue_remove.py DOX
+
+## Purpose
+
+- Own the `message_queue_remove.py` API endpoint.
+- This module handles message queue remove API requests.
+- Keep this file-level DOX profile synchronized with `message_queue_remove.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `message_queue_remove.py` owns the runtime implementation.
+- `message_queue_remove.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `MessageQueueRemove` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `MessageQueueRemove` is an `ApiHandler`.
+- `MessageQueueRemove` defines `process(...)`.
+- Observed side-effect areas: filesystem deletion, settings/state persistence.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`, `helpers.state_monitor_integration`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `mq.remove`, `mark_dirty_for_context`, `Response`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/message_queue_send.py.dox.md b/api/message_queue_send.py.dox.md
new file mode 100644
index 000000000..4f7294a94
--- /dev/null
+++ b/api/message_queue_send.py.dox.md
@@ -0,0 +1,44 @@
+# message_queue_send.py DOX
+
+## Purpose
+
+- Own the `message_queue_send.py` API endpoint.
+- This module handles message queue send API requests.
+- Keep this file-level DOX profile synchronized with `message_queue_send.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `message_queue_send.py` owns the runtime implementation.
+- `message_queue_send.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `MessageQueueSend` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `MessageQueueSend` is an `ApiHandler`.
+- `MessageQueueSend` defines `process(...)`.
+- Observed side-effect areas: settings/state persistence.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`, `helpers.state_monitor_integration`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `mq.send_message`, `mark_dirty_for_context`, `Response`, `mq.has_queue`, `mq.send_all_aggregated`, `mq.pop_item`, `mq.pop_first`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/notification_create.py.dox.md b/api/notification_create.py.dox.md
new file mode 100644
index 000000000..a83b82cf4
--- /dev/null
+++ b/api/notification_create.py.dox.md
@@ -0,0 +1,46 @@
+# notification_create.py DOX
+
+## Purpose
+
+- Own the `notification_create.py` API endpoint.
+- This module handles notification notification create requests.
+- Keep this file-level DOX profile synchronized with `notification_create.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `notification_create.py` owns the runtime implementation.
+- `notification_create.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `NotificationCreate` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `NotificationCreate` is an `ApiHandler`.
+- `NotificationCreate` defines `process(...)`.
+- `NotificationCreate` defines `requires_auth(...)`.
+- Imported dependency areas include: `flask`, `helpers.api`, `helpers.notification`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `NotificationManager.send_notification`, `NotificationType`, `notification.output`, `notification_type.lower`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_download_toast_regressions.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/notifications_clear.py.dox.md b/api/notifications_clear.py.dox.md
new file mode 100644
index 000000000..dc5503b73
--- /dev/null
+++ b/api/notifications_clear.py.dox.md
@@ -0,0 +1,45 @@
+# notifications_clear.py DOX
+
+## Purpose
+
+- Own the `notifications_clear.py` API endpoint.
+- This module handles notification notifications clear requests.
+- Keep this file-level DOX profile synchronized with `notifications_clear.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `notifications_clear.py` owns the runtime implementation.
+- `notifications_clear.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `NotificationsClear` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `NotificationsClear` is an `ApiHandler`.
+- `NotificationsClear` defines `process(...)`.
+- `NotificationsClear` defines `requires_auth(...)`.
+- Imported dependency areas include: `agent`, `flask`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `AgentContext.get_notification_manager`, `notification_manager.clear_all`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/notifications_history.py.dox.md b/api/notifications_history.py.dox.md
new file mode 100644
index 000000000..a14b5bc86
--- /dev/null
+++ b/api/notifications_history.py.dox.md
@@ -0,0 +1,45 @@
+# notifications_history.py DOX
+
+## Purpose
+
+- Own the `notifications_history.py` API endpoint.
+- This module handles notification notifications history requests.
+- Keep this file-level DOX profile synchronized with `notifications_history.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `notifications_history.py` owns the runtime implementation.
+- `notifications_history.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `NotificationsHistory` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `NotificationsHistory` is an `ApiHandler`.
+- `NotificationsHistory` defines `process(...)`.
+- `NotificationsHistory` defines `requires_auth(...)`.
+- Imported dependency areas include: `agent`, `flask`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `AgentContext.get_notification_manager`, `notification_manager.output_all`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/notifications_mark_read.py.dox.md b/api/notifications_mark_read.py.dox.md
new file mode 100644
index 000000000..a06cbbdcf
--- /dev/null
+++ b/api/notifications_mark_read.py.dox.md
@@ -0,0 +1,45 @@
+# notifications_mark_read.py DOX
+
+## Purpose
+
+- Own the `notifications_mark_read.py` API endpoint.
+- This module handles notification notifications mark read requests.
+- Keep this file-level DOX profile synchronized with `notifications_mark_read.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `notifications_mark_read.py` owns the runtime implementation.
+- `notifications_mark_read.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `NotificationsMarkRead` (`ApiHandler`)
+ - `requires_auth(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `NotificationsMarkRead` is an `ApiHandler`.
+- `NotificationsMarkRead` defines `process(...)`.
+- `NotificationsMarkRead` defines `requires_auth(...)`.
+- Imported dependency areas include: `agent`, `flask`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `AgentContext.get_notification_manager`, `notification_manager.mark_read_by_ids`, `notification_manager.mark_all_read`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/nudge.py.dox.md b/api/nudge.py.dox.md
new file mode 100644
index 000000000..f1be2cf3d
--- /dev/null
+++ b/api/nudge.py.dox.md
@@ -0,0 +1,44 @@
+# nudge.py DOX
+
+## Purpose
+
+- Own the `nudge.py` API endpoint.
+- This module handles nudge API requests.
+- Keep this file-level DOX profile synchronized with `nudge.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `nudge.py` owns the runtime implementation.
+- `nudge.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Nudge` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Nudge` is an `ApiHandler`.
+- `Nudge` defines `process(...)`.
+- Imported dependency areas include: `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`, `context.nudge`, `context.log.log`, `Exception`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_browser_agent_regressions.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/pause.py.dox.md b/api/pause.py.dox.md
new file mode 100644
index 000000000..37d0528e9
--- /dev/null
+++ b/api/pause.py.dox.md
@@ -0,0 +1,45 @@
+# pause.py DOX
+
+## Purpose
+
+- Own the `pause.py` API endpoint.
+- This module handles pause API requests.
+- Keep this file-level DOX profile synchronized with `pause.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `pause.py` owns the runtime implementation.
+- `pause.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Pause` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Pause` is an `ApiHandler`.
+- `Pause` defines `process(...)`.
+- Imported dependency areas include: `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_multi_tab_isolation.py`
+ - `tests/test_snapshot_schema_v1.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/plugins.py.dox.md b/api/plugins.py.dox.md
new file mode 100644
index 000000000..e64b22dc9
--- /dev/null
+++ b/api/plugins.py.dox.md
@@ -0,0 +1,52 @@
+# plugins.py DOX
+
+## Purpose
+
+- Own the `plugins.py` API endpoint.
+- This module manages plugin actions and plugin settings through the core API.
+- Keep this file-level DOX profile synchronized with `plugins.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `plugins.py` owns the runtime implementation.
+- `plugins.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Plugins` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Plugins` is an `ApiHandler`.
+- `Plugins` defines `process(...)`.
+- Observed side-effect areas: filesystem reads, filesystem writes, filesystem deletion, subprocess/runtime control, plugin state, settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.api`, `helpers.localization`, `json`, `os`, `subprocess`, `sys`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `Response`, `plugins.find_plugin_assets`, `plugins.get_plugin_meta`, `plugins.get_default_plugin_config`, `plugins.save_plugin_config`, `plugins.toggle_plugin`, `plugins.find_plugin_dir`, `files.get_abs_path`, `Localization.get.now_iso`, `plugins.determine_plugin_asset_path`, `self._get_config`, `self._get_toggle_status`, `self._list_configs`, `self._delete_config`, `self._delete_plugin`, `self._get_default_config`, `self._save_config`, `self._toggle_plugin`, `self._get_doc`, `self._run_execute_script`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_a0_connector_computer_use_metadata.py`
+ - `tests/test_a0_connector_prompt_gating.py`
+ - `tests/test_browser_agent_regressions.py`
+ - `tests/test_chat_compaction.py`
+ - `tests/test_default_prompt_budget.py`
+ - `tests/test_document_query_plugin.py`
+ - `tests/test_error_retry_plugin.py`
+ - `tests/test_host_browser_connector.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/plugins_list.py.dox.md b/api/plugins_list.py.dox.md
new file mode 100644
index 000000000..599b5a307
--- /dev/null
+++ b/api/plugins_list.py.dox.md
@@ -0,0 +1,46 @@
+# plugins_list.py DOX
+
+## Purpose
+
+- Own the `plugins_list.py` API endpoint.
+- This module returns plugin inventory and activation metadata for plugin UI surfaces.
+- Keep this file-level DOX profile synchronized with `plugins_list.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `plugins_list.py` owns the runtime implementation.
+- `plugins_list.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `PluginsList` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `PluginsList` is an `ApiHandler`.
+- `PluginsList` defines `process(...)`.
+- Observed side-effect areas: plugin state, settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `plugins.get_enhanced_plugins_list`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_plugin_activation_ui.py`
+ - `tests/test_speech_plugin_split.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/poll.py.dox.md b/api/poll.py.dox.md
new file mode 100644
index 000000000..851a03318
--- /dev/null
+++ b/api/poll.py.dox.md
@@ -0,0 +1,52 @@
+# poll.py DOX
+
+## Purpose
+
+- Own the `poll.py` API endpoint.
+- This module returns chat/log/status changes for polling clients.
+- Keep this file-level DOX profile synchronized with `poll.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `poll.py` owns the runtime implementation.
+- `poll.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Poll` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Poll` is an `ApiHandler`.
+- `Poll` defines `process(...)`.
+- Observed side-effect areas: settings/state persistence.
+- Imported dependency areas include: `helpers.api`, `helpers.state_snapshot`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `build_snapshot`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_multi_tab_isolation.py`
+ - `tests/test_oauth_github_copilot.py`
+ - `tests/test_oauth_providers.py`
+ - `tests/test_office_document_store.py`
+ - `tests/test_snapshot_parity.py`
+ - `tests/test_snapshot_schema_v1.py`
+ - `tests/test_timezone_regressions.py`
+ - `tests/test_tunnel_remote_link.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/projects.py.dox.md b/api/projects.py.dox.md
new file mode 100644
index 000000000..d55500c21
--- /dev/null
+++ b/api/projects.py.dox.md
@@ -0,0 +1,59 @@
+# projects.py DOX
+
+## Purpose
+
+- Own the `projects.py` API endpoint.
+- This module manages project create, update, delete, clone, and metadata flows.
+- Keep this file-level DOX profile synchronized with `projects.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `projects.py` owns the runtime implementation.
+- `projects.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Projects` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+ - `get_active_projects_list(self)`
+ - `get_active_projects_options(self)`
+ - `create_project(self, project: dict | None)`
+ - `clone_project(self, project: dict | None)`
+ - `load_project(self, name: str | None)`
+ - `update_project(self, project: dict | None)`
+ - `delete_project(self, name: str | None)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Projects` is an `ApiHandler`.
+- `Projects` defines `process(...)`.
+- Observed side-effect areas: filesystem deletion, settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.api`, `helpers.notification`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `projects.get_active_projects_list`, `projects.BasicProjectData`, `projects.create_project`, `projects.load_edit_project_data`, `NotificationManager.send_notification`, `projects.EditProjectData`, `projects.update_project`, `projects.delete_project`, `projects.activate_project`, `projects.deactivate_project`, `projects.load_basic_project_data`, `projects.get_file_structure`, `self.use_context`, `Exception`, `projects.clone_git_project`, `self.get_active_projects_list`, `self.get_active_projects_options`, `self.load_project`, `self.create_project`, `self.clone_project`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_model_config_project_presets.py`
+ - `tests/test_office_document_store.py`
+ - `tests/test_plugin_activation_ui.py`
+ - `tests/test_projects.py`
+ - `tests/test_skills_runtime.py`
+ - `tests/test_task_scheduler_timezone.py`
+ - `tests/test_time_travel.py`
+ - `tests/test_tool_action_contracts.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/rename_work_dir_file.py.dox.md b/api/rename_work_dir_file.py.dox.md
new file mode 100644
index 000000000..624bedc54
--- /dev/null
+++ b/api/rename_work_dir_file.py.dox.md
@@ -0,0 +1,47 @@
+# rename_work_dir_file.py DOX
+
+## Purpose
+
+- Own the `rename_work_dir_file.py` API endpoint.
+- This module handles workdir file operations for rename work dir file.
+- Keep this file-level DOX profile synchronized with `rename_work_dir_file.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `rename_work_dir_file.py` owns the runtime implementation.
+- `rename_work_dir_file.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `RenameWorkDirFile` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+- Top-level functions:
+- `async rename_item(file_path: str, new_name: str) -> bool`
+- `async create_folder(parent_path: str, folder_name: str) -> bool`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `RenameWorkDirFile` is an `ApiHandler`.
+- `RenameWorkDirFile` defines `process(...)`.
+- Observed side-effect areas: filesystem writes.
+- Imported dependency areas include: `api`, `helpers`, `helpers.api`, `helpers.file_browser`, `posixpath`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `FileBrowser`, `browser.rename_item`, `browser.create_folder`, `strip`, `runtime.call_development_function`, `posixpath.join`, `file_path.startswith`, `extension.call_extensions_async`, `str.rstrip`, `posixpath.dirname`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/restart.py.dox.md b/api/restart.py.dox.md
new file mode 100644
index 000000000..4e3156f6e
--- /dev/null
+++ b/api/restart.py.dox.md
@@ -0,0 +1,48 @@
+# restart.py DOX
+
+## Purpose
+
+- Own the `restart.py` API endpoint.
+- This module requests server restart or reload behavior.
+- Keep this file-level DOX profile synchronized with `restart.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `restart.py` owns the runtime implementation.
+- `restart.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Restart` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Restart` is an `ApiHandler`.
+- `Restart` defines `process(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `process.reload`, `Response`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_browser_agent_regressions.py`
+ - `tests/test_download_toast_regressions.py`
+ - `tests/test_self_update_tag_filter.py`
+ - `tests/test_timezone_regressions.py`
+ - `tests/test_ws_manager.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/rfc.py.dox.md b/api/rfc.py.dox.md
new file mode 100644
index 000000000..6598649ea
--- /dev/null
+++ b/api/rfc.py.dox.md
@@ -0,0 +1,47 @@
+# rfc.py DOX
+
+## Purpose
+
+- Own the `rfc.py` API endpoint.
+- This module dispatches remote function calls through the RFC helper layer.
+- Keep this file-level DOX profile synchronized with `rfc.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `rfc.py` owns the runtime implementation.
+- `rfc.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `RFC` (`ApiHandler`)
+ - `requires_csrf(cls) -> bool`
+ - `requires_auth(cls) -> bool`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `RFC` is an `ApiHandler`.
+- `RFC` defines `process(...)`.
+- `RFC` defines `requires_auth(...)`.
+- `RFC` defines `requires_csrf(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `runtime.handle_rfc`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/scheduler_task_create.py.dox.md b/api/scheduler_task_create.py.dox.md
new file mode 100644
index 000000000..332118085
--- /dev/null
+++ b/api/scheduler_task_create.py.dox.md
@@ -0,0 +1,44 @@
+# scheduler_task_create.py DOX
+
+## Purpose
+
+- Own the `scheduler_task_create.py` API endpoint.
+- This module handles scheduler task create requests.
+- Keep this file-level DOX profile synchronized with `scheduler_task_create.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `scheduler_task_create.py` owns the runtime implementation.
+- `scheduler_task_create.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SchedulerTaskCreate` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SchedulerTaskCreate` is an `ApiHandler`.
+- `SchedulerTaskCreate` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, secret handling, scheduler state.
+- Imported dependency areas include: `helpers.api`, `helpers.localization`, `helpers.print_style`, `helpers.projects`, `helpers.task_scheduler`, `random`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `PrintStyle`, `scheduler.get_task_by_uuid`, `serialize_task`, `Localization.get.set_timezone`, `scheduler.reload`, `ValueError`, `ScheduledTask.create`, `scheduler.add_task`, `requested_project_slug.strip`, `load_basic_project_data`, `random.randint`, `schedule.split`, `TaskSchedule`, `PlannedTask.create`, `AdHocTask.create`, `printer.error`, `type`, `parse_task_plan`, `parse_task_schedule`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/scheduler_task_delete.py.dox.md b/api/scheduler_task_delete.py.dox.md
new file mode 100644
index 000000000..2112e95ee
--- /dev/null
+++ b/api/scheduler_task_delete.py.dox.md
@@ -0,0 +1,44 @@
+# scheduler_task_delete.py DOX
+
+## Purpose
+
+- Own the `scheduler_task_delete.py` API endpoint.
+- This module handles scheduler task delete requests.
+- Keep this file-level DOX profile synchronized with `scheduler_task_delete.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `scheduler_task_delete.py` owns the runtime implementation.
+- `scheduler_task_delete.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SchedulerTaskDelete` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SchedulerTaskDelete` is an `ApiHandler`.
+- `SchedulerTaskDelete` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, filesystem deletion, settings/state persistence, scheduler state.
+- Imported dependency areas include: `agent`, `helpers`, `helpers.api`, `helpers.localization`, `helpers.task_scheduler`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `scheduler.get_task_by_uuid`, `Localization.get.set_timezone`, `scheduler.reload`, `self.use_context`, `scheduler.cancel_running_task`, `AgentContext.remove`, `persist_chat.remove_chat`, `scheduler.remove_task_by_uuid`, `context.reset`, `scheduler.update_task`, `scheduler.save`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/scheduler_task_run.py.dox.md b/api/scheduler_task_run.py.dox.md
new file mode 100644
index 000000000..f4aa4b158
--- /dev/null
+++ b/api/scheduler_task_run.py.dox.md
@@ -0,0 +1,44 @@
+# scheduler_task_run.py DOX
+
+## Purpose
+
+- Own the `scheduler_task_run.py` API endpoint.
+- This module handles scheduler task run requests.
+- Keep this file-level DOX profile synchronized with `scheduler_task_run.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `scheduler_task_run.py` owns the runtime implementation.
+- `scheduler_task_run.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SchedulerTaskRun` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SchedulerTaskRun` is an `ApiHandler`.
+- `SchedulerTaskRun` defines `process(...)`.
+- Observed side-effect areas: settings/state persistence, scheduler state.
+- Imported dependency areas include: `helpers.api`, `helpers.localization`, `helpers.print_style`, `helpers.task_scheduler`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `PrintStyle`, `scheduler.get_task_by_uuid`, `Localization.get.set_timezone`, `scheduler.reload`, `self._printer.error`, `scheduler.serialize_task`, `scheduler.run_task_by_uuid`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/scheduler_task_update.py.dox.md b/api/scheduler_task_update.py.dox.md
new file mode 100644
index 000000000..a7c9bfced
--- /dev/null
+++ b/api/scheduler_task_update.py.dox.md
@@ -0,0 +1,44 @@
+# scheduler_task_update.py DOX
+
+## Purpose
+
+- Own the `scheduler_task_update.py` API endpoint.
+- This module handles scheduler task update requests.
+- Keep this file-level DOX profile synchronized with `scheduler_task_update.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `scheduler_task_update.py` owns the runtime implementation.
+- `scheduler_task_update.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SchedulerTaskUpdate` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SchedulerTaskUpdate` is an `ApiHandler`.
+- `SchedulerTaskUpdate` defines `process(...)`.
+- Observed side-effect areas: settings/state persistence, secret handling, scheduler state.
+- Imported dependency areas include: `helpers.api`, `helpers.localization`, `helpers.task_scheduler`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `scheduler.get_task_by_uuid`, `serialize_task`, `Localization.get.set_timezone`, `scheduler.reload`, `TaskState`, `scheduler.update_task`, `parse_task_schedule`, `parse_task_plan`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/scheduler_tasks_list.py.dox.md b/api/scheduler_tasks_list.py.dox.md
new file mode 100644
index 000000000..5e5533206
--- /dev/null
+++ b/api/scheduler_tasks_list.py.dox.md
@@ -0,0 +1,44 @@
+# scheduler_tasks_list.py DOX
+
+## Purpose
+
+- Own the `scheduler_tasks_list.py` API endpoint.
+- This module handles scheduler tasks list requests.
+- Keep this file-level DOX profile synchronized with `scheduler_tasks_list.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `scheduler_tasks_list.py` owns the runtime implementation.
+- `scheduler_tasks_list.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SchedulerTasksList` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SchedulerTasksList` is an `ApiHandler`.
+- `SchedulerTasksList` defines `process(...)`.
+- Observed side-effect areas: scheduler state.
+- Imported dependency areas include: `helpers.api`, `helpers.localization`, `helpers.print_style`, `helpers.task_scheduler`, `traceback`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `scheduler.serialize_all_tasks`, `Localization.get.set_timezone`, `scheduler.reload`, `PrintStyle.error`, `traceback.format_exc`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/scheduler_tick.py.dox.md b/api/scheduler_tick.py.dox.md
new file mode 100644
index 000000000..d19ae305c
--- /dev/null
+++ b/api/scheduler_tick.py.dox.md
@@ -0,0 +1,50 @@
+# scheduler_tick.py DOX
+
+## Purpose
+
+- Own the `scheduler_tick.py` API endpoint.
+- This module handles scheduler tick requests.
+- Keep this file-level DOX profile synchronized with `scheduler_tick.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `scheduler_tick.py` owns the runtime implementation.
+- `scheduler_tick.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SchedulerTick` (`ApiHandler`)
+ - `requires_loopback(cls) -> bool`
+ - `requires_auth(cls) -> bool`
+ - `requires_csrf(cls) -> bool`
+ - `async process(self, input: Input, request: Request) -> Output`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SchedulerTick` is an `ApiHandler`.
+- `SchedulerTick` defines `process(...)`.
+- `SchedulerTick` defines `requires_auth(...)`.
+- `SchedulerTick` defines `requires_csrf(...)`.
+- `SchedulerTick` defines `requires_loopback(...)`.
+- Observed side-effect areas: settings/state persistence, scheduler state.
+- Imported dependency areas include: `datetime`, `helpers.api`, `helpers.localization`, `helpers.print_style`, `helpers.task_scheduler`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `datetime.now.strftime`, `PrintStyle`, `scheduler.get_tasks`, `scheduler.serialize_all_tasks`, `Localization.get.set_timezone`, `scheduler.reload`, `scheduler.tick`, `datetime.now`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/self_update_get.py.dox.md b/api/self_update_get.py.dox.md
new file mode 100644
index 000000000..261cac93f
--- /dev/null
+++ b/api/self_update_get.py.dox.md
@@ -0,0 +1,45 @@
+# self_update_get.py DOX
+
+## Purpose
+
+- Own the `self_update_get.py` API endpoint.
+- This module handles self update get API requests.
+- Keep this file-level DOX profile synchronized with `self_update_get.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `self_update_get.py` owns the runtime implementation.
+- `self_update_get.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SelfUpdateGet` (`ApiHandler`)
+ - `get_methods(cls) -> list[str]`
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SelfUpdateGet` is an `ApiHandler`.
+- `SelfUpdateGet` defines `process(...)`.
+- `SelfUpdateGet` defines `get_methods(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self_update.get_update_info`, `runtime.is_dockerized`, `self_update.load_pending_update`, `self_update.load_last_status`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/self_update_schedule.py.dox.md b/api/self_update_schedule.py.dox.md
new file mode 100644
index 000000000..b865070c9
--- /dev/null
+++ b/api/self_update_schedule.py.dox.md
@@ -0,0 +1,45 @@
+# self_update_schedule.py DOX
+
+## Purpose
+
+- Own the `self_update_schedule.py` API endpoint.
+- This module handles self update schedule API requests.
+- Keep this file-level DOX profile synchronized with `self_update_schedule.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `self_update_schedule.py` owns the runtime implementation.
+- `self_update_schedule.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SelfUpdateSchedule` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SelfUpdateSchedule` is an `ApiHandler`.
+- `SelfUpdateSchedule` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, subprocess/runtime control.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `runtime.is_dockerized`, `self_update.schedule_update`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_self_update_tag_filter.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/self_update_tags.py.dox.md b/api/self_update_tags.py.dox.md
new file mode 100644
index 000000000..fdf78cd23
--- /dev/null
+++ b/api/self_update_tags.py.dox.md
@@ -0,0 +1,43 @@
+# self_update_tags.py DOX
+
+## Purpose
+
+- Own the `self_update_tags.py` API endpoint.
+- This module handles self update tags API requests.
+- Keep this file-level DOX profile synchronized with `self_update_tags.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `self_update_tags.py` owns the runtime implementation.
+- `self_update_tags.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SelfUpdateTags` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SelfUpdateTags` is an `ApiHandler`.
+- `SelfUpdateTags` defines `process(...)`.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `str.strip.lower`, `self_update.get_repo_version_info.get.strip.lower`, `self_update.get_available_branch_values`, `self_update.get_selector_tag_options`, `str.strip`, `self_update.get_repo_version_info.get.strip`, `runtime.is_dockerized`, `self_update.get_repo_version_info`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/settings_get.py.dox.md b/api/settings_get.py.dox.md
new file mode 100644
index 000000000..e584283fd
--- /dev/null
+++ b/api/settings_get.py.dox.md
@@ -0,0 +1,46 @@
+# settings_get.py DOX
+
+## Purpose
+
+- Own the `settings_get.py` API endpoint.
+- This module returns current application settings.
+- Keep this file-level DOX profile synchronized with `settings_get.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `settings_get.py` owns the runtime implementation.
+- `settings_get.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `GetSettings` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+ - `get_methods(cls) -> list[str]`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `GetSettings` is an `ApiHandler`.
+- `GetSettings` defines `process(...)`.
+- `GetSettings` defines `get_methods(...)`.
+- Observed side-effect areas: settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `settings.get_settings`, `settings.convert_out`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/settings_set.py.dox.md b/api/settings_set.py.dox.md
new file mode 100644
index 000000000..0f08f9862
--- /dev/null
+++ b/api/settings_set.py.dox.md
@@ -0,0 +1,44 @@
+# settings_set.py DOX
+
+## Purpose
+
+- Own the `settings_set.py` API endpoint.
+- This module persists application settings updates.
+- Keep this file-level DOX profile synchronized with `settings_set.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `settings_set.py` owns the runtime implementation.
+- `settings_set.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SetSettings` (`ApiHandler`)
+ - `async process(self, input: dict[Any, Any], request: Request) -> dict[Any, Any] | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SetSettings` is an `ApiHandler`.
+- `SetSettings` defines `process(...)`.
+- Observed side-effect areas: settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.api`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `settings.convert_in`, `settings.set_settings`, `settings.convert_out`, `settings.Settings`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/settings_workdir_file_structure.py.dox.md b/api/settings_workdir_file_structure.py.dox.md
new file mode 100644
index 000000000..e52a742ef
--- /dev/null
+++ b/api/settings_workdir_file_structure.py.dox.md
@@ -0,0 +1,46 @@
+# settings_workdir_file_structure.py DOX
+
+## Purpose
+
+- Own the `settings_workdir_file_structure.py` API endpoint.
+- This module handles settings workdir file structure API requests.
+- Keep this file-level DOX profile synchronized with `settings_workdir_file_structure.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `settings_workdir_file_structure.py` owns the runtime implementation.
+- `settings_workdir_file_structure.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SettingsWorkdirFileStructure` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+ - `get_methods(cls) -> list[str]`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SettingsWorkdirFileStructure` is an `ApiHandler`.
+- `SettingsWorkdirFileStructure` defines `process(...)`.
+- `SettingsWorkdirFileStructure` defines `get_methods(...)`.
+- Observed side-effect areas: filesystem reads, settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `files.get_abs_path_development`, `Exception`, `file_tree.file_tree`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/skills.py.dox.md b/api/skills.py.dox.md
new file mode 100644
index 000000000..8da5af317
--- /dev/null
+++ b/api/skills.py.dox.md
@@ -0,0 +1,54 @@
+# skills.py DOX
+
+## Purpose
+
+- Own the `skills.py` API endpoint.
+- This module lists and manages available skills for settings and agent-facing skill flows.
+- Keep this file-level DOX profile synchronized with `skills.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `skills.py` owns the runtime implementation.
+- `skills.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Skills` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+ - `list_skills(self, input: Input)`
+ - `delete_skill(self, input: Input)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Skills` is an `ApiHandler`.
+- `Skills` defines `process(...)`.
+- Observed side-effect areas: filesystem reads, filesystem deletion.
+- Imported dependency areas include: `helpers`, `helpers.api`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `skills.list_skills`, `result.sort`, `str.strip`, `skills.delete_skill`, `projects.get_project_folder`, `runtime.is_development`, `Exception`, `self.list_skills`, `strip`, `files.normalize_a0_path`, `files.get_abs_path`, `self.delete_skill`, `files.is_in_dir`, `projects.get_project_meta`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_a0_connector_prompt_gating.py`
+ - `tests/test_browser_agent_regressions.py`
+ - `tests/test_document_query_plugin.py`
+ - `tests/test_fasta2a_client.py`
+ - `tests/test_office_canvas_setup.py`
+ - `tests/test_office_document_store.py`
+ - `tests/test_skills_runtime.py`
+ - `tests/test_time_travel.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/skills_import.py.dox.md b/api/skills_import.py.dox.md
new file mode 100644
index 000000000..4940e833a
--- /dev/null
+++ b/api/skills_import.py.dox.md
@@ -0,0 +1,44 @@
+# skills_import.py DOX
+
+## Purpose
+
+- Own the `skills_import.py` API endpoint.
+- This module handles skills import API requests.
+- Keep this file-level DOX profile synchronized with `skills_import.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `skills_import.py` owns the runtime implementation.
+- `skills_import.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SkillsImport` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SkillsImport` is an `ApiHandler`.
+- `SkillsImport` defines `process(...)`.
+- Observed side-effect areas: filesystem reads, filesystem writes, filesystem deletion.
+- Imported dependency areas include: `__future__`, `helpers`, `helpers.api`, `helpers.skills_import`, `os`, `pathlib`, `time`, `uuid`, `werkzeug.datastructures`, `werkzeug.utils`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`, `strip.lower`, `Path`, `tmp_dir.mkdir`, `secure_filename`, `time.strftime`, `skills_file.save`, `strip`, `files.get_abs_path`, `base.lower.endswith`, `import_skills`, `files.deabsolute_path`, `uuid.uuid4`, `tmp_path.unlink`, `base.lower`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/skills_import_preview.py.dox.md b/api/skills_import_preview.py.dox.md
new file mode 100644
index 000000000..107f55b2a
--- /dev/null
+++ b/api/skills_import_preview.py.dox.md
@@ -0,0 +1,44 @@
+# skills_import_preview.py DOX
+
+## Purpose
+
+- Own the `skills_import_preview.py` API endpoint.
+- This module handles skills import preview API requests.
+- Keep this file-level DOX profile synchronized with `skills_import_preview.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `skills_import_preview.py` owns the runtime implementation.
+- `skills_import_preview.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SkillsImportPreview` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `SkillsImportPreview` is an `ApiHandler`.
+- `SkillsImportPreview` defines `process(...)`.
+- Observed side-effect areas: filesystem reads, filesystem writes, filesystem deletion.
+- Imported dependency areas include: `__future__`, `helpers`, `helpers.api`, `helpers.skills_import`, `os`, `pathlib`, `time`, `uuid`, `werkzeug.datastructures`, `werkzeug.utils`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `self.use_context`, `strip.lower`, `Path`, `tmp_dir.mkdir`, `secure_filename`, `time.strftime`, `skills_file.save`, `strip`, `files.get_abs_path`, `base.lower.endswith`, `import_skills`, `files.deabsolute_path`, `uuid.uuid4`, `tmp_path.unlink`, `base.lower`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/skills_scan.py b/api/skills_scan.py
new file mode 100644
index 000000000..c1ba6871a
--- /dev/null
+++ b/api/skills_scan.py
@@ -0,0 +1,124 @@
+from __future__ import annotations
+
+import shutil
+import time
+import uuid
+from pathlib import Path
+from typing import Any
+
+from helpers import files, skills
+from helpers.api import ApiHandler, Request, Response
+from helpers.skills_import import extract_skills_zip
+from werkzeug.datastructures import FileStorage
+from werkzeug.utils import secure_filename
+
+
+class SkillsScan(ApiHandler):
+ """
+ Prepare skill scan targets for the Settings > Skills scanner.
+ """
+
+ async def process(self, input: dict[str, Any], request: Request) -> dict[str, Any] | Response:
+ if "skills_file" in request.files:
+ return self._prepare_uploaded_archive(request.files["skills_file"])
+
+ action = str(input.get("action") or "targets").strip().lower()
+ if action == "targets":
+ return self._list_installed_targets()
+
+ return {"success": False, "error": "Invalid action"}
+
+ def _list_installed_targets(self) -> dict[str, Any]:
+ targets: list[dict[str, Any]] = []
+ seen: set[str] = set()
+ total_skills = 0
+
+ for raw_root in skills.get_skill_roots():
+ root = Path(raw_root)
+ if not root.is_dir():
+ continue
+
+ skill_files = skills.discover_skill_md_files(root)
+ if not skill_files:
+ continue
+
+ key = str(root.resolve())
+ if key in seen:
+ continue
+ seen.add(key)
+
+ skill_count = len(skill_files)
+ total_skills += skill_count
+ targets.append(
+ {
+ "path": str(root),
+ "display_path": files.normalize_a0_path(str(root)),
+ "skill_count": skill_count,
+ }
+ )
+
+ targets.sort(key=lambda item: item["path"])
+ return {
+ "success": True,
+ "target_type": "installed",
+ "target_label": "Installed Agent Zero skills",
+ "targets": targets,
+ "paths": [item["path"] for item in targets],
+ "skill_count": total_skills,
+ }
+
+ def _prepare_uploaded_archive(self, skills_file: FileStorage) -> dict[str, Any]:
+ if not skills_file.filename:
+ return {"success": False, "error": "No file selected"}
+
+ base = secure_filename(skills_file.filename) # type: ignore[arg-type]
+ if not base.lower().endswith(".zip"):
+ return {"success": False, "error": "Skill scan uploads must be .zip files"}
+
+ tmp_dir = Path(files.get_abs_path("tmp", "uploads"))
+ tmp_dir.mkdir(parents=True, exist_ok=True)
+ unique = uuid.uuid4().hex[:8]
+ stamp = time.strftime("%Y%m%d_%H%M%S")
+ tmp_path = tmp_dir / f"skills_scan_{stamp}_{unique}_{base}"
+ skills_file.save(str(tmp_path))
+
+ cleanup_root: Path | None = None
+ try:
+ scan_root, cleanup_root = extract_skills_zip(
+ tmp_path,
+ tmp_subdir="skill_scans",
+ prefix=f"scan_{unique}",
+ )
+ skill_files = skills.discover_skill_md_files(scan_root)
+ skill_entries = [
+ {
+ "path": str(skill_md.parent),
+ "relative_path": str(skill_md.parent.relative_to(scan_root)),
+ }
+ for skill_md in skill_files
+ ]
+ warnings = []
+ if not skill_entries:
+ warnings.append("No SKILL.md files were found in the uploaded archive.")
+
+ return {
+ "success": True,
+ "target_type": "uploaded_archive",
+ "target_label": base,
+ "paths": [str(scan_root)],
+ "scan_path": str(scan_root),
+ "display_path": files.normalize_a0_path(str(scan_root)),
+ "cleanup_paths": [str(cleanup_root)],
+ "skill_count": len(skill_entries),
+ "skills": skill_entries,
+ "warnings": warnings,
+ }
+ except Exception as exc:
+ if cleanup_root:
+ shutil.rmtree(cleanup_root, ignore_errors=True)
+ return {"success": False, "error": f"Failed to prepare skill scan: {exc}"}
+ finally:
+ try:
+ tmp_path.unlink(missing_ok=True) # type: ignore[arg-type]
+ except Exception:
+ pass
diff --git a/api/skills_scan.py.dox.md b/api/skills_scan.py.dox.md
new file mode 100644
index 000000000..b277ae73e
--- /dev/null
+++ b/api/skills_scan.py.dox.md
@@ -0,0 +1,46 @@
+# skills_scan.py DOX
+
+## Purpose
+
+- Own the `skills_scan.py` API endpoint.
+- Provide scan target discovery and uploaded skills archive preparation for the Settings > Skills scanner.
+- Keep this file-level DOX profile synchronized with `skills_scan.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `skills_scan.py` owns the runtime implementation.
+- `skills_scan.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `SkillsScan` (`ApiHandler`)
+ - `async process(self, input: dict[str, Any], request: Request) -> dict[str, Any] | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- The JSON request action `targets` returns existing installed skill roots that contain at least one `SKILL.md`.
+- Multipart requests with `skills_file` accept only `.zip` uploads, extract them into `tmp/skill_scans`, discover contained `SKILL.md` folders, and return `paths` plus `cleanup_paths` for the scanner prompt.
+- Uploaded archives are not imported, installed, or executed by this endpoint.
+- Temporary uploaded zip files under `tmp/uploads` are deleted after extraction or failure.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- Observed side-effect areas: filesystem reads, filesystem writes, filesystem deletion.
+- Imported dependency areas include: `__future__`, `helpers`, `helpers.api`, `helpers.skills_import`, `pathlib`, `shutil`, `time`, `typing`, `uuid`, `werkzeug.datastructures`, `werkzeug.utils`.
+
+## Key Concepts
+
+- Installed target discovery uses `helpers.skills.get_skill_roots()` and filters to roots where `discover_skill_md_files()` finds skills.
+- Uploaded zip preparation uses `extract_skills_zip()` so zip entries remain bounded to the temp extraction root.
+- Response paths are local absolute paths for the scanner agent, while `display_path` provides normalized `/a0/...` style display when possible.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Do not execute uploaded files or scan targets in this endpoint.
+- Keep temp extraction paths explicit so the LLM-driven scan prompt can clean them up.
+
+## Verification
+
+- Run endpoint-specific or API tests for changed behavior; smoke-test uploaded zip and installed-skill scan modal flows when practical.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/subagents.py.dox.md b/api/subagents.py.dox.md
new file mode 100644
index 000000000..85777218f
--- /dev/null
+++ b/api/subagents.py.dox.md
@@ -0,0 +1,49 @@
+# subagents.py DOX
+
+## Purpose
+
+- Own the `subagents.py` API endpoint.
+- This module returns subordinate agent profile data for UI and delegation flows.
+- Keep this file-level DOX profile synchronized with `subagents.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `subagents.py` owns the runtime implementation.
+- `subagents.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Subagents` (`ApiHandler`)
+ - `async process(self, input: Input, request: Request) -> Output`
+ - `get_subagents_list(self)`
+ - `load_agent(self, name: str | None)`
+ - `save_agent(self, name: str | None, data: dict | None)`
+ - `delete_agent(self, name: str | None)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Subagents` is an `ApiHandler`.
+- `Subagents` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, filesystem deletion.
+- Imported dependency areas include: `helpers`, `helpers.api`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `subagents.get_agents_list`, `subagents.load_agent_data`, `subagents.SubAgent`, `subagents.save_agent_data`, `subagents.delete_agent_data`, `self.use_context`, `Exception`, `self.get_subagents_list`, `self.load_agent`, `self.save_agent`, `self.delete_agent`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_skills_runtime.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/tunnel.py.dox.md b/api/tunnel.py.dox.md
new file mode 100644
index 000000000..3618edc6e
--- /dev/null
+++ b/api/tunnel.py.dox.md
@@ -0,0 +1,48 @@
+# tunnel.py DOX
+
+## Purpose
+
+- Own the `tunnel.py` API endpoint.
+- This module manages tunnel provider status, start, and stop actions.
+- Keep this file-level DOX profile synchronized with `tunnel.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `tunnel.py` owns the runtime implementation.
+- `tunnel.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `Tunnel` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+- Top-level functions:
+- `async process(input: dict) -> dict | Response`
+- `stop()`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `Tunnel` is an `ApiHandler`.
+- `Tunnel` defines `process(...)`.
+- Observed side-effect areas: tunnel state.
+- Imported dependency areas include: `helpers`, `helpers.api`, `helpers.tunnel_manager`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `TunnelManager.get_instance`, `tunnel_manager.stop_tunnel`, `runtime.get_web_ui_port`, `tunnel_manager.start_tunnel`, `tunnel_manager.get_last_error`, `process`, `tunnel_manager.get_notifications`, `stop`, `tunnel_manager.get_tunnel_url`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_tunnel_remote_link.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/tunnel_proxy.py.dox.md b/api/tunnel_proxy.py.dox.md
new file mode 100644
index 000000000..b713482c9
--- /dev/null
+++ b/api/tunnel_proxy.py.dox.md
@@ -0,0 +1,46 @@
+# tunnel_proxy.py DOX
+
+## Purpose
+
+- Own the `tunnel_proxy.py` API endpoint.
+- This module proxies tunnel-related HTTP traffic through the configured tunnel provider.
+- Keep this file-level DOX profile synchronized with `tunnel_proxy.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `tunnel_proxy.py` owns the runtime implementation.
+- `tunnel_proxy.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `TunnelProxy` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+- Top-level functions:
+- `async process(input: dict) -> dict | Response`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `TunnelProxy` is an `ApiHandler`.
+- `TunnelProxy` defines `process(...)`.
+- Observed side-effect areas: network calls, settings/state persistence, tunnel state.
+- Imported dependency areas include: `helpers`, `helpers.api`, `helpers.tunnel_manager`, `requests`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `runtime.get_arg`, `requests.post`, `process`, `dotenv.get_dotenv_value`, `response.json`, `local_process`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/upload.py.dox.md b/api/upload.py.dox.md
new file mode 100644
index 000000000..9fccb571f
--- /dev/null
+++ b/api/upload.py.dox.md
@@ -0,0 +1,47 @@
+# upload.py DOX
+
+## Purpose
+
+- Own the `upload.py` API endpoint.
+- This module accepts general uploads into runtime upload storage.
+- Keep this file-level DOX profile synchronized with `upload.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `upload.py` owns the runtime implementation.
+- `upload.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `UploadFile` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+ - `allowed_file(self, filename)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `UploadFile` is an `ApiHandler`.
+- `UploadFile` defines `process(...)`.
+- Observed side-effect areas: filesystem reads, settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.api`, `helpers.security`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `request.files.getlist`, `Exception`, `self.allowed_file`, `safe_filename`, `file.save`, `files.get_abs_path`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_browser_agent_regressions.py`
+ - `tests/test_image_get_security.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/upload_work_dir_files.py.dox.md b/api/upload_work_dir_files.py.dox.md
new file mode 100644
index 000000000..6cf9370ae
--- /dev/null
+++ b/api/upload_work_dir_files.py.dox.md
@@ -0,0 +1,47 @@
+# upload_work_dir_files.py DOX
+
+## Purpose
+
+- Own the `upload_work_dir_files.py` API endpoint.
+- This module handles workdir file operations for upload work dir files.
+- Keep this file-level DOX profile synchronized with `upload_work_dir_files.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `upload_work_dir_files.py` owns the runtime implementation.
+- `upload_work_dir_files.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `UploadWorkDirFiles` (`ApiHandler`)
+ - `async process(self, input: dict, request: Request) -> dict | Response`
+- Top-level functions:
+- `async upload_files(uploaded_files: list[FileStorage], current_path: str)`
+- `async upload_file(current_path: str, filename: str, base64_content: str)`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `UploadWorkDirFiles` is an `ApiHandler`.
+- `UploadWorkDirFiles` defines `process(...)`.
+- Observed side-effect areas: filesystem writes.
+- Imported dependency areas include: `api`, `base64`, `helpers`, `helpers.api`, `helpers.file_browser`, `os`, `posixpath`, `werkzeug.datastructures`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `runtime.is_development`, `FileBrowser`, `browser.save_file_b64`, `request.files.getlist`, `browser.save_files`, `Exception`, `upload_files`, `runtime.call_development_function`, `file.stream.read`, `base64.b64encode.decode`, `extension.call_extensions_async`, `base64.b64encode`, `posixpath.join`, `str.rstrip`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/ws_dev_test.py.dox.md b/api/ws_dev_test.py.dox.md
new file mode 100644
index 000000000..aaad42608
--- /dev/null
+++ b/api/ws_dev_test.py.dox.md
@@ -0,0 +1,44 @@
+# ws_dev_test.py DOX
+
+## Purpose
+
+- Own the `ws_dev_test.py` API endpoint.
+- This module provides a development WebSocket test namespace.
+- Keep this file-level DOX profile synchronized with `ws_dev_test.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `ws_dev_test.py` owns the runtime implementation.
+- `ws_dev_test.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `WsDevTest` (`WsHandler`)
+ - `async process(self, event: str, data: dict, sid: str) -> dict[str, Any] | WsResult | None`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `WsDevTest` is a `WsHandler`.
+- `WsDevTest` defines `process(...)`.
+- Observed side-effect areas: filesystem writes, network calls, WebSocket state.
+- Imported dependency areas include: `asyncio`, `helpers`, `helpers.print_style`, `helpers.ws`, `helpers.ws_manager`, `typing`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `event.startswith`, `self.manager.register_diagnostic_watcher`, `self.manager.unregister_diagnostic_watcher`, `PrintStyle.info`, `PrintStyle.debug`, `PrintStyle.warning`, `runtime.is_development`, `WsResult.error`, `self.broadcast`, `asyncio.sleep`, `self.emit_to`, `self.dispatch_to_all_sids`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/ws_hello.py.dox.md b/api/ws_hello.py.dox.md
new file mode 100644
index 000000000..863b99167
--- /dev/null
+++ b/api/ws_hello.py.dox.md
@@ -0,0 +1,44 @@
+# ws_hello.py DOX
+
+## Purpose
+
+- Own the `ws_hello.py` API endpoint.
+- This module provides a small WebSocket hello/test namespace.
+- Keep this file-level DOX profile synchronized with `ws_hello.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `ws_hello.py` owns the runtime implementation.
+- `ws_hello.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `WsHello` (`WsHandler`)
+ - `async process(self, event: str, data: dict, sid: str) -> dict | None`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `WsHello` is a `WsHandler`.
+- `WsHello` defines `process(...)`.
+- Observed side-effect areas: WebSocket state.
+- Imported dependency areas include: `helpers.print_style`, `helpers.ws`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `PrintStyle.info`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- No direct test reference was found by name search; choose the nearest behavioral test or perform a focused smoke check.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/api/ws_webui.py.dox.md b/api/ws_webui.py.dox.md
new file mode 100644
index 000000000..a9cebf317
--- /dev/null
+++ b/api/ws_webui.py.dox.md
@@ -0,0 +1,49 @@
+# ws_webui.py DOX
+
+## Purpose
+
+- Own the `ws_webui.py` API endpoint.
+- This module owns the primary WebUI WebSocket namespace and event bridge.
+- Keep this file-level DOX profile synchronized with `ws_webui.py` because this directory is intentionally flat.
+
+## Ownership
+
+- `ws_webui.py` owns the runtime implementation.
+- `ws_webui.py.dox.md` owns durable notes about responsibilities, contracts, side effects, and verification for that implementation.
+- Classes:
+- `WsWebui` (`WsHandler`)
+ - `async on_connect(self, sid: str) -> None`
+ - `async on_disconnect(self, sid: str) -> None`
+ - `async process(self, event: str, data: dict, sid: str) -> dict | None`
+
+## Runtime Contracts
+
+- HTTP handlers must derive from `helpers.api.ApiHandler`; WebSocket handlers must derive from `helpers.ws.WsHandler`.
+- Update this file whenever request payloads, authentication or CSRF requirements, response shapes, route side effects, or WebSocket event contracts change.
+- `WsWebui` is a `WsHandler`.
+- `WsWebui` defines `process(...)`.
+- Observed side-effect areas: network calls, WebSocket state, settings/state persistence.
+- Imported dependency areas include: `helpers`, `helpers.ws`.
+
+## Key Concepts
+
+- Important called helpers/classes observed in the source: `extension.call_extensions_async`.
+- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
+
+## Work Guidance
+
+- Preserve authentication, CSRF, loopback, and API-key checks unless the endpoint contract explicitly changes.
+- Update frontend callers, plugin callers, and tests together when payload shape changes.
+- Use `helpers.api.Response` for non-JSON responses, files, redirects, or status-specific replies.
+
+## Verification
+
+- Run endpoint-specific or API/WebSocket tests for changed behavior; smoke-test browser callers when no focused test exists.
+- Related tests observed by source search:
+ - `tests/test_state_sync_handler.py`
+ - `tests/test_state_sync_welcome_screen.py`
+ - `tests/test_ws_handlers.py`
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/conf/model_providers.yaml b/conf/model_providers.yaml
index 12235e58f..58bb2e7cc 100644
--- a/conf/model_providers.yaml
+++ b/conf/model_providers.yaml
@@ -14,7 +14,7 @@
#
# Optional fields:
# kwargs: A dictionary of extra parameters to pass to LiteLLM.
-# This is useful for `api_base`, `extra_headers`, etc.
+# This is useful for `api_base`, `extra_headers`, non-secret local placeholders, etc.
#
# Optional model listing fields (used by the Model Configuration plugin):
# models_list:
@@ -83,6 +83,20 @@ chat:
models_list:
endpoint_url: "/v1/models"
default_base: "http://host.docker.internal:1234"
+ kwargs:
+ a0_api_mode: chat
+ api_base: "http://host.docker.internal:1234/v1"
+ api_key: "lm-studio"
+ llama_cpp:
+ name: llama.cpp
+ litellm_provider: hosted_vllm
+ models_list:
+ endpoint_url: "/v1/models"
+ default_base: "http://host.docker.internal:8080"
+ kwargs:
+ a0_api_mode: chat
+ api_base: "http://host.docker.internal:8080/v1"
+ api_key: "llama-cpp"
mistral:
name: Mistral AI
litellm_provider: mistral
@@ -107,12 +121,26 @@ chat:
endpoint_url: "/api/tags"
format: "ollama"
default_base: "http://host.docker.internal:11434"
+ kwargs:
+ a0_api_mode: chat
+ api_base: "http://host.docker.internal:11434"
+ omlx:
+ name: oMLX
+ litellm_provider: hosted_vllm
+ models_list:
+ endpoint_url: "/v1/models"
+ default_base: "http://host.docker.internal:8000"
+ kwargs:
+ a0_api_mode: chat
+ api_base: "http://host.docker.internal:8000/v1"
+ api_key: "omlx"
ollama_cloud:
name: Ollama Cloud
litellm_provider: openai
models_list:
endpoint_url: "/models"
kwargs:
+ a0_api_mode: chat
api_base: https://ollama.com/v1
openai:
name: OpenAI
@@ -150,9 +178,20 @@ chat:
models_list:
endpoint_url: "https://api.venice.ai/api/v1/models"
kwargs:
+ a0_api_mode: chat
api_base: https://api.venice.ai/api/v1
venice_parameters:
include_venice_system_prompt: false
+ vllm:
+ name: vLLM
+ litellm_provider: hosted_vllm
+ models_list:
+ endpoint_url: "/v1/models"
+ default_base: "http://host.docker.internal:8000"
+ kwargs:
+ a0_api_mode: chat
+ api_base: "http://host.docker.internal:8000/v1"
+ api_key: "vllm"
xai:
name: xAI
litellm_provider: xai
@@ -175,6 +214,8 @@ chat:
other:
name: Other OpenAI compatible
litellm_provider: openai
+ kwargs:
+ a0_api_mode: chat
embedding:
huggingface:
@@ -186,12 +227,29 @@ embedding:
lm_studio:
name: LM Studio
litellm_provider: lm_studio
+ kwargs:
+ api_base: "http://host.docker.internal:1234/v1"
+ api_key: "lm-studio"
+ llama_cpp:
+ name: llama.cpp
+ litellm_provider: hosted_vllm
+ kwargs:
+ api_base: "http://host.docker.internal:8080/v1"
+ api_key: "llama-cpp"
mistral:
name: Mistral AI
litellm_provider: mistral
ollama:
name: Ollama
litellm_provider: ollama
+ kwargs:
+ api_base: "http://host.docker.internal:11434"
+ omlx:
+ name: oMLX
+ litellm_provider: hosted_vllm
+ kwargs:
+ api_base: "http://host.docker.internal:8000/v1"
+ api_key: "omlx"
openai:
name: OpenAI
litellm_provider: openai
@@ -225,6 +283,12 @@ embedding:
endpoint_url: "https://api.venice.ai/api/v1/models"
kwargs:
api_base: https://api.venice.ai/api/v1
+ vllm:
+ name: vLLM
+ litellm_provider: hosted_vllm
+ kwargs:
+ api_base: "http://host.docker.internal:8000/v1"
+ api_key: "vllm"
other:
name: Other OpenAI compatible
litellm_provider: openai
diff --git a/docker/AGENTS.md b/docker/AGENTS.md
index 0b58f32e8..757608953 100644
--- a/docker/AGENTS.md
+++ b/docker/AGENTS.md
@@ -31,4 +31,9 @@
## Child DOX Index
-No child DOX files.
+Direct child DOX files:
+
+| Child | Scope |
+| --- | --- |
+| [base/AGENTS.md](base/AGENTS.md) | Base image Dockerfile, copied filesystem, and installation scripts. |
+| [run/AGENTS.md](run/AGENTS.md) | Runnable image Dockerfile, compose example, entrypoints, and install scripts. |
diff --git a/docker/base/AGENTS.md b/docker/base/AGENTS.md
new file mode 100644
index 000000000..bcfb986d7
--- /dev/null
+++ b/docker/base/AGENTS.md
@@ -0,0 +1,34 @@
+# Docker Base Image DOX
+
+## Purpose
+
+- Own the Agent Zero base image build context.
+- Build the operating system, package, Python, SearXNG, SSH, and bootstrap layers reused by runnable images.
+
+## Ownership
+
+- `Dockerfile` owns base image layering and installation order.
+- `build.txt` owns maintainer build and push command notes.
+- `fs/ins/` owns installation scripts copied into the image.
+- Files under `fs/` are copied to container root during the base build.
+
+## Local Contracts
+
+- Preserve cache-friendly package and runtime installation stages.
+- Keep locale and timezone defaults compatible with the root Docker contract.
+- Do not add secrets, user data, or local environment files to the image context.
+- Installation scripts must be noninteractive and suitable for multi-architecture buildx runs.
+
+## Work Guidance
+
+- Keep base dependencies here only when they are common to runnable Agent Zero images.
+- Coordinate Python runtime changes with root Docker documentation and runnable image setup.
+
+## Verification
+
+- Build `docker/base` when changing Dockerfile or install scripts.
+- Run a runnable image smoke check when base runtime behavior changes.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/docker/run/AGENTS.md b/docker/run/AGENTS.md
new file mode 100644
index 000000000..b5607ae69
--- /dev/null
+++ b/docker/run/AGENTS.md
@@ -0,0 +1,39 @@
+# Docker Runtime Image DOX
+
+## Purpose
+
+- Own the runnable Agent Zero image context and local compose example.
+- Install Agent Zero from a selected branch onto the base image and prepare runtime entrypoints.
+
+## Ownership
+
+- `Dockerfile` owns branch-based image assembly, exposed ports, and container startup command.
+- `docker-compose.yml` owns the local compose service example.
+- `build.txt` owns maintainer build and push command notes.
+- `fs/exe/` owns runtime entrypoint, supervisor, self-update, Node eval, and service scripts.
+- `fs/ins/` owns preinstall, installation, virtualenv, Playwright, SSH, and postinstall scripts.
+- Files under `fs/` are copied to container root during the runtime build.
+
+## Local Contracts
+
+- `BRANCH` is required for branch-based Docker builds.
+- Preserve exposed ports for SSH, HTTP, and tunneled services unless docs and workflows are updated together.
+- Keep the two-runtime Python model aligned with the root contract.
+- Do not bake secrets, local `.env` values, or user data into the image.
+- Runtime startup must ensure `/a0/usr/uploads` exists before supervised services start.
+- Runtime startup raises the soft open-file limit toward `A0_NOFILE_LIMIT` (default `65535`) before supervisord starts, bounded by the container hard limit.
+- Self-update user-data backups skip Time Travel shadow history under `usr/.time_travel/` and transient Desktop agent state.
+
+## Work Guidance
+
+- Keep startup scripts explicit about framework runtime versus execution runtime.
+- Coordinate tag, branch, and publishing changes with GitHub workflow automation.
+
+## Verification
+
+- Build `docker/run` when changing Dockerfile or install scripts.
+- Smoke-test container startup after entrypoint, supervisor, port, or compose changes.
+
+## Child DOX Index
+
+No child DOX files.
diff --git a/docker/run/docker-compose.yml b/docker/run/docker-compose.yml
index cc48f3f1b..b80da0272 100644
--- a/docker/run/docker-compose.yml
+++ b/docker/run/docker-compose.yml
@@ -5,4 +5,10 @@ services:
volumes:
- ./agent-zero:/a0
ports:
- - "50080:80"
\ No newline at end of file
+ - "50080:80"
+ ulimits:
+ nofile:
+ soft: 65535
+ hard: 65535
+ extra_hosts:
+ - "host.docker.internal:host-gateway"
diff --git a/docker/run/fs/exe/initialize.sh b/docker/run/fs/exe/initialize.sh
index 8c329bb30..f4c4dcd25 100644
--- a/docker/run/fs/exe/initialize.sh
+++ b/docker/run/fs/exe/initialize.sh
@@ -9,9 +9,49 @@ if [ -z "$1" ]; then
fi
BRANCH="$1"
+raise_open_file_limit() {
+ local requested="${A0_NOFILE_LIMIT:-65535}"
+ local soft
+ local hard
+ local target
+
+ if ! [[ "$requested" =~ ^[0-9]+$ ]] || [ "$requested" -lt 1 ]; then
+ echo "Warning: invalid A0_NOFILE_LIMIT='$requested'; keeping open file limit at $(ulimit -S -n)." >&2
+ return
+ fi
+
+ soft="$(ulimit -S -n)"
+ hard="$(ulimit -H -n)"
+
+ if [ "$soft" = "unlimited" ]; then
+ echo "Open file limit is already unlimited."
+ return
+ fi
+
+ target="$requested"
+ if [ "$hard" != "unlimited" ] && [ "$target" -gt "$hard" ]; then
+ target="$hard"
+ fi
+
+ if [ "$target" -gt "$soft" ]; then
+ if ulimit -S -n "$target"; then
+ echo "Raised open file soft limit from $soft to $(ulimit -S -n) (hard: $hard)."
+ else
+ echo "Warning: failed to raise open file soft limit from $soft to $target (hard: $hard)." >&2
+ fi
+ else
+ echo "Open file soft limit is $soft (target: $requested, hard: $hard)."
+ fi
+}
+
+raise_open_file_limit
+
# Copy all contents from persistent /per to root directory (/) without overwriting
cp -r --no-preserve=ownership,mode /per/* /
+# Ensure upload storage exists before API and connector callers can reference it.
+mkdir -p /a0/usr/uploads
+
# allow execution of /root/.bashrc and /root/.profile
chmod 444 /root/.bashrc
chmod 444 /root/.profile
diff --git a/docker/run/fs/exe/self_update_manager.py b/docker/run/fs/exe/self_update_manager.py
index abf4e595f..4ae23ce28 100644
--- a/docker/run/fs/exe/self_update_manager.py
+++ b/docker/run/fs/exe/self_update_manager.py
@@ -405,6 +405,11 @@ def should_exclude_from_usr_backup(
logger: AttemptLogger,
) -> bool:
parts = relative_dir.parts
+ if parts and parts[0] == ".time_travel":
+ logger.log(
+ f"Skipping Time Travel history during usr backup: {Path('usr') / relative_dir}"
+ )
+ return True
if (
len(parts) >= 6
and parts[0] == "plugins"
diff --git a/docker/run/fs/ins/install_A0.sh b/docker/run/fs/ins/install_A0.sh
index 0aeaf13ff..7b5d0d807 100644
--- a/docker/run/fs/ins/install_A0.sh
+++ b/docker/run/fs/ins/install_A0.sh
@@ -36,8 +36,6 @@ fi
# Install remaining A0 python packages
uv pip install -r /git/agent-zero/requirements.txt
-# override for packages that have unnecessarily strict dependencies
-uv pip install -r /git/agent-zero/requirements2.txt
# install playwright
bash /ins/install_playwright.sh "$@"
diff --git a/docker/run/fs/ins/install_additional.sh b/docker/run/fs/ins/install_additional.sh
index 31546deaa..e9c844be7 100644
--- a/docker/run/fs/ins/install_additional.sh
+++ b/docker/run/fs/ins/install_additional.sh
@@ -46,16 +46,11 @@ install_xpra_repo() {
apt-get update
if ! xpra_install_check; then
- if [ "$arch" != "amd64" ]; then
- echo "xpra packages are not installable from ${uri} ${suite} for ${arch}; falling back to https://xpra.org trixie"
- XPRA_PACKAGES=(xpra-server xpra-x11 xpra-html5)
- configure_xpra_repo "https://xpra.org" "trixie" "$arch"
- apt-get update
- if ! xpra_install_check; then
- cat /tmp/xpra-install-check.log
- exit 1
- fi
- else
+ echo "xpra packages are not installable from ${uri} ${suite} for ${arch}; falling back to https://xpra.org trixie"
+ XPRA_PACKAGES=(xpra-server xpra-x11 xpra-html5)
+ configure_xpra_repo "https://xpra.org" "trixie" "$arch"
+ apt-get update
+ if ! xpra_install_check; then
cat /tmp/xpra-install-check.log
exit 1
fi
diff --git a/docs/AGENTS.md b/docs/AGENTS.md
index 3f3002b01..c81fb55bd 100644
--- a/docs/AGENTS.md
+++ b/docs/AGENTS.md
@@ -9,6 +9,7 @@
- `README.md`, `quickstart.md`, `guides/`, and `setup/` cover user-facing setup and workflows.
- `developer/` covers compact developer references and source handoffs.
+- `plans/` covers implementation plans, migration notes, and staged technical roadmaps.
- `res/` contains documentation images and other documentation assets.
## Local Contracts
diff --git a/docs/README.md b/docs/README.md
index 655dc0e44..5c668f029 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -12,8 +12,9 @@ docs focus on practical setup, screenshots, and user workflows.
## Quick Start
- **[Quickstart Guide](quickstart.md):** Get up and running in 5 minutes with Agent Zero.
-- **[First-Run Onboarding](guides/onboarding.md):** Choose Cloud or Local, add a provider key, and select main and utility models.
-- **[Installation Guide](setup/installation.md):** Install scripts, updates, and advanced Docker setup (includes [How to Update](setup/installation.md#how-to-update-agent-zero)).
+- **[Agent Zero Launcher](guides/launcher.md):** Use the desktop app to set up Docker, install Agent Zero, open Instances, or connect a remote Instance.
+- **[First-Run Onboarding](guides/onboarding.md):** Choose Cloud, AI account, or Local access, then select main and utility models.
+- **[Installation Guide](setup/installation.md):** A0 Launcher downloads, A0 Install, direct Docker, updates, and advanced Docker setup (includes [How to Update](setup/installation.md#how-to-update-agent-zero)).
- **[A0 CLI Connector](guides/a0-cli-connector.md):** Install the host connector for a running Agent Zero instance, use the command palette, and switch Browser modes.
- **[Self Update](guides/self-update.md):** How the in-app updater works (technical reference).
- **[VPS Deployment](setup/vps-deployment.md):** Deploy Agent Zero on a remote server.
@@ -22,12 +23,13 @@ docs focus on practical setup, screenshots, and user workflows.
## User Guides
- **[Usage Guide](guides/usage.md):** Practical tour of Agent Zero's main workflows.
+- **[Agent Zero Launcher](guides/launcher.md):** Fresh-machine Launcher walkthrough, Docker setup gate, Installs, Instances, and docs screenshot capture with Playwright/Electron.
- **[First-Run Onboarding](guides/onboarding.md):** Set up OpenRouter, our proxy API or another provider with the guided wizard.
- **[Browser Guide](guides/browser.md):** Use the built-in Browser, live Canvas surface, annotations, screenshots, host browser mode, and extensions.
- **[Desktop Guide](guides/desktop.md):** Use the built-in Linux desktop, GUI apps, and LibreOffice Writer/Calc/Impress Cowork.
- **[A0 CLI Connector](guides/a0-cli-connector.md):** Terminal-first host connector for Agent Zero, with screenshots of the host picker, connected shell, command palette, and Browser modes.
- **[Create a Small Plugin](guides/create-plugin.md):** Build and review a tiny Web UI plugin that adds an unread dot to the chat list.
-- **[Skills Guide](guides/skills.md):** Open the Skills selector, add active skills, and remove prompt extras you no longer need.
+- **[Skills Guide](guides/skills.md):** Open the Skills selector, add active skills, and remove prompt protocol entries you no longer need.
- **[Agent Profiles](guides/agent-profiles.md):** Switch the current chat profile or create a new guided profile from the chat input.
- **[Model Presets](guides/model-presets.md):** Create simple named shortcuts for model setups.
- **[Memory Guide](guides/memory.md):** Search, edit, delete, and curate memories so useful context does not become stale noise.
@@ -63,6 +65,7 @@ docs focus on practical setup, screenshots, and user workflows.
- [Quick Start](#quick-start)
- [Quickstart Guide](quickstart.md)
+ - [Agent Zero Launcher](guides/launcher.md)
- [First-Run Onboarding](guides/onboarding.md)
- [Installation Guide](setup/installation.md)
- [How to Update Agent Zero](setup/installation.md#how-to-update-agent-zero)
@@ -113,6 +116,7 @@ docs focus on practical setup, screenshots, and user workflows.
- [File Browser](guides/usage.md#file-browser)
- [Memory Management](guides/usage.md#memory-management)
- [Backup And Restore](guides/usage.md#backup-and-restore)
+ - [Agent Zero Launcher](guides/launcher.md)
- [Browser Guide](guides/browser.md)
- [Desktop Guide](guides/desktop.md)
- [A0 CLI Connector](guides/a0-cli-connector.md)
diff --git a/docs/guides/a0-cli-connector.md b/docs/guides/a0-cli-connector.md
index 6dd4c2edc..f0812efc8 100644
--- a/docs/guides/a0-cli-connector.md
+++ b/docs/guides/a0-cli-connector.md
@@ -132,15 +132,22 @@ machine.
- [ ] Keep A0 CLI connected to the Agent Zero chat.
- [ ] In Agent Zero Web UI, open Browser plugin settings and choose **Bring Your
Own Browser**.
-- [ ] If you want Agent Zero to use an already-open personal Chrome window, open
- that browser first.
-- [ ] In that browser, go to `chrome://inspect/#remote-debugging`.
+- [ ] If you want Agent Zero to use an already-open personal browser window,
+ open that browser first.
+- [ ] In that browser, open its remote debugging page.
- [ ] Enable **Allow remote debugging for this browser instance**.
+Remote debugging pages:
+
+| Browser | Page |
+| --- | --- |
+| Chrome, Edge, Brave, Vivaldi, Chromium | `chrome://inspect/#remote-debugging` |
+| Opera | `opera://inspect/#remote-debugging` |
+

When Agent Zero performs its first Browser action against that host browser,
-Chrome asks for confirmation. Click **Allow** if you trust this Agent Zero
+the browser asks for confirmation. Click **Allow** if you trust this Agent Zero
instance and A0 CLI connection.

@@ -149,11 +156,30 @@ A0 CLI does not take over the browser while it is only checking status. Browser
control starts when Agent Zero actually needs to use the browser.
> [!IMPORTANT]
-> Remote debugging gives the connected app full control of that Chrome session,
+> Remote debugging gives the connected app full control of that browser session,
> including access to saved data, cookies, site data, and navigation. Use it only
> with trusted Agent Zero instances and browser windows you intend the agent to
> control.
+The **Host browser** list in Browser settings comes from the connected local A0
+CLI, not from the Agent Zero Web UI server. It shows Automatic, currently
+advertised debug endpoints, and **Custom endpoint**. If a newly authorized
+browser does not appear, restart or reconnect A0 CLI.
+
+If the inspect checkbox is not enough for your browser build, launch it with an
+explicit remote debugging port and a separate profile:
+
+```bash
+opera --remote-debugging-port=9222 --user-data-dir="$HOME/.config/a0-opera-debug"
+```
+
+Then choose **Custom endpoint** in Browser settings, run `/browser ws://...` in
+A0 CLI, or pass the full DevTools websocket endpoint to A0 CLI:
+
+```bash
+export A0_HOST_BROWSER_REMOTE_DEBUGGING_ENDPOINTS="ws://127.0.0.1:9222/devtools/browser/..."
+```
+
### Browser Profiles
```bash
diff --git a/docs/guides/agent-profiles.md b/docs/guides/agent-profiles.md
index 41b885fd2..b3e9d9751 100644
--- a/docs/guides/agent-profiles.md
+++ b/docs/guides/agent-profiles.md
@@ -66,6 +66,10 @@ These controls are related, but they solve different problems.
| **Project** | Files, workspace, memories, instructions, secrets, and long-running context. |
| **Model Preset** | Which models are used for the chat. |
+For small local models that narrate instead of calling tools, use the bundled
+**Tiny Local** profile or the project-scoped Prompt Include recipe in
+[Local Model Tool Use](local-model-tool-use.md).
+
Example:
- use a **Project** for a client repository;
diff --git a/docs/guides/browser.md b/docs/guides/browser.md
index ed406327a..33d1b719f 100644
--- a/docs/guides/browser.md
+++ b/docs/guides/browser.md
@@ -137,8 +137,8 @@ development, Agent Zero can install it the first time it is needed.
## Bring Your Own Browser
-Bring Your Own Browser lets Agent Zero use Chrome, Edge, or Chromium on your own
-computer through A0 CLI.
+Bring Your Own Browser lets Agent Zero use Chrome, Edge, Brave, Opera, Vivaldi,
+or Chromium on your own computer through A0 CLI.
Use it when the page, login, or browser profile should stay on your machine.
@@ -146,8 +146,35 @@ Requirements:
- [ ] Keep A0 CLI connected to the Agent Zero chat.
- [ ] Choose **Bring Your Own Browser** in Browser settings.
-- [ ] Use Chrome, Edge, or Chromium on the host.
-- [ ] For personal Chrome remote debugging, open the host browser first, go to `chrome://inspect/#remote-debugging`, and enable **Allow remote debugging for this browser instance**.
+- [ ] Use a Chromium-family browser on the host: Chrome, Edge, Brave, Opera, Vivaldi, or Chromium.
+- [ ] For an already-open browser, open its remote debugging page and enable **Allow remote debugging for this browser instance**.
+
+Remote debugging pages:
+
+| Browser | Page |
+| --- | --- |
+| Chrome, Edge, Brave, Vivaldi, Chromium | `chrome://inspect/#remote-debugging` |
+| Opera | `opera://inspect/#remote-debugging` |
+
+The **Host browser** list shows Automatic, currently advertised debug endpoints,
+and **Custom endpoint**. If a browser does not appear after enabling remote
+debugging, restart or reconnect the local A0 CLI. Restarting only the Agent Zero
+Web UI server does not refresh the browser inventory; the list comes from the
+connected CLI.
+
+As a fallback, launch the browser with an explicit debugging port and profile
+directory:
+
+```bash
+opera --remote-debugging-port=9222 --user-data-dir="$HOME/.config/a0-opera-debug"
+```
+
+Then choose **Custom endpoint** in Browser settings, or pass the full DevTools
+websocket endpoint to the CLI:
+
+```bash
+export A0_HOST_BROWSER_REMOTE_DEBUGGING_ENDPOINTS="ws://127.0.0.1:9222/devtools/browser/..."
+```

diff --git a/docs/guides/launcher.md b/docs/guides/launcher.md
new file mode 100644
index 000000000..2036eb88e
--- /dev/null
+++ b/docs/guides/launcher.md
@@ -0,0 +1,177 @@
+# Agent Zero Launcher
+
+Agent Zero Launcher is the desktop app for installing, running, switching, and
+opening Dockerized Agent Zero Instances without starting from Docker commands.
+
+Use it when you are setting up a new machine, when you want a quiet inventory of
+installed Agent Zero images, or when you want one place to open local and remote
+Instances.
+
+## Start Fresh On A New Machine
+
+1. Download Agent Zero Launcher from the
+ [A0 Launcher releases](https://github.com/agent0ai/a0-launcher/releases).
+2. Open the app.
+3. If the launcher cannot reach Docker yet, follow the setup dialog.
+4. If Agent Zero is already hosted on another computer or VPS, click
+ **Add remote Instance** instead of setting up local Docker.
+
+
+
+The first setup dialog keeps the choice simple:
+
+- **Continue** starts the local runtime setup or refreshes the runtime state.
+- **Refresh** checks again after you start Docker yourself.
+- **Add remote Instance** saves an existing Agent Zero URL and lets you use the
+ Launcher without local Docker.
+
+## Installs
+
+When Docker is ready, Launcher opens to **Installs**. This page shows official
+Agent Zero release lines and local images.
+
+
+
+Cards usually mean:
+
+- **latest** tracks the newest published Agent Zero release image.
+- **ready** tracks the development-ready image when you intentionally work from
+ that branch.
+- Version cards such as **1.20**, **1.19**, or **1.18** are pinned release
+ images.
+- **Install** downloads an image.
+- **Run** starts an installed image as a local Instance.
+
+## Instances
+
+Open **Instances** after you run Agent Zero. This is where local containers and
+saved remote Instances live.
+
+Use the Instance card to:
+
+- open the Web UI;
+- start, stop, rename, or delete the container;
+- open logs;
+- use **Backup `/a0/usr`** to download the same user-data backup you can create
+ from Agent Zero Core;
+- use **Restore `/a0/usr`** to restore that backup zip into the selected
+ Instance;
+- open A0 CLI when the host connector is installed.
+
+Launcher keeps local Instances and remote Instances separate, so deleting a
+container is not the same as deleting a saved remote URL or a workspace backup.
+
+## Updating With Launcher
+
+For same-major Agent Zero updates, the Web UI **Self Update** is still the
+normal path.
+
+For a major image jump such as v1.20 -> v2.0, use Launcher or Docker to start a
+new v2.0 Instance, then restore a backup from the old Instance. In Launcher, the
+flow is: **Instances -> Backup `/a0/usr`** on the old v1.20 Instance, **Installs
+-> latest -> Install/Run**, then **Instances -> Restore `/a0/usr`** on the new
+v2.0 Instance. This avoids mixing an old root install with a new Docker image.
+
+See [Updating from v1.20 to v2.0](../setup/installation.md#updating-from-v120-to-v20).
+
+## Capture Launcher Screenshots With Playwright
+
+Launcher is an Electron app, so browser-only Playwright commands are not enough.
+Use Playwright's Electron bridge and the local Electron binary from the Launcher
+repo.
+
+The pattern below installs Playwright into a temporary folder outside the repo,
+launches local Launcher content, waits for the `a0app://content/` window, and
+saves a screenshot.
+
+```bash
+mkdir -p /tmp/a0-launcher-playwright
+npm install --prefix /tmp/a0-launcher-playwright playwright
+```
+
+```bash
+NODE_PATH=/tmp/a0-launcher-playwright/node_modules node <<'JS'
+const { _electron: electron } = require("playwright");
+
+const launcher = "/home/eclypso/a0/a0-launcher";
+const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+
+(async () => {
+ const app = await electron.launch({
+ executablePath: `${launcher}/node_modules/electron/dist/electron`,
+ args: [launcher],
+ env: {
+ ...process.env,
+ A0_LAUNCHER_LOCAL_REPO: launcher,
+ ELECTRON_DISABLE_SECURITY_WARNINGS: "true",
+ },
+ });
+
+ const windows = [];
+ app.on("window", (page) => windows.push(page));
+ windows.push(await app.firstWindow());
+
+ let page = null;
+ const deadline = Date.now() + 45000;
+ while (Date.now() < deadline && !page) {
+ page = windows.find((item) =>
+ item && !item.isClosed() && item.url().startsWith("a0app://content/")
+ ) || null;
+ if (!page) {
+ await app.waitForEvent("window", { timeout: 1000 })
+ .then((item) => windows.push(item))
+ .catch(() => null);
+ await sleep(250);
+ }
+ }
+
+ if (!page) throw new Error("Launcher content window did not open");
+
+ await page.waitForLoadState("networkidle", { timeout: 10000 }).catch(() => null);
+ await sleep(3000);
+ await page.screenshot({
+ path: `${launcher}/output/playwright/launcher-installs.png`,
+ fullPage: false,
+ });
+ await app.close();
+})();
+JS
+```
+
+For docs screenshots that show the first-run runtime gate without changing the
+real machine state, open the real Launcher page and render the real runtime-gate
+component with a minimal demo state:
+
+```js
+await page.evaluate(async () => {
+ const { renderRuntimeGate } = await import(
+ "a0app://content/components/docker-manager/runtime-gate/runtime-gate.js"
+ );
+
+ renderRuntimeGate({
+ stateLoaded: true,
+ dockerAvailable: false,
+ runtime: {
+ platform: "linux",
+ state: "not_provisioned",
+ action: "install",
+ canProvision: true,
+ setupActionLabel: "Setup Agent Zero",
+ detail: "No local container runtime was found.",
+ },
+ versions: [{ id: "latest", availability: "available" }],
+ images: [],
+ containers: [],
+ remoteInstances: [],
+ }, {
+ refresh() {},
+ provisionRuntime() {},
+ openDockerDownload() {},
+ addRemoteInstance() {},
+ });
+});
+
+await page.locator(".dm-runtime-gate").screenshot({
+ path: "/home/eclypso/a0/a0-launcher/output/playwright/launcher-runtime-setup.png",
+});
+```
diff --git a/docs/guides/local-model-tool-use.md b/docs/guides/local-model-tool-use.md
new file mode 100644
index 000000000..803583b77
--- /dev/null
+++ b/docs/guides/local-model-tool-use.md
@@ -0,0 +1,65 @@
+# Local Model Tool Use
+
+Small local models can struggle with Agent Zero's full default communication shape. The safest first fix is prompt/profile/plugin-only: use a smaller behavior contract while leaving Agent Zero's core parser and execution code unchanged.
+
+Use this guide for Ollama, LM Studio, Qwen, and similar local chat models when the model explains commands instead of calling tools.
+
+## Use The Tiny Local Profile
+
+Choose the **Tiny Local** profile when starting or switching a chat that uses a small local model.
+
+The bundled profile lives at:
+
+```text
+agents/tiny-local/
+```
+
+Tiny Local keeps the normal Agent Zero tool-call shape, but removes visible reasoning fields from the communication prompt. It tells the model to emit one executable JSON object with `tool_name` and `tool_args`.
+
+## Use A Project Prompt Include
+
+If you want to keep your current profile, create a project-local file that matches the Prompt Include plugin pattern (`*.promptinclude.md`):
+
+```text
+local-model-tool-use.promptinclude.md
+```
+
+Put this content in that file:
+
+```markdown
+## Local model tool-use discipline
+
+You are Agent Zero. Act on the user's behalf.
+
+When the user asks you to do something, do it directly. Do not explain how the user could do it themselves.
+
+Your visible assistant message must be exactly one valid JSON object.
+
+Use exactly these top-level fields: `tool_name` and `tool_args`.
+
+Do not include markdown fences, prose before the JSON, prose after the JSON, hidden reasoning, analysis, thoughts, or headlines.
+
+Choose a tool from the tools listed in the system prompt. Do not invent tool names, action names, or generic names such as `read`, `write`, `terminal`, or `multi`.
+
+For a final user-facing answer, use the `response` tool:
+
+`{"tool_name":"response","tool_args":{"text":"Done."}}`
+
+Use `response` only when the work is complete, blocked, or no tool is needed. If the user says "proceed", "continue", "go ahead", or similar after the agent named a next step, call the next appropriate tool instead of replying with a promise or status update.
+
+For work that requires a command, file action, browser action, or any other available tool, call the appropriate tool immediately.
+
+If the framework warns that your prior message was malformed, repeated, or reasoning-only, output a corrected JSON tool request immediately without explaining the warning.
+```
+
+## Keep This Prompt-Only
+
+Do not change `agent.py` for this workflow.
+
+Do not change `helpers/extract_tools.py` for this workflow.
+
+Do not create parser repair code for this workflow.
+
+Do not add duplicate execution suppression, LiteLLM transport changes, memory runtime changes, or text-editor file operation changes for this workflow.
+
+If a specific local model still cannot follow the prompt/profile/plugin-only contract, capture the exact model, prompt, response, and tool warning before considering deeper framework changes.
diff --git a/docs/guides/model-presets.md b/docs/guides/model-presets.md
index 18f3bc0f2..ff23a06fd 100644
--- a/docs/guides/model-presets.md
+++ b/docs/guides/model-presets.md
@@ -83,7 +83,7 @@ You can always rename them later.
| **Model Preset** | Which models power the chat. |
| **Agent Profile** | The agent's role, tone, and prompt behavior. |
| **Project** | Workspace, files, memory, secrets, and project instructions. |
-| **Skill** | A specific procedure added to prompt extras. |
+| **Skill** | A specific procedure added to prompt protocol. |
For example, you can use the same "Researcher" Agent Profile with a cheaper
preset for simple questions and a stronger preset for difficult investigations.
diff --git a/docs/guides/onboarding.md b/docs/guides/onboarding.md
index 012e56c5c..1b9879190 100644
--- a/docs/guides/onboarding.md
+++ b/docs/guides/onboarding.md
@@ -1,62 +1,59 @@
# First-Run Onboarding
Use onboarding the first time you open Agent Zero, or any time the Web UI says
-your models still need setup. The wizard helps you pick a provider, add the
-needed key or connection, choose the main model, choose the utility model, and
-start chatting.
+your models still need setup. The wizard helps you pick Cloud, AI account, or
+Local access, configure a main model, choose a utility model, and start
+chatting.
-This example uses **Agent Zero API** with a fake demo key and
-`claude-opus-4-6`. Replace the demo key with your own key.
+This example uses **OpenRouter** with a masked demo key. Replace the demo key
+with your own key.
-## Choose Cloud Or Local
+## Open Onboarding
-Open the Web UI and click **Start Onboarding** from the welcome banner. On the
-first screen, choose whether Agent Zero should use a hosted provider or a local
-model server.
+Open the Web UI. The welcome screen can show account shortcuts, and the message
+composer is ready immediately.
-
+
-Choose **Cloud** when you want to use Agent Zero API, OpenRouter, Anthropic,
-OpenAI, Google, Venice, or another hosted provider. Choose **Local** when you
-want to connect to Ollama, LM Studio, or another model server running on your
-machine.
+If you send a message before models are configured, Agent Zero creates the chat,
+holds the message, and shows the model gate inside the conversation. Choose
+**Cloud provider**, **AI account**, or **Local model** to open onboarding.
-## Pick Agent Zero API
+
-On the Cloud provider screen, click **Agent Zero API**.
+## Choose A Provider Path
-
+Choose **Cloud** when you want to paste an API key for OpenRouter, OpenAI,
+Anthropic, Google, Venice, or another hosted provider.
-The ChatGPT/Codex account option is also available on this Cloud screen if you
-want to connect by device code instead of pasting a provider key.
+
-## Add The Key And Main Model
+Choose **Account** when you want to sign in with Codex/ChatGPT, GitHub Copilot,
+Google Cloud Gemini, or xAI Grok instead of pasting a provider key.
-Paste your Agent Zero API key in **API key**. The screenshot uses a fake example
-key, and the field is masked.
+
-Click the magnifier in **Main model** to open the model list, then choose
-`claude-opus-4-6`.
+Choose **Local** when you want to connect to Ollama, LM Studio, oMLX, llama.cpp,
+vLLM, or another model server running on your machine.
-
+
-After the model is selected, click **Choose utility model**.
+## Configure The Main Model
-
+For API-key providers, choose the main model and paste the provider key. The
+screenshot uses a masked demo key.
+
+
+
+After the main model is selected, click **Choose utility model**.
## Choose The Utility Model
-The utility model handles supporting work such as summarizing, organizing
-memory, and other background tasks.
+The utility model handles quick internal tasks such as summaries, naming, and
+memory. A small, fast, cheap model usually works best here. The wizard may
+prefill a utility provider and model, but it remains an explicit choice.
-Leave **Use same as Main Model** checked if you want both roles to use
-`claude-opus-4-6`.
-
-
-
-You can also uncheck **Use same as Main Model** and choose a different utility
-provider and model. For example, you might keep a strong main model for chat and
-use a faster or cheaper model for utility tasks.
+
Click **Finish setup** when the utility model looks right.
diff --git a/docs/guides/self-update.md b/docs/guides/self-update.md
index 22e490a09..e52b6a438 100644
--- a/docs/guides/self-update.md
+++ b/docs/guides/self-update.md
@@ -10,6 +10,8 @@ For day-to-day upgrades inside a running instance:
The UI will tell you when a new A0 update is available for download. Backups are automatically managed internally during the update process.
+
+
---
## Technical reference
@@ -71,6 +73,24 @@ Self-update is intentionally limited to changes within the same major line.
If a newer major line exists, the UI points you to the Docker setup guide because those upgrades require downloading a new Docker image. They can include operating system level changes or other breaking changes outside the repository checkout.
+## v1.20 to v2.0
+
+Use the Docker image update path for v1.20 -> v2.0. Self Update can show that a
+newer major release line exists, but it intentionally keeps the version selector
+inside the current major line.
+
+
+
+The important part is moving a backup zip into a fresh v2.0 container:
+
+1. In the old v1.20 Web UI, create a backup from **Settings -> Check for Updates -> Backup & Restore -> Create Backup**.
+2. Pull `agent0ai/agent-zero:latest` in Docker Desktop or Docker CLI. For the v2.0 release, `latest` is the v2.0 image.
+3. Start a new container from that image, or use the **latest** card in **Agent Zero Launcher**.
+4. Restore the downloaded backup zip into the new v2.0 Instance.
+5. Verify the new Instance before deleting the old v1.20 container.
+
+For command examples, see [Updating from v1.20 to v2.0](../setup/installation.md#updating-from-v120-to-v20).
+
## Safety notes
- Gitignored paths are preserved during update
diff --git a/docs/guides/skills.md b/docs/guides/skills.md
index 215d87586..3a959d157 100644
--- a/docs/guides/skills.md
+++ b/docs/guides/skills.md
@@ -29,7 +29,7 @@ Click a skill to add it. Active skills are shown at the top of the selector.
To remove a skill, use the remove button in **Active skills** or uncheck it in
the list.
-Active skills are added to the **Extras** part of the system prompt. That means
+Active skills are added to the **Protocol** part of the prompt. That means
Agent Zero sees them every turn while they are active.
> [!TIP]
@@ -56,7 +56,7 @@ usually easier for the agent to follow.
| Control | What it changes |
| --- | --- |
-| **Skills** | Adds a specific procedure to the current prompt extras. |
+| **Skills** | Adds a specific procedure to the current prompt protocol. |
| **Agent Profiles** | Changes the broader role and behavior of the chat. |
| **Projects** | Adds workspace, files, memory, secrets, and project instructions. |
diff --git a/docs/guides/troubleshooting.md b/docs/guides/troubleshooting.md
index e41a7141a..21f8e979e 100644
--- a/docs/guides/troubleshooting.md
+++ b/docs/guides/troubleshooting.md
@@ -41,6 +41,7 @@ PLAYWRIGHT_BROWSERS_PATH=tmp/playwright playwright install chromium
If **Bring Your Own Browser** mode fails:
- keep A0 CLI connected to the chat;
+- restart or reconnect A0 CLI after enabling remote debugging in a browser;
- run `/browser status` in A0 CLI;
- check that Browser settings still say **Bring Your Own Browser**;
- check **Page content access** if page text or screenshots are blocked.
diff --git a/docs/guides/usage.md b/docs/guides/usage.md
index d5bc931e5..76dda859c 100644
--- a/docs/guides/usage.md
+++ b/docs/guides/usage.md
@@ -74,7 +74,7 @@ Use the selector to add or remove active skills.

-Active skills are added to the **Extras** part of the system prompt, so keep the
+Active skills are added to the **Protocol** part of the prompt, so keep the
list short and intentional. See the [Skills guide](skills.md).
### Agent Profiles
diff --git a/docs/quickstart.md b/docs/quickstart.md
index 59c569fe3..bdc446cf3 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -5,7 +5,13 @@ Agent Zero, add a model or API key, open the Web UI, and give it a concrete job.
## Installation (recommended)
-Run one command; the script handles Docker, image pull, and container setup.
+Choose the path that matches your machine:
+
+- Use [A0 Launcher](guides/launcher.md) if you want a desktop app on a fresh
+ machine. It can set up the local runtime, download Agent Zero, open
+ Instances, or save a remote Instance URL.
+- Use A0 Install if you want the terminal path. The script handles Docker
+ detection, image pull, and container setup.
**macOS / Linux:**
```bash
@@ -23,7 +29,7 @@ Follow the CLI prompts for port and authentication, complete onboarding, then op
> To update later, open **Settings UI -> Update tab -> Open Self Update** (see [How to Update](setup/installation.md#how-to-update-agent-zero)). Backups are automatically managed internally.
> [!NOTE]
-> For manual Docker Desktop setup, volume mapping, and platform-specific detail, see the [Installation Guide](setup/installation.md#manual-installation-advanced).
+> For Launcher downloads, headless installer flags, direct Docker, manual Docker Desktop setup, volume mapping, and platform-specific detail, see the [Installation Guide](setup/installation.md).
## Use Agent Zero on your real local files
@@ -56,18 +62,19 @@ For the full setup flow, host picker screenshots, command palette guidance, Brow
### Open the Web UI and complete onboarding
Open your browser and navigate to `http://localhost: