agent_ui: Refine "reject"/"keep" behavior when regenerating previous prompts (#43347)

Danilo Leal created

Closes https://github.com/zed-industries/zed/issues/42753

Consider the following flow: you submit prompt A. Prompt A generates
some edits. You don't click on either "reject" or "keep"; they stay in a
pending state. You then submit prompt B, but before the agent outputs
any response, you click to edit prompt B, thus submitting a
regeneration.

Before this PR, the above flow would make the edits originated from
prompt A to be auto-rejected. This feels very incorrect and can surprise
users when they see that the edits that were pending got rejected. It
feels more correct to only auto-reject changes if you're regenerating
the prompt that directly generated those edits in the first place. Then,
it also feels more correct to assume that if there was a follow-up
prompt after some edits were made, those edits were passively
"accepted".

So, this is what this PR is doing. Consider the following flow to get a
picture of the behavior change:
- You submit prompt A. 
- Prompt A generates some edits. 
- You don't click on either "reject" or "keep"; they're pending. 
- You then submit prompt B, but before the agents outputs anything, you
click to edit prompt B, submitting a regeneration.
- Now, edits from prompt A will be auto-kept.

Release Notes:

- agent: Improved the "reject"/"keep" behavior when regenerating older
prompts by auto-keeping pending edits that don't originate from the
prompt to-be-regenerated.

Change summary

crates/agent_ui/src/acp/thread_view.rs | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

Detailed changes

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -1273,6 +1273,28 @@ impl AcpThreadView {
         };
 
         cx.spawn_in(window, async move |this, cx| {
+            // Check if there are any edits from prompts before the one being regenerated.
+            //
+            // If there are, we keep/accept them since we're not regenerating the prompt that created them.
+            //
+            // If editing the prompt that generated the edits, they are auto-rejected
+            // through the `rewind` function in the `acp_thread`.
+            let has_earlier_edits = thread.read_with(cx, |thread, _| {
+                thread
+                    .entries()
+                    .iter()
+                    .take(entry_ix)
+                    .any(|entry| entry.diffs().next().is_some())
+            })?;
+
+            if has_earlier_edits {
+                thread.update(cx, |thread, cx| {
+                    thread.action_log().update(cx, |action_log, cx| {
+                        action_log.keep_all_edits(None, cx);
+                    });
+                })?;
+            }
+
             thread
                 .update(cx, |thread, cx| thread.rewind(user_message_id, cx))?
                 .await?;