Skip to content

Preserve shared-session viewer prompts that never reach the sharer - #14822

Draft
warp-agent-staging[bot] wants to merge 6 commits into
masterfrom
factory/app-5223-buffer-viewer-prompts
Draft

Preserve shared-session viewer prompts that never reach the sharer#14822
warp-agent-staging[bot] wants to merge 6 commits into
masterfrom
factory/app-5223-buffer-viewer-prompts

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Description

An Agent Mode prompt submitted by a shared-session viewer could disappear. Routing is decided from SharedSessionStatus, which stays ActiveViewer while the viewer websocket is reconnecting, so the prompt was handed to the viewer network — and Network::send_message_to_server returned silently for any stage other than JoinedSuccessfully. The prompt was neither delivered nor retained, and the input could stay frozen with a stale Warping... indicator indefinitely.

This makes the send observable and the prompt recoverable, using the existing queued-prompts panel as the single durable surface rather than introducing a second queue.

1. Viewer prompt acceptance is observable and correlated. send_message_to_server now returns ServerMessageSendOutcome::{LocallyQueued, Undeliverable} and warns on every local rejection with the network stage, the session id, and a content-free UpstreamMessage kind (an exhaustive static label — the message itself is never Debug-rendered, because prompt text and attachments are nested inside it). send_agent_prompt_request takes the caller's AgentPromptRequestId so the server's AgentPromptRequestInFlight echo can be matched to the prompt that was staged. try_send acceptance is explicitly not treated as delivery; the correlated acknowledgement is, with a five-second bound.

2. Undelivered prompts land in the existing queue. A rejected send, or one the sharer does not acknowledge within five seconds, becomes an unlocked QueuedQueryOrigin::DisconnectedViewer row carrying the prompt, its pending attachments, and the session + server conversation token it was addressed to. The input unfreezes immediately, and a turn that this submission itself started is no longer left reported as in progress — a turn that was already running when the prompt was submitted is left alone, so a genuinely active stream keeps its indicator.

3. Dispatch is transactional. Every prompt row carries a stable request id that commit_edit re-mints, and claim_prompt_head hands a row to exactly one dispatch attempt. Racing lifecycle events cannot both submit it, a failed attempt restores it at its original position, and a late or duplicate acknowledgement can only finalize the unchanged row it belongs to — never delete a newer edit.

4. Three explicit drain triggers. A same-session rejoin retries the FIFO head (after RejoinedSuccessfully has flushed buffered input updates); a fatal ambient end converts the head into the cloud follow-up that starts the replacement execution; and the replacement network's JoinedSuccessfully — not ExecutionSessionReady, which fires before it connects — continues the queue. Each validates the row's session and conversation token first and leaves a non-matching head queued rather than redirecting it.

5. Queued cloud follow-ups keep their attachments. The stale "cloud follow-up does not support attachments" comment and its drop are gone; the row's attachments now go through upload_pending_attachments_to_task before dispatch, and the row is restored on upload failure.

Linked Issue

APP-5223. The approved spec is committed on this branch at .agents/specs/APP-5223-buffer-disconnected-viewer-prompts.md and is unchanged by these commits.

Testing

Reproduction test, verified fail-before / pass-after. viewer_prompt_submitted_while_reconnecting_is_preserved_as_an_editable_queue_row drives a prompt through the real input → view → terminal-manager → network path with the viewer network in Stage::Reconnecting. Disabling the fallback-queueing branch makes it fail with exactly the pre-fix symptom (zero queue rows); it passes with the fix. It detects the defect rather than restating the fix.

Also added:

  • viewer/network_tests.rsUndeliverable for every non-joined stage and for a closed proxy channel; LocallyQueued only under the caller's request id; the drop diagnostic reports stage/session/kind and contains none of the sentinel prompt, attachment-name, or attachment-id content; the kind label does not vary with the payload.
  • viewer/terminal_manager_tests.rs — the joined happy path leaves no fallback row, and an unknown or duplicate acknowledgement is a no-op.
  • queued_query_tests.rs — the disconnected-viewer row stays unlocked and deletable; the head is claimable exactly once; a restored claim returns to its original index; retargeting preserves row identity, text, and attachments without duplicating; a locked, command, or being-edited head is not claimable; commit_edit retires the request id.

Repository gates (all from the repo root):

  • ./script/format --check — clean
  • ./script/check_no_inline_test_modules — clean
  • cargo clippy --workspace --exclude warp_completer --all-targets --tests -- -D warnings — clean
  • cargo clippy -p warp --all-targets --tests -- -D warnings — clean
  • cargo clippy -p warp_completer --all-targets --tests -- -D warnings — clean
  • cargo nextest run -p warp --no-fail-fast — 6225/6228 passed. The three failures (ambient_agent_headers_for_task_overrides_existing_cloud_agent_header, test_histignorespace_support_in_zsh, test_decorations_with_multibyte_chars) reproduce identically on master in the same sandbox and are unrelated to this change.
  • cargo test --doc -p warp — passed
  • The base was merged in and the touched-module tests re-run green afterwards.

Still outstanding before this is ready to merge

The spec's validation section lists 20 criteria. The ones below are not yet satisfied, and the PR is deliberately left as a draft because of them. They are listed rather than quietly dropped.

  • Criterion 20 (running-UI verification). Not performed. It needs the real Warp GUI driven against a live shared ambient session — an authenticated Warp account, a real cloud agent VM, and the ability to induce both a transient websocket reconnect and reconnect exhaustion. None of that is reachable from the sandbox this was built in (no GPU, no Warp build, no credentials), so it needs a credentialed run before merge.
  • Criteria 5, 7, 9, 11, 12, 13, 14, 15, 17. The behavior each describes is implemented, but the test matrices are not written yet: the async attachment race, the acknowledgement-timeout boundary cases, the rejoin mismatch matrix, the ineligible-fatal matrix, the replacement-join duplicate-send cases, ordering across all triggers under delayed futures, and the queued-cloud attachment success and failure paths.

Screenshots / Videos

None yet — see criterion 20 above.

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

CHANGELOG-BUG-FIX: Agent prompts sent while a shared session is reconnecting are no longer lost; they are kept in the queued prompts panel, the input is released immediately, and they are retried when the session reconnects or continued as a cloud follow-up.

Originating thread: https://staging.warp.dev/conversation/613eb99f-d7d2-4fd8-9d29-f7e817cdee85

Co-Authored-By: Warp Agent <agent@warp.dev>
@warp-agent-staging
warp-agent-staging Bot requested a review from bnavetta August 7, 2026 14:30
@cla-bot cla-bot Bot added the cla-signed label Aug 7, 2026
@warp-agent-staging warp-agent-staging Bot added the warpy-factory Label associated to the "Wob the Wuilder" factory on staging, also known as Warpy Factory. label Aug 7, 2026
Comment thread .agents/specs/APP-5223-buffer-disconnected-viewer-prompts.md Outdated
Comment thread .agents/specs/APP-5223-buffer-disconnected-viewer-prompts.md Outdated
Co-Authored-By: Warp Agent <agent@warp.dev>
@warp-agent-staging
warp-agent-staging Bot requested a review from bnavetta August 7, 2026 15:44
A viewer prompt was routed to the viewer network from SharedSessionStatus,
which stays ActiveViewer while the websocket reconnects. send_message_to_server
then returned silently for any stage other than JoinedSuccessfully, so the
prompt was neither delivered nor kept, and the input stayed frozen.

Make the send observable and recoverable:

- Network::send_message_to_server returns ServerMessageSendOutcome and warns
  with the stage, session id, and a content-free UpstreamMessage kind on every
  local rejection. send_agent_prompt_request now takes the caller's
  AgentPromptRequestId so the server's AgentPromptRequestInFlight echo can be
  correlated with the prompt that was staged.
- Undeliverable sends, and sends the sharer does not acknowledge within five
  seconds, become an unlocked QueuedQueryOrigin::DisconnectedViewer row holding
  the prompt, its attachments, and the session/conversation it targeted. The
  input unfreezes immediately and a turn the submission itself started is no
  longer left reported as in progress.
- Queue rows carry a stable request id that commit_edit re-mints, and
  claim_prompt_head hands a row to exactly one dispatch attempt, so racing
  lifecycle events and late acknowledgements cannot submit or remove a prompt
  twice.
- A same-session rejoin retries the FIFO head, a fatal ambient end converts it
  to a cloud follow-up, and the replacement execution's join continues the
  queue. Each trigger validates the row's session and conversation token first.
- Queued cloud follow-ups upload their attachments to the task definition
  instead of dropping them.

Co-Authored-By: Warp Agent <agent@warp.dev>
The end-to-end case drives a prompt through the real input -> view ->
terminal-manager -> network path with the viewer network in Stage::Reconnecting
and asserts the prompt survives as one unlocked DisconnectedViewer row pinned to
the session it was addressed to. Disabling the fallback-queueing branch makes it
fail with the pre-fix symptom (no row at all), so it detects the defect rather
than just describing the fix.

Alongside it:

- viewer/network_tests.rs asserts Undeliverable for every non-joined stage and a
  closed proxy channel, LocallyQueued only under the caller's request id, and
  that the drop diagnostic reports stage/session/kind while carrying none of the
  sentinel prompt or attachment content.
- terminal_manager_tests.rs covers the joined happy path and the no-op cases for
  an unknown and a duplicate acknowledgement.
- queued_query_tests.rs covers the unlocked disconnected-viewer row, claiming
  the head exactly once, restoring it in place, retargeting without duplication,
  the locked/command/being-edited head, and commit_edit retiring the request id.

Co-Authored-By: Warp Agent <agent@warp.dev>
Co-Authored-By: Warp Agent <agent@warp.dev>
@warp-agent-staging warp-agent-staging Bot changed the title Spec: Preserve disconnected viewer prompts across reconnect and cloud follow-up Preserve shared-session viewer prompts that never reach the sharer Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed warpy-factory Label associated to the "Wob the Wuilder" factory on staging, also known as Warpy Factory.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants