From aad01edd3e0d367ad0defeb691d5689ddf2ee617 Mon Sep 17 00:00:00 2001 From: scoootscooob Date: Sun, 1 Mar 2026 01:54:41 -0800 Subject: [PATCH] fix(hooks): thread agentId through to after_tool_call hook context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #30511 — the after_tool_call hook context was passing `agentId: undefined` because SubscribeEmbeddedPiSessionParams did not carry the agent identity. This threads sessionAgentId (resolved in attempt.ts) through the session params into the tool handler context, giving plugins accurate agent-scoped context for both before_tool_call and after_tool_call hooks. Changes: - Add `agentId?: string` to SubscribeEmbeddedPiSessionParams - Add "agentId" to ToolHandlerParams Pick type - Pass `agentId: sessionAgentId` at the subscribeEmbeddedPiSession() call site in attempt.ts - Wire ctx.params.agentId into the after_tool_call hook context - Update tests to assert agentId propagation Co-Authored-By: Claude Opus 4.6 --- src/agents/pi-embedded-runner/run/attempt.ts | 1 + src/agents/pi-embedded-subscribe.handlers.tools.ts | 2 +- src/agents/pi-embedded-subscribe.handlers.types.ts | 2 +- src/agents/pi-embedded-subscribe.types.ts | 2 ++ src/plugins/wired-hooks-after-tool-call.e2e.test.ts | 5 +++++ 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index a4fca4ca59c..7cd8964aca6 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -1186,6 +1186,7 @@ export async function runEmbeddedAttempt( enforceFinalTag: params.enforceFinalTag, config: params.config, sessionKey: params.sessionKey ?? params.sessionId, + agentId: sessionAgentId, }); const { diff --git a/src/agents/pi-embedded-subscribe.handlers.tools.ts b/src/agents/pi-embedded-subscribe.handlers.tools.ts index e41346a9e24..4a5893c3236 100644 --- a/src/agents/pi-embedded-subscribe.handlers.tools.ts +++ b/src/agents/pi-embedded-subscribe.handlers.tools.ts @@ -426,7 +426,7 @@ export async function handleToolExecutionEnd( void hookRunnerAfter .runAfterToolCall(hookEvent, { toolName, - agentId: undefined, + agentId: ctx.params.agentId, sessionKey: ctx.params.sessionKey, }) .catch((err) => { diff --git a/src/agents/pi-embedded-subscribe.handlers.types.ts b/src/agents/pi-embedded-subscribe.handlers.types.ts index bd6c95b8190..d7488d767ad 100644 --- a/src/agents/pi-embedded-subscribe.handlers.types.ts +++ b/src/agents/pi-embedded-subscribe.handlers.types.ts @@ -132,7 +132,7 @@ export type EmbeddedPiSubscribeContext = { */ export type ToolHandlerParams = Pick< SubscribeEmbeddedPiSessionParams, - "runId" | "onBlockReplyFlush" | "onAgentEvent" | "onToolResult" | "sessionKey" + "runId" | "onBlockReplyFlush" | "onAgentEvent" | "onToolResult" | "sessionKey" | "agentId" >; export type ToolHandlerState = Pick< diff --git a/src/agents/pi-embedded-subscribe.types.ts b/src/agents/pi-embedded-subscribe.types.ts index afa635d7307..426daf2fd15 100644 --- a/src/agents/pi-embedded-subscribe.types.ts +++ b/src/agents/pi-embedded-subscribe.types.ts @@ -31,6 +31,8 @@ export type SubscribeEmbeddedPiSessionParams = { enforceFinalTag?: boolean; config?: OpenClawConfig; sessionKey?: string; + /** Agent identity for hook context — resolved from session config in attempt.ts. */ + agentId?: string; }; export type { BlockReplyChunking } from "./pi-embedded-block-chunker.js"; diff --git a/src/plugins/wired-hooks-after-tool-call.e2e.test.ts b/src/plugins/wired-hooks-after-tool-call.e2e.test.ts index 5471e2d0dff..11d073e8356 100644 --- a/src/plugins/wired-hooks-after-tool-call.e2e.test.ts +++ b/src/plugins/wired-hooks-after-tool-call.e2e.test.ts @@ -127,6 +127,7 @@ describe("after_tool_call hook wiring", () => { expect(event.error).toBeUndefined(); expect(typeof event.durationMs).toBe("number"); expect(context.toolName).toBe("read"); + expect(context.agentId).toBe("main"); expect(context.sessionKey).toBe("test-session"); }); @@ -166,6 +167,10 @@ describe("after_tool_call hook wiring", () => { throw new Error("missing hook call payload"); } expect(event.error).toBeDefined(); + + // agentId should be undefined when not provided + const context = firstCall?.[1] as { agentId?: string } | undefined; + expect(context?.agentId).toBeUndefined(); }); it("does not call runAfterToolCall when no hooks registered", async () => {