use std::time::Duration;
use tau_proto::CborValue;
use super::{BLOCKER_TOOL_NAME, ToolCallDisplay, ToolStatus};
#[derive(Clone, Copy)]
pub(super) enum BlockerAction {
Add,
Cancel,
List,
}
impl BlockerAction {
pub(super) fn as_str(self) -> &'static str {
match self {
Self::Add => "add",
Self::Cancel => "cancel",
Self::List => "list",
}
}
}
pub(super) fn blocker_action_descriptor(started: &tau_proto::ToolStarted) -> Option<BlockerAction> {
if !is_blocker_tool_name(started.tool_name.as_str()) {
return None;
}
let CborValue::Map(entries) = &started.arguments else {
return None;
};
let mut action = None;
for (key, value) in entries {
if !matches!(key, CborValue::Text(key) if key == "action") {
continue;
}
let CborValue::Text(value) = value else {
return None;
};
if action.is_some() {
return None;
}
action = match value.as_str() {
"add" => Some(BlockerAction::Add),
"cancel" => Some(BlockerAction::Cancel),
"list" => Some(BlockerAction::List),
_ => return None,
};
}
action
}
pub(super) fn is_blocker_tool_name(name: &str) -> bool {
name == BLOCKER_TOOL_NAME
|| name
.strip_suffix("_task_blocker")
.is_some_and(|prefix| !prefix.is_empty())
}
pub(super) fn effective_shell_timeout(started: &tau_proto::ToolStarted) -> Option<Duration> {
const DEFAULT_TIMEOUT_SECS: u64 = 300;
if !matches!(started.tool_name.as_str(), "shell" | "gpt_shell") {
return None;
}
let CborValue::Map(entries) = &started.arguments else {
return None;
};
let mut timeout = None;
for (key, value) in entries {
if !matches!(key, CborValue::Text(key) if key == "timeout") {
continue;
}
let CborValue::Integer(value) = value else {
return None;
};
let Ok(value) = u64::try_from(*value) else {
return None;
};
if timeout.replace(value).is_some() {
return None;
}
}
Some(Duration::from_secs(timeout.unwrap_or(DEFAULT_TIMEOUT_SECS)))
}
pub(super) fn is_activating_input_wait(started: &tau_proto::ToolStarted) -> bool {
started.tool_name.as_str() == "wait"
&& tau_harness::normalized_wait_timeout_minutes(&started.arguments)
.ok()
.flatten()
.is_some()
}
pub(super) fn normalize_wait_display_timeout(
is_activating_input_wait: bool,
display: &mut ToolCallDisplay,
) -> Option<Duration> {
if !is_activating_input_wait {
return None;
}
let minutes = display.args.strip_suffix('m')?.parse::<u64>().ok()?;
let seconds = minutes.checked_mul(60)?;
display.args = "input".to_owned();
Some(Duration::from_secs(seconds))
}
pub(super) fn sanitize_blocker_display(
display: &mut ToolCallDisplay,
is_blocker: bool,
action: Option<BlockerAction>,
) {
if !is_blocker {
return;
}
display.mode.clear();
display.args = action.map_or_else(String::new, |action| action.as_str().to_owned());
display.range = None;
display.suffixes.retain(|suffix| {
matches!(
suffix.status,
ToolStatus::Success
| ToolStatus::Warning
| ToolStatus::Error
| ToolStatus::Pending
| ToolStatus::Progress
| ToolStatus::Time
)
});
display.payload = None;
}