From dde129b9c7f87fd114615114e7650f9dae2e5e12 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Wed, 26 Aug 2026 11:31:53 -0500 Subject: [PATCH] Shorten provider stalls and fix selection copy --- AGENTS.md | 2 +- app/src/ai/blocklist/block.rs | 16 +++++++--- .../ai/runtime/provider_run_coordinator.rs | 2 +- app/src/terminal/view.rs | 30 ++++++++++--------- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 94ec2889..0415ad26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -134,7 +134,7 @@ context_size = 128000 Key invariants: - Every direct-provider `AgentRuntime::start_turn` performs exactly one model call; only `ProviderRun` may schedule another turn or retry -- Direct-provider model calls allow 120 seconds for stream startup and 300 seconds between stream events; either timeout is a recoverable transport failure that enters the existing bounded retry lifecycle with the same work identity +- Direct-provider model calls allow 120 seconds for stream startup and 90 seconds between stream events; either timeout is a recoverable transport failure that enters the existing bounded retry lifecycle with the same work identity - Direct-provider remote telemetry records requested, started, retry-scheduled, and finished model-turn phases with explicit `llm_finished` state; root `provider_run_finished` records distinguish clean completion from failure or cancellation and mark the response stream terminal - `use_rig` and provider selection may choose request/transport details but must never choose lifecycle ownership - Direct-provider output may be projected through `ResponseStream`, but provider progress must not depend on response-stream result draining or `AfterStreamFinished` diff --git a/app/src/ai/blocklist/block.rs b/app/src/ai/blocklist/block.rs index 66bed3c2..250d240d 100644 --- a/app/src/ai/blocklist/block.rs +++ b/app/src/ai/blocklist/block.rs @@ -5287,9 +5287,18 @@ impl AIBlock { /// There **shouldn't** be more than one instance of selected text at any given time across /// any view within the same `AIBlock` view sub-hierarchy. pub fn selected_text(&self, ctx: &AppContext) -> Option { - self.code_editor_views - .iter() - .find_map(|editor_view| editor_view.view.as_ref(ctx).selected_text(ctx)) + // A completed block-level drag is the newest selection whenever it is present. Prefer it + // over stale nested selections that could not be cleared while their SelectableArea was + // still mid-drag; otherwise copy can return an older single-word child selection. + self.selected_text + .read() + .clone() + .filter(|selection| !selection.is_empty()) + .or_else(|| { + self.code_editor_views + .iter() + .find_map(|editor_view| editor_view.view.as_ref(ctx).selected_text(ctx)) + }) .or_else(|| { self.requested_commands .values() @@ -5315,7 +5324,6 @@ impl AIBlock { .values() .find_map(|comment| comment.rich_text_editor.as_ref(ctx).selected_text(ctx)) }) - .or_else(|| self.selected_text.read().clone()) .filter(|selection| !selection.is_empty()) } diff --git a/app/src/ai/runtime/provider_run_coordinator.rs b/app/src/ai/runtime/provider_run_coordinator.rs index 1c61fd7c..fa9479bd 100644 --- a/app/src/ai/runtime/provider_run_coordinator.rs +++ b/app/src/ai/runtime/provider_run_coordinator.rs @@ -24,7 +24,7 @@ use crate::ai::model_output_logging; pub(crate) const BASE_PROVIDER_PROFILE: &str = "base"; pub(crate) const CLI_MONITOR_PROVIDER_PROFILE: &str = "cli-monitor"; const PROVIDER_MODEL_START_TIMEOUT: Duration = Duration::from_secs(120); -const PROVIDER_MODEL_EVENT_IDLE_TIMEOUT: Duration = Duration::from_secs(300); +const PROVIDER_MODEL_EVENT_IDLE_TIMEOUT: Duration = Duration::from_secs(90); const PROVIDER_MODEL_RETRY_DELAYS: [Duration; 3] = [ Duration::from_secs(1), Duration::from_secs(3), diff --git a/app/src/terminal/view.rs b/app/src/terminal/view.rs index b10c50a4..6e4646d3 100644 --- a/app/src/terminal/view.rs +++ b/app/src/terminal/view.rs @@ -16601,7 +16601,22 @@ impl TerminalView { } fn copy(&mut self, ctx: &mut ViewContext) { - // First check if there's selected text in the CLI subagent views + // Prefer the terminal model's active selection. Nested CLI views can retain stale text + // after focus moves, which otherwise makes copy return an older single-word selection. + let semantic_selection = SemanticSelection::as_ref(ctx); + if let Some(selected) = self.model.lock().selection_to_string( + semantic_selection, + self.is_inverted_blocklist(ctx), + ctx, + ) { + if !selected.is_empty() { + ctx.clipboard() + .write(ClipboardContent::plain_text(selected)); + } + return; + } + + // Then check if there's selected text in the CLI subagent views. for subagent_view in self.cli_subagent_views.values() { if let Some(selected_text) = subagent_view.as_ref(ctx).selected_text(ctx) { ctx.clipboard() @@ -16622,19 +16637,6 @@ impl TerminalView { } } - let semantic_selection = SemanticSelection::as_ref(ctx); - if let Some(selected) = self.model.lock().selection_to_string( - semantic_selection, - self.is_inverted_blocklist(ctx), - ctx, - ) { - if !selected.is_empty() { - ctx.clipboard() - .write(ClipboardContent::plain_text(selected)); - } - return; - } - // Prioritize selected text in the input over selected blocks (APP-4330): // it's possible to have both a block and input text selected at the same // time, and in that case the user almost always means to copy the input.