diff --git a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts index 8a68e92b89..8b8c4c6bee 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts @@ -4009,6 +4009,71 @@ describe("AcpAdapterV2", () => { }).pipe(Effect.provide(layerTest), Effect.scoped), ); + it.effect("SCRATCH-27022 model set requests", () => + Effect.gen(function* () { + const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; + const path = yield* Path.Path; + const selfInvocation = yield* resolveSelfInvocation(); + const mockAgentPath = yield* path.fromFileUrl( + new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), + ); + const protocolEvents = yield* Queue.bounded(1024); + const instanceId = ProviderInstanceId.make("acp-test"); + const adapter = yield* makeAcpAdapterV2({ + instanceId, + flavor: { + driver: ACP_TEST_DRIVER, + capabilities: AcpProviderCapabilitiesV2, + makeRuntime: makeMockRuntime({ childProcessSpawner, mockAgentPath, protocolEvents }), + }, + selfInvocation, + }); + const threadId = ThreadId.make("thread-scratch-27022"); + const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ + runtimeMode: "full-access", + interactionMode: "default", + cwd: process.cwd(), + }); + const sentModels = Effect.map(Queue.takeAll(protocolEvents), (events) => + Array.from(events) + .filter( + (event) => + event.direction === "outgoing" && + (rawProtocolMethod(event) === "session/set_model" || + (rawProtocolMethod(event) === "session/set_config_option" && + rawProtocolRequestParam(event, "configId") === "model")), + ) + .map((event) => rawProtocolMethod(event) + "=" + String(rawProtocolRequestParam(event, "value") ?? rawProtocolRequestParam(event, "modelId"))), + ).pipe(Effect.tap(() => Effect.void)); + const allOut = Effect.map(Queue.takeAll(protocolEvents), (events) => + Array.from(events).filter((e) => e.direction === "outgoing").map((e) => String(rawProtocolMethod(e)) + ":" + String(rawProtocolRequestParam(e, "configId") ?? "") + "=" + String(rawProtocolRequestParam(e, "value") ?? rawProtocolRequestParam(e, "modelId") ?? "")), + ); + const sel = (model: string) => ({ instanceId, model }) satisfies ModelSelection; + const runtime = yield* adapter.openSession({ + threadId, + providerSessionId: ProviderSessionId.make("provider-session-scratch-27022"), + modelSelection: sel("default"), + runtimePolicy, + }); + const providerThread = yield* runtime.ensureThread({ threadId, modelSelection: sel("default"), runtimePolicy }); + const turn = (model: string) => + Effect.gen(function* () { + yield* runtime.startTurn( + makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, modelSelection: sel(model), now: yield* DateTime.now }), + ); + yield* runtime.events.pipe(Stream.filter((event) => event.type === "turn.terminal"), Stream.runHead); + }); + const afterOpen = yield* allOut; + yield* turn("default"); + const afterSameTurn = yield* allOut; + yield* turn("composer-2"); + const afterSwitch = yield* allOut; + yield* turn("composer-2"); + const afterSameAgain = yield* allOut; + require("node:fs").writeFileSync("/tmp/issue-lead-27022-r1/scratch-result.json", JSON.stringify({ afterOpen, afterSameTurn, afterSwitch, afterSameAgain }, null, 1)); + }).pipe(Effect.provide(layerTest), Effect.scoped), + ); + it.live("terminalizes an empty successful foreground Bash tool when the turn completes", () => Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;