The assistant message_end fan-out is fire-and-forget in the session layer
and can be parked on extension delivery while agent_end is flushed through
#endInFlight, so agent_end can overtake it. #finishPrompt then unsubscribes
the prompt turn and the mapAssistantMessageEnd fallback never runs: an ACP
client that only received agent_thought_chunk updates (thinking streamed,
text arrived only on the trailing message) stays stuck on the thinking
block with no visible answer. On agent_end, emit the last assistant
message's text before resolving the prompt when live-message progress shows
no text was ever delivered, and defer the live-state reset past that flush
so a late message_end cannot resurrect fresh progress and double-emit.
Fixes#4902
The first-result viewport-repaint gate assumed only streamed
__partialJson placeholder shapes (SSH) could re-anchor; the write
renderer's collapsed pending preview paints a tail window from decoded
content, so its first partial result re-anchored to the top of the file
and left the committed tail rows stale above the new frame.
Resolve forceFirstResultViewportRepaint per renderer as a boolean or an
(args, options) predicate evaluated at paint time: write opts in when a
collapsed preview outgrew the streaming tail window, SSH stays scoped to
the streamed-placeholder shape it always covered.
Adopted from PR #4478 (roboomp) with an allocation-free line-count scan
and terminal-buffer regression coverage.
Fixes#4477
Esc during an active streaming turn required a second press within 2s
(two-step arm from #3493). In the no-input-waiter submit path the turn
starts with isStreaming=true but no working loader, so Esc fell into
the two-step branch and the agent_start subscription then wiped the
arm — repeated presses kept re-arming and never aborted. The loader-up
path already aborted on a single press, so the confirmation guarded no
coherent state. First Esc now aborts the streaming turn directly.
Adopted from PR #4938 (test + input-controller + changelog hunks only;
unrelated workflow-notice.md churn dropped).
Fixes#4921
Extensions calling ctx.ui.addAutocompleteProvider (e.g. @ff-labs/pi-fff)
crashed at load with 'TypeError: ... is not a function' because omp's
ExtensionAPI.ui omitted pi's autocomplete-provider API; the throw also
aborted the rest of a try/catch-guarded session_start init.
ExtensionUIContext now declares addAutocompleteProvider(factory).
Interactive mode stacks each factory on the built-in editor provider in
registration order, re-applies the stack on every slash-command refresh,
and skips throwing/malformed factories; RPC, ACP, and headless contexts
accept the factory as a no-op, matching upstream pi's RPC behavior.
Fixes#4919
Extension sendUserMessage() without deliverAs fell through to prompt(),
which throws AgentBusyError during an active stream; the message was
dropped and surfaced as 'Extension sendUserMessage failed'. Route the
omitted-deliverAs path through prompt() with streamingBehavior 'steer'
so streaming queues a steer with normal prompt-flow side effects
(keyword notices, advisor auto-resume reset) and idle still starts a
turn.
ACP skill-command prompts now pass streamingBehavior 'steer'; the RPC
skill fast-path honors the prompt command's streamingBehavior field
(default steer) like the plain-prompt path already did. Documented the
extension-facing delivery semantics.
Synthesized from PR #4942 (prompt-flow steer routing, docs, tests) and
PR #4922 (RPC streamingBehavior threading, steer regression test);
dropped PR #4942's unrelated workflow-notice.md ellipsis churn.
Fixes#4923
Co-authored-by: roboomp <omp@can.ac>
Co-authored-by: metaphorics <metaphorics@users.noreply.github.com>
The v16.3.12 explicit `selector` field (ff3b0c795c) was consumed by the
read tool but never threaded into the TUI renderers: ReadRenderArgs in
both readToolRenderer (read.ts) and ReadToolGroupComponent only derived
selectors from path-embedded `:sel` suffixes, so split-arg calls like
{ path, selector: "2-3" } rendered bare paths without line ranges or
raw modifiers.
Joined the explicit selector (trimmed, leading colons stripped, non-string
guarded) back onto the display path in renderCall, renderResult error and
success branches, and the grouped read summary, keeping hyperlinks on the
base path only.
Adopted from PR #4904 (both commits squashed), minus its unrelated
workflow-notice.md prompt churn.
Fixes#4899
computeNonMessageTokens / computeNonMessageBreakdown re-tokenize the system
prompt and every tool's wire schema (per-tool JSON.stringify) on each call,
but the per-turn compaction and context-threshold paths call them several
times (getContextBreakdown twice, #estimateStoredContextTokens once) over
inputs that change at most once per turn. Memoize on the identity of
(systemPrompt, tools, skills) -- the same stable refs the StatusLineComponent
cache already trusts -- so the expensive parts run at most once per input
change instead of per call.
Stopped new-session and session-switch UI paths from detaching active loader/render components without running their disposal hooks.
Added container and loader coverage for disposing children before destructive transcript/status replacement.
Fixes#4686
- Detected copied shell-prompt transcripts before the Python shortcut router.
- Forwarded OMP terminal chrome pastes through normal prompt submission.
- Added regression coverage for the #4678 transcript shape.
Fixes#4678
- Moved emergency terminal restore registration to the TUI terminal initialization logic.
- Ensured terminal restoration triggers correctly on fatal exits by moving registration out of a side-effect-heavy barrel module.
- Added a registration guard to prevent redundant postmortem handler attachments.
- Added throttling and debouncing to HUD data rendering and observer UI synchronization to coalesce update bursts.
- Constrained the subagent HUD display to a maximum of 8 rows with a truncation notice for hidden sessions.
- Enhanced the session observer registry to categorize update types, enabling more granular UI reconciliation.
- Verified render coalescing and display truncation behavior with comprehensive integration tests using fake timers.
Resolves the two Codex P2s raised on #4420 that merged unaddressed:
- wrapUrlRows indented every continuation chunk. A multi-row terminal
selection includes the newline plus that indent; address bars strip
newlines but preserve or percent-encode embedded spaces, so the
reassembled URL was corrupted at every chunk boundary - silently,
when the damage landed inside a query value. Chunk rows now carry
zero leading bytes (label rows keep their indent), and the test
reassembly helper concatenates chunks raw instead of stripping the
indent that previously masked exactly this defect.
- #launchUrlIfSafe advertised a localhost /launch copy target for
flows whose redirectUri never returns to the loopback server. Its
catch-comment assumed custom-scheme URIs are non-parseable, but
new URL('vscode://gitlab.gitlab-workflow/authentication') parses
fine and sailed through the pathname check. The guard now requires
an http(s) loopback redirectUri (localhost / 127.0.0.1 / [::1]);
custom schemes, non-loopback hosts, and unparseable URIs all
suppress the launch URL. Regression tests cover the GitLab Duo
vscode:// shape and a fixed non-loopback HTTPS redirect.
Refs #4418
- Propagated builtin allowArgs metadata into TUI autocomplete entries.
- Let no-arg slash-looking prompts fall through to prompt-composer completions while keeping argument-capable commands scoped.
- Added regressions for /settings @ and /settings #copy.
- Stopped prompt-composer # actions and @ file references from claiming submitted slash-command argument text without explicit argument completions.
- Added provider regression tests for /rename title arguments and explicit command argument completions.
Fixes#4600
macOS laptops have no dedicated Forward Delete key. Fn+Backspace is the
only way to send \e[3~, and many macOS terminals (Terminal.app, some
iTerm2 profiles) deliver \x7f for that combo instead — so the keystroke
landed in the search box, not the delete handler, making session deletion
unreachable for those users.
Add a Backspace-on-empty-search handler alongside the existing Delete
check. With a typed query, Backspace stays bound to the search Input so
users can still edit their filter text. The existing confirmation dialog
guards against accidents.
Footer hint updated: [Del delete] -> [Del/⌫ delete].
Rendered the status-line token rate as an explicit tok/s unit so Ghostty no longer auto-detects the numeric value as a URL.
Added a regression test for the token_rate segment rendering contract.
Fixes#4541
Replace the visible-anchor suppression with a billed-usage predicate so live and resume paths agree on rendering the badge whenever the turn actually consumed tokens. Only genuinely free turns (no input, output, cache, or premium requests) drop the row, so hidden automated turns keep cost transparency.
Fixes#4532
- Exposed retry fallback chains in the model settings panel.
- Added a /model action that assigns the selected model as the default retry fallback.
- Cleared retry cooldown suppression when users manually switch models.
Fixes#4533