PAIPAI
API Reference@pai/xai-grok

@pai/xai-grok

Run Grok models with an explicitly authorized xAI subscription credential.

@pai/xai-grok is an experimental, server-only Vercel AI SDK provider for local or personal PAI agents. It uses a Grok subscription credential rather than an xAI API key.

The package delegates every protocol concern to @ai-sdk/xai. PAI owns only the OAuth credential lifecycle and the authenticated transport. Unlike the ChatGPT subscription provider, there is no separate subscription endpoint to adapt to: the credential authorizes the same https://api.x.ai/v1 that an API key does, so model selection, provider options, tools, streaming, and response handling all behave exactly as they do upstream.

Install And Authenticate

pnpm add @pai/xai-grok ai zod
pnpm exec pai-xai-grok login

Login is an RFC 8628 device authorization: the CLI prints a short code and a verification link, opens the link in a browser, and waits for approval. Because nothing listens on a loopback port, the same command works over SSH, in a container, and on a laptop. Pass --no-browser to print the link instead of opening it.

The default credential is plaintext at .pai/xai-grok/auth.json in the current user's home directory, protected with owner-only permissions on Unix. Treat it as a password. Windows mode bits are best effort; inject a keychain-backed store when stronger at-rest protection is required.

Create A Provider

Authentication and model creation are deliberately explicit:

import { createXaiGrok, createXaiGrokAuth } from "@pai/xai-grok";
import { defineAgent } from "@pai/core";

const auth = createXaiGrokAuth();
const grok = createXaiGrok({ auth });

export const agent = defineAgent({
  name: "coding-agent",
  model: {
    model: grok("grok-4.6"),
    providerOptions: { xai: { reasoningEffort: "high" } },
  },
  instructions: "Help with the user's coding task.",
});

Provider and model construction perform no filesystem, browser, listener, or network work. The first model request reads the credential.

Model Surface

The provider is the upstream XaiProvider, so responses, chat, image, video, and files all work, credentialed by the subscription. Which model IDs a given subscription exposes is decided by xAI, not by this package.

experimental_realtime is the one exception and throws unsupported_transport. xAI's realtime API is a WebSocket that authenticates from headers passed to the WebSocket constructor rather than through fetch, so a subscription credential cannot reach it. The same applies to the WebSocket-based streaming transcription path. Use @ai-sdk/xai with an API key for those.

Model Discovery

listXaiGrokModels reads the models the signed-in account may actually use, so a host can show real choices rather than a list PAI would have to curate:

import { listXaiGrokModels } from "@pai/xai-grok";

const models = await listXaiGrokModels(auth);
const coding = models.filter((model) => model.kind === "language");

Each entry carries its id, any aliases xAI accepts for it, and a contextLength where one is published. kind is derived from what the model is billed for — a language model is charged for completion text, an image model for images — rather than from an ID prefix, so it keeps working as xAI's naming changes. The call requires a credential: the answer is account-specific, so an unauthenticated list would be a guess.

Provider Options

Use the upstream xai namespace, matching @ai-sdk/xai metadata and tool conventions. The provider adds, removes, and rewrites nothing. The option types are re-exported under their upstream names, so typing them takes no direct @ai-sdk/xai dependency:

import type { XaiResponsesProviderOptions } from "@pai/xai-grok";

const selection = {
  model: grok("grok-4.6"),
  providerOptions: {
    xai: {
      reasoningEffort: "high",
      reasoningSummary: "auto",
    } satisfies XaiResponsesProviderOptions,
  },
};

Prompt caching

xAI caches prompt prefixes on its own and reports hits in usage.inputTokenDetails.cacheReadTokens. There is no per-thread cache key to set, so unlike the ChatGPT subscription provider this one has nothing for a caller to configure. Structure reusable context as stable message prefixes.

Credential Lifecycle

Access tokens last six hours, and xAI rotates the refresh token on every exchange: the previous one dies the moment a refresh succeeds. Two independent refreshes therefore race to spend the same token, and the loser is left holding a dead credential.

The auth manager exists to make that safe:

  • Refreshes are serialized per credential store — including across separately constructed auth managers — so concurrent model calls collapse onto one rotation.
  • A rotated refresh token is kept whenever it can be read, even if the rest of the response is unusable or the credential file cannot be written. An unwritable store leaves the new credential in memory so the process keeps working, and the write is retried until it succeeds. A response that cannot be read at all is the one case where a rotation is genuinely lost and a new login is needed.
  • A credential written by something else — pai-xai-grok login in another process — takes over from one held only in memory, so the documented recovery works even mid-failure.
  • Rotations have a minimum interval. A badly wrong host clock makes every credential look expiring, and without a floor each request would retire another working refresh token.

Share one auth manager (and one provider) per credential; run one process per credential file.

A refresh xAI rejects with invalid_grant — an expired, reused, or revoked token — is not retried against the same refresh token. It surfaces as refresh_rejected and requires a new login. Failures carrying no OAuth error code, including a 401 from a captive portal or proxy, are treated as transient and retried.

Security Boundary

  • The bearer token is attached only to the configured base URL. A request aimed at any other origin, or at a path outside it, fails with unsupported_request before the credential is read.
  • Inbound authorization, cookie, proxy-authorization, and x-api-key headers are dropped before the real credential is attached, so no second identity travels with the request.
  • Error messages never include tokens, device codes, or OAuth response bodies.
  • status() reports only whether a credential exists and when it expires.

Supply fetch only from a trusted source: it observes OAuth credentials and bearer headers.

Errors

Every failure this package originates is an XaiGrokError with a stable code. Two things travel unwrapped: a cancellation rejects with the caller's own abort reason, and a custom credentialStore surfaces whatever it throws — only the built-in file store maps storage failures onto credentials_io. The codes are not_logged_in, login_denied, login_timeout, login_cancelled, login_failed, login_in_progress, refresh_failed, refresh_rejected, credentials_corrupt, credentials_insecure, credentials_io, unsupported_request, and unsupported_transport.

Scope

This is a personal-subscription integration for local or personal agents. A subscription authorizes one person's use; it is not a way to serve a multi-tenant product. Check your xAI subscription terms before relying on it.

On this page