use std::sync::Arc;
use agentyk_core::cancellation::CancellationToken;
use agentyk_core::capability::{CommandContext, CommandDescriptor};
use agentyk_core::controls::TurnControls;
use agentyk_core::error::Result;
use agentyk_core::event::Event;
use agentyk_core::event_log::EventLog;
use agentyk_core::executor::{TurnHost, TurnResult};
use agentyk_core::id::SessionId;
use agentyk_core::message::Message;
use agentyk_core::replay::messages_from_events;
use agentyk_core::tool::ToolOutput;
use crate::agent::Agent;
#[derive(Default)]
pub struct RunOptions {
pub cancellation: CancellationToken,
pub controls: TurnControls,
}
pub struct Session {
agent: Agent,
id: SessionId,
log: Arc<dyn EventLog>,
messages: Vec<Message>,
}
impl Session {
pub(crate) fn new(agent: Agent, log: Arc<dyn EventLog>) -> Self {
Self {
agent,
id: SessionId::new(),
log,
messages: Vec::new(),
}
}
pub(crate) async fn resume(
agent: Agent,
log: Arc<dyn EventLog>,
session_id: SessionId,
) -> Result<Self> {
let events = log.read(session_id).await?;
let messages = messages_from_events(&events);
Ok(Self {
agent,
id: session_id,
log,
messages,
})
}
pub fn id(&self) -> SessionId {
self.id
}
pub fn messages(&self) -> &[Message] {
&self.messages
}
pub async fn events(&self) -> Result<Vec<Event>> {
self.log.read(self.id).await
}
pub fn commands(&self) -> Vec<CommandDescriptor> {
self.agent
.inner
.capabilities
.iter()
.flat_map(|capability| capability.commands())
.collect()
}
pub async fn execute_command(&self, name: &str, args: &str) -> Result<ToolOutput> {
let context = CommandContext {
session_id: self.id,
};
for capability in &self.agent.inner.capabilities {
if capability.commands().iter().any(|c| c.name == name)
&& let Some(output) = capability.execute_command(name, args, &context).await
{
return Ok(output);
}
}
Ok(ToolOutput::error(format!("unknown command `/{name}`")))
}
pub async fn run(&mut self, input: impl Into<String>) -> Result<TurnResult> {
self.run_with_options(input, RunOptions::default()).await
}
pub async fn run_cancellable(
&mut self,
input: impl Into<String>,
token: CancellationToken,
) -> Result<TurnResult> {
self.run_with_options(
input,
RunOptions {
cancellation: token,
..Default::default()
},
)
.await
}
pub async fn run_controlled(
&mut self,
input: impl Into<String>,
controls: TurnControls,
) -> Result<TurnResult> {
self.run_with_options(
input,
RunOptions {
controls,
..Default::default()
},
)
.await
}
pub async fn run_with_options(
&mut self,
input: impl Into<String>,
options: RunOptions,
) -> Result<TurnResult> {
let agent = self.agent.inner.clone();
let executor = agent.executor.clone();
let effective_model = options.controls.resolve(&agent.model);
let mut host = TurnHost {
session_id: self.id,
system_prompt: &agent.system_prompt,
model: &effective_model,
capabilities: &agent.capabilities,
drivers: &agent.drivers,
listeners: &agent.listeners,
max_iterations: agent.max_iterations,
log: self.log.as_ref(),
messages: &mut self.messages,
cancellation: options.cancellation,
pre_tool_hooks: &agent.pre_tool_hooks,
post_tool_hooks: &agent.post_tool_hooks,
budget_checker: agent.budget_checker.clone(),
context_assembler: agent.context_assembler.as_ref(),
extensions: &agent.extensions,
};
executor.run_turn(&mut host, Message::user(input)).await
}
}