Skip to content

[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
masterfrom
factory/remote-2208-retained-failed-session
Open

[REMOTE-2208] Make retained failed cloud agent sessions attachable, interactive, and kept alive by activity#14561
warp-agent-staging[bot] wants to merge 6 commits into
masterfrom
factory/remote-2208-retained-failed-session

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

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 AmbientAgentTask treats only InProgress runs as having a live execution. So a run that has already gone FAILED/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:

  • AmbientAgentTask now gates live-session state on a new supports_live_session() helper that accepts InProgress plus terminal Failed/Error. Liveness still additionally requires a running sandbox and a joinable session signal, so this only promotes genuinely retained runs. This flows through active_live_session_state(), active_execution_session_id(), and has_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 as LiveRemoteVm for follow-up interaction.
  • entry_for_task now derives the agent-list open capability from the resolved AmbientAgentLiveSessionState instead 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:

  • A failed run whose sandbox has ended stays transcript-only, is not attachable, and remains eligible for a cloud follow-up (can_submit_cloud_followup).
  • Succeeded/Cancelled/Blocked runs never become attachable, even with stale is_sandbox_running metadata.
  • No session-sharing protocol change and no self-hosted-worker change — nothing in the code required one.

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's OpenOrAttachAmbientAgentConversation handler 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 through ActiveAgentViewsModel::register_ambient_session). PaneGroup::attach_execution_session_to_ambient_pane cannot join a session from such a pane — it is backed by a MockTerminalManager with no network — but it swallowed the failed downcast and still returned true, 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 (FinishedViewer status + 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_pane now refuses a conversation transcript viewer outright and propagates a failed viewer-manager downcast as false instead of reporting success.
  • New TerminalView::prepare_for_live_session_reattach clears the finished/read-only state before rejoining: it removes the ended-conversation tombstone, lifts FinishedViewer (which is what makes TerminalModel::is_read_only true, and therefore what hides the input box in is_input_box_visible), and restores InteractionState::Editable.
  • The workspace now falls back to opening a fresh, writable shared-session tab (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_until must be pushed forward on activity, not fixed at failure time.
Added AIClient::extend_run_session_retentionPOST /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 pure session_keepalive_run_id(&AIQueryRouting) helper so it is unit-testable.

Two deliberate choices:

  • The call is fire-and-forget: a prompt submission must never block or fail on the keepalive.
  • It deliberately does not consult the client's cached run state. A run the client still believes is InProgress may 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.rsattach_execution_session_refuses_read_only_transcript_viewer_pane
  • app/src/terminal/view/shared_session/view_impl_tests.rstest_prepare_for_live_session_reattach_restores_interactive_input
  • app/src/terminal/view/shared_session/cloud_conversation_continuation_tests.rssession_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_targets

See 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 texttest_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 after prepare_for_live_session_reattach.

Proof that submitting into a live ambient session fires the keepalivesession_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_routingsession_keepalive_run_id), then runs the production dispatch (Input::extend_retained_session_window) against a recorded MockAIClient, awaits the spawned future, and asserts extend_run_session_retention was called exactly once for that run id. To make the dispatch observable, extend_retained_session_window now takes the AIClient as a parameter (resolved by the caller) and returns its SpawnedFutureHandle.

Proof of the wire contractbuild_run_session_keepalive_url extracts the endpoint path (matching the existing build_run_followup_url convention) and is pinned by build_run_session_keepalive_url_routes_to_run_session_keepalive, with deserialize_run_session_keepalive_response_for_a_retained_run / ..._for_a_run_that_is_not_retained covering 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_reattach neutered to a no-op the typed text stays empty (left: "" / right: "echo hello-from-retained"), and with session_keepalive_run_id returning None for 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-fast6077 / 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 (the nsc workload-token command is denied in this container), test_decorations_with_multibyte_chars, and test_histignorespace_support_in_zsh.
  • Focused run of the REMOTE-2208 tests (screenshot below): 10 passed, 0 failed.
PASS [ 0.018s] ( 1/10) warp server::server_api::ai::tests::deserialize_run_session_keepalive_response_for_a_run_that_is_not_retained
PASS [ 0.019s] ( 2/10) warp terminal::view::shared_session::cloud_conversation_continuation::tests::session_keepalive_skips_non_live_and_read_only_targets
PASS [ 0.019s] ( 3/10) warp terminal::view::shared_session::cloud_conversation_continuation::tests::session_keepalive_targets_ambient_run_for_executor_viewer
PASS [ 0.019s] ( 4/10) warp server::server_api::ai::tests::deserialize_run_session_keepalive_response_for_a_retained_run
PASS [ 0.019s] ( 5/10) warp server::server_api::ai::tests::build_run_session_keepalive_url_routes_to_run_session_keepalive
PASS [ 0.021s] ( 6/10) warp terminal::view::shared_session::cloud_conversation_continuation::tests::session_keepalive_targets_retained_failed_run
PASS [ 0.021s] ( 7/10) warp terminal::view::shared_session::cloud_conversation_continuation::tests::session_keepalive_targets_run_regardless_of_client_side_run_state
PASS [ 0.137s] ( 8/10) warp terminal::view::shared_session::view_impl::tests::test_prepare_for_live_session_reattach_restores_interactive_input
PASS [ 0.173s] ( 9/10) warp terminal::input::tests::session_keepalive_is_dispatched_for_an_attached_ambient_executor_pane
PASS [ 0.154s] (10/10) warp terminal::view::shared_session::view_impl::tests::test_prepare_for_live_session_reattach_accepts_typed_text
     Summary [ 0.185s] 10 tests run: 10 passed, 6077 skipped

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:

  1. Fail a run in an environment with Keep session after failure enabled, then open the run from the agent list — the tab must be a writable Cloud agent run terminal, not a transcript.
  2. Open the run's read-only transcript first, then open the run again from the agent list — it must open a separate, writable session tab rather than re-focusing the transcript. This is the exact regression fixed here.
  3. Send a query in that tab and confirm POST /api/v1/agent/runs/{run_id}/session-keepalive fires and the run's retained_until moves forward.

Linked Issue

REMOTE-2208

Testing

Regression coverage added for both branches of the new behavior (each fails on master and passes here):

  • app/src/ai/ambient_agents/task_tests.rs
    • retained_failed_and_error_tasks_have_attachable_live_sessions
    • ended_failed_task_with_stale_session_metadata_is_inactive
    • succeeded_task_does_not_become_attachable_from_stale_running_metadata
  • app/src/ai/agent_conversations_model_tests.rs
    • test_resolve_open_action_opens_retained_failed_ambient_session_from_link
    • test_resolve_open_action_uses_transcript_for_ended_failed_task
    • test_resolve_copy_link_prefers_session_link_for_retained_failed_task
    • test_resolve_copy_link_uses_conversation_link_for_ended_failed_task
  • app/src/terminal/view/shared_session/cloud_conversation_continuation_tests.rs
    • routing_is_live_remote_vm_for_retained_failed_execution
    • routing_starts_new_cloud_vm_for_ended_failed_execution
    • retained_failed_task_execution_returns_active_execution_error
    • ended_failed_task_uses_ordinary_cloud_continuation

Commands run:

  • ./script/format — clean.
  • ./script/presubmitcargo fmt --check, the inline-test-module check, cargo clippy … -D warnings, clang-format, and wgslfmt all pass.
  • cargo nextest run -p warp --lib --no-fail-fast6052 / 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 (the nsc workload-token command exits non-zero in this container), terminal::input::decorations::tests::test_decorations_with_multibyte_chars, and terminal::input::tests::test_histignorespace_support_in_zsh.
  • Full-workspace presubmit test phase additionally fails in build_cache (needs non-interactive sudo) and the integration suite (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

  • Warp Agent Mode - This PR was created via Warp's AI 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

xterm terminal showing the cargo nextest command and its output: 10 tests passed, 6077 skipped.

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.

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>
@cla-bot cla-bot Bot added the cla-signed label Jul 30, 2026
@warp-agent-staging
warp-agent-staging Bot marked this pull request as ready for review July 30, 2026 22:26
@warp-agent-staging
warp-agent-staging Bot requested a review from dmichelin July 30, 2026 22:26
oz-agent and others added 2 commits July 31, 2026 01:09
…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>
@warp-agent-staging warp-agent-staging Bot changed the title [REMOTE-2208] Make retained failed cloud agent sessions attachable from the client [REMOTE-2208] Make retained failed cloud agent sessions attachable, interactive, and kept alive by activity Jul 31, 2026
oz-agent and others added 3 commits July 31, 2026 08:22
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants