(paneId: string, backendType: PaneBackendType | undefined, teamName: string, teammateId: string, teammateName: string, setAppState: (f: (prev: AppState) => AppState) => void)
| 545 | return !prev; |
| 546 | } |
| 547 | async function killTeammate(paneId: string, backendType: PaneBackendType | undefined, teamName: string, teammateId: string, teammateName: string, setAppState: (f: (prev: AppState) => AppState) => void): Promise<void> { |
| 548 | // Kill the pane using the backend that created it (handles -s / -L flags correctly). |
| 549 | // Wrapped in try/catch so cleanup (removeMemberFromTeam, unassignTeammateTasks, |
| 550 | // setAppState) always runs — matches useInboxPoller.ts error isolation. |
| 551 | if (backendType) { |
| 552 | try { |
| 553 | // Use ensureBackendsRegistered (not detectAndGetBackend) — this process may |
| 554 | // be a teammate that never ran detection, but we only need class imports |
| 555 | // here, not subprocess probes that could throw in a different environment. |
| 556 | await ensureBackendsRegistered(); |
| 557 | await getBackendByType(backendType).killPane(paneId, !isInsideTmuxSync()); |
| 558 | } catch (error) { |
| 559 | logForDebugging(`[TeamsDialog] Failed to kill pane ${paneId}: ${error}`); |
| 560 | } |
| 561 | } else { |
| 562 | // backendType undefined: old team files predating this field, or in-process. |
| 563 | // Old tmux-file case is a migration gap — the pane is orphaned. In-process |
| 564 | // teammates have no pane to kill, so this is correct for them. |
| 565 | logForDebugging(`[TeamsDialog] Skipping pane kill for ${paneId}: no backendType recorded`); |
| 566 | } |
| 567 | // Remove from team config file |
| 568 | removeMemberFromTeam(teamName, paneId); |
| 569 | |
| 570 | // Unassign tasks and build notification message |
| 571 | const { |
| 572 | notificationMessage |
| 573 | } = await unassignTeammateTasks(teamName, teammateId, teammateName, 'terminated'); |
| 574 | |
| 575 | // Update AppState to keep status line in sync and notify the lead |
| 576 | setAppState(prev => { |
| 577 | if (!prev.teamContext?.teammates) return prev; |
| 578 | if (!(teammateId in prev.teamContext.teammates)) return prev; |
| 579 | const { |
| 580 | [teammateId]: _, |
| 581 | ...remainingTeammates |
| 582 | } = prev.teamContext.teammates; |
| 583 | return { |
| 584 | ...prev, |
| 585 | teamContext: { |
| 586 | ...prev.teamContext, |
| 587 | teammates: remainingTeammates |
| 588 | }, |
| 589 | inbox: { |
| 590 | messages: [...prev.inbox.messages, { |
| 591 | id: randomUUID(), |
| 592 | from: 'system', |
| 593 | text: jsonStringify({ |
| 594 | type: 'teammate_terminated', |
| 595 | message: notificationMessage |
| 596 | }), |
| 597 | timestamp: new Date().toISOString(), |
| 598 | status: 'pending' as const |
| 599 | }] |
| 600 | } |
| 601 | }; |
| 602 | }); |
| 603 | logForDebugging(`[TeamsDialog] Removed ${teammateId} from teamContext`); |
| 604 | } |
no test coverage detected