When a user's config has a stale `channels.<id>` entry (e.g. `appId`
or tokens left over from an earlier install) and the plugin is no
longer on disk -- for instance because the externalized npm package
was uninstalled or pruned during an upgrade -- `handleChannelChoice`
used to dead-end with "<channel> plugin not available." and leave
onboard stuck until the user manually deleted the config entry and
re-ran the CLI.
Two discovery paths are affected:
1. The `installedCatalogEntry` branch: when
`loadScopedChannelPlugin` returns null but the catalog entry still
carries `install.npmSpec`, fall back to
`ensureChannelSetupPluginInstalled` with the same entry so onboard
can reinstall the plugin from the official catalog.
2. The bundled-enable `else` branch: with a non-empty
`channels.<id>` record, `isStaticallyChannelConfigured` drops the
channel from `installableCatalogEntries`; if the plugin is also
missing on disk (so it never enters `manifestInstalledIds`), both
discovery buckets come back empty and the channel falls through to
`enableBundledPluginForSetup`. Before delegating to that bundled
path, consult the trusted catalog via
`getTrustedChannelPluginCatalogEntry` and, if an `install.npmSpec`
is available, drive the same catalog install flow used by a fresh
pick of the channel.
Both new fallbacks re-apply the `resolveConfigDisabledHint` guard
that `enableBundledPluginForSetup` has always enforced, so an
operator-disabled channel (`plugins.entries.<id>.enabled === false`
or explicit `channels.<id>.enabled === false`) with a stale config
entry cannot be silently reinstalled or re-enabled through the
catalog path.
Both branches also keep their previous behavior when no catalog npm
spec is available (e.g. purely bundled channels), so this change is
a superset of the old flow rather than a replacement.
Affects all externalized channel plugins listed in the core
package's `files` exclusion (qqbot, bluebubbles, discord, whatsapp,
line, msteams, feishu, googlechat, nostr, zalo, zalouser,
synology-chat, tlon, twitch, and similar).
* fix(doctor): commit legacy migrations even when unrelated validation fails (#76798)
migrateLegacyConfig previously returned config: null when post-migration
validation found any issue (e.g. a missing plugin). The caller then kept
the unmigrated config as the candidate, so doctor --fix never wrote the
legacy migration to disk.
Now when validation fails after a successful migration, the migrated
config is returned with partiallyValid: true. applyLegacyCompatibilityStep
always commits the migrated config to state.candidate, ensuring
agents.defaults.llm and other known-legacy keys are cleaned up on
doctor --fix even when an unrelated provider or plugin issue blocks
the full validator.
Adds regression test asserting that candidate is updated to the migrated
shape when partiallyValid is set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fixup: extend skipPluginValidation to write path with E2E coverage
Thread skipPluginValidationOnWrite through loadAndMaybeMigrateDoctorConfig
return value into runWriteConfigHealth so replaceConfigFile bypasses plugin
validation when migration is only partially valid. Add E2E test verifying
the flag propagates end-to-end.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fixup(doctor): wire skipPluginValidation through full write path (#76800)
Clawsweeper P2 x2:
1. io.ts exported writeConfigFile wrapper now passes skipPluginValidation to
createConfigIO so both write-phase validation and post-write loadConfig
re-read honor the flag.
2. mutate.ts tryWriteSingleTopLevelIncludeMutation now skips plugin validation
when writeOptions.skipPluginValidation is set, so include-write fast path
no longer blocks safe legacy migrations with unrelated plugin errors.
Adds regression test: skipPluginValidation bypasses plugin schema rejection
on writeConfigFile and falls back to throwing when flag is not set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fixup(doctor): bail out of include fast path when skipPluginValidation is set (#76800)
Clawsweeper P2: after the include write, readConfigFileSnapshotForWrite()
calls loadConfig() which validates with plugins; refreshedSnapshot.valid is
false when an unrelated plugin issue exists, causing the include path to
throw even though skipPluginValidation was requested.
Simplest fix: return false from tryWriteSingleTopLevelIncludeMutation when
skipPluginValidation is set, letting the root writer handle the write with
plugin validation disabled end-to-end (including post-write readback via
createConfigIO({ pluginValidation: "skip" })).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(doctor): cover partial legacy migration writes
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- The PR adds `sensitive` support to wizard text prompts, routes sensitive Clack prompts through `password()`, ... preserves existing gateway secrets through masked-preview confirms, and adds tests plus a changelog entry.
- Reproducibility: yes. Source inspection shows current main routes onboarding credential entry through visibl ... y provides a concrete Windows PowerShell `openclaw onboard --install-daemon` reproduction with screenshots.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head a3db64c265.
- Required merge gates passed before the squash merge.
Prepared head SHA: a3db64c265
Review: https://github.com/openclaw/openclaw/pull/76693#issuecomment-4366253531
Co-authored-by: anurag-bg-neu <bheemappagnanamurt.a@northeastern.edu>
Reduce WebUI/Gateway latency churn by avoiding redundant session reloads, carrying session keys through transcript update events, and deferring explicit media provider discovery. Includes changelog attribution and closes the referenced runtime latency issues.
Summary:
- The PR adds a 2026.5.2 doctor repair pass for actively used configured downloadable plugins, prefers ClawHub ... pm fallback, records installed plugin state, extends upgrade-survivor coverage, and updates docs/changelog.
- Reproducibility: yes. Static inspection of current main and the PR head gives a high-confidence reproduction ... d-plugin install pass, while the PR tests the new repair-only path, success stamping, and warning behavior.
ClawSweeper fixups:
- Included follow-up commit: test: cover configured plugin install update path
- Included follow-up commit: test: isolate channel option metadata cache
- Included follow-up commit: fix: keep configured plugin repair scoped
Validation:
- ClawSweeper review passed for head d3519ce42c.
- Required merge gates passed before the squash merge.
Prepared head SHA: d3519ce42c
Review: https://github.com/openclaw/openclaw/pull/76129#issuecomment-4364120658
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Simplify plugin installation and runtime loading around package-manager-owned dependencies, with Jiti reserved for local/TS fallback paths.
Also scans npm plugin install roots so hoisted transitive dependencies are covered by dependency denylist and node_modules symlink checks.
Previously, models from unconfigured providers were shown with an
"auth missing" hint, flooding the picker with 900+ unusable entries.
Now addModelSelectOption early-returns when the provider has no auth,
so only usable models appear in /models and the web chat dropdown.
Fixes#74423
* feat(nvidia): add NVIDIA provider with onboarding flow
Add the NVIDIA build.nvidia.com API as a bundled provider. Default model
is nvidia/nvidia/nemotron-3-super-120b-a12b: first segment is the provider
id, remaining "nvidia/nemotron-3-super-120b-a12b" is the literal upstream
model id (which happens to start with "nvidia/" because NVIDIA is also the
model maker).
Supporting core change: introduce a provider capability flag
nativeIdsIncludeProviderPrefix so providers whose native catalog ids
intentionally include their provider prefix (OpenRouter) opt into self-prefix
dedupe in modelKey, without hardcoding provider names in core. Providers
whose ids merely happen to start with their own name (NVIDIA) leave the flag
unset and get the full <provider>/<model-id> concatenation.
- extensions/nvidia/*: new plugin, catalog, onboarding, tests, docs
- extensions/openrouter/index.ts: declare nativeIdsIncludeProviderPrefix
- src/plugins/types.ts: add field to ProviderPlugin
- src/plugins/registry.ts: populate self-prefix set on registration
- src/agents/provider-self-prefix.ts: sync accessor used by modelKey
- src/agents/model-ref-shared.ts: modelKey consults the flag
- test updates for affected surfaces
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(model-picker): simplify literal-prefix display to label-only
* fix(model-picker): pass workspaceDir/env to allowlist literal-prefix resolution
* chore: untrack generated baseline JSON artifacts (gitignored)
* fix(nvidia): show literal model ref in picker and onboarding notes
* fix(nvidia): show hint whenever display label differs from stored config
* fix(nvidia): drop redundant hint from Keep current label
* fix(nvidia): restore literal double-prefix display labels
* fix(picker): handle literal-prefix fast path
* fix(picker): show literal keep label
* fix(docs): update nvidia provider docs
* fix(nvidia): update test helper imports
* fix(changelog): add nvidia provider entry
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(onboarding): skip redundant install prompt when only one source exists
When the channel-setup flow asks 'Install <plugin>?' after the user has
already picked the channel in the previous menu, and the only real
install source available is npm (or local), the prompt degenerates into
'<that source> vs Skip'. The user already expressed intent by picking
the channel, so re-confirming adds friction without offering a
meaningful choice.
Resolve directly to the available source in that case. Keep the prompt
when both npm and local sources exist so the user can still pick which
to use, and keep it when no real source exists (the prompt then only
offers Skip, which is informative).
* fix ci
* fix ci
* fix(channel-setup): skip redundant install prompt when only one source exists
Add autoConfirmSingleSource opt-in parameter to promptInstallChoice /
ensureOnboardingPluginInstalled / ensureChannelSetupPluginInstalled.
When set and only one real install source (npm or local, not both)
exists, the 'Install <plugin>? / Skip' prompt is skipped and the
single source is used directly.
Only channel-setup.ts passes autoConfirmSingleSource: true — the user
already expressed intent by picking the channel in the previous menu,
so re-confirming adds friction without a meaningful choice. The
onboarding and quickstart entry points keep the existing prompt
behavior unchanged.
Also fix findBundledPluginSourceInMap mock type in
onboarding-plugin-install.test.ts to avoid TS2345.
* fix(tests): revert auto-confirm test expectations and fix mock leak
- Revert 'offers registry npm specs' test to expect the prompt
(autoConfirmSingleSource not passed)
- Revert channel-setup 'does not default to bundled local path' test
to expect the prompt
- Reset findBundledPluginSourceInMap and
resolveBundledInstallPlanForCatalogEntry mocks after the bundled
prompt test to prevent cross-test leakage
* fix ci
* docs(changelog): add #73419