diff --git a/CHANGELOG.md b/CHANGELOG.md index d7b92cefa14..18b5e8c8406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai - Onboarding/setup: keep first-run config reads, plugin compatibility notices, and post-model sanity checks on cold metadata paths unless the user chooses to browse all models, avoiding full plugin/runtime catalog work between prompts. Thanks @shakkernerd. - Onboarding/auth: run manifest-owned provider auth choices through scoped setup providers so selecting OpenAI Codex browser/device auth no longer loads every provider runtime before OAuth starts. Thanks @shakkernerd. +- Onboarding/auth: keep the post-auth default-model policy lookup on manifest/setup metadata so the next prompt appears without loading broad provider runtime. Thanks @shakkernerd. - Onboarding/models: keep skip-auth and provider-scoped model picker prompts off the full global model catalog path, and cache provider catalog hook resolution so setup no longer stalls after auth on large plugin registries. Thanks @shakkernerd. - Gateway/Bonjour: suppress known @homebridge/ciao cancellation and network assertion failures through scoped process handlers so malformed mDNS packets or restricted VPS networking disable/restart Bonjour instead of crashing the gateway. Fixes #67578. Thanks @zenassist26-create. - Discord: keep late clicks on already-resolved exec approval buttons quiet when elevated mode auto-resolved the request, while still surfacing real approval submission failures. Fixes #66906. Thanks @rlerikse. diff --git a/src/wizard/setup.test.ts b/src/wizard/setup.test.ts index 5794796a530..bb0f08c6829 100644 --- a/src/wizard/setup.test.ts +++ b/src/wizard/setup.test.ts @@ -23,6 +23,8 @@ const applyAuthChoice = vi.hoisted(() => vi.fn(async (args) => ({ config: args.config })), ); const resolvePreferredProviderForAuthChoice = vi.hoisted(() => vi.fn(async () => "demo-provider")); +const resolveManifestProviderAuthChoice = vi.hoisted(() => vi.fn(() => undefined)); +const resolvePluginSetupProvider = vi.hoisted(() => vi.fn(() => undefined)); const resolveProviderPluginChoice = vi.hoisted(() => vi.fn(() => null), ); @@ -166,10 +168,17 @@ vi.mock("../commands/auth-choice.js", () => ({ warnIfModelConfigLooksOff, })); +vi.mock("../plugins/provider-auth-choices.js", () => ({ + resolveManifestProviderAuthChoice, +})); + +vi.mock("../plugins/setup-registry.js", () => ({ + resolvePluginSetupProvider, +})); + vi.mock("../plugins/provider-auth-choice.runtime.js", () => ({ resolveProviderPluginChoice, resolvePluginProviders: resolvePluginProvidersRuntime, - resolvePluginSetupProvider: vi.fn(() => undefined), })); vi.mock("../commands/model-picker.js", () => ({ @@ -960,4 +969,59 @@ describe("runSetupWizard", () => { ), ).toBe(true); }); + + it("uses manifest setup metadata for post-auth model policy without loading provider runtime", async () => { + promptDefaultModel.mockClear(); + resolvePluginProvidersRuntime.mockClear(); + resolveManifestProviderAuthChoice.mockReturnValue({ + pluginId: "openai", + providerId: "openai-codex", + methodId: "oauth", + choiceId: "openai-codex", + choiceLabel: "OpenAI Codex Browser Login", + }); + resolvePluginSetupProvider.mockReturnValue({ + id: "openai-codex", + label: "OpenAI Codex", + auth: [ + { + id: "oauth", + label: "OpenAI Codex Browser Login", + kind: "oauth", + wizard: { + modelSelection: { + allowKeepCurrent: false, + }, + }, + run: vi.fn(async () => ({ profiles: [] })), + }, + ], + }); + promptAuthChoiceGrouped.mockResolvedValueOnce("openai-codex"); + const prompter = buildWizardPrompter({}); + const runtime = createRuntime(); + + await runSetupWizard( + { + acceptRisk: true, + flow: "quickstart", + installDaemon: false, + skipSkills: true, + skipSearch: true, + skipHealth: true, + skipUi: true, + }, + runtime, + prompter, + ); + + expect(resolvePluginSetupProvider).toHaveBeenCalledWith( + expect.objectContaining({ + provider: "openai-codex", + pluginIds: ["openai"], + }), + ); + expect(resolvePluginProvidersRuntime).not.toHaveBeenCalled(); + expect(promptDefaultModel).toHaveBeenCalledWith(expect.objectContaining({ allowKeep: false })); + }); }); diff --git a/src/wizard/setup.ts b/src/wizard/setup.ts index 624600b7717..48761819632 100644 --- a/src/wizard/setup.ts +++ b/src/wizard/setup.ts @@ -1,3 +1,4 @@ +import { normalizeProviderId } from "../agents/provider-id.js"; import { formatCliCommand } from "../cli/command-format.js"; import { commitConfigWriteWithPendingPluginInstalls } from "../cli/plugins-install-record-commit.js"; import type { @@ -88,6 +89,35 @@ async function resolveAuthChoiceModelSelectionPolicy(params: { env: params.env, }); + const [{ resolveManifestProviderAuthChoice }, { resolvePluginSetupProvider }] = await Promise.all( + [import("../plugins/provider-auth-choices.js"), import("../plugins/setup-registry.js")], + ); + const manifestChoice = resolveManifestProviderAuthChoice(params.authChoice, { + config: params.config, + workspaceDir: params.workspaceDir, + env: params.env, + includeUntrustedWorkspacePlugins: false, + }); + if (manifestChoice) { + const setupProvider = resolvePluginSetupProvider({ + provider: manifestChoice.providerId, + config: params.config, + workspaceDir: params.workspaceDir, + env: params.env, + pluginIds: [manifestChoice.pluginId], + }); + const setupMethod = setupProvider?.auth.find( + (method) => normalizeProviderId(method.id) === normalizeProviderId(manifestChoice.methodId), + ); + const setupPolicy = + setupMethod?.wizard?.modelSelection ?? setupProvider?.wizard?.setup?.modelSelection; + return { + preferredProvider, + promptWhenAuthChoiceProvided: setupPolicy?.promptWhenAuthChoiceProvided === true, + allowKeepCurrent: setupPolicy?.allowKeepCurrent ?? true, + }; + } + const { resolvePluginProviders, resolveProviderPluginChoice } = await import("../plugins/provider-auth-choice.runtime.js"); const providers = resolvePluginProviders({