@oh-my-pi/pi-agent-core
Added
- Added
hasSteeringMessagestoAgentLoopConfig(wired byAgentto its steering queue): a peek used by the immediate-interrupt poll during tool execution, so the loop can detect queued steering without dequeuing and the queue keeps owning its messages until the injection boundary - The agent loop now re-samples after a non-terminal stop (
stopReason: "stop"withstopDetails: { type: "pause_turn" }, emitted by the Codex providers forend_turn: falsecommentary-only responses): the assistant message is committed to history and the model is called again without ending the turn. Consecutive pause continuations without an intervening tool call are capped at 8 to bound a backend that never stops pausing.
Changed
- Changed steering handling so queued steering messages are now dequeued only at injection boundaries, with immediate mid-batch interrupt polling using
hasSteeringMessages. Consumers constructingAgentLoopConfigdirectly with onlygetSteeringMessagesno longer get mid-batch interrupts — steering degrades to boundary-only delivery until they also supplyhasSteeringMessages - Compaction, handoff, short-summary, and branch-summarization helpers now accept an
ApiKey(static string or resolver) instead of a pre-resolved string, so a 401 mid-compaction force-refreshes and rotates the credential through the central auth-retry policy before any model-level fallback. The remote OpenAI compaction request is wrapped inwithAuthand its HTTP failures now carry.status, so the retry classifier actually fires on remote-compaction 401s. transformProviderContextnow receives the dispatch model as a second argument ((context, model) => Context), so per-request transforms can gate on model capabilities (vision input, provider, API family). Existing single-argument implementations keep working unchanged.- Remote-compaction and summarization failures now throw pi-ai's typed
ProviderHttpErrorinstead of mutating plainErrors with a.statusproperty; the genericrequestRemoteCompactionerror now carries.status(and response headers) too.
Fixed
- Fixed a regression where steering messages could be injected into history during an aborted in-flight tool batch, leaving them hidden from queue consumers for post-abort continue
@oh-my-pi/pi-ai
Added
- Codex/Responses providers now map
end_turn: falseon the terminal stream event (Codex backend signal for "response ended, turn didn't" — commentary-only progress updates) tostopDetails: { type: "pause_turn" }with stopReason"stop", so the agent loop can re-sample instead of ending the turn. Wired inopenai-codex-responsesandprocessResponsesStream(openai-responses/azure-openai-responses); inert for backends that never send the field. - Added Codex upstream protocol features to
openai-codex-responses(tracking codex-rs as of June 2026):onModerationMetadatacallback surfacingresponse.metadata→openai_chatgpt_moderation_metadataon both transports;reasoningContextoption emittingreasoning.context(auto/current_turn/all_turns);clientMetadataoption emittingclient_metadatain the request body (canonicalx-codex-turn-metadataenvelope) without breaking the websocket append fast-path; and an opt-inresponsesLitemode mirroring codex-rs — lite header on HTTP requests and the websocket upgrade,ws_request_header_*marker inresponse.createclient metadata, lite-keyed socket pooling, image-detail stripping, forced serial tool calls, andreasoning.context: all_turnsdefault. Dormant until OpenAI flipsuse_responses_litein the model catalog. - Added
withOAuthAccess— thewithAuthcounterpart for OAuth-access consumers: runs an operation through the central a/b/c auth-retry policy (resolve → force-refresh same account → rotate to a sibling) while handing the attempt the fullOAuthAccess(bearer plusaccountId/projectId/enterpriseUrlidentity metadata). Use it instead of hand-rolledgetOAuthAccess+ fetch flows so 401s and usage-limits rotate credentials instead of failing the call. - Added
ProviderHttpError— a typed HTTP error carryingstatus,headers, andcode— replacing the ad-hocas Error & { status?... }/Object.assignhacks at provider throw sites, with per-provider subclassesCodexApiError,AuthGatewayError,GoogleApiError,GeminiCliApiError,OllamaApiError, andBedrockApiError;AnthropicApiErrornow extends it. Google, Gemini CLI, Ollama, and Bedrock HTTP errors now also carry response headers, so server-suggestedretry-afterdelays are visible to retry classification on those paths. The internalwithHttpStatushelper was removed. - Added stateful SSE turn chaining for OpenAI Codex (on by default; disable with
PI_CODEX_STATEFUL=0orstatefulResponses: false): SSE requests now reuseprevious_response_idwith delta-only input instead of replaying the full transcript, mirroring the websocket fast-path via a shared transport-aware builder. Any history mutation or option change falls back to a full replay; a server-sideprevious_response_not_found(HTTP or in-stream) resets the chain and retries the turn with full context, and three consecutive stale failures disable chaining for the session. - Added stateful
previous_response_idchaining to the platform OpenAI Responses provider (openai-responses): on by default against the official api.openai.com endpoint (forcesstore: true, which chaining requires), off for other Responses endpoints; override withstatefulResponsesorPI_OPENAI_STATEFUL. Chain detection compares the wire form of the conversation arguments alone — per-turn trailing scaffolding such as the GPT-5 "Juice: 0" developer item is excluded from the append-baseline prefix check and re-appended to the delta — and a rejected/stale previous response falls back to a one-shot full replay with the same circuit breaker. - Added
AuthStorage.getOAuthAccountIdentity()and theOAuthAccountIdentitytype — a read-only lookup returning theaccountId/email/projectIdof the OAuth credential a session is currently routed to, for display and metadata paths.
Changed
- The GPT-5 "Juice: 0" no-reasoning developer item in
applyResponsesReasoningParamsis now gated on the resolvedcompat.requiresJuiceZeroHackflag (auto-detected from GPT-5-family model names by@oh-my-pi/pi-catalog, overridable per model) instead of an inline model-name check.
Fixed
- Fixed websocket append fast-path to remain usable when only
client_metadatachanges between turns - Fixed
onModerationMetadatahandling so exceptions thrown by callback observers no longer terminate the response stream - Fixed local SQLite OAuth credential caches returning a stale Anthropic access token after another
ompprocess refreshed and persisted the same row.AuthStoragenow syncs the selected row from storage before returning or force-refreshing OAuth credentials, so concurrent sessions pick up peer-rotated tokens instead of surfacing a one-turn401 Invalid authentication credentials. - Fixed forced OAuth preflight refresh failures being swallowed silently in credential selection; they now emit a debug log (
OAuth preflight refresh failed) so stale-refresh-token replays from concurrent sessions are diagnosable.
@oh-my-pi/pi-catalog
Fixed
- Fixed MiniMax M2-family and OpenAI gpt-oss model metadata so OpenAI-compatible catalog entries declare only
low|medium|highthinking efforts. Their upstreams rejectminimal,xhigh, and Fireworks'minimal → nonewire mapping, sofireworks/minimax-m2.7as the smol auto-thinking classifier model 400ed on every turn. OpenAI-compatible provider effort maps (Groq qwen/qwen3-32b, DeepSeek-family, OpenRouter Anthropic adaptive, Fireworksminimal → none) now bake intothinking.effortMapin catalog metadata instead ofbuildOpenAICompat, and request builders read that field directly. Regeneratedmodels.jsonnow makesdisableReasoningchooselowfor those families while leaving GLM-5.x and other Fireworks models on the existingminimal → nonepath (#2315).
Added
- Added
requiresJuiceZeroHackResponses-API compat flag, resolved bybuildOpenAIResponsesCompatfrom GPT-5-family model names and overridable via sparse modelcompatconfig. Replaces the request-timemodel.name.startsWith("gpt-5")sniff that gated the trailing# Juice: 0 !importantno-reasoning developer item.
@oh-my-pi/pi-coding-agent
Added
- Added mouse-driven interaction to
/settings, including tab and setting row hover highlighting, wheel scrolling, and left-click activation for entries and submenus - Added fullscreen
/settingsmouse-event handling so scrolling and clicks work in an alternate-screen overlay ModelRegistry.resolvernow accepts a model directly —resolver(model, sessionId)— derivingprovider,baseUrl, andmodelIdfrom it; all model-scoped call sites migrated from the verboseresolver(model.provider, { sessionId, baseUrl, modelId })form.- Added experimental
snapcompact.systemPromptandsnapcompact.toolResultssettings (off by default,/settings→ Context → Experimental) that render the system prompt and large historical tool results as dense snapcompact PNG frames on vision-capable models to cut token cost. Frames are built per-request in the provider-context transform, cached across turns, capped by a per-provider image budget, and gated on a token-savings estimate — they never reachsession.jsonl. - Added a Personality selector to
/settings(Model → Prompt):default(the previous built-in reply style),friendly,pragmatic, ornone. The selected spec renders into a dedicated<personality>system-prompt block (extracted from the former<reply-guidelines>section) and applies to the live session immediately; subagents always omit the block. - Added
mnemopi.polyphonicRecallandmnemopi.enhancedRecallconfig.yml settings (off by default,/settings→ Memory → Mnemopi) that enable the mnemopi 4-voice polyphonic recall engine and the tiered query result cache without environment variables;MNEMOPI_POLYPHONIC_RECALL/MNEMOPI_ENHANCED_RECALLstill override the configured values when set (#2323).
Changed
-
Changed
/settingskeyboard navigation soTabandShift+Tabtoggle focus between section headings and setting rows in sectioned lists, with↑/↓jumping sections,←/→switching tabs, and status text reflecting the active controls -
The
tasktool description now mandates batching parallel spawns into one call'stasks[](sharingcontextonce) instead of presenting multipletaskcalls per message as an equal alternative; separate calls are reserved for different agent types or unrelated context -
Updated
/settingson-screen navigation hints to match the new section-focus behavior (↑/↓andTab/Enter) and tab-switching arrows -
Changed
/settingsto open as a full-screen overlay on the alternate screen so it no longer shares space with the underlying transcript -
Codex, Gemini, and Perplexity web search now route their OAuth bearers through the new
withOAuthAccessdriver: a 401 or usage-limit force-refreshes the same account and then rotates to a sibling instead of failing the search, while identity metadata (chatgpt-account-id, GoogleprojectId) is re-derived from the refreshed credential on every retry. -
Kagi web search, the xAI TTS tool, and model-discovery list fetches now resolve their bearers through
withAuthwith an auth-storage resolver instead of a one-shot key snapshot, gaining the same force-refresh + rotate retry on 401. -
Compaction, handoff, and branch-summarization call sites now hand the compactor a per-candidate API-key resolver (availability still gated on a key snapshot), so credential refresh happens before the #986 fallback-model loop advances.
-
The mnemopi backend now passes an OpenRouter resolver for default embedding/extraction setups (AuthStorage-stored keys included), keeping pinned literal keys and custom endpoints unchanged.
-
Reorganized the
/settingspanel for findability: every tab now has titled sections backed by a per-tab layout contract (TAB_GROUPS); on wide terminals the panel renders a section sidebar with the active section's settings beside it (narrow terminals keep a flat list with inline headings), and PgUp/PgDn jump section-to-section. The Editing tab became Files (edit/read/LSP) and a new Shell tab hosts bash, eval, and Python settings. Misplaced settings were rehomed: bash toggles united under Shell, tool approval mode and policies together under Interaction → Approvals, marketplace auto-update next to startup update checks, and the todo auto-clear delay beside the other todo settings. -
Restyled the
/settingschrome: the tab strip moved below the content as a label-less footer, the panel keeps one constant height across navigation, value changes, and submenus (no more viewport jumps after each change), and typing now runs a global cross-tab search — results group under per-tab headings, the footer shows live match counts with non-matching tabs muted, Tab hops between matching tabs, and Esc exits search landing on the selected result's tab. -
Normalized
/settingslabels and descriptions: consistent Title Case labels (e.g. "Todo Auto-Clear Delay", "GitHub View Cache"), uniform unit placement, articles and verb-first phrasing in descriptions ("If false…"/"Whether to…" rewritten), and a stale browser-tool description (Ulixee Hero) corrected to the actual puppeteer/Chromium implementation. -
/settingssection headings are now underlined — the active section's heading stays bold, and headings outside the active section render dim with the same underline — so section boundaries read at a glance. -
/settings: Tab now toggles keyboard focus between section headings and the setting rows — while headings are focused, ↑/↓ jump whole sections and Enter/Esc drop back into the rows — instead of cycling tabs. ←/→ still switch tabs everywhere, tabs without sections (e.g. Plugins) keep Tab as tab-switching, and the footer hint follows the focus state. -
Image-generation (Antigravity, xAI, OpenRouter, Gemini) and xAI TTS request failures now throw pi-ai's typed
ProviderHttpErrorcarrying status and response headers instead ofObject.assign-patchedErrors. -
Collapsed bash, ssh, and eval previews now cap the command/code section to a viewport-sized tail window (terminal rows minus a chrome reserve) that renders identically while streaming and after completion, with
ctrl+oas the only way to uncap. Previously bash/ssh capped the command only while streaming and snapped it fully open the moment the tool finished, and eval never capped cell code at all. -
The
jobrenderer now shimmers running-job labels while the poll block is live, freezing to static once the block seals, and drops the id column when the label repeats it — task jobs label themselves with their agent id, so rows read⟨task⟩ SessionTreeinstead ofSessionTree ⟨task⟩ SessionTree. -
Task progress rows now render static text with the task icon instead of shimmering the description or showing the pending/hourglass status glyph.
-
app.clipboard.pasteImage(Ctrl+V) now falls back to pasting clipboard text when no image is present, so hosts that deliver only that chord (VS Code's integrated terminal forwardingCtrl+V, Windows clipboard history viaWin+V) cover both payload kinds; WSL text reads now reach the Windows clipboard through host PowerShell like image reads already did (#1628). -
Changed
/usageto show the OAuth account currently selected for the active model provider when usage reports include multiple accounts, making multi-account sessions easier to verify without marking unrelated providers.
Fixed
- Fixed session tree connector rendering in
/treeand the HTML export: flattened chain rows under a last-sibling (└─) branch drew a dangling│in the corner column itself and shifted columns once the chain branched deeper; the chain anchor now sits one level right, below the branch head's content, so connectors terminate at└─and indentation stays stable (#2325). - Fixed prompt and image input typed while the agent is idle between turns from being dropped so steering now queues and auto-resumes processing once the session is ready
- Fixed image-bearing prompts queued during compaction registering their local-submission signature without the image count, so the delivery event treated them as foreign messages — clearing any draft typed since queuing and leaking the stale signature.
- Fixed Esc/Alt+Up queue restoration dropping attached images:
clearQueue()andpopLastQueuedMessage()now hand queued images back alongside the text, and the restore paths return them to the pending-image buffer instead of silently discarding them. - Fixed a failed steer submission in the no-input-waiter path discarding the message: the text and images are restored to the editor for retry instead of being lost to history-only recovery.
- Fixed local memory consolidation on Responses-style models that reject user-only requests by sending a dedicated stage-two system prompt.
- Fixed the settings sidebar divider alignment in
/settingsby locking sidebar width to the widest group name so switching tabs no longer shifts the layout - Fixed plan-review overlay mouse hit-testing so wheel, hover, TOC, and body clicks map to the intended regions
- Fixed
lsp.formatOnWritesending a hardcodedtabSize: 3, insertSpaces: trueon everytextDocument/formattingrequest, which silently re-indented 2-space YAML (and any LSP-formatted file) to 3-space on every write/edit through formatter-aware servers likeyaml-language-server. Format options are now resolved per-file from.editorconfig(indent_size,indent_style,tab_width), falling back to the indent sniffed from the in-memory content the agent is about to write, then to a 2-space default. The duplicateDEFAULT_FORMAT_OPTIONSconstant inlsp/index.tsandlsp/clients/lsp-linter-client.tsis replaced with a single sharedresolveFormatOptionshelper (#2329). - Fixed stale OpenAI Responses replay failures such as
Item with id 'rs_...' not found.by resetting the provider session and retrying without advancing fallback chains. - Fixed
/settingsEscape handling so an open submenu receives Esc and returns to the settings list before a second Esc closes the panel (#2331). - Fixed Escape not closing
/settingson the Plugins tab while the async plugin list is still loading:PluginSettingsComponentnow closes on Esc while no child view is mounted, and an npm plugin registry listing failure is caught (like marketplace failures) so a bad registry no longer leaves the tab permanently blank (#2331). - Fixed unconfigured
pi/smol,pi/slow, andpi/designeragent model roles using cloud-priority defaults before the user's configuredmodelRoles.default, which could route local-default setups to authenticated paid providers (#2336). - Fixed
issue://reads failing on older GitHub CLI releases that reject the optionalstateReasonissue JSON field; single issue reads now retry without it and issue listings no longer request it (#2333). - Fixed image generation ignoring
/login-stored OpenRouter and Google API keys: provider selection and requests now resolve through the model registry (with env-var fallback) instead of environment variables only. - Fixed detached (
taskasync spawn) progress snapshots repainting commit-eligible rows after the block leaves the live transcript region; later partial snapshots are dropped while the final completion snapshot still applies. - Fixed five consumers treating Zod tool-parameter schemas as plain JSON Schema objects, leaking schema-instance internals (
def,shape, methods stringified asundefined) or silently reading nothing:/dumpand the RPCget_statedumpToolspayload now convert parameters through the same wire-schema conversion providers receive; context-usage token estimation no longer stringifies the Zoddeftree (overcounting tool schema tokens in the status line); tool-discovery search indexing recoversschemaKeysfrom Zod tools (previously empty, weakening BM25 ranking); and the extension inspector panel renders Zod tool arguments instead of "(no arguments)".
@oh-my-pi/hashline
Added
- Added inward landing correction for
insert after block N:: a body indented deeper than the block's closing line now slides back across the block's trailing closer lines and lands inside the block at its claimed depth, with a warning naming the landing line. Same conservative guards as the outward shift — comparable indentation only, closers only, abandoned when another hunk targets a crossed line; plaininsert after M:stays literal - Added closer-anchor lowering for
insert after block N:: anchoring on a pure closing-delimiter line (where no block begins, so resolution previously failed the whole patch) now applies as plaininsert after N:with a warning teaching the opener-only rule.resolveBlockEditsgained anonWarningcallback; apply, preview, and patcher paths surface it onwarnings
Changed
- Condensed the edit-tool prompt: one-line op definitions, 5–20-word rules, and a tighter
<critical>recap; landing-correction mechanics are no longer described to the agent
@oh-my-pi/pi-mnemopi
Added
- Added
configureRecallFeatures()(exported from the package root,core, andconfig) so hosts can enable the polyphonic recall engine and the enhanced recall query cache programmatically.polyphonicRecallEnabled(),enhancedRecallEnabled(), andisEnhancedRecallEnabled()now fall back to these configured defaults, with theMNEMOPI_POLYPHONIC_RECALL/MNEMOPI_ENHANCED_RECALLenvironment variables still taking precedence whenever they are set. (#2323)
Fixed
- Fixed the embedding pipeline's silent
catch {}blocks (runEmbedding(),getLocalModel(), and the local-model path ofembed()) swallowing failures with zero diagnostics. These best-effort paths still degrade gracefully (returnnull/ skip the write), but now emit structuredlogger.debugentries with the error and per-site context (item count, model name). Themnemopi.debugconfig flag now propagates into the core library via runtime options (MnemopiOptions.debug→ResolvedMnemopiRuntimeOptions.debug) and escalates these logs towarnso they surface at the default log level. (#2322)
Changed
- Extraction, embedding, and remote-LLM clients now accept an
ApiKey(static string or resolver) and resolve it per request throughwithAuth, so 401s force-refresh and rotate credentials via the central auth-retry policy instead of failing with a stale key. Empty-key setups (local/proxy endpoints withoutAuthorization) and pinned literal keys behave exactly as before. - Embedding and remote-LLM 401 errors now throw pi-ai's typed
ProviderHttpErrorinstead ofObject.assign-patchedErrors, keeping the same structural.statuscontract for the auth-retry classifier. - SHMR consolidation clustering (
core/shmr) now uses the real embedding provider when one is configured instead of always hashing:embed(), the newembedBatch(),clusterBySimilarity(),computeHarmonyScore(),harmonize(), andrecallBeliefs()are now async, batch-embed candidate texts in a single provider call, and reuse precomputed vectors frommemory_embeddingsfor episodic candidates. The SHA1 bag-of-words hash remains as the deterministic fallback when no provider is available or embedding fails. (#2324)
@oh-my-pi/pi-natives
Fixed
- Fixed
blockRangeAt(and thus the edit tool'sreplace block/insert after blockops) failing on extensionless shell rc/profile files.Path::extensionreturnsNonefor both bare (zshrc) and dotfile (.zshrc,.bashrc) forms, so language inference fell through to "unrecognized" and block resolution was permanently unresolvable on those files — an agent retrying the block op would loop on the same error. Known shell rc/profile basenames (zshrc/zshenv/zprofile/zlogin/zlogout/bashrc/bash_profile/bash_login/bash_logout/bash_aliases/profile/kshrc/mkshrc/shrc, with or without a leading dot) now resolve to the bash grammar.
@oh-my-pi/snapcompact
Breaking Changes
- Renamed every export to drop the
snapcompact/Snapcompact/SNAPCOMPACT_qualifier — the package is meant to be consumed viaimport * as snapcompact from "@oh-my-pi/snapcompact". Functions:snapcompactCompact→compact,renderSnapcompactFrame→render,snapcompactGeometry→geometry,normalizeForSnapcompact→normalize,serializeSnapcompactConversation→serializeConversation,snapcompactImages→images,getPreservedSnapcompactArchive→getPreservedArchive,isSnapcompactShape→isShape,resolveSnapcompactShape→resolveShape,createSnapcompactFileOps→createFileOps,computeSnapcompactFileLists→computeFileLists,upsertSnapcompactFileOperations→upsertFileOperations. Types:SnapcompactShape→Shape,SnapcompactFrame→Frame,SnapcompactArchive→Archive,SnapcompactGeometry→Geometry,SnapcompactOptions→Options,SnapcompactSerializeOptions→SerializeOptions,SnapcompactFileOperations→FileOperations,SnapcompactCompactionDetails/Preparation/Result→CompactionDetails/CompactionPreparation/CompactionResult,SnapcompactConvertToLlm→ConvertToLlm. Constants:SNAPCOMPACT_X→X(SHAPES,FRAME_SIZE,MAX_FRAMES,FRAME_TOKEN_ESTIMATE,PRESERVE_KEY,TOOL_RESULT_MAX_CHARS,TOOL_ARG_MAX_CHARS,TOOL_CALL_MAX_CHARS,TRUNCATE_HEAD_RATIO,DIM_ON,DIM_OFF).
Added
- Added
renderMany()for paging arbitrary text into snapcompact PNG frames as LLM image blocks, andframes()for predicting the frame count without rendering
@oh-my-pi/pi-tui
Added
- Added
partialHoldTimeouttoStdinBufferOptionsto control the maximum extra delay held for unambiguous incomplete escape sequences before they are flushed - Added
SettingsList.sidebarWidthoption for a fixed split-layout sidebar width - Added mouse pointer support APIs to
SettingsListwithsetHoverItem,hitTest,hoverTest, androuteSubmenuMousefor row targeting and submenu routing - Added
SettingsList.setMaxVisible(rows)andSettingsList.handleWheel(delta)for dynamic viewport sizing and mouse-wheel step selection - Added compact tab features with new
Tab.shortlabels andTabBar.selectTab(id)for id-based activation of non-muted tabs - Added pointer-hover and hit-testing APIs to
TabBarwithsetHoverTab,tabAt, andhoverTabtheme - Added exported SGR mouse utilities
parseSgrMouse,SgrMouseEvent, andMouseRoutable - Added section support to
SettingsList:SettingItem.headingrows split the list into sections, PgUp/PgDn (tui.select.pageUp/pageDown) jump between sections (or page when none exist), and wide renders use a split layout — section sidebar on the left, the active section's items on the right — falling back to inline heading rows when the width cannot fit both panes. Headings are skipped by navigation, excluded from search, and styled through the optionalSettingsListTheme.heading(which receives adimmedflag for headings outside the active section) andsection. - Added a host-integration surface to
SettingsList: aSettingsListOptionsconstructor arg (layoutto force the flat layout,typeToSearch: falseto hand the query to a parent,emptyText,hint),selectItem(id),getSelectedItem(),onSelectionChange,hasOpenSubmenu(), and the exportedgetSettingItemFilterTexthelper. - Added keyboard section focus to
SettingsList:toggleSectionFocus()/sectionFocused/hasSectionFocusTargets()flip Up/Down between row navigation and whole-section jumps — the cursor glyph parks on the active sidebar entry (or the active heading row in the flat layout) while the row cursor hides, Enter/Esc drop focus back to the rows, and any explicit row selection (selectItem, wheel, filtering) exits it. - Added muted tabs to
TabBar(Tab.muted+TabBarTheme.mutedTab, skipped by keyboard navigation),setTabs(tabs, activeId?)/setActiveById(id)for re-rendering the strip without firingonTabChange, an optional empty label (drops theLabel:prefix), and ashowHintswitch for the trailing "(tab to cycle)" hint.
Changed
- Changed
SettingsListsection-focused keyboard handling soUp/Downnow jump between sections andEnter/Escapeexit section focus before confirming or cancelling a setting - Changed
SettingsListsplit layout at wide widths to render the full list in the right pane and dim items outside the active section instead of showing only the active-section rows - Changed
SettingsListto omit the default hint row (and preceding blank line) whenoptions.hintis set to an empty string - Changed tab-bar overflow handling to collapse tabs to their
shortforms before wrapping to multiple lines
Fixed
- Fixed
StdinBufferhandling of split SGR mouse reports so fragmented sequences are reassembled instead of leaking their tail bytes as literal input - Fixed Esc being unreliable (or seconds-slow) inside fullscreen overlays such as
/settingson kitty-protocol terminals (Ghostty/kitty): the kitty keyboard mode stack is per-screen, so entering the alternate screen silently reverted keys to legacy encoding while the app still parsed them as kitty input. The TUI now re-pushes the active kitty flags right after\x1b[?1049hand pops them before\x1b[?1049l. - Fixed
StdinBuffertearing a buffered bareESCfollowed by another escape sequence: the\x1b\x1bcandidate was consumed as alt+esc before the CSI/SS3 continuation byte was ever inspected, swallowing the Esc keypress and leaking the follower's tail ([B,[<35;22;17M) as typed text into focused components. Meta-CSI chords (\x1b\x1b[A) now stay whole, andESC+ SGR mouse report is split into a real Esc keypress plus a parseable report. - Lowered
PARTIAL_HOLD_MAX_MSfrom 500ms to 150ms so a dangling escape partial that never completes (e.g. a bareESCarriving while the kitty-active flag is stale) is delivered after at most ~200ms instead of half a second. - Fixed deferred partial-flush behavior so pending incomplete escapes are not split across timer boundaries and can still complete when the next chunk arrives
- Fixed kitty keyboard-mode handling of a dangling
ESCso it can be joined with subsequent CSI mouse/kitty input instead of being emitted as a standalone sequence - Fixed
SettingsListto clear section-focus state when filtering items, changing data, scrolling with the mouse wheel, or selecting by ID so stale heading focus does not persist across interactions SettingsListnow renders every state — list, open submenu, filtered results, empty — at one stable height, so interacting with a bottom-anchored settings panel no longer resizes the live terminal region on each keystroke (which forced re-anchoring and could strand stale scrollback rows).
@oh-my-pi/pi-utils
Added
- Added
getEditorConfigFormatting(file): returns the.editorconfig-pinnedtabSize/insertSpaces(both optional, no fallback) so LSP-format callers can layer per-file defaults under it without paving over silence with the renderer's display tab width (#2329).
What's Changed
- fix(tool): tolerate missing issue stateReason field by @roboomp in #2335
- fix(agent): fall back unset smol and slow roles to default by @roboomp in #2338
- fix(settings): let Esc close submenus before the settings panel by @roboomp in #2332
- fix(lsp): resolve per-file format options from editorconfig and content by @roboomp in #2330
- session accent: derive from theme mode with collision avoidance by @H4vC in #2256
- fix(catalog): clamp MiniMax M2 / GPT-OSS reasoning_effort on Fireworks by @roboomp in #2317
- fix(natives): resolve bash for extensionless shell rc files (zshrc/.bashrc) by @blingdivinity in #2339
- fix(coding-agent): add local memory consolidation instructions by @bjin in #2311
- feat(coding-agent): fall back to text paste when clipboard holds no image by @chan1103 in #2248
- Show active OAuth account in usage by @insodimension in #2260
New Contributors
- @blingdivinity made their first contribution in #2339
- @chan1103 made their first contribution in #2248
- @insodimension made their first contribution in #2260
Full Changelog: v15.11.3...v15.11.4