Remove default agent turn cap and preserve queued tool status
Allow new provider runs to continue without an arbitrary lifetime turn budget while preserving explicit limits, checkpoint compatibility, and bounded retries. Keep pending tool proposals visibly queued until execution status arrives, and show cancellation only when supported by actual state. Validation: formatting, inline test-layout checks, both presubmit Clippy commands, 325 targeted regressions, and 54 core tests after final cleanup passed.
This commit is contained in:
@@ -6,6 +6,7 @@ use crate::ai::agent::{
|
||||
AIAgentAction, AIAgentActionId, AIAgentActionType, AIAgentInput, AIAgentOutputMessageType,
|
||||
SummarizationType,
|
||||
};
|
||||
use crate::ai::blocklist::action_model::AIActionStatus;
|
||||
use crate::ai::blocklist::BlocklistAIActionModel;
|
||||
use crate::BlocklistAIHistoryModel;
|
||||
|
||||
@@ -14,6 +15,12 @@ use crate::BlocklistAIHistoryModel;
|
||||
// These are defined within a separate trait rather than default implementations of `AIBlockModel`
|
||||
// so implementations cannot errantly override them.
|
||||
pub trait AIBlockModelHelper {
|
||||
fn action_status_for_display(
|
||||
&self,
|
||||
action_model: &BlocklistAIActionModel,
|
||||
action_id: &AIAgentActionId,
|
||||
app: &AppContext,
|
||||
) -> Option<AIActionStatus>;
|
||||
fn is_first_action_in_output(&self, action_id: &AIAgentActionId, app: &AppContext) -> bool;
|
||||
fn conversation<'a>(&self, app: &'a AppContext) -> Option<&'a AIConversation>;
|
||||
|
||||
@@ -41,6 +48,26 @@ pub trait AIBlockModelHelper {
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AIBlockModel> AIBlockModelHelper for T {
|
||||
fn action_status_for_display(
|
||||
&self,
|
||||
action_model: &BlocklistAIActionModel,
|
||||
action_id: &AIAgentActionId,
|
||||
app: &AppContext,
|
||||
) -> Option<AIActionStatus> {
|
||||
let conversation = self.conversation(app)?;
|
||||
let status = action_model.get_action_status(conversation.id(), action_id);
|
||||
// Proposals appear before the full batch is handed to the executor. A
|
||||
// finished output message is not evidence that an unstarted tool was cancelled.
|
||||
status.or_else(|| {
|
||||
(!conversation.status().is_done()
|
||||
&& self.is_latest_visible_exchange_in_root_task(app)
|
||||
&& self.status(app).output_to_render().is_some_and(|output| {
|
||||
output.get().actions().any(|action| action.id == *action_id)
|
||||
}))
|
||||
.then_some(AIActionStatus::Queued)
|
||||
})
|
||||
}
|
||||
|
||||
fn is_first_action_in_output(&self, action_id: &AIAgentActionId, app: &AppContext) -> bool {
|
||||
self.status(app).output_to_render().is_some_and(|output| {
|
||||
output
|
||||
|
||||
@@ -3702,17 +3702,11 @@ pub fn action_icon<V: View>(
|
||||
app: &AppContext,
|
||||
) -> galaxyui::elements::Icon {
|
||||
let appearance = Appearance::as_ref(app);
|
||||
let status = ai_block_model
|
||||
.conversation_id(app)
|
||||
.and_then(|conversation_id| {
|
||||
action_model
|
||||
.as_ref(app)
|
||||
.get_action_status(conversation_id, action_id)
|
||||
});
|
||||
let status = ai_block_model.action_status_for_display(action_model.as_ref(app), action_id, app);
|
||||
match status {
|
||||
Some(status) => match status {
|
||||
AIActionStatus::Preprocessing => icons::gray_circle_icon(appearance),
|
||||
AIActionStatus::Queued => icons::gray_stop_icon(appearance),
|
||||
AIActionStatus::Queued => icons::gray_clock_icon(appearance),
|
||||
AIActionStatus::Blocked => icons::yellow_stop_icon(appearance),
|
||||
AIActionStatus::RunningAsync => icons::yellow_running_icon(appearance),
|
||||
AIActionStatus::Finished(result) => {
|
||||
@@ -3741,8 +3735,13 @@ pub fn action_icon<V: View>(
|
||||
} else {
|
||||
icons::gray_circle_icon(appearance)
|
||||
}
|
||||
} else {
|
||||
} else if ai_block_model
|
||||
.conversation(app)
|
||||
.is_some_and(|conversation| conversation.status().is_cancelled())
|
||||
{
|
||||
inline_action_icons::cancelled_icon(appearance)
|
||||
} else {
|
||||
icons::gray_circle_icon(appearance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1171,10 +1171,11 @@ impl RequestedCommandView {
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let appearance = Appearance::as_ref(app);
|
||||
let action_status = self
|
||||
.action_model
|
||||
.as_ref(app)
|
||||
.get_action_status(self.client_ids.conversation_id, &self.action_id);
|
||||
let action_status = self.block_model.action_status_for_display(
|
||||
self.action_model.as_ref(app),
|
||||
&self.action_id,
|
||||
app,
|
||||
);
|
||||
|
||||
let mut title: Cow<'static, str>;
|
||||
let mut font_override = None;
|
||||
@@ -1541,10 +1542,11 @@ impl View for RequestedCommandView {
|
||||
fn render(&self, app: &AppContext) -> Box<dyn Element> {
|
||||
let appearance = Appearance::as_ref(app);
|
||||
let theme = appearance.theme();
|
||||
let action_status = self
|
||||
.action_model
|
||||
.as_ref(app)
|
||||
.get_action_status(self.client_ids.conversation_id, &self.action_id);
|
||||
let action_status = self.block_model.action_status_for_display(
|
||||
self.action_model.as_ref(app),
|
||||
&self.action_id,
|
||||
app,
|
||||
);
|
||||
|
||||
let is_last_output_message_in_output = self
|
||||
.block_model
|
||||
|
||||
Reference in New Issue
Block a user