Files
openclaw/src/plugins/wired-hooks-subagent.test.ts
2026-03-28 03:30:25 +00:00

166 lines
5.5 KiB
TypeScript

/**
* Test: subagent_spawning, subagent_delivery_target, subagent_spawned & subagent_ended hook wiring
*/
import { describe, expect, it, vi } from "vitest";
import { createHookRunnerWithRegistry } from "./hooks.test-helpers.js";
describe("subagent hook runner methods", () => {
const baseRequester = {
channel: "discord",
accountId: "work",
to: "channel:123",
threadId: "456",
};
const baseSubagentCtx = {
runId: "run-1",
childSessionKey: "agent:main:subagent:child",
requesterSessionKey: "agent:main:main",
};
async function invokeSubagentHook(params: {
hookName:
| "subagent_spawning"
| "subagent_spawned"
| "subagent_delivery_target"
| "subagent_ended";
event: Record<string, unknown>;
ctx: Record<string, unknown>;
handlerResult?: unknown;
}) {
const handler = vi.fn(async () => ({ status: "ok", threadBindingReady: true as const }));
if (params.handlerResult !== undefined) {
handler.mockResolvedValue(params.handlerResult as never);
}
const { runner } = createHookRunnerWithRegistry([{ hookName: params.hookName, handler }]);
const result =
params.hookName === "subagent_spawning"
? await runner.runSubagentSpawning(params.event as never, params.ctx as never)
: params.hookName === "subagent_spawned"
? await runner.runSubagentSpawned(params.event as never, params.ctx as never)
: params.hookName === "subagent_delivery_target"
? await runner.runSubagentDeliveryTarget(params.event as never, params.ctx as never)
: await runner.runSubagentEnded(params.event as never, params.ctx as never);
expect(handler).toHaveBeenCalledWith(params.event, params.ctx);
return result;
}
it.each([
{
name: "runSubagentSpawning invokes registered subagent_spawning hooks",
hookName: "subagent_spawning" as const,
methodName: "runSubagentSpawning" as const,
event: {
childSessionKey: "agent:main:subagent:child",
agentId: "main",
label: "research",
mode: "session" as const,
requester: baseRequester,
threadRequested: true,
},
ctx: {
childSessionKey: "agent:main:subagent:child",
requesterSessionKey: "agent:main:main",
},
handlerResult: { status: "ok", threadBindingReady: true as const },
expectedResult: { status: "ok", threadBindingReady: true },
},
{
name: "runSubagentSpawned invokes registered subagent_spawned hooks",
hookName: "subagent_spawned" as const,
methodName: "runSubagentSpawned" as const,
event: {
runId: "run-1",
childSessionKey: "agent:main:subagent:child",
agentId: "main",
label: "research",
mode: "run" as const,
requester: baseRequester,
threadRequested: true,
},
ctx: baseSubagentCtx,
},
{
name: "runSubagentDeliveryTarget invokes registered subagent_delivery_target hooks",
hookName: "subagent_delivery_target" as const,
methodName: "runSubagentDeliveryTarget" as const,
event: {
childSessionKey: "agent:main:subagent:child",
requesterSessionKey: "agent:main:main",
requesterOrigin: baseRequester,
childRunId: "run-1",
spawnMode: "session" as const,
expectsCompletionMessage: true,
},
ctx: baseSubagentCtx,
handlerResult: {
origin: {
channel: "discord" as const,
accountId: "work",
to: "channel:777",
threadId: "777",
},
},
expectedResult: {
origin: {
channel: "discord",
accountId: "work",
to: "channel:777",
threadId: "777",
},
},
},
{
name: "runSubagentEnded invokes registered subagent_ended hooks",
hookName: "subagent_ended" as const,
methodName: "runSubagentEnded" as const,
event: {
targetSessionKey: "agent:main:subagent:child",
targetKind: "subagent" as const,
reason: "subagent-complete",
sendFarewell: true,
accountId: "work",
runId: "run-1",
outcome: "ok" as const,
},
ctx: baseSubagentCtx,
},
] as const)("$name", async ({ hookName, event, ctx, handlerResult, expectedResult }) => {
const result = await invokeSubagentHook({ hookName, event, ctx, handlerResult });
if (expectedResult !== undefined) {
expect(result).toEqual(expectedResult);
return;
}
expect(result).toBeUndefined();
});
it("runSubagentDeliveryTarget returns undefined when no matching hooks are registered", async () => {
const { runner } = createHookRunnerWithRegistry([]);
const result = await runner.runSubagentDeliveryTarget(
{
childSessionKey: "agent:main:subagent:child",
requesterSessionKey: "agent:main:main",
requesterOrigin: baseRequester,
childRunId: "run-1",
spawnMode: "session",
expectsCompletionMessage: true,
},
baseSubagentCtx,
);
expect(result).toBeUndefined();
});
it("hasHooks returns true for registered subagent hooks", () => {
const { runner } = createHookRunnerWithRegistry([
{ hookName: "subagent_spawning", handler: vi.fn() },
{ hookName: "subagent_delivery_target", handler: vi.fn() },
]);
expect(runner.hasHooks("subagent_spawning")).toBe(true);
expect(runner.hasHooks("subagent_delivery_target")).toBe(true);
expect(runner.hasHooks("subagent_spawned")).toBe(false);
expect(runner.hasHooks("subagent_ended")).toBe(false);
});
});