use crate::config::ToolControl;
use crate::harness_root;
use crate::prompt::dispatch::tools::injected;
use crate::prompt::inbox::AdvanceLauncher;
use crate::prompt::resolve::{ConfigSource, resolve_worker};
use crate::prompt::step;
use crate::prompt::tool::SpawnTool;
use crate::prompt::tool::builtin::dispatch::EnvLookup;
use crate::prompt::tool::inject::{InjectedTool, ToolInjection};
use crate::prompt::tool::{ENV_CONV_BRANCH, ENV_CONV_REPO};
use crate::prompt::{Deps, NanoIdGen, RealSleeper, SpawnAdapter, SystemClock};
use std::path::{Path, PathBuf};
use thiserror::Error;
pub(crate) struct Caller {
pub(crate) workspace: PathBuf,
pub(crate) agent: String,
pub(crate) step_dir: PathBuf,
pub(crate) data_root: PathBuf,
pub(crate) role: String,
pub(crate) grant: Vec<String>,
pub(crate) injected: Vec<InjectedTool>,
pub(crate) tool_control: Option<ToolControl>,
}
#[derive(Debug, Error)]
pub(crate) enum Error {
#[error("missing env var {0:?} (set by the harness per ARCH §3.3)")]
MissingEnv(&'static str),
#[error(
"agent {agent:?} has no step under {workspace}: an inner invocation records \
inside the step that raised it (ARCH §2.3)"
)]
NoStep { agent: String, workspace: PathBuf },
#[error(transparent)]
Window(#[from] crate::prompt::Error),
}
pub(crate) fn resolve(
env: &dyn EnvLookup,
driver_target: &Path,
adapter_target: Option<&Path>,
stop: &std::sync::atomic::AtomicBool,
injection: Option<&dyn ToolInjection>,
) -> Result<Caller, Error> {
let workspace = PathBuf::from(require(env, ENV_CONV_REPO)?);
let agent = require(env, ENV_CONV_BRANCH)?
.into_string()
.map_err(|_| Error::MissingEnv(ENV_CONV_BRANCH))?;
let step_dir = step::in_flight(&workspace, &agent).ok_or_else(|| Error::NoStep {
agent: agent.clone(),
workspace: workspace.clone(),
})?;
let roots = harness_root::resolve().map_err(crate::prompt::Error::from)?;
let executor =
SpawnTool::new(&roots.data, &SystemClock, driver_target).with_injection(injection);
let launcher = AdvanceLauncher::with_exe(driver_target.to_path_buf());
let rng = crate::workspace::agent_name::mint::SplitMix64::from_entropy();
let deps = Deps {
adapter: &SpawnAdapter,
sleeper: &RealSleeper,
git: &crate::template::RealGit::new(),
clock: &SystemClock,
id_gen: &NanoIdGen,
tool_executor: &executor,
config_root: &roots.config,
data_root: &roots.data,
adapter_target,
stop,
launcher: &launcher,
rng: &rng,
};
let worker = resolve_worker(&workspace, ConfigSource::Agent(&agent), &deps)?;
let resolved = worker.as_resolved();
let injected = injected(resolved.grant.role, &executor, &workspace, &agent);
Ok(Caller {
role: resolved.grant.role.to_string(),
grant: resolved.grant.tools.to_vec(),
tool_control: resolved.workflow.tool_control.clone(),
injected,
data_root: roots.data.clone(),
workspace,
agent,
step_dir,
})
}
fn require(env: &dyn EnvLookup, key: &'static str) -> Result<std::ffi::OsString, Error> {
env.get(key).ok_or(Error::MissingEnv(key))
}