@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 makeyieldIfDue()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 injectableYieldGate(withyieldIfDue()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 toUsageReportfor disclaimers that apply to every limit (e.g. "OMP-observed spend only"). The field is declared in both theusage.tsschema 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
notesto provider-levelnotes, 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.effortis emitted alongsidethinking.budget_tokenswhen model metadata declaresmode: "anthropic-budget-effort". - Fixed
anthropic-messagessilently dropping caller-suppliedAuthorization/X-Api-Keyfrommodel.headersandANTHROPIC_CUSTOM_HEADERS, blocking custom proxy auth schemes. Non-OAuth requests now honor the caller's value (matchingopenai-responses); the lower-level client also suppresses itsX-Api-Keyadd when a customAuthorizationis 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_predictignoring the provider's 65536 output-token cap so stalemodels.dbrows (or custommodelOverridesre-enabling output caps) that carriedmaxTokens: 1048576from a pre-omitMaxOutputTokens catalog 400'd every request withmax_tokens (1048576) exceeds model's maximum output tokens (65536) for model deepseek-v4-pro. The Ollama provider now clampsnum_predictfor anyollama-cloudrequest at the documented 65536 cap before sending, independent of the cached spec'smaxTokensand on top of the existingomitMaxOutputTokenspolicy — so the request stays valid even when the load-time policy never normalized the spec. Self-hostedollamatraffic 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 blockerrors 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 tottl: "1h". (#3397)
@oh-my-pi/pi-catalog
Fixed
- Fixed the Umans GLM-5.2 thinking-level picker collapsing to a single
hightier after dynamic discovery: themaxupstream level now resolves to the internalxhigheffort, the picker shows bothhighandxhigh, and the metadata mapsxhighback to Umans's nativemaxwire tier. (#3192) - Fixed GitHub Copilot business and enterprise endpoints accepting image inputs that they reject with
400 vision is not supported. The Copilot/modelsresponse advertisescapabilities.supports.vision = truefor Claude/GPT chat models on every host, but only the canonical personal endpoint (https://api.githubcopilot.com) actually serves them;githubCopilotModelManagerOptionsnow forcesinput: ["text"]whenever discovery resolves to a non-personal base URL, andmergeDynamicModelhonours 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.retainMessagesstill stores the full multi-role window for episodic recall, but passes only user-authored turns asextractText, so assistant prose containingalways/neverno longer becomes durable userInstruction:memory. (#3372) - Fixed lazy tool auto-downloads hanging when
Bun.write(dest, response)receives a streamingfetch()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.joinwas used unconditionally, producing Windows-style paths that POSIX shells can't resolve. The installer now usespath.posix.joinfor non-Windows platforms and normalizes script paths to forward slashes for POSIX shell alias blocks, soomp --aliasworks 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 becauseexecuteBuiltinSlashCommandreturnedtruebeforeaddToHistorywas 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
asktool's "Other (type your own)" free-text editor (prompt-styleHookEditorComponent) ignoring Ctrl+Q and Ctrl+Enter, so Windows Terminal users who learned theapp.message.followUpchord from the main editor (#1903 / fixed by #1905) got zero feedback on submit. The hook-style and main-editor surfaces honoredmatchesAppFollowUp; 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).#handlePromptStyleInputnow checksmatchesAppFollowUpfirst — mirroring#handleHookStyleInput— and the hint readsenter or ctrl+q submitso 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, butExtensionUiControllerhad 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).SelectorControllernow restores focus to whatever currently owns the editor slot via afocusActiveEditorArea()helper, applied to settings, extensions dashboard, and agents dashboard close paths. (#3349) - Fixed
/settingscoercing 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-arm64release binary (downloaded directly or via a Homebrew tap wrapper) because__computeBunfsPackageRootmis-handledimport.meta.dir = "//root/omp-darwin-arm64". Bun 1.3.14 reports<bunfs-root>/<binary-name>for the compiled entry'simport.meta.dir, but the pre-fix function joinedmetaDir + "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 failedexistsSyncvalidation andresolveCanonicalPiSpecifierfell through to a bunfsBun.resolveSyncthat 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 originalmetaDir; the production bunfs shim join path also preserves Bun's bunfs-native//root/B:\~BUN\rootprefix thatpath.joinwould otherwise collapse. (#3329) - Fixed llama.cpp discovery to prefer per-model
/v1/modelsmeta.n_ctx/meta.n_ctx_trainvalues, refresh selected models after lazy load, and bypass fresh-cache reuse so server restarts update context windows. (#3310) - Fixed
task.maxConcurrency: 0serializing subagent spawns instead of running them unbounded. The settings UI labels0as "Unlimited", but the session-scoped spawnSemaphoreclampedmaxviaMath.max(1, max), so the second subagent body in a batch always waited for the first to release the seat. The constructor now treatsmax <= 0(and any non-finite input) as unbounded viaNumber.POSITIVE_INFINITY, matching the evalparallel()/pipeline()worker-pool semantics (#3305). - Fixed MCP tool calls forwarding empty optional placeholder arguments (
""and{}) totools/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/#9ccfffpastels plus a manual\x1b[2mdim, so any light theme dropped the body to ~1.5:1 contrast (well under WCAG AA).renderWelcomeTipinpackages/coding-agent/src/modes/components/welcome.tsnow paints the label throughtheme.fg("customMessageLabel", …)and the body throughtheme.fg("muted", …)(no manual dim), so the line tracks the active theme and stays legible on light backgrounds. (#3337) - Fixed
omp usageand the/usagecommand 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 shortcutsafter the?shortcut was deliberately removed (commit dcf482c). The tips section now points users at/hotkeysinstead. (#1614) - Fixed Devin provider models silently producing empty responses under the default
defaultThinkingLevel: auto. Devin models advertisereasoning: truebut nothinking.efforts(Cascade selects effort by routing to sibling model ids, not a wire param), sogetSupportedEfforts(model)was empty;clampAutoThinkingEffortreturned the classifier-picked effort as-is, which then trippedrequireSupportedEffortinpi-ai/stream.tswithThinking effort low is not supported by devin/<id>. Supported efforts:(silently swallowed by the TUI).clampAutoThinkingEffortnow returnsundefinedwhen the model has no controllable effort surface, matchingclampThinkingLevelForModel; the auto-thinking turn hook also short-circuits the classifier call for these models. (#3356) - Fixed
omp tiny-models downloadexiting 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 forDownloaded .../Failed ...completion. (#3291) - Fixed marketplace plugin installs registering only in
installed_plugins.jsonand never in the runtime plugin tree, leaving slash commands and extensions unavailable afteromp plugin install name@marketplace. The runtime loader now also enumerates the project-scope plugins root (<projectAnchor>/.omp/plugins) so--scope projectinstalls surface alongside user-scope installs, with project entries shadowing same-named user entries (#3244). - Fixed
umansrequests 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 nomaxFramesoverride, so it defaulted toMAX_FRAMES_DEFAULT = 80; the projection inAgentSessionchargesFRAME_TOKEN_ESTIMATE = 5024per frame block (the conservative high-res Anthropic ceiling), making 80 × 5024 ≈ 402k frame-token projections that always overflow a 200k budget.AgentSession.#computeSnapcompactMaxFramesnow sizes themaxFramescap from a shape-aware reserve —2 × geometry(shape).capacityworth 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#projectSnapcompactContextTokenswill charge once frames land. The shape comes from the samesnapcompact.resolveShape(model, settings)call the auto and manual paths pass intosnapcompact.compact(). The cap reserve applies only to the frame-cap math, not the skip decision: snapcompact is skipped outright only whenkept-recent + non-message ≥ ctxWindow − reserve(no headroom at all), so the frame-lesstext.length <= 2 * edgeCapshort-circuit inplanArchivecan 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
usagesegment ignoring Codex subscription limits that carry ascope.tier(#2877). - Fixed extension
tool_call/tool_resultevents for hashlineeditcalls to exposeevent.input.pathfor single-file edits andevent.input.pathsfor 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
evalagent()subagents continuing after a successfulyieldwhen a trailing empty assistantstoparrived after the executor's yield-triggered abort. The session'sagent_endmaintenance 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 scheduledagent.continue(), reviving the already-yielded child. The yield handler now sets a sticky#yieldTerminationPendingflag (cleared on the nextprompt()) 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 whenevermodel.provider === "github-copilot"and the resolvedbaseUrlis 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 anextractTextoverride so hosts can store full transcripts while mining facts from a safer projection; also tightened deterministicInstruction:extraction to require an explicitI/yousubject instead of treating everyalways/neverclause 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-timecfg!(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 differentsession_file, and a one-shot migration on the nextomp statsrun 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/getHangulCompatibilityJamoWidthto set the jamo width profile ("platform" | "unicode" | 1 | 2); the profile is mirrored into the nativesetHangulCompatJamoWidthOverride.
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 usespath.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_IDresolves to the inner terminal (whose markers leak into the tmux session env), which maps toNotifyProtocol.Osc9/NotifyProtocol.Osc99, andsendNotification()wrote that raw OSC straight to stdout — tmux dropped it on the floor andmonitor-bell/monitor-activitynever fired, so a backgrounded omp pane had no way to flag completion oraskblockage. UnderTMUX, OSC-protocol notifications are now wrapped in tmux's\x1bPtmux;…\x1b\\DCS passthrough envelope (so users withset -g allow-passthrough onstill get the real toast on the outer terminal) and followed by a\x07BEL (soset -g monitor-bell onreliably flags the window otherwise). The OSC 99 capability probe interminal.tsis wrapped the same way so rich notifications keep working across tmux.NotifyProtocol.Bellpaths 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