use std::path::PathBuf;
use std::time::Duration;
use crate::context::ContextBudget;
use crate::resilience::{RetryPolicy, StallPolicy};
use crate::verify::Verification;
#[derive(Debug, Clone)]
pub struct TaskContract {
pub goal: String,
pub file: PathBuf,
pub root: Option<PathBuf>,
pub constraints: Vec<String>,
pub verify: Verification,
pub max_steps: u32,
pub max_duration: Option<Duration>,
pub max_tokens: Option<u64>,
pub max_retries: u32,
#[allow(clippy::doc_markdown)]
pub mcp: Vec<crate::mcp::McpServer>,
#[cfg(feature = "media")]
pub images: Vec<crate::provider::Media>,
pub commit_identity: crate::tools::git::Identity,
pub tools: crate::tools::Toolbox,
pub retry: RetryPolicy,
pub stall: StallPolicy,
pub context: ContextBudget,
pub exec_timeout: Duration,
pub skills: Option<PathBuf>,
}
impl TaskContract {
pub fn new(goal: impl Into<String>, file: impl Into<PathBuf>, verify: Verification) -> Self {
Self {
goal: goal.into(),
file: file.into(),
root: None,
constraints: Vec::new(),
verify,
max_steps: 8,
max_duration: None,
max_tokens: None,
max_retries: 2,
mcp: Vec::new(),
commit_identity: crate::tools::git::Identity::default(),
#[cfg(feature = "media")]
images: Vec::new(),
tools: crate::tools::Toolbox::new(),
context: ContextBudget::default(),
retry: RetryPolicy::default(),
stall: StallPolicy::default(),
exec_timeout: crate::tools::DEFAULT_EXEC_TIMEOUT,
skills: None,
}
}
pub fn workspace(
goal: impl Into<String>,
root: impl Into<PathBuf>,
verify: Verification,
) -> Self {
let root = root.into();
Self {
goal: goal.into(),
file: root.clone(),
root: Some(root),
constraints: Vec::new(),
verify,
max_steps: 12,
max_duration: None,
max_tokens: None,
max_retries: 2,
mcp: Vec::new(),
commit_identity: crate::tools::git::Identity::default(),
#[cfg(feature = "media")]
images: Vec::new(),
tools: crate::tools::Toolbox::new(),
context: ContextBudget::default(),
retry: RetryPolicy::default(),
stall: StallPolicy::default(),
exec_timeout: crate::tools::DEFAULT_EXEC_TIMEOUT,
skills: None,
}
}
pub fn with_mcp<I>(mut self, servers: I) -> Self
where
I: IntoIterator<Item = crate::mcp::McpServer>,
{
self.mcp = servers.into_iter().collect();
self
}
#[cfg(feature = "media")]
#[must_use]
pub fn with_images<I>(mut self, images: I) -> Self
where
I: IntoIterator<Item = crate::provider::Media>,
{
self.images.extend(images);
self
}
#[must_use]
pub fn with_commit_identity(
mut self,
name: impl Into<String>,
email: impl Into<String>,
) -> Self {
self.commit_identity = crate::tools::git::Identity {
name: name.into(),
email: email.into(),
};
self
}
pub fn with_tools(mut self, tools: crate::tools::Toolbox) -> Self {
self.tools = tools;
self
}
pub fn with_skills(mut self, dir: impl Into<PathBuf>) -> Self {
self.skills = Some(dir.into());
self
}
pub(crate) fn discover_skills(&self) -> crate::Result<crate::skills::Skills> {
match &self.skills {
Some(dir) => crate::skills::Skills::discover(dir),
None => Ok(crate::skills::Skills::none()),
}
}
pub fn with_max_steps(mut self, max_steps: u32) -> Self {
self.max_steps = max_steps;
self
}
pub fn with_time_budget(mut self, max_duration: Duration) -> Self {
self.max_duration = Some(max_duration);
self
}
pub fn with_token_budget(mut self, max_tokens: u64) -> Self {
self.max_tokens = Some(max_tokens);
self
}
#[must_use]
pub fn with_exec_timeout(mut self, exec_timeout: Duration) -> Self {
self.exec_timeout = exec_timeout;
self
}
pub fn with_context_budget(mut self, context: ContextBudget) -> Self {
self.context = context;
self
}
pub fn with_retry_policy(mut self, retry: RetryPolicy) -> Self {
self.retry = retry;
self
}
pub fn with_stall_policy(mut self, stall: StallPolicy) -> Self {
self.stall = stall;
self
}
pub fn with_max_retries(mut self, max_retries: u32) -> Self {
self.max_retries = max_retries;
self
}
pub fn with_constraint(mut self, constraint: impl Into<String>) -> Self {
self.constraints.push(constraint.into());
self
}
}