- 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
New live, rebuilt, and transcript-builder assistant components now inherit the active tool-output expansion state before provider errors render.
Added regression coverage for an error arriving after expanded mode was already enabled.
Fixes#6555
Turn-ending provider errors rendered inline through
AssistantMessageComponent#appendErrorBlock, capped at 8 lines with no
setExpanded method, so isExpandable filtered the component out of the
Ctrl+O tool-output expansion and the truncated tail was unreachable in
the live TUI (full text was persisted but not shown).
- Add setExpanded to AssistantMessageComponent; when expanded the error
block renders the full body (tabs replaced, blank lines preserved,
Text word-wraps to width).
- Collapsed view appends a dim "+N more lines (Ctrl+O to expand)" hint so
the truncation and its remedy are discoverable.
- Only re-render on toggle when the last render produced a truncatable
error block, so expansion skips ordinary turns.
Fixes#6555
- Add `CodexAttestationProvider` hook and `getCodexAttestationHeader` resolver that gates on ChatGPT-OAuth credentials.
- Integrate attestation into WebSocket transport, SSE event stream, and OpenAI compaction requests.
- Wire `setCodexAttestationProvider(generateCodexAttestation)` in model-registry init for OAuth sessions.
- Added `LIVE_DELEGATION_MESSAGE_TYPE` constant and delegation message handling for voice sessions.
- Implemented turn-based transcript coalescing with user and assistant turn counters.
- Added transcript display row with normalized rendering in the live visualizer.
- Refactored controller to send delegation messages via `sendCustomMessage` with configurable frame styling.
- Removed microphone permission error reporting from silence detection logic.
- Extracted #formatToolExecution into a standalone formatDefaultToolExecution module.
- Updated xdev renderXdevCall and renderXdevResult to use the default card when no mounted renderer exists.
- Added fallback rendering that shows tool label, args, and output with appropriate theming.
- Added integration test verifying generic card renders for mounted tools without bespoke renderers.
- Add `pinSessionOAuthAccount` storage method and active account flag to the auth storage API.
- Introduce session account selector component, controller logic, and interactive mode delegation.
- Implement the `/session pin` builtin slash command with text listing and account pinning capabilities.
- Add unit tests covering session account selection, component navigation, and command handling.
- Pinned displaceable snapshots like hub waiting polls and todo lists to the viewport during active execution.
- Prevented unpinned spinner ticks from repeatedly committing mutating rows to native scrollback and force-sealing blocks.
- Consolidated Jujutsu (jj) integration logic into a centralized utility module with working-copy and status handling.
- Removed deprecated jj-info helper modules and their corresponding test files.
- Updated status line component and associated tests to consume the new centralized jj utility API.
- Added comprehensive test coverage for working-copy label parsing, status summary mapping, and repository root resolution.
- Replaced single-provider preferences with ordered priority lists for web search and image generation.
- Added a `MultiSelectSubmenu` component supporting toggle and reordering interactions in settings.
- Implemented migration logic to convert legacy single-provider preferences into ordered priority lists.
- Updated setup wizard scenes, image generation fallback logic, and search provider chains to use priority lists.
- Provider sign-in, theme, and web-search lists now shrink their visible row window to fit available height.
- Decorative chrome (sign-in hint, theme mock preview) yields to the list when space is tight.
- SelectList and OAuthSelector expose `setMaxVisible`/`setMaxHeight` so hosts can refit lists after construction.
- Implemented native UTF-16 text processing in Rust diff module with support for unpaired surrogates.
- Removed `similar` crate from Rust workspace and `diff` npm package from coding-agent, hashline, and natives.
- Removed jsdiff fallback wrappers and `isWellFormed()` guards from TypeScript diff implementations.
- Added comprehensive test suite for native diff functions covering random inputs and edge cases including surrogates and emoji.
- Renamed model `codex-auto-review` to `gpt-5.3-codex-spark` with updated pricing and context window.
- Merged the executor-reported fallback flag into the live-session badge path so a Fireworks Fast to base degrade (which arms no session retry state) keeps its provenance.
- Added a live-row regression test for a fallback that populates no retryFallbackModel.
Fixes#6316
- Threaded a resolvedModelIsFallback flag through AgentProgress and SingleResult.
- Set the flag from the executor retry-fallback handlers and settled results.
- Rendered the observer/no-session hub path as fallback -> provider/model.
- Added an observer-only fallback-badge regression test.
Fixes#6316
- Exposed the active retry fallback selector from live agent sessions.
- Rendered fallback rows with an explicit marker and resolved provider/model.
- Added an end-to-end fallback-to-Agent-Hub regression assertion.
Fixes#6316