fix(regression): canonicalize exec session routing

This commit is contained in:
Tak Hoffman
2026-03-27 21:01:31 -05:00
parent fe295b15a5
commit 59a0411a78
2 changed files with 30 additions and 2 deletions

View File

@@ -174,6 +174,33 @@ describe("node exec events", () => {
expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ reason: "exec-event" });
});
it("canonicalizes exec session key before enqueue and wake", async () => {
loadSessionEntryMock.mockReturnValueOnce({
...buildSessionLookup("node-node-2"),
canonicalKey: "agent:main:node-node-2",
});
const ctx = buildCtx();
await handleNodeEvent(ctx, "node-2", {
event: "exec.finished",
payloadJSON: JSON.stringify({
runId: "run-2",
exitCode: 0,
timedOut: false,
output: "done",
}),
});
expect(loadSessionEntryMock).toHaveBeenCalledWith("node-node-2");
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
"Exec finished (node=node-2 id=run-2, code 0)\ndone",
{ sessionKey: "agent:main:node-node-2", contextKey: "exec:run-2" },
);
expect(requestHeartbeatNowMock).toHaveBeenCalledWith({
reason: "exec-event",
sessionKey: "agent:main:node-node-2",
});
});
it("suppresses noisy exec.finished success events with empty output", async () => {
const ctx = buildCtx();
await handleNodeEvent(ctx, "node-2", {

View File

@@ -525,11 +525,12 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
if (!obj) {
return;
}
const sessionKey =
const sessionKeyRaw =
typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : `node-${nodeId}`;
if (!sessionKey) {
if (!sessionKeyRaw) {
return;
}
const { canonicalKey: sessionKey } = loadSessionEntry(sessionKeyRaw);
// Respect tools.exec.notifyOnExit setting (default: true)
// When false, skip system event notifications for node exec events.