No description
Find a file
guochao cc3ff756b4 feat(models): bidirectional [1m] long-context variant model
Reuse Anthropic's `[1m]` long-context suffix as a model-list annotation
that works across both protocol families.

- A string-form model `"name[1m]"` decodes to base id `name` +
  context_window 1000000. Any model (string or object form) with
  context_window >= 1000000 exposes two selectable variants: plain
  `name` and long `name[1m]`. Object-form ids are taken verbatim (a
  literal trailing `[1m]` is a plain name, never decoded).
- New `LaunchModel` carries per-protocol wire names resolved once at
  launch: Anthropic gets the variant label (marker kept), OpenAI gets
  the base id (marker stripped) plus a forced `model_context_window`
  for the long variant only.
- `default_model` / `--model` accept either variant label; shell
  completion and describe-help expand variants; `models list` keeps
  base rows and prints a hint about available `[1m]` variants.
- `--force model` synthesizes a plain LaunchModel from the literal
  name instead of panicking; an empty model list with `--model` now
  returns a guided error. Duplicate selectable labels (variant
  collision across entries) emit a warning.
- Drop the old flat `sanitize_chat_completions_model` / `wire_model_id`
  stripping; the variant model supersedes it.
- Document the feature in README.md and add a sample in
  config.demo.yaml.
2026-07-13 01:23:39 +08:00
docs refactor: adapter process-group kill, codex home injection, profile naming, drop hermes/crush 2026-07-10 11:09:51 +08:00
scripts feat(models): add lua filters and update crush sync 2026-06-07 09:50:03 +00:00
src feat(models): bidirectional [1m] long-context variant model 2026-07-13 01:23:39 +08:00
.envrc chore: switch direnv to flake and ignore .direnv/ 2026-05-05 19:54:17 +08:00
.gitignore chore: switch direnv to flake and ignore .direnv/ 2026-05-05 19:54:17 +08:00
AGENTS.md refactor: adapter process-group kill, codex home injection, profile naming, drop hermes/crush 2026-07-10 11:09:51 +08:00
build.rs feat(models): add lua filters and update crush sync 2026-06-07 09:50:03 +00:00
Cargo.lock refactor: adapter process-group kill, codex home injection, profile naming, drop hermes/crush 2026-07-10 11:09:51 +08:00
Cargo.toml refactor: adapter process-group kill, codex home injection, profile naming, drop hermes/crush 2026-07-10 11:09:51 +08:00
CLAUDE.md docs: add CLAUDE.md with uv run guidance 2026-05-05 19:52:37 +08:00
config.demo.yaml feat(models): bidirectional [1m] long-context variant model 2026-07-13 01:23:39 +08:00
deepseek-pricing.json feat(models): add lua filters and update crush sync 2026-06-07 09:50:03 +00:00
default.nix feat: inject git build metadata and add version command 2026-05-03 18:11:58 +08:00
flake.lock feat: add claude, codex, and hermes launch adapters with arg forwarding 2026-04-29 23:16:15 +08:00
flake.nix feat: inject git build metadata and add version command 2026-05-03 18:11:58 +08:00
nvchecker.toml feat(models): add lua filters and update crush sync 2026-06-07 09:50:03 +00:00
README.md feat(models): bidirectional [1m] long-context variant model 2026-07-13 01:23:39 +08:00
shell.nix feat: add claude, codex, and hermes launch adapters with arg forwarding 2026-04-29 23:16:15 +08:00

agent-run

agent-run is a small launcher for coding agents.

It keeps provider settings in one place, then adapts them for each target agent instead of asking you to manually rewrite per-agent config files every time.

Current focus:

  • claude
  • codex

Current protocol support:

  • anthropic
  • openai-responses
  • openai-chat-completions

Why

Different coding agents expect different configuration shapes:

  • some read environment variables
  • some want a config file or profile
  • some mix both

agent-run gives you one provider config and adapts it to the target agent at launch time.

The provider config stays generic; each agent adapter is responsible for mapping that generic config into the shape the downstream tool expects.

What It Does

  • Centralizes provider definitions in one config file
  • Resolves secrets from either key or key_command
  • Optionally loads model lists from provider APIs and caches normalized results on disk
  • Validates protocol compatibility before launch
  • Negotiates the final protocol for agents that support more than one wire API
  • Generates temporary runtime config where needed
  • Forwards extra args to the underlying agent command

Supported Agents

Claude Code

  • protocol: anthropic
  • launch mode: environment variables
  • supports both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY
  • ensures onboarding is marked complete before launch

Codex

  • protocol: openai-responses
  • launch mode: uses ~/.codex by default, or an isolated CODEX_HOME when configured
  • touches/injects provider config instead of overwriting config.toml
  • selects the model via generated profile <provider>-<normalized-model>.config.toml

Shell

  • exposes all supported protocols as environment variables
  • launches a shell (or any command) with ANTHROPIC_* / OPENAI_* set from the provider

Configuration

Default config path:

~/.config/agent-run/config.yaml

Minimal example:

providers:
  deepseek:
    protocols:
      - openai-chat-completions
      - anthropic
    base_urls:
      openai: https://api.deepseek.com
      anthropic: https://api.deepseek.com/anthropic
    key_command:
      - printenv
      - DEEPSEEK_API_KEY
    default_model: deepseek-v4-pro
    models:
      - deepseek-v4-pro
    model_api_filters: []

  kimi-code:
    protocols:
      - openai-chat-completions
      - anthropic
    base_urls:
      openai: https://api.kimi.com/coding/v1
      anthropic: https://api.kimi.com/coding
    key_command:
      - printenv
      - KIMI_API_KEY
    anthropic_use_api_key: true
    default_model: kimi-for-coding
    models:
      - kimi-for-coding

isolated_homes:
  codex:
    sandbox: {}

See config.demo.yaml for a fuller example.

Protocol Negotiation

protocols declares what a provider can speak.

  • claude requires anthropic
  • codex requires openai-responses
  • shell exposes all three families

For single-protocol agents, launch fails unless the provider supports that protocol or --force protocol is used. Shell mode does not require a specific protocol.

Models

Each provider declares its models under models. An entry is either a bare string (the model id) or an object with id plus optional metadata (name, context_window, max_output_tokens, reasoning, vision, supports_attachments, pricing fields):

models:
  - deepseek-v4-pro
  - id: anthropic/claude-opus-4.6
    name: Claude Opus 4.6
    context_window: 200000
    vision: true

Long-context variants with [1m]

Anthropic exposes 1M-context models by appending [1m] to the model name (e.g. glm-5.2[1m]). agent-run reuses that suffix as a model-list annotation so a single declaration works across both protocol families:

  • A string-form model "deepseek-v4-flash[1m]" decodes to the base id deepseek-v4-flash with context_window: 1000000.
  • Any model (string or object form) whose context_window >= 1000000 automatically exposes two selectable variants: the plain name and the long-context name[1m].

default_model and --model may reference either variant. The two are wired differently per protocol:

variant Anthropic endpoint OpenAI endpoint
name name name
name[1m] name[1m] (marker kept) name (marker stripped) + forced model_context_window

The [1m] marker is only decoded from string-form declarations and only as a trailing suffix. An object-form id is taken verbatim, so { id: "some-model[1m]" } is a plain model whose real name contains [1m] — the marker is never stripped for it. If a provider's real model id literally ends in [1m] and you do NOT want it treated as a long-context base, declare it as an object.

models:
  - "deepseek-v4-flash[1m]"        # base `deepseek-v4-flash`, ctx 1M; variants: deepseek-v4-flash, deepseek-v4-flash[1m]
  - id: glm-5.2
    context_window: 2000000        # variants: glm-5.2, glm-5.2[1m] (forces 2M on openai)
  - kimi-for-coding                # plain only
default_model: deepseek-v4-flash[1m]

The {model} value in extra_env templates (e.g. ${context:agent}:${context:model}) resolves to the selected variant label (name or name[1m]), not the bare wire id.

Usage

Open or initialize your config:

agent-run config
agent-run config --bootstrap-config

agent-run config only opens an existing config. Use --bootstrap-config to write the embedded sample config first when the file does not exist.

Launch Claude:

agent-run launch deepseek claude
agent-run launch kimi-code claude

Launch Codex:

agent-run launch ollama codex
agent-run launch openrouter --model openai/gpt-5.3-codex codex
agent-run launch sandbox codex

isolated_homes notes:

  • isolated_homes.codex.<name> allows launching Codex with an isolated runtime home even when no provider exists.
  • Entries are empty objects today. They do not accept key, base_url, model, or custom paths.
  • When both a provider and an isolated home entry share the same name, agent-run combines them: isolated runtime home plus provider-derived runtime config.
  • By default Codex uses ~/.codex and agent-run only injects/touches the provider configuration instead of overwriting config.toml.
  • Isolated runtime homes persist across launches. agent-run reuses the existing directory and only updates the generated config file.

Generate shell completion:

agent-run completion bash
agent-run completion zsh

When installed from Nix, bash and zsh completion files are installed automatically.

Manual shell setup is only needed when running the binary outside the Nix package:

source <(agent-run completion bash)
source <(agent-run completion zsh)

Model catalog:

agent-run models list openrouter
agent-run models list --refresh openrouter
agent-run models list --all

Forward extra args to the underlying agent:

agent-run launch deepseek claude resume
agent-run launch ollama codex resume --last

Both forms are supported:

  • agent-run launch provider agent arg1 arg2
  • agent-run launch provider agent -- arg1 arg2

Completion notes:

  • Bash and Zsh are supported.
  • Provider completion is loaded from local config.yaml.
  • --model completion refreshes remote model cache by default when model_api_filters is enabled.
  • Set AGENT_RUN_DISABLE_MODEL_COMPLETION_REFRESH=1 to make completion use local models plus existing cache only.
  • Default log level is WARN. Completion runs stay silent unless you explicitly set RUST_LOG.
  • Trailing agent_args are forwarded but are not completed.

Model API filter notes:

  • Omit model_api_filters to use the default catch-all rule.
  • Set model_api_filters: [] or model_api_filters: null to disable remote model loading and cache interaction for that provider.
  • Filters are applied to normalized remote models before cache write.

Secret Handling

Each provider can use either:

  • key
  • key_command

key_command is preferred when you want to fetch secrets from an external source such as a password manager, shell environment, or local secret helper.

Extra Environment Variables

Each provider may declare an extra_env map that is injected into the launched agent process. The map is applied last and may override the env vars agent-run sets by default (e.g. ANTHROPIC_API_KEY, CODEX_HOME, OPENAI_API_KEY).

Values support inline template expansion:

  • ${env:NAME} — reads env var NAME from the launcher process; errors if unset.
  • ${context:FIELD} — reads a resolved launch field. Supported fields: provider, protocol, model, key, agent, base_url.

Example:

providers:
  openrouter:
    # ...other fields...
    extra_env:
      OPENROUTER_API_KEY: "${context:key}"
      HTTPS_PROXY: "${env:CORP_PROXY}"
      AGENT_RUN_TRACE: "${context:agent}:${context:model}"

Runtime Strategy

agent-run avoids modifying long-lived agent config for single-provider agents.

  • claude uses temporary env plus a one-time onboarding state fix
  • codex uses ~/.codex by default, or an isolated CODEX_HOME when configured; only touches/injects the provider block and writes a profile file
  • shell injects provider settings into the child process environment

Development

This repository uses a minimal Rust + Nix setup.

Useful commands:

cargo check
cargo clippy -- -D warnings
cargo fmt

Docs