In the previous chapter, we learned how to hire "Contractors" (Background Agents) to go away, do a job, and come back with a receipt.
But sometimes, you don't want a contractor. You want a Co-pilot. You want someone sitting right next to you, looking at the same instruments, who asks for permission before pushing the big red button.
In this chapter, we explore In-Process Teammates.
Imagine you ask an AI to "Clean up the database."
If you use a Background Agent (from Chapter 3), it might:
You couldn't stop it because you weren't watching it in real-time. You needed a way to review its plan before it executed the dangerous commands.
In-Process Teammates are specialized tasks that live inside the main application loop (hence "In-Process").
Unlike generic tasks, they have:
Teammates are unique because you can have a back-and-forth conversation with them while they work.
The most powerful feature of a Teammate is Plan Mode. Instead of just acting, the Teammate proposes a set of steps.
login.ts, 2. Delete old_login.ts."awaitingPlanApproval)How does the system handle these interactive, persistent agents?
Unlike a fire-and-forget command, a Teammate has a conversational loop.
Let's look at InProcessTeammateTask/types.ts to see how we define this special identity.
A Teammate isn't just a generic task. It has an identity object. This tells the UI who this agent is (e.g., "The Researcher").
// From types.ts
export type InProcessTeammateTaskState = TaskStateBase & {
type: 'in_process_teammate',
// Who is this?
identity: {
agentName: string, // e.g. "coder"
teamName: string, // e.g. "engineering"
planModeRequired: boolean // Must they ask for permission?
},
// Are we waiting for the user to say "Yes"?
awaitingPlanApproval: boolean
}
Why this matters: The planModeRequired flag fundamentally changes the agent's behavior. If true, the agent cannot execute tools until awaitingPlanApproval is cleared by the user.
Since the teammate is running in a loop, we can't just overwrite its prompt. We need to "inject" messages into its consciousness.
We use injectUserMessageToTeammate in InProcessTeammateTask.tsx.
// From InProcessTeammateTask.tsx
export function injectUserMessageToTeammate(
taskId: string,
message: string,
setAppState: SetAppState
): void {
updateTaskState(taskId, setAppState, task => ({
...task,
// 1. Queue message for the Agent to process
pendingUserMessages: [...task.pendingUserMessages, message],
// 2. Add to UI history immediately so the user sees it
messages: appendCappedMessage(task.messages, createUserMessage({
content: message
}))
}));
}
What happens here:
pendingUserMessages. The agent checks this queue before taking its next step.Teammates might run for days. If we kept every single log message in the main AppState, the browser would crash.
We use a "Capped Message" strategy. We only keep the last 50 messages in the UI state (TEAMMATE_MESSAGES_UI_CAP), even if the agent remembers more internally.
// From types.ts
export const TEAMMATE_MESSAGES_UI_CAP = 50
export function appendCappedMessage<T>(prev: T[], item: T): T[] {
// If we have too many messages...
if (prev.length >= TEAMMATE_MESSAGES_UI_CAP) {
// ...slice off the old ones and add the new one
const next = prev.slice(-(TEAMMATE_MESSAGES_UI_CAP - 1))
next.push(item)
return next
}
return [...prev, item]
}
This ensures that even if you have a 500-turn conversation with your Co-pilot, the UI remains snappy.
Since Teammates have names, we often look them up by agentId rather than the random Task ID.
// From InProcessTeammateTask.tsx
export function findTeammateTaskByAgentId(
agentId: string,
tasks: Record<string, TaskStateBase>
): InProcessTeammateTaskState | undefined {
// Look through all tasks...
for (const task of Object.values(tasks)) {
if (isInProcessTeammateTask(task) && task.identity.agentId === agentId) {
// Return the one that is currently running!
if (task.status === 'running') return task;
}
}
}
In this chapter, we learned:
We now have three very different ways to run code:
As our system grows, we will have dozens of these tasks running at once. How do we keep track of them all without getting overwhelmed?
Next Chapter: Task Visibility & Summarization
Generated by Code IQ