use bamboo_agent_core::tools::ToolResult;
use bamboo_domain::task::{TaskBlocker, TaskBlockerKind};
use bamboo_domain::TaskItemStatus;
use chrono::Utc;
use super::{TaskLoopContext, TaskLoopItem};
impl TaskLoopContext {
pub fn auto_match_tool_to_item(&mut self, tool_name: &str) {
if self.active_item_id.is_some() {
return;
}
let tool_lower = tool_name.to_lowercase();
let matching_item_id = self
.items
.iter()
.find(|item| {
if matches!(item.status, TaskItemStatus::Completed) {
return false;
}
if !self.unresolved_dependencies(&item.depends_on).is_empty() {
return false;
}
let desc_lower = item.description.to_lowercase();
desc_lower.contains(&tool_lower)
|| (tool_lower.contains("file") && desc_lower.contains("file"))
|| (tool_lower.contains("command")
&& (desc_lower.contains("run") || desc_lower.contains("execute")))
})
.map(|item| item.id.clone());
if let Some(item_id) = matching_item_id {
self.set_active_item(&item_id);
}
}
pub fn auto_update_status(&mut self, tool_name: &str, result: &ToolResult) {
if self.active_item_id.is_none() {
self.auto_match_tool_to_item(tool_name);
}
if let Some(ref active_id) = self.active_item_id.clone() {
let action = self
.items
.iter()
.find(|item| &item.id == active_id)
.and_then(|item| {
if result.success {
if self.should_mark_completed(item) {
Some(TaskItemStatus::Completed)
} else {
None
}
} else if self.should_mark_blocked(item) {
Some(TaskItemStatus::Blocked)
} else {
None
}
});
if let Some(new_status) = action {
if let Some(item) = self.items.iter_mut().find(|item| &item.id == active_id) {
if matches!(new_status, TaskItemStatus::Blocked) {
let mut summary = format!("Tool `{}` failed repeatedly", tool_name);
let detail = result.result.trim();
if !detail.is_empty() {
let clipped: String = detail.chars().take(120).collect();
if detail.chars().count() > 120 {
summary.push_str(&format!(": {}…", clipped.trim_end()));
} else {
summary.push_str(&format!(": {clipped}"));
}
}
TaskLoopContext::add_item_blocker(
item,
TaskBlocker {
kind: TaskBlockerKind::ToolFailure,
summary,
waiting_on: None,
},
);
}
let reason = if result.success {
Some("Auto-updated after sufficient successful tool calls")
} else {
Some("Auto-updated after repeated tool failures")
};
let changed = TaskLoopContext::transition_item(
item,
new_status.clone(),
reason,
Some(self.current_round),
);
if changed && matches!(new_status, TaskItemStatus::Completed) {
item.completed_at_round = Some(self.current_round);
self.active_item_id = None;
}
if changed {
self.version += 1;
self.updated_at = Utc::now();
}
}
}
}
}
fn should_mark_completed(&self, item: &TaskLoopItem) -> bool {
if !item.completion_criteria.is_empty() {
return false;
}
let success_count = item
.tool_calls
.iter()
.filter(|record| record.success)
.count();
success_count >= 3
}
fn should_mark_blocked(&self, item: &TaskLoopItem) -> bool {
let recent_failures = item
.tool_calls
.iter()
.rev()
.take(2)
.filter(|record| !record.success)
.count();
recent_failures >= 2
}
}