diff --git a/docs/providers/longcat.md b/docs/providers/longcat.md
index d15b2f823ddc..335b46f5c49e 100644
--- a/docs/providers/longcat.md
+++ b/docs/providers/longcat.md
@@ -8,7 +8,7 @@ read_when:
[LongCat](https://longcat.ai) provides a hosted API for LongCat-2.0, a
reasoning model built for coding and agentic workloads. OpenClaw provides the
-official `longcat` plugin for LongCat's OpenAI-compatible endpoint.
+official LongCat plugin for LongCat's OpenAI-compatible endpoint.
| Property | Value |
| ---------- | ---------------------------------- |
@@ -77,7 +77,7 @@ the provider's expected message shape.
The built-in catalog uses LongCat's pay-as-you-go list prices in USD per million
tokens: $0.75 uncached input, $0.015 cached input, and $2.95 output. LongCat may
-offer temporary discounts; the [pricing page](https://longcat.chat/platform/docs/Pricing/LongCat-2.0.html)
+offer temporary discounts; the [pricing page](https://longcat.chat/platform/docs/pricing/long-cat-2.0)
and your billing records are authoritative.
## Self-hosted LongCat-2.0
diff --git a/extensions/longcat/doctor-contract-api.test.ts b/extensions/longcat/doctor-contract-api.test.ts
new file mode 100644
index 000000000000..4d564c924985
--- /dev/null
+++ b/extensions/longcat/doctor-contract-api.test.ts
@@ -0,0 +1,99 @@
+// LongCat tests cover the plugin-owned persisted catalog repair.
+import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
+import { describe, expect, it } from "vitest";
+import { legacyConfigRules, normalizeCompatibilityConfig } from "./doctor-contract-api.js";
+
+const LEGACY_STOCK_MODEL = {
+ id: "LongCat-2.0",
+ name: "LongCat 2.0",
+ reasoning: true,
+ input: ["text"],
+ contextWindow: 1_048_576,
+ maxTokens: 131_072,
+ cost: { input: 0.75, output: 2.95, cacheRead: 0.015, cacheWrite: 0.75 },
+ compat: {
+ supportsStore: false,
+ supportsDeveloperRole: false,
+ supportsReasoningEffort: false,
+ supportsUsageInStreaming: false,
+ supportsStrictMode: false,
+ maxTokensField: "max_tokens",
+ requiresReasoningContentOnAssistantMessages: true,
+ thinkingFormat: "deepseek",
+ },
+};
+
+function longcatConfig(models: unknown[]): OpenClawConfig {
+ return {
+ models: {
+ providers: {
+ longcat: {
+ baseUrl: "https://api.longcat.chat/openai",
+ api: "openai-completions",
+ models,
+ },
+ },
+ },
+ } as OpenClawConfig;
+}
+
+describe("LongCat doctor contract", () => {
+ it("repairs only the exact historical stock row", () => {
+ const custom = {
+ ...LEGACY_STOCK_MODEL,
+ name: "My LongCat",
+ cost: { ...LEGACY_STOCK_MODEL.cost },
+ };
+ const other = { id: "custom-model", name: "Custom" };
+ const config = longcatConfig([structuredClone(LEGACY_STOCK_MODEL), custom, other]);
+
+ expect(legacyConfigRules[0]?.match?.(config.models?.providers?.longcat?.models)).toBe(true);
+
+ const result = normalizeCompatibilityConfig({ cfg: config });
+ expect(result.changes).toEqual([
+ "Updated the historical stock LongCat-2.0 cache-write price from $0.75 to $0.",
+ ]);
+ expect(result.config.models?.providers?.longcat?.models).toEqual([
+ {
+ ...LEGACY_STOCK_MODEL,
+ cost: { ...LEGACY_STOCK_MODEL.cost, cacheWrite: 0 },
+ },
+ custom,
+ other,
+ ]);
+ expect(config.models?.providers?.longcat?.models?.[0]?.cost.cacheWrite).toBe(0.75);
+ expect(normalizeCompatibilityConfig({ cfg: result.config })).toEqual({
+ config: result.config,
+ changes: [],
+ });
+ });
+
+ it("repairs the historical row after core Doctor removes catalog-owned compat", () => {
+ const { compat: _compat, ...normalizedLegacyStockModel } = LEGACY_STOCK_MODEL;
+ const custom = {
+ ...normalizedLegacyStockModel,
+ compat: { supportsStore: true },
+ };
+ const config = longcatConfig([normalizedLegacyStockModel, custom]);
+
+ const result = normalizeCompatibilityConfig({ cfg: config });
+ expect(result.config.models?.providers?.longcat?.models).toEqual([
+ {
+ ...normalizedLegacyStockModel,
+ cost: { ...normalizedLegacyStockModel.cost, cacheWrite: 0 },
+ },
+ custom,
+ ]);
+ });
+
+ it("preserves customized prices and already-correct rows", () => {
+ for (const cacheWrite of [0, 0.5]) {
+ const model = {
+ ...LEGACY_STOCK_MODEL,
+ cost: { ...LEGACY_STOCK_MODEL.cost, cacheWrite },
+ };
+ const config = longcatConfig([model]);
+ expect(normalizeCompatibilityConfig({ cfg: config })).toEqual({ config, changes: [] });
+ }
+ });
+});
diff --git a/extensions/longcat/doctor-contract-api.ts b/extensions/longcat/doctor-contract-api.ts
new file mode 100644
index 000000000000..ef9b6bfaa5f6
--- /dev/null
+++ b/extensions/longcat/doctor-contract-api.ts
@@ -0,0 +1,104 @@
+// LongCat doctor contract repairs the historical stock model price persisted
+// by onboarding. Match the complete stock row so operator-customized models
+// and prices are never rewritten when the vendor catalog changes.
+import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
+import { asObjectRecord } from "openclaw/plugin-sdk/runtime-doctor-migrations";
+
+const MODELS_PATH = ["models", "providers", "longcat", "models"];
+const LEGACY_CACHE_WRITE_PRICE = 0.75;
+
+function isStringArray(value: unknown, expected: readonly string[]): boolean {
+ return (
+ Array.isArray(value) &&
+ value.length === expected.length &&
+ value.every((entry, index) => entry === expected[index])
+ );
+}
+
+function isLegacyStockLongCatModel(value: unknown): boolean {
+ const model = asObjectRecord(value);
+ const cost = asObjectRecord(model?.cost);
+ const compatValue = model?.compat;
+ const compat = asObjectRecord(compatValue);
+ // Core Doctor removes catalog-owned compat fields before plugin repairs run.
+ // Accept that normalized shape, but preserve rows with any divergent compat override.
+ const hasHistoricalCompat =
+ compatValue === undefined ||
+ Boolean(
+ compat &&
+ compat.supportsStore === false &&
+ compat.supportsDeveloperRole === false &&
+ compat.supportsReasoningEffort === false &&
+ compat.supportsUsageInStreaming === false &&
+ compat.supportsStrictMode === false &&
+ compat.maxTokensField === "max_tokens" &&
+ compat.requiresReasoningContentOnAssistantMessages === true &&
+ compat.thinkingFormat === "deepseek" &&
+ Object.keys(compat).length === 8,
+ );
+ return Boolean(
+ model &&
+ model.id === "LongCat-2.0" &&
+ model.name === "LongCat 2.0" &&
+ model.reasoning === true &&
+ isStringArray(model.input, ["text"]) &&
+ model.contextWindow === 1_048_576 &&
+ model.maxTokens === 131_072 &&
+ cost?.input === 0.75 &&
+ cost.output === 2.95 &&
+ cost.cacheRead === 0.015 &&
+ cost.cacheWrite === LEGACY_CACHE_WRITE_PRICE &&
+ hasHistoricalCompat,
+ );
+}
+
+function hasLegacyStockLongCatModel(value: unknown): boolean {
+ return Array.isArray(value) && value.some(isLegacyStockLongCatModel);
+}
+
+export const legacyConfigRules = [
+ {
+ path: MODELS_PATH,
+ message:
+ 'models.providers.longcat.models contains the historical stock LongCat-2.0 cache-write price; run "openclaw doctor --fix" to update it without changing customized rows.',
+ match: hasLegacyStockLongCatModel,
+ },
+];
+
+export function normalizeCompatibilityConfig({ cfg }: { cfg: OpenClawConfig }): {
+ config: OpenClawConfig;
+ changes: string[];
+} {
+ const models = asObjectRecord(cfg.models);
+ const providers = asObjectRecord(models?.providers);
+ const provider = asObjectRecord(providers?.longcat);
+ const configuredModels = provider?.models;
+ if (!hasLegacyStockLongCatModel(configuredModels) || !Array.isArray(configuredModels)) {
+ return { config: cfg, changes: [] };
+ }
+
+ const nextModels = configuredModels.map((model) => {
+ if (!isLegacyStockLongCatModel(model)) {
+ return model;
+ }
+ const row = asObjectRecord(model) ?? {};
+ const cost = asObjectRecord(row.cost) ?? {};
+ return Object.assign({}, row, {
+ cost: Object.assign({}, cost, { cacheWrite: 0 }),
+ });
+ });
+
+ return {
+ config: {
+ ...cfg,
+ models: {
+ ...models,
+ providers: {
+ ...providers,
+ longcat: { ...provider, models: nextModels },
+ },
+ } as unknown as OpenClawConfig["models"],
+ },
+ changes: ["Updated the historical stock LongCat-2.0 cache-write price from $0.75 to $0."],
+ };
+}
diff --git a/extensions/longcat/openclaw.plugin.json b/extensions/longcat/openclaw.plugin.json
index df1ef242908f..6ed442f14de3 100644
--- a/extensions/longcat/openclaw.plugin.json
+++ b/extensions/longcat/openclaw.plugin.json
@@ -2,6 +2,9 @@
"id": "longcat",
"name": "LongCat",
"description": "OpenClaw LongCat provider plugin.",
+ "doctorContract": {
+ "configRepair": true
+ },
"activation": {
"onStartup": false
},
@@ -31,7 +34,7 @@
"input": 0.75,
"output": 2.95,
"cacheRead": 0.015,
- "cacheWrite": 0.75
+ "cacheWrite": 0
},
"compat": {
"supportsStore": false,
diff --git a/ui/public/provider-icons/ATTRIBUTION.md b/ui/public/provider-icons/ATTRIBUTION.md
index dd30b166c2b7..3fd338684cda 100644
--- a/ui/public/provider-icons/ATTRIBUTION.md
+++ b/ui/public/provider-icons/ATTRIBUTION.md
@@ -34,6 +34,18 @@ by LM Studio:
- Brand guidelines:
https://lmstudio.ai/brand
+## LongCat icon
+
+`ProviderIcon-longcat.svg` is a metadata-cleaned copy of the official LongCat
+brand mark used across the LongCat API Platform (favicon and docs branding),
+contributed by the LongCat team at Meituan with permission to use it here:
+
+- Source: https://s3plus.meituan.net/aigc-media-resources/longcat/yeqian-logo.svg
+ (the favicon of https://longcat.chat/platform/docs/)
+- The white background plate was removed and the inner glyph recolored to
+ `currentColor` so the mark renders on both light and dark themes; the
+ geometry otherwise matches the cited source.
+
## llama.cpp icon
`ProviderIcon-llamacpp.svg` is a metadata-cleaned copy of
diff --git a/ui/public/provider-icons/ProviderIcon-longcat.svg b/ui/public/provider-icons/ProviderIcon-longcat.svg
new file mode 100644
index 000000000000..30ef06481eac
--- /dev/null
+++ b/ui/public/provider-icons/ProviderIcon-longcat.svg
@@ -0,0 +1 @@
+
diff --git a/ui/src/components/provider-icon.ts b/ui/src/components/provider-icon.ts
index 0f176f571108..654a9333e29c 100644
--- a/ui/src/components/provider-icon.ts
+++ b/ui/src/components/provider-icon.ts
@@ -40,6 +40,7 @@ const PROVIDER_ICON_NAMES = new Set([
"llamacpp",
"llmproxy",
"lmstudio",
+ "longcat",
"manus",
"mimo",
"minimax",
@@ -93,6 +94,7 @@ const PROVIDER_DISPLAY_LABELS: Readonly> = {
"github-copilot": "GitHub",
"llama-cpp": "llama.cpp",
lmstudio: "LM Studio",
+ longcat: "LongCat",
openai: "OpenAI",
moonshot: "Moonshot AI",
opencode: "OpenCode",