diff --git a/apps/server/src/provider/Layers/DroidAdapter.ts b/apps/server/src/provider/Layers/DroidAdapter.ts index 04f8e7ba..ca3c4c88 100644 --- a/apps/server/src/provider/Layers/DroidAdapter.ts +++ b/apps/server/src/provider/Layers/DroidAdapter.ts @@ -2052,7 +2052,6 @@ export function makeDroidAdapter( cwd, clientName: "Scient Model Discovery", }); - yield* runtime.start(); const result = yield* discoverDroidAcpModels(runtime); const commands = yield* runtime.getAvailableCommands; setDroidDiscoveryCacheEntry(commandDiscoveryCache, cacheKey, { diff --git a/apps/server/src/provider/acp/DroidAcpSupport.test.ts b/apps/server/src/provider/acp/DroidAcpSupport.test.ts index 663610f5..3c3a6687 100644 --- a/apps/server/src/provider/acp/DroidAcpSupport.test.ts +++ b/apps/server/src/provider/acp/DroidAcpSupport.test.ts @@ -246,7 +246,9 @@ describe("applyDroidAcpInteractionMode", () => { }); describe("discoverDroidAcpModels", () => { - it("reads each model's reasoning choices from session config options", async () => { + it("starts the session before reading each model's reasoning choices", async () => { + let startCalls = 0; + let started = false; let currentModel = "model-a"; const configOptions = (): ReadonlyArray => [ { @@ -283,7 +285,22 @@ describe("discoverDroidAcpModels", () => { }, ]; const runtime = { - getConfigOptions: Effect.sync(configOptions), + start: () => + Effect.sync(() => { + startCalls += 1; + started = true; + return { + sessionId: "droid-model-discovery-test", + initializeResult: { protocolVersion: 1 }, + sessionSetupResult: { + sessionId: "droid-model-discovery-test", + configOptions: configOptions(), + }, + modelConfigId: "model", + sessionSetupMethod: "new" as const, + }; + }), + getConfigOptions: Effect.sync(() => (started ? configOptions() : [])), setConfigOption: (configId: string, value: string | boolean) => { if (configId === "model") { currentModel = String(value); @@ -293,6 +310,7 @@ describe("discoverDroidAcpModels", () => { }; const result = await Effect.runPromise(discoverDroidAcpModels(runtime)); + expect(startCalls).toBe(1); expect(result.models).toEqual([ expect.objectContaining({ slug: "model-a", diff --git a/apps/server/src/provider/acp/DroidAcpSupport.ts b/apps/server/src/provider/acp/DroidAcpSupport.ts index 592386e1..e0bdb473 100644 --- a/apps/server/src/provider/acp/DroidAcpSupport.ts +++ b/apps/server/src/provider/acp/DroidAcpSupport.ts @@ -400,13 +400,18 @@ function droidModelDescriptor( } /** - * Reads the model catalog from ACP and reselects each model so Droid returns that - * model's current reasoning choices. Discovery runs in a disposable session. + * Starts the disposable ACP session, reads its model catalog, and reselects each + * model so Droid returns that model's current reasoning choices. + * + * Owning startup here keeps every discovery caller on the same lifecycle path; + * reading configuration before `session/new` completes always produces an empty + * inventory. */ export function discoverDroidAcpModels( - runtime: Pick, + runtime: Pick, ): Effect.Effect { return Effect.gen(function* () { + yield* runtime.start(); const initialOptions = yield* runtime.getConfigOptions; const modelConfig = findDroidSelectConfig(initialOptions, { id: DROID_MODEL_CONFIG_ID,