Skip to content

Send retained message IDs to server when forking at exchange boundary#11064

Closed
harryalbert wants to merge 1 commit into
masterfrom
harry/app-4504-client-fork-message-ids
Closed

Send retained message IDs to server when forking at exchange boundary#11064
harryalbert wants to merge 1 commit into
masterfrom
harry/app-4504-client-fork-message-ids

Conversation

@harryalbert

Copy link
Copy Markdown
Contributor

Description

When forking a conversation at a specific exchange boundary (e.g. "Fork from exact exchange"), the client truncates the conversation locally to only include messages up to that exchange. However, the server's ForkConversation endpoint copies the entire GCS conversation data — including exchanges after the fork point. This mismatch causes the server-side fork to have more exchanges than the local fork, leading to TaskNotFound errors and transcript corruption during cloud-to-cloud handoff session sharing replay.

This PR adds an optional message_ids field to the ForkConversationRequest. When the client forks at an exchange boundary, it computes the flattened set of retained message IDs and sends them to the server so the server can truncate its copy to match.

Changes

  • ForkConversationRequest: Added optional message_ids: Option<Vec<String>> field (serialized with skip_serializing_if)
  • AIClient::fork_conversation: Added message_ids parameter to the trait method and implementation
  • BlocklistAIHistoryModel::compute_retained_message_ids: New helper that computes the flat list of message IDs to retain when forking at a specific exchange — mirrors the truncation logic in fork_conversation_at_exchange
  • fork_ai_conversation (workspace/view.rs): Computes message_ids_for_server from the source conversation before the async server fork call
  • Full-fork callers (handoff, rewind backup) pass None since they copy the entire conversation

Linked Issue

  • The linked issue is labeled ready-to-spec or ready-to-implement.
  • Where appropriate, screenshots or a short video of the implementation are included below (especially for user-visible or UI changes).

Testing

Server-side counterpart required to actually truncate. This PR only sends the IDs; the server needs to read and filter them.

  • I have manually tested my changes locally with ./script/run

Agent Mode

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

Conversation: https://staging.warp.dev/conversation/e0c9487c-9002-412e-a111-d9bcc83c1532
Run: https://oz.staging.warp.dev/runs/019e2dae-62d5-7985-b442-bdb183f9508e
This PR was generated with Oz.

When forking a conversation at a specific exchange, the client truncates
locally to only include messages up to that exchange. However, the server's
ForkConversation endpoint copies the entire GCS conversation data, including
exchanges after the fork point. This causes transcript corruption during
cloud-to-cloud handoff replay.

Fix: Add an optional message_ids field to ForkConversationRequest. When
provided, the server truncates each task's messages to only retain those
with matching IDs. The client computes the flattened set of retained
message IDs from the conversation's exchange structure before calling the
server fork endpoint.

Changes:
- Add message_ids to ForkConversationRequest and AIClient::fork_conversation
- Add BlocklistAIHistoryModel::compute_retained_message_ids() helper
- Thread message_ids through fork_ai_conversation in workspace/view.rs
- Pass None for full forks (handoff, rewind backup)

Co-Authored-By: Oz <oz-agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label May 15, 2026
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.

1 participant