Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled

Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
2026-07-05 09:36:54 +00:00
parent 3216769225
commit bedb527145
21108 changed files with 6010766 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
// Openrouter tests cover openrouter plugin behavior.
import OpenAI from "openai";
import { AuthStorage, ModelRegistry } from "openclaw/plugin-sdk/agent-sessions";
import {
registerProviderPlugin,
requireRegisteredProvider,
} from "openclaw/plugin-sdk/plugin-test-runtime";
import { describe, expect, it } from "vitest";
import plugin from "./index.js";
import { normalizeOpenRouterApiModelId } from "./models.js";
const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
const OPENROUTER_MISTRAL_PROVIDER_PREFIX = "mistralai/";
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY ?? "";
const LIVE_MODEL_REF =
process.env.OPENCLAW_LIVE_OPENROUTER_PLUGIN_MODEL?.trim() ||
"openrouter/anthropic/claude-sonnet-4.6";
const LIVE_MODEL_ID = LIVE_MODEL_REF.startsWith("openrouter/")
? LIVE_MODEL_REF
: `openrouter/${LIVE_MODEL_REF}`;
const LIVE_CACHE_MODEL_ID =
process.env.OPENCLAW_LIVE_OPENROUTER_CACHE_MODEL?.trim() || "deepseek/deepseek-v3.2";
const liveEnabled = OPENROUTER_API_KEY.trim().length > 0 && process.env.OPENCLAW_LIVE_TEST === "1";
const liveCatalogEnabled = process.env.OPENCLAW_LIVE_TEST === "1";
const describeLive = liveEnabled ? describe : describe.skip;
const describeCatalogLive = liveCatalogEnabled ? describe : describe.skip;
const describeCacheLive =
liveEnabled && process.env.OPENCLAW_LIVE_CACHE_TEST === "1" ? describe : describe.skip;
const ModelRegistryCtor = ModelRegistry as unknown as {
new (authStorage: AuthStorage, modelsJsonPath?: string): ModelRegistry;
};
type OpenRouterModelsResponse = {
data?: Array<{ id?: unknown }>;
};
const registerOpenRouterPlugin = async () =>
registerProviderPlugin({
plugin,
id: "openrouter",
name: "OpenRouter Provider",
});
function buildStableCachePrefix(): string {
return Array.from(
{ length: 700 },
(_, index) =>
`Stable OpenRouter cache probe sentence ${
index % 20
}: this prefix must stay byte-identical across repeated requests.`,
).join("\n");
}
async function completeOpenRouterChat(params: {
client: OpenAI;
messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[];
model: string;
}) {
return params.client.chat.completions.create({
model: params.model,
messages: params.messages,
max_tokens: 8,
});
}
async function expectWeatherToolCall(client: OpenAI, model: string): Promise<void> {
const response = await client.chat.completions.create({
model,
messages: [{ role: "user", content: "Call get_weather for Paris." }],
tools: [
{
type: "function",
function: {
name: "get_weather",
description: "Get the weather for a city.",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
additionalProperties: false,
},
},
},
],
tool_choice: {
type: "function",
function: { name: "get_weather" },
},
max_tokens: 64,
});
const toolCall = response.choices[0]?.message?.tool_calls?.find(
(call) => call.type === "function",
);
expect(toolCall?.type).toBe("function");
expect(toolCall?.function.name).toBe("get_weather");
expect(JSON.parse(toolCall?.function.arguments ?? "{}")).toMatchObject({ city: "Paris" });
}
async function fetchOpenRouterModelIds(): Promise<string[]> {
const response = await fetch(OPENROUTER_MODELS_URL, {
headers: { "accept-encoding": "identity" },
});
expect(response.ok).toBe(true);
const json = (await response.json()) as OpenRouterModelsResponse;
return (json.data ?? [])
.map((model) => model.id)
.filter((id): id is string => typeof id === "string" && id.length > 0);
}
describeLive("openrouter plugin live", () => {
it("normalizes a prefixed OpenRouter model and completes a live tool call", async () => {
const { providers } = await registerOpenRouterPlugin();
const provider = requireRegisteredProvider(providers, "openrouter");
const resolved = provider.resolveDynamicModel?.({
provider: "openrouter",
modelId: LIVE_MODEL_ID,
modelRegistry: new ModelRegistryCtor(AuthStorage.inMemory()),
});
if (!resolved) {
throw new Error(`openrouter provider did not resolve ${LIVE_MODEL_ID}`);
}
expect(resolved.provider).toBe("openrouter");
expect(resolved.id).toBe(LIVE_MODEL_ID);
expect(resolved.api).toBe("openai-completions");
expect(resolved.baseUrl).toBe("https://openrouter.ai/api/v1");
const normalized =
provider.normalizeResolvedModel?.({
provider: "openrouter",
modelId: resolved.id,
model: resolved,
}) ?? resolved;
expect(normalized.id).toBe(normalizeOpenRouterApiModelId(LIVE_MODEL_ID));
const client = new OpenAI({
apiKey: OPENROUTER_API_KEY,
baseURL: normalized.baseUrl,
});
const autoResolved = provider.resolveDynamicModel?.({
provider: "openrouter",
modelId: "openrouter/auto",
modelRegistry: new ModelRegistryCtor(AuthStorage.inMemory()),
});
if (!autoResolved) {
throw new Error("openrouter provider did not resolve openrouter/auto");
}
const autoModel =
provider.normalizeResolvedModel?.({
provider: "openrouter",
modelId: autoResolved.id,
model: autoResolved,
}) ?? autoResolved;
expect(autoModel.id).toBe("openrouter/auto");
await expectWeatherToolCall(client, autoModel.id);
await expectWeatherToolCall(client, normalized.id);
}, 30_000);
});
describeCatalogLive("openrouter plugin live model catalog", () => {
it("applies strict9 replay policy to current OpenRouter Mistral-family routes", async () => {
const liveMistralModelIds = (await fetchOpenRouterModelIds()).filter((id) =>
id.startsWith(OPENROUTER_MISTRAL_PROVIDER_PREFIX),
);
expect(liveMistralModelIds.length).toBeGreaterThan(0);
const { providers } = await registerOpenRouterPlugin();
const provider = requireRegisteredProvider(providers, "openrouter");
for (const modelId of liveMistralModelIds) {
const policy = provider.buildReplayPolicy?.({
provider: "openrouter",
modelApi: "openai-completions",
modelId,
} as never);
expect.soft(policy?.sanitizeToolCallIds, modelId).toBe(true);
expect.soft(policy?.toolCallIdMode, modelId).toBe("strict9");
}
}, 30_000);
});
describeCacheLive("openrouter plugin live cache", () => {
it("observes automatic cache reads for DeepSeek model refs after cache construction", async () => {
const { providers } = await registerOpenRouterPlugin();
const provider = requireRegisteredProvider(providers, "openrouter");
const resolved = provider.resolveDynamicModel?.({
provider: "openrouter",
modelId: LIVE_CACHE_MODEL_ID,
modelRegistry: new ModelRegistryCtor(AuthStorage.inMemory()),
});
if (!resolved) {
throw new Error(`openrouter provider did not resolve ${LIVE_CACHE_MODEL_ID}`);
}
const client = new OpenAI({
apiKey: OPENROUTER_API_KEY,
baseURL: resolved.baseUrl,
});
const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
{
role: "system",
content: `You are testing prompt caching.\n${buildStableCachePrefix()}`,
},
{ role: "user", content: "Reply with exactly OK." },
];
await completeOpenRouterChat({ client, model: resolved.id, messages });
await new Promise((resolve) => {
setTimeout(resolve, 2_000);
});
const cached = await completeOpenRouterChat({ client, model: resolved.id, messages });
const cachedTokens = cached.usage?.prompt_tokens_details?.cached_tokens ?? 0;
expect(cached.choices[0]?.message?.content?.trim()).toMatch(/^OK[.!]?$/);
expect(cachedTokens).toBeGreaterThan(1024);
}, 60_000);
});