use crate::protocol::message::{
AgentInfo, AgentList, ClientMessage, ConversationHistory, ConversationInfo, ConversationList,
CreateAgentMsg, DaemonStats, DeleteAgentMsg, DeleteConversationMsg, DeleteMcpMsg,
DeleteProviderMsg, ErrorMsg, GetAgentMsg, GetConversationHistoryMsg, GetStats,
InstallPluginMsg, ListAgentsMsg, ListConversationsMsg, ListMcpsMsg, ListModelsMsg,
ListPluginsMsg, ListProviderPresetsMsg, ListProvidersMsg, ListSkillsMsg, ListSubscriptionsMsg,
McpInfo, McpList, ModelInfo, ModelList, Ping, PluginEvent, PluginInfo, PluginList,
PluginSearchList, ProviderInfo, ProviderList, ProviderPresetInfo, ProviderPresetList,
PublishEventMsg, RenameAgentMsg, SearchPluginsMsg, SendMsg, SendResponse, ServerMessage,
ServiceLogOutput, ServiceLogsMsg, SetActiveModelMsg, SetProviderMsg, SkillInfo, SkillList,
StartServiceMsg, StopServiceMsg, StreamEvent, StreamMsg, SubscribeEventMsg, SubscriptionInfo,
SubscriptionList, UninstallPluginMsg, UnsubscribeEventMsg, UpdateAgentMsg, UpsertMcpMsg,
client_message, plugin_event, server_message, stream_event,
};
use anyhow::Result;
use futures_core::Stream;
use futures_util::StreamExt;
pub trait Client: Send {
fn request(
&mut self,
msg: ClientMessage,
) -> impl std::future::Future<Output = Result<ServerMessage>> + Send;
fn request_stream(
&mut self,
msg: ClientMessage,
) -> impl Stream<Item = Result<ServerMessage>> + Send + '_;
fn send(
&mut self,
req: SendMsg,
) -> impl std::future::Future<Output = Result<SendResponse>> + Send {
async move { SendResponse::try_from(self.request(req.into()).await?) }
}
fn stream(
&mut self,
req: StreamMsg,
) -> impl Stream<Item = Result<stream_event::Event>> + Send + '_ {
self.request_stream(req.into())
.take_while(|r| {
std::future::ready(!matches!(
r,
Ok(ServerMessage {
msg: Some(server_message::Msg::Stream(StreamEvent {
event: Some(stream_event::Event::End(_))
}))
})
))
})
.map(|r| r.and_then(stream_event::Event::try_from))
}
fn ping(&mut self) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::Ping(Ping {})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn get_stats(&mut self) -> impl std::future::Future<Output = Result<DaemonStats>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::GetStats(GetStats {})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Stats(stats)),
} => Ok(stats),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn list_agents(&mut self) -> impl std::future::Future<Output = Result<Vec<AgentInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListAgents(ListAgentsMsg {})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::AgentList(AgentList { agents })),
} => Ok(agents),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn get_agent(
&mut self,
name: String,
) -> impl std::future::Future<Output = Result<AgentInfo>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::GetAgent(GetAgentMsg { name })),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::AgentInfo(info)),
} => Ok(info),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn create_agent(
&mut self,
name: String,
config: String,
prompt: String,
) -> impl std::future::Future<Output = Result<AgentInfo>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::CreateAgent(CreateAgentMsg {
name,
config,
prompt,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::AgentInfo(info)),
} => Ok(info),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn update_agent(
&mut self,
name: String,
config: String,
prompt: String,
) -> impl std::future::Future<Output = Result<AgentInfo>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::UpdateAgent(UpdateAgentMsg {
name,
config,
prompt,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::AgentInfo(info)),
} => Ok(info),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn delete_agent(
&mut self,
name: String,
) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::DeleteAgent(DeleteAgentMsg { name })),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn rename_agent(
&mut self,
old_name: String,
new_name: String,
) -> impl std::future::Future<Output = Result<AgentInfo>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::RenameAgent(RenameAgentMsg {
old_name,
new_name,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::AgentInfo(info)),
} => Ok(info),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn list_providers(
&mut self,
) -> impl std::future::Future<Output = Result<Vec<ProviderInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListProviders(ListProvidersMsg {})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::ProviderList(ProviderList { providers })),
} => Ok(providers),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn install_plugin(
&mut self,
plugin: String,
branch: String,
path: String,
force: bool,
) -> impl Stream<Item = Result<plugin_event::Event>> + Send + '_ {
self.request_stream(ClientMessage {
msg: Some(client_message::Msg::InstallPlugin(InstallPluginMsg {
plugin,
branch,
path,
force,
})),
})
.take_while(|r| {
std::future::ready(!matches!(
r,
Ok(ServerMessage {
msg: Some(server_message::Msg::PluginEvent(PluginEvent {
event: Some(plugin_event::Event::Done(d))
}))
}) if d.error.is_empty()
))
})
.map(|r| r.and_then(plugin_event::Event::try_from))
}
fn uninstall_plugin(
&mut self,
plugin: String,
) -> impl Stream<Item = Result<plugin_event::Event>> + Send + '_ {
self.request_stream(ClientMessage {
msg: Some(client_message::Msg::UninstallPlugin(UninstallPluginMsg {
plugin,
})),
})
.take_while(|r| {
std::future::ready(!matches!(
r,
Ok(ServerMessage {
msg: Some(server_message::Msg::PluginEvent(PluginEvent {
event: Some(plugin_event::Event::Done(d))
}))
}) if d.error.is_empty()
))
})
.map(|r| r.and_then(plugin_event::Event::try_from))
}
fn list_plugins(
&mut self,
) -> impl std::future::Future<Output = Result<Vec<PluginInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListPlugins(ListPluginsMsg {})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::PluginList(PluginList { plugins })),
} => Ok(plugins),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn search_plugins(
&mut self,
query: String,
) -> impl std::future::Future<Output = Result<Vec<PluginInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::SearchPlugins(SearchPluginsMsg {
query,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::PluginSearchList(PluginSearchList { plugins })),
} => Ok(plugins),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn list_conversations(
&mut self,
agent: String,
sender: String,
) -> impl std::future::Future<Output = Result<Vec<ConversationInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListConversations(
ListConversationsMsg { agent, sender },
)),
})
.await?
{
ServerMessage {
msg:
Some(server_message::Msg::ConversationList(ConversationList { conversations })),
} => Ok(conversations),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn get_conversation_history(
&mut self,
file_path: String,
) -> impl std::future::Future<Output = Result<ConversationHistory>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::GetConversationHistory(
GetConversationHistoryMsg { file_path },
)),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::ConversationHistory(history)),
} => Ok(history),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn delete_conversation(
&mut self,
file_path: String,
) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::DeleteConversation(
DeleteConversationMsg { file_path },
)),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn list_mcps(&mut self) -> impl std::future::Future<Output = Result<Vec<McpInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListMcps(ListMcpsMsg {})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::McpList(McpList { mcps })),
} => Ok(mcps),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => anyhow::bail!("server error ({code}): {message}"),
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn upsert_mcp(
&mut self,
config: String,
) -> impl std::future::Future<Output = Result<McpInfo>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::UpsertMcp(UpsertMcpMsg { config })),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::McpInfo(info)),
} => Ok(info),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => anyhow::bail!("server error ({code}): {message}"),
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn delete_mcp(&mut self, name: String) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::DeleteMcp(DeleteMcpMsg { name })),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => anyhow::bail!("server error ({code}): {message}"),
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn set_provider(
&mut self,
name: String,
config: String,
) -> impl std::future::Future<Output = Result<ProviderInfo>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::SetProvider(SetProviderMsg {
name,
config,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::ProviderList(ProviderList { providers })),
} => providers
.into_iter()
.next()
.ok_or_else(|| anyhow::anyhow!("empty provider list in response")),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => anyhow::bail!("server error ({code}): {message}"),
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn delete_provider(
&mut self,
name: String,
) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::DeleteProvider(DeleteProviderMsg {
name,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => anyhow::bail!("server error ({code}): {message}"),
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn set_active_model(
&mut self,
model: String,
) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::SetActiveModel(SetActiveModelMsg {
model,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => anyhow::bail!("server error ({code}): {message}"),
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn list_provider_presets(
&mut self,
) -> impl std::future::Future<Output = Result<Vec<ProviderPresetInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListProviderPresets(
ListProviderPresetsMsg {},
)),
})
.await?
{
ServerMessage {
msg:
Some(server_message::Msg::ProviderPresetList(ProviderPresetList { presets })),
} => Ok(presets),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => anyhow::bail!("server error ({code}): {message}"),
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn start_service(
&mut self,
name: String,
force: bool,
) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::StartService(StartServiceMsg {
name,
force,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn stop_service(
&mut self,
name: String,
) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::StopService(StopServiceMsg { name })),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn list_skills(&mut self) -> impl std::future::Future<Output = Result<Vec<SkillInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListSkills(ListSkillsMsg {})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::SkillList(SkillList { skills })),
} => Ok(skills),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn list_models(&mut self) -> impl std::future::Future<Output = Result<Vec<ModelInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListModels(ListModelsMsg {})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::ModelList(ModelList { models })),
} => Ok(models),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn service_logs(
&mut self,
name: String,
lines: u32,
) -> impl std::future::Future<Output = Result<String>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ServiceLogs(ServiceLogsMsg {
name,
lines,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::ServiceLogOutput(ServiceLogOutput { content })),
} => Ok(content),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn subscribe_event(
&mut self,
source: String,
target_agent: String,
once: bool,
) -> impl std::future::Future<Output = Result<SubscriptionInfo>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::SubscribeEvent(SubscribeEventMsg {
source,
target_agent,
once,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::SubscriptionInfo(info)),
} => Ok(info),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn unsubscribe_event(
&mut self,
id: u64,
) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::UnsubscribeEvent(UnsubscribeEventMsg {
id,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn list_subscriptions(
&mut self,
) -> impl std::future::Future<Output = Result<Vec<SubscriptionInfo>>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::ListSubscriptions(
ListSubscriptionsMsg {},
)),
})
.await?
{
ServerMessage {
msg:
Some(server_message::Msg::SubscriptionList(SubscriptionList { subscriptions })),
} => Ok(subscriptions),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
fn publish_event(
&mut self,
source: String,
payload: String,
) -> impl std::future::Future<Output = Result<()>> + Send {
async move {
match self
.request(ClientMessage {
msg: Some(client_message::Msg::PublishEvent(PublishEventMsg {
source,
payload,
})),
})
.await?
{
ServerMessage {
msg: Some(server_message::Msg::Pong(_)),
} => Ok(()),
ServerMessage {
msg: Some(server_message::Msg::Error(ErrorMsg { code, message })),
} => {
anyhow::bail!("server error ({code}): {message}")
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
}
}
}