Transcript
[00:00] In this video, we are going to teach our local coding agent to deploy sub-agents. To do that, I’m going to need to take some of this stuff out of our index. I’m going to create a new file here called config and drop all that in. And we will export all these. And we’ll jump back over here and we will import from our config. Let’s see, olama, model, and work directory. And actually, I’m going to rename olama to API. I just like that better. And we could potentially later decide we want to switch this over to open router or some other open AI-like interface. Okay, so we’ve got all that set up. We’re going to jump into our tools. We’re going to bring all those guys in. I’m going to close this sidebar. So here we no longer need this guy.
[01:06] What we’re going to do is we’re going to create a new tool. I’m going to come down here after all the tools. I’m going to create a new executable here called run sub-agent. It is going to take in just the prompt, which would be a string. It’s going to return a promise of string. Okay. And this is going to look a lot like, as a matter of fact, I can probably just go copy it. It’s going to look a lot like our regular agents. Let me copy this out of here just to keep us moving. So we’ve got our text from generate text, which we’re going to need to import. So for messages, we’re just going to hard code one. It’s going to be role user. And the content will be the prompt that was passed in. Now here, we’re going to say you’re a coding sub-agent. And complete your given task. Then summarize your findings. Yeah, that way we’re more likely to get something back. We’re going to bring in is loop finished there. So model name. Is it not actually model name? I think it’s just model. Yeah. Okay. So we’ve got our new executable. And now what we’re going to do is we’re going to export parent tools, which will be tools plus a new one. We’ll call it task. That’ll be a tool. Let’s go to our description. Spawn a sub-agent with fresh context. It shares the file system, but not the conversation history. We’ll have an input schema, just like we have with the rest of them. It’ll be our Zod schema. It’ll be a Z object with a task, which is a Z string. And on our execute, we’ll have an async function. It takes in the task, this returns, wait, run, sub-agent, task. Name this up.
[03:31] Now you may be wondering, like, why would we do this? The reason we’re doing this is if I ask it to do something, if I ask our AI to do something, go look at a bunch of files and summarize them, the context window is going to blow up with reading all of those files, whereas I just wanted the summary. So what the AI can do is pass it off to a sub-agent, have it go do all the reading and the summarizing, and then just pass that summarization back up to the parent agent. It becomes part of our history, but we didn’t blow it up with a whole bunch of stuff that we don’t actually care about. So just to prove that this is working, what I’m going to do is I’m going to take away all the other tools from the parent agent, and I’m only going to give it the task, so it only has that one tool. It basically has no choice but to use that, so that’ll be fun. So parent, we’ll come down here, run our index, see if I didn’t screw anything up, and actually, before we do that, I’m going to come back over here. Let me just stop this for a second because we want to actually see that it’s occurring, so on this async, we’re going to break that out. We’re going to return the same thing, but here we’re going to console.log, sub-agent, and the task, just so that we see that firing off. Okay, so let’s give this a try. What I’m going to say is read all the TS files, exclude node modules, and summarize what each one does. Okay, so we’re expecting to see it fire off its first sub-agent. Cool, so it fired off a sub-agent, told it more or less exactly what I told it. Okay, so here is a summary. We’ve got the config, the index, the tools, everything’s here. So it did it, but it’s important to point out that our parent agent, again, didn’t have access to bash or read or write or edit or anything like that, so it had to utilize the sub-agent, but at this point, we can turn the tools back on, and everything’s back to normal. It can use any of the tools it feels are important, and we can also tell it specifically, delegate this, or use a task for this specific task. So now our agent can fire up other agents.
Unlock scalable AI automation by teaching your local coding agent to deploy sub-agents, solving context window blowup and enabling true delegation. Build multi-agent systems on Apple Silicon Macs that handle complex software development tasks without performance bottlenecks.
First we’ll move some stuff around to make it more modular
import { createOpenAI } from "@ai-sdk/openai";import { generateText, isLoopFinished } from "ai";import type { ModelMessage } from "ai";import * as readline from "readline";import { TOOLS } from './tools'import { api, MODEL, WORKDIR } from './config'
/** CONSTANTS */const WORKDIR = process.cwd();const MODEL = "qwen3.5:35b-a3b-coding-nvfp4";
/** API */const ollama = createOpenAI({ baseURL: "http://localhost:11434/v1", apiKey: "ollama",});
...
/** AGENT LOOP */
const agentLoop = async (messages: ModelMessage[]): Promise<string> => { const { text } = await generateText({ model: ollama.chat(MODEL), model: api.chat(MODEL), system: `You are a coding agent at ${WORKDIR}. Use tools to solve tasks. Act, dont explain.`, messages, tools: TOOLS, stopWhen: isLoopFinished(), }); return text;};
prompt();And we’ll create a new module called config.ts
import { createOpenAI, generateText } from "@ai-sdk/openai";
/** CONSTANTS */export const WORKDIR = process.cwd();export const MODEL = "qwen3.5:35b-a3b-coding-nvfp4";
/** API */export const api = createOpenAI({ // renamed from ollama -> api baseURL: "http://localhost:11434/v1", apiKey: "ollama",});Now we’ll create our new tool in tools.ts
import { tool, zodSchema } from "ai";import { tool, zodSchema, generateText, isLoopFinished } from "ai";import {z} from "zod":import { spawnSync } from "node: child_process";import { api, MODEL, WORKDIR } from './config'
/** CONSTANTS */const WORKDIR = process.cwd();const BLOCKED_COMMANDS = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"];
const safePath = (p: string) => { const resolved = path.resolve(WORKDIR, p); if (!resolved.startsWith(WORKDIR)) { throw new Error(`Path is not in ${WORKDIR}`); } return resolved;};
/** TOOLS */const runBash = (command: string): string => {/* ... */};
const runRead = (filePath: string, limit?: number) => {/* ... */};
const runWrite = (filePath: string, content: string) => {/* ... */};
const runEdit = (filePath: string, oldText: string, newText: string) => {/* ... */};
export const TOOLS = { /* ... */};
const runSubagent = async (prompt: string): Promise<string> => { const { text } = await generateText({ model: api.chat(MODEL), system: `You are a coding subagent at ${WORKDIR}. Complete your given task, then summarize your findings.`, messages: [ { role: "user", content: prompt, }, ], tools: TOOLS, stopWhen: isLoopFinished(), }); return text;};
export const PARENT_TOOLS = { ...TOOLS, task: tool({ description: "Spawn a subagent with fresh context, it shares the file system, but not the conversation history", inputSchema: zodSchema( z.object({ task: z.string(), }), ), execute: async ({ task }) => await runSubagent(task), }),};Finally we’ll add our new PARENT_TOOLS to the index.ts
import { createOpenAI } from "@ai-sdk/openai";import { generateText, isLoopFinished } from "ai";import type { ModelMessage } from "ai";import * as readline from "readline";import { TOOLS } from './tools'import { PARENT_TOOLS } from './tools'import { api, MODEL, WORKDIR } from './config'
...
/** AGENT LOOP */
const agentLoop = async (messages: ModelMessage[]): Promise<string> => { const { text } = await generateText({ model: api.chat(MODEL), system: `You are a coding agent at ${WORKDIR}. Use tools to solve tasks. Act, dont explain.`, messages, tools: TOOLS, tools: PARENT_TOOLS, stopWhen: isLoopFinished(), }); return text;};