Files
openclaw/src/agents/model-suppression.ts
hcl 1fb096f0e6 fix(models): unconditionally suppress stale openai-codex/gpt-5.4-mini inline entries (#74451) (#74655)
* fix(models): block stale openai-codex/gpt-5.4-mini inline entries via unconditional suppression (#74451)

Suppress explicitly user-configured openai-codex/gpt-5.4-mini inline entries
so a stale models config written by `openclaw doctor --fix` cannot bypass the
manifest capability block and cause repeated assistant-turn failures when the
runtime switches to that model on ChatGPT-backed Codex accounts.

Adds `unconditionalOnly` flag to `buildManifestBuiltInModelSuppressionResolver`
and a `shouldUnconditionallySuppress` helper. Inside `resolveExplicitModelWithRegistry`,
inline matches are now gated on unconditional suppressions (no `when` clause)
before returning. Conditional suppressions such as the qwen Coding Plan endpoint
guard remain bypassable by explicit user configuration, preserving the existing
`resolves explicitly configured qwen3.6-plus before Coding Plan built-in suppression`
behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(changelog): add missing reporter attribution for #74451 models suppression fix

* docs: credit codex mini suppression contributors

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Shakker <shakkerdroid@gmail.com>
2026-04-30 02:02:23 +01:00

106 lines
3.3 KiB
TypeScript

import type { OpenClawConfig } from "../config/types.openclaw.js";
import {
buildManifestBuiltInModelSuppressionResolver,
resolveManifestBuiltInModelSuppression,
} from "../plugins/manifest-model-suppression.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { normalizeProviderId } from "./provider-id.js";
function resolveBuiltInModelSuppressionFromManifest(params: {
provider?: string | null;
id?: string | null;
baseUrl?: string | null;
config?: OpenClawConfig;
unconditionalOnly?: boolean;
}) {
const provider = normalizeProviderId(params.provider ?? "");
const modelId = normalizeLowercaseStringOrEmpty(params.id);
if (!provider || !modelId) {
return undefined;
}
return resolveManifestBuiltInModelSuppression({
provider,
id: modelId,
...(params.config ? { config: params.config } : {}),
...(params.baseUrl ? { baseUrl: params.baseUrl } : {}),
unconditionalOnly: params.unconditionalOnly,
env: process.env,
});
}
function resolveBuiltInModelSuppression(params: {
provider?: string | null;
id?: string | null;
baseUrl?: string | null;
config?: OpenClawConfig;
}) {
const manifestResult = resolveBuiltInModelSuppressionFromManifest(params);
if (manifestResult?.suppress) {
return manifestResult;
}
const provider = normalizeProviderId(params.provider ?? "");
const modelId = normalizeLowercaseStringOrEmpty(params.id);
if (!provider || !modelId) {
return undefined;
}
return undefined;
}
export function shouldSuppressBuiltInModelFromManifest(params: {
provider?: string | null;
id?: string | null;
config?: OpenClawConfig;
}) {
return resolveBuiltInModelSuppressionFromManifest(params)?.suppress ?? false;
}
export function shouldSuppressBuiltInModel(params: {
provider?: string | null;
id?: string | null;
baseUrl?: string | null;
config?: OpenClawConfig;
}) {
return resolveBuiltInModelSuppression(params)?.suppress ?? false;
}
// Checks only unconditional suppressions (no `when` clause). Used for inline
// model entries where user configuration may override conditional suppressions
// (e.g. custom endpoint overrides) but not absolute provider capability blocks.
export function shouldUnconditionallySuppress(params: {
provider?: string | null;
id?: string | null;
config?: OpenClawConfig;
}): boolean {
return (
resolveBuiltInModelSuppressionFromManifest({ ...params, unconditionalOnly: true })?.suppress ??
false
);
}
export function buildSuppressedBuiltInModelError(params: {
provider?: string | null;
id?: string | null;
baseUrl?: string | null;
config?: OpenClawConfig;
}): string | undefined {
return resolveBuiltInModelSuppression(params)?.errorMessage;
}
export function buildShouldSuppressBuiltInModel(params: {
config?: OpenClawConfig;
}): (input: { provider?: string | null; id?: string | null; baseUrl?: string | null }) => boolean {
const resolver = buildManifestBuiltInModelSuppressionResolver({
config: params.config,
env: process.env,
});
return (input) => {
const provider = normalizeProviderId(input.provider ?? "");
const id = normalizeLowercaseStringOrEmpty(input.id);
if (!provider || !id) {
return false;
}
return resolver({ ...input, provider, id })?.suppress ?? false;
};
}