Skip to content

Releases

v0.6.3

Choose a tag to compare

@mugpeng mugpeng released this 18 Sep 01:14

0.6.3 - 2026-09-18

Features

  • Surface broken-config state in serve mode instead of crash-looping

Documentation

  • Add awefork, awecontrib, and AgentX to the ecosystem section
  • Point aweskill website links at aweskill.wehuman.top
  • Retarget companion tool and self links to wehuman01

CI

  • Adopt awecontrib verify entry point

v0.6.2

Choose a tag to compare

@github-actions github-actions released this 11 Sep 04:07

Three token-saver layers and multi-account logins: awecompress frozen-summary compression joins ODCP and RTK, subscription logins multiply via authHome, and a batch of integration hardening fixes lands with the first PyPI release of awecompress.

Added

  • awecompress profile flag ("awecompress": true, or an object tuning summaryModel ("flash" default, "pro", or a literal model id that must be declared in providers.json models), thresholdTokens, keepRecentTurns, minSpanTokens, transcriptResultCap, protectedTools, protectedFilePatterns). Ahead of ODCP/RTK, the oldest complete turns freeze into one cached summary — summary calls ride the flash (or chosen) destination with the provider's own auth, savings land in the usage log as awecompress_saved, and the frozen store is shared with the standalone proxy. Fail-open end to end; X-Awerouter-Token-Saver: off disables it with the rest. Requires pip install awerouter[compress] — awecompress is now on PyPI as 0.2.0.
  • Provider authHome field (providers.json), legal only next to an "auth": "codex" / "auth": "claude" sentinel: codex reads <authHome>/auth.json (an aweswitch account dir works as-is — ~/.config/aweswitch/accounts/codex/<name>); claude keeps its owned store at <authHome>/claude-auth.json. Absent authHome is exactly the old single-login behavior. Combined with the pool tag (gateway) or backups (profiles), same-model failover across accounts of one vendor becomes a few lines of config.
  • Per-login failover bookkeeping: rejected_auths keys on sentinel+authHome, so a dead account's double-401 excludes only that account — its sibling accounts stay eligible later in the same request. Serve-start login warnings and 503/refresh hints name the exact dir that lacks a login, one line per login.
  • awerouter config login claude <dir> / config logout claude <dir> (and codex's CODEX_HOME-aware hint, shell-quoted and Windows-safe): manage one account's login in its authHome; each claude store refreshes independently.
  • odcp profile flag ("odcp": true, or {"dedup": bool, "purgeErrors": bool | {"turns": int}}, default off). Before routing and before RTK, one pairing pass over the history applies two rules: dedup keeps only the newest output of repeated identical tool calls (same tool + normalized arguments — explicit JSON null stays distinct from an omitted key), replacing older ones with a one-line placeholder; purgeErrors strips a failed call's input strings once it is turns user messages old (default 4), keeping the error text (anthropic only — other protocols carry no error mark on the wire). Editors and task/planning tools are never rewritten — including codex's shell-wrapped apply_patch — replacements that wouldn't shrink the body are skipped, and the trailing turn is untouched. Deterministic and fail-open, with the X-Awerouter-Token-Saver: off bypass; /v1/messages/count_tokens prunes identically. Unlike RTK, pruning rewrites earlier history when a new duplicate appears or an error crosses the age threshold — an accepted prompt-cache trade. Behavior follows Opencode-DCP's public documentation (AGPL-3.0); implemented from that description, not translated. odcp_saved is logged per request and reported by usage next to rtk's; serve banner and serve status show odcp when on.

Fixed

  • awecompress summary calls to Anthropic-shaped upstreams now carry anthropic-version (the internal sender used to omit it, so Anthropic 400'd the summary call and compression silently failed open).
  • Gateway hot reload validates the fresh profiles' awecompress config (summaryModel declarations, package presence) before swapping them in — an invalid reload is skipped and the previous config keeps serving, same as the single-profile reload.
  • Codex config login/logout hints quote the CODEX_HOME path (spaces no longer break the printed command) and refuse paths containing a double quote on Windows (no set " injection into the copy-pasted command).

v0.6.1

Choose a tag to compare

@github-actions github-actions released this 05 Sep 14:49

Robust resident service management: serve restart one-word reloads, crash-proof request handling, and macOS installs that actually stick.

Fixed

  • A request-path failure no longer kills the daemon: a die() inside request handling (e.g. a ${VAR} auth reference whose value is missing from the daemon's environment) used to raise SystemExit through the handler and take the whole process down — under KeepAlive, a crash loop. A guard middleware on both apps now answers that one request with 503 (message included) and keeps serving the next.
  • Resident installs on macOS no longer intermittently end up "installed — not running": launchctl bootout tears down asynchronously, and right after it returns the old job can still read as loaded while a racing bootstrap is silently never submitted. install() now waits for the domain to actually release the old job, then bootstraps and verifies the job really loaded (retrying within 5s before dying loudly). serve restart hit this ~half the time before.

Added

  • awerouter serve restart [PROFILE]: the one-word way to apply a changed environment variable or secret. Resident services are re-installed from the current shell — same command line, port and host as installed, env baked fresh — and an installed-but-stopped service is started too. Plain background instances are stopped and re-spawned from the current shell on the same port; foreground instances are skipped (they belong to their terminal).

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 05 Sep 01:50

Failover becomes first-class: routing destinations gain ordered backups queues with quota-aware cooldowns, providers declare a multimodal flag so the image guard survives failover, and gateway direct-forwards support a pool tag for same-model account rotation.

Added

  • Failover queues (backups) in routing.json: each destination key may declare an ordered failover queue — same "provider,model" strings as destinations. When the primary answers 429/408/5xx or a network error before the first streamed byte, the request walks its queue in order; smart routing itself never changes (the tier is the request's property — failover only swaps who serves it). Zero config, each tier carries one implicit cross-tier hop (flash→pro / pro→flash); an explicit list replaces it. A 429 or any response carrying Retry-After puts that candidate on an in-process cooldown (Retry-After honored when parseable, else 30s; capped at 60s). An exhausted queue passes the last upstream response through; a network-error 502 names every candidate tried. Every hop is stamped into the label (background→fb:glm,glm-4.7-flash) and counted as fallback_hops in the usage log.
  • Provider multimodal flag: image-bearing requests filter their failover queue on the provider's declared multimodal flag — the image guard holds during failover, and undeclared means no.
  • Provider pool tag for gateway direct-forwards: entries sharing a tag within one protocol group are the same vendor's accounts (same models, different keys). A gateway provider/<model> forward that 429s fails over to the same model on the next pool member — declaration-order wrapping from the named entry, so each account's overflow flows to the next. One tag per entry replaces mutual-backups lists; untagged entries stay pinned. Cooldowns, →fb: labels, fallback_hops, and the multimodal image guard all apply.
  • Track rejected_auths to avoid reusing dead logins within a request.

Fixed

  • Config load now validates that both flash and pro destinations are present.
  • Skip implicit hop when both tiers point to identical provider+model.
  • Scope cooldown keys by protocol so one protocol's 429 does not cool another's.
  • git_diff RTK filter defaults to a line cap; git_status handles long-form Untracked files.
  • Update-check is resilient to fetch exceptions (preserves cached latestVersion and records the check).

v0.5.9

Choose a tag to compare

@github-actions github-actions released this 04 Sep 12:24

Windows CI repair: since v0.5.5 the windows-latest CI legs have been red — v0.5.7's resident-service feature made serve status die on Windows, and the _is_awerouter_process tests assume a readable command line that Windows cannot provide.

Fixed

  • awerouter serve status works on Windows again: installed_services() now reports none on platforms without a service manager instead of exiting with the resident-services error (which is what service_kind() raised). Mutating paths (--install, resident stop/purge) still refuse loudly through the new require_service_kind() wrapper — same message as before (src/awerouter/service.py; TestStatus in tests/test_cli.py runs on Windows again).
  • _read_cmdline on Windows returns "unverifiable" (None) instead of a false "": there is no /proc, and MSYS ps cannot see native processes, so the old fallback misclassified every live pid as dead. The serve stop guard it feeds is POSIX-only today, but the contract no longer lies for any future Windows stop path (src/awerouter/runtime.py). TestIsAwerouterProcess skips on Windows with that reason — command-line matching cannot be observed where reading is impossible.
  • serve status tests no longer read the developer's real LaunchAgents/systemd directories: a machine with a resident service installed failed TestStatus locally even though CI was green (tests/test_cli.py).

v0.5.8

Choose a tag to compare

@github-actions github-actions released this 03 Sep 15:32

Two correctness fixes to v0.5.7's features: gateway direct forwards no longer pollute per-profile usage stats, and resident-service installs keep percent-encoded env values intact.

Fixed

  • Gateway direct-forward usage attribution: a <provider>/<model> forward belongs to no routing profile, but the usage log recorded it under whichever profile happened to serve the protocol, skewing usage --profile. Those requests now log direct as profile/destination, with the real provider and model preserved (tests/test_gateway.py).
  • systemd Environment= values escape % as %% (systemd expands % specifiers there), so a percent-encoded proxy URL baked into a resident service by serve run --install reaches the daemon verbatim (tests/test_service.py).

v0.5.6

Choose a tag to compare

@github-actions github-actions released this 03 Sep 06:17

Gateway mode: awerouter serve all turns all routing.json profiles into one local endpoint. A request model name selects the profile (<profile>/auto|flash|pro, e.g. step-glm/auto); GET /v1/models advertises every alias. The existing serve run <profile> model remains unchanged, so users can migrate one client at a time or run both forms concurrently.

Added

  • awerouter serve all [--port N] [--host HOST] [-d]: one daemon and one port for every profile. It preserves the same-protocol proxy contract and per-profile routing/settings/providers; requests select the target profile from the model prefix, then auto runs the usual L1–L4 pipeline while flash/pro force that profile's destination. The gateway ignores per-profile port fields (its own --port or the normal 20128 scan is authoritative), hot-reloads profile additions/removals and every config edit, registers as gateway for serve status, and serve stop gateway stops it. Background mode writes serve-gateway.log.
  • Top-level routing.json defaultProfile: optional gateway-only bare-name target for auto / flash / pro, keeping existing Claude Code tier environment variables compatible. It must name a configured profile; with multiple profiles and no default, bare names fail loudly and callers must use <profile>/...; a lone profile is its own natural default. It is not valid inside a profile and does not affect single-profile serve.
  • Gateway discovery and honest failures: /v1/models lists default bare tiers when routable plus every profile alias; / identifies gateway mode. Unknown profile, invalid tier, and endpoint/profile protocol mismatch each return a descriptive 400 naming the problem and available alternatives. /v1/messages/count_tokens follows the same alias selection and model rewrite as inference.
  • Gateway-specific test coverage (alias selection, forced/custom tiers, default behavior, all protocols, errors, token counting, config parsing/reload and hints) plus README/README_cn documentation.

Fixed

  • The gateway's registration name (serve status / serve stop gateway / serve-gateway.log) now comes from a single GATEWAY_PROFILE_NAME constant instead of scattered literals, so no real profile named gateway can collide with it inconsistently.

v0.5.5

Choose a tag to compare

@github-actions github-actions released this 03 Sep 04:51

Daemon lifecycle becomes first-class: awerouter serve run <profile> -d runs the daemon detached — it survives the terminal (log: <state>/serve-<profile>.log), awerouter serve status lists every running serve instance (foreground and background, registered under <state>/run/ at bind time), and awerouter serve stop [PROFILE] stops them with a graceful SIGTERM. Serve now also watches routing.json/providers.json and hot-reloads edits — destinations, thresholds, tool routing, settings overrides, and provider entries apply to the next request without a restart. The CLI also reorganizes: serve becomes a command group (run / status / stop) and login / logout / restore move under config — the released top-level spellings keep working as hidden aliases, so nothing breaks.

Added

  • login, logout, and restore moved under the config group — awerouter config login [claude|codex], awerouter config logout [claude|codex], awerouter config restore [providers|routing] — grouping every command that manages config-owned state (files, backups, the claude login store) next to config path/show/edit. The released top-level spellings keep working as hidden aliases (awerouter login etc.), so existing docs, scripts, and muscle memory don't break; awerouter --help lists only the config form. Error hints that name the command (missing claude login 503/warnings, init --merge behavior-shift note) now print the new spelling.
  • serve is now a command group — run (start a daemon), status, stop — instead of a bare command with top-level status/stop. The pre-group spellings keep working: awerouter serve <profile> [--port/--host/-d] and awerouter <profile> are equivalent to awerouter serve run <profile> (an unknown subcommand under serve is a profile name, same as the top level), and bare awerouter serve still auto-selects the single profile. The one removal: awerouter serve -d (background auto-select, no profile) — name the profile or use serve run.
  • awerouter serve run [PROFILE] -d/--background: spawns the daemon detached via python -m awerouter __serve_daemon__ <profile> (new __main__.py), waits for it to bind, then prints pid/port/log path plus a status/stop hint; notes when the profile is already running (starts another anyway, same as concurrent foreground instances). A child that dies before binding fails the command with the log tail. POSIX-only (os.name == "nt" refuses).
  • awerouter serve status / awerouter serve stop [PROFILE] (new awerouter.runtime module): every serve process registers {pid, profile, protocol, port, host, background, started} under <state>/run/<pid>.json at bind time and unregisters on shutdown (SIGTERM/SIGHUP now shut down gracefully via signal handlers on the serve event loop). serve status lists live instances (stale/dead-pid files pruned on sight); serve stop SIGTERMs matching instances after a command-line guard (ps token match: awerouter, -m awerouter, .../bin/awerouter) so a reused pid is never signaled. Windows: pid_alive probes via OpenProcess (os.kill(pid, 0) would TerminateProcess there), and -d/serve stop refuse outright.
  • Hot reload: _serve polls both config files' mtimes (1s) and swaps the live app's profile/settings/providers from a fresh load_for_profile — destinations, thresholds, tool routing, per-profile overrides, provider entries, and "auto" threshold re-resolution all apply to the next request without a restart. A file that fails to load prints one config reload skipped line and the previous config keeps serving until the file parses again; a changed port field prints a restart hint (rebinding is out of scope). The serve banner prints hot reload -> on.
  • Skill refresh nudge: awerouter self-update (after a successful upgrade) and the daily update reminder now add one line when the awerouter agent skill is detected in ~/.agents/skills/ or ~/.claude/skills/ — the skill is updated along with awerouter releases, and refreshing it is best done by the coding agent itself: aweskill update awerouter. awerouter never writes the skill file: its lifecycle (store, projections, local-edit protection) belongs to aweskill, and the skill is not packaged in the wheel, so awerouter only points at the refresh command.

v0.5.4

Choose a tag to compare

@github-actions github-actions released this 02 Sep 02:44

Image bridge: settings.imageBridge (default false, opt-in) gives a text-only pro model second-hand vision. When a request carries images only in history — the final message is text — awerouter has the multimodal flash destination (imageModel) transcribe each distinct image, replaces the image blocks with the transcription text before routing, and the session falls through to defaultModel (pro) instead of being pinned to flash forever. A fresh upload this turn still routes to flash natively; router.py is unchanged — after the rewrite has_image is false and the request lands in L3/default, before the rewrite the L1 image guard routes it as always. The step-glm-mm template now ships with imageBridge: true.

Added

  • settings.imageBridge (true/false, global or per profile, validated at load and shown by config show): the request-pipeline transform described above. It runs before rtk compression and routing in _proxy_flow, so what rtk compresses, what L3 scores, and what goes upstream are exactly the same body; handle_count_tokens applies the same bridge so the client's context-window estimate matches. The serve banner prints image bridge -> on (...) naming the transcribing destination.
  • awerouter.vision (pure logic): per-protocol content keys (image_key), caption body building and response parsing for all three wire protocols, in-place image-block rewriting ([Image n, transcribed by <model>] text blocks), and a content-addressed caption cache (bounded FIFO, process lifetime — a restart re-transcribes each distinct image once; the cache key includes provider+model, so a destination switch never serves another model's transcription).
  • Failure fallback: any caption call failure (network, non-200, empty text) leaves the request body untouched — one printed line, and the L1 image guard routes the request exactly as before. Codex subscription logins (auth: "codex", SSE-only backend) skip the bridge entirely.
  • step-glm-mm template sets imageBridge: true; init --merge flags a newly-written imageBridge as a behavior shift next to imageModel/defaultModel. README/README_cn document it as an "Image bridge" (图片桥接) section; README.ai.md and the awerouter skill mention the template flag.

Behavior notes

  • Bridged "vision" is second-hand: its ceiling is the transcription quality, and each distinct image costs one extra flash call (cached per image content for the process lifetime).
  • The flash→pro rescue fallback (flash down on an un-rewritten image request) still carries raw images to a possibly text-only pro — pre-existing behavior, unchanged and out of scope.

v0.5.3

Choose a tag to compare

@github-actions github-actions released this 01 Sep 04:05

Per-profile settings overrides and template merging: every settings key (backgroundModel, thinkModel, webSearchModel, imageModel, defaultModel, searchResultDiscount, toolRouting, longContextAuto) can now be written directly in a profile body, flat next to protocol/destinations — it applies to that profile alone, and missing keys (missing fields inside toolRouting/longContextAuto included) inherit from the global settings block, so a profile re-tunes exactly what differs. backgroundModel/thinkModel in a profile body are the pre-v0.5.2 per-profile shape, honored again. Serve and the router use the merged result (RoutingProfile.settings); a profile's own longContextAuto.fallbackThreshold now backs its "auto" threshold; usage calibrate/token views with --profile use that profile's discount. config show <profile> prints the effective values with the override keys flat in the entry, the serve banner lists them on an overrides -> ... line. Unknown keys die at load naming the offender — profile bodies now validate their key set too (previously silently ignored), and the global block gets the same check, so a typo'd key never silently inherits.

awerouter init <template> --merge folds a bundled template into an existing config: missing provider entries, profiles, and settings keys are filled in; existing entries always win (your base_url/auth, your profile bodies, your explicitly-set settings). Profile id collisions are skipped and reported, so merging is idempotent. Newly-written imageModel/defaultModel — global keys that re-route every existing profile — print a warning with an awerouter restore hint. Both files are snapshotted to .bak before writing and the merged config is load-validated before init reports done.

Added

  • Per-profile settings overrides: every settings key may also be written directly in a profile body, flat next to protocol/destinations — it applies to that profile alone, and missing keys (missing fields inside toolRouting/longContextAuto included) inherit from the global settings block, so a profile re-tunes exactly what differs (e.g. one multimodal sidekick flipping imageModel/defaultModel without touching everyone else). backgroundModel/thinkModel in a profile body are the pre-v0.5.2 per-profile shape, honored again. Serve and the router use the merged result (RoutingProfile.settings); a profile's own longContextAuto.fallbackThreshold now backs its "auto" threshold; usage calibrate/token views with --profile use that profile's discount. config show <profile> prints the effective values with the override keys flat in the entry, the serve banner lists them on an overrides -> ... line. Unknown keys die at load naming the offender — profile bodies now validate their key set too (previously silently ignored), and the global block gets the same check, so a typo'd key never silently inherits.
  • awerouter init <template> --merge folds a bundled template into an existing config: missing provider entries, profiles, and settings keys are filled in; existing entries always win (your base_url/auth, your profile bodies, your explicitly-set settings). Profile id collisions are skipped and reported, so merging is idempotent. Newly-written imageModel/defaultModel — global keys that re-route every existing profile — print a warning with an awerouter restore hint. Both files are snapshotted to .bak before writing and the merged config is load-validated before init reports done.

Unreleased

Added