# Tool Design & Rules

## Tool design rules

The four mandatory rules for adding or editing tools (build-time contract from `reasoning_guide(topics=["tool_generate"])`, exposed to external agents as `tool_generate_guide`; ongoing quality checked by the `code-reviewer` skill, which is no longer bundled since v1.0.17 and is installed with `/skill` → `system` (`/skills` before v1.0.22)):

1. **Name is the primary semantic carrier** — since v1.2.0 the model sees only tool names until `find_tools` fetches a schema — in the per-turn `Tools` list for every provider except `claude-code`, which receives the names alone as well, so the name is what the model picks from
2. **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.
3. **English only** — Chinese only appears in user-facing handler return messages
4. **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 five behavioral flags:

| Flag | Effect |
|---|---|
| `AlwaysAllow` | Exempt from the confirm gate (read-only tools) |
| `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 default (`DEFAULT_TOOL_TIMEOUT`, 15 min since v1.1.2 — 1 min before); `NoToolTimeout` (used by `ask_user` since v1.0.23) removes the limit |

The `AlwaysLoad` flag was removed; a schema is loaded through `find_tools` (Removed in v1.2.0).

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 and MCP — in the tool's registered timeout (`DEFAULT_TOOL_TIMEOUT` unless overridden — 15 min since v1.1.2, 1 min before). Adapters apply their own limit inside that ceiling:

| Adapter | Adapter default | Configurable | Effective ceiling |
|---|---|---|---|
| Built-in | 15 min | `Def.Timeout` per tool | `Def.Timeout` |
| Script (`script_*`) | 15 min (300 s before v1.1.2) | `tool.json` `"timeout": <seconds>` in `~/.config/agenvoy/tools/script/<name>/` — also registered as the dispatch timeout | The `tool.json` value, else 15 min |
| MCP HTTP | Transport `ResponseHeaderTimeout` 15 min (60 s before v1.1.2) | n/a | 15 min dispatch default |
| MCP stdio | none | n/a | 15 min dispatch default |

Long-running script tools emit `running name=... elapsed=Ys/Zs` to the daemon log every 30 s for visibility. The API tool adapter (`api_*`) and its `Endpoint.Timeout` were Removed in v1.3.2 together with user-defined API tools.

Long-running built-ins declare their own caps at registration: `run_command` = 30 min (`RUN_COMMAND_TIMEOUT`; 60 min before v1.1.2), `subagents` = `MAX_SUBAGENT_TIMEOUT_MIN` (30 min, foreground legs), `download_file` = 10 min registered, with the request itself limited by its own parameter (default 120 s, max 600 s; ignored by a `render=true` page save, added in v1.3.6), `generate_audio` = 5 min, `fetch_page` / `search_web` = 90 s, `http_request` = its own parameter (default 60 s, max 300 s) inside the 15-minute default, `generate_image` = 15 min, `html_template` = 30 s, `open_file` = 10 s, `ask_user` = no limit. `background_command` and `background_subagents` register no cap of their own because a call returns a task id at once; the background command or leg is limited by its `timeout_min` (1-60 min, default 60) instead. Outside the TUI both are excluded. `run_command_background` was renamed `background_command` (Removed in v1.3.2); `run_websocket` and its `timeout` (1-1440 min) were Removed in v1.3.2, and the `subagents` `background` / `timeout_min` parameters were Removed in v1.3.2 in favor of `background_subagents`.

## Credential auto-heal

`store_secret` is a `SystemUse` tool, hidden from the ordinary `find_tools(mode=list)` listing; since v1.2.0 its schema is fetched through `find_tools` like every other tool outside the fixed set (`find_tools`, `run_tool`, `reasoning_guide`, `write_todo`, `chat_history`, `ask_user`, `write_result`). The former `§10 Credential auto-heal` section of the system prompt was removed; since v1.2.0 the system prompt keeps one short line — credentials live in the OS keychain (service `agenvoy`) and `store_secret` describes the lookup. The tool's own description holds the lookup order (keychain, account = key name, then `~/.config/agenvoy/.secrets` on Linux, then the environment variable of that name), notes that an empty `printenv` proves nothing because nothing is exported into the process environment, and 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.
