From c388c9262713d442fde6ee152875e93a7a6e5cfd Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 28 Oct 2026 19:53:17 +0000 Subject: [PATCH 1/6] test(server): reproduce #26619, t3_thread_launch accepts unadvertised models These tests reproduce #26619: t3_thread_launch passes an explicit modelSelection straight to launchThread, so a model the provider instance does not advertise creates a thread that then fails at the provider. The test expects model_unavailable with no thread created, as delegate_task returns, and an advertised model still launching. Co-Authored-By: Claude Opus 5.5 --- .../src/mcp/toolkits/project/handlers.test.ts | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/apps/server/src/mcp/toolkits/project/handlers.test.ts b/apps/server/src/mcp/toolkits/project/handlers.test.ts index 4031654a8c826..bf12a739795a2 100644 --- a/apps/server/src/mcp/toolkits/project/handlers.test.ts +++ b/apps/server/src/mcp/toolkits/project/handlers.test.ts @@ -2,11 +2,13 @@ import { expect, it } from "@effect/vitest"; import { EnvironmentId, ProjectId, + ProviderDriverKind, ProviderInstanceId, type Project as ProjectRecord, type ModelSelection, type RuntimeMode, type OrchestrationV2ThreadShell, + type ServerProvider, } from "@t3tools/contracts"; import * as NodeCrypto from "@effect/platform-node/NodeCrypto"; import * as NodeServices from "@effect/platform-node/NodeServices"; @@ -20,6 +22,7 @@ import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; import * as DocumentText from "../../../provider/DocumentText.ts"; +import * as ProviderRegistry from "../../../provider/ProviderRegistry.ts"; import * as ThreadLaunch from "../../../orchestration-v2/ThreadLaunchService.ts"; import * as ThreadManagement from "../../../orchestration-v2/ThreadManagementService.ts"; import * as ServerConfig from "../../../config.ts"; @@ -34,6 +37,20 @@ import * as McpToolAccess from "../../McpToolAccess.ts"; import * as ProjectHandlers from "./handlers.ts"; import { ProjectToolkit } from "./tools.ts"; +const advertisedProvider = (instanceId: string, models: ReadonlyArray): ServerProvider => ({ + instanceId: ProviderInstanceId.make(instanceId), + driver: ProviderDriverKind.make(instanceId), + enabled: true, + installed: true, + version: "test", + status: "ready", + auth: { status: "authenticated" }, + checkedAt: "2026-10-28T00:00:00.000Z", + models: models.map((slug) => ({ slug, name: slug, isCustom: false, capabilities: null })), + slashCommands: [], + skills: [], +}); + const clientLaunchHarness = (input: { readonly runtimeModeCeiling: RuntimeMode; readonly defaultRuntimeMode?: RuntimeMode; @@ -126,6 +143,12 @@ const clientLaunchHarness = (input: { : Option.none(), ), }), + Layer.mock(ProviderRegistry.ProviderRegistry)({ + getProviders: Effect.succeed([ + advertisedProvider("claudeAgent", ["claude-opus", "claude-sonnet"]), + advertisedProvider("codex", ["gpt-settings"]), + ]), + }), Layer.mock(PeerForwarding.PeerForwarding)({}), Layer.mock(ManagedProjectFolders.ManagedProjectFolders)({ namedProjectsRoot: "/projects" }), DocumentText.layer.pipe(Layer.provideMerge(NodeServices.layer)), @@ -224,6 +247,53 @@ it.effect( }), ); +it.effect("a launch with a model its provider does not advertise creates no thread", () => + Effect.gen(function* () { + const launched: Array = []; + const { projectId, dependencies } = clientLaunchHarness({ + runtimeModeCeiling: "auto", + launched, + }); + const toolkit = yield* ProjectToolkit.pipe( + Effect.provide( + McpToolAccess.HandlersLayer.layer(ProjectHandlers.layer).pipe(Layer.provide(dependencies)), + ), + ); + const launchWith = (instanceId: string, model: string) => + toolkit + .handle("t3_thread_launch", { + title: "Fix", + projectId, + message: "Fix the bug", + modelSelection: { instanceId: ProviderInstanceId.make(instanceId), model }, + }) + .pipe( + Stream.unwrap, + Stream.runCollect, + Effect.map((result) => result.at(-1)?.result), + Effect.provide(dependencies), + ); + + for (const [instanceId, model] of [ + ["claudeAgent", "claude-issue-26619-unadvertised"], + ["claudeAgent", "gpt-settings"], + ["codex", "claude-opus"], + ] as const) { + expect(yield* launchWith(instanceId, model)).toMatchObject({ + code: "model_unavailable", + message: `Model ${model} is not advertised by provider ${instanceId}.`, + }); + } + expect(launched).toHaveLength(0); + + expect(yield* launchWith("claudeAgent", "claude-sonnet")).toMatchObject({ + projectId, + modelSelection: { instanceId: "claudeAgent", model: "claude-sonnet" }, + }); + expect(launched).toHaveLength(1); + }), +); + it.effect("a launch retried with the same clientRequestId replays the first one", () => Effect.gen(function* () { const launched: Array = []; From 82001a95eb80795ac479f2bd1bbce29b6cdcc631 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 28 Oct 2026 19:57:09 +0000 Subject: [PATCH 2/6] fix(server): t3_thread_launch refuses models the provider does not advertise t3_thread_launch passed an explicit modelSelection straight to launchThread, so a model the provider instance does not advertise created a thread that then failed at the provider. delegate_task already refused that with model_unavailable. The rule moves into an exported requireAdvertisedModel helper in OrchestratorMcpService, which delegate_task and t3_thread_launch both use. The launch checks only an explicit modelSelection on a new launch, before any thread is created; replays, inherited and project-default models, and unregistered instances keep their current handling. Fixes pingdotgg/t3code#26619 Co-Authored-By: Claude Opus 5.5 --- apps/server/src/mcp/OrchestratorMcpService.ts | 22 ++++++++++--------- .../src/mcp/toolkits/project/handlers.ts | 9 ++++++++ apps/server/src/mcp/toolkits/project/tools.ts | 2 ++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 8b32603a7194e..eac749980cfa5 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -336,6 +336,17 @@ function capabilityModelOption(descriptor: ProviderOptionDescriptor): Orchestrat }; } +/** A provider that lists no models accepts any slug. */ +export const requireAdvertisedModel = (provider: ServerProvider, model: string) => + provider.models.length > 0 && !provider.models.some((candidate) => candidate.slug === model) + ? Effect.fail( + failure( + "model_unavailable", + `Model ${model} is not advertised by provider ${provider.instanceId}.`, + ), + ) + : Effect.void; + function providerConstraints( provider: ServerProvider | undefined, supportsOrchestrationV2: boolean, @@ -1343,16 +1354,7 @@ const make = Effect.gen(function* () { `Provider ${instanceId} has no model available for inheritance.`, ); } - if ( - requestedModel !== undefined && - provider.models.length > 0 && - !provider.models.some((candidate) => candidate.slug === requestedModel) - ) { - return yield* failure( - "model_unavailable", - `Model ${requestedModel} is not advertised by provider ${instanceId}.`, - ); - } + if (requestedModel !== undefined) yield* requireAdvertisedModel(provider, requestedModel); const requestedOptions = input.target?.options; if (requestedOptions !== undefined) { diff --git a/apps/server/src/mcp/toolkits/project/handlers.ts b/apps/server/src/mcp/toolkits/project/handlers.ts index 950cb38c6c4fd..2391a5f4c4ca3 100644 --- a/apps/server/src/mcp/toolkits/project/handlers.ts +++ b/apps/server/src/mcp/toolkits/project/handlers.ts @@ -14,6 +14,7 @@ import * as ThreadLaunch from "../../../orchestration-v2/ThreadLaunchService.ts" import * as ThreadMessageIntake from "../../../orchestration-v2/ThreadMessageIntake.ts"; import * as Claims from "../../../orchestration-v2/AttachmentClaims.ts"; import * as Project from "../../../project/ProjectService.ts"; +import * as ProviderRegistry from "../../../provider/ProviderRegistry.ts"; import * as ManagedProjectFolders from "../../../project/ManagedProjectFolders.ts"; import * as Repositories from "../../../sourceControl/SourceControlRepositoryService.ts"; import * as GitVcsDriver from "../../../vcs/GitVcsDriver.ts"; @@ -23,6 +24,7 @@ import { formatThreadLink } from "@t3tools/shared/threadLinks"; import { resolveRuntimeMode } from "../../delegatedTaskModes.ts"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; import * as McpToolAccess from "../../McpToolAccess.ts"; +import * as OrchestrationMcp from "../../OrchestratorMcpService.ts"; import { newCommandId, readCaller, resolveProjectId, unavailable } from "../../threadAccess.ts"; import { ProjectToolkit } from "./tools.ts"; @@ -142,6 +144,13 @@ export const layer = McpToolAccess.toLayer(ProjectToolkit, { }); yield* assertProjectWorktree(project.workspaceRoot, input.workspaceStrategy.worktreePath); } + if (!replaying && input.modelSelection !== undefined) { + const { instanceId, model } = input.modelSelection; + const providers = yield* (yield* ProviderRegistry.ProviderRegistry).getProviders; + const provider = providers.find((candidate) => candidate.instanceId === instanceId); + if (provider !== undefined) + yield* OrchestrationMcp.requireAdvertisedModel(provider, model); + } // Reject unresolved base refs before accepting a launch; worktree provisioning happens later. const strategy = input.workspaceStrategy; if (!replaying && strategy?.type === "worktree") diff --git a/apps/server/src/mcp/toolkits/project/tools.ts b/apps/server/src/mcp/toolkits/project/tools.ts index 38931596868b4..fbdb4fe74c513 100644 --- a/apps/server/src/mcp/toolkits/project/tools.ts +++ b/apps/server/src/mcp/toolkits/project/tools.ts @@ -33,6 +33,7 @@ import * as ManagedProjectFolders from "../../../project/ManagedProjectFolders.t import * as ThreadManagementService from "../../../orchestration-v2/ThreadManagementService.ts"; import * as SourceControlRepositoryService from "../../../sourceControl/SourceControlRepositoryService.ts"; import * as GitVcsDriver from "../../../vcs/GitVcsDriver.ts"; +import * as ProviderRegistry from "../../../provider/ProviderRegistry.ts"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; const shared = { @@ -167,6 +168,7 @@ const ThreadLaunchTool = Tool.make("t3_thread_launch", { ServerConfig.ServerConfig, ServerSettings.ServerSettingsService, DocumentText.DocumentText, + ProviderRegistry.ProviderRegistry, ], }) .annotate(Tool.Destructive, true) From 23e3e036def93a8b3f9c3ab3f5c37854c48b91df Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 28 Oct 2026 20:22:16 +0000 Subject: [PATCH 3/6] test(server): peer box registries advertise no providers t3_thread_launch now reads ProviderRegistry.getProviders for an explicit model, so the box environments in the peer tests give their registry mock an empty provider list, which skips the check as an unregistered instance does. Co-Authored-By: Claude Opus 5.5 --- apps/server/src/peer/PeerForwarding.test.ts | 4 +++- apps/server/src/peer/RemoteDelegation.test.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/server/src/peer/PeerForwarding.test.ts b/apps/server/src/peer/PeerForwarding.test.ts index 50be40682066b..b0812d3e42c3b 100644 --- a/apps/server/src/peer/PeerForwarding.test.ts +++ b/apps/server/src/peer/PeerForwarding.test.ts @@ -224,7 +224,9 @@ const serveBox = (seen: Ref.Ref, linkSession: Ref.Ref) => ), ), Layer.provide(NodeServices.layer), - Layer.provide(Layer.mock(ProviderRegistry.ProviderRegistry)({})), + Layer.provide( + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]) }), + ), Layer.provide(Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({})), Layer.provide(Layer.mock(ScheduledTaskService.ScheduledTaskService)({})), Layer.provide(Layer.mock(SecretRequests.SecretRequests)({})), diff --git a/apps/server/src/peer/RemoteDelegation.test.ts b/apps/server/src/peer/RemoteDelegation.test.ts index a344f63c523ab..cce7093992021 100644 --- a/apps/server/src/peer/RemoteDelegation.test.ts +++ b/apps/server/src/peer/RemoteDelegation.test.ts @@ -481,7 +481,9 @@ const boxToolkitLayer = (thread: BoxThread) => { Layer.provide(NodeServices.layer), ), ), - Layer.provide(Layer.mock(ProviderRegistry.ProviderRegistry)({})), + Layer.provide( + Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([]) }), + ), Layer.provide(Layer.mock(ProviderAdapterRegistry.ProviderAdapterRegistryV2)({})), Layer.provide(Layer.mock(ScheduledTaskService.ScheduledTaskService)({})), Layer.provide( From 798adaf182bf344b8147b4dce4db43d789767058 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 28 Oct 2026 20:38:53 +0000 Subject: [PATCH 4/6] fix(server): accept advertised model aliases in requireAdvertisedModel t3_thread_launch and delegate_task refused a model given by an advertised alias such as "opus", which launched on main because the adapters resolve aliases case-insensitively. Match aliases the same way; slugs stay exact and the requested model is passed through unchanged. Co-Authored-By: Claude Opus 5.5 --- apps/server/src/mcp/OrchestratorMcpService.ts | 7 +++++- .../src/mcp/toolkits/project/handlers.test.ts | 25 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index eac749980cfa5..7ca517111bf3d 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -338,7 +338,12 @@ function capabilityModelOption(descriptor: ProviderOptionDescriptor): Orchestrat /** A provider that lists no models accepts any slug. */ export const requireAdvertisedModel = (provider: ServerProvider, model: string) => - provider.models.length > 0 && !provider.models.some((candidate) => candidate.slug === model) + provider.models.length > 0 && + !provider.models.some( + (candidate) => + candidate.slug === model || + candidate.aliases?.some((alias) => alias.toLowerCase() === model.toLowerCase()), + ) ? Effect.fail( failure( "model_unavailable", diff --git a/apps/server/src/mcp/toolkits/project/handlers.test.ts b/apps/server/src/mcp/toolkits/project/handlers.test.ts index bf12a739795a2..259a6efc48de8 100644 --- a/apps/server/src/mcp/toolkits/project/handlers.test.ts +++ b/apps/server/src/mcp/toolkits/project/handlers.test.ts @@ -37,7 +37,11 @@ import * as McpToolAccess from "../../McpToolAccess.ts"; import * as ProjectHandlers from "./handlers.ts"; import { ProjectToolkit } from "./tools.ts"; -const advertisedProvider = (instanceId: string, models: ReadonlyArray): ServerProvider => ({ +const advertisedProvider = ( + instanceId: string, + models: ReadonlyArray, + aliases: Readonly>> = {}, +): ServerProvider => ({ instanceId: ProviderInstanceId.make(instanceId), driver: ProviderDriverKind.make(instanceId), enabled: true, @@ -46,7 +50,13 @@ const advertisedProvider = (instanceId: string, models: ReadonlyArray): status: "ready", auth: { status: "authenticated" }, checkedAt: "2026-10-28T00:00:00.000Z", - models: models.map((slug) => ({ slug, name: slug, isCustom: false, capabilities: null })), + models: models.map((slug) => ({ + slug, + name: slug, + ...(aliases[slug] === undefined ? {} : { aliases: aliases[slug] }), + isCustom: false, + capabilities: null, + })), slashCommands: [], skills: [], }); @@ -145,7 +155,9 @@ const clientLaunchHarness = (input: { }), Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed([ - advertisedProvider("claudeAgent", ["claude-opus", "claude-sonnet"]), + advertisedProvider("claudeAgent", ["claude-opus", "claude-sonnet"], { + "claude-opus": ["opus"], + }), advertisedProvider("codex", ["gpt-settings"]), ]), }), @@ -278,6 +290,7 @@ it.effect("a launch with a model its provider does not advertise creates no thre ["claudeAgent", "claude-issue-26619-unadvertised"], ["claudeAgent", "gpt-settings"], ["codex", "claude-opus"], + ["codex", "opus"], ] as const) { expect(yield* launchWith(instanceId, model)).toMatchObject({ code: "model_unavailable", @@ -290,7 +303,11 @@ it.effect("a launch with a model its provider does not advertise creates no thre projectId, modelSelection: { instanceId: "claudeAgent", model: "claude-sonnet" }, }); - expect(launched).toHaveLength(1); + expect(yield* launchWith("claudeAgent", "Opus")).toMatchObject({ + projectId, + modelSelection: { instanceId: "claudeAgent", model: "Opus" }, + }); + expect(launched).toHaveLength(2); }), ); From 6084b62be7b99e9421950fd444e7ca77deda0142 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 28 Oct 2026 20:52:33 +0000 Subject: [PATCH 5/6] fix(server): delegate_task resolves option checks through a model alias requireAdvertisedModel accepted an advertised alias, but delegate_task then looked up option descriptors and the parent's model by the alias string, so an alias skipped option validation and dropped the parent's options. The helper now yields the advertised model it matched, and resolveTarget compares by its slug. The requested model string is still passed through unchanged. Co-Authored-By: Claude Opus 5.5 --- .../src/mcp/OrchestratorMcpService.test.ts | 61 +++++++++++++++++++ apps/server/src/mcp/OrchestratorMcpService.ts | 24 +++++--- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/apps/server/src/mcp/OrchestratorMcpService.test.ts b/apps/server/src/mcp/OrchestratorMcpService.test.ts index 6450f9acac2cb..08f2640fc6ad1 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.test.ts @@ -2516,6 +2516,11 @@ describe("OrchestratorMcpService provider resolution", () => { target: { options: {} }, expected: { ...inherited, options: [] }, }, + { + name: "same model by alias", + target: { model: "GPT" }, + expected: { ...inherited, model: "GPT" }, + }, { name: "change model", target: { model: "other-model" }, @@ -2572,6 +2577,7 @@ describe("OrchestratorMcpService provider resolution", () => { name: model, isCustom: false, capabilities: null, + ...(model === inherited.model ? { aliases: ["gpt"] } : {}), })), }, providerSnapshot({ instanceId: peerId, driver, model: inherited.model }), @@ -2602,6 +2608,61 @@ describe("OrchestratorMcpService provider resolution", () => { }), ); + it.effect("validates options against the model a target names by alias", () => + Effect.gen(function* () { + const dependencies = Layer.mergeAll( + NodeServices.layer, + Layer.mock(ThreadManagementService.ThreadManagementService)({ + hasReceipt: () => Effect.succeed(false), + assertProviderInstanceEnabled: () => Effect.void, + getThreadRecords: (threadId) => + Effect.succeed(threadId === parentThreadId ? parentProjection([]) : childProjection), + dispatch: () => Effect.die("an invalid target must not dispatch"), + }), + ServerSettings.layerTest(), + providerRegistryLayer([ + { + ...providerSnapshot({ + instanceId: codexInstanceId, + driver: ProviderDriverKind.make("codex"), + }), + models: [ + { + slug: "gpt-5.4", + name: "gpt-5.4", + aliases: ["gpt"], + isCustom: false, + capabilities: { + optionDescriptors: [{ id: "fastMode", label: "Fast", type: "boolean" }], + }, + }, + ], + }, + ]), + adapterRegistryLayer([codexInstanceId]), + Layer.mock(ProjectService.ProjectService)({}), + Layer.mock(SecretRequests.SecretRequests)({}), + Layer.mock(RemoteDelegation.RemoteDelegation)({}), + Layer.mock(ScheduledTaskService.ScheduledTaskService)({}), + ); + yield* Effect.gen(function* () { + const service = yield* OrchestratorMcpService.OrchestratorMcpService; + for (const model of ["gpt-5.4", "GPT"]) { + const error = yield* service + .delegateTask(scope, { + task: "Review the change.", + target: yield* decodeTarget({ model, options: { bogus: "x" } }), + mode: "async", + clientRequestId: model, + }) + .pipe(Effect.flip); + assert.equal(error.code, "invalid_request", model); + assert.include(error.message, "Unknown option bogus", model); + } + }).pipe(Effect.provide(layerOrchestratorService.pipe(Layer.provide(dependencies)))); + }), + ); + describe("resolveRuntimeMode", () => { const muse = { modelSelection: { instanceId: ProviderInstanceId.make("muse"), model: "muse-model" }, diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 7ca517111bf3d..0b7b8a70a6f4c 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -336,21 +336,22 @@ function capabilityModelOption(descriptor: ProviderOptionDescriptor): Orchestrat }; } -/** A provider that lists no models accepts any slug. */ -export const requireAdvertisedModel = (provider: ServerProvider, model: string) => - provider.models.length > 0 && - !provider.models.some( +/** Yields the model a slug or alias names; a provider that lists no models accepts any slug. */ +export const requireAdvertisedModel = (provider: ServerProvider, model: string) => { + const advertised = provider.models.find( (candidate) => candidate.slug === model || candidate.aliases?.some((alias) => alias.toLowerCase() === model.toLowerCase()), - ) + ); + return provider.models.length > 0 && advertised === undefined ? Effect.fail( failure( "model_unavailable", `Model ${model} is not advertised by provider ${provider.instanceId}.`, ), ) - : Effect.void; + : Effect.succeed(advertised); +}; function providerConstraints( provider: ServerProvider | undefined, @@ -1359,12 +1360,15 @@ const make = Effect.gen(function* () { `Provider ${instanceId} has no model available for inheritance.`, ); } - if (requestedModel !== undefined) yield* requireAdvertisedModel(provider, requestedModel); + const slug = + requestedModel === undefined + ? model + : ((yield* requireAdvertisedModel(provider, requestedModel))?.slug ?? model); const requestedOptions = input.target?.options; if (requestedOptions !== undefined) { - const descriptors = provider.models.find((candidate) => candidate.slug === model) - ?.capabilities?.optionDescriptors; + const descriptors = provider.models.find((candidate) => candidate.slug === slug)?.capabilities + ?.optionDescriptors; const invalid = invalidOptionSelections(requestedOptions, descriptors); if (invalid.length > 0) { return yield* failure( @@ -1375,7 +1379,7 @@ const make = Effect.gen(function* () { } const inheritsOptions = - instanceId === inheritedSelection.instanceId && model === inheritedSelection.model; + instanceId === inheritedSelection.instanceId && slug === inheritedSelection.model; let options = inheritsOptions ? inheritedSelection.options : undefined; if (requestedOptions !== undefined) { const selections = new Map( From 89b42becea9b0da06cdc3249d46fcfca7e380ef0 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 28 Oct 2026 20:54:01 +0000 Subject: [PATCH 6/6] fix(server): delegate_task inherits options when the parent named the same alias Co-Authored-By: Claude Opus 5.5 --- apps/server/src/mcp/OrchestratorMcpService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 0b7b8a70a6f4c..dd474d7e86baf 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -1379,7 +1379,8 @@ const make = Effect.gen(function* () { } const inheritsOptions = - instanceId === inheritedSelection.instanceId && slug === inheritedSelection.model; + instanceId === inheritedSelection.instanceId && + (model === inheritedSelection.model || slug === inheritedSelection.model); let options = inheritsOptions ? inheritedSelection.options : undefined; if (requestedOptions !== undefined) { const selections = new Map(