Hmbown 462858207b merge: provider-truth billing harvest
Composes the #4797 cost-truth repair (HEAD) with the provider-truth
harvest (codex/v092-ptruth-harvest). Both lanes close truth-critical
blockers; where they touched the same seam the rule applied was: one
receipt type per job, classification computed from the dispatched
receipt, and the fail-closed answer wherever the two disagreed.

route_billing.rs
- Kept the harvest's single `classify(provider, identity, base_url,
  product)` and its `capture_product`/`RouteProduct` credential truth.
  It fully subsumes the cost lane's `minimax_billing`, which only read
  `mode`; the harvest reads the same mode plus non-secret key-shape
  provenance and never opens the keyring. Dropped `minimax_billing`,
  `stepfun_billing`, `uses_zai_coding_plan`.
- Added `subscription_plan` to the MiniMax plan modes so the cost
  lane's documented spelling is not silently discarded as unprovable.
- `for_endpoint_without_config` is now a thin wrapper over `classify`
  with no identity and an unproven product, instead of a second copy of
  the endpoint rules. Same fail-closed contract, one implementation.
- Moved the cost lane's endpoint gate into `classify`'s catch-all
  (`endpoint_shaped_payg_billing`): a first-party or aggregator provider
  on an unrecognized host is Unknown, not metered-by-provider-name
  (#4318). This also fixes the harvest's noted hole where an empty
  endpoint fell through to metered.
- `billing_surface_for_dispatch` kept as-is (three live callers) and now
  benefits from the harvest's `for_route`.

core/events.rs, core/engine.rs
- `TurnRoute` carries both layers, documented at the definition because
  they are captured at different instants and answer different
  questions: `base_url`/`billing_product`/`provider_identity` are the
  DispatchedReceipt frozen at client-freeze (readable from TurnStarted),
  while `billing: Option<RouteBillingEnvelope>` is the wire-boundary
  envelope that must be structurally absent for an undispatched route.
- The envelope's `billing_mode` is now classified from that same frozen
  receipt via `for_dispatched_receipt` rather than a second ambient
  `for_route` read, so the two halves cannot disagree.

subagent mailbox/mod, subagent_routing
- Kept the cost lane's `source_id` + `route: EffectiveRouteEnvelope` on
  `MailboxMessage::TokenUsage` and dropped the harvest's parallel
  `billing: Option<ChildBillingProvenance>` field. The envelope is the
  child's dispatch receipt: the client it ran on froze provider,
  identity, endpoint fingerprint, billing surface and billing mode at
  construction, and `RouteBillingMode` has the same variant set as
  `ChildBillingProvenance` plus strictly more evidence. Child provenance
  still wins; it just travels on the richer receipt.
- The turn-end mailbox barrier (seal/drain/await before TurnComplete)
  and its exactly-once detached-child accounting are unchanged.

tui/tool_routing.rs
- Kept the cost lane's path: bill from the child's own
  `EffectiveRouteEnvelope`, rehydrated from the complete `child_*`
  metadata emitted by all three real producers (review, verify, rlm).
  The harvest's reader was explicitly unwired ("no tool producer emits
  the keys yet") and its parent-inheritance fallback is contradicted by
  the cost lane's tested contract
  (`legacy_child_usage_metadata_fails_closed_without_parent_route_fallback`),
  which is the stronger, fail-closed one: incomplete child metadata is
  Unknown and reported as missing spend, never inherited.
- Consequently the harvest's `ActiveTurnMetadata` receipt mirror
  (`billing_identity`/`billing_product`/`billing_base_url` and
  `dispatched_receipt()`) had no production consumer and was removed;
  `TurnRoute::cost_envelope()` is the same receipt one layer down and is
  already consumed.

Dead code after composition (no -D warnings, nothing silently kept)
- `ChildBillingProvenance`, `static_subscription_label`,
  `for_child_route_receipt`, `ChildParentRoute`, `ChildRouteClaim` are
  now `#[cfg(test)]`-gated with a note at each definition explaining
  that the wired child receipt is `EffectiveRouteEnvelope`. Their tests
  are kept as the executable record of the serialization and
  identity-comparison contracts.

Tests changed, and why
- `child_route_billing_fails_closed_for_every_ambiguous_provider`: the
  cost lane expected Metered for PAYG aggregators and an exact
  subscription label for OpenaiCodex/OpencodeGo children. The harvest's
  `for_child_route` returns Unknown for every non-local cross-provider
  child without provenance. The harvest's contract is stronger — a
  provider name is not evidence of what a turn billed, and Unknown
  (unlike a subscription label) keeps the turn in `/cost`'s money
  coverage denominator — so the weaker expectations were updated, and a
  same-provider inheritance case was added.
- `minimax_requires_an_explicit_saved_billing_mode`: expected label
  changed from the generic "MiniMax subscription plan" to the harvest's
  "MiniMax Token Plan quota", which names the actual product.

docs/PROVIDERS.md: kept the cost lane's StepFun billing-route setup row
and the harvest's MiniMax product-split row; the harvest's Moonshot
product-split and K3-clamp rows auto-merged.

Verification: route_billing 48, pricing 64, cost 87, receipt 157,
subagent 507, subagent_routing 18, tool_routing 11, prompt_suggestion
23, engine 461, model_inventory 17, tui::app 383, ui::tests 653,
config:: 458, mailbox 34 — all 0 failures. `cargo fmt --all -- --check`
clean; CI clippy (--workspace --all-features --locked, five -A allows)
clean; check-tui-locale-parity.py PASS.

Note: `failed_paused_dispatch_preserves_app_checkpoint_state_and_engine_gate`
overflows the default 2 MiB test stack under batch parallelism and
passes with RUST_MIN_STACK=32M. It is an unmodified HEAD test and a
pre-existing stack-depth papercut, not a merge regression.
2026-07-27 05:31:18 -07:00

Codewhale

An open source coding agent for your terminal — bring your own model.

Codewhale started as a native experience for DeepSeek. It has since grown into a community-driven project: one coding harness that fits a growing international community and supports as many models and providers as possible — open models first, hosted or local, none privileged over the rest.

Give it a provider, a model, and a task. It reads your code, edits files, runs commands, and checks its own work, then stops when the job is done or it needs you. Switch models mid-task with /model. Work interactively in the TUI, or run codewhale exec in scripts and CI. It's written in Rust, licensed MIT, and runs on your machine.

We're always looking for contributors and ways to improve. If a model or provider you use is missing, or something breaks, telling us is one of the most useful things you can do — see Contributing.

简体中文 · 日本語 · Tiếng Việt · 한국어 · Español · Português · Русский · Українська · codewhale.net · Docs · Changelog

CI crates.io npm

Codewhale running in a terminal

Install

npm install -g codewhale

Cargo, Docker, Nix, Scoop, prebuilt archives, Android/Termux, and a CNB mirror for anyone who can't reach GitHub are covered in docs/INSTALL.md. Coming from deepseek-tui? Your config and sessions carry over — see docs/REBRAND.md.

Use

codewhale auth set --provider deepseek   # or export ANTHROPIC_API_KEY, etc.
codewhale                                # open the TUI
codewhale exec "fix the failing test"    # headless
codewhale web                            # local browser client on 127.0.0.1

In the TUI: /model switches provider and model together, /fleet runs a team of workers, and /restore undoes a turn. When the composer is idle, Tab cycles Plan / Act / Operate and Shift+Tab cycles the Ask / Auto-Review / Full Access permission posture. ! runs a shell command through the normal approval path.

What it does

  • Any model, any provider. DeepSeek, Claude, GPT, Kimi, GLM, and 30+ providers, plus your own vLLM, SGLang, or Ollama with no key — all through one runtime and one toolset. Context limits and prices come from the real route, and an unknown price shows as unknown rather than $0.
  • Read-only until you allow more. Plan mode can't change files, and approvals gate risky commands. When an OS sandbox actually wraps a command, Codewhale says so: Seatbelt on macOS where available, opt-in bubblewrap on Linux. A repo's constitution.json compiles into write holds that even Full Access can't skip.
  • Work you can resume. A fleet records every step to an append-only ledger, so fleet resume picks up where you left off.

Learn more

  • docs/PROVIDERS.md — every provider route: hosted, gateway, and local
  • docs/FLEET.md — fleets, the ledger, and resume
  • docs/CONFIGURATION.mdconfig.toml, hooks, and the constitution
  • docs/HOOKS.md — the eleven TUI lifecycle hook events, their payloads, and which three of them can steer a turn (codewhale exec and the CLI subcommands do not fire hooks)
  • docs/WEB.md — the loopback-only browser client and its one-time authentication boundary

Everything else — modes, keybindings, sandbox details, MCP, the runtime API, and architecture — lives in docs and on codewhale.net.

Contributing

Issues, PRs, repro steps, logs, and feature requests are all real project work, and first contributions are welcome. When a PR can't merge as-is, maintainers harvest what works and keep the author credited — in the commit, the changelog, and docs/CONTRIBUTORS.md.

Thanks to DeepSeek for the models and support that started the project, DataWhale 🐋 for welcoming us into the Whale Brother family, and OpenWarp and Open Design for collaborating on the terminal-agent experience.

License

MIT. An independent community project, not affiliated with any model provider.

Star History Chart

Description
面向 DeepSeek V4 的终端原生编程智能体:100 万 token 上下文、思考模式流式推理、前缀缓存感知。自包含 Rust 二进制发布——开箱即带 MCP 客户端、沙箱和持久化任务队列
Readme MIT 378 MiB
Languages
Rust 92.9%
TypeScript 3.2%
JavaScript 1.7%
Python 1%
Shell 0.7%
Other 0.3%