diff --git a/packages/coding-agent/src/modes/setup-wizard/scenes/model.ts b/packages/coding-agent/src/modes/setup-wizard/scenes/model.ts index 3a6d80bd6..d614c7c5d 100644 --- a/packages/coding-agent/src/modes/setup-wizard/scenes/model.ts +++ b/packages/coding-agent/src/modes/setup-wizard/scenes/model.ts @@ -109,7 +109,11 @@ class ModelSceneController implements SetupSceneController { this.#status = theme.fg("muted", `Saving ${selector} as the default model…`); this.host.requestRender(); try { - await this.host.ctx.session.setModel(model, "default", { selector, persist: true }); + const projectScope = this.host.ctx.settings.get("modelRoleStorage") === "project"; + await this.host.ctx.session.setModel(model, "default", { selector, persist: !projectScope }); + if (projectScope) { + this.host.ctx.settings.setProjectModelRole("default", selector); + } await this.host.ctx.settings.flush(); if (!this.#disposed) this.host.finish("done"); } catch (error) { diff --git a/packages/coding-agent/test/setup-wizard.test.ts b/packages/coding-agent/test/setup-wizard.test.ts index 309fa19d9..5aecf6376 100644 --- a/packages/coding-agent/test/setup-wizard.test.ts +++ b/packages/coding-agent/test/setup-wizard.test.ts @@ -113,21 +113,21 @@ describe("setup wizard scene selection", () => { }); describe("setup wizard model selection", () => { - it("discovers and saves an uncached custom model as the default", async () => { + const CUSTOM_MODEL: Model = buildModel({ + id: "minimax-m3", + name: "MiniMax M3", + api: "openai-completions", + provider: "spark", + baseUrl: "http://127.0.0.1:8000/v1", + reasoning: true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 100_000, + maxTokens: 32_000, + }); + + async function pickModelDuringSetup(settings: Settings): Promise { await initTheme(false, "unicode", false, "titanium", "dark"); - const settings = Settings.isolated(); - const model: Model = buildModel({ - id: "minimax-m3", - name: "MiniMax M3", - api: "openai-completions", - provider: "spark", - baseUrl: "http://127.0.0.1:8000/v1", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 100_000, - maxTokens: 32_000, - }); let available: Model[] = []; const finished = Promise.withResolvers(); const setModel = mock( @@ -151,7 +151,7 @@ describe("setup wizard model selection", () => { getAvailable: () => available, getAll: () => available, refresh: async (strategy: string) => { - if (strategy === "online-if-uncached") available = [model]; + if (strategy === "online-if-uncached") available = [CUSTOM_MODEL]; }, }, setModel, @@ -171,10 +171,27 @@ describe("setup wizard model selection", () => { await controller.onMount?.(); expect(controller.render?.(120).join("\n")).toContain("minimax-m3"); controller.handleInput?.("\r"); - const result = await finished.promise; + return finished.promise; + } + + it("discovers and saves an uncached custom model as the global default", async () => { + const settings = Settings.isolated(); + + const result = await pickModelDuringSetup(settings); - expect(settings.getModelRole("default")).toBe("spark/minimax-m3"); expect(result).toBe("done"); + expect(settings.getGlobalModelRole("default")).toBe("spark/minimax-m3"); + expect(settings.getProjectModelRole("default")).toBeUndefined(); + }); + + it("saves to the project layer under project role storage", async () => { + const settings = Settings.isolated({ modelRoleStorage: "project" }); + + const result = await pickModelDuringSetup(settings); + + expect(result).toBe("done"); + expect(settings.getProjectModelRole("default")).toBe("spark/minimax-m3"); + expect(settings.getGlobalModelRole("default")).toBeUndefined(); }); });