import { CROSS_TEAM_GET_OUTBOX, CROSS_TEAM_LIST_TARGETS, CROSS_TEAM_SEND, // eslint-disable-next-line boundaries/element-types -- IPC channel constants are shared between main and preload by design } from '@preload/constants/ipcChannels'; import { createLogger } from '@shared/utils/logger'; import { isAgentActionMode } from '../services/team/actionModeInstructions'; import { validateTaskId, validateTeamName } from './guards'; import type { CrossTeamService } from '../services/team/CrossTeamService'; import type { IpcResult, TaskRef } from '@shared/types'; import type { IpcMain, IpcMainInvokeEvent } from 'electron'; const logger = createLogger('IPC:crossTeam'); let crossTeamService: CrossTeamService | null = null; export function initializeCrossTeamHandlers(service: CrossTeamService): void { crossTeamService = service; } function validateTaskRefs( value: unknown ): { valid: true; value: TaskRef[] | undefined } | { valid: false; error: string } { if (value === undefined) { return { valid: true, value: undefined }; } if (!Array.isArray(value)) { return { valid: false, error: 'taskRefs must be an array' }; } const taskRefs: TaskRef[] = []; for (const entry of value) { if (!entry || typeof entry !== 'object') { return { valid: false, error: 'taskRefs entries must be objects' }; } const row = entry as Partial; const taskId = typeof row.taskId === 'string' ? row.taskId.trim() : ''; const displayId = typeof row.displayId === 'string' ? row.displayId.trim() : ''; const teamName = typeof row.teamName === 'string' ? row.teamName.trim() : ''; if (!taskId || !displayId || !teamName) { return { valid: false, error: 'Each taskRef must include taskId, displayId, and teamName' }; } const vTaskId = validateTaskId(taskId); if (!vTaskId.valid) { return { valid: false, error: vTaskId.error ?? 'Invalid taskRef taskId' }; } const vTeamName = validateTeamName(teamName); if (!vTeamName.valid) { return { valid: false, error: vTeamName.error ?? 'Invalid taskRef teamName' }; } taskRefs.push({ taskId: vTaskId.value!, displayId, teamName: vTeamName.value! }); } return { valid: true, value: taskRefs }; } function getService(): CrossTeamService { if (!crossTeamService) { throw new Error('CrossTeamService not initialized'); } return crossTeamService; } async function wrapCrossTeamHandler( operation: string, handler: () => Promise ): Promise> { try { const data = await handler(); return { success: true, data }; } catch (error) { const message = error instanceof Error ? error.message : String(error); logger.error(`[crossTeam:${operation}] ${message}`); return { success: false, error: message }; } } async function handleSend( _event: IpcMainInvokeEvent, request: unknown ): Promise> { return wrapCrossTeamHandler('send', () => { if (!request || typeof request !== 'object') { throw new Error('Invalid request'); } const req = request as Record; if (req.actionMode !== undefined && !isAgentActionMode(req.actionMode)) { throw new Error('actionMode must be one of: do, ask, delegate'); } const taskRefs = validateTaskRefs(req.taskRefs); if (!taskRefs.valid) { throw new Error(taskRefs.error); } return getService().send({ fromTeam: String(req.fromTeam ?? ''), fromMember: String(req.fromMember ?? ''), toTeam: String(req.toTeam ?? ''), conversationId: typeof req.conversationId === 'string' ? req.conversationId : undefined, replyToConversationId: typeof req.replyToConversationId === 'string' ? req.replyToConversationId : undefined, text: String(req.text ?? ''), taskRefs: taskRefs.value, actionMode: isAgentActionMode(req.actionMode) ? req.actionMode : undefined, summary: typeof req.summary === 'string' ? req.summary : undefined, chainDepth: typeof req.chainDepth === 'number' ? req.chainDepth : undefined, }); }); } async function handleListTargets( _event: IpcMainInvokeEvent, excludeTeam?: string ): Promise> { return wrapCrossTeamHandler('listTargets', () => getService().listAvailableTargets(typeof excludeTeam === 'string' ? excludeTeam : undefined) ); } async function handleGetOutbox( _event: IpcMainInvokeEvent, teamName: string ): Promise> { return wrapCrossTeamHandler('getOutbox', () => { if (typeof teamName !== 'string' || !teamName.trim()) { throw new Error('teamName is required'); } return getService().getOutbox(teamName); }); } export function registerCrossTeamHandlers(ipcMain: IpcMain): void { ipcMain.handle(CROSS_TEAM_SEND, handleSend); ipcMain.handle(CROSS_TEAM_LIST_TARGETS, handleListTargets); ipcMain.handle(CROSS_TEAM_GET_OUTBOX, handleGetOutbox); } export function removeCrossTeamHandlers(ipcMain: IpcMain): void { ipcMain.removeHandler(CROSS_TEAM_SEND); ipcMain.removeHandler(CROSS_TEAM_LIST_TARGETS); ipcMain.removeHandler(CROSS_TEAM_GET_OUTBOX); }