From aa146b8669ef7ee3dffd95195f79e83f871004d0 Mon Sep 17 00:00:00 2001 From: Tejas Rahane <17571323+trahane@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:54:39 -0700 Subject: [PATCH 1/4] fix(acp): reject models unavailable to the active session --- .../Adapters/AcpRegistryAdapterV2.test.ts | 12 +-- ...RegistryReplayFixtures.integration.test.ts | 87 +++++++++++-------- packages/provider-acp/src/server/adapter.ts | 13 +++ 3 files changed, 68 insertions(+), 44 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts index fab0020b7e03e..227c4babe03aa 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts @@ -771,7 +771,7 @@ describe("AcpRegistryAdapterV2", () => { ); // regression: #19519 models come from session model state, not a hardcoded list - it.effect("leaves unadvertised model IDs alone in both model selection protocols", () => + it.effect("keeps unadvertised default and auto selections agent-controlled", () => Effect.gen(function* () { const { configurations } = yield* openWithStoredPicks({ setup: { @@ -781,15 +781,7 @@ describe("AcpRegistryAdapterV2", () => { }, }, storedModel: "default", - turnModels: [ - "auto", - "removed-model", - "other", - "default", - "auto", - "hermes", - "removed-model", - ], + turnModels: ["auto", "default", "other", "default", "auto", "default", "auto"], pickFrames: [ ...prompt(), ...prompt(), diff --git a/apps/server/src/orchestration-v2/testkit/AcpRegistryReplayFixtures.integration.test.ts b/apps/server/src/orchestration-v2/testkit/AcpRegistryReplayFixtures.integration.test.ts index e0a9a233bfd9e..58a2f2d8eb28f 100644 --- a/apps/server/src/orchestration-v2/testkit/AcpRegistryReplayFixtures.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/AcpRegistryReplayFixtures.integration.test.ts @@ -32,11 +32,15 @@ describe("ACP registry replay fixtures", () => { { name: "open", firstTurn: false, code: -32602 }, { name: "switch", firstTurn: true, code: -32602 }, { name: "authentication", firstTurn: false, code: -32000 }, + { name: "restricted-config", firstTurn: false, code: -32602 }, + { name: "restricted-legacy", firstTurn: false, code: -32602 }, ])("preserves the ACP model-write $name failure through orchestration", (testCase) => Effect.gen(function* () { const name = `acp_model_write_${testCase.name}`; const sessionId = "grok-replay-session-1"; const rejectedModel = "replay-model-b"; + const restricted = testCase.name.startsWith("restricted-"); + const legacy = testCase.name === "restricted-legacy"; const modelSelection = { instanceId: ProviderInstanceId.make("acpRegistry"), model: rejectedModel, @@ -61,48 +65,63 @@ describe("ACP registry replay fixtures", () => { method: "session/new", result: { sessionId, - configOptions: [ - { - id: "model", - name: "Model", - category: "model", - type: "select", - currentValue: "replay-model-a", - options: [ - { value: "replay-model-a", name: "Model A" }, - { value: rejectedModel, name: "Model B" }, + configOptions: legacy + ? [] + : [ + { + id: "model", + name: "Model", + category: "model", + type: "select", + currentValue: "replay-model-a", + options: [ + { value: "replay-model-a", name: "Model A" }, + ...(!restricted ? [{ value: rejectedModel, name: "Model B" }] : []), + ], + }, ], - }, - ], + ...(legacy + ? { + models: { + currentModelId: "replay-model-a", + availableModels: [{ modelId: "replay-model-a", name: "Model A" }], + }, + } + : {}), }, }, }, - ...(testCase.firstTurn + ...(testCase.firstTurn || restricted ? recorded.entries .slice(sessionIndex + 1) .filter((entry) => entry.type !== "runtime_exit") : []), - { - type: "expect_outbound", - label: "session.set_model", - frame: { - kind: "request", - method: "session/set_config_option", - params: { sessionId, configId: "model", value: rejectedModel }, - }, - }, - { - type: "emit_inbound", - label: "session.set_model.rejected", - frame: { - kind: "response", - method: "session/set_config_option", - error: { - code: testCase.code, - message: testCase.code === -32602 ? "Invalid provider" : "Authentication required", - }, - }, - }, + ...(!restricted + ? [ + { + type: "expect_outbound" as const, + label: "session.set_model", + frame: { + kind: "request", + method: "session/set_config_option", + params: { sessionId, configId: "model", value: rejectedModel }, + }, + }, + { + type: "emit_inbound" as const, + label: "session.set_model.rejected", + frame: { + kind: "response", + method: "session/set_config_option", + error: { + code: testCase.code, + message: + testCase.code === -32602 ? "Invalid provider" : "Authentication required", + }, + }, + }, + ] + : []), ], } satisfies ProviderReplayTranscript; const materialized = yield* materializeFixtureInput({ diff --git a/packages/provider-acp/src/server/adapter.ts b/packages/provider-acp/src/server/adapter.ts index b88e31d203948..67a26286d516f 100644 --- a/packages/provider-acp/src/server/adapter.ts +++ b/packages/provider-acp/src/server/adapter.ts @@ -6705,6 +6705,19 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( } appliedModel = requestedModel; } + if ( + appliedModel === undefined && + requestedModel !== "default" && + requestedModel !== "auto" && + (modelOption?.type === "select" || modelState != null) + ) { + return yield* new AcpModelSelectionError({ + model: modelSelection.model, + cause: EffectAcpErrors.AcpRequestError.invalidParams( + "The requested model is not available in this session.", + ), + }); + } } if (appliedModel !== undefined) { const applied = appliedModel; From 603ef49293db9d8ee565820181614e95f2dce1d8 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Fri, 30 Oct 2026 03:28:18 +0000 Subject: [PATCH 2/4] fix(acp): revalidate selections after catalog changes --- .../Adapters/AcpRegistryAdapterV2.test.ts | 55 +++++++++++- ...RegistryReplayFixtures.integration.test.ts | 87 ++++++++----------- packages/provider-acp/src/server/adapter.ts | 29 +++++-- 3 files changed, 112 insertions(+), 59 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts index a31a8af5f3dfe..e85bdc4cafd6d 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts @@ -28,6 +28,7 @@ import * as HttpClientResponse from "effect/http/HttpClientResponse"; import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as EffectAcpErrors from "effect-acp/errors"; +import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import { @@ -466,6 +467,7 @@ describe("AcpRegistryAdapterV2", () => { readonly storedModel?: string; readonly storedModePick?: string; readonly turnModels?: ReadonlyArray; + readonly rejectedModel?: string; }) { const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; // Scope release gives the replay agent at most a second to exit, so the status @@ -603,7 +605,19 @@ describe("AcpRegistryAdapterV2", () => { configurations.push(published); current = target; } - }).pipe(Effect.scoped); + }).pipe( + Effect.match({ + onFailure: (error) => { + assert.isDefined(input.rejectedModel); + assert.equal( + makeProviderFailure({ cause: error }).message, + `The agent did not accept the model "${input.rejectedModel}". Choose another model for this provider, then retry.`, + ); + }, + onSuccess: () => assert.isUndefined(input.rejectedModel), + }), + Effect.scoped, + ); yield* Effect.all(agentExits, { discard: true }); yield* makeAcpReplayCompletenessAssertion(fileSystem, statusPath, transcript); return { configurations }; @@ -770,6 +784,45 @@ describe("AcpRegistryAdapterV2", () => { }).pipe(Effect.provide(layerTest), Effect.scoped), ); + it.effect.each(["config", "legacy", "updated-config"] as const)( + "rejects an unavailable explicit model in a %s catalog before inference", + (catalog) => + openWithStoredPicks({ + setup: + catalog === "legacy" + ? { + models: { + currentModelId: "other", + availableModels: [{ modelId: "other", name: "Other" }], + }, + } + : { + configOptions: [ + { + ...modelOption("model", "hermes"), + options: [{ value: "hermes", name: "Hermes" }], + }, + ], + }, + storedModel: catalog === "updated-config" ? "hermes" : "missing", + ...(catalog === "updated-config" ? { turnModels: ["hermes", "hermes"] } : {}), + rejectedModel: catalog === "updated-config" ? "hermes" : "missing", + pickFrames: + catalog === "updated-config" + ? [ + ...prompt( + configOptionUpdate([ + { + ...modelOption("model", "other"), + options: [{ value: "other", name: "Other" }], + }, + ]), + ), + ] + : [], + }).pipe(Effect.provide(layerTest), Effect.scoped), + ); + // regression: #19519 models come from session model state, not a hardcoded list it.effect("keeps unadvertised default and auto selections agent-controlled", () => Effect.gen(function* () { diff --git a/apps/server/src/orchestration-v2/testkit/AcpRegistryReplayFixtures.integration.test.ts b/apps/server/src/orchestration-v2/testkit/AcpRegistryReplayFixtures.integration.test.ts index 58a2f2d8eb28f..e0a9a233bfd9e 100644 --- a/apps/server/src/orchestration-v2/testkit/AcpRegistryReplayFixtures.integration.test.ts +++ b/apps/server/src/orchestration-v2/testkit/AcpRegistryReplayFixtures.integration.test.ts @@ -32,15 +32,11 @@ describe("ACP registry replay fixtures", () => { { name: "open", firstTurn: false, code: -32602 }, { name: "switch", firstTurn: true, code: -32602 }, { name: "authentication", firstTurn: false, code: -32000 }, - { name: "restricted-config", firstTurn: false, code: -32602 }, - { name: "restricted-legacy", firstTurn: false, code: -32602 }, ])("preserves the ACP model-write $name failure through orchestration", (testCase) => Effect.gen(function* () { const name = `acp_model_write_${testCase.name}`; const sessionId = "grok-replay-session-1"; const rejectedModel = "replay-model-b"; - const restricted = testCase.name.startsWith("restricted-"); - const legacy = testCase.name === "restricted-legacy"; const modelSelection = { instanceId: ProviderInstanceId.make("acpRegistry"), model: rejectedModel, @@ -65,63 +61,48 @@ describe("ACP registry replay fixtures", () => { method: "session/new", result: { sessionId, - configOptions: legacy - ? [] - : [ - { - id: "model", - name: "Model", - category: "model", - type: "select", - currentValue: "replay-model-a", - options: [ - { value: "replay-model-a", name: "Model A" }, - ...(!restricted ? [{ value: rejectedModel, name: "Model B" }] : []), - ], - }, + configOptions: [ + { + id: "model", + name: "Model", + category: "model", + type: "select", + currentValue: "replay-model-a", + options: [ + { value: "replay-model-a", name: "Model A" }, + { value: rejectedModel, name: "Model B" }, ], - ...(legacy - ? { - models: { - currentModelId: "replay-model-a", - availableModels: [{ modelId: "replay-model-a", name: "Model A" }], - }, - } - : {}), + }, + ], }, }, }, - ...(testCase.firstTurn || restricted + ...(testCase.firstTurn ? recorded.entries .slice(sessionIndex + 1) .filter((entry) => entry.type !== "runtime_exit") : []), - ...(!restricted - ? [ - { - type: "expect_outbound" as const, - label: "session.set_model", - frame: { - kind: "request", - method: "session/set_config_option", - params: { sessionId, configId: "model", value: rejectedModel }, - }, - }, - { - type: "emit_inbound" as const, - label: "session.set_model.rejected", - frame: { - kind: "response", - method: "session/set_config_option", - error: { - code: testCase.code, - message: - testCase.code === -32602 ? "Invalid provider" : "Authentication required", - }, - }, - }, - ] - : []), + { + type: "expect_outbound", + label: "session.set_model", + frame: { + kind: "request", + method: "session/set_config_option", + params: { sessionId, configId: "model", value: rejectedModel }, + }, + }, + { + type: "emit_inbound", + label: "session.set_model.rejected", + frame: { + kind: "response", + method: "session/set_config_option", + error: { + code: testCase.code, + message: testCase.code === -32602 ? "Invalid provider" : "Authentication required", + }, + }, + }, ], } satisfies ProviderReplayTranscript; const materialized = yield* materializeFixtureInput({ diff --git a/packages/provider-acp/src/server/adapter.ts b/packages/provider-acp/src/server/adapter.ts index e49ba0805fd5f..e441fda0f0f2b 100644 --- a/packages/provider-acp/src/server/adapter.ts +++ b/packages/provider-acp/src/server/adapter.ts @@ -95,7 +95,11 @@ import { resolveEmbeddedTerminalContent, type AcpClientTerminals, } from "./clientTerminals.ts"; -import { ACP_EMPTY_OPTION_VALUE, ACP_SESSION_MODE_OPTION_ID } from "./sessionConfig.ts"; +import { + ACP_EMPTY_OPTION_VALUE, + ACP_SESSION_MODE_OPTION_ID, + flattenSelectChoices, +} from "./sessionConfig.ts"; import * as AcpSessionRuntime from "./AcpSessionRuntime.ts"; import { t3AcpPromptWithInstructions, @@ -5814,6 +5818,23 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( Effect.void ); } + if (notification.update.sessionUpdate === "config_option_update") { + const selection = yield* Ref.get(activeSelection); + const modelOption = findModelConfigOption(yield* runtime.getConfigOptions); + const model = + selection === null + ? undefined + : (flavor.resolveModelId?.(selection) ?? selection.model); + if ( + model !== undefined && + modelOption?.type === "select" && + !flattenSelectChoices(modelOption.options).some( + (choice) => choice.value === model, + ) + ) { + yield* Ref.set(activeSelection, null); + } + } if ( notification.update.sessionUpdate === "config_option_update" || notification.update.sessionUpdate === "current_mode_update" @@ -6697,10 +6718,8 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( if (modelOption?.type === "select") { if ( isCustomModel || - modelOption.options.some((entry) => - "value" in entry - ? entry.value === requestedModel - : entry.options.some((choice) => choice.value === requestedModel), + flattenSelectChoices(modelOption.options).some( + (choice) => choice.value === requestedModel, ) ) { yield* runtime From 599674f4c99088c4de72fefc73aa9fa3cebb8937 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Fri, 30 Oct 2026 03:35:14 +0000 Subject: [PATCH 3/4] fix(acp): validate the effective catalog before each turn --- .../Adapters/AcpRegistryAdapterV2.test.ts | 64 ++++++++++++------- packages/provider-acp/src/server/adapter.ts | 60 ++++++++--------- 2 files changed, 72 insertions(+), 52 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts index e85bdc4cafd6d..621a50e468729 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts @@ -784,43 +784,63 @@ describe("AcpRegistryAdapterV2", () => { }).pipe(Effect.provide(layerTest), Effect.scoped), ); - it.effect.each(["config", "legacy", "updated-config"] as const)( + it.effect.each([ + "config", + "legacy", + "updated-config", + "removed-config", + "configuration-update", + ] as const)( "rejects an unavailable explicit model in a %s catalog before inference", - (catalog) => - openWithStoredPicks({ - setup: - catalog === "legacy" + (catalog) => { + const duringTurn = catalog === "updated-config" || catalog === "removed-config"; + const duringConfiguration = catalog === "configuration-update"; + const model = duringTurn || duringConfiguration ? "hermes" : "missing"; + const restrictedOption = { + ...modelOption("model", "other"), + options: [{ value: "other", name: "Other" }], + }; + return openWithStoredPicks({ + setup: { + ...(catalog === "legacy" || catalog === "removed-config" ? { models: { currentModelId: "other", availableModels: [{ modelId: "other", name: "Other" }], }, } - : { - configOptions: [ + : {}), + configOptions: + catalog === "legacy" + ? [] + : [ { ...modelOption("model", "hermes"), options: [{ value: "hermes", name: "Hermes" }], }, + ...(duringConfiguration ? [permissionModeOption("ask")] : []), ], - }, - storedModel: catalog === "updated-config" ? "hermes" : "missing", - ...(catalog === "updated-config" ? { turnModels: ["hermes", "hermes"] } : {}), - rejectedModel: catalog === "updated-config" ? "hermes" : "missing", - pickFrames: - catalog === "updated-config" + }, + storedModel: model, + ...(duringTurn ? { turnModels: [model, model] } : {}), + ...(duringConfiguration ? { storedModePick: "auto" } : {}), + rejectedModel: model, + pickFrames: duringTurn + ? prompt(configOptionUpdate(catalog === "removed-config" ? [] : [restrictedOption])) + : duringConfiguration ? [ - ...prompt( - configOptionUpdate([ - { - ...modelOption("model", "other"), - options: [{ value: "other", name: "Other" }], - }, - ]), - ), + outbound("session/set_config_option", { + sessionId: "agent-session", + configId: "permission-mode", + value: "auto", + }), + answer("session/set_config_option", { + configOptions: [restrictedOption, permissionModeOption("auto")], + }), ] : [], - }).pipe(Effect.provide(layerTest), Effect.scoped), + }).pipe(Effect.provide(layerTest), Effect.scoped); + }, ); // regression: #19519 models come from session model state, not a hardcoded list diff --git a/packages/provider-acp/src/server/adapter.ts b/packages/provider-acp/src/server/adapter.ts index e441fda0f0f2b..d7693215dad84 100644 --- a/packages/provider-acp/src/server/adapter.ts +++ b/packages/provider-acp/src/server/adapter.ts @@ -5818,23 +5818,6 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( Effect.void ); } - if (notification.update.sessionUpdate === "config_option_update") { - const selection = yield* Ref.get(activeSelection); - const modelOption = findModelConfigOption(yield* runtime.getConfigOptions); - const model = - selection === null - ? undefined - : (flavor.resolveModelId?.(selection) ?? selection.model); - if ( - model !== undefined && - modelOption?.type === "select" && - !flattenSelectChoices(modelOption.options).some( - (choice) => choice.value === model, - ) - ) { - yield* Ref.set(activeSelection, null); - } - } if ( notification.update.sessionUpdate === "config_option_update" || notification.update.sessionUpdate === "current_mode_update" @@ -6685,6 +6668,34 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( return activated; }); + const validateModelSelection = Effect.fn("AcpAdapterV2.validateModelSelection")(function* ( + modelSelection: ModelSelection, + ) { + const model = flavor.resolveModelId?.(modelSelection) ?? modelSelection.model; + if ( + flavor.applyModelSelection !== undefined || + model.length === 0 || + model === "default" || + model === "auto" || + flavor.customModels?.includes(model) === true + ) + return; + const option = findModelConfigOption(yield* runtime.getConfigOptions); + const models = (yield* Ref.get(activeSessionSetup))?.sessionSetupResult.models; + const available = + option?.type === "select" + ? flattenSelectChoices(option.options).some((choice) => choice.value === model) + : models?.availableModels.some((choice) => choice.modelId === model); + if (available === false) { + return yield* new AcpModelSelectionError({ + model: modelSelection.model, + cause: EffectAcpErrors.AcpRequestError.invalidParams( + "The requested model is not available in this session.", + ), + }); + } + }); + const configureSession = Effect.fnUntraced(function* ( startResult: AcpSessionRuntime.AcpSessionRuntimeStartResult, modelSelection: ModelSelection, @@ -6737,19 +6748,6 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( } appliedModel = requestedModel; } - if ( - appliedModel === undefined && - requestedModel !== "default" && - requestedModel !== "auto" && - (modelOption?.type === "select" || modelState != null) - ) { - return yield* new AcpModelSelectionError({ - model: modelSelection.model, - cause: EffectAcpErrors.AcpRequestError.invalidParams( - "The requested model is not available in this session.", - ), - }); - } } if (appliedModel !== undefined) { const applied = appliedModel; @@ -6904,6 +6902,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( nativeBuildConfigurationBySessionId.delete(startResult.sessionId); } } + yield* validateModelSelection(modelSelection); yield* ( flavor.onSessionConfigurationUpdate?.( yield* runtime.getConfigOptions, @@ -7400,6 +7399,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( yield* Ref.set(activeInteractionMode, turnInput.runtimePolicy.interactionMode); } } + yield* validateModelSelection(turnInput.modelSelection); yield* Ref.set(lastTurnRoute, { threadId: turnInput.threadId, providerThreadId: turnInput.providerThread.id, From ad839a7054d357757f2a59597819ffa7edae2cf6 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Fri, 30 Oct 2026 03:41:24 +0000 Subject: [PATCH 4/4] fix(acp): reject unavailable models before configuration --- .../orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts | 7 +++++-- packages/provider-acp/src/server/adapter.ts | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts index 621a50e468729..fee9ad4cbb221 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts @@ -790,11 +790,14 @@ describe("AcpRegistryAdapterV2", () => { "updated-config", "removed-config", "configuration-update", + "unavailable-before-configuration", ] as const)( "rejects an unavailable explicit model in a %s catalog before inference", (catalog) => { const duringTurn = catalog === "updated-config" || catalog === "removed-config"; const duringConfiguration = catalog === "configuration-update"; + const changesOptions = + duringConfiguration || catalog === "unavailable-before-configuration"; const model = duringTurn || duringConfiguration ? "hermes" : "missing"; const restrictedOption = { ...modelOption("model", "other"), @@ -818,12 +821,12 @@ describe("AcpRegistryAdapterV2", () => { ...modelOption("model", "hermes"), options: [{ value: "hermes", name: "Hermes" }], }, - ...(duringConfiguration ? [permissionModeOption("ask")] : []), + ...(changesOptions ? [permissionModeOption("ask")] : []), ], }, storedModel: model, ...(duringTurn ? { turnModels: [model, model] } : {}), - ...(duringConfiguration ? { storedModePick: "auto" } : {}), + ...(changesOptions ? { storedModePick: "auto" } : {}), rejectedModel: model, pickFrames: duringTurn ? prompt(configOptionUpdate(catalog === "removed-config" ? [] : [restrictedOption])) diff --git a/packages/provider-acp/src/server/adapter.ts b/packages/provider-acp/src/server/adapter.ts index d7693215dad84..effe2ef2bf0e6 100644 --- a/packages/provider-acp/src/server/adapter.ts +++ b/packages/provider-acp/src/server/adapter.ts @@ -6702,6 +6702,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, ) { yield* Ref.set(activeSelection, null); + yield* validateModelSelection(modelSelection); const requestedModel = flavor.resolveModelId?.(modelSelection) ?? modelSelection.model; // Invalid params is the agent refusing the value, and the runtime // raises the same code when the session's model option does not offer