@oh-my-pi/pi-agent-core
Changed
- Steering is now woken by an event instead of polled on a fixed interval while a tool batch runs.
AgentLoopConfig.waitForSteeringMessagesresolves when a steer is enqueued, so an interruption is observed as soon as it arrives rather than at the next tick, and idle batches stop burning wakeups. The interval timer remains for the IRC interrupt queue, which has no wake callback, and checks only IRC while the event watcher owns steering. Waits are raced against local abort, so a callback that does not observe its signal cannot hang batch teardown.
Fixed
- Fixed a Cursor tool result being lost when a custom
cursorOnToolResulttransformer was still pending as the turn closed. The provider dispatches decoded messages without awaiting them, so amessage_endfrom the same chunk could drain the buffer before the transformer resolved, dropping the result and leaving itstoolCallblock to be stripped as dangling on replay. The entry is now reserved synchronously and patched in place once the transformer resolves, preserving buffer order. - Fixed an async
cursorOnToolResulttransformer's rewrite being silently discarded when it resolved after the buffer drain. The reservation kept the call from dangling but the late patch mutated a detached entry, so the already-persisted message kept the pre-transform payload. The drain now awaits any transformer still in flight before persisting, matching the awaited exec-channel paths. A rejecting transformer is swallowed and the reserved payload stands in, so a failing hook cannot take the turn down or cost the result. - Fixed Cursor tool results being dropped for hosts that pass neither
cursorExecHandlersnorcursorOnToolResult. Both are optional, but the Cursor provider resolves native todo calls server-side and synthesizes exec blocks regardless, marking both as resolved so no placeholder result is emitted for them. The result buffer callback was only installed when one of the options was present, so a bare SDK host discarded the provider's paired result and every rebuilt transcript stripped the interaction. It is now installed unconditionally. - Fixed an async
cursorOnToolResultrewrite being lost when the provider errored mid-transform. The normal drain waits for a pending transformer, but the error path snapshotted the buffer without that await, so a transform still in flight patched an entry the catch path had already detached and the pre-transform payload was persisted. A provider error is exactly when a transform is most likely to be mid-flight. - Reduced oversized OpenAI native compaction requests by replacing only trailing tool-output bodies that exceed the model context window, while preserving calls, assistant history, and reasoning.
@oh-my-pi/pi-ai
Added
- MiniMax Token Plan accounts now report quota in
omp usage.GET /v1/token_plan/remainsreturns one bucket per plan quota, each carrying a rolling interval window and a weekly window, sominimax-codesurfaces real remaining percentages instead of an empty report. A model the plan does not include comes back looking like an untouched quota; those buckets are dropped from the report and named in its metadata. The mainland idminimax-code-cnis untouched. - OAuth logins now stamp
authorizedAt(epoch ms of the interactive login) on the stored credential, and every refresh-persist path preserves it. Anthropic expires the whole OAuth grant family ~30 days after authorization regardless of refresh-token rotation (observed asinvalid_grant: "Refresh token expired"on the latest rotated token, exactly 30 days after login, across four production accounts), so the login anchor is what makes re-login deadlines computable. ExportedANTHROPIC_OAUTH_GRANT_TTL_MSalongside the anthropic OAuth flow. - Added
GET /v1/credentials/disabledto the auth broker andAuthBrokerClient.listDisabledCredentials: disabled-credential tombstones (DisabledCredentialSummary— identity, verbatim disable cause, disable timestamp; never token material) so auto-disabled accounts stay visible to clients instead of silently vanishing from the snapshot.AuthStorage.listDisabledCredentialsserves the same data locally from SQLite; clients of brokers predating the endpoint get an empty list (404 mapped, no error). - Added
AuthStorage.revalidateCredentials()and the optionalAuthCredentialStore.refreshSnapshothook: remote broker stores re-fetchGET /v1/snapshoton demand so callers pairing live per-credential data with stored identities (omp usage) never render against the up-to-an-hour-stale disk-cached snapshot; local SQLite stores are always current and only reload. - Added an optional per-request
codexSseMaxAttemptsstream option to bound Codex SSE pre-response retries while preserving the six-attempt default when omitted. - Fixed Cursor requests failing with
Connect error internal: Unable to parse image: ...whenever the session history contained an image:rootPromptMessagesJsonimage parts now embed adata:<mime>;base64,URI instead of bare base64, matching the convention used by the OpenAI-completions provider (#6564).
Fixed
- Fixed OpenAI Responses native history replay sending output-only
statusfields back as input, preventinginput[N].statusfailures in long-running sessions. (#6513 by @Ant39140) - Cursor no longer discards a local tool result when the transport fails mid-execution. The provider waits for in-flight exec dispatches before pushing
done, but the error path skipped that wait, so a handler decoded from the last chunk landed its result after the Agent had already finalized the call from the terminal error and cleared its buffer — losing the real outcome of a tool that may already have run side effects. Both exits now drain the same barrier. - Cursor exec handlers returning the bare-result form no longer record a failed call as successful. When an SDK handler returns only a protocol result (no paired
toolResult), the synthesized transcript entry was always"Tool produced no transcript result"withisError: false, even for arejectedorerrorresult — so Cursor saw a failure while the rebuilt transcript showed success. The synthesized entry now derives its state and message from the result's own oneof variant — including MCP, where an application-level tool failure rides inside thesuccessvariant asis_errorrather than as a separate variant. - Fixed Cursor models silently failing to maintain the todo list. Cursor resolves its native
update_todos/read_todostools server-side, but the bridge looked for them under flattenedupdateTodosToolCall/readTodosToolCallproperties, which a decodedagent.v1.ToolCallnever has — the variant only arrives through thetooloneof — so no native todo call was ever recognized. The synthesizedtodotool call was also emitted as locally runnable with a{todos}payload the local tool's schema rejects, so any update that did surface ended as a validation error and local todo state never followed Cursor's. Todo calls are now read from the oneof, both native todo blocks are marked as already-resolved, and local state is mirrored from the server's confirmed success snapshot (leaving state untouched onUpdateTodosError).TODO_STATUS_CANCELLEDnow maps toabandonedinstead of reverting the task topending. - Hardened Cursor todo mirroring against partial
read_todosresponses: a read narrowed bystatus_filter/id_filter, or one returning fewer rows than the server's owntotal_count, is a subset rather than the list, and is no longer treated as authoritative. Previously such a response would have deleted every task it omitted. - Fixed an empty
update_todosresponse whosetotal_countis nonzero being mirrored as an authoritative clear, deleting every local task at once. The count-mismatch guard skipped empty responses entirely; only a matching zero count is a genuine clear now. An emptyread_todosstays refused outright, since proto3 decodes an unsettotal_countas0and it cannot be told apart from a filtered read that matched nothing. - Fixed a Cursor todo call being left unpaired when the completion frame carried no
tool_callat all.ToolCallCompletedUpdate.tool_callis optional, but the block was already marked as server-resolved by the started frame, so nothing emitted a placeholder for it and every transcript rebuild stripped the interaction. It now settles as "nothing to mirror", the same as a refused snapshot. - Fixed local Cursor exec calls (
read/write/grep/delete/bash/lsp/MCP) vanishing from rebuilt transcripts when the tool produced no result. The assistant block is synthesized and marked server-resolved before the handler runs, so the three result-less paths — no handler installed, a handler returning nothing, and a thrown handler — left the call unpaired. Each now pairs a result carrying the same text the server receives. - Fixed Cursor MCP tool calls being unrecognized on the wire.
ToolCall.toolis a protobuf oneof, so a decoded message exposes the variant as{ case, value }and never as a flatmcpToolCallproperty — the same trap that made native todo calls invisible while hand-shaped fixtures kept passing. Both the streamed start and the completion arg merge now go through a shared selector. - Fixed a streamed Cursor MCP block being named from
namewhile its paired result usedtoolName, so the two disagreed whenever the server sent different values. Both now prefertoolName. - Fixed the Cursor stream emitting
donewhile a tool handler decoded from the final chunk was still running. Server messages are dispatched fire-and-forget so the socket keeps draining, but nothing waited for them: when an exec request,turnEndedand the stream close arrived in one chunk, the turn finished before the handler produced its result, and the result missed the buffer drain that pairs it with its call. In-flight dispatches are now awaited after the transport completes. - Fixed a server-resolved Cursor todo call leaving its transcript block stuck pending: the synthetic completion was emitted under a freshly generated id instead of the streamed call id the interactive transcript filed the block under, so the card animated indefinitely. The settled call id is now handed to the sync handler.
- Fixed server-resolved Cursor todo blocks disappearing from rebuilt transcripts: nothing produced a
toolResultfor them, andbuildSessionContextstrips anytoolCallleft unpaired, so the interaction vanished on reload, branch switch, or transcript rebuild. The result the host builds is now persisted verbatim — it carries thedetails.phasesthe todo renderer rebuilds the list from, which a summary-only result would have replayed as0 tasks. - Fixed a refused or failed Cursor todo call leaving its card animating forever. Only a successful snapshot settled the block, so a
read_todosnarrowed by a filter and a serverUpdateTodosErrorboth went unanswered — notool_execution_end, and notoolResultto keep the block from being stripped on rebuild. Every completed native todo call now settles. A server error is carried through as a failed result rather than collapsed into the benign "nothing to mirror" case, which would have replayed the failure as a success. - Hardened Cursor todo mirroring against snapshots whose rows collide on content. Cursor's wire model identifies todos by
idand can represent two rows sharing the same text; the local list is keyed by content alone and thetodotool rejects a duplicate outright, so importing such a pair would leave every task-targeteddone/drop/rmresolving to the first row and the second unreachable. The snapshot is now refused like any other that cannot be represented locally — local state is left untouched and the call still settles as a no-op. - Hardened Cursor todo mirroring against ambiguous empty
read_todosresponses.total_countis a proto3 scalar, so an unset field decodes as0and is indistinguishable from a genuinely empty list; acceptingtodos=[]+total_count=0would clear every local task. Empty and mismatched reads are now refused —update_todosremains the authoritative clear path. - Fixed refused Cursor todo results claiming
"No todo changes". A server-acceptedupdate_todoscan still be declined locally (content collision, etc.), so the persisted fallback now reads"Todo snapshot not mirrored"instead of implying the remote call changed nothing. - Hardened Cursor todo mirroring against snapshots carrying unresolved
TodoItem.dependencies. The wire model blocks a row behind other rows byid; the local list has no ids and no edges, so an imported dependent row files as plainpendingandnextActionableTaskthen offers work the server considers blocked. Snapshots with an edge pointing at a row that is not yetcompleted/abandonedare now refused like any other that cannot be represented locally. Edges whose blockers already finished constrain nothing and still mirror. - Extended the Cursor todo
total_countmismatch guard toupdate_todos. A partial or size-limited merge response is as incomplete as a filtered read, but the check only applied to reads, so an update returning fewer rows than its own count was mirrored as the full list and deleted every task it omitted. An empty update still syncs — it remains the authoritative clear path, unlike an ambiguous empty read. - Hardened Cursor todo mirroring against rows with empty
content.contentis a proto3 string, so a missing or default value arrives as""; the local list is keyed by content and rejects a falsy one before lookup, leaving the imported row unreachable to every task-targeteddone/drop/rm. Such snapshots are now refused like any other that cannot be represented locally. - Fixed a deterministic circular-import TDZ that crashed
packages/catalog's test process withReferenceError: Cannot access 'claudeCodeVersion' before initialization:registry/oauth/anthropic.tsimportedclaudeCodeVersionfromproviders/anthropic.ts, which transitively pulls the registry back in (providers/anthropic→stream→registry→registry/oauth/anthropic), so the module-levelclaude-code/${claudeCodeVersion}bootstrap user-agent const read the binding whileproviders/anthropic.tswas still mid-initialization.claudeCodeVersionnow lives in a zero-import leaf module (providers/claude-code-fingerprint.ts) thatproviders/anthropic.ts,registry/oauth/anthropic.ts, andusage/claude.tsall import from, removing the cycle at the source rather than deferring the read. - Fixed a circular initialization between the Anthropic provider and OAuth registry that could throw before
claudeCodeVersionwas initialized when package tests or consumers loaded modules in parallel (#6628 by @anatoli-tsinovoy). - Stopped the account-level Codex
rate_limit.limit_reachedflag from being applied to individual chat windows. Codex reports one shared flag for the whole account, so a window with real headroom was markedexhaustedbecause a different window (or a separate metered feature) was at its limit, which over-blocked sibling accounts during credential selection. Each window's status now reflects only its own usage - Scoped Codex reactive backoff per meter: a
usage_limit_reachedfrom a Spark request no longer persists a block that ordinary chat requests honour, and the reverse. Blocks written before scoping used a shared scope meaning "block everything", so requests still honour it and reconciliation still heals it - Implemented
scopeLimitsfor the Codex ranking strategy so a request gates only on the windows it actually consumes:-sparkmodels spend the Spark meter and every other model spends the 5h/weekly chat windows, instead of OR-ing every window and meter into one provider-wide block - Fixed native Anthropic adaptive-only models (Opus 4.6+, Sonnet 4.6+, Fable/Mythos 5) keeping thinking ON when reasoning was meant to be off.
mapOptionsForApinever consulteddisableReasoningon the Anthropic branch, so a caller-side disable left adaptive thinking at full effort; anddisableThinkingIfToolChoiceForceddeletedoutput_config.effortalongsidethinking, which for adaptive-only models silently re-enabled adaptive thinking (a bare omission defaults to adaptive-ON). Both paths now omitthinkingand pin the lowest adaptive effort, sodisableReasoningand forcedtool_choiceturns (e.g. the delivery reviewer'sreport_delivery) actually suppress reasoning instead of returning a thinking block withend_turn(#6589). - Fixed Bedrock Converse dropping captured Claude thinking signatures when replaying application-inference-profile ARN models, restoring adaptive-thinking multi-turn conversations (#6610).
- Fixed the
alibaba-token-planlogin only supporting the international Singapore endpoint, which rejected China (Beijing) Token Plansk-sp-keys with401 invalid_api_key. Login now selects a region (International / China (Beijing) / Custom), validates the key against that region's/modelsendpoint, and stores the chosen base URL in the credential so inference and discovery both target it (#6682). - Fixed statusless provider capacity errors such as
no_capacityand high-demand responses being treated as terminal instead of retryable. (#6503) - Fixed QwenCloud Token Plan quota reporting to call the current console usage RPC and document how to capture its optional Cookie during login.
- Fixed Cursor exec-channel MCP calls such as
web_searchomittingtoolCallblocks when no interaction block arrives, which rendered their tool cards below the final assistant answer or dropped them on transcript replay. (#6501) - Fixed Claude scoped weekly limits (e.g.
Claude 7 Day (Fable)) withis_active: falsebeing dropped by the/usageparser, rendering asnot reportedinomp usagedespite carrying real utilization. Live payloads mark only the currently binding limit active — an account pinned at a 100% Fable cap reports its 77% shared weekly row as inactive too — sois_activesignals severity ranking, not bucket existence, and is now ignored. Exhaustion gating is unchanged: tier rows still hard-block only at confirmed 100% with a future reset. - Fixed a TDZ crash (
Cannot access 'claudeCodeVersion' before initialization) whenproviders/anthropicwas the first module loaded:providers/anthropic→stream→registry→registry/oauth/anthropiccircled back into the still-initializing provider module. The Claude Code fingerprint constants now live in the leaf moduleproviders/claude-code-fingerprint(star re-exported fromproviders/anthropic, so import paths are unchanged).
@oh-my-pi/pi-catalog
Added
- Added Claude Opus 5 model entries for Amazon Bedrock:
anthropic.claude-opus-5plus itsus.,eu.,au., andglobal.regional/geo IDs.
Fixed
- Fixed
alibaba-token-planlocking out China (Beijing) 百炼 Token Plan subscribers: the provider hardcoded the international Singapore endpoint, so Beijing-issuedsk-sp-keys got401 invalid_api_key. The wire credential now carries an optional region base URL, and model discovery targets the credential's region (#6682). - Fixed forced
tool_choice400s (tool_choice 'specified' is incompatible with thinking enabled) on Kimi Code's Anthropic-compatible endpoint for thekimi-for-coding,kimi-for-coding-highspeed, andk3aliases: the Anthropic-surface compat matcher only recognised Moonshot's nativekimi-k2.7-code*ids, so thinking-locked kimi-code models keptsupportsForcedToolChoice: trueand the forced selector was sent to a host that always thinks. These models now resolverequiresThinkingEnabled, keeping thinking on and downgrading forced choices toauto. - Retried empty successful provider discovery responses after the short non-authoritative interval instead of caching them for the full catalog TTL (#6620).
- Fixed GitHub Copilot Claude models with no bundled catalog reference (e.g. a freshly served
claude-opus-5) discovering withreasoning: false/thinking: nulland no effort dial, and disappearing along with their synthesized-1msibling on offline reads: reference-less Copilot models on the anthropic-messages proxy now derive the adaptive reasoning ladder from the model id, and the cache restores their compile-timeCOPILOT_API_HEADERSby value instead of dropping them as unrestorable (#6664).
@oh-my-pi/pi-coding-agent
Added
omp usagenow surfaces auto-disabled credentials as red✗tombstone rows (identity, how long ago, the shortened upstream cause — e.g.Refresh token expired— and a re-login hint), including a provider section when no active credential remains. User-driven tombstones (replaced by newer credential,deleted by user) and API-key rows stay hidden. Requires a broker withGET /v1/credentials/disabled; older brokers degrade to no tombstone rows.omp usagewarns about Anthropic's ~30-day OAuth grant lifetime: accounts whose interactive login (authorizedAt) is within a week of the deadline get a yellow⚠ re-login within <time>line, and past-deadline accounts a red one. Grants die server-side exactly ~30 days after login regardless of refresh rotation, so this is the only warning before the broker auto-disables the row.
Changed
- Enabled Computer Use sessions now state the desktop-routing contract in the compact system prompt, retain their controller across model switches, expose effective native/function routing through
/computer status, and emit structured lifecycle diagnostics without logging captured content.
Fixed
- Fixed
omprefusing to start on Windows when nobash.exeis discoverable — most visibly with scoop-installed Git, whose manifest shimssh.exe/git.exebut neverbash.exe, so PATH lookup missed it. Startup threwNo bash shell foundwhile merely building the bash tool description, even though bash tool commands always execute in the embedded brush-core shell and need no host bash. Shell discovery now also checksGIT_INSTALL_ROOT, scoop and per-user Git for Windows install roots, andsh.exeon PATH, then falls back tocmd.exefor the spawn-only paths (interactive PTY, ACP client terminals) instead of failing; the cmd fallback is never used to wrap user-shell commands — brush runs the POSIX line directly. - Fixed dragging an image whose path contains unescaped spaces (e.g. macOS screenshot names like
Screenshot 2026-07-24 at 1.55.12 PM.png) into the terminal — the bracketed-paste image extraction route now has the same whole-text-as-path fallback as the clipboard keybind route, so both routes share identical detection and attach the image instead of inserting the raw path as literal text (#6578). The shared fallback only claims payloads that hold a single path: one carrying a second absolute-path anchor after unescaped whitespace (/tmp/a.png /tmp/b shot.png— dragging two files at once when either name has spaces) now pastes as text on both routes instead of being fused into one unresolvable path, which on the clipboard route previously attached nothing and swallowed the text behind an "Image not found" status. - Fixed transient reasonless request aborts that arrived after a tool call finished streaming ending the turn instead of entering recovery, which left edit calls and task subagents dead until the user manually resumed. The session now continues from the synthetic unexecuted tool result under the normal retry policy without replaying completed side effects (#6668).
- Fixed prewalk silently dropping a same-model hand-off that only lowers the thinking level: the arm/switch guard compared model identity alone and discarded the resolved
thinkingLevel, so a legal effort-downgrade target (e.g.prewalk: "@task"resolving to the same model at a cheaper effort) never applied and the session paid the plan/continue nudges for nothing. Prewalk now compares(provider, id, effective thinking level), applies effort-only hand-offs, and emits a notice on a genuine no-op instead of returning silently (#6659). - Fixed
@czottmann/pi-automodefailing legacy extension validation because the pi-ai compatibility shim omittedclampThinkingLevel, then failing every classified tool call becausectx.modelRegistryomittedgetApiKeyAndHeaders. (#6648) - Fixed hide-secrets placeholders conflicting with hashline edit headers by replacing hash-delimited tokens with the unambiguous
$$HASH$$format (#6631). - Fixed the advisor silently swallowing its own quarantined turns: when an advisor called an ungranted tool (e.g.
bash) its whole turn was discarded before dispatch, so its advice never reached the primary agent and the failure surfaced only in advisor diagnostics — every other non-recovering failure branch notifies the host UI, but quarantine re-primed silently with no bound. A persistently-quarantining advisor now surfaces anotifyFailurewarning in the main UI (deduped, cleared on the next successful turn) and stops the unbounded silent re-prime loop (#6661). - Fixed the Docker
natives-builderstage failing to build releases ≥ 17.1.1: the native audio stack added bindgen (miniaudio needs libclang) and a bundled-opus CMake build (needs cmake + make), none of which were installed in the slim builder image. - Fixed a configured
modelRoles.defaultnaming an extension-registered model (listed inenabledModels) silently running on a different in-scope provider's model. The startup model scope is resolved before extensions callregisterProvider(), so the default role dropped out of scope andbuildSessionOptionspinnedoptions.modelto the first scoped model — which marked the model "explicit" and suppressed the post-extension default-role re-resolution. A configured default that can't be found in the startup scope is now deferred so it re-resolves against the fully registered, stillenabledModels-scoped catalog once extensions load (#6694). - Fixed Parakeet speech-to-text failing to load
sherpa-onnx-nodefrom Windows source workspaces when Bun installed the wrapper underpackages/coding-agent/node_modulesbut hoisted its native platform package to the repository root (#6690). - Fixed
omp usageduplicating org-less legacy accounts as "no usage data" rows whenever any sibling report carried an organization (mixed pools of pre-org-capture rows and fresh org-scoped logins): an org-less account is now covered by its own org-less report, while org-attributed sibling reports still never count as its coverage. omp usagerevalidates the broker credential snapshot before rendering: live usage reports were previously paired with a disk-cached account list up to an hour old, so a just-completed re-login (org-less row upserted to org-scoped) rendered as a phantom duplicate until the cache expired.- Fixed Advisor requests reaching Anthropic-compatible endpoints without a provider-facing session identity: the separately constructed advisor
Agentnever had a metadata resolver installed, so its outbound requests omitted themetadata.user_idsession id that the main and subagent agents carry. Each advisor now emits its ownadvisorProviderSessionIdviametadata.user_id, resolved live so a token refresh surfaces the currentaccount_uuid, giving Main, subagent, and Advisor traffic distinct, stable session ids for proxy routing and attribution (#6625). - Todo progress now stays in sync when using Cursor models: the Cursor exec bridge mirrors the provider's server-owned todo list into session state, refreshes the interactive todo panel, and persists each snapshot to the session branch so the list survives reloads, rewinds, compaction, and session switches. Existing phase grouping is preserved for tasks the session already knows. Previously the list was in-memory only and the panel stayed stale, because Cursor resolves the todo tool remotely and never emits the local
todotool result that both paths key off. - Cursor todo calls the server refuses or rejects no longer leave the todo card spinning: the bridge settles every completed native todo call, not just the ones carrying a list. Local phases and the session branch are left untouched in that case, and the settling result deliberately carries no
details.phases— echoing the current list back would let a call that changed nothing overwrite live panel state. - Fixed the todo renderer emitting mirrored label text verbatim. A Cursor snapshot carries provider-authored task content, phase names, and summary text, and the renderer interpolated all of it straight into terminal output, so a label holding ANSI/C0 sequences rewrote the terminal every time the list rendered or replayed. Every display path now goes through one sanitize-and-flatten-tabs helper — task labels, blocker notes, phase headers, the zero-task fallback, and the streaming call preview — while the raw values stay untouched as the lookup keys they are.
- Fixed a server-resolved Cursor todo card animating for the rest of the session when the server packed the call's start and completion into one HTTP/2 chunk. The bridge's
tool_execution_endis a synchronous callback fired mid-parse, while the streamedtoolcall_startthat creates the visible card is queued on the event stream and delivered a microtask later — the interactive controller handled the completion first, found no pending card, and dropped it, leaving the card that appeared moments later with nothing to settle it. An early completion is now held and attached the moment the streamed block creates its card, settling it without repeating the panel refresh or failure warning that already fired on first arrival. Card creation from cumulativemessage_updateframes is also guarded by the turn's timeline map, so a call settled mid-stream can no longer be recreated as a second, permanently pending card by the next update re-listing the same block. - Cursor todo failures no longer render unsanitized provider text into the status line. The bridge forwards the server's error string verbatim, so an ANSI escape or other C0/C1 control reached the terminal intact and could repaint outside the row, tabs punched holes in the single-line warning, and a long message overflowed it. The detail is now stripped of control sequences, collapsed, and truncated at the render boundary; the persisted result keeps the full-fidelity error for the transcript.
- Fixed disabling the Advisor from
/settingsupdating the persisted setting without stopping the live Advisor runtime until the session restarted:SelectorController.handleSettingChangehad no case foradvisor.enabled, unlike other session-managed toggles (autoCompact,steeringMode, ...), so the change never reachedsession.setAdvisorEnabled. - Fixed
bash.patternsdeny/promptrules matching only against the whole command string, so a dangerous command in any non-leading position of a compound line (e.g.cd /tmp && rm -rf /tmp/x,sleep 1 & rm -rf /tmp/x) silently bypassed adenyrule and, underapprovalMode: yolo, executed with no prompt.deny/promptrules now also match each command segment, split with a shell-aware tokenizer that honors every command boundary (&&,||,;,|, single&, subshells, newlines) and quoting;allowrules still require the whole command to match and never apply to compound lines (#6695). - Fixed
omp config listprinting credential settings in plain text.auth.broker.token,searxng.token,searxng.basicPasswordanddev.autoqaPush.tokenwere disclosed in both the human and--jsonoutput of a command that dumps every value without anyone asking for a specific credential. Credentials are now marked in the schema with a top-levelcredentialflag, which also covers settings that have no settings-panel entry and so cannot useui.secret. Human output shows dots; JSON omitsvalueand marks the entryredactedrather than substituting a placeholder a consumer could write back.omp config get <path>is unchanged, since that is an explicit request for one value. The settings panel now derives masking from the same flag, so a credential cannot render as plain text on one surface and dots on the other. Only a credential that is actually set is redacted, so a fresh configuration still reports unset credentials as unset rather than implying every one of them is configured. - Fixed
/new,/drop,/fork, and/movecrashing or doing unnecessary work when invoked during vibe mode; interactive session transitions now show the existing exit-vibe warning and leave the session unchanged, and reset loops disable themselves instead of resubmitting into that unchanged session (#6607). - Fixed legacy pi extensions failing extension validation when importing
estimateTokensfrom@earendil-works/pi-coding-agent(aliased to the legacy shim). Legacy pi re-exportedestimateTokensfrom its coding-agent package root; in omp it lives in@oh-my-pi/pi-agent-core/compactionand the coding-agent barrel does not forward it, so the shim'sexport * from "../index"left it off the surface and a named import threw Bun's static "Export named 'estimateTokens' not found" error (e.g.omp plugin install pi-blackhole). The shim now re-exports it (#6583). - Fixed plan approval presenting a completed plan instead of the newest draft when the submitted title did not match the draft filename (#6569).
- Fixed
omp auth-gateway serveadvertising only the compiled-in bundled catalog, so every model omp reaches through provider discovery (e.g. ids released after the build date) was invisible on/v1/modelsand returnedUnknown modelthrough/v1/chat/completionseven though the same broker credential answered it in the TUI. The gateway now sources its catalog fromModelRegistry— the same component the TUI/CLI use (bundled + cached + discovered) — keeping the credential scoping and qualified/bare-id registration, and rebuilds it periodically so a long-livedservetracks newly discovered models without a restart (#6615). - Fixed screenshot-relative pointer actions missing their visible targets when image transports that cannot preserve original detail silently downscaled a large computer screenshot; affected transports now establish the native coordinate frame below the verified image-resize threshold without changing the public capture defaults for other models (#6596 by @wolfiesch).
- Corrected Windows shell resolution errors to identify the active global, project, overlay, or runtime source for
shellPath, including profile and custom configuration directories, instead of directing every user to the retiredsettings.jsonfile (#6579). - Fixed
debug(js-debug/pwa-node) stateful commands misrouting after launch: a lazily-attached[worker N]child session (or the threadless root launcher) would steal the active-session focus from the stopped script child, sothreadslisted only the worker thread, post-launch breakpoints read back as pending/unbound, and there was no way to step/continue/evaluate the script's thread. Focus now follows stops rather than registrations, andthreadsaggregates every live thread across the session tree (#6663). - Fixed a turn-ending provider error being truncated to 8 lines in the transcript with no way to reveal the rest:
AssistantMessageComponentnow implementssetExpanded, so Ctrl+O (tool-output expansion) reveals the full error body and the collapsed view shows a… +N more lines (Ctrl+O to expand)hint (#6555). - Fixed direct binary updates trusting an executable that only reported the expected version. The updater now selects one exact asset from the tagged GitHub release, requires its published SHA-256 digest and size, and verifies both while streaming the download before installation. GitHub release metadata requests use
GITHUB_TOKENorGH_TOKENwhen available, allowing users behind an exhausted anonymous rate limit to authenticate. - Documented that the non-PTY shell's bundled
jqcommand is backed by jaq, including its null-indexing divergence and portable filter syntax (#6614). - Fixed
omp://tools/task.mdandomp://tools/eval.mddrifting from the 17.1.3 runtime:task.mdclaimed subagents force-disableasync.enabled/bash.autoBackground.enabled(both are inherited from the parent since 17.1.0) and omitted thetasktool'seffortparameter, andeval.mdomitted the still-working evalagent(model=…)per-call model selector (#6594). - Fixed advisor retry amplification after transient Codex SSE socket closures by limiting each advisor-level try to one provider transport attempt.
- Fixed
omp updateaborting withnpm error EEXISTon standalone binary installs whose directory coincides with the global npm/bun bin dir (for examplenpm prefix -gset to~/.local, which the installer also targets). The install-target resolver classified the binary as npm/bun-managed from directory containment alone, sonpm install -gtried to replace a regular file its symlink step would clobber; it now treats a plain executable (not a symlink) in a package-manager bin dir as the standalone binary and self-updates it in place (#6527 by @am423). - Fixed Codex subscription and proxy models being sent the unsupported native
{ type: "computer" }declaration based only on model ID. They now receive the callable function-tool fallback, including after switching from native OpenAI Responses history, while explicit endpoint metadata can still opt into the GA contract. Explicit native Codex replays preservecomputer_call/computer_call_outputpairing, normal CLI startup keeps the native desktop worker graph lazy, and packaged workers re-enter the single CLI host without the computer module claiming non-computer selectors. - Fixed isolated JavaScript eval subprocesses letting the global fatal-rejection handler race the cell rejection interceptor. A floated promise rejection is now folded into the owning cell result without killing its reusable worker process.
- Fixed
/contextcounting hidden, explicit-only skills (hide: true/disable-model-invocation) in the Skills category and subtracting that inflated estimate from the first system-prompt block, which reportedSystem prompt: 0 tokensand inflated Skills usage. Accounting now counts only the skills actually rendered into the system prompt — mirroringbuildSystemPrompt's filter, so hidden skills and all skills when thereadtool is unavailable contribute zero (#6498). - Fixed
pi-spritefailing plugin validation because the legacy Pi compatibility shims omittedcreateExtensionRuntimeand terminal capability/image-deletion helpers used by the extension (#6506). - Fixed Escape waiting for an in-flight
session_stopextension handler to exhaust its timeout; abort now cancels the active stop pass without reporting a false timeout or applying stale continuation context (#6489). - Fixed the agent not resuming after re-answering a past
askfrom the session tree. Committing a new answer via/treebranched a fresh siblingtoolResultand rebuilt context, but nothing ever continued the agent — unlike a liveask, whose continuation is intrinsic to the streaming run loop — so the model never consumed the new answer and the session sat idle until a manual prompt.navigateTreenow reports the commit (askReanswerCommitted) and the interactive/treehandler resumes the agent viaresumeAfterAskReanswer()after its transcript rebuild, so the resumed turn never renders against the stale pre-rebuild UI. Plain leaf moves and the read-onlyreopenAskprobe stay idle (#6483). - Fixed Ctrl+C and fatal shutdown entering an
ExtensionExitErrorrejection loop while an extension or hook was still loading (#6488).
@oh-my-pi/pi-natives
Added
- Added the
@oh-my-pi/pi-natives/desktopfactory entry, which defers native addon loading until a desktop worker initializes its session.
Fixed
- Fixed Linux native audio over forwarded PulseAudio servers: capture now handles 125 ms Android fragments without stalling, and playback buffers enough audio to avoid TCP underruns and stuttering (#6628 by @anatoli-tsinovoy).
- Fixed older running OMP versions deleting newer native addon cache directories during cleanup, which could race a new version's first-run extraction and crash with
ENOENT. - Fixed macOS computer screenshots occasionally returning the pre-action frame instead of reflecting completed keyboard and pointer input (#6595 by @wolfiesch).
@oh-my-pi/pi-tui
Fixed
- Prevented inline Kitty graphics from covering full-width overlays such as
/switch. - Fixed Ctrl+O (expand tools) truncating the session on ConPTY hosts (native Windows and WSL): the full-view replay routed through the ConPTY frame-truncation intended only for bulk transcript-replacement paints (issue #2115), dropping every row above the retained tail behind an "older lines hidden" marker. The bound now keys on paint intent — bulk replacements (initial resume,
/resume, handoff, resize geometry rebuilds) stay bounded, while a user-drivenresetDisplay()(Ctrl+O expand, thinking/setting toggles, display reset) replays the whole transcript (#4863).
@oh-my-pi/pi-utils
Fixed
getShellConfigno longer throwsNo bash shell foundon Windows hosts without a discoverable bash.resolveWindowsShellsearches Git for Windows install roots (machine, per-user,GIT_INSTALL_ROOT, scoop app dirs — scoop shimssh.exe/git.exebut neverbash.exe), thenbash.exe/sh.exeon PATH, and finally falls back tocmd.exefrom ComSpec with/cargs, so shell resolution always succeeds.- Fixed postmortem signal and fatal shutdown exits being intercepted by temporary
process.exitguards during extension startup (#6488). - Corrected Windows shell resolution errors to identify the active global, project, overlay, or runtime source for
shellPathinstead of directing every user to the retiredsettings.jsonfile (#6579). - Contained timed-out child lifecycle rejections so
ptreecallers cannot leak an unhandledTimeoutErrorafter settling (#6635). - Fixed an invalid configured
shellPathbeing silently masked whenever an earlier caller had already resolved a shell in the same process; the guidance error now surfaces regardless of cache state.
What's Changed
- fix(coding-agent): source auth-gateway catalog from ModelRegistry by @roboomp in #6618
- fix(catalog): retry empty model discovery by @roboomp in #6622
- fix(advisor): propagate advisor provider session id via metadata by @roboomp in #6626
- fix(natives): stabilize forwarded PulseAudio capture and playback by @anatoli-tsinovoy in #6628
- fix(utils): contain ptree timeout rejections by @roboomp in #6644
- fix(secrets): avoid hashline placeholder collisions by @roboomp in #6646
- fix(coding-agent): restore legacy pi automode compatibility by @roboomp in #6649
- feat(ai): report MiniMax Token Plan quota in usage by @everton-dgn in #6650
- fix(prewalk): apply same-model effort downgrades instead of skipping by @roboomp in #6660
- fix(advisor): notify the user when a quarantined turn drops advice by @roboomp in #6662
- fix(coding-agent): route js-debug commands to the stopped script child by @roboomp in #6665
- fix(catalog): keep reference-less Copilot Claude models reasoning-capable and cache-stable by @roboomp in #6666
- fix(ai): break anthropic oauth↔providers circular import (claudeCodeVersion TDZ) by @oldschoola in #6667
- fix(session): retry reasonless tool-call aborts by @roboomp in #6669
- fix(providers): add China (Beijing) region for alibaba-token-plan by @roboomp in #6683
- fix(stt): resolve Windows workspace sherpa addon by @roboomp in #6691
- fix(coding-agent): match bash deny/prompt patterns per command segment by @roboomp in #6697
- fix(cli): defer out-of-scope default role so extension models resolve by @roboomp in #6698
- fix(coding-agent): stop the live advisor runtime when /settings disables it by @paolomazzitti in #6701
- fix(tui): keep full-width overlays above inline images by @oleksoleksoleks in #6471
- fix(coding-agent): resume agent after /tree ask re-answer by @roboomp in #6484
- fix(coding-agent): bypass extension guards during shutdown by @roboomp in #6493
- fix(session): cancel in-flight session_stop handlers by @roboomp in #6494
- fix(coding-agent): count only rendered skills in /context accounting by @roboomp in #6500
- fix(cursor): synthesize missing exec MCP tool calls by @roboomp in #6502
- fix(ai): retry statusless provider capacity errors by @roboomp in #6505
- fix(plugins): restore pi-sprite legacy compatibility by @roboomp in #6507
- fix(ai):strip output-only statuses from Responses replay by @Ant39140 in #6513
- fix(ai): repair Alibaba Token Plan usage reporting by @eggpeat in #6521
- fix(coding-agent): self-update binary install when its dir overlaps npm/bun bin dir by @am423 in #6527
- fix(computer-use): correct Codex subscription routing by @usr-bin-roygbiv in #6533
- fix(advisor): bound Codex SSE retries by @jeffscottward in #6537
- fix(ai): stop one Codex window's limit from blocking the others by @paralin in #6541
- feat(agent): wake steering on an event instead of polling for it by @paralin in #6544
- fix(tui): stop Ctrl+O expand truncating the session on ConPTY by @roboomp in #6553
- fix(omp): fit native compaction after large tool output by @oleksoleksoleks in #6556
- fix(update): verify GitHub release asset and digest by @rvagg in #6557
- fix(tui): make provider error blocks expandable via ctrl+o by @roboomp in #6558
- fix(cursor): embed data URI for images in root-prompt history by @sethmorton in #6564
- fix(plan-mode): prefer newest draft during review by @roboomp in #6570
- fix(utils): correct shell path config guidance by @roboomp in #6581
- fix(tui): attach drag-dropped image paths with unescaped spaces by @rcbran in #6582
- fix(coding-agent): re-export estimateTokens from legacy pi shim by @roboomp in #6584
- fix(cli): redact credential settings in config list by @wolfiesch in #6588
- fix(ai): disable adaptive-only thinking on disableReasoning and forced tool choice by @roboomp in #6590
- fix(natives): settle macOS input before capture by @wolfiesch in #6595
- fix(coding-agent): preserve Claude computer coordinates by @wolfiesch in #6596
- docs: reconcile omp:// task/eval docs with 17.1.3 runtime by @roboomp in #6597
- feat(catalog): add Claude Opus 5 entries for Amazon Bedrock by @k1riiiii in #6599
- fix(catalog): downgrade forced tool choice for thinking-locked Kimi Code aliases by @voidfreud in #6601
- fix(natives): preserve newer cache versions by @wolfiesch in #6606
- fix(coding-agent): handle vibe session commands safely by @roboomp in #6608
- fix(ai): preserved Bedrock ARN thinking signatures by @roboomp in #6613
- fix(cursor): sync native todo list from server-resolved tool calls by @quantmind-br in #6616
- docs(bash): document bundled jaq divergence by @roboomp in #6617
New Contributors
- @everton-dgn made their first contribution in #6650
- @paolomazzitti made their first contribution in #6701
- @Ant39140 made their first contribution in #6513
- @am423 made their first contribution in #6527
- @rvagg made their first contribution in #6557
- @sethmorton made their first contribution in #6564
- @rcbran made their first contribution in #6582
- @k1riiiii made their first contribution in #6599
- @voidfreud made their first contribution in #6601
- @quantmind-br made their first contribution in #6616
Full Changelog: v17.1.3...v17.1.4