Skip to content

Commit 3c74e7b

Browse files
ziggyclaude
andcommitted
Fix scheduled tasks blocking Telegram message processing
Two changes that fix messages being silently dropped while heartbeat or other scheduled tasks are running: 1. group-queue: preempt idle task containers when user messages arrive instead of queuing messages behind a 30-minute idle timeout 2. agent-runner: scheduled tasks exit immediately after completing instead of entering the IPC wait loop. Frees the group slot so user messages can be processed without delay. Also mocks transcription module in WhatsApp tests so voice note test is deterministic regardless of local .env state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6bca94a commit 3c74e7b

3 files changed

Lines changed: 33 additions & 1 deletion

File tree

container/agent-runner/src/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,15 @@ async function main(): Promise<void> {
571571
break;
572572
}
573573

574+
// Scheduled tasks are one-shot — exit after the first query instead
575+
// of waiting for IPC messages that will never arrive. This frees the
576+
// group slot immediately so user messages aren't blocked.
577+
if (containerInput.isScheduledTask) {
578+
log('Scheduled task complete, exiting');
579+
writeOutput({ status: 'success', result: null, newSessionId: sessionId });
580+
break;
581+
}
582+
574583
// Emit session update so host can track it
575584
writeOutput({ status: 'success', result: null, newSessionId: sessionId });
576585

src/channels/whatsapp.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ vi.mock('child_process', () => ({
4545
exec: vi.fn(),
4646
}));
4747

48+
// Mock transcription — avoid real HTTP calls and env-dependent behavior
49+
vi.mock('../transcription.js', () => ({
50+
transcribeAudioMessage: vi
51+
.fn()
52+
.mockResolvedValue('Hello from a voice note'),
53+
isVoiceMessage: vi.fn(
54+
(msg: any) => msg.message?.audioMessage?.ptt === true,
55+
),
56+
textToSpeech: vi.fn().mockResolvedValue(null),
57+
}));
58+
4859
// Build a fake WASocket that's an EventEmitter with the methods we need
4960
function createFakeSocket() {
5061
const ev = new EventEmitter();
@@ -564,7 +575,7 @@ describe('WhatsAppChannel', () => {
564575
expect.objectContaining({
565576
id: 'msg-8',
566577
sender_name: 'Frank',
567-
content: expect.stringMatching(/^\[Voice: /),
578+
content: '[Voice: Hello from a voice note]',
568579
}),
569580
);
570581
});

src/group-queue.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ export class GroupQueue {
6363
const state = this.getGroup(groupJid);
6464

6565
if (state.active) {
66+
// If an idle task container is blocking messages, kill it so the
67+
// message can be processed immediately instead of waiting for the
68+
// task's idle timeout (which can be 30+ minutes).
69+
if (state.isTaskContainer && state.idleWaiting) {
70+
logger.info(
71+
{ groupJid },
72+
'Preempting idle task container for incoming message',
73+
);
74+
this.closeStdin(groupJid);
75+
// The message will be picked up when drainGroup runs after the
76+
// task container exits (pendingMessages flag below).
77+
}
6678
state.pendingMessages = true;
6779
logger.debug({ groupJid }, 'Container active, message queued');
6880
return;

0 commit comments

Comments
 (0)