feat(ai): provider and OAuth parity for /login#193
Conversation
Add 18 API-key providers (alibaba-coding-plan, cursor, deepinfra, firepass, fugu, gitlab-duo, glm-zcode, kagi, kilo, kimi-code, litellm, lm-studio, minimax-code(-cn), moonshot, nanogpt, ollama(-cloud), openai-codex-device, parallel, perplexity, qianfan, qwen-portal, synthetic, tavily, venice, vllm, zenmux) plus OAuth flows for cursor, gitlab-duo, glm-zcode, google-antigravity, google-gemini-cli, kilo, kimi-code, openai-codex-device, perplexity, xai, and the remaining Gajae provider-ID gaps for full /login parity. The coding-agent provider wiring (model-resolver defaults, provider-display-names, auth-providers oauth-only set) travels here because model-resolver is exhaustive over the ai KnownProvider union. Source of truth for keys remains env-api-keys.ts; parity is enforced by test/api-key-provider-parity.test.ts.
Declare the API-key providers added for /login parity (alibaba-coding-plan, deepinfra, firepass, fugu, kagi, litellm, lm-studio, nanogpt, ollama, ollama-cloud, parallel, qianfan, qwen-portal, synthetic, tavily, venice, vllm, zenmux) across the three synchronized environment surfaces: .devcontainer/devcontainer.json secrets, scripts/devenv-setup.mjs PROVIDER_KEYS, and .agents/skills/senpi-qa/references/env-vars.md. Source of truth remains packages/ai/src/env-api-keys.ts.
- Remove kagi, parallel, and tavily providers (search-only, no chat) - Fix GLM ZCode OAuth to pin endpoints to z.ai - Add CRLF-delimited SSE parsing for provider streams - Add GitLab Duo and Perplexity OAuth test coverage - Update model-registry and model-resolver for removed providers
Resolve changes.md conflicts in packages/ai and packages/coding-agent by keeping both the provider parity sections and the upstream sections.
Resolve provider-union conflicts with main Radius support while keeping parity providers. Drop residual kagi/tavily/parallel env-surface wiring.
|
@code-yeongyu This PR's CI is blocked before any jobs start: the fork workflow run concluded Could you open the run below and choose Approve and run workflows? https://github.com/code-yeongyu/senpi/actions/runs/29494372152 No branch-protection or workflow change is requested; this only approves the existing run for this PR head SHA. |
code-yeongyu
left a comment
There was a problem hiding this comment.
Review verdict: NEEDS-CHANGES
Thanks — the provider/OAuth surface is broad and most of the pure OAuth flow logic, env-key mappings, and tests are solid (mocked fetch, no real creds, no token spend; models.generated.ts is generator-consistent). But several advertised login paths cannot produce a working model request yet, and main has since restructured packages/ai (utils/oauth/ → auth/oauth/, provider factories, typed load.ts + bun-oauth.ts bundled loaders) and independently landed xAI device OAuth — so this needs a re-port, not a mechanical rebase.
Findings
| Pri | Location | Finding |
|---|---|---|
| P1 | packages/ai/src/providers/cursor.ts:17 |
Use Cursor's Connect transport instead of OpenAI completions — The new cursor/default catalog is declared as openai-completions, and cursorProvider() dispatches it through the generic OpenAI adapter. Cursor's provider contract uses its HTTP/2 Connect/protobuf AgentRun RPC rather than an OpenAI /chat/completions endpoint, so login can succeed but every subsequent Cursor model request is sent with the wrong protocol and fails. |
| P1 | packages/ai/src/utils/oauth/perplexity.ts:205 |
Separate Perplexity session OAuth from direct API-key auth — The OTP/native-app flow returns a www.perplexity.ai web-session JWT, but this provider exposes that JWT through getApiKey() and the model provider sends it to https://api.perplexity.ai via the generic OpenAI adapter (packages/ai/src/providers/perplexity.ts:11-17). Perplexity session tokens are not direct API keys and are rejected by that endpoint, so the advertised OAuth path fails on its first model request. |
| P1 | packages/ai/src/providers/gitlab-duo.ts:12 |
Exchange GitLab PAT credentials before proxy requests — The provider advertises GITLAB_TOKEN as an ordinary API key, but only OAuth credentials pass through getGitLabDuoDirectAccess(). Stored or environment API-key credentials therefore reach the GitLab AI proxy as the raw PAT and omit the short-lived direct-access token and instance headers that this patch itself obtains in packages/ai/src/utils/oauth/gitlab-duo.ts:96-131. |
| P1 | packages/ai/src/providers/gitlab-duo.models.ts:21 |
Route GitLab GPT-5.1 through chat completions — This catalog marks gpt-5.1-2025-11-13 as openai-responses, so the provider posts a Responses payload to the GitLab OpenAI proxy. The GitLab Duo mapping for this model is the chat-completions API; only Codex-family mappings use Responses. |
| P1 | packages/ai/src/utils/oauth/glm-zcode.ts:493 |
Preserve GLM bearer auth in the compatibility provider — glmZcodeOAuth.toAuth() returns both the provisioned key and Authorization: Bearer …, but the compatibility OAuthProviderInterface below exposes only getApiKey. The new coding-agent request-auth dispatcher falls back to that method when getRequestAuth is absent, so it drops the bearer header and the Anthropic adapter sends the key as X-Api-Key instead; stored GLM OAuth credentials cannot authenticate model calls. |
| P2 | packages/coding-agent/src/core/auth-providers.ts:17 |
Exclude OAuth-only Google providers from API-key login — The new exclusion set contains only openai-codex-device, while BUILT_IN_PROVIDER_DISPLAY_NAMES now includes google-gemini-cli and google-antigravity. Because display-name presence unconditionally makes a provider API-key eligible, /login advertises API-key input for both providers even though their definitions expose only OAuth and their runtime requires serialized { token, projectId } credentials. |
Merge path
- Re-port flows into
packages/ai/src/auth/oauth/againstauth/types.ts(OAuthAuth/AuthInteraction), extendOAuthFlowLoadersinauth/oauth/load.ts, and register static imports insrc/bun-oauth.ts. - Drop the PR's xAI OAuth + provider changes entirely — main already has them (
5220aba61,a01baaaea). - Reconcile
kimi-codewith main's existingkimi-coding(same product, sameKIMI_API_KEY, different API flavor) — one login entry, explicit decision. - Fix the P1s above before re-advertising Cursor/Perplexity/GitLab/GLM in
/login. - GLM zcode flow: add PKCE (custom-scheme code interception risk) or document acceptance; Perplexity desktop-session borrow should be opt-in with user-facing consent.
FYI the Check and test failure on your last run was the flaky TestAttachOrSpawn_RaceExactlyOneDaemon Go test (it also flakes on main); I rerailed it and approved the pending workflow runs.
(Reviewed with the PR refs fetched locally against origin/main; line numbers refer to the PR head.)
… P1s, drop GLM ZCode Re-port OAuth flows from utils/oauth to auth/oauth with bundledLoaders, route Cursor through Connect/protobuf ChatService, fix GitLab PAT exchange and gpt-5.1 chat-completions routing, delist Perplexity session OAuth from /login, exclude Google CCA providers from API-key login, remove unofficial GLM ZCode login, keep browser smoke Node-free, and update stale kimi-coding live-test model ids.
Review follow-up statusProvider/OAuth parity review fixes were pushed earlier on this branch:
Downstream stack also updated:
|
Bring PR code-yeongyu#193 onto current main auth/ModelRuntime layout while preserving provider OAuth parity review fixes (Cursor Connect, GitLab, Perplexity delist, Google OAuth-only, GLM ZCode removed) and regenerating model catalogs.
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
… main rebase Rebase the broker branch onto the conflict-resolved code-yeongyu#193/main auth stack, re-port multi-account vault hooks onto CredentialStore AuthStorage, and keep broker review P1/P2 fixes (CAS disable, extras, lease prune, getRequestAuth).
…in rebase Bring the gateway branch onto the conflict-resolved code-yeongyu#193/code-yeongyu#194 stack and keep auth-gateway review P1/P2 fixes (broker URL validation, CORS preflight order, verifier evidence, 502 mapping, qualified models).
Conflict resolution vs
|
…er-parity PR base is upstream main (not the fork). Merge upstream main and keep provider/OAuth parity review fixes while accepting upstream model catalogs.
Drop the PR's xAI browser OAuth in favor of main's device-code flow, restore OAuth-only API-key login exclusions for Google CCA providers, and keep the provider/OAuth parity work on current main.
Keep main's device-code xAI flow (drop PR browser discovery), add a thin legacy OAuthProviderInterface adapter for the compat registry, and exclude google-gemini-cli / google-antigravity from API-key /login.
Babysit update (merge + review fixes)Done this cycle
Already present on branch (prior
|
| Review P1 | Status |
|---|---|
| Cursor Connect transport (not openai-completions) | OK — cursorProvider() uses cursorConnectApi() |
| Perplexity session OAuth separated from API-key path | OK — provider is API-key only; session flow not in getOAuthProviders() registry; tests assert no OAuth on model provider |
GitLab PAT → getGitLabDuoDirectAccess for API-key creds |
OK — gitlabDuoApiKeyAuth().resolve exchanges PAT |
| GitLab GPT-5.1 → openai-completions | OK — catalog type is openai-completions |
GLM zcode bearer / getRequestAuth |
N/A — GLM ZCode deliberately dropped from PR |
Re-port into auth/oauth + load.ts + bun-oauth.ts |
OK — flows live under packages/ai/src/auth/oauth/ with bundled loaders |
Verification
cd packages/ai && npx vitest --run test/xai-oauth.test.ts→ 14/14 pass (main device flow)- Also green:
perplexity-oauth,gitlab-duo-oauth,gitlab-duo-models,cursor-connect(25 tests total in that batch) biome checkon touched xAI + auth-providers files clean- PR
mergeableis now MERGEABLE (was CONFLICTING)
Remaining (not fully closed this cycle — still under CHANGES_REQUESTED)
kimi-codevs mainkimi-codingreconciliation — both remain registered (KIMI_API_KEYshared, different API flavors: OpenAI completions vs Anthropic messages). Review asked for one login entry / explicit product decision; not collapsed yet.- Perplexity desktop-session borrow consent / opt-in UX and GLM zcode PKCE notes from review remain product/docs decisions (GLM path removed; Perplexity session flow is preserved but not advertised on the model provider).
- Full-repo
npm run checknot run this cycle (scoped tests + biome only). CI will re-run on the pushed head.
Pushed: a00dc64d3 on islee23520/senpi:feat/auth-provider-parity.
builtinProviders listed googleProvider() twice, so Models.setProvider collapsed to 63 unique providers while the array length was 64 and providers.test failed. Matches main's single registration.
Evidence-based follow-up (CI green path)Review findings verification
CI root cause (this push)
Tests run locally (this cycle)
Not merging until Check and test is green and review is re-approved. |
Expose minimax-code, minimax-code-cn, moonshot, and opencode-zen as API-key login targets via display-name eligibility.
Evidence cycle 2Added missing Local: ai provider/oauth suite 41/41, senpi-qa cli-smoke + mock-loop + rpc self-tests pass ( Open product item remains: kimi-code vs kimi-coding single login entry. Not merging until green CI + re-review. |
…render Long absolute worktree paths wrap at render width 120, so compact collapsed text is no longer a single line. Flatten whitespace before asserting the compact resource label.
Build harness under real timers and enable fake timers only for the idle-unload countdown so Vitest no longer flakes with 30s hangs on advanceTimersByTimeAsync/runOnlyPendingTimersAsync in CI.
CI fix: app-server idle-unload timer flakeRoot cause
Those tests called Fix (head
|
kimi-code and kimi-coding are the same product at api.kimi.com/coding with the same KIMI_API_KEY. Upstream kimi-coding is the maintained entry (k3 video input, adaptive thinking), so drop the PR-only kimi-code OAuth provider and its catalog so /login shows exactly one Kimi entry. Adds a regression asserting a single kimi-* provider and KIMI_API_KEY resolution.
kimi-code vs kimi-coding — decision appliedDecision: keep upstream What changed (head
|
|
@code-yeongyu The kimi-code vs kimi-coding review item is now resolved per the decision above (single |
…parity # Conflicts: # packages/ai/src/providers/data/openrouter.json
…parity # Conflicts: # packages/ai/test/abort.test.ts # packages/ai/test/context-overflow.test.ts # packages/ai/test/cross-provider-handoff.test.ts # packages/ai/test/empty.test.ts # packages/ai/test/image-tool-result.test.ts # packages/ai/test/stream.test.ts # packages/ai/test/tokens.test.ts # packages/ai/test/tool-call-without-result.test.ts # packages/ai/test/total-tokens.test.ts # packages/ai/test/unicode-surrogate.test.ts
Summary
Adds 18 API-key providers + OAuth flows (cursor, gitlab-duo, glm-zcode, google-antigravity, google-gemini-cli, kilo, kimi-code, openai-codex-device, perplexity, xai) and the remaining Gajae provider-ID gaps for full
/loginparity.packages/aionly, plus the coding-agent provider wiring (model-resolver,provider-display-names,auth-providers) that is exhaustive over the aiKnownProviderunion.Stack
Base unit of the auth split. The credential-broker and auth-gateway PRs stack on top of this branch.
Evidence
npm run checkgreen (biome, tsgo, shrinkwrap, install-lock, neo).packages/ai/src/env-api-keys.ts; parity enforced bytest/api-key-provider-parity.test.ts.Note: the only pre-existing
aitest failures are Cloudflare live-E2E tests that fail identically onmain(live CF endpoint errors in this environment) — unrelated to this PR.Note
This PR now also includes the synchronized environment-surface wiring (
.devcontainer/devcontainer.jsonsecrets,scripts/devenv-setup.mjsPROVIDER_KEYS,env-vars.md) — per the repo contract, those surfaces must trackenv-api-keys.ts, so they travel with the providers.