From 6ec9a2bd79ef0becded4fd20b32f24bcddab9200 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 10 Apr 2026 18:47:18 +0900 Subject: [PATCH] fix(openclaw): skip session.created dispatch for subagent sessions --- src/plugin/event.test.ts | 50 ++++++++++++++++++++++++++++++++++++++-- src/plugin/event.ts | 5 +++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/plugin/event.test.ts b/src/plugin/event.test.ts index 1d39b1ed5..f9a82da75 100644 --- a/src/plugin/event.test.ts +++ b/src/plugin/event.test.ts @@ -590,7 +590,8 @@ describe("createEventHandler - event forwarding", () => { expect(createdSessions).toHaveLength(0) }) - it("dispatches OpenClaw after session.created using tracked pane metadata", async () => { + it("dispatches OpenClaw after session.created for main sessions (no parentID)", async () => { + //#given const openClawSpy = spyOn(openclawRuntimeDispatch, "dispatchOpenClawEvent").mockResolvedValue(null) const eventHandler = createEventHandler({ ctx: asEventHandlerContext({ directory: "/tmp/project-created" }), @@ -620,13 +621,15 @@ describe("createEventHandler - event forwarding", () => { hooks: createEventHandlerHooks({}), }) + //#when - main session created (no parentID) await eventHandler(asEventHandlerInput({ event: { type: "session.created", - properties: { info: { id: "ses_openclaw_created", parentID: "ses_parent" } }, + properties: { info: { id: "ses_openclaw_created" } }, }, })) + //#then - OpenClaw dispatch called for main session const [call] = openClawSpy.mock.calls[0] ?? [] expect(call).toMatchObject({ rawEvent: "session.created", @@ -638,6 +641,49 @@ describe("createEventHandler - event forwarding", () => { }) }) + it("does NOT dispatch OpenClaw for subagent sessions (with parentID)", async () => { + //#given + const openClawSpy = spyOn(openclawRuntimeDispatch, "dispatchOpenClawEvent").mockResolvedValue(null) + const eventHandler = createEventHandler({ + ctx: asEventHandlerContext({ directory: "/tmp/project-created" }), + pluginConfig: asPluginConfig({ + openclaw: { enabled: true, gateways: {}, hooks: {} }, + tmux: { + enabled: true, + layout: "main-vertical", + main_pane_size: 60, + main_pane_min_width: 120, + agent_pane_min_width: 40, + isolation: "inline", + }, + }), + firstMessageVariantGate: { + markSessionCreated: () => {}, + clear: () => {}, + }, + managers: createEventHandlerManagers({ + skillMcpManager: { disconnectSession: async () => {} }, + tmuxSessionManager: { + onSessionCreated: async () => {}, + onSessionDeleted: async () => {}, + getTrackedPaneId: (sessionID: string) => (sessionID === "ses_subagent" ? "%10" : undefined), + }, + }), + hooks: createEventHandlerHooks({}), + }) + + //#when - subagent session created (with parentID) + await eventHandler(asEventHandlerInput({ + event: { + type: "session.created", + properties: { info: { id: "ses_subagent", parentID: "ses_parent" } }, + }, + })) + + //#then - OpenClaw dispatch NOT called for subagent session (handled by specialized callbacks) + expect(openClawSpy.mock.calls.length).toBe(0) + }) + it("forwards session.deleted to write-existing-file-guard hook", async () => { //#given const forwardedEvents: EventInput[] = [] diff --git a/src/plugin/event.ts b/src/plugin/event.ts index 10c1da6df..5fc8c89ef 100644 --- a/src/plugin/event.ts +++ b/src/plugin/event.ts @@ -382,7 +382,10 @@ export function createEventHandler(args: { ); } - if (pluginConfig.openclaw && sessionInfo?.id) { + // Skip subagent sessions — they are dispatched by specialized callbacks + // in create-managers.ts (async) and tool-registry.ts (sync) + const isSubagentSession = !!sessionInfo?.parentID; + if (pluginConfig.openclaw && sessionInfo?.id && !isSubagentSession) { await dispatchOpenClawEvent({ config: pluginConfig.openclaw, rawEvent: event.type,