fix(heartbeat): guard bridge availability before send and fix sentToTelegram TOCTOU#357
Conversation
Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: xlabtg#356
…elegram TOCTOU - In runHeartbeat() (index.ts): check bridge.isAvailable() before attempting sendMessage() so a disconnected bridge logs a warning instead of throwing an uncaught error inside the heartbeat tick. - In /heartbeat/trigger (agent-actions.ts): replace the TOCTOU pattern where sentToTelegram was computed by calling isAvailable() a second time after the send. Now a boolean flag is set at the moment the message is actually sent, ensuring the reported value is accurate even if bridge availability changes between the send and the response. - Log a warn-level message when the bridge is unavailable so silent alert-drop failures are visible in logs. - Add createLogger to agent-actions.ts (was missing). - Add src/webui/__tests__/agent-actions.test.ts with 8 unit tests covering: disabled heartbeat, missing admin_ids, NO_ACTION suppression, __SILENT__ suppression, actionable alert with bridge available, actionable alert with bridge unavailable, processMessage error path, and TOCTOU sentToTelegram correctness.
Solution summaryDone. Here's a summary of what was done: What was fixedRoot cause analysis of issue #356 ("errors occur in the agent's logs when the heartbeat function is called"): Bug 1 —
|
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $1.775644📊 Context and tokens usage:Claude Sonnet 4.6:
Total: (59.8K + 3.6M cached) input tokens, 15.6K output tokens, $1.551538 cost Claude Haiku 4.5:
Total: (82.8K + 880.1K cached) input tokens, 6.5K output tokens, $0.224106 cost 🤖 Models used:
📎 Log file uploaded as Gist (2412KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
🔄 Auto-restart triggered (iteration 1)Reason: Merge conflicts detected Starting new session to address the issues. Auto-restart-until-mergeable mode is active. Will continue until PR becomes mergeable. |
🔄 Auto-restart-until-mergeable Log (iteration 1)This log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $0.180248📊 Context and tokens usage:
Total: (14.3K + 315.1K cached) input tokens, 2.2K output tokens, $0.180248 cost 🤖 Models used:
📎 Log file uploaded as Gist (2802KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
✅ Ready to mergeThis pull request is now ready to be merged:
Monitored by hive-mind with --auto-restart-until-mergeable flag |
Summary
Fixes #356
Two concrete bugs were found in the Heartbeat function and corrected:
runHeartbeat()(index.ts) — calledbridge.sendMessage()unconditionally even when the bridge was disconnected, causing an error to be thrown and caught inside the heartbeat tick. Now checksbridge.isAvailable()first and logs awarnif the bridge is down so alert-drop failures are visible in logs instead of appearing as uncaught errors./heartbeat/trigger(agent-actions.ts) — thesentToTelegramfield in the response was computed by callingbridge.isAvailable()a second time after the message was already sent (TOCTOU). If the bridge disconnected between the send and the response, the reported value would be wrong. Now asentToTelegramboolean flag is set at the exact momentsendMessage()is awaited successfully, making the value accurate. When the bridge is unavailable, awarnlog is now emitted instead of silently dropping the alert.Missing logger —
createLoggerwas not imported inagent-actions.ts; added.How to reproduce the original issues
Heartbeat error) instead of a clean warning about bridge unavailability./heartbeat/triggerwhen the bridge drops between the send attempt and the response —sentToTelegramin the API response would befalseeven though the message had been sent.Tests added
src/webui/__tests__/agent-actions.test.ts— 8 unit tests forPOST /heartbeat/trigger:NO_ACTIONresponse — does not send to Telegram__SILENT__response — does not send to TelegramsentToTelegram=falsewhen bridge unavailableprocessMessagethrowssentToTelegramreflects actual send, not a re-check of availability (TOCTOU regression test)All 8 tests pass locally.