diff --git a/src/gateway/server-node-events.test.ts b/src/gateway/server-node-events.test.ts index 6cb7a79d70c..206e3a90141 100644 --- a/src/gateway/server-node-events.test.ts +++ b/src/gateway/server-node-events.test.ts @@ -111,7 +111,10 @@ describe("node exec events", () => { "Exec started (node=node-1 id=run-1): ls -la", { sessionKey: "agent:main:main", contextKey: "exec:run-1" }, ); - expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ reason: "exec-event" }); + expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ + reason: "exec-event", + sessionKey: "agent:main:main", + }); }); it("enqueues exec.finished events with output", async () => { @@ -185,7 +188,10 @@ describe("node exec events", () => { "Exec denied (node=node-3 id=run-3, allowlist-miss): rm -rf /", { sessionKey: "agent:demo:main", contextKey: "exec:run-3" }, ); - expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ reason: "exec-event" }); + expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ + reason: "exec-event", + sessionKey: "agent:demo:main", + }); }); it("suppresses exec.started when notifyOnExit is false", async () => { diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index 17495a6e737..8aa7146302e 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -10,7 +10,7 @@ import { buildOutboundSessionContext } from "../infra/outbound/session-context.j import { resolveOutboundTarget } from "../infra/outbound/targets.js"; import { registerApnsToken } from "../infra/push-apns.js"; import { enqueueSystemEvent } from "../infra/system-events.js"; -import { normalizeMainKey } from "../routing/session-key.js"; +import { normalizeMainKey, parseAgentSessionKey } from "../routing/session-key.js"; import { defaultRuntime } from "../runtime.js"; import { parseMessageWithAttachments } from "./chat-attachments.js"; import { normalizeRpcAttachmentsToChatAttachments } from "./server-methods/attachment-normalize.js"; @@ -574,7 +574,14 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt } enqueueSystemEvent(text, { sessionKey, contextKey: runId ? `exec:${runId}` : "exec" }); - requestHeartbeatNow({ reason: "exec-event" }); + // Scope wakes only for canonical agent sessions. Synthetic node-* fallback + // keys should keep legacy unscoped behavior so enabled non-main heartbeat + // agents still run when no explicit agent session is provided. + requestHeartbeatNow( + parseAgentSessionKey(sessionKey) + ? { reason: "exec-event", sessionKey } + : { reason: "exec-event" }, + ); return; } case "push.apns.register": {