yetone
73b650b281
routing: a request a group's rule sent to a member that can't take it fails over to the members the rules after it that match too name, in their order, before the group's others ( #112 ) — two rules for images, the first's model out of quota, now go on to the second's vision model rather than the group's text-only first; and while the first's rests, the second's goes first. The trace names who comes next, and says a turn stays with whoever took it, not the model the rule named
2026-09-27 20:28:38 +08:00
yetone
90fd780eef
grok: served straight through the Responses API the grok CLI talks to, not the CLI run behind the gateway — each request goes to cli-chat-proxy.grok.com with the CLI's sign-in and version, as the CLI's own do: the conversation as it is, the caller's tools, images and all, relayed to a Responses client and translated for the rest. The models are the backend's own list, with each one's context window and efforts, and Grok searches the web by itself. A further account signs with its own home. Tested live from a sandbox: Claude Code and OpenCode on grok-4.7 list, read and write files through several tool rounds; an image and a web search answered
2026-09-27 19:41:12 +08:00
yetone
4e83dc6be8
web: magpie web knows its version and updates itself as the app does — the badge's restart puts the new version in and runs it in the old one's place with the same arguments and key, so the open page reloads onto it ( #111 ); routing: a gateway served by magpie serve, which has no window, is told apart from another magpie's window, and the routing view says to stop it so this magpie serves the gateway and shows routing live ( #110 )
2026-09-27 19:39:45 +08:00
Zhizhen He and Claude Code
41de0d87eb
gateway: count_tokens counts the masked prompt, and a count that cannot be had is told apart — the count Claude Code asks for before every turn went to the vendor as the conversation was written, so Mask secrets was defeated on every one; it now goes masked, the same prompt the turn it counts sends. An endpoint that doesn't implement counting (404/405/501, or a 400 that says so, in English or Chinese) falls back to the local estimate as before; a limited key or a broken connection yields to the next checked key and rests the one that failed, as a generation would, while a client's own cancellation rests nothing; and a real error — a wrong key, a prompt past the model's context, an image it won't take — reaches the agent. The gateway tests are isolated from the machine now (a temp HOME, inert stubs for the sign-in CLIs and the keychain) so a cache that outlives a test can't reach a real login, and the redaction tests' cold starts run in parallel. ( #103 )
...
Co-authored-by: Claude Code <noreply@anthropic.com >
2026-09-27 18:58:23 +08:00
yetone
ab7dd01506
codex: /fast works on a routing group too — a group with a ChatGPT account's GPT model in it is listed to Codex with the Fast tier, and the service_tier Codex then sends is kept through the group's routing and passed on to OpenAI's own backends (a ChatGPT account, api.openai.com) only; before, the request was rebuilt without it and Fast did nothing
2026-09-27 17:51:38 +08:00
yetone
b5340c7033
grok, cursor: a run ends with its caller — one whose client left mid-reply (Esc, /clear, Claude Code quit) is stopped at once instead of answering no one, one waiting on tool results that never come is let go after 5 minutes, not 30, and a stop kills what the CLI started too (its own session on Mac and Linux, a job on Windows). A tool that runs longer still gets its answer, from a run told the whole conversation. Runs are stopped with the gateway
2026-09-27 17:42:01 +08:00
yetone
c96cfa0ba0
codex: warm-up — a ChatGPT account's next window can be started as soon as the last one resets, by a one-word request to the mini model at low effort, so the 5-hour (or weekly) clock is already running when work begins; Settings → Codex 预热: off, weekly, or weekly and 5-hour. Checked every 5 minutes, only a window seen idle (nothing used, its reset a full span away) is warmed, three tries per reset
2026-09-27 17:36:20 +08:00
yetone
154d071699
routing: a group's effort can be picked per turn by any classifier, not only TypeSafe's Jev — a model that isn't a decision API (Jev Router on OpenRouter, Jev on OpenCode Zen, or any small fast model) is asked in words which of low, medium, high or xhigh the turn needs, in a call of its own beside the intent's, and told what the turn before needed; the routing editor offers every model for it
2026-09-27 16:11:23 +08:00
yetone
ce8d0e065c
devin: served straight through Devin's API, not the CLI over ACP — each request goes to the GetChatMessage RPC the devin CLI itself uses, with its sign-in: the conversation as it is, the caller's tools, images and all, and the reply streamed back text, thinking and tool calls as they come. A family's name is sent as its variant at the effort asked for; Adaptive and Fusion, which the API doesn't serve, are no longer listed. The instructions go at the head of the first message, as Kiro's do, since Devin turns away Claude Code's own in its system field. Tested live from a sandbox: Claude Code and OpenCode on Opus 5.5, SWE-2 and GPT-6 Sol list, read and write files through several tool rounds; images and a tool round trip answered on Claude, SWE, GPT and Gemini models
2026-09-27 15:30:24 +08:00
yetone
1d4930dd8e
agents: Alma is known as Alma, not ai-sdk — its requests go out with the AI SDK's User-Agent and nothing of Alma's, so magpie's provider in Alma now carries the key magpie-alma and the gateway knows an agent by a magpie-<agent> key before its User-Agent (usage, the routing view, the models it is shown). An Alma wired before gets the key on the next sync, the key alone sent, as a baseURL would have Alma drop its models
2026-09-27 15:28:33 +08:00
herrkaefer
c46840a0b0
gateway: validate requests before routing Codex models ( #99 )
2026-09-27 14:46:22 +08:00
yetone
6cd76567aa
devin: a turn Devin ends because one of its own tools was refused goes on — it sometimes lists the magpie tools, finds the list long and reaches for its own grep or read to look through it, which are off, and Devin then ends the turn (agent_stopped, tool_rejected) as if it were done, so Claude Code showed half an answer and no error. magpie now tells it to carry on with the magpie tools, up to three times, and the prompt says they are already there to call. Reproduced in interactive Claude Code on devin/swe-2 and checked there: the refused read is followed by the rest of the task, files written and tests run
2026-09-27 14:42:54 +08:00
yetone
17f289dd4a
gateway: shared on the local network from Settings — Share on local network puts the gateway on every interface at its port, at once and without a restart, so agents on other computers use magpie's models; a request from another machine must carry the API key magpie makes for it (shown with the addresses, copied, renewed in place), and goes on as this computer's own, which need none as before. Off puts it back on loopback. Tested live from a Windows machine on the LAN: no key or a wrong one is turned away (401), the key lists the models and a chat completion goes through to the vendor with the provider's own key. Saving the Settings page no longer drops what other pages keep in settings.json (models' names and levels, who sees which, the network sharing)
2026-09-27 13:48:57 +08:00
yetone
7c4a5d114c
providers: an OpenCode model is sent on the API models.dev says it's served on — Grok and GPT on /responses, Claude and MiniMax on /messages — so Claude Code asking OpenCode Go for Grok goes straight to /responses; OpenCode's "Model does not support this protocol" is also taken as the model being served elsewhere
2026-09-27 12:51:43 +08:00
yetone
6cd4344a58
gateway: OpenCode's "Model … is not supported for format anthropic" (and oa-compat) is taken as the model being served on another endpoint, so Claude Code asking OpenCode Go for a Grok model — served on /responses only — goes on to it rather than stopping at the first refusal, and later requests go straight there
2026-09-27 12:34:36 +08:00
yetone
cae88f031c
agents: Qoder — magpie's models in Qoder's CLI (1.1) as a "magpie" custom provider in ~/.qoder/settings.json ($QODER_CONFIG_DIR): picking magpie/<model> or a routing group writes the provider, with every magpie model, its context, output, images and the efforts it takes, and makes it model.name; the effort is model.reasoningEffort. The comments and entries of the user's settings stay, and stepping out of magpie takes its provider away and puts back the model the user had. Qoder offers custom providers only to a signed-in account whose plan has BYOK. It asks every model for enable_thinking, which OpenAI's API refuses, so that is dropped for openai.com. Tested live: magpie → settings.json → qodercli -p → magpie's gateway → upstream, the reply streamed back
2026-09-27 06:28:14 +08:00
e136cf9a27
gateway: Gemini PDF and audio files kept on text-only routes ( #95 )
...
* gateway: keep Gemini document and audio file data on text-only routes
* fix(gateway): avoid translating Gemini files as images
* fix(gateway): preserve Gemini files for Code Assist while translating safely
* fix(gateway): retain Gemini file placeholder for Claude subscriptions
---------
Co-authored-by: Instinct Agent <agent@instinct.com >
Co-authored-by: Charan Rathore <charan-rathore@users.noreply.github.com >
2026-09-27 04:24:03 +08:00
yetone
cf60231465
gateway: a web search magpie runs for Claude Code is told as Anthropic's own — a server_tool_use block with its query and a web_search_tool_result block with the pages found — so Claude Code's WebSearch counts it and lists its links, where it said "Did 0 searches" with the answer alone. This holds for magpie's search tool on any provider, the Claude subscription's WebSearch and magpie's search tool run inside it (no longer passed to the client as a call), and a Responses provider's own web_search_call, whose sources are asked for. Tested live with Claude Code against a Claude subscription, Grok and Codex: each search comes back with its links
2026-09-27 03:41:53 +08:00
5bad1d117f
gateway: count usage from multiline and final SSE events ( #94 )
...
* gateway: count usage from multiline and final SSE events
* fix(gateway): count single-newline SSE events alongside multiline data
---------
Co-authored-by: Instinct Agent <agent@instinct.com >
Co-authored-by: Charan Rathore <charan-rathore@users.noreply.github.com >
2026-09-27 03:29:23 +08:00
yetone
b3f613acb4
gateway: Claude Code with ENABLE_TOOL_SEARCH can search the web — it defers WebSearch and loads it with ToolSearch, whose result is a tool_reference magpie read as nothing, and a Claude subscription's run, told its tools as it began, went on without the tool the client had just loaded, so the model said WebSearch would not load. A tool_reference is now told as the tool being loaded, DeferredToolPlaceholder is left out, and a subscription run whose client now offers a tool it lacks is replaced by one told the conversation with it. Tested with Claude Code on Opus (Claude subscription), Grok and Codex models
2026-09-27 02:53:39 +08:00
yetone
0f59ca2fc0
routing: a turn a rule sent to one of a group's models stays on it for its tool rounds when another member is on the same account — the conversation was kept to the account that answered, so its next request went to that account's first member: Claude Code's turn Jev sent to codex/gpt-5.5 had its tool results answered by claude/claude-sonnet-5, both served by one provider (as Opus and Sonnet on one Claude sign-in are). And a group whose intents are levels has every turn at one of them however torn Jev is: 继续,再加一个基准测试 after a 复杂任务 came out 0.54 to 0.46, below the confidence counted, and went to the group's first member; a torn turn now stays at the level of the one before, or takes the likelier. Tested with Claude Code through Jev and the real Claude and ChatGPT subscriptions
2026-09-27 02:25:24 +08:00
yetone
68408d2d00
gateway: a web search a client offers — Claude Code's WebSearch, Codex's web_search, a Chat client's web_search_options, Gemini's googleSearch — is done for every provider, not only the Claude subscription. A provider that searches by itself is asked to (a Codex account and OpenAI/xAI with Responses' web_search, Anthropic's API with web_search_20250305, OpenRouter with its web plugin); any other model — DeepSeek, Kiro, Grok, Cursor, Devin, a relay — is given a web_search tool of magpie's, which the first provider set up that can search answers with a small model of its (the Claude subscription's Haiku, then Codex, Anthropic, OpenAI, OpenRouter), and the model is asked again with what was found: the client sees one reply and no tool call. A request offering search is no longer relayed as is to a provider that would ignore the tool. Tested live: Claude Code → DeepSeek with WebSearch (2 turns), Responses and Chat clients → DeepSeek, Kiro and Grok, with the Claude subscription, Codex and OpenRouter each doing the search
2026-09-27 02:21:49 +08:00
yetone
f2b88ad5b4
jev: a group whose intents are levels has every message in one of them — Jev is asked once per set whether the kinds are levels (a noul, kept), and for such a set each turn is read from a choice among the kinds alone, as with a "none of these" option it put 你好 in none; Jev is also told the kind and the reasoning of the turn before, so 继续 after a 复杂任务 stays complex at its effort. Tested live against System One and OpenRouter: 你好 → nano/low, a deadlock analysis → mini/high (xhigh capped), 继续 → mini, 你是谁 after it → nano/low, Claude Code and Responses requests → the effort picked is what is sent
2026-09-27 01:53:04 +08:00
yetone
6fd85894ea
routing: Jev (TypeSafe's System One) as a group's classifier — asked once as a turn begins which of the rules' intents the message is, with how sure it is, and, for a group with effort auto, how hard the turn is to think about; the turn's requests then ask their model for that reasoning, where the agent asked for any. TypeSafe is a decision provider: it serves no conversation and is no group's member
2026-09-27 01:53:04 +08:00
yetone
c576153dd7
gateway: a web search asked of a Claude subscription is done — Claude Code's own WebSearch tool asks for Anthropic's web_search, which magpie dropped as a server tool while running claude -p with no tools of its own, so the model answered unsearched. The run now has Claude Code's WebSearch when the client offers web_search; its call and result stay inside the turn, the client hearing one reply whose usage counts both requests
2026-09-27 01:39:22 +08:00
yetone
a5a8d29ba4
providers: a model list that says which endpoints each model is served on (supported_endpoints, as Command Code's does) is heeded for every provider, not Copilot's alone — a live-fetched list left it out, so Claude Code's request for Command Code's deepseek went to its /messages, which serves only its Claude models, and was turned away; it now goes to /chat/completions, translated, and a Claude model asked for over Chat goes to /messages. /messages and /v1/messages both name Anthropic's. Issue #93
2026-09-27 01:29:33 +08:00
yetone
4f22d103d3
providers: Kiro is served through Kiro's own API, as pi-provider-kiro does, not kiro-cli over ACP — magpie reads the sign-in kiro-cli or the Kiro IDE keeps (Google, GitHub, AWS Builder ID / IAM Identity Center, or an external IdP), or takes a Kiro API key saved on the provider, and calls generateAssistantResponse itself: the caller's system prompt, whole history, tools, tool calls and results and images go to the model as they are, with no agent's own prompt and tools between, and its event stream comes back as text, thinking (asked for with Kiro's thinking mode), tool calls streamed as they are written, and usage. kiro-cli no longer has to be installed. A token near its end is refreshed — by kiro-cli when it is its sign-in, else directly, the new one written back where it came from — and one Kiro turns down is refreshed once and the call tried again. History is made the shape Kiro takes: it opens and ends with the user, every call is answered in the next message (an interrupted one said to be so), orphan results go, tools the conversation used but no longer offers are described, ids Kiro rejects are hashed, only the latest images are sent again. Kiro's usage limit is a 429, a prompt too long the error the rest of magpie takes for it. The models are Kiro's list with their context and image support; the account is its email and plan; the Usage page shows its credits and free trial. Issue #90
2026-09-27 01:25:37 +08:00
yetone
050c33b07d
providers: Kiro — a Kiro subscription served through the genuine kiro-cli over ACP, as Devin's is: magpie runs kiro-cli-chat acp as an agent of its own written into an empty workspace, whose only tools are the caller's (the magpie MCP helper, trusted; includeMcpJson off), picks the model with session/set_model, streams text and thinking back, and refuses permission for anything but the caller's tools. The account is whoever kiro-cli whoami says is signed in; a Kiro API key saved on the provider goes to the CLI as KIRO_API_KEY, the user's own environment's never. Missing CLI, signed out, MCP turned off by Kiro and ACP errors' data are each said plainly
2026-09-27 00:39:12 +08:00
yetone
47e12df418
models: a provider's model can go by a name of the user's, and offer only some of its reasoning levels — Claude Opus 5.5 · Claude Code can be Opus 5.5, and a model with six levels can offer just low, medium and high. Both are kept in settings.json by provider/model (modelNames, modelEfforts), apart from the vendor's list, so a Refresh, a restart or an upgrade leaves them, and the same model from another provider keeps its own; a provider renamed takes them along. They are applied where the catalog is made, so the gateway's /v1/models, the lists magpie writes into the agents (Codex's model catalog, ZCode, Pi, OpenCode, OMP …) and magpie's own pages all show them, and a change rewrites the agents' lists as any other change of the catalog does. Only what is shown changes: the id, where requests go, and the levels the vendor is asked at stay as they were; a found routing group keeps the model's own name. The provider's editor has Names & levels under its models — a name typed in place, a tick per level, Restore default — and the terminal has magpie model name <provider/model> <name>|--reset and magpie model efforts <provider/model> low,medium,high|--reset. Issue #92
2026-09-26 23:23:45 +08:00
yetone
97d4b3154e
gateway: a model's id without a provider in it that names a routing group is served by the group — gpt-6-sol goes where group/gpt-6-sol goes, its cheapest member first and the next when one fails, where it went to the first provider that serves it with no failover; and a found group is named however a vendor spells its model, so grok-4-7 (the group of grok-4.7) is served rather than unknown. With no group it is one provider's as before, a provider/model id is always that provider's, and the Routing view shows the group it went to. Asked on Discord
2026-09-26 23:01:53 +08:00
yetone
cd3222d213
intent: a turn that only carries on from the one before — 继续, 好的就这么做, fix that — is of the kind that one was; the classifier is told what it said of the conversation's turn before, as it only saw the new message and put 继续 after a 复杂任务 among the simple ones. A message that asks for something of its own is still classed by itself (你是谁 after a 复杂任务 is 简单任务), and the Routing view says what it was told
2026-09-26 22:51:17 +08:00
yetone
13aa2049a4
intent: a group whose intents are levels — 简单任务 and 复杂任务, how hard a request is — has every message in one of them; the classifier was asked which kind of request a message is, with 0 for none, and put 你是谁 and 你好 in neither, so those turns matched no rule and went to the group's usual route (Claude Opus) rather than the simple one. It now picks the kind that fits best, answering 0 only when a message is plainly none of them: groups by topic still get 0 for a greeting or a poem
2026-09-26 22:39:30 +08:00
yetone
2eb5015df3
accounts: Codex is signed in to its next ChatGPT account when the one it is on has used its allowance up — the Codex app, knowing its account is out, may not send at all, so magpie's failover never saw the turn: now the magpie serving the gateway looks every five minutes and, with another of Codex's accounts on in magpie that has room, switches Codex to the first of those as the Accounts page would (the one out stays on, next in line); a Codex started after that is on it. An account rests by its user, so the one Codex is switched to doesn't take over the rest of the one it left. Asked on Discord
2026-09-26 21:32:42 +08:00
yetone
0f0b8fc87e
gateway: Codex on one of its own models moves on to the next ChatGPT account when the one it is signed in to has used its allowance up — with more of Codex's accounts on in magpie, Codex's requests go through magpie even for its own models (openai_base_url kept, and put back or taken out as accounts are switched on or off) and are served as the codex subscription's, which fails over as its other models do; the account out of allowance rests until ChatGPT says it resets (resets_at), up to an hour at a time. Asked on Discord
2026-09-26 21:11:42 +08:00
yetone
4790c06b02
usage: each call keeps where it went — the provider's host, or the account it was — so a provider id later pointed somewhere else no longer puts the earlier place's tokens and cost under the new one: a model's usage is told apart by host (provider/model @ host) when its provider went to more than one place in the period, or to somewhere other than where it goes now. Asked on Discord
2026-09-26 19:03:14 +08:00
Jeremy Levartovsky
48071e9ab9
gateway: a Claude reply cut off by the context window says it was cut off ( #88 )
...
Claude 4.5 and later stop with model_context_window_exceeded when the
reply runs into the context window before max_tokens. It was read as a
normal end, so a Chat Completions client got finish_reason "stop" and a
Responses client status "completed" for a truncated reply; it now
reads as length, as max_tokens does.
2026-09-26 18:59:26 +08:00
yetone
d05763a5dc
opencode: each model of magpie's carries its window (limit.context), a group's context= with it, so OpenCode compacts when the model needs it and not at its own guess; and magpie sync puts magpie's provider back into opencode.json when a model of magpie's is chosen there but the provider is gone. /v1/models says each model's window too (context_window, context_length, max_input_tokens, max_output_tokens). Asked on Discord
2026-09-26 18:50:04 +08:00
yetone
5dbd28c39e
zcode: what you choose in ZCode for magpie stays — the provider turned off stays off, its rule stays where you moved it, and a rule you removed isn't put back on the next sync. gateway: a model that always thinks (GLM-5.3 answers 1210 to thinking turned off) is asked again with thinking left to it, rather than failing. Asked on Discord
2026-09-26 18:45:49 +08:00
yetone
a1dd2b1222
routing: a turn on one of Codex's own models (gpt-5.5 and the rest, which go to ChatGPT with Codex's own sign-in) shows in the Routing view's live trace as those on magpie's models do — before, only magpie's were traced, so Codex on its own models showed nothing going on. Asked on Discord
2026-09-26 18:31:46 +08:00
yetone
a77f7c1fcc
gateway: a test that when a rule's vision model fails, the text-only member that answers is sent the conversation without its older images ( #87 )
2026-09-26 18:25:16 +08:00
Charan Rathore and Charan Rathore
d583acaf46
fix: avoid sending group images to text-only fallbacks ( #87 )
...
Co-authored-by: Charan Rathore <charan-rathore@users.noreply.github.com >
2026-09-26 18:25:34 +08:00
yetone
d2da93ef11
group: an unlisted text-only model still keeps images out of its group — whether a group takes images was asked of the listed models only, so an image went to a member kept out of the pickers that can't see it; now every member counts, as for the context since #86
2026-09-26 17:52:54 +08:00
Charan Rathore and Charan Rathore
70a37c63f1
fix: count unlisted group members when routing long turns ( #86 )
...
Co-authored-by: Charan Rathore <charan-rathore@users.noreply.github.com >
2026-09-26 17:52:49 +08:00
yetone
7b69b22a76
quota: what is left of every subscription, plan and key balance, for scripts and agents — magpie quota [<provider>] [--json] prints each window's use and remaining percent and when it starts again, and each key's balance; the gateway answers the same at GET /v1/magpie/quotas (only to this machine, as it names the accounts), so an agent can pick where to send its work ( #85 )
2026-09-26 17:22:00 +08:00
yetone
73be42eca8
models: each agent can be shown only some of them — a provider or group gets a family (magpie provider set relay-x family=relay), and magpie visible zcode relay shows zcode only that family's models (or named providers and groups): in the files magpie writes for it and in what /v1/models answers it, told apart by its User-Agent; magpie models zcode lists what it is shown and what is kept from it, and magpie visible zcode all shows it everything again. A model kept from an agent is still served when asked for by name. Asked for on Discord
2026-09-26 16:02:29 +08:00
yetone
1c960ac62e
claude: a turn in which Claude answered with two tool calls no longer gets stuck — Claude Code makes its MCP calls one after another, so the second was made only once the first had its result, while the agent ran both and sent both results back at once; the second result, for a call not made yet, was dropped, and Claude waited for it while the agent waited for Claude. A result sent before its call is now kept and handed over as the call is made, and only the results sent since the last answer are looked at ( #84 )
2026-09-26 15:51:13 +08:00
yetone
189c8949ed
codex: one of Codex's own models picked while Codex is signed in with an API key OpenAI refuses (one a relay issued, left in ~/.codex/auth.json) failed with OpenAI's bare 401 "Incorrect API key provided" on every turn; the refusal now says it is Codex's own sign-in that was refused and to pick one of magpie's models in Codex or sign Codex in again
2026-09-26 15:14:33 +08:00
yetone
67c539d5da
gateway: secrets can stay on the machine — with Settings › Privacy › Mask secrets on, API keys, private keys, tokens, JWTs and the passwords of connection strings and assignments in what an agent sends go to the vendor as placeholders like {{API_KEY_k3v9x2mq}}, and what the vendor answers has them back, streams included (a placeholder split between two events is put back whole, a tool call's arguments get the value escaped); personal data (emails, phone numbers, ID and bank card numbers, checksum-checked) and the user's own words can be masked too. A placeholder is the same for the same value every time, so a conversation's history and what the vendor cached of it stay the same turn after turn, and the request log shows what the vendor saw. Suggested on X as maskit's idea; written from scratch
2026-09-26 14:21:14 +08:00
yetone
67056512e0
codex: back on one of Codex's own models after another vendor's, the conversation goes on — another vendor's reasoning (an rs_… id with nothing sealed in it, as Volcengine writes it) went on to OpenAI, which keeps nothing (store is false), looked the id up and answered 404 "Item with id 'rs_…' not found" on every turn and compaction; it now stays out, and an item OpenAI still refuses (sealed content it can't verify, an id it doesn't have) is taken out and the rest asked again instead of the session stuck on it
2026-09-26 13:59:01 +08:00
yetone
d4135b55e1
group: a routing group's models can be another group — group/<id> as a member, routed in its place by its own routing and rules, so Coder can be deepseek-v4-pro then Fast, a Coder rule can send a turn to group/fast and Fast's own rules pick within it; a group can't be in itself directly or through another (refused on save, left out of the picker), groups go at most 8 deep, and one can't be removed while another has it. The Routing tab shows a group in a group as a heading with its models under it, the packet going through it, and the log says what each group's rules and classifier decided
2026-09-26 13:41:17 +08:00