fix: resolve branch typecheck regressions

This commit is contained in:
Val Alexander
2026-03-13 19:10:35 -05:00
parent 917b26d418
commit 8ff48f0a74
11 changed files with 103 additions and 35 deletions

View File

@@ -432,7 +432,7 @@ function createToolWithScreenshotter(
function expectArtifactOnlyFileResult( function expectArtifactOnlyFileResult(
screenshotter: DiffScreenshotter, screenshotter: DiffScreenshotter,
result: { details?: Record<string, unknown> } | null | undefined, result: { details?: unknown } | null | undefined,
) { ) {
expect(screenshotter.screenshotHtml).toHaveBeenCalledTimes(1); expect(screenshotter.screenshotHtml).toHaveBeenCalledTimes(1);
expect((result?.details as Record<string, unknown>).mode).toBe("file"); expect((result?.details as Record<string, unknown>).mode).toBe("file");

View File

@@ -1,3 +1,4 @@
import type { Mock } from "vitest";
import { vi } from "vitest"; import { vi } from "vitest";
type MatrixBotSdkMockParams = { type MatrixBotSdkMockParams = {
@@ -7,7 +8,26 @@ type MatrixBotSdkMockParams = {
includeVerboseLogService?: boolean; includeVerboseLogService?: boolean;
}; };
export function createMatrixBotSdkMock(params: MatrixBotSdkMockParams = {}) { type MatrixBotSdkMock = {
ConsoleLogger: new () => {
trace: Mock<() => void>;
debug: Mock<() => void>;
info: Mock<() => void>;
warn: Mock<() => void>;
error: Mock<() => void>;
};
MatrixClient: unknown;
LogService: {
setLogger: Mock<() => void>;
warn?: Mock<() => void>;
info?: Mock<() => void>;
debug?: Mock<() => void>;
};
SimpleFsStorageProvider: unknown;
RustSdkCryptoStorageProvider: unknown;
};
export function createMatrixBotSdkMock(params: MatrixBotSdkMockParams = {}): MatrixBotSdkMock {
return { return {
ConsoleLogger: class { ConsoleLogger: class {
trace = vi.fn(); trace = vi.fn();

View File

@@ -2,9 +2,11 @@ import {
buildSglangProvider, buildSglangProvider,
configureOpenAICompatibleSelfHostedProviderNonInteractive, configureOpenAICompatibleSelfHostedProviderNonInteractive,
emptyPluginConfigSchema, emptyPluginConfigSchema,
promptAndConfigureOpenAICompatibleSelfHostedProviderAuth, promptAndConfigureOpenAICompatibleSelfHostedProvider,
type OpenClawPluginApi, type OpenClawPluginApi,
type ProviderAuthContext,
type ProviderAuthMethodNonInteractiveContext, type ProviderAuthMethodNonInteractiveContext,
type ProviderAuthResult,
type ProviderDiscoveryContext, type ProviderDiscoveryContext,
} from "openclaw/plugin-sdk/core"; } from "openclaw/plugin-sdk/core";
@@ -28,8 +30,8 @@ const sglangPlugin = {
label: "SGLang", label: "SGLang",
hint: "Fast self-hosted OpenAI-compatible server", hint: "Fast self-hosted OpenAI-compatible server",
kind: "custom", kind: "custom",
run: (ctx) => run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {
promptAndConfigureOpenAICompatibleSelfHostedProviderAuth({ const result = await promptAndConfigureOpenAICompatibleSelfHostedProvider({
cfg: ctx.config, cfg: ctx.config,
prompter: ctx.prompter, prompter: ctx.prompter,
providerId: PROVIDER_ID, providerId: PROVIDER_ID,
@@ -37,7 +39,18 @@ const sglangPlugin = {
defaultBaseUrl: DEFAULT_BASE_URL, defaultBaseUrl: DEFAULT_BASE_URL,
defaultApiKeyEnvVar: "SGLANG_API_KEY", defaultApiKeyEnvVar: "SGLANG_API_KEY",
modelPlaceholder: "Qwen/Qwen3-8B", modelPlaceholder: "Qwen/Qwen3-8B",
}), });
return {
profiles: [
{
profileId: result.profileId,
credential: result.credential,
},
],
configPatch: result.config,
defaultModel: result.modelRef,
};
},
runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) => runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) =>
configureOpenAICompatibleSelfHostedProviderNonInteractive({ configureOpenAICompatibleSelfHostedProviderNonInteractive({
ctx, ctx,

View File

@@ -1,4 +1,5 @@
import type { IncomingMessage, ServerResponse } from "node:http"; import type { IncomingMessage, ServerResponse } from "node:http";
import type { Mock } from "vitest";
import { vi } from "vitest"; import { vi } from "vitest";
export type RegisteredRoute = { export type RegisteredRoute = {
@@ -7,11 +8,13 @@ export type RegisteredRoute = {
handler: (req: IncomingMessage, res: ServerResponse) => Promise<void>; handler: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
}; };
export const registerPluginHttpRouteMock = vi.fn<(params: RegisteredRoute) => () => void>(() => export const registerPluginHttpRouteMock: Mock<(params: RegisteredRoute) => () => void> = vi.fn(
vi.fn(), () => vi.fn(),
); );
export const dispatchReplyWithBufferedBlockDispatcher = vi.fn().mockResolvedValue({ counts: {} }); export const dispatchReplyWithBufferedBlockDispatcher: Mock<
() => Promise<{ counts: Record<string, number> }>
> = vi.fn().mockResolvedValue({ counts: {} });
async function readRequestBodyWithLimitForTest(req: IncomingMessage): Promise<string> { async function readRequestBodyWithLimitForTest(req: IncomingMessage): Promise<string> {
return await new Promise<string>((resolve, reject) => { return await new Promise<string>((resolve, reject) => {

View File

@@ -85,12 +85,12 @@ type TelegramSendOptions = NonNullable<Parameters<TelegramSendFn>[2]>;
function buildTelegramSendOptions(params: { function buildTelegramSendOptions(params: {
cfg: OpenClawConfig; cfg: OpenClawConfig;
mediaUrl?: string; mediaUrl?: string | null;
mediaLocalRoots?: readonly string[]; mediaLocalRoots?: readonly string[] | null;
accountId?: string; accountId?: string | null;
replyToId?: string; replyToId?: string | null;
threadId?: string; threadId?: string | number | null;
silent?: boolean; silent?: boolean | null;
}): TelegramSendOptions { }): TelegramSendOptions {
return { return {
verbose: false, verbose: false,
@@ -108,13 +108,13 @@ async function sendTelegramOutbound(params: {
cfg: OpenClawConfig; cfg: OpenClawConfig;
to: string; to: string;
text: string; text: string;
mediaUrl?: string; mediaUrl?: string | null;
mediaLocalRoots?: readonly string[]; mediaLocalRoots?: readonly string[] | null;
accountId?: string; accountId?: string | null;
deps?: { sendTelegram?: TelegramSendFn }; deps?: { sendTelegram?: TelegramSendFn };
replyToId?: string; replyToId?: string | null;
threadId?: string; threadId?: string | number | null;
silent?: boolean; silent?: boolean | null;
}) { }) {
const send = const send =
params.deps?.sendTelegram ?? getTelegramRuntime().channel.telegram.sendMessageTelegram; params.deps?.sendTelegram ?? getTelegramRuntime().channel.telegram.sendMessageTelegram;

View File

@@ -154,8 +154,17 @@ function applyTlonSetupConfig(params: {
} }
type ResolvedTlonAccount = ReturnType<typeof resolveTlonAccount>; type ResolvedTlonAccount = ReturnType<typeof resolveTlonAccount>;
type ConfiguredTlonAccount = ResolvedTlonAccount & {
ship: string;
url: string;
code: string;
};
function resolveOutboundContext(params: { cfg: OpenClawConfig; accountId?: string; to: string }) { function resolveOutboundContext(params: {
cfg: OpenClawConfig;
accountId?: string | null;
to: string;
}) {
const account = resolveTlonAccount(params.cfg, params.accountId ?? undefined); const account = resolveTlonAccount(params.cfg, params.accountId ?? undefined);
if (!account.configured || !account.ship || !account.url || !account.code) { if (!account.configured || !account.ship || !account.url || !account.code) {
throw new Error("Tlon account not configured"); throw new Error("Tlon account not configured");
@@ -166,15 +175,15 @@ function resolveOutboundContext(params: { cfg: OpenClawConfig; accountId?: strin
throw new Error(`Invalid Tlon target. Use ${formatTargetHint()}`); throw new Error(`Invalid Tlon target. Use ${formatTargetHint()}`);
} }
return { account, parsed }; return { account: account as ConfiguredTlonAccount, parsed };
} }
function resolveReplyId(replyToId?: string, threadId?: string) { function resolveReplyId(replyToId?: string | null, threadId?: string | number | null) {
return (replyToId ?? threadId) ? String(replyToId ?? threadId) : undefined; return (replyToId ?? threadId) ? String(replyToId ?? threadId) : undefined;
} }
async function withHttpPokeAccountApi<T>( async function withHttpPokeAccountApi<T>(
account: ResolvedTlonAccount & { ship: string; url: string; code: string }, account: ConfiguredTlonAccount,
run: (api: Awaited<ReturnType<typeof createHttpPokeApi>>) => Promise<T>, run: (api: Awaited<ReturnType<typeof createHttpPokeApi>>) => Promise<T>,
) { ) {
const api = await createHttpPokeApi({ const api = await createHttpPokeApi({
@@ -241,7 +250,7 @@ const tlonOutbound: ChannelOutboundAdapter = {
shipUrl: account.url, shipUrl: account.url,
shipName: account.ship.replace(/^~/, ""), shipName: account.ship.replace(/^~/, ""),
verbose: false, verbose: false,
getCode: async () => account.code!, getCode: async () => account.code,
}); });
const uploadedUrl = mediaUrl ? await uploadImageFromUrl(mediaUrl) : undefined; const uploadedUrl = mediaUrl ? await uploadImageFromUrl(mediaUrl) : undefined;

View File

@@ -2,9 +2,11 @@ import {
buildVllmProvider, buildVllmProvider,
configureOpenAICompatibleSelfHostedProviderNonInteractive, configureOpenAICompatibleSelfHostedProviderNonInteractive,
emptyPluginConfigSchema, emptyPluginConfigSchema,
promptAndConfigureOpenAICompatibleSelfHostedProviderAuth, promptAndConfigureOpenAICompatibleSelfHostedProvider,
type OpenClawPluginApi, type OpenClawPluginApi,
type ProviderAuthContext,
type ProviderAuthMethodNonInteractiveContext, type ProviderAuthMethodNonInteractiveContext,
type ProviderAuthResult,
type ProviderDiscoveryContext, type ProviderDiscoveryContext,
} from "openclaw/plugin-sdk/core"; } from "openclaw/plugin-sdk/core";
@@ -28,8 +30,8 @@ const vllmPlugin = {
label: "vLLM", label: "vLLM",
hint: "Local/self-hosted OpenAI-compatible server", hint: "Local/self-hosted OpenAI-compatible server",
kind: "custom", kind: "custom",
run: (ctx) => run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {
promptAndConfigureOpenAICompatibleSelfHostedProviderAuth({ const result = await promptAndConfigureOpenAICompatibleSelfHostedProvider({
cfg: ctx.config, cfg: ctx.config,
prompter: ctx.prompter, prompter: ctx.prompter,
providerId: PROVIDER_ID, providerId: PROVIDER_ID,
@@ -37,7 +39,18 @@ const vllmPlugin = {
defaultBaseUrl: DEFAULT_BASE_URL, defaultBaseUrl: DEFAULT_BASE_URL,
defaultApiKeyEnvVar: "VLLM_API_KEY", defaultApiKeyEnvVar: "VLLM_API_KEY",
modelPlaceholder: "meta-llama/Meta-Llama-3-8B-Instruct", modelPlaceholder: "meta-llama/Meta-Llama-3-8B-Instruct",
}), });
return {
profiles: [
{
profileId: result.profileId,
credential: result.credential,
},
],
configPatch: result.config,
defaultModel: result.modelRef,
};
},
runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) => runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) =>
configureOpenAICompatibleSelfHostedProviderNonInteractive({ configureOpenAICompatibleSelfHostedProviderNonInteractive({
ctx, ctx,

View File

@@ -230,7 +230,9 @@ const voiceCallPlugin = {
const respondToCallMessageAction = async (params: { const respondToCallMessageAction = async (params: {
requestParams: GatewayRequestHandlerOptions["params"]; requestParams: GatewayRequestHandlerOptions["params"];
respond: GatewayRequestHandlerOptions["respond"]; respond: GatewayRequestHandlerOptions["respond"];
action: (request: Awaited<ReturnType<typeof resolveCallMessageRequest>>) => Promise<{ action: (
request: Exclude<Awaited<ReturnType<typeof resolveCallMessageRequest>>, { error: string }>,
) => Promise<{
success: boolean; success: boolean;
error?: string; error?: string;
transcript?: string; transcript?: string;

View File

@@ -27,6 +27,8 @@ export function createMockFollowupRun(
enqueuedAt: Date.now(), enqueuedAt: Date.now(),
originatingTo: "channel:C1", originatingTo: "channel:C1",
run: { run: {
agentId: "agent",
agentDir: "/tmp/agent",
sessionId: "session", sessionId: "session",
sessionKey: "main", sessionKey: "main",
messageProvider: "whatsapp", messageProvider: "whatsapp",
@@ -34,7 +36,10 @@ export function createMockFollowupRun(
sessionFile: "/tmp/session.jsonl", sessionFile: "/tmp/session.jsonl",
workspaceDir: "/tmp", workspaceDir: "/tmp",
config: {}, config: {},
skillsSnapshot: {}, skillsSnapshot: {
prompt: "",
skills: [],
},
provider: "anthropic", provider: "anthropic",
model: "claude", model: "claude",
thinkLevel: "low", thinkLevel: "low",

View File

@@ -26,7 +26,9 @@ const routeState = vi.hoisted(() => ({
})); }));
const chromeMcpMocks = vi.hoisted(() => ({ const chromeMcpMocks = vi.hoisted(() => ({
evaluateChromeMcpScript: vi.fn(async () => true), evaluateChromeMcpScript: vi.fn(
async (_params: { profileName: string; targetId: string; fn: string }) => true,
),
navigateChromeMcpPage: vi.fn(async ({ url }: { url: string }) => ({ url })), navigateChromeMcpPage: vi.fn(async ({ url }: { url: string }) => ({ url })),
takeChromeMcpScreenshot: vi.fn(async () => Buffer.from("png")), takeChromeMcpScreenshot: vi.fn(async () => Buffer.from("png")),
takeChromeMcpSnapshot: vi.fn(async () => ({ takeChromeMcpSnapshot: vi.fn(async () => ({

View File

@@ -1,7 +1,8 @@
import { vi } from "vitest"; import { vi } from "vitest";
import type { MockFn } from "../../test-utils/vitest-mock-fn.js";
export const preflightDiscordMessageMock = vi.fn(); export const preflightDiscordMessageMock: MockFn = vi.fn();
export const processDiscordMessageMock = vi.fn(); export const processDiscordMessageMock: MockFn = vi.fn();
vi.mock("./message-handler.preflight.js", () => ({ vi.mock("./message-handler.preflight.js", () => ({
preflightDiscordMessage: preflightDiscordMessageMock, preflightDiscordMessage: preflightDiscordMessageMock,