Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ agent-shim profile set <name> [--category <category>=<bool>]... [--entry <catego
agent-shim profile set <name> [--extends <profile>]... [--no-extends] [--description <text> | --no-description] [--json]
agent-shim profile set <name> [--[no-]launch-skip-permissions] [--[no-]launch-remote-control] [--[no-]launch-headroom] [--[no-]launch-track-usage]
agent-shim profile set <name> [--launch-claude-version <version> | --no-launch-claude-version] [--json] [--json]
agent-shim profile set <name> [--launch-delay <duration> | --no-launch-delay] [--[no-]launch-pause] [--[no-]launch-explain] [--[no-]launch-route-by-name] [--json]
agent-shim profile set <name> [--launch-delay <duration> | --no-launch-delay] [--[no-]launch-pause] [--[no-]launch-explain] [--json]
agent-shim profile set <name> [--launch-provider <provider> | --no-launch-provider] [--json]
agent-shim profile list [--json]
agent-shim profile show <name> [--json]
Expand Down Expand Up @@ -196,7 +196,6 @@ agent-shim run [@<identity>] [launch flags] [claude arguments]
# --claude-version <version> run exactly this installed Claude Code version
# --delay <duration> wait this long after the launcher's own output before starting claude; --[no-]pause waits for a key press instead
# --[no-]explain with @pool:<name>, list every member the pick ranked and why, not only the one picked
# --[no-]route-by-name also route bare virtual-model names through the models block (qualified names route on every session)
# --native run the real claude with nothing from agent-shim applied; cannot be combined with the flags above
claude @<identity> ... # the same, once `agent-shim shim enable` has run
```
Expand Down
4 changes: 1 addition & 3 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ The full flag/command reference table, the complete command list, `configure`'s
| **Claude Code version** | `agent-shim profile set <name> --launch-claude-version <version>` (or `--no-launch-claude-version`); a `launch.claudeVersion` key in `~/.agent-shim/config.json` or any cascade layer | `claude --claude-version <version>` / `AGENT_SHIM_CLAUDE_VERSION=<version> claude` | rule's inline `"launch": { "claudeVersion": "2.1.220" }` field; or `.agent-shim.json`'s `"launch"` |
| **Models (cascade)** | `models` in a configuration profile, directory rule or committed `.agent-shim.json`; any definition may be `null` to remove what the shallower layers set | not applicable (the global `models` block is the first layer) | the committed file's `models` merges like any other layer |
| **Launch delay and pause** | `agent-shim profile set <name> --launch-delay <duration>` (or `--no-launch-delay`) and `--launch-pause` (or `--no-launch-pause`); `launch.delay` and `launch.pause` keys in `~/.agent-shim/config.json` or any cascade layer | `claude --delay <duration>` / `claude --pause` / `claude --no-pause` / `AGENT_SHIM_DELAY=5s claude` / `AGENT_SHIM_PAUSE=true claude` | a rule's inline `"launch": { "delay": "5s" }` field; or `.agent-shim.json`'s `"launch"` |
| **Route by model name** | `agent-shim profile set <name> --launch-route-by-name` (or `--no-launch-route-by-name`); a `launch.routeByName` key in `~/.agent-shim/config.json` or any cascade layer | `claude --route-by-name` / `claude --no-route-by-name` / `AGENT_SHIM_ROUTE_BY_NAME=true claude` | a rule's inline `"launch": { "routeByName": true }` field; or `.agent-shim.json`'s `"launch"` |
| **Explain a pool pick** | `agent-shim profile set <name> --launch-explain` (or `--no-launch-explain`); a `launch.explain` key in `~/.agent-shim/config.json` or any cascade layer | `claude --explain` / `claude --no-explain` / `AGENT_SHIM_EXPLAIN=true claude` | a rule's inline `"launch": { "explain": true }` field; or `.agent-shim.json`'s `"launch"` |
| **Usage tracking** | `agent-shim profile set <name> --launch-track-usage` (or `--no-launch-track-usage`); a `launch.trackUsage` key in `~/.agent-shim/config.json` or any cascade layer | `claude --track-usage` / `claude --no-track-usage` / `AGENT_SHIM_TRACK_USAGE=true claude` | rule's inline `"launch": { "trackUsage": true }` field; or `.agent-shim.json`'s `"launch"` |
| **Identity credential** | `agent-shim identity set <name> --credential <source> [--credential-target oauthToken]` (in its `identity.json`); `--no-credential` returns it to its stored login | not applicable | not applicable: a credential belongs to the identity, not a directory |
Expand Down Expand Up @@ -71,7 +70,7 @@ agent-shim profile set <name> [--category <category>=<bool>]... [--entry <catego
agent-shim profile set <name> [--extends <profile>]... [--no-extends] [--description <text> | --no-description]
agent-shim profile set <name> [--[no-]launch-skip-permissions] [--[no-]launch-remote-control] [--[no-]launch-headroom] [--[no-]launch-track-usage]
agent-shim profile set <name> [--launch-claude-version <version> | --no-launch-claude-version]
agent-shim profile set <name> [--launch-delay <duration> | --no-launch-delay] [--[no-]launch-pause] [--[no-]launch-explain] [--[no-]launch-route-by-name]
agent-shim profile set <name> [--launch-delay <duration> | --no-launch-delay] [--[no-]launch-pause] [--[no-]launch-explain]
agent-shim profile set <name> [--launch-provider <provider> | --no-launch-provider]
agent-shim profile list [--json]
agent-shim profile show <name> [--json]
Expand Down Expand Up @@ -175,7 +174,6 @@ agent-shim run [@<identity>] [launch flags] [claude arguments]
# --claude-version <version> run exactly this installed Claude Code version
# --delay <duration> wait this long after the launcher's own output before starting claude; --[no-]pause waits for a key press instead
# --[no-]explain with @pool:<name>, list every member the pick ranked and why, not only the one picked
# --[no-]route-by-name also route bare virtual-model names through the models block (qualified names route on every session)
# --native run the real claude with nothing from agent-shim applied; cannot be combined with the flags above
claude @<identity> ... # the same, once `agent-shim shim enable` has run
```
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ Some providers report their quota only through a usage endpoint of their own, ne

## Virtual models and tiers

A `models` block in `~/.agent-shim/config.json` defines virtual models over the providers' models: `agent-shim model add fast --mode soonest-reset --member z/glm-5.3 --member m/MiniMax-M3` names a choice, and its `mode` is how the choice is taken (`ordered` tries the members in order and falls to the fallback whose allowance is worth the most when one is spent, `most-allowance` serves from the member holding the most remaining allowance, `soonest-reset` spends what is about to reset, `spread` walks the members turn by turn, `adaptive` rules out what the request rules out and any member whose window's own pace so far (the allowance spent over the time since it opened, once a tenth of the period has passed) would exhaust it before the reset, and stays on the current model while it stands unless another's gain, as the share more of the allowance a steady burn would leave unspent, beats the share of a full one-million-token context that re-sending the conversation costs, `lowest-latency` and `highest-success` serve from the observed best, and `cheapest` ranks by the `model price` entries weighted by the tokens the session has actually been sending). A member names a provider model (`z/glm-5.3`), an account model (`acct:work/opus`), another virtual model (cycles are refused), or a whole model family (`family:<canonical>`, every provider and account offering it). `agent-shim model tier opus --ref fast` points a Claude tier at one of these refs: for a session that routes by name the tier's model setting (`ANTHROPIC_DEFAULT_OPUS_MODEL` and its siblings) becomes the ref, which the door resolves per request, overriding that provider's own tier table for the tier, and `--clear` returns the tier to the table. A session that does not route by name keeps its provider's table, because only the door's name resolution can serve a ref. Nothing reads the block until a launch selects it, so a defined-but-unselected model changes nothing. `doctor` checks the block's own consistency: members naming no defined provider, virtual model or identity, cycles, a `cheapest` member with no prices, and a ref both hidden and shown. The block cascades: the global block is the shallowest layer, and a configuration profile, a directory rule and a committed `.agent-shim.json` carry the same `models` key in a layer form where any definition may be `null` to remove what the shallower layers set (`hide` and `show` replace wholesale), and the deepest layer naming a definition wins. The result is what a launch lists in `/model`, checks credentials for and applies tier pointers from; when a deeper layer changed it, the launch records the block it resolved in its session's registry entry and the door resolves that session's names against it, and when none did the door follows the global block live. Every session lists the catalogue's qualified rows in `/model`: every provider's models under its name (`z / glm-5.3`), honouring the block's `hide` and `show` lists; a session that routes by name lists its virtual models first, then one row per model family offered by more than one target (its description names every target, and selecting it lets the family's mode choose among them). A provider-qualified model name (`m/glm-5.3`) is routed on every session, whatever it launched on and with no setting asked for: a slash whose first segment names a defined provider sends the request to that provider, rewritten under its own model name, and a slash naming no provider (an aggregator's `vendor/model`) stays the session's own provider's business. A launch turns the bare names on with `launch.routeByName` (`--[no-]route-by-name`, `AGENT_SHIM_ROUTE_BY_NAME`, a profile's `--launch-route-by-name`, a directory rule or a committed file): a provider session then sends a virtual model's or a family's name to the provider its mode picks instead of its own, and a `router` provider (a kind with no endpoint or credential) does the same whatever it launches with. A provider-less (OAuth) session routes the names the block defines itself (a virtual model's, a `family:<key>`) the same way, while every other bare name still goes to Anthropic, and its `/model` rows follow Claude Code's own rather than replacing them. The front door takes a virtual model's choice per request, through its mode over the live signals: the quota windows the session's identity last recorded for each member's provider, the latency, success and token mix each target's recent requests observed (seeded from the usage log at door start), and the block's prices, so an edited block or a spent window changes the next request's answer with no restart. Whatever the request named, the target receives its own model name: a qualified or virtual name is rewritten in the request's one field, exactly as a provider's own `routes` entry that names a model is. A launch with routing on also resolves every provider the block names before it starts, a `family:` member included (through every provider offering a target of the family), so a provider whose credential yields no token fails the launch naming it instead of the first request that names it. The door also reads what a request needs (an image, tools, extended thinking, the size of the context it re-sends) against what each candidate's family carries, which `adaptive` filters on. The same or a similarly named model offered by several providers and accounts is one family: its canonical key is the model's family and version with any variant after a `+` (`glm-5-1`, `claude-opus-5-5+high`), which `agent-shim model match` lists beside the pairs that are only similar. `model same z/glm-5.1 o/zai-glm-51` asserts that names the normaliser cannot connect are one model, `model distinct` asserts that two alike names are two, and `model family glm-5-1 --mode cheapest --no-vision --context 200000 --name o/odd-name` sets one family's own settings: how a row selecting the whole family chooses among its targets (`ordered` when absent), the names pinned into it, and what every one of its targets can do, which `adaptive` reads instead of a hand-tagged target. A variant never merges into its family by name alone: a member names the variant it means (`family:claude-opus-5-5+high`) and a bare key means the targets without one. These assertions and settings cascade with the rest of the block, accumulating across layers rather than replacing. `doctor` warns when a virtual model lists targets that look like one model, when a `same` assertion contradicts what the names say, and when a `family:` member names a family no offered target belongs to.
A `models` block in `~/.agent-shim/config.json` defines virtual models over the providers' models: `agent-shim model add fast --mode soonest-reset --member z/glm-5.3 --member m/MiniMax-M3` names a choice, and its `mode` is how the choice is taken (`ordered` tries the members in order and falls to the fallback whose allowance is worth the most when one is spent, `most-allowance` serves from the member holding the most remaining allowance, `soonest-reset` spends what is about to reset, `spread` walks the members turn by turn, `adaptive` rules out what the request rules out and any member whose window's own pace so far (the allowance spent over the time since it opened, once a tenth of the period has passed) would exhaust it before the reset, and stays on the current model while it stands unless another's gain, as the share more of the allowance a steady burn would leave unspent, beats the share of a full one-million-token context that re-sending the conversation costs, `lowest-latency` and `highest-success` serve from the observed best, and `cheapest` ranks by the `model price` entries weighted by the tokens the session has actually been sending). A member names a provider model (`z/glm-5.3`), an account model (`acct:work/opus`), another virtual model (cycles are refused), or a whole model family (`family:<canonical>`, every provider and account offering it). `agent-shim model tier opus --ref fast` points a Claude tier at one of these refs: the tier's model setting (`ANTHROPIC_DEFAULT_OPUS_MODEL` and its siblings) becomes the ref, which the door resolves per request, overriding that provider's own tier table for the tier, and `--clear` returns the tier to the table. Nothing reads the block until a launch selects it, so a defined-but-unselected model changes nothing. `doctor` checks the block's own consistency: members naming no defined provider, virtual model or identity, cycles, a `cheapest` member with no prices, and a ref both hidden and shown. The block cascades: the global block is the shallowest layer, and a configuration profile, a directory rule and a committed `.agent-shim.json` carry the same `models` key in a layer form where any definition may be `null` to remove what the shallower layers set (`hide` and `show` replace wholesale), and the deepest layer naming a definition wins. The result is what a launch lists in `/model`, checks credentials for and applies tier pointers from; when a deeper layer changed it, the launch records the block it resolved in its session's registry entry and the door resolves that session's names against it, and when none did the door follows the global block live. Every session lists the catalogue's qualified rows in `/model`: every provider's models under its name (`z / glm-5.3`), honouring the block's `hide` and `show` lists; its virtual models first, then one row per model family offered by more than one target (its description names every target, and selecting it lets the family's mode choose among them). A provider-qualified model name (`m/glm-5.3`) is routed on every session, whatever it launched on and with no setting asked for: a slash whose first segment names a defined provider sends the request to that provider, rewritten under its own model name, and a slash naming no provider (an aggregator's `vendor/model`) stays the session's own provider's business. A bare name is routed the same way when only the models block defines it: a virtual model's or a family's name goes to the provider its mode picks, on every session and for a `router` provider (a kind with no endpoint or credential) as well. A bare name the session's own provider serves itself stays that provider's, because the launch chose it for its own names; a name neither the block nor the provider defines stays where it was sent. A provider-less (OAuth) session keeps every other bare name on Anthropic, and its `/model` rows follow Claude Code's own rather than replacing them. The front door takes a virtual model's choice per request, through its mode over the live signals: the quota windows the session's identity last recorded for each member's provider, the latency, success and token mix each target's recent requests observed (seeded from the usage log at door start), and the block's prices, so an edited block or a spent window changes the next request's answer with no restart. Whatever the request named, the target receives its own model name: a qualified or virtual name is rewritten in the request's one field, exactly as a provider's own `routes` entry that names a model is. A launch with a models block also resolves every provider the block names before it starts, a `family:` member included (through every provider offering a target of the family), so a provider whose credential yields no token fails the launch naming it instead of the first request that names it. The door also reads what a request needs (an image, tools, extended thinking, the size of the context it re-sends) against what each candidate's family carries, which `adaptive` filters on. The same or a similarly named model offered by several providers and accounts is one family: its canonical key is the model's family and version with any variant after a `+` (`glm-5-1`, `claude-opus-5-5+high`), which `agent-shim model match` lists beside the pairs that are only similar. `model same z/glm-5.1 o/zai-glm-51` asserts that names the normaliser cannot connect are one model, `model distinct` asserts that two alike names are two, and `model family glm-5-1 --mode cheapest --no-vision --context 200000 --name o/odd-name` sets one family's own settings: how a row selecting the whole family chooses among its targets (`ordered` when absent), the names pinned into it, and what every one of its targets can do, which `adaptive` reads instead of a hand-tagged target. A variant never merges into its family by name alone: a member names the variant it means (`family:claude-opus-5-5+high`) and a bare key means the targets without one. These assertions and settings cascade with the rest of the block, accumulating across layers rather than replacing. `doctor` warns when a virtual model lists targets that look like one model, when a `same` assertion contradicts what the names say, and when a `family:` member names a family no offered target belongs to.

An `acct:` member is served by relaunch, one login per process: the door leaves a move request for the session's supervisor (a pool launch under supervision) and answers the request saying it is being moved, which resumes under that identity with the picked model; a session nothing supervises is told the command to run instead (`claude @work --resume`). `agent-shim model explain <name>` shows which member a virtual model's mode picks over the same signals, and why each candidate stands where it does.

Expand Down
3 changes: 0 additions & 3 deletions schema/ConfigProfile.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,6 @@
"pause": {
"type": "boolean"
},
"routeByName": {
"type": "boolean"
},
"explain": {
"type": "boolean"
}
Expand Down
3 changes: 0 additions & 3 deletions schema/DirectoryRule.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,6 @@
"pause": {
"type": "boolean"
},
"routeByName": {
"type": "boolean"
},
"explain": {
"type": "boolean"
}
Expand Down
3 changes: 0 additions & 3 deletions schema/DirectoryRules.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,6 @@
"pause": {
"type": "boolean"
},
"routeByName": {
"type": "boolean"
},
"explain": {
"type": "boolean"
}
Expand Down
Loading