Tool Design & Rules
Tool design rules
The four mandatory rules for adding or editing tools (build-time contract from reasoning_guide(topic=tool_generate), exposed to external agents as tool_generate_guide; ongoing quality checked by the code-reviewer skill):
- Name is the only semantic carrier — stub-tool first calls only see the name; description and params arrive on the second round
- Description is exactly 3 lines, 60-200 chars — What (core action) / When (trigger vs alternatives, e.g.
use for X; Y for Z) / Precondition (key constraint, omit if none). No filler, no bold, no output-schema dump. - English only — Chinese only appears in user-facing handler return messages
- Optional fields must declare a
default— handlers still defend against nil/missing
Trigger conditions and comparisons with similar tools are required in the When line, not forbidden — a description with only the core action and no trigger signal is treated as incomplete. Parameter descriptions must cover How/When/Example, and a non-trivial type (object/array/enum) under 20 chars is incomplete.
Tool concurrency markers
toolRegister.Def carries six behavioral flags:
| Flag | Effect |
|---|---|
AlwaysAllow |
Exempt from the confirm gate (read-only tools) |
AlwaysLoad |
Schema is present on the first round instead of needing find_tools(mode=search) activation. Fifteen tools carry it: ask_user, calculate, chat_history, edit_file, fetch_page, find_files, find_note, find_tools, read_files, reasoning_guide, run_command, run_skill, search_web, write_report, write_todo |
Concurrent |
Opts into Pass 2 fan-out (one goroutine per call) |
Background |
Result is not awaited by the loop |
SystemUse |
Internal bookkeeping tool — hidden from the ordinary listing, surfaced only when find_tools(mode=list) is asked for system tools |
Timeout |
Per-tool override of the 1-minute default |
Adding Concurrent: true requires both "no side effects" and "upstream allows parallelism". The current concurrent set is listed on the Execution Engine page.
Tool timeout matrix
toolRegister.Dispatch wraps every call — built-in, script, API and MCP — in the tool's registered timeout (1 minute unless overridden). Adapters apply their own limit inside that ceiling:
| Adapter | Adapter default | Configurable | Effective ceiling |
|---|---|---|---|
| Built-in | 1 min | Def.Timeout per tool |
Def.Timeout |
Script (script_*) |
5 min (300 s) | tool.json "timeout": <seconds> in ~/.config/agenvoy/tools/script/<name>/ — also registered as the dispatch timeout |
The tool.json value; without one the 1-minute dispatch default cuts in first |
API (api_*) |
60 s | doc.Endpoint.Timeout in extensions/apis/<name>.json — not registered with dispatch |
1 min dispatch default |
| MCP HTTP | Transport ResponseHeaderTimeout 60 s |
n/a | 1 min dispatch default |
| MCP stdio | none | n/a | 1 min dispatch default |
Long-running tools (script + API) emit running name=... elapsed=Ys/Zs to the daemon log every 30 s for visibility.
Long-running built-ins declare their own caps at registration: run_command = 60 min, subagents = MaxSubagentTimeoutMin (30 min), download_file = 10 min registered, with the request itself limited by its own parameter (default 120 s, max 600 s), generate_audio = 5 min, fetch_page / search_web = 90 s, http_request = its own parameter (default 60 s, max 300 s) inside the 1-minute default, generate_image = 15 min, open_file = 10 s.
Credential auto-heal
store_secret is a SystemUse tool — it is not in the first-round payload and is pulled in through find_tools when it is needed. The former §10 Credential auto-heal section of the system prompt was removed; the system prompt now carries one line pointing at store_secret, and the tool's own description holds the procedure. On an auth failure — missing key, 401, 403, expired token — the agent takes the key name from the error, calls store_secret (which captures the new value through masked input — the value never reaches the LLM), then re-invokes the tool that failed. Capped at two store_secret rounds per tool per turn.