fix: clear WhatsApp typing indicator after response (#243)

WhatsApp's "typing..." indicator lingers for 15-25 seconds after the bot
finishes responding because there was no way to clear it. This adds
stopTypingIndicator() which sends a "paused" presence update to
immediately dismiss it.

- stopTypingIndicator?() added to ChannelAdapter interface (optional)
- WhatsApp adapter implements it via sendPresenceUpdate("paused")
- bot.ts calls it in the finally block after stream processing

Written by Cameron ◯ Letta Code

"First, solve the problem. Then, write the code." - John Johnson
This commit is contained in:
Cameron
2026-02-09 16:25:52 -08:00
committed by GitHub
parent 5f7cdd3471
commit 6f5a322840
4 changed files with 26 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ export interface ChannelAdapter {
sendMessage(msg: OutboundMessage): Promise<{ messageId: string }>; sendMessage(msg: OutboundMessage): Promise<{ messageId: string }>;
editMessage(chatId: string, messageId: string, text: string): Promise<void>; editMessage(chatId: string, messageId: string, text: string): Promise<void>;
sendTypingIndicator(chatId: string): Promise<void>; sendTypingIndicator(chatId: string): Promise<void>;
stopTypingIndicator?(chatId: string): Promise<void>;
// Capabilities (optional) // Capabilities (optional)
supportsEditing?(): boolean; supportsEditing?(): boolean;

View File

@@ -47,6 +47,7 @@ import {
sendWhatsAppMessage, sendWhatsAppMessage,
sendWhatsAppFile, sendWhatsAppFile,
sendTypingIndicator, sendTypingIndicator,
stopTypingIndicator,
sendReadReceipt, sendReadReceipt,
type LidMapper, type LidMapper,
} from "./outbound.js"; } from "./outbound.js";
@@ -1017,6 +1018,11 @@ export class WhatsAppAdapter implements ChannelAdapter {
if (!this.sock) return; if (!this.sock) return;
await sendTypingIndicator(this.sock, chatId); await sendTypingIndicator(this.sock, chatId);
} }
async stopTypingIndicator(chatId: string): Promise<void> {
if (!this.sock) return;
await stopTypingIndicator(this.sock, chatId);
}
} }
// Export types and config // Export types and config

View File

@@ -168,6 +168,24 @@ export async function sendTypingIndicator(
} }
} }
/**
* Stop typing indicator for a chat.
* Sends "paused" presence to immediately clear the "typing..." indicator
* instead of waiting for WhatsApp's built-in timeout (~15-25s).
*/
export async function stopTypingIndicator(
sock: import("@whiskeysockets/baileys").WASocket,
chatId: string
): Promise<void> {
if (!sock) return;
try {
await sock.sendPresenceUpdate("paused", chatId);
} catch {
// Ignore presence errors
}
}
/** /**
* Send read receipt for a message. * Send read receipt for a message.
* *

View File

@@ -725,6 +725,7 @@ export class LettaBot implements AgentSession {
} }
} finally { } finally {
clearInterval(typingInterval); clearInterval(typingInterval);
adapter.stopTypingIndicator?.(msg.chatId)?.catch(() => {});
} }
// Handle no-reply marker // Handle no-reply marker