Simplify -v flag from two verbosity levels to a single boolean

The `-vv` (full detail) mode was not useful in practice. This collapses
the two-tier `-v`/`-vv` into a single `--verbose` boolean and removes
the now-dead `format_event_detail` function and its tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-02 12:29:29 -05:00
parent 1fa26aa1ec
commit effe165f31
5 changed files with 16 additions and 439 deletions

View file

@ -106,7 +106,7 @@ pub async fn serve_command(args: ServeArgs, styles: &'static Styles) -> anyhow::
Some(Box::new(AgentApiBackend::new(
model.clone(),
provider_enum,
0,
false,
styles,
)))
}

View file

@ -25,14 +25,14 @@ use crate::outcome::StageUsage;
pub struct AgentApiBackend {
model: String,
provider: Provider,
verbose: u8,
verbose: bool,
styles: &'static Styles,
sessions: Mutex<HashMap<String, Session>>,
}
impl AgentApiBackend {
#[must_use]
pub fn new(model: String, provider: Provider, verbose: u8, styles: &'static Styles) -> Self {
pub fn new(model: String, provider: Provider, verbose: bool, styles: &'static Styles) -> Self {
Self {
model,
provider,
@ -283,7 +283,7 @@ impl CodergenBackend for AgentApiBackend {
}
// Verbose stderr printing (gated on verbosity)
if verbose >= 1 {
if verbose {
match &event.event {
AgentEvent::ToolCallStarted {
tool_name,
@ -299,21 +299,6 @@ impl CodergenBackend for AgentApiBackend {
args = format_tool_args(arguments),
);
}
AgentEvent::ToolCallCompleted {
tool_name,
output,
is_error,
..
} if verbose >= 2 => {
let label = if *is_error { "error" } else { "result" };
eprintln!(
"{dim}[{node_id}] [{label}] {tool_name}:{reset}\n{}",
serde_json::to_string_pretty(output)
.unwrap_or_else(|_| output.to_string()),
dim = styles.dim,
reset = styles.reset,
);
}
AgentEvent::Error { error } => {
eprintln!(
"{dim}[{node_id}]{reset} {red}\u{2717} {error}{reset}",
@ -379,7 +364,7 @@ impl CodergenBackend for AgentApiBackend {
stage_usage.cost = super::compute_stage_cost(&stage_usage);
// Print session summary to stderr.
if self.verbose >= 1 {
if self.verbose {
let total_tokens = total_usage.input_tokens + total_usage.output_tokens;
let token_str = if total_tokens >= 1000 {
format!("{}k tokens", total_tokens / 1000)
@ -469,10 +454,10 @@ mod tests {
#[test]
fn agent_backend_stores_config() {
let styles = Box::leak(Box::new(Styles::new(false)));
let backend = AgentApiBackend::new("claude-opus-4-6".to_string(), Provider::OpenAi, 2, styles);
let backend = AgentApiBackend::new("claude-opus-4-6".to_string(), Provider::OpenAi, true, styles);
assert_eq!(backend.model, "claude-opus-4-6");
assert_eq!(backend.provider, Provider::OpenAi);
assert_eq!(backend.verbose, 2);
assert!(backend.verbose);
}
#[test]
@ -481,7 +466,7 @@ mod tests {
let backend = AgentApiBackend::new(
"claude-opus-4-6".to_string(),
Provider::Anthropic,
0,
false,
styles,
);
assert!(backend.sessions.lock().unwrap().is_empty());
@ -493,7 +478,7 @@ mod tests {
let backend = AgentApiBackend::new(
"claude-opus-4-6".to_string(),
Provider::Anthropic,
0,
false,
styles,
);
let mut profile = backend.build_profile();

View file

@ -106,9 +106,9 @@ pub struct RunArgs {
#[arg(long)]
pub provider: Option<String>,
/// Verbosity level (-v summary, -vv full details)
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
/// Enable verbose output
#[arg(short, long)]
pub verbose: bool,
/// Sandbox for agent tools
#[arg(long, value_enum)]
@ -571,353 +571,6 @@ pub fn format_event_summary(event: &WorkflowRunEvent, styles: &Styles) -> String
format!("{dim}{body}{reset}", dim = styles.dim, reset = styles.reset)
}
/// Multi-line detail view of a workflow run event for `-vv` output.
/// Box-drawing is dimmed; values are normal.
#[must_use]
pub fn format_event_detail(event: &WorkflowRunEvent, styles: &Styles) -> String {
let d = styles.dim;
let r = styles.reset;
match event {
WorkflowRunEvent::WorkflowRunStarted { name, run_id, .. } => {
format!(
"{d}── WORKFLOW_RUN_STARTED ─────────────────────────{r}\n {d}name:{r} {name}\n {d}id:{r} {run_id}\n"
)
}
WorkflowRunEvent::WorkflowRunCompleted {
duration_ms,
artifact_count,
total_cost,
..
} => {
let mut s = format!("{d}── WORKFLOW_RUN_COMPLETED ───────────────────────{r}\n {d}duration_ms:{r} {duration_ms}\n {d}artifact_count:{r} {artifact_count}\n");
if let Some(cost) = total_cost {
s.push_str(&format!(" {d}total_cost:{r} {}\n", format_cost(*cost)));
}
s
}
WorkflowRunEvent::WorkflowRunFailed { error, duration_ms, .. } => {
format!("{d}── WORKFLOW_RUN_FAILED ──────────────────────────{r}\n {d}error:{r} {error}\n {d}duration_ms:{r} {duration_ms}\n")
}
WorkflowRunEvent::StageStarted { name, index, handler_type, attempt, max_attempts } => {
let mut s = format!(
"{d}── STAGE_STARTED ────────────────────────────{r}\n {d}name:{r} {name}\n {d}index:{r} {index}\n"
);
if let Some(ht) = handler_type {
s.push_str(&format!(" {d}handler_type:{r} {ht}\n"));
}
s.push_str(&format!(" {d}attempt:{r} {attempt}/{max_attempts}\n"));
s
}
WorkflowRunEvent::StageCompleted {
name,
index,
duration_ms,
status,
preferred_label,
suggested_next_ids,
usage,
failure_reason,
notes,
files_touched,
attempt,
max_attempts,
failure_class,
} => {
let mut s = format!("{d}── STAGE_COMPLETED ──────────────────────────{r}\n {d}name:{r} {name}\n {d}index:{r} {index}\n {d}duration_ms:{r} {duration_ms}\n {d}status:{r} {status}\n");
if let Some(label) = preferred_label {
s.push_str(&format!(" {d}preferred_label:{r} {label}\n"));
}
if !suggested_next_ids.is_empty() {
s.push_str(&format!(" {d}suggested_next_ids:{r} {}\n", suggested_next_ids.join(", ")));
}
if let Some(u) = usage {
let total = u.input_tokens + u.output_tokens;
s.push_str(&format!(" {d}model:{r} {}\n", u.model));
s.push_str(&format!(" {d}tokens:{r} {} ({} in / {} out)\n",
format_tokens_human(total),
format_tokens_human(u.input_tokens),
format_tokens_human(u.output_tokens),
));
if let Some(cache_read) = u.cache_read_tokens {
s.push_str(&format!(" {d}cache_read:{r} {}\n", format_tokens_human(cache_read)));
}
if let Some(cache_write) = u.cache_write_tokens {
s.push_str(&format!(" {d}cache_write:{r} {}\n", format_tokens_human(cache_write)));
}
if let Some(reasoning) = u.reasoning_tokens {
s.push_str(&format!(" {d}reasoning:{r} {}\n", format_tokens_human(reasoning)));
}
if let Some(cost) = compute_stage_cost(u) {
s.push_str(&format!(" {d}cost:{r} {}\n", format_cost(cost)));
}
}
if !files_touched.is_empty() {
s.push_str(&format!(" {d}files_touched:{r} {} files\n", files_touched.len()));
}
if let Some(reason) = failure_reason {
s.push_str(&format!(" {d}failure_reason:{r} {reason}\n"));
}
if let Some(n) = notes {
s.push_str(&format!(" {d}notes:{r} {n}\n"));
}
s.push_str(&format!(" {d}attempt:{r} {attempt}/{max_attempts}\n"));
if let Some(fc) = failure_class {
s.push_str(&format!(" {d}failure_class:{r} {fc}\n"));
}
s
}
WorkflowRunEvent::StageFailed {
name,
index,
error,
will_retry,
failure_reason,
failure_class,
} => {
let mut s = format!("{d}── STAGE_FAILED ─────────────────────────────{r}\n {d}name:{r} {name}\n {d}index:{r} {index}\n {d}error:{r} {error}\n {d}will_retry:{r} {will_retry}\n");
if let Some(reason) = failure_reason {
s.push_str(&format!(" {d}failure_reason:{r} {reason}\n"));
}
if let Some(fc) = failure_class {
s.push_str(&format!(" {d}failure_class:{r} {fc}\n"));
}
s
}
WorkflowRunEvent::StageRetrying {
name,
index,
attempt,
max_attempts,
delay_ms,
} => {
format!("{d}── STAGE_RETRYING ───────────────────────────{r}\n {d}name:{r} {name}\n {d}index:{r} {index}\n {d}attempt:{r} {attempt}/{max_attempts}\n {d}delay_ms:{r} {delay_ms}\n")
}
WorkflowRunEvent::ParallelStarted { branch_count, join_policy, error_policy } => {
format!("{d}── PARALLEL_STARTED ─────────────────────────{r}\n {d}branch_count:{r} {branch_count}\n {d}join_policy:{r} {join_policy}\n {d}error_policy:{r} {error_policy}\n")
}
WorkflowRunEvent::ParallelBranchStarted { branch, index } => {
format!("{d}── PARALLEL_BRANCH_STARTED ──────────────────{r}\n {d}branch:{r} {branch}\n {d}index:{r} {index}\n")
}
WorkflowRunEvent::ParallelBranchCompleted {
branch,
index,
duration_ms,
status,
} => {
format!("{d}── PARALLEL_BRANCH_COMPLETED ────────────────{r}\n {d}branch:{r} {branch}\n {d}index:{r} {index}\n {d}duration_ms:{r} {duration_ms}\n {d}status:{r} {status}\n")
}
WorkflowRunEvent::ParallelCompleted {
duration_ms,
success_count,
failure_count,
} => {
format!("{d}── PARALLEL_COMPLETED ───────────────────────{r}\n {d}duration_ms:{r} {duration_ms}\n {d}success_count:{r} {success_count}\n {d}failure_count:{r} {failure_count}\n")
}
WorkflowRunEvent::InterviewStarted { question, stage, question_type } => {
format!("{d}── INTERVIEW_STARTED ────────────────────────{r}\n {d}stage:{r} {stage}\n {d}question:{r} {question}\n {d}question_type:{r} {question_type}\n")
}
WorkflowRunEvent::InterviewCompleted {
question,
answer,
duration_ms,
} => {
format!("{d}── INTERVIEW_COMPLETED ──────────────────────{r}\n {d}question:{r} {question}\n {d}answer:{r} {answer}\n {d}duration_ms:{r} {duration_ms}\n")
}
WorkflowRunEvent::InterviewTimeout {
question,
stage,
duration_ms,
} => {
format!("{d}── INTERVIEW_TIMEOUT ────────────────────────{r}\n {d}question:{r} {question}\n {d}stage:{r} {stage}\n {d}duration_ms:{r} {duration_ms}\n")
}
WorkflowRunEvent::CheckpointSaved { node_id } => {
format!(
"{d}── CHECKPOINT_SAVED ─────────────────────────{r}\n {d}node_id:{r} {node_id}\n"
)
}
WorkflowRunEvent::GitCheckpoint { run_id, node_id, status, git_commit_sha } => {
format!(
"{d}── GIT_CHECKPOINT ───────────────────────────{r}\n {d}run_id:{r} {run_id}\n {d}node_id:{r} {node_id}\n {d}status:{r} {status}\n {d}sha:{r} {git_commit_sha}\n"
)
}
WorkflowRunEvent::EdgeSelected { from_node, to_node, label, condition } => {
let mut s = format!("{d}── EDGE_SELECTED ────────────────────────────{r}\n {d}from:{r} {from_node}\n {d}to:{r} {to_node}\n");
if let Some(l) = label {
s.push_str(&format!(" {d}label:{r} {l}\n"));
}
if let Some(c) = condition {
s.push_str(&format!(" {d}condition:{r} {c}\n"));
}
s
}
WorkflowRunEvent::LoopRestart { from_node, to_node } => {
format!("{d}── LOOP_RESTART ─────────────────────────────{r}\n {d}from:{r} {from_node}\n {d}to:{r} {to_node}\n")
}
WorkflowRunEvent::Prompt { stage, text } => {
format!("{d}── PROMPT ───────────────────────────────────{r}\n {d}stage:{r} {stage}\n {d}text:{r}\n{text}\n")
}
WorkflowRunEvent::Agent { stage, event } => match event {
AgentEvent::AssistantMessage { text, model, usage, tool_call_count } => {
let total = usage.input_tokens + usage.output_tokens;
let truncated = if text.len() > 200 { &text[..200] } else { text.as_str() };
let mut s = format!("{d}── ASSISTANT_MESSAGE ────────────────────────{r}\n {d}stage:{r} {stage}\n {d}model:{r} {model}\n {d}tokens:{r} {} ({} in / {} out)\n {d}tool_calls:{r} {tool_call_count}\n",
format_tokens_human(total),
format_tokens_human(usage.input_tokens),
format_tokens_human(usage.output_tokens),
);
if let Some(cache_read) = usage.cache_read_tokens {
s.push_str(&format!(" {d}cache_read:{r} {}\n", format_tokens_human(cache_read)));
}
if let Some(cache_write) = usage.cache_write_tokens {
s.push_str(&format!(" {d}cache_write:{r} {}\n", format_tokens_human(cache_write)));
}
if let Some(reasoning) = usage.reasoning_tokens {
s.push_str(&format!(" {d}reasoning:{r} {}\n", format_tokens_human(reasoning)));
}
s.push_str(&format!(" {d}text:{r} {truncated}\n"));
s
}
AgentEvent::ToolCallStarted { tool_name, tool_call_id, arguments } => {
let args_str = serde_json::to_string(arguments).unwrap_or_else(|_| arguments.to_string());
let truncated = if args_str.len() > 200 { &args_str[..200] } else { &args_str };
format!("{d}── TOOL_CALL_STARTED ────────────────────────{r}\n {d}stage:{r} {stage}\n {d}tool_name:{r} {tool_name}\n {d}tool_call_id:{r} {tool_call_id}\n {d}arguments:{r} {truncated}\n")
}
AgentEvent::ToolCallCompleted { tool_name, tool_call_id, output, is_error } => {
let output_str = serde_json::to_string(output).unwrap_or_else(|_| output.to_string());
let truncated = if output_str.len() > 200 { &output_str[..200] } else { &output_str };
format!("{d}── TOOL_CALL_COMPLETED ──────────────────────{r}\n {d}stage:{r} {stage}\n {d}tool_name:{r} {tool_name}\n {d}tool_call_id:{r} {tool_call_id}\n {d}is_error:{r} {is_error}\n {d}output:{r} {truncated}\n")
}
AgentEvent::Error { error } => {
format!("{d}── SESSION_ERROR ────────────────────────────{r}\n {d}stage:{r} {stage}\n {d}error:{r} {error}\n")
}
AgentEvent::ContextWindowWarning { estimated_tokens, context_window_size, usage_percent } => {
format!("{d}── CONTEXT_WINDOW_WARNING ───────────────────{r}\n {d}stage:{r} {stage}\n {d}estimated_tokens:{r} {estimated_tokens}\n {d}context_window_size:{r} {context_window_size}\n {d}usage_percent:{r} {usage_percent}%\n")
}
AgentEvent::LoopDetected => {
format!("{d}── LOOP_DETECTED ────────────────────────────{r}\n {d}stage:{r} {stage}\n")
}
AgentEvent::TurnLimitReached { max_turns } => {
format!("{d}── TURN_LIMIT_REACHED ───────────────────────{r}\n {d}stage:{r} {stage}\n {d}max_turns:{r} {max_turns}\n")
}
AgentEvent::CompactionStarted { estimated_tokens, context_window_size } => {
format!("{d}── COMPACTION_STARTED ───────────────────────{r}\n {d}stage:{r} {stage}\n {d}estimated_tokens:{r} {estimated_tokens}\n {d}context_window_size:{r} {context_window_size}\n")
}
AgentEvent::CompactionCompleted { original_turn_count, preserved_turn_count, summary_token_estimate, tracked_file_count } => {
format!("{d}── COMPACTION_COMPLETED ─────────────────────{r}\n {d}stage:{r} {stage}\n {d}original_turn_count:{r} {original_turn_count}\n {d}preserved_turn_count:{r} {preserved_turn_count}\n {d}summary_token_estimate:{r} {summary_token_estimate}\n {d}tracked_file_count:{r} {tracked_file_count}\n")
}
AgentEvent::LlmRetry { provider, model, attempt, delay_secs, error } => {
let delay_ms = (*delay_secs * 1000.0) as u64;
format!("{d}── LLM_RETRY ────────────────────────────────{r}\n {d}stage:{r} {stage}\n {d}provider:{r} {provider}\n {d}model:{r} {model}\n {d}attempt:{r} {attempt}\n {d}delay_ms:{r} {delay_ms}\n {d}error:{r} {error}\n")
}
AgentEvent::SubAgentSpawned { agent_id, depth, task } => {
let task_preview = if task.len() > 200 { &task[..200] } else { task.as_str() };
format!("{d}── SUBAGENT_SPAWNED ─────────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n {d}task:{r} {task_preview}\n")
}
AgentEvent::SubAgentCompleted { agent_id, depth, success, turns_used } => {
format!("{d}── SUBAGENT_COMPLETED ───────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n {d}success:{r} {success}\n {d}turns_used:{r} {turns_used}\n")
}
AgentEvent::SubAgentFailed { agent_id, depth, error } => {
format!("{d}── SUBAGENT_FAILED ──────────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n {d}error:{r} {error}\n")
}
AgentEvent::SubAgentClosed { agent_id, depth } => {
format!("{d}── SUBAGENT_CLOSED ──────────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n")
}
AgentEvent::SubAgentEvent { agent_id, depth, event } => {
format!("{d}── SUBAGENT_EVENT ───────────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n {d}event:{r} {event:?}\n")
}
other => format!("{d}── AGENT ────────────────────────────────────{r}\n {d}stage:{r} {stage}\n {d}event:{r} {other:?}\n"),
}
WorkflowRunEvent::ParallelEarlyTermination {
reason,
completed_count,
pending_count,
} => {
format!("{d}── PARALLEL_EARLY_TERMINATION ───────────────{r}\n {d}reason:{r} {reason}\n {d}completed_count:{r} {completed_count}\n {d}pending_count:{r} {pending_count}\n")
}
WorkflowRunEvent::SubgraphStarted { node_id, start_node } => {
format!("{d}── SUBGRAPH_STARTED ─────────────────────────{r}\n {d}node_id:{r} {node_id}\n {d}start_node:{r} {start_node}\n")
}
WorkflowRunEvent::SubgraphCompleted {
node_id,
steps_executed,
status,
duration_ms,
} => {
format!("{d}── SUBGRAPH_COMPLETED ───────────────────────{r}\n {d}node_id:{r} {node_id}\n {d}steps_executed:{r} {steps_executed}\n {d}status:{r} {status}\n {d}duration_ms:{r} {duration_ms}\n")
}
WorkflowRunEvent::Sandbox { event } => {
use arc_agent::SandboxEvent;
match event {
SandboxEvent::Initializing { provider } => {
format!("{d}── SANDBOX_INITIALIZING ────────────────────{r}\n {d}provider:{r} {provider}\n")
}
SandboxEvent::Ready { provider, duration_ms } => {
format!("{d}── SANDBOX_READY ───────────────────────────{r}\n {d}provider:{r} {provider}\n {d}duration_ms:{r} {duration_ms}\n")
}
SandboxEvent::InitializeFailed { provider, error, duration_ms } => {
format!("{d}── SANDBOX_INIT_FAILED ─────────────────────{r}\n {d}provider:{r} {provider}\n {d}error:{r} {error}\n {d}duration_ms:{r} {duration_ms}\n")
}
SandboxEvent::CleanupStarted { provider } => {
format!("{d}── SANDBOX_CLEANUP_STARTED ─────────────────{r}\n {d}provider:{r} {provider}\n")
}
SandboxEvent::CleanupCompleted { provider, duration_ms } => {
format!("{d}── SANDBOX_CLEANUP_COMPLETED ───────────────{r}\n {d}provider:{r} {provider}\n {d}duration_ms:{r} {duration_ms}\n")
}
SandboxEvent::CleanupFailed { provider, error } => {
format!("{d}── SANDBOX_CLEANUP_FAILED ──────────────────{r}\n {d}provider:{r} {provider}\n {d}error:{r} {error}\n")
}
SandboxEvent::SnapshotPulling { name } => {
format!("{d}── SANDBOX_SNAPSHOT_PULLING ───────────────────{r}\n {d}name:{r} {name}\n")
}
SandboxEvent::SnapshotPulled { name, duration_ms } => {
format!("{d}── SANDBOX_SNAPSHOT_PULLED ────────────────────{r}\n {d}name:{r} {name}\n {d}duration_ms:{r} {duration_ms}\n")
}
SandboxEvent::SnapshotEnsuring { name } => {
format!("{d}── SANDBOX_SNAPSHOT_ENSURING ───────────────{r}\n {d}name:{r} {name}\n")
}
SandboxEvent::SnapshotCreating { name } => {
format!("{d}── SANDBOX_SNAPSHOT_CREATING ───────────────{r}\n {d}name:{r} {name}\n")
}
SandboxEvent::SnapshotReady { name, duration_ms } => {
format!("{d}── SANDBOX_SNAPSHOT_READY ──────────────────{r}\n {d}name:{r} {name}\n {d}duration_ms:{r} {duration_ms}\n")
}
SandboxEvent::SnapshotFailed { name, error } => {
format!("{d}── SANDBOX_SNAPSHOT_FAILED ─────────────────{r}\n {d}name:{r} {name}\n {d}error:{r} {error}\n")
}
SandboxEvent::GitCloneStarted { url, branch } => {
let branch_str = branch.as_deref().unwrap_or("(default)");
format!("{d}── SANDBOX_GIT_CLONE_STARTED ──────────────{r}\n {d}url:{r} {url}\n {d}branch:{r} {branch_str}\n")
}
SandboxEvent::GitCloneCompleted { url, duration_ms } => {
format!("{d}── SANDBOX_GIT_CLONE_COMPLETED ────────────{r}\n {d}url:{r} {url}\n {d}duration_ms:{r} {duration_ms}\n")
}
SandboxEvent::GitCloneFailed { url, error } => {
format!("{d}── SANDBOX_GIT_CLONE_FAILED ───────────────{r}\n {d}url:{r} {url}\n {d}error:{r} {error}\n")
}
}
}
WorkflowRunEvent::SetupStarted { command_count } => {
format!("{d}── SETUP_STARTED ────────────────────────────{r}\n {d}command_count:{r} {command_count}\n")
}
WorkflowRunEvent::SetupCommandStarted { command, index } => {
format!("{d}── SETUP_COMMAND_STARTED ────────────────────{r}\n {d}index:{r} {index}\n {d}command:{r} {command}\n")
}
WorkflowRunEvent::SetupCommandCompleted { command, index, exit_code, duration_ms } => {
format!("{d}── SETUP_COMMAND_COMPLETED ──────────────────{r}\n {d}index:{r} {index}\n {d}command:{r} {command}\n {d}exit_code:{r} {exit_code}\n {d}duration_ms:{r} {duration_ms}\n")
}
WorkflowRunEvent::SetupCompleted { duration_ms } => {
format!("{d}── SETUP_COMPLETED ──────────────────────────{r}\n {d}duration_ms:{r} {duration_ms}\n")
}
WorkflowRunEvent::SetupFailed { command, index, exit_code, stderr } => {
format!("{d}── SETUP_FAILED ─────────────────────────────{r}\n {d}index:{r} {index}\n {d}command:{r} {command}\n {d}exit_code:{r} {exit_code}\n {d}stderr:{r} {stderr}\n")
}
WorkflowRunEvent::StallWatchdogTimeout { node, idle_seconds } => {
format!("{d}── STALL_WATCHDOG_TIMEOUT ────────────────────{r}\n {d}node:{r} {node}\n {d}idle_seconds:{r} {idle_seconds}\n")
}
}
}
/// Compute the dollar cost for a stage's token usage, if pricing is available.
#[must_use]
pub fn compute_stage_cost(usage: &StageUsage) -> Option<f64> {
@ -1007,20 +660,6 @@ mod tests {
assert!(s.contains("3"));
}
#[test]
fn format_detail_sandbox_ready() {
let event = WorkflowRunEvent::Sandbox {
event: arc_agent::SandboxEvent::Ready {
provider: "local".into(),
duration_ms: 42,
},
};
let s = format_event_detail(&event, test_styles());
assert!(s.contains("SANDBOX_READY"));
assert!(s.contains("local"));
assert!(s.contains("42"));
}
#[test]
fn format_summary_subagent_spawned() {
let event = WorkflowRunEvent::Agent {
@ -1054,36 +693,6 @@ mod tests {
assert!(s.contains("turns=5"));
}
#[test]
fn format_detail_subagent_failed() {
let event = WorkflowRunEvent::Agent {
stage: "code".into(),
event: AgentEvent::SubAgentFailed {
agent_id: "abcdef12-xxxx".into(),
depth: 2,
error: "timeout".into(),
},
};
let s = format_event_detail(&event, test_styles());
assert!(s.contains("SUBAGENT_FAILED"));
assert!(s.contains("timeout"));
assert!(s.contains("depth"));
}
#[test]
fn format_detail_subagent_closed() {
let event = WorkflowRunEvent::Agent {
stage: "code".into(),
event: AgentEvent::SubAgentClosed {
agent_id: "abcdef12-xxxx".into(),
depth: 1,
},
};
let s = format_event_detail(&event, test_styles());
assert!(s.contains("SUBAGENT_CLOSED"));
assert!(s.contains("abcdef12-xxxx"));
}
#[test]
fn format_summary_subagent_event() {
let event = WorkflowRunEvent::Agent {
@ -1099,17 +708,4 @@ mod tests {
assert!(s.contains("abcdef12"));
}
#[test]
fn format_detail_setup_command_completed() {
let event = WorkflowRunEvent::SetupCommandCompleted {
command: "npm install".into(),
index: 0,
exit_code: 0,
duration_ms: 5000,
};
let s = format_event_detail(&event, test_styles());
assert!(s.contains("SETUP_COMMAND_COMPLETED"));
assert!(s.contains("npm install"));
assert!(s.contains("5000"));
}
}

View file

@ -27,7 +27,7 @@ use super::backend::AgentApiBackend;
use super::cli_backend::{BackendRouter, AgentCliBackend};
use super::task_config;
use super::{
compute_stage_cost, format_cost, format_duration_human, format_event_detail,
compute_stage_cost, format_cost, format_duration_human,
format_event_summary, format_tokens_human, print_diagnostics, read_dot_file, SandboxProvider,
RunArgs,
};
@ -154,7 +154,7 @@ pub async fn run_command(args: RunArgs, styles: &'static Styles) -> anyhow::Resu
}
}
if args.verbose >= 1 {
if args.verbose {
eprintln!(
"{dim}Logs: {}{reset}",
logs_dir.display(),
@ -230,11 +230,7 @@ pub async fn run_command(args: RunArgs, styles: &'static Styles) -> anyhow::Resu
});
}
if args.verbose >= 2 {
emitter.on_event(move |event| {
eprint!("{}", format_event_detail(event, styles));
});
} else if args.verbose >= 1 {
if args.verbose {
emitter.on_event(move |event| {
eprintln!("{}", format_event_summary(event, styles));
});

View file

@ -7208,7 +7208,7 @@ async fn arc_e2e_with_real_llm() {
Some(Box::new(AgentApiBackend::new(
model.clone(),
Provider::Anthropic,
0,
false,
&TEST_STYLES,
))
as Box<dyn arc_workflows::handler::codergen::CodergenBackend>)