[REMOTE-2208] Make retained failed cloud agent sessions attachable, interactive, and kept alive by activity - #14561
Open
warp-agent-staging[bot] wants to merge 6 commits into
Open
Conversation
A cloud agent environment retained after a failure (FAILED/ERROR with a still-running sandbox and a joinable shared session) was unusable from the desktop client: `AmbientAgentTask` only treated `InProgress` runs as having a live execution, so the retained run fell back to a read-only transcript and follow-ups were never routed to the live remote VM. Gate live-session state on a new `supports_live_session()` helper that also accepts terminal FAILED/ERROR runs, so a retained run with a running sandbox is attachable, opens the live session from the agent list, copies the live session link, and routes follow-up interaction to the remote VM. Runs whose sandbox has ended stay transcript-only and cloud-follow-up eligible, and success/cancel states are unaffected. Also derive the agent-list open capability from the resolved live-session state so a retained run whose session id is only available via its session link is correctly openable instead of falling through to the transcript. Co-Authored-By: Warp <agent@warp.dev> Co-Authored-By: Oz <oz-agent@warp.dev>
…t alive on activity
Two gaps remained after making a retained failed cloud agent run attachable.
**The retained session tab was not interactive.**
`OpenOrAttachAmbientAgentConversation` reuses any pane registered for the run,
which includes a read-only conversation transcript viewer opened earlier for the
same run. `attach_execution_session_to_ambient_pane` cannot join a session from
such a pane (it is backed by a mock terminal manager with no network), but it
swallowed that failure and reported success, so the user was focused onto a
transcript with no input box - "the session opens but the terminal is not
usable". A pane that had already been switched to the ended-run view
(`FinishedViewer` status, ended-conversation tombstone, non-editable input) was
also reused as-is, leaving it read-only after the rejoin.
Refuse the attach for a transcript viewer, propagate a failed viewer-manager
downcast instead of reporting success, and clear the finished/read-only state
before rejoining. The workspace now falls back to opening a fresh, writable
shared-session tab whenever reuse is not possible.
**The retention window was fixed at failure time.**
Retention is a sliding idle window server-side: every query the user sends into
a retained session must push `retained_until` forward by the environment's
configured duration. Add `AIClient::extend_run_session_retention`
(`POST /api/v1/agent/runs/{run_id}/session-keepalive`) and fire it from the
submission router whenever a prompt is forwarded to a live remote VM for an
ambient run. The call is fire-and-forget and deliberately does not consult the
client's (possibly stale) run state - a run the client still believes is in
progress may already have failed into retention - which is safe because the
endpoint is a documented no-op for a run that is not retained.
Co-Authored-By: Warp <agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
…-retained-failed-session
…ch and keepalive Review round 2 asked for in-process, verifiable proof of the two rework behaviors, since a genuinely retained cloud session cannot be joined until warp-server#12943 is deployed. - `test_prepare_for_live_session_reattach_accepts_typed_text` asserts the reported symptom directly: text typed into an ended-run pane is dropped, and the same text lands in the input buffer after the pane is prepared for the retained-session rejoin. - `session_keepalive_is_dispatched_for_an_attached_ambient_executor_pane` drives the production routing decision for an attached ambient executor pane and then the production keepalive dispatch against a recorded AI client, so the recorded call proves the run's retention window is refreshed on submit. `Input::extend_retained_session_window` now takes the client and returns its spawn handle so the dispatch can be awaited and observed. - `build_run_session_keepalive_url` extracts the endpoint path (matching the existing `build_run_followup_url` convention) and is pinned by a test, with response-contract tests for both the retained and the no-op response shapes. Co-Authored-By: Oz <oz-agent@warp.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Makes a retained failed cloud agent environment actually usable from the Warp desktop client, the client half of REMOTE-2208 (server half: warpdotdev/warp-server#12943).
Today
AmbientAgentTasktreats onlyInProgressruns as having a live execution. So a run that has already goneFAILED/ERROR— but whose sandbox is deliberately retained for debugging and still has a joinable shared session — was surfaced as a dead run: it fell back to a read-only transcript, its live session link was not offered, and a follow-up was never routed to the still-running remote VM.What changed:
AmbientAgentTasknow gates live-session state on a newsupports_live_session()helper that acceptsInProgressplus terminalFailed/Error. Liveness still additionally requires a running sandbox and a joinable session signal, so this only promotes genuinely retained runs. This flows throughactive_live_session_state(),active_execution_session_id(), andhas_active_execution(), which in turn makes a retained failure attachable, openable as a live session from the agent list/details, copyable as a live session link, and routed asLiveRemoteVmfor follow-up interaction.entry_for_tasknow derives the agent-list open capability from the resolvedAmbientAgentLiveSessionStateinstead of the raw session-id string, so a retained run whose session id is only recoverable from its session link is correctly openable rather than falling through to a transcript.Deliberately unchanged:
can_submit_cloud_followup).Succeeded/Cancelled/Blockedruns never become attachable, even with staleis_sandbox_runningmetadata.Rework changes (review round 1)
Two blocking gaps were reported by the requester on the first round. Both are addressed here; the retention spec on warpdotdev/warp-server#12943 was updated by the server-side change to make the sliding-window semantics explicit.
1. The retained session tab was not interactive (no text entry, no usable terminal).
Root cause:
WorkspaceView'sOpenOrAttachAmbientAgentConversationhandler reuses any pane registered for the run, which includes a read-only conversation transcript viewer opened earlier for the same run (transcript panes register themselves throughActiveAgentViewsModel::register_ambient_session).PaneGroup::attach_execution_session_to_ambient_panecannot join a session from such a pane — it is backed by aMockTerminalManagerwith no network — but it swallowed the failed downcast and still returnedtrue, so the workspace focused a transcript with no input box instead of opening a joinable tab. A pane that had already been switched to the ended-run view (FinishedViewerstatus + ended-conversation tombstone + non-editable input) was likewise reused as-is and stayed read-only after the rejoin.Fix, in three parts:
attach_execution_session_to_ambient_panenow refuses a conversation transcript viewer outright and propagates a failed viewer-manager downcast asfalseinstead of reporting success.TerminalView::prepare_for_live_session_reattachclears the finished/read-only state before rejoining: it removes the ended-conversation tombstone, liftsFinishedViewer(which is what makesTerminalModel::is_read_onlytrue, and therefore what hides the input box inis_input_box_visible), and restoresInteractionState::Editable.add_tab_for_joining_shared_session) whenever reuse is not possible, and only focuses the existing pane when the attach actually succeeded.2. Activity-based keepalive —
retained_untilmust be pushed forward on activity, not fixed at failure time.Added
AIClient::extend_run_session_retention→POST /api/v1/agent/runs/{run_id}/session-keepalive(the endpoint shipped by the server half on warpdotdev/warp-server#12943), returning{retained, extended, retained_until}. It is fired from the submission router (Input::maybe_route_ai_query_to_remote_target) whenever a prompt is forwarded to a live remote VM for an ambient run — i.e. the same condition under which the viewer prompt path runs. The decision is factored into a puresession_keepalive_run_id(&AIQueryRouting)helper so it is unit-testable.Two deliberate choices:
InProgressmay already have failed into retention server-side, and the endpoint is a documented no-op (retained: false) for a non-retained run, so calling unconditionally on the live-VM path is both correct and simpler than mirroring server state. Read-only viewers, cloud-to-cloud follow-ups (which start a brand new VM), and shared local sessions are excluded, because none of them is activity on a retained ambient session.Rework testing
New regression tests, each verified failing before the fix and passing after:
app/src/pane_group/mod_tests.rs—attach_execution_session_refuses_read_only_transcript_viewer_paneapp/src/terminal/view/shared_session/view_impl_tests.rs—test_prepare_for_live_session_reattach_restores_interactive_inputapp/src/terminal/view/shared_session/cloud_conversation_continuation_tests.rs—session_keepalive_targets_ambient_run_for_executor_viewer,session_keepalive_targets_retained_failed_run,session_keepalive_targets_run_regardless_of_client_side_run_state,session_keepalive_skips_non_live_and_read_only_targetsSee Rework changes (review round 2) below for the current validation run.
Rework changes (review round 2)
Round 2 raised one blocking finding: the visual proof in this PR showed the pre-rework broken fixture ("Shared session not found"), and end-to-end proof of text entry and of the keepalive was still outstanding. A genuinely retained cloud session cannot be joined from here until warp-server#12943 is deployed, so this round adds deterministic, in-process proof of both behaviors instead, and drops the misleading fixture media.
Proof that the reattached pane accepts text —
test_prepare_for_live_session_reattach_accepts_typed_text(app/src/terminal/view/shared_session/view_impl_tests.rs) asserts the reported symptom directly rather than the flags behind it: text typed into the ended-run pane is dropped (buffer stays empty — exactly what the reporter saw), and the same text lands in the input buffer afterprepare_for_live_session_reattach.Proof that submitting into a live ambient session fires the keepalive —
session_keepalive_is_dispatched_for_an_attached_ambient_executor_pane(app/src/terminal/input_tests.rs) builds an attached ambient executor pane, runs the production routing decision over it (resolve_ai_query_routing→session_keepalive_run_id), then runs the production dispatch (Input::extend_retained_session_window) against a recordedMockAIClient, awaits the spawned future, and assertsextend_run_session_retentionwas called exactly once for that run id. To make the dispatch observable,extend_retained_session_windownow takes theAIClientas a parameter (resolved by the caller) and returns itsSpawnedFutureHandle.Proof of the wire contract —
build_run_session_keepalive_urlextracts the endpoint path (matching the existingbuild_run_followup_urlconvention) and is pinned bybuild_run_session_keepalive_url_routes_to_run_session_keepalive, withdeserialize_run_session_keepalive_response_for_a_retained_run/..._for_a_run_that_is_not_retainedcovering both response shapes (the no-op form is what makes the unconditional call safe).Both behavior tests were verified to fail before the fix: with
prepare_for_live_session_reattachneutered to a no-op the typed text stays empty (left: "" / right: "echo hello-from-retained"), and withsession_keepalive_run_idreturningNonefor an executor the dispatch test fails (left: None / right: Some(AmbientAgentTaskId(...))).Round 2 validation
./script/format --check— clean../script/check_no_inline_test_modules— clean.cargo clippy -p warp --all-targets --tests -- -D warnings— clean.cargo nextest run -p warp --lib --no-fail-fast— 6077 / 6080 pass. The only failures are the same 3 pre-existing sandbox-environment ones (unchanged by this diff):ambient_agent_headers_for_task_overrides_existing_cloud_agent_header(thenscworkload-token command is denied in this container),test_decorations_with_multibyte_chars, andtest_histignorespace_support_in_zsh.Still needs a deployed server to confirm live
Post-failure retention is not deployed anywhere reachable from this environment (warp-server#12943 is unmerged), so no genuinely retained cloud session exists to join and a live end-to-end capture remains impossible here. The round-1 fixture screenshots and video, which only ever showed the "Shared session not found / Starting …" state, have been removed because they documented the broken pre-rework state rather than the fix.
Once warp-server#12943 is deployed, a reviewer should confirm:
POST /api/v1/agent/runs/{run_id}/session-keepalivefires and the run'sretained_untilmoves forward.Linked Issue
REMOTE-2208
Testing
Regression coverage added for both branches of the new behavior (each fails on
masterand passes here):app/src/ai/ambient_agents/task_tests.rsretained_failed_and_error_tasks_have_attachable_live_sessionsended_failed_task_with_stale_session_metadata_is_inactivesucceeded_task_does_not_become_attachable_from_stale_running_metadataapp/src/ai/agent_conversations_model_tests.rstest_resolve_open_action_opens_retained_failed_ambient_session_from_linktest_resolve_open_action_uses_transcript_for_ended_failed_tasktest_resolve_copy_link_prefers_session_link_for_retained_failed_tasktest_resolve_copy_link_uses_conversation_link_for_ended_failed_taskapp/src/terminal/view/shared_session/cloud_conversation_continuation_tests.rsrouting_is_live_remote_vm_for_retained_failed_executionrouting_starts_new_cloud_vm_for_ended_failed_executionretained_failed_task_execution_returns_active_execution_errorended_failed_task_uses_ordinary_cloud_continuationCommands run:
./script/format— clean../script/presubmit—cargo fmt --check, the inline-test-module check,cargo clippy … -D warnings,clang-format, andwgslfmtall pass.cargo nextest run -p warp --lib --no-fail-fast— 6052 / 6055 pass. The 3 failures are pre-existing sandbox-environment failures unrelated to this diff and untouched by it:server::server_api::ai::tests::ambient_agent_headers_for_task_overrides_existing_cloud_agent_header(thenscworkload-token command exits non-zero in this container),terminal::input::decorations::tests::test_decorations_with_multibyte_chars, andterminal::input::tests::test_histignorespace_support_in_zsh.build_cache(needs non-interactivesudo) and theintegrationsuite (needs SSH password prompts, a real display, and free ports) — all environmental in this container, none touching this code.Screenshots / Videos
The screenshot below is the focused run of the REMOTE-2208 regression tests above, captured with computer use in a terminal on this machine: 10 passed, 0 failed. It is the verifiable proof of the reattach text-entry behavior and the keepalive dispatch that round 2 asked for.
The round-1 media (a synthetic fixture pointing at a non-existent session id, stuck on "Shared session not found / Starting …") has been removed: it documented the broken pre-rework state, not the fix.
Agent Mode
CHANGELOG-IMPROVEMENT: Failed cloud agent runs whose environment is retained for debugging can now be opened and interacted with live from the agent list, instead of only showing a read-only transcript.
Computer-use screenshots
Originating thread: https://linear.app/warpdotdev/issue/REMOTE-2208/configurable-extended-session-lifetime-for-failed-cloud-agents
Conversation: https://staging.warp.dev/conversation/8b9817fe-8f49-4f16-9931-6377351cfe7b
Run: https://oz.staging.warp.dev/runs/019fb729-6a05-7f1f-a60b-986b8907764e
This PR was generated with Oz.