From 116f5afea3e7f0860b420e37cd2f0e94a630aceb Mon Sep 17 00:00:00 2001 From: cpojer Date: Tue, 17 Feb 2026 14:33:26 +0900 Subject: [PATCH] chore: Fix types in tests 31/N. --- ...nition-adapter.after-tool-call.e2e.test.ts | 36 +++++++++---- .../pi-tool-definition-adapter.e2e.test.ts | 30 ++++++++--- .../pi-tools.before-tool-call.e2e.test.ts | 53 ++++++++++++++----- .../skills-install.download.e2e.test.ts | 10 ++-- src/agents/skills/refresh.test.ts | 7 ++- .../subagent-registry.persistence.e2e.test.ts | 9 +++- .../subagent-registry.steer-restart.test.ts | 8 +-- src/agents/tools/image-tool.e2e.test.ts | 19 +++++-- src/agents/tools/memory-tool.e2e.test.ts | 27 +++++++--- src/agents/tools/sessions-access.test.ts | 24 ++++----- src/agents/tools/web-fetch.ssrf.e2e.test.ts | 4 +- src/agents/tools/web-tools.fetch.e2e.test.ts | 43 ++++++++------- 12 files changed, 185 insertions(+), 85 deletions(-) diff --git a/src/agents/pi-tool-definition-adapter.after-tool-call.e2e.test.ts b/src/agents/pi-tool-definition-adapter.after-tool-call.e2e.test.ts index accaa05fa88..9d54b7de4cf 100644 --- a/src/agents/pi-tool-definition-adapter.after-tool-call.e2e.test.ts +++ b/src/agents/pi-tool-definition-adapter.after-tool-call.e2e.test.ts @@ -1,14 +1,15 @@ import type { AgentTool } from "@mariozechner/pi-agent-core"; +import { Type } from "@sinclair/typebox"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { toToolDefinitions } from "./pi-tool-definition-adapter.js"; const hookMocks = vi.hoisted(() => ({ runner: { - hasHooks: vi.fn(() => false), + hasHooks: vi.fn((_: string) => false), runAfterToolCall: vi.fn(async () => {}), }, isToolWrappedWithBeforeToolCallHook: vi.fn(() => false), - consumeAdjustedParamsForToolCall: vi.fn(() => undefined), + consumeAdjustedParamsForToolCall: vi.fn((_: string) => undefined as unknown), runBeforeToolCallHook: vi.fn(async ({ params }: { params: unknown }) => ({ blocked: false, params, @@ -30,18 +31,28 @@ function createReadTool() { name: "read", label: "Read", description: "reads", - parameters: {}, + parameters: Type.Object({}), execute: vi.fn(async () => ({ content: [], details: { ok: true } })), - } satisfies AgentTool; + } satisfies AgentTool; } +type ToolExecute = ReturnType[number]["execute"]; +const extensionContext = {} as Parameters[3]; + function enableAfterToolCallHook() { hookMocks.runner.hasHooks.mockImplementation((name: string) => name === "after_tool_call"); } async function executeReadTool(callId: string) { const defs = toToolDefinitions([createReadTool()]); - return await defs[0].execute(callId, { path: "/tmp/file" }, undefined, undefined); + const args: Parameters<(typeof defs)[number]["execute"]> = [ + callId, + { path: "/tmp/file" }, + undefined, + extensionContext, + undefined, + ]; + return await defs[0].execute(...args); } function expectReadAfterToolCallPayload(result: Awaited>) { @@ -87,7 +98,7 @@ describe("pi tool definition adapter after_tool_call", () => { it("uses wrapped-tool adjusted params for after_tool_call payload", async () => { enableAfterToolCallHook(); hookMocks.isToolWrappedWithBeforeToolCallHook.mockReturnValue(true); - hookMocks.consumeAdjustedParamsForToolCall.mockReturnValue({ mode: "safe" }); + hookMocks.consumeAdjustedParamsForToolCall.mockReturnValue({ mode: "safe" } as unknown); const result = await executeReadTool("call-ok-wrapped"); expect(result.details).toMatchObject({ ok: true }); @@ -101,14 +112,21 @@ describe("pi tool definition adapter after_tool_call", () => { name: "bash", label: "Bash", description: "throws", - parameters: {}, + parameters: Type.Object({}), execute: vi.fn(async () => { throw new Error("boom"); }), - } satisfies AgentTool; + } satisfies AgentTool; const defs = toToolDefinitions([tool]); - const result = await defs[0].execute("call-err", { cmd: "ls" }, undefined, undefined); + const args: Parameters<(typeof defs)[number]["execute"]> = [ + "call-err", + { cmd: "ls" }, + undefined, + extensionContext, + undefined, + ]; + const result = await defs[0].execute(...args); expect(result.details).toMatchObject({ status: "error", diff --git a/src/agents/pi-tool-definition-adapter.e2e.test.ts b/src/agents/pi-tool-definition-adapter.e2e.test.ts index e54ec613a9f..362e9fbca04 100644 --- a/src/agents/pi-tool-definition-adapter.e2e.test.ts +++ b/src/agents/pi-tool-definition-adapter.e2e.test.ts @@ -1,21 +1,32 @@ import type { AgentTool } from "@mariozechner/pi-agent-core"; +import { Type } from "@sinclair/typebox"; import { describe, expect, it } from "vitest"; import { toToolDefinitions } from "./pi-tool-definition-adapter.js"; +type ToolExecute = ReturnType[number]["execute"]; +const extensionContext = {} as Parameters[3]; + describe("pi tool definition adapter", () => { it("wraps tool errors into a tool result", async () => { const tool = { name: "boom", label: "Boom", description: "throws", - parameters: {}, + parameters: Type.Object({}), execute: async () => { throw new Error("nope"); }, - } satisfies AgentTool; + } satisfies AgentTool; const defs = toToolDefinitions([tool]); - const result = await defs[0].execute("call1", {}, undefined, undefined); + const args: Parameters<(typeof defs)[number]["execute"]> = [ + "call1", + {}, + undefined, + extensionContext, + undefined, + ]; + const result = await defs[0].execute(...args); expect(result.details).toMatchObject({ status: "error", @@ -30,14 +41,21 @@ describe("pi tool definition adapter", () => { name: "bash", label: "Bash", description: "throws", - parameters: {}, + parameters: Type.Object({}), execute: async () => { throw new Error("nope"); }, - } satisfies AgentTool; + } satisfies AgentTool; const defs = toToolDefinitions([tool]); - const result = await defs[0].execute("call2", {}, undefined, undefined); + const args: Parameters<(typeof defs)[number]["execute"]> = [ + "call2", + {}, + undefined, + extensionContext, + undefined, + ]; + const result = await defs[0].execute(...args); expect(result.details).toMatchObject({ status: "error", diff --git a/src/agents/pi-tools.before-tool-call.e2e.test.ts b/src/agents/pi-tools.before-tool-call.e2e.test.ts index 20145cb2af5..e364d1f9a9c 100644 --- a/src/agents/pi-tools.before-tool-call.e2e.test.ts +++ b/src/agents/pi-tools.before-tool-call.e2e.test.ts @@ -32,11 +32,17 @@ describe("before_tool_call hook integration", () => { agentId: "main", sessionKey: "main", }); + const extensionContext = {} as Parameters[3]; - await tool.execute("call-1", { path: "/tmp/file" }, undefined, undefined); + await tool.execute("call-1", { path: "/tmp/file" }, undefined, extensionContext); expect(hookRunner.runBeforeToolCall).not.toHaveBeenCalled(); - expect(execute).toHaveBeenCalledWith("call-1", { path: "/tmp/file" }, undefined, undefined); + expect(execute).toHaveBeenCalledWith( + "call-1", + { path: "/tmp/file" }, + undefined, + extensionContext, + ); }); it("allows hook to modify parameters", async () => { @@ -45,14 +51,15 @@ describe("before_tool_call hook integration", () => { const execute = vi.fn().mockResolvedValue({ content: [], details: { ok: true } }); // oxlint-disable-next-line typescript/no-explicit-any const tool = wrapToolWithBeforeToolCallHook({ name: "exec", execute } as any); + const extensionContext = {} as Parameters[3]; - await tool.execute("call-2", { cmd: "ls" }, undefined, undefined); + await tool.execute("call-2", { cmd: "ls" }, undefined, extensionContext); expect(execute).toHaveBeenCalledWith( "call-2", { cmd: "ls", mode: "safe" }, undefined, - undefined, + extensionContext, ); }); @@ -65,10 +72,11 @@ describe("before_tool_call hook integration", () => { const execute = vi.fn().mockResolvedValue({ content: [], details: { ok: true } }); // oxlint-disable-next-line typescript/no-explicit-any const tool = wrapToolWithBeforeToolCallHook({ name: "exec", execute } as any); + const extensionContext = {} as Parameters[3]; - await expect(tool.execute("call-3", { cmd: "rm -rf /" }, undefined, undefined)).rejects.toThrow( - "blocked", - ); + await expect( + tool.execute("call-3", { cmd: "rm -rf /" }, undefined, extensionContext), + ).rejects.toThrow("blocked"); expect(execute).not.toHaveBeenCalled(); }); @@ -78,10 +86,16 @@ describe("before_tool_call hook integration", () => { const execute = vi.fn().mockResolvedValue({ content: [], details: { ok: true } }); // oxlint-disable-next-line typescript/no-explicit-any const tool = wrapToolWithBeforeToolCallHook({ name: "read", execute } as any); + const extensionContext = {} as Parameters[3]; - await tool.execute("call-4", { path: "/tmp/file" }, undefined, undefined); + await tool.execute("call-4", { path: "/tmp/file" }, undefined, extensionContext); - expect(execute).toHaveBeenCalledWith("call-4", { path: "/tmp/file" }, undefined, undefined); + expect(execute).toHaveBeenCalledWith( + "call-4", + { path: "/tmp/file" }, + undefined, + extensionContext, + ); }); it("normalizes non-object params for hook contract", async () => { @@ -93,8 +107,9 @@ describe("before_tool_call hook integration", () => { agentId: "main", sessionKey: "main", }); + const extensionContext = {} as Parameters[3]; - await tool.execute("call-5", "not-an-object", undefined, undefined); + await tool.execute("call-5", "not-an-object", undefined, extensionContext); expect(hookRunner.runBeforeToolCall).toHaveBeenCalledWith( { @@ -136,14 +151,16 @@ describe("before_tool_call hook deduplication (#15502)", () => { sessionKey: "main", }); const [def] = toToolDefinitions([wrapped]); + const extensionContext = {} as Parameters[3]; - await def.execute( + const args: Parameters = [ "call-dedup", { url: "https://example.com" }, undefined, + extensionContext, undefined, - undefined, - ); + ]; + await def.execute(...args); expect(hookRunner.runBeforeToolCall).toHaveBeenCalledTimes(1); }); @@ -183,8 +200,16 @@ describe("before_tool_call hook integration for client tools", () => { onClientToolCall, { agentId: "main", sessionKey: "main" }, ); + const extensionContext = {} as Parameters[3]; - await tool.execute("client-call-1", { value: "ok" }, undefined, undefined, undefined); + const args: Parameters = [ + "client-call-1", + { value: "ok" }, + undefined, + extensionContext, + undefined, + ]; + await tool.execute(...args); expect(onClientToolCall).toHaveBeenCalledWith("client_tool", { value: "ok", diff --git a/src/agents/skills-install.download.e2e.test.ts b/src/agents/skills-install.download.e2e.test.ts index d80b39dea8d..7e234610708 100644 --- a/src/agents/skills-install.download.e2e.test.ts +++ b/src/agents/skills-install.download.e2e.test.ts @@ -51,7 +51,7 @@ async function seedZipDownloadResponse() { zip.file("hello.txt", "hi"); const buffer = await zip.generateAsync({ type: "nodebuffer" }); fetchWithSsrFGuardMock.mockResolvedValue({ - response: new Response(buffer, { status: 200 }), + response: new Response(new Uint8Array(buffer), { status: 200 }), release: async () => undefined, }); } @@ -107,7 +107,7 @@ describe("installSkill download extraction safety", () => { const buffer = await zip.generateAsync({ type: "nodebuffer" }); fetchWithSsrFGuardMock.mockResolvedValue({ - response: new Response(buffer, { status: 200 }), + response: new Response(new Uint8Array(buffer), { status: 200 }), release: async () => undefined, }); @@ -150,7 +150,7 @@ describe("installSkill download extraction safety", () => { const buffer = await fs.readFile(archivePath); fetchWithSsrFGuardMock.mockResolvedValue({ - response: new Response(buffer, { status: 200 }), + response: new Response(new Uint8Array(buffer), { status: 200 }), release: async () => undefined, }); @@ -182,7 +182,7 @@ describe("installSkill download extraction safety", () => { zip.file("package/hello.txt", "hi"); const buffer = await zip.generateAsync({ type: "nodebuffer" }); fetchWithSsrFGuardMock.mockResolvedValue({ - response: new Response(buffer, { status: 200 }), + response: new Response(new Uint8Array(buffer), { status: 200 }), release: async () => undefined, }); @@ -215,7 +215,7 @@ describe("installSkill download extraction safety", () => { zip.file("hello.txt", "hi"); const buffer = await zip.generateAsync({ type: "nodebuffer" }); fetchWithSsrFGuardMock.mockResolvedValue({ - response: new Response(buffer, { status: 200 }), + response: new Response(new Uint8Array(buffer), { status: 200 }), release: async () => undefined, }); diff --git a/src/agents/skills/refresh.test.ts b/src/agents/skills/refresh.test.ts index 64701c3ec28..2108674eb54 100644 --- a/src/agents/skills/refresh.test.ts +++ b/src/agents/skills/refresh.test.ts @@ -19,8 +19,11 @@ describe("ensureSkillsWatcher", () => { mod.ensureSkillsWatcher({ workspaceDir: "/tmp/workspace" }); expect(watchMock).toHaveBeenCalledTimes(1); - const targets = watchMock.mock.calls[0]?.[0] as string[]; - const opts = watchMock.mock.calls[0]?.[1] as { ignored?: unknown }; + const firstCall = ( + watchMock.mock.calls as unknown as Array<[string[], { ignored?: unknown }]> + )[0]; + const targets = firstCall?.[0] ?? []; + const opts = firstCall?.[1] ?? {}; expect(opts.ignored).toBe(mod.DEFAULT_SKILLS_WATCH_IGNORED); const posix = (p: string) => p.replaceAll("\\", "/"); diff --git a/src/agents/subagent-registry.persistence.e2e.test.ts b/src/agents/subagent-registry.persistence.e2e.test.ts index d6fafbcfb1d..d23344340be 100644 --- a/src/agents/subagent-registry.persistence.e2e.test.ts +++ b/src/agents/subagent-registry.persistence.e2e.test.ts @@ -130,7 +130,12 @@ describe("subagent registry persistence", () => { cleanup: string; label?: string; }; - const first = announceSpy.mock.calls[0]?.[0] as unknown as AnnounceParams; + const first = (announceSpy.mock.calls as unknown as Array<[unknown]>)[0]?.[0] as + | AnnounceParams + | undefined; + if (!first) { + throw new Error("expected announce call"); + } expect(first.childSessionKey).toBe("agent:main:subagent:test"); expect(first.requesterOrigin?.channel).toBe("whatsapp"); expect(first.requesterOrigin?.accountId).toBe("acct-main"); @@ -167,7 +172,7 @@ describe("subagent registry persistence", () => { await new Promise((r) => setTimeout(r, 0)); // announce should NOT be called since cleanupHandled was true - const calls = announceSpy.mock.calls.map((call) => call[0]); + const calls = (announceSpy.mock.calls as unknown as Array<[unknown]>).map((call) => call[0]); const match = calls.find( (params) => (params as { childSessionKey?: string }).childSessionKey === "agent:main:subagent:two", diff --git a/src/agents/subagent-registry.steer-restart.test.ts b/src/agents/subagent-registry.steer-restart.test.ts index 4e036efe6cf..c61fdd17978 100644 --- a/src/agents/subagent-registry.steer-restart.test.ts +++ b/src/agents/subagent-registry.steer-restart.test.ts @@ -28,7 +28,7 @@ vi.mock("../config/config.js", () => ({ })), })); -const announceSpy = vi.fn(async () => true); +const announceSpy = vi.fn(async (_params: unknown) => true); vi.mock("./subagent-announce.js", () => ({ runSubagentAnnounceFlow: announceSpy, })); @@ -101,7 +101,7 @@ describe("subagent registry steer restarts", () => { await flushAnnounce(); expect(announceSpy).toHaveBeenCalledTimes(1); - const announce = announceSpy.mock.calls[0]?.[0] as { childRunId?: string }; + const announce = (announceSpy.mock.calls[0]?.[0] ?? {}) as { childRunId?: string }; expect(announce.childRunId).toBe("run-new"); }); @@ -161,7 +161,7 @@ describe("subagent registry steer restarts", () => { await flushAnnounce(); expect(announceSpy).toHaveBeenCalledTimes(1); - const announce = announceSpy.mock.calls[0]?.[0] as { childRunId?: string }; + const announce = (announceSpy.mock.calls[0]?.[0] ?? {}) as { childRunId?: string }; expect(announce.childRunId).toBe("run-failed-restart"); }); @@ -234,7 +234,7 @@ describe("subagent registry steer restarts", () => { await flushAnnounce(); const childRunIds = announceSpy.mock.calls.map( - (call) => (call[0] as { childRunId?: string }).childRunId, + (call) => ((call[0] ?? {}) as { childRunId?: string }).childRunId, ); expect(childRunIds.filter((id) => id === "run-parent")).toHaveLength(2); expect(childRunIds.filter((id) => id === "run-child")).toHaveLength(1); diff --git a/src/agents/tools/image-tool.e2e.test.ts b/src/agents/tools/image-tool.e2e.test.ts index 5d4142bf427..cd2370d0f8d 100644 --- a/src/agents/tools/image-tool.e2e.test.ts +++ b/src/agents/tools/image-tool.e2e.test.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; +import type { ModelDefinitionConfig } from "../../config/types.models.js"; import { createOpenClawCodingTools } from "../pi-tools.js"; import { createHostSandboxFsBridge } from "../test-helpers/host-sandbox-fs-bridge.js"; import { __testing, createImageTool, resolveImageModelConfigForTool } from "./image-tool.js"; @@ -62,6 +63,18 @@ function createMinimaxImageConfig(): OpenClawConfig { }; } +function makeModelDefinition(id: string, input: Array<"text" | "image">): ModelDefinitionConfig { + return { + id, + name: id, + reasoning: false, + input, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128_000, + maxTokens: 8_192, + }; +} + async function expectImageToolExecOk( tool: { execute: (toolCallId: string, input: { prompt: string; image: string }) => Promise; @@ -171,8 +184,8 @@ describe("image tool implicit imageModel config", () => { providers: { acme: { models: [ - { id: "text-1", input: ["text"] }, - { id: "vision-1", input: ["text", "image"] }, + makeModelDefinition("text-1", ["text"]), + makeModelDefinition("vision-1", ["text", "image"]), ], }, }, @@ -215,7 +228,7 @@ describe("image tool implicit imageModel config", () => { models: { providers: { acme: { - models: [{ id: "vision-1", input: ["text", "image"] }], + models: [makeModelDefinition("vision-1", ["text", "image"])], }, }, }, diff --git a/src/agents/tools/memory-tool.e2e.test.ts b/src/agents/tools/memory-tool.e2e.test.ts index 38e2caab24d..6ebb3e92ef9 100644 --- a/src/agents/tools/memory-tool.e2e.test.ts +++ b/src/agents/tools/memory-tool.e2e.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { OpenClawConfig } from "../../config/config.js"; let backend: "builtin" | "qmd" = "builtin"; let searchImpl: () => Promise = async () => [ @@ -42,6 +43,10 @@ vi.mock("../../memory/index.js", () => { import { createMemoryGetTool, createMemorySearchTool } from "./memory-tool.js"; +function asOpenClawConfig(config: Partial): OpenClawConfig { + return config as OpenClawConfig; +} + beforeEach(() => { backend = "builtin"; searchImpl = async () => [ @@ -61,7 +66,10 @@ beforeEach(() => { describe("memory search citations", () => { it("appends source information when citations are enabled", async () => { backend = "builtin"; - const cfg = { memory: { citations: "on" }, agents: { list: [{ id: "main", default: true }] } }; + const cfg = asOpenClawConfig({ + memory: { citations: "on" }, + agents: { list: [{ id: "main", default: true }] }, + }); const tool = createMemorySearchTool({ config: cfg }); if (!tool) { throw new Error("tool missing"); @@ -74,7 +82,10 @@ describe("memory search citations", () => { it("leaves snippet untouched when citations are off", async () => { backend = "builtin"; - const cfg = { memory: { citations: "off" }, agents: { list: [{ id: "main", default: true }] } }; + const cfg = asOpenClawConfig({ + memory: { citations: "off" }, + agents: { list: [{ id: "main", default: true }] }, + }); const tool = createMemorySearchTool({ config: cfg }); if (!tool) { throw new Error("tool missing"); @@ -87,10 +98,10 @@ describe("memory search citations", () => { it("clamps decorated snippets to qmd injected budget", async () => { backend = "qmd"; - const cfg = { + const cfg = asOpenClawConfig({ memory: { citations: "on", backend: "qmd", qmd: { limits: { maxInjectedChars: 20 } } }, agents: { list: [{ id: "main", default: true }] }, - }; + }); const tool = createMemorySearchTool({ config: cfg }); if (!tool) { throw new Error("tool missing"); @@ -102,10 +113,10 @@ describe("memory search citations", () => { it("honors auto mode for direct chats", async () => { backend = "builtin"; - const cfg = { + const cfg = asOpenClawConfig({ memory: { citations: "auto" }, agents: { list: [{ id: "main", default: true }] }, - }; + }); const tool = createMemorySearchTool({ config: cfg, agentSessionKey: "agent:main:discord:dm:u123", @@ -120,10 +131,10 @@ describe("memory search citations", () => { it("suppresses citations for auto mode in group chats", async () => { backend = "builtin"; - const cfg = { + const cfg = asOpenClawConfig({ memory: { citations: "auto" }, agents: { list: [{ id: "main", default: true }] }, - }; + }); const tool = createMemorySearchTool({ config: cfg, agentSessionKey: "agent:main:discord:group:c123", diff --git a/src/agents/tools/sessions-access.test.ts b/src/agents/tools/sessions-access.test.ts index 0f18191b5b9..b563d75fc9d 100644 --- a/src/agents/tools/sessions-access.test.ts +++ b/src/agents/tools/sessions-access.test.ts @@ -11,11 +11,11 @@ import { describe("resolveSessionToolsVisibility", () => { it("defaults to tree when unset or invalid", () => { - expect(resolveSessionToolsVisibility({} as OpenClawConfig)).toBe("tree"); + expect(resolveSessionToolsVisibility({} as unknown as OpenClawConfig)).toBe("tree"); expect( resolveSessionToolsVisibility({ tools: { sessions: { visibility: "invalid" } }, - } as OpenClawConfig), + } as unknown as OpenClawConfig), ).toBe("tree"); }); @@ -23,7 +23,7 @@ describe("resolveSessionToolsVisibility", () => { expect( resolveSessionToolsVisibility({ tools: { sessions: { visibility: "ALL" } }, - } as OpenClawConfig), + } as unknown as OpenClawConfig), ).toBe("all"); }); }); @@ -33,7 +33,7 @@ describe("resolveEffectiveSessionToolsVisibility", () => { const cfg = { tools: { sessions: { visibility: "all" } }, agents: { defaults: { sandbox: { sessionToolsVisibility: "spawned" } } }, - } as OpenClawConfig; + } as unknown as OpenClawConfig; expect(resolveEffectiveSessionToolsVisibility({ cfg, sandboxed: true })).toBe("tree"); }); @@ -41,21 +41,21 @@ describe("resolveEffectiveSessionToolsVisibility", () => { const cfg = { tools: { sessions: { visibility: "all" } }, agents: { defaults: { sandbox: { sessionToolsVisibility: "all" } } }, - } as OpenClawConfig; + } as unknown as OpenClawConfig; expect(resolveEffectiveSessionToolsVisibility({ cfg, sandboxed: true })).toBe("all"); }); }); describe("sandbox session-tools context", () => { it("defaults sandbox visibility clamp to spawned", () => { - expect(resolveSandboxSessionToolsVisibility({} as OpenClawConfig)).toBe("spawned"); + expect(resolveSandboxSessionToolsVisibility({} as unknown as OpenClawConfig)).toBe("spawned"); }); it("restricts non-subagent sandboxed sessions to spawned visibility", () => { const cfg = { tools: { sessions: { visibility: "all" } }, agents: { defaults: { sandbox: { sessionToolsVisibility: "spawned" } } }, - } as OpenClawConfig; + } as unknown as OpenClawConfig; const context = resolveSandboxedSessionToolContext({ cfg, agentSessionKey: "agent:main:main", @@ -71,7 +71,7 @@ describe("sandbox session-tools context", () => { const cfg = { tools: { sessions: { visibility: "all" } }, agents: { defaults: { sandbox: { sessionToolsVisibility: "spawned" } } }, - } as OpenClawConfig; + } as unknown as OpenClawConfig; const context = resolveSandboxedSessionToolContext({ cfg, agentSessionKey: "agent:main:subagent:abc", @@ -85,7 +85,7 @@ describe("sandbox session-tools context", () => { describe("createAgentToAgentPolicy", () => { it("denies cross-agent access when disabled", () => { - const policy = createAgentToAgentPolicy({} as OpenClawConfig); + const policy = createAgentToAgentPolicy({} as unknown as OpenClawConfig); expect(policy.enabled).toBe(false); expect(policy.isAllowed("main", "main")).toBe(true); expect(policy.isAllowed("main", "ops")).toBe(false); @@ -99,7 +99,7 @@ describe("createAgentToAgentPolicy", () => { allow: ["ops-*", "main"], }, }, - } as OpenClawConfig); + } as unknown as OpenClawConfig); expect(policy.isAllowed("ops-a", "ops-b")).toBe(true); expect(policy.isAllowed("main", "ops-a")).toBe(true); @@ -113,7 +113,7 @@ describe("createSessionVisibilityGuard", () => { action: "send", requesterSessionKey: "agent:main:main", visibility: "all", - a2aPolicy: createAgentToAgentPolicy({} as OpenClawConfig), + a2aPolicy: createAgentToAgentPolicy({} as unknown as OpenClawConfig), }); expect(guard.check("agent:ops:main")).toEqual({ @@ -129,7 +129,7 @@ describe("createSessionVisibilityGuard", () => { action: "history", requesterSessionKey: "agent:main:main", visibility: "self", - a2aPolicy: createAgentToAgentPolicy({} as OpenClawConfig), + a2aPolicy: createAgentToAgentPolicy({} as unknown as OpenClawConfig), }); expect(guard.check("agent:main:main")).toEqual({ allowed: true }); diff --git a/src/agents/tools/web-fetch.ssrf.e2e.test.ts b/src/agents/tools/web-fetch.ssrf.e2e.test.ts index 6c259c9ad58..db6c031f7b4 100644 --- a/src/agents/tools/web-fetch.ssrf.e2e.test.ts +++ b/src/agents/tools/web-fetch.ssrf.e2e.test.ts @@ -16,7 +16,7 @@ function redirectResponse(location: string): Response { status: 302, headers: makeHeaders({ location }), body: { cancel: vi.fn() }, - } as Response; + } as unknown as Response; } function textResponse(body: string): Response { @@ -25,7 +25,7 @@ function textResponse(body: string): Response { status: 200, headers: makeHeaders({ "content-type": "text/plain" }), text: async () => body, - } as Response; + } as unknown as Response; } function setMockFetch(impl?: (...args: unknown[]) => unknown) { diff --git a/src/agents/tools/web-tools.fetch.e2e.test.ts b/src/agents/tools/web-tools.fetch.e2e.test.ts index 0339a500853..1ef58bf4abe 100644 --- a/src/agents/tools/web-tools.fetch.e2e.test.ts +++ b/src/agents/tools/web-tools.fetch.e2e.test.ts @@ -77,7 +77,7 @@ function errorHtmlResponse( text: async () => html, }; } -function requestUrl(input: RequestInfo): string { +function requestUrl(input: RequestInfo | URL): string { if (typeof input === "string") { return input; } @@ -90,9 +90,9 @@ function requestUrl(input: RequestInfo): string { return ""; } -function installMockFetch(impl: (input: RequestInfo) => Promise) { - const mockFetch = vi.fn(impl); - global.fetch = mockFetch; +function installMockFetch(impl: (input: RequestInfo | URL) => Promise) { + const mockFetch = vi.fn(async (input: RequestInfo | URL) => await impl(input)); + global.fetch = mockFetch as typeof global.fetch; return mockFetch; } @@ -145,7 +145,7 @@ describe("web_fetch extraction fallbacks", () => { }); it("wraps fetched text with external content markers", async () => { - installMockFetch((input: RequestInfo) => + installMockFetch((input: RequestInfo | URL) => Promise.resolve({ ok: true, status: 200, @@ -183,7 +183,7 @@ describe("web_fetch extraction fallbacks", () => { it("enforces maxChars after wrapping", async () => { const longText = "x".repeat(5_000); - installMockFetch((input: RequestInfo) => + installMockFetch((input: RequestInfo | URL) => Promise.resolve({ ok: true, status: 200, @@ -206,7 +206,7 @@ describe("web_fetch extraction fallbacks", () => { }); it("honors maxChars even when wrapper overhead exceeds limit", async () => { - installMockFetch((input: RequestInfo) => + installMockFetch((input: RequestInfo | URL) => Promise.resolve({ ok: true, status: 200, @@ -232,7 +232,7 @@ describe("web_fetch extraction fallbacks", () => { // The sanitization of these fields is verified by external-content.test.ts tests. it("falls back to firecrawl when readability returns no content", async () => { - installMockFetch((input: RequestInfo) => { + installMockFetch((input: RequestInfo | URL) => { const url = requestUrl(input); if (url.includes("api.firecrawl.dev")) { return Promise.resolve(firecrawlResponse("firecrawl content")) as Promise; @@ -253,8 +253,11 @@ describe("web_fetch extraction fallbacks", () => { }); it("throws when readability is disabled and firecrawl is unavailable", async () => { - installMockFetch((input: RequestInfo) => - Promise.resolve(htmlResponse("hi", requestUrl(input))), + installMockFetch( + (input: RequestInfo | URL) => + Promise.resolve( + htmlResponse("hi", requestUrl(input)), + ) as Promise, ); const tool = createFetchTool({ @@ -268,7 +271,7 @@ describe("web_fetch extraction fallbacks", () => { }); it("throws when readability is empty and firecrawl fails", async () => { - installMockFetch((input: RequestInfo) => { + installMockFetch((input: RequestInfo | URL) => { const url = requestUrl(input); if (url.includes("api.firecrawl.dev")) { return Promise.resolve(firecrawlError()) as Promise; @@ -288,7 +291,7 @@ describe("web_fetch extraction fallbacks", () => { }); it("uses firecrawl when direct fetch fails", async () => { - installMockFetch((input: RequestInfo) => { + installMockFetch((input: RequestInfo | URL) => { const url = requestUrl(input); if (url.includes("api.firecrawl.dev")) { return Promise.resolve(firecrawlResponse("firecrawl fallback", url)) as Promise; @@ -314,7 +317,7 @@ describe("web_fetch extraction fallbacks", () => { it("wraps external content and clamps oversized maxChars", async () => { const large = "a".repeat(80_000); installMockFetch( - (input: RequestInfo) => + (input: RequestInfo | URL) => Promise.resolve(textResponse(large, requestUrl(input))) as Promise, ); @@ -340,8 +343,11 @@ describe("web_fetch extraction fallbacks", () => { "Not Found

Not Found

" + long + "

"; - installMockFetch((input: RequestInfo) => - Promise.resolve(errorHtmlResponse(html, 404, requestUrl(input), "Text/HTML; charset=utf-8")), + installMockFetch( + (input: RequestInfo | URL) => + Promise.resolve( + errorHtmlResponse(html, 404, requestUrl(input), "Text/HTML; charset=utf-8"), + ) as Promise, ); const tool = createFetchTool({ firecrawl: { enabled: false } }); @@ -361,8 +367,9 @@ describe("web_fetch extraction fallbacks", () => { it("strips HTML errors when content-type is missing", async () => { const html = "Oops

Oops

"; - installMockFetch((input: RequestInfo) => - Promise.resolve(errorHtmlResponse(html, 500, requestUrl(input), null)), + installMockFetch( + (input: RequestInfo | URL) => + Promise.resolve(errorHtmlResponse(html, 500, requestUrl(input), null)) as Promise, ); const tool = createFetchTool({ firecrawl: { enabled: false } }); @@ -377,7 +384,7 @@ describe("web_fetch extraction fallbacks", () => { }); it("wraps firecrawl error details", async () => { - installMockFetch((input: RequestInfo) => { + installMockFetch((input: RequestInfo | URL) => { const url = requestUrl(input); if (url.includes("api.firecrawl.dev")) { return Promise.resolve({