pub mod ui_protocol;
#[cfg(feature = "runtime")]
pub mod common;
#[cfg(feature = "runtime")]
pub mod effects;
#[cfg(feature = "runtime")]
pub mod mcp;
#[cfg(feature = "runtime")]
pub mod plugin_manager;
#[cfg(feature = "runtime")]
pub mod domain;
#[cfg(feature = "runtime")]
pub mod error;
#[cfg(feature = "runtime")]
pub mod ffi;
#[cfg(feature = "runtime")]
pub mod hooks;
#[cfg(feature = "runtime")]
pub mod logging;
#[cfg(feature = "runtime")]
pub mod protocol;
#[cfg(feature = "runtime")]
pub mod util;
#[cfg(feature = "runtime")]
pub mod handlers;
#[cfg(feature = "runtime")]
pub mod layout;
#[cfg(feature = "runtime")]
pub mod services;
#[cfg(feature = "runtime")]
pub use common::{ErrorCode, ErrorContext, HostError, HostResult};
#[cfg(feature = "runtime")]
pub use effects::{EffectError, EffectHandler, EffectRegistry, EffectResult};
#[cfg(feature = "runtime")]
pub use plugin_manager::PluginManager;
#[cfg(feature = "runtime")]
pub use domain::{
AbsolutePath, DomainError, GithubOwner, GithubRepo, IssueNumber, PathError, Role, SessionId,
ToolName, ToolPermission,
};
#[cfg(feature = "runtime")]
pub use error::{ExoMonadError, Result};
#[cfg(feature = "runtime")]
pub use ffi::{
ErrorCode as FFIErrorCode, ErrorContext as FFIErrorContext, FFIBoundary, FFIError, FFIResult,
};
#[cfg(feature = "runtime")]
pub use hooks::HookConfig;
#[cfg(feature = "runtime")]
pub use logging::{init_logging, init_logging_with_default};
#[cfg(feature = "runtime")]
pub use protocol::{
ClaudePreToolUseOutput, ClaudeStopHookOutput, GeminiStopHookOutput, HookEventType, HookInput,
HookSpecificOutput, InternalStopHookOutput, PermissionDecision, Runtime as ProtocolRuntime,
StopDecision,
};
#[cfg(feature = "runtime")]
pub use util::{build_prompt, find_exomonad_binary, shell_quote};
#[cfg(feature = "runtime")]
pub use handlers::{
AgentHandler, CopilotHandler, FilePRHandler, FsHandler, GitHandler, GitHubHandler, LogHandler,
PopupHandler,
};
#[cfg(feature = "runtime")]
pub use services::{Services, ValidatedServices};
#[cfg(feature = "runtime")]
pub mod prelude {
pub use crate::handlers::*;
pub use crate::services::{Services, ValidatedServices};
}
#[cfg(feature = "runtime")]
use std::path::PathBuf;
#[cfg(feature = "runtime")]
use std::sync::Arc;
#[cfg(feature = "runtime")]
pub struct RuntimeBuilder {
registry: EffectRegistry,
wasm_bytes: Option<Vec<u8>>,
}
#[cfg(feature = "runtime")]
impl RuntimeBuilder {
pub fn new() -> Self {
Self {
registry: EffectRegistry::new(),
wasm_bytes: None,
}
}
pub fn with_effect_handler(mut self, handler: impl EffectHandler + 'static) -> Self {
self.registry.register_owned(handler);
self
}
pub fn with_effect_handler_arc(mut self, handler: Arc<dyn EffectHandler>) -> Self {
self.registry.register(handler);
self
}
pub fn with_wasm_bytes(mut self, bytes: Vec<u8>) -> Self {
self.wasm_bytes = Some(bytes);
self
}
pub fn registry(&self) -> &EffectRegistry {
&self.registry
}
pub fn into_registry(self) -> EffectRegistry {
self.registry
}
pub async fn build(self) -> anyhow::Result<Runtime> {
let wasm_bytes = self
.wasm_bytes
.ok_or_else(|| anyhow::anyhow!("WASM bytes not set"))?;
let registry = Arc::new(self.registry);
let plugin_manager = PluginManager::new(&wasm_bytes, registry.clone()).await?;
Ok(Runtime {
plugin_manager,
registry,
})
}
}
#[cfg(feature = "runtime")]
impl Default for RuntimeBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "runtime")]
pub struct Runtime {
pub plugin_manager: PluginManager,
pub registry: Arc<EffectRegistry>,
}
#[cfg(feature = "runtime")]
impl Runtime {
pub fn plugin_manager(&self) -> &PluginManager {
&self.plugin_manager
}
pub fn registry(&self) -> &EffectRegistry {
&self.registry
}
pub async fn dispatch_effect(
&self,
effect_type: &str,
payload: &[u8],
) -> EffectResult<Vec<u8>> {
self.registry.dispatch(effect_type, payload).await
}
pub fn into_mcp_state(self, project_dir: PathBuf) -> mcp::McpState {
mcp::McpState {
project_dir,
plugin: Arc::new(self.plugin_manager),
}
}
}
#[cfg(feature = "runtime")]
pub fn register_builtin_handlers(
builder: RuntimeBuilder,
services: &Arc<ValidatedServices>,
) -> RuntimeBuilder {
let mut builder = builder;
builder = builder.with_effect_handler(handlers::GitHandler::new(services.git().clone()));
if let Some(github) = services.github() {
builder = builder.with_effect_handler(handlers::GitHubHandler::new(github.clone()));
}
builder = builder.with_effect_handler(handlers::LogHandler::new());
builder = builder.with_effect_handler(handlers::AgentHandler::new(
services.agent_control().clone(),
));
builder = builder.with_effect_handler(handlers::FsHandler::new(services.filesystem().clone()));
if let Some(session) = services.zellij_session() {
builder = builder.with_effect_handler(handlers::PopupHandler::new(session.to_string()));
}
builder = builder.with_effect_handler(handlers::FilePRHandler::new());
builder = builder.with_effect_handler(handlers::CopilotHandler::new());
builder
}