Wrap the streaming Enter steer dispatch in try/catch so prompt failures restore text plus pendingImages / pendingImageLinks / imageLinks and surface showError instead of losing an image-only draft.\n\nAlso remove a forbidden ReturnType<> from the follow-up image regression helper.\n\nFixes #3467
Snapshot pendingImageLinks alongside pendingImages at the top of handleFollowUp and wrap both the streaming and idle session.prompt dispatches in try/catch that restores text + pendingImages + pendingImageLinks + imageLinks and surfaces showError, mirroring the main submit and focused submit error paths so an image-only or text+image Ctrl+Enter draft survives dispatch rejection.\n\nFixes #3467
Snapshot pendingImageLinks and re-seed editor.pendingImages / pendingImageLinks / imageLinks in the focused-session submit catch block so an image-only or text+image draft survives a viewSession.prompt rejection, mirroring the main controller error path.\n\nFixes #3467
Treat pending pasted images as submit content in the main and focused input controller paths so image-only Enter/Ctrl+Enter submissions queue instead of dropping or aborting.\n\nFixes #3467
brush-core's Process::wait calls tokio::signal::unix::signal(SIGTSTP) to
detect when its children get stopped. Per tokio's documented contract,
the first call for a SignalKind permanently replaces the kernel-default
handler for the lifetime of the process. So once omp has executed any
bash tool call — even /usr/bin/true — SIGTSTP's default "stop" action
is gone, and InputController.handleCtrlZ's process.kill(0, "SIGTSTP")
gets swallowed by tokio. The TUI tore down via ui.stop() but the process
kept running in Sl+ state, leaving the user with a dead terminal that
only kill -9 could recover.
Send SIGSTOP to our own PID instead. SIGSTOP can't be caught, blocked,
or ignored — it stops the process at the kernel regardless of installed
handlers. Targeting self (not pgid=0) also leaves long-lived children
(MCP stdio servers, the persistent brush native shell) running across
the suspend, so they no longer freeze mid-IPC during a quick fg/bg
detour.
Fixes#3461
- Removed `onPasteFilePath` handler to prevent automatic background file attachment when pasting paths.
- Updated `CustomEditor` to treat non-image paths as literal text input.
- Cleaned up unused file system utilities and paste path resolution logic.
- Implemented SGR mouse event routing for dashboard interaction, including tab selection and pane scrolling.
- Added mouse-driven list manipulation in the extension viewer with selection highlighting, click toggling, and wheel navigation.
- Enabled fullscreen alternate-screen behavior and host terminal mouse tracking for the dashboard overlay.
- Integrated hit-testing and row selection logic into the extension list to support unified mouse and keyboard inputs.
renderUsageReports (command-controller) carried the #3268 dedup contract
with no regression test; the PR's added CLI test asserts the opposite
(per-limit CLI rendering shows the note twice). Export renderUsageReports
and add a real regression through it: two accounts sharing one window group
render a provider-wide UsageReport.note once and an identical per-limit note
once. Verified failing on the pre-fix flatMap form (0 and 2 occurrences) and
passing on head (1 and 1).
When /settings (or the Extensions/Agents dashboard) is open and a tool
approval prompt fires, ExtensionUiController.showHookSelector swaps the
editor out of editorContainer for the HookSelectorComponent. On exit,
the overlay's done() called overlayHandle.hide() + setFocus(editor),
both pointing at the editor captured as preFocus when the overlay
opened — now no longer mounted. The visible approval prompt then sat
unreachable: Up/Down/Enter/Esc routed to the unmounted editor and only
Ctrl+C escaped (issue #3349).
SelectorController now exposes focusActiveEditorArea(), which restores
focus to editorContainer.children[0] (the live slot owner) or falls
back to the editor. Wired into showSettingsSelector, showExtensionsDashboard,
and showAgentsDashboard close paths after overlay.hide().
Tests: unit test verifying focusActiveEditorArea picks the live slot
owner; TUI overlay-focus regression pinning the post-fix contract plus
a 'pre-fix snapshot' test pinning the broken pre-fix behavior so the
restore-from-preFocus assumption can't silently change.
Fixes#3349
Converted bracketed non-image filesystem path pastes into session-local attachment references while preserving the existing image path flow.
Added regression coverage for editor routing and controller local file attachment behavior.
Fixes#3360
Address review feedback: provider notes could contain tabs, embedded
newlines, or control characters that break TUI rendering. Both note
rendering sites (provider-wide and per-group) now wrap the joined text
through sanitizeText → truncateToWidth → replaceTabs per AGENTS.md
TUI Sanitization rules.
Provider-wide disclaimers (e.g. OpenCode Go's "OMP-observed spend
only") were duplicated onto every UsageLimit, then repeated N times
in the TUI aggregate renderer (once per account × window). With
2 accounts × 3 windows, the same disclaimer appeared 6 times
bullet-joined.
Structural fix:
- Add notes?: string[] to UsageReport (interface + both schema
copies: usage.ts and auth-broker/wire-schemas.ts) so the field
survives the broker client's "+": "reject" deserialization gate.
- Move opencode-go's disclaimer from per-limit notes to
provider-level notes.
Defensive fix:
- Dedup identical per-limit notes in the TUI aggregate renderer
(command-controller.ts) via [...new Set(...)].
- Render provider-level notes once above per-account sections in
all three rendering paths: TUI (command-controller), CLI
(usage-cli), and ACP (usage-report helper).
Regression tests:
- usage-cli.test.ts: provider-level notes render once, not
duplicated per account or limit; positioned above per-account rows.
- usage-report-notes-schema.test.ts: wire-schema round-trip proving
notes survives usageResponseSchema validation.
Fixes#3268
Address P2 review: when executeBuiltinSlashCommand returns a string
(e.g. /loop 10 fix bug → 'fix bug'), the original slash command text
was not recorded to history — only the extracted prompt was. Now the
original text is added to history before reassigning, so Up Arrow
recalls '/loop 10 fix bug' rather than just 'fix bug'.
Applied to both Enter and Ctrl+Enter submit paths.
Address three P1 code review comments on PR #3352:
1. Colon-separator bypass: parseSlashCommand() treats ':' as an argument
separator, but shouldSkipHistory only split on whitespace. So
/login:?code=abc&state=xyz bypassed the filter. Now uses the same
earliest-whitespace-or-colon splitting as parseSlashCommand.
2. /join <link> secret: the collab join link carries a 32-byte room key
and optional write token. Add /join to the denylist — skip any /join
with arguments.
3. Added regression tests for colon-separator forms and /join denylist.
parseCallbackInput() accepts three forms: redirect URLs, query strings
(?code=...), and raw auth codes — all carry OAuth secrets. The previous
filter only skipped URL-like inputs, leaking query strings and raw codes.
Skip ALL /login commands with any argument. The minor convenience loss
(can't recall /login <provider>) is far less important than the risk of
persisting OAuth authorization codes.
Previously only 4 commands (/plan, /goal, /mcp, /ssh) stored their text
in history via per-handler addToHistory calls. All other built-in slash
commands were silently skipped because executeBuiltinSlashCommand returned
true before the input controller's addToHistory was reached.
- Centralize history recording in the input controller after successful
slash command dispatch, for both Enter and Ctrl+Enter submit paths.
- Remove all 10 per-command addToHistory calls from slash command handlers
to prevent duplicates.
- Add shouldSkipHistory() security filter to exclude commands that may
carry secrets: /login <url> (OAuth callback with code=/state= params)
and /mcp add --token <token> (bearer token).
- Add regression tests for the security filter (8 cases).
- Update 7 existing test files to remove handler-level addToHistory
assertions (now the input controller's responsibility).
- Introduced `generateHandoffFromContext` to enable provider-aware oneshot generation and improved cache hit rates via the live-turn pipeline.
- Updated `buildSideRequestContext` to support pinning custom system prompts, preventing per-turn hook leakage during handoff.
- Added concurrency guards across CLI and RPC modes to block manual `/handoff` requests while a session is actively streaming.
- Standardized handoff execution to force `toolChoice: "none"` and enforce consistent cache-routing behavior.
- Added a `share.store` configuration option (`blob` | `gist`) that allows users to choose between the default share server or a GitHub gist for storing exported session data.
- Changed the default upload target from secret GitHub gists to the share server to avoid GitHub API rate limits for shared sessions.
- Enabled fallback to the share server when a gist upload fails or the GitHub CLI is unavailable.
- Centralized draft state and image management by migrating fields from context to the CustomEditor component.
- Standardized transcript row construction by introducing shared helpers for background jobs, IRC traffic, and file mentions.
- Refactored redundant UI logic and helper functions into reusable utility modules to streamline message submission and component rendering.
- Standardized event handler types by consolidating lifecycle definitions into a shared module while maintaining public API stability.
Skipped optimistic replacement when a user message_start matches another recorded local submission, preserving the pending prompt bubble until its own expanded event arrives.
Added coverage for the queued-message drain race between startPendingSubmission and prompt dispatch.
Fixes#3199
Replaced raw optimistic slash-command transcript entries with the canonical user message emitted by AgentSession when prompt expansion changes the text.
Added coverage for prompt-template expansion reconciliation so the transcript keeps one expanded user message.
Fixes#3199
- Inlined the temporary model status formatting logic directly into the controller.
- Removed the unused `formatTemporaryModelStatus` utility function and its associated test.
- Synchronize tool arguments with the component state upon receipt of `tool_execution_start` to ensure visual consistency when final update events are missed.
- Terminate active argument reveal streams to prevent late ticks from overwriting valid, fully-materialized tool arguments with stale partial data.
- Add test coverage to verify that tool UI components render finalized arguments even in the absence of intermediate streaming updates.
- Replaced the `/debug dump-next-request` command with an updated `/dump` command that exports LLM request context to JSON sidecar files.
- Removed persistent debug path state and manual path configuration in favor of automated generation.
- Updated session logic to handle serializing LLM request context to temporary directories.
- Refactored testing suites to remove path-based debug tests and verify dynamic request file generation.
Updated /mcp enable and /mcp disable so they connect or disconnect only the named server instead of reloading every MCP server in the session. Added regression coverage for both toggle directions and updated the coding-agent changelog.
Fixes#3157
- Added validation to scan for non-ASCII characters before performing snap-compaction, falling back to LLM-based summarization if the unrenderable ratio is too high.
- Updated event handling and status reporting to explicitly support snapcompact actions, including specific error warnings and cancellation states in the UI.
- Updated session logic to default to snapcompact strategy when auto-compaction is enabled.
- Stop the Esc key from aborting active background maintenance (compaction, handoff, or retry) while a subagent is focused.
- Remove "(esc to cancel)" hints from maintenance loaders when a subagent is active to avoid false affordance.
- Ensure that main-session maintenance remains cancellable via Esc when no subagent is focused.
Fixes#2819
Adds 'c copy' to the completed /btw panel footer (alongside b branch / Esc
dismiss), copying the sanitized visible answer to the clipboard. The copy
shortcut is guarded by canCopyBtw + main-editor focus + empty editor.
- Added a `proseOnlyThinking` configuration setting to suppress raw code blocks in AI thinking traces.
- Implemented `formatThinkingForDisplay` utility to replace code blocks with ellipses in the UI.
- Integrated runtime toggling and live refreshing of message components via streaming reveal controllers.
- Added a live tokens-per-second indicator to the assistant thinking pulse.
- Verified logic with new unit and integration tests for thinking block presentation.
- Added `omitThinking` setting to allow instruction of upstream providers to omit thinking summaries.
- Decoupled UI-level thinking block visibility from backend data retrieval.
- Updated session creation to use `omitThinking` for configuring provider-side summaries.
The session picker auto-switched into all-projects scope whenever the
current cwd had no sessions, so /resume from a fresh project silently
surfaced every other project's history. The empty-folder hint already
tells users to Tab into all-projects, but the auto-switch made it
unreachable. Both call sites (the /resume slash command and `omp
--resume` startup) now always open in folder scope; `omp --resume`
keeps the global probe only to early-exit with 'No sessions found' when
nothing exists anywhere. The component-level `startInAllScope` option
is deleted along with its callers.
Fixes#3099
- Improved thinking loop detection logic by refining text normalization and treating stalls as retryable errors.
- Instrumented the agent session to recognize thinking loop markers within retryable error conditions.
- Automated the clearing of stale error banners upon successful auto-retry execution.
- Added comprehensive test coverage for chunked thinking loop errors and banner management.
- Replaced usage of `ReturnType<typeof setTimeout>` and `ReturnType<typeof setInterval>` with the explicit `Timer` type across the codebase.
- Updated several type definitions and function signatures to use concrete types instead of inferred return types for improved clarity and maintainability.
- Renamed the global `INTENT_FIELD` constant from `_i` to `i`.
- Updated documentation strings, type annotations, and test expectations across packages to reflect the new field name.
- Ensured consistent usage of the constant in tool schema construction and intent serialization.
- Added tracking for expected cache invalidations during model changes, compactions, and plan-mode transitions.
- Included `cacheMissExplainedAt` metadata in session context to prevent displaying misleading cache miss warnings in the transcript.
- Updated controller logic to reset assistant usage markers when mode-switching or performing actions that invalidate the prompt cache.