1use std::sync::Arc;
2
3use codei_agent::{AgentError, AgentEvent, AgentLoop, TurnOutcome};
4use codei_config::ResolvedConfig;
5use codei_session::{Session, SessionStore};
6use codei_tools::{handler_for_policy, ApprovalPolicy, ToolContext};
7use tokio::sync::mpsc::{self, UnboundedSender};
8use tokio::task::JoinHandle;
9
10use crate::error::SdkError;
11use crate::runtime::AgentRuntime;
12use crate::session::SessionHandle;
13
14pub fn approval_policy(auto_approve: bool) -> ApprovalPolicy {
15 if auto_approve {
16 ApprovalPolicy::Never
17 } else {
18 ApprovalPolicy::OnDestructive
19 }
20}
21
22pub fn tool_context(config: &Arc<ResolvedConfig>, policy: ApprovalPolicy) -> ToolContext {
23 ToolContext {
24 cwd: config.cwd.clone(),
25 config: Arc::clone(config),
26 approval: Arc::from(handler_for_policy(policy)),
27 }
28}
29
30pub fn agent_loop(
31 runtime: &AgentRuntime,
32 tool_ctx: ToolContext,
33 events: Option<UnboundedSender<AgentEvent>>,
34) -> AgentLoop {
35 AgentLoop::new(
36 Arc::clone(&runtime.config),
37 Arc::clone(&runtime.model),
38 Arc::clone(&runtime.provider),
39 runtime.provider_name.clone(),
40 tool_ctx,
41 runtime.mcp.clone(),
42 events,
43 )
44}
45
46pub async fn run_turn(
47 agent: &AgentLoop,
48 session: &mut Session,
49 prompt: &str,
50 store: &SessionStore,
51) -> Result<TurnOutcome, AgentError> {
52 agent.run_turn(session, prompt, store).await
53}
54
55pub async fn run_turn_with_events<F>(
56 runtime: &AgentRuntime,
57 session: &mut Session,
58 prompt: &str,
59 policy: ApprovalPolicy,
60 mut on_event: F,
61) -> Result<TurnOutcome, SdkError>
62where
63 F: FnMut(AgentEvent),
64{
65 let (tx, mut rx) = mpsc::unbounded_channel();
66 let agent = agent_loop(runtime, tool_context(&runtime.config, policy), Some(tx));
67 let prompt = prompt.to_string();
68 let store = runtime.store.clone();
69
70 let mut agent_task = Box::pin(async {
71 agent
72 .run_turn(session, &prompt, store.as_ref())
73 .await
74 .map_err(SdkError::Agent)
75 });
76
77 let mut outcome = TurnOutcome::default();
78 loop {
79 tokio::select! {
80 event = rx.recv() => {
81 match event {
82 Some(AgentEvent::TurnComplete { usage }) => {
83 outcome.usage = usage;
84 on_event(AgentEvent::TurnComplete { usage: outcome.usage });
85 break;
86 }
87 Some(other) => on_event(other),
88 None => break,
89 }
90 }
91 result = &mut agent_task => {
92 outcome = result?;
93 break;
94 }
95 }
96 }
97
98 Ok(outcome)
99}
100
101pub fn spawn_turn(
102 handle: Arc<SessionHandle>,
103 prompt: String,
104 policy: ApprovalPolicy,
105 tx: UnboundedSender<AgentEvent>,
106) -> JoinHandle<()> {
107 tokio::spawn(async move {
108 let _guard = handle.turn_lock.lock().await;
109 let tool_ctx = tool_context(&handle.runtime.config, policy);
110 let agent = agent_loop(&handle.runtime, tool_ctx, Some(tx.clone()));
111 let session = Arc::clone(&handle.session);
112 let store = Arc::clone(&handle.runtime.store);
113 let mut session = session.write().await;
114 if let Err(err) = agent.run_turn(&mut session, &prompt, store.as_ref()).await {
115 let _ = tx.send(AgentEvent::Error {
116 message: err.to_string(),
117 });
118 }
119 })
120}