npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vibekit-switchback

v0.1.1

Published

Switchback — a per-turn cascade model router for AI agents. Classifies each request, starts on the cheapest model that can handle it, and escalates mid-turn if the cheap one fumbles. Brand-locked, framework-agnostic.

Readme

Switchback

Per-turn cascade model router for AI agents. Starts cheap, climbs the tiers when the cheap one fumbles.

npm Powers VibeKit

Don't want to wire it yourself? Switchback ships fully hosted inside VibeKit — every "Auto" turn runs through this cascade with brand-locked ladders, telemetry, and billing built in. Or hit the hosted classifier directly: POST https://vibekit.bot/api/v1/switchback/route.

Most AI agents pick one model per app and use it for every turn. A "fix this typo" turn and a "refactor the auth flow" turn both go to the same flagship — wasted spend on the trivial half, no escalation safety on the complex half.

Switchback classifies each user turn (trivial / standard / complex), starts the agent on the cheapest model in your ladder that can plausibly handle it, then escalates mid-turn — swapping to a more capable model on the next agent round — if it detects the cheap one is fumbling.

Born inside VibeKit. Extracted as a standalone library because the routing pattern is useful well beyond one product.

Install

npm install vibekit-switchback

Quick start

import {
  createCascade,
  createOpenRouterClassifier,
} from 'vibekit-switchback';

const cascade = createCascade({
  // Cheap → flagship. Brand-locked is your call: if your user has an
  // Anthropic key, hand Switchback an all-Anthropic ladder; if they're
  // on OpenAI, an all-OpenAI ladder. Switchback stays inside whatever
  // ladder you give it.
  ladder: [
    'openai/gpt-5.4-mini',
    'openai/gpt-5.4',
    'openai/gpt-5.5',
  ],
  classify: createOpenRouterClassifier({
    apiKey: process.env.OPENROUTER_API_KEY!,
  }),
});

const state = await cascade.init({
  userMessage: 'add a dark-mode toggle to the header',
  recentHistory: [], // optional: last 1-2 assistant turns for context
});

// state.initialModel === 'openai/gpt-5.4' (classifier picked tier 1)
// state.classifier.why === 'small UI feature'

// Run your agent's first round with state.initialModel:
const round0 = await runAgentRound({ model: state.initialModel, ... });
cascade.recordTokens(state, round0.totalTokens);

// After each round, check for escalation:
const decision = cascade.evaluate(state, {
  hasNoText: round0.text.trim() === '',
  endedOnToolUse: round0.endedOnToolUse,
  steps: round0.steps,           // optional — enables tool_error_spike trigger
  joinedText: round0.text,       // optional — enables refusal_signal trigger
}, /* round = */ 0);

if (decision.escalate) {
  const nextModel = cascade.escalate(state, decision.reason!, 0, round0.totalTokens);
  // ...use nextModel for the next round
}

// At end-of-turn, persist telemetry:
const telemetry = cascade.telemetry(state);
//  { initial_model, final_model, escalations, signals[], per_tier_tokens }

What makes Switchback different

  • Mid-turn escalation. Other routers (NotDiamond, Martian) pick a model at request start and that's that. Switchback can hand off mid-conversation if the cheap tier gives up — requires deep agent-loop integration but catches failures other routers can't.
  • Brand-locked by design. Switchback doesn't decide "use Claude or GPT" — the caller hands it one ladder per user, all within their BYOK provider. Your user's key stays your user's key, every tier.
  • Agent-loop signals. Five escalation triggers built around actual agent execution: empty-tool exits, tool-error spikes, continuation exhaustion, explicit retry-sentinels, refusal phrases. Not just chat-completion patterns.
  • Telemetry baked in. Every cascade.telemetry(state) returns a clean JSON record ready to write to your usage_logs / metrics sink. Five columns: initial model, final model, escalation count, signal log, per-tier tokens.

API

createCascade(config)

| field | type | description | | ---------- | ------------------------------------------------------------------- | ---------------------------------------- | | ladder | readonly string[] | Ordered list of model ids, cheap first. | | classify | (userMessage, recentHistory) => Promise<ClassifierResult> | Your classifier function. See below. |

Returns: { init, evaluate, escalate, recordTokens, telemetry }.

createOpenRouterClassifier(opts)

Drop-in classifier using OpenRouter. Fail-soft (returns tier 1 on any error so the cascade never blocks on classifier issues).

| option | type | default | description | | ---------- | --------- | ---------------------- | ------------------------------------ | | apiKey | string | (required) | OpenRouter API key. | | model | string | openai/gpt-5.4-mini | Model slug for classification. | | baseUrl | string | https://openrouter.ai/api/v1 | Override for OR-compatible proxies. | | timeoutMs| number | 8000 | Per-call timeout. |

Escalation triggers (in cascade.evaluate)

| trigger | fires when | | ------------------------ | ------------------------------------------------------------------------- | | empty_tool_exit | 2 consecutive rounds end on tool_use with no text output. | | tool_error_spike | ≥3 tool-error entries in this round's steps[]. | | continuation_exhaust | Round index ≥5 with still no text accumulated. | | sentinel_after_retry | Your agent loop hit its empty-response retry and the retry was also empty. | | refusal_signal | First ~200 chars of the assistant reply start with "I can't" etc. |

Each escalation bumps tier by 1 (capped at ladder top).

When NOT to use this

  • You only have one model. Save yourself the classifier latency. Switchback is for cascades.
  • You don't run an agent loop. The mid-turn escalation only helps if you have multiple rounds per user turn. For one-shot chat-completion routing, you want NotDiamond / Martian / OpenRouter's auto instead.
  • Latency-critical chat. The classifier adds ~200-500ms before the first token of round 0. If your UX needs sub-200ms first-token-latency, skip it.

License

MIT — see LICENSE.

Credits

Built by VibeKit. The approach is heavily inspired by Factory.ai's Factory Router — same thesis, different implementation. Switchback's contributions are the brand-locked ladders, mid-turn escalation, and agent-loop-signal triggers.