types.ts
1 import type { AgentColorName } from '../../../tools/AgentTool/agentColorManager.js' 2 3 /** 4 * Types of backends available for teammate execution. 5 * - 'tmux': Uses tmux for pane management (works in tmux or standalone) 6 * - 'iterm2': Uses iTerm2 native split panes via the it2 CLI 7 * - 'in-process': Runs teammate in the same Node.js process with isolated context 8 */ 9 export type BackendType = 'tmux' | 'iterm2' | 'in-process' 10 11 /** 12 * Subset of BackendType for pane-based backends only. 13 * Used in messages and types that specifically deal with terminal panes. 14 */ 15 export type PaneBackendType = 'tmux' | 'iterm2' 16 17 /** 18 * Opaque identifier for a pane managed by a backend. 19 * For tmux, this is the tmux pane ID (e.g., "%1"). 20 * For iTerm2, this is the session ID returned by it2. 21 */ 22 export type PaneId = string 23 24 /** 25 * Result of creating a new teammate pane. 26 */ 27 export type CreatePaneResult = { 28 /** The pane ID for the newly created pane */ 29 paneId: PaneId 30 /** Whether this is the first teammate pane (affects layout strategy) */ 31 isFirstTeammate: boolean 32 } 33 34 /** 35 * Interface for pane management backends. 36 * Abstracts operations for creating and managing terminal panes 37 * for teammate visualization in swarm mode. 38 */ 39 export type PaneBackend = { 40 /** The type identifier for this backend */ 41 readonly type: BackendType 42 43 /** Human-readable display name for this backend */ 44 readonly displayName: string 45 46 /** Whether this backend supports hiding and showing panes */ 47 readonly supportsHideShow: boolean 48 49 /** 50 * Checks if this backend is available on the system. 51 * For tmux: checks if tmux command exists. 52 * For iTerm2: checks if it2 CLI is installed and configured. 53 */ 54 isAvailable(): Promise<boolean> 55 56 /** 57 * Checks if we're currently running inside this backend's environment. 58 * For tmux: checks if we're in a tmux session. 59 * For iTerm2: checks if we're running in iTerm2. 60 */ 61 isRunningInside(): Promise<boolean> 62 63 /** 64 * Creates a new pane for a teammate in the swarm view. 65 * The backend handles layout strategy (with/without leader pane). 66 * 67 * @param name - The teammate's name for display 68 * @param color - The color to use for the pane border/title 69 * @returns The pane ID and whether this was the first teammate 70 */ 71 createTeammatePaneInSwarmView( 72 name: string, 73 color: AgentColorName, 74 ): Promise<CreatePaneResult> 75 76 /** 77 * Sends a command to execute in a specific pane. 78 * 79 * @param paneId - The pane to send the command to 80 * @param command - The command string to execute 81 * @param useExternalSession - If true, uses external session socket (tmux-specific) 82 */ 83 sendCommandToPane( 84 paneId: PaneId, 85 command: string, 86 useExternalSession?: boolean, 87 ): Promise<void> 88 89 /** 90 * Sets the border color for a pane. 91 * 92 * @param paneId - The pane to style 93 * @param color - The color to apply to the border 94 * @param useExternalSession - If true, uses external session socket (tmux-specific) 95 */ 96 setPaneBorderColor( 97 paneId: PaneId, 98 color: AgentColorName, 99 useExternalSession?: boolean, 100 ): Promise<void> 101 102 /** 103 * Sets the title for a pane (displayed in pane border/header). 104 * 105 * @param paneId - The pane to title 106 * @param name - The title to display 107 * @param color - The color for the title text 108 * @param useExternalSession - If true, uses external session socket (tmux-specific) 109 */ 110 setPaneTitle( 111 paneId: PaneId, 112 name: string, 113 color: AgentColorName, 114 useExternalSession?: boolean, 115 ): Promise<void> 116 117 /** 118 * Enables pane border status display (shows titles in borders). 119 * 120 * @param windowTarget - The window to enable status for (optional) 121 * @param useExternalSession - If true, uses external session socket (tmux-specific) 122 */ 123 enablePaneBorderStatus( 124 windowTarget?: string, 125 useExternalSession?: boolean, 126 ): Promise<void> 127 128 /** 129 * Rebalances panes to achieve the desired layout. 130 * 131 * @param windowTarget - The window containing the panes 132 * @param hasLeader - Whether there's a leader pane (affects layout strategy) 133 */ 134 rebalancePanes(windowTarget: string, hasLeader: boolean): Promise<void> 135 136 /** 137 * Kills/closes a specific pane. 138 * 139 * @param paneId - The pane to kill 140 * @param useExternalSession - If true, uses external session socket (tmux-specific) 141 * @returns true if the pane was killed successfully, false otherwise 142 */ 143 killPane(paneId: PaneId, useExternalSession?: boolean): Promise<boolean> 144 145 /** 146 * Hides a pane by breaking it out into a hidden window. 147 * The pane remains running but is not visible in the main layout. 148 * 149 * @param paneId - The pane to hide 150 * @param useExternalSession - If true, uses external session socket (tmux-specific) 151 * @returns true if the pane was hidden successfully, false otherwise 152 */ 153 hidePane(paneId: PaneId, useExternalSession?: boolean): Promise<boolean> 154 155 /** 156 * Shows a previously hidden pane by joining it back into the main window. 157 * 158 * @param paneId - The pane to show 159 * @param targetWindowOrPane - The window or pane to join into 160 * @param useExternalSession - If true, uses external session socket (tmux-specific) 161 * @returns true if the pane was shown successfully, false otherwise 162 */ 163 showPane( 164 paneId: PaneId, 165 targetWindowOrPane: string, 166 useExternalSession?: boolean, 167 ): Promise<boolean> 168 } 169 170 /** 171 * Result from backend detection. 172 */ 173 export type BackendDetectionResult = { 174 /** The backend that should be used */ 175 backend: PaneBackend 176 /** Whether we're running inside the backend's native environment */ 177 isNative: boolean 178 /** If iTerm2 is detected but it2 not installed, this will be true */ 179 needsIt2Setup?: boolean 180 } 181 182 // ============================================================================= 183 // In-Process Teammate Types 184 // ============================================================================= 185 186 /** 187 * Identity fields for a teammate. 188 * This is a subset shared with TeammateContext (Task #4) to avoid circular deps. 189 * lifecycle-specialist defines the full TeammateContext with additional fields. 190 */ 191 export type TeammateIdentity = { 192 /** Agent name (e.g., "researcher", "tester") */ 193 name: string 194 /** Team name this teammate belongs to */ 195 teamName: string 196 /** Assigned color for UI differentiation */ 197 color?: AgentColorName 198 /** Whether plan mode approval is required before implementation */ 199 planModeRequired?: boolean 200 } 201 202 /** 203 * Configuration for spawning a teammate (any execution mode). 204 */ 205 export type TeammateSpawnConfig = TeammateIdentity & { 206 /** Initial prompt to send to the teammate */ 207 prompt: string 208 /** Working directory for the teammate */ 209 cwd: string 210 /** Model to use for this teammate */ 211 model?: string 212 /** System prompt for this teammate (resolved from workflow config) */ 213 systemPrompt?: string 214 /** How to apply the system prompt: 'replace' or 'append' to default */ 215 systemPromptMode?: 'default' | 'replace' | 'append' 216 /** Optional git worktree path */ 217 worktreePath?: string 218 /** Parent session ID (for context linking) */ 219 parentSessionId: string 220 /** Tool permissions to grant this teammate */ 221 permissions?: string[] 222 /** Whether this teammate can show permission prompts for unlisted tools. 223 * When false (default), unlisted tools are auto-denied. */ 224 allowPermissionPrompts?: boolean 225 } 226 227 /** 228 * Result from spawning a teammate. 229 */ 230 export type TeammateSpawnResult = { 231 /** Whether spawn was successful */ 232 success: boolean 233 /** Unique agent ID (format: agentName@teamName) */ 234 agentId: string 235 /** Error message if spawn failed */ 236 error?: string 237 238 /** 239 * Abort controller for lifecycle management (in-process only). 240 * Leader uses this to cancel/kill the teammate. 241 * For pane-based teammates, use kill() method instead. 242 */ 243 abortController?: AbortController 244 245 /** 246 * Task ID in AppState.tasks (in-process only). 247 * Used for UI rendering and progress tracking. 248 * agentId is the logical identifier; taskId is for AppState indexing. 249 */ 250 taskId?: string 251 252 /** Pane ID (pane-based only) */ 253 paneId?: PaneId 254 } 255 256 /** 257 * Message to send to a teammate. 258 */ 259 export type TeammateMessage = { 260 /** Message content */ 261 text: string 262 /** Sender agent ID */ 263 from: string 264 /** Sender display color */ 265 color?: string 266 /** Message timestamp (ISO string) */ 267 timestamp?: string 268 /** 5-10 word summary shown as preview in the UI */ 269 summary?: string 270 } 271 272 /** 273 * Common interface for teammate execution backends. 274 * Abstracts the differences between pane-based (tmux/iTerm2) and in-process execution. 275 * 276 * PaneBackend handles low-level pane operations; TeammateExecutor handles 277 * high-level teammate lifecycle operations that work across all backends. 278 */ 279 export type TeammateExecutor = { 280 /** Backend type identifier */ 281 readonly type: BackendType 282 283 /** Check if this executor is available on the system */ 284 isAvailable(): Promise<boolean> 285 286 /** Spawn a new teammate with the given configuration */ 287 spawn(config: TeammateSpawnConfig): Promise<TeammateSpawnResult> 288 289 /** Send a message to a teammate */ 290 sendMessage(agentId: string, message: TeammateMessage): Promise<void> 291 292 /** Terminate a teammate (graceful shutdown request) */ 293 terminate(agentId: string, reason?: string): Promise<boolean> 294 295 /** Force kill a teammate (immediate termination) */ 296 kill(agentId: string): Promise<boolean> 297 298 /** Check if a teammate is still active */ 299 isActive(agentId: string): Promise<boolean> 300 } 301 302 // ============================================================================= 303 // Type Guards 304 // ============================================================================= 305 306 /** 307 * Type guard to check if a backend type uses terminal panes. 308 */ 309 export function isPaneBackend(type: BackendType): type is 'tmux' | 'iterm2' { 310 return type === 'tmux' || type === 'iterm2' 311 }