GLM Provider
Zhipu's GLM as an alternative session provider: enabling it and managing accounts, the naming split between the account UI and the model picker, the model choices, the readiness gaps, and the option to run on company credits.
What it is
Omniscio supports spawning Claude-Code-style sessions backed by Zhipu's GLM Anthropic-compatible API (hosted at z.ai) as an additional provider, alongside claude (the default), codex, gemini, antigravity, deepseek, and kimi.
What the user sees
A GLM session looks identical in the sidebar and main pane to a Claude session — same status dots, same streaming bubbles, same Ctrl+Enter to send, same Plan / Auto-Accept / Bypass Permissions modes, same tool-call approval UI. The provider is invisible in normal chat. The only user-visible difference is the GLM icon (a non-Claude provider mark, no text label) in the session header.
Tool-call behavior is identical to Claude because the same claude CLI binary is doing the talking — only the model and the upstream endpoint differ. There is no yolo / auto-approve mode like Gemini or Anti-Gravity; the standard Claude approval flow applies.
Branding: "Z.ai" (account UI) vs "GLM" (model picker)
The vendor brand is surfaced as "Z.ai" in the account/subscription UI, while the model family stays "GLM" in the session/model picker — the two name the same provider (Zhipu's GLM models on z.ai's Anthropic-compatible API), shown under the name that fits each surface:
- Account/subscription surfaces say "Z.ai" — the Settings → Accounts card, its allow-toggle, key fields, and the account-popover tab (below) all read "Z.ai".
- The session + model picker say "GLM" — the per-project Default-provider radios, the
ChangeProviderButton, and the model list (glm-5.2[1m]) keep "GLM" (the model-family name, stable as z.ai ships new versions). - Internal ids are unchanged —
provider: 'glm',providerSlug: 'glm',allowGlmSessionSpawn,glmApiKey, theACCOUNT_GLM_*IPC channels, andregistry.ts's model-pickerlabel: 'GLM'all stayglm/"GLM"; only display strings in the account surfaces changed.
Where to find it
Managing accounts — Settings and the account popover
Z.ai (GLM) accounts are personal z.ai API keys, backed by the shared glmAccounts store slice + the ACCOUNT_GLM_* IPC. (Omniscio credits are no longer an entry in this list: they are a row of GLM's supply list — see below.) Manage them in two interchangeable places:
- Settings → Accounts → "Other AI tools" → Z.ai — the full manager (GlmAccountsManager.tsx): add a key (validated before store), multi-account list with 5h + weekly usage bars, switch / rename / remove.
- Account popover → "Z.ai" tab — a condensed mirror (GlmAccountsPopoverTab.tsx) in the lower-left account popover, beside the Claude and Codex tabs (
AccountIndicator.tsx). Add a key inline (same validated flow), see per-account usage, refresh, switch, and confirm-gated remove. Modeled on the Codex popover tab — but Z.ai is added by pasting an API key (no OAuth login), so it carries a compact key form rather than a one-click login button. Reuses the same accounts + IPC as Settings, so the two stay in sync (both refetch onACCOUNTS_CHANGED).
How to enable
Master toggle required first. GLM is an alternative provider — by default Omniscio ships as a Claude-only product, and the entire GLM setup section below is hidden in Settings → Accounts. Flip Settings → Accounts → Show alternative AI providers to ON and the GLM panel appears alongside Gemini, Codex, Anti-Gravity, DeepSeek, and Kimi. With the master off, the per-project "Default provider" radio collapses to a single Claude row and the session header's provider switcher (ChangeProviderButton) hides GLM — GLM effectively does not exist in the UI even if every gate below is configured. Your saved API key and "Allow GLM sessions" toggle are preserved across master-toggle flips.
You need two of these in place (note: no binary check — same claude binary as Claude). The opt-in toggle leads and gates the key field — the API key input appears only once GLM is turned on, so a key can't be entered into a disabled provider (when off, a hint keeps the glm-api-key Settings-search anchor landable and a saved key is preserved):
- Settings → Account → Allow GLM sessions in any project — opt-in toggle, off by default; same security stance as the Codex/Gemini/DeepSeek/Kimi spawn guards. Turning it on reveals the key field below.
- Settings → Account → GLM → API key — paste a z.ai API key from the z.ai API console (encrypted at rest via Electron
safeStorage).
Once both are green, you can launch a GLM session in three ways:
- Per-launch override — on a fresh (zero-message) session, the ChangeProviderButton in the new session's main panel (the launch-config pickers on a fresh session) lets you pick a one-off provider before sending the first message. Non-ready providers appear
aria-disabledwith an "Add key…" / "Enable in Settings…" suffix and deep-link to the right Settings panel. - Per-project default — Edit Project dialog (three-dot menu → Edit) has a "Default provider" section listing every enabled provider. Pick GLM and the project's sidebar "+ New Session" button spawns GLM automatically.
- Programmatically — anything that creates a session with
provider: 'glm'(recipes, agent-driven sessions, the CLI control HTTP API). Same readiness gates apply on the backend.
Company GLM credits (run on Omniscio's key, draw down your credits)
Instead of pasting your own z.ai key, a signed-in user on a Pro plan can run GLM sessions on Omniscio's company key — usage draws down that user's prepaid credit balance (the same balance shown in Settings → My API Keys). It is the Omniscio credits row of GLM's supply list in Settings → Accounts → Who pays & who serves: add the row (a new user's GLM list starts with their own z.ai account only), and place it above or below your own account to choose which pays first. See model-vendors.md.
Rules:
- Signed-in and Pro only. The row authenticates to Omniscio's gateway with your sign-in token and is checked against your plan; signed out or below Pro it is skipped ("Not signed in" / "Not available to you" on the card) and the next row serves — never a broken session.
- The list's order decides. Whichever of your own account and the credits row comes first in GLM's list pays first; the other is used when the first cannot serve.
- Out of credits → the next row, or a clean stop. When your balance reaches zero the gateway refuses; the session moves to the next row of GLM's list that can serve, and only when none is left does it stop with an "out of credits — top up" message, not a raw error.
How it works: the GLM session runs through the JLS gateway (the same Cloud Run service behind "My API Keys") at <gateway>/v1/glm, with your sign-in token as the bearer. The company z.ai key lives only in the gateway (Secret Manager) — it never reaches your machine. The gateway meters the real per-call cost and debits your prepaid credits. The lane's tier and funding are one glm row in FUNDABLE_PROVIDERS (fundable-providers.ts), kept equal to the gateway's own route by a parity guard.
Operator note (going live): the credits row is inert until the org adds the company GLM key to the gateway (gateway-glm-key secret + the glm/* block in gateway/litellm/config.yaml), deploys, and grants users credits (gateway/scripts/grant-credits.mjs). A deploy-time check (a real /v1/glm request must return a non-zero cost) confirms credits actually drain. Full invariants + steps: company-glm-gateway-contract.md. (Gemini can't use this path — its CLI can't be routed through the gateway and reports no cost to meter.)
How it behaves
Choosing the model
A fresh GLM session lets you pick the model in the launch-config pickers (next to the provider chooser). The list is registry-driven (MODELS_BY_PROVIDER.glm in provider-models.ts) and offers GLM 5.3 (the current flagship), GLM 5.3 Flash (its cheap, fast tier — z.ai's $0.15/$0.50-per-M model, roughly 9× cheaper than the flagship), and GLM 5.2 (the previous generation), each in two id forms: a [1m] form (e.g. glm-5.3[1m], where the bracket suffix unlocks the full 1,000,000-token context z.ai documents for Claude Code) and a bare form (e.g. glm-5.3, the same model at a smaller default context window). Use default spawns glm-5.3[1m] rather than omitting the model flag — GLM requires a real z.ai model id (the shared claude binary's built-in default is a Claude id that z.ai 404s as "model not found"), so the registry pins one via getProviderDefaultModel. GLM has no separate "reasoning effort" knob. The model list is z.ai's own — a GLM session never inherits your Claude default model (those are Claude ids its endpoint would reject). See start-a-new-session.md.
Per-project default
Each project remembers a default provider in the projectDefaultProviders setting (a Record<projectId, ProviderId>). Empty by default — every project falls back to Claude. Change it from the project's three-dot menu → Edit → Default provider radios; the change persists immediately.
When you set a project's default to GLM but GLM isn't ready (toggle off or key missing), an amber "!" badge appears on that project's sidebar row. Hover the badge for a tooltip explaining the gap; click it to deep-link straight to the right Settings panel. The badge re-checks on every render, so fixing the gap clears it without a refresh. Claude defaults never show the badge — Claude is always considered ready.
Error states and fixes
Two gap codes — fewer than Gemini/Codex because there is no separate binary to check:
| Gap | What it means | Click-to-fix lands you at… |
|---|---|---|
toggle-off |
"Allow GLM sessions" is OFF in Settings | Settings → Account → Allow GLM sessions toggle |
key-missing |
No row of GLM's supply list can serve (no z.ai account or key, and no other row ready) | Settings → Account → GLM API key field, or Who pays & who serves |
Test your key. The GLM API-key field has a "Test key" button that pings z.ai's real endpoint with your key and the default model, returning one plain-language verdict (works / invalid key / out of balance / model not available) — so a misconfigured key surfaces before you spawn a session rather than as a confusing mid-turn failure.
Retired / unavailable model. If a session's selected model is no longer accepted by z.ai — either Omniscio has removed it from the picker or the vendor killed a still-listed id server-side — Omniscio surfaces a clear "this GLM model is no longer available — open the model picker and choose another" and parks the session as needs_you / recovery_failed, instead of silently retrying the rejected request into the generic placeholder-stuck give-up loop (which never names the real model problem). Two guards back this: a pre-spawn refusal (the model isn't in the current picker list) and a placeholder-stuck config-error guard (an anthropic-compat session that produced no output and hit the rejected-request signature). See provider-spawn-model-availability-contract.md.
The real reason, not a blanket "model unavailable." A z.ai rejection arrives the same way regardless of cause — a synthetic placeholder with zero output — so when the rejection is not about the model (the account ran out of balance, the conversation outgrew the context window, the key became invalid, or you hit a rate limit), Omniscio now reads z.ai's actual error off that placeholder and surfaces the specific cause + fix instead of the generic "model unavailable": out of balance → "Your GLM account is out of balance — recharge it, then resend" (switching models can't fund an unfunded account, and a retry just re-hits the wall); conversation too long → start a fresh session or pick a larger-context model; bad/expired key → check the GLM key in Settings; rate-limited → wait a moment, then resend. The "model no longer available — choose another" wording above is now reserved for a genuinely unknown/retired model. This is message-only — the session still parks as needs_you / recovery_failed exactly as before, except that running out of balance first tries the backup key (if you set one) and then the next row of GLM's supply list that can serve, and parks only when neither can. Motivating case: a multi-hour GLM audit that hit z.ai's "Insufficient balance … Please recharge" 429 and was shown the misleading "switch models" prompt — classification (classifyAnthropicCompatPlaceholderError) reads the four causes off the placeholder text, the same spot the Consumer-Terms gate already reads. See provider-spawn-model-availability-contract.md vendor-error-is-classified-and-named.
For agents
How it works under the hood
Spawn-env redirect, no alternate manager. Zhipu publishes an Anthropic-compatible API for GLM — same request/response shape as Anthropic's Messages API, just at a different base URL. So Omniscio reuses the existing claude CLI binary (the same one Claude sessions use) and redirects every HTTP call at spawn time by setting two environment variables on the child process:
ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic— sends every HTTP request to z.ai's compat endpoint instead ofapi.anthropic.com(the CLI appends/v1/messages).ANTHROPIC_AUTH_TOKEN=<your z.ai API key>— supersedes the Claude OAuth/API-key for this child only.
The child process never knows it isn't talking to Anthropic. Streaming, tool calls, --resume for multi-turn, --output-format stream-json parsing all "just work" because they're handled by the standard claude binary against an upstream that speaks the same protocol.
No alternate session manager exists for GLM — readiness gates, the spawn_non_claude_session feature event fires, then it falls through to the same createSessionWithPrompt() path Claude sessions use. The provider: 'glm' value is stored on the session row in the database; spawn-cluster-manager.ts reads that column and injects the right spawn-env on every CLI invocation (initial spawn, resume, aside-runner).
Because GLM runs the claude binary, it is eligible for the same crash/restart/suspended/re-arm recovery as native Claude (claude --resume re-applies the z.ai env overlay from the session row) — distinct from Anthropic account machinery (rate-limit recovery, load-balancing), which it stays out of since it authenticates with your z.ai key, not an Anthropic login.
Compared to:
- DeepSeek / Kimi — same shape exactly (Anthropic-compat, no binary, spawn-env redirect). The only differences are the vendor base URL and the credential field. GLM is the third member of this
anthropic-compatfamily and shares every code path with them. - Codex / Anti-Gravity — separate CLI binaries with their own protocols; need dedicated
*-session-manager.tsfiles. GLM does not. - Gemini — per-turn one-shot subprocess. GLM keeps the long-lived
claudechild process; resume viaclaude --resume <uuid>. - OpenClaw — remote WebSocket gateway. GLM is plain HTTP to a vendor-hosted compat endpoint.
Cost tracking
Spend hits the standard api_cost_log table because the claude CLI emits per-turn cost events in its stream-json output and the spawn layer already plumbs those into the cost tracker. The source field on the cost row carries the provider ID (glm), so the Stats → Spend dashboard breaks down GLM spend separately from Claude/Codex/Gemini.
Cost is re-priced, not taken verbatim. The claude binary has no z.ai price table, so its total_cost_usd bills GLM models at Claude rates. GLM is therefore costReporting: 'estimated', and Omniscio recomputes each turn from the token counts × GLM's real rate (MODEL_PRICING) via applyAnthropicCompatReprice — the path shared by all four anthropic-compat vendors (DeepSeek/Kimi/GLM/MiniMax). See central-ai-spend-contract.md anthropic-compat-cost-is-repriced. (The company-credit gateway path meters its own real cost separately for credit debiting.)
Prompt-cache reads bill at z.ai's real $0.26/M. GLM caches heavily — on a long session the prompt-cache reads are typically 90%+ of the input — and z.ai charges those cached-input tokens at $0.26/M for GLM-5.x (its official rate, ~0.19× the $1.40/M fresh-input rate). The GLM rows in MODEL_PRICING carry a cacheRead override so calculateCost prices cache reads at that real rate instead of the flat 0.1×-input default ($0.14/M), which had under-counted a cached GLM session's spend ~46% (e.g. a live 95%-cache-hit session showed $2.11 when the real z.ai charge was ~$3.18). z.ai reports no separate cache-write tier — a cache MISS is billed as ordinary fresh input.
Telemetry: every successful GLM spawn fires the spawn_non_claude_session feature event, recording { provider: 'glm' } only — no session ID, project ID, or prompt content.
Files
- src/shared/providers/registry.ts — the
glmdescriptor (anthropic-compatruntimeKind, capabilities, label, pickerOrder) - src/main/services/providers/main-registry.ts — the GLM readiness wiring (toggle + API-key gates, no binary gate)
- src/main/services/anthropic-compat-provider.ts — the
z.aibase URL + credential accessors (ANTHROPIC_COMPAT_PROVIDERSmembership) - src/main/services/glm-credential-store.ts — the encrypted
glmApiKeystore - src/main/process/spawn-cluster-manager.ts — injects
ANTHROPIC_BASE_URL+ANTHROPIC_AUTH_TOKENper session row'sprovidercolumn - src/shared/types/provider-readiness.ts —
ProviderIdunion - src/shared/types/settings/accounts-providers-settings.ts —
allowGlmSessionSpawnsetting - src/shared/types/settings/ai-features-settings.ts —
glmApiKeysetting - src/renderer/src/features/settings/sections/accounts/AccountSettings.tsx — GLM section under "Show alternative AI providers" (keys and accounts only; who pays is the Who pays & who serves card)
- src/main/services/gateway-routing.ts —
buildCompanyGatewayEnv, the gateway spawn-env overlay a credits row connects through - src/main/services/providers/supply-connect.ts — connects a GLM session on the first row of GLM's supply list that can serve: a personal account (or the backup key once latched), or the Omniscio-credits row
- src/shared/types/settings/accounts-providers-settings.ts —
supplyLists.glm, GLM's supply list (the olderproviderFunding.glmchoice is read once, to build it) - gateway/gateway/routing.ts — the gateway
glmprovider route + thev1/messagespath theclaudebinary uses
Related
- kimi-provider.md — same shape (Anthropic-compat, no binary, spawn-env redirect), different vendor + key
- deepseek-provider.md — the other
anthropic-compatsibling - ai-providers.md — landing page for the full provider matrix
- provider-registry-contract.md — the DeepSeek + Kimi + GLM (
anthropic-compat) invariants - company-glm-gateway-contract.md — the invariants of GLM's Omniscio credits row (gateway-routed company credit)
- model-vendors.md — who pays for a GLM session and which company serves it (the supply list)
- api-keys.md — the "My API Keys" gateway + prepaid-credit balance the company-credit path draws down
Last verified 2026-10-06