teammateContext.ts
1 /** 2 * TeammateContext - Runtime context for in-process teammates 3 * 4 * This module provides AsyncLocalStorage-based context for in-process teammates, 5 * enabling concurrent teammate execution without global state conflicts. 6 * 7 * Relationship with other teammate identity mechanisms: 8 * - Env vars (CLAUDE_CODE_AGENT_ID): Process-based teammates spawned via tmux 9 * - dynamicTeamContext (teammate.ts): Process-based teammates joining at runtime 10 * - TeammateContext (this file): In-process teammates via AsyncLocalStorage 11 * 12 * The helper functions in teammate.ts check AsyncLocalStorage first, then 13 * dynamicTeamContext, then env vars. 14 */ 15 16 import { AsyncLocalStorage } from 'async_hooks' 17 18 /** 19 * Runtime context for in-process teammates. 20 * Stored in AsyncLocalStorage for concurrent access. 21 */ 22 export type TeammateContext = { 23 /** Full agent ID, e.g., "researcher@my-team" */ 24 agentId: string 25 /** Display name, e.g., "researcher" */ 26 agentName: string 27 /** Team name this teammate belongs to */ 28 teamName: string 29 /** UI color assigned to this teammate */ 30 color?: string 31 /** Whether teammate must enter plan mode before implementing */ 32 planModeRequired: boolean 33 /** Leader's session ID (for transcript correlation) */ 34 parentSessionId: string 35 /** Discriminator - always true for in-process teammates */ 36 isInProcess: true 37 /** Abort controller for lifecycle management (linked to parent) */ 38 abortController: AbortController 39 } 40 41 const teammateContextStorage = new AsyncLocalStorage<TeammateContext>() 42 43 /** 44 * Get the current in-process teammate context, if running as one. 45 * Returns undefined if not running within an in-process teammate context. 46 */ 47 export function getTeammateContext(): TeammateContext | undefined { 48 return teammateContextStorage.getStore() 49 } 50 51 /** 52 * Run a function with teammate context set. 53 * Used when spawning an in-process teammate to establish its execution context. 54 * 55 * @param context - The teammate context to set 56 * @param fn - The function to run with the context 57 * @returns The return value of fn 58 */ 59 export function runWithTeammateContext<T>( 60 context: TeammateContext, 61 fn: () => T, 62 ): T { 63 return teammateContextStorage.run(context, fn) 64 } 65 66 /** 67 * Check if current execution is within an in-process teammate. 68 * This is faster than getTeammateContext() !== undefined for simple checks. 69 */ 70 export function isInProcessTeammate(): boolean { 71 return teammateContextStorage.getStore() !== undefined 72 } 73 74 /** 75 * Create a TeammateContext from spawn configuration. 76 * The abortController is passed in by the caller. For in-process teammates, 77 * this is typically an independent controller (not linked to parent) so teammates 78 * continue running when the leader's query is interrupted. 79 * 80 * @param config - Configuration for the teammate context 81 * @returns A complete TeammateContext with isInProcess: true 82 */ 83 export function createTeammateContext(config: { 84 agentId: string 85 agentName: string 86 teamName: string 87 color?: string 88 planModeRequired: boolean 89 parentSessionId: string 90 abortController: AbortController 91 }): TeammateContext { 92 return { 93 ...config, 94 isInProcess: true, 95 } 96 }