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
+50
View File
@@ -0,0 +1,50 @@
# APP-4365: Use Queued Query UI for Oz Cloud Mode Queries
## Summary
When a user submits an initial or follow-up Oz cloud mode query, Warp should immediately show the same queued-query UI used for third-party cloud agents instead of inserting a bespoke optimistic user-query block. Setup-command rich content should continue to appear unchanged, and the real user query from the cloud session transcript should render normally once it arrives.
## Problem
Oz cloud mode currently uses a different pending-query presentation than third-party cloud agents. The bespoke optimistic query block requires special handling to hide the later real user-query element, making cloud mode behavior harder to reason about and causing Oz and third-party agent startup states to feel inconsistent.
## Goals
- Use one queued-query visual pattern for cloud submissions that are waiting for the cloud session to produce the real transcript.
- Keep setup-command rich content behavior unchanged.
- Let the real user query element render normally when it arrives from the cloud session transcript.
- Apply the behavior to both initial Oz cloud runs and Oz cloud follow-up runs.
## Non-goals
- Redesigning the queued-query UI.
- Changing third-party cloud agent queued-query behavior.
- Changing setup-command grouping, ordering, expansion, or collapse behavior.
- Changing the cloud submission API, follow-up API, or agent execution lifecycle.
- Adding new user controls to the queued-query card.
## Figma
Figma: none provided
Use the existing in-app third-party cloud agent queued-query UI as the reference.
## Behavior
1. When a user submits an initial Oz cloud mode query and the submission is accepted by Warp, the terminal immediately shows a queued-query UI item for that submitted prompt.
2. When a user submits an Oz cloud mode follow-up query after a cloud execution has ended and the submission is accepted by Warp, the terminal immediately shows a queued-query UI item for that submitted follow-up prompt.
3. The queued-query item for Oz uses the same visual pattern as third-party cloud agents in cloud mode:
- The submitted prompt is shown as the user-authored query.
- The item communicates that the query is queued or waiting.
- The item does not show dismiss or "send now" controls when those controls are absent from the third-party cloud queued-query pattern.
- The item uses the same user identity/avatar treatment as the third-party cloud queued-query pattern.
4. The queued-query item preserves the displayed prompt text the user expects to see. If the user submitted a cloud query through a mode prefix such as `/plan` or `/orchestrate`, the queued-query item shows the user-facing prompt form consistently with the rest of Warp's query UI.
5. Warp does not insert the bespoke Oz optimistic user-query block for initial Oz cloud mode queries.
6. Warp does not insert the bespoke Oz optimistic user-query block for Oz cloud follow-up queries.
7. Setup-command rich content remains unchanged. Any setup-command intro text, setup-command blocks, setup-command ordering, visibility, collapse state, and transitions continue to behave as they did before this feature.
8. The queued-query item does not replace setup-command rich content. If setup commands are executed while the cloud run is starting, the user sees both the queued-query state and the normal setup-command rich content in the same relative flow where pending query and setup progress are shown today.
9. The queued-query item remains visible while Warp is waiting for the cloud execution or follow-up session to become ready and no real transcript item for that submitted prompt is available yet.
10. The queued-query item remains visible after the shared session attaches if the real shared-session transcript has not yet delivered the submitted user query. Session readiness, setup-command output, progress updates, agent status updates, or generic agent output are not sufficient reasons to remove the queued-query item.
11. When the real shared-session transcript delivers the actual submitted user query, the queued-query item is removed or otherwise replaced so the user sees the query exactly once in the final transcript.
12. The real user query element from the cloud session transcript is not hidden merely because Warp previously showed a queued-query item for the same submitted prompt.
13. If the replayed or attached cloud transcript includes the submitted user query, that real user query renders using the normal transcript user-query presentation for Oz conversations.
14. If cloud session attach or replay delivers the real user query before the queued-query item has visibly rendered, Warp may skip showing the queued-query item, but the user must not see both a queued item and a duplicate real user query for the same submitted prompt at rest.
15. If the cloud submission fails before Warp accepts it, Warp should not leave behind a queued-query item for a query that was not actually queued. The user's prompt should remain available for retry according to the existing failed-submission behavior.
16. If the cloud submission is accepted but the run later fails, is cancelled, requires authentication, or hits another startup error before a real transcript item appears, the queued-query item follows the same lifecycle as the third-party cloud queued-query UI for that state.
17. Authentication, cancellation, capacity, quota, and startup error UI remains unchanged except for the absence of the bespoke Oz optimistic user-query block.
18. Starting a new Oz cloud run from an empty cloud compose state shows at most one queued-query item for the accepted initial prompt.
19. Starting an Oz cloud follow-up from a tombstone or other follow-up entrypoint shows at most one queued-query item for the accepted follow-up prompt.
20. Repeated lifecycle updates while the cloud run is starting do not insert duplicate queued-query items for the same accepted prompt.
21. If a user leaves and re-enters the relevant agent view while the cloud query is still waiting, the queued-query item remains associated with the same conversation context and does not appear in unrelated conversations.
22. Exiting the agent view or changing panes does not convert the queued-query item into a bespoke optimistic query block.
23. Queued-query UI for Oz does not affect the content or visibility of prior terminal output, prior agent responses, existing tombstones, or already-rendered setup-command blocks.
24. When the cloud run becomes live and begins streaming agent output, the transition from queued state to transcript state should feel continuous: the prompt is not lost, duplicated, or visually reordered around the first agent response.
25. The behavior is consistent between initial and follow-up Oz cloud queries. A user should not need to learn one pending-query presentation for the first cloud prompt and another for subsequent cloud prompts.
26. The behavior is consistent between Oz and third-party cloud agents wherever both are waiting for a cloud session to produce the real transcript. Any intentional differences should be limited to agent identity, iconography, or existing agent-specific transcript rendering, not the pending-query pattern.
+93
View File
@@ -0,0 +1,93 @@
# APP-4365: Use Queued Query UI for Oz Cloud Mode Queries — Tech Spec
## Context
`PRODUCT.md` defines the user-visible target: initial and follow-up Oz cloud mode prompts should use the same queued-query UI as third-party cloud agents, setup-command rich content should remain unchanged, and the real transcript query should render normally when it arrives.
Cloud mode panes are deferred shared-session viewers. `create_cloud_mode_view` creates a deferred viewer manager, subscribes it to `AmbientAgentViewModel`, and connects or appends a session on `SessionReady` / `FollowupSessionReady` in `app/src/terminal/view/ambient_agent/mod.rs (42-105)`. `is_cloud_agent_pre_first_exchange` identifies the setup interval after a session is ready but before the first exchange arrives, including the third-party harness-start escape hatch in `app/src/terminal/view/ambient_agent/mod.rs (110-162)`.
`AmbientAgentViewModel` owns cloud run state. It tracks `Status::WaitingForSession { kind: InitialRun | Followup }`, the selected harness, task/session IDs, pending follow-up prompt, and today also tracks Oz optimistic-query state via `has_inserted_cloud_mode_user_query_block` and `optimistically_rendered_user_queries` in `app/src/terminal/view/ambient_agent/model.rs (64-145)`. Initial cloud runs call `spawn_internal`, store the request, transition to `WaitingForSession`, and emit `DispatchedAgent` in `app/src/terminal/view/ambient_agent/model.rs (660-679)`. Follow-up runs call `submit_cloud_followup`, set `pending_followup_prompt`, transition to `WaitingForSession`, and emit `FollowupDispatched` in `app/src/terminal/view/ambient_agent/model.rs (492-529)`.
The queued-query visual pattern already exists as `PendingUserQueryBlock`: it renders the prompt with user avatar, dimmed text, and a `Queued` badge, with optional close/send-now controls in `app/src/ai/blocklist/block/pending_user_query_block.rs (16-175)`. `TerminalView::insert_cloud_mode_queued_user_query_block` inserts that block without buttons and without a queued callback, specifically for cloud mode lifecycle-owned prompts, in `app/src/terminal/view/pending_user_query.rs (76-91)`. It is inserted as rich content with `RichContentMetadata::PendingUserQuery` and `PinToBottom` in `app/src/terminal/view/pending_user_query.rs (28-74)` and `app/src/terminal/view/rich_content.rs (183-244)`.
The current event handling creates divergent UI. On `DispatchedAgent`, third-party harnesses rebuild the display prompt with `display_user_query_with_mode` and call `insert_cloud_mode_queued_user_query_block`, while Oz creates `CloudModeInitialUserQuery`, inserts it as rich content, sets `has_inserted_cloud_mode_user_query_block`, and records the stripped prompt as optimistic UI in `app/src/terminal/view/ambient_agent/view_impl.rs (129-189)`. On `FollowupDispatched`, Oz creates `CloudModeFollowupUserQuery`, inserts it, and records the optimistic prompt in `app/src/terminal/view/ambient_agent/view_impl.rs (200-229)`.
The bespoke optimistic views live in `app/src/terminal/view/ambient_agent/block/query.rs (20-159)`. They share normal AI query rendering and show a `Failed` label when the ambient model has an error. They are re-exported through `app/src/terminal/view/ambient_agent/block.rs (1-8)`.
The duplicate-hiding logic lives in the AI block renderer. `should_hide_ai_block_query_and_header` hides a real AI block query/header for the first cloud exchange or any recorded optimistic follow-up prompt while the viewer is live and not receiving replay in `app/src/ai/blocklist/block/view_impl.rs (99-123)`. `AIBlock::render` asks the ambient model for `has_inserted_cloud_mode_user_query_block` and `has_optimistic_user_query` before deciding whether to render the actual query/header in `app/src/ai/blocklist/block/view_impl.rs (842-907)`. Tests currently assert this hiding behavior in `app/src/ai/blocklist/block/view_impl/cloud_mode_setup_tests.rs (1-37)`.
Setup-command rich content is independent of the optimistic prompt blocks. `maybe_insert_setup_command_blocks` gates on CloudModeSetupV2 and `is_cloud_agent_pre_first_exchange`, then inserts the setup intro and setup-command block before the real terminal block in `app/src/terminal/view/ambient_agent/view_impl.rs (373-453)`. When the first Oz exchange is appended, `handle_ai_history_model_event` turns off `is_executing_oz_environment_startup_commands` before inserting the real AI block in `app/src/terminal/view.rs (5086-5159)`.
Follow-up submission is already routed away from the stale shared-session network. `try_submit_pending_cloud_followup` intercepts `InputEvent::SendAgentPrompt`, validates the task, calls `submit_cloud_followup`, and resets the input in `app/src/terminal/view.rs (19735-19794)`. If not intercepted, `TerminalViewEvent::SendAgentPrompt` still goes to the viewer network in `app/src/terminal/shared_session/viewer/terminal_manager.rs (1323-1398)`. Existing follow-up and tombstone tests cover this route in `app/src/terminal/view_test.rs (552-576)` and `app/src/terminal/view/shared_session/view_impl_test.rs (638-805)`.
## Proposed changes
### Use one cloud queued-query insertion path
Update `TerminalView::handle_ambient_agent_event` so both Oz and third-party cloud runs use `insert_cloud_mode_queued_user_query_block` while waiting for the real cloud transcript.
For `DispatchedAgent`:
- Keep the existing viewer short-circuit and `CloudModeSetupV2` gate.
- Remove the `is_third_party_harness()` branch that sends Oz down the bespoke optimistic path.
- Build the display prompt from `AmbientAgentViewModel::request()` using the existing `display_user_query_with_mode(request.mode, &request.prompt)` helper, matching the current third-party path and satisfying `PRODUCT.md` Behavior 4.
- Insert the queued-query block when the display prompt is non-empty.
- Do not call `set_has_inserted_cloud_mode_user_query_block` or `record_optimistic_user_query`.
For `FollowupDispatched`:
- Keep the conversation-status update to `ConversationStatus::InProgress`.
- Read `pending_followup_prompt()` and call `insert_cloud_mode_queued_user_query_block(prompt, ctx)` instead of creating `CloudModeFollowupUserQuery`.
- Do not record the prompt as optimistic UI.
This intentionally reuses `PendingUserQueryBlock` rather than `QueuedQueryModel`. `QueuedQueryModel` in `app/src/ai/blocklist/queued_query.rs (1-67)` is not currently wired into the terminal rich-content path, while `insert_cloud_mode_queued_user_query_block` is the existing third-party cloud UI surface the product spec names as the reference.
### Remove the queued item only when the real transcript user query arrives
Add cloud-specific removal at the point the real Oz AI exchange with a renderable user query is appended. In `handle_ai_history_model_event`, after turning off `is_executing_oz_environment_startup_commands` for ambient sessions and before inserting the `AIBlock`, call `remove_pending_user_query_block(ctx)` only when:
- `CloudModeSetupV2` is enabled,
- the terminal is an ambient agent session,
- the appended exchange belongs to the visible root task that will produce an AI block,
- the exchange contains an input that will render a user query for the submitted prompt, and
- `pending_user_query_view_id` is set.
The key invariant is that the queued item survives session attach and any intermediate shared-session output until the actual user-query element from the real shared-session response exists. `SessionReady`, `FollowupSessionReady`, progress updates, setup-command blocks, harness-start transitions, and generic output without a renderable user query must not remove the queued item.
The removal should not depend on prompt string matching unless the existing exchange/input model exposes a cheap exact display-query comparison. The cloud queued-query helper owns a single lifecycle-managed pending item, and the first visible real user-query exchange for that ambient conversation is the replacement surface. This avoids retaining a stale queued item while also avoiding the old real-query hiding logic. If the exchange is filtered out by `blocklist_filter::should_show_task_in_blocklist`, or if it does not contain a renderable user query, leave the pending item untouched until a later visible user-query exchange, failure, cancellation, auth event, or other terminal lifecycle event handles it.
Keep the existing error/auth/cancel cleanup at the top of `handle_ambient_agent_event` in `app/src/terminal/view/ambient_agent/view_impl.rs (111-126)`. That cleanup already removes lifecycle-owned cloud queued items and should continue to apply to Oz and third-party runs. Do not remove the queued item on `SessionReady` or `FollowupSessionReady`; those events mean a shared session exists, not that the real user-query transcript has rendered.
### Delete Oz optimistic-query rendering and tracking
Remove `CloudModeInitialUserQuery`, `CloudModeFollowupUserQuery`, and the private `render_user_query` helper from `app/src/terminal/view/ambient_agent/block/query.rs`. If that leaves the file empty, remove `mod query;` and `pub use query::*;` from `app/src/terminal/view/ambient_agent/block.rs`.
Remove `AmbientAgentViewModel` fields and methods that only support optimistic query hiding:
- `has_inserted_cloud_mode_user_query_block`
- `optimistically_rendered_user_queries`
- `has_inserted_cloud_mode_user_query_block()`
- `set_has_inserted_cloud_mode_user_query_block(...)`
- `record_optimistic_user_query(...)`
- `has_optimistic_user_query(...)`
Also remove their initialization and reset logic in `new` and `reset_for_new_cloud_prompt`.
Remove `should_hide_ai_block_query_and_header` from `app/src/ai/blocklist/block/view_impl.rs`, delete the ambient-model lookup in `AIBlock::render`, and let the existing `query_and_index` path render the real query/header normally. This implements `PRODUCT.md` Behavior 11-14 directly: the queued rich content is the transient placeholder, and the AI block is the real transcript representation.
### Preserve setup-command rich content
Do not change `maybe_insert_setup_command_blocks`, `CloudModeSetupTextBlock`, `CloudModeSetupCommandBlock`, `is_cloud_agent_pre_first_exchange`, or the block-list startup-command flags. The queued-query change should be limited to which pending prompt rich content is inserted and when it is removed. This preserves `PRODUCT.md` Behavior 7-8 and avoids changing the setup-command grouping/order behavior already covered by CloudModeSetupV2.
### Update comments and names where they become stale
Update comments on `insert_cloud_mode_queued_user_query_block` from “non-oz Cloud Mode run” to “Cloud Mode run waiting for the real transcript” so it accurately covers Oz and third-party runs. Update comments in `pending_user_query_view_id` only if needed; the field remains shared by normal `/queue` prompts and lifecycle-owned cloud queued prompts, but cloud insertion still uses the callback-free helper.
No new feature flag is needed. Initial-run behavior remains under `CloudModeSetupV2`, and cloud follow-up behavior remains reachable only through the existing `HandoffCloudCloud` follow-up entrypoints.
## End-to-end flow
1. User submits an initial Oz cloud prompt.
2. `AmbientAgentViewModel::spawn_internal` stores the stripped request and emits `DispatchedAgent`.
3. `TerminalView::handle_ambient_agent_event` reconstructs the display prompt and inserts a callback-free `PendingUserQueryBlock`.
4. Setup commands, if any, continue through `maybe_insert_setup_command_blocks`.
5. When the real shared-session response appends an Oz exchange with a renderable user query, `handle_ai_history_model_event` removes the queued-query rich content and inserts the normal `AIBlock`.
6. `AIBlock::render` renders the real query/header because the optimistic-query hiding gate is gone.
7. For follow-up prompts, `try_submit_pending_cloud_followup` calls `submit_cloud_followup`, `FollowupDispatched` inserts the same queued-query block, and the next appended exchange that contains the actual follow-up user query replaces it the same way.
## Testing and validation
Unit tests should cover the product invariants without depending on pixel rendering:
- Add or update a terminal view test that enables `CloudModeSetupV2`, dispatches an Oz initial cloud run, and asserts that `pending_user_query_view_id` is set and the corresponding rich content metadata is `PendingUserQuery`. This covers Behavior 1, 3, 5, 18, 20, and 25.
- Add the same assertion for a non-Oz harness to ensure the existing third-party path is unchanged. This covers Behavior 3 and 26.
- Add a follow-up test around `FollowupDispatched` or `try_submit_pending_cloud_followup` that asserts a queued-query block is inserted instead of an optimistic follow-up view. This covers Behavior 2, 6, 19, 20, and 25.
- Add a history-model append test for an ambient cloud conversation that starts with a queued-query block, appends a visible exchange containing a renderable user query, and asserts the pending block is removed while an AI block remains. This covers Behavior 9-14 and 24.
- Add a negative history/session test that attaches a session, emits progress/setup-command/generic output, or appends an exchange without a renderable user query, and asserts the queued-query block remains visible. This covers Behavior 10.
- Remove or rewrite `app/src/ai/blocklist/block/view_impl/cloud_mode_setup_tests.rs (1-37)`. The old assertions should no longer hold; if a lightweight replacement is useful, assert that normal AI block query/header rendering is not suppressed for cloud exchanges.
- Keep existing setup-command tests or add a regression assertion that `maybe_insert_setup_command_blocks` still inserts setup text and command blocks while a queued-query block exists and does not remove that queued item. This covers Behavior 7-8, 10, and 23.
- Keep existing follow-up route tests in `app/src/terminal/view_test.rs (552-576)` and `app/src/terminal/view/shared_session/view_impl_test.rs (638-805)` passing; extend them only if useful to assert queued-query insertion after `submit_cloud_followup`.
Targeted validation commands:
- `cargo test -p warp cloud_mode_setup_tests`
- `cargo test -p warp pending_cloud_followup`
- `cargo test -p warp append_followup`
- `cargo check -p warp --features handoff_cloud_cloud`
Before opening or updating a PR, follow repository rules for formatting and clippy. Do not use `cargo fmt --all` or file-specific `cargo fmt`.
Manual validation:
- With CloudModeSetupV2 enabled, submit an initial Oz cloud prompt and verify the queued-query item appears immediately, remains visible after session attach and while setup-command rich content runs, and is replaced only when the real transcript user query appears.
- Submit a follow-up from an eligible cloud tombstone or owned follow-up input and verify the same queued-query item appears and is replaced only by the real follow-up transcript query.
- Repeat the same initial run with a third-party harness to confirm its queued-query behavior is unchanged.
- Test failure/auth/cancel during startup and confirm the queued item follows the existing third-party lifecycle and no bespoke `Failed` optimistic-query block remains.
## Risks and mitigations
### Removing the queued item too early
`SessionReady`, setup-command output, progress events, or an exchange without a renderable user query can arrive before the real user-query transcript is rendered. Removing the queued item there would create a visible gap. Mitigate by removing only when a visible exchange contains the actual user query from the shared-session transcript.
### Removing unrelated `/queue` prompts
`pending_user_query_view_id` is shared by normal queued prompts and cloud lifecycle prompts. Mitigate by only using the new `AppendedExchange` removal inside ambient cloud-session handling, and only after a visible cloud exchange with a renderable user query is about to render.
### Prompt display mismatch for `/plan` and `/orchestrate`
The spawn request stores the stripped prompt and separate mode. Mitigate by keeping the existing `display_user_query_with_mode` reconstruction already used by third-party cloud runs.
### Setup-command regressions
Setup-command insertion is adjacent to the query UI in the same event flow. Mitigate by not touching `maybe_insert_setup_command_blocks` or startup-command flags, and by adding a regression check that setup blocks still render alongside the queued item.
### Stale optimistic-query code paths
Leaving optimistic tracking in place could continue hiding real transcript queries. Mitigate by deleting the model fields/methods and the AI block hiding helper in the same change that switches insertion to queued-query UI.
## Parallelization
Parallel implementation is not beneficial for this change. The work is tightly coupled across one event handler, one model cleanup, one AI-block render gate, and a small set of targeted tests; splitting it across agents would increase merge conflicts more than it would reduce wall-clock time.