๐ File detail
tasks/InProcessTeammateTask/types.ts
๐ฏ Use case
This module is a โInProcessTeammateTaskโ task implementation โ concrete work units the task runner schedules and monitors. On the API surface it exposes TeammateIdentity, InProcessTeammateTaskState, isInProcessTeammateTask, TEAMMATE_MESSAGES_UI_CAP, and appendCappedMessage โ mainly types, interfaces, or factory objects. It composes internal code from Task, tools, utils, and LocalAgentTask (relative imports).
Generated from folder role, exports, dependency roots, and inline comments โ not hand-reviewed for every path.
๐ง Inline summary
import type { TaskStateBase } from '../../Task.js' import type { AgentToolResult } from '../../tools/AgentTool/agentToolUtils.js' import type { AgentDefinition } from '../../tools/AgentTool/loadAgentsDir.js' import type { Message } from '../../types/message.js' import type { PermissionMode } from '../../utils/permissions/PermissionMode.js'
๐ค Exports (heuristic)
TeammateIdentityInProcessTeammateTaskStateisInProcessTeammateTaskTEAMMATE_MESSAGES_UI_CAPappendCappedMessage
๐ฅ๏ธ Source preview
import type { TaskStateBase } from '../../Task.js'
import type { AgentToolResult } from '../../tools/AgentTool/agentToolUtils.js'
import type { AgentDefinition } from '../../tools/AgentTool/loadAgentsDir.js'
import type { Message } from '../../types/message.js'
import type { PermissionMode } from '../../utils/permissions/PermissionMode.js'
import type { AgentProgress } from '../LocalAgentTask/LocalAgentTask.js'
/**
* Teammate identity stored in task state.
* Same shape as TeammateContext (runtime) but stored as plain data.
* TeammateContext is for AsyncLocalStorage; this is for AppState persistence.
*/
export type TeammateIdentity = {
agentId: string // e.g., "researcher@my-team"
agentName: string // e.g., "researcher"
teamName: string
color?: string
planModeRequired: boolean
parentSessionId: string // Leader's session ID
}
export type InProcessTeammateTaskState = TaskStateBase & {
type: 'in_process_teammate'
// Identity as sub-object (matches TeammateContext shape for consistency)
// Stored as plain data in AppState, NOT a reference to AsyncLocalStorage
identity: TeammateIdentity
// Execution
prompt: string
// Optional model override for this teammate
model?: string
// Optional: Only set if teammate uses a specific agent definition
// Many teammates run as general-purpose agents without a predefined definition
selectedAgent?: AgentDefinition
abortController?: AbortController // Runtime only, not serialized to disk - kills WHOLE teammate
currentWorkAbortController?: AbortController // Runtime only - aborts current turn without killing teammate
unregisterCleanup?: () => void // Runtime only
// Plan mode approval tracking (planModeRequired is in identity)
awaitingPlanApproval: boolean
// Permission mode for this teammate (cycled independently via Shift+Tab when viewing)
permissionMode: PermissionMode
// State
error?: string
result?: AgentToolResult // Reuse existing type since teammates run via runAgent()
progress?: AgentProgress
// Conversation history for zoomed view (NOT mailbox messages)
// Mailbox messages are stored separately in teamContext.inProcessMailboxes
messages?: Message[]
// Tool use IDs currently being executed (for animation in transcript view)
inProgressToolUseIDs?: Set<string>
// Queue of user messages to deliver when viewing teammate transcript
pendingUserMessages: string[]
// UI: random spinner verbs (stable across re-renders, shared between components)
spinnerVerb?: string
pastTenseVerb?: string
// Lifecycle
isIdle: boolean
shutdownRequested: boolean
// Callbacks to notify when teammate becomes idle (runtime only)
// Used by leader to efficiently wait without polling
onIdleCallbacks?: Array<() => void>
// Progress tracking (for computing deltas in notifications)
lastReportedToolCount: number
lastReportedTokenCount: number
}
export function isInProcessTeammateTask(
task: unknown,
): task is InProcessTeammateTaskState {
return (
typeof task === 'object' &&
task !== null &&
'type' in task &&
task.type === 'in_process_teammate'
)
}
/**
* Cap on the number of messages kept in task.messages (the AppState UI mirror).
*
* task.messages exists purely for the zoomed transcript dialog, which only
* needs recent context. The full conversation lives in the local allMessages
* array (inProcessRunner) and on disk at the agent transcript path.
*
* BQ analysis (round 9, 2026-03-20) showed ~20MB RSS per agent at 500+ turn
* sessions and ~125MB per concurrent agent in swarm bursts. Whale session
* 9a990de8 launched 292 agents in 2 minutes and reached 36.8GB. The dominant
* cost is this array holding a second full copy of every message.
*/
export const TEAMMATE_MESSAGES_UI_CAP = 50
/**
* Append an item to a message array, capping the result at
* TEAMMATE_MESSAGES_UI_CAP entries by dropping the oldest. Always returns
* a new array (AppState immutability).
*/
export function appendCappedMessage<T>(
prev: readonly T[] | undefined,
item: T,
): T[] {
if (prev === undefined || prev.length === 0) {
return [item]
}
if (prev.length >= TEAMMATE_MESSAGES_UI_CAP) {
const next = prev.slice(-(TEAMMATE_MESSAGES_UI_CAP - 1))
next.push(item)
return next
}
return [...prev, item]
}