Auto-monitor long-running commands with agent

This commit is contained in:
Ryan Ward
2026-07-28 17:11:34 -05:00
parent b75aa00fb0
commit 5c14241b91
4 changed files with 98 additions and 16 deletions
@@ -93,6 +93,8 @@ pub struct ResponseStream {
id: ResponseStreamId,
params: api::RequestParams,
retry_count: usize,
/// One-time fallback from the profile's thinking model to its coding model.
coding_model_fallback_attempted: bool,
start_time: DateTime<Local>,
time_to_latest_event: TimeDelta,
cancellation_tx: Option<oneshot::Sender<()>>,
@@ -254,6 +256,7 @@ impl ResponseStream {
time_to_latest_event: TimeDelta::seconds(0),
cancellation_tx: Some(cancellation_tx),
retry_count: 0,
coding_model_fallback_attempted: false,
original_error: None,
has_received_client_actions: false,
ai_identifiers,
@@ -320,7 +323,7 @@ impl ResponseStream {
let request_id = Uuid::new_v4();
self.current_request_id = Some(request_id);
let params = self.params.clone();
let mut params = self.params.clone();
let provider_config = Self::resolve_provider_config(params.model.as_str(), ctx);
let server_api = ServerApiProvider::as_ref(ctx).get_ai_client().clone();
let _ = ctx.spawn(
@@ -334,6 +337,28 @@ impl ResponseStream {
);
}
fn should_fallback_to_coding_model(
&self,
error: &Arc<crate::server::server_api::AIApiError>,
) -> bool {
if self.coding_model_fallback_attempted || self.has_received_client_actions {
return false;
}
let coding_model = self.params.coding_model.as_str();
!coding_model.is_empty()
&& coding_model != self.params.model.as_str()
&& matches!(
error.as_ref(),
crate::server::server_api::AIApiError::QuotaLimit { .. }
)
}
fn retry_with_coding_model(&mut self, ctx: &mut ModelContext<Self>) {
self.coding_model_fallback_attempted = true;
self.params.model = self.params.coding_model.clone();
self.retry(ctx);
}
/// Cancels the stream. The conversation_id is preserved in the emitted event for async handling.
pub(super) fn cancel(
&mut self,
@@ -478,6 +503,14 @@ impl ResponseStream {
self.original_error = Some(format!("{e:?}"));
}
if self.should_fallback_to_coding_model(&e) {
log::warn!(
"Thinking model rate-limited; retrying with the profile coding model"
);
self.retry_with_coding_model(ctx);
return;
}
let is_online = NetworkStatus::as_ref(ctx).is_online();
match recovery_action(
self.has_received_client_actions,