diff --git a/apps/controller/src/app/container.ts b/apps/controller/src/app/container.ts index f5b2bbdeb..f6a4bed6e 100644 --- a/apps/controller/src/app/container.ts +++ b/apps/controller/src/app/container.ts @@ -152,11 +152,12 @@ export async function createContainer(): Promise { ); const githubStarVerificationService = new GithubStarVerificationService(); - // Wire cloud state change callback to sync refreshed cloud inventory without - // auto-switching the default model during startup or first-channel connect. + // Wire cloud state change callback to sync refreshed cloud inventory and + // auto-select a default model when inventory changes. configStore.onCloudStateChanged = async (change) => { + await modelProviderService.ensureValidDefaultModel(); await openclawSyncService.syncAll(); - if (!change.hadCloudInventory && change.hasCloudInventory) { + if (change.hadCloudInventory !== change.hasCloudInventory) { await openclawProcess.stop(); openclawProcess.enableAutoRestart(); openclawProcess.start(); diff --git a/apps/controller/src/routes/desktop-compat-routes.ts b/apps/controller/src/routes/desktop-compat-routes.ts index f188c3f8c..93f4704bb 100644 --- a/apps/controller/src/routes/desktop-compat-routes.ts +++ b/apps/controller/src/routes/desktop-compat-routes.ts @@ -301,8 +301,12 @@ export function registerDesktopCompatRoutes( }, }, }), - async (c) => - c.json(await container.desktopLocalService.disconnectCloud(), 200), + async (c) => { + const result = await container.desktopLocalService.disconnectCloud(); + await container.modelProviderService.ensureValidDefaultModel(); + const { configPushed } = await container.openclawSyncService.syncAll(); + return c.json({ ...result, configPushed }, 200); + }, ); app.openapi( diff --git a/apps/controller/src/store/nexu-config-store.ts b/apps/controller/src/store/nexu-config-store.ts index f733911f6..1b3a51393 100644 --- a/apps/controller/src/store/nexu-config-store.ts +++ b/apps/controller/src/store/nexu-config-store.ts @@ -2688,6 +2688,12 @@ export class NexuConfigStore { const previousCloud = readDesktopCloud(await this.getConfig()); this.abortDesktopCloudPolling(); + const config = await this.getConfig(); + const currentModelId = config.runtime.defaultModelId; + const wasCloudModel = + resolveManagedCloudModel(currentModelId, previousCloud.models ?? []) !== + null; + await this.setDesktopCloudState({ connected: false, polling: false, @@ -2699,6 +2705,11 @@ export class NexuConfigStore { apiKey: null, models: [], }); + + if (wasCloudModel) { + await this.setDefaultModel(""); + } + await this.onCloudStateChanged?.({ hadCloudInventory: (previousCloud.models?.length ?? 0) > 0, hasCloudInventory: false, diff --git a/apps/controller/static/runtime-plugins/nexu-runtime-model/index.js b/apps/controller/static/runtime-plugins/nexu-runtime-model/index.js index bcf0c814f..3e289f16b 100644 --- a/apps/controller/static/runtime-plugins/nexu-runtime-model/index.js +++ b/apps/controller/static/runtime-plugins/nexu-runtime-model/index.js @@ -44,7 +44,7 @@ const plugin = { register(api) { api.on("before_model_resolve", async () => { const state = loadState(); - if (!state) { + if (!state || !state.selectedModelRef) { return; } const slashIndex = state.selectedModelRef.indexOf("/"); @@ -63,7 +63,7 @@ const plugin = { api.on("before_prompt_build", async () => { const state = loadState(); - if (!state?.promptNotice) { + if (!state?.selectedModelRef || !state?.promptNotice) { return; } return { diff --git a/apps/web/src/layouts/workspace-layout.tsx b/apps/web/src/layouts/workspace-layout.tsx index b08336aef..47d6a49fd 100644 --- a/apps/web/src/layouts/workspace-layout.tsx +++ b/apps/web/src/layouts/workspace-layout.tsx @@ -1085,28 +1085,6 @@ function WorkspaceLayoutInner() { {sidebarCreditBreakdown.giftedBalance} -
- - {t("layout.sidebar.balancePopup.recharged")} - - - - {t( - "layout.sidebar.balancePopup.rechargedTooltip", - )} - - - - - {sidebarCreditBreakdown.planBalance} - -