Multi-Agent Orchestration

Minibot supports multi-agent orchestration with specialist AI agents, tool scoping, and delegation. Agent definitions live in ./agents/*.md as markdown files with YAML frontmatter followed by a system prompt body. The main agent discovers and delegates to specialists at runtime.

Delegation Tools

  • fetch_agent_info — inspect a specialist’s name, description, defaults and system prompt. Always enabled when specialist definitions exist.

  • delegate — hand work to a specialist by exact agent_name when the main agent needs the result to keep working. The result comes back as a new turn. Requires [tasks].enabled (on by default; the SQLite backend needs no broker). See Async Tasks.

  • spawn_task — the same hand-off, but the worker’s answer is the final deliverable and goes straight to the user. Registered only with [tasks] delegation_mode = "auto" (the default).

Delegation is asynchronous: both tools return a task_id and the main agent does not wait inside the turn. With spawn_task the worker’s answer, including any files it produced, is sent straight to the conversation as a later message (fire and forget). A short notice with the agent, the task id and the outcome is also recorded in the conversation history as a user message, so the main agent knows the task finished and can call get_task for its result on the user’s next message. The worker’s output itself is never written to the history. Use delegate when the main agent needs the result to keep working, such as a second step that depends on it or a comparison: the result then comes back as a new turn, saved to the conversation history, and the main agent answers from it instead of the raw worker text. Failures and timeouts come back the same way, so the agent can explain or retry. Worker output reaches the model marked as untrusted data. One turn can start at most three delegations, and a chain of delegated tasks is limited to three levels; past either limit delegate is rejected with task:continuation_limit and the model answers the user or uses spawn_task. [tasks] delegation_mode = "delegate" registers only delegate (no spawn_task), so every task continues the turn; past the limits it falls back to fire and forget instead of failing, and its result reports "continue_turn": false. The default, "auto", leaves the choice to the model through the tool it calls. The removed continue_turn_mode = "always" is now delegation_mode = "delegate", and continue_turn_default is gone. A rate-limit retry notice still goes straight to the user, and a delegated task that is cancelled does not report back. Use get_task to retrieve a result, list_tasks to see what is running, and cancel_task to stop one.

Two switches decide whether any of this exists. [orchestration.specialists].enabled (default true) is the one for specialists themselves: with it off, the roster, fetch_agent_info and named delegation all disappear, and delegate and spawn_task still run a generic worker. [tasks].enabled (default true) is the execution backend, so the default follows it: with tasks off and the key omitted the roster is disabled. An explicit [orchestration.specialists].enabled = true with [tasks].enabled = false is a config error rather than a roster nothing can act on. Turning [tasks] off turns multi-agent orchestration off as well: the specialist roster is dropped from the system prompt along with the tool that could act on it.

Runtime Agent Management

Runtime management is separate and off by default. [orchestration.agent_management] reload = true exposes reload_agents so an owner can hand-edit agents/*.md and re-read them without a restart; write = true additionally exposes controlled create_agent, update_agent and delete_agent tools for model-authored definitions. Neither switch changes how existing owner-authored agents run.

The four modes, all reachable by config alone:

Mode

Config

What exists

No specialists

[orchestration.specialists] enabled = false

No roster, no fetch_agent_info, no named delegation. delegate and spawn_task still run a generic worker while [tasks] is on.

Owner-only (default)

both management switches false

The roster and delegation, from files you write. No management tools or skill at all.

Reload only

reload = true

Adds reload_agents: edit a file yourself, then re-read it without a restart.

Managed agents

write = true

Adds create_agent/update_agent/delete_agent, the bundled create-agent skill, and a managed definition directory the model may write to.

A managed definition is never more powerful than the ceiling in [orchestration.agent_management]. It may not use tools_deny, must grant exact tool names, claims MCP servers through mcp_servers, and may only target a provider the owner listed. Owner files are not subject to the ceiling, so the two trust levels stay distinct. The loader records which directory each definition was read from; a symlink in the managed directory remains managed, including when the worker applies model overrides. Creating or updating a managed definition whose name collides with an enabled owner-authored agent is rejected before writing. See Configuration Reference for the field reference.

Runtime definition reads and managed writes run off the event loop. Reload prepares and validates candidate definitions in a worker thread, then applies the roster and tool changes on the event-loop thread. Cancelling a managed write waits for its filesystem operation to finish before releasing the management lock; cancellation does not roll back a completed write.

That ceiling bounds what an agent may be told to do; it is not a sandbox. A managed agent granted bash or the managed-file tools can reach anything the daemon’s OS user can, exactly as an owner-authored one can. See Security & Sandboxing.

Main-agent visibility is a separate thing from the managed ceiling. [orchestration.main_agent] and tool_ownership_mode decide what the main agent sees; they never widen or narrow what a specialist may be granted, and a specialist may deliberately own a tool the main agent is denied.

Agent Definitions

Minimal example:

---
name: workspace_manager_agent
description: Handles workspace file operations
mode: agent
model_provider: openai_responses
model: gpt-5.6-luna
temperature: 0.1
tools_allow:
  - list_files
  - file_info
  - write_file
  - move_file
  - delete_file
  - send_file
  - glob_files
  - read_file
  - self_insert_artifact
---

You manage files in the workspace safely and precisely.

Frontmatter fields: name, description, mode (always "agent"), enabled (default true), model_provider, model, temperature, max_new_tokens, omit_temperature (send no temperature at all, for models that reject the parameter), reasoning_effort, max_tool_iterations, timeout_seconds (per-agent wall-clock budget; it is the default when a spawn_task or delegate call names no timeout_seconds of its own, capped by [tasks].worker_timeout_seconds), tools_allow, tools_deny, mcp_servers.

Tool Scoping

tools_allow and tools_deny are mutually exclusive. Wildcards (fnmatch) are supported:

  • tools_allow: ["mcp_dice_cli__*"]

  • tools_deny: ["mcp_dice_cli__roll_dice"]

Behavior rules:

  • If neither is set, local (non-MCP) tools are not exposed to the agent.

  • mcp_servers limits MCP tools to the listed server names; tools from other servers are excluded.

  • In tools_allow mode: allowed local tools + allowed MCP-server tools are exposed.

  • In tools_deny mode: all local tools except denied + allowed MCP-server tools are exposed.

  • A local-only agent: use tools_deny: ["mcp*"] with no mcp_servers.

Main-agent tool policy is set under [orchestration.main_agent]:

[orchestration.main_agent]
tools_allow = ["memory_*", "schedule_prompt", "http_request"]

tool_ownership_mode under [orchestration] controls sharing:

  • shared (default) — all agents share tools.

  • exclusive — specialist-owned tools are removed from the main agent.

  • exclusive_mcp — only specialist-owned MCP tools are removed from the main agent.

shared_mcp_servers lists MCP servers that stay visible to the main agent in both exclusive modes, even when a specialist also claims them — for example a read-only web search server that both the main agent and a research specialist use. The [orchestration.main_agent] tools_allow and tools_deny policy still applies first, so tools_deny = ["mcp*"] hides a shared server too:

[orchestration]
tool_ownership_mode = "exclusive_mcp"
shared_mcp_servers = ["exa"]

Assigning an MCP server to an agent:

---
name: dice_agent
description: Dice-rolling specialist
mode: agent
model_provider: openai_responses
model: gpt-5.6-luna
mcp_servers:
  - dice_cli
---

Use the dice tools to roll and report results.

Browser Automation

Browser automation is not MCP-based: the specialist drives the playwright-cli binary directly through the bash tool, guided by a skill rather than a remote MCP server. See agents/browser_agent.md for the canonical setup — its tools_allow lists bash, list_files, read_file, grep, http_request, pre_response, and wait (no mcp_servers entry).

Agent Skills

Skills are reusable instruction packs the model loads on demand, and they are shared with the main agent rather than being an orchestration feature. The browser_agent above is driven by a skill rather than an MCP server.

With tools_allow, a specialist gets skill tools only when listed: activate_skill, plus list_skills if it should discover skills on its own. A tools_deny specialist gets both (and wait, when enabled) unless it denies them. Unlike the main agent, a specialist receives no skill catalog in its prompt, so without list_skills its prompt must name the skill to activate. install_skill is never available to a specialist.

See Agent Skills for the format, discovery order, and configuration.

OpenRouter Custom Params per Agent

For agents running on OpenRouter, override provider-routing params in frontmatter using openrouter_provider_<field_name> keys:

---
name: browser_agent
description: Browser automation specialist
mode: agent
model_provider: openrouter
model: x-ai/grok-4.3
openrouter_provider_only:
  - openai
  - anthropic
openrouter_provider_sort: price
openrouter_provider_allow_fallbacks: true
---

Use browser tools to navigate, inspect, and extract results.

Supported keys mirror [llm.openrouter.provider] fields (only, sort, order, allow_fallbacks, max_price, etc.). Agent-level values override global provider config for matching fields. Keep credentials in [providers.openrouter] — never in agent files.

Choosing a Model

Run minibot configure and it queries your configured provider for the models it currently serves, then offers them as a list — which beats any set of names written down here, since those go stale as providers retire and rename models. The model IDs in the examples above are illustrative only; use whatever your provider lists.

reasoning_effort is unset by default, which leaves the provider’s own default in place. Set it per agent when you want to steer that: higher for agents that plan multi-step work, lower for agents that mostly call one tool and summarize.

Retargeting One Call

The frontmatter above is the agent’s default, not a fixed binding. spawn_task and delegate accept model_provider, model and reasoning_effort for a single task, so the main agent can honour a request like “run that on the deepseek subagent, high effort” without a config change or a restart. The specialist’s prompt, tool scope and timeouts are unchanged; only the target model is.

fetch_agent_info is the discovery surface: besides the specialist’s prompt it returns that agent’s own defaults and the providers that actually have credentials configured, with their api_format, base_url and advisory models list (see Named providers). A provider that is not on that list is rejected before the task is queued, because a provider without a key would otherwise answer with MiniBot’s local echo fallback.

A retargeted task keeps its budget: the context window and output cap are re-derived from the target model’s own limits rather than inherited from the agent’s frontmatter, so mid-run compaction stays armed. A model that models.dev does not list has no known window and runs without compaction.