Skip to content

v17.1.4

Latest

Choose a tag to compare

@github-actions github-actions released this 26 Jul 18:55
f8dbb36

@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.waitForSteeringMessages resolves 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 cursorOnToolResult transformer was still pending as the turn closed. The provider dispatches decoded messages without awaiting them, so a message_end from the same chunk could drain the buffer before the transformer resolved, dropping the result and leaving its toolCall block 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 cursorOnToolResult transformer'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 cursorExecHandlers nor cursorOnToolResult. 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 cursorOnToolResult rewrite 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/remains returns one bucket per plan quota, each carrying a rolling interval window and a weekly window, so minimax-code surfaces 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 id minimax-code-cn is 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 as invalid_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. Exported ANTHROPIC_OAUTH_GRANT_TTL_MS alongside the anthropic OAuth flow.
  • Added GET /v1/credentials/disabled to the auth broker and AuthBrokerClient.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.listDisabledCredentials serves 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 optional AuthCredentialStore.refreshSnapshot hook: remote broker stores re-fetch GET /v1/snapshot on 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 codexSseMaxAttempts stream 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: rootPromptMessagesJson image parts now embed a data:<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 status fields back as input, preventing input[N].status failures 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" with isError: false, even for a rejected or error result — 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 the success variant as is_error rather than as a separate variant.
  • Fixed Cursor models silently failing to maintain the todo list. Cursor resolves its native update_todos/read_todos tools server-side, but the bridge looked for them under flattened updateTodosToolCall/readTodosToolCall properties, which a decoded agent.v1.ToolCall never has — the variant only arrives through the tool oneof — so no native todo call was ever recognized. The synthesized todo tool 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 on UpdateTodosError). TODO_STATUS_CANCELLED now maps to abandoned instead of reverting the task to pending.
  • Hardened Cursor todo mirroring against partial read_todos responses: a read narrowed by status_filter/id_filter, or one returning fewer rows than the server's own total_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_todos response whose total_count is 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 empty read_todos stays refused outright, since proto3 decodes an unset total_count as 0 and 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_call at all. ToolCallCompletedUpdate.tool_call is 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.tool is a protobuf oneof, so a decoded message exposes the variant as { case, value } and never as a flat mcpToolCall property — 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 name while its paired result used toolName, so the two disagreed whenever the server sent different values. Both now prefer toolName.
  • Fixed the Cursor stream emitting done while 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, turnEnded and 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 toolResult for them, and buildSessionContext strips any toolCall left unpaired, so the interaction vanished on reload, branch switch, or transcript rebuild. The result the host builds is now persisted verbatim — it carries the details.phases the todo renderer rebuilds the list from, which a summary-only result would have replayed as 0 tasks.
  • Fixed a refused or failed Cursor todo call leaving its card animating forever. Only a successful snapshot settled the block, so a read_todos narrowed by a filter and a server UpdateTodosError both went unanswered — no tool_execution_end, and no toolResult to 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 id and can represent two rows sharing the same text; the local list is keyed by content alone and the todo tool rejects a duplicate outright, so importing such a pair would leave every task-targeted done/drop/rm resolving 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_todos responses. total_count is a proto3 scalar, so an unset field decodes as 0 and is indistinguishable from a genuinely empty list; accepting todos=[] + total_count=0 would clear every local task. Empty and mismatched reads are now refused — update_todos remains the authoritative clear path.
  • Fixed refused Cursor todo results claiming "No todo changes". A server-accepted update_todos can 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 by id; the local list has no ids and no edges, so an imported dependent row files as plain pending and nextActionableTask then offers work the server considers blocked. Snapshots with an edge pointing at a row that is not yet completed/abandoned are 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_count mismatch guard to update_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. content is 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-targeted done/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 with ReferenceError: Cannot access 'claudeCodeVersion' before initialization: registry/oauth/anthropic.ts imported claudeCodeVersion from providers/anthropic.ts, which transitively pulls the registry back in (providers/anthropicstreamregistryregistry/oauth/anthropic), so the module-level claude-code/${claudeCodeVersion} bootstrap user-agent const read the binding while providers/anthropic.ts was still mid-initialization. claudeCodeVersion now lives in a zero-import leaf module (providers/claude-code-fingerprint.ts) that providers/anthropic.ts, registry/oauth/anthropic.ts, and usage/claude.ts all 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 claudeCodeVersion was initialized when package tests or consumers loaded modules in parallel (#6628 by @anatoli-tsinovoy).
  • Stopped the account-level Codex rate_limit.limit_reached flag from being applied to individual chat windows. Codex reports one shared flag for the whole account, so a window with real headroom was marked exhausted because 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_reached from 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 scopeLimits for the Codex ranking strategy so a request gates only on the windows it actually consumes: -spark models 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. mapOptionsForApi never consulted disableReasoning on the Anthropic branch, so a caller-side disable left adaptive thinking at full effort; and disableThinkingIfToolChoiceForced deleted output_config.effort alongside thinking, which for adaptive-only models silently re-enabled adaptive thinking (a bare omission defaults to adaptive-ON). Both paths now omit thinking and pin the lowest adaptive effort, so disableReasoning and forced tool_choice turns (e.g. the delivery reviewer's report_delivery) actually suppress reasoning instead of returning a thinking block with end_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-plan login only supporting the international Singapore endpoint, which rejected China (Beijing) Token Plan sk-sp- keys with 401 invalid_api_key. Login now selects a region (International / China (Beijing) / Custom), validates the key against that region's /models endpoint, 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_capacity and 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_search omitting toolCall blocks 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)) with is_active: false being dropped by the /usage parser, rendering as not reported in omp usage despite 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 — so is_active signals 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) when providers/anthropic was the first module loaded: providers/anthropicstreamregistryregistry/oauth/anthropic circled back into the still-initializing provider module. The Claude Code fingerprint constants now live in the leaf module providers/claude-code-fingerprint (star re-exported from providers/anthropic, so import paths are unchanged).

@oh-my-pi/pi-catalog

Added

  • Added Claude Opus 5 model entries for Amazon Bedrock: anthropic.claude-opus-5 plus its us., eu., au., and global. regional/geo IDs.

Fixed

  • Fixed alibaba-token-plan locking out China (Beijing) 百炼 Token Plan subscribers: the provider hardcoded the international Singapore endpoint, so Beijing-issued sk-sp- keys got 401 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_choice 400s (tool_choice 'specified' is incompatible with thinking enabled) on Kimi Code's Anthropic-compatible endpoint for the kimi-for-coding, kimi-for-coding-highspeed, and k3 aliases: the Anthropic-surface compat matcher only recognised Moonshot's native kimi-k2.7-code* ids, so thinking-locked kimi-code models kept supportsForcedToolChoice: true and the forced selector was sent to a host that always thinks. These models now resolve requiresThinkingEnabled, keeping thinking on and downgrading forced choices to auto.
  • 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 with reasoning: false/thinking: null and no effort dial, and disappearing along with their synthesized -1m sibling 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-time COPILOT_API_HEADERS by value instead of dropping them as unrestorable (#6664).

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

Added

  • omp usage now 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 with GET /v1/credentials/disabled; older brokers degrade to no tombstone rows.
  • omp usage warns 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 omp refusing to start on Windows when no bash.exe is discoverable — most visibly with scoop-installed Git, whose manifest shims sh.exe/git.exe but never bash.exe, so PATH lookup missed it. Startup threw No bash shell found while 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 checks GIT_INSTALL_ROOT, scoop and per-user Git for Windows install roots, and sh.exe on PATH, then falls back to cmd.exe for 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-automode failing legacy extension validation because the pi-ai compatibility shim omitted clampThinkingLevel, then failing every classified tool call because ctx.modelRegistry omitted getApiKeyAndHeaders. (#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 a notifyFailure warning in the main UI (deduped, cleared on the next successful turn) and stops the unbounded silent re-prime loop (#6661).
  • Fixed the Docker natives-builder stage 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.default naming an extension-registered model (listed in enabledModels) silently running on a different in-scope provider's model. The startup model scope is resolved before extensions call registerProvider(), so the default role dropped out of scope and buildSessionOptions pinned options.model to 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, still enabledModels-scoped catalog once extensions load (#6694).
  • Fixed Parakeet speech-to-text failing to load sherpa-onnx-node from Windows source workspaces when Bun installed the wrapper under packages/coding-agent/node_modules but hoisted its native platform package to the repository root (#6690).
  • Fixed omp usage duplicating 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 usage revalidates 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 Agent never had a metadata resolver installed, so its outbound requests omitted the metadata.user_id session id that the main and subagent agents carry. Each advisor now emits its own advisorProviderSessionId via metadata.user_id, resolved live so a token refresh surfaces the current account_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 todo tool 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_end is a synchronous callback fired mid-parse, while the streamed toolcall_start that 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 cumulative message_update frames 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 /settings updating the persisted setting without stopping the live Advisor runtime until the session restarted: SelectorController.handleSettingChange had no case for advisor.enabled, unlike other session-managed toggles (autoCompact, steeringMode, ...), so the change never reached session.setAdvisorEnabled.
  • Fixed bash.patterns deny/prompt rules 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 a deny rule and, under approvalMode: yolo, executed with no prompt. deny/prompt rules now also match each command segment, split with a shell-aware tokenizer that honors every command boundary (&&, ||, ;, |, single &, subshells, newlines) and quoting; allow rules still require the whole command to match and never apply to compound lines (#6695).
  • Fixed omp config list printing credential settings in plain text. auth.broker.token, searxng.token, searxng.basicPassword and dev.autoqaPush.token were disclosed in both the human and --json output of a command that dumps every value without anyone asking for a specific credential. Credentials are now marked in the schema with a top-level credential flag, which also covers settings that have no settings-panel entry and so cannot use ui.secret. Human output shows dots; JSON omits value and marks the entry redacted rather 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 /move crashing 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 estimateTokens from @earendil-works/pi-coding-agent (aliased to the legacy shim). Legacy pi re-exported estimateTokens from its coding-agent package root; in omp it lives in @oh-my-pi/pi-agent-core/compaction and the coding-agent barrel does not forward it, so the shim's export * 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 serve advertising 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/models and returned Unknown model through /v1/chat/completions even though the same broker credential answered it in the TUI. The gateway now sources its catalog from ModelRegistry — 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-lived serve tracks 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 retired settings.json file (#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, so threads listed 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, and threads aggregates 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: AssistantMessageComponent now implements setExpanded, 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_TOKEN or GH_TOKEN when available, allowing users behind an exhausted anonymous rate limit to authenticate.
  • Documented that the non-PTY shell's bundled jq command is backed by jaq, including its null-indexing divergence and portable filter syntax (#6614).
  • Fixed omp://tools/task.md and omp://tools/eval.md drifting from the 17.1.3 runtime: task.md claimed subagents force-disable async.enabled/bash.autoBackground.enabled (both are inherited from the parent since 17.1.0) and omitted the task tool's effort parameter, and eval.md omitted the still-working eval agent(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 update aborting with npm error EEXIST on standalone binary installs whose directory coincides with the global npm/bun bin dir (for example npm prefix -g set to ~/.local, which the installer also targets). The install-target resolver classified the binary as npm/bun-managed from directory containment alone, so npm install -g tried 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 preserve computer_call/computer_call_output pairing, 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 /context counting 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 reported System prompt: 0 tokens and inflated Skills usage. Accounting now counts only the skills actually rendered into the system prompt — mirroring buildSystemPrompt's filter, so hidden skills and all skills when the read tool is unavailable contribute zero (#6498).
  • Fixed pi-sprite failing plugin validation because the legacy Pi compatibility shims omitted createExtensionRuntime and terminal capability/image-deletion helpers used by the extension (#6506).
  • Fixed Escape waiting for an in-flight session_stop extension 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 ask from the session tree. Committing a new answer via /tree branched a fresh sibling toolResult and rebuilt context, but nothing ever continued the agent — unlike a live ask, 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. navigateTree now reports the commit (askReanswerCommitted) and the interactive /tree handler resumes the agent via resumeAfterAskReanswer() after its transcript rebuild, so the resumed turn never renders against the stale pre-rebuild UI. Plain leaf moves and the read-only reopenAsk probe stay idle (#6483).
  • Fixed Ctrl+C and fatal shutdown entering an ExtensionExitError rejection loop while an extension or hook was still loading (#6488).

@oh-my-pi/pi-natives

Added

  • Added the @oh-my-pi/pi-natives/desktop factory 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-driven resetDisplay() (Ctrl+O expand, thinking/setting toggles, display reset) replays the whole transcript (#4863).

@oh-my-pi/pi-utils

Fixed

  • getShellConfig no longer throws No bash shell found on Windows hosts without a discoverable bash. resolveWindowsShell searches Git for Windows install roots (machine, per-user, GIT_INSTALL_ROOT, scoop app dirs — scoop shims sh.exe/git.exe but never bash.exe), then bash.exe/sh.exe on PATH, and finally falls back to cmd.exe from ComSpec with /c args, so shell resolution always succeeds.
  • Fixed postmortem signal and fatal shutdown exits being intercepted by temporary process.exit guards during extension startup (#6488).
  • Corrected Windows shell resolution errors to identify the active global, project, overlay, or runtime source for shellPath instead of directing every user to the retired settings.json file (#6579).
  • Contained timed-out child lifecycle rejections so ptree callers cannot leak an unhandled TimeoutError after settling (#6635).
  • Fixed an invalid configured shellPath being 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

Full Changelog: v17.1.3...v17.1.4