Multi-agent teams
Run several agents in one Habakkuk: route conversations to them, switch agents in a chat, delegate to sub-agents, hand off work on the board, and talk to agents elsewhere over A2A.
On this page
One Habakkuk process serves any number of agents. Each has its own models, permission mode, workspace, toolsets, SOUL.md, memory and skills (see Agents).
agents:
habakkuk:
model: anthropic/claude-opus-5-5
permission: workspace
research:
model: anthropic/claude-sonnet-5-5
fallbacks: [deepseek/deepseek-chat]
permission: read-only
toolsets: [core, memory, browser]
ops:
model: anthropic/claude-sonnet-5-5
approvals: reviewer_then_human
Who answers a conversation#
- Per channel. A channel's
agentis the default for its conversations. - Per conversation or workspace.
routesmaps a conversation id (or a Slack team id) to an agent:routes: {T0123456: ops}. - In the chat.
/agentslists the agents;/agent NAMEswitches this conversation (kept instate.db) and starts a fresh session;/agent defaultgoes back to routing. - Elsewhere.
habakkuk chat -agent A,habakkuk run -agent A,"agent": "ops"in the web chat API, andhabakkuk/<agent>as the model name in the OpenAI-compatible API.
Delegation#
delegate hands a self-contained task to a sub-agent: another configured agent, or a fresh copy of the caller. The task becomes a durable job, so a parent that crashed and repeats the call reattaches to the running or finished child. Several delegate calls in one response run in parallel, sub-turns have their own worker pool, and sub-agents cannot delegate further.
Options:
model(provider/model) is tried first, the agent's chain after it.toolsetsmust be a subset of the parent's.output_schemais a JSON Schema the child's final answer must match; the parent gets just the JSON.worktree: truegives the child its owngit worktreeof the repository holding the parent's workspace, on branchhabakkuk/<job>, and commits its changes there.background: truereturns at once; when the child ends, the parent gets a new turn with the report, so its answer reaches the chat./subagentslists running children and/subagents stop IDcancels one.
Sub-agents run unattended: calls that need approval are denied unless the agent has an approval reviewer. Architecture: delegation has the details.
The board#
For work that should outlive the conversation, an agent creates a card on the task board assigned to another agent. The assignee works on it in its own session, across restarts, and the result comes back to the creator's conversation.
Teams and home chats#
Several agents can work as a team, each in its own chat. Say marketing, sales and support are WhatsApp agents (one whatsappagent channel each, made under Settings > Agents) and an orchestrator answers you on Slack:
agents:
orchestrator:
model: anthropic/claude-opus-5-5 # the default toolsets include delegate and board
marketing:
model: anthropic/claude-sonnet-5-5
home: wa-marketing:creator # its own WhatsApp chat
sales:
model: anthropic/claude-sonnet-5-5
home: wa-sales:creator
report_home: all # also "Working on: ..." when work starts
support:
model: anthropic/claude-sonnet-5-5
home: wa-support:creator
channels:
slack:
type: slack
app_token_env: SLACK_APP_TOKEN
bot_tokens_env: [SLACK_BOT_TOKEN]
agent: orchestrator
wa-marketing:
type: whatsappagent
token_env: WA_MARKETING_KEY
agent: marketing
wa-sales:
type: whatsappagent
token_env: WA_SALES_KEY
agent: sales
wa-support:
type: whatsappagent
token_env: WA_SUPPORT_KEY
agent: support
Ask the orchestrator on Slack "what's up with the campaign?" and it delegates to marketing. Marketing's answer goes back to the orchestrator, which sums it up on Slack, and a short version lands in your Marketing chat on WhatsApp, so each agent's chat keeps the history of its own work.
home is the agent's home chat, written like a habakkuk send target: CHANNEL:CONVERSATION or CHANNEL:CONVERSATION:THREAD, with a configured channel. On a whatsappagent channel creator stands for you; send each agent one message first so it knows your id. report_home decides what is posted there on its own:
results(the default oncehomeis set): when the agent finishes work that did not start in its home chat, it posts "Done: the task", its final reply cut to about 1,200 characters, and who got the full result.all: also "Working on: the task" when such work starts.off: nothing automatic.
Work that did not start in the home chat is a task another agent delegated, a board card, a scheduled run, an A2A task, or a message in another channel. A turn from the home chat (any thread of it, when the home names no thread) is answered there and never reported to itself. Nor are turns typed in a local client (terminal, editor, API), whose answer you already see, compactions, [SILENT] replies, or a task an agent delegated to a copy of itself: its parent turn reports instead, so a chain of sub-agents posts once. Reports are ordinary queued deliveries with ids derived from the turn, so a retried turn never posts twice, and a report never starts a turn, so agents cannot loop. A run that fails is not reported; whoever asked gets the error.
An agent with a home also gets the message_home tool, to post progress, questions or fuller results to its home chat in the middle of delegated or scheduled work. It needs no approval, like a reply, and refuses in the home chat itself. Once a turn has read outside content (web pages, search results, network tools, an unvetted message), message_home sends only a short status: at most 280 characters, with no links and no code. That keeps text planted in a page from reaching you looking like your agent's own words; the details go in the final reply instead.
Agents on other servers#
Habakkuk speaks the Agent2Agent protocol both ways: it can serve its agents to other A2A clients, and agents with the a2a toolset can discover and message agents elsewhere. See Integrations.