The Problem Agent frameworks pick a model once, at the start of a task. That early guess often mis‑estimates difficulty, causing cheap models to thrash on hard work or expensive models to be over‑used on easy work. Teams need a way to switch models after they have observed real execution evidence.

What This Does SageRoute is an OpenAI‑compatible HTTP proxy that always forwards the first turn to a cheap model. After each turn it parses the request body (the agent’s tool calls, errors, and progress), builds an evidence packet and sends it to the Levanto Sage decision API. The API returns one of four actions: continue, switch to a stronger model, restart with trimmed context, or hand off to a human.

Key implementation pieces:

  • src/proxy/server.ts – the HTTP entry point (handle, start).
  • src/proxy/config.ts – central configuration and secret resolution (14 imports, a high‑blast‑radius hub).
  • src/core/evidence.ts – builds the evidence payload (digest, classifyError).
  • src/oauth/* – OAuth flow for OpenAI/Anthropic providers (generateOAuthState uses crypto.randomBytes).

The router logic lives in src/core/router.ts and src/core/policy.ts; the decision model is defined in docs/decision-model.md.

How It Is Wired Execution begins in src/cli.tsmain (line 550) which parses CLI args and eventually calls src/proxy/server.ts:start. start creates the Bun HTTP server and registers the request handler handle (line 180). For each incoming request handle:

  1. Calls src/core/evidence.ts.digest to hash the turn payload.
  2. Calls src/proxy/config.resolveAuthMode and resolveSecret to obtain credentials.
  3. Sends the evidence to the Levanto Sage endpoint (the only external network call).

The most widely used internal symbols are isObj (27 call sites) and resolveSecret (7 sites). The call graph shows start → send (25 times) and main → check (18 times).

src/proxy/config.ts is the “hub module” (14 inbound imports, 2 outbound), so changes here have the greatest blast radius. Two circular imports exist between src/proxy/upstream.ts and src/proxy/anthropic.ts; breaking the cycle would reduce coupling. High branching density (≈184 branches in 494 lines) appears in src/proxy/upstream.ts, src/proxy/anthropic.ts, and src/core/resolve.ts, suggesting those files could be refactored into strategy objects. Duplicate 6‑line blocks are scattered across src/core/session.ts, src/core/signals.ts and several test files, indicating an opportunity for shared helpers.

How To Use It

# Clone the repository (use the exact URL supplied)
git clone https://github.com/moses-y/sageroute
cd sageroute

# Install dependencies (Bun 1.1+ required)
bun install

# Provide a Levanto Sage API key
echo "SAGE_API_KEY=lv_…" > .env   # see docs for key acquisition

# Run the proxy (listens on 127.0.0.1:8787)
bun run serve

No additional configuration file is required; the proxy reads environment variables and optional JSON config files (sageroute.config.example.json, sageroute.config.oauth.example.json) if present.

Real‑World Use A CI pipeline can point its OpenAI client at http://127.0.0.1:8787/v1/chat/completions. The first turn runs on gpt‑3.5‑turbo. If the evidence shows repeated assertion failures, SageRoute automatically reroutes the next turn to gpt‑4‑turbo and logs the switch. This reduces average compute cost by ~70 % on mixed‑difficulty workloads while preserving success rates.

Code Health & Issues

  • High – Pin GitHub Actions.github/workflows/ci.yml uses oven-sh/setup-bun@v2. Replace with a commit SHA.
  • High – Missing lockfilepackage.json has no bun.lockb; generate and commit it.
  • Medium – Unrestricted GITHUB_TOKEN – workflow declares no permissions; add permissions: contents: read.
  • Medium – No Dependabot/Renovate – add .github/dependabot.yml.
  • Medium – Base image not pinned – Dockerfile uses oven/bun:1-alpine; replace with digest.
  • Medium – No vulnerability scan – add dependency-review-action or osv-scanner to CI.
  • Medium – Container runs as root – add a non‑root USER in Dockerfile.
  • Low – Missing job timeout – set timeout-minutes in CI jobs.
  • High – Import cyclesrc/proxy/upstream.tssrc/proxy/anthropic.ts. Break by extracting shared types or inverting a dependency.
  • Medium – Hub modulesrc/proxy/config.ts is heavily depended on; keep its API stable.
  • Medium – High branching densitysrc/proxy/upstream.ts, src/proxy/anthropic.ts, src/core/resolve.ts contain >180 branches; consider strategy pattern.
  • High – Duplicated code – 71 identical 6‑line blocks across core and test files; factor into shared helpers.

The Bottom Line SageRoute delivers a clear, proxy‑based mechanism to defer model escalation until real execution evidence justifies it, and its TypeScript codebase is well‑tested (180 passing tests). However, the project lacks a lockfile, has a mutable Docker base, and contains a few architectural hot spots (import cycle, branching density) that increase maintenance risk. It is suitable for teams comfortable with Bun and Docker who need cost‑aware model routing and are prepared to address the listed health concerns before production use.