diff --git a/src/auto-reply/reply/commands-core.test.ts b/src/auto-reply/reply/commands-core.test.ts new file mode 100644 index 00000000000..76aa4c9e827 --- /dev/null +++ b/src/auto-reply/reply/commands-core.test.ts @@ -0,0 +1,63 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { HookRunner } from "../../plugins/hooks.js"; +import type { HandleCommandsParams } from "./commands-types.js"; + +const hookRunnerMocks = vi.hoisted(() => ({ + hasHooks: vi.fn(), + runBeforeReset: vi.fn(), +})); + +vi.mock("../../plugins/hook-runner-global.js", () => ({ + getGlobalHookRunner: () => + ({ + hasHooks: hookRunnerMocks.hasHooks, + runBeforeReset: hookRunnerMocks.runBeforeReset, + }) as unknown as HookRunner, +})); + +const { emitResetCommandHooks } = await import("./commands-core.js"); + +describe("emitResetCommandHooks", () => { + beforeEach(() => { + hookRunnerMocks.hasHooks.mockReset(); + hookRunnerMocks.runBeforeReset.mockReset(); + hookRunnerMocks.hasHooks.mockImplementation((hookName) => hookName === "before_reset"); + hookRunnerMocks.runBeforeReset.mockResolvedValue(undefined); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("passes the bound agent id to before_reset hooks for multi-agent session keys", async () => { + const command = { + surface: "discord", + senderId: "rai", + channel: "discord", + from: "discord:rai", + to: "discord:bot", + resetHookTriggered: false, + } as HandleCommandsParams["command"]; + + await emitResetCommandHooks({ + action: "new", + ctx: {} as HandleCommandsParams["ctx"], + cfg: {} as HandleCommandsParams["cfg"], + command, + sessionKey: "agent:navi:main", + previousSessionEntry: { + sessionId: "prev-session", + } as HandleCommandsParams["previousSessionEntry"], + workspaceDir: "/tmp/openclaw-workspace", + }); + + await vi.waitFor(() => expect(hookRunnerMocks.runBeforeReset).toHaveBeenCalledTimes(1)); + const [, ctx] = hookRunnerMocks.runBeforeReset.mock.calls[0] ?? []; + expect(ctx).toMatchObject({ + agentId: "navi", + sessionKey: "agent:navi:main", + sessionId: "prev-session", + workspaceDir: "/tmp/openclaw-workspace", + }); + }); +}); diff --git a/src/auto-reply/reply/commands-core.ts b/src/auto-reply/reply/commands-core.ts index d57d679fdb6..6548ea40afd 100644 --- a/src/auto-reply/reply/commands-core.ts +++ b/src/auto-reply/reply/commands-core.ts @@ -3,7 +3,7 @@ import { resetAcpSessionInPlace } from "../../acp/persistent-bindings.js"; import { logVerbose } from "../../globals.js"; import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js"; import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js"; -import { isAcpSessionKey } from "../../routing/session-key.js"; +import { isAcpSessionKey, resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; import { resolveSendPolicy } from "../../sessions/send-policy.js"; import { shouldHandleTextCommands } from "../commands-registry.js"; import { handleAcpCommand } from "./commands-acp.js"; @@ -120,7 +120,7 @@ export async function emitResetCommandHooks(params: { await hookRunner.runBeforeReset( { sessionFile, messages, reason: params.action }, { - agentId: params.sessionKey?.split(":")[0] ?? "main", + agentId: resolveAgentIdFromSessionKey(params.sessionKey), sessionKey: params.sessionKey, sessionId: prevEntry?.sessionId, workspaceDir: params.workspaceDir,