115 lines
7.5 KiB
Markdown
115 lines
7.5 KiB
Markdown
# /queue Slash Command & Auto-Queue Toggle — Technical Spec
|
|
|
|
## Problem
|
|
|
|
Users need a way to queue a follow-up prompt while the agent is mid-response. This requires a new slash command, an auto-queue toggle in the status bar, validation logic, a workspace action to route the prompt, and updates to the pending user query block to support dismissal.
|
|
|
|
## Relevant Code
|
|
|
|
- `app/src/search/slash_command_menu/static_commands/commands.rs (288-297)` — `QUEUE` static command definition
|
|
- `app/src/terminal/input/slash_commands/mod.rs (739-768)` — `/queue` execution handler
|
|
- `app/src/terminal/input.rs` — `submit_queued_prompt` (re-submits queued prompts through slash/skill/plain-text routing) and `maybe_queue_input_for_in_progress_conversation` (auto-queue toggle bottleneck)
|
|
- `app/src/workspace/action.rs (453-456)` — `QueuePromptForConversation` workspace action variant
|
|
- `app/src/workspace/view.rs (19634-19658)` — workspace-level handler that routes to `TerminalView`
|
|
- `app/src/terminal/view/pending_user_query.rs` — pending query block insertion, removal, and auto-send logic
|
|
- `app/src/ai/blocklist/block/pending_user_query_block.rs` — pending block view with dismiss button
|
|
- `app/src/ai/blocklist/block/status_bar.rs` — auto-queue toggle button in the warping indicator
|
|
- `app/src/ai/blocklist/context_model.rs` — `queue_next_prompt_enabled` state
|
|
- `app/src/terminal/view.rs:18101` — `active_ai_block` helper (skips pending query blocks)
|
|
- `app/src/terminal/view.rs:20044` — `last_ai_block` helper (skips pending query blocks)
|
|
- `app/src/terminal/view/init.rs` — `TOGGLE_QUEUE_NEXT_PROMPT_KEYBINDING` (`Cmd+Shift+J`)
|
|
|
|
## Current State
|
|
|
|
The codebase already has a `PendingUserQueryBlock` and `send_user_query_after_next_conversation_finished` mechanism used by `/compact-and` and `/fork-and-compact`. These commands queue a prompt that fires after summarization completes. The `/queue` command reuses this infrastructure for a simpler case: queue a prompt during any in-progress conversation (no summarization involved).
|
|
|
|
Prior to this change, `PendingUserQueryBlock` had no dismiss functionality, and `active_ai_block`/`last_ai_block` did not account for pending query blocks at the tail of the rich content list.
|
|
|
|
## Proposed Changes
|
|
|
|
### 1. Slash command registration
|
|
|
|
New `QUEUE` static command in `commands.rs` with:
|
|
- Required argument (the prompt text).
|
|
- Availability: `AGENT_VIEW | ACTIVE_CONVERSATION | NO_LRC_CONTROL`.
|
|
- `requires_ai_mode: true`.
|
|
- Gated behind `FeatureFlag::QueueSlashCommand` (dogfood). The command only appears in the slash command menu when the flag is enabled.
|
|
|
|
### 2. Command execution (`slash_commands/mod.rs`)
|
|
|
|
The handler validates two preconditions:
|
|
1. Active conversation exists (else error toast).
|
|
2. Prompt argument is non-empty (else error toast).
|
|
|
|
Then checks conversation status:
|
|
- If `is_in_progress()` or `is_blocked()`: dispatches `WorkspaceAction::QueuePromptForConversation { prompt }`.
|
|
- If idle (conversation finished): sends the prompt immediately via `submit_queued_prompt`, which routes through slash command / skill / plain-text detection.
|
|
|
|
### 3. Workspace action routing (`action.rs`, `view.rs`)
|
|
|
|
New `QueuePromptForConversation { prompt }` variant on `WorkspaceAction`. The workspace handler resolves the active terminal view and calls `terminal.send_user_query_after_next_conversation_finished(prompt, interruptible: true, ctx)`. The `interruptible` parameter controls whether the pending block shows a "Send now" button; `/queue` and auto-queue set this to `true`, while summarization-triggered queuing (`/compact-and`, `/fork-and-compact`) sets it to `false`.
|
|
|
|
### 4. Dismiss and Send-now support on `PendingUserQueryBlock`
|
|
|
|
- Block now owns a `ViewHandle<ActionButton>` close button (X icon, `ButtonSize::XSmall`, `NakedTheme`) and an optional `ViewHandle<ActionButton>` send-now button (Play icon), created only when the block is constructed with `interruptible: true`.
|
|
- New `PendingUserQueryBlockAction::{Dismiss, SendNow}` and `PendingUserQueryBlockEvent::{Dismissed, SendNow}`.
|
|
- `PendingUserQueryBlock` implements `TypedActionView` to translate actions → events.
|
|
- The button column ("Remove queued prompt" + optional "Send now") is positioned top-right via `Stack` + `OffsetPositioning`.
|
|
- The caller in `pending_user_query.rs` subscribes to `Dismissed` (→ `remove_pending_user_query_block`) and `SendNow` (→ `send_queued_prompt_now`, which removes the block and immediately submits the queued prompt through the input) events.
|
|
|
|
### 5. Skip pending blocks in `active_ai_block` / `last_ai_block`
|
|
|
|
Both methods use `Iterator::rfind` to skip all trailing non-AI items (usage footers and pending query blocks) rather than checking only the last entry.
|
|
|
|
### 6. Auto-queue toggle (`context_model.rs`, `status_bar.rs`, `input.rs`)
|
|
|
|
- `queue_next_prompt_enabled: bool` on `BlocklistAIContextModel` tracks the toggle state.
|
|
- `toggle_queue_next_prompt()` flips the flag and emits `QueueNextPromptToggled`.
|
|
- The status bar renders a `ClockPlus` icon button gated behind `FeatureFlag::QueueSlashCommand`. The icon is accent-colored when active.
|
|
- Keybinding `Cmd+Shift+J` maps to `TerminalAction::ToggleQueueNextPrompt`.
|
|
- `maybe_queue_input_for_in_progress_conversation` in `input.rs` is the bottleneck: when the toggle is on and the active conversation is in progress, it captures the input buffer, clears it, and dispatches `QueuePromptForConversation`.
|
|
- The toggle persists across exchanges (same semantics as fast-forward).
|
|
|
|
## End-to-End Flow
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant User
|
|
participant Input as TerminalInput
|
|
participant WS as WorkspaceView
|
|
participant TV as TerminalView
|
|
participant PUQ as PendingUserQueryBlock
|
|
|
|
User->>Input: /queue fix the tests
|
|
Input->>Input: Validate conversation, prompt, status
|
|
Input->>WS: dispatch QueuePromptForConversation
|
|
WS->>TV: send_user_query_after_next_conversation_finished
|
|
TV->>PUQ: insert_pending_user_query_block
|
|
Note over PUQ: Shows queued prompt with dismiss button
|
|
|
|
alt Agent completes successfully
|
|
TV->>TV: on_next_conversation_finished(Complete)
|
|
TV->>Input: submit_queued_prompt("fix the tests")
|
|
Note over Input: Routes through slash/skill/plain-text detection
|
|
TV->>PUQ: remove block
|
|
else Agent errors/cancelled
|
|
TV->>TV: on_next_conversation_finished(Error|Cancelled)
|
|
TV->>TV: restore prompt to input buffer
|
|
TV->>PUQ: remove block
|
|
else User dismisses
|
|
User->>PUQ: click X
|
|
PUQ->>TV: PendingUserQueryBlockEvent::Dismissed
|
|
TV->>TV: remove_pending_user_query_block (cancels callback)
|
|
end
|
|
```
|
|
|
|
## Risks and Mitigations
|
|
|
|
- **Only one queued prompt at a time**: calling `/queue` again while one is pending replaces the previous one (via `remove_pending_user_query_block` in `insert_pending_user_query_block`). This is acceptable for v1.
|
|
- **Pending indicator behind feature flag**: the `PendingUserQueryIndicator` flag gates the visual block (dogfood). Without the flag, the prompt is still queued and auto-sent — just without visual feedback. This is the same behavior as `/compact-and`.
|
|
- **Queue command behind feature flag**: the `QueueSlashCommand` flag gates the `/queue` command registration (dogfood). The command handler, workspace action, and pending block infrastructure are always compiled in but only reachable when the flag is enabled.
|
|
|
|
## Testing and Validation
|
|
|
|
- Manual testing of the five error/success paths described in the product spec.
|
|
- Code review of the `active_ai_block` / `last_ai_block` skip logic to ensure pending blocks don't break conversation state detection. |