diff --git a/src/discord/monitor.test.ts b/src/discord/monitor.test.ts index 97de8d4bf7..a9042db801 100644 --- a/src/discord/monitor.test.ts +++ b/src/discord/monitor.test.ts @@ -87,7 +87,9 @@ describe("DiscordMessageListener", () => { expect(handler).toHaveBeenCalledOnce(); expect(handlerResolved).toBe(false); - resolveHandler?.(); + if (resolveHandler) { + resolveHandler(); + } await handlerPromise; }); @@ -132,12 +134,15 @@ describe("DiscordMessageListener", () => { ); vi.setSystemTime(31_000); - resolveHandler?.(); + if (resolveHandler) { + resolveHandler(); + } await handlerPromise; await Promise.resolve(); expect(logger.warn).toHaveBeenCalled(); - const [, meta] = logger.warn.mock.calls[0] ?? []; + const warnMock = logger.warn as unknown as { mock: { calls: unknown[][] } }; + const [, meta] = warnMock.mock.calls[0] ?? []; expect(meta?.durationMs).toBeGreaterThanOrEqual(30_000); } finally { vi.useRealTimers(); @@ -935,7 +940,10 @@ describe("discord DM reaction handling", () => { await listener.handle(data, client); expect(resolveAgentRouteMock).toHaveBeenCalledOnce(); - const routeArgs = resolveAgentRouteMock.mock.calls[0][0]; + const [routeArgs] = resolveAgentRouteMock.mock.calls[0] ?? []; + if (!routeArgs) { + throw new Error("expected route arguments"); + } expect(routeArgs.peer).toEqual({ kind: "direct", id: "user-42" }); }); @@ -950,7 +958,10 @@ describe("discord DM reaction handling", () => { await listener.handle(data, client); expect(resolveAgentRouteMock).toHaveBeenCalledOnce(); - const routeArgs = resolveAgentRouteMock.mock.calls[0][0]; + const [routeArgs] = resolveAgentRouteMock.mock.calls[0] ?? []; + if (!routeArgs) { + throw new Error("expected route arguments"); + } expect(routeArgs.peer).toEqual({ kind: "group", id: "channel-1" }); }); }); diff --git a/src/discord/monitor.tool-result.accepts-guild-messages-mentionpatterns-match.e2e.test.ts b/src/discord/monitor.tool-result.accepts-guild-messages-mentionpatterns-match.e2e.test.ts index 383a7a6370..6e334f7436 100644 --- a/src/discord/monitor.tool-result.accepts-guild-messages-mentionpatterns-match.e2e.test.ts +++ b/src/discord/monitor.tool-result.accepts-guild-messages-mentionpatterns-match.e2e.test.ts @@ -78,7 +78,7 @@ async function createHandler(cfg: LoadedConfig) { const { createDiscordMessageHandler } = await import("./monitor.js"); return createDiscordMessageHandler({ cfg, - discordConfig: cfg.channels.discord, + discordConfig: cfg.channels?.discord, accountId: "default", token: "token", runtime: makeRuntime(), @@ -90,7 +90,7 @@ async function createHandler(cfg: LoadedConfig) { replyToMode: "off", dmEnabled: true, groupDmEnabled: false, - guildEntries: cfg.channels.discord.guilds, + guildEntries: cfg.channels?.discord?.guilds, }); } @@ -275,7 +275,9 @@ describe("discord tool result dispatch", () => { }, }, session: { store: "/tmp/openclaw-sessions.json" }, - discord: { dm: { enabled: true, policy: "open" } }, + channels: { + discord: { dm: { enabled: true, policy: "open" } }, + }, } as ReturnType; const command = createDiscordNativeCommand({ @@ -285,9 +287,8 @@ describe("discord tool result dispatch", () => { acceptsArgs: true, }, cfg, - discordConfig: cfg.discord, + discordConfig: cfg.channels!.discord!, accountId: "default", - token: "token", sessionPrefix: "discord:slash", ephemeralDefault: true, }); @@ -295,7 +296,7 @@ describe("discord tool result dispatch", () => { const reply = vi.fn().mockResolvedValue(undefined); const followUp = vi.fn().mockResolvedValue(undefined); - await command.run({ + const interaction = { user: { id: "u1", username: "Ada", globalName: "Ada" }, channel: { type: ChannelType.DM }, guild: null, @@ -303,7 +304,9 @@ describe("discord tool result dispatch", () => { options: { getString: vi.fn().mockReturnValue("on") }, reply, followUp, - }); + } as unknown as Parameters[0]; + + await command.run(interaction); expect(dispatchMock).toHaveBeenCalledTimes(1); expect(reply).toHaveBeenCalledTimes(1); diff --git a/src/discord/monitor.tool-result.sends-status-replies-responseprefix.test.ts b/src/discord/monitor.tool-result.sends-status-replies-responseprefix.test.ts index b0133a830d..983252058a 100644 --- a/src/discord/monitor.tool-result.sends-status-replies-responseprefix.test.ts +++ b/src/discord/monitor.tool-result.sends-status-replies-responseprefix.test.ts @@ -23,15 +23,15 @@ beforeEach(() => { upsertPairingRequestMock.mockReset().mockResolvedValue({ code: "PAIRCODE", created: true }); }); -const BASE_CFG = { +const BASE_CFG: Config = { agents: { defaults: { - model: "anthropic/claude-opus-4-5", + model: { primary: "anthropic/claude-opus-4-5" }, workspace: "/tmp/openclaw", }, }, session: { store: "/tmp/openclaw-sessions.json" }, -} as const; +}; const CATEGORY_GUILD_CFG = { ...BASE_CFG, @@ -47,13 +47,13 @@ const CATEGORY_GUILD_CFG = { }, }, routing: { allowFrom: [] }, -} as Config; +} satisfies Config; async function createDmHandler(opts: { cfg: Config; runtimeError?: (err: unknown) => void }) { const { createDiscordMessageHandler } = await import("./monitor.js"); return createDiscordMessageHandler({ cfg: opts.cfg, - discordConfig: opts.cfg.channels.discord, + discordConfig: opts.cfg.channels?.discord, accountId: "default", token: "token", runtime: { @@ -89,7 +89,7 @@ async function createCategoryGuildHandler() { const { createDiscordMessageHandler } = await import("./monitor.js"); return createDiscordMessageHandler({ cfg: CATEGORY_GUILD_CFG, - discordConfig: CATEGORY_GUILD_CFG.channels.discord, + discordConfig: CATEGORY_GUILD_CFG.channels?.discord, accountId: "default", token: "token", runtime: { diff --git a/src/gateway/server-methods/server-methods.test.ts b/src/gateway/server-methods/server-methods.test.ts index 40bcd8b89c..38e2de9dfb 100644 --- a/src/gateway/server-methods/server-methods.test.ts +++ b/src/gateway/server-methods/server-methods.test.ts @@ -240,10 +240,12 @@ describe("gateway chat transcript writes (guardrail)", () => { }); describe("exec approval handlers", () => { - const execApprovalNoop = () => {}; + const execApprovalNoop = () => false; type ExecApprovalHandlers = ReturnType; type ExecApprovalRequestArgs = Parameters[0]; type ExecApprovalResolveArgs = Parameters[0]; + type ExecApprovalRequestRespond = ExecApprovalRequestArgs["respond"]; + type ExecApprovalResolveRespond = ExecApprovalResolveArgs["respond"]; const defaultExecApprovalRequestParams = { command: "echo ok", @@ -266,7 +268,7 @@ describe("exec approval handlers", () => { async function requestExecApproval(params: { handlers: ExecApprovalHandlers; - respond: ReturnType; + respond: ExecApprovalRequestRespond; context: { broadcast: (event: string, payload: unknown) => void }; params?: Record; }) { @@ -287,14 +289,24 @@ describe("exec approval handlers", () => { async function resolveExecApproval(params: { handlers: ExecApprovalHandlers; id: string; - respond: ReturnType; + respond: ExecApprovalResolveRespond; context: { broadcast: (event: string, payload: unknown) => void }; }) { return params.handlers["exec.approval.resolve"]({ params: { id: params.id, decision: "allow-once" } as ExecApprovalResolveArgs["params"], respond: params.respond, context: toExecApprovalResolveContext(params.context), - client: { connect: { client: { id: "cli", displayName: "CLI" } } }, + client: { + connect: { + client: { + id: "cli", + displayName: "CLI", + version: "1.0.0", + platform: "test", + mode: "cli", + }, + }, + } as unknown as ExecApprovalResolveArgs["client"], req: { id: "req-2", type: "req", method: "exec.approval.resolve" }, isWebchatConnect: execApprovalNoop, }); @@ -304,7 +316,7 @@ describe("exec approval handlers", () => { const manager = new ExecApprovalManager(); const handlers = createExecApprovalHandlers(manager); const broadcasts: Array<{ event: string; payload: unknown }> = []; - const respond = vi.fn(); + const respond = vi.fn() as unknown as ExecApprovalRequestRespond; const context = { broadcast: (event: string, payload: unknown) => { broadcasts.push({ event, payload }); @@ -375,7 +387,7 @@ describe("exec approval handlers", () => { undefined, ); - const resolveRespond = vi.fn(); + const resolveRespond = vi.fn() as unknown as ExecApprovalResolveRespond; await resolveExecApproval({ handlers, id, @@ -398,7 +410,7 @@ describe("exec approval handlers", () => { const manager = new ExecApprovalManager(); const handlers = createExecApprovalHandlers(manager); const respond = vi.fn(); - const resolveRespond = vi.fn(); + const resolveRespond = vi.fn() as unknown as ExecApprovalResolveRespond; const resolveContext = { broadcast: () => {}, @@ -479,7 +491,7 @@ describe("gateway healthHandlers.status scope handling", () => { await healthHandlers.status({ respond, client: { connect: { role: "operator", scopes: ["operator.read"] } }, - } as HealthStatusHandlerParams); + } as unknown as HealthStatusHandlerParams); expect(vi.mocked(status.getStatusSummary)).toHaveBeenCalledWith({ includeSensitive: false }); expect(respond).toHaveBeenCalledWith(true, { ok: true }, undefined); @@ -493,7 +505,7 @@ describe("gateway healthHandlers.status scope handling", () => { await healthHandlers.status({ respond, client: { connect: { role: "operator", scopes: ["operator.admin"] } }, - } as HealthStatusHandlerParams); + } as unknown as HealthStatusHandlerParams); expect(vi.mocked(status.getStatusSummary)).toHaveBeenCalledWith({ includeSensitive: true }); expect(respond).toHaveBeenCalledWith(true, { ok: true }, undefined); @@ -510,7 +522,9 @@ describe("gateway mesh.plan.auto scope handling", () => { req: { id: "req-mesh-read", type: "req", method: "mesh.plan.auto", params: {} }, respond, context: {} as Parameters[0]["context"], - client: { connect: { role: "operator", scopes: ["operator.read"] } }, + client: { connect: { role: "operator", scopes: ["operator.read"] } } as unknown as Parameters< + typeof handleGatewayRequest + >[0]["client"], isWebchatConnect: () => false, extraHandlers: { "mesh.plan.auto": handler }, }); @@ -535,7 +549,9 @@ describe("gateway mesh.plan.auto scope handling", () => { req: { id: "req-mesh-write", type: "req", method: "mesh.plan.auto", params: {} }, respond, context: {} as Parameters[0]["context"], - client: { connect: { role: "operator", scopes: ["operator.write"] } }, + client: { + connect: { role: "operator", scopes: ["operator.write"] }, + } as unknown as Parameters[0]["client"], isWebchatConnect: () => false, extraHandlers: { "mesh.plan.auto": handler }, }); diff --git a/src/gateway/server.auth.e2e.test.ts b/src/gateway/server.auth.e2e.test.ts index 93732593fc..f270adb18e 100644 --- a/src/gateway/server.auth.e2e.test.ts +++ b/src/gateway/server.auth.e2e.test.ts @@ -123,10 +123,13 @@ async function sendRawConnectReq( }, }), ); - return onceMessage<{ ok: boolean; payload?: unknown; error?: { message?: string } }>( - ws, - isConnectResMessage(params.id), - ); + return onceMessage<{ + type?: string; + id?: string; + ok?: boolean; + payload?: Record | null; + error?: { message?: string }; + }>(ws, isConnectResMessage(params.id)); } async function startRateLimitedTokenServerWithPairedDeviceToken() { @@ -350,10 +353,11 @@ describe("gateway server auth/connect", () => { test("sends connect challenge on open", async () => { const ws = new WebSocket(`ws://127.0.0.1:${port}`); - const evtPromise = onceMessage<{ payload?: unknown }>( - ws, - (o) => o.type === "event" && o.event === "connect.challenge", - ); + const evtPromise = onceMessage<{ + type?: string; + event?: string; + payload?: Record | null; + }>(ws, (o) => o.type === "event" && o.event === "connect.challenge"); await new Promise((resolve) => ws.once("open", resolve)); const evt = await evtPromise; const nonce = (evt.payload as { nonce?: unknown } | undefined)?.nonce; @@ -378,7 +382,7 @@ describe("gateway server auth/connect", () => { test("rejects non-connect first request", async () => { const ws = await openWs(port); ws.send(JSON.stringify({ type: "req", id: "h1", method: "health" })); - const res = await onceMessage<{ ok: boolean; error?: unknown }>( + const res = await onceMessage<{ type?: string; id?: string; ok?: boolean; error?: unknown }>( ws, (o) => o.type === "res" && o.id === "h1", ); @@ -627,10 +631,11 @@ describe("gateway server auth/connect", () => { "x-forwarded-for": "203.0.113.10", }, }); - const challengePromise = onceMessage<{ payload?: unknown }>( - ws, - (o) => o.type === "event" && o.event === "connect.challenge", - ); + const challengePromise = onceMessage<{ + type?: string; + event?: string; + payload?: Record | null; + }>(ws, (o) => o.type === "event" && o.event === "connect.challenge"); await new Promise((resolve) => ws.once("open", resolve)); const challenge = await challengePromise; const nonce = (challenge.payload as { nonce?: unknown } | undefined)?.nonce; diff --git a/src/gateway/server.channels.e2e.test.ts b/src/gateway/server.channels.e2e.test.ts index 87661d846b..d7ee02e99e 100644 --- a/src/gateway/server.channels.e2e.test.ts +++ b/src/gateway/server.channels.e2e.test.ts @@ -26,7 +26,7 @@ const registryState = vi.hoisted(() => ({ cliRegistrars: [], services: [], diagnostics: [], - } as PluginRegistry, + } as unknown as PluginRegistry, })); vi.mock("./server-plugins.js", async () => { @@ -150,13 +150,13 @@ describe("gateway server channels", () => { const res = await rpcReq<{ channels?: Record< string, - | { - configured?: boolean; - tokenSource?: string; - probe?: unknown; - lastProbeAt?: unknown; - } - | { linked?: boolean } + { + configured?: boolean; + tokenSource?: string; + probe?: unknown; + lastProbeAt?: unknown; + linked?: boolean; + } >; }>(ws, "channels.status", { probe: false, timeoutMs: 2000 }); expect(res.ok).toBe(true); diff --git a/src/imessage/monitor.gating.test.ts b/src/imessage/monitor.gating.test.ts index f13970ede4..8180a40c89 100644 --- a/src/imessage/monitor.gating.test.ts +++ b/src/imessage/monitor.gating.test.ts @@ -56,8 +56,8 @@ function buildDispatchContextPayload(params: { cfg: OpenClawConfig; message: IMe accountId: "default", message, opts: {}, - messageText: message.text, - bodyText: message.text, + messageText: message.text ?? "", + bodyText: message.text ?? "", allowFrom: ["*"], groupAllowFrom: [], groupPolicy: "open", @@ -67,6 +67,9 @@ function buildDispatchContextPayload(params: { cfg: OpenClawConfig; message: IMe groupHistories, }); expect(decision.kind).toBe("dispatch"); + if (decision.kind !== "dispatch") { + throw new Error("expected dispatch decision"); + } const { ctxPayload } = buildIMessageInboundContext({ cfg, @@ -169,8 +172,8 @@ describe("imessage monitor gating + envelope builders", () => { accountId: "default", message, opts: {}, - messageText: message.text, - bodyText: message.text, + messageText: message.text ?? "", + bodyText: message.text ?? "", allowFrom: ["*"], groupAllowFrom: [], groupPolicy: "open", @@ -180,6 +183,9 @@ describe("imessage monitor gating + envelope builders", () => { groupHistories, }); expect(decision.kind).toBe("dispatch"); + if (decision.kind !== "dispatch") { + throw new Error("expected dispatch decision"); + } expect(decision.isGroup).toBe(true); expect(decision.route.sessionKey).toBe("agent:main:imessage:group:2"); });