From 6e7c1c16e75f386d753688c10619e333f54121f6 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Feb 2026 03:48:28 +0100 Subject: [PATCH] test: remove duplicate legacy sessions_spawn e2e file --- ...rmalizes-allowlisted-agent-ids.e2e.test.ts | 377 ------------------ 1 file changed, 377 deletions(-) delete mode 100644 src/agents/openclaw-tools.subagents.sessions-spawn-normalizes-allowlisted-agent-ids.e2e.test.ts diff --git a/src/agents/openclaw-tools.subagents.sessions-spawn-normalizes-allowlisted-agent-ids.e2e.test.ts b/src/agents/openclaw-tools.subagents.sessions-spawn-normalizes-allowlisted-agent-ids.e2e.test.ts deleted file mode 100644 index dff85562695..00000000000 --- a/src/agents/openclaw-tools.subagents.sessions-spawn-normalizes-allowlisted-agent-ids.e2e.test.ts +++ /dev/null @@ -1,377 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { emitAgentEvent } from "../infra/agent-events.js"; -import { createOpenClawTools } from "./openclaw-tools.js"; -import "./test-helpers/fast-core-tools.js"; -import { resetSubagentRegistryForTests } from "./subagent-registry.js"; - -type SessionsSpawnTestConfig = ReturnType<(typeof import("../config/config.js"))["loadConfig"]>; - -const hoisted = vi.hoisted(() => { - const callGatewayMock = vi.fn(); - const defaultConfigOverride = { - session: { - mainKey: "main", - scope: "per-sender", - }, - } as SessionsSpawnTestConfig; - const state = { configOverride: defaultConfigOverride }; - return { callGatewayMock, defaultConfigOverride, state }; -}); - -const callGatewayMock = hoisted.callGatewayMock; - -function resetConfigOverride() { - hoisted.state.configOverride = hoisted.defaultConfigOverride; -} - -function setConfigOverride(next: SessionsSpawnTestConfig) { - hoisted.state.configOverride = next; -} - -vi.mock("../gateway/call.js", () => ({ - callGateway: (opts: unknown) => hoisted.callGatewayMock(opts), -})); -// Some tools import callGateway via "../../gateway/call.js" (from nested folders). Mock that too. -vi.mock("../../gateway/call.js", () => ({ - callGateway: (opts: unknown) => hoisted.callGatewayMock(opts), -})); - -vi.mock("../config/config.js", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - loadConfig: () => hoisted.state.configOverride, - resolveGatewayPort: () => 18789, - }; -}); - -// Same module, different specifier (used by tools under src/agents/tools/*). -vi.mock("../../config/config.js", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - loadConfig: () => hoisted.state.configOverride, - resolveGatewayPort: () => 18789, - }; -}); - -describe("openclaw-tools: subagents", () => { - beforeEach(() => { - resetConfigOverride(); - }); - - it("sessions_spawn normalizes allowlisted agent ids", async () => { - resetSubagentRegistryForTests(); - callGatewayMock.mockReset(); - setConfigOverride({ - session: { - mainKey: "main", - scope: "per-sender", - }, - agents: { - list: [ - { - id: "main", - subagents: { - allowAgents: ["Research"], - }, - }, - ], - }, - }); - - let childSessionKey: string | undefined; - callGatewayMock.mockImplementation(async (opts: unknown) => { - const request = opts as { method?: string; params?: unknown }; - if (request.method === "agent") { - const params = request.params as { sessionKey?: string } | undefined; - childSessionKey = params?.sessionKey; - return { runId: "run-1", status: "accepted", acceptedAt: 5200 }; - } - if (request.method === "agent.wait") { - return { status: "timeout" }; - } - return {}; - }); - - const tool = createOpenClawTools({ - agentSessionKey: "main", - agentChannel: "whatsapp", - }).find((candidate) => candidate.name === "sessions_spawn"); - if (!tool) { - throw new Error("missing sessions_spawn tool"); - } - - const result = await tool.execute("call10", { - task: "do thing", - agentId: "research", - }); - - expect(result.details).toMatchObject({ - status: "accepted", - runId: "run-1", - }); - expect(childSessionKey?.startsWith("agent:research:subagent:")).toBe(true); - }); - it("sessions_spawn forbids cross-agent spawning when not allowed", async () => { - resetSubagentRegistryForTests(); - callGatewayMock.mockReset(); - setConfigOverride({ - session: { - mainKey: "main", - scope: "per-sender", - }, - agents: { - list: [ - { - id: "main", - subagents: { - allowAgents: ["alpha"], - }, - }, - ], - }, - }); - - const tool = createOpenClawTools({ - agentSessionKey: "main", - agentChannel: "whatsapp", - }).find((candidate) => candidate.name === "sessions_spawn"); - if (!tool) { - throw new Error("missing sessions_spawn tool"); - } - - const result = await tool.execute("call9", { - task: "do thing", - agentId: "beta", - }); - expect(result.details).toMatchObject({ - status: "forbidden", - }); - expect(callGatewayMock).not.toHaveBeenCalled(); - }); - - it("sessions_spawn runs cleanup via lifecycle events", async () => { - resetSubagentRegistryForTests(); - callGatewayMock.mockReset(); - const calls: Array<{ method?: string; params?: unknown }> = []; - let agentCallCount = 0; - let deletedKey: string | undefined; - let childRunId: string | undefined; - let childSessionKey: string | undefined; - const waitCalls: Array<{ runId?: string; timeoutMs?: number }> = []; - - callGatewayMock.mockImplementation(async (opts: unknown) => { - const request = opts as { method?: string; params?: unknown }; - calls.push(request); - if (request.method === "agent") { - agentCallCount += 1; - const runId = `run-${agentCallCount}`; - const params = request.params as { - message?: string; - sessionKey?: string; - channel?: string; - timeout?: number; - lane?: string; - }; - if (params?.lane === "subagent") { - childRunId = runId; - childSessionKey = params?.sessionKey ?? ""; - expect(params?.channel).toBe("discord"); - expect(params?.timeout).toBe(1); - } - return { - runId, - status: "accepted", - acceptedAt: 1000 + agentCallCount, - }; - } - if (request.method === "agent.wait") { - const params = request.params as { runId?: string; timeoutMs?: number } | undefined; - waitCalls.push(params ?? {}); - return { - runId: params?.runId ?? "run-1", - status: "ok", - startedAt: 1000, - endedAt: 2000, - }; - } - if (request.method === "sessions.delete") { - const params = request.params as { key?: string } | undefined; - deletedKey = params?.key; - return { ok: true }; - } - return {}; - }); - - const tool = createOpenClawTools({ - agentSessionKey: "discord:group:req", - agentChannel: "discord", - }).find((candidate) => candidate.name === "sessions_spawn"); - if (!tool) { - throw new Error("missing sessions_spawn tool"); - } - - const result = await tool.execute("call1", { - task: "do thing", - runTimeoutSeconds: 1, - cleanup: "delete", - }); - expect(result.details).toMatchObject({ - status: "accepted", - runId: "run-1", - }); - - if (!childRunId) { - throw new Error("missing child runId"); - } - vi.useFakeTimers(); - try { - emitAgentEvent({ - runId: childRunId, - stream: "lifecycle", - data: { - phase: "end", - startedAt: 1234, - endedAt: 2345, - }, - }); - - await vi.runAllTimersAsync(); - } finally { - vi.useRealTimers(); - } - - const childWait = waitCalls.find((call) => call.runId === childRunId); - expect(childWait?.timeoutMs).toBe(1000); - - const agentCalls = calls.filter((call) => call.method === "agent"); - const spawnCalls = agentCalls.filter((call) => { - const params = call.params as { lane?: string } | undefined; - return params?.lane === "subagent"; - }); - expect(spawnCalls).toHaveLength(1); - const announceCalls = agentCalls.filter((call) => { - const params = call.params as { sessionKey?: string; deliver?: boolean } | undefined; - return params?.deliver === true && params?.sessionKey === "discord:group:req"; - }); - expect(announceCalls).toHaveLength(1); - - const first = spawnCalls[0]?.params as - | { - lane?: string; - deliver?: boolean; - sessionKey?: string; - channel?: string; - } - | undefined; - expect(first?.lane).toBe("subagent"); - expect(first?.deliver).toBe(false); - expect(first?.channel).toBe("discord"); - expect(first?.sessionKey?.startsWith("agent:main:subagent:")).toBe(true); - expect(childSessionKey?.startsWith("agent:main:subagent:")).toBe(true); - - const second = announceCalls[0]?.params as - | { - sessionKey?: string; - message?: string; - deliver?: boolean; - } - | undefined; - expect(second?.sessionKey).toBe("discord:group:req"); - expect(second?.deliver).toBe(true); - expect(second?.message).toContain("subagent task"); - - const sendCalls = calls.filter((c) => c.method === "send"); - expect(sendCalls.length).toBe(0); - - expect(deletedKey?.startsWith("agent:main:subagent:")).toBe(true); - }); - - it("sessions_spawn announces with requester accountId", async () => { - resetSubagentRegistryForTests(); - callGatewayMock.mockReset(); - const calls: Array<{ method?: string; params?: unknown }> = []; - let agentCallCount = 0; - let childRunId: string | undefined; - - callGatewayMock.mockImplementation(async (opts: unknown) => { - const request = opts as { method?: string; params?: unknown }; - calls.push(request); - if (request.method === "agent") { - agentCallCount += 1; - const runId = `run-${agentCallCount}`; - const params = request.params as { lane?: string; sessionKey?: string } | undefined; - if (params?.lane === "subagent") { - childRunId = runId; - } - return { - runId, - status: "accepted", - acceptedAt: 4000 + agentCallCount, - }; - } - if (request.method === "agent.wait") { - const params = request.params as { runId?: string; timeoutMs?: number } | undefined; - return { - runId: params?.runId ?? "run-1", - status: "ok", - startedAt: 1000, - endedAt: 2000, - }; - } - if (request.method === "sessions.delete" || request.method === "sessions.patch") { - return { ok: true }; - } - return {}; - }); - - const tool = createOpenClawTools({ - agentSessionKey: "main", - agentChannel: "whatsapp", - agentAccountId: "kev", - }).find((candidate) => candidate.name === "sessions_spawn"); - if (!tool) { - throw new Error("missing sessions_spawn tool"); - } - - const result = await tool.execute("call2", { - task: "do thing", - runTimeoutSeconds: 1, - cleanup: "keep", - }); - expect(result.details).toMatchObject({ - status: "accepted", - runId: "run-1", - }); - - if (!childRunId) { - throw new Error("missing child runId"); - } - vi.useFakeTimers(); - try { - emitAgentEvent({ - runId: childRunId, - stream: "lifecycle", - data: { - phase: "end", - startedAt: 1000, - endedAt: 2000, - }, - }); - - await vi.runAllTimersAsync(); - } finally { - vi.useRealTimers(); - } - - const agentCalls = calls.filter((call) => call.method === "agent"); - expect(agentCalls).toHaveLength(2); - const announceParams = agentCalls[1]?.params as - | { accountId?: string; channel?: string; deliver?: boolean } - | undefined; - expect(announceParams?.deliver).toBe(true); - expect(announceParams?.channel).toBe("whatsapp"); - expect(announceParams?.accountId).toBe("kev"); - }); -});