README.md

September 18, 2026 · View on GitHub

jev-classifier — Intercept. Classify. Enforce the right tool.

Windows · macOS · Linux   |   Node.js 22+   |   MIT

Quick start · Integrations · Monitoring · Settings · Reference


jev-classifier connects your coding agent to Jev, a model that predicts which tool to use next. Start by watching its suggestions. Then, where supported, let it choose the tool your agent calls. Every decision goes into a log you can review.

Keep using your agent's model and login. Configure a separate key for Jev during setup.

The original idea and inspiration for jev-classifier come from jev-eval-agent by vinilana. This project brings that approach to a local gateway and MCP tools for coding agents.

ConnectionAgentsHow it works
ProxyCodex, Claude Code, Grok Build, OpenCodeRequests pass through jev-classifier on their way to the model
MCP toolCursor, AntigravityThe agent calls Jev when it wants a suggestion

Codex Responses Lite records suggestions without forcing a tool. OpenCode supports API keys with its v1 configuration. Cursor and Antigravity choose when to ask Jev and whether to follow it.

Quick start

1. Install

Install Node.js 22 or newer, then install jev-classifier globally from npm:

npm install --global jev-classifier
jev-classifier --help

The global install makes jev-classifier available from every project and keeps desktop startup entries tied to a stable installation. No repository checkout or build step is required.

Update or uninstall

Install the latest release:

npm install --global jev-classifier@latest

Before uninstalling, stop the gateway and remove its optional desktop startup entry:

jev-classifier stop
jev-classifier startup disable
npm uninstall --global jev-classifier

If no gateway is running or startup was never enabled, the first two commands may report that there is nothing to stop or disable. Your saved settings and logs are left in place.

Install from source

Use this only when developing the project or testing unreleased changes:

git clone https://github.com/felpsdev/jev-classifier.git
cd jev-classifier
npm ci
npm run build
node dist/cli.js setup

In the commands below, replace jev-classifier with node dist/cli.js when running from source.

2. Configure

jev-classifier setup

Setup walks you through choosing a Jev provider, entering its key, and picking your agent. You don't need to set environment variables. Your choices are saved for all projects.

Choose Observe to watch Jev's decisions first. If you don't have a key yet, choose Offline test to check the connection without calling Jev. Review your settings, then save. Running jev-classifier without a command opens the interactive menu.

3. Connect an agent

AgentCommand
Codexjev-classifier run codex
Claude Codejev-classifier run claude
Grok Buildjev-classifier run grok
OpenCodejev-classifier run opencode
Cursorjev-classifier connect cursor
Antigravityjev-classifier connect antigravity

For Codex, Claude Code, Grok Build, and OpenCode, run starts the gateway and opens your agent. If the gateway is already running, it uses that one. Connection settings apply to this session; your agent's configuration files stay as they are.

The gateway keeps running after you close the agent. Stop it with jev-classifier stop. To pass an option to the agent, put it after --:

jev-classifier run codex -- --no-alt-screen

For Cursor and Antigravity, open the editor and reload its MCP servers after connecting.

4. Check that it works

jev-classifier doctor --check
jev-classifier status
jev-classifier logs --follow

doctor --check sends a small request to Jev, which your provider may charge for. In status, watch the request and classification counts increase while your agent works.

For Cursor or Antigravity, ask the agent to call jev_status. To try a prediction, ask it to call jev_choose_next_tool with your task and available tools. Check logs --decisions for the result.

Agent integrations

Codex, Claude Code, and Grok Build

Pick Existing login in setup to use the account you're already signed into. Pick Provider API key to use a separate key for the agent's model. The agent handles sign-in and refreshes its own login. Jev's key is used only for classification.

For manual setup, run jev-classifier config <agent>. You can also expand the connection instructions in Reference. The Grok integration uses the official Grok Build CLI.

OpenCode

Run jev-classifier run opencode. Inside OpenCode:

  1. Use /connect to add an OpenAI, Anthropic, or xAI API key.
  2. Use /models to pick a model from that provider.
  3. Run a task and check the opencode counts in jev-classifier status.

This connection supports OpenCode v1 with API keys. Subscription plugins may send requests directly to the provider and skip the proxy. Other providers and the v2 config format are not supported yet.

How OpenCode settings are applied

The launcher sets OPENCODE_CONFIG_CONTENT for this session. It changes the OpenAI, Anthropic, and xAI base URLs and keeps your other inline options. Managed OpenCode settings can take priority, so check status to confirm that requests reach the proxy.

See OpenCode's provider settings and config priority.

Cursor and Antigravity

Run jev-classifier connect cursor or jev-classifier connect antigravity, then open the editor and reload its MCP servers. The command adds jev-classifier to your global MCP config, keeps other servers, and backs up the file before changing it.

The editor starts jev-classifier when it needs the MCP tools. You don't need to run serve. It reads your saved Jev settings; your editor keeps its own login.

ToolWhat to ask
jev_status"Call jev_status to check the connection."
jev_choose_next_tool"Ask jev_choose_next_tool which tool to use next. Include my task, available tools, and completed actions."

The agent decides when to ask Jev and whether to follow its suggestion. Jev sees only the context included in the call, which is sent to your chosen provider. It cannot watch the whole session or force the agent's next step.

Use logs --decisions to review suggestions under cursor or antigravity. Restart the editor's MCP server after changing Jev settings. This connection works with local agents; remote cloud agents cannot use the local process.

MCP config files and backups
EditorConfig file
Cursor~/.cursor/mcp.json
Antigravity~/.gemini/config/mcp_config.json

If the current Antigravity file is missing, an existing ~/.gemini/antigravity/mcp_config.json is used.

Running connect again leaves an identical entry alone. If the file contains invalid JSON, the command leaves it untouched. Run config <editor> to print the entry and add it yourself. run cursor and run antigravity perform the same setup; open the editor yourself afterward.

MCP suggestions have applied: false in the decision log. They appear in logs, metrics, and the dashboard. The HTTP gateway's counters cover only proxy traffic.

See Cursor MCP and Antigravity MCP.

Run agents without Jev

Turn the proxy off for future run commands, or bypass it for one session:

jev-classifier proxy off
jev-classifier run claude
jev-classifier run codex
jev-classifier proxy on
jev-classifier run claude --no-proxy
jev-classifier proxy status

The main interactive menu also offers Turn Jev proxy on or off. The saved preference applies to Codex, Claude, Grok and OpenCode. Direct launches need no Jev key and do not start or contact its gateway. Agents manage their own authentication; --auth applies only to proxy launches. Codex uses its built-in openai provider in direct mode.

Restart your agent to switch an existing session. Disabling the proxy does not stop a running gateway or change desktop startup; use stop and startup disable for those separately. Cursor and Antigravity use MCP instead of the proxy; disable their Jev entry in the editor's MCP settings to stop using it.

Direct launches remove inherited Jev URLs from agent environment variables and OpenCode's inline provider settings, without changing the parent terminal. If you manually saved Jev URLs in Claude, Grok or OpenCode configuration files, remove those overrides there too. To launch Codex directly outside this wrapper, restore its model_provider from jev to openai in its configuration. Environment and explicit --env-file settings override the saved preference; --no-proxy always bypasses it for that launch.

Where Jev runs

Choose a Jev provider in setup. Your coding agent keeps using its own model provider.

Jev providerAPI-key variableDefault Jev model
TypeSafeTYPESAFE_API_KEYjev-latest
OpenRouterOPENROUTER_API_KEY~typesafe/jev-latest
Vercel AI GatewayAI_GATEWAY_API_KEYtypesafe-ai/jev

OpenRouter and Vercel AI Gateway handle Jev's predictions only. They don't run your agent's coding model through this tool.

For proxy connections, choose how much control to give Jev:

ModeWhat happens
Observe (shadow)Record Jev's prediction, let the agent choose, and compare the two
Enforce (enforce)Set tool_choice to Jev's prediction when confidence is high enough

Start with Observe. Change modes in setup, or pass --shadow to observe for a run. Enforce is the default when no mode is configured. It adds a hint instead of forcing a tool when confidence is low.

jev-classifier keeps the full tool list in every request, so that part of the prompt cache stays valid. It also checks whether the requested work is done before accepting Jev's suggestion to respond. If classification fails, the original request continues to the model.

Codex Responses Lite always observes. Requests with duplicate tool names or previous_response_id also fall back to Observe. Check shadowReason in the decision log.

How predictions work

The proxy turns the conversation into a RouterState, asks Jev which tool comes next and whether the work is done, then checks both answers. This follows jev-eval-agent.

In enforce mode, respond_to_user maps to tool_choice: auto. If confidence is below MIN_CONFIDENCE, the proxy adds a hint with the three most likely tools. The done gate can override a suggestion to respond when work remains.

OpenRouter uses its alpha Decisions API with choice and noul questions. Vercel uses its evaluation API with choice and boolean. The boolean probability is used to check completion.

If the provider omits confidence, the chosen option's probability is used. Missing or invalid probabilities count as a classification failure.

Gateway and startup

jev-classifier serve (or start) frees the original terminal and reuses an existing gateway. Use serve --foreground to keep the server in the current terminal.

PlatformDefault startAutomatic startup
WindowsGateway in a separate PowerShell windowCurrent user's Startup shortcut
macOSBackground gateway with a Terminal log viewerUser LaunchAgent at next sign-in
Linux desktopBackground gateway with a detected terminal log viewerXDG desktop autostart at next sign-in
Linux headless / SSHBackground gateway; use logs --followDesktop autostart does not run without a graphical login

Linux detects x-terminal-emulator, gnome-terminal, konsole, or xterm. If no display or usable terminal is available, the gateway continues in the background. On macOS/Linux, closing the log viewer leaves the gateway running; use stop to end it. On Windows, closing the gateway window also stops its process. Background console output is saved as gateway-console.log alongside the settings; routine activity and errors are also in the rotating event log.

Choose startup behavior in setup or use these commands:

jev-classifier startup               # interactive Enable / Disable menu
jev-classifier startup enable        # register for the next desktop sign-in
jev-classifier startup disable
jev-classifier startup status

Startup applies to your user account and needs no administrator access. It starts the gateway when you sign in to your desktop, using your saved settings. It does not run before login. Disabling startup leaves a running gateway active. Enable it again after moving this checkout or changing where Node is installed.

Startup files and stopping older gateways

macOS uses ~/Library/LaunchAgents/ai.jev.classifier.plist. Linux uses $XDG_CONFIG_HOME/autostart/jev-classifier.desktop, or ~/.config/autostart/ by default.

The stop command uses a local control token saved beside your settings. Health responses do not include that token. If a gateway was started with an older version, close it in its original terminal once. The new start/stop commands can then manage the next instance.

Monitoring

Terminal logs use aligned time, level, and agent columns. Event details use labeled fields such as chosen=Read, confidence=98%, and jev=124ms. Long messages wrap under the message column; narrow terminals show the details below the event header. Saved logs include the local date as well as the time. logs --json keeps the original machine-readable records.

CommandShows
doctorInstallation, saved configuration, and gateway availability
doctor --checkWhether Jev responds to a test request
status --watchRequests, predictions, errors, and the last decision for each agent
logs --followLive gateway and MCP activity; Ctrl+C stops viewing
logs --lines 100Recent saved activity, including prior runs
logs --decisionsClassification history
metricsDecision totals grouped by agent, model, and mode
uiDashboard at http://localhost:8090

Prefix commands with jev-classifier. Use --json with doctor, status, settings, logs, or startup for machine-readable output. --no-color or NO_COLOR disables colors. Output redirected to a file uses plain text. status --watch --json emits one JSON object per line.

doctor --check tests the settings loaded for that command. If a gateway is already running, it may still have older settings; doctor reports when the provider or model differs. Watch the classification count in status to confirm that requests are reaching Jev. For MCP suggestions, use logs, metrics, or the dashboard.

Gateway and MCP events are saved in gateway.jsonl beside the global settings. It rotates at 5 MB and retains one previous segment. Decision history lives in decisions.jsonl, unless customized. Detached macOS/Linux launches also save console output in gateway-console.log.

Health counters and Codex observe-mode behavior

Open http://127.0.0.1:8080/__jev/health or run:

Invoke-RestMethod http://127.0.0.1:8080/__jev/health

requests counts incoming proxy traffic; classified counts successful Jev decisions. classificationFailures, noTools, and invalidJson explain requests that passed through without classification. jevConfigured reports whether the selected classifier has a key (not whether authentication succeeded); jevProvider and jevModel identify the classifier backend, without exposing credentials; stub identifies offline routing. Counters reset on restart. Decisions are written after the upstream response ends. The proxy also prints when classification starts and why it skips a request.

Codex 0.154 Responses Lite puts tool declarations in input items with type: "additional_tools", often wrapped in namespaces. The proxy reads those declarations as well as root-level tools, without moving or changing them. Responses Lite currently always uses shadow mode, even when JEV_MODE=enforce: forcing a tool in live Codex sessions coincided with repeated intermediate messages without a final answer. The proxy still asks Jev and logs its prediction, but forwards the original request byte for byte. The exact cause of that behavior remains under investigation. The decision log records mode: "shadow", applied: false, and shadowReason. Duplicate tool names across declarations also fall back to shadow mode.

Settings

Run jev-classifier setup to change preferences. Choices are saved globally:

PlatformConfiguration file
Windows%APPDATA%/jev-classifier/config.json
macOS / Linux$XDG_CONFIG_HOME/jev-classifier/config.json, or ~/.config/jev-classifier/config.json

Keys are saved as plain text in this file. On macOS/Linux, a new config file is readable and writable only by its owner. On Windows, it uses the user folder's permissions. Setup hides keys on the review screen, and settings --json hides their values too.

Logs live beside the config file by default. Set JEV_CONFIG_HOME to use a different folder.

Your saved settings work from any project. Before you save them for the first time, the CLI can load a local .env and import it during setup. After that, use --env-file .env if you want to load a project's settings.

When the same setting appears in several places, the first one in this list wins:

  1. Command flags.
  2. Existing environment variables.
  3. An explicitly selected environment file.
  4. Saved global preferences.

--global skips automatic .env loading. Restart the gateway or editor's MCP server after changing settings. The CLI trusts certificates installed on your computer when Node supports it; you can change that in advanced settings.

Reference

Manual authentication and connection settings

Login and token refresh stay in the agent. The proxy forwards each request's credentials; it does not read credential files, copy refresh tokens, or implement another browser login. Configure a separate Jev key in setup (TypeSafe, OpenRouter, or Vercel AI Gateway), unless using JEV_STUB=1.

AgentLocal base URLSession destination
Claude Codehttp://127.0.0.1:8080/oauth/claudehttps://api.anthropic.com
Codexhttp://127.0.0.1:8080/oauth/codexhttps://chatgpt.com/backend-api/codex
Official Grok Buildhttp://127.0.0.1:8080/oauth/grok/v1https://cli-chat-proxy.grok.com/v1

Claude Code, in PowerShell:

Remove-Item Env:ANTHROPIC_API_KEY -ErrorAction SilentlyContinue
Remove-Item Env:ANTHROPIC_AUTH_TOKEN -ErrorAction SilentlyContinue
$env:ANTHROPIC_BASE_URL = "http://127.0.0.1:8080/oauth/claude"
claude

Use /login if needed. Disable an existing apiKeyHelper when using subscription login. Claude's OAuth capability headers are preserved. Claude gateway authentication.

Codex: merge into the user-level $CODEX_HOME/config.toml (default ~/.codex/config.toml):

model_provider = "jev" # top level, before any [table]

[model_providers.jev]
name = "jev-classifier"
base_url = "http://127.0.0.1:8080/oauth/codex"
wire_api = "responses"
requires_openai_auth = true
supports_websockets = false

No env_key for this flow. Run codex login if needed and use codex login status to confirm ChatGPT login, then start codex. API-key login belongs on /api/codex/v1 instead. Codex authentication.

Official Grok Build, in PowerShell:

Remove-Item Env:XAI_API_KEY -ErrorAction SilentlyContinue
Remove-Item Env:GROK_MODELS_BASE_URL -ErrorAction SilentlyContinue
grok login # only if not already signed in
$env:GROK_CLI_CHAT_PROXY_BASE_URL = "http://127.0.0.1:8080/oauth/grok/v1"
grok

Remove per-model api_key / env_key overrides to use the session. This is the official Grok Build, not third-party CLIs also named grok. Endpoint variable confirmed in the installed Grok 1.0.34 embedded documentation. Grok authentication.

Session routes ignore UPSTREAM, API upstream variables, and UPSTREAM_API_KEY, so gateway settings cannot replace a session token or redirect it to another provider. Trusted deployments can override CLAUDE_OAUTH_UPSTREAM, CODEX_OAUTH_UPSTREAM, or GROK_OAUTH_UPSTREAM explicitly. Auxiliary routes (models, compact, token counting) use the same selected destination. A 401 is passed back to the agent; the proxy does not retry or refresh credentials. Capture files omit auth/account headers and query strings; request bodies can still contain private prompts and code.

Direct OAuth integrations are Claude Code, Codex, and official Grok Build. Their API-key routes are /api/claude, /api/codex, and /api/grok; append the API path such as /v1/responses. env_key is the name of an environment variable, never its secret value. OpenRouter and Vercel AI Gateway are classifier backends only; they are not agent destinations.

Validation uses local mock upstreams (routing, token changes, 401, headers, logging, JSON/SSE). Live subscription inference has not been verified. Codex uses HTTP/SSE here; WebSockets are unsupported.

Environment variables for automation

Use setup for everyday changes. For scripts and automation, you can also use environment variables. For example, save this in a file and load it with --env-file .env:

JEV_PROVIDER=openrouter
OPENROUTER_API_KEY=your-openrouter-key

For Vercel, use JEV_PROVIDER=vercel and AI_GATEWAY_API_KEY. No upstream override is needed. JEV_MODEL selects the classifier model, not the coding model.

VarDefaultUse
JEV_PROVIDERtypesafeJev backend: typesafe, openrouter, or vercel
TYPESAFE_API_KEY(unset)classifier key for TypeSafe
OPENROUTER_API_KEY(unset)classifier key for OpenRouter
AI_GATEWAY_API_KEY(unset)classifier key for Vercel AI Gateway
JEV_API_KEY(unset)optional override for the selected classifier key
JEV_MODELprovider-specificJev model ID override
JEV_STUB01 routes without Jev, no API key needed
JEV_MODEenforceenforce rewrites tool_choice; shadow only logs
JEV_PROXY10 launches agents directly, without the Jev gateway
DONE_THRESHOLD0.5minimum probability that the requested work is done
MIN_CONFIDENCE0.5below this, no tool is forced
PORT8080proxy port
ANTHROPIC_UPSTREAMhttps://api.anthropic.comupstream for /v1/messages
OPENAI_UPSTREAMhttps://api.openai.comupstream for /v1/responses
XAI_UPSTREAMhttps://api.x.aiupstream for /v1/chat/completions
UPSTREAMUnsetoverrides API-key route destinations; ignored on /oauth/*
UPSTREAM_API_KEYUnsetreplaces API-route credentials; ignored on /oauth/*
CLAUDE_OAUTH_UPSTREAMhttps://api.anthropic.comsession destination for /oauth/claude
CODEX_OAUTH_UPSTREAMhttps://chatgpt.com/backend-api/codexsession destination for /oauth/codex
GROK_OAUTH_UPSTREAMhttps://cli-chat-proxy.grok.comsession destination for /oauth/grok
JEV_LOGglobal config directory + /decisions.jsonldecision log
JEV_DEFAULT_AGENTcodexdefault for run
JEV_AUTHoauthauthentication for Codex, Claude Code, and Grok Build
JEV_SYSTEM_CA1trust system certificates when supported
JEV_CONFIG_HOMEOS-specificglobal settings directory
Decision records and request captures

Metrics and the dashboard read the same decision JSONL. Example record:

{
  "ts": "2026-09-17T12:00:00.000Z",
  "agent": "anthropic",
  "model": "example-model",
  "session": "example-session",
  "mode": "enforce",
  "chosen": "Edit",
  "confidence": 0.82,
  "done": 0.1,
  "gated": false,
  "truncated": false,
  "top3": [
    {
      "name": "Edit",
      "p": 0.82
    }
  ],
  "jevMs": 180,
  "upstreamMs": 2400,
  "toolsCount": 14,
  "applied": true,
  "actual": "Edit",
  "match": true
}

jev-classifier serve --capture saves requests in .jev-classifier/captures/. It removes authentication and account headers, plus query strings. Request bodies can still contain private prompts and code. Review captures before sharing them.

Troubleshooting
SymptomCheck
No proxy requests appearStart the agent through run; confirm the gateway port and selected provider
Jev is configured but classifications failRun doctor --check; inspect provider/model settings and logs
Changes do not affect the running sessionRestart the gateway or editor's MCP server
Cursor or Antigravity has no HTTP countersCheck MCP discovery with jev_status and activity in logs --decisions
Codex reports an observe-mode fallbackResponses Lite intentionally preserves requests; inspect shadowReason
A managed Windows computer reports certificate errorsCheck system trust; Node 22.19+ also supports node --use-system-ca dist/cli.js serve

Limits

  • Jev accepts up to 255 choices, including respond_to_user. Larger catalogs are shortened for classification and marked truncated. The full tool list still goes to your agent's model.
  • Each prediction takes time. Check jevMs in the log and the p95 timing in metrics to see how much delay Jev adds.

Development

npm install
npm run typecheck
npm test

CI is configured for Windows, macOS, and Linux with Node 22 and 24. Tests cover protocol forwarding, MCP stdio discovery and calls, configuration merging, process control, and startup-file generation. The new integrations were developed and tested on Windows; native macOS/Linux desktop launches and real Cursor/Antigravity sessions still require platform validation. OpenCode's configuration and mock upstream routing are tested separately from live provider inference.

License

MIT.