Releases: fkiene/llmtrim
Release list
llmtrim 0.11.9
Fixed
-
Claude Code under always-sub skip-login actually hits the MITM. Node 20.18+/22 undici
ignoresHTTPS_PROXYunlessNODE_USE_ENV_PROXY=1, so the dummyANTHROPIC_AUTH_TOKEN
was reaching real Anthropic and returning401 Invalid bearer token. Setup now exports
the flag in the managed env block, Windows registry, andsettings.jsonnext to the dummy
token, and heals older profile blocks that lack it. -
llmtrim ensureno longer re-asks about cheaper/compactmodels after they are configured.
compact_models_configured()usedstr::parse::<toml::Value>(), which in toml 1.x only accepts
a single value — a real multi-keyconfig.tomlfailed the parse, the error was swallowed, and
ensure/doctor always reported compact as missing. It now usestoml::from_strlike every other
config reader. -
Cold-cache guard shows the blocked draft on the TTY again.
suppressOriginalPromptstopped
Claude Code nestingOriginal prompt:into the warning, but also hid the draft entirely once
the input box was cleared — next-turnadditionalContextonly helped if you typed something
else. The block reason now carries the draft under our own label, so a cold session still shows
what was not sent; the disk stash + next-turn reinject path is unchanged.
llmtrim 0.11.8
Added
- Always-sub can skip Anthropic
/login. With globalsubinalwaysmode, llmtrim
injects a dummyANTHROPIC_AUTH_TOKENinto~/.claude/settings.json(same idea as
claude-code-proxy) so Claude Code does not need a live Anthropic OAuth session. The MITM
strips the dummy credential, injects the real Grok/Codex/Kimi token, and answers non-messages
Anthropic probes locally (with request-body drain so keep-alive stays healthy). Trade-off:
Claude Code disables claude.ai connectors while any API-key auth is set. Opt out with
llmtrim sub anthropic-login keep(connectors work again; Anthropic/loginrequired when
OAuth expires). Window/sub offunder skip-login returns a clear error instead of a bare
Anthropic 401.
Fixed
- Guard no longer echoes nested prompts and reinjects blocked drafts. Nested Claude Code
prompt echoes are suppressed; a blocked draft is reinjected so the user does not lose the
text they were about to send.
llmtrim 0.11.7
Fixed
-
Grok full-body ghost turns are classified and greppable. After the 0.11.6 transport-reset
hardening, some Grok sub turns still landed in the ledger as anonymousout=0/ zero usage rows
with nooutcomeand noserve.logtransport line: the stream had accepted (HTTP 2xx) then died
with only synthetic Anthropicmessage_startzeros (or nothing at all), andFinalizetreated
those zeros as a real completion.Finalizenow blanks all-zero / missing usage, tags
outcome=empty_stream(no client-visible content) oroutcome=incomplete_stream(content
without positive usage — client abort / truncate), and logs
llmtrim: stream <outcome> model=… sub=… session=…on stderr. On the reroute path, a quiet EOF
before any content also triggers one buffered replay when the body is still available — same
safety rule as thehandle_errorretry (never after the client has seen bytes). -
Grok reasoning history is replayed for prompt-cache continuity. cli-chat-proxy returns
Responsesreasoningitems (summary always;encrypted_contentwhen
include: ["reasoning.encrypted_content"]is set). llmtrim previously dropped Anthropic
thinking/redacted_thinkingblocks when building Grokinput[], which is the top
documented cause of cache misses on reasoning models. The reducer now tunnels Grok
encrypted_contentout as a marked thinking signature (same pattern as Codex), requests the
encrypted include on every turn, and rebuildsreasoningitems on the next request — encrypted
when the signature is ours, summary-only plaintext as a best-effort fallback for history that
never received a Grok blob. Also dual-sendsx-grok-conv-idfrom the Claude Code session id
alongsideprompt_cache_key. Still usesstore: false(noprevious_response_idcontinuation).
llmtrim 0.11.6
Added
-
Grok reroute pins
prompt_cache_keyto the Claude Code session id. Codex and Kimi already
set Responsesprompt_cache_keyfromx-claude-code-session-id; Grok did not, so multi-session
concurrency (and any server-side routing change) could dropcached_tokensto 0/128 on an
otherwise append-only conversation. cli-chat-proxy accepts the field (HTTP 200) and reports
input_tokens_details.cached_tokenson hits. -
Grok upstream usage capture under
LLMTRIM_CAPTURE_DIR. When the capture corpus is on, each
completed Grok turn also writes*-upstream-usage.jsonwith the rawresponse.completed.usage
object and the mapped ledger fields, so a cache-collapse audit can compare Grok's wire usage
against the Anthropic-shaped ledger without re-running the turn.
Fixed
- Upstream transport resets are retried and recorded instead of becoming silent zero-output rows.
Stale keep-alive connections to subscription backends (especially Grok) used to surface as hyper
SendRequest: connection resetand land in the ledger as anonymousout=0rows. The outbound
client now expires idle pool sockets after a short timeout (with hyper-util'spool_timerwired
so the timeout actually fires), retries once when the request body is still available to replay,
logs session/model on every transport failure, and recordsoutcome=transport_resetrather than
a silent zero-output row.
llmtrim 0.11.5
Added
llmtrim setup --envcreates CA files (if needed) and prints envvars When this option is
used, setup ensures the local CA (and, on POSIX, the combined OS-trust bundle) exist, then
prints an evaluatable snippet —exportlines on POSIX,$env:assignments on Windows — to
wire the interceptor into your current shell.
Fixed
-
The managed shell-profile block now single-quotes its values.
setupinterpolated the proxy
URL and CA paths into bare double quotes, so a home directory or CA path containing",$, a
backtick or a backslash produced a profile line that broke or expanded at shell start. Values
are now emitted as inert single-quoted literals on both the POSIX and PowerShell paths, matching
the quotingsetup --envalready used. Re-runllmtrim setupto rewrite an existing block. -
A legacy
subvalue keeps its formatting when migrated to a[sub]table. Coercing
sub = "codex"or an inlinesub = { … }emitted the header as[sub ], leaking the space
that sat before the old=, and dropped a trailing comment on the line. The header is now
clean and the comment moves to its own line above it. -
The config writers now preserve comments and key order, and no longer corrupt the file.
All three (compact.models,theme, and thesubreroute editors) go throughtoml_edit,
a format-preserving TOML editor, instead of hand-rolled line edits and a full re-serialize.
Three bugs go with it:compact.models = [...]written in TOML's dotted top-level form was
invisible to the writer, which appended a second[compact]section and left the key defined
twice so the file stopped parsing;llmtrim subre-serialized the whole document and silently
deleted every comment while reordering keys; andthemewas matched anywhere in the file
rather than only at the top level. A writer handed a file that doesn't parse now reports it
and leaves the file untouched, rather than editing it blind and producing plausible-looking
output from an already-broken config. -
compact.modelsno longer corrupts the config when it was written as a multi-line array.
The writer replaced the singlemodels = [line and left the array's continuation lines
behind, stranding"haiku",/]after the new value and producing aconfig.tomlthat
llmtrim could no longer parse. It now consumes the whole array. A file already damaged this
way still needs a manual edit: once the tail is orphaned nothing distinguishes it from real
config, so repairing it automatically would risk deleting keys.
llmtrim 0.11.4
Added
- Status line shows the active
/subplan's rate limits (Codex first). Under a Codex
reroute the◔segment now reads the ChatGPT plan's windows (weekly always; 5h when the plan
reports one) fromGET /backend-api/wham/usage, cached at~/.llmtrim/sub-rate-limits.jsonby
the proxy on each throttled turn. A matching snapshot replaces Claude Code's Anthropic blob
only while the proxy is healthy; until the first poll lands (or if the proxy is down) the
Claude numbers stay so the segment isn't blank. Kimi/Grok share the cache schema; their usage
endpoints are not wired yet.
Fixed
- Codex traffic is compressed and tracked again. Codex CLI 0.144+ sends its request bodies
withContent-Encoding: zstd, which the interceptor could not parse, so every Codex turn was
forwarded verbatim and never reached the ledger —llmtrim statusshowed no Codex activity at
all (#193). The interceptor now decodes zstd- and gzip-encoded request bodies (capped at
256 MB) before compression and attribution, and forwards the body as plain JSON. A body it
cannot decode still passes through verbatim, so a decode problem can never break the call. - Codex sessions are labeled
codexin the cost breakdown again. Two attribution gaps hid
them: the fingerprint markers predated Codex 0.144's system prompt (which opens with "You are
Codex" and no longer says "Codex CLI"), and identity extraction read only the first
system/developer item of a Responses body — Codex now leads with a non-message
additional_toolsitem, so the identity message behind it was skipped. The fingerprint gained
the current marker and identity extraction concatenates every system/developer message,
skipping tool registrations (whose schemas would also destabilize the session hash).
llmtrim 0.11.3
Fixed
/compactredirects to a cheaper model only when the prompt cache is cold. The redirect
used to run on every/compact. But/compactre-sends the conversation Claude Code has been
caching against your selected model, so while that cache is warm a cache-read there is cheaper
than a cold read on a smaller model, so redirecting a warm compact cost more, not less. The
redirect now stays on your selected model when the session's last turn is within the cache TTL.
Unknown freshness (no session row yet, or a build without thebreakdownfeature) still
redirects, so the feature is never silently disabled.
llmtrim 0.11.2
Fixed
- Window
/subsurvives Claude Code/clear(and/compact) without the env token.
Retention used to depend only onLLMTRIM_CLAUDE_WINDOW_TOKENsurviving into the next
SessionStarthook. Claude Code often drops that env on clear, so/sub on grokwas lost and
the next turn fell back to Anthropic. SessionEnd now keeps the live session map and a
short-livedclearedbackup; SessionStart reattaches via session map, that backup, or the
env token last (so a stale token from another window cannot override this session). /subskill bash no longer trips Claude Code permission checks. Session id is read from
process env (not shell-expanded on the skill line), andBash(<llmtrim> window-sub slash *)is
pre-approved via skillallowed-tools+settings.permissions.allow.ensuretreats a
missing/stale allow rule as stale so upgrades self-heal.
llmtrim 0.11.1
Changed
- Overview and non-TTY status dollars match Sessions (frozen proxy bills). KPI "You paid" /
"Total saved" / today / week / trend / top models, and the pipedstatushero, read
breakdown_turns.bill_microsplus a per-turn frozen-rate input counterfactual
(llmtrim-ledger::money) — not live re-pricing ofcompressions. Lifetime $ may drop vs
older builds when breakdown retention is shorter than compressions, or for CLI/MCP-only
traffic (no session bill). Token % gauge is unchanged. When there is no proxy bill yet, the
UI shows a billing notice (not$0.00). - Sessions columns:
saved→saved$(money) +trim(token size %). Detail bill
footer usesSUM(bill_micros)(blocks allocate;*when they residual). status --json/ MCPllmtrim_stats: additivemoneyobject withunavailable+
null dollars when there is no proxy attribution. Legacycostremains compressions +
live list prices with"source": "compressions_live_prices"— prefermoneywhen
unavailableis false.
Added
- Money coverage UX on Overview (and doctor empty-case only) when compressions exist but no
breakdown bills — banner instead of fake$0.00. llmtrim-ledger::money: sharedMoneyTotals/ coverage / per-day / per-model queries;
sharedsaved_microsSQL for Overview and Sessions.- Grok remote auth: paste the callback URL or xAI one-time code into
llmtrim sub auth grok login, or use RFC 8628llmtrim sub auth grok devicefor SSH/headless
hosts (no localhost callback).
llmtrim 0.11.0
Breaking
SubProvider/StreamReducergain aGrokvariant and are marked#[non_exhaustive]so
further backends can land without a major bump. External exhaustivematches need a_arm.
Workspace version is 0.11.0 (0.x minor = semver-breaking).
Added
llmtrim ensure: bring this machine to the recommended state. Installs or refreshes owned
Claude Code integrations (statusline, cold-cache guard, window-local/sub, default compact
model chain), tray login when the GUI binary is present, and restarts a version-skewed daemon.
Opt-outs live in~/.llmtrim/integrations.jsonso ensure does not re-enable after an explicit no.llmtrim doctor --fixand statusfrun the same ensure path.- Quiet auto-heal on
startand status open when the binary version advanced or owned pieces
look stale (no prompts; no network tray download; no first-time installs). - Linux desktop tray one-shot download during interactive ensure when the tray binary is
missing (optional; default off when non-interactive). - One-time subscription onboarding in the status TUI (
sfor login steps,nto dismiss)
when Claude Code is present andsubis not configured. - Subscription reroute: Grok (SuperGrok / Grok Build).
llmtrim sub auth grok loginsigns in
via OAuth againstauth.x.ai(browser PKCE);llmtrim sub use grokmaps Claude tiers to
grok-4.5(opus/sonnet/fable) andgrok-composer-2.5-fast(haiku) and routes Claude Code's
Anthropic/v1/messagestraffic tocli-chat-proxy.grok.com/v1/responses. Tokens live at
~/.llmtrim/grok/auth.json. Works withsub mode fallbackchains (codex,kimi,grok) and the
window/subcommand. Device-code login is not available yet (browser only). - Window
/sub on <provider>. The Claude Code slash command accepts an explicit backend:
/sub on codex,/sub on kimi, or/sub on grok. Bare/sub onstill re-enables the last
window provider or the globalsubsetting. - Status line shows window
/suboverrides immediately. Mid-session/sub on grok(or any
provider) now paints→grok-4.5even when earlier turns were Anthropic and globalsubis
off. Window overrides are treated as always-mode for the arrow (matching the proxy), and a
policy switch overrides a stale ledger backend until the new one serves a turn. - Window
/subno longer inherits another provider's tier map. With globalllmtrim sub on codexand a Claude Code/sub on grok, the proxy was already routing to Grok but still applied
[sub.codex.tiers](e.g.opus → gpt-5.6-terra) on the Grok request. Tiers are now loaded per
serving provider, and foreign model ids in overrides are ignored. After upgrading, run
llmtrim ensure(orfinstatus) so owned/subhooks refresh and a version-skewed
daemon restarts onto the new binary.
Changed
setupand binary-channelupdatecall ensure. First install wires
statusline/guard//sub/compact. Binaryupdaterestarts the daemon and runsensure -qon the
new binary (never in-process after a self-replace). Package-manager channels print the package
command then a singlellmtrim ensurefollow-up.- Owned Claude Code hooks and statusline rewrite in place when the binary path or payload
(e.g.refreshInterval) changes. Quiet auto-heal only refreshes stale owned pieces and daemon
skew; it never first-installs integrations or enables tray login. - Top-level help leads with
setup/update/ensure/doctor. Power-user
statusline/guard/compactstay available but are hidden from the main command list. - Opt-outs stick:
statusline/guard/window-sub/compact offwrite
integrations.jsonopt-outs; corrupt state refuses auto-apply instead of resetting them. - Safer hooks and tray install: shell-quoted hook paths, atomic
settings.jsonwrites,
sharedCLAUDE_CONFIG_DIR, arch-aware tray download via path-filtered tar into a temp dir,
then promote.