Opt-in probe of native model routing, usage and UI; not the production router.

The production router now lives in hooks/native-router.mjs. This directory contains isolated probes, reproducible CLI drivers and sanitized acceptance results. It is excluded from the shipped plugin.
See the architecture, configuration and user guide for the supported native setup. Main conversations are routed. Every subagent model choice passes through unchanged.
| Check | Recorded result |
|---|---|
| HTTP deadline, retry, slow body and burst admission | HTTP acceptance: 20 samples, p95 1504 ms; one pending request |
| Clear, circuit expiry, reload, replacement, unload and resume/fork | results/persistent-*-acceptance.json, driven by persistent-acceptance.mjs |
| Interrupt during the main controller's Jev wait | Interrupt acceptance: late advice discarded; socket closed in 4 ms |
| Native engine fallback | Fallback acceptance: subsequent tool step kept the fallback |
| Deferred MCP tools and large requested output setting on Haiku | Deferred tool/output gate: direct and rewritten requests succeeded |
| Request shape per routed model (loopback stub) | Compat acceptance: engine builds each model's request; names only |
| Billed pin chain Opus → Haiku → Sonnet with Read on every turn | Live compat: five turns, each with a tool call, none rejected |
| Installed Team package and authenticated Jev | Team canary: one router, version 1.0.0, native Sonnet response |
| Historical decision replay | Trace evaluation: coverage and uncertainty bounds, not measured savings |
Earlier direct probes accepted signed-thinking/tool history across Sonnet → Opus → Haiku and input contexts of 589399 tokens on Sonnet and 589481 on Opus. Their summary is in runtime gates. These observations apply to Claude Code 2.1.289 and the tested Team policy.
From the repository root, with Team authentication:
node experiments/mod-router/scripts/launch-team-native.mjs
node experiments/mod-router/scripts/interrupt-acceptance.mjs
node experiments/mod-router/scripts/persistent-acceptance.mjs resume
node experiments/mod-router/scripts/persistent-acceptance.mjs unload
node experiments/mod-router/scripts/team-canary.mjs
node experiments/mod-router/scripts/fallback-acceptance.mjs
node experiments/mod-router/scripts/compat-acceptance.mjs
node experiments/mod-router/scripts/live-compat-acceptance.mjs
The candidate launcher builds a temporary plugin and disables the installed gateway for that launch. Persistent drivers use ce peer-team and synthetic loopback Jev credentials. The canary uses the installed Team package and real provider requests. Circuit expiry takes at least 60 seconds. Drivers remove temporary plugins and close their child processes.
The marker prototype in hooks/register.mjs is historical research. Likewise lib/legacy-*.mjs preserves v0.8 arithmetic and request rewriting solely for comparison. No gateway, subprocess classifier or Node API is imported by the production Mod.
Mods are early access. Native usage exposes no cache TTL split. Unknown cache lifetime stays unknown. No one-hour TTL or achieved savings is inferred.
Primary references: Mods announcement, examples, events, UI and state.
hooks/register.mjs 61 lines1import { atom, read, update } from 'claude-code';
2import { registerProbe } from './runtime-probe.mjs';
3
4const MODEL_IDS = { haiku: 'claude-haiku-4-5', sonnet: 'claude-sonnet-5-5', opus: 'claude-opus-5-5' };
5
6const route = atom(
7 { plugin: 'router-mod-probe', key: 'route' },
8 {
9 turnId: '',
10 model: null,
11 requested: null,
12 actual: null,
13 },
14);
15
16export function register(on, options) {
17 on('tool.describe', { tool: 'mcp__router_probe__verification_marker' }, async ($, e, next) => {
18 const result = await next(e);
19 $.ui.log('DEFERRED_PROBE isDeferred=true', { to: 'debug' });
20 return { ...result, isDeferred: true };
21 });
22 registerProbe(on, options);
23 on('turn.start', async ($, e, next) => {
24 const model = e.text.match(/^\[router-mod:(haiku|sonnet|opus)\]/)?.[1] ?? null;
25 await update($, route, () => ({ turnId: e.turnId, model, requested: null, actual: null }));
26 return next(e);
27 });
28
29 on('turn.step', async function* ($, e, next) {
30 const chosen = await read($, route);
31 // Keep the experiment out of subagents and unrelated turns.
32 if (e.agentId || chosen.turnId !== e.turnId || !chosen.model) return yield* next(e);
33
34 const request = { ...e, model: MODEL_IDS[chosen.model] };
35 if (chosen.model !== 'haiku') request.effort = 'low';
36 else delete request.effort;
37 await update($, route, (current) => ({ ...current, requested: request.model }));
38 $.ui.log(`router-mod-probe request: ${e.model} -> ${request.model}, step ${e.index}`, { to: 'debug' });
39 const result = yield* next(request);
40 if (result.usage) {
41 await update($, route, (current) => ({ ...current, actual: result.usage.model }));
42 $.ui.log(`router-mod-probe response: ${result.usage.model}`, { to: 'debug' });
43 }
44 return result;
45 });
46
47 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
48 const chosen = await read($, route);
49 const { Box, Text } = $.ui.resolve(e);
50 return Box({
51 children: [
52 await next(e),
53 Text({
54 color: 'cyan',
55 children: `Router probe: ${chosen.actual ?? chosen.requested ?? chosen.model ?? 'passthrough'}`,
56 }),
57 ],
58 });
59 });
60}
61hooks/runtime-probe.mjs 86 lines1// Probe reloads are intentional: outstanding host work must be cancelled.
2function report($, value) {
3 $.ui.log(`RUNTIME_PROBE ${JSON.stringify(value)}`, { to: 'debug' });
4}
5
6export function registerProbe(on, options) {
7 on('session.start', async ($, e, next) => {
8 await $.command.register({ name: 'router-probe', description: 'Run isolated router runtime checks.' });
9 report($, { event: 'start', root: $.plugin.root, session: await $.session.id() });
10 return next(e);
11 });
12
13 on('session.end', ($, e, next) => {
14 report($, { event: 'end', reason: e.reason, session: e.sessionId });
15 return next(e);
16 });
17
18 on('session.compact', async ($, e, next) => {
19 const result = await next(e);
20 report($, { event: 'compact', trigger: e.trigger, skipped: Boolean(result.skip), agent: e.agentId ?? null });
21 return result;
22 });
23
24 on('command.run', { command: 'model' }, async ($, e, next) => {
25 const result = await next(e);
26 await $.env.set('JEV_ROUTER_MODE', 'manual');
27 report($, { event: 'model-command', model: await $.session.model() });
28 return result;
29 });
30
31 on('command.run', { command: 'router-probe' }, async ($, e) => {
32 const action = e.args.trim();
33 if (action === 'manual') await $.env.set('JEV_ROUTER_MODE', 'manual');
34 if (action === 'auto') await $.env.set('JEV_ROUTER_MODE', 'auto');
35 const key =
36 typeof options.typesafe_api_key === 'string'
37 ? options.typesafe_api_key
38 : (await $.env.get('TYPESAFE_API_KEY')) || (await $.env.get('CLAUDE_PLUGIN_OPTION_TYPESAFE_API_KEY'));
39 if (action === 'inspect' || action === 'manual' || action === 'auto') {
40 const value = {
41 event: 'inspect',
42 session: await $.session.id(),
43 model: await $.session.model(),
44 mode: (await $.env.get('JEV_ROUTER_MODE')) ?? null,
45 baseURL: (await $.env.get('ANTHROPIC_BASE_URL')) ?? null,
46 optionKeyPresent: Boolean(options.typesafe_api_key),
47 keyPresent: Boolean(key),
48 root: $.plugin.root,
49 };
50 report($, value);
51 return { text: JSON.stringify(value) };
52 }
53 const started = await $.clock.now();
54 if (action === 'process') {
55 try {
56 await $.process.run(['node', '-e', 'setTimeout(() => {}, 60000)'], { timeoutMs: 1500 });
57 return { text: 'unexpected process completion', exitCode: 1 };
58 } catch {
59 const value = { event: 'process-timeout', elapsedMs: (await $.clock.now()) - started };
60 report($, value);
61 return { text: JSON.stringify(value) };
62 }
63 }
64 const base = await $.env.get('ROUTER_PROBE_URL');
65 if (!base || !['hang', 'delay', 'slow', 'retry'].includes(action))
66 return { text: 'Use inspect, manual, auto, process, hang, delay, slow or retry.' };
67 const request = $.http.fetch(`${base}/${action}`, { headers: { authorization: 'Bearer synthetic-probe-key' } });
68 request.then(
69 (r) => report($, { event: 'http-settled', status: r.status, elapsedMs: Date.now() - started }),
70 () => report($, { event: 'http-settled', rejected: true, elapsedMs: Date.now() - started }),
71 );
72 const timer = new AbortController();
73 const deadline = $.clock.sleep(1500, { signal: timer.signal }).then(() => null);
74 const response = await Promise.race([request, deadline]);
75 timer.abort();
76 const value = {
77 event: 'http-return',
78 timeout: response === null,
79 status: response?.status ?? null,
80 elapsedMs: (await $.clock.now()) - started,
81 };
82 report($, value);
83 return { text: JSON.stringify(value) };
84 });
85}
86types/index.d.ts 8 lines1declare module 'claude-code' {
2 interface PluginState {
3 'router-mod-probe': {
4 route: { turnId: string; model: string | null; requested: string | null; actual: string | null };
5 };
6 }
7}
8