mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 01:51:24 +00:00
feat(heartbeat): add accountId config option for multi-agent routing (#8702)
* feat(heartbeat): add accountId config option for multi-agent routing Add optional accountId field to heartbeat configuration, allowing multi-agent setups to explicitly specify which Telegram account should be used for heartbeat delivery. Previously, heartbeat delivery would use the accountId from the session's deliveryContext. When a session had no prior conversation history, heartbeats would default to the first/primary account instead of the agent's intended bot. Changes: - Add accountId to HeartbeatSchema (zod-schema.agent-runtime.ts) - Use heartbeat.accountId with fallback to session accountId (targets.ts) Backward compatible: if accountId is not specified, behavior is unchanged. Closes #8695 * fix: improve heartbeat accountId routing (#8702) (thanks @lsh411) * fix: harden heartbeat accountId routing (#8702) (thanks @lsh411) * fix: expose heartbeat accountId in status (#8702) (thanks @lsh411) * chore: format status + heartbeat tests (#8702) (thanks @lsh411) --------- Co-authored-by: m1 16 512 <m116512@m1ui-MacBookAir-2.local> Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import type { HeartbeatEventPayload } from "../infra/heartbeat-events.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { withProgress } from "../cli/progress.js";
|
||||
@@ -120,6 +121,14 @@ export async function statusCommand(
|
||||
}),
|
||||
)
|
||||
: undefined;
|
||||
const lastHeartbeat =
|
||||
opts.deep && gatewayReachable
|
||||
? await callGateway<HeartbeatEventPayload | null>({
|
||||
method: "last-heartbeat",
|
||||
params: {},
|
||||
timeoutMs: opts.timeoutMs,
|
||||
}).catch(() => null)
|
||||
: null;
|
||||
|
||||
const configChannel = normalizeUpdateChannel(cfg.update?.channel);
|
||||
const channelInfo = resolveEffectiveUpdateChannel({
|
||||
@@ -157,7 +166,7 @@ export async function statusCommand(
|
||||
nodeService: nodeDaemon,
|
||||
agents: agentStatus,
|
||||
securityAudit,
|
||||
...(health || usage ? { health, usage } : {}),
|
||||
...(health || usage || lastHeartbeat ? { health, usage, lastHeartbeat } : {}),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
@@ -275,6 +284,21 @@ export async function statusCommand(
|
||||
.filter(Boolean);
|
||||
return parts.length > 0 ? parts.join(", ") : "disabled";
|
||||
})();
|
||||
const lastHeartbeatValue = (() => {
|
||||
if (!opts.deep) {
|
||||
return null;
|
||||
}
|
||||
if (!gatewayReachable) {
|
||||
return warn("unavailable");
|
||||
}
|
||||
if (!lastHeartbeat) {
|
||||
return muted("none");
|
||||
}
|
||||
const age = formatAge(Date.now() - lastHeartbeat.ts);
|
||||
const channel = lastHeartbeat.channel ?? "unknown";
|
||||
const accountLabel = lastHeartbeat.accountId ? `account ${lastHeartbeat.accountId}` : null;
|
||||
return [lastHeartbeat.status, `${age} ago`, channel, accountLabel].filter(Boolean).join(" · ");
|
||||
})();
|
||||
|
||||
const storeLabel =
|
||||
summary.sessions.paths.length > 1
|
||||
@@ -371,6 +395,7 @@ export async function statusCommand(
|
||||
{ Item: "Probes", Value: probesValue },
|
||||
{ Item: "Events", Value: eventsValue },
|
||||
{ Item: "Heartbeat", Value: heartbeatValue },
|
||||
...(lastHeartbeatValue ? [{ Item: "Last heartbeat", Value: lastHeartbeatValue }] : []),
|
||||
{
|
||||
Item: "Sessions",
|
||||
Value: `${summary.sessions.count} active · default ${defaults.model ?? "unknown"}${defaultCtx} · ${storeLabel}`,
|
||||
|
||||
Reference in New Issue
Block a user