diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b1db3aa6..7d0160a4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
## 2026.1.10
+### New Features and Changes
+- Onboarding/Models: add first-class Z.AI (GLM) auth choice (`zai-api-key`) + `--zai-api-key` flag.
+
### Fixes
- Agents/OpenAI: fix Responses tool-only → follow-up turn handling (avoid standalone `reasoning` items that trigger 400 “required following item”).
- Auth: update Claude Code keychain credentials in-place during refresh sync; extract CLI sync helpers + coverage.
diff --git a/docs/cli/index.md b/docs/cli/index.md
index 177234287..d73979756 100644
--- a/docs/cli/index.md
+++ b/docs/cli/index.md
@@ -178,7 +178,7 @@ Options:
- `--workspace
`
- `--non-interactive`
- `--mode `
-- `--auth-choice `
+- `--auth-choice `
- `--token-provider ` (non-interactive; used with `--auth-choice token`)
- `--token ` (non-interactive; used with `--auth-choice token`)
- `--token-profile-id ` (non-interactive; default: `:manual`)
@@ -186,6 +186,7 @@ Options:
- `--anthropic-api-key `
- `--openai-api-key `
- `--gemini-api-key `
+- `--zai-api-key `
- `--minimax-api-key `
- `--opencode-zen-api-key `
- `--gateway-port `
diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md
index 508057782..7e97a3273 100644
--- a/docs/gateway/configuration.md
+++ b/docs/gateway/configuration.md
@@ -1491,6 +1491,8 @@ Notes:
Z.AI models are available via the built-in `zai` provider. Set `ZAI_API_KEY`
in your environment and reference the model by provider/model.
+Shortcut: `clawdbot onboard --auth-choice zai-api-key`.
+
```json5
{
agents: {
diff --git a/docs/start/wizard.md b/docs/start/wizard.md
index 9594c6e7d..25b60cc46 100644
--- a/docs/start/wizard.md
+++ b/docs/start/wizard.md
@@ -186,6 +186,17 @@ clawdbot onboard --non-interactive \
--gateway-bind loopback
```
+Z.AI example:
+
+```bash
+clawdbot onboard --non-interactive \
+ --mode local \
+ --auth-choice zai-api-key \
+ --zai-api-key "$ZAI_API_KEY" \
+ --gateway-port 18789 \
+ --gateway-bind loopback
+```
+
OpenCode Zen example:
```bash
diff --git a/src/cli/program.test.ts b/src/cli/program.test.ts
index e69655f37..ff7f9c095 100644
--- a/src/cli/program.test.ts
+++ b/src/cli/program.test.ts
@@ -156,6 +156,29 @@ describe("cli program", () => {
);
});
+ it("passes zai api key to onboard", async () => {
+ const program = buildProgram();
+ await program.parseAsync(
+ [
+ "onboard",
+ "--non-interactive",
+ "--auth-choice",
+ "zai-api-key",
+ "--zai-api-key",
+ "sk-zai-test",
+ ],
+ { from: "user" },
+ );
+ expect(onboardCommand).toHaveBeenCalledWith(
+ expect.objectContaining({
+ nonInteractive: true,
+ authChoice: "zai-api-key",
+ zaiApiKey: "sk-zai-test",
+ }),
+ runtime,
+ );
+ });
+
it("runs providers login", async () => {
const program = buildProgram();
await program.parseAsync(["providers", "login", "--account", "work"], {
diff --git a/src/cli/program.ts b/src/cli/program.ts
index 80ae5c7d5..f0afdcc25 100644
--- a/src/cli/program.ts
+++ b/src/cli/program.ts
@@ -245,7 +245,7 @@ export function buildProgram() {
.option("--mode ", "Wizard mode: local|remote")
.option(
"--auth-choice ",
- "Auth: setup-token|claude-cli|token|openai-codex|openai-api-key|codex-cli|antigravity|gemini-api-key|apiKey|minimax-cloud|minimax-api|minimax|opencode-zen|skip",
+ "Auth: setup-token|claude-cli|token|openai-codex|openai-api-key|codex-cli|antigravity|gemini-api-key|zai-api-key|apiKey|minimax-cloud|minimax-api|minimax|opencode-zen|skip",
)
.option(
"--token-provider ",
@@ -266,6 +266,7 @@ export function buildProgram() {
.option("--anthropic-api-key ", "Anthropic API key")
.option("--openai-api-key ", "OpenAI API key")
.option("--gemini-api-key ", "Gemini API key")
+ .option("--zai-api-key ", "Z.AI API key")
.option("--minimax-api-key ", "MiniMax API key")
.option("--opencode-zen-api-key ", "OpenCode Zen API key")
.option("--gateway-port ", "Gateway port")
@@ -313,6 +314,7 @@ export function buildProgram() {
| "codex-cli"
| "antigravity"
| "gemini-api-key"
+ | "zai-api-key"
| "apiKey"
| "minimax-cloud"
| "minimax-api"
@@ -327,6 +329,7 @@ export function buildProgram() {
anthropicApiKey: opts.anthropicApiKey as string | undefined,
openaiApiKey: opts.openaiApiKey as string | undefined,
geminiApiKey: opts.geminiApiKey as string | undefined,
+ zaiApiKey: opts.zaiApiKey as string | undefined,
minimaxApiKey: opts.minimaxApiKey as string | undefined,
opencodeZenApiKey: opts.opencodeZenApiKey as string | undefined,
gatewayPort:
diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts
index c154ef4a4..50115073d 100644
--- a/src/commands/auth-choice-options.ts
+++ b/src/commands/auth-choice-options.ts
@@ -97,6 +97,7 @@ export function buildAuthChoiceOptions(params: {
label: "Google Antigravity (Claude Opus 4.5, Gemini 3, etc.)",
});
options.push({ value: "gemini-api-key", label: "Google Gemini API key" });
+ options.push({ value: "zai-api-key", label: "Z.AI (GLM) API key" });
options.push({ value: "apiKey", label: "Anthropic API key" });
// Token flow is currently Anthropic-only; use CLI for advanced providers.
options.push({
diff --git a/src/commands/auth-choice.ts b/src/commands/auth-choice.ts
index 5f292bdd6..1d09e220c 100644
--- a/src/commands/auth-choice.ts
+++ b/src/commands/auth-choice.ts
@@ -44,12 +44,15 @@ import {
applyMinimaxProviderConfig,
applyOpencodeZenConfig,
applyOpencodeZenProviderConfig,
+ applyZaiConfig,
MINIMAX_HOSTED_MODEL_REF,
setAnthropicApiKey,
setGeminiApiKey,
setMinimaxApiKey,
setOpencodeZenApiKey,
+ setZaiApiKey,
writeOAuthCredentials,
+ ZAI_DEFAULT_MODEL_REF,
} from "./onboard-auth.js";
import { openUrl } from "./onboard-helpers.js";
import type { AuthChoice } from "./onboard-types.js";
@@ -598,6 +601,45 @@ export async function applyAuthChoice(params: {
agentModelOverride = GOOGLE_GEMINI_DEFAULT_MODEL;
await noteAgentModel(GOOGLE_GEMINI_DEFAULT_MODEL);
}
+ } else if (params.authChoice === "zai-api-key") {
+ const key = await params.prompter.text({
+ message: "Enter Z.AI API key",
+ validate: (value) => (value?.trim() ? undefined : "Required"),
+ });
+ await setZaiApiKey(String(key).trim(), params.agentDir);
+ nextConfig = applyAuthProfileConfig(nextConfig, {
+ profileId: "zai:default",
+ provider: "zai",
+ mode: "api_key",
+ });
+ if (params.setDefaultModel) {
+ nextConfig = applyZaiConfig(nextConfig);
+ await params.prompter.note(
+ `Default model set to ${ZAI_DEFAULT_MODEL_REF}`,
+ "Model configured",
+ );
+ } else {
+ nextConfig = {
+ ...nextConfig,
+ agents: {
+ ...nextConfig.agents,
+ defaults: {
+ ...nextConfig.agents?.defaults,
+ models: {
+ ...nextConfig.agents?.defaults?.models,
+ [ZAI_DEFAULT_MODEL_REF]: {
+ ...nextConfig.agents?.defaults?.models?.[ZAI_DEFAULT_MODEL_REF],
+ alias:
+ nextConfig.agents?.defaults?.models?.[ZAI_DEFAULT_MODEL_REF]
+ ?.alias ?? "GLM",
+ },
+ },
+ },
+ },
+ };
+ agentModelOverride = ZAI_DEFAULT_MODEL_REF;
+ await noteAgentModel(ZAI_DEFAULT_MODEL_REF);
+ }
} else if (params.authChoice === "apiKey") {
const key = await params.prompter.text({
message: "Enter Anthropic API key",
diff --git a/src/commands/onboard-auth.ts b/src/commands/onboard-auth.ts
index 90406d145..65af6913c 100644
--- a/src/commands/onboard-auth.ts
+++ b/src/commands/onboard-auth.ts
@@ -134,6 +134,51 @@ export async function setMinimaxApiKey(key: string, agentDir?: string) {
});
}
+export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7";
+
+export async function setZaiApiKey(key: string, agentDir?: string) {
+ // Write to the multi-agent path so gateway finds credentials on startup
+ upsertAuthProfile({
+ profileId: "zai:default",
+ credential: {
+ type: "api_key",
+ provider: "zai",
+ key,
+ },
+ agentDir: agentDir ?? resolveDefaultAgentDir(),
+ });
+}
+
+export function applyZaiConfig(cfg: ClawdbotConfig): ClawdbotConfig {
+ const models = { ...cfg.agents?.defaults?.models };
+ models[ZAI_DEFAULT_MODEL_REF] = {
+ ...models[ZAI_DEFAULT_MODEL_REF],
+ alias: models[ZAI_DEFAULT_MODEL_REF]?.alias ?? "GLM",
+ };
+
+ const existingModel = cfg.agents?.defaults?.model;
+ return {
+ ...cfg,
+ agents: {
+ ...cfg.agents,
+ defaults: {
+ ...cfg.agents?.defaults,
+ models,
+ model: {
+ ...(existingModel &&
+ "fallbacks" in (existingModel as Record)
+ ? {
+ fallbacks: (existingModel as { fallbacks?: string[] })
+ .fallbacks,
+ }
+ : undefined),
+ primary: ZAI_DEFAULT_MODEL_REF,
+ },
+ },
+ },
+ };
+}
+
export function applyAuthProfileConfig(
cfg: ClawdbotConfig,
params: {
diff --git a/src/commands/onboard-non-interactive.ts b/src/commands/onboard-non-interactive.ts
index 0b54de492..de6293296 100644
--- a/src/commands/onboard-non-interactive.ts
+++ b/src/commands/onboard-non-interactive.ts
@@ -36,10 +36,12 @@ import {
applyMinimaxConfig,
applyMinimaxHostedConfig,
applyOpencodeZenConfig,
+ applyZaiConfig,
setAnthropicApiKey,
setGeminiApiKey,
setMinimaxApiKey,
setOpencodeZenApiKey,
+ setZaiApiKey,
} from "./onboard-auth.js";
import {
applyWizardMetadata,
@@ -225,6 +227,25 @@ export async function runNonInteractiveOnboarding(
mode: "api_key",
});
nextConfig = applyGoogleGeminiModelDefault(nextConfig).next;
+ } else if (authChoice === "zai-api-key") {
+ const resolved = await resolveNonInteractiveApiKey({
+ provider: "zai",
+ cfg: baseConfig,
+ flagValue: opts.zaiApiKey,
+ flagName: "--zai-api-key",
+ envVar: "ZAI_API_KEY",
+ runtime,
+ });
+ if (!resolved) return;
+ if (resolved.source !== "profile") {
+ await setZaiApiKey(resolved.key);
+ }
+ nextConfig = applyAuthProfileConfig(nextConfig, {
+ profileId: "zai:default",
+ provider: "zai",
+ mode: "api_key",
+ });
+ nextConfig = applyZaiConfig(nextConfig);
} else if (authChoice === "openai-api-key") {
const resolved = await resolveNonInteractiveApiKey({
provider: "openai",
diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts
index 09375bcb7..9f8b92d50 100644
--- a/src/commands/onboard-types.ts
+++ b/src/commands/onboard-types.ts
@@ -14,6 +14,7 @@ export type AuthChoice =
| "antigravity"
| "apiKey"
| "gemini-api-key"
+ | "zai-api-key"
| "minimax-cloud"
| "minimax"
| "minimax-api"
@@ -43,6 +44,7 @@ export type OnboardOptions = {
anthropicApiKey?: string;
openaiApiKey?: string;
geminiApiKey?: string;
+ zaiApiKey?: string;
minimaxApiKey?: string;
opencodeZenApiKey?: string;
gatewayPort?: number;