first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
+58
View File
@@ -0,0 +1,58 @@
# Auto-queue prompts during agent-requested long-running commands
Linear: [QUALITY-839](https://linear.app/warpdotdev/issue/QUALITY-839/auto-enable-prompt-queueing-during-lrc)
## Summary
While an agent is in control of a long-running command (LRC) that the agent requested as part of a conversation, submitting a prompt auto-queues it instead of immediately sending it to the agent driving the command when regular queue mode is otherwise off. LRC-auto-queued prompts are delivered to the agent when the command finishes only when doing so preserves existing queue order; prompts queued by regular queue mode keep normal end-of-response semantics. The user can also press Enter on an empty input to fire the next queued prompt earlier. A new cloud-synced dropdown setting, "Default long-running command submission mode", controls whether prompts are queued or sent immediately during eligible LRCs. It only applies — and is only shown — when the default prompt submission mode is Interrupt.
Figma: none provided.
## Problem
Today, a prompt submitted while an agent controls an agent-requested LRC is delivered to that agent immediately, steering it mid-command. Users often type thoughts ahead of time and don't want them injected into the running command the instant they hit Enter; they want them held until they deliberately release them or until the exchange finishes.
## Behavior
### Trigger and scope
1. Auto-queue activates for a conversation exactly when the agent holds control of an active long-running command that the agent requested in that conversation and the settings call for it: the default prompt submission mode is Interrupt and the LRC submission mode is "Queue until command finishes" (see 18). This includes the state where the agent is blocked on user approval to interact with the command.
2. Auto-queue does not activate for user-started LRCs where the user explicitly tagged in the agent, or when the user is in control of the LRC — e.g. before the agent has taken control, or after a manual takeover, stop, or agent-initiated transfer of control back to the user.
3. Auto-queue activation is per-conversation: it affects only the conversation whose agent controls the LRC. Other conversations' queue toggle states are untouched.
4. The behavior is gated on the same feature availability as the existing prompt-queue feature (the queue chip / `/queue` surface). Where the queue feature is unavailable, behavior is unchanged from today.
5. When the default prompt submission mode is Queue, the LRC machinery is entirely inert: prompts queue until the end of the full response per existing queue-mode behavior, the chip toggle behaves persistently, and the LRC setting is hidden (see 19).
### Queuing while the LRC runs
6. While auto-queue is active and regular queue mode is otherwise off, submitting a non-empty prompt appends it to the conversation's queued prompts (the same queue used by the auto-queue chip and `/queue` today) instead of sending it to the agent, and the input clears. If the current queue head is absent or is itself queued until command finish, the queued prompts panel shows the new row with an italic, secondary-colored "(queued until the command finishes)" suffix after its preview text — the same treatment as the model picker's "(selected)" label. If the current queue head is not queued until command finish, the prompt appends as a regular queued row with no command-finish suffix.
7. If regular queue mode is already enabled for the conversation (via the queue chip/keybinding or the default prompt submission mode), submissions during the LRC use regular queue semantics: they append as normal queued rows with no command-finish suffix and drain at the end of the response unless the user sends them manually.
8. Pressing Enter on an empty input sends the top queued row immediately — delivered to the same target an immediate submission would have used (the agent controlling the LRC) — per the existing empty-input-Enter send-now behavior. Each press sends exactly one row.
9. All existing queue interactions (panel rows, edit, delete, reorder, send-now buttons, pause on error/cancel) behave exactly as they do for manually-enabled queue mode.
10. When the command finishes, leading prompts that were auto-queued during it (the suffixed rows at the head of the queue) are sent to the agent immediately, in queue order — including when the user manually took over the command before it finished. Rows queued by other means (`/queue`, an explicit queue-mode toggle, queue default mode) are untouched and drain per the existing end-of-response rules; command-finish delivery never skips over them.
11. Shell-command rows queued while the agent controls the LRC are regular queued commands (no suffix): they cannot be delivered to the agent, do not fire at command end, and keep the existing queued-command drain semantics.
### Status chip and ghost text
12. While auto-queue is active, the prompt-queue chip in the warping indicator renders in its active (accent-colored) state, identical to when the user enables queue mode manually.
13. While auto-queue is active and the input is in AI mode with an empty buffer, the ghost text shows the existing queue hint copy ("Queue a follow up for the running agent", with the classic-input "or backspace to exit" variant), replacing the steer hint shown today during an LRC.
### Reverting and manual override
14. Auto-queue is a derived state, not a sticky toggle: when the LRC ends (command finishes, or control transfers to the user for any reason), the conversation's queue mode reverts to whatever it was before the LRC — the user's per-conversation toggle state, or the default from the queue-vs-interrupt setting. Rows that did not fire per (10) remain queued.
15. If the user manually toggles queue mode off (chip click or its keybinding) while the agent still controls the LRC, the override is respected for the remainder of that LRC: prompts submit immediately to the agent, as today. The override is scoped to that LRC only — it does not change the conversation's persistent toggle state, and the next eligible agent-requested LRC in the conversation auto-enables again.
16. Toggling queue mode back on after such an override re-enables regular queue mode for the conversation; prompts submitted after that toggle use normal queued-row semantics rather than command-finish LRC semantics. Reverting at LRC end still applies per (14).
17. If the conversation was already in queue mode before the LRC (via a per-conversation toggle), entering and exiting the LRC produces no visible change: queue mode stays on throughout and after, and its rows drain at end of response per (10).
### Setting
18. A new setting, "Default long-running command submission mode", controls invariants (1)(17). It is a dropdown with two options — "Send immediately" and "Queue until command finishes" (the default) — cloud-synced, and visible on the AI settings page directly below the "Default prompt submission mode" (queue vs. interrupt) dropdown. Its description reads: "What happens when you submit a prompt while an agent is driving a long-running command. LRC-queued prompts are sent to the agent when the command finishes."
19. The dropdown is only rendered while "Default prompt submission mode" is Interrupt. With Queue selected it is hidden (and ignored), since prompts already queue until the end of the full response.
20. When set to "Send immediately", behavior during eligible agent-requested LRCs is unchanged from today: prompts submit immediately to the agent, and the chip/ghost text reflect only the user's own queue toggle state.
21. The setting is also settable from the Command Palette via "Set long-running command submission: …" entries, shown only while the default prompt submission mode is Interrupt.
22. Changing the setting takes effect immediately, including mid-LRC: switching to "Send immediately" while auto-queue is active reverts the conversation to its non-LRC queue state; switching to "Queue until command finishes" while an agent controls an eligible agent-requested LRC activates auto-queue (subject to any manual override per (15)).
### Edge cases
23. If multiple exchanges occur within one conversation, each eligible agent-requested LRC independently triggers auto-queue on entry and reverts on exit; manual overrides per (15) never outlive the LRC they were made in.
24. Read-only shared-session viewers and other states where prompt sending is unavailable keep their existing restrictions; auto-queue does not create new send affordances there.
25. Auto-queue never queues an empty submission; Enter on an empty input follows (8) when rows are queued, and otherwise keeps its existing behavior.
+86
View File
@@ -0,0 +1,86 @@
# QUALITY-839 — Auto-queue prompts during agent-requested long-running commands
See `specs/QUALITY-839/PRODUCT.md` for behavior. Researched at commit `8e984f0d784f38684472054978db10f39ff7ea5c` (branch `harry/quality-839-auto-enable-prompt-queueing-during-lrc`, stacked on the APP-4717 empty-input-Enter send-now work).
## Context
All read sites for "is queue mode on?" already funnel through one method, so the core of this feature is making that method LRC-aware:
- [`app/src/ai/blocklist/queued_query.rs:366 @ 8e984f0d`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/ai/blocklist/queued_query.rs#L366) — `QueuedQueryModel::is_queue_next_prompt_enabled`: per-conversation override falling back to the cached `AISettings::default_prompt_submission_mode`. `ConversationQueueState` (L152-164) holds the per-conversation override; `toggle_queue_next_prompt` (L377) flips it.
- [`app/src/terminal/input.rs:13778 @ 8e984f0d`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/terminal/input.rs#L13778) — `maybe_queue_input_for_in_progress_conversation`: the submission intercept; consults `is_queue_next_prompt_enabled` and conversation in-progress/blocked status. During an eligible agent-requested LRC the conversation status is `InProgress` (or `Blocked`), so no change is needed to its status gating.
- [`app/src/terminal/input.rs:6141 @ 8e984f0d`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/terminal/input.rs#L6141) — `agent_mode_hint_text`: ghost text switches to the queue hint (`AGENT_MODE_AI_ENABLED_QUEUE_HINT_TEXT_*`, L453-455) when `is_queue_next_prompt_enabled` is true and the conversation is in progress. PRODUCT §13 falls out automatically.
- [`app/src/ai/blocklist/block/status_bar.rs:838 @ 8e984f0d`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/ai/blocklist/block/status_bar.rs#L838) — the queue chip (`queue_next_prompt_button`) renders accent-colored when `is_queue_next_prompt_enabled` is true. PRODUCT §12 falls out automatically.
- [`app/src/terminal/model/block/interaction_mode.rs:102 @ 8e984f0d`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/terminal/model/block/interaction_mode.rs#L102) — `Block::is_agent_in_control` plus `Block::is_agent_requested_command()` form the trigger condition (PRODUCT §1-2). This covers the blocked-on-approval state (`LongRunningCommandControlState::Agent { is_blocked, .. }`) for agent-requested commands, while excluding user-in-control, tagged-in-only, and user-started LRCs where the user explicitly tagged in the agent.
- [`app/src/terminal/view.rs:27035 @ 8e984f0d`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/terminal/view.rs#L27035) — `ToggleQueueNextPrompt` handler (chip click + `Cmd-Shift-J`): resolves the active conversation and calls `QueuedQueryModel::toggle_queue_next_prompt`. `TerminalView` holds `self.model`, so it can check LRC control state when routing the toggle.
- Re-render on LRC transitions is already wired: the status bar notifies on `CLISubagentEvent::UpdatedControl` and `ModelEvent::BlockCompleted` ([`status_bar.rs:231-248, 312-330`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/ai/blocklist/block/status_bar.rs#L231-L248)), and its warping-indicator render already locks the terminal model and reads `is_agent_in_control` (L752-770). The input likewise already locks `self.model` on hot paths (e.g. `is_input_mode_toggle_disabled`, [`input.rs:14409`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/terminal/input.rs#L14409)).
- [`app/src/settings/ai.rs:496-533 @ 8e984f0d`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/settings/ai.rs#L496-L533) — `PromptSubmissionMode` setting; the new enum setting is defined next to it and follows the same `implement_setting_for_enum!` pattern.
- [`app/src/settings_view/ai_page.rs:5771-5790 @ 8e984f0d`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/settings_view/ai_page.rs#L5771-L5790) — `AIInputWidget::render` places the "Default prompt submission mode" dropdown under `FeatureFlag::QueueSlashCommand`; the new dropdown goes directly below it. Palette wiring pattern for the sibling setting: `init_actions_from_parent_view` (L367-388) + context flags in [`settings_view/mod.rs:521-522`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/settings_view/mod.rs#L521-L522) + flag computation in [`workspace/view.rs:22371`](https://github.com/warpdotdev/warp/blob/8e984f0d784f38684472054978db10f39ff7ea5c/app/src/workspace/view.rs#L22371).
No new feature flag (per decision): everything ships under the existing `QueueSlashCommand` gate that already wraps the chip, the queue panel, and the submission intercept.
## Proposed changes
### 1. Setting (`app/src/settings/ai.rs`)
New enum setting in `AISettings`, defined next to `PromptSubmissionMode`:
```
pub enum LongRunningCommandSubmissionMode {
SendImmediately,
#[default]
QueueUntilCommandCompletes,
}
```
registered via `implement_setting_for_enum!` (cloud-synced, `toml_path: "agents.warp_agent.other.long_running_command_submission_mode"`, `feature_flag: FeatureFlag::QueueSlashCommand`) and stored as the `long_running_command_submission_mode` field. `display_name()` returns "Send immediately" / "Queue until command finishes"; `command_palette_description()` the matching "Set long-running command submission: …" strings. The settings macro generates the matching `AISettingsChangedEvent::LongRunningCommandSubmissionMode` variant used below.
### 2. LRC-aware enablement, computed at the call sites (`queued_query.rs`, `input.rs`, `status_bar.rs`)
No LRC state is pushed into `QueuedQueryModel`; each call site determines the LRC context itself from the terminal model it already holds, and the model only answers the enablement question given that context.
- `QueuedQueryModel::is_queue_next_prompt_enabled` gains a `lrc_auto_queue_active: bool` parameter, computed via the shared `is_lrc_auto_queue_active` helper (`queued_query.rs`): true exactly when the queue feature flag is on, `default_prompt_submission_mode == Interrupt`, `long_running_command_submission_mode == QueueUntilCommandCompletes`, and the active block's agent controls an agent-requested command for this conversation (`is_agent_requested_command()`). The Interrupt requirement keeps the whole LRC machinery inert in Queue mode (PRODUCT §5). When true, return the LRC-scoped override if set, else `true` (auto-enabled); when false, existing logic (persistent override → cached default). The persistent override is never consulted or written while the LRC branch is in effect, which yields the revert-on-LRC-end semantics for PRODUCT §14, §17.
- One new field on `ConversationQueueState`: `queue_next_lrc_prompt_override: Option<bool>` — a manual toggle made during an eligible agent-requested LRC. It is explicitly cleared when the command ends: `TerminalView` calls `clear_queue_next_lrc_prompt_override` on `CLISubagentEvent::FinishedSubagent` (PRODUCT §15-16, §23). Also dropped with the conversation's queue state.
- Call sites that compute `lrc_auto_queue_active` (each already holds the terminal model):
- `maybe_queue_input_for_in_progress_conversation` (`input.rs`) — the routing decision stays in the input, as today.
- `agent_mode_hint_text` (`input.rs`) — ghost text (PRODUCT §13).
- `render_warping_indicator_for_latest_exchange` (`status_bar.rs`) — the chip's `is_active` (PRODUCT §12); this render already reads `is_agent_in_control` from the locked terminal model.
- The settings are read directly from `AISettings` at each call site (no cache), so mid-LRC setting flips take effect on the next render/submission (PRODUCT §22). For chip/hint re-render on the setting change, `QueuedQueryModel`'s `AISettingsChangedEvent` subscription also re-emits `DefaultModeChanged` for the `LongRunningCommandSubmissionMode` variant.
### 3. Toggle routing (`app/src/terminal/view.rs`)
In the `ToggleQueueNextPrompt` handler, check `is_lrc_auto_queue_active`: when true, call `QueuedQueryModel::toggle_queue_next_prompt_during_lrc(conversation_id, ctx)`, which writes `queue_next_lrc_prompt_override = Some(!current_effective)`; otherwise the existing `toggle_queue_next_prompt`. Both emit `QueueNextPromptToggled`, which the status bar and input already subscribe to. Re-render on control transitions themselves (agent takes/loses control) is covered by the status bar's existing `UpdatedControl`/`BlockCompleted` notifies; the input additionally subscribes to `CLISubagentEvent` (`SpawnedSubagent`/`UpdatedControl`/`FinishedSubagent`/`ControlHandedBackAfterTransfer`) to refresh the ghost text, since its hint subscriptions did not previously cover control transitions.
### 4. Queued-row origin (`queued_query.rs`, `input.rs`, `server/telemetry/events.rs`)
New `QueuedQueryOrigin::LrcAutoQueue` variant (and matching `TelemetryQueuedQueryOrigin` value). `maybe_queue_input_for_in_progress_conversation` uses it instead of `AutoQueueToggle` when the LRC branch is the effective enabler (`lrc_auto_queue_active` is true and the persistent non-LRC queue toggle/default would be off), the submission is a prompt, and the current queue head is absent or already has `LrcAutoQueue` origin. If regular queue mode is already enabled, or if the current queue head has any other origin, the new prompt keeps `AutoQueueToggle` so command-finish delivery cannot jump it over older queued rows. Command rows always keep `AutoQueueToggle` since they cannot be delivered to the agent (PRODUCT §11). The origin drives both the send-on-command-finish behavior (§5 below) and the panel row suffix (§6 below), and distinguishes the rows in `QueuedPrompt*` telemetry. Exhaustive matches on the enum get the new arm.
### 5. Send queued prompts when the command finishes (`app/src/terminal/view.rs`)
New `TerminalView::send_lrc_queued_prompts(conversation_id, ctx)`: collects the conversation's leading queued rows with `LrcAutoQueue` origin, and for each (in queue order) dispatches it via `Input::submit_queued_prompt_for_active_pane` + `QueuedQueryModel::remove_fired_row` — the same path the panel's send-now button uses. Called from the `CLISubagentEvent::FinishedSubagent` handler, right after `clear_queue_next_lrc_prompt_override` (PRODUCT §10). `FinishedSubagent` fires when the command block completes regardless of who held control at that moment, which gives the fire-after-manual-takeover behavior of §10. Rows with other origins stop the command-finish drain and keep the existing end-of-response drain (`drain_queued_prompts`).
### 6. Queued row suffix (`app/src/terminal/view/queued_prompts_panel.rs`)
In `render_row`, non-command rows with `LrcAutoQueue` origin render an italic `sub_text_color` suffix — `"(queued until the command finishes)"` (`LRC_AUTO_QUEUE_ROW_SUFFIX`) — after the preview text, mirroring the model picker's "(selected)" treatment. The preview is wrapped in `Shrinkable::new(1., …)` so it shrinks to its text (clipping with an ellipsis when long) and the suffix hugs it.
### 7. Settings UI (`app/src/settings_view/ai_page.rs`)
Inside the existing `FeatureFlag::QueueSlashCommand.is_enabled()` block in `AIInputWidget::render`, after the "Default prompt submission mode" dropdown: a second `render_dropdown_item` labeled "Default long-running command submission mode", rendered only when `default_prompt_submission_mode == Interrupt` (PRODUCT §19). The dropdown handle (`lrc_submission_mode_dropdown`) lives on `AISettingsPageView`, is built by `OtherAIWidget::create_lrc_submission_mode_dropdown` (the `create_default_prompt_submission_mode_dropdown` pattern), and re-syncs its selection on `AISettingsChangedEvent::LongRunningCommandSubmissionMode`. A new `AISettingsPageAction::SetLongRunningCommandSubmissionMode(mode)` persists via `set_value` (the `SetPromptSubmissionMode` pattern). LRC terms live in `AIInputWidget::search_terms`.
### 8. Command palette (`settings_view/mod.rs`, `workspace/view.rs`, `ai_page.rs`)
- New context flags `LRC_SUBMISSION_SEND_IMMEDIATELY` / `LRC_SUBMISSION_QUEUE_UNTIL_COMMAND_COMPLETES` in `settings_view/mod.rs` flags, set from `ai_settings.long_running_command_submission_mode` in the workspace context computation (the `PROMPT_SUBMISSION_*` pattern).
- Per-mode `FixedBinding`s registered in `ai_page::init_actions_from_parent_view` next to the `PromptSubmissionMode` bindings, additionally gated on `PROMPT_SUBMISSION_INTERRUPT` so the entries hide when the setting is hidden (PRODUCT §21).
## Testing and validation
- `app/src/ai/blocklist/queued_query_tests.rs` (model-level, maps to PRODUCT invariants):
- `is_queue_next_prompt_enabled` with `lrc_auto_queue_active` → enabled by default; without → existing behavior unchanged (§1, §14, §20).
- `toggle_queue_next_prompt_during_lrc` writes the LRC-scoped override, leaves the persistent override untouched, and re-toggling re-enables (§15, §16); `clear_queue_next_lrc_prompt_override` (command end) restores auto-enable for the next LRC (§23) and the pre-LRC state is what the non-LRC path returns afterward (§14, §17).
- `app/src/terminal/input_tests.rs` (host-level, next to the existing queue host tests): with the active block's agent in control for an agent-requested command and the default settings, a non-empty AI submission queues instead of submitting, with `LrcAutoQueue` origin when regular queue mode is off and the queue is empty or its head is already `LrcAutoQueue` (§6); if regular queue mode is on or the current queue head is not `LrcAutoQueue`, the submission keeps the generic origin and does not fire at command finish (§7, §10); user-tagged LRCs do not auto-queue (§2); ghost text returns the queue hint (§13); "Send immediately" → submission routes as today (§20, §22); Queue default mode → row queues with the generic origin (§5); `send_lrc_queued_prompts` fires leading `LrcAutoQueue` rows in order and leaves other rows queued (§10).
- Chip state (§12) is a pure read of `is_queue_next_prompt_enabled` — covered by the model tests; verify visually in the manual smoke.
- Manual smoke: run a dev-server-style command via the agent, let the agent take control, submit prompts into an empty queue while regular queue mode is off (they queue with the row suffix and fire together at command finish), repeat with regular queue mode on or a non-LRC queued row at the head (the new row has no suffix and does not fire at command finish), toggle the chip off mid-LRC (submission steers immediately), and flip the dropdown in Settings → AI mid-LRC (including hiding it by switching the default mode to Queue).
- `cargo check` + `./script/format`; full presubmit before PR per repo workflow.
## Parallelization
Not beneficial: the change is a single coupled chain (setting → model API → call sites → settings UI) where each step consumes the previous one's types. A single agent implements it on this branch (`harry/quality-839-auto-enable-prompt-queueing-during-lrc`).