From be0e994cf0b5daff42a8943bf515c6956ce19f5b Mon Sep 17 00:00:00 2001 From: "junpei.o" <14040213+livingghost@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:47:13 +0900 Subject: [PATCH] feat(plugins): expose runId in agent hook context (#54265) --- src/agents/pi-embedded-runner/run.ts | 1 + src/agents/pi-embedded-runner/run/attempt.ts | 4 ++++ src/plugins/hooks.before-agent-start.test.ts | 20 ++++++++++++++++++++ src/plugins/hooks.test-helpers.ts | 1 + src/plugins/types.ts | 2 ++ 5 files changed, 28 insertions(+) diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index 0dc848ebfb0..b915690eb88 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -146,6 +146,7 @@ export async function runEmbeddedPiAgent( await ensureOpenClawModelsJson(params.config, agentDir); const hookRunner = getGlobalHookRunner(); const hookCtx = { + runId: params.runId, agentId: workspaceResolution.agentId, sessionKey: params.sessionKey, sessionId: params.sessionId, diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index d908be23b0e..036c587a93e 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -1331,6 +1331,7 @@ export async function runEmbeddedAttempt( }, ); const hookCtx = { + runId: params.runId, agentId: hookAgentId, sessionKey: params.sessionKey, sessionId: params.sessionId, @@ -1463,6 +1464,7 @@ export async function runEmbeddedAttempt( imagesCount: imageResult.images.length, }, { + runId: params.runId, agentId: hookAgentId, sessionKey: params.sessionKey, sessionId: params.sessionId, @@ -1693,6 +1695,7 @@ export async function runEmbeddedAttempt( durationMs: Date.now() - promptStartedAt, }, { + runId: params.runId, agentId: hookAgentId, sessionKey: params.sessionKey, sessionId: params.sessionId, @@ -1755,6 +1758,7 @@ export async function runEmbeddedAttempt( usage: getUsageTotals(), }, { + runId: params.runId, agentId: hookAgentId, sessionKey: params.sessionKey, sessionId: params.sessionId, diff --git a/src/plugins/hooks.before-agent-start.test.ts b/src/plugins/hooks.before-agent-start.test.ts index d89ca2826e6..1162654d304 100644 --- a/src/plugins/hooks.before-agent-start.test.ts +++ b/src/plugins/hooks.before-agent-start.test.ts @@ -175,4 +175,24 @@ describe("before_agent_start hook merger", () => { expect(result?.modelOverride).toBe("llama3.3:8b"); expect(result?.providerOverride).toBe("ollama"); }); + + it("passes runId through the agent context to hook handlers", async () => { + const registry = createEmptyPluginRegistry(); + let capturedCtx: typeof stubCtx | undefined; + addTestHook({ + registry, + pluginId: "ctx-spy", + hookName: "before_agent_start", + handler: ((_event: unknown, ctx: typeof stubCtx) => { + capturedCtx = ctx; + return {}; + }) as PluginHookRegistration["handler"], + }); + + const runner = createHookRunner(registry); + await runner.runBeforeAgentStart({ prompt: "test" }, stubCtx); + + expect(capturedCtx).toBeDefined(); + expect(capturedCtx?.runId).toBe("test-run-id"); + }); }); diff --git a/src/plugins/hooks.test-helpers.ts b/src/plugins/hooks.test-helpers.ts index 6c9b1365f70..da0fb553a8a 100644 --- a/src/plugins/hooks.test-helpers.ts +++ b/src/plugins/hooks.test-helpers.ts @@ -40,6 +40,7 @@ export function createMockPluginRegistry( } export const TEST_PLUGIN_AGENT_CTX: PluginHookAgentContext = { + runId: "test-run-id", agentId: "test-agent", sessionKey: "test-session", sessionId: "test-session-id", diff --git a/src/plugins/types.ts b/src/plugins/types.ts index 5adfa4ebb23..44436d165f4 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1682,6 +1682,8 @@ export const isPromptInjectionHookName = (hookName: PluginHookName): boolean => // Agent context shared across agent hooks export type PluginHookAgentContext = { + /** Unique identifier for this agent run. */ + runId?: string; agentId?: string; sessionKey?: string; sessionId?: string;