Repository navigation
feat(routing): make advisor narrowing visible, and optional - #482
Merged
Merged
Conversation
Claude Code declares the advisor tool on every request, invoked or not, so the advisor model's eligibility is paid by all of a client's traffic. When only some accounts could serve the advisor's model, strict filtering (#98) confined the whole fleet's traffic to them and said nothing: the one diagnostic fired at zero eligible accounts, not at one. Selection now counts, once per request in getActiveAccount, how many of the accounts that can serve the request can also serve its advisor, so the session-distribution walks are covered as well as the plain one. A strict subset is logged, throttled to once a minute like the degrade line, and reported by getStatus() as `advisorNarrowing` beside the mode. A new `advisorEligibility` config key chooses what happens then: "strict" (the default, unchanged) keeps the filter; "prefer" routes a narrowed request by its main model only, as though it declared no advisor, so every account that can serve the request is a candidate and whichever serves it runs the advisor call too. Unknown values are "strict", said once. Applied live on reload like the neighbouring routing knobs, and read the same way by pickAlternate so a failover hop does not re-pin what the first attempt spread. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Merged
MagicalTux
added a commit
that referenced
this pull request
Sep 29, 2026
Thirty-two commits since 1.1.21. Two change routing on an existing config without an opt-in (#480, #481); the rest is opt-in, additive, or display. Behaviour changes #481 an API-key account's 401 is a cooldown, not a permanent `error`: 1 min, then 5, 15 and 60 for every further rejection with no success in between; any 2xx/3xx resets it. The request still fails over and the client never sees the 401. OAuth accounts are unchanged #480 with session distribution on, requests carrying no session id stay within the top priority tier, so a fallback gateway no longer answers Claude Code's bootstrap and connector calls #470 a 200 whose SSE stream reports a provider failure before any output (`server_is_overloaded`, `response.failed`) fails over once, like a status-shaped failure would #465 a reload removes running accounts whose config entry is gone from disk, so `teamclaude remove` from another shell takes effect at once #460 `import` refuses an account whose token upstream has definitively rejected (401/403), even with `--name`; a 5xx or timeout still imports Rename #483 the project is being renamed to TeamRouter (#72). This release accepts the new name everywhere the old one is read and changes nothing an install has on disk: `teamrouter` runs the same CLI, every `TEAMCLAUDE_*` variable is also read as `TEAMROUTER_*` (which wins when both are set), every `/teamclaude/…` control route also answers at `/teamrouter/…`, and `~/.config/teamrouter.json` is used when it exists Features #441 per-account egress proxy (`accounts[].routing`: http, socks4/4a, socks5/5h) for refresh, probes and requests; `login --routing`, `teamclaude routing set/show/clear`, a connection check before it is relied on, and a short hold when the proxy is unreachable #427 `accounts[].allowExtraUsage: true` lets a paid extra-usage account serve once every account is past its threshold, instead of a 429 #466 `accounts[].maxSpend`, a money cap judged against the month-to-date extra-usage spend upstream reports; the TUI shows what an account has billed #436 `autoRedeemResets` spends a free Codex rate-limit reset credit when the Codex pool runs dry (off by default) #482 `advisorEligibility: "strict" | "prefer"`; when the advisor model narrows selection to a subset of the fleet the log says so, and status carries the reading (`advisorNarrowing`) #478 `stripOverageHeaders` drops another org's per-organization billing headers from responses, for a pool spanning several orgs (#476) #471 `quota.unified5hSeenAt` / `unified7dSeenAt` in status: when upstream last stated each shared window #446 client and dimension usage for the last 5h and 24h in status and the dashboard, resumed across restarts #458 #459 #461 the dashboard sets the switch threshold, enables/disables and reprioritizes an account, and has a light theme remembered per browser #464 `l` in the TUI signs an account in `error` in again from the dashboard #457 status records which Codex limit meters each model (`quota.codexModelLimits`) #442 `quotaBarPercent` drops the percentage beside a TUI bar's countdown #451 `stripRequestFields` takes `content.<block type>` to drop content blocks a strict Anthropic-compatible upstream rejects #469 `proxy.mcp` schemas declare their item types, the write audit line records what happened, and the write queue has a depth (#447–#450) Fixes #477 a refused WebSocket handshake whose headers all drop is relayed as a well-formed head instead of a blank line and body bytes #474 two members of one ChatGPT workspace are told apart by user id, so a second `login --codex` no longer replaces the first #469 a Codex Responses stream with no Content-Type is relayed as a stream and booked; thread repair on the global upstream; TUI settings and status gaps; a hint when a local login would have served #463 a Codex row with no session window draws one wide weekly bar Tests #484 #485 #486 the suite asserts behaviour, not the scheduler: wall-clock upper bounds are gone, and subprocess tests spawn the server through `test-helpers/spawn-server.js`, which verifies the server it reached by `server.pid` (new in status) instead of trusting a port Tooling #452 #453 #454 #455 docker workflow actions bumped
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #479.
Claude Code declares the advisor tool on every request, so the advisor model's eligibility is paid by all of a client's traffic. When only some accounts could serve it, selection was confined to them and nothing said so: the only diagnostic fired at zero eligible accounts.
Two changes, neither altering the default routing:
The narrowing is announced. Once per minute at most:
Advisor model "claude-fable-5-1" narrows selection to 1 of 4 accounts — set advisorEligibility to "prefer" to route by request model instead. The counts are the accounts able to serve the request now and how many of those can serve the advisor as well; they are computed once ingetActiveAccount, so the session-distribution and untagged-spread paths are covered too.GET /teamclaude/statuscarries the last reading asadvisorNarrowing: { model, eligible, of, at } | nullbesideadvisorEligibility.advisorEligibility: "strict" | "prefer"(top-level, defaultstrict, applied live on reload).preferroutes a narrowed request by its main model only, as though it declared no advisor: every account that can serve the request is a candidate under the ordinary rotation, and whichever serves it runs the advisor call. Where every candidate can serve the advisor nothing changes, and zero-eligible still degrades exactly as before. Failover hops (pickAlternate) read the mode the same way. Any other value isstrictwith a one-time warning.Docs in
docs/configuration.mdanddocs/routing.md;config.example.jsonlists the key.Tests:
test/advisor-eligibility.test.js(16 tests: mode parsing, strict still pins, the log fires once per window and not when the sets are equal, both distribution paths, status readout, prefer spreads sessions and untagged requests, route pins,pickAlternatein both modes, zero-eligible degrade, live reload through a real server subprocess).🤖 Generated with Claude Code