diff --git a/src/gateway/auth.test.ts b/src/gateway/auth.test.ts index 4ada722a62..f0982ab253 100644 --- a/src/gateway/auth.test.ts +++ b/src/gateway/auth.test.ts @@ -7,10 +7,15 @@ function createLimiterSpy(): AuthRateLimiter & { recordFailure: ReturnType; reset: ReturnType; } { + const check = vi.fn( + (_ip, _scope) => ({ allowed: true, remaining: 10, retryAfterMs: 0 }) as const, + ); + const recordFailure = vi.fn((_ip, _scope) => {}); + const reset = vi.fn((_ip, _scope) => {}); return { - check: vi.fn(() => ({ allowed: true, remaining: 10, retryAfterMs: 0 })), - recordFailure: vi.fn(), - reset: vi.fn(), + check, + recordFailure, + reset, size: () => 0, prune: () => {}, dispose: () => {}, diff --git a/src/gateway/boot.test.ts b/src/gateway/boot.test.ts index 9ad72ef3a1..932707d11b 100644 --- a/src/gateway/boot.test.ts +++ b/src/gateway/boot.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { SessionScope } from "../config/sessions/types.js"; const agentCommand = vi.fn(); @@ -14,7 +15,12 @@ const { resolveStorePath } = await import("../config/sessions/paths.js"); const { loadSessionStore, saveSessionStore } = await import("../config/sessions/store.js"); describe("runBootOnce", () => { - const resolveMainStore = (cfg: { session?: { store?: string } } = {}) => { + const resolveMainStore = ( + cfg: { + session?: { store?: string; scope?: SessionScope; mainKey?: string }; + agents?: { list?: Array<{ id?: string; default?: boolean }> }; + } = {}, + ) => { const sessionKey = resolveMainSessionKey(cfg); const agentId = resolveAgentIdFromSessionKey(sessionKey); const storePath = resolveStorePath(cfg.session?.store, { agentId }); diff --git a/src/gateway/gateway-cli-backend.live.test.ts b/src/gateway/gateway-cli-backend.live.test.ts index d4b06c5728..f8a8439ce7 100644 --- a/src/gateway/gateway-cli-backend.live.test.ts +++ b/src/gateway/gateway-cli-backend.live.test.ts @@ -7,6 +7,7 @@ import { parseModelRef } from "../agents/model-selection.js"; import { loadConfig } from "../config/config.js"; import { isTruthyEnvValue } from "../infra/env.js"; import { getFreePortBlockWithPermissionFallback } from "../test-utils/ports.js"; +import { GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; import { GatewayClient } from "./client.js"; import { renderCatNoncePngBase64 } from "./live-image-probe.js"; import { startGatewayServer } from "./server.js"; @@ -144,7 +145,7 @@ async function connectClient(params: { url: string; token: string }) { const client = new GatewayClient({ url: params.url, token: params.token, - clientName: "vitest-live-cli-backend", + clientName: GATEWAY_CLIENT_NAMES.TEST, clientVersion: "dev", mode: "test", onHelloOk: () => stop(undefined, client), diff --git a/src/gateway/gateway-misc.test.ts b/src/gateway/gateway-misc.test.ts index c48e4965a0..8ed1dac162 100644 --- a/src/gateway/gateway-misc.test.ts +++ b/src/gateway/gateway-misc.test.ts @@ -36,11 +36,10 @@ describe("GatewayClient", () => { wsMockState.last = null; const client = new GatewayClient({ url: "ws://127.0.0.1:1" }); client.start(); + const last = wsMockState.last as { url: unknown; opts: unknown } | null; - expect(wsMockState.last?.url).toBe("ws://127.0.0.1:1"); - expect(wsMockState.last?.opts).toEqual( - expect.objectContaining({ maxPayload: 25 * 1024 * 1024 }), - ); + expect(last?.url).toBe("ws://127.0.0.1:1"); + expect(last?.opts).toEqual(expect.objectContaining({ maxPayload: 25 * 1024 * 1024 })); }); }); @@ -153,7 +152,8 @@ describe("late-arriving invoke results", () => { context, }); - const [ok, payload, error] = respond.mock.lastCall ?? []; + const [ok, rawPayload, error] = respond.mock.lastCall ?? []; + const payload = rawPayload as { ok?: boolean; ignored?: boolean } | undefined; // Late-arriving results return success instead of error to reduce log noise. expect(ok).toBe(true); diff --git a/src/gateway/gateway-models.profiles.live.test.ts b/src/gateway/gateway-models.profiles.live.test.ts index 098dae6e9d..31331ec0a7 100644 --- a/src/gateway/gateway-models.profiles.live.test.ts +++ b/src/gateway/gateway-models.profiles.live.test.ts @@ -22,7 +22,7 @@ import { getApiKeyForModel } from "../agents/model-auth.js"; import { ensureOpenClawModelsJson } from "../agents/models-config.js"; import { discoverAuthStorage, discoverModels } from "../agents/pi-model-discovery.js"; import { loadConfig } from "../config/config.js"; -import type { OpenClawConfig, ModelProviderConfig } from "../config/types.js"; +import type { ModelsConfig, OpenClawConfig, ModelProviderConfig } from "../config/types.js"; import { isTruthyEnvValue } from "../infra/env.js"; import { DEFAULT_AGENT_ID } from "../routing/session-key.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; @@ -401,6 +401,7 @@ function buildLiveGatewayConfig(params: { ...providerOverrides, }; const providers = Object.keys(nextProviders).length > 0 ? nextProviders : baseProviders; + const baseModels = params.cfg.models; return { ...params.cfg, agents: { @@ -418,7 +419,9 @@ function buildLiveGatewayConfig(params: { }, }, models: - Object.keys(providers).length > 0 ? { ...params.cfg.models, providers } : params.cfg.models, + Object.keys(providers).length > 0 + ? ({ ...baseModels, providers } as ModelsConfig) + : baseModels, }; } @@ -1149,10 +1152,10 @@ describeLive("gateway live (dev agent, profile keys)", () => { await fs.writeFile(toolProbePath, `nonceA=${nonceA}\nnonceB=${nonceB}\n`); const port = await getFreeGatewayPort(); - const server = await startGatewayServer({ - configPath: cfg.__meta?.path, - port, - token, + const server = await startGatewayServer(port, { + bind: "loopback", + auth: { mode: "token", token }, + controlUiEnabled: false, }); const client = await connectClient({ diff --git a/src/gateway/hooks-mapping.test.ts b/src/gateway/hooks-mapping.test.ts index e9ba5a5725..882ab0e18b 100644 --- a/src/gateway/hooks-mapping.test.ts +++ b/src/gateway/hooks-mapping.test.ts @@ -109,7 +109,7 @@ describe("hooks mapping", () => { ], }); expect(result?.ok).toBe(true); - if (result?.ok && result.action.kind === "agent") { + if (result?.ok && result.action && result.action.kind === "agent") { expect(result.action.model).toBe("openai/gpt-4.1-mini"); } }); diff --git a/src/gateway/server-methods/agents-mutate.test.ts b/src/gateway/server-methods/agents-mutate.test.ts index c45a6f55d0..1905e24163 100644 --- a/src/gateway/server-methods/agents-mutate.test.ts +++ b/src/gateway/server-methods/agents-mutate.test.ts @@ -126,7 +126,8 @@ function createErrnoError(code: string) { } function mockWorkspaceStateRead(params: { onboardingCompletedAt?: string; errorCode?: string }) { - mocks.fsReadFile.mockImplementation(async (filePath: string | URL | number) => { + mocks.fsReadFile.mockImplementation(async (...args: unknown[]) => { + const filePath = args[0]; if (String(filePath).endsWith("workspace-state.json")) { if (params.errorCode) { throw createErrnoError(params.errorCode); diff --git a/src/gateway/server-methods/chat.abort-persistence.test.ts b/src/gateway/server-methods/chat.abort-persistence.test.ts index 428efefa3c..df8551e6c2 100644 --- a/src/gateway/server-methods/chat.abort-persistence.test.ts +++ b/src/gateway/server-methods/chat.abort-persistence.test.ts @@ -107,6 +107,9 @@ describe("chat abort transcript persistence", () => { params: { sessionKey: "main", runId }, respond, context: context as never, + req: {} as never, + client: null, + isWebchatConnect: () => false, }); const [ok1, payload1] = respond.mock.calls.at(-1) ?? []; @@ -121,6 +124,9 @@ describe("chat abort transcript persistence", () => { params: { sessionKey: "main", runId }, respond, context: context as never, + req: {} as never, + client: null, + isWebchatConnect: () => false, }); const lines = await readTranscriptLines(transcriptPath); @@ -178,6 +184,9 @@ describe("chat abort transcript persistence", () => { params: { sessionKey: "main" }, respond, context: context as never, + req: {} as never, + client: null, + isWebchatConnect: () => false, }); const [ok, payload] = respond.mock.calls.at(-1) ?? []; @@ -235,7 +244,9 @@ describe("chat abort transcript persistence", () => { }, respond, context: context as never, - client: undefined, + req: {} as never, + client: null, + isWebchatConnect: () => false, }); const [ok, payload] = respond.mock.calls.at(-1) ?? []; diff --git a/src/gateway/server-methods/chat.inject.parentid.e2e.test.ts b/src/gateway/server-methods/chat.inject.parentid.e2e.test.ts index 5cf209b605..2d04e1cb9c 100644 --- a/src/gateway/server-methods/chat.inject.parentid.e2e.test.ts +++ b/src/gateway/server-methods/chat.inject.parentid.e2e.test.ts @@ -26,7 +26,7 @@ describe("gateway chat.inject transcript writes", () => { ); vi.doMock("../session-utils.js", async (importOriginal) => { - const original = await importOriginal(); + const original = await importOriginal(); return { ...original, loadSessionEntry: () => ({ @@ -50,7 +50,10 @@ describe("gateway chat.inject transcript writes", () => { await chatHandlers["chat.inject"]({ params: { sessionKey: "k1", message: "hello" }, respond, - context, + req: {} as never, + client: null as never, + isWebchatConnect: () => false, + context: context as unknown as GatewayRequestContext, }); expect(respond).toHaveBeenCalled(); diff --git a/src/gateway/server-methods/skills.update.normalizes-api-key.test.ts b/src/gateway/server-methods/skills.update.normalizes-api-key.test.ts index ac4dc51672..6476ad671d 100644 --- a/src/gateway/server-methods/skills.update.normalizes-api-key.test.ts +++ b/src/gateway/server-methods/skills.update.normalizes-api-key.test.ts @@ -28,6 +28,10 @@ describe("skills.update", () => { skillKey: "brave-search", apiKey: "abc\r\ndef", }, + req: {} as never, + client: null as never, + isWebchatConnect: () => false, + context: {} as never, respond: (success, _result, err) => { ok = success; error = err; diff --git a/src/gateway/server-methods/usage.sessions-usage.test.ts b/src/gateway/server-methods/usage.sessions-usage.test.ts index 3731bdd4e0..7eb48fd821 100644 --- a/src/gateway/server-methods/usage.sessions-usage.test.ts +++ b/src/gateway/server-methods/usage.sessions-usage.test.ts @@ -106,7 +106,9 @@ describe("sessions.usage", () => { expect(respond).toHaveBeenCalledTimes(1); expect(respond.mock.calls[0]?.[0]).toBe(true); - const result = respond.mock.calls[0]?.[1] as unknown as { sessions: Array }; + const result = respond.mock.calls[0]?.[1] as unknown as { + sessions: Array<{ key: string; agentId: string }>; + }; expect(result.sessions).toHaveLength(2); // Sorted by most recent first (mtime=200 -> opus first). diff --git a/src/gateway/server-methods/usage.test.ts b/src/gateway/server-methods/usage.test.ts index e7b5fe30ce..5f9bae6299 100644 --- a/src/gateway/server-methods/usage.test.ts +++ b/src/gateway/server-methods/usage.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { OpenClawConfig } from "../../config/config.js"; vi.mock("../../infra/session-cost-usage.js", async () => { const actual = await vi.importActual( @@ -63,7 +64,7 @@ describe("gateway usage helpers", () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-02-05T00:00:00.000Z")); - const config = {} as unknown as ReturnType; + const config = {} as OpenClawConfig; const a = await __test.loadCostUsageSummaryCached({ startMs: 1, endMs: 2, diff --git a/src/gateway/server-plugins.test.ts b/src/gateway/server-plugins.test.ts index 0901a5116d..7fb34ff5ef 100644 --- a/src/gateway/server-plugins.test.ts +++ b/src/gateway/server-plugins.test.ts @@ -15,6 +15,7 @@ const createRegistry = (diagnostics: PluginDiagnostic[]): PluginRegistry => ({ hooks: [], typedHooks: [], channels: [], + commands: [], providers: [], gatewayHandlers: {}, httpHandlers: [], diff --git a/src/gateway/server.agent.gateway-server-agent-b.e2e.test.ts b/src/gateway/server.agent.gateway-server-agent-b.e2e.test.ts index 08c999a8eb..fc02f9393a 100644 --- a/src/gateway/server.agent.gateway-server-agent-b.e2e.test.ts +++ b/src/gateway/server.agent.gateway-server-agent-b.e2e.test.ts @@ -306,9 +306,14 @@ describe("gateway server agent", () => { const ack = await ackP; const final = await finalP; - expect(ack.payload.runId).toBeDefined(); - expect(final.payload.runId).toBe(ack.payload.runId); - expect(final.payload.status).toBe("ok"); + const ackPayload = ack.payload; + const finalPayload = final.payload; + if (!ackPayload || !finalPayload) { + throw new Error("missing websocket payload"); + } + expect(ackPayload.runId).toBeDefined(); + expect(finalPayload.runId).toBe(ackPayload.runId); + expect(finalPayload.status).toBe("ok"); }); test("agent dedupes by idempotencyKey after completion", async () => { diff --git a/src/gateway/server.canvas-auth.e2e.test.ts b/src/gateway/server.canvas-auth.e2e.test.ts index 3d0d7c0cd3..b5e7a5946c 100644 --- a/src/gateway/server.canvas-auth.e2e.test.ts +++ b/src/gateway/server.canvas-auth.e2e.test.ts @@ -63,6 +63,7 @@ async function withCanvasGatewayHarness(params: { const canvasWss = new WebSocketServer({ noServer: true }); const canvasHost: CanvasHostHandler = { rootDir: "test", + basePath: "/canvas", close: async () => {}, handleUpgrade: (req, socket, head) => { const url = new URL(req.url ?? "/", "http://localhost"); diff --git a/src/gateway/server.chat.gateway-server-chat-b.e2e.test.ts b/src/gateway/server.chat.gateway-server-chat-b.e2e.test.ts index 183b4f7d86..5521dea21f 100644 --- a/src/gateway/server.chat.gateway-server-chat-b.e2e.test.ts +++ b/src/gateway/server.chat.gateway-server-chat-b.e2e.test.ts @@ -270,7 +270,7 @@ describe("gateway server chat", () => { test("smoke: supports abort and idempotent completion", async () => { const tempDirs: string[] = []; const { server, ws } = await startServerWithClient(); - const spy = vi.mocked(getReplyFromConfig); + const spy = vi.mocked(getReplyFromConfig) as unknown as ReturnType; let aborted = false; try { diff --git a/src/gateway/server.config-apply.e2e.test.ts b/src/gateway/server.config-apply.e2e.test.ts index 85b22c6e65..f1b7156851 100644 --- a/src/gateway/server.config-apply.e2e.test.ts +++ b/src/gateway/server.config-apply.e2e.test.ts @@ -44,10 +44,10 @@ describe("gateway config.apply", () => { }, }), ); - const res = await onceMessage<{ ok: boolean; error?: { message?: string } }>( - ws, - (o) => o.type === "res" && o.id === id, - ); + const res = await onceMessage<{ ok: boolean; error?: { message?: string } }>(ws, (o) => { + const msg = o as { type?: string; id?: string }; + return msg.type === "res" && msg.id === id; + }); expect(res.ok).toBe(false); expect(res.error?.message ?? "").toMatch(/invalid|SyntaxError/i); } finally { @@ -69,10 +69,10 @@ describe("gateway config.apply", () => { }, }), ); - const res = await onceMessage<{ ok: boolean; error?: { message?: string } }>( - ws, - (o) => o.type === "res" && o.id === id, - ); + const res = await onceMessage<{ ok: boolean; error?: { message?: string } }>(ws, (o) => { + const msg = o as { type?: string; id?: string }; + return msg.type === "res" && msg.id === id; + }); expect(res.ok).toBe(false); expect(res.error?.message ?? "").toContain("raw"); } finally { diff --git a/src/gateway/server.cron.e2e.test.ts b/src/gateway/server.cron.e2e.test.ts index 61b7398398..49af77648c 100644 --- a/src/gateway/server.cron.e2e.test.ts +++ b/src/gateway/server.cron.e2e.test.ts @@ -561,7 +561,7 @@ describe("gateway server cron", () => { await yieldToEventLoop(); expect(fetchMock).toHaveBeenCalledTimes(2); - cronIsolatedRun.mockResolvedValueOnce({ status: "ok" }); + cronIsolatedRun.mockResolvedValueOnce({ status: "ok", summary: "" }); const noSummaryRes = await rpcReq(ws, "cron.add", { name: "webhook no summary", enabled: true, diff --git a/src/gateway/server.node-invoke-approval-bypass.e2e.test.ts b/src/gateway/server.node-invoke-approval-bypass.e2e.test.ts index af98b2d1f9..d7e3137fc1 100644 --- a/src/gateway/server.node-invoke-approval-bypass.e2e.test.ts +++ b/src/gateway/server.node-invoke-approval-bypass.e2e.test.ts @@ -268,10 +268,11 @@ describe("node.invoke approval bypass", () => { }); expect(invoke.ok).toBe(true); - expect(lastInvokeParams).toBeTruthy(); - expect(lastInvokeParams?.approved).toBe(true); - expect(lastInvokeParams?.approvalDecision).toBe("allow-once"); - expect(lastInvokeParams?.injected).toBeUndefined(); + const invokeParams = lastInvokeParams as Record | null; + expect(invokeParams).toBeTruthy(); + expect(invokeParams?.["approved"]).toBe(true); + expect(invokeParams?.["approvalDecision"]).toBe("allow-once"); + expect(invokeParams?.["injected"]).toBeUndefined(); ws.close(); ws2.close();