1use std::path::Path;
2use std::sync::Arc;
3
4use async_trait::async_trait;
5use harn_serve::{AcpProfileConfig, AcpRuntimeConfigurator, AcpServerConfig, AuthPolicy};
6use tokio::sync::mpsc;
7
8struct CliAcpRuntimeConfigurator;
9
10#[async_trait(?Send)]
11impl AcpRuntimeConfigurator for CliAcpRuntimeConfigurator {
12 async fn configure(
13 &self,
14 vm: &mut harn_vm::Vm,
15 source_path: Option<&Path>,
16 ) -> Result<(), String> {
17 #[cfg(feature = "hostlib")]
23 {
24 let _ = harn_hostlib::install_default(vm);
25 }
26
27 #[cfg(feature = "guard-neural")]
32 {
33 let base_dir = source_path
34 .and_then(std::path::Path::parent)
35 .unwrap_or_else(|| std::path::Path::new("."))
36 .to_path_buf();
37 harn_vm::security::set_injection_classifier_loader(Box::new(move |selector| {
38 harn_guard::load_classifier(&base_dir, selector)
39 }));
40 }
41
42 let Some(path) = source_path else {
43 return Ok(());
44 };
45
46 let extensions = crate::package::load_runtime_extensions(path);
47 crate::package::install_runtime_extensions(&extensions);
48 crate::package::install_manifest_triggers(vm, &extensions)
49 .await
50 .map_err(|error| format!("failed to install manifest triggers: {error}"))?;
51 crate::package::install_manifest_hooks(vm, &extensions)
52 .await
53 .map_err(|error| format!("failed to install manifest hooks: {error}"))?;
54 Ok(())
55 }
56}
57
58pub(crate) fn server_config(pipeline: Option<String>, auth_policy: AuthPolicy) -> AcpServerConfig {
59 let extensions = pipeline
60 .as_deref()
61 .map(Path::new)
62 .map(crate::package::load_runtime_extensions)
63 .unwrap_or_default();
64 AcpServerConfig::new(pipeline)
65 .with_auth_policy(auth_policy)
66 .with_runtime_configurator(Arc::new(CliAcpRuntimeConfigurator))
67 .with_llm_overrides(extensions.llm, extensions.capabilities)
68}
69
70pub(crate) fn ensure_acp_event_log(pipeline: Option<&str>) {
71 if harn_vm::event_log::active_event_log().is_none() {
72 let base_dir = pipeline
73 .map(Path::new)
74 .and_then(Path::parent)
75 .unwrap_or_else(|| Path::new("."));
76 if let Err(error) = harn_vm::event_log::install_default_for_base_dir(base_dir) {
77 eprintln!(
78 "[harn] ACP session replay disabled: failed to initialize EventLog for {}: {error}",
79 base_dir.display()
80 );
81 }
82 }
83}
84
85pub(crate) async fn run_acp_server(
86 pipeline: Option<&str>,
87 auth_policy: AuthPolicy,
88 trace: bool,
89 profile: AcpProfileConfig,
90) {
91 ensure_acp_event_log(pipeline);
92 if trace {
93 harn_vm::llm::enable_tracing();
94 }
95 harn_serve::run_acp_server(
96 server_config(pipeline.map(str::to_string), auth_policy).with_profile(profile),
97 )
98 .await;
99 if trace {
100 eprint!("{}", crate::commands::run::render_trace_summary());
101 }
102}
103
104pub(crate) async fn run_acp_channel_server(
105 pipeline: Option<String>,
106 request_rx: mpsc::UnboundedReceiver<serde_json::Value>,
107 response_tx: mpsc::UnboundedSender<String>,
108) {
109 harn_serve::run_acp_channel_server(
110 server_config(pipeline, AuthPolicy::allow_all()),
111 request_rx,
112 response_tx,
113 )
114 .await;
115}