In the previous chapter, Task & Team Coordination, we learned how to organize agents into teams and track their work using a shared Task Board.
But a team that only moves cards on a board without ever speaking is inefficient. Agents need to ask for passwords, report "Mission Accomplished" to the user, or ask a teammate to review code.
This chapter introduces Communication Channels: the specific tools agents use to speak to the User and to Each Other.
Imagine a "Backend Agent" is writing code and realizes the database credentials are missing.
Without a communication channel:
With Communication Channels:
Agents, like humans, get blocked.
We divide communication into two distinct categories:
The runtime provides three specific tools for these scenarios.
Brief)
When an agent wants to give a status update, it uses the Brief tool. This isn't for debug logs; it's for "polite conversation" with the user.
// Input to Brief tool
{
"message": "I have finished analyzing the logs. No errors found.",
"status": "normal" // or "proactive" if interrupting the user
}
Result: The user sees a nicely formatted message in their console.
AskUserQuestion)Sometimes the agent cannot proceed without human input. It uses this tool to render a blocking menu.
// Input to AskUserQuestion tool
{
"questions": [
{
"question": "Which environment should I deploy to?",
"options": [
{ "label": "Staging", "description": "Internal test server" },
{ "label": "Production", "description": "Live user traffic" }
]
}
]
}
Result: The agent pauses execution completely until the user selects an option.
SendMessage)This is the internal chat system (like Slack for AI). Agents can Direct Message (DM) specific peers or Broadcast to everyone.
// Input to SendMessage tool
{
"to": "qa-tester-bot",
"message": "I updated api.ts. Please verify the build."
}
How does a message get from Agent A to Agent B? It doesn't use complex networking. It uses the file system (Mailboxes), similar to how tasks work.
Let's look at the code that powers these three tools.
BriefTool.tsThis tool is simple. It takes a message and passes it through to the user interface.
// Simplified from BriefTool.ts
export const BriefTool = buildTool({
name: "Brief",
// This function runs when the AI calls the tool
async call({ message, attachments }, context) {
// 1. Log analytics so we know the agent is talking
logEvent('brief_send', { attachment_count: attachments?.length });
// 2. Return the data. The UI layer (outside this file)
// observes this return value and renders it to the console.
return {
data: {
message,
sentAt: new Date().toISOString()
}
};
}
});
Explanation: The Brief tool doesn't "print" to the screen directly. It returns a structured object. The Recursive Agent Runtime (covered in Chapter 1) catches this result and decides how to show it to the user.
AskUserQuestionTool.tsx
This tool is unique because it uses React components (tsx) to render an interactive menu in the terminal.
// Simplified from AskUserQuestionTool.tsx
export const AskUserQuestionTool = buildTool({
name: "AskUserQuestion",
// This flag tells the runtime: "Don't let the agent continue yet!"
requiresUserInteraction() {
return true;
},
// This renders the actual UI the user sees (The Menu)
renderToolResultMessage({ answers }) {
return <AskUserQuestionResult answers={answers} />;
},
async call({ questions }, context) {
// The runtime pauses here until the user clicks an option.
// The 'answers' are injected into the context once the user replies.
return {
data: { questions, answers: context.answers }
};
}
});
Explanation: The requiresUserInteraction() creates a "Break" in the agent's thought loop. The agent literally stops thinking. Once the user clicks "Staging", the runtime wakes the agent up with the result.
SendMessageTool.tsThis tool manages the "Inbox" system. It allows agents to coordinate without a central brain controlling every move.
// Simplified from SendMessageTool.ts
async function handleMessage(recipientName, content, context) {
const senderName = getAgentName(); // Who am I?
// We write to a specific file that the recipient watches
await writeToMailbox(
recipientName,
{
from: senderName,
text: content,
timestamp: new Date().toISOString(),
}
);
return {
data: {
success: true,
message: `Message sent to ${recipientName}'s inbox`
}
}
}
Explanation:
getAgentName).writeToMailbox is a helper that appends a JSON object to a file.* (wildcard), the tool loops through all teammates and writes to all their mailboxes.In this chapter, we gave our agents a voice.
Now that agents can talk and work together, how do they decide what to do next on a larger scale? They need a plan.
Next Chapter: Planning Workflow
Generated by Code IQ