Skip to content

feat(eve): add direct Codex model auth#417

Draft
ruiconti wants to merge 12 commits into
mainfrom
rui/codex-subscription-models
Draft

feat(eve): add direct Codex model auth#417
ruiconti wants to merge 12 commits into
mainfrom
rui/codex-subscription-models

Conversation

@ruiconti

@ruiconti ruiconti commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Part of #44 and #131.

What changed

  • Adds eve/codex with experimentalCodex({ model }), backed by the local Codex CLI auth file.
  • Replaces the app-server prototype with a direct AI SDK OpenAI Responses model and a Codex fetch transport.
  • Splits model auth classification from endpoint readiness. The compiler records an auth tag; internal/model-endpoint-auth probes AI Gateway OIDC/API-key state and Codex local auth for /eve/v1/info.
  • Teaches compile and /model flows to understand codex/<slug> ids from the local Codex model catalog.
  • Warns during production builds when the compiled manifest uses Codex auth.

Codex model auth

experimentalCodex creates an OpenAI Responses model under the codex provider namespace. It does not start codex app-server.

For API-key Codex login state, eve sends the request to the normal OpenAI Responses endpoint with the key from ~/.codex/auth.json.

For ChatGPT Codex login state, eve reads ~/.codex/auth.json, reuses a fresh access token when it can, and refreshes through https://auth.openai.com/oauth/token when it needs to. ChatGPT-authenticated model calls go to https://chatgpt.com/backend-api/codex/responses. Refreshed tokens are written back to the Codex auth file so the next request sees the updated local login state.

eve does not implement codex login. The user still signs in with the Codex CLI.

Endpoint readiness

model-auth/classify only decides the compiled auth tag: AI Gateway, Codex, or external provider. It does not read env, OIDC, or ~/.codex.

model-endpoint-auth/ai-gateway and model-endpoint-auth/codex own the live credential checks. AI Gateway checks AI_GATEWAY_API_KEY first, then Vercel OIDC. Codex checks local Codex auth state and reports whether it has an API key, a fresh ChatGPT access token, or a ChatGPT refresh token. External providers still report only that they are external, because eve cannot inspect arbitrary provider credentials.

resolve-model-endpoint-status is the dispatcher used by /eve/v1/info. That status is parsed by the client schema and rendered in the dev TUI status line. Missing or invalid Codex login state now shows as a Codex endpoint warning instead of looking ready until the first model call fails.

Catalog, setup, and source edits

The model catalog loader now recognizes codex/<slug> ids. It asks the local Codex CLI for codex debug models, falling back to the bundled CLI catalog when needed.

/model gets a Codex-specific picker when the authored model uses experimentalCodex({ model }). It rewrites only the helper's inner model literal. It still refuses to write Codex ids into Gateway string models, or Gateway ids into the Codex helper.

Production build warning

eve build warns when the primary or compaction model uses Codex auth. This is a build-time warning, not a runtime block. The warning keeps deployment policy at the point where the artifact is produced.

Add an experimental eve/codex adapter backed by the local Codex CLI app-server, including Codex model catalog lookup for compile/setup flows and /model source rewrites for experimental_codex.

Warn during production builds when a compiled manifest uses Codex subscription models, since /info should stay a runtime inspection contract.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
eve-docs Ready Ready Preview, Comment, Open in v0 Jul 1, 2026 10:22pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
eve-docs-4759 Skipped Skipped Open in v0 Jul 1, 2026 10:22pm

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Bundle + Package Summary: apps/fixtures/weather-agent

Key takeaways

  • No notable deltas vs main (70ebe69).

Delta vs main (70ebe69)

Area Metric Baseline Current Delta
Package Packed tarball 3.48 MB 3.50 MB +11.2 kB ⚠️
Package Unpacked publish size 12.66 MB 12.71 MB +46.7 kB ⚠️
Package Installed footprint 52.68 MB 52.72 MB +46.7 kB ⚠️
Package Published files 2378 2398 +20
Package Installed files 5587 5607 +20
Runtime Unique function payloads 2 2 0
Runtime Total function bytes 9.20 MB 9.20 MB +2.6 kB ⚠️
Runtime Public routes 9 9 0
Changed function payloads vs main (70ebe69) (2)
Function Status Baseline Current Delta Route changes
functions/__server.func changed 3.47 MB 3.47 MB +1.4 kB ⚠️ none
functions/.well-known/workflow/v1/flow.func changed 5.73 MB 5.73 MB +1.2 kB ⚠️ none
Build Metadata
  • Preset: vercel
  • Nitro: nitro@3.0.260610-beta
  • Output directory: apps/fixtures/weather-agent/.vercel/output
  • Build metadata timestamp: 2026-06-30T00:53:25.361Z
  • Route aliases: 9 public, 1 internal (10 total aliases)
  • Vercel routes in config: 10
  • Severity legend: 🔴 dominant/large, 🟠 notable, 🟡 watch, ⚪ small
Package Drill-Down

Package Details

  • Package: eve@0.17.1
  • Package directory: packages/eve
  • Tarball: 3.50 MB (eve-0.17.1.tgz)
  • Unpacked payload: 12.71 MB across 2398 published files
  • Installed footprint: 52.72 MB across 5607 installed files
  • Installed root package: 11.44 MB
  • Installed dependencies: 41.28 MB
  • Runtime dependencies: 1
  • Peer dependencies: 12 (11 optional)

Installed footprint is measured from an isolated temporary npm install of the packed tarball.

Heavy installed dependencies

  • @rolldown/binding-linux-x64-gnu: 20.26 MB (38.4%)
  • eve: 11.44 MB (21.7%)
  • ai: 6.28 MB (11.9%)
  • zod: 5.04 MB (9.6%)
  • nitro: 2.41 MB (4.6%)
Publish payload breakdown
Published file size
🟠 dist/src/compiled/experimental-ai-sdk-code-mo... [####....................] 1.51 MB 11.8%
🟡 dist/src/compiled/@workflow/core/runtime.js      [##......................] 794.4 kB 6.3%
🟡 dist/src/compiled/@vercel/sandbox/index.js       [##......................] 632.0 kB 5.0%
🟡 dist/src/compiled/@chat-adapter/slack/index.js   [#.......................] 436.6 kB 3.4%
🟡 dist/src/compiled/_chunks/workflow/attribute-... [#.......................] 371.6 kB 2.9%
🔴 Other published files                            [########################] 8.97 MB 70.6%
Installed footprint breakdown
Installed package size
🔴 @rolldown/binding-linux-x64-gnu [########################] 20.26 MB 38.4%
🔴 eve                             [##############..........] 11.44 MB 21.7%
🔴 ai                              [#######.................] 6.28 MB 11.9%
🔴 zod                             [######..................] 5.04 MB 9.6%
🟠 nitro                           [###.....................] 2.41 MB 4.6%
🟡 rolldown                        [#.......................] 771.7 kB 1.5%
🔴 Other installed packages        [########................] 6.53 MB 12.4%
Runtime dependencies (1)
Package Range Notes
nitro 3.0.260610-beta
Peer dependencies (12)
Package Range Notes
@opentelemetry/api ^1.0.0 optional peer
@sveltejs/kit ^2.0.0 optional peer
ai catalog:
braintrust ^3.0.0 optional peer
just-bash ^3.0.0 optional peer
microsandbox ^0.5.0 optional peer
next ^16.0.0 optional peer
nuxt ^4.0.0 optional peer
react ^19.0.0 optional peer
svelte ^5.0.0 optional peer
vite ^8.0.0 optional peer
vue ^3.5.0 optional peer
Function Drill-Down

Payload Size Graph

Unique function payload size and share of total
🔴 functions/.well-known/workflow/v1/flow.func     [########################] 5.73 MB 62.3%
🟠 functions/__server.func                         [###############.........] 3.47 MB 37.7%

Top Function Payloads

🟠 functions/.well-known/workflow/v1/flow.func • 1 public route • 5.73 MB
Metric Value
Public routes /.well-known/workflow/v1/flow
Runtime nodejs24.x
Handler index.mjs
Payload 5.73 MB
Function files 5.73 MB across 26 files
Traced dependencies 0 B
Signal 🟠 Bundled file __eve_nitro_handler__.mjs is 1.96 MB (34.2%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 __eve_nitro_handler__.mjs              [########################] 1.96 MB 34.2%
🟠 _chunks/runtime.mjs                    [############............] 983.9 kB 17.2%
🟡 _chunks/sandbox.mjs                    [#########...............] 766.0 kB 13.4%
🟡 _chunks/attribute-changes-zAifvEhb.mjs [######..................] 473.4 kB 8.3%
🟡 _libs/@ai-sdk/gateway+[...].mjs        [#####...................] 413.5 kB 7.2%
🟠 Other bundled files                    [##############..........] 1.14 MB 19.8%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x",
  "environment": {
    "NODE_OPTIONS": "--experimental-require-module"
  },
  "maxDuration": "max",
  "experimentalTriggers": [
    {
      "type": "queue/v2beta",
      "topic": "__eve776561746865722d6167656e74_wkf_workflow_*",
      "consumer": "default",
      "retryAfterSeconds": 5,
      "initialDelaySeconds": 0
    }
  ]
}

🟠 functions/__server.func • 8 public routes, 1 internal alias • 3.47 MB
Metric Value
Public routes /
/eve/v1/callback/[token]
/eve/v1/connections/[name]/callback/[token]
/eve/v1/health
/eve/v1/info
/eve/v1/session
/eve/v1/session/[sessionId]
/eve/v1/session/[sessionId]/stream
Internal aliases /__server
Runtime nodejs24.x
Handler index.mjs
Payload 3.47 MB
Function files 3.47 MB across 19 files
Traced dependencies 0 B
Signal 🟠 Bundled file index.mjs is 992.2 kB (28.6%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 index.mjs                              [########################] 992.2 kB 28.6%
🟠 _chunks/runtime.mjs                    [######################..] 891.6 kB 25.7%
🟠 _chunks/sandbox.mjs                    [###################.....] 766.0 kB 22.1%
🟡 _chunks/attribute-changes-zAifvEhb.mjs [###########.............] 449.0 kB 12.9%
⚪ _libs/zod.mjs                          [###.....................] 114.2 kB 3.3%
🟡 Other bundled files                    [######..................] 255.4 kB 7.4%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x"
}

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
ruiconti added 5 commits July 1, 2026 18:00
Move model-endpoint-auth/* and codex-model-catalog under
internal/model-auth/endpoint/ and rework the codex internals:

- auth.json parses once into a {state, credentials} snapshot; a single
  selectCodexCredentials picks the active login (an explicit auth_mode
  wins when its credential is usable, otherwise usable ChatGPT tokens
  outrank an API key), so the status view and the transport can no
  longer disagree over stale mode markers or residual tokens.
- resolveCodexEndpointStatus reads auth.json once per /info, not twice.
- the transport drops its whole-body store:false JSON rewrite; the
  model wrapper owns that invariant, the transport owns credentials and
  the endpoint rewrite.
- the model wrapper strips OpenAI itemIds copy-on-write instead of
  structuredClone-ing the entire prompt on every call.
- JWT payloads decode via vendored jose decodeJwt, local isRecord
  copies defer to shared guards, and the codex tests share one
  unsigned-JWT fixture.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Codex auth is now an explicit opt-in: only the development-gated
experimental.useCodexSubscription flag produces auth {kind:"codex"}.
classifyModelAuth no longer maps a provider named "codex" to codex
auth, so an authored LanguageModel with that provider name keeps its
own baseURL and credentials instead of being silently rerouted through
the local Codex login (previously in production builds too). The
runtime codex resolver drops the parallel codex/<slug> id grammar the
authoring surface already rejected.

auth becomes required on InternalAgentModelDefinition — the compiled
manifest schema already required it — deleting the routing-derived
`auth ?? modelAuthForRouting(routing)` fallbacks, which would have
misclassified a codex model as gateway had they ever fired. The
codex-in-production-build warning is deleted as provably dead code:
`eve build` compiles in production mode, where the flag is off, so a
production manifest can never carry codex auth.

The leftover eve/codex export surface is removed and the changeset is
rewritten to describe the flag that actually shipped. The dev TUI
setup-issues/status-line surfaces and docs follow the same semantics.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Replacing the client's inline agent-info schemas with the shared
.strict() ones made client.info() throw on any field a newer server
adds to model.routing or model.endpoint — exactly the kind of addition
this branch itself makes. The endpoint-status schema is client-only,
so it simply drops .strict(); model routing keeps the strict variant
for compiled-manifest validation (where unknown keys mean compiler
drift) and gains a strip-mode client variant built from the same shape
objects, so the two cannot diverge.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
changeAgentModel now runs the uneditable-model refusal itself and
derives the slug-validation boundary from the compiled model's auth,
so the stringly ModelChangeValidation union and the
modelChangePreflight / modelChangeRefusalForUneditableModel exports
disappear, and both callers — the /model menu flow and the TUI's
`/model <slug>` handler — stop threading validation state across the
module boundary. The codex-transport-id rejection message is built in
one place, and the openai/ id formatter moves into the codex catalog
next to the prefix it formats.

apply-model-name also loses the vestiges of the removed multi-property
editor: applyStringLiteralChange is back to (source, literal, to), and
findProperty/stringLiteralFromProperty re-merge into their single
composition.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Direct (non-gateway) AI_APICallError failures — the codex transport's
error shape — now summarize with the nested API message when the body
carries one, or an HTTP-status + response-body fallback otherwise.

Two guards keep the summaries honest: transient 408/429/5xx failures
return to the generic retry-exhausted framing instead of "AI Gateway
model request rejected", and bare param/status field values are no
longer promoted to user-facing messages (a 400 with param "input"
previously surfaced as "Model provider API error: input"); structured
error.code objects still are.

The AI Gateway provider-attempt (providerMetadata.gateway.routing.
modelAttempts) mining that rode along here is dropped: codex requests
bypass the gateway, nothing else consumed it, and it deserves its own
change.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
ruiconti added 2 commits July 1, 2026 18:01
The exact Sandbox.get assertions only passed on machines without
Vercel credentials: with env vars cleared, getVercelSandboxCredentials
still falls through to getVercelOidcToken, which refreshes a real
token from the local CLI login (network I/O in a unit test) and
injects credentials, fetch, and signal into the lookup options — which
is why 6c1d67f loosened the assertions to objectContaining. Mock the
vendored @vercel/oidc module to fail instead, so every machine
exercises the deterministic credential-less path CI sees and the exact
{ name, resume } contract is pinned again.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Signed-off-by: Rui Conti <ruiconti@gmail.com>
The stored ModelAuth union was almost entirely derivable — ai-gateway
and external mirrored the model's routing, and codex was the only real
bit of information. Storing that derived classification forced auth
onto every model reference (and into ~40 test fixtures) for one flag's
worth of signal.

The compiled model reference now carries an optional
`transport?: "codex"` literal instead, set by the compiler only when
experimental.useCodexSubscription applies; absent means the model is
served by whatever its routing implies. The ModelAuth type,
modelAuthSchema, classifyModelAuth/cloneModelAuth, and the classify
module are deleted; endpoint status resolves from {routing, transport};
the /model flow reads a plain codex boolean; and every fixture file the
required-auth change touched reverts to its merge-base content. The
provider-name hijack fix holds by construction: nothing but the flag
path can set the marker.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant