Compression where the agent actually is — and fidelity where it matters. A
shellMCP tool so the Codex Desktop/Cloud app compresses even without
lifecycle hooks, plus a self-diagnosing, additive shell allowlist so permitting
one command no longer means wiping out the defaults. Navigation output (map/
signatures) now carries line ranges so agents jump straight to a symbol, and
already-compact formats (TOON) pass through untouched instead of being
recompressed away. The proxy also speaks OpenAI's new Responses API.Supersedes 3.7.2: an automated release misfire published an incomplete 3.7.2 to
crates.io / npm before this work had landed, and those registries permanently
reject re-publishing a version — so 3.7.3 is the first clean release of this work
across every channel (crates.io, npm, GitHub, Homebrew).
Added
- OpenAI Responses API support in the proxy (#346, thanks @Lctrs): clients that moved to OpenAI's new Responses API (
POST /v1/responses) — opencode, the OpenAI Agents SDK — were forwarded untouched because the proxy only understood Chat Completions (messages). The proxy now compresses the Responses-API shape too: eachfunction_call_output.output(the Responses analogue of a role:"tool"message — a string, or aninput_textcontent array) is run through the same pattern pipeline as every other tool result. The conversationinputarray is intentionally left structurally intact (no history pruning) so afunction_callcan never be separated from its matchingfunction_call_outputand trigger a 400. Retrieve/cancel/delete sub-paths (/v1/responses/{id}/…) pass through cleanly, and/statusintrospection now reports an accurate token breakdown for Responses requests (instructions→ system prompt;inputitems → user/assistant/tool buckets;input_imagecounted). Chat Completions remains fully supported. shellMCP tool (#337): the instruction-only fix in 3.7.1 wasn't enough — the Codex Desktop / Cloud app loads the MCP server but its agent ignoresctx_shelland reaches for a nativeshell/Bashtool, so nothing compressed. lean-ctx now exposes ashelltool (familiar name + model-optimized description) that transparently delegates to the same 95+-pattern compression pipeline asctx_shell, giving the Desktop/Cloud app the compression the CLI gets via hooks. Registered for all MCP clients.lean-ctx allow <cmd>(#341): permit a binary on the shell allowlist additively through the newshell_allowlist_extraconfig field — so allowing e.g.aclikeepsgit/cargo/npm/… intact instead of replacing the whole built-in list.lean-ctx allow --listprints the effective allowlist plus the exact config path in use;--removereverts. Picked up on the next command — no MCP/daemon restart needed.- Line ranges in
map/signaturesoutput (#340, thanks @iohansson): every entity in the navigation-focusedmapandsignaturesviews now carries a compact@Lstart[-end]suffix (e.g.fn ⊛ build() → Config @L42-58), so an agent can jump straight to a symbol instead of issuing a follow-up search. Spans are populated consistently across the tree-sitter extractors (all languages, not just Kotlin) and the regex fallbacks (TS/JS, Rust, Python, Go, generic), with Vue/Svelte SFC spans shifted back to file-absolute lines. Mode-aware by design: the suffix is emitted only inmap/signatures(MCP + CLI) where locating code is the point — compression-first paths (aggressive,entropy, full-body reads, andctx_compress/ctx_outline/ctx_fill/ctx_analyze/repo-graph) stay byte-identical and pay zero extra tokens. Themap/signaturescompression caches are version-bumped so stale range-less entries are never served. - Format-aware passthrough for already-compact output (#342, thanks @pomazanbohdan):
ctx_shell/lean-ctx -cno longer recompress output that is already in a compact, token-oriented format. lean-ctx detects TOON (Token-Oriented Object Notation) by its structural markers — the tabularkey[N]{f1,f2}:header and the length-prefixedkey[N]:array header — and returns it verbatim, because a second pass saves little while rewriting the exact line/field shape an agent needs to validate a CLI output contract. The decision is output-shape based, not command based, so any tool emitting TOON is covered without enumerating it inexcluded_commands. Controlled by the newpreserve_compact_formatsconfig (default["toon"]; set to[]to disable) and surfaced as a "Compact-format passthrough" line inlean-ctx doctor. - Clearer path to the public leaderboard (community feedback): the
lean-ctx gainrecap now always shows a one-line, state-aware hint — how to publish toleanctx.com/metricswith--leaderboard, how to claim a display name (--name="…") instead of showing up as "anonymous", and how to--unpublish. Thegain --publishoutput likewise points a private-only publisher to the public board and nudges nameless leaderboard entries toward a handle, so getting on (and managing) the leaderboard is never a guess. - VS Code / Cursor extension, now publishable (community feedback): the editor extension is consolidated into a single, marketplace-ready package (
vscode-extension) and shipped to the VS Code Marketplace and Open VSX (Cursor, VSCodium, Windsurf) via a dedicated, tag-triggered CI workflow (publish-vscode.yml). It gains binary auto-detection (PATH /~/.cargo/bin/ Homebrew, for GUI editors with a stripped PATH),setup/doctor/gain/heatmap/ web-dashboard commands, one-click workspace MCP wiring, plus an Apache-2.0 license and PNG icon. The duplicate scaffold (packages/vscode-lean-ctx) was removed.
Fixed
- MCP stdio stays protocol-clean (#348): confirmed and regression-guarded that lean-ctx routes all
tracingdiagnostics to stderr — never the stdout JSON-RPC transport — so a log line can never be interleaved into an MCP client's message stream and break parsing. This has held since ≤3.7.1 (the transport writes only framed JSON-RPC, the auto-started proxy runs as a subprocess with stdout redirected tonull, and tool handlers return strings rather than printing); a source-level guard now fails the build if the logging writer is ever switched to stdout. shell_allowlistedits silently ignored in MCP/editor mode (#341): allowlist changes looked like no-ops whilelean-ctx -c(CLI, warn-only) still ran the command, due to three compounding traps. (1) A malformedconfig.tomlfell back to the defaults with the warning printed only to stderr — invisible over an MCP/stdio transport; the parse error is now surfaced in the block message and inlean-ctx doctor. (2) Settingshell_allowlistdirectly replaced the entire default list — the new additiveshell_allowlist_extra(written bylean-ctx allow) avoids that footgun. (3) The "not in allowlist" message now names the exact config path the runtime reads plus the precise additive fix, so a config-path/HOME mismatch between the editor's MCP process and your shell is immediately visible.lean-ctx doctorgains a "Shell allowlist" check (effective command count + parse status).- Codex instructions no longer claim Desktop "can't" run hooks (#350, thanks @iohansson): the block lean-ctx injected into
~/.codex/AGENTS.md(andLEAN-CTX.md) asserted as fact that "lifecycle hooks do not run" in the Codex Desktop/Cloud app — false (hooks do run there, trust-gated via/hookssince Codex 0.129.0) and traceable to a misread ofopenai/codex#13019, which is about completion notifications, not lifecycle hooks. A false absolute like that is exactly the kind of thing that confuses the agent. The instructions now make no surface-specific "hooks don't run" claim; they frame the lean-ctx MCP/CLI tools (ctx_shell/ctx_read/ctx_search, orlean-ctx -c) as the path that compresses reliably on every surface regardless of hook status, andlean-ctx doctor's Codex note is corrected to match. A regression test fails the build if the docs ever re-introduce a "hooks do not run" / "no automatic compression" absolute. - Proxy no longer mangles file/source reads (#351, external testing feedback): the request-compressing proxy treated every tool result as shell output, so a
Readof a large source file was run through command-output truncation (head/tail + "safety" lines) on the very next turn — gutting the file the model was mid-refactor on and forcing an uncounted re-read. The proxy now resolves each tool result's originating tool name (tool_use/tool_calls/function_call/GeminifunctionResponse.name) and never lossy-compresses a file read or content that heuristically looks like source code, across all four providers (Anthropic, OpenAI Chat, OpenAI Responses, Gemini). Shell/search/command output still compresses as before. History pruning is likewise code-aware: an older file read is replaced with an honest, actionable "re-read the file if you need it again" stub instead of a misleading 3-line excerpt. - Proxy stopped failing large-refactor and long-generation calls (#351, external testing feedback): the request-body ceiling was 10 MiB, so a big-codebase refactor with several files in context hit a hard
400mid-task — now64 MiBand configurable viaLEAN_CTX_PROXY_MAX_BODY_MB. A single 2-minute total request timeout also aborted long streaming generations (e.g. Opus doing a large refactor) mid-stream; it is replaced by a connect timeout plus a read (idle) timeout (LEAN_CTX_PROXY_CONNECT_TIMEOUT_SECS,LEAN_CTX_PROXY_READ_TIMEOUT_SECS, defaults 15s / 300s), so a slow-but-alive stream is never cut while a genuinely dead upstream still fails.
Changed
- Identifier α-substitution (
§MAP) is now opt-in (#351, external testing feedback):aggressivereads on large projects used to replace long identifiers with short α-codes plus a§MAP:decode table (symbol_map_auto, previously auto-on above 50 source files). A tester found the abbreviated form obscured package/symbol names exactly when editing. It is now off by default — setsymbol_map_auto = true(orLEAN_CTX_SYMBOL_MAP=1) to opt back in for maximum exploration savings. - Editing intents always read the full file (#351, external testing feedback): when the active task classifies as
refactor,fix-bug, orgenerate,auto-mode reads now resolve tofullregardless of model tier — you cannot safely edit a file you can only partially see, and an abbreviated/signaturesview just forced a follow-up read. Exploration/review intents still compress as before. - Per-model cost breakdown in the proxy (#351, external testing feedback):
/statusnow reports aper_modelarray (requests, estimated tokens saved, and USD saved priced from the shared model table) instead of a single flat number, and discloses that savings are request-side and do not subtract agent re-reads. Token figures remain explicitly labelled estimates.
Upgrade
lean-ctx update # recommended (auto-downloads + refreshes shell hooks)
cargo install lean-ctx # or
npm update -g lean-ctx-bin # or
brew upgrade lean-ctxNote: After upgrading via cargo/npm/brew, run
lean-ctx setupto refresh shell aliases.lean-ctx updatedoes this automatically.
Full Changelog: v3.7.3...v3.7.3