source dump of claude code
at main 129 lines 4.3 kB view raw
1/** 2 * Teammate Initialization Module 3 * 4 * Handles initialization for Claude Code instances running as teammates in a swarm. 5 * Registers a Stop hook to notify the team leader when the teammate becomes idle. 6 */ 7 8import type { AppState } from '../../state/AppState.js' 9import { logForDebugging } from '../debug.js' 10import { addFunctionHook } from '../hooks/sessionHooks.js' 11import { applyPermissionUpdate } from '../permissions/PermissionUpdate.js' 12import { jsonStringify } from '../slowOperations.js' 13import { getTeammateColor } from '../teammate.js' 14import { 15 createIdleNotification, 16 getLastPeerDmSummary, 17 writeToMailbox, 18} from '../teammateMailbox.js' 19import { readTeamFile, setMemberActive } from './teamHelpers.js' 20 21/** 22 * Initializes hooks for a teammate running in a swarm. 23 * Should be called early in session startup after AppState is available. 24 * 25 * Registers a Stop hook that sends an idle notification to the team leader 26 * when this teammate's session stops. 27 */ 28export function initializeTeammateHooks( 29 setAppState: (updater: (prev: AppState) => AppState) => void, 30 sessionId: string, 31 teamInfo: { teamName: string; agentId: string; agentName: string }, 32): void { 33 const { teamName, agentId, agentName } = teamInfo 34 35 // Read team file to get leader ID 36 const teamFile = readTeamFile(teamName) 37 if (!teamFile) { 38 logForDebugging(`[TeammateInit] Team file not found for team: ${teamName}`) 39 return 40 } 41 42 const leadAgentId = teamFile.leadAgentId 43 44 // Apply team-wide allowed paths if any exist 45 if (teamFile.teamAllowedPaths && teamFile.teamAllowedPaths.length > 0) { 46 logForDebugging( 47 `[TeammateInit] Found ${teamFile.teamAllowedPaths.length} team-wide allowed path(s)`, 48 ) 49 50 for (const allowedPath of teamFile.teamAllowedPaths) { 51 // For absolute paths (starting with /), prepend one / to create //path/** pattern 52 // For relative paths, just use path/** 53 const ruleContent = allowedPath.path.startsWith('/') 54 ? `/${allowedPath.path}/**` 55 : `${allowedPath.path}/**` 56 57 logForDebugging( 58 `[TeammateInit] Applying team permission: ${allowedPath.toolName} allowed in ${allowedPath.path} (rule: ${ruleContent})`, 59 ) 60 61 setAppState(prev => ({ 62 ...prev, 63 toolPermissionContext: applyPermissionUpdate( 64 prev.toolPermissionContext, 65 { 66 type: 'addRules', 67 rules: [ 68 { 69 toolName: allowedPath.toolName, 70 ruleContent, 71 }, 72 ], 73 behavior: 'allow', 74 destination: 'session', 75 }, 76 ), 77 })) 78 } 79 } 80 81 // Find the leader's name from the members array 82 const leadMember = teamFile.members.find(m => m.agentId === leadAgentId) 83 const leadAgentName = leadMember?.name || 'team-lead' 84 85 // Don't register hook if this agent is the leader 86 if (agentId === leadAgentId) { 87 logForDebugging( 88 '[TeammateInit] This agent is the team leader - skipping idle notification hook', 89 ) 90 return 91 } 92 93 logForDebugging( 94 `[TeammateInit] Registering Stop hook for teammate ${agentName} to notify leader ${leadAgentName}`, 95 ) 96 97 // Register Stop hook to notify leader when this teammate stops 98 addFunctionHook( 99 setAppState, 100 sessionId, 101 'Stop', 102 '', // No matcher - applies to all Stop events 103 async (messages, _signal) => { 104 // Mark this teammate as idle in the team config (fire and forget) 105 void setMemberActive(teamName, agentName, false) 106 107 // Send idle notification to the team leader using agent name (not UUID) 108 // Must await to ensure the write completes before process shutdown 109 const notification = createIdleNotification(agentName, { 110 idleReason: 'available', 111 summary: getLastPeerDmSummary(messages), 112 }) 113 await writeToMailbox(leadAgentName, { 114 from: agentName, 115 text: jsonStringify(notification), 116 timestamp: new Date().toISOString(), 117 color: getTeammateColor(), 118 }) 119 logForDebugging( 120 `[TeammateInit] Sent idle notification to leader ${leadAgentName}`, 121 ) 122 return true // Don't block the Stop 123 }, 124 'Failed to send idle notification to team leader', 125 { 126 timeout: 10000, 127 }, 128 ) 129}