feat: add Vertex AI Grok support with provider abstraction#290
feat: add Vertex AI Grok support with provider abstraction#290AmeerJ97 wants to merge 14 commits into
Conversation
Three packaging fixes that affect every install path (npm pack, npm publish, npm link, install-from-folder, GitHub release artifacts): 1. Tighten .npmignore so tarballs no longer ship local runtime state (.agents/, .cursor/, .codex/, .claude/, .grok/, .husky/, .omx/) or stale dist/ files left over from previous branches. Also exclude compiled test files (dist/**/*.test.*, dist/**/*.spec.*) and platform-specific standalone binaries (dist/grok-standalone*) which belong in GitHub release artifacts, not the npm tarball. 2. Run `clean:dist` before each tsc build so emitted dist/ never carries cross-branch debris. Add a `prepack` script so `npm pack` and `npm publish` always rebuild from a clean tree. 3. Preserve the executable bit on dist/index.js after TypeScript emits it. The tsc compiler does not preserve +x, so npm-linked installs and the `grok` bin symlink fail with permission errors. The build script now chmods the entrypoint after emit. Squashes three commits from the prior PR train (5547fe0, 307e638, 70977bf) that addressed the same packaging concerns. No source code or runtime behavior changes.
Two related fixes to install-manager and update-checker so locally-built PR binaries (e.g. when reviewers `npm install -g .` against a feature branch) do not get clobbered by the auto-updater pulling the latest public release. 1. install-manager exposes getScriptInstallContext() which returns the metadata of the script that's running, including its declared package version. update-checker uses this to detect "this is a local build, not a release install" and skips auto-update in that case. 2. The desktop launcher can resolve to ~/.grok/bin/grok, so a local PR build placed on that path must not immediately consume the latest public release and lose the unreleased work it was meant to test. Treat any mismatch between the running package version and the recorded script metadata as a non-release install for updater purposes. Squashes two commits from the prior PR train (dab125e, aaa05f7). The dab125e commit also touched src/grok/vertex-{adapter,auth}.ts in the prior branch; those Vertex changes are intentionally NOT included here — they are re-implemented properly in the upcoming provider abstraction commits. Tested: - bunx vitest run src/utils/install-manager.test.ts src/utils/update-checker.test.ts - bun run typecheck - bun run lint - bun run build
…irst impl
This is a pure architectural refactor with zero user-visible behavior change.
It establishes the provider abstraction the upcoming Vertex AI work will
build on, with xAI extracted as the first concrete implementation.
The agent loop, tool registry, media generation, compaction, and side-
question helpers now depend on a GrokProviderAdapter interface
(src/providers/types.ts) that exposes:
- kind: ProviderKind ("xai" | "vertex")
- capabilities: per-provider feature flags (responsesApi,
hostedSearch, imageGeneration, videoGeneration,
batchApi, reasoningEffort, audioStt)
- chatModel(modelId): AI SDK LanguageModel for chat
- responsesModel(...): optional, for xAI's /responses endpoint
- imageModel(...): optional, for hosted image generation
- videoModel(...): optional, for hosted video generation
- hostedTools: optional, for provider-native web/X search
- resolveRuntime(id): ResolvedModelRuntime with model + options
- getBatchClientApiKey: optional, for the batch client
The xAI implementation (src/providers/xai.ts) wraps @ai-sdk/xai and
declares the full capability set. The xAI model catalog moved to
src/providers/xai-models.ts; src/grok/models.ts is now a thin
backward-compatible re-export so existing imports continue to work.
src/grok/client.ts also stays as a backward-compat shim: it preserves
the legacy createProvider(apiKey, baseURL?) signature and the
resolveModelRuntime helper, both delegating to the adapter. Existing
consumers (agent.ts, tools.ts, compaction.ts, side-question.ts,
media.ts, client.test.ts, media.test.ts) all compile against the new
adapter type. ProviderCapabilityError is exported for consumers that
want typed capability errors.
A vertex case in createProvider() throws a clear "not yet wired up"
error so the next commit can plug in the Vertex implementation without
touching the Agent.
Tested:
- bun run typecheck (clean)
- bun run lint (clean; 5 pre-existing upstream warnings remain in
src/lsp/* and src/tools/computer.ts, none in changed files)
- bun run test (256/256 passing across 47/48 test files; the 1 failed
file is src/storage/sessions.test.ts, a pre-existing upstream
failure where vitest's worker can't import bun:sqlite. Same failure
reproduces on origin/main without these changes.)
- bun run build (clean dist tree, executable entrypoint)
- New tests in src/providers/provider-contract.test.ts cover the xAI
adapter, capability flags, model resolution, alias normalization,
factory error for the not-yet-wired vertex case, and
ProviderCapabilityError shape.
Extends UserSettings to carry provider selection and Vertex AI configuration, preserving complete backward compatibility for existing xAI users (default provider remains "xai" when unset). New types and helpers in src/utils/settings.ts: - ProviderKind imported from src/providers/types - VertexAuthMode = "adc" | "oauth_token" | "service_account_api_key" - VertexSettings: fully-resolved tuple used at request time - VertexUserSettings: optional fields as persisted in user-settings.json - DEFAULT_VERTEX_LOCATION = "global" - DEFAULT_VERTEX_BASE_URL = "https://aiplatform.googleapis.com" - DEFAULT_VERTEX_AUTH_MODE = "adc" UserSettings gains two optional fields: - provider?: ProviderKind ("xai" | "vertex"; defaults to "xai") - vertex?: VertexUserSettings New public functions: - getActiveProvider(): resolves env > settings > "xai" - isVertexProviderActive(): convenience for capability checks - resolveVertexSettings(): merges env > settings > defaults; returns projectId as `string | undefined` so callers can decide how to handle missing values - requireVertexSettings(): same resolution but throws a descriptive error when projectId is missing — for request-path call sites that cannot proceed without one saveUserSettings now normalizes both fields. Invalid provider or authMode values are silently rejected (kept undefined) rather than written through, preventing typos from corrupting the config. Env var precedence (high → low): GROK_PROVIDER > settings.provider > "xai" GROK_VERTEX_PROJECT_ID > GCP_PROJECT_ID > settings GROK_VERTEX_LOCATION > GCP_VERTEX_LOCATION > settings > "global" GROK_VERTEX_BASE_URL > settings > default GROK_VERTEX_AUTH_MODE > settings > "adc" Behavior change: none for existing users. The Vertex backend is not yet wired up — createProvider({ kind: "vertex" }) still throws "not yet wired up" until commit 7. Settings simply parse and persist. Tested: - bun run typecheck (clean) - bun run lint (clean; 5 pre-existing upstream warnings) - bun run test (273/273 passing across 48/49 files; the 1 failed file is the same pre-existing src/storage/sessions.test.ts upstream bun:sqlite issue) - 17 new tests in src/utils/settings-vertex.test.ts cover: provider defaults, env override precedence, GCP_PROJECT_ID fallback, blank- string normalization, baseURL trailing-slash stripping, missing- projectId error message, save/merge round-trip, invalid value rejection.
Encodes the four Vertex AI Grok SKUs documented at https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-grok as data, alongside the helpers needed to round-trip between canonical ids, declared aliases, and the on-the-wire request format. Catalog (src/providers/vertex/models.ts): - grok-4.20-reasoning 200K context, reasoning, vision, fn-calls - grok-4.20-non-reasoning 200K context, no reasoning - grok-4.1-fast-reasoning 128K context, reasoning, cost-efficient - grok-4.1-fast-non-reasoning 128K context, no reasoning, cost-efficient Defaults: - DEFAULT_VERTEX_MODEL = grok-4.20-reasoning - DEFAULT_VERTEX_TITLE_MODEL = grok-4.1-fast-non-reasoning Helpers: - normalizeVertexModelId(id) resolves aliases + strips xai/ prefix - getVertexModelInfo(id) catalog lookup with alias resolution - getVertexModelIds() canonical id list - isKnownVertexModelId(id) membership check - getVertexRequestModelId(id) returns "xai/<id>" form for Vertex's OpenAPI chat-completions endpoint Notable Vertex divergences from the xAI catalog (encoded in the data): - supportsReasoningEffort = false on every SKU. Vertex selects reasoning behavior by SKU rather than by the reasoning_effort knob, so per-request effort overrides are intentionally disabled here. - Smaller context windows than the xAI flagship. - Pricing fields mirror xAI public pricing for the same model family as a cost estimate; actual Vertex billing flows through the customer's GCP contract and may differ. - Batch predictions are not supported for Grok on Vertex (gated in a later commit through the provider capability flags). Behavior change: none. The catalog is queryable but no transport calls it yet — that lands in commit 7 alongside the OpenAPI chat-completions client. Tested: - bun run typecheck (clean) - bun run lint (clean) - 21 new tests in src/providers/vertex/models.test.ts cover catalog identity, context windows per SKU, reasoning flags, default selection, alias resolution, prefix stripping, request-id formatting, and unknown-id surfacing for error messages.
Adds the credential layer for the upcoming Vertex AI transport. Supports
the three auth modes declared by VertexAuthMode in src/utils/settings:
1. ADC (default) — Application Default Credentials, the path
the Vertex quickstart docs recommend. Uses
google-auth-library to resolve credentials
from `gcloud auth application-default login`,
GOOGLE_APPLICATION_CREDENTIALS, or workload
identity, in that order.
2. Explicit OAuth token — Returns a caller-supplied bearer token
verbatim. Useful for short-lived tokens
minted by `gcloud auth print-access-token`.
3. Service-account API key — Returns the configured API key. Reserved
for organizations that have enabled
service-account-bound API keys; not
verified end-to-end against the
Grok-on-Vertex endpoint and exposed
behind the explicit "service_account_api_key"
mode rather than as an automatic fallback.
The GoogleAuth client is cached as a module singleton so token caching and
refresh handled by google-auth-library are not bypassed. resetVertexAuthClient
clears the cache for recovery flows and test isolation.
formatVertexAuthErrorMessage produces a human-friendly error specifically
distinguishing the "ADC needs reauthentication" case (invalid_rapt /
invalid_grant / reauth required) from generic transport failures, so
the TUI can surface actionable guidance instead of opaque OAuth errors.
Adds google-auth-library@10.6.2 as a runtime dependency; no other
dependencies touched.
Behavior change: none. The auth function exists but no transport calls
it yet — that lands in commit 7.
Tested:
- bun run typecheck (clean)
- bun run lint (clean; 5 pre-existing upstream warnings)
- bun run test (310/310 passing across 50/51 files; the 1 failed file
is the same pre-existing src/storage/sessions.test.ts upstream
bun:sqlite issue)
- 16 new tests in src/providers/vertex/auth.test.ts cover: ADC happy
path, ADC null token, ADC re-auth detection, ADC generic error
formatting, GoogleAuth client caching, resetVertexAuthClient,
oauth_token verbatim + trim + missing-token error, service_account_api_key
passthrough + missing-key error, and JSON-string error parsing.
Wires the Vertex AI Grok backend into the provider factory. After this
commit, createProvider({ kind: "vertex" }) returns a working adapter
that translates the agent's chat requests into Vertex
generateContent / streamGenerateContent calls and converts responses
back into the OpenAI-compatible shape the AI SDK expects.
Files:
- src/providers/vertex/openapi.ts (NEW, 977 lines)
The chat-completions translator. Exports createVertexFetch,
buildVertexModelUrl, getVertexModelId, convertXaiChatRequestToVertex,
sanitizeVertexSchema, convertMessagesToVertexContents,
convertVertexGenerateResponseToOpenAI, createVertexSseStream,
and convertVertexStreamResponseToOpenAIChunks.
Translation logic ported intact from the prior PR's validated
vertex-adapter.ts (live-tested against project vierla-prod /
grok-4.20-0309-reasoning per closed PR superagent-ai#287's test notes), with
three changes for the new architecture:
* Imports rewired to ../../utils/settings and ./auth
* isTruthyEnv inlined here (clean main does not export one)
* getVertexAccessToken now passes { mode: settings.authMode }
so the auth resolver can pick ADC / oauth_token /
service_account_api_key based on saved settings
- src/providers/vertex/index.ts (NEW)
VertexProviderAdapter implements GrokProviderAdapter. Internally
builds an @ai-sdk/xai client with VERTEX_SDK_PLACEHOLDER_KEY plus
the createVertexFetch shim — the SDK never sees the placeholder
on the wire because the fetch shim rewrites the Authorization
header with the resolved Google access token.
The Shape 2 design we agreed on: the API boundary is correct
(Agent depends on GrokProviderAdapter, no scattered
isVertexModeEnabled checks anywhere), and the fetch-interception
transport is a private implementation detail of this directory.
A future PR can swap to a native LanguageModelV2 implementation
without touching anything outside src/providers/vertex/.
Capability flags advertised:
responsesApi: false (Vertex Grok has no Responses API)
hostedSearch: false (xAI-only)
imageGeneration: false (xAI-only)
videoGeneration: false (xAI-only)
batchApi: false (Vertex Grok model cards mark batch
predictions unsupported)
reasoningEffort: false (Vertex selects via SKU, not knob)
audioStt: false (xAI-only)
getBatchClientApiKey() throws ProviderCapabilityError instead of
returning a placeholder, so anything downstream that reaches for
the batch path fails loudly at the right boundary.
- src/providers/index.ts
The vertex case in createProvider() now delegates to
createVertexAdapter() instead of throwing "not yet wired up".
Exports the new factory, default model constants.
- src/providers/provider-contract.test.ts
The previous "rejects vertex" test is replaced by two new tests
that construct the vertex adapter under a stubbed
GROK_VERTEX_PROJECT_ID env var, verify kind, capabilities, and
the typed batch error.
Behavior change: provider=vertex now selects a working backend when
GROK_VERTEX_PROJECT_ID (or saved vertex.projectId) is configured.
Default provider remains "xai" so existing users are unaffected.
Tested:
- bun run typecheck (clean)
- bun run lint (clean; 5 pre-existing upstream warnings)
- bun run test (311/311 passing across 50/51 files; the 1 failed
file is the same pre-existing src/storage/sessions.test.ts upstream
bun:sqlite issue)
- Adapter-level coverage (function calling, streaming, vision,
structured output) lands in the next commit.
…d, vision
Ports the validated test suite from the prior PR's
src/grok/vertex-adapter.test.ts into src/providers/vertex/openapi.test.ts.
Adapts imports to the new module paths (./openapi, ./auth) and to the
required-authMode shape of VertexSettings.
The 21 ported tests cover:
- URL construction
* global vs regional location paths
* non-streaming generateContent vs SSE streamGenerateContent
* trailing-slash baseURL normalization
- Model id mapping
* native xAI ids translated to Vertex publisher ids
* unknown ids passed through
- Message conversion
* system → systemInstruction
* user/assistant/tool round-trip including tool_calls and
tool_call_id correlation
- Tool / function-calling translation
* function declarations forwarded with sanitized parameters
* tool_choice mapped to Vertex functionCallingConfig
* GROK_VERTEX_DISABLE_TOOLS emergency disable
* invalid function names dropped instead of sent
- Schema sanitization (structured output, function parameters)
* type fields uppercased to Vertex enum form
* additionalProperties / minLength / maxItems stripped
* anyOf-with-null collapsed to nullable: true
* union schemas resolved to a representative type
- Response conversion
* Vertex generateContent → OpenAI chat.completions shape
* usageMetadata → usage block
* functionCall parts → tool_calls
- Streaming
* SSE chunk parsing
* tool_call delta indexing kept contiguous across chunks
- Auth integration
* fetch shim invokes getVertexAccessToken with the right mode
* auth failure surfaces as a 401 vertex_auth_failed envelope
This is the validated end-to-end coverage from the prior PR (which the
contributor live-tested against project vierla-prod / grok-4.20-0309-
reasoning), repositioned under the new src/providers/vertex/ tree.
Behavior change: none. Test-only commit that lifts coverage to match
the production code that landed in commit 7.
Tested:
- bun run typecheck (clean)
- bun run lint (clean; 5 pre-existing upstream warnings)
- bun run test (332/332 passing across 51/52 files; the 1 failed file
is the same pre-existing src/storage/sessions.test.ts upstream
bun:sqlite issue).
…o Agent
Wires the Agent (and the few non-tool surfaces that talk to the
provider directly) to the GrokProviderAdapter capability flags
introduced in commit 3, replacing implicit assumptions that the
backend is always xAI.
Agent (src/agent/agent.ts):
- The constructor now constructs a Vertex provider when no apiKey is
supplied AND getActiveProvider() === "vertex" AND a Vertex project
id is configured (env or saved settings). Existing xAI users with
GROK_API_KEY see no change.
- setApiKey() now routes through buildProvider(), which switches on
getActiveProvider() and returns either the Vertex adapter (no
static key needed; auth handled by Google ADC) or the xAI adapter.
- hasApiKey() (kept on its current name for UI back-compat) now
returns true when Vertex mode is active and the Vertex provider
is constructed, even without a static apiKey. The TUI auth modal
state machine relies on this signal to skip the "enter API key"
flow when Vertex is the selected backend.
- getBatchClientOptions() now consults provider.capabilities.batchApi
and provider.getBatchClientApiKey() instead of reaching for
this.apiKey directly. With the Vertex adapter (batchApi=false),
callers get a typed actionable error instead of a confusing
OpenAI-shaped 401 from the Vertex endpoint.
Audio STT (src/audio/stt/engine.ts):
- createTelegramAudioInputEngine() now refuses to construct the
Grok STT engine when getActiveProvider() !== "xai", with a typed
error that names the active provider and tells the user how to
proceed. xAI behavior unchanged.
Notes on what is NOT in this commit:
- tools.ts hostedTools/responsesModel checks (search_web, search_x)
were already gated in commit 3 when the adapter contract landed.
- media.ts imageModel/videoModel checks were already gated in
commit 3.
- The TUI auth modal that lets users select Vertex from the UI
lands in commit 10, alongside the adjacent app.tsx fixes.
- The CLI --provider flag lands in commit 11.
Behavior change: Vertex users can now have an Agent constructed
end-to-end with no static API key. xAI behavior is unchanged for
existing users. xAI-only features called from a Vertex session
produce typed errors instead of opaque transport failures.
Tested:
- bun run typecheck (clean)
- bun run lint (clean; 5 pre-existing upstream warnings)
- bun run test (332/332 passing across 51/52 files; the 1 failed file
is the same pre-existing src/storage/sessions.test.ts upstream
bun:sqlite issue).
Bundles three TUI fixes that live in src/ui/{app,plan,processing-input}.tsx
into the same commit because they touch adjacent code in src/ui/app.tsx
and the cleaner V6 (Vertex UX) work that follows would otherwise have to
unpick them from a merge dance. Each fix is self-contained and
behavior-preserving for users who never hit the bug.
1. Plan questions leaking into chat (src/ui/plan.tsx + plan.test.ts)
Plan-mode question modals must own keyboard focus until answered.
The previous flow let unanswered tabs advance into the chat input,
which submitted accidental empty messages and let plan-question
text leak as user input. Now the modal absorbs key events and
plan-question submission is the only path that advances state.
2. Async TUI updates not painting without input (src/ui/app.tsx)
OpenTUI was leaving async tool-result and plan-question updates
visually stale until the next keyboard event. Requesting a
renderer paint from the existing scroll wake keeps long-running
turns and plan modals visible without changing message flow.
3. Liveness nudges polluting the follow-up queue
(src/ui/app.tsx + src/ui/processing-input.{ts,test.ts} NEW)
During an active turn, typing a status nudge ("continue", "status",
etc.) was both waking the TUI and enqueueing a real follow-up
message. The new processing-input helper isProcessingStatusNudge
identifies exact liveness nudges and routes them to a repaint /
status request only, preventing accidental conversation mutation
while preserving normal queued follow-ups.
These fixes were originally landed as separate commits on the prior
branch (7d36b5d, cb8e932, 655d0a0). They are bundled here because
the cleanest fix to the third one referenced state that exists in the
messy branch's app.tsx but not the clean one — bundling lets us land
all three coherently while the file is being touched.
Behavior change: TUI bug fixes only. No effect on agent/provider/auth
behavior. None of these are Vertex-related; they ride along because
they touch the same files the Vertex UX work in commit 11+ will edit.
Tested:
- bun run typecheck (clean)
- bun run lint (clean; 5 pre-existing upstream warnings)
- bun run test (338/338 passing across 53/54 files; the 1 failed file
is the same pre-existing src/storage/sessions.test.ts upstream
bun:sqlite issue).
- 6 new tests in src/ui/plan.test.ts and src/ui/processing-input.test.ts
cover the modal-focus invariants and the nudge classifier.
Adds the user-facing entry point for selecting the active backend.
grok --provider vertex # Vertex AI Grok via Google ADC
grok --provider xai # Native xAI (default; same as before)
GROK_PROVIDER=vertex grok ... # Equivalent env override
Implementation:
- New --provider <kind> CLI option with parseProviderKind validator
that rejects values other than "xai" or "vertex" early with a
descriptive message.
- applyProviderOverride() exports the chosen value to process.env
before any settings reads happen, so every downstream
getActiveProvider() consult — Agent construction, capability
checks, settings persistence — sees the same active provider
without further plumbing.
- requireApiKey() now allows empty strings through when
getActiveProvider() === "vertex", because Vertex authenticates via
Google ADC instead of a static key. The xAI error message is
extended to point users at --provider vertex when no API key is
configured.
This is the minimum change required for end-to-end Vertex usage from
the CLI: with --provider vertex and a configured GROK_VERTEX_PROJECT_ID
(or saved vertex.projectId), the Agent constructs the Vertex adapter,
ADC resolves the access token, and chat/streaming/tool-calling all
flow through src/providers/vertex/openapi.ts.
The full TUI auth modal that lets users switch providers from inside
the running app remains a follow-up — out of scope for the immediately-
mergeable replacement of superagent-ai#287, on the path for a subsequent PR.
Behavior change: --provider becomes selectable from the CLI. Default
remains xAI; existing GROK_API_KEY users see no change. Vertex users
no longer hit the apiKey-required guard.
Tested:
- bun run typecheck (clean)
- bun run lint (clean; 5 pre-existing upstream warnings)
- bun run test (338/338 passing across 53/54 files; the 1 failed file
is the same pre-existing src/storage/sessions.test.ts upstream
bun:sqlite issue).
Closes the loop on the user-facing surface for Vertex AI support.
README.md
Adds a "Vertex AI Grok (Google Cloud backend)" section between the
API key configuration and the Telegram section. Covers:
- The four documented Vertex Grok SKUs
- gcloud auth application-default login prerequisite
- Three ways to select the backend (CLI flag, env var, settings)
- Capability matrix: what works on Vertex, what's xAI-only and
returns a typed error
- Full env-var reference table
- Disclaimer that Vertex Grok is a Google partner integration and
that this project is independent of xAI Corp. and Google
CHANGELOG.md
Adds an "Added" entry summarizing the Vertex backend, the
GrokProviderAdapter contract, and the typed-error behavior for
xAI-only features. Adds a "Fixed" block covering the bundled TUI
fixes (plan-leak, async repaint, liveness nudge handling) and the
packaging/install fixes from commits 1 and 2.
.env.example
Adds the Vertex env-var block with comments matching the README
reference: GROK_PROVIDER, GROK_VERTEX_PROJECT_ID, GCP_PROJECT_ID
fallback, GROK_VERTEX_LOCATION (default "global"),
GROK_VERTEX_BASE_URL, GROK_VERTEX_AUTH_MODE.
Behavior change: docs only. No code paths touched.
Tested:
- bun run typecheck (clean)
- bun run lint (clean; 5 pre-existing upstream warnings)
- bun run test (338/338 passing across 53/54 files; the 1 failed file
is the same pre-existing src/storage/sessions.test.ts upstream
bun:sqlite issue)
- bun run build (clean dist)
There was a problem hiding this comment.
Pull request overview
Adds a provider abstraction layer to support routing Grok calls through Google Cloud Vertex AI (in addition to native xAI), plus several related CLI/TUI and packaging/update-safety improvements.
Changes:
- Introduces
GrokProviderAdapterand refactors agent/tools/media/helpers to use capability-gated provider adapters. - Adds a Vertex provider implementation (auth, model catalog, OpenAI-shape ↔ Vertex translation, tests) and CLI/config plumbing (
--provider,GROK_PROVIDER, Vertex settings). - Hardens script-managed install update behavior and includes several TUI UX fixes (plan questions, repainting, processing nudges).
Reviewed changes
Copilot reviewed 39 out of 40 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/update-checker.ts | Skips update checks/updates when not running the active script-managed install. |
| src/utils/update-checker.test.ts | Adds tests for script-managed install gating in update check/update flows. |
| src/utils/side-question.ts | Migrates side-question helper to accept GrokProviderAdapter. |
| src/utils/settings.ts | Adds provider selection + Vertex settings resolution/normalization helpers. |
| src/utils/settings-vertex.test.ts | New tests for provider selection + Vertex settings env/user-settings merging. |
| src/utils/install-manager.ts | Adds detection for “current executable is the script-managed install”. |
| src/utils/install-manager.test.ts | Tests for script-managed install detection logic. |
| src/ui/processing-input.ts | Adds helper to classify “liveness nudge” inputs during processing. |
| src/ui/processing-input.test.ts | Tests for processing-nudge classification. |
| src/ui/plan.tsx | Extracts/exports plan-question helper functions; adds required-answer notice handling. |
| src/ui/plan.test.ts | Tests plan-question helper behavior (advance/required notice). |
| src/ui/app.tsx | Wires new plan helpers + processing-nudge behavior; improves repaint behavior. |
| src/providers/xai.ts | Implements xAI provider adapter wrapping @ai-sdk/xai. |
| src/providers/xai-models.ts | Moves native xAI model catalog into providers layer (with alias normalization). |
| src/providers/vertex/openapi.ts | Implements chat-completions translation + SSE handling from Vertex to OpenAI-like shapes. |
| src/providers/vertex/openapi.test.ts | Adds transport/translation tests for Vertex OpenAPI adapter (tools/schema/streaming). |
| src/providers/vertex/models.ts | Adds authoritative Vertex Grok SKU catalog + normalization helpers. |
| src/providers/vertex/models.test.ts | Tests Vertex catalog invariants and normalization behavior. |
| src/providers/vertex/index.ts | Implements Vertex provider adapter using @ai-sdk/xai plus a fetch shim. |
| src/providers/vertex/auth.ts | Adds Google ADC auth + additional auth-mode scaffolding. |
| src/providers/vertex/auth.test.ts | Tests ADC token acquisition, error messaging, and non-ADC modes. |
| src/providers/types.ts | Defines provider contract, capabilities, runtime resolution, and typed capability error. |
| src/providers/provider-contract.test.ts | Tests adapter factory behavior and basic contract expectations. |
| src/providers/index.ts | Adds provider factory (createProvider) and exports provider types/adapters. |
| src/index.ts | Adds --provider flag, env override plumbing, and Vertex-aware API key handling. |
| src/grok/tools.ts | Migrates hosted-search tools to capability-gated provider adapter calls. |
| src/grok/models.ts | Converts legacy model module to re-export xAI catalog from providers layer. |
| src/grok/media.ts | Migrates image/video gen to provider adapter + capability gating. |
| src/grok/media.test.ts | Updates media tool tests to match adapter-based image/video model access. |
| src/grok/client.ts | Refactors client utilities to use provider adapters; keeps backward-compatible exports. |
| src/grok/client.test.ts | Updates client tests to construct providers via createProvider. |
| src/audio/stt/engine.ts | Adds explicit provider gating for Telegram audio STT (xAI-only). |
| src/agent/compaction.ts | Migrates compaction summarization to use provider adapter type. |
| src/agent/agent.ts | Refactors agent to build/consume provider adapters; adds capability gating in batch/vision flows. |
| README.md | Documents Vertex provider usage, supported features, and environment variables. |
| package.json | Adds google-auth-library dependency; hardens build/prepack and dist executable bit. |
| CHANGELOG.md | Notes Vertex provider + adapter refactor + TUI/install/update/package changes. |
| bun.lock | Locks google-auth-library and transitive dependencies. |
| .npmignore | Tightens package contents (exclude tests/dev state/standalone binaries, etc.). |
| .env.example | Documents Vertex provider env vars and basic setup steps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const isStreaming = xaiRequest.stream === true; | ||
| let accessToken: string; | ||
| try { | ||
| accessToken = await getVertexAccessToken({ mode: vertexSettings.authMode }); | ||
| } catch (err: unknown) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| return vertexErrorResponse(message, 401, "vertex_auth_failed"); | ||
| } |
|
|
||
| function unsupportedVertexEndpointResponse(url: URL): Response { | ||
| return vertexErrorResponse( | ||
| `GROK_USE_VERTEX=1 supports Vertex AI chat completions only. The xAI endpoint "${url.pathname}" is not available through Vertex AI; native xAI-only features such as Responses API search, image/video generation, STT, and Batch API require GROK_API_KEY with GROK_USE_VERTEX unset.`, |
| function resolveExplicitToken(token: string | undefined): string { | ||
| const trimmed = token?.trim(); | ||
| if (!trimmed) { | ||
| throw new Error( | ||
| "Vertex auth mode is 'oauth_token' but no token was provided. Set vertex.oauthToken in settings or run `gcloud auth print-access-token` and pass the result.", | ||
| ); | ||
| } | ||
| return trimmed; | ||
| } | ||
|
|
||
| function resolveServiceAccountApiKey(apiKey: string | undefined): string { | ||
| const trimmed = apiKey?.trim(); | ||
| if (!trimmed) { | ||
| throw new Error( | ||
| "Vertex auth mode is 'service_account_api_key' but no API key was provided. Save vertex.apiKey via settings, or switch to ADC by setting GROK_VERTEX_AUTH_MODE=adc.", | ||
| ); |
| export function requireVertexSettings(): VertexSettings { | ||
| const resolved = resolveVertexSettings(); | ||
| if (!resolved.projectId) { | ||
| throw new Error( | ||
| "Vertex AI is selected but no project id is configured. Set GROK_VERTEX_PROJECT_ID, GCP_PROJECT_ID, or save vertex.projectId via the TUI auth modal.", | ||
| ); | ||
| } |
| }, | ||
| } | ||
| : {}), | ||
| ...(partial.provider !== undefined ? { provider: normalizeProviderKind(partial.provider) } : {}), | ||
| ...(partial.vertex !== undefined | ||
| ? { | ||
| vertex: normalizeVertexUserSettings({ | ||
| ...current.vertex, | ||
| ...partial.vertex, | ||
| }), | ||
| } |
|
|
||
| private requireProvider(): XaiProvider { | ||
| private requireProvider(): GrokProviderAdapter { | ||
| if (!this.provider) { |
| resolveRuntime(requestedModelId: string): ResolvedModelRuntime { | ||
| const modelId = normalizeVertexModelId(requestedModelId); | ||
| const modelInfo = getVertexModelInfo(modelId); | ||
| // Vertex selects reasoning by SKU, not via per-request reasoning_effort, | ||
| // so providerOptions.xai.reasoningEffort is intentionally omitted. We | ||
| // still consult the saved per-model setting to preserve ergonomics for | ||
| // cross-provider tooling that round-trips the value. | ||
| const requestedEffort = getReasoningEffortForModel(modelId); | ||
| return { | ||
| model: this.sdk(modelId), | ||
| modelId, | ||
| modelInfo, | ||
| providerOptions: requestedEffort ? { xai: { reasoningEffort: requestedEffort } } : undefined, | ||
| }; |
| | `GCP_PROJECT_ID` | Fallback for `GROK_VERTEX_PROJECT_ID` | — | | ||
| | `GROK_VERTEX_LOCATION` | Vertex region (e.g. `us-central1`) | `global` | | ||
| | `GROK_VERTEX_BASE_URL` | Vertex API host override | `https://aiplatform.googleapis.com` | | ||
| | `GROK_VERTEX_AUTH_MODE` | `adc`, `oauth_token`, or `service_account_api_key` | `adc` | |
…ai#290 Single follow-up commit that addresses every concern flagged by cursor[bot] and Copilot on PR superagent-ai#290. Net diff is -31 LOC because most of the fixes are removals: stripping out auth modes that were documented but never wired up. cursor[bot] (Medium severity, src/providers/vertex/openapi.ts:193): "Vertex non-ADC auth modes always fail silently" Copilot review notes addressed (8 inline comments): src/providers/vertex/openapi.ts:197 same root cause as above src/providers/vertex/openapi.ts:776 stale GROK_USE_VERTEX text src/providers/vertex/auth.ts:81 dead config-path text src/utils/settings.ts:316 invalid provider clobbers existing src/utils/settings.ts:462 "TUI auth modal" mention out of scope src/agent/agent.ts:2248 requireProvider misleading on Vertex src/providers/vertex/index.ts:84 comment/code mismatch on reasoningEffort README.md:306 env table lists unsupported modes Changes: 1. Drop oauth_token and service_account_api_key auth modes (covers cursor[bot] and Copilot 1, 2, 4, 9). VertexAuthMode narrows to "adc" only. The docs and code paths these modes touched in this PR were incomplete — VertexUserSettings had no oauthToken/apiKey fields and the call site in openapi.ts only passed { mode }, so the modes always threw at request time. They are not part of the Vertex AI quickstart's recommended auth path either; the research that informed this PR explicitly recommends starting with ADC and treating other modes as a follow-up after live verification against Grok-on-Vertex. Files changed: - src/utils/settings.ts: VertexAuthMode type narrowed; doc comment explains the deferral. - src/providers/vertex/auth.ts: switch collapsed to ADC-only; resolveExplicitToken and resolveServiceAccountApiKey deleted. VertexAuthOptions interface simplified to just { mode }. - src/providers/vertex/auth.test.ts: oauth_token and service_account_api_key suites replaced with one defensive test that confirms unsupported modes throw a typed error. - src/utils/settings-vertex.test.ts: fixture updated from authMode: "oauth_token" to authMode: "adc"; new test verifies a saved unsupported authMode normalizes back to the ADC default. - README.md: env-var table no longer lists GROK_VERTEX_AUTH_MODE values; replaced with a one-line note that ADC is the only supported mode in v1. - .env.example: GROK_VERTEX_AUTH_MODE block replaced with the same note. 2. Stale GROK_USE_VERTEX text (Copilot 3). The unsupported-endpoint error in src/providers/vertex/openapi.ts referenced the legacy GROK_USE_VERTEX=1 flag from the prior branch. Updated to point at the new --provider xai / unset GROK_PROVIDER plus GROK_API_KEY path that this PR actually ships. 3. Invalid-provider clobbers (Copilot 5). saveUserSettings used the spread pattern { ...partial, ...(normalize? { provider: x } : {}) }. Because ...partial spreads partial.provider directly, an invalid value like saveUserSettings({ provider: "anthropic" }) would land on next.provider and the conditional override would not run. Fixed by ALWAYS overriding when partial.provider is supplied: returns { provider: normalized ?? current.provider } so an invalid update either keeps the previously saved provider or omits the field via JSON.stringify's undefined elision (matches the existing test). 4. "TUI auth modal" reference (Copilot 6). requireVertexSettings error rewritten to point at the actual supported configuration paths: GROK_VERTEX_PROJECT_ID, GCP_PROJECT_ID, or vertex.projectId in ~/.grok/user-settings.json. The TUI auth modal remains a planned follow-up per the PR description; the error no longer promises it. 5. Provider-aware requireProvider() (Copilot 7). When this.provider is null, surface "Vertex AI is selected but no project id is configured..." in Vertex mode rather than "API key required". xAI message unchanged. 6. Vertex resolveRuntime + reasoningEffort (Copilot 8). Comment in src/providers/vertex/index.ts said reasoningEffort was omitted but the code populated providerOptions when a per-model effort was saved. Since capabilities.reasoningEffort is false on the Vertex adapter, dropping the option matches the capability matrix and prevents the agent from surfacing a setting Vertex would silently ignore. resolveRuntime now returns providerOptions: undefined for Vertex; the comment matches the code. Tested: - bun run typecheck (clean) - bun run lint (clean; 5 pre-existing upstream warnings, none in changed files) - bun run test (334/334 passing across 53/54 files; the 1 failed file is the same pre-existing src/storage/sessions.test.ts upstream bun:sqlite issue) - bun run build (clean dist)
Brin PR Security ScanThis PR has findings that should be reviewed.
Findings:
Analyzed by Brin |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0d71af5. Configure here.
…rovider Addresses both fresh cursor[bot] findings on commit 0d71af5. Audited the rest of the PR for adjacent dead-code / unused-export risk before committing — no other set-but-never-read fields, no other genuinely dead exports, no remaining stale references. cursor[bot] Medium severity (src/providers/vertex/index.ts:59): "Vertex adapter stores settings but never uses them" VertexProviderAdapter held `private readonly settings: VertexSettings` populated in the constructor, but the field was never read elsewhere. All request-time settings resolution happens inside createVertexFetch via its own requireVertexSettings() lookup. The documented options.settings parameter (advertised as "Tests can pass a fully- formed object") was therefore validated at construction and silently ignored at request time — a contract violation if a caller ever passed it. Fix: drop the field, drop the options.settings parameter from VertexAdapterOptions, drop the now-unused VertexSettings type import. The constructor still calls requireVertexSettings() for the side-effect (eager misconfiguration failure at construction rather than at first request), with a comment explaining the intent. cursor[bot] Low severity (src/providers/index.ts:49): "Exported createDefaultProvider function is never used" Confirmed by grep: only the declaration, no callers anywhere in src/. The function duplicated logic already available via createProvider({ kind: "xai", apiKey, baseURL }) and the legacy createProvider(apiKey, baseURL) shim in src/grok/client.ts. Fix: delete the function and its only-used-by-it ProviderKind type-only import. ProviderKind is still re-exported from the module via the existing `export type { ... } from "./types"` block, so external API surface is unchanged. Adjacent audit (no further changes needed): - 6 of 7 ProviderCapabilities flags are not consulted by any consumer, but they are interface fields (API surface, not dead code) and cursor[bot] did not flag them on prior reviews. Method- presence checks (provider.imageModel?, provider.responsesModel?, etc.) gate the corresponding behavior already; the boolean flags serve as adapter-implementer documentation. - XaiAdapterOptions / VertexAdapterOptions / VertexAuthOptions are exported but no external consumer imports them. They are parameter types of exported functions, so removing them would force callers to inline or re-derive the type. Kept as public API surface. - No set-but-never-read fields remain on either adapter class. - No TODO/FIXME/XXX markers in changed code. - No stale references to GROK_USE_VERTEX, "TUI auth modal", createDefaultProvider, vertex.oauthToken, or vertex.apiKey anywhere in src/, README.md, .env.example, or CHANGELOG.md. Tested: - bun run typecheck (clean) - bun run lint (clean; 5 pre-existing upstream warnings, none in changed files) - bun run test (334/334 passing across 53/54 files; the 1 failed file is the same pre-existing src/storage/sessions.test.ts upstream bun:sqlite issue) - bun run build (clean dist) Net diff: 8 insertions, 20 deletions (-12 LOC).
|
@homanp Finally clean. My bad for the noise on #287 and the closed-without-merging PRs leading up to this. The Vertex partner-model API has more hidden details and i kept getting baited lol (separate reasoning vs non-reasoning SKUs instead of a This PR covers Vertex AI Grok (forreal this time) via Google ADC but behind a |

Summary
Adds Google Cloud Vertex AI as a second backend for Grok, behind a clean
GrokProviderAdaptercontract atsrc/providers/. Existing xAI users see no change; Vertex users opt in with--provider vertex(orGROK_PROVIDER=vertex) and a configuredGROK_VERTEX_PROJECT_ID, then authenticate via Google Application Default Credentials.Live-tested end-to-end against a real GCP project with
grok-4.20-0309-reasoning— chat, streaming, function calling, and provider self-detection all work:What works on Vertex (and what's xAI-only)
src/providers/vertex/openapi.ts--batch-apisearch_web/search_xgenerate_image/generate_videoreasoning_effortknob-reasoningvs-non-reasoning)The four documented Vertex Grok SKUs are encoded as data:
grok-4.20-{reasoning,non-reasoning}(200K context) andgrok-4.1-fast-{reasoning,non-reasoning}(128K context). Pricing fields mirror xAI public pricing for the same family as a CLI cost-estimate; actual billing flows through your Google Cloud contract.Architecture
The Agent now depends on a
GrokProviderAdapterinterface, not on@ai-sdk/xaidirectly. Each backend is a self-contained directory:Capability gating is explicit:
provider.capabilities.batchApi,provider.hostedTools,provider.imageModel, etc. There are zeroisVertexModeEnabled()callsites scattered across the codebase — every gating decision goes through the adapter.src/grok/{client,models}.tsare kept as thin backward-compatible re-export shims so adjacent imports keep working without churn.Why this PR replaces a prior attempt
This is the architectural redo of the closed draft #287. That branch implemented Vertex as a
fetchinterceptor wrapping@ai-sdk/xaiwith a sentinel API key, and scatteredisVertexModeEnabled()checks across 8+ files. This PR keeps the same end-user capability but routes everything through the provider abstraction, so future Vertex-only work (or a third backend) doesn't require another round of dispatch-point surgery. The npm packaging hygiene and adjacent TUI fixes (plan-mode chat leak, async repaint, liveness-nudge handling) from that branch are bundled here because they live in files this PR already rewrites.Reviewer focus
The 12-commit history is structured as a guided tour — each commit is one logical step, behavior-preserving until commit 11 — and reviewing it commit-by-commit is significantly easier than the unified diff. If you only have time for three things:
src/providers/types.ts— the contract, ~100 lines, sets the entire shape.src/providers/vertex/index.ts+openapi.ts— the Vertex transport. The 977-lineopenapi.tsis the validated translator from the prior live-tested branch, ported with adapted imports; the smallindex.tsis the new wrapper.src/agent/agent.ts+src/grok/tools.ts+src/grok/media.ts— the consumer-side migration toprovider.chatModel(...),provider.responsesModel?(...),provider.hostedTools?.webSearch(),provider.imageModel?(...), etc., all behind capability flags.Tested
bun run typecheck— cleanbun run lint— clean (5 pre-existing upstream warnings insrc/lsp/*andsrc/tools/computer.ts, none in changed files)bun run test— 338/338 passing across 53/54 test files. The one failed test file issrc/storage/sessions.test.ts, which fails on animport { Database } from "bun:sqlite"line; this same failure reproduces onorigin/mainwithout these changes (vitest worker can't importbun:sqlite).bun run build— clean dist tree, executable entrypoint preservednpm pack --dry-run— 316 files, 362.8 kB, no surprise additionsgcloud auth application-default login,GROK_VERTEX_PROJECT_ID=my-gcp-project bun run dev --provider vertex, full TUI session including bash tool calling and provider self-detection (headline screenshot above)grok-4.3(an xAI-only model not present in the Vertex catalog) and confirmed Vertex's own 404 surfaces cleanly through the adapter with the real on-the-wire URLaiplatform.googleapis.com/v1/projects/<redacted>/locations/global/publishers/xai/models/grok-4.3:streamGenerateContent— proof that ADC auth, URL construction, and error passthrough all work; the user-visible message is Vertex's own "Publisher Model ... was not found" rather than an opaque adapter crash:Out of scope (planned follow-ups)
--providerCLI flag — sufficient for first release. Modal lands in a follow-up PR scoped just tosrc/ui/.fix/git-root-inspection-errors) as its own focused submission. Genuinely unrelated to Vertex; bundling it here would be the kitchen-sink anti-pattern that closed feat: add Vertex AI Grok support with Google ADC #287.Commit train
Each commit has a one-paragraph body explaining scope, rationale, and what was tested at that checkpoint.
Note
High Risk
High risk because it introduces a new backend transport (Vertex AI via ADC) and refactors core agent/provider plumbing and tool integrations, which can affect authentication, model routing, and runtime behavior across the CLI.
Overview
Adds an opt-in Vertex AI Grok backend selectable via
--provider vertex/GROK_PROVIDER=vertex, including new Vertex env/settings (GROK_VERTEX_PROJECT_ID, location, base URL) and ADC auth/token handling viagoogle-auth-library.Refactors the agent to depend on a new backend-agnostic
GrokProviderAdapterwith capability flags, wiring provider-aware behavior across batch, hosted search, image/video generation, and Telegram STT (unsupported features now fail with clearer/typed errors under Vertex).Implements the Vertex transport as an OpenAI-shaped chat-completions translator (including streaming SSE and schema/tool-call sanitization), adds Vertex model catalog support, and updates packaging/build hygiene (prepack build, preserve
grokexecutable bit, tighten.npmignore) plus several TUI plan-mode repaint/validation and “status nudge” input handling fixes.Reviewed by Cursor Bugbot for commit 8dda814. Bugbot is set up for automated code reviews on this repo. Configure here.