use crate::bridge::poll_config_defaults::PollIntervalConfig;
use crate::bridge::repl_bridge_handle::{BridgeControlRequest, BridgeControlResponse, BridgeState};
use crate::bridge::repl_bridge_transport::ReplBridgeTransport;
use crate::bridge::SDKMessage;
use crate::error::AgentError;
use std::pin::Pin;
use std::sync::Arc;
use tokio::sync::RwLock;
type BoxFuture<T> = Pin<Box<dyn std::future::Future<Output = T> + Send>>;
#[derive(Clone)]
pub struct ReplBridgeOptions {
pub dir: String,
pub machine_name: String,
pub branch: String,
pub git_repo_url: Option<String>,
pub title: String,
pub base_url: String,
pub session_ingress_url: String,
pub worker_type: String,
pub get_access_token: Arc<dyn Fn() -> Option<String> + Send + Sync>,
pub create_session: Arc<
dyn Fn(
String,
String,
Option<String>,
String,
) -> BoxFuture<Result<Option<String>, AgentError>>
+ Send
+ Sync,
>,
pub archive_session: Arc<dyn Fn(String) -> BoxFuture<Result<(), AgentError>> + Send + Sync>,
pub get_current_title: Option<Arc<dyn Fn() -> String + Send + Sync>>,
pub to_sdk_messages:
Option<Arc<dyn Fn(Vec<crate::types::Message>) -> Vec<SDKMessage> + Send + Sync>>,
pub on_auth_401:
Option<Arc<dyn Fn(String) -> BoxFuture<Result<bool, AgentError>> + Send + Sync>>,
pub get_poll_interval_config: Option<Arc<dyn Fn() -> PollIntervalConfig + Send + Sync>>,
pub initial_history_cap: Option<u32>,
pub initial_messages: Option<Vec<crate::types::Message>>,
pub previously_flushed_uuids:
Option<Arc<dyn Fn() -> std::collections::HashSet<String> + Send + Sync>>,
pub on_inbound_message: Option<Arc<dyn Fn(SDKMessage) + Send + Sync>>,
pub on_permission_response: Option<Arc<dyn Fn(BridgeControlResponse) + Send + Sync>>,
pub on_interrupt: Option<Arc<dyn Fn() + Send + Sync>>,
pub on_set_model: Option<Arc<dyn Fn(Option<String>) + Send + Sync>>,
pub on_set_max_thinking_tokens: Option<Arc<dyn Fn(Option<u32>) + Send + Sync>>,
pub on_set_permission_mode:
Option<Arc<dyn Fn(crate::permission::PermissionMode) -> Result<(), String> + Send + Sync>>,
pub on_state_change: Option<Arc<dyn Fn(BridgeState, Option<String>) + Send + Sync>>,
pub on_user_message: Option<Arc<dyn Fn(String, String) -> bool + Send + Sync>>,
pub perpetual: Option<bool>,
pub initial_sse_sequence_num: Option<u64>,
}
impl Default for ReplBridgeOptions {
fn default() -> Self {
Self {
dir: std::env::current_dir()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default(),
machine_name: "unknown".to_string(),
branch: String::new(),
git_repo_url: None,
title: String::new(),
base_url: String::new(),
session_ingress_url: String::new(),
worker_type: "repl".to_string(),
get_access_token: Arc::new(|| None),
create_session: Arc::new(|_, _, _, _| Box::pin(async { Ok(None) })),
archive_session: Arc::new(|_| Box::pin(async { Ok(()) })),
get_current_title: None,
to_sdk_messages: None,
on_auth_401: None,
get_poll_interval_config: None,
initial_history_cap: None,
initial_messages: None,
previously_flushed_uuids: None,
on_inbound_message: None,
on_permission_response: None,
on_interrupt: None,
on_set_model: None,
on_set_max_thinking_tokens: None,
on_set_permission_mode: None,
on_state_change: None,
on_user_message: None,
perpetual: None,
initial_sse_sequence_num: None,
}
}
}
pub struct ReplBridge {
pub session_id: RwLock<String>,
pub environment_id: RwLock<String>,
pub session_ingress_url: String,
pub inner: RwLock<Option<Arc<dyn ReplBridgeHandleInner>>>,
}
pub trait ReplBridgeHandleInner: Send + Sync {
fn write_messages(&self, messages: Vec<SDKMessage>);
fn write_sdk_messages(&self, messages: Vec<SDKMessage>);
fn send_control_request(&self, request: BridgeControlRequest);
fn send_control_response(&self, response: BridgeControlResponse);
fn send_control_cancel_request(&self, request_id: &str);
fn send_result(&self);
fn teardown(&self) -> BoxFuture<()>;
}
impl ReplBridge {
pub fn new(session_id: String, environment_id: String, session_ingress_url: String) -> Self {
Self {
session_id: RwLock::new(session_id),
environment_id: RwLock::new(environment_id),
session_ingress_url,
inner: RwLock::new(None),
}
}
pub async fn bridge_session_id(&self) -> String {
self.session_id.read().await.clone()
}
pub async fn environment_id(&self) -> String {
self.environment_id.read().await.clone()
}
pub fn session_ingress_url(&self) -> &str {
&self.session_ingress_url
}
pub fn write_messages(&self, messages: Vec<SDKMessage>) {
if let Some(inner) = self.inner.blocking_read().as_ref() {
inner.write_messages(messages);
}
}
pub fn write_sdk_messages(&self, messages: Vec<SDKMessage>) {
if let Some(inner) = self.inner.blocking_read().as_ref() {
inner.write_sdk_messages(messages);
}
}
pub fn send_control_request(&self, request: BridgeControlRequest) {
if let Some(inner) = self.inner.blocking_read().as_ref() {
inner.send_control_request(request);
}
}
pub fn send_control_response(&self, response: BridgeControlResponse) {
if let Some(inner) = self.inner.blocking_read().as_ref() {
inner.send_control_response(response);
}
}
pub fn send_control_cancel_request(&self, request_id: &str) {
if let Some(inner) = self.inner.blocking_read().as_ref() {
inner.send_control_cancel_request(request_id);
}
}
pub fn send_result(&self) {
if let Some(inner) = self.inner.blocking_read().as_ref() {
inner.send_result();
}
}
pub async fn teardown(&self) {
if let Some(inner) = self.inner.write().await.take() {
inner.teardown().await;
}
}
}
pub async fn init_repl_bridge(
_options: ReplBridgeOptions,
) -> Result<Option<ReplBridge>, AgentError> {
Ok(None)
}
static REPL_BRIDGE_HANDLE: std::sync::OnceLock<ReplBridge> = std::sync::OnceLock::new();
pub fn set_repl_bridge_handle(bridge: Option<ReplBridge>) {
let _ = REPL_BRIDGE_HANDLE.set(
bridge.unwrap_or_else(|| ReplBridge::new(String::new(), String::new(), String::new())),
);
}
pub fn get_repl_bridge_handle() -> Option<&'static ReplBridge> {
REPL_BRIDGE_HANDLE.get()
}