From 882ddc4665d5d2d747f2d4d1fa44b8c0b50d0b2b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 3 May 2026 00:20:58 -0700 Subject: [PATCH] fix(gateway): keep agent image attachment checks accurate --- CHANGELOG.md | 1 + .../server.models-voicewake-misc.test.ts | 21 +++++-------------- src/gateway/session-utils.ts | 4 ++-- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f99d1f2d49..f6d785c6daf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Docs: https://docs.openclaw.ai - Gateway/sessions: keep agent runtime metadata on lightweight `sessions.list` rows so model-only session patches do not make Control UI lose runtime identity. Thanks @vincentkoc. - Gateway/sessions: keep bulk `sessions.list` rows lightweight by skipping per-row transcript usage fallback, display model inference, and plugin projection, avoiding event-loop stalls in large session stores. Thanks @Marvinthebored and @vincentkoc. - Gateway/models: keep read-only `models.list` fallbacks on persisted/current metadata and configured rows while using static auth checks, so missing `models.json` files no longer runtime-load provider discovery or stall gateway after restart. Fixes #76382; refs #76360 and #75707. Thanks @trojy13, @RayWoo, @AnathemaOfficial, and @vincentkoc. +- Gateway/models: keep agent image attachment capability checks on the full catalog while preserving the read-only `models.list` path, so image sends are not rejected after static catalog fallback. - CLI/plugins: reject missing plugin ids before config writes in `plugins enable` and `plugins disable` so a typo no longer persists a stale config entry. (#73554) Thanks @ai-hpc. - Agents/sessions: preserve delivered trailing assistant replies during session-file repair so Telegram/WebChat history is not rewritten to drop already-delivered responses. Fixes #76329. Thanks @obviyus. - Gateway/chat history: preserve oversized transcript turns as explicit omitted-message placeholders while avoiding large JSONL parse stalls. Thanks @Marvinthebored and @vincentkoc. diff --git a/src/gateway/server.models-voicewake-misc.test.ts b/src/gateway/server.models-voicewake-misc.test.ts index 453634927db..e75a7da2cd7 100644 --- a/src/gateway/server.models-voicewake-misc.test.ts +++ b/src/gateway/server.models-voicewake-misc.test.ts @@ -518,7 +518,7 @@ describe("gateway server models + voicewake", () => { ); }); - test("models.list configured view includes auth-backed provider catalog entries", async () => { + test("models.list configured view does not run runtime discovery without a read-only catalog", async () => { await withEnvAsync( { ANTHROPIC_API_KEY: undefined, @@ -528,21 +528,11 @@ describe("gateway server models + voicewake", () => { async () => { await withModelsConfig({}, async () => { await seedPiCatalog(); + const discoverCallsBefore = piSdkMock.discoverCalls; const res = await listModels({ view: "configured" }); expect(res.ok).toBe(true); - expect(res.payload?.models).toEqual([ - { - id: "gpt-test-a", - name: "A-Model", - provider: "openai", - contextWindow: 8000, - }, - { - id: "gpt-test-z", - name: "gpt-test-z", - provider: "openai", - }, - ]); + expect(res.payload?.models).toEqual([]); + expect(piSdkMock.discoverCalls).toBe(discoverCallsBefore); }); }, ); @@ -654,9 +644,8 @@ describe("gateway server models + voicewake", () => { expected: [ { id: "claude-test-a", - name: "A-Model", + name: "claude-test-a", provider: "anthropic", - contextWindow: 200_000, }, { id: "gpt-test-z", diff --git a/src/gateway/session-utils.ts b/src/gateway/session-utils.ts index 18c1e967a55..564d4dfc631 100644 --- a/src/gateway/session-utils.ts +++ b/src/gateway/session-utils.ts @@ -1257,7 +1257,7 @@ export function resolveSessionModelRef( } export async function resolveGatewayModelSupportsImages(params: { - loadGatewayModelCatalog: () => Promise; + loadGatewayModelCatalog: (params?: { readOnly?: boolean }) => Promise; provider?: string; model?: string; }): Promise { @@ -1266,7 +1266,7 @@ export async function resolveGatewayModelSupportsImages(params: { } try { - const catalog = await params.loadGatewayModelCatalog(); + const catalog = await params.loadGatewayModelCatalog({ readOnly: false }); const modelEntry = findModelCatalogEntry(catalog, { provider: params.provider, modelId: params.model,