- Removed early FTS-only return so prefix and substring matches merge.
- Sorted merged results globally by prompt recency before limiting.
- Added /force, /copy, and /shake tips.
- Skipped titling for greeting/filler/empty first messages deterministically, retrying on later user messages.
- Let capable title models decline taskless input via a "none" sentinel.
- Guarded against clobbering a name set by a concurrent attempt.
Centralized append-only context auto-mode resolution so SDK sessions, interactive sessions, and the status display use the same provider checks. Auto mode now enables for Xiaomi/SGLang hosts and explicit stored-request compat signals while preserving DeepSeek behavior.
Added focused regression coverage for Xiaomi Token Plan SGLang HiCache endpoints and explicit on/off behavior.
Fixes#1851
Marketplace plugin toggles in Settings → Plugins wrote the installed-plugin
registry and then only requested a render through the settings callback. In a
live TUI session that left already-loaded slash commands, hooks, tools, agents,
and SSH tool state unchanged until the user manually reloaded plugins or
restarted.
Make the plugin-change callback awaitable and wire the settings selector to the
same reload pipeline used by the slash-command runtime: clear plugin-root caches
for the active project registry, refresh slash command state, refresh the SSH
tool, then request a render. Await the callback after npm and marketplace
registry writes so Settings → Plugins reflects live plugin state immediately.
Add a regression test proving a marketplace toggle calls setPluginEnabled before
the plugin-change reload callback resolves.
`MarketplacePluginDetailComponent` rendered the raw `installPath` from
the installed-plugin registry, which leaks the user's home directory
into the TUI when the plugin lives under `~/.omp/cache`. Route it
through `shortenPath` so $HOME becomes `~` before the value reaches
`Text`, matching the repo's TUI sanitization rules (per review on #1844).
`PluginSettingsComponent.#showPluginList()` only inspected the npm plugin
registry, so the TUI's `Settings → Plugins` panel rendered "No plugins
installed" whenever the user had no npm plugins — even when many
marketplace plugins were installed. The `/plugins list` slash command,
`omp plugin list` CLI, and the install/uninstall selectors already
merged `PluginManager.list()` with `MarketplaceManager.listInstalledPlugins()`;
the settings panel was the only surface that missed the marketplace
registry when it was added.
Extend the panel to consume both data sources:
- Replace `PluginListComponent`'s `InstalledPlugin[]` input with a tagged
union `PluginListEntry = { kind: "npm"; plugin: InstalledPlugin } | { kind:
"marketplace"; plugin: InstalledPluginSummary }`. Each row now carries an
`[npm]`/`[marketplace]` kind badge, a scope tag, and the shadow indicator
for user installs overridden by a project entry. Widen the SelectList
primary column so long `name@marketplace` ids stay readable.
- Add `MarketplacePluginDetailComponent`: single `Enabled` toggle plus the
read-only `InstalledPluginEntry` metadata (version, install path,
installed-at, last-updated, git commit SHA). The toggle calls
`MarketplaceManager.setPluginEnabled(pluginId, enabled, scope)` — same
path `/plugins enable|disable` uses.
- Update `PluginSettingsComponent` to await both `PluginManager.list()` and
`MarketplaceManager.listInstalledPlugins()` in parallel, build the merged
list, and route selection to the kind-specific detail view. The empty
state now lists both install commands (npm and marketplace).
Fixes#1842
Move the color math added for the session-accent fix into the shared
@oh-my-pi/pi-utils color module instead of a coding-agent-local file:
- colorLuma, relativeLuminance, and hslToHex now live in
packages/utils/src/color.ts (hslToHex lifted out of session-color.ts).
- Drop the duplicate hex parser: toRgb reuses the existing hexToRgb and
paletteToRgb returns the shared RGB type, so hex parsing lives once.
- Delete packages/coding-agent/src/utils/color.ts; theme.ts and
session-color.ts import from @oh-my-pi/pi-utils.
- Move the color unit test into the utils package; repoint session-color
test imports.
No behavior change to accent luminance capping.
- Added TranscriptContainer that snapshots non-live blocks to avoid stale scrollback duplicates.
- Thawed snapshots at prompt-submit checkpoints for reconciliation.
- Repainted eager streaming tail on overflowing shrink to unfreeze UI (issue #1682).
- Added `selectionMarker`, `checkedIndices`, and `markableCount` options to render radio/checkbox glyphs per row.
- Moved checkbox rendering from inline label prefixes into the selector component.
- Kept trailing control rows like "Other"/"Done" on the plain cursor.
- Renamed `TodoWriteTool` to `TodoTool` and its source/prompt files.
- Updated tool registration, schema, renderers, and gating to `todo`.
- Adjusted cursor provider native tool names and tests to match.
- Renamed strike-animation constants and `todo-error-reminder` type.
- Classified each session's final message as done, interrupted, aborted, error, or pending from a 32 KiB tail read.
- Rendered the status as a colored segment on the session metadata line.
- Added `peekFileTail` and `readTextSuffix` across storage backends to read file tails in one pass.
- Simplified `MemorySessionStorage` to a string array mirror with a sidecar mtime map.
- Added `textSizing` terminal capability and `tui.textSizing` setting for Kitty OSC66 scaling.
- Updated OSC66 range and visible-width handling to preserve grapheme slice behavior.
- Updated markdown rendering to apply heading text sizing only when enabled.
- Fixed DECCARA trailing-background fill tracking and DECRPM status 3/4 recognition.
- Updated completion and ask notifications to pass structured payloads.
- Updated completion and ask notifications to include explicit message metadata fields.
- Adjusted abort-guard and retry-capability tests for the new notification/options behavior.
- Added `ImageBudget` keeping only N recent images live, demoting older ones to text via a full redraw plus Kitty graphics purge.
- Changed Kitty images to transmit-once + placement, so repaints re-send only tiny placement sequences instead of base64.
- Added `tui.maxInlineImages` setting (default 3, 0 disables) wired through the coding-agent renderers.
- Rendered single-choice questions with circular radio glyphs instead of checkboxes.
- Kept rectangular checkboxes for multi-select questions.
- Added radio.selected/unselected symbols to unicode, nerd-font, and ASCII presets.
- Updated hook option rendering to support description modes for full text, bounded rows, or hidden detail output.
- Adjusted compact-mode row budgeting so list height is based on labels, preventing a single description from consuming the visible budget.
- Added pre-wrapping with ellipsis truncation for selected option descriptions and updated overflow tests to validate the collapsed label-first behavior.
When OTEL_EXPORTER_OTLP_ENDPOINT (or _TRACES_ENDPOINT) is set, register a
global OTLP/proto TracerProvider and switch on the agent loop's telemetry
so its invoke_agent / chat / execute_tool spans reach a collector instead
of a no-op tracer. Honors the standard OTEL_* env contract
(OTEL_SERVICE_NAME, OTEL_SDK_DISABLED, OTEL_TRACES_EXPORTER=none) and the
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT capture toggle; no-op
when no endpoint is configured.
Uses the exporter-trace-otlp-proto 2.x line, which exports cleanly under
Bun (the 1.x line deadlocks). Adds a flushTelemetryExport() helper and a
periodic + signal-driven flush so long-lived processes surface spans.
This makes the existing telemetry usable from hosts that spawn omp as a
child process (e.g. an ACP harness), where an in-process provider
registered by the parent can't reach the child process.
Per review on #1715: the cap fed gamma-encoded luma into the WCAG contrast formula, so the >=3:1 target wasn't real (light-poimandres #340df2 was ~2.5:1 actual). Split the color helpers into colorLuma (perceptual, for light/dark classification — unchanged behavior) and relativeLuminance (WCAG-linearized sRGB, for contrast ratios). Session accents are now sized, and tests asserted, against relativeLuminance, so >=3:1 holds against the actual surface. Theme keeps luma for isLight/isLightTheme and exposes the WCAG status-line luminance for the cap.
Per review on #1715: the light-theme cap was a fixed absolute luminance (0.2) tuned for near-white backgrounds. On a mid-light shipped theme like light-poimandres (statusLineBg #7390aa, luminance ~0.55) that still left accents at only ~1.8-2.4:1. Theme now exposes statusLineLuminance + accentSurfaceLuminance; getSessionAccentHex takes the surface luminance and caps the accent to guarantee >=3:1 (WCAG AA large) against the actual surface, so near-white and mid-light light themes are both legible. Tests assert contrast across light + mid-light surfaces.
Per review on #1715: theme color values may be a 256-color palette index (colorValueSchema = string | number), so a custom theme can set a light statusLineBg numerically (e.g. 15 = white). bgIsLight only handled hex strings and returned false for numbers, routing those light themes through the dark-accent path and reintroducing the unreadable accent. Add paletteLuminance (0-15 xterm defaults, 16-231 color cube, 232-255 grayscale) and a colorLuminance(string|number) dispatcher; route bgIsLight and isLightTheme through it. Tests added.
Per review on #1715 (nits 3 & 4): the BT.709 relative-luminance + #rrggbb parsing was duplicated three ways (session-color relativeLuminance, theme bgIsLight, isLightTheme), and each rejected #rgb shorthand via length !== 7 — so a custom theme using shorthand backgrounds silently kept the washed-out accent. Lift it into utils/color.ts hexLuminance() (accepts #rgb and #rrggbb), route all three call sites through it, and add tests.
Per review on #1715: Theme.isLight drove accent darkening from userMessageBg, but the accents render on the status line / editor chrome. Themes that style the chat bubble darker than the status line (e.g. built-in porcelain: userMessageBg #5070a0, statusLineBg #f8f9fc) misclassified, leaving the bright dark-theme accent on a light status line. Derive isLight from statusLineBg; add a regression test over porcelain / light-catppuccin / dark-catppuccin.
The status line session_name (plus editor border / gap fill) colors itself with a per-session accent hashed from the name via hslToHex(hue, 0.9, 0.72). The fixed lightness is tuned for dark backgrounds; on light themes high-luminance hues (yellow/lime/cyan) render near-invisible (~1.3:1 contrast). statusLine.sessionAccent (#918) only gates the border/gap fill, not the segment text, so there was no way to make the title legible on a light theme.
Add Theme.isLight (from userMessageBg luminance) and make getSessionAccentHex bisect lightness to cap perceived luminance on light themes, keeping the per-session hue while staying readable. Pass theme.isLight at all four accent call sites; add a unit test.
Refs #918, #680.
- Add CMUX_SURFACE_ID env var fallback to getTerminalId() so cmux
surfaces get a stable terminal identifier alongside kitty/tmux.
Enables per-surface session breadcrumbs for omp -c in cmux.
- Update tip text to mention cmux alongside kitty/tmux.
- Tracked the latest in-flight preview diff recompute in ToolExecutionComponent and exposed it through a new whenPreviewSettled method.
- Updated component paths that trigger preview recomputation to retain the returned promise instead of fire-and-forget calls.
- Updated the streaming preview height test to await settled diff recomputation before assertions, eliminating requestRender race-based flakes.
- Added `Settings.reloadForCwd` to mutate the live instance in place, so `/move` and cross-project resume pick up the destination project's `.claude/settings.yml` and path-scoped `enabledModels`/`disabledProviders`.
- Wired `reloadForCwd` into `applyCwdChange` (interactive mode) and the `--resume` startup path so settings always follow the active working directory.
- Added tests covering path-scoped re-resolution, no-op on same directory, and disk-backed project layer load/drop.
- Enabled resume picker to preload sessions and toggle folder/all scope with Tab.
- Enabled resume flow to fall back to all-project sessions and switch cwd on resume.
- Added centralized applyCwdChange to refresh caches, commands, and UI after cwd updates.
- Updated session restoration to adopt restored session cwd and sessionDir when present.
- Fixed `session_id` never being created or populated; every history row had `NULL` for session.
- Added schema migration (`ALTER TABLE history ADD COLUMN session_id`) for pre-existing databases.
- Wired interactive mode to call `setSessionResolver(...)` so prompts are stamped with the active session at submission time.
- Re-enabled session ranking in `--resume` and in-session pickers via `matchingSessionIds()`, merging fuzzy and prompt-history signals.
Filtered the logout selector to credentials that /logout can actually remove and reported ambient env/config auth when direct logout cannot clear it. Added selector coverage for env-only OpenCode providers.\n\nFixes #1658
When the autocomplete popup is visible, ESC unconditionally falls through
to the editor base class so it can dismiss the popup. Only an ESC with no
popup visible reaches onEscape and routes to the global interrupt handler.
Removed the shouldBypassAutocompleteOnEscape callback that paved over the
popup-dismissal path whenever the agent was busy (streaming, bash, /btw,
auto-compaction, etc.) — that is precisely the moment the user is most
likely to hit ESC while the popup is open, and dismissing-then-aborting in
one keystroke is a footgun, not a feature. Two-press semantics now match
the standard TUI/IDE pattern: first ESC closes the popup, second ESC
aborts.
Tests cover both branches in custom-editor-keybindings.test.ts and the
input-controller escape suite no longer asserts the dead callback.
Fixes#1655
Applied tasks.todoClearDelay to the interactive Todos panel without mutating the session todo history used by the model.
Added regression coverage for instant, delayed, and disabled auto-clear behavior.
Fixes#1644
- Tool execution now renders every 16ms and advances spinner glyphs only every 80ms using a tracked last-advance timestamp.
- Output block shimmer ticks were reduced to 16ms so border-frame updates align with the 60fps cadence for smoother animation.