Commit Graph
352 Commits
Author SHA1 Message Date
Felipe Coury 814347ecd2 Improve sidebar chat selection and tab hover highlighting (#59928)
## Why

The sidebar chooser used hard-coded keys and hints instead of configured list bindings, limiting navigation and making custom shortcuts inconsistent.

## What changed

- Use configured list actions for chooser navigation, acceptance, and cancellation, including key chords, page movement, and jumps to the first or last choice. Show the configured shortcuts in footer and retry hints.
- Place “New side chat” first and group existing chats into compact rows with a count and optional thread colors. Keep the selected chat visible in short panes.
- Highlight inactive side-chat tabs on hover while preserving the selected tab's styling. Redraw when the hovered tab changes and suppress hover highlighting during pane resizing.

## Testing

Extend interaction tests and snapshots to cover custom acceptance chords, `j`/`k` navigation, paging, and tab hover styles and redraws with either pane focused, including the fallback when terminal colors are unavailable.

GitOrigin-RevId: bad992b3786215dcb5569acfc72af8841fad949d
2026-11-06 15:34:29 +00:00
psachdeva-oai ddd1812a51 Carry targeted moderation decisions on final-answer messages (#59842)
## What changed

Read `final_answer_moderation` metadata and attach `moderation: { moderated: boolean }` to the completed final-answer message only when its item ID matches. Apply this in both normal and plan modes, and reset the pending decision when a new response starts.

Preserve the answer's native item ID and moderation flag through legacy events and history reconstruction. Combine text parts into one legacy event when a moderation decision is present.

Expose nullable moderation data on app-server agent messages and update the JSON schemas, TypeScript types, and Python models.

## Testing

Add an integration test for moderation on completed answers and their legacy events. Extend protocol conversion tests to cover populated moderation data and serialization as `null` when absent.

GitOrigin-RevId: f286b67731a84b3daf5d10b7644f2e31b62f0e53
2026-11-06 07:43:44 +00:00
Felipe Coury 5fab289359 Enable resizing the sidebar chooser (#59824)
## Why

Sidebar resizing previously required an open side chat, leaving the chooser without the same width controls.

## What changed

- Support keyboard resizing, width presets, and divider dragging for the sidebar chooser, with resize hints that fit narrow sidebars.
- Keep keyboard resize mode active across startup thread assignment and replacement of the chooser by a new side chat, so `Enter` finishes resizing without submitting the parent draft.
- Activate resize bindings only when a split is available, and prevent divider drags from starting while the split is animating.

## Testing

Add regression coverage for chooser resizing, width preservation when switching between the chooser and a chat, startup and side-chat creation transitions, draft preservation, and unavailable resize bindings in narrow terminals.

GitOrigin-RevId: 511cd4bb5894e6f33f0cf5d7472bb3013361ffe0
2026-11-06 04:53:30 +00:00
Benjamin Carlsson a142c90e26 Handle TUI message submission receipts and preserve uncertain drafts (#59817)
## Why

The TUI's cached active turn can be stale when a cloud message is submitted. A lost reply also leaves delivery uncertain, so automatically resending the draft risks submitting it twice.

## What changed

- Let the cloud server choose whether an Enter submission starts or steers a turn, and apply its receipt to the matching client message ID.
- Keep Tab-queued input waiting until the admitted turn finishes, including before its start notification arrives.
- Restore rejected submissions to the composer. Hold duplicate or unconfirmed submissions out of automatic delivery until history confirms them.
- Reconcile saved drafts and pending steers by client message ID when resuming sessions, and preserve submissions belonging to other turns during interruption recovery.
- Classify cloud preflight failures as invalid parameters so rejected queued input can be restored safely.

## Testing

Add regression coverage for cloud receipt outcomes, queued-input ordering, rejected drafts, lost replies, interruption recovery, and session resume with identical message text but distinct client IDs.

GitOrigin-RevId: ec4eb581e450488434bc4614ec59273d73ffd0a1
2026-11-06 03:51:37 +00:00
Felipe Coury d63a342256 Support multiple side chats in the TUI sidebar (#59784)
## What changed

- When side retention is enabled, let the sidebar chooser create additional side chats from the main chat or reopen existing ones.
- Add numbered, clickable tabs and keyboard navigation in the chooser. Keep the selected tab visible when space is limited.
- Preserve drafts, transcript history, and the sidebar layout when switching between sibling side chats.
- Make `/side` with a prompt request a new side chat, while `/side` without arguments reopens the selected side chat. Preserve existing reuse behavior when side retention is disabled.

## Testing

Extend TUI tests and snapshots to cover creating sibling side chats, switching through the chooser and tabs, retaining drafts and history, and restoring image attachments after failed creation.

GitOrigin-RevId: 4166010c253c40a409390f7020b69478c04cf98c
2026-11-06 01:49:57 +00:00
Felipe Coury 9df200a068 Add sidebar tabs and nonblocking side-chat creation (#59767)
## Why

Starting a side chat waits for fork and setup requests. Keep the workspace responsive during those requests and let users return to an existing side chat through the sidebar chooser.

## What changed

- Add a clickable `Side chat` tab and `+` chooser, plus the configurable `focus_sidebar_tabs` action bound to `leader t` by default.
- Create workspace side chats in the background, showing loading and failure states with `Enter` to retry and `Esc` to go back. Preserve opening prompts and avoid taking focus when the user has moved away.
- Retain inherited permission profiles and defer startup notifications until the side chat is registered on its owning backend.
- Quiet the parent composer while the chooser has focus and prevent completion popups or repeated activation keys from consuming chooser input.

## Testing

Extend tests and snapshots for loading and failure states, returning to an existing chat, draft preservation, inherited permissions, focus transitions, and deferred MCP startup notifications.

GitOrigin-RevId: 244bcd3f170819e2260f21dc1041cf8899fe048c
2026-11-06 00:21:16 +00:00
Felipe Coury 194f2662b8 Add a sidebar chooser and floating workspace shortcut panels (#59739)
## What changed

- Open a sidebar chooser when no side chat exists, with keyboard and mouse controls to start a new side chat while preserving the parent draft.
- Animate sidebar opening and closing, including reversing an unfinished transition, when animations are enabled.
- Show context-aware workspace shortcuts in a floating panel after a short delay, and render presets in the same overlay without moving the composer.
- Prevent chooser input from accepting an approval dialog before it has been painted.

## Testing

Add coverage for chooser activation and draft preservation, sidebar animation reversal, shortcut and preset snapshots, and approval input handling.

GitOrigin-RevId: 014112622a94798f4e611756c55112dfd8016051
2026-11-05 21:05:51 +00:00
alexsong-oai b2a8e68c74 Allow choosing the default provider for new chats in the TUI (#59722)
## What changed

- Add Space to set the default provider from Model providers settings, persisting `model_provider` on the connected server. Keep provider details available and disable selection when server configuration or client launch settings fix the provider.
- Apply the choice to `/new` while preserving the current chat. Refresh account details for new chats and reject account data that does not match the started provider.
- Block stale `/model` choices during saves and after confirmed or uncertain provider changes, including across reconnects. Tell users to restart the app server and TUI to refresh the model list, and discard queued picker actions from another backend or chat.

## Testing

Add coverage for provider saves, retry after disconnection, concurrent-save prevention, stale model choices, and account/provider matching, with updated provider-picker snapshots.

GitOrigin-RevId: af6d149f7de7a2eaa09a31935f7088faa64d6a59
2026-11-05 19:11:18 +00:00
Felipe Coury ed8eac4083 Add keyboard controls for TUI pane resizing and zoom (#59595)
## What changed

Add configurable pane actions with default leader shortcuts:

- `leader z` zooms or restores the focused conversation pane.
- `leader ,`, `leader .`, and `leader =` minimize, maximize, or equalize pane widths within layout limits.
- `leader shift-h` and `leader shift-l` move the divider while the side pane has focus.
- `leader r` enters resize mode, highlighting the divider and showing keyboard hints. Arrow keys or `h`/`l` move it one column; Shift+Left/Right move it ten columns. Enter or Esc saves the width to `tui.sidebar_width_percent`.

Discard resize previews when terminal focus is lost or the split layout becomes unavailable. Preserve exact column widths at the current terminal size for presets and completed keyboard resizing, avoiding percentage-rounding drift.

## Testing

Extend TUI tests and snapshots to cover zoom and restore, width presets, divider movement, resize previews and persistence, and interrupted resizing. Extend keymap tests to cover zoom binding conflicts and custom chords.

GitOrigin-RevId: 7dd823fd8c2a0c90368c66f254c4fdd7c863c8a8
2026-11-05 07:34:45 +00:00
Felipe Coury e8e3197e84 Make side conversation panes resizable and interactive (#59527)
## What changed

- Allow dragging the pane divider and save the preferred width in `tui.sidebar_width_percent`, defaulting to one third of the terminal. Defer reflow until release when retained images are present.
- Support scrolling and loading older history in the unfocused pane, opening links, copying selections, and clicking its composer to switch focus while preserving the clicked text position.
- Keep assistant replies, running command completion, and turn status current in the unfocused pane without duplicating answers or completion metadata when focus returns.

## Testing

Add regression coverage for divider previews and saved widths, image-aware resizing, composer clicks, unfocused history pagination and copying, and streaming replies and command completion across focus changes.

GitOrigin-RevId: a26accc0f8df3c17f53bc915db29a11c8de3c480
2026-11-05 00:19:26 +00:00
Felipe Coury 4f6acbf02d Preserve transcript interaction when switching panes with arrow keys (#59472)
## Why

Directional pane switching cleared the parent conversation's transcript selection or Find state, so returning to it interrupted the interaction.

## What changed

Route left/right pane switches through the existing conversation toggle to park and restore each pane's transcript interaction. Explicitly focusing the main composer still clears transcript interaction.

## Testing

Extend the side-conversation test to cover restoring selections, Find text and caret position, and composer input after scrolling. Add a snapshot verifying that Find remains editable after switching away and back.

GitOrigin-RevId: 172f9be0df15517fbfe7f3773b63cbd1cef1cc77
2026-11-04 18:56:44 +00:00
Eric Traut ba057e4375 Drain queued side-conversation events before switching focus (#59458)
## Why

Pressing `Ctrl-/` immediately after `/btw` must not switch focus before the queued side-conversation prompt is dispatched.

## What changed

Wait for queued app events to drain before reading terminal input when the displayed thread belongs to a parent/side conversation pair, even when side retention is disabled. This keeps queued prompts and transcript updates ahead of focus changes.

## Testing

Extend the side-conversation startup test to toggle focus at the earliest allowed opportunity and verify that the turn starts on the side thread. Also check that a queued request to reopen the side conversation blocks terminal input until handled.

GitOrigin-RevId: cb46636d35de97b02994ed68a32eb971cd7bf6db
2026-11-04 17:23:44 +00:00
Felipe Coury db3c44414c Add directional conversation focus shortcuts in workspace mode (#59287)
## What changed

- Add configurable `focus_pane_left` and `focus_pane_right` actions, defaulting to `leader left` / `leader h` and `leader right` / `leader l`.
- Move focus to the parent or open side conversation without reopening a hidden pane. Preserve the current interaction when no neighbor is available, and accept fresh typing after a no-op shortcut while suppressing reported key repeats.
- Expose the actions in the keymap picker and configuration schema, and prevent workspace-only pane bindings from claiming keys in classic mode.

## Testing

Add regression coverage for pane navigation, preserved transcript selection and search, fresh typing after a no-op, key capture, and binding conflicts and limits.

GitOrigin-RevId: 7f7629d15cffcd35d1353b36c5c9208dbb1034e8
2026-11-04 01:40:12 +00:00
Felipe Coury 6b2a2757de Add shortcuts to focus the main composer and side conversation (#59208)
## What changed

- Add configurable `focus_main_composer` and `focus_sidebar_content` actions, with default `leader c` and `leader s` bindings in workspace mode.
- Let users return to the main composer while keeping the side pane open, or focus the side conversation and hide it when already focused.
- Clear transcript search, selection, and activity focus when returning to the main composer, including when hiding the side pane.

## Testing

Extend tests for focus switching, blocked thread switches, offline composer focus, key capture, and binding conflicts.

GitOrigin-RevId: da624ce7e00a63c4f13870e55c4bc95f09b32cc4
2026-11-03 18:55:44 +00:00
Felipe Coury eb15d3a0f5 Add a shortcut to hide the focused side conversation pane (#59130)
## What changed

Add `tui.keymap.global.hide_pane`, defaulting to `leader -` in workspace mode. Hiding a focused side conversation returns to its parent while retaining the conversation, drafts, and view state for reopening.

Expose the action in the workspace keymap picker, validate binding conflicts, and suppress held shortcut keys after switching focus.

## Testing

Add coverage for hiding and reopening at split-pane and narrow widths, preserving conversation state, shortcut availability, key capture, and repeat/release handling.

GitOrigin-RevId: 6897c34b208041e6f90ae0e18c781eebd62566bd
2026-11-03 12:08:29 +00:00
aojiao-oai 16ddd538bd Expose assistant content token counts in completion notifications (#59041)
## What changed

Add optional `contentTokenCount` metadata to `item/completed` notifications, carrying the producer-supplied token count for each completed assistant message in default and plan modes. Counts include any sampled footer and are `null` when unavailable.

Keep this telemetry separate from stored thread items and omit it from subsequent model requests. Update the protocol schemas and generated TypeScript and Python types.

## Testing

Add coverage for per-message counts, including zero, in default and plan modes; missing and malformed metadata; backward-compatible notification decoding; and exclusion from subsequent model requests.

GitOrigin-RevId: 35973037a42650fa2d7569da8e88cb81215cf971
2026-11-03 02:26:29 +00:00
Benjamin Carlsson 26f73fba8f Add host cycling and saved defaults for new TUI conversations (#59007)
## What changed

- With multi-backend support enabled, show the current host before the first submission and use `leader b` to cycle through available connections. Expose the shortcut as `tui.keymap.global.change_host`.
- Preserve the conversation name and composer draft, including cursor position, attachments, pending pastes, and literal versus shell-command interpretation of `!`.
- Save explicit host choices in user-only `tui.default_hosts`, scoped to the local project and user identity. Use the saved host for new conversations, falling back to the first available connection when it is unavailable.
- Prepare the destination and check directory trust before replacing the current conversation. Block switching during voice calls and active turns, and remove replaced blank conversations from the session overview.

## Testing

Add coverage for shortcut visibility and remapping, draft and name preservation, project/user preference isolation, unavailable-host fallback, destination trust and startup failures, and voice-call guards.

GitOrigin-RevId: def7b088951336bec8fe3962954fba9b861f91c6
2026-11-02 23:19:03 +00:00
Felipe Coury db227aed42 Add opt-in side conversation panes to the TUI (#58992)
## What changed

- Add the disabled-by-default `tui_workspace` feature for the owned transcript view. At terminal widths of at least 100 columns, show the parent conversation beside its side conversation, with only the focused composer accepting input.
- Retain the inactive conversation's transcript, draft, and status, and restore reading and search state when switching focus.
- Keep drawing and input paused until history replay completes, while refreshing skills in the background.

## Testing

Add snapshots and regression tests for pane resizing and composer styling, retained streaming output, history search after replay, pending skills refreshes, and modal input protection.

GitOrigin-RevId: 68938f263353b0cd3a9e9562e06293bfc8251cc6
2026-11-02 21:47:45 +00:00
Felipe Coury 8a17396d13 Fix live stream reflow at the allocated conversation width (#58927)
## Why

Streaming answers and plans need to wrap to the conversation's allocated width, which can differ from the terminal width. Resizing while reading earlier output should not report new activity unless new output has arrived.

## What changed

Reflow live streams when preparing the owned conversation layout, using its allocated width. Update the observed live revision after presentation-only reflow so it does not trigger `New activity`, while preserving notifications for unseen output.

## Testing

Add regression coverage for streaming answer and plan tables across width changes, including a narrow-layout snapshot, preservation of the reading position, and activity notifications when new output arrives before a resize.

GitOrigin-RevId: a9831c5a1a5ffb7477e7868d4ecca430d6ee50f3
2026-11-02 16:56:09 +00:00
Tamir Duberstein 181b4a853d Prioritize pending approvals in the subagent picker (#58720)
## What changed

- Group threads needing approval first in `/subagents`, mark them with `(needs approval)`, and initially select the first waiting thread.
- Refresh the open picker and approval notice as requests and agent metadata change, preserving the selected thread when rows move.
- Use consistent agent labels across the picker and approval notice, preferring agent paths and using UUID suffixes to distinguish unnamed agents.
- Track approval requests that arrive before thread metadata and refresh pending approvals after reconnecting.

## Testing

Extend the picker test and snapshots to cover approvals arriving while the picker is open, agent paths arriving later, and selection staying on the same thread after reordering.

GitOrigin-RevId: b7dd943d151ce98d1978f7c6b9f5e16903b9075b
2026-11-01 15:51:11 +00:00
Felipe Coury 8c811e8d93 Support rendering TUI conversations within an inset area (#58718)
## What changed

Split conversation rendering into preparation, painting, and post-draw steps. Use the supplied conversation bounds for transcript wrapping, composer layout, suggestion overlays, cursor placement, and the empty-state animation.

Preserve surrounding text and image placements during input-triggered redraws. Keep centered dialogs relative to the full viewport and restore neighboring content when they close.

## Testing

Extend regression tests and add a snapshot for conversations rendered at an offset, covering selection, link hit testing, cursor placement, scroll preservation, neighboring images, and dialog dismissal.

GitOrigin-RevId: 0dbca3f8d1064f2d58856c3bc77a4dab1c099cdd
2026-11-01 15:50:25 +00:00
Matthew Ma 5e769a1cda Preserve transcript history when clearing the terminal with Ctrl+L (#58266)
## Why

`Ctrl+L` discarded the retained transcript along with the terminal display, making earlier exchanges unavailable in the detailed transcript.

## What changed

Keep earlier exchanges accessible through `Ctrl+T` while starting a fresh terminal display with a new header. Treat that header as the boundary for compact scrolling, pinned prompts, and resize replay so cleared content stays hidden in the normal view. Allow the detailed transcript to load older history after clearing.

## Testing

Extend the `Ctrl+L` regression test and add a detailed-transcript snapshot covering retained exchanges, draft preservation, older-history loading, and cleared-content visibility after resizing and replay.

GitOrigin-RevId: f1eb73e903a7957cedafb52678c607858fb2d77f
2026-10-30 06:54:13 +00:00
Joe Savona 797bab4746 Add an experimental UI tree renderer for the status line (#57970)
## What changed

- Add the disabled-by-default `tui_extensions_statusline` feature to render configured status items and their picker preview through a shared UI tree renderer.
- Support text, HTTP(S) links, rows, spacers, dividers, and gaps with rendering budgets, text sanitization, and width-aware truncation that keeps the ellipsis outside hyperlinks.
- Use plain text for the extension status line and hide the picker's theme color toggle while preserving the saved color preference.

## Testing

Add coverage for tree serialization and URL validation, Unicode widths, hyperlink clipping, picker previews, color preference preservation, and token usage updates across renderer switches.

GitOrigin-RevId: a6b87c2b1096765d61296d73e142834fcd53ceee
2026-10-29 03:07:15 +00:00
Abhinav 7ce5fa7bdc Validate thread providers before resume and input (#57840)
## Why

Existing chats retain their provider configuration when the selected provider changes. Reject resume and input until the configured provider matches the chat, while keeping history readable and allowing recovery after configuration is restored.

## What changed

- Validate saved providers before applying resume overrides, preserving the provider when only model or reasoning settings change.
- Check the current provider selection, including profiles and launch overrides, before accepting input. Reject input when configuration cannot be loaded.
- Return structured `ModelProviderError` data for provider mismatches, missing definitions, and policy conflicts, with generated JSON Schema, TypeScript, and Python types.

## Testing

Add coverage for override bypass prevention, provider restoration, readable history after rejection, and blocked requests sending no inference traffic. Verify schema fields and Python round trips match the error wire format.

GitOrigin-RevId: 051f97c3815c547d8411c71b770df30eae969f68
2026-10-28 21:33:02 +00:00
Benjamin Carlsson b7f7e7f7e8 Separate TUI session resume preparation from activation (#57342)
## What changed

Split saved-thread resume into preparation and activation. Preparation resolves configuration and attaches to the destination thread before activation replaces the displayed conversation.

Keep stored-backend configuration and local settings separate from active settings during preparation, while retaining the existing eager settings refresh for active-backend resumes. Use the destination backend for working-directory, permission, and directory-trust checks.

Carry the backend identity in the prepared result and reject activation through a different backend before changing the active conversation or composer.

## Testing

Extend coverage for preserving active settings and composer drafts during preparation, rejecting activation through another backend, applying destination remote-permission checks, and retaining active-backend settings refresh behavior.

GitOrigin-RevId: 7171f1abcf4bdd62793c21232a8b895bb809cf70
2026-10-27 04:09:04 +00:00
Ian MacLeod a014908c50 Represent user message presentation with an enum (#57291)
## What changed

Replace `UserHistoryCell`'s `spoken` boolean with `UserMessagePresentation`, defaulting to `Plain` and using `Spoken` for speech transcripts. Route rendering checks through `is_spoken()`, preserving the existing prompt styling and whitespace handling.

GitOrigin-RevId: d0683a2b536a7d71f68fd9a089e6839d0ab0cc65
2026-10-26 22:11:27 +00:00
Charlie Marsh a797dd7c6f Expose lifecycle timestamps on app-server thread items (#57172)
## What changed

- Add nullable `startedAtMs` and `completedAtMs` fields to `ThreadItem` in live notifications and turn history. Values are Unix milliseconds when recorded.
- Preserve recorded item times through history reconstruction, pagination, and resume, including command start times recorded during approval.
- Separate the Rust item payload into `ThreadItemPayload` while keeping the JSON payload flat, and update consumers, schemas, and generated TypeScript and Python types.

## Testing

Extend coverage for timestamp serialization, history reconstruction, paginated reads and resume, and persisted Python SDK items.

GitOrigin-RevId: ecbcee95af52db7c807b46a6e9deb30e5fba1f80
2026-10-26 18:13:00 +00:00
Dmitriy Kovalenko 2d3de37834 Surface automatic marketplace update failures as startup warnings (#57166)
## Why

Automatic marketplace update failures were only logged, leaving users without a visible startup warning.

## What changed

Send marketplace update errors and task startup failures through app-server warning notifications. Queue background warnings until a client initializes in both standalone and in-process servers, and centralize startup configuration warning delivery.

## Testing

Add a TUI integration test and snapshot covering two marketplace name mismatch failures, the warning count, and navigation between their details with `F2` and the arrow keys.

GitOrigin-RevId: af55a5555affd9d89ac15a546163daeaafd3b3e8
2026-10-26 17:56:43 +00:00
Eric Traut 430a16799d Preserve thread model settings across resume and revert (#55923)
## Why

Resuming a thread can replace saved model settings with global defaults. A selected service tier can also be lost when the current model does not support it.

## What changed

- Restore saved service tier, reasoning summary, and personality on resume unless the caller explicitly overrides them, respecting thread ownership of settings snapshots.
- Retain the selected service tier for persistence, settings notifications, and resume comparisons while filtering the effective tier against the current model's capabilities.
- Carry the runtime model, service tier, and personality into thread revert reconstruction.
- Persist initial thread settings before user input during MCP startup so an interrupted first turn retains its settings checkpoint.

## Testing

Extend regression coverage for cold resume and explicit overrides, unsupported service tiers, settings preservation during revert, and settings checkpoints before user input when MCP startup is interrupted.

GitOrigin-RevId: e5ec012a5678048c5eedd443e524f38c142bf9fd
2026-10-21 15:34:43 +00:00
Eric Traut 7389eebf45 Fix TUI permission selection and session state handling (#55682)
## Why

Queued prompts can resume before a pending permissions menu opens, and local
`/permissions` commands can bypass guards for outstanding permission changes.
Attaching to a different project can also retain model access information from
the launch directory.

## What changed

- Route `/permissions` through app permission guards and hold queued prompts
  while the menu is pending. Settle the hold when opening is rejected, discovery
  is invalidated, or a covering request is resolved.
- Apply the custom permission profile selection check beyond embedded servers.
- Refresh API-key model access when the first attached session changes the
  working directory, including with an embedded server. Propagate the widget's
  model catalog to the app and server even when the model response matches the
  widget's already-updated catalog.
- Skip sessions reported as `thread not loaded` during name lookup for all
  server types instead of falling back to unverified list metadata.

## Testing

Add regression coverage for permission guards, queued prompts across successive
permission menus, invalidated and covered menus, model catalog propagation,
project changes on initial attachment, and name lookup across server pages with
unreadable entries.

GitOrigin-RevId: eb49cb5268ce0ce1e40009b73a0180f23e61ed72
2026-10-20 21:40:30 +00:00
Benjamin Carlsson 63ba55817a Manage TUI backend sessions independently (#55381)
## What changed

Add a `BackendSessions` collection to own app-server sessions and poll their events fairly with cancellation-safe receives. Track listening state per backend so pausing or closing one stream leaves the others available, and resume polling after reconnect. Shut down all registered sessions on exit.

Ignore background backend disconnect, lag, account, and rate-limit events that would otherwise update the displayed conversation's UI.

## Testing

Add coverage for polling three sessions, paused and closed stream isolation, cancellation, and session replacement after reconnect. Extend event ownership coverage to verify that background lifecycle and account events leave the displayed connection and rate-limit state untouched.

GitOrigin-RevId: 9ed82abc77aaf4bfeb86eb72161ad90fe20177ad
2026-10-19 21:03:57 +00:00
Joe Savona 0d337e5fe7 Fix recall of command-line initial prompts in the TUI (#55372)
## Why

Command-line initial prompts bypass the composer and were missing from local recall history, so pressing Up could skip them and retrieve cross-session history instead.

## What changed

Record accepted initial prompts in local recall history with their draft metadata. Add image placeholders where needed so recalled local attachments survive resubmission. Leave synchronously rejected prompts out of recall history.

## Testing

Add regression tests for recall order before cross-session history, ordering after transcript replay, queued prompts appearing once, rejected submissions, and image attachment recall and resubmission. Add a snapshot of the recalled initial prompt.

GitOrigin-RevId: 0a12221ae285f01bff4b5860e546c8f7ee3c71e3
2026-10-19 20:21:30 +00:00
Benjamin Carlsson e4568a3538 Reject TUI events from a different backend (#55304)
## Why

Events from another backend must not mutate the current backend's thread or connection state, even when native thread IDs collide.

## What changed

Attach the session's `BackendId` to incoming app-server events and check it before dispatching to thread caches or UI handlers. Apply the same check when waiting for a thread revert notification.

## Testing

Add regression tests covering ignored foreign notifications, approval requests, lag and disconnect events, accepted events from the current backend, and preservation of backend identity on transport events.

GitOrigin-RevId: 2577ab78e0592726f5f05e3426772899e6cb7c7e
2026-10-19 15:20:42 +00:00
jif d28be12577 Honor per-thread unload delays in app-server (#55270)
## Why

Thread unload timing used the server-wide delay, ignoring per-thread configuration overrides. Clients need to control how long individual idle, unsubscribed threads remain loaded.

## What changed

- Resolve unload timing from each loaded thread's `thread_unload_delay_secs` configuration. Setting it to `0` allows immediate unloading once the thread has no subscribers or activity.
- Allow this runtime retention override in `experimentalPredictionMode` forks while continuing to reject other configuration overrides. Reject negative values and delays too large to schedule.
- Replace the source-specific immediate-unload behavior with explicit configuration.

## Testing

Extend integration coverage for per-thread delays, unloading after active turns finish, fork overrides, and invalid prediction-fork delays. Verify that immediate unloading preserves persistent history and that cold resumes and ordinary nested forks use their default delay instead of retaining the previous runtime's override.

GitOrigin-RevId: fcb82bd73a101260c51e2a8476f94320c23ac929
2026-10-19 12:30:33 +00:00
victor-openai 0e67502123 Preserve MCP tool-list metadata in server status (#54686)
## Why

MCP tool discovery discarded response-level `_meta`, leaving server-status clients unable to inspect metadata associated with the tool catalog.

## What changed

- Preserve opaque `_meta` from the first `tools/list` page through catalog refreshes and caches.
- Expose it as `toolListMeta` in `McpServerStatus`, with `null` when unavailable, and update generated schemas and bindings.
- Scope cached Apps catalog metadata to discovery settings, including client capabilities and extensions, while retaining tools from older caches without metadata.
- Restrict server capabilities in status snapshots to the selected servers.

## Testing

Add regression coverage for listing legacy cached Apps tools during failed startup, and update protocol serialization expectations for nullable `toolListMeta`.

GitOrigin-RevId: 62335e2fd8c482067a7a78a7612b58c3eaa9fb55
2026-10-16 19:26:46 +00:00
Benjamin Carlsson 440312f113 Keep TUI session selections bound to their backend (#54681)
## Why

A thread ID alone does not identify the backend that owns a session. Resume and fork selections must retain that ownership across connection replacement so they cannot operate on another backend, even when thread IDs match.

## What changed

- Add a process-local `BackendId` to app-server sessions and session targets, preserving it across reconnects and auxiliary picker connections.
- Reject selections from a different backend before changing session state or sending resume, fork, or unarchive requests.
- Disable local-daemon fallback to an embedded server for auxiliary pickers and after selecting a session, while retaining fallback for fresh startup.

## Testing

Add regression coverage for foreign selections with matching thread IDs, startup resume and fork rejection before requests, shared ownership across picker connections, and fallback eligibility.

GitOrigin-RevId: 05a3ca0a901a8dc24337d130548aa2c048b4d586
2026-10-16 19:13:40 +00:00
Felipe Coury dcd9e95459 Add /presets for reusable prompts and slash commands (#54380)
## What changed

- Add a TUI picker with ten numbered preset slots, an editor, and confirmation before replacement or deletion.
- Create presets from composer text or current model, reasoning effort, and speed settings. Run saved inputs through the normal queued submission path while preserving the composer draft.
- Persist presets under `tui.presets` in the active user configuration, respecting configuration layer ownership and leaving in-memory settings unchanged when writes fail.
- Preserve permissions when changing effort or speed for the effective model, keep default collaboration settings intact in Plan mode, and allow standard-speed selection with older app servers.

## Testing

Add persistence tests for save, replace, delete, configuration ownership, and write failures; picker and confirmation snapshots; and regression coverage for model permissions and standard-speed fallback.

GitOrigin-RevId: 5f11699112cd404cc8e954872d53495c6edd0c0a
2026-10-15 19:02:09 +00:00
Felipe Coury 0884c8047a Support inline model, reasoning, and speed selection in /model (#54378)
## What changed

Allow `/model [model] --reasoning <level> --speed <tier>` to update the current session without changing saved defaults. Both space-separated and `=` flag values are supported; omitting the model applies settings to the current model. Bare `/model` still opens the picker.

Validate model availability, supported reasoning levels, and speed tiers before applying changes. Reject unknown flags, duplicate settings, and missing values. Apply model, reasoning, and speed together after server acceptance.

Restore each thread's speed when switching threads, and use destination configuration defaults for new sessions instead of carrying over the active session's speed.

## Testing

Extend regression coverage for inline commands, invalid arguments, unchanged saved defaults, settings-only updates to hidden current models, destination speed defaults, and speed restoration on the next turn after switching threads.

GitOrigin-RevId: ad9cecbdb3554e34a6518367364978a37fb68439
2026-10-15 19:01:10 +00:00
Felipe Coury c313eb75fd Prevent duplicate assistant messages around interleaved commands (#54365)
## Why

When a command starts before an assistant message completes, the TUI can flush the streamed text and then render it again when the completion arrives. This duplicates the preamble in both live output and replay.

## What changed

Track the turn ID, item ID, and source text of the flushed message. Skip rendering its completion again when the identity and text match, allowing for the trailing newline added during stream finalization. Continue rendering completions whose identity or text differs.

## Testing

Add an end-to-end test that delays message completion until a command starts and checks that live and replayed transcripts contain the preamble once. Add regression coverage for completions with different text or item IDs.

GitOrigin-RevId: 59af0198a99f26ba8ba2a47644ba662301c30011
2026-10-15 17:59:14 +00:00
Channing Conger dec54912f9 Report terminal completion states and blocking reasons (#54114)
## Why

Terminal status previously collapsed completed and failed turns into `idle` and reported blocking prompts without a reason. Blocking detection also depended on terminal title settings.

## What changed

- Emit `done` and `error` through OSC 7501 for live turn results, retaining them until deliberate keyboard, paste, or mouse input acknowledges them. Mouse movement does not acknowledge results.
- Classify blocking requests with `kind=permission`, `kind=question`, or `kind=auth`, including requests hidden behind another view or delayed while typing, independently of terminal title settings.
- Keep pending follow-up work and active goals `working`. Treat interrupted turns and replayed results as `idle` rather than new results.
- Preserve the existing iTerm2 OSC 21337 labels and indicators, mapping `done` and `error` to `Idle`.

## Testing

Add lifecycle and input-acknowledgment tests covering approvals, completion, failure, interruption, replay, pending follow-ups, and active goals. Extend blocking-request assertions and terminal protocol snapshots for the new states and reasons.

GitOrigin-RevId: 90bad1d54d8b3b9e3cec66fbd3bf3df0c42b995b
2026-10-14 22:58:37 +00:00
Eric Traut 4906ddf5b8 Resume unloaded subagent threads when selected in the TUI (#53895)
## Why

Persisted subagent threads could be reported as unavailable when selected after unloading, even though their saved history could be restored.

## What changed

Attempt to resume selected threads marked closed, including those with a cached live channel, and restore their transcript through the existing attachment flow.

## Testing

Extend coverage for V1 and V2 subagents to verify saved transcript restoration, the appropriate direct-input behavior, and reattachment when a close notification from the previous runtime is still queued.

GitOrigin-RevId: f45d16b954ce5f9f30ca4c6c6f640937390dd78d
2026-10-14 07:07:30 +00:00
Ian MacLeod c22a06c180 Record bucketed terminal sizes in TUI telemetry (#53859)
## What changed

- Add `terminal_columns` and `terminal_rows` buckets to `codex.tui.start`.
- Record `codex.tui.terminal_size` for new turns in the active thread and for resize bucket changes after the terminal size settles for 15 seconds. Reuse launch context and suppress duplicate turn notifications.
- Respect analytics consent for the built-in metrics exporter while allowing explicitly configured OTLP collectors. Apply consent checks to the active exporter across provider replacements.

## Testing

Add coverage for exporter replacement and consent after config reloads. Extend the PTY integration test to verify startup size buckets, turn-start observations, and delayed resize observations with analytics disabled and an explicit OTLP collector.

GitOrigin-RevId: e01a900cd3f04a36731078f725c75374596cb01b
2026-10-14 03:52:30 +00:00
Benjamin Carlsson 6f47fcb0be Route TUI request replies to their originating connection and thread (#53436)
## Why

Pending requests can outlive a session switch, and different threads or connections can reuse request IDs. Replies and resolution notifications must stay associated with the request that prompted them.

## What changed

- Retain the original app-server reply handle for pending and buffered requests, including requests replayed after thread attachment or session reset.
- Scope user input, MCP elicitation, and user verification bookkeeping by thread, and send user input answers to the requesting thread.
- Keep background resolution notifications from dismissing foreground prompts.
- Consume failed replies to closed connections and clear pending replay state without falling through to another request route.

## Testing

Add WebSocket regression tests for direct and buffered approvals with colliding request IDs and replies to closed connections. Extend coverage for foreground prompt preservation and thread-scoped user verification cancellation.

GitOrigin-RevId: 541acb9910e92f6f2e83c27d2a2384185d4f8fec
2026-10-12 17:39:17 +00:00
Eric Traut 27065a91cc Allow restarting managed background servers from the TUI (#53173)
## Why

Startup configuration warnings can reflect settings from when the background server started, even after those settings have been fixed. A restart lets users refresh the server without leaving the TUI.

## What changed

- Add a confirmed restart action to `/daemon`, warning that active or queued work in other chats may be interrupted. Reconnect automatically and let an in-progress restart finish if the TUI exits.
- Verify that Codex manages a local server before enabling restart, including servers discovered with auto-start disabled and those started by `codex agents`.
- Show a restart hint on the first startup configuration warning for managed servers, preserving the original diagnostic for copying and dismissal.
- Repaint the full surface of centered dialogs to restore background colors over cached blank cells.

## Testing

Add CLI integration coverage for agents startup and restarting a discovered daemon with auto-start disabled. Extend TUI tests for confirmation, cancellation, unmanaged servers, warning hints and dismissal, and dialog background rendering.

GitOrigin-RevId: 8e4319fab84f13bf6e7335deb93364fccaff14a7
2026-10-11 17:43:08 +00:00
Felipe Coury cc7ba33601 Add a searchable /config preferences panel to the TUI (#52990)
## What changed

- Group preferences into Appearance, Notifications, Input, Sessions, and Text selection tabs, with search and links to existing theme, status line, terminal title, and keyboard editors.
- Persist preference changes to the selected local configuration file, apply supported changes immediately, and show save errors, configuration overrides, and restart requirements in the panel.
- Add a delayed notification test and controls for notification timing while preserving custom event filters.
- Add `tui.copy_on_select_clear_selection`, disabled by default, to clear transcript selection highlights only after confirmed automatic clipboard delivery.
- Keep local configuration editors usable while disconnected and prevent held activation keys or buffered input from triggering actions across editor and reconnect transitions.

## Testing

Add regression tests and rendering snapshots for panel navigation, search, dependency states, editor handoffs, offline persistence, notification timers, custom filter preservation, and confirmed-copy deselection.

GitOrigin-RevId: a16d218046060fcb6e61705a803fcd9297266e6e
2026-10-10 22:28:01 +00:00
jif 47bcb3a3f9 Reduce stack usage in async TUI tests (#52959)
## Why

Large test futures can occupy their caller's stack frame even when passed directly to `Box::pin`.

## What changed

- Add a non-inlined `box_future` helper and `boxed_future!` macro to construct and box futures outside the caller's poll frame. Use them for heavyweight TUI fixtures and test scenarios.
- Return the shared `App` fixture as `Box<App>` to keep it on the heap.
- Treat `ConnectionAborted` as a closed connection in the realtime speech test server, alongside `BrokenPipe` and `ConnectionReset`.

GitOrigin-RevId: 7bbf2020a5ec12bae8e0365ddf5641deef93b544
2026-10-10 18:50:34 +00:00
Eric Traut 5fe2152b60 Preserve side conversations across thread navigation (#52184)
## Why

Navigating to another thread discarded side conversations, preventing users from returning to them later.

## What changed

- Keep side conversations and their event listeners, channels, and pending requests across thread switches and session resets.
- Reopen a parent's existing side conversation with `/side` and submit any supplied question there. Resolve side toggles by the current thread's parent relationship.
- Limit explicit return-and-discard behavior to the active side conversation, and shut down retained side conversations when exiting.

## Testing

Extend regression tests to cover reopening a side conversation after navigating away, preserving its listener and pending approval during a reset, and restricting discard selection to the active side.

GitOrigin-RevId: ff80566970414ba190ff650d5e413ef63dc65449
2026-10-08 17:31:08 +00:00
Eddie Chen cfc946f4e1 Expose and persist turn lineage across the app server (#51415)
## Why

Clients starting delegated work need to attribute descendant turns to the original turn and retain that attribution through completion and history reads.

## What changed

- Accept optional `parentTurnId` and `rootTurnId` in `turn/start`, and expose `rootTurnId` on returned turns and lifecycle notifications.
- Default new turns to their own root when none is supplied. Preserve the active turn's root when steering, ignoring incoming lineage fields.
- Carry root IDs through completion, interruption, recovery, and fork boundaries, and persist them for paginated history reads. Leave unknown roots null in older history.
- Update protocol schemas and generated TypeScript and Python bindings.

## Testing

Extend coverage for default and explicit roots, steering without changing lineage, interrupted and recovered turns, history reads after restart in both history modes, nested fork pagination, and compatibility with older database writers.

GitOrigin-RevId: f1a570a12bd11742929fd2e8b017e5d35aa07dcb
2026-10-06 16:56:39 +00:00
Eddie Chen 551bd409eb Preserve turn attribution across recovery and compaction (#51402)
## Why

Recovering an interrupted turn replaced its original trigger with `retry` and restored only its root lineage from model context. Turns suspended before that context was written could lose their attribution entirely.

## What changed

- Persist regular-turn attribution in `TurnStartedEvent` and compaction resume metadata, including the trigger, parent and root turn IDs, and initiating agent path.
- Restore matching attribution during recovery, keep unknown triggers absent, and retain the model-context root fallback for older records.
- Reconstruct attribution from surviving turns across rollback and late completion or abort events, and clear inherited attribution when spawning a forked agent.

## Testing

Add coverage for repeated cold recovery, suspension before context persistence, legacy rollouts, fresh attribution on subsequent turns, compaction checkpoints, rollback, and late terminal events.

GitOrigin-RevId: 85b5763e8fbd48a1ea2c07cfda8196e63c2cd33a
2026-10-06 15:50:27 +00:00
Eric Traut e57fc9ea5a Prune TUI snapshots and consolidate behavior tests (#50808)
## What changed

- Remove redundant snapshot fixtures and selected rendering test cases across the TUI.
- Replace several full-output snapshots with direct assertions on messages, popup state, status indicators, and rendered content.
- Consolidate slash-command aliases, command-prefix selection, category titles, and hook events into shared test loops.
- Use shared snapshots for the setup import prompt and paginated transcript overlay across platforms.

GitOrigin-RevId: 461e30ebaf70ba930d91a135d2fc598ee4f04f27
2026-10-04 06:46:50 +00:00