diff --git a/packages/coding-agent/src/core/model-resolver.ts b/packages/coding-agent/src/core/model-resolver.ts index e6657e9eb..bd331f690 100644 --- a/packages/coding-agent/src/core/model-resolver.ts +++ b/packages/coding-agent/src/core/model-resolver.ts @@ -128,13 +128,18 @@ function splitProviderPrefix( text: string, models: readonly Model[], ): { provider: string; modelText: string } | undefined { - const slashIndex = text.indexOf("/"); - if (slashIndex === -1) return undefined; - const prefix = text.substring(0, slashIndex).trim().toLowerCase(); - const modelText = text.substring(slashIndex + 1).trim(); - if (!prefix || !modelText) return undefined; - const provider = models.find((model) => model.provider.toLowerCase() === prefix)?.provider; - return provider ? { provider, modelText } : undefined; + // Provider ids may contain "/" themselves (e.g. URL-derived ids), so match known ids, longest first + const lowerText = text.toLowerCase(); + let best: { provider: string; modelText: string } | undefined; + for (const { provider } of models) { + const prefix = provider.trim().toLowerCase(); + if (!prefix || (best && best.provider.length >= provider.length)) continue; + const slashIndex = lowerText.indexOf("/", prefix.length); + if (slashIndex === -1 || lowerText.substring(0, slashIndex).trim() !== prefix) continue; + const modelText = text.substring(slashIndex + 1).trim(); + if (modelText) best = { provider, modelText }; + } + return best; } /** diff --git a/packages/coding-agent/test/model-resolver.test.ts b/packages/coding-agent/test/model-resolver.test.ts index f8226a2be..018f4c8d1 100644 --- a/packages/coding-agent/test/model-resolver.test.ts +++ b/packages/coding-agent/test/model-resolver.test.ts @@ -115,6 +115,8 @@ const referenceCatalog = [ referenceModel("gw-b", "claude-opus-4-8"), referenceModel("gw-b", "gw-b/free"), referenceModel("gw-b", "zz:free"), + // URL-derived provider id containing "/" (#11394) + referenceModel("llama=http://host:10800", "unsloth/qwen-gguf:BF16"), ]; type ReferenceExpected = @@ -152,6 +154,16 @@ const referenceCases: ReferenceCase[] = [ text: "openrouter/qwen/qwen3-coder:exacto:high", expected: { model: "openrouter/qwen/qwen3-coder:exacto", thinkingLevel: "high" }, }, + { + name: "provider id containing a slash", + text: "llama=http://host:10800/unsloth/qwen-gguf:BF16", + expected: { model: "llama=http://host:10800/unsloth/qwen-gguf:BF16" }, + }, + { + name: "provider id containing a slash, with thinking suffix", + text: "llama=http://host:10800/unsloth/qwen-gguf:BF16:high", + expected: { model: "llama=http://host:10800/unsloth/qwen-gguf:BF16", thinkingLevel: "high" }, + }, { name: "raw id wins when the provider prefix names no matching model", text: "openai/gpt-4o:extended",