use super::super::{
assembler, canonical, child_result, model_call, one_call, result_deposit, step_commit,
stop_signal, terminal, tool_step, tools, transcript,
};
use crate::config::Event;
use crate::prompt::inbox::Epitaph;
use crate::prompt::resolve::WorkerConfig;
use crate::prompt::step::{STAGING_FILE, next_step_seq};
use crate::prompt::workflow_actions;
use crate::prompt::{Deps, Error};
use brazen::Content;
use model_call::ModelCall;
use std::path::Path;
pub(super) enum StepOutcome {
ToolsRan,
Terminal(Epitaph),
Held,
}
pub(super) fn step(
workspace: &Path,
agent_id: &str,
worktree: &Path,
cfg: &WorkerConfig,
deps: &Deps<'_>,
) -> Result<StepOutcome, Error> {
if stop_signal::stopped(deps.stop) {
return Ok(StepOutcome::Terminal(Epitaph::Stopped));
}
let resolved = cfg.as_resolved();
if terminal::budget_exhausted(
workspace,
agent_id,
agent_id,
worktree,
&resolved.budgets,
deps,
)? {
return Ok(StepOutcome::Terminal(Epitaph::BudgetExhausted));
}
workflow_actions::run_step_hook(&cfg.workflow, Event::PreStep, worktree, agent_id, deps.git)?;
let goal = std::fs::read_to_string(worktree.join(step_commit::GOAL_FILE))?;
let name = crate::workspace::agent_name::in_worktree(worktree);
let system_with_goal = step_commit::compose_system(&goal, name.as_deref(), &resolved.soul);
let call = ModelCall {
adapter: deps.adapter,
sleeper: deps.sleeper,
binary: &resolved.binary,
provider_row: resolved.provider_row,
retry: resolved.retry,
stop: deps.stop,
expect_handshake: resolved.expect_handshake,
};
let step_seq = next_step_seq(workspace, agent_id)?;
step_commit::refresh_descriptors(worktree, agent_id, &resolved.grant, deps.git)?;
let commit_sha = step_commit::read_branch_tip(worktree, deps)?;
let messages = assembler::assemble(worktree, resolved.manifest)?;
let tools = tools::compose(
worktree,
&resolved.grant,
&messages,
&tools::injected(resolved.grant.role, deps.tool_executor, workspace, agent_id),
)?;
let request = canonical::build_request(
resolved.model_id,
&system_with_goal,
messages,
tools,
resolved.max_output_tokens,
resolved.effort,
resolved.priority,
);
let step = one_call::Step {
conv_repo: workspace,
conv_id: agent_id,
seq: step_seq,
tip: commit_sha,
};
let Some(step_dir_rel_str) = one_call::issue(step, &request, &call, &resolved, deps)? else {
return Ok(StepOutcome::Terminal(Epitaph::Stopped));
};
let staging_path = workspace.join(&step_dir_rel_str).join(STAGING_FILE);
let assistant_content = transcript::commit_assistant(
worktree,
agent_id,
resolved.model_id,
&staging_path,
deps.git,
)?;
workflow_actions::run_step_hook(&cfg.workflow, Event::PostStep, worktree, agent_id, deps.git)?;
if !assistant_content
.iter()
.any(|b| matches!(b, Content::ToolUse { .. }))
{
let response = result_deposit::terminal_text(&assistant_content);
result_deposit::deposit_terminal(
workspace,
agent_id,
worktree,
Epitaph::FinalResponse,
response.as_deref(),
deps,
)?;
return Ok(StepOutcome::Terminal(Epitaph::FinalResponse));
}
match tool_step::run_tool_calls(
workspace,
worktree,
agent_id,
&resolved,
&step_dir_rel_str,
&assistant_content,
deps,
)? {
tool_step::ToolWindow::Stopped => return Ok(StepOutcome::Terminal(Epitaph::Stopped)),
tool_step::ToolWindow::Held => return Ok(StepOutcome::Held),
tool_step::ToolWindow::Completed => {}
}
if stop_signal::stopped(deps.stop) {
return Ok(StepOutcome::Terminal(Epitaph::Stopped));
}
workflow_actions::run_step_hook(
&cfg.workflow,
Event::OnToolReturn,
worktree,
agent_id,
deps.git,
)?;
child_result::run_flush(workspace, agent_id, worktree, &cfg.workflow, deps)?;
Ok(StepOutcome::ToolsRan)
}