use std::time::Duration;
use funera_core::security::audit::AuditEvent;
use funera_core::security::policy::{ShellPolicy, ToolPolicy};
use funera_orchestrate::{Agent, AgentRuntime, ApprovalHandle, DeepSeekProvider};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let shell_policy = ShellPolicy::with_allowed(vec!["git".into(), "cargo".into()]);
let tool_policy = ToolPolicy {
allowed_tools: Some(
["read", "write", "edit"]
.into_iter()
.map(String::from)
.collect(),
),
denied_tools: ["shell".into()].into_iter().collect(),
shell_policy: Some(shell_policy),
max_args_size: 1024 * 1024,
max_timeout_secs: 60.0,
..Default::default()
};
let (approval_tx, mut approval_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
let api_key = std::env::var("OPENAI_API_KEY")?;
let runtime = AgentRuntime::<DeepSeekProvider>::builder()
.api_key(api_key)
.model("deepseek-v4-flash")
.with_builtin_tools()
.with_tool_policy(tool_policy)
.on_approval_required(move |call_id, tool, reason| {
eprintln!("[approval] \"{tool}\" needs approval: {reason}");
eprintln!("[approval] auto-approving");
let _ = approval_tx.send(call_id.to_string());
})
.with_approval_timeout(Duration::from_secs(30))
.build()?;
let approver: ApprovalHandle = runtime.approval_handle();
tokio::spawn(async move {
while let Some(call_id) = approval_rx.recv().await {
if let Err(e) = approver.approve_tool_call(&call_id, true).await {
eprintln!("[approval] failed: {e}");
}
}
});
let mut audit_rx = runtime.subscribe_audit().await;
tokio::spawn(async move {
while let Ok(event) = audit_rx.recv().await {
match event {
AuditEvent::ToolExecuted {
tool_name,
duration_ms,
success,
..
} => {
let status = if success { "OK" } else { "ERR" };
eprintln!("[audit] {tool_name} [{status}] {duration_ms}ms");
}
AuditEvent::ToolDenied {
tool_name, reason, ..
} => {
eprintln!("[audit] {tool_name} denied: {reason}");
}
other => eprintln!("[audit] {other:?}"),
}
}
});
let agent = Agent::builder()
.system_prompt("You can use read/write/edit tools.")
.build();
let resp = agent
.fire("List all .rs files in the current directory", &runtime)
.await?;
println!("Agent: {}", resp.content);
Ok(())
}