Skip to content

fix: clear stale cloud model on disconnect and hide plan credits row#1079

Open
alchemistklk wants to merge 3 commits intomainfrom
fix/cloud-disconnect-model-fallback
Open

fix: clear stale cloud model on disconnect and hide plan credits row#1079
alchemistklk wants to merge 3 commits intomainfrom
fix/cloud-disconnect-model-fallback

Conversation

@alchemistklk
Copy link
Copy Markdown
Contributor

What

Clear stale cloud model reference after cloud disconnect and remove the plan credits row from the sidebar balance popup.

Why

After logging out of a Nexu cloud account, the bot could still use cloud/Link models because:

  1. defaultModelId was never reset — it kept pointing to the old cloud model
  2. ensureValidDefaultModel() was not called in the disconnect route (unlike all other cloud mutation routes)
  3. OpenClaw was not restarted on disconnect, so cached link provider credentials persisted in memory

The plan credits (套餐积分) row in the sidebar balance popup is no longer needed.

How

Cloud disconnect fix (3 changes):

  • desktop-compat-routes.ts: Add ensureValidDefaultModel() + syncAll() to cloud-disconnect handler, matching the cloud-profile/disconnect pattern. Handles BYOK fallback.
  • nexu-config-store.ts: In disconnectDesktopCloud(), check if defaultModelId was a cloud model using resolveManagedCloudModel() (already imported). If so, clear it to "". Handles the no-BYOK case.
  • container.ts: Change onCloudStateChanged condition from !hadCloud && hasCloud to hadCloud !== hasCloud so OpenClaw restarts on both connect and disconnect.

UI fix:

  • workspace-layout.tsx: Remove the recharged/plan credits row from the sidebar balance popup.

Affected areas

  • Controller (backend / API)
  • Web dashboard (React UI)

Checklist

  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes
  • pnpm generate-types run (if API routes/schemas changed)
  • No credentials or tokens in code or logs
  • No any types introduced (use unknown with narrowing)

Notes for reviewers

The OpenClaw restart via openclawProcess.stop()/.start() is a no-op in launchd mode (controller doesn't own the process). Existing sessions may still use cached link provider until they expire or OpenClaw is fully restarted by the desktop process. A follow-up may be needed to have syncAll() trigger session invalidation when model providers are removed (similar to the existing touchAnySkillMarker() approach for skills).

After cloud disconnect, the defaultModelId still pointed to a cloud/link
model, allowing the bot to keep using it. Three fixes:

1. cloud-disconnect route now calls ensureValidDefaultModel() + syncAll()
   so BYOK fallback works (matches cloud-profile/disconnect pattern)
2. disconnectDesktopCloud() clears defaultModelId when it was a cloud
   model, preventing stale refs when no BYOK is available
3. onCloudStateChanged restarts OpenClaw on both connect and disconnect
   (was connect-only) to flush cached provider credentials

Also removes the plan credits (套餐积分) row from the sidebar balance popup.
@sentry
Copy link
Copy Markdown

sentry Bot commented Apr 13, 2026

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a89239e29e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const result = await container.desktopLocalService.disconnectCloud();
await container.modelProviderService.ensureValidDefaultModel();
const { configPushed } = await container.openclawSyncService.syncAll();
return c.json({ ...result, configPushed }, 200);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update cloud-disconnect schema to include configPushed

This handler now returns configPushed, but the route is still declared with cloudDisconnectResponseSchema (currently just { ok: boolean } in packages/shared/src/schemas/provider.ts). That leaves the OpenAPI contract and generated SDK types out of sync for /api/internal/desktop/cloud-disconnect, so typed clients cannot rely on configPushed and schema-driven consumers may strip/reject it. Please either extend the shared response schema (and regenerate types) or remove the extra field from the response.

Useful? React with 👍 / 👎.

Add ensureValidDefaultModel() to onCloudStateChanged callback so that
after re-login, a cloud model is auto-selected when defaultModelId is
empty. This fixes the "No model configured" state after logout + re-login.
The nexu-runtime-model plugin reads the model ref from disk on every
turn. When selectedModelRef is "" (after cloud disconnect), the plugin
was still returning { modelOverride: "" } which let OpenClaw fall back
to the session's cached provider. Now it returns early with no override,
so OpenClaw uses whatever is in the current compiled config (which has
no link provider after disconnect).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant