- add discovery of `TITLE_SYSTEM.md` and pass it through interactive startup context
- route custom title prompts to online and local tiny title generators via protocol
- update session-title docs and changelog with override behavior
- add tests for prompt discovery, forwarding, and fallback to bundled title prompts
- Added "available" status so recognized servers show under lazy mode without warmup.
- Rendered full welcome box as pre-TUI splash with fixed slot heights to avoid layout shift.
- Reported lazy servers as available in /status instead of omitting the section.
Move bundled models, model cache/manager, thinking metadata, effort helpers,
provider descriptors/discovery, wire constants, and model identity utilities
into the new @oh-my-pi/pi-catalog package.
Update pi-ai to keep provider runtime/auth concerns, move catalog provider
metadata into CATALOG_PROVIDERS, and migrate coding-agent, agent, stats, docs,
and tests to import catalog values from pi-catalog.
Split coding-agent model registry helpers into discovery, roles, and models
config modules while preserving registry orchestration.
BREAKING CHANGE: @oh-my-pi/pi-ai no longer exports catalog subpaths such as
/models, /model-cache, /model-manager, /model-thinking, /effort,
/provider-models*, discovery helpers, and provider wire constants; use the
matching @oh-my-pi/pi-catalog subpaths instead.
Project-tagged Hindsight mental-model seeds now use project-qualified ids and legacy bare ids only satisfy the matching project tag. The injected mental-model block also filters tagged models to the active project while retaining untagged models.\n\nFixes #2218
per-block grapheme counts cached (blocks only grow) and in-flight partials bypass the markdown render LRU, removing repeated full Intl.Segmenter walks per 33ms tick and retained stale partial snapshots on long replies.
- Capped live transcript IRC cards at four and evicted oldest cards when over limit.
- Reworked IRC card expiry handling to retire cards via timers only while live.
- Added rewrite-floor tracking so rewritten rows are not repeatedly promoted to scrollback.
- Removed terminal risk mode toggles from config, terminal state collection, and render controllers.
- Dropped snapshot freezing, thaw tracking, and finalized-block replay in transcript rendering.
- Removed clear-on-shrink settings and initialization hooks from selector and interactive mode flows.
- Simplified render scheduling by using requestRender() without mutation flags or stream checkpoints.
- Updated bracketed-image parsing to recognize multiple image paths in a single paste, including quoted values and shell-escaped spaces.
- Changed the editor image-path handler to process each matched path in order, awaiting async handlers.
- Added tests for multi-path routing/normalization and recorded the fix in the coding-agent changelog entry.
MCP OAuth fallback prompts now emit an auth-safe terminal hyperlink even when auto-detection disables normal URL hyperlinks, matching the provider login behavior while preserving the raw copy URL.\n\nFixes #2196
- Defer MCP server discovery off the first-paint critical path for UI
sessions; tools and slash commands stream in through the existing
live-refresh channel once each server connects (non-UI modes keep the
blocking path). ~290ms off first paint with MCP servers configured.
- Build the model catalog's canonical-equivalence index lazily on first
read instead of eagerly in the ModelRegistry constructor. A default
interactive launch never reads it pre-paint, moving the ~210ms build
(over ~3,200 models) off the critical path: ~244ms (~16%) off cold boot.
- Memoize per-session read summaries (tree-sitter parse) on the content
hash of the freshly-read bytes; the file is still read fresh each call
so results stay correct. Repeat same-file summary read 17ms -> 2.4ms.
- Reuse the Markdown subtree across streaming reveal ticks, memoize
grapheme counting, and stop re-highlighting finalized thinking blocks.
- Attribute the previously-unlabeled synchronous boot region in the
PI_TIMING table and add a bench:guard boot-regression target.
On kitty (Linux/Wayland) and any terminal supporting OSC 5522 enhanced
paste, the /login API-key prompt for OpenCode Zen — and any other modal
Input prompt (Perplexity OTP, GitHub Enterprise URL, manual OAuth
redirect URL, …) — silently dropped pasted content. The user could not
paste their API key; on Enter or Esc the key surfaced in the main
prompt.
Cause: InputController.#setupEnhancedPaste enables kitty's enhanced
clipboard protocol on TUI start and consumes the resulting OSC 5522
packets in an addInputListener that runs *before* focus dispatch in
tui.#handleInput. The controller's pasteText callback then routed
unconditionally to this.ctx.editor.pasteText(text) — the main
CustomEditor — even when selector-controller had cleared editorContainer
and focused a temporary Input. The text accumulated in the detached
editor and only resurfaced when the modal was dismissed.
Fix:
- TUI.getFocused() exposes the currently focused component.
- Input.pasteText(text) mirrors Editor.pasteText so any Input can absorb
a payload from a non-bracketed transport.
- InputController's enhanced-paste callback consults getFocused() and
routes text to the focused component when it exposes pasteText,
falling back to the editor only when no modal target is in focus.
- Image pastes refuse with a status message when a modal Input is
focused, instead of stuffing a binary blob into the hidden editor.
Regression tests in packages/tui/test/input.test.ts and
packages/coding-agent/test/issue-2127-repro.test.ts pin both the new
TUI/Input contract and the InputController routing source.
Fixes#2127
Threshold-driven auto-compaction with strategy=shake auto-continued even when
the shake reclaimed nothing material, and the next agent turn re-triggered the
same shake (which had nothing new to drop on a second pass), spinning forever.
After shake completes, recompute the post-shake context estimate; when it
still exceeds the auto-compact threshold (or shake reclaimed nothing on overflow
recovery), emit a one-shot fallback warning and hand off to the summarization
driven context-full path so progress actually resumes. Idle is exempt — its
60s+ timer self-throttles and cannot dead-loop on its own.
Fixes#2119
Rendered the MCP OAuth fallback as a short terminal hyperlink plus a single unwrapped copy URL line so terminals do not receive hard-broken authorization URLs.\n\nFixes #2121
- Derived descriptors, default-model map, env keys, login list, and refresh dispatch from one ProviderDefinition per provider.
- Disabled OpenAI Codex stream obfuscation and interrupted whitespace-only tool-call argument deltas.
- Derived auth-broker callback ports and paste-code login set from the registry.
- Added status.done and tool.* symbols to theme mappings and presets.
- Replaced generic success glyphs with contextual +/-, tool icons, and warnings.
- Mapped tool/task/job completions to status.done or status.enabled with icon overrides.
- Triggered runtime provider refresh after extension registration and warned on failure.
- Serialized hook selector, input, and editor dialogs to prevent overlapping surfaces.
- Queued dialog requests FIFO and skipped requests aborted before they could be shown.
- Added a shared #presentDialog path for modal focus and hide lifecycle.
- Added atomicTokenPattern support to treat image and paste markers as indivisible tokens, enabling atomic deletion on backspace and forward-delete instead of character-by-character removal.
- Extended image marker format to include pixel dimensions [Image #N, WxH] and updated paste marker format to [Paste #N, +X lines] or [Paste #N, Y chars] with consistent comma-separated metadata.
- Implemented accent styling for paste markers in editor rendering while preserving hyperlinks for image markers, improving visual distinction between marker types.
- Refactored image reference handling to unified placeholder system supporting both image and paste markers with kind-aware rendering callbacks and regex patterns.
- Passed pending and input images into compaction queueing for steering and follow-up flows.
- Delivered queued follow-up/steer prompts with stored images via session.prompt and followUp.
- Normalized empty image arrays to undefined and cleared stale pending image state.
- Added regression tests for image-backed compaction queueing and follow-up forwarding.
- Added a sealed state to `ReadToolGroupComponent` to force-close a read group when a turn ends without a read result.
- Updated transcript finalization to treat pending read entries as active unless the group is sealed, preventing premature finalization when outputs are still in flight.
- Expanded turn-end pending-tool cleanup to seal `ReadToolGroupComponent` instances in addition to `ToolExecutionComponent`.
- Removed `<turn-aborted>` guidance injection from `transformMessages`, deleted `turn-aborted-guidance.md`, and dropped the synthetic abort note path for aborted/error turns.
- Updated `c`/`.` continue shortcuts to submit `manual-continue.md` as a hidden synthetic `developer` message via `session.prompt(..., { synthetic: true })` instead of sending an empty user turn.
- Updated tests and changelog notes in AI and coding-agent to reflect the revised abort-context and continue behavior.
- Added an interrupt state in EventController to switch the working label to `Interrupting...` and suspend intent-driven updates until the turn resets.
- Called `notifyInterrupting()` from streaming-interrupt paths in InputController and exposed it on InteractiveModeContext so Esc acknowledgement shows immediately while tools tear down.
- Added tests to verify loader acknowledgement, freeze of late intent updates during interruption, and label updates resuming on the next agent start.
Route follow-up shortcut submissions through the builtin slash-command dispatcher before queueing them as deferred prompts.
Added regression coverage for /goal set submitted through InputController.handleFollowUp while a stream is active.
Fixes#2038
handleCtrlZ called process.kill(0, "SIGTSTP") unconditionally; on
Windows the runtime rejects the signal name with TypeError, which
propagated out of the TUI input dispatcher and crashed the agent
with an [Uncaught Exception]. Even on POSIX the call could fail
(sandboxes that block sending to pid=0, runtime-reserved signals),
and a thrown error after we already stopped the TUI and registered
a one-shot SIGCONT listener left the UI stranded with a leaked
handler that would fire on the next unrelated continue and try to
re-start() an already-running TUI.
Branch on process.platform first so Windows shows a status notice
and never calls process.kill, and wrap the POSIX kill in try/catch
that removes the SIGCONT listener and re-starts the TUI on failure.
Fixes#2036
- Added `display.smoothStreaming` to settings schema.
- Updated streaming-reveal behavior to produce smooth 30 FPS reveals.
- Updated event-controller flow to support smooth streaming reveal mode.
- Expanded streaming-reveal tests for the new smooth streaming behavior.
- Reduced shimmer-driven UI animations and borders to a 30fps cadence.
- Collapsed consecutive read tool calls into one finalized transcript block.
- Tracked visible assistant content to finalize and reset read groups correctly.
- Updated running task rows to static bullet markers with shimmer-only subagent labels.
- Updated InputController streaming submit handling to interrupt and resume when queued steering exists, refreshing the pending-message UI and render.
- Added AgentSession.interruptAndFlushQueuedMessages to abort active work and continue processing queued messages immediately.
- Added tests for queued steering interruption in both agent-session concurrency and input-controller keybinding flows.
- Added `/tan` slash command registration and interactive handling.
- Added TanCommandController validation and async task scheduling for `/tan` dispatch.
- Added session cloning that suppresses breadcrumbs, copies artifacts, and handles abort cleanup.
- Added `promptCacheKey` support in Agent and inherited `providerPromptCacheKey` in session creation.
- Collapsed non-touched phases to one-line summaries in multi-phase todo renders while retaining full output for active, touched, or expanded views.
- Computed touched phases from in-progress tasks, completion transitions, and tool operations, with init operations now marking all phases.
- Added renderer tests covering collapsed rendering, argument-less fallback behavior, expanded mode, and separator-free phase output.
- Added OSC5522 parsing and a payload controller that prefers image MIME and reassembles chunks.
- Added bracketed image-path detection and `onPasteImagePath` callback wiring in the editor.
- Added local image-path paste insertion with fallback to text paste on image-size errors.
- Added `Editor.pasteText()` and OSC5522 disable sequencing on terminal cleanup.
- Added optional `reason` parameters to `Agent.abort` and `AgentSession.abort` APIs.
- Passed abort reasons through interrupt flows into underlying agent cancellation.
- Replaced hard-coded abort text with `resolveAbortLabel` for streaming and replayed messages.
- Fell back to generic `Request was aborted` text when no abort reason was provided.
- Removed the built-in `background` (`bg`) slash command and its `handleBackgroundCommand` path from the interactive flow.
- Deleted background event subscription and shutdown handling by removing `handleBackgroundEvent` from the event, input, and interactive controllers.
- Simplified `InteractiveModeContext` by dropping background-only fields and helpers such as `isBackgrounded`, background UI context creation, and background event callbacks.
- Added `AgentSession.freshSession()` to rotate provider-facing IDs and prune provider stream state.
- Added `/fresh` command handling in the builtin registry and mode command flow.
- Kept persisted session metadata intact during `/fresh` and cleared transient IDs on session switches.
- Invalidated `appendOnlyContext` and provider caches when refreshing provider state.
- Added ChatBlock and ChatBlockHost with mount, finish, and dispose lifecycle callbacks.
- Added InteractiveModeContext.present and resetTranscript APIs and used them to mount/repaint blocks.
- Reworked controller rendering paths to emit command, event, extension, and selector outputs via ctx.present.
- Implemented component and container dispose hooks in tui so loaders and child blocks cleanup timers/effects.
- Centralized transcript spacing by stripping blank edges and inserting separators.
- Removed per-component leading spacers and empty placeholders that added extra gaps.
- Introduced TranscriptBlock grouping so related outputs render as single transcript children.
- Updated transcript-related tests to validate one-row block separators and blank-line trimming.
- Lazy-loaded OTEL SDK, HTML export, TTSR, and autoresearch modules.
- Made resolveMemoryBackend async to import backends on demand.
- Replaced backend resolution with direct settings reads for rekey checks.
- BLOCKING: reorder resolveProviderCredentialIdentityKey so email
identity takes priority over project — two users with different
emails on the same GCP project no longer get merged/hard-deleted.
- Added #getUsageReportScopeProjectId helper so Gemini CLI reports
(which set projectId on limit.scope but not metadata) still get
dedup coverage. Both metadata and scope projectId paths checked.
- formatAggregateAmount now falls back to limits.length when no
scope.accountId values are present, preserving pre-existing
behaviour for providers that don't set accountId on limits.
- Added 9 contract tests for the antigravity usage merge logic:
tier dedup, worst-fraction-wins, mixed-case collapsing,
reset-time-from-other-entry, windowId separation, metadata,
sort order, and null-on-no-project.
- Nits: label='Usage' (so formatLimitTitle renders 'Usage (Default)'
not bare 'Default'), id uses params.provider instead of hardcoded
string, tier field drops redundant ?? undefined.