v2 phase 5: pending questions with interactive cards

End-to-end ask_user_question flow:
- Agent MCP tool writes question card to messages_out
- Host delivery creates pending_questions row, delivers as Discord Card with buttons
- Local webhook server receives Gateway INTERACTION_CREATE events
- Acknowledges interaction + updates card to show selected answer
- Routes response back to session DB as system message
- MCP tool poll picks up response and returns to agent

Key fixes:
- Poll loop now skips system messages (reserved for MCP tool responses)
- Gateway listener uses webhookUrl forwarding mode for interaction support
- Button custom_id encodes questionId + option text for self-contained routing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-09 03:26:16 +03:00
parent c348fabf22
commit c31bb02c06
7 changed files with 233 additions and 6 deletions

View File

@@ -6,7 +6,7 @@ import Database from 'better-sqlite3';
import fs from 'fs';
import path from 'path';
import { getRunningSessions, getActiveSessions } from './db/sessions.js';
import { getRunningSessions, getActiveSessions, createPendingQuestion } from './db/sessions.js';
import { getAgentGroup } from './db/agent-groups.js';
import { log } from './log.js';
import { openSessionDb, sessionDir } from './session-manager.js';
@@ -157,6 +157,20 @@ async function deliverMessage(
return;
}
// Track pending questions for ask_user_question flow
if (content.type === 'ask_question' && content.questionId) {
createPendingQuestion({
question_id: content.questionId,
session_id: session.id,
message_out_id: msg.id,
platform_id: msg.platform_id,
channel_type: msg.channel_type,
thread_id: msg.thread_id,
created_at: new Date().toISOString(),
});
log.info('Pending question created', { questionId: content.questionId, sessionId: session.id });
}
// Channel delivery
if (!msg.channel_type || !msg.platform_id) {
log.warn('Message missing routing fields', { id: msg.id });
@@ -180,7 +194,12 @@ async function deliverMessage(
}
await deliveryAdapter.deliver(msg.channel_type, msg.platform_id, msg.thread_id, msg.kind, msg.content, files);
log.info('Message delivered', { id: msg.id, channelType: msg.channel_type, platformId: msg.platform_id, fileCount: files?.length });
log.info('Message delivered', {
id: msg.id,
channelType: msg.channel_type,
platformId: msg.platform_id,
fileCount: files?.length,
});
// Clean up outbox directory after successful delivery
if (fs.existsSync(outboxDir)) {