Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

agentr — AgentRouter proxy for coding agents

An OpenAI/Anthropic-compatible proxy that sits between your coding agent (Claude Code, OpenCode, Cursor, plain SDKs) and agentrouter.org. It repairs the quirks that make agents fail against that upstream: null-valued fields, broken SSE frames, and false-positive content blocks.

Architecture

Rendering mermaid diagram...

Files

FileRole
serve.tsLocal Deno server (run this for real traffic)
main.tsVal Town HTTP entrypoint — same logic, hosted
lib/proxy.tsCore handler: headers, retry, stats, response finalize
lib/clients.tsAgent detection (Claude Code / OpenCode / Cursor / SDK / curl)
lib/body.tsNull stripping, preamble injection, context trimming
lib/sse.tsSSE stream repair

Features

  1. Agent + dialect detection — inspects User-Agent, anthropic-version, x-api-key, x-app and the request path to classify each request as claude-code, opencode, cursor, anthropic-sdk, openai-sdk, curl, or browser, and whether it speaks the OpenAI or Anthropic dialect. The detected identity is forwarded as X-Client-Kind / X-Client-Version and counted in /_stats.
  2. Dialect-aware preamble injection — the educational preamble goes into messages[].role=system for OpenAI-style requests and into the top-level system field for Anthropic-style ones (string or text-block form).
  3. Null stripping — recursively removes null fields (response_format: null etc.) that AgentRouter rejects with 400 expected object, received null.
  4. SSE repair — drops data: null and billing.summary frames that crash strict AI-SDK parsers, while preserving event framing.
  5. Content-block auto-retry — on a block-looking 400/403/451, retries once with context trimmed to system + last user message, and logs the offending message.
  6. Stats endpointGET /_stats reports requests, blocks, retry outcomes, filtered SSE lines, and per-client / per-path counters.
  7. UA modesopencode (default, the known-accepted UA), auto (per-client UA), or passthrough (forward the client's own).

agentrouter.org is behind an Alibaba Cloud WAF that challenges datacenter IPs with a JS captcha, so hosted requests come back as an HTML captcha page. From your own machine it works normally.

# no download needed — Deno runs it straight from Val Town deno run --allow-net --allow-env https://esm.town/v/allanhorca07/agentr/serve.ts # or clone and run vt clone allanhorca07/agentr && cd agentr deno run --allow-net --allow-env serve.ts

Listens on http://localhost:4182 (pass a port as the first arg to change it).

Environment variables

VarDefaultPurpose
AGENTROUTER_API_KEYFallback key when the client sends none
AGENTROUTER_UPSTREAMhttps://agentrouter.orgUpstream base URL
AGENTROUTER_DEBUGoff1 = log each request, drops, retries
AGENTROUTER_UA_MODEopencodeopencode | auto | passthrough

Test it

curl http://localhost:4182/_stats curl http://localhost:4182/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $AGENTROUTER_API_KEY" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Say hello"}],"max_tokens":50}'

Point your agent at it

# OpenCode / OpenAI-compatible clients export OPENAI_BASE_URL=http://localhost:4182/v1 export OPENAI_API_KEY=sk-... # Claude Code export ANTHROPIC_BASE_URL=http://localhost:4182 export ANTHROPIC_API_KEY=sk-...

Hosted endpoint

https://agentr.val.run runs the same code. /_stats works; proxied calls to the upstream return the WAF captcha page because of the datacenter-IP block. Use it to verify proxy behavior, not for real traffic.