fix(status): tolerate malformed session model refs

This commit is contained in:
Vincent Koc
2026-05-03 10:44:55 -07:00
parent fb73f2161e
commit 96886381a3
4 changed files with 45 additions and 15 deletions

View File

@@ -29,6 +29,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Status/sessions: ignore malformed non-string persisted session provider/model metadata instead of throwing while rendering status summaries. Thanks @vincentkoc.
- CLI/config: remove only the targeted array element for `openclaw config unset array[index]` instead of replaying the unset during config write and deleting the shifted next element. Fixes #76290. Thanks @SymbolStar and @vincentkoc.
- Agents/tools: stop treating `tools.deny: ["write"]` as an implicit `apply_patch` deny; operators who want to block patch writes should deny `apply_patch` or `group:fs` explicitly. Fixes #76749. (#76795) Thanks @Nek-12 and @hclsys.
- Plugins/release: verify published plugin npm tarballs expose compiled runtime entries after publish, catching TS-only package artifacts before release closeout. Thanks @vincentkoc.

View File

@@ -448,6 +448,18 @@ describe("model-selection", () => {
model: "anthropic/claude-haiku-4.5",
});
});
it("ignores malformed persisted model metadata instead of throwing", () => {
expect(
resolvePersistedSelectedModelRef({
defaultProvider: "anthropic",
runtimeProvider: { provider: "openai" },
runtimeModel: false,
overrideProvider: ["openrouter"],
overrideModel: 123,
}),
).toBeNull();
});
});
describe("inferUniqueProviderFromConfiguredModels", () => {

View File

@@ -4,7 +4,10 @@ import {
toAgentModelListLike,
} from "../config/model-input.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "../shared/string-coerce.js";
import {
resolveAgentConfig,
resolveAgentEffectiveModelPrimary,
@@ -80,13 +83,13 @@ export { isCliProvider } from "./model-selection-cli.js";
export function resolvePersistedOverrideModelRef(params: {
defaultProvider: string;
overrideProvider?: string;
overrideModel?: string;
overrideProvider?: unknown;
overrideModel?: unknown;
allowPluginNormalization?: boolean;
}): ModelRef | null {
const defaultProvider = params.defaultProvider.trim();
const overrideProvider = params.overrideProvider?.trim();
const overrideModel = params.overrideModel?.trim();
const overrideProvider = normalizeOptionalString(params.overrideProvider);
const overrideModel = normalizeOptionalString(params.overrideModel);
if (!overrideModel) {
return null;
}
@@ -107,15 +110,15 @@ export function resolvePersistedOverrideModelRef(params: {
*/
export function resolvePersistedModelRef(params: {
defaultProvider: string;
runtimeProvider?: string;
runtimeModel?: string;
overrideProvider?: string;
overrideModel?: string;
runtimeProvider?: unknown;
runtimeModel?: unknown;
overrideProvider?: unknown;
overrideModel?: unknown;
allowPluginNormalization?: boolean;
}): ModelRef | null {
const defaultProvider = params.defaultProvider.trim();
const runtimeProvider = params.runtimeProvider?.trim();
const runtimeModel = params.runtimeModel?.trim();
const runtimeProvider = normalizeOptionalString(params.runtimeProvider);
const runtimeModel = normalizeOptionalString(params.runtimeModel);
if (runtimeModel) {
if (runtimeProvider) {
return { provider: runtimeProvider, model: runtimeModel };
@@ -144,10 +147,10 @@ export function resolvePersistedModelRef(params: {
*/
export function resolvePersistedSelectedModelRef(params: {
defaultProvider: string;
runtimeProvider?: string;
runtimeModel?: string;
overrideProvider?: string;
overrideModel?: string;
runtimeProvider?: unknown;
runtimeModel?: unknown;
overrideProvider?: unknown;
overrideModel?: unknown;
allowPluginNormalization?: boolean;
}): ModelRef | null {
const override = resolvePersistedOverrideModelRef({

View File

@@ -106,4 +106,18 @@ describe("statusSummaryRuntime.resolveSessionModelRef", () => {
model: "gpt-5.4",
});
});
it("falls back to configured defaults when persisted session model fields are malformed", () => {
expect(
statusSummaryRuntime.resolveSessionModelRef(cfg, {
modelProvider: { provider: "openai" },
model: false,
providerOverride: ["anthropic"],
modelOverride: 123,
} as never),
).toEqual({
provider: "anthropic",
model: "claude-sonnet-4-6",
});
});
});