The live AskDialog trusted question.question while its render helpers
(replaceTabs, renderQuestionTitle, questionTabLabel) assume a string. A
question reaching AskDialogComponent without a string question field threw
an uncaught TypeError that escaped the TUI render loop and killed the
session. The transcript renderer already normalizes the same malformed
data via normalizeRenderQuestions; the live path did not.
Normalize the questions array at dialog entry (new normalizeDialogQuestions),
coercing question/id/label to strings and options to a well-formed array,
matching the transcript path.
Fixes#7211
- Centralized PNG conversion behind an async boundary so constructor and encoder throws become promise rejections.
- Kept live and restored Kitty image rendering interactive by omitting failed conversions.
- Added regressions for both tool-result render paths.
Fixes#7160
macOS Terminal.app consumes Option for character composition, so Alt+Up
never reaches the app and the dequeue is unreachable there. Bind Shift+Up
alongside it -- Shift is not intercepted, and the key was unbound in the
input path.
The three overlay handlers that already use shift+up for fast scroll
(scroll-view, model-hub, log-viewer) match keys directly rather than
through the manager, so they never see this binding.
Both default tables move together: the registry in config/keybindings.ts
and DEFAULT_ACTION_KEYS in custom-editor.ts, which silently shadows the
registry when they disagree.
- Wrap status line path segment labels and worktree names in file hyperlinks.
- Apply hyperlink generation using the project directory and rendered path text.
- Add the `app.live.toggle` keybinding defaulted to `Ctrl+L` to start or stop live voice mode.
- Remap the default display-reset action (`app.display.reset`) from `Ctrl+L` to `Alt+L`.
- Update the live visualizer to listen for stop keys so the toggle chord terminates active sessions.
- Implemented clipboard register management, parsing, and execution rules for CUT, COPY, and PASTE operations in the hashline engine.
- Added session-persistent clipboard state and integration across agent session execution, diff previews, and streaming tools.
- Added comprehensive validation, error messages, recovery handling, and test coverage for clipboard and block operations.
- Updated task tool execution to pin live regions and drop partial snapshots once rows commit.
- Tracked background task frozen styled rows and render timestamps to prevent clock drift on committed history.
- Added tests verifying detached and blocking task progress do not duplicate rows in scrollback.
- Implemented session stores and metadata converters to import Claude and Codex sessions into OMP.
- Added `--from-claude` and `--from-codex` CLI flags and `/resume` command arguments for foreign session resolution.
- Updated session selector components and controllers to support listing and picking external agent sessions.
- Added comprehensive unit tests and documentation covering foreign session import functionality.
- Replaced the `XdevRegistry` class with the `XdevState` interface and pure helper functions across core and session tools.
- Updated session configurations, tool execution, and renderers to utilize canonical tool map initialization and sharing.
- Adapted unit tests and mocks to use `XdevState` and associated helper functions for permission and dispatch verification.
- branch() and navigateTree() now return the selected user message's image
parts (selectedImages/editorImages) alongside the text, extracted in marker
order by #extractUserMessageImages.
- CustomEditor.setDraft() replaces the composer draft with text plus its
pending images, so restored [Image #N] markers resolve on resubmit instead
of degrading to literal text.
- Wired all six restore call sites (selector-controller, extension-ui-controller)
through setDraft; updated rpc-subagents mocks for the new branch shape.
- Added offline regression tests for branch/navigateTree image restitution,
multi-image marker order, and text-only prompts.
- Add an LRU cache to `scanSessionFile` in `session-listing.ts` keyed by file path, stat identity, and scan mode.
- Add a match key union probe in `CustomEditor` in `custom-editor.ts` to bypass per-action lookups on plain text input.
- Add tests covering cache hits, size and mtime invalidations, and negative result caching.
Render the Advisor spend next to the primary-model cost as `$2.67 (sub) + $0.41 (adv)`, leaving the status line unchanged until an Advisor cost exists.
Record the cost from finalized advisor `message_end` events in a per-session ledger instead of deriving it from the live advisor transcript, so an in-session compaction or any other history rewrite no longer resets the reported spend. The ledger is cleared for a new session and once a different-session switch commits, and survives a switch that rolls back.
handleDraftEdit routed everything through the base editor, which reserves Ctrl+C for the parent and returns without touching the buffer, so the configured app.clear never ran and the guard's 'finish or clear the prompt' hint had no working clear key when Ctrl+C reached the guard.
handleDraftEdit now dispatches the app.clear action explicitly (onClear, falling back to clearing its own text), which empties the draft and lifts the guard without swapping the editor slot.
Fixes#6737
The draft editor renders an insertion cursor only when its focused flag is set, but ask holds TUI focus, so the preserved draft had no visible caret while it required finishing or clearing.
The input guard now mirrors its blocked state onto the draft editor each ask render (editor is the next sibling in the same container), showing the cursor while it owns input and dropping it once the draft clears.
Fixes#6737
Forwarding raw keys through CustomEditor.handleInput enabled its app-slot shortcuts (Agent Hub, model selector, ...) while an ask dialog was open over a draft; those clear editorContainer and orphan the pending ask promise.
handleDraftEdit bypasses the shortcut interception so only text editing, cursor movement, and submission reach the buffer.
Fixes#6737
- Kept a populated editor visible beneath an asynchronously opened Ask form.
- Routed input to the draft until it is submitted or cleared, then activated Ask controls.
- Added regression coverage for the focus handoff.
Fixes#6737
Codex review flagged that /tmp/a.png ./b shot.png slipped past the
interior-anchor guard (absolute prefixes only) and fused into one bogus
attach that swallows the paste. Add ./, ../ and .\ as second-path
anchors; bare relatives (dir/b shot.png) stay recoverable because an
interior token/ after a space is exactly the shape of a spaced
directory name (/Users/me/My Photos/shot 1.png). 4 tests pin both
sides of the boundary.
omp config list printed every configured value, including auth.broker.token,
searxng.token, searxng.basicPassword and dev.autoqaPush.token, in both the
human and --json output. Nobody asked for those specific credentials; the
command dumps everything.
Credentials are marked with a top-level credential flag rather than ui.secret,
because four of them have no settings-panel entry and so have nowhere to put a
UI-level flag. isCredential is the single accessor both the CLI and the panel
consult, so the two spellings cannot produce different behaviour on different
surfaces.
Human output shows dots. JSON omits value and marks the entry redacted instead
of substituting a placeholder, which a consumer could not distinguish from a
real value and might write back.
config get <path> is deliberately unchanged: that is an explicit request for a
single value, and masking it would break a retrieval API with no way to read
your own token back.
The whole-text-as-path fallback added in the previous commit claimed any
single-line payload starting with an absolute-path anchor, including one
holding several paths. Dragging two files at once emits
`/tmp/a.png /tmp/b shot.png`, which the segment splitter also refuses
because `shot.png` is not explicit, so the fallback fused the pair into
one unresolvable path. `handleImagePathPaste`'s ENOENT branch only
surfaces a status and — unlike its too-large and generic-error branches
— never re-pastes the text, so both paths vanished.
On `main` the bracketed route returned undefined for that payload and
fell through to a text paste, so this was a regression introduced by the
previous commit rather than behavior inherited from the clipboard route.
It is reachable by the same gesture that motivated #6578, with one more
file selected: macOS screenshot names always contain spaces.
`extractWholeTextImagePath` now rejects payloads carrying a second
absolute-path anchor after unescaped whitespace. The anchor alternation
moves into a shared `ABSOLUTE_PATH_PREFIX_SOURCE` so the leading-anchor
and second-anchor tests cannot drift apart across the POSIX, `~/`,
`file://`, UNC and Windows-drive families. Escaped whitespace is exempt,
since the escape is the terminal asserting the space belongs to the path.
Guarding the shared helper rather than the bracketed caller also settles
`extractImagePathFromText`, whose JSDoc already claimed multi-path text
falls through to a text paste while the fallback leaked around it.
Ambiguous input — a directory whose name ends in a space, as in
`/tmp/odd dir /sub/x.png` — is treated as multi-path and pastes as text:
a text paste loses nothing, a bogus attach loses everything.
11 tests added covering each anchor family, tab separation, the
escaped-space exemption and the unchanged splitter-success path.
The bracketed-paste (drag-drop) image route required every whitespace-split
segment to look path-like, so a raw macOS screenshot path (spaces unescaped,
per the attachment convention terminals implement) degraded to literal text.
Extract the keybind route's whole-text-as-path pass into a shared helper and
apply it when the segment splitter fails; route the bracketed extractor
through the stripped-marker one so both share identical detection.
Fixes#6578
While a provider error is pinned in the banner above the editor the inline transcript block is suppressed, so the prior guard skipped re-rendering on Ctrl+O and the full body stayed unreachable until the next turn.
- Track whether the message carries a truncatable error regardless of pinning, so setExpanded re-renders while pinned.
- Render the inline error block in full when expanded even while pinned; keep it suppressed only while pinned and collapsed.
- Disable the streaming fast path whenever the inline error block is drawn.
Fixes#6555