feat: Discord-based agent wakeup replacing spawn

New wakeup flow:
1. Create private Discord channel for the agent
2. Send wakeup message with slot context + workflow reference
3. If Dirigent detected (globalThis.__dirigent), create work-type channel
4. Fallback to api.spawn if Discord not configured

New config fields: discordBotToken, discordGuildId
New file: plugin/calendar/discord-wakeup.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
operator
2026-04-18 20:28:59 +00:00
parent 7e4750fcc4
commit be30b4b3f4
4 changed files with 209 additions and 29 deletions

View File

@@ -171,53 +171,52 @@ export default {
}
/**
* Wake/spawn agent with task context for slot execution.
* Wake agent via Discord channel creation + message.
* This is the callback invoked by CalendarScheduler when a slot is ready.
*
* Priority:
* 1. Discord wakeup (create private channel + send message)
* 2. OpenClaw spawn API (fallback if Discord not configured)
*/
async function wakeAgent(context: AgentWakeContext): Promise<boolean> {
logger.info(`Waking agent for slot: ${context.taskDescription}`);
const live = resolveConfig();
const agentId = process.env.AGENT_ID || 'unknown';
try {
// Method 1: Use OpenClaw spawn API if available (preferred)
// Method 1: Discord wakeup (preferred)
const discordBotToken = (live as any).discordBotToken as string | undefined;
const discordGuildId = (live as any).discordGuildId as string | undefined;
if (discordBotToken && discordGuildId) {
const { wakeAgentViaDiscord } = await import('./calendar/discord-wakeup.js');
const success = await wakeAgentViaDiscord({
botToken: discordBotToken,
guildId: discordGuildId,
agentId,
message: context.prompt,
logger,
});
if (success) return true;
logger.warn('Discord wakeup failed, trying spawn fallback');
}
// Method 2: OpenClaw spawn API (fallback)
if (api.spawn) {
const result = await api.spawn({
task: context.prompt,
timeoutSeconds: context.slot.estimated_duration * 60, // Convert to seconds
timeoutSeconds: context.slot.estimated_duration * 60,
});
if (result?.sessionId) {
logger.info(`Agent spawned for calendar slot: session=${result.sessionId}`);
// Track session completion
trackSessionCompletion(result.sessionId, context);
return true;
}
}
// Method 2: Send notification/alert to wake agent (fallback)
// This relies on the agent's heartbeat to check for notifications
logger.warn('OpenClaw spawn API not available, using notification fallback');
// Send calendar wakeup notification via backend
const live = resolveConfig();
const agentId = process.env.AGENT_ID || 'unknown';
const notifyResponse = await fetch(`${live.backendUrl}/calendar/agent/notify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Agent-ID': agentId,
'X-Claw-Identifier': live.identifier || hostname(),
},
body: JSON.stringify({
agent_id: agentId,
message: context.prompt,
slot_id: context.slot.id || context.slot.virtual_id,
task_description: context.taskDescription,
}),
});
return notifyResponse.ok;
logger.warn('No wakeup method available (configure discordBotToken + discordGuildId)');
return false;
} catch (err) {
logger.error('Failed to wake agent:', err);