@oh-my-pi/pi-ai
Added
- Added
requestModelIdandthinking.suppressoptions togoogle-gemini-cliso collapsed effort-tier variants serialize their per-effort upstream wire id, and thinking-off requests on models withthinking.suppressWhenOffsend an explicitthinkingConfig(includeThoughts: falsewiththinkingLevel: "MINIMAL"orthinkingBudget: 0) — Cloud Code Assist re-applies the per-id baked server default when the config is omitted, silently thinking and billing the tokens - Added mandatory-reasoning clamping: models baked with
thinking.requiresEffortfloor omitted or disabled reasoning to the lowest supported effort in every api mapping, anddisableReasoningno longer emits OpenRouterreasoning: { enabled: false }for them — fixesomp benchand utility requests 400ing with "Reasoning is mandatory for this endpoint and cannot be disabled" on OpenRouter Gemini 3.x
Changed
- Changed
google-gemini-clirequest mapping to route per-request wire ids viaresolveWireModelId: the session effort picks the backing variant id (collapsedgemini-3.5-flashat high →gemini-3.5-flash-low; claude pairs route off → bare id, efforts →-thinking) whileAssistantMessage.modeland usage attribution stay on the logical id. A thinking budget clamped to zero now falls through to the thinking-off path (off routing plus suppression) instead of only disabling thinking - Changed
openai-completionsandanthropic-messagesto serialize per-request wire ids viaresolveWireModelId, so collapsedX/X-thinkingpairs on aggregators and custom providers switch to the thinking SKU when reasoning is enabled (previously onlygoogle-gemini-clirouted effort-tier variants)
Fixed
- Fixed
google-gemini-cliignoringModel.requestModelIdwhen serializing the request model id
@oh-my-pi/pi-catalog
Added
- Added effort-tier variant collapsing (
variant-collapse): providers that expose one logical model as several effort/thinking-suffixed upstream ids (Antigravity CCAgemini-3.5-flash-extra-low/-low/gemini-3-flash-agent,gemini-3[.1]-pro-low|high,claude-*[-thinking]pairs,gpt-oss-120b-medium) collapse into one logical entry carrying per-effort upstream routing inthinking.effortRouting(plusthinking.suppressWhenOfffor Cloud Code Assist ids whose baked server default re-applies whenthinkingConfigis omitted). Request-time code resolves the outbound id viaresolveWireModelId(model, effort); selection, caching, and usage attribution key on the logical id. - Added the automatic
X/X-thinkingpair rule (deriveThinkingPairFamilies): any provider's live bare/thinking twin collapses into the bare id, routing thinking-enabled requests to the-thinkingbacking id (trailing or infix token, sokimi-k2-thinking-turbopairs withkimi-k2-turbo). Gated on same api and compatible pricing — all-zero cost rows count as unknown, while twins that both carry real, differing prices remain separate SKUs. - Added
collapseBuiltModelVariantsand wired collapsing at every materialization point — Antigravity discovery, the catalog generator, and the model-manager merge — so stale sources (old static beside collapsed dynamic results, mixed cache rows) converge on logical entries instead of unioning raw tier ids back into the catalog. - Added
thinking.requiresEffort, baked for reasoning-only upstreams — Gemini 3.x (levels only, no off), Gemini 2.5 Pro (thinkingBudget floors at 128, rejects 0), OpenAI o-series, MiniMax M2, and thinking-variant SKUs (*-thinking/*-reasoner/*-reasoning, with a negation-aware token grammar sonon-thinkingids never match). Identity derivation bakes it for new entries andfillThinkingWireDefaultsbackfills explicit/cached metadata;minimumSupportedEffortexposes the canonical floor. Pair-collapsed twins drop member flags (their off routes to the bare SKU), while identity re-flags pairs whose logical id is itself mandatory
Changed
- Changed model display names to drop model-extrinsic decorations: gateway author prefixes (
OpenAI: …,Google: …),(latest)alias markers,(Antigravity)provider attribution, price tiers (($$$$)), and promo/lifecycle tags ((20% off),(retires …)).cleanModelNameis applied inbuildModel(covers live discovery and stale caches) and as a catalog-generator pass; Antigravity discovery no longer appends(Antigravity)to display names. Variant tags that map to distinct wire ids ((Thinking),(free),(Fast), dates, regions) are preserved. - Changed the
google-antigravitydefault model fromgemini-3-pro-hightogemini-3.1-pro - Changed
gemini-2.5-flash-thinkinghandling from discovery-denylist to collapsing intogemini-2.5-flash(thinking-enabled requests route to the-thinkingbacking id) - Bumped the model cache schema to v5 so rows predating effort-tier variant collapsing (raw
-low/-high/-thinkingmember ids) are invalidated
Fixed
- Fixed catalog generation to apply effort-tier variant collapsing before provider grouping to ensure collapsed model families are consistently materialized without being impacted by in-loop mutation
- Fixed Kimi K2.6 OpenAI-compatible compat metadata to use a 300s stream watchdog floor, covering Fire Pass router ids as well as public
kimi-k2.6ids so long reasoning starts do not hit the generic first-event timeout (#2366).
@oh-my-pi/pi-coding-agent
Added
- Added mouse support to the setup wizard, including hover, wheel scrolling, and click-to-select for provider sign-in, web-search provider, glyph, and theme option lists
- Added pointer support for the providers tab bar so tabs can be selected by click and wheel scrolling works inside the active panel
- Added left-click navigation in setup wizard splash/outro screens to advance or exit without pressing Enter
- Added
benchCLI command to benchmark model selectors with a shared prompt and report time-to-first-token and throughput - Added
omp benchoptions--runs,--max-tokens,--prompt, and--jsonto control run count, response length, prompt text, and machine-readable output - Added benchmark failure reporting that shows per-run errors, flags failed runs in the summary, and exits with a non-zero status when any model benchmark fails
- Added
snapcompact.shapesetting to pick the frame variant snapcompact prints text with —auto(each provider's eval winner) or any research-eval variant: the square grids (8x8r/8x8u/6x6u/5x8× sentence-hue/black ink) and the per-model winners (6x12-dim,8x13-bw,8on16-bw,doc-8on16-bw,doc-8on16-sent,doc-8on16-sent-dim); applies to both the compaction archive and inline system-prompt/tool-result imaging
Changed
- Updated
snapcompact.shapeoption help text so theautomode and8on16-bwdescriptions now reflect OpenAI’s current default and GPT winner variants - Model selectors keyed by retired effort-tier variant ids keep resolving after catalog collapsing: reference resolution and bare-id matching consult the hand-table aliases (
gemini-3.5-flash-low,gemini-pro-agent, recycledgemini-3-flash) plus theX-thinking→Xgrammar for auto-collapsed pairs, with exact matches always winning while a raw id is live; explicit:effortsuffixes transfer unchanged. models.ymlmodelOverridesand rate-limit selector suppressions keyed by raw member ids re-key onto the collapsed model - Custom/config provider model lists now collapse
X/X-thinkingtwins at registry rebuild (collapseBuiltModelVariants), folding config-defined twins into one entry whose thinking toggle routes to the-thinkingbacking id
Fixed
- Fixed Ctrl+T thinking-block toggles clearing the pending user message and loader before the assistant stream starts (#2370).
- Fixed Windows CodeGraph MCP launches from npm
.cmdshims by resolving the shim's Node entry point and spawning it directly, keeping stdio attached to CodeGraph instead of the transientcmd.exewrapper (#2367). - Fixed snapcompact archives going partially unreadable on OpenRouter, which hard-caps requests at 8 images and silently drops the excess: compaction now passes a provider-aware frame budget (
providerFrameBudget) so the archive never exceeds the cap (unknown providers get a safe floor of 5), with overflow kept as a text tail on the summary instead of rendering frames the gateway would drop; inline system-prompt/tool-result imaging shares the same per-provider budget (providerImageBudget), so once existing images exhaust it (e.g. 8 archive frames on OpenRouter) tool results ship verbatim as text - Fixed Hindsight retains to send offset-aware local timestamps instead of UTC
Zstrings so extraction prompts keep the user's local time-of-day context (#2363). - Fixed tool calls taller than the viewport reading as cut off while streaming (the head reappeared only once the result landed): the 15.11.6 stranded-preview fix marked every collapsed pending tool preview commit-unstable, which also blocked durable top-anchored streams — e.g. a task call's context/assignment markdown — from reaching native scrollback mid-run. Commit stability is now classified per renderer (
ToolRenderer.provisionalPendingPreview): only the tail-window previews the result render re-anchors (edit/apply_patch streamed-diff tails, bash/ssh command caps, eval cells with interleaved outputs) stay provisional; every other pending preview commits its settled head mid-stream again - Fixed
omp benchreporting "tokens 0, TPS 0.0" successes on repeated OpenRouter runs: pi-ai opts every OpenRouter request into response caching, so bench's byte-identical request replayed a cached generation with zeroed usage at edge speed. Bench now sendsX-OpenRouter-Cache: falseso every run measures a fresh generation - Fixed
omp benchfailing with HTTP 400{"detail":"Instructions are required"}againstopenai-codexmodels: bench requests now carry a minimal default system prompt (same guard as eval's completion bridge) - Fixed pressing
Ctrl+T(toggle thinking blocks) — or hitting any other rebuild path such as the theme/preset selector — during the pre-streaming window after a submission erasing the just-submitted user message until the first assistant token arrived: the user message is rendered optimistically beforesession.prompt(...)lands it in session entries, sorebuildChatFromMessages()had no record of it; it now replays an in-flight optimistic submission after rebuilding the transcript, gated onoptimisticUserMessageSignature(cleared byEventControlleronce the realmessage_startlands) so it cannot duplicate post-streaming (#2372).
@oh-my-pi/pi-natives
Added
- Added the X.org misc
6x12and8x13BDF fonts (public domain, vendored incrates/pi-natives/src/fonts/) torenderSnapcompactPng, alongside two new options for the snapcompact eval-winner shapes:stretch: falserenders glyphs at natural size on the requested cell box while keeping the 4-bit indexed encoder (e.g. 8x13 glyphs on an 8x16 pitch, the "8on16" shapes), andcolumns: 2flows pre-wrapped newline-separated lines down two newspaper columns with a 3-cell gutter (the "doc" shapes); in doc mode sentence hues also advance across a terminator followed by a newline - Added a line-break marker to
renderSnapcompactPng:U+2588(FULL BLOCK) fills its entire cell box with pitch-black ink in both grid and doc layouts, ignoring the sentence hue and dim state, and counts as a sentence boundary after a./!/?terminator
Changed
renderSnapcompactPngnow clips the frame height to the text: the PNG stayssizepixels wide but is onlyusedRows * lineRepeat * cellHeighttall (dim toggles are zero-width; doc layout counts\n-separated lines), so a partially filled frame no longer pads to a full square of blank rowsrenderSnapcompactPngindexed frames now narrow the palette to the colors actually printed and pick the matching bit depth (plainbw1-bit, dim/banded 2-bit, sentence hues up to 4-bit), and both encode paths moved fromBalancedtoHighdeflate:8on16-bwframes shrink ~35%,6x12-dim~10%, sentence-hue doc frames ~9% — pure PNG, no decoder-side changes (lossless WebP was measured at only ~8% beyond this and rejected for provider-compatibility risk)
@oh-my-pi/snapcompact
Added
- Added
SHAPE_VARIANTS, the catalog of research-eval frame variants the native renderer reproduces faithfully (8x8r/8x8u/6x6u/5x8×sent/bw), withShapeVariantName,SHAPE_VARIANT_NAMES, and theisShapeVariantNameguard resolveShape(api, variant?)now accepts an explicit variant name (or"auto"); forced variants keep their geometry but are re-priced for the target provider's image billing (token estimate and OpenAIoriginaldetail hint)- Added the six research-eval winning frame variants to
SHAPE_VARIANTS:6x12-dim(Claude fable),8x13-bw(Opus),8on16-bw(GPT grid runner-up),doc-8on16-bw(GPT),doc-8on16-sent(GLM), anddoc-8on16-sent-dim(Gemini/Kimi), backed by newShapefieldsstretch(disable Lanczos stretch: natural glyphs on a larger cell pitch),columns(two word-wrapped newspaper columns),stopwordDim, and the X.org6x12/8x13fonts - Added
dimStopwords(), which prints high-frequency function words in dim ink via zero-width markers (skipping spans that are already dim), andwrap(), the greedy word-wrap used to typeset doc-layout pages;geometry/render/renderMany/frames/compactunderstand doc shapes (wrap once, paginate into2 * rows-line pages), and compaction frames persistcolumns/stopwordDimfor mixed-shape detection resolveShapenow takes aShapeTarget({ api, id }— a pi-aiModelworks as-is) and detects the ideal shape from the model id, not just the wire API: a Claude routed through Vertex or an OpenAI-compatible gateway keeps its Claude shape, with billing still priced by the API family actually carrying the request.idealShapeVariant(modelId)exposes the model-line table; unmeasured models fall back to the API family's winnerresolveShapenow also resolves an ideal frame size per model line, and billing estimates come from verified per-family formulas instead of flat 1568px constants: Anthropic bills 28px patches capped at 4,784 visual tokens (+5% margin), Gemini 3.x bills a fixed 1,120-tokenmedia_resolutionbudget per image at any pixel size, and OpenAI bills 32px patches × 1.2 under the 10,000-patchdetail: "original"budget. High-res Claude lines (Opus 4.7+, Fable, Mythos — native 2576px-edge ingestion) get 1932px frames (same recall and cost, a third fewer frames); Gemini gets 2048px frames (+70% chars per frame at the same bill); GPT and Kimi stay at 1568px (area-proportional billing and a model-side 1792px processor cap, respectively).idealShapeVariantnow returns anIdealShape({ variant, frameSize? })- Added per-provider image-count budgets:
PROVIDER_IMAGE_BUDGETS,DEFAULT_PROVIDER_IMAGE_BUDGET,providerImageBudget(), andproviderFrameBudget()(the image budget clamped toMAX_FRAMES). OpenRouter is capped at its measured hard limit of 8 images per request (excess images are silently dropped with no error); unknown providers get a safe floor of 5 - Added
Archive.textTail: archive content past the frame budget is no longer dropped —compact()stops rendering at the budget and keeps the newest unframed slice as verbatim text on the summary (capped at two frame capacities with middle elision, counted intotruncatedCharswhen elided). The tail persists inpreserveDataand is folded back into frames by the next compaction
Changed
- Frames are no longer padded to a square: the native renderer clips each PNG's height to the text rows actually printed, so a partially filled frame (typically the newest) bills only the pixel rows it uses
- Changed the OpenAI default shape from
6x6u-sentto8on16-bw. A production-regime mono eval (gpt-5.5, the full 800k-char SQuAD flow in one request, n=50) scored the old dense default f1 .602 vs .851 for8on16-bwrendered by the production pipeline, at near-equal total cost (the dense cells burned the frame savings on reasoning tokens); chunked exp14 had already scored8on16-bw.906.SHAPES.openaiDenseis renamed toSHAPES.openai - Changed the Google default shape from
8x8r-senttodoc-8on16-sent-dim. Production-rendered mono eval on gemini-3.5-flash (400k chars, one request, n=25): f1 .900 vs .853 for the repeated grid at lower cost, agreeing with the chunked round-2 winner - Changed the Anthropic default shape from
8x8r-bwto6x12-dim. Production mono eval on claude-fable (400k chars, one request, n=25): f1 .840 vs .877 for the repeated grid — within noise — at 37% lower cost (12 frames instead of 21 per 400k chars), with clean completions in every probe; opus reads the same trade (.800 vs .833 at 42% lower cost) normalize()now keeps line structure: whitespace runs containing a line break collapse toNEWLINE_GLYPH(U+2588 FULL BLOCK, drawn by the native renderer as a pitch-black cell one character wide) instead of a plain space; leading/trailing breaks are trimmed, and the frame-reading prompt explains the markernormalize()now skips characters the fonts cannot render instead of printing?blanks: whole ANSI escape sequences are stripped, and bare control characters, zero-width format characters (ZWSP, BOM, directional marks), combining marks, and lone surrogates are dropped without occupying a cell;?remains the fallback for unsupported graphic characters only
What's Changed
- fix(hindsight): preserve local timezone on retain timestamps by @roboomp in #2364
- fix(catalog): widen Kimi K2.6 stream watchdog by @roboomp in #2368
- fix(mcp): launch Windows npm cmd shims directly by @roboomp in #2369
- fix(tui): preserve pending message on thinking toggle by @roboomp in #2371
- fix(tui): replay optimistic user message on chat rebuild by @roboomp in #2373
Full Changelog: v15.11.6...v15.11.7