diff --git a/src/browser/client-fetch.loopback-auth.test.ts b/src/browser/client-fetch.loopback-auth.test.ts index 27f2dd8594d..f0c49b3163d 100644 --- a/src/browser/client-fetch.loopback-auth.test.ts +++ b/src/browser/client-fetch.loopback-auth.test.ts @@ -49,7 +49,7 @@ describe("fetchBrowserJson loopback auth", () => { }); it("adds bearer auth for loopback absolute HTTP URLs", async () => { - const fetchMock = vi.fn( + const fetchMock = vi.fn<(input: RequestInfo | URL, init?: RequestInit) => Promise>( async () => new Response(JSON.stringify({ ok: true }), { status: 200, @@ -61,13 +61,13 @@ describe("fetchBrowserJson loopback auth", () => { const res = await fetchBrowserJson<{ ok: boolean }>("http://127.0.0.1:18888/"); expect(res.ok).toBe(true); - const init = fetchMock.mock.calls[0]?.[1] as RequestInit; + const init = fetchMock.mock.calls[0]?.[1]; const headers = new Headers(init?.headers); expect(headers.get("authorization")).toBe("Bearer loopback-token"); }); it("does not inject auth for non-loopback absolute URLs", async () => { - const fetchMock = vi.fn( + const fetchMock = vi.fn<(input: RequestInfo | URL, init?: RequestInit) => Promise>( async () => new Response(JSON.stringify({ ok: true }), { status: 200, @@ -78,13 +78,13 @@ describe("fetchBrowserJson loopback auth", () => { await fetchBrowserJson<{ ok: boolean }>("http://example.com/"); - const init = fetchMock.mock.calls[0]?.[1] as RequestInit; + const init = fetchMock.mock.calls[0]?.[1]; const headers = new Headers(init?.headers); expect(headers.get("authorization")).toBeNull(); }); it("keeps caller-supplied auth header", async () => { - const fetchMock = vi.fn( + const fetchMock = vi.fn<(input: RequestInfo | URL, init?: RequestInit) => Promise>( async () => new Response(JSON.stringify({ ok: true }), { status: 200, @@ -99,7 +99,7 @@ describe("fetchBrowserJson loopback auth", () => { }, }); - const init = fetchMock.mock.calls[0]?.[1] as RequestInit; + const init = fetchMock.mock.calls[0]?.[1]; const headers = new Headers(init?.headers); expect(headers.get("authorization")).toBe("Bearer caller-token"); }); diff --git a/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts b/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts index 401b284874d..8324acfbe11 100644 --- a/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts +++ b/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts @@ -51,7 +51,7 @@ describe("pw-tools-core", () => { }); const res = await p; - const outPath = vi.mocked(saveAs).mock.calls[0]?.[0]; + const outPath = (vi.mocked(saveAs).mock.calls as unknown as Array<[string]>)[0]?.[0]; return { res, outPath }; } diff --git a/src/browser/server-context.hot-reload-profiles.test.ts b/src/browser/server-context.hot-reload-profiles.test.ts index b448a872fbf..6a0e416f5ef 100644 --- a/src/browser/server-context.hot-reload-profiles.test.ts +++ b/src/browser/server-context.hot-reload-profiles.test.ts @@ -57,7 +57,7 @@ describe("server-context hot-reload profiles", () => { const resolved = resolveBrowserConfig(cfg.browser, cfg); // Verify cache is primed (without desktop) - expect(cfg.browser.profiles.desktop).toBeUndefined(); + expect(cfg.browser?.profiles?.desktop).toBeUndefined(); const state = { server: null, port: 18791, @@ -79,7 +79,7 @@ describe("server-context hot-reload profiles", () => { // 3. Verify without clearConfigCache, loadConfig() still returns stale cached value const staleCfg = loadConfig(); - expect(staleCfg.browser.profiles.desktop).toBeUndefined(); // Cache is stale! + expect(staleCfg.browser?.profiles?.desktop).toBeUndefined(); // Cache is stale! // 4. Hot-reload should read fresh config for the lookup (createConfigIO().loadConfig()), // without flushing the global loadConfig cache. @@ -97,7 +97,7 @@ describe("server-context hot-reload profiles", () => { // 6. Verify GLOBAL cache was NOT cleared - subsequent simple loadConfig() still sees STALE value // This confirms the fix: we read fresh config for the specific profile lookup without flushing the global cache const stillStaleCfg = loadConfig(); - expect(stillStaleCfg.browser.profiles.desktop).toBeUndefined(); + expect(stillStaleCfg.browser?.profiles?.desktop).toBeUndefined(); }); it("forProfile still throws for profiles that don't exist in fresh config", async () => { diff --git a/src/browser/server-context.remote-tab-ops.test.ts b/src/browser/server-context.remote-tab-ops.test.ts index 0febccf5f95..9d7f876bef2 100644 --- a/src/browser/server-context.remote-tab-ops.test.ts +++ b/src/browser/server-context.remote-tab-ops.test.ts @@ -27,6 +27,8 @@ function makeState( cdpIsLoopback: profile !== "remote", remoteCdpTimeoutMs: 1500, remoteCdpHandshakeTimeoutMs: 3000, + evaluateEnabled: false, + extraArgs: [], color: "#FF4500", headless: true, noSandbox: false, @@ -62,7 +64,7 @@ describe("browser server-context remote profile tab operations", () => { listPagesViaPlaywright, createPageViaPlaywright, closePageByTargetIdViaPlaywright, - } as Awaited>); + } as unknown as Awaited>); const fetchMock = vi.fn(async () => { throw new Error("unexpected fetch"); @@ -127,7 +129,7 @@ describe("browser server-context remote profile tab operations", () => { closePageByTargetIdViaPlaywright: vi.fn(async () => { throw new Error("unexpected close"); }), - } as Awaited>); + } as unknown as Awaited>); const fetchMock = vi.fn(async () => { throw new Error("unexpected fetch"); @@ -154,7 +156,7 @@ describe("browser server-context remote profile tab operations", () => { vi.spyOn(pwAiModule, "getPwAiModule").mockResolvedValue({ listPagesViaPlaywright, focusPageByTargetIdViaPlaywright, - } as Awaited>); + } as unknown as Awaited>); const fetchMock = vi.fn(async () => { throw new Error("unexpected fetch"); @@ -180,7 +182,7 @@ describe("browser server-context remote profile tab operations", () => { listPagesViaPlaywright: vi.fn(async () => { throw new Error("boom"); }), - } as Awaited>); + } as unknown as Awaited>); const fetchMock = vi.fn(async () => { throw new Error("unexpected fetch"); diff --git a/src/commands/agent.e2e.test.ts b/src/commands/agent.e2e.test.ts index 86e2b11907c..b821acf3906 100644 --- a/src/commands/agent.e2e.test.ts +++ b/src/commands/agent.e2e.test.ts @@ -43,7 +43,7 @@ function mockConfig( home: string, storePath: string, agentOverrides?: Partial["defaults"]>>, - telegramOverrides?: Partial>, + telegramOverrides?: Partial["telegram"]>>, agentsList?: Array<{ id: string; default?: boolean }>, ) { configSpy.mockReturnValue({ @@ -57,7 +57,9 @@ function mockConfig( list: agentsList, }, session: { store: storePath, mainKey: "main" }, - telegram: telegramOverrides ? { ...telegramOverrides } : undefined, + channels: { + telegram: telegramOverrides ? { ...telegramOverrides } : undefined, + }, }); } @@ -342,7 +344,7 @@ describe("agentCommand", () => { await agentCommand({ message: "hi", to: "+1999", json: true }, runtime); - const logged = (runtime.log as MockInstance).mock.calls.at(-1)?.[0] as string; + const logged = (runtime.log as unknown as MockInstance).mock.calls.at(-1)?.[0] as string; const parsed = JSON.parse(logged) as { payloads: Array<{ text: string; mediaUrl?: string | null }>; meta: { durationMs: number }; @@ -376,6 +378,7 @@ describe("agentCommand", () => { const deps = { sendMessageWhatsApp: vi.fn(), sendMessageTelegram: vi.fn().mockResolvedValue({ messageId: "t1", chatId: "123" }), + sendMessageSlack: vi.fn(), sendMessageDiscord: vi.fn(), sendMessageSignal: vi.fn(), sendMessageIMessage: vi.fn(), diff --git a/src/commands/message.e2e.test.ts b/src/commands/message.e2e.test.ts index f4c69be3088..3ef7ec70d36 100644 --- a/src/commands/message.e2e.test.ts +++ b/src/commands/message.e2e.test.ts @@ -29,22 +29,22 @@ vi.mock("../web/session.js", () => ({ webAuthExists, })); -const handleDiscordAction = vi.fn(async () => ({ details: { ok: true } })); +const handleDiscordAction = vi.fn(async (..._args: unknown[]) => ({ details: { ok: true } })); vi.mock("../agents/tools/discord-actions.js", () => ({ handleDiscordAction, })); -const handleSlackAction = vi.fn(async () => ({ details: { ok: true } })); +const handleSlackAction = vi.fn(async (..._args: unknown[]) => ({ details: { ok: true } })); vi.mock("../agents/tools/slack-actions.js", () => ({ handleSlackAction, })); -const handleTelegramAction = vi.fn(async () => ({ details: { ok: true } })); +const handleTelegramAction = vi.fn(async (..._args: unknown[]) => ({ details: { ok: true } })); vi.mock("../agents/tools/telegram-actions.js", () => ({ handleTelegramAction, })); -const handleWhatsAppAction = vi.fn(async () => ({ details: { ok: true } })); +const handleWhatsAppAction = vi.fn(async (..._args: unknown[]) => ({ details: { ok: true } })); vi.mock("../agents/tools/whatsapp-actions.js", () => ({ handleWhatsAppAction, })); diff --git a/src/commands/onboard-auth.e2e.test.ts b/src/commands/onboard-auth.e2e.test.ts index 36b1306e8b0..0e53d35b4c4 100644 --- a/src/commands/onboard-auth.e2e.test.ts +++ b/src/commands/onboard-auth.e2e.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import type { OAuthCredentials } from "@mariozechner/pi-ai"; import { afterEach, describe, expect, it } from "vitest"; +import type { OpenClawConfig } from "../config/config.js"; import { applyAuthProfileConfig, applyLitellmProviderConfig, @@ -36,12 +37,12 @@ import { function createLegacyProviderConfig(params: { providerId: string; - api: string; + api: "anthropic-messages" | "openai-completions" | "openai-responses"; modelId?: string; modelName?: string; baseUrl?: string; apiKey?: string; -}) { +}): OpenClawConfig { return { models: { providers: { @@ -63,7 +64,7 @@ function createLegacyProviderConfig(params: { }, }, }, - }; + } as OpenClawConfig; } const EXPECTED_FALLBACKS = ["anthropic/claude-opus-4-5"] as const; diff --git a/src/config/channel-capabilities.test.ts b/src/config/channel-capabilities.test.ts index 7af4c320108..896be0fab3e 100644 --- a/src/config/channel-capabilities.test.ts +++ b/src/config/channel-capabilities.test.ts @@ -115,7 +115,7 @@ describe("resolveChannelCapabilities", () => { capabilities: { inlineButtons: "dm" }, }, }, - }; + } as unknown as Partial; // Should return undefined (not crash), allowing channel-specific handlers to process it. expect( diff --git a/src/config/config.legacy-config-detection.rejects-routing-allowfrom.e2e.test.ts b/src/config/config.legacy-config-detection.rejects-routing-allowfrom.e2e.test.ts index c2bd0405042..0fe46d9d58b 100644 --- a/src/config/config.legacy-config-detection.rejects-routing-allowfrom.e2e.test.ts +++ b/src/config/config.legacy-config-detection.rejects-routing-allowfrom.e2e.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from "vitest"; import { migrateLegacyConfig, validateConfigObject } from "./config.js"; +function getLegacyRouting(config: unknown) { + return (config as { routing?: Record } | undefined)?.routing; +} + describe("legacy config detection", () => { it("rejects routing.allowFrom", async () => { const res = validateConfigObject({ @@ -27,7 +31,7 @@ describe("legacy config detection", () => { }); expect(res.changes).toContain("Moved routing.allowFrom → channels.whatsapp.allowFrom."); expect(res.config?.channels?.whatsapp?.allowFrom).toEqual(["+15555550123"]); - expect(res.config?.routing?.allowFrom).toBeUndefined(); + expect(getLegacyRouting(res.config)?.allowFrom).toBeUndefined(); }); it("drops routing.allowFrom when whatsapp missing", async () => { const res = migrateLegacyConfig({ @@ -35,7 +39,7 @@ describe("legacy config detection", () => { }); expect(res.changes).toContain("Removed routing.allowFrom (channels.whatsapp not configured)."); expect(res.config?.channels?.whatsapp).toBeUndefined(); - expect(res.config?.routing?.allowFrom).toBeUndefined(); + expect(getLegacyRouting(res.config)?.allowFrom).toBeUndefined(); }); it("migrates routing.groupChat.requireMention to channels whatsapp/telegram/imessage groups when whatsapp configured", async () => { const res = migrateLegacyConfig({ @@ -54,7 +58,7 @@ describe("legacy config detection", () => { expect(res.config?.channels?.whatsapp?.groups?.["*"]?.requireMention).toBe(false); expect(res.config?.channels?.telegram?.groups?.["*"]?.requireMention).toBe(false); expect(res.config?.channels?.imessage?.groups?.["*"]?.requireMention).toBe(false); - expect(res.config?.routing?.groupChat?.requireMention).toBeUndefined(); + expect(getLegacyRouting(res.config)?.groupChat).toBeUndefined(); }); it("migrates routing.groupChat.requireMention to telegram/imessage when whatsapp missing", async () => { const res = migrateLegacyConfig({ @@ -72,7 +76,7 @@ describe("legacy config detection", () => { expect(res.config?.channels?.whatsapp).toBeUndefined(); expect(res.config?.channels?.telegram?.groups?.["*"]?.requireMention).toBe(false); expect(res.config?.channels?.imessage?.groups?.["*"]?.requireMention).toBe(false); - expect(res.config?.routing?.groupChat?.requireMention).toBeUndefined(); + expect(getLegacyRouting(res.config)?.groupChat).toBeUndefined(); }); it("migrates routing.groupChat.mentionPatterns to messages.groupChat.mentionPatterns", async () => { const res = migrateLegacyConfig({ @@ -82,7 +86,7 @@ describe("legacy config detection", () => { "Moved routing.groupChat.mentionPatterns → messages.groupChat.mentionPatterns.", ); expect(res.config?.messages?.groupChat?.mentionPatterns).toEqual(["@openclaw"]); - expect(res.config?.routing?.groupChat?.mentionPatterns).toBeUndefined(); + expect(getLegacyRouting(res.config)?.groupChat).toBeUndefined(); }); it("migrates routing agentToAgent/queue/transcribeAudio to tools/messages/media", async () => { const res = migrateLegacyConfig({ @@ -117,7 +121,7 @@ describe("legacy config detection", () => { }, ], }); - expect(res.config?.routing).toBeUndefined(); + expect(getLegacyRouting(res.config)).toBeUndefined(); }); it("migrates audio.transcription with custom script names", async () => { const res = migrateLegacyConfig({ diff --git a/src/config/model-alias-defaults.test.ts b/src/config/model-alias-defaults.test.ts index 025b946368e..62afb0a6bc7 100644 --- a/src/config/model-alias-defaults.test.ts +++ b/src/config/model-alias-defaults.test.ts @@ -65,7 +65,17 @@ describe("applyModelDefaults", () => { baseUrl: "https://proxy.example/v1", apiKey: "sk-test", api: "openai-completions", - models: [{ id: "gpt-5.2", name: "GPT-5.2" }], + models: [ + { + id: "gpt-5.2", + name: "GPT-5.2", + reasoning: false, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200_000, + maxTokens: 8192, + }, + ], }, }, }, @@ -86,8 +96,20 @@ describe("applyModelDefaults", () => { models: { providers: { myproxy: { + baseUrl: "https://proxy.example/v1", + apiKey: "sk-test", api: "openai-completions", - models: [{ id: "gpt-5.2", name: "GPT-5.2", contextWindow: 32768, maxTokens: 40960 }], + models: [ + { + id: "gpt-5.2", + name: "GPT-5.2", + reasoning: false, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 32768, + maxTokens: 40960, + }, + ], }, }, }, diff --git a/src/config/redact-snapshot.test.ts b/src/config/redact-snapshot.test.ts index 8f262d545f9..e26c19edbce 100644 --- a/src/config/redact-snapshot.test.ts +++ b/src/config/redact-snapshot.test.ts @@ -33,7 +33,7 @@ function makeSnapshot>( issues: [], warnings: [], legacyIssues: [], - } as TestSnapshot; + } as unknown as TestSnapshot; } function restoreRedactedValues( diff --git a/src/cron/isolated-agent/session.test.ts b/src/cron/isolated-agent/session.test.ts index 270628e8152..53e9d37dd6c 100644 --- a/src/cron/isolated-agent/session.test.ts +++ b/src/cron/isolated-agent/session.test.ts @@ -151,7 +151,7 @@ describe("resolveCronSession", () => { updatedAt: Date.now() - 1000, modelOverride: "some-model", }, - } as ReturnType); + } as unknown as ReturnType); vi.mocked(evaluateSessionFreshness).mockReturnValue({ fresh: true }); const result = resolveCronSession({