Repository navigation
2026.10.5: model aliases and specified models in routing rules (core v0.62.0) - #284
Merged
Merged
Conversation
The key dialog's visible-models table now lists aliases from GET /models with an "alias" mark. An alias whose listed model is in scope (picked or matched by a pattern) is shown checked and locked with "allowed with <model>"; picking or matching only the alias leaves its models hidden. Pattern hit counts stay name-based (aliases included), the visible count includes inherited aliases, and a note under the table states the rule. Inheritance is computed, never written: allow keeps the user's entries. ModelInput accepts KnownModel-shaped options besides plain names; each suggestion then carries a right-aligned muted note (providers, or "alias · providers"), and the input marks a value that names an alias. Types come from src/aliases/api.provisional.ts until core's bindings land. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s model The traffic table's model column keeps the client's name. The upstream cell now marks "Alias" when an alias made the serving attempt send another name, and "Pinned" when the deciding rule pins upstream + model. The request drawer's Routing tab adds an "Alias" row (resolved per upstream) or a "Pinned model" row naming the rule, and the attempt chain lists each hop's sent model when anything was renamed, with a tooltip worded by cause: alias, rule rewrite (existing wording), pinned model, plugin, or a plain statement when the cause no longer matches the current config. The record has no "why": the cause is worked out from the client model, each attempt's sent model, the route / rule / rewritten_by, and the current alias table (GET /models alias items) and routes (overview). When those disagree, nothing is claimed. Rows now carry route, rule, rewritten_by and the serving hop's sent model from events and history. src/aliases/api.provisional.ts holds the contract's provisional types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The rule dialog's "Forward to" is now a choice between "Upstream or group" (today's select) and "Specified models": an ordered list of upstream + model rows (the model picked from that upstream's own list, with its context window), sent in order and as written, not through the alias table. With specified models, "Change model to" is hidden and not written to the config. - RuleDraft carries the target kind and the pinned list; draftFromView / draftToInput round-trip both shapes of `to`, and validation asks for at least one row with an upstream and a model. - The model condition's hint says that an alias matches only requests that use it, and that an upstream model name also matches the aliases that point to it (keeping the "matches N known models" count). - The route dialog's rule table shows pinned targets as "upstream · model" with backups on the second line, marks an alias condition, and names the aliases a model name also matches. The route list and the route map handle pinned targets. - The dry run shows the model each candidate is sent and where it came from (alias, rule rewrite, specified model). - Provisional types until core's model aliases land: the contract's src/aliases/api.provisional.ts, plus src/routing/provisional.ts for the widened RuleView/RuleInput/DryRunResult shapes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… aliases and providers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nts, and aliases in the upstream models popover
- src/aliases/AliasDialog.tsx: name (red = problems from core that block saving,
yellow = warnings), upstream models as a list input with the upstreams offering
each name and unserved names flagged, the same model on other upstreams as
checkboxes, and a read-only preview of the name each upstream receives
(including upstreams that no longer receive the name). Previews through
POST /aliases/preview as the draft changes; a name equal to a real model is
listed first when creating; renames report which keys and rules changed.
- src-tauri/src/aliases.rs: `alias_hints` command. Hint codes with params (no
sentences) for clients detected on this machine: Claude Code's own tier names,
a name that looks like one vendor family listing another family's models, and
whether a taken-over Claude Desktop shows the name; plus the taken-over clients
whose model lists will be offered an update. tw-adopt's `cloud::is_alias` is now pub.
- Upstream models popover: an "alias x" mark on rows listed in an alias, and an
"Add alias…" button on hover that opens the dialog with that model listed.
- The /aliases endpoints are called through `invoke("call")` with the provisional
types until core ships them and the allowlists can name them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts key
When the gateway lists no model Claude Desktop accepts, the takeover
dialog now asks which upstream model Claude Desktop should use instead of
telling the user to hand-write a rewrite rule. Connecting writes
claude-sonnet-5 into its configuration as before and adds a pinned-model
rule on Claude Desktop's own key, first in the route that key uses:
{ name: Claude Desktop, when: { client: <its key> },
to: [{ provider, model }, ...] } # every upstream offering the model
Other clients are unaffected and no global alias is created. The rule is
written after the plan is checked and before the files, and is put back
if the files cannot be written. Restoring (one client, restore all,
uninstall) deletes it afterwards; when it cannot be deleted, the restore
still succeeds and says so. Connecting again changes the rule in place,
and deletes it once the gateway offers Claude models. A rule is ours only
while it keeps its name, its single key condition and a pinned target with
no deny or rewrite; rules the user changed are left alone, and a
same-named rule of the user's in that route stops the takeover.
- tw-adopt: Plan.pick_model (desktop::model_pick) replaces the
adopt.plan.claude_desktop.no_claude_model note
- clients::desktop_rule: the rule's view, change, write, revert and
removal, against the model-alias contract's JSON (to: string or
[{provider, model}]) with provisional types until tw-api has them;
tested against a fake control plane
- PlanView.desktop_rule; adopt_client takes the chosen model; new codes
adopt.plan.gateway_changed, adopt.plan.claude_desktop.rule_name_taken,
adopt.claude_desktop.rule_left / rule_unchecked / rule_not_reverted
- PlanDialog: the model choice and a "Gateway configuration" summary
- src/aliases/api.provisional.ts from the model-alias contract
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new tab between Upstreams and Proxies lists the aliases from
GET /aliases: each alias with the name every upstream receives for it,
its context window and 24-hour requests and cost, plus amber lines when
no upstream serves it or when an upstream's same-named model is shadowed.
- Suggestions for one model under different names show as a banner
(one: "Create alias…"; several: a count and a dialog), and are listed
directly in the empty state. "Dismiss" is remembered per suggestion with
the guidance hints, so Settings' "show hidden hints" brings them back.
- The tab label carries the alias count and a dot while undismissed
suggestions exist and the tab is not active.
- Row menu (also on right-click): Edit…, Copy name, View traffic, Delete….
- The delete dialog lists where the name is used (GET /aliases/{name}/usage
plus connected clients whose config lists it) with links to each place.
- DetectedClient now carries the model list written in the client's
config, so the delete dialog can name the clients that list the alias.
The create/edit dialog is mounted at integration (marked in
UpstreamsPage). The six alias endpoints are called through the `call`
command with provisional types until they are in the generated contract
and the webview allow-lists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Points the six core dependencies at ThinkWatch-Core 4a1775d on feat/model-aliases until v0.62.0 is tagged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s endpoints Regenerates src/generated against protocol 38 and replaces every provisional type with the generated one: the alias views, RuleTarget and PinnedModel, KnownModel.alias/aliases, ModelRow.aliases and the dry-run candidate_models. The six /aliases endpoints join WEBVIEW_ENDPOINTS and ALLOWED, so src/aliases/api.ts calls them through the typed call() and absorbs the dialog's separate API module. The dry run now reads candidate_models directly; core sends sent_model for every candidate, so the attempt list only names it when model_via says it differs. The screenshot fixtures are regenerated against the same core and the screenshot mocks answer the alias endpoints. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chinese for the eight config.alias_* and six engine.pinned_* checks, the four gw.model admission errors, control.replay_alias_unserved and gw.plugin.alias_unserved, and 'alias' in the kind vocabulary used by config.edit.* and control.name_*. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
desktop_rule.rs drops its own copies of the route, rule, key and model types and calls ep::Overview, ep::KnownModels and ep::UpdateRoute directly; rules read as RuleView are written back as RuleInput. The pinned models shown in the confirmation are tw_api::PinnedModel, so lite-api.ts keeps importing that type from tw-api.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/aliases/AliasMark.tsx replaces the five marks the lanes drew on their own (key scope, model input, upstream models popover, rule conditions, traffic upstream cell): a small secondary badge in the label size. The traffic cell's Pinned mark shares it, since both explain why the model sent differs from the one the client asked for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…code makes The 'name looks like another family' hint listed clients from memory (Claude Code, Codex, Qwen Code, opencode deciding parameters by model name), which nothing in tw-adopt backs. It now names the adopted clients whose config the adoption writes with a request format picked by model name, Pi and oh-my-pi (pi::api_for) and Grok Build (grok::backend_for), and only when the alias name and the upstream model get different formats. The sentence says what follows: the request goes out in that family's format and the gateway converts it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uncated Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es, moved plugin path Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Model aliases, specified models in routing rules and the Claude Desktop takeover without a Claude upstream, on core v0.62.0 (control-plane protocol 38), released as 2026.10.5. Answers ThinkWatchProject/ThinkWatch-Core#283.
Model aliases
POST /alias-preview: the name each upstream receives, upstream models of the same name it takes over, and connected clients that treat the name specially (Claude Desktop's model-name rule; Pi, oh-my-pi and Grok Build, whose API format follows the name).PUT /aliases/{name}, which updates the keys' scopes and the rules that name it; deleting shows what still refers to it (GET /aliases/{name}/usage).Routing
Claude Desktop
Core v0.62.0
/aliases,/alias-preview, the moved/provider-test,/provider-preview,/proxy-test,/plugin-*paths) and the Chinese translations of the new message codes.2026.10.5
release-notes/2026.10.5.md.twcore checkon 0.62.0; request history is kept (schema unchanged).Checked locally: typecheck, 834 unit tests, build,
cargo fmt, clippy and the Rust tests.🤖 Generated with Claude Code