Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
# Cloud task consistency tech spec
## Context
Cloud agent runs can currently show inconsistent status across client surfaces. A live/restored conversation and its tombstone may show a terminal state while the cloud-mode details panel and agent management row still show the cached task as `In progress`, including a cancel action that should no longer be visible.
There is no sibling `PRODUCT.md` for this work. The intended user-visible behavior is that every client surface representing the same ambient agent run should show the same status, filter into the same status bucket, and expose actions that match that user-visible status.
`AgentConversationsModel` owns the task cache used by the affected surfaces. `ConversationOrTask::status()` maps raw `AmbientAgentTaskState` into `ConversationStatus` for task rows, and also reads live conversation status for non-task conversation rows in `app/src/ai/agent_conversations_model.rs (171-221)`. The same model intentionally hides local conversation rows when a task represents the same run in `app/src/ai/agent_conversations_model.rs (1109-1217)`, so task-backed rows are the visible representation when both records exist.
The management view renders task row status and status filtering through `ConversationOrTask` in `app/src/ai/agent_management/view.rs (762-961, 1450-1733)`. The details panel is populated from raw task state through `ConversationDetailsData::from_task()` in `app/src/ai/conversation_details_panel.rs (287-340)`, and its action buttons use `AmbientAgentTaskState::is_cancellable()` through `ActionButtonsConfig::for_task()` in `app/src/ai/agent_management/details_action_buttons.rs (45-73)`.
The cloud-mode details panel also reads cached task data from `AgentConversationsModel::get_or_async_fetch_task_data()` in `app/src/terminal/view/ambient_agent/view_impl.rs (594-626)`. That method returns cached task data without forcing a refresh when the task is already present in `app/src/ai/agent_conversations_model.rs (1283-1316)`.
The terminal/tombstone path derives completed state from the live/restored `AIConversation`. `ConversationEndedTombstoneView` reads `conversation_output_status_from_conversation()` in `app/src/terminal/view/shared_session/conversation_ended_tombstone_view.rs (52-127)`. `TaskStatusSyncModel` separately maps `AIConversation.status()` changes to server task states in `app/src/ai/blocklist/task_status_sync_model.rs (77-214)`.
The current model event stream separates task and conversation updates. `AgentConversationsModel::handle_history_event()` emits `ConversationUpdated` for `UpdatedConversationStatus`, while task fetch/RTC paths emit `TasksUpdated` or `NewTasksReceived` in `app/src/ai/agent_conversations_model.rs (1047-1107)`. Since task-backed rows shadow local conversation rows, downstream task surfaces need a model-layer status abstraction that responds to both streams without mutating either raw state source.
## Proposed changes
Keep `AmbientAgentTaskState` and `ConversationStatus` separate. Do not project conversation status into cached `AmbientAgentTask` records, and do not overwrite task states fetched from the server.
Add a dedicated model-layer display status type near `AgentConversationsModel` / `ConversationOrTask`, for example `AgentRunDisplayStatus` or `AmbientConversationDisplayStatus`. It should not live in UI components. It should represent the user-visible status for a row/details panel/action configuration and should distinguish raw task state from raw conversation state in its variant names.
The display status type should provide the behavior that status-dependent callers need:
- display text
- icon and color
- status filter bucket
- whether cancel should be shown
- whether continue locally should be available
- whether the run should be considered working or terminal for UI purposes
The derived status algorithm should be exhaustively matched and easy to audit:
- `Queued`, `Pending`, and `Claimed` task states use task-derived setup/pre-session status.
- `InProgress` task state uses the matching live/restored `AIConversation.status()` when one is available, and falls back to task-derived in-progress status when no matching conversation is available.
- Terminal task states use task-derived terminal status, even if a matching conversation exists.
- `Unknown` should be treated as failure-like, matching existing `AmbientAgentTaskState` behavior.
Use existing matching logic to associate a task with a conversation. `conversation_id_shadowed_by_task()` already matches by orchestration task/run id and then by server conversation token in `app/src/ai/agent_conversations_model.rs (1077-1096)`. The new display-status helper should reuse that relationship rather than introducing a separate matching path.
Expose the computed status through `ConversationOrTask` or a nearby helper that has access to `AppContext`, because computing status may require reading `BlocklistAIHistoryModel`. Avoid forcing each caller to independently choose between raw task state and raw conversation status.
Route status-dependent behavior through the computed status:
- Management row icon/text should render the computed status.
- Status filtering should use the computed status filter bucket so a row that displays Done does not remain under Working.
- Details panel status badges should render the computed status rather than raw `task_state` when the panel is showing an ambient task.
- Cancel button visibility should follow computed cancellability, not raw `AmbientAgentTaskState::is_cancellable()`. If raw task state is `InProgress` but the matching conversation is terminal, cancel should be hidden.
- Continue-local availability should follow computed working/terminal status so terminal conversations can be continued even if the raw server task is stale.
Update status rendering to consume the new display status type. `app/src/ai/conversation_status_ui.rs` currently renders `ConversationStatus`, while `AmbientAgentTaskState` has separate icon/text helpers in `app/src/ai/ambient_agents/task.rs (296-402)`. Centralizing display behavior on the new type avoids forcing setup states into `ConversationStatus`.
Add robust eventing for derived-status changes. At minimum, all consumers that cache row/action/details data must refresh when either `TasksUpdated`/`NewTasksReceived` or `ConversationUpdated` fires. If this remains ambiguous after implementation, add a clearer `AgentConversationsModelEvent` variant such as `AmbientConversationStatusUpdated { task_id }` and emit it when `UpdatedConversationStatus` changes the computed status for a shadowing task even though the raw task record is unchanged.
Preserve server task fetch and RTC behavior as authoritative for task data and metadata. Incoming server task records continue to update cached task state normally; the computed status layer decides what to show without mutating task state or conversation state.
## End-to-end flow
When a cloud task is in a setup phase, the task row, details panel, and actions render from the task state.
When the task reaches `InProgress`, the associated conversation becomes the source for user-visible progress if it is available. A terminal `ConversationStatus` then causes rows, filters, details, and actions to update even if the cached server task still says `InProgress`.
When the server task later reaches a terminal state, that raw task state becomes the source for user-visible status. This preserves the server task lifecycle while avoiding stale in-progress UI during the live-session window.
## Testing and validation
Add unit tests in `app/src/ai/agent_conversations_model_tests.rs` for the display-status algorithm:
- setup/pre-session task states render task-derived statuses
- `InProgress` task with no matching conversation renders task-derived in-progress status
- `InProgress` task with matching live conversation success, cancelled, blocked, and error renders conversation-derived statuses
- terminal task states render task-derived terminal statuses even when a matching conversation has a conflicting status
- `Unknown` maps to a failure-like display status
Add tests proving the same display status drives status filter buckets. In particular, a task whose raw state is `InProgress` but whose matching conversation is `Success` should appear in the Done bucket and not the Working bucket.
Add tests proving action helpers use the computed status. A stale raw `InProgress` task with a terminal conversation should not be cancellable and should be eligible for continue-local behavior when a conversation token is available.
Add eventing tests around `UpdatedConversationStatus`. When a task row shadows the conversation whose status changes, the model should emit an event that causes task-backed consumers to refresh even if the cached task record did not change.
Run focused tests for `agent_conversations_model_tests` and a targeted compile/check command for the app crate. For a final PR, follow the repository PR workflow for formatting and linting, without running file-specific `cargo fmt`.
## Risks and mitigations
The largest risk is introducing another partial status path. Mitigate by making the display status type the only API used by row rendering, filtering, details status, cancel visibility, and continue-local availability.
Another risk is stale UI due to derived status changing without a task update. Mitigate by either wiring all consumers to refresh on both conversation and task events or by adding a specific derived-status event.
There is also a naming risk if the new type looks like raw server state. Use display/status naming that makes it clear the type is user-visible and derived.
## Parallelization
Implementation can split cleanly after approval. One agent can implement the display-status type, precedence algorithm, and unit tests in `AgentConversationsModel`. Another can audit and update UI consumers so row display, filters, details, cancel actions, and continue-local behavior all use the computed status abstraction. Validation should run after both parts are integrated.