diff --git a/docs/concepts/oauth.md b/docs/concepts/oauth.md
index 784e54271698..a25ee5b0144e 100644
--- a/docs/concepts/oauth.md
+++ b/docs/concepts/oauth.md
@@ -3,7 +3,7 @@ summary: "OAuth in OpenClaw: token exchange, storage, and multi-account patterns
read_when:
- You want to understand OpenClaw OAuth end-to-end
- You hit token invalidation / logout issues
- - You want setup-token or OAuth auth flows
+ - You want setup-token, Claude CLI, or OAuth auth flows
- You want multiple accounts or profile routing
title: "OAuth"
---
@@ -153,10 +153,14 @@ Claude CLI path:
3. store no new auth profile; switch model selection to `claude-cli/...`
4. keep existing Anthropic auth profiles for rollback
-Wizard paths:
+Interactive assistant path:
-- `openclaw onboard` → auth choice `anthropic-cli`
-- `openclaw onboard` → auth choice `setup-token` (Anthropic)
+- `openclaw onboard` / `openclaw configure` → auth choice `anthropic-cli`
+
+Manual setup-token path:
+
+- `openclaw models auth setup-token --provider anthropic`
+- `openclaw models auth paste-token --provider anthropic`
### OpenAI Codex (ChatGPT OAuth)
diff --git a/docs/gateway/authentication.md b/docs/gateway/authentication.md
index ad055ee4b9b7..a542b31e5008 100644
--- a/docs/gateway/authentication.md
+++ b/docs/gateway/authentication.md
@@ -158,6 +158,10 @@ Onboarding shortcut:
openclaw onboard --auth-choice anthropic-cli
```
+Interactive `openclaw onboard` and `openclaw configure` prefer Claude CLI for
+Anthropic and do not show setup-token in the assistant picker. Setup-token
+remains supported through the manual commands above.
+
## Checking model auth status
```bash
diff --git a/docs/help/faq.md b/docs/help/faq.md
index ade10109b30c..ecb7381cc55a 100644
--- a/docs/help/faq.md
+++ b/docs/help/faq.md
@@ -590,7 +590,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
- `claude setup-token` generates a **token string** via the Claude Code CLI (it is not available in the web console). You can run it on **any machine**. Choose **Anthropic token (paste setup-token)** in onboarding or paste it with `openclaw models auth paste-token --provider anthropic`. The token is stored as an auth profile for the **anthropic** provider and used like an API key (no auto-refresh). More detail: [OAuth](/concepts/oauth).
+ `claude setup-token` generates a **token string** via the Claude Code CLI (it is not available in the web console). You can run it on **any machine**. Interactive onboarding/configure no longer shows setup-token as an assistant choice; use `openclaw models auth setup-token --provider anthropic` or paste an existing token with `openclaw models auth paste-token --provider anthropic`. The token is stored as an auth profile for the **anthropic** provider and used like an API key (no auto-refresh). More detail: [OAuth](/concepts/oauth).
@@ -600,17 +600,17 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
claude setup-token
```
- Copy the token it prints, then choose **Anthropic token (paste setup-token)** in onboarding. If you want to run it on the gateway host, use `openclaw models auth setup-token --provider anthropic`. If you ran `claude setup-token` elsewhere, paste it on the gateway host with `openclaw models auth paste-token --provider anthropic`. See [Anthropic](/providers/anthropic).
+ Copy the token it prints, then either run `openclaw models auth setup-token --provider anthropic` on the gateway host or paste it there with `openclaw models auth paste-token --provider anthropic`. See [Anthropic](/providers/anthropic).
Yes. You can either:
- - use a **setup-token**
- reuse a local **Claude CLI** login on the gateway host with `openclaw models auth login --provider anthropic --method cli --set-default`
+ - use a **setup-token** manually with `openclaw models auth setup-token --provider anthropic`
- Setup-token is still supported. Claude CLI migration is simpler when the gateway host already runs Claude Code. See [Anthropic](/providers/anthropic) and [OAuth](/concepts/oauth).
+ Claude CLI is the preferred interactive Anthropic path. Setup-token is still supported for manual config. See [Anthropic](/providers/anthropic) and [OAuth](/concepts/oauth).
Important: Anthropic changed third-party harness billing on **April 4, 2026
at 12:00 PM PT / 8:00 PM BST**. Anthropic says Claude subscription limits no
diff --git a/docs/providers/anthropic.md b/docs/providers/anthropic.md
index fdf8ae1730cf..fe80339c3023 100644
--- a/docs/providers/anthropic.md
+++ b/docs/providers/anthropic.md
@@ -305,6 +305,11 @@ Or in onboarding:
openclaw onboard --auth-choice anthropic-cli
```
+Interactive `openclaw onboard` and `openclaw configure` now prefer **Anthropic
+Claude CLI** first and **Anthropic API key** second. The setup-token flow
+remains supported through manual auth commands, but is not shown in the
+assistant picker.
+
What this does:
- verifies Claude CLI is already signed in on the gateway host
@@ -339,7 +344,7 @@ you need to.
More details: [/gateway/cli-backends](/gateway/cli-backends)
-## Option C: Claude setup-token
+## Option C: Claude setup-token (manual)
**Best for:** using your Claude subscription with Anthropic **Extra Usage**
enabled, or while transitioning to API-key billing.
@@ -352,25 +357,18 @@ Setup-tokens are created by the **Claude Code CLI**, not the Anthropic Console.
claude setup-token
```
-Paste the token into OpenClaw (wizard: **Anthropic token (paste setup-token)**), or run it on the gateway host:
+Then either run it on the gateway host:
```bash
openclaw models auth setup-token --provider anthropic
```
-If you generated the token on a different machine, paste it:
+Or if you generated the token on a different machine, paste it:
```bash
openclaw models auth paste-token --provider anthropic
```
-### CLI setup (setup-token)
-
-```bash
-# Paste a setup-token during setup
-openclaw onboard --auth-choice setup-token
-```
-
### Config snippet (setup-token)
```json5
diff --git a/docs/reference/wizard.md b/docs/reference/wizard.md
index 6854d092009b..356919470ab4 100644
--- a/docs/reference/wizard.md
+++ b/docs/reference/wizard.md
@@ -31,8 +31,8 @@ For a high-level overview, see [Onboarding (CLI)](/start/wizard).
- **Anthropic API key**: uses `ANTHROPIC_API_KEY` if present or prompts for a key, then saves it for daemon use.
- - **Anthropic Claude CLI**: on macOS onboarding checks Keychain item "Claude Code-credentials" (choose "Always Allow" so launchd starts don't block); on Linux/Windows it reuses `~/.claude/.credentials.json` if present and switches model selection to `claude-cli/...`.
- - **Anthropic token (paste setup-token)**: run `claude setup-token` on any machine, then paste the token (you can name it; blank = default).
+ - **Anthropic Claude CLI**: preferred Anthropic assistant choice in onboarding/configure. On macOS onboarding checks Keychain item "Claude Code-credentials" (choose "Always Allow" so launchd starts don't block); on Linux/Windows it reuses `~/.claude/.credentials.json` if present and switches model selection to `claude-cli/...`.
+ - **Anthropic setup-token**: still supported as a manual auth flow with `openclaw models auth setup-token --provider anthropic` or `openclaw models auth paste-token --provider anthropic`, but no longer shown in the interactive assistant picker.
- **OpenAI Code (Codex) subscription (Codex CLI)**: if `~/.codex/auth.json` exists, onboarding can reuse it. Reused Codex CLI credentials stay managed by Codex CLI; OpenClaw re-reads that source on expiry instead of rotating the copied refresh token itself.
- **OpenAI Code (Codex) subscription (OAuth)**: browser flow; paste the `code#state`.
- Sets `agents.defaults.model` to `openai-codex/gpt-5.2` when model is unset or `openai/*`.
diff --git a/docs/start/wizard-cli-automation.md b/docs/start/wizard-cli-automation.md
index f373f3d4bc60..04ac02763910 100644
--- a/docs/start/wizard-cli-automation.md
+++ b/docs/start/wizard-cli-automation.md
@@ -51,6 +51,19 @@ openclaw onboard --non-interactive \
## Provider-specific examples
+
+ ```bash
+ openclaw onboard --non-interactive \
+ --mode local \
+ --auth-choice anthropic-cli \
+ --gateway-port 18789 \
+ --gateway-bind loopback
+ ```
+
+ Requires Claude CLI already installed and signed in on the same gateway
+ host.
+
+
```bash
openclaw onboard --non-interactive \
@@ -182,6 +195,11 @@ openclaw onboard --non-interactive \
+Anthropic setup-token remains supported for manual flows, but interactive
+onboarding/configure no longer offers it as an assistant choice. Use
+`openclaw models auth setup-token --provider anthropic` or
+`openclaw models auth paste-token --provider anthropic` when you need it.
+
## Add another agent
Use `openclaw agents add ` to create a separate agent with its own workspace,
diff --git a/docs/start/wizard-cli-reference.md b/docs/start/wizard-cli-reference.md
index 016553721fd3..b75b6861b40e 100644
--- a/docs/start/wizard-cli-reference.md
+++ b/docs/start/wizard-cli-reference.md
@@ -16,7 +16,7 @@ For the short guide, see [Onboarding (CLI)](/start/wizard).
Local mode (default) walks you through:
-- Model and auth setup (OpenAI Code subscription OAuth, Anthropic API key or setup token, plus MiniMax, GLM, Ollama, Moonshot, StepFun, and AI Gateway options)
+- Model and auth setup (OpenAI Code subscription OAuth, Anthropic Claude CLI or API key, plus MiniMax, GLM, Ollama, Moonshot, StepFun, and AI Gateway options)
- Workspace location and bootstrap files
- Gateway settings (port, bind, auth, tailscale)
- Channels and providers (Telegram, WhatsApp, Discord, Google Chat, Mattermost plugin, Signal)
@@ -130,15 +130,22 @@ What you set:
Reuses a local Claude CLI login on the gateway host and switches model
selection to `claude-cli/...`.
+ This is the preferred interactive Anthropic path in `openclaw onboard` and
+ `openclaw configure`.
+
- macOS: checks Keychain item "Claude Code-credentials"
- Linux and Windows: reuses `~/.claude/.credentials.json` if present
On macOS, choose "Always Allow" so launchd starts do not block.
-
- Run `claude setup-token` on any machine, then paste the token.
- You can name it; blank uses default.
+
+ Supported for manual config, not shown as an interactive assistant choice.
+
+ - Generate on any machine: `claude setup-token`
+ - Run on the gateway host: `openclaw models auth setup-token --provider anthropic`
+ - Or paste an existing token: `openclaw models auth paste-token --provider anthropic`
+
If `~/.codex/auth.json` exists, the wizard can reuse it.
diff --git a/docs/start/wizard.md b/docs/start/wizard.md
index a7db4d9b0e93..9a6799382e6e 100644
--- a/docs/start/wizard.md
+++ b/docs/start/wizard.md
@@ -66,12 +66,13 @@ Onboarding starts with **QuickStart** (defaults) vs **Advanced** (full control).
**Local mode (default)** walks you through these steps:
-1. **Model/Auth** — choose any supported provider/auth flow (API key, OAuth, or setup-token), including Custom Provider
+1. **Model/Auth** — choose any supported provider/auth flow (API key, OAuth, or provider-specific manual auth), including Custom Provider
(OpenAI-compatible, Anthropic-compatible, or Unknown auto-detect). Pick a default model.
Security note: if this agent will run tools or process webhook/hooks content, prefer the strongest latest-generation model available and keep tool policy strict. Weaker/older tiers are easier to prompt-inject.
For non-interactive runs, `--secret-input-mode ref` stores env-backed refs in auth profiles instead of plaintext API key values.
In non-interactive `ref` mode, the provider env var must be set; passing inline key flags without that env var fails fast.
In interactive runs, choosing secret reference mode lets you point at either an environment variable or a configured provider ref (`file` or `exec`), with a fast preflight validation before saving.
+ For Anthropic, interactive onboarding/configure prefers **Anthropic Claude CLI** first, then **Anthropic API key**. The **setup-token** flow remains supported through manual auth commands.
2. **Workspace** — Location for agent files (default `~/.openclaw/workspace`). Seeds bootstrap files.
3. **Gateway** — Port, bind address, auth mode, Tailscale exposure.
In interactive token mode, choose default plaintext token storage or opt into SecretRef.
diff --git a/extensions/anthropic/index.ts b/extensions/anthropic/index.ts
index 3d645bc38ebd..741f51f90c56 100644
--- a/extensions/anthropic/index.ts
+++ b/extensions/anthropic/index.ts
@@ -382,6 +382,7 @@ export default definePluginEntry({
choiceId: "anthropic-cli",
choiceLabel: "Anthropic Claude CLI",
choiceHint: "Reuse a local Claude CLI login on this host",
+ assistantPriority: -20,
groupId: "anthropic",
groupLabel: "Anthropic",
groupHint: "Claude CLI + setup-token + API key",
@@ -409,6 +410,7 @@ export default definePluginEntry({
choiceId: "token",
choiceLabel: "Anthropic token (paste setup-token)",
choiceHint: "Run `claude setup-token` elsewhere, then paste the token here",
+ assistantVisibility: "manual-only",
groupId: "anthropic",
groupLabel: "Anthropic",
groupHint: "Claude CLI + setup-token + API key",
diff --git a/extensions/anthropic/openclaw.plugin.json b/extensions/anthropic/openclaw.plugin.json
index 21af2607d39b..85b919986928 100644
--- a/extensions/anthropic/openclaw.plugin.json
+++ b/extensions/anthropic/openclaw.plugin.json
@@ -17,6 +17,7 @@
"deprecatedChoiceIds": ["claude-cli"],
"choiceLabel": "Anthropic Claude CLI",
"choiceHint": "Reuse a local Claude CLI login on this host",
+ "assistantPriority": -20,
"groupId": "anthropic",
"groupLabel": "Anthropic",
"groupHint": "Claude CLI + setup-token + API key"
@@ -27,6 +28,7 @@
"choiceId": "token",
"choiceLabel": "Anthropic token (paste setup-token)",
"choiceHint": "Run `claude setup-token` elsewhere, then paste the token here",
+ "assistantVisibility": "manual-only",
"groupId": "anthropic",
"groupLabel": "Anthropic",
"groupHint": "Claude CLI + setup-token + API key"
diff --git a/src/commands/auth-choice-options.static.ts b/src/commands/auth-choice-options.static.ts
index 73bf6d48d6e8..e4980811b9b7 100644
--- a/src/commands/auth-choice-options.static.ts
+++ b/src/commands/auth-choice-options.static.ts
@@ -1,5 +1,5 @@
-import { resolveLegacyAuthChoiceAliasesForCli } from "./auth-choice-legacy.js";
import type { AuthChoice, AuthChoiceGroupId } from "./onboard-types.js";
+import { resolveLegacyAuthChoiceAliasesForCli } from "./auth-choice-legacy.js";
export type { AuthChoiceGroupId };
@@ -10,6 +10,8 @@ export type AuthChoiceOption = {
groupId?: AuthChoiceGroupId;
groupLabel?: string;
groupHint?: string;
+ assistantPriority?: number;
+ assistantVisibility?: "visible" | "manual-only";
};
export type AuthChoiceGroup = {
diff --git a/src/commands/auth-choice-options.test.ts b/src/commands/auth-choice-options.test.ts
index 0cc771f19e10..453344cccf2d 100644
--- a/src/commands/auth-choice-options.test.ts
+++ b/src/commands/auth-choice-options.test.ts
@@ -378,6 +378,51 @@ describe("buildAuthChoiceOptions", () => {
expect(litellmGroup?.options.some((opt) => opt.value === "litellm-api-key")).toBe(true);
});
+ it("prefers Anthropic Claude CLI over API key and hides manual-only setup-token in grouped selection", () => {
+ resolveManifestProviderAuthChoices.mockReturnValue([
+ {
+ pluginId: "anthropic",
+ providerId: "anthropic",
+ methodId: "api-key",
+ choiceId: "apiKey",
+ choiceLabel: "Anthropic API key",
+ groupId: "anthropic",
+ groupLabel: "Anthropic",
+ },
+ {
+ pluginId: "anthropic",
+ providerId: "anthropic",
+ methodId: "cli",
+ choiceId: "anthropic-cli",
+ choiceLabel: "Anthropic Claude CLI",
+ assistantPriority: -20,
+ groupId: "anthropic",
+ groupLabel: "Anthropic",
+ },
+ {
+ pluginId: "anthropic",
+ providerId: "anthropic",
+ methodId: "setup-token",
+ choiceId: "token",
+ choiceLabel: "Anthropic token (paste setup-token)",
+ assistantVisibility: "manual-only",
+ groupId: "anthropic",
+ groupLabel: "Anthropic",
+ },
+ ]);
+ const { groups } = buildAuthChoiceGroups({
+ store: EMPTY_STORE,
+ includeSkip: false,
+ });
+ const anthropicGroup = groups.find((group) => group.value === "anthropic");
+
+ expect(anthropicGroup).toBeDefined();
+ expect(anthropicGroup?.options.map((option) => option.value)).toEqual([
+ "anthropic-cli",
+ "apiKey",
+ ]);
+ });
+
it("groups OpenCode Zen and Go under one OpenCode entry", () => {
resolveManifestProviderAuthChoices.mockReturnValue([
{
diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts
index 4fd6c47b0775..8bee688cc4f4 100644
--- a/src/commands/auth-choice-options.ts
+++ b/src/commands/auth-choice-options.ts
@@ -1,5 +1,6 @@
import type { AuthProfileStore } from "../agents/auth-profiles.js";
import type { OpenClawConfig } from "../config/config.js";
+import type { AuthChoice, AuthChoiceGroupId } from "./onboard-types.js";
import {
resolveManifestProviderSetupFlowContributions,
resolveProviderSetupFlowContributions,
@@ -10,12 +11,17 @@ import {
type AuthChoiceOption,
formatStaticAuthChoiceChoicesForCli,
} from "./auth-choice-options.static.js";
-import type { AuthChoice, AuthChoiceGroupId } from "./onboard-types.js";
function compareOptionLabels(a: AuthChoiceOption, b: AuthChoiceOption): number {
return a.label.localeCompare(b.label);
}
+function compareAssistantOptions(a: AuthChoiceOption, b: AuthChoiceOption): number {
+ const priorityA = a.assistantPriority ?? 0;
+ const priorityB = b.assistantPriority ?? 0;
+ return priorityA - priorityB || compareOptionLabels(a, b);
+}
+
function compareGroupLabels(a: AuthChoiceGroup, b: AuthChoiceGroup): number {
return a.label.localeCompare(b.label);
}
@@ -63,6 +69,7 @@ export function formatAuthChoiceChoicesForCli(params?: {
export function buildAuthChoiceOptions(params: {
store: AuthProfileStore;
includeSkip: boolean;
+ assistantVisibleOnly?: boolean;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
@@ -80,9 +87,11 @@ export function buildAuthChoiceOptions(params: {
optionByValue.set(option.value, option);
}
- const options: AuthChoiceOption[] = Array.from(optionByValue.values()).toSorted(
- compareOptionLabels,
- );
+ const options: AuthChoiceOption[] = Array.from(optionByValue.values())
+ .toSorted(compareOptionLabels)
+ .filter((option) =>
+ params.assistantVisibleOnly ? option.assistantVisibility !== "manual-only" : true,
+ );
if (params.includeSkip) {
options.push({ value: "skip", label: "Skip for now" });
@@ -104,6 +113,7 @@ export function buildAuthChoiceGroups(params: {
const options = buildAuthChoiceOptions({
...params,
includeSkip: false,
+ assistantVisibleOnly: true,
});
const groupsById = new Map();
@@ -126,7 +136,7 @@ export function buildAuthChoiceGroups(params: {
const groups = Array.from(groupsById.values())
.map((group) => ({
...group,
- options: [...group.options].toSorted(compareOptionLabels),
+ options: [...group.options].toSorted(compareAssistantOptions),
}))
.toSorted(compareGroupLabels);
diff --git a/src/flows/provider-flow.ts b/src/flows/provider-flow.ts
index 676d61f52e17..31724a4f33d0 100644
--- a/src/flows/provider-flow.ts
+++ b/src/flows/provider-flow.ts
@@ -1,12 +1,12 @@
import type { OpenClawConfig } from "../config/config.js";
+import type { ProviderPlugin } from "../plugins/types.js";
+import type { FlowContribution, FlowOption } from "./types.js";
import { resolveManifestProviderAuthChoices } from "../plugins/provider-auth-choices.js";
import {
resolveProviderModelPickerEntries,
resolveProviderWizardOptions,
} from "../plugins/provider-wizard.js";
import { resolvePluginProviders } from "../plugins/providers.runtime.js";
-import type { ProviderPlugin } from "../plugins/types.js";
-import type { FlowContribution, FlowOption } from "./types.js";
import { mergeFlowContributions, sortFlowContributionsByLabel } from "./types.js";
export type ProviderFlowScope = "text-inference" | "image-generation";
@@ -95,6 +95,10 @@ export function resolveManifestProviderSetupFlowContributions(params?: {
value: choice.choiceId,
label: choice.choiceLabel,
...(choice.choiceHint ? { hint: choice.choiceHint } : {}),
+ ...(choice.assistantPriority !== undefined
+ ? { assistantPriority: choice.assistantPriority }
+ : {}),
+ ...(choice.assistantVisibility ? { assistantVisibility: choice.assistantVisibility } : {}),
...(choice.groupId && choice.groupLabel
? {
group: {
@@ -142,6 +146,10 @@ export function resolveRuntimeFallbackProviderSetupFlowContributions(params?: {
value: option.value,
label: option.label,
...(option.hint ? { hint: option.hint } : {}),
+ ...(option.assistantPriority !== undefined
+ ? { assistantPriority: option.assistantPriority }
+ : {}),
+ ...(option.assistantVisibility ? { assistantVisibility: option.assistantVisibility } : {}),
group: {
id: option.groupId,
label: option.groupLabel,
diff --git a/src/flows/types.ts b/src/flows/types.ts
index dd36792d7960..30f373a0aad2 100644
--- a/src/flows/types.ts
+++ b/src/flows/types.ts
@@ -19,6 +19,8 @@ export type FlowOption = {
hint?: string;
group?: FlowOptionGroup;
docs?: FlowDocsLink;
+ assistantPriority?: number;
+ assistantVisibility?: "visible" | "manual-only";
};
export type FlowContribution = {
diff --git a/src/plugins/manifest-registry.test.ts b/src/plugins/manifest-registry.test.ts
index f9a81a6bcd1c..b6a0350bf858 100644
--- a/src/plugins/manifest-registry.test.ts
+++ b/src/plugins/manifest-registry.test.ts
@@ -2,11 +2,11 @@ import fs from "node:fs";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { PluginCandidate } from "./discovery.js";
+import type { OpenClawPackageManifest } from "./manifest.js";
import {
clearPluginManifestRegistryCache,
loadPluginManifestRegistry,
} from "./manifest-registry.js";
-import type { OpenClawPackageManifest } from "./manifest.js";
import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js";
vi.unmock("../version.js");
@@ -389,6 +389,8 @@ describe("loadPluginManifestRegistry", () => {
method: "api-key",
choiceId: "openai-api-key",
choiceLabel: "OpenAI API key",
+ assistantPriority: 10,
+ assistantVisibility: "visible",
},
],
configSchema: { type: "object" },
@@ -411,6 +413,8 @@ describe("loadPluginManifestRegistry", () => {
method: "api-key",
choiceId: "openai-api-key",
choiceLabel: "OpenAI API key",
+ assistantPriority: 10,
+ assistantVisibility: "visible",
},
]);
});
diff --git a/src/plugins/manifest.ts b/src/plugins/manifest.ts
index 33ef17e3aac8..ee949972b98f 100644
--- a/src/plugins/manifest.ts
+++ b/src/plugins/manifest.ts
@@ -1,11 +1,11 @@
+import JSON5 from "json5";
import fs from "node:fs";
import path from "node:path";
-import JSON5 from "json5";
import type { ChannelConfigRuntimeSchema } from "../channels/plugins/types.plugin.js";
+import type { PluginConfigUiHint, PluginKind } from "./types.js";
import { MANIFEST_KEY } from "../compat/legacy-names.js";
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { isRecord } from "../utils.js";
-import type { PluginConfigUiHint, PluginKind } from "./types.js";
export const PLUGIN_MANIFEST_FILENAME = "openclaw.plugin.json";
export const PLUGIN_MANIFEST_FILENAMES = [PLUGIN_MANIFEST_FILENAME] as const;
@@ -91,6 +91,10 @@ export type PluginManifestProviderAuthChoice = {
/** Optional user-facing choice label/hint for grouped onboarding UI. */
choiceLabel?: string;
choiceHint?: string;
+ /** Lower values sort earlier in interactive assistant pickers. */
+ assistantPriority?: number;
+ /** Keep the choice out of interactive assistant pickers while preserving manual CLI support. */
+ assistantVisibility?: "visible" | "manual-only";
/** Legacy choice ids that should point users at this replacement choice. */
deprecatedChoiceIds?: string[];
/** Optional grouping metadata for auth-choice pickers. */
@@ -202,6 +206,14 @@ function normalizeProviderAuthChoices(
}
const choiceLabel = typeof entry.choiceLabel === "string" ? entry.choiceLabel.trim() : "";
const choiceHint = typeof entry.choiceHint === "string" ? entry.choiceHint.trim() : "";
+ const assistantPriority =
+ typeof entry.assistantPriority === "number" && Number.isFinite(entry.assistantPriority)
+ ? entry.assistantPriority
+ : undefined;
+ const assistantVisibility =
+ entry.assistantVisibility === "manual-only" || entry.assistantVisibility === "visible"
+ ? entry.assistantVisibility
+ : undefined;
const deprecatedChoiceIds = normalizeStringList(entry.deprecatedChoiceIds);
const groupId = typeof entry.groupId === "string" ? entry.groupId.trim() : "";
const groupLabel = typeof entry.groupLabel === "string" ? entry.groupLabel.trim() : "";
@@ -221,6 +233,8 @@ function normalizeProviderAuthChoices(
choiceId,
...(choiceLabel ? { choiceLabel } : {}),
...(choiceHint ? { choiceHint } : {}),
+ ...(assistantPriority !== undefined ? { assistantPriority } : {}),
+ ...(assistantVisibility ? { assistantVisibility } : {}),
...(deprecatedChoiceIds.length > 0 ? { deprecatedChoiceIds } : {}),
...(groupId ? { groupId } : {}),
...(groupLabel ? { groupLabel } : {}),
@@ -320,7 +334,7 @@ export function loadPluginManifest(
}
let raw: unknown;
try {
- raw = JSON5.parse(fs.readFileSync(opened.fd, "utf-8")) as unknown;
+ raw = JSON5.parse(fs.readFileSync(opened.fd, "utf-8"));
} catch (err) {
return {
ok: false,
diff --git a/src/plugins/provider-auth-choices.test.ts b/src/plugins/provider-auth-choices.test.ts
index 066017905ddb..a25f00285621 100644
--- a/src/plugins/provider-auth-choices.test.ts
+++ b/src/plugins/provider-auth-choices.test.ts
@@ -59,6 +59,8 @@ describe("provider auth choice manifest helpers", () => {
method: "api-key",
choiceId: "openai-api-key",
choiceLabel: "OpenAI API key",
+ assistantPriority: 10,
+ assistantVisibility: "visible",
onboardingScopes: ["text-inference"],
optionKey: "openaiApiKey",
cliFlag: "--openai-api-key",
@@ -74,6 +76,8 @@ describe("provider auth choice manifest helpers", () => {
methodId: "api-key",
choiceId: "openai-api-key",
choiceLabel: "OpenAI API key",
+ assistantPriority: 10,
+ assistantVisibility: "visible",
onboardingScopes: ["text-inference"],
optionKey: "openaiApiKey",
cliFlag: "--openai-api-key",
diff --git a/src/plugins/provider-auth-choices.ts b/src/plugins/provider-auth-choices.ts
index 58f5ad25daa3..c751acb2032f 100644
--- a/src/plugins/provider-auth-choices.ts
+++ b/src/plugins/provider-auth-choices.ts
@@ -1,5 +1,5 @@
-import { normalizeProviderIdForAuth } from "../agents/model-selection.js";
import type { OpenClawConfig } from "../config/config.js";
+import { normalizeProviderIdForAuth } from "../agents/model-selection.js";
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
@@ -10,6 +10,8 @@ export type ProviderAuthChoiceMetadata = {
choiceId: string;
choiceLabel: string;
choiceHint?: string;
+ assistantPriority?: number;
+ assistantVisibility?: "visible" | "manual-only";
deprecatedChoiceIds?: string[];
groupId?: string;
groupLabel?: string;
@@ -59,6 +61,12 @@ export function resolveManifestProviderAuthChoices(params?: {
choiceId: choice.choiceId,
choiceLabel: choice.choiceLabel ?? choice.choiceId,
...(choice.choiceHint ? { choiceHint: choice.choiceHint } : {}),
+ ...(choice.assistantPriority !== undefined
+ ? { assistantPriority: choice.assistantPriority }
+ : {}),
+ ...(choice.assistantVisibility
+ ? { assistantVisibility: choice.assistantVisibility }
+ : {}),
...(choice.deprecatedChoiceIds
? { deprecatedChoiceIds: choice.deprecatedChoiceIds }
: {}),
diff --git a/src/plugins/provider-validation.ts b/src/plugins/provider-validation.ts
index 28f4e1671bef..b5f15bb16ac3 100644
--- a/src/plugins/provider-validation.ts
+++ b/src/plugins/provider-validation.ts
@@ -105,6 +105,14 @@ function normalizeProviderWizardSetup(params: {
...(normalizeText(params.setup.choiceHint)
? { choiceHint: normalizeText(params.setup.choiceHint) }
: {}),
+ ...(typeof params.setup.assistantPriority === "number" &&
+ Number.isFinite(params.setup.assistantPriority)
+ ? { assistantPriority: params.setup.assistantPriority }
+ : {}),
+ ...(params.setup.assistantVisibility === "manual-only" ||
+ params.setup.assistantVisibility === "visible"
+ ? { assistantVisibility: params.setup.assistantVisibility }
+ : {}),
...(normalizeText(params.setup.groupId)
? { groupId: normalizeText(params.setup.groupId) }
: {}),
diff --git a/src/plugins/provider-wizard.ts b/src/plugins/provider-wizard.ts
index 07b78bc44faa..a4271e6455c9 100644
--- a/src/plugins/provider-wizard.ts
+++ b/src/plugins/provider-wizard.ts
@@ -1,19 +1,19 @@
-import { DEFAULT_PROVIDER } from "../agents/defaults.js";
-import { normalizeProviderId } from "../agents/model-selection.js";
import type { OpenClawConfig } from "../config/config.js";
import type { WizardPrompter } from "../wizard/prompts.js";
-import {
- buildPluginSnapshotCacheEnvKey,
- resolvePluginSnapshotCacheTtlMs,
- shouldUsePluginSnapshotCache,
-} from "./cache-controls.js";
-import { resolvePluginProviders } from "./providers.runtime.js";
import type {
ProviderAuthMethod,
ProviderPlugin,
ProviderPluginWizardModelPicker,
ProviderPluginWizardSetup,
} from "./types.js";
+import { DEFAULT_PROVIDER } from "../agents/defaults.js";
+import { normalizeProviderId } from "../agents/model-selection.js";
+import {
+ buildPluginSnapshotCacheEnvKey,
+ resolvePluginSnapshotCacheTtlMs,
+ shouldUsePluginSnapshotCache,
+} from "./cache-controls.js";
+import { resolvePluginProviders } from "./providers.runtime.js";
export const PROVIDER_PLUGIN_CHOICE_PREFIX = "provider-plugin:";
type ProviderWizardCacheEntry = {
@@ -45,6 +45,8 @@ export type ProviderWizardOption = {
groupLabel: string;
groupHint?: string;
onboardingScopes?: Array<"text-inference" | "image-generation">;
+ assistantPriority?: number;
+ assistantVisibility?: "visible" | "manual-only";
};
export type ProviderModelPickerEntry = {
@@ -114,6 +116,13 @@ function buildSetupOptionForMethod(params: {
groupLabel: params.wizard.groupLabel?.trim() || params.provider.label,
groupHint: params.wizard.groupHint?.trim(),
...(params.wizard.onboardingScopes ? { onboardingScopes: params.wizard.onboardingScopes } : {}),
+ ...(typeof params.wizard.assistantPriority === "number" &&
+ Number.isFinite(params.wizard.assistantPriority)
+ ? { assistantPriority: params.wizard.assistantPriority }
+ : {}),
+ ...(params.wizard.assistantVisibility
+ ? { assistantVisibility: params.wizard.assistantVisibility }
+ : {}),
};
}
diff --git a/src/plugins/types.ts b/src/plugins/types.ts
index b7776c92c0c3..8b760cd753bb 100644
--- a/src/plugins/types.ts
+++ b/src/plugins/types.ts
@@ -1,9 +1,9 @@
-import type { IncomingMessage, ServerResponse } from "node:http";
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { StreamFn } from "@mariozechner/pi-agent-core";
import type { Api, Model } from "@mariozechner/pi-ai";
import type { ModelRegistry } from "@mariozechner/pi-coding-agent";
import type { Command } from "commander";
+import type { IncomingMessage, ServerResponse } from "node:http";
import type {
ApiKeyCredential,
AuthProfileCredential,
@@ -925,6 +925,8 @@ export type ProviderPluginWizardSetup = {
choiceId?: string;
choiceLabel?: string;
choiceHint?: string;
+ assistantPriority?: number;
+ assistantVisibility?: "visible" | "manual-only";
groupId?: string;
groupLabel?: string;
groupHint?: string;