In the previous chapter, we learned how to run dumb, simple commands using Local Shell Execution. We solved the "Invisible Typist" problem using a Watchdog.
But what if the job is too complex for a single command? What if you need someone to read code, think about a bug, try a fix, run tests, and fix it again if it fails?
You don't need a Typist; you need a Contractor. This is where Background Agent Execution comes in.
Imagine you are working with a basic AI chatbot. You ask it to "Refactor this file."
We need a way to wrap an AI Agent so it can work asynchronously (independently) while you do other things.
Background Agent Execution (LocalAgentTask) treats an AI Agent like an independent contractor.
In our system, we don't just call the LLM directly. We wrap the LLM loop inside a LocalAgentTask.
To start an agent, we use registerAsyncAgent. This creates the task state and immediately puts it in the background.
// From LocalAgentTask.tsx
const taskState = registerAsyncAgent({
agentId: 'agent-123',
description: 'Refactor login.ts',
prompt: 'Analyze login.ts and improve error handling',
selectedAgent: myCodingAgent, // The AI logic
setAppState: appStateSetter
});
What happens here:
LocalAgentTaskState.'running'.How does the system manage this "Contractor" while it works?
The Agent Loop is more complex than a Shell command because it goes back and forth between the AI and the Operating System (Tools).
Let's look at the specific mechanisms that make this work in LocalAgentTask.tsx.
Unlike a shell command (which just has an output log), an Agent Task has rich data about what it is doing. We call this the progress object.
// From LocalAgentTask.tsx
export type AgentProgress = {
toolUseCount: number; // How many actions taken?
tokenCount: number; // How expensive is this?
lastActivity?: ToolActivity; // What is it doing RIGHT NOW?
summary?: string; // A 1-sentence update
};
As the agent runs, it constantly updates its progress. This allows the dashboard to show "Reading file..." instead of just "Running...".
// From LocalAgentTask.tsx
export function updateAgentProgress(
taskId: string,
progress: AgentProgress,
setAppState: SetAppState
): void {
updateTaskState(taskId, setAppState, task => ({
...task,
// Merge new progress with existing state
progress: { ...task.progress, ...progress }
}));
}
When the agent decides it is finished, we must wrap up the project. We calculate the final stats and send a notification.
// From LocalAgentTask.tsx
export function completeAgentTask(
result: AgentToolResult,
setAppState: SetAppState
): void {
// 1. Mark as completed
updateTaskState(result.agentId, setAppState, task => ({
...task,
status: 'completed',
result: result, // Store the final answer
endTime: Date.now()
}));
// 2. Clean up memory output
void evictTaskOutput(result.agentId);
}
Sometimes, you are chatting with the AI in the main window, and you realize: "This is going to take a long time."
We handle this with LocalMainSessionTask. It converts the current active chat into a background task.
// From LocalMainSessionTask.ts
// Marks the task as "backgrounded" so the UI can clear
// and you can start a new topic while this one finishes.
export function backgroundAgentTask(taskId: string, ...): boolean {
setAppState(prev => ({
...prev,
tasks: {
...prev.tasks,
[taskId]: { ...prev.tasks[taskId], isBackgrounded: true }
}
}));
return true;
}
In this chapter, we learned:
toolUseCount) and expenses (tokenCount).lastActivity) so the user knows if the agent is "Reading" or "Writing," unlike a silent shell command.Now we have a "Contractor" working in the background. But what if that contractor needs to hire their own subcontractors?
In the next chapter, we will explore how agents can spawn other agents using In-Process Teammates.
Next Chapter: In-Process Teammates
Generated by Code IQ