mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
fix(heartbeat): report the cadence of active agent heartbeats (#126869)
This commit is contained in:
committed by
GitHub
parent
b00734bd4c
commit
40e3ab8784
@@ -107,6 +107,7 @@ describe("collectGatewayHealthSnapshot legacy owner projection", () => {
|
||||
expect(explicit.defaultAgentId).toBeUndefined();
|
||||
expect(explicit.agents.every((agent) => !agent.isDefault)).toBe(true);
|
||||
expect(explicit.agents.every((agent) => !agent.heartbeat.enabled)).toBe(true);
|
||||
expect(explicit.heartbeatSeconds).toBe(0);
|
||||
});
|
||||
|
||||
it("projects the configured heartbeat owner's cadence", async () => {
|
||||
@@ -129,4 +130,34 @@ describe("collectGatewayHealthSnapshot legacy owner projection", () => {
|
||||
);
|
||||
expect(health.heartbeatSeconds).toBe(5 * 60);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ label: "an earlier agent", heartbeatAgentId: undefined },
|
||||
{ label: "the configured owner", heartbeatAgentId: "ops" },
|
||||
])(
|
||||
"reports the active heartbeat when $label disables its cadence",
|
||||
async ({ heartbeatAgentId }) => {
|
||||
testConfig = {
|
||||
agents: {
|
||||
ownership: "explicit",
|
||||
defaults: {
|
||||
heartbeat: {
|
||||
every: "30m",
|
||||
...(heartbeatAgentId ? { agentId: heartbeatAgentId } : {}),
|
||||
},
|
||||
},
|
||||
entries: {
|
||||
ops: { heartbeat: { every: "0m" } },
|
||||
research: { heartbeat: { every: "1h" } },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const health = await collectGatewayHealthSnapshot({ audience: "admin", probe: false });
|
||||
|
||||
expect(health.agents.map((agent) => agent.agentId)).toEqual(["ops", "research"]);
|
||||
expect(health.agents.map((agent) => agent.heartbeat.enabled)).toEqual([false, true]);
|
||||
expect(health.heartbeatSeconds).toBe(60 * 60);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -232,7 +232,11 @@ export async function collectGatewayHealthSnapshot(params: {
|
||||
);
|
||||
const heartbeatSummaryAgent =
|
||||
(configuredHeartbeatAgentId
|
||||
? agents.find((agent) => agent.agentId === normalizeAgentId(configuredHeartbeatAgentId))
|
||||
? agents.find(
|
||||
(agent) =>
|
||||
agent.heartbeat.enabled &&
|
||||
agent.agentId === normalizeAgentId(configuredHeartbeatAgentId),
|
||||
)
|
||||
: undefined) ??
|
||||
agents.find((agent) => agent.heartbeat.enabled) ??
|
||||
summaryAgent;
|
||||
|
||||
@@ -375,6 +375,43 @@ describe("resolveHeartbeatIntervalMs", () => {
|
||||
expect(resolveHeartbeatSummaryForAgent(cfg, "main").session).toBe("telegram:alerts");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
label: "global",
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
heartbeat: { every: "0m", target: "last", session: "telegram:default" },
|
||||
},
|
||||
},
|
||||
},
|
||||
session: "telegram:default",
|
||||
},
|
||||
{
|
||||
label: "per-agent",
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
heartbeat: { every: "30m", target: "last", session: "telegram:default" },
|
||||
},
|
||||
list: [{ id: "main", heartbeat: { every: "0m", session: "telegram:alerts" } }],
|
||||
},
|
||||
},
|
||||
session: "telegram:alerts",
|
||||
},
|
||||
] satisfies Array<{ label: string; cfg: OpenClawConfig; session: string }>)(
|
||||
"reports a disabled $label heartbeat as disabled",
|
||||
({ cfg, session }) => {
|
||||
expect(resolveHeartbeatSummaryForAgent(cfg, "main")).toMatchObject({
|
||||
enabled: false,
|
||||
every: "disabled",
|
||||
everyMs: null,
|
||||
target: "last",
|
||||
session,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("returns default when unset", () => {
|
||||
expect(resolveHeartbeatIntervalMs({})).toBe(30 * 60_000);
|
||||
});
|
||||
|
||||
@@ -97,37 +97,17 @@ export function resolveHeartbeatSummaryForAgent(
|
||||
const defaults = cfg.agents?.defaults?.heartbeat;
|
||||
const overrides = agentId ? resolveAgentConfig(cfg, agentId)?.heartbeat : undefined;
|
||||
const merged = defaults || overrides ? { ...defaults, ...overrides } : undefined;
|
||||
const enabled = isHeartbeatEnabledForAgent(cfg, agentId);
|
||||
|
||||
if (!enabled) {
|
||||
return {
|
||||
enabled: false,
|
||||
every: "disabled",
|
||||
everyMs: null,
|
||||
prompt: resolveHeartbeatPromptText(merged?.prompt),
|
||||
target: merged?.target ?? DEFAULT_HEARTBEAT_TARGET,
|
||||
model: merged?.model,
|
||||
session: merged?.session,
|
||||
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 session = merged?.session;
|
||||
const ackMaxChars = DEFAULT_HEARTBEAT_ACK_MAX_CHARS;
|
||||
const enabled = isHeartbeatEnabledForAgent(cfg, agentId) && everyMs !== null;
|
||||
|
||||
return {
|
||||
enabled: true,
|
||||
every,
|
||||
everyMs,
|
||||
prompt,
|
||||
target,
|
||||
model,
|
||||
session,
|
||||
ackMaxChars,
|
||||
enabled,
|
||||
every: enabled ? (merged?.every ?? DEFAULT_HEARTBEAT_EVERY) : "disabled",
|
||||
everyMs: enabled ? everyMs : null,
|
||||
prompt: resolveHeartbeatPromptText(merged?.prompt),
|
||||
target: merged?.target ?? DEFAULT_HEARTBEAT_TARGET,
|
||||
model: merged?.model,
|
||||
session: merged?.session,
|
||||
ackMaxChars: DEFAULT_HEARTBEAT_ACK_MAX_CHARS,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user