Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@gumadeiras
Copy link
Contributor

Summary of Fixes and Behavior Changes

In fixing #1029 I realized the problem ran a little bit deeper.

Problem

In multi‑agent setups on a single gateway, health and status output was misleading:

  • Only one Telegram bot was shown even when multiple agents had distinct bots.
  • Session store path and recent sessions only reflected the main agent.
  • Heartbeat values were incorrect and not sourced from configuration.
  • status output showed the same issues as health.

Root Causes

  • Telegram account bindings were ignored when picking default account ↔ agent mapping.
  • health/status summaries only considered one account and one session store.
  • Heartbeat logic implicitly inherited defaults for agents without explicit heartbeat blocks.
  • Probe calls did not fan out across all accounts in verbose/deep modes.

Behavior Changes (What Changed)

Health

  • Normal run: shows only default account/agent details (unchanged for compact output).
  • --verbose: probes all accounts and shows per‑account timing in a concise, readable format.
  • Sessions and session stores are now per‑agent and visible.
  • Heartbeat is resolved from config (defaults + per‑agent overrides). Agents explicitly disabled show as disabled.

Status

  • Normal run: shows aggregate session count across agents and a summary of session stores.
  • --deep: uses the same concise per‑account probe format as health and probes all accounts.
  • Heartbeat uses the same enablement rules as health.

Telegram Account Mapping

  • Fixed bindings usage so the default agent maps to the correct bot/account.
  • Probing now respects the binding map; default probes are performed per‑agent and become complete when run in verbose/deep modes.

Heartbeat Enablement (Multi‑Agent)

  • If any agent has a heartbeat block, only those agents with explicit heartbeat blocks run heartbeats.
  • Agents without heartbeat blocks are explicitly disabled — they no longer implicitly inherit heartbeat behavior from a global default (as explained in https://docs.clawd.bot/gateway/heartbeat#per-agent-heartbeats).
  • Defaults are still merged into per‑agent heartbeat blocks when a block exists for that agent.

Tests

  • Added coverage for per‑account probe formatting and multi‑agent session aggregation.
  • Added coverage validating heartbeat enablement behavior (explicit vs implicit agents).

current clawdbot health --verbose
→ wrong heartbeat duration; probing a random account instead of the one associated with default agent; missing session for other agents
image

new ones fix heartbeat reading; identifies the default account<>agent and others; sessions for other agents
new clawdbot health --verbose
image

current clawdbot status --deep

new clawdbot status --deep
image

The Telegram webhook and monitor now accept and pass through accountId and config parameters, enabling routing and configuration per Telegram account. Tests have been updated to verify correct bot instantiation and DM routing based on accountId bindings.
- Align health/status with multi‑agent heartbeat enablement, per‑account probes, and per‑agent sessions.

- Share heartbeat summary logic via heartbeat-runner, update status/health outputs, and TUI status rendering.

- Add/adjust tests for heartbeat enablement, per‑account probe formatting, and multi‑agent sessions.
Copilot AI review requested due to automatic review settings January 16, 2026 23:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes multi-agent setup issues with health/status commands, heartbeat enablement logic, and Telegram account binding resolution. It ensures that multi-agent gateways correctly display per-agent information for heartbeats, sessions, and channel probes.

Changes:

  • Fixed Telegram account-to-agent binding resolution to respect configured bindings instead of always defaulting to the first account
  • Corrected heartbeat enablement logic so agents without explicit heartbeat blocks are disabled when other agents have explicit blocks
  • Enhanced health and status commands to display per-agent session stores, heartbeats, and channel probes (especially in verbose/deep modes)

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/tui/tui-types.ts Updated GatewayStatusSummary type to support multiple heartbeat agents and session paths
src/tui/tui-status-summary.ts Updated formatting to display per-agent heartbeat and multiple session stores
src/telegram/bot.test.ts Added test for group reply handling with requireMention enabled
src/telegram/accounts.ts Added binding resolution logic to map default agent to correct Telegram account
src/infra/heartbeat-runner.ts Added isHeartbeatEnabledForAgent and resolveHeartbeatSummaryForAgent functions
src/infra/heartbeat-runner.returns-default-unset.test.ts Added tests for heartbeat enablement logic with explicit agents
src/gateway/server-methods/health.ts Added probe parameter support to enable verbose health checks
src/commands/status.types.ts Added HeartbeatStatus type and updated StatusSummary for multi-agent support
src/commands/status.test.ts Added test for multi-agent session aggregation
src/commands/status.summary.ts Refactored to aggregate sessions across all agents
src/commands/status.command.ts Updated output formatting for multi-agent heartbeats and session stores
src/commands/health.ts Extensive refactoring to support per-agent health snapshots and channel account probing
src/commands/health.test.ts Added test for per-account probe timing formatting
src/commands/health.snapshot.test.ts Added test for heartbeat disabling with explicit agent blocks
src/commands/health.command.coverage.test.ts Updated test data to include new agent-related fields
Comments suppressed due to low confidence (4)

src/commands/health.ts:1

  • The fallback chain on lines 122-123 is redundant. If merged is defined (line 121), it already contains the merged values of defaults and overrides, so checking defaults?.every and overrides?.every again is unnecessary. Consider simplifying to merged?.every ?? DEFAULT_HEARTBEAT_EVERY.
import { resolveDefaultAgentId } from "../agents/agent-scope.js";

src/commands/health.ts:1

  • Similar to the previous comment, the fallback chain defaults?.prompt ?? overrides?.prompt is redundant since merged already contains these values merged. Simplify to merged?.prompt.
import { resolveDefaultAgentId } from "../agents/agent-scope.js";

src/commands/health.ts:1

  • Same redundancy issue: defaults?.target ?? overrides?.target is unnecessary since merged already contains the merged result. Simplify the fallback chain.
import { resolveDefaultAgentId } from "../agents/agent-scope.js";

src/commands/health.ts:1

  • Same redundancy: defaults?.ackMaxChars ?? overrides?.ackMaxChars is unnecessary since merged already contains these values. Simplify to merged?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS.
import { resolveDefaultAgentId } from "../agents/agent-scope.js";

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +122 to +135
const every =
merged?.every ?? defaults?.every ?? overrides?.every ?? DEFAULT_HEARTBEAT_EVERY;
const everyMs = resolveHeartbeatIntervalMs(cfg, undefined, merged);
const prompt = resolveHeartbeatPromptText(
merged?.prompt ?? defaults?.prompt ?? overrides?.prompt,
);
const target = merged?.target ?? defaults?.target ?? overrides?.target ?? DEFAULT_HEARTBEAT_TARGET;
const model = merged?.model ?? defaults?.model ?? overrides?.model;
const ackMaxChars = Math.max(
0,
merged?.ackMaxChars ??
defaults?.ackMaxChars ??
overrides?.ackMaxChars ??
DEFAULT_HEARTBEAT_ACK_MAX_CHARS,
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple fallback chains in this function have redundant checks for defaults and overrides after already computing merged which contains the spread of both. All these chains should be simplified to use only merged with the final default value.

Suggested change
const every =
merged?.every ?? defaults?.every ?? overrides?.every ?? DEFAULT_HEARTBEAT_EVERY;
const everyMs = resolveHeartbeatIntervalMs(cfg, undefined, merged);
const prompt = resolveHeartbeatPromptText(
merged?.prompt ?? defaults?.prompt ?? overrides?.prompt,
);
const target = merged?.target ?? defaults?.target ?? overrides?.target ?? DEFAULT_HEARTBEAT_TARGET;
const model = merged?.model ?? defaults?.model ?? overrides?.model;
const ackMaxChars = Math.max(
0,
merged?.ackMaxChars ??
defaults?.ackMaxChars ??
overrides?.ackMaxChars ??
DEFAULT_HEARTBEAT_ACK_MAX_CHARS,
const every = merged?.every ?? DEFAULT_HEARTBEAT_EVERY;
const everyMs = resolveHeartbeatIntervalMs(cfg, undefined, merged);
const prompt = resolveHeartbeatPromptText(merged?.prompt);
const target = merged?.target ?? DEFAULT_HEARTBEAT_TARGET;
const model = merged?.model;
const ackMaxChars = Math.max(
0,
merged?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS,

Copilot uses AI. Check for mistakes.
@steipete steipete self-assigned this Jan 17, 2026
@steipete
Copy link
Contributor

Landed on main via commits:

  • f14d622 (refactor: centralize account bindings + health probes)
  • c592f39 (test: update health/status + legacy migration coverage)
  • fa2b92b (docs: update health/status + doctor docs)
  • e16ce1a (style: format health/status files)
  • 767f55b (fix: wire previous session entry and stabilize jpeg test)
  • 3fb699a (style: apply oxfmt)

Closing this PR since the changes are already on main. Thanks @gumadeiras!

@steipete steipete closed this Jan 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants