use mentra::{
ContentBlock, SpawnedAgentStatus,
runtime::CommandOutput,
tool::{ToolContext, ToolResult},
};
use super::depth::Depth;
pub(super) async fn command(ctx: &ToolContext<'_>, command: &str) -> ToolResult {
let cwd = ctx.resolve_working_directory(None)?;
let output = ctx
.execute_shell_command(command.to_string(), None, None, cwd)
.await?;
narrate(ctx, &output);
read_back(output)
}
fn narrate(ctx: &ToolContext<'_>, output: &CommandOutput) {
for line in output.stdout.lines() {
ctx.emit_progress(format!("stdout: {line}"));
}
for line in output.stderr.lines() {
ctx.emit_progress(format!("stderr: {line}"));
}
}
fn read_back(output: CommandOutput) -> ToolResult {
if output.success() {
return Ok(if output.stdout.is_empty() {
output.stderr
} else {
output.stdout
});
}
Err(if !output.stderr.trim().is_empty() {
output.stderr
} else if !output.stdout.trim().is_empty() {
output.stdout
} else if output.timed_out {
"Command timed out after the configured limit".to_string()
} else {
format!(
"Command exited with status {}",
output
.status_code
.map_or_else(|| "unknown".to_string(), |code| code.to_string())
)
})
}
pub(super) async fn delegate(
ledger: &Depth,
ctx: &mut ToolContext<'_>,
prompt: &str,
depth: usize,
) -> ToolResult {
let mut child = ctx
.spawn_subagent()
.map_err(|error| format!("spawn could not start a subagent: {error}"))?;
let child_id = child.id().to_string();
let _entered = ledger.entered(&child_id, depth + 1);
let started = ctx.register_subagent(&child);
ctx.emit_progress(format!(
"delegating to {} ({})",
started.name, started.model
));
let options = ctx.child_run_options();
let answer = Box::pin(child.run(vec![ContentBlock::text(prompt)], options)).await;
let status = match &answer {
Ok(_) => SpawnedAgentStatus::Finished,
Err(error) => SpawnedAgentStatus::Failed(error.to_string()),
};
ctx.finish_subagent(&child_id, status);
ctx.refresh_tasks().map_err(|error| {
format!("the delegated run finished but its tasks did not load: {error}")
})?;
let message = answer.map_err(|error| format!("the delegated run failed: {error}"))?;
let text = message.text();
Ok(if text.trim().is_empty() {
"The subagent finished without saying anything.".to_string()
} else {
text
})
}