From 1d7703c954dcac87d7d781f4c0440878c64b9b22 Mon Sep 17 00:00:00 2001 From: James Mtendamema Date: Wed, 15 Jul 2026 04:27:47 -0600 Subject: [PATCH 1/3] fix(zoo-gateway): enable image attach from live vision model tags Prefer models-API vision tags over the stale hardcoded allowlist so Zoo Gateway (and Vercel AI Gateway) correctly enable image attachment for models like Claude Sonnet 4.5. Co-authored-by: Cursor --- apps/vscode-e2e/src/types/global.d.ts | 2 ++ .../types/src/providers/vercel-ai-gateway.ts | 5 +++ .../__tests__/vercel-ai-gateway.spec.ts | 33 +++++++++++++++++++ .../fetchers/__tests__/zoo-gateway.spec.ts | 22 +++++++++++++ .../providers/fetchers/vercel-ai-gateway.ts | 9 +++-- 5 files changed, 69 insertions(+), 2 deletions(-) diff --git a/apps/vscode-e2e/src/types/global.d.ts b/apps/vscode-e2e/src/types/global.d.ts index 5c7fb164ad..70f09e7121 100644 --- a/apps/vscode-e2e/src/types/global.d.ts +++ b/apps/vscode-e2e/src/types/global.d.ts @@ -1,6 +1,8 @@ import type { RooCodeAPI } from "@roo-code/types" declare global { + // Ambient globals must use var; let/const are not valid here. + // eslint-disable-next-line no-var -- TypeScript ambient global declaration var api: RooCodeAPI } diff --git a/packages/types/src/providers/vercel-ai-gateway.ts b/packages/types/src/providers/vercel-ai-gateway.ts index 38e959649e..a231dd51d3 100644 --- a/packages/types/src/providers/vercel-ai-gateway.ts +++ b/packages/types/src/providers/vercel-ai-gateway.ts @@ -53,12 +53,14 @@ export const VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS = new Set([ "anthropic/claude-3-sonnet", "anthropic/claude-3.5-sonnet", "anthropic/claude-3.7-sonnet", + "anthropic/claude-haiku-4.5", "anthropic/claude-opus-4", "anthropic/claude-opus-4.1", "anthropic/claude-opus-4.5", "anthropic/claude-opus-4.6", "anthropic/claude-fable-5", "anthropic/claude-sonnet-4", + "anthropic/claude-sonnet-4.5", "anthropic/claude-sonnet-4.6", "anthropic/claude-sonnet-5", "google/gemini-1.5-flash", @@ -85,6 +87,9 @@ export const VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS = new Set([ "openai/gpt-4.5-preview", "openai/gpt-4o", "openai/gpt-4o-mini", + "openai/gpt-5", + "openai/gpt-5-mini", + "openai/gpt-5-nano", "openai/gpt-oss-120b", "openai/gpt-oss-20b", "openai/o3", diff --git a/src/api/providers/fetchers/__tests__/vercel-ai-gateway.spec.ts b/src/api/providers/fetchers/__tests__/vercel-ai-gateway.spec.ts index dfb8ad4b3b..28e08d776b 100644 --- a/src/api/providers/fetchers/__tests__/vercel-ai-gateway.spec.ts +++ b/src/api/providers/fetchers/__tests__/vercel-ai-gateway.spec.ts @@ -269,6 +269,39 @@ describe("Vercel AI Gateway Fetchers", () => { ) }) + it("prefers live vision tags over hardcoded allowlists", () => { + const taggedVision = parseVercelAiGatewayModel({ + id: "anthropic/claude-sonnet-4.5", + model: { + ...baseModel, + id: "anthropic/claude-sonnet-4.5", + tags: ["tool-use", "vision"], + }, + }) + expect(taggedVision.supportsImages).toBe(true) + + const taggedTextOnly = parseVercelAiGatewayModel({ + id: "anthropic/claude-sonnet-4", + model: { + ...baseModel, + id: "anthropic/claude-sonnet-4", + tags: ["tool-use"], + }, + }) + expect(taggedTextOnly.supportsImages).toBe(false) + }) + + it("falls back to allowlists when tags are absent", () => { + const result = parseVercelAiGatewayModel({ + id: "anthropic/claude-sonnet-4.5", + model: { + ...baseModel, + id: "anthropic/claude-sonnet-4.5", + }, + }) + expect(result.supportsImages).toBe(true) + }) + it("handles missing cache pricing", () => { const modelNoCachePricing = { ...baseModel, diff --git a/src/api/providers/fetchers/__tests__/zoo-gateway.spec.ts b/src/api/providers/fetchers/__tests__/zoo-gateway.spec.ts index 0eb4a753dc..ae9bdcc4b1 100644 --- a/src/api/providers/fetchers/__tests__/zoo-gateway.spec.ts +++ b/src/api/providers/fetchers/__tests__/zoo-gateway.spec.ts @@ -159,6 +159,28 @@ describe("Zoo Gateway Fetchers", () => { }) describe("parseZooGatewayModel", () => { + it("enables image attachment from Zoo Gateway vision tags", () => { + const result = parseZooGatewayModel({ + id: "anthropic/claude-sonnet-4.5", + model: { + id: "anthropic/claude-sonnet-4.5", + object: "model", + owned_by: "anthropic", + name: "Claude Sonnet 4.5", + context_window: 200000, + max_tokens: 64000, + type: "language", + tags: ["tool-use", "vision"], + pricing: { + input: "3.00", + output: "15.00", + }, + }, + }) + + expect(result.supportsImages).toBe(true) + }) + it("delegates to the vercel-ai-gateway parser", () => { const result = parseZooGatewayModel({ id: "anthropic/claude-sonnet-4", diff --git a/src/api/providers/fetchers/vercel-ai-gateway.ts b/src/api/providers/fetchers/vercel-ai-gateway.ts index ba748ddd51..496231adb0 100644 --- a/src/api/providers/fetchers/vercel-ai-gateway.ts +++ b/src/api/providers/fetchers/vercel-ai-gateway.ts @@ -34,6 +34,8 @@ const vercelAiGatewayModelSchema = z.object({ context_window: z.number(), max_tokens: z.number(), type: z.string(), + // Vercel AI Gateway / Zoo Gateway capability tags (e.g. "vision", "tool-use"). + tags: z.array(z.string()).optional(), pricing: vercelAiGatewayPricingSchema, }) @@ -99,8 +101,11 @@ export const parseVercelAiGatewayModel = ({ id, model }: { id: string; model: Ve const cacheReadsPrice = model.pricing?.input_cache_read ? parseApiPrice(model.pricing?.input_cache_read) : undefined const supportsPromptCache = typeof cacheWritesPrice !== "undefined" && typeof cacheReadsPrice !== "undefined" - const supportsImages = - VERCEL_AI_GATEWAY_VISION_ONLY_MODELS.has(id) || VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS.has(id) + // Prefer live capability tags from the models API; fall back to static allowlists when + // tags are absent (older Zoo Gateway catalog rows, or partial/offline fixtures). + const supportsImages = Array.isArray(model.tags) + ? model.tags.includes("vision") + : VERCEL_AI_GATEWAY_VISION_ONLY_MODELS.has(id) || VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS.has(id) const modelInfo: ModelInfo = { maxTokens: model.max_tokens, From 4e6de0a7b0718f11984249ca265af25e0f29c742 Mon Sep 17 00:00:00 2001 From: James Mtendamema Date: Wed, 15 Jul 2026 04:44:55 -0600 Subject: [PATCH 2/3] fix(zoo-gateway): drop unused eslint-disable that failed CI lint CI does not flag no-var on the ambient global declaration, so the disable became an unused-directive warning. Co-authored-by: Cursor --- apps/vscode-e2e/src/types/global.d.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/vscode-e2e/src/types/global.d.ts b/apps/vscode-e2e/src/types/global.d.ts index 70f09e7121..5c7fb164ad 100644 --- a/apps/vscode-e2e/src/types/global.d.ts +++ b/apps/vscode-e2e/src/types/global.d.ts @@ -1,8 +1,6 @@ import type { RooCodeAPI } from "@roo-code/types" declare global { - // Ambient globals must use var; let/const are not valid here. - // eslint-disable-next-line no-var -- TypeScript ambient global declaration var api: RooCodeAPI } From 42c4b8ab8f09d6b09d387417b019ec088564ed78 Mon Sep 17 00:00:00 2001 From: James Mtendamema Date: Wed, 15 Jul 2026 05:04:29 -0600 Subject: [PATCH 3/3] chore(zoo-gateway): remove redundant vision-capability comments Co-authored-by: Cursor --- src/api/providers/fetchers/vercel-ai-gateway.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/api/providers/fetchers/vercel-ai-gateway.ts b/src/api/providers/fetchers/vercel-ai-gateway.ts index 496231adb0..1c4a4279e1 100644 --- a/src/api/providers/fetchers/vercel-ai-gateway.ts +++ b/src/api/providers/fetchers/vercel-ai-gateway.ts @@ -34,7 +34,6 @@ const vercelAiGatewayModelSchema = z.object({ context_window: z.number(), max_tokens: z.number(), type: z.string(), - // Vercel AI Gateway / Zoo Gateway capability tags (e.g. "vision", "tool-use"). tags: z.array(z.string()).optional(), pricing: vercelAiGatewayPricingSchema, }) @@ -101,8 +100,6 @@ export const parseVercelAiGatewayModel = ({ id, model }: { id: string; model: Ve const cacheReadsPrice = model.pricing?.input_cache_read ? parseApiPrice(model.pricing?.input_cache_read) : undefined const supportsPromptCache = typeof cacheWritesPrice !== "undefined" && typeof cacheReadsPrice !== "undefined" - // Prefer live capability tags from the models API; fall back to static allowlists when - // tags are absent (older Zoo Gateway catalog rows, or partial/offline fixtures). const supportsImages = Array.isArray(model.tags) ? model.tags.includes("vision") : VERCEL_AI_GATEWAY_VISION_ONLY_MODELS.has(id) || VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS.has(id)