github can1357/oh-my-pi v15.9.0

11 hours ago

@oh-my-pi/pi-ai

Fixed

  • Fixed MiniMax-compatible OpenAI-completions hosts (e.g. minimax-code-cn/MiniMax-M3) losing tool-call arguments when the stream delivers function.arguments as a complete object instead of the OpenAI JSON-string contract. The streaming buffer previously concatenated the object into a string, coercing it to [object Object] and leaving bash/edit calls with empty or malformed inputs; the tool-call block now holds the object payload directly. (#1776)
  • Fixed Cloud Code Assist (Gemini / Antigravity) rejecting tool schemas with Invalid JSON payload received. Unknown name "propertyNames" (HTTP 400) when a tool exposed a property literally named properties (e.g. the Resend MCP create_contact tool). The schema normalizer's insideProperties flag was re-asserted when descending into such a property's value schema, so Google-unsupported keywords (propertyNames, additionalProperties, …) nested inside it were never stripped. The flag is now only set when entering a real properties map from a schema node, not from within another properties map.
  • Fixed local/self-hosted providers leaking machine-specific endpoints into the bundled models.json. A generate-models run on a machine with a LiteLLM proxy baked 1202 litellm models pinned to http://localhost:4000/v1 into the committed catalog. litellm (and lm-studio) now join ollama/vllm in the generator's discovery-only exclusion set, so local providers are never fetched during generation nor written to models.json — they are discovered dynamically at runtime instead. LiteLLM model discovery now enriches metadata against models.dev (the same reference source the other gateway providers use) rather than a bundled reference map. Added a regression test pinning the invariant (no local provider blocks, no loopback/private-network baseUrls in the bundled catalog).

@oh-my-pi/pi-coding-agent

Breaking Changes

  • Removed synchronous readTextSync from SessionStorage and core implementations (MemorySessionStorage, FileSessionStorage, RedisSessionStorage, SqlSessionStorage), requiring callers to use async text reads
  • Replaced the public SessionStorage readTextPrefix(path, maxBytes) and readTextSuffix(path, maxBytes) methods with readTextSlices(path, prefixBytes, suffixBytes): Promise<[string, string]>; custom session storage backends must implement the new combined slice API.

Added

  • Added env-driven OpenTelemetry trace export. When OTEL_EXPORTER_OTLP_ENDPOINT (or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) is set, omp registers a global OTLP/proto trace exporter and switches on the agent loop's telemetry, so the invoke_agent / chat / execute_tool spans actually reach a collector instead of a no-op tracer. Honors the standard OTEL_* env contract (endpoint, headers, OTEL_SERVICE_NAME, OTEL_SDK_DISABLED and OTEL_TRACES_EXPORTER=none parsed case-insensitively) and the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT capture toggle; it is a no-op when no endpoint is configured. Only the http/protobuf transport is supported — a grpc or http/json OTEL_EXPORTER_OTLP*_PROTOCOL declines rather than misrouting spans. This makes the existing telemetry usable from headless hosts that run omp as a spawned child process, where an in-process TracerProvider registered by the parent can't reach the child. Uses the @opentelemetry/exporter-trace-otlp-proto 2.x line, which exports cleanly under Bun.

Fixed

  • Fixed the status line session name (and the editor border / status-line gap fill) being nearly illegible on light themes.
  • Added IndexedSessionStorage and SessionStorageBackend exports to support shared metadata-indexed session backends
  • Added the tui.maxInlineImages setting (default 8) capping how many inline images render as live terminal graphics. Once a new image pushes the count past the cap, the oldest images are hidden via a full redraw — replaced by their [Image: …] text placeholder and purged from the terminal's graphics store — so long sessions with many screenshots/diagrams stop piling up images (and, on Kitty, stop leaving scrollback ghosts). Set to 0 to keep every image inline.
  • Added a "View: terminal state" item to the /debug menu that prints the detected terminal, live geometry and cell size, multiplexer, and the negotiated subprotocols actually in use — graphics (Kitty/iTerm2/Sixel), desktop notifications (BEL/OSC 9/OSC 99, plus whether OSC 99 was confirmed via a device-attributes probe), OSC 8 hyperlinks, 24-bit color, DECCARA rectangular-SGR background fills, and DEC 2026 synchronized output — alongside the scrollback-clear strategy (CSI 22 J vs CSI 2 J redraw / ED3 eager-erase risk) and the raw TERM/TERM_PROGRAM/COLORTERM detection signals.
  • Added a "Test: terminal protocols" item to the /debug menu that renders one live sample of every special escape protocol the renderer can emit — SGR text attributes (bold/italic/underline/strikethrough/inverse/dim), themed and 24-bit truecolor, OSC 8 hyperlinks, OSC 66 text sizing (large text), and an inline graphics swatch via the active image protocol (Kitty/iTerm2/Sixel, with a text fallback) — and fires a desktop notification, so you can eyeball which protocols the current terminal actually honors. The sample image is a gradient PNG generated in-process, so the graphics test needs no asset on disk.
  • Added the tui.textSizing setting (default off) that renders Markdown H1 headings at 2x scale via Kitty's OSC 66 text-sizing protocol. It replaces the undocumented PI_TUI_TEXT_SIZING env var with a real setting, and only takes effect on Kitty terminals (where OSC 66 is implemented) — it is ignored everywhere else so headings never emit raw escape bytes.
  • Added a lifecycle status to the /resume session picker. Each session's tail (last 32 KiB) is now read alongside the existing header window in a single pass, and its final message classified as done (the agent ended its turn and yielded control back), interrupted (a trailing tool call or tool result the loop never continued from), aborted, error, or pending (a trailing user message with no reply). The status renders as a colored segment on each session's metadata line. When the final message is larger than the tail window the status is omitted rather than guessed.
  • Added support for disable-model-invocation: true frontmatter field from the Agent Skills standard. Skills using this field are now hidden from the system prompt listing, matching the behavior of hide: true.

Changed

  • Changed the task tool description to tag read-only agents and explicitly forbid assigning them file edits/commands or offloading reasoning to quick_task/explore.
  • Changed Redis and SQL session storage initialization to load only indexed metadata (size, mtimeMs) instead of full session content
  • Changed SessionStorage read paths to rely on backend-backed metadata/indexed storage, so session content is fetched on demand rather than cached as full in-memory mirrors
  • Changed session-list slice reads to go through SessionStorage.readTextSlices across all backends, removing the file-only single-open branch and caller-managed buffers. FileSessionStorage now reads both windows via peekFileEnds, while Redis and SQL backends encode session content once per combined read.
  • Changed the ask tool transcript renderer to mark single-choice questions with circular radio glyphs (/) instead of the rectangular checkbox glyphs (/) it shares with multi-select questions, so a "pick one" combo box visually reads as a radio group rather than a checklist. Multi-select questions keep checkboxes. Added a radio.selected/radio.unselected symbol pair across the unicode, nerd-font, and ASCII presets.
  • Changed the ask tool transcript renderer to mark the chosen answer inside the question form rather than re-listing the questions in a detached summary block below it. Once a question is answered, the standalone prompt preview is dropped and the result redraws the same form — every offered option still shown, with the selected one(s) filled in (/, highlighted) and the rest dimmed (/); custom free-text answers and cancellations render in place as the final entry. This removes the duplicate question/option listing that previously appeared once as the call preview and again as the result.
  • Changed task-completion and ask desktop notifications to structured terminal notifications (title, body, type, and a focus-on-click action). On Kitty these render through OSC 99 as a proper title/body with click-to-focus; terminals without confirmed OSC 99 support collapse them to the previous single-line message (BEL/OSC 9).
  • Updated the "each kitty/tmux split" tip to include cmux.

Fixed

  • Fixed tiny-model startup in compiled binaries by resolving @huggingface/transformers and its runtime dependencies from the installed cache using package.json exports/main metadata, preventing module-resolution failures when launching models
  • Fixed tiny runtime installation flow in compiled binaries by using the build-time resolved @huggingface/transformers version and ensuring the runtime lock directory’s parent exists before acquiring the install lock, preventing mismatch and setup failures on fresh installs
  • Fixed the terminal protocol debug probe reusing one stable Kitty graphics id across repeated panels, which could move/replace an earlier swatch instead of rendering a new one.
  • Fixed selector dialogs (the ask tool, hook prompts) collapsing to a single visible option on shorter terminals when options carried long descriptions: the highlighted option's wrapped description consumed the entire row budget, hiding every other option and making the menu feel unnavigable (down moved the lone visible entry, left/right did nothing). When the fully-expanded list overflows, HookSelectorComponent now renders a compact list — every option label stays on screen and only the highlighted option expands its description, truncated to the remaining rows — so the whole menu is always visible and the detail pane follows the cursor.
  • Fixed read failing with "Path not found" on web URLs whose scheme // collapsed to a single / (e.g. https:/github.com/...), which happens when a URL is routed through Node's path.normalize/path.resolve. The fetch URL recognizer now accepts a single-slash scheme and repairs it back to // before fetching, so collapsed URLs resolve instead of falling through to filesystem lookup.
  • Fixed subagent slow-model priority falling through to older Claude Opus aliases when Opus 4.8 is available by adding Opus 4.8 and 4.7 aliases ahead of older Opus fallbacks (#1753).
  • Fixed the web-search provider selectors in TUI settings/setup to derive from the shared provider metadata, so newly added providers cannot be omitted from the preference list.

@oh-my-pi/pi-natives

Fixed

  • Bounded sorted glob() scans to maxResults during uncached traversal and emitted onMatch callbacks only for entries admitted to the bounded top-maxResults heap so broad OMP find progress and timeout partials stay consistent with the returned mtime-ranked set while keeping parent-process memory bounded (#1761).
  • Fixed wrapTextWithAnsi hanging (infinite loop) on text containing a BEL-terminated string escape — DCS/SOS/PM/APC (ESC P/ESC X/ESC ^/ESC _) closed by BEL instead of ST. ansi_seq_len_u16 only accepted the ST (ESC \) terminator for these (OSC already accepted both), so a BEL-terminated APC such as the TUI cursor marker (ESC _ pi:c BEL) was left unclassified: it was miscounted as visible width and break_long_word's non-ESC scan could not advance past the ESC, spinning forever. The terminator set now matches OSC (ST or BEL), and break_long_word defensively emits and steps over any escape it cannot classify so a malformed/unknown sequence can never wedge the wrap loop.

@oh-my-pi/swarm-extension

Fixed

  • Fixed swarm /swarm run failing with authStorage/modelRegistry identity error (#1472)

@oh-my-pi/pi-tui

Added

  • Added Kitty CSI 22 J screen-to-scrollback clears for non-destructive full paints, while keeping ED3 for destructive history/session rebuilds.
  • Added Kitty OSC 99 rich notification formatting and startup capability probing.
  • Added Kitty OSC 66 text-sized Markdown H1 headings (2x scale) plus native text-width support for OSC 66 spans. Off by default and gated to Kitty (the only terminal implementing OSC 66) via the TERMINAL.textSizing capability; hosts enable it through setTextSizing.
  • Added Kitty Unicode placeholder image rendering (U=1 + U+10EEEE with explicit row/column diacritics): inline images are drawn as real text cells that carry the image id in their foreground color, so they survive horizontal slicing, reflow, and overlapping draws instead of relying on cursor-positioned a=p placements. Enabled by default on Kitty-family terminals; opt out with PI_NO_KITTY_PLACEHOLDERS=1, and falls back to direct placement when a grid exceeds the diacritic table's addressable range.
  • Added Kitty temp-file image transmission (t=t): on local sessions, decoded PNG bytes are written to a tty-graphics-protocol temp file and the path is sent instead of in-band base64, gated behind a startup a=q,t=t support probe. Controlled by PI_KITTY_IMAGE_TRANSMISSION=direct|temp-file|auto; disabled over SSH unless explicitly forced.
  • Added DECRQM capability detection for DEC private modes 2026 (synchronized output) and 2048 (in-band resize). Synchronized-output paint wrappers are dropped when the terminal reports 2026 unsupported (preserving the PI_NO_SYNC_OUTPUT override), and DEC 2048 in-band resize is enabled when supported — reported geometry and cell pixel size are updated from CSI 48 ; rows ; cols ; yPx ; xPx t reports, with SIGWINCH and CSI 16 t kept as fallbacks.
  • Added an injectable render scheduler for TUI tests, allowing deterministic render drains without patching global clocks or event-loop timing.
  • Added ImageBudget, an inline-image cap that keeps only the most recent N images as live terminal graphics and demotes older ones to their text fallback. Once a new image pushes the count past the cap, the renderer hides the oldest via a full redraw plus an explicit Kitty graphics purge (a=d,d=I) — text-clear escapes (CSI 2 J/CSI 3 J) do not remove Kitty images. Configure the cap via TUI#setMaxInlineImages (0 disables it).
  • Changed Kitty inline images to a transmit-once + placement scheme: the base64 data is sent a single time (a=t) keyed by a stable image id, then every repaint emits only the tiny placement (a=p,i=…,p=…). Repaints — including full redraws — no longer re-send image data or stack duplicate placements, and the diff/line buffers and render caches hold short placement strings instead of multi-KB base64. The ImageBudget doubles as the transmit store (it tracks which ids are loaded and re-transmits after a purge frees the data). iTerm2/Sixel, which have no addressable image store, keep sending inline data as before.
  • Added a renderer-level DECCARA rectangular-SGR optimizer that paints solid background panels/rows (Box/Text/Markdown fills, status bars, any full-width theme.bg row) as a single coalesced rectangle escape (CSI 2*x / CSI Pt;Pl;Pb;Pr;<sgr>$r / CSI *x) instead of emitting a full-width run of background-styled spaces on every visible row. It operates at emit time on the final ANSI strings — components are unchanged — and strips only trailing padding it can prove sits under a single non-default background span, coalescing vertically adjacent identical fills into one rectangle and falling back to the original bytes whenever the rectangle would not save bytes. Enabled only on Kitty, which implements the SGR-background extension (docs/deccara.rst); Ghostty is intentionally excluded because its CSI $r is unimplemented (ghostty-org/ghostty#632) and would drop the background entirely. Scrollback-bound rows and the append/scroll paths always keep the padded representation so native history preserves colored cells, and the PI_NO_DECCARA kill switch (plus tmux/screen/zellij detection) forces the fallback.
  • Added CMUX_SURFACE_ID environment variable support to getTerminalId(), so cmux terminal surfaces get a stable identifier alongside kitty, tmux, macOS Terminal.app, and Windows Terminal — enabling per-surface session breadcrumbs for omp -c in cmux.

Changed

  • Changed TUI tests to use Ghostty's VT engine (ghostty-web) instead of @xterm/headless.
  • Changed the default inline-image live graphics budget from 3 to 8 images.

Fixed

  • Fixed the DECCARA background-fill optimizer rejecting or repainting the wrong cells when a trailing fill crossed from default-background spaces into colored spaces.

  • Fixed DEC private-mode reports with DECRPM status 3/4 being treated as unsupported, so permanent 2026/2048 reports stay recognized.

  • Fixed OSC 66 text-sizing width and slicing edge cases, including ZWJ emoji payloads and partial slices through scaled spans.

  • Fixed focused Input components following TUI#setShowHardwareCursor, so single-line prompts render either the terminal cursor or software cursor consistently with the editor.

  • Fixed the DECCARA background-fill optimizer painting fills on the wrong rows ("split into unaligned halves") in the differential repaint path. When a diff grew the transcript past the viewport, writing the rewritten rows scrolled the terminal, but the absolute DECCARA rectangle coordinates were derived from the pre-scroll viewport top, so every fill landed scrollAmount rows too low while the relatively-positioned text settled correctly; rows scrolled into history were also shortened, dropping their background padding from native scrollback. Rectangles now target the post-scroll rows and only rows remaining in the final viewport are optimized.

  • Fixed native scrollback desynchronization after terminal width or height changes reflowed overflowing content while the viewport was not at the bottom

  • Fixed a notification chip (or any injected block) rendering on top of an actively streaming tool render on ED3-risk terminals (Ghostty/kitty/Alacritty/iTerm2). While a foreground tool streams, its header's elapsed-time counter ticks every frame; once output scrolls the header above the viewport top, each tick is an offscreen edit that — because the eager scrollback-rebuild opt-in is gated off on these terminals — repaints the viewport in place and advances the rendered line count without committing the new overflow to native history. #scrollbackHighWater then lagged the logical viewport top, so a later content shrink whose changes landed in the visible region slipped past the shrink-across-boundary guard and reached the differential emitter, which is anchored to #maxLinesRendered - height: it rewrote only the suffix, dropped the newly exposed top row, and left a blank at the bottom, drifting every row below the edit one line up so it painted over the rows above. Such shrinks now re-anchor the bottom of the viewport with a non-destructive repaint, and the foreground-streaming shrink-across-boundary case repaints the live tail instead of padding and pinning the pre-shrink viewport.

  • Fixed a terminal resize during foreground-tool streaming on an unknown-viewport / ED3-risk host (Ghostty/kitty/Alacritty/iTerm2/WSL) leaving native scrollback permanently out of sync, so scrolling back after the turn showed missing rows. A pure geometry resize (no content change) takes the in-place viewport-repaint path, which — unlike a content-bearing resize that rebuilds via the geometry branch — never flagged native history. Because the prompt-submit checkpoint (refreshNativeScrollbackIfDirty) only rebuilds when scrollback is marked dirty on these hosts, the discrepancy was never reconciled. Overflowing geometry repaints whose viewport is not known to be at the bottom now mark scrollback dirty so the next checkpoint rebuilds an exact copy of the transcript.

@oh-my-pi/pi-utils

Added

  • Added color helpers colorLuma (perceptual luma), relativeLuminance (WCAG, linearized sRGB), and hslToHex to the color utilities. The luminance helpers parse #rgb/#rrggbb hex and 256-color palette indices, returning undefined for unparseable values.

  • Added peekFileEnds, a single-open head-and-tail file peek helper that reuses the head bytes for the tail when the file fits the head window.

  • Added peekFileTail, the tail mirror of peekFile: reads up to the last maxBytes of a file ending at EOF, reusing the same pooled-buffer strategy (no per-call allocation for small reads).

What's Changed

  • fix(search): default paths to workspace root instead of hard-failing by @GratefulDave in #1808
  • fix: recognize disable-model-invocation from Agent Skills spec by @fabkho in #1803
  • fix(coding-agent/mcp): handle async broken-pipe rejections in stdio transport by @VoidChecksum in #1783
  • Fix slow agent Opus priority by @daandden in #1754
  • fix(swarm): remove redundant authStorage discovery from swarm pipeline (#1472) by @WodenJay in #1726
  • Fix web search provider TUI options by @daandden in #1685
  • Add cmux terminal surface detection to getTerminalId by @basedcorp99 in #1702
  • fix(natives): bound sorted glob scans by @roboomp in #1762
  • fix(tui): cap session accent luminance on light themes by @paweljw in #1715
  • feat(coding-agent): env-driven OTLP trace export for headless hosts by @cgreeno in #1797

New Contributors

Full Changelog: v15.8.3...v15.9.0

Don't miss a new oh-my-pi release

NewReleases is sending notifications on new releases.