use agent_client_protocol::{
Agent, Client, ConnectionTo, ConnectTo,
role::{HasPeer, Role},
schema::{InitializeRequest, InitializeResponse, ProtocolVersion},
};
use agent_client_protocol_conductor::{AgentOnly, ConductorImpl, McpBridgeMode};
use schemars::JsonSchema;
use serde::de::DeserializeOwned;
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
use tracing::{debug, info, instrument};
use std::sync::Arc;
use crate::think::ThinkObserver;
use crate::ThinkBuilder;
pub struct Determinishtic<R: Role = Agent>
where
R: HasPeer<Agent>,
{
cx: ConnectionTo<R>,
task: Option<JoinHandle<Result<(), agent_client_protocol::Error>>>,
observer: Option<Arc<dyn ThinkObserver>>,
}
impl<R: Role> Determinishtic<R>
where
R: HasPeer<Agent>,
{
pub fn from_connection(cx: ConnectionTo<R>) -> Self {
Self { cx, task: None, observer: None }
}
pub fn think<'bound, Output>(&self) -> ThinkBuilder<'bound, Output, R>
where
Output: Send + JsonSchema + DeserializeOwned + 'static,
{
ThinkBuilder::new(self.cx.clone(), self.observer.clone())
}
pub fn set_observer(&mut self, observer: Arc<dyn ThinkObserver>) {
self.observer = Some(observer);
}
}
impl Determinishtic<Agent> {
#[instrument(name = "Determinishtic::new", skip_all)]
pub async fn new(
component: impl ConnectTo<Client> + 'static,
) -> Result<Self, crate::Error> {
debug!("spawning connection task");
let (tx, rx) = oneshot::channel();
let task = tokio::spawn(async move {
Client
.builder()
.with_spawned(|cx| async move {
let _ = tx.send(cx);
std::future::pending::<Result<(), agent_client_protocol::Error>>().await
})
.connect_to(ConductorImpl::new_agent(
"determinishtic-conductor",
AgentOnly(component),
McpBridgeMode::default(),
))
.await
});
let cx = rx.await.map_err(|_| crate::Error::ConnectionClosed)?;
info!("connection established");
let InitializeResponse { .. } = cx
.send_request(InitializeRequest::new(ProtocolVersion::LATEST))
.block_task()
.await?;
Ok(Self { cx, task: Some(task), observer: None })
}
}
impl<R: Role> Drop for Determinishtic<R>
where
R: HasPeer<Agent>,
{
fn drop(&mut self) {
if let Some(ref task) = self.task {
task.abort();
}
}
}