fix(regression): hydrate run-scoped tool event metadata

This commit is contained in:
Tak Hoffman
2026-03-27 20:55:04 -05:00
parent c42ec81e37
commit e890cde041
2 changed files with 70 additions and 1 deletions

View File

@@ -716,6 +716,71 @@ describe("agent event handler", () => {
resetAgentRunContextForTest();
});
it("hydrates run-scoped tool events with session ownership metadata", () => {
const { broadcastToConnIds, toolEventRecipients, handler } = createHarness({
resolveSessionKeyForRun: () => "session-1",
});
vi.mocked(loadGatewaySessionRow).mockReturnValue({
key: "session-1",
kind: "direct",
spawnedBy: "agent:main:main",
spawnedWorkspaceDir: "/tmp/subagent",
forkedFromParent: true,
spawnDepth: 2,
subagentRole: "orchestrator",
subagentControlScope: "children",
lastThreadId: 42,
fastMode: true,
verboseLevel: "on",
updatedAt: 1_200,
});
registerAgentRunContext("run-tool-owner", { sessionKey: "session-1", verboseLevel: "off" });
toolEventRecipients.add("run-tool-owner", "conn-run");
handler({
runId: "run-tool-owner",
seq: 1,
stream: "tool",
ts: 1_234,
data: {
phase: "start",
name: "exec",
toolCallId: "tool-run-1",
args: { command: "echo hi" },
},
});
expect(broadcastToConnIds).toHaveBeenCalledTimes(1);
expect(broadcastToConnIds).toHaveBeenCalledWith(
"agent",
expect.objectContaining({
runId: "run-tool-owner",
sessionKey: "session-1",
spawnedBy: "agent:main:main",
spawnedWorkspaceDir: "/tmp/subagent",
forkedFromParent: true,
spawnDepth: 2,
subagentRole: "orchestrator",
subagentControlScope: "children",
lastThreadId: 42,
fastMode: true,
verboseLevel: "on",
stream: "tool",
ts: 1_234,
data: expect.objectContaining({
phase: "start",
name: "exec",
toolCallId: "tool-run-1",
args: { command: "echo hi" },
}),
}),
new Set(["conn-run"]),
);
resetAgentRunContextForTest();
});
it("broadcasts terminal session status to session subscribers on lifecycle end", () => {
const { broadcastToConnIds, sessionEventSubscribers, handler } = createHarness({
resolveSessionKeyForRun: () => "session-finished",

View File

@@ -770,7 +770,11 @@ export function createAgentEventHandler({
// messages to messaging surfaces (Telegram, Discord, etc.).
const recipients = toolEventRecipients.get(evt.runId);
if (recipients && recipients.size > 0) {
broadcastToConnIds("agent", toolPayload, recipients);
broadcastToConnIds(
"agent",
sessionKey ? { ...toolPayload, ...buildSessionEventSnapshot(sessionKey) } : toolPayload,
recipients,
);
}
// Session subscribers power operator UIs that attach to an existing
// in-flight session after the run has already started. Those clients do