feat(coding-agent): Vertex Model Garden open models via ADC

Merged
SL
9d ago
+923-9

Summary

Adds the builtin vertex-model-garden extension for Vertex Model Garden open models via Google Application Default Credentials only. Configure ADC (gcloud auth application-default login); the lane joins the same shared credential chain the builtin google-vertex provider already uses — no token script or local proxy.

The vertex-garden starter list is exactly:

  • deepseek-ai/deepseek-v4.1-flash-maas — DeepSeek V4.1 Flash (Vertex), global-only, reasoning enabled, 1,000,000-token context from senpi's existing deepseek-v4-1-flash model data. Published list price $0.30/$1.20 per 1M tokens, cache hit $0.03 (promotional: at no charge through 2026-10-16).
  • zai-org/glm-5.2-maas — GLM 5.2 (Vertex), published list price $1.40/$4.40 per 1M tokens, cached input $0.14.

Marketplace partner models, including Claude and Grok, are excluded because they are card-billed and promotional credits do not apply.

Request path: global OpenAI-compatible endpoint, ADC bearer auth, x-goog-user-project quota header, existing OpenAI streaming. The project comes from GOOGLE_CLOUD_PROJECT/GCLOUD_PROJECT, else the ADC quota project, else getProjectId() — never from an API-key slot. google-auth-library stays a pinned direct dependency, reached through a Node-only lazy boundary.

Maintainer review fixes (head 4a0780618, merged with origin/main 2fc705f55)

  1. The provider is ambient, not configured. vertex-garden no longer registers apiKey: "$GOOGLE_CLOUD_PROJECT". It declares an ambientAuth hook on the extension provider config (packages/coding-agent/src/core/provider-composer.ts, .../extensions/types.ts, composed into an ambient ApiKeyAuth in .../core/provider-api-key-auth.ts), mirroring the builtin google-vertex ADC lane (packages/ai/src/providers/google-vertex.ts:92). Availability is the resolution of the ADC chain itself (.../vertex-model-garden/auth.ts), so the models are no longer listed without ADC, the auth status carries ambient: true, and the lane never outranks a provider the user configured nor becomes the automatic default over it. fallbackEligible: () => false is unchanged.
  2. The project no longer travels through the API-key slot. .../vertex-model-garden/transport.ts (defaultResolveVertexAccess) passes options.env only, so the ADC quota-project and getProjectId() fallbacks are reachable and a stored secret can never reach the request URL.
  3. Offline tests for the real ADC path. packages/ai/test/google-vertex-adc.test.ts (5 cases: token minted by the file's own token endpoint, GOOGLE_CLOUD_PROJECT/GCLOUD_PROJECT precedence, explicit-option precedence, ADC quota-project fallback, empty-token error). packages/coding-agent/test/suite/vertex-model-garden-extension.test.ts adds: default resolver ignores the apiKey slot, quota fallback reachable, registration/availability gating (ambient + available with ADC, unavailable without), and default-model selection unchanged for a user with only Vertex ADC + GOOGLE_CLOUD_PROJECT (+ location for google-vertex). The fixture is a workload-identity external_account ADC file whose token_url and subject-token URL point at a loopback server — a service_account file cannot be used offline because that flow hardcodes Google's token endpoint — so no request leaves the machine. No network, no real credentials.
  4. Paid models no longer show $0. Both models carry the prices published on the Agent Platform pricing page ("Deepseek's models" and "GLM's models"; cited in .../builtin/changes.md).

Also, since the optional note applied: every thinking level is marked unsupported (thinkingLevelMap all null) because these endpoints accept no thinking parameters, so no level is offered that would be silently ignored. The 403/404 hint suggestion was left out to keep the diff scoped; it needs a wire-error seam the transport does not own today.

Validation at 4a0780618

  • Scoped vitest green: packages/ai 5/5 (test/google-vertex-adc.test.ts), packages/coding-agent 7/7 (test/suite/vertex-model-garden-extension.test.ts). Provider/auth-adjacent files also run green (82 + 33 tests).
  • Failing-first proof: the new default-selection/gating cases fail on the pre-fix extension code (4 failed | 3 passed, exit 1) — red-pre-fix.txt.
  • node scripts/check-pr-changelog.mjs exactly as CI runs it (CHANGELOG_GATE_BASE = PR base 322c56ef…, no label): exit 0, 16 production paths covered; node scripts/audit-changes-md.mjs: 489/489 covered, exit 0.
  • NODE_OPTIONS=--max-old-space-size=6144 bun run check: exit 2 — the only failures are two pre-existing tsc errors from origin/main's catalog regeneration (packages/ai/test/context-overflow.test.ts:581 and packages/ai/test/total-tokens.test.ts:749 still name anthropic/claude-sonnet-4, which main's regeneration dropped from the OpenRouter catalog). A clean origin/main worktree fails identically, and both files are untouched by this branch. Every other step (biome, pinned deps, ts-imports, lockfiles, platform lock, browser smoke) passed; full receipt in root-check.txt.
  • Live QA through ADC project able-brace-426917-c5 on the global endpoint, isolated senpi sandbox, real auth asserted unchanged: both IDs exist and answer —
deepseek-ai/deepseek-v4.1-flash-maas: exit=0 timedOut=false ok=true output=VERTEX_DEEPSEEK_OK
zai-org/glm-5.2-maas: exit=0 timedOut=false ok=true output=VERTEX_GLM_OK
real auth.json unchanged: PASS
  • git diff scan for home paths, tokens and credential patterns: clean. Evidence stays ignored and uncommitted.

Receipt excerpts:

Test Files  1 passed (1)   Tests  5 passed (5)
Test Files  1 passed (1)   Tests  7 passed (7)
RED_PRE_FIX_EXIT_CODE=1
changelog-gate: PASS - changes.md coverage complete (16 production path(s) covered); changelog entry updated (packages/ai/CHANGELOG.md, packages/coding-agent/CHANGELOG.md)
CHANGELOG_GATE_EXIT_CODE=0
ROOT_CHECK_EXIT_CODE=2

Receipt hashes (local-ignore/qa-evidence/20261016-vertex-garden-maintainer-fixes/, also archived in the shared checkout):

7124df60afe64b94edb0788d79721e60f4d4ee035850809acb7fa70a1110b6e7  red-pre-fix.txt
751ad236007c9c4e75631e02687f501a989bced44365c4f16358dafa5341a7af  root-check.txt
bd114cf0e15d284444f64459b4c7e29f7e9da9d3e5abacfcedf1dbf06ad6fc41  deepseek-cli.json
f1d8c3b4d2a2ae018f0542d4c79b8d9ac2e175df04074a0b752ada2a47d035e8  glm-cli.json
7ec903ab55ea715fcee7a38746c690e4cecf447ee9b32d1ebc36be485119f68b  live-cli.txt

Branch history

  • a638f4dfa: merged origin/main (earlier base) with a merge commit, preserving both sides of both tracker conflicts.
  • ada7c709f: trimmed the implementation to the two ADC open models.
  • 308fc3f7: earlier gate fixes (changelog entries, tracker coverage, duplicate-test removal).
  • cd403e1b7: merge of origin/main at 2fc705f55; tracker conflicts resolved by keeping both sides.
  • 4a0780618: this review round — ambient registration, project off the key slot, offline ADC tests, published prices, thinking-level consistency.

Normal push; no force-push or rebase. Do not merge: maintainer re-review is required.