Skip to content

v16.1.17

Choose a tag to compare

@github-actions github-actions released this 24 Jun 19:36
· 62 commits to main since this release
8d60776

@oh-my-pi/pi-agent-core

Fixed

  • Hardened the agent-loop cooperative yield against backward wall-clock jumps. A stale future timestamp left in the shared yield gate (NTP step, or a fake-timer test mocking Date.now) could make yieldIfDue() gate forever and stop yielding to the event loop; the gate now treats a backward clock delta as due and re-anchors. The gate is exposed as an injectable YieldGate (with yieldIfDue() retained as the shared singleton) so it can be exercised without mocking process-global timers.

@oh-my-pi/pi-ai

Added

  • Added provider-level notes?: string[] field to UsageReport for disclaimers that apply to every limit (e.g. "OMP-observed spend only"). The field is declared in both the usage.ts schema and the auth-broker wire schema copy so it survives the "+": "reject" deserialization gate. (#3268)

Fixed

  • Moved the OpenCode Go "OMP-observed spend only" disclaimer from per-limit notes to provider-level notes, so it renders once per provider instead of duplicating across every account × window. (#3268)
  • Fixed Anthropic rate-limit header usage cache entries retaining legacy missing account metadata after refresh.
  • Fixed Anthropic-compatible budget-effort models dropping the selected effort before request serialization, so output_config.effort is emitted alongside thinking.budget_tokens when model metadata declares mode: "anthropic-budget-effort".
  • Fixed anthropic-messages silently dropping caller-supplied Authorization / X-Api-Key from model.headers and ANTHROPIC_CUSTOM_HEADERS, blocking custom proxy auth schemes. Non-OAuth requests now honor the caller's value (matching openai-responses); the lower-level client also suppresses its X-Api-Key add when a custom Authorization is supplied for a non-official endpoint so the proxy receives a single credential. OAuth bearer + Cloudflare AI Gateway keep their pre-existing enforced auth headers. (#3391)
  • Fixed Ollama Cloud num_predict ignoring the provider's 65536 output-token cap so stale models.db rows (or custom modelOverrides re-enabling output caps) that carried maxTokens: 1048576 from a pre-omitMaxOutputTokens catalog 400'd every request with max_tokens (1048576) exceeds model's maximum output tokens (65536) for model deepseek-v4-pro. The Ollama provider now clamps num_predict for any ollama-cloud request at the documented 65536 cap before sending, independent of the cached spec's maxTokens and on top of the existing omitMaxOutputTokens policy — so the request stays valid even when the load-time policy never normalized the spec. Self-hosted ollama traffic is unaffected. (#3392)
  • Fixed OpenRouter Anthropic models on the Responses path omitting cache_control, so prompt caching engages without forcing Chat Completions. (#3397)
  • Fixed OpenRouter Anthropic Responses follow-up requests replaying prior reasoning items with stale signatures, which caused HTTP 400 Invalid signature in thinking block errors after a thinking turn. (#3399)
  • Fixed OpenRouter Anthropic models on the Responses path omitting cache_control, so prompt caching engages without forcing Chat Completions. cacheRetention: "long" now upgrades the breakpoint to ttl: "1h". (#3397)

@oh-my-pi/pi-catalog

Fixed

  • Fixed the Umans GLM-5.2 thinking-level picker collapsing to a single high tier after dynamic discovery: the max upstream level now resolves to the internal xhigh effort, the picker shows both high and xhigh, and the metadata maps xhigh back to Umans's native max wire tier. (#3192)
  • Fixed GitHub Copilot business and enterprise endpoints accepting image inputs that they reject with 400 vision is not supported. The Copilot /models response advertises capabilities.supports.vision = true for Claude/GPT chat models on every host, but only the canonical personal endpoint (https://api.githubcopilot.com) actually serves them; githubCopilotModelManagerOptions now forces input: ["text"] whenever discovery resolves to a non-personal base URL, and mergeDynamicModel honours the dynamic value (instead of OR-upgrading) when the merged endpoint differs from the bundled reference. (#3387)
  • Fixed OpenRouter Anthropic compat to strip Responses reasoning history during replay so signed thinking blocks are not sent back to routed Anthropic providers. (#3399)

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

Fixed

  • Fixed mnemopi auto-retain extracting facts/entities from assistant-authored transcript turns. MnemopiSessionState.retainMessages still stores the full multi-role window for episodic recall, but passes only user-authored turns as extractText, so assistant prose containing always/never no longer becomes durable user Instruction: memory. (#3372)
  • Fixed lazy tool auto-downloads hanging when Bun.write(dest, response) receives a streaming fetch() Response; tool assets now stream the response body to disk with the existing download abort signal and remove partial files on abort. (#3369)
  • Fixed profile-alias installer producing backslash-separated paths for bash/zsh/fish config files on Windows. path.join was used unconditionally, producing Windows-style paths that POSIX shells can't resolve. The installer now uses path.posix.join for non-Windows platforms and normalizes script paths to forward slashes for POSIX shell alias blocks, so omp --alias works correctly in Git Bash and WSL.
  • Fixed pasted or dragged non-image file paths in the TUI prompt staying as inert raw text; existing files now attach as clean local://attachment-N.<ext> references while image paths keep the image attachment flow. (#3360)
  • Slash commands are now recorded in input history (Up Arrow recall). Previously only 4 commands (/plan, /goal, /mcp, /ssh) stored their text; all other built-in slash commands were silently skipped because executeBuiltinSlashCommand returned true before addToHistory was called. History is now centralized in the input controller after successful command dispatch. Commands that may carry secrets (/login <url> with OAuth callback params, /mcp add --token <token>) are excluded from history to prevent credential leakage (#3148)
  • Fixed the ask tool's "Other (type your own)" free-text editor (prompt-style HookEditorComponent) ignoring Ctrl+Q and Ctrl+Enter, so Windows Terminal users who learned the app.message.followUp chord from the main editor (#1903 / fixed by #1905) got zero feedback on submit. The hook-style and main-editor surfaces honored matchesAppFollowUp; the prompt-style handler did not, leaving plain Enter as the sole submit path and Ctrl+Enter falling through to Editor as a newline (silently swallowed by WT). #handlePromptStyleInput now checks matchesAppFollowUp first — mirroring #handleHookStyleInput — and the hint reads enter or ctrl+q submit so the chord is discoverable. (#3353)
  • Fixed the TUI freezing when a tool approval prompt fires while /settings (or the Extensions/Agents dashboard) is open. The fullscreen overlay's close handler restored focus to the editor it had captured at open time, but ExtensionUiController had since swapped the editor out of the editor slot for the approval prompt — so on exit the visible prompt sat unreachable while keystrokes routed to the now-unmounted editor (no Enter/Up/Down/Esc response, only Ctrl+C escaped). SelectorController now restores focus to whatever currently owns the editor slot via a focusActiveEditorArea() helper, applied to settings, extensions dashboard, and agents dashboard close paths. (#3349)
  • Fixed /settings coercing enum/text values to display strings before handing them to the TUI list, preventing YAML numeric enum values from reaching native truncation (#3338).
  • Fixed all extension loading silently failing on the cross-compiled omp-darwin-arm64 release binary (downloaded directly or via a Homebrew tap wrapper) because __computeBunfsPackageRoot mis-handled import.meta.dir = "//root/omp-darwin-arm64". Bun 1.3.14 reports <bunfs-root>/<binary-name> for the compiled entry's import.meta.dir, but the pre-fix function joined metaDir + "packages" and produced /root/omp-darwin-arm64/packages — the binary basename was baked into every bunfs path, so the TypeBox/legacy-pi shims and every @oh-my-pi/pi-* package-root override failed existsSync validation and resolveCanonicalPiSpecifier fell through to a bunfs Bun.resolveSync that also could not find the module. The function now detects the bunfs-root + binary-basename shape (path.basename(path.dirname(metaDir)) === "root") and strips the trailing binary segment by slicing the original metaDir; the production bunfs shim join path also preserves Bun's bunfs-native //root / B:\~BUN\root prefix that path.join would otherwise collapse. (#3329)
  • Fixed llama.cpp discovery to prefer per-model /v1/models meta.n_ctx/meta.n_ctx_train values, refresh selected models after lazy load, and bypass fresh-cache reuse so server restarts update context windows. (#3310)
  • Fixed task.maxConcurrency: 0 serializing subagent spawns instead of running them unbounded. The settings UI labels 0 as "Unlimited", but the session-scoped spawn Semaphore clamped max via Math.max(1, max), so the second subagent body in a batch always waited for the first to release the seat. The constructor now treats max <= 0 (and any non-finite input) as unbounded via Number.POSITIVE_INFINITY, matching the eval parallel()/pipeline() worker-pool semantics (#3305).
  • Fixed MCP tool calls forwarding empty optional placeholder arguments ("" and {}) to tools/call; optional placeholders are now omitted while required fields and meaningful falsy values are preserved. (#3302)
  • Fixed the welcome Tip: line rendering with hardcoded #b48cff / #9ccfff pastels plus a manual \x1b[2m dim, so any light theme dropped the body to ~1.5:1 contrast (well under WCAG AA). renderWelcomeTip in packages/coding-agent/src/modes/components/welcome.ts now paints the label through theme.fg("customMessageLabel", …) and the body through theme.fg("muted", …) (no manual dim), so the line tracks the active theme and stays legible on light backgrounds. (#3337)
  • Fixed omp usage and the /usage command duplicating provider-wide disclaimer notes (e.g. OpenCode Go's "OMP-observed spend only") once per account × limit window. Provider-level notes now render once above the per-account sections in the TUI, CLI, and ACP render paths, and identical per-limit notes are deduplicated in the TUI aggregate renderer. (#3268)
  • Fixed the welcome panel advertising ? for keyboard shortcuts after the ? shortcut was deliberately removed (commit dcf482c). The tips section now points users at /hotkeys instead. (#1614)
  • Fixed Devin provider models silently producing empty responses under the default defaultThinkingLevel: auto. Devin models advertise reasoning: true but no thinking.efforts (Cascade selects effort by routing to sibling model ids, not a wire param), so getSupportedEfforts(model) was empty; clampAutoThinkingEffort returned the classifier-picked effort as-is, which then tripped requireSupportedEffort in pi-ai/stream.ts with Thinking effort low is not supported by devin/<id>. Supported efforts: (silently swallowed by the TUI). clampAutoThinkingEffort now returns undefined when the model has no controllable effort surface, matching clampThinkingLevelForModel; the auto-thinking turn hook also short-circuits the classifier call for these models. (#3356)
  • Fixed omp tiny-models download exiting before its unref'd worker subprocess could install the runtime or download model weights. The tiny-model client now references the worker while requests are pending so standalone CLI downloads wait for Downloaded ... / Failed ... completion. (#3291)
  • Fixed marketplace plugin installs registering only in installed_plugins.json and never in the runtime plugin tree, leaving slash commands and extensions unavailable after omp plugin install name@marketplace. The runtime loader now also enumerates the project-scope plugins root (<projectAnchor>/.omp/plugins) so --scope project installs surface alongside user-scope installs, with project entries shadowing same-named user entries (#3244).
  • Fixed umans requests with more than 10 live context images still sending every image despite the provider budget; outgoing provider contexts now drop the oldest images above the active provider cap while preserving text and newest images (#3230).
  • Fixed snapcompact auto-compaction looping the "snapcompact could not bring the context under the limit — using an LLM summary instead" warning on every threshold tick for sub-1M-token models (Claude Sonnet 4.5, GPT-5.x, Gemini 2.x). snapcompact.compact() was called with no maxFrames override, so it defaulted to MAX_FRAMES_DEFAULT = 80; the projection in AgentSession charges FRAME_TOKEN_ESTIMATE = 5024 per frame block (the conservative high-res Anthropic ceiling), making 80 × 5024 ≈ 402k frame-token projections that always overflow a 200k budget. AgentSession.#computeSnapcompactMaxFrames now sizes the maxFrames cap from a shape-aware reserve — 2 × geometry(shape).capacity worth of verbatim text-edge chars billed at the tiktoken cl100k 4-chars/token baseline (with a 1.15 multiplier for tokenizer drift), plus a 2k summary-template allowance — mirroring what #projectSnapcompactContextTokens will charge once frames land. The shape comes from the same snapcompact.resolveShape(model, settings) call the auto and manual paths pass into snapcompact.compact(). The cap reserve applies only to the frame-cap math, not the skip decision: snapcompact is skipped outright only when kept-recent + non-message ≥ ctxWindow − reserve (no headroom at all), so the frame-less text.length <= 2 * edgeCap short-circuit in planArchive can still land a valid text-only archive when residual headroom is positive but below the cap reserve. The projection guard catches any actual frame-bearing archive that overflows. (#3247)
  • Fixed large-session TUI stalls by tailing appended transcript JSONL and collapsing compacted history on the live display surface (#3258).
  • Fixed status-line usage segment ignoring Codex subscription limits that carry a scope.tier (#2877).
  • Fixed extension tool_call/tool_result events for hashline edit calls to expose event.input.path for single-file edits and event.input.paths for every parsed target, so planning-mode gates can allow one markdown plan edit but still block multi-file hashline calls that cannot be represented by one path (#1678).
  • Fixed scripted eval agent() subagents continuing after a successful yield when a trailing empty assistant stop arrived after the executor's yield-triggered abort. The session's agent_end maintenance compared #assistantEndedWithSuccessfulYield(msg) against the trailing empty-stop message — not the prior yield-bearing one — so the empty-stop recovery path appended a retry reminder and scheduled agent.continue(), reviving the already-yielded child. The yield handler now sets a sticky #yieldTerminationPending flag (cleared on the next prompt()) that short-circuits empty-stop / unexpected-stop / compaction continuations for the rest of the run, so a successful yield is terminal regardless of trailing stops (#3389).
  • Fixed snapcompact rasterizing transcript frames into requests bound for GitHub Copilot business and enterprise endpoints, which then rejected the session permanently with 400 vision is not supported. The snapcompact vision gate now also short-circuits whenever model.provider === "github-copilot" and the resolved baseUrl is not the canonical personal-Copilot host, protecting cached/stale Model specs that still advertise ["text","image"] on a non-personal endpoint. (#3387)

@oh-my-pi/pi-mnemopi

Fixed

  • Fixed remember(..., { extract: true }) fact/entity extraction accepting an extractText override so hosts can store full transcripts while mining facts from a safer projection; also tightened deterministic Instruction: extraction to require an explicit I/you subject instead of treating every always/never clause as a user instruction. (#3372)

@oh-my-pi/pi-natives

Added

  • Added setHangulCompatJamoWidthOverride(value) to override the Hangul Compatibility Jamo (U+3131..U+318E) display width at runtime via a process-global atomic, instead of relying solely on the compile-time cfg!(target_os = "macos") heuristic. The actual width is decided by the client terminal (not the host OS), so the TUI resolves it from the terminal identity and pushes the result here. Encoding: 0 = platform default (macOS narrow, otherwise UAX#11), 1 = narrow (1 cell), 2 = wide (2 cells), 3 = Unicode width (no correction). The leaf width helpers read this override, so no width/slice/truncate/wrap signatures change.

@oh-my-pi/omp-stats

Fixed

  • Stats sync counted the same provider request multiple times when a forked or branched session file copied the parent's entries verbatim. Inserts now skip rows whose (entry_id, timestamp) already exists under a different session_file, and a one-shot migration on the next omp stats run collapses any pre-existing duplicates (#3370).

@oh-my-pi/pi-tui

Added

  • Added runtime resolution of the Hangul Compatibility Jamo (U+3131..U+318E) display width for terminals known to disagree with the platform default (e.g. Ghostty, which renders these at 2 cells). Fixes doubled/ghosted jamo during Korean IME composition; the resolved width is pushed into the native width engine before the first paint. Other terminals keep the platform default (macOS narrow, otherwise UAX#11), so the override is a no-op outside Ghostty. A runtime DSR/CPR probe for unknown terminals is tracked separately.
  • Added setHangulCompatibilityJamoWidth / getHangulCompatibilityJamoWidth to set the jamo width profile ("platform" | "unicode" | 1 | 2); the profile is mirrored into the native setHangulCompatJamoWidthOverride.

Fixed

  • Removed the 30-second OSC 11 background-color poll that ran on terminals without DEC Mode 2031 support (macOS Terminal.app, Warp, VS Code's built-in terminal, older Alacritty/WezTerm). Each poll's OSC 11 + DA1 write wiped the user's active text selection on several of those terminals, causing intermittent "can't copy" failures whenever a poll fired mid-drag — most visibly during the Ask tool dialog when the user wants to quote text back from the conversation (#3297). Theme detection now relies on the initial startup probe plus Mode 2031 push notifications; affected terminals pick up OS-theme changes on next launch.
  • Fixed @-path autocomplete failing on Windows for paths outside the cwd. Windows absolute paths (e.g. C:\\Users\\...) were not detected as absolute — only / was checked — so they were incorrectly joined with the base directory, producing invalid search paths and empty suggestions. Path-join calls also introduced backslashes into suggestion values, breaking round-trip insertion. Absolute path detection now uses path.isAbsolute() (handles drive letters) and suggestion paths are normalized to forward slashes (valid on all platforms).
  • Fixed settings rows crashing native text truncation when a malformed config value reaches the renderer as a non-string (#3338).
  • Fixed desktop notifications being silently lost under tmux on the common stack of tmux + kitty/ghostty/wezterm/iTerm2. TERMINAL_ID resolves to the inner terminal (whose markers leak into the tmux session env), which maps to NotifyProtocol.Osc9 / NotifyProtocol.Osc99, and sendNotification() wrote that raw OSC straight to stdout — tmux dropped it on the floor and monitor-bell / monitor-activity never fired, so a backgrounded omp pane had no way to flag completion or ask blockage. Under TMUX, OSC-protocol notifications are now wrapped in tmux's \x1bPtmux;…\x1b\\ DCS passthrough envelope (so users with set -g allow-passthrough on still get the real toast on the outer terminal) and followed by a \x07 BEL (so set -g monitor-bell on reliably flags the window otherwise). The OSC 99 capability probe in terminal.ts is wrapped the same way so rich notifications keep working across tmux. NotifyProtocol.Bell paths are unchanged. (#3395)

What's Changed

  • fix(coding-agent): handled <bunfs-root>/<binary> in __computeBunfsPackageRoot by @roboomp in #3330
  • fix(mcp): omit unused optional tool args by @roboomp in #3304
  • fix(task): treat maxConcurrency 0 as unbounded in spawn semaphore by @roboomp in #3307
  • fix(providers): honor llama.cpp per-model context windows by @roboomp in #3311
  • fix(tui): deliver notifications under tmux via DCS passthrough + BEL fallback by @roboomp in #3396
  • fix(tui): runtime Hangul Compatibility Jamo width override + Ghostty detection by @ZergRocks in #1800
  • fix(catalog): restore Umans GLM-5.2 max reasoning by @roboomp in #3193
  • fix(agent): clamp provider context images by @roboomp in #3232
  • fix(cli): register marketplace plugin installs by @roboomp in #3245
  • fix(agent): size snapcompact maxFrames by the live model window by @roboomp in #3249
  • fix(tui): reduce large transcript stalls by @roboomp in #3259
  • fix(tui): include tiered Codex usage limits by @riverpilot in #3289
  • fix(cli): keep tiny-model downloads alive by @roboomp in #3292
  • fix(usage): dedup provider-wide notes and add report-level notes field by @oldschoola in #3312
  • fix(welcome): replace stale ? shortcut with /hotkeys in tips panel by @oldschoola in #3315
  • fix(tui): stop OSC 11 poll from wiping text selection by @oldschoola in #3344
  • fix(tui): @-path autocomplete on Windows for paths outside cwd by @oldschoola in #3345
  • fix(cli): profile-alias installer produces correct paths for POSIX shells on Windows by @oldschoola in #3346
  • fix: store slash commands in input history by @oldschoola in #3352
  • fix(tui): theme-aware welcome tip line for light-theme legibility by @roboomp in #3376
  • fix(settings): prevent numeric config values from crashing settings UI by @roboomp in #3377
  • fix(tools): stream tool downloads without Bun.write Response by @roboomp in #3379
  • fix(coding-agent): clamp auto thinking to undefined for models without controllable effort by @roboomp in #3380
  • fix(coding-agent): honor app.message.followUp chord in ask prompt-style editor by @roboomp in #3381
  • fix(stats): dedupe forked-session entries to stop double-counting by @roboomp in #3382
  • fix(memory): scope mnemopi entity extraction to user turns by @roboomp in #3383
  • fix(tui): attach pasted file paths as local refs by @roboomp in #3384
  • fix(coding-agent): restore TUI focus to live editor-slot owner when a fullscreen overlay closes by @roboomp in #3385
  • fix(catalog,coding-agent): disable vision on non-personal Copilot endpoints (#3387) by @roboomp in #3388
  • fix(session): suppress empty-stop retry after successful yield by @roboomp in #3390
  • fix(ai/anthropic): honor caller-supplied Authorization/X-Api-Key for custom proxies (#3391) by @roboomp in #3393
  • fix(ai/ollama): clamp num_predict at the Ollama Cloud 65536 cap by @roboomp in #3394
  • fix(providers): strip OpenRouter Anthropic reasoning replay by @roboomp in #3400
  • fix(coding-agent): expose hashline edit path to extensions by @roboomp in #1681

Full Changelog: v16.1.16...v16.1.17