In Chapter 5: Event Stream (The Nervous System), we connected our agent's brain to the user interface, allowing for real-time interaction. Before that, we built the Agent Runtime (The Engine) and gave it Tooling & MCP Integrations (The Hands).
We have all the pieces to run a powerful AI assistant. But there is one final hurdle: Configuration.
Writing the JSON configuration for a complex Workflow—defining agents, linking triggers, and configuring tool schemas—is difficult and error-prone. One missing comma can break the whole project.
Wouldn't it be easier if we could just ask someone to write the configuration for us?
Enter the Copilot.
The Copilot is a Meta-Agent. This means it is an AI Agent whose job is to build other AI Agents.
Think of the Copilot as a Senior Architect.
Instead of you manually editing the "Blueprint" (The Workflow from Chapter 1), you chat with the Copilot, and it edits the file for you.
Imagine you want to add a new agent to your project that checks emails for leads.
Without Copilot:
You have to find the documentation for the Gmail tool, copy the schema, open workflow.json, create a new agent object, paste the tool, and ensure the IDs match.
With Copilot: You simply type:
"Create a new agent named 'Sales Rep'. Give it access to Gmail to read emails."
The Copilot will:
The Copilot works differently than a standard chat agent. It needs to be aware of the structure of your project.
Here is how the Copilot processes a request:
The most important part of the Copilot is context. If it doesn't know what your project currently looks like, it might overwrite your existing work.
We inject the current state of the project directly into the prompt before sending it to the AI.
Located in src/application/lib/copilot/copilot.ts, this function prepares the "brief" for the Architect.
// src/application/lib/copilot/copilot.ts
function getCurrentWorkflowPrompt(workflow: Workflow): string {
// We convert the entire JSON workflow into a string
// This allows the AI to "read" the current blueprint
return `Context:\n\nThe current workflow config is:
\`\`\`json
${JSON.stringify(workflow)}
\`\`\`
`;
}
Explanation: When you send a message, we secretly stick your entire project configuration at the top. This ensures the Copilot knows that "Agent A" already exists and "Agent B" is what needs to be added.
The Copilot also needs to know the rules of the system (e.g., "Agents must have unique IDs"). We load this into a SYSTEM_PROMPT.
// src/application/lib/copilot/copilot.ts
const SYSTEM_PROMPT = [
COPILOT_INSTRUCTIONS_MULTI_AGENT, // The rulebook
CURRENT_WORKFLOW_PROMPT, // The current state
].join('\n\n');
Ideally, the Copilot shouldn't guess tool definitions. If it guesses the inputs for gmail_send_email, it might get them wrong.
We give the Copilot its own set of tools (Meta-Tools) to look up information.
The Copilot uses a tool called search_relevant_tools to find real integrations (like Composio).
// src/application/lib/copilot/copilot.ts
tools: {
"search_relevant_tools": tool({
description: "Use this to find tools like Gmail, Slack, etc...",
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => {
// 1. Search the external library (Composio)
const result = await searchRelevantTools(usageTracker, query);
// 2. Return the accurate JSON schema for the tool
return result;
},
}),
// ... other tools like search_triggers
},
Explanation:
search_relevant_tools("Gmail").Just like in Chapter 5, we stream the Copilot's response so the user can see what's happening.
// src/application/lib/copilot/copilot.ts
export async function* streamMultiAgentResponse(...) {
// 1. Prepare the AI call with context and tools
const { fullStream } = streamText({
model: openai('gpt-4'),
messages: updatedMessages, // Includes workflow context
tools: copilotTools, // Includes tool search
system: SYSTEM_PROMPT,
});
// 2. Yield events as they happen
for await (const event of fullStream) {
if (event.type === "text-delta") {
yield { content: event.textDelta }; // Talking to user
}
else if (event.type === "tool-call") {
yield { type: 'tool-call', ... }; // Searching for tools
}
}
}
Explanation: This function is the heartbeat of the Copilot. It orchestrates the conversation, giving the AI the ability to pause, search for tools, and then continue writing the configuration.
On the frontend, this looks like a chat box, but it is actually a Command Center.
When the Copilot finishes generating the new configuration, the UI updates the "Draft Workflow" (remember Chapter 1?).
The user sees the changes appear visually in the graph or list view, verifies them, and then hits "Publish" to move them to the Live Workflow.
We have reached the end of our journey!
In this chapter, we built Copilot, the intelligent layer that sits on top of everything else. It uses:
You have now walked through the entire architecture of rowboat:
You are now ready to build powerful, autonomous agents. Happy building!
Generated by Code IQ