← Back to PRs

#20948: fix: propagate accountId from heartbeat delivery context to agent run

by odrobnik open 2026-02-19 13:30 View on GitHub →
size: XS
## Problem When a heartbeat fires for a non-default agent (e.g. one bound to a specific Telegram account like `accountId: "leon"`), the heartbeat runner correctly resolves `delivery.accountId` from the agent's heartbeat config. However, this `accountId` was never passed into the `ctx` (MsgContext) object that feeds into `getReplyFromConfig()`. This caused the agent's `message` tool to be created without a default `agentAccountId`, so any outbound messages sent during the heartbeat run fell back to the `default` channel account — which may be a completely different bot that the target user has never interacted with. **Symptom:** `Telegram send failed: chat not found` when a heartbeat-triggered agent tries to message a user who only knows the agent's specific bot. ## Root Cause In `heartbeat-runner.ts`, the context object was missing `AccountId`: ```ts const ctx = { Body: ..., From: sender, To: sender, Provider: "heartbeat", SessionKey: sessionKey, // AccountId was missing here! }; ``` The `AccountId` flows through: ``` ctx.AccountId → getReplyFromConfig → get-reply-run.ts (sessionCtx.AccountId) → agentAccountId → createMessageTool() → message-tool.ts fallback ``` Without it, `agentAccountId` resolves to `undefined`, and the message tool uses the channel's default account. ## Fix One-line addition: `AccountId: delivery.accountId` in the heartbeat `ctx` object. ## When This Matters - Multiple accounts configured for a channel (e.g. `default` + `leon` for Telegram) - A non-default agent has heartbeat enabled with explicit `accountId` - The agent sends messages during heartbeat runs (check-ins, proactive alerts, etc.) Regular inbound message replies are unaffected because the incoming message carries the account context naturally. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes a bug where heartbeat-triggered agents with non-default account configurations (e.g., `accountId: "leon"` for a specific Telegram bot) would fail to send messages during heartbeat runs. The fix adds the missing `AccountId: delivery.accountId` field to the context object created in `heartbeat-runner.ts:671`. The `accountId` flows through the following path: - `ctx.AccountId` → `getReplyFromConfig()` → `sessionCtx.AccountId` (get-reply-run.ts:403) - → `agentAccountId` → `createMessageTool()` → message-tool.ts fallback logic Without this field, the message tool would fall back to the channel's `default` account, causing "chat not found" errors when the target user had only interacted with the specific non-default bot. The fix is verified by: - Existing tests in `heartbeat-runner.returns-default-unset.test.ts` that validate `accountId` propagation - Consistent pattern with other message handlers (Discord, Telegram, etc.) which all include `AccountId` in their context objects - The `delivery.accountId` is already populated by `resolveHeartbeatDeliveryTarget()` and used elsewhere in the same function (lines 872, 888) This is a targeted, one-line fix that aligns heartbeat context with the established pattern used throughout the codebase. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with no risk - The fix is a simple, one-line addition that adds a missing field to an existing context object. The change is well-tested through existing test coverage, follows established patterns throughout the codebase, and addresses a clear bug with a precise solution. The `delivery.accountId` value is already validated and used elsewhere in the same function, so there are no edge cases or risks introduced. - No files require special attention <sub>Last reviewed commit: 0394cd2</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs