/** * Discord-based agent wakeup: create a private channel and send a wakeup message. * * If Dirigent is detected (via globalThis.__dirigent), creates a work-type channel. * Otherwise, creates a plain private Discord channel. */ const DISCORD_API = 'https://discord.com/api/v10'; interface WakeupConfig { botToken: string; guildId: string; agentDiscordId?: string; agentId: string; message: string; logger: { info: (...args: any[]) => void; warn: (...args: any[]) => void; error: (...args: any[]) => void; }; } interface DirigentApi { createWorkChannel?: (params: { guildId: string; name: string; agentDiscordId: string; }) => Promise; } /** * Get bot user ID from token (decode JWT-like Discord token). */ function getBotUserIdFromToken(token: string): string | null { try { const base64 = token.split('.')[0]; const decoded = Buffer.from(base64, 'base64').toString('utf8'); return decoded || null; } catch { return null; } } /** * Create a private Discord channel visible only to the target agent and bot. */ async function createPrivateChannel( token: string, guildId: string, name: string, memberIds: string[], logger: WakeupConfig['logger'] ): Promise { const botId = getBotUserIdFromToken(token); // Permission overwrites: deny @everyone, allow specific members const permissionOverwrites = [ { id: guildId, type: 0, deny: '1024' }, // deny @everyone view ...memberIds.map(id => ({ id, type: 1, allow: '1024' })), // allow members view ...(botId ? [{ id: botId, type: 1, allow: '1024' }] : []), ]; try { const res = await fetch(`${DISCORD_API}/guilds/${guildId}/channels`, { method: 'POST', headers: { 'Authorization': `Bot ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name, type: 0, // text channel permission_overwrites: permissionOverwrites, }), }); if (!res.ok) { logger.warn(`Discord channel creation failed: ${res.status} ${await res.text()}`); return null; } const data = await res.json() as { id: string }; return data.id; } catch (err) { logger.error(`Discord channel creation error: ${String(err)}`); return null; } } /** * Send a message to a Discord channel. */ async function sendMessage( token: string, channelId: string, content: string, logger: WakeupConfig['logger'] ): Promise { try { const res = await fetch(`${DISCORD_API}/channels/${channelId}/messages`, { method: 'POST', headers: { 'Authorization': `Bot ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ content }), }); if (!res.ok) { logger.warn(`Discord message send failed: ${res.status}`); return false; } return true; } catch (err) { logger.error(`Discord message send error: ${String(err)}`); return false; } } /** * Wake an agent via Discord: create a private channel and send the wakeup message. */ export async function wakeAgentViaDiscord(config: WakeupConfig): Promise { const { botToken, guildId, agentDiscordId, agentId, message, logger } = config; if (!botToken || !guildId) { logger.warn('Discord wakeup: botToken or guildId not configured'); return false; } // Check if Dirigent is available for work channel creation const dirigent = (globalThis as Record)['__dirigent'] as DirigentApi | undefined; let channelId: string | null = null; const channelName = `hf-wakeup-${agentId}-${Date.now()}`; if (dirigent?.createWorkChannel && agentDiscordId) { // Use Dirigent to create a work-type channel (with turn management) try { channelId = await dirigent.createWorkChannel({ guildId, name: channelName, agentDiscordId, }); logger.info(`Wakeup channel created via Dirigent: ${channelId}`); } catch (err) { logger.warn(`Dirigent work channel creation failed, falling back to plain channel: ${String(err)}`); } } if (!channelId) { // Fallback: create a plain private Discord channel const memberIds = agentDiscordId ? [agentDiscordId] : []; channelId = await createPrivateChannel(botToken, guildId, channelName, memberIds, logger); if (channelId) { logger.info(`Wakeup channel created (plain): ${channelId}`); } } if (!channelId) { logger.error('Failed to create wakeup channel'); return false; } // Send the wakeup message const sent = await sendMessage(botToken, channelId, message, logger); if (sent) { logger.info(`Wakeup message sent to ${channelId} for agent ${agentId}`); } return sent; }