pub mod agents;
pub mod config;
pub mod error;
pub mod event;
pub mod logger;
pub mod models;
pub mod normalize;
pub mod process;
pub mod registry;
pub mod runner;
pub mod settings;
pub use config::{AgentKind, OutputFormat, PermissionMode, TaskConfig, TaskConfigBuilder};
pub use error::{Error, Result};
pub use event::{Event, UsageData};
pub use models::{ModelEntry, ModelRegistry, ModelResolution};
pub use normalize::NormalizeConfig;
pub use process::StreamHandle;
pub use runner::{AgentCapabilities, AgentRunner, EventStream};
pub use tokio_util::sync::CancellationToken;
pub async fn run_task(config: &TaskConfig) -> Result<EventStream> {
let handle = run_task_with_cancel(config, None).await?;
Ok(handle.stream)
}
pub async fn run_task_with_cancel(
config: &TaskConfig,
cancel_token: Option<tokio_util::sync::CancellationToken>,
) -> Result<StreamHandle> {
let runner = agents::create_runner(config.agent);
if config.binary_path.is_none() && !runner.is_available() {
return Err(Error::BinaryNotFound {
agent: config.agent.display_name().to_string(),
binary: config.agent.default_binary().to_string(),
});
}
let mut handle = runner.run(config, cancel_token).await?;
let norm_config = NormalizeConfig {
cwd: config
.cwd
.as_ref()
.map(|p| p.display().to_string())
.or_else(|| std::env::current_dir().ok().map(|p| p.display().to_string())),
model: config.model.clone(),
prompt: Some(config.prompt.clone()),
};
handle.stream = normalize::normalize_stream(handle.stream, norm_config);
Ok(handle)
}
pub fn available_agents() -> Vec<AgentKind> {
use AgentKind::*;
[Claude, OpenCode, Codex, Cursor]
.into_iter()
.filter(|kind| {
let runner = agents::create_runner(*kind);
runner.is_available()
})
.collect()
}