* Get teammate mailbox attachments for agent swarm communication * Teammates are independent Claude Code sessions running in parallel (swarms), * not parent-child subagent relationships. * * This function checks two sources for messages: * 1. File-based mailbox (for messages that arrived betwee
( toolUseContext: ToolUseContext, )
| 3530 | * allowing teammates to receive messages without waiting for the turn to end. |
| 3531 | */ |
| 3532 | async function getTeammateMailboxAttachments( |
| 3533 | toolUseContext: ToolUseContext, |
| 3534 | ): Promise<Attachment[]> { |
| 3535 | if (!isAgentSwarmsEnabled()) { |
| 3536 | return [] |
| 3537 | } |
| 3538 | if (process.env.USER_TYPE !== 'ant') { |
| 3539 | return [] |
| 3540 | } |
| 3541 | |
| 3542 | // Get AppState early to check for team lead status |
| 3543 | const appState = toolUseContext.getAppState() |
| 3544 | |
| 3545 | // Use agent name from helper (checks AsyncLocalStorage, then dynamicTeamContext) |
| 3546 | const envAgentName = getAgentName() |
| 3547 | |
| 3548 | // Get team name (checks AsyncLocalStorage, dynamicTeamContext, then AppState) |
| 3549 | const teamName = getTeamName(appState.teamContext) |
| 3550 | |
| 3551 | // Check if we're the team lead (uses shared logic from swarm utils) |
| 3552 | const teamLeadStatus = isTeamLead(appState.teamContext) |
| 3553 | |
| 3554 | // Check if viewing a teammate's transcript (for in-process teammates) |
| 3555 | const viewedTeammate = getViewedTeammateTask(appState) |
| 3556 | |
| 3557 | // Resolve agent name based on who we're VIEWING: |
| 3558 | // - If viewing a teammate, use THEIR name (to read from their mailbox) |
| 3559 | // - Otherwise use env var if set, or leader's name if we're the team lead |
| 3560 | let agentName = viewedTeammate?.identity.agentName ?? envAgentName |
| 3561 | if (!agentName && teamLeadStatus && appState.teamContext) { |
| 3562 | const leadAgentId = appState.teamContext.leadAgentId |
| 3563 | // Look up the lead's name from agents map (not the UUID) |
| 3564 | agentName = appState.teamContext.teammates[leadAgentId]?.name || 'team-lead' |
| 3565 | } |
| 3566 | |
| 3567 | logForDebugging( |
| 3568 | `[SwarmMailbox] getTeammateMailboxAttachments called: envAgentName=${envAgentName}, isTeamLead=${teamLeadStatus}, resolved agentName=${agentName}, teamName=${teamName}`, |
| 3569 | ) |
| 3570 | |
| 3571 | // Only check inbox if running as an agent in a swarm or team lead |
| 3572 | if (!agentName) { |
| 3573 | logForDebugging( |
| 3574 | `[SwarmMailbox] Not checking inbox - not in a swarm or team lead`, |
| 3575 | ) |
| 3576 | return [] |
| 3577 | } |
| 3578 | |
| 3579 | logForDebugging( |
| 3580 | `[SwarmMailbox] Checking inbox for agent="${agentName}" team="${teamName || 'default'}"`, |
| 3581 | ) |
| 3582 | |
| 3583 | // Check mailbox for unread messages (routes to in-process or file-based) |
| 3584 | // Filter out structured protocol messages (permission requests/responses, shutdown |
| 3585 | // messages, etc.) — these must be left unread for useInboxPoller to route to their |
| 3586 | // proper handlers (workerPermissions queue, sandbox queue, etc.). Without filtering, |
| 3587 | // attachment generation races with InboxPoller: whichever reads first marks all |
| 3588 | // messages as read, and if attachments wins, protocol messages get bundled as raw |
| 3589 | // LLM context text instead of being routed to their UI handlers. |
no test coverage detected