From 3c6a73072c09c5ed377878089a68c3abc529b8cf Mon Sep 17 00:00:00 2001 From: hoangqnguyen Date: Mon, 13 Jul 2026 07:20:43 +0700 Subject: [PATCH] fix(telegram): queue messages received while Claude is busy instead of dropping them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a Telegram message arrived while a main-queue run was in flight, handleMessage() replied "Claude is busy — try again in a moment" and returned early, silently discarding the message. Users had to notice the reply and manually resend. The runner already serializes work per-thread: runUserMessage() routes through enqueue(), keyed by sessionKey, so messages from the same chat run in order. The busy branch was short-circuiting before ever reaching that queue. Remove the early return so a busy-time message falls through into the existing per-thread queue and runs after the current task finishes. The user still gets an immediate heads-up that their message is queued (and a pointer to /fork and /kill), and the typing indicator already stays alive across the queued wait, so the chat no longer looks frozen. No new concurrency: per-thread serialization is unchanged; this only stops dropping the enqueued work. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- src/commands/telegram.ts | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index f5430793..a38a6a41 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "name": "claudeclaw", "source": "./", "description": "Cron-like daemon that runs Claude prompts on a schedule", - "version": "1.0.40", + "version": "1.0.41", "keywords": [ "cron", "heartbeat", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 2688e3ab..e2be5931 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,5 +1,5 @@ { "name": "claudeclaw", - "version": "1.0.40", + "version": "1.0.41", "description": "Cron-like daemon that runs Claude prompts on a schedule" } diff --git a/src/commands/telegram.ts b/src/commands/telegram.ts index fd7584cd..c38bb2bf 100644 --- a/src/commands/telegram.ts +++ b/src/commands/telegram.ts @@ -1449,9 +1449,13 @@ async function handleMessage(message: TelegramMessage): Promise { let streamMsgId: number | null = null; let hadToolLines = false; if (busy) { - await sendMessage(config.token, chatId, "Claude is busy — try again in a moment, or use /fork for a quick parallel task.", threadId); - return; - } else { + // Don't drop the message — queue it behind the in-flight run. runUserMessage + // routes through enqueue(), which serializes per-thread (keyed by sessionKey), + // so messages from this chat run in order instead of being silently lost. + // Let the user know it's waiting rather than pretending nothing happened. + await sendMessage(config.token, chatId, "⏳ Claude is busy — your message is queued and will run after the current one finishes. Use /fork for a quick parallel task, or /kill to cancel what's running.", threadId); + } + { const stream = makeStreamCallback(config.token, chatId, threadId, { verbose }); result = await runUserMessage("telegram", prefixedPrompt, sessionKey, undefined, stream.onChunk, stream.onToolEvent, modelOverride); const streamResult = await stream.waitForStreamMsg();