Fix empty result JSON dump and missing heartbeat prefix

Bug fixes:
- Empty result field handling: Changed truthy check to explicit type
  check (`typeof parsed?.text === "string"`) in command-reply.ts.
  Previously, Claude CLI returning `result: ""` would cause raw JSON
  to be sent to WhatsApp.
- Response prefix on heartbeat: Apply `responsePrefix` to heartbeat
  alert messages in runReplyHeartbeat, matching behavior of regular
  message handler.
This commit is contained in:
Peter Steinberger
2025-12-02 04:29:17 +00:00
parent c5677df56e
commit c5ab442f46
3 changed files with 13 additions and 2 deletions

View File

@@ -2,6 +2,10 @@
## 1.2.3 — Unreleased
### Bug Fixes
- **Empty result field handling:** Fixed bug where Claude CLI returning `result: ""` (empty string) would cause raw JSON to be sent to WhatsApp instead of being treated as valid empty output. Changed truthy check to explicit type check in `command-reply.ts`.
- **Response prefix on heartbeat replies:** Fixed `responsePrefix` (e.g., `🦞`) not being applied to heartbeat alert messages. The prefix was only applied in the regular message handler, not in `runReplyHeartbeat`.
### Changes
- **Auto-recovery from stuck WhatsApp sessions:** Added watchdog timer that detects when WhatsApp event emitter stops firing (e.g., after Bad MAC decryption errors) and automatically restarts the connection after 30 minutes of no message activity. Heartbeat logging now includes `minutesSinceLastMessage` and warns when >30 minutes without messages. The 30-minute timeout is intentionally longer than typical `heartbeatMinutes` configs to avoid false positives.
- **Early allowFrom filtering:** Unauthorized senders are now blocked in `inbound.ts` BEFORE encryption/decryption attempts, preventing Bad MAC errors from corrupting session state. Previously, messages from unauthorized senders would trigger decryption failures that could silently kill the event emitter.

View File

@@ -230,7 +230,7 @@ export async function runCommandReply(
`Claude JSON raw: ${JSON.stringify(parsed.parsed, null, 2)}`,
);
}
if (parsed?.text) {
if (typeof parsed?.text === "string") {
logVerbose(
`Claude JSON parsed -> ${parsed.text.slice(0, 120)}${parsed.text.length > 120 ? "…" : ""}`,
);

View File

@@ -862,9 +862,16 @@ export async function monitorWebProvider(
return;
}
// Apply response prefix if configured (same as regular messages)
let finalText = stripped.text;
const responsePrefix = cfg.inbound?.responsePrefix;
if (responsePrefix && finalText && !finalText.startsWith(responsePrefix)) {
finalText = `${responsePrefix} ${finalText}`;
}
const cleanedReply: ReplyPayload = {
...replyResult,
text: stripped.text,
text: finalText,
};
await deliverWebReply({