1use std::sync::atomic::{AtomicU64, Ordering};
2use std::sync::{Arc, Mutex, Weak};
3
4use serde_json::Value;
5use tokio::sync::{Mutex as AsyncMutex, broadcast, broadcast::error::RecvError, mpsc};
6use tokio_util::sync::CancellationToken;
7
8use crate::FnvIndexMap;
9use crate::agent::{Agent, AgentMessage, AgentStatus, agent_new};
10use crate::config::{AgentConfigs, AgentConfigsMap};
11use crate::context::AgentContext;
12use crate::definition::{AgentConfigSpecs, AgentDefinition, AgentDefinitions};
13use crate::error::AgentError;
14use crate::id::{new_id, update_ids};
15use crate::message::{self, AgentEventMessage};
16use crate::preset::{Preset, PresetInfo};
17use crate::registry;
18use crate::spec::{AgentSpec, ConnectionSpec, PresetSpec};
19use crate::value::AgentValue;
20
21const MESSAGE_LIMIT: usize = 1024;
22const EVENT_CHANNEL_CAPACITY: usize = 256;
23
24const CONTEXT_TOKEN_PRUNE_THRESHOLD: usize = 1024;
29
30static AGENT_TOKEN_GENERATION: AtomicU64 = AtomicU64::new(1);
34
35#[cfg_attr(feature = "file", doc = "```rust,no_run")]
53#[cfg_attr(not(feature = "file"), doc = "```rust,no_run,ignore")]
54pub type SharedAgent = Arc<AsyncMutex<Box<dyn Agent>>>;
77
78pub(crate) type ConnectionTarget = (String, String, String);
80
81#[derive(Clone)]
82pub struct ModularAgent {
83 pub(crate) agents: Arc<Mutex<FnvIndexMap<String, SharedAgent>>>,
85
86 pub(crate) agent_txs: Arc<Mutex<FnvIndexMap<String, mpsc::Sender<AgentMessage>>>>,
88
89 pub(crate) external_input_agents: Arc<Mutex<FnvIndexMap<String, Vec<String>>>>,
91
92 pub(crate) external_values: Arc<Mutex<FnvIndexMap<String, AgentValue>>>,
94
95 pub(crate) connections: Arc<Mutex<FnvIndexMap<String, Vec<ConnectionTarget>>>>,
97
98 pub(crate) defs: Arc<Mutex<AgentDefinitions>>,
100
101 pub(crate) presets: Arc<Mutex<FnvIndexMap<String, Arc<AsyncMutex<Preset>>>>>,
103
104 pub(crate) global_configs_map: Arc<Mutex<FnvIndexMap<String, AgentConfigs>>>,
106
107 pub(crate) preset_tokens: Arc<Mutex<FnvIndexMap<String, CancellationToken>>>,
109
110 pub(crate) agent_tokens: Arc<Mutex<FnvIndexMap<String, (u64, CancellationToken)>>>,
112
113 pub(crate) context_tokens: Arc<Mutex<FnvIndexMap<usize, Weak<CancellationToken>>>>,
115
116 pub(crate) tx: Arc<Mutex<Option<mpsc::Sender<AgentEventMessage>>>>,
118
119 pub(crate) observers: broadcast::Sender<ModularAgentEvent>,
121}
122
123impl Default for ModularAgent {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl ModularAgent {
130 pub fn new() -> Self {
135 let (tx, _rx) = broadcast::channel(EVENT_CHANNEL_CAPACITY);
136 Self {
137 agents: Default::default(),
138 agent_txs: Default::default(),
139 external_input_agents: Default::default(),
140 external_values: Default::default(),
141 connections: Default::default(),
142 defs: Default::default(),
143 presets: Default::default(),
144 global_configs_map: Default::default(),
145 preset_tokens: Default::default(),
146 agent_tokens: Default::default(),
147 context_tokens: Default::default(),
148 tx: Arc::new(Mutex::new(None)),
149 observers: tx,
150 }
151 }
152
153 pub(crate) fn tx(&self) -> Result<mpsc::Sender<AgentEventMessage>, AgentError> {
154 self.tx
155 .lock()
156 .unwrap()
157 .clone()
158 .ok_or(AgentError::TxNotInitialized)
159 }
160
161 pub fn init() -> Result<Self, AgentError> {
174 let ma = Self::new();
175 ma.register_agents();
176 Ok(ma)
177 }
178
179 fn register_agents(&self) {
180 registry::register_inventory_agents(self);
181 }
182
183 pub async fn ready(&self) -> Result<(), AgentError> {
200 self.spawn_message_loop().await?;
201 Ok(())
202 }
203
204 pub fn quit(&self) {
224 let mut tx_lock = self.tx.lock().unwrap();
225 *tx_lock = None;
226 }
227
228 pub async fn shutdown(&self) -> Result<(), AgentError> {
246 self.quit();
247 #[cfg(feature = "mcp")]
248 crate::mcp::shutdown_all_mcp_connections().await?;
249 Ok(())
250 }
251
252 pub fn new_preset(&self) -> Result<String, AgentError> {
259 let spec = PresetSpec::default();
260 let id = self.add_preset(spec)?;
261 Ok(id)
262 }
263
264 pub fn new_preset_with_name(&self, name: String) -> Result<String, AgentError> {
268 let spec = PresetSpec::default();
269 let id = self.add_preset_with_name(spec, name)?;
270 Ok(id)
271 }
272
273 pub fn get_preset(&self, id: &str) -> Option<Arc<AsyncMutex<Preset>>> {
277 let presets = self.presets.lock().unwrap();
278 presets.get(id).cloned()
279 }
280
281 pub fn add_preset(&self, spec: PresetSpec) -> Result<String, AgentError> {
286 self.add_preset_raw(spec, None)
287 }
288
289 pub fn add_preset_with_name(
293 &self,
294 spec: PresetSpec,
295 name: String,
296 ) -> Result<String, AgentError> {
297 self.add_preset_raw(spec, Some(name))
298 }
299
300 fn add_preset_raw(&self, spec: PresetSpec, name: Option<String>) -> Result<String, AgentError> {
301 let mut preset = Preset::new(spec);
302 if let Some(name) = name {
303 preset.set_name(name);
304 }
305 let id = preset.id().to_string();
306
307 for agent in &preset.spec().agents {
309 if let Err(e) = self.add_agent_internal(id.clone(), agent.clone()) {
310 log::error!("Failed to add_agent {}: {}", agent.id, e);
311 }
312 }
313
314 for connection in &preset.spec().connections {
316 self.add_connection_internal(connection.clone())
317 .unwrap_or_else(|e| {
318 log::error!("Failed to add_connection {}: {}", connection.source, e);
319 });
320 }
321
322 let mut presets = self.presets.lock().unwrap();
324 if presets.contains_key(&id) {
325 return Err(AgentError::DuplicateId(id));
326 }
327 presets.insert(id.to_string(), Arc::new(AsyncMutex::new(preset)));
328
329 Ok(id)
330 }
331
332 pub async fn rename_preset(&self, id: &str, new_name: String) -> Result<(), AgentError> {
334 let preset = self
335 .get_preset(id)
336 .ok_or_else(|| AgentError::PresetNotFound(id.to_string()))?;
337 let mut preset = preset.lock().await;
338 preset.set_name(new_name);
339 Ok(())
340 }
341
342 pub async fn remove_preset(&self, id: &str) -> Result<(), AgentError> {
346 let preset = self
347 .get_preset(id)
348 .ok_or_else(|| AgentError::PresetNotFound(id.to_string()))?;
349
350 let mut preset = preset.lock().await;
351 preset.stop(self).await.unwrap_or_else(|e| {
352 log::error!("Failed to stop preset {}: {}", id, e);
353 });
354
355 for agent in &preset.spec().agents {
357 self.remove_agent_internal(&agent.id)
358 .await
359 .unwrap_or_else(|e| {
360 log::error!("Failed to remove_agent {}: {}", agent.id, e);
361 });
362 }
363 for connection in &preset.spec().connections {
364 self.remove_connection_internal(connection);
365 }
366
367 drop(preset);
369
370 {
372 let mut presets = self.presets.lock().unwrap();
373 presets.swap_remove(id);
374 }
375 self.remove_preset_token(id);
376
377 Ok(())
378 }
379
380 pub async fn start_preset(&self, id: &str) -> Result<(), AgentError> {
385 let preset = self
386 .get_preset(id)
387 .ok_or_else(|| AgentError::PresetNotFound(id.to_string()))?;
388 let mut preset = preset.lock().await;
389 preset.start(self).await?;
390
391 Ok(())
392 }
393
394 pub async fn stop_preset(&self, id: &str) -> Result<(), AgentError> {
399 let preset = self
400 .get_preset(id)
401 .ok_or_else(|| AgentError::PresetNotFound(id.to_string()))?;
402 let mut preset = preset.lock().await;
403 preset.stop(self).await?;
404
405 Ok(())
406 }
407
408 #[cfg(feature = "file")]
418 pub async fn open_preset_from_file(
419 &self,
420 path: &str,
421 name: Option<String>,
422 ) -> Result<String, AgentError> {
423 let json_str =
424 std::fs::read_to_string(path).map_err(|e| AgentError::IoError(e.to_string()))?;
425 let spec = PresetSpec::from_json(&json_str)?;
426 let id = self.add_preset_raw(spec, name)?;
427 Ok(id)
428 }
429
430 #[cfg(feature = "file")]
435 pub async fn save_preset(&self, id: &str, path: &str) -> Result<(), AgentError> {
436 let Some(preset_spec) = self.get_preset_spec(id).await else {
437 return Err(AgentError::PresetNotFound(id.to_string()));
438 };
439 let json_str = preset_spec.to_json()?;
440 std::fs::write(path, json_str).map_err(|e| AgentError::IoError(e.to_string()))?;
441 Ok(())
442 }
443
444 pub async fn get_preset_spec(&self, id: &str) -> Option<PresetSpec> {
448 let preset = self.get_preset(id)?;
449 let mut preset_spec = {
450 let preset = preset.lock().await;
451 preset.spec().clone()
452 };
453
454 let mut agent_specs = Vec::new();
456 for agent in &preset_spec.agents {
457 if let Some(spec) = self.get_agent_spec(&agent.id).await {
458 agent_specs.push(spec);
459 }
460 }
461 preset_spec.agents = agent_specs;
462
463 Some(preset_spec)
466 }
467
468 pub async fn update_preset_spec(&self, id: &str, value: &Value) -> Result<(), AgentError> {
470 let preset = self
471 .get_preset(id)
472 .ok_or_else(|| AgentError::PresetNotFound(id.to_string()))?;
473 let mut preset = preset.lock().await;
474 preset.update_spec(value)?;
475 Ok(())
476 }
477
478 pub async fn get_preset_info(&self, id: &str) -> Option<PresetInfo> {
482 let preset = self.get_preset(id)?;
483 Some(PresetInfo::from(&*preset.lock().await))
484 }
485
486 pub async fn get_preset_infos(&self) -> Vec<PresetInfo> {
488 let presets = {
489 let presets = self.presets.lock().unwrap();
490 presets.values().cloned().collect::<Vec<_>>()
491 };
492 let mut preset_infos = Vec::new();
493 for preset in presets {
494 let preset_guard = preset.lock().await;
495 preset_infos.push(PresetInfo::from(&*preset_guard));
496 }
497 preset_infos
498 }
499
500 pub fn register_agent_definiton(&self, def: AgentDefinition) {
509 let def_name = def.name.clone();
510 let def_global_configs = def.global_configs.clone();
511
512 let mut defs = self.defs.lock().unwrap();
513 defs.insert(def.name.clone(), def);
514
515 if let Some(def_global_configs) = def_global_configs {
517 let mut new_configs = AgentConfigs::default();
518 for (key, config_entry) in def_global_configs.iter() {
519 new_configs.set(key.clone(), config_entry.value.clone());
520 }
521 self.set_global_configs(def_name, new_configs);
522 }
523 }
524
525 pub fn get_agent_definitions(&self) -> AgentDefinitions {
529 let defs = self.defs.lock().unwrap();
530 defs.clone()
531 }
532
533 pub fn get_agent_definition(&self, def_name: &str) -> Option<AgentDefinition> {
537 let defs = self.defs.lock().unwrap();
538 defs.get(def_name).cloned()
539 }
540
541 pub fn get_agent_config_specs(&self, def_name: &str) -> Option<AgentConfigSpecs> {
543 let defs = self.defs.lock().unwrap();
544 let def = defs.get(def_name)?;
545 def.configs.clone()
546 }
547
548 pub async fn get_agent_spec(&self, agent_id: &str) -> Option<AgentSpec> {
550 let agent = {
551 let agents = self.agents.lock().unwrap();
552 agents.get(agent_id)?.clone()
553 };
554 let agent = agent.lock().await;
555 Some(agent.spec().clone())
556 }
557
558 pub async fn update_agent_spec(&self, agent_id: &str, value: &Value) -> Result<(), AgentError> {
560 let agent = {
561 let agents = self.agents.lock().unwrap();
562 let Some(agent) = agents.get(agent_id) else {
563 return Err(AgentError::AgentNotFound(agent_id.to_string()));
564 };
565 agent.clone()
566 };
567 let mut agent = agent.lock().await;
568 agent.update_spec(value)?;
569 Ok(())
570 }
571
572 pub fn new_agent_spec(&self, def_name: &str) -> Result<AgentSpec, AgentError> {
574 let def = self
575 .get_agent_definition(def_name)
576 .ok_or_else(|| AgentError::AgentDefinitionNotFound(def_name.to_string()))?;
577 Ok(def.to_spec())
578 }
579
580 pub async fn add_agent(
586 &self,
587 preset_id: String,
588 mut spec: AgentSpec,
589 ) -> Result<String, AgentError> {
590 let preset = self
591 .get_preset(&preset_id)
592 .ok_or_else(|| AgentError::PresetNotFound(preset_id.to_string()))?;
593
594 let id = new_id();
595 spec.id = id.clone();
596 self.add_agent_internal(preset_id, spec.clone())?;
597
598 let mut preset = preset.lock().await;
599 preset.add_agent(spec.clone());
600
601 Ok(id)
602 }
603
604 fn add_agent_internal(&self, preset_id: String, spec: AgentSpec) -> Result<(), AgentError> {
605 let mut agents = self.agents.lock().unwrap();
606 if agents.contains_key(&spec.id) {
607 return Err(AgentError::AgentAlreadyExists(spec.id.to_string()));
608 }
609 let spec_id = spec.id.clone();
610 let mut agent = agent_new(self.clone(), spec_id.clone(), spec)?;
611 agent.set_preset_id(preset_id);
612 agents.insert(spec_id, Arc::new(AsyncMutex::new(agent)));
613 Ok(())
614 }
615
616 pub fn get_agent(&self, agent_id: &str) -> Option<SharedAgent> {
618 let agents = self.agents.lock().unwrap();
619 agents.get(agent_id).cloned()
620 }
621
622 pub async fn add_connection(
627 &self,
628 preset_id: &str,
629 connection: ConnectionSpec,
630 ) -> Result<(), AgentError> {
631 {
633 let agents = self.agents.lock().unwrap();
634 if !agents.contains_key(&connection.source) {
635 return Err(AgentError::AgentNotFound(connection.source.to_string()));
636 }
637 if !agents.contains_key(&connection.target) {
638 return Err(AgentError::AgentNotFound(connection.target.to_string()));
639 }
640 }
641
642 if connection.source_handle.is_empty() {
644 return Err(AgentError::EmptySourceHandle);
645 }
646 if connection.target_handle.is_empty() {
647 return Err(AgentError::EmptyTargetHandle);
648 }
649
650 let preset = self
651 .get_preset(preset_id)
652 .ok_or_else(|| AgentError::PresetNotFound(preset_id.to_string()))?;
653 let mut preset = preset.lock().await;
654 preset.add_connection(connection.clone());
655 self.add_connection_internal(connection)?;
656 Ok(())
657 }
658
659 fn add_connection_internal(&self, connection: ConnectionSpec) -> Result<(), AgentError> {
660 let mut connections = self.connections.lock().unwrap();
661 if let Some(targets) = connections.get_mut(&connection.source) {
662 if targets
663 .iter()
664 .any(|(target, source_handle, target_handle)| {
665 *target == connection.target
666 && *source_handle == connection.source_handle
667 && *target_handle == connection.target_handle
668 })
669 {
670 return Err(AgentError::ConnectionAlreadyExists);
671 }
672 targets.push((
673 connection.target,
674 connection.source_handle,
675 connection.target_handle,
676 ));
677 } else {
678 connections.insert(
679 connection.source,
680 vec![(
681 connection.target,
682 connection.source_handle,
683 connection.target_handle,
684 )],
685 );
686 }
687 Ok(())
688 }
689
690 pub fn has_connections(&self, source_agent: &str, port: &str) -> bool {
696 let connections = self.connections.lock().unwrap();
697 connections.get(source_agent).is_some_and(|targets| {
698 targets
699 .iter()
700 .any(|(_, source_port, _)| source_port == port)
701 })
702 }
703
704 pub async fn add_agents_and_connections(
709 &self,
710 preset_id: &str,
711 agents: &Vec<AgentSpec>,
712 connections: &Vec<ConnectionSpec>,
713 ) -> Result<(Vec<AgentSpec>, Vec<ConnectionSpec>), AgentError> {
714 let (agents, connections) = update_ids(agents, connections);
715
716 let preset = self
717 .get_preset(preset_id)
718 .ok_or_else(|| AgentError::PresetNotFound(preset_id.to_string()))?;
719 let mut preset = preset.lock().await;
720
721 for agent in &agents {
722 self.add_agent_internal(preset_id.to_string(), agent.clone())?;
723 preset.add_agent(agent.clone());
724 }
725
726 for connection in &connections {
727 self.add_connection_internal(connection.clone())?;
728 preset.add_connection(connection.clone());
729 }
730
731 Ok((agents, connections))
732 }
733
734 pub async fn remove_agent(&self, preset_id: &str, agent_id: &str) -> Result<(), AgentError> {
738 {
739 let preset = self
740 .get_preset(preset_id)
741 .ok_or_else(|| AgentError::PresetNotFound(preset_id.to_string()))?;
742 let mut preset = preset.lock().await;
743 preset.remove_agent(agent_id);
744 }
745 self.remove_agent_internal(agent_id).await?;
746 Ok(())
747 }
748
749 async fn remove_agent_internal(&self, agent_id: &str) -> Result<(), AgentError> {
750 self.stop_agent(agent_id).await?;
751
752 {
754 let mut connections = self.connections.lock().unwrap();
755 let mut sources_to_remove = Vec::new();
756 for (source, targets) in connections.iter_mut() {
757 targets.retain(|(target, _, _)| target != agent_id);
758 if targets.is_empty() {
759 sources_to_remove.push(source.clone());
760 }
761 }
762 for source in sources_to_remove {
763 connections.swap_remove(&source);
764 }
765 connections.swap_remove(agent_id);
766 }
767
768 {
770 let mut agents = self.agents.lock().unwrap();
771 agents.swap_remove(agent_id);
772 }
773
774 Ok(())
775 }
776
777 pub async fn remove_connection(
779 &self,
780 preset_id: &str,
781 connection: &ConnectionSpec,
782 ) -> Result<(), AgentError> {
783 let preset = self
784 .get_preset(preset_id)
785 .ok_or_else(|| AgentError::PresetNotFound(preset_id.to_string()))?;
786 let mut preset = preset.lock().await;
787 let Some(connection) = preset.remove_connection(connection) else {
788 return Err(AgentError::ConnectionNotFound(format!(
789 "{}:{}->{}:{}",
790 connection.source,
791 connection.source_handle,
792 connection.target,
793 connection.target_handle
794 )));
795 };
796 self.remove_connection_internal(&connection);
797 Ok(())
798 }
799
800 fn remove_connection_internal(&self, connection: &ConnectionSpec) {
801 let mut connections = self.connections.lock().unwrap();
802 if let Some(targets) = connections.get_mut(&connection.source) {
803 targets.retain(|(target, source_handle, target_handle)| {
804 *target != connection.target
805 || *source_handle != connection.source_handle
806 || *target_handle != connection.target_handle
807 });
808 if targets.is_empty() {
809 connections.swap_remove(&connection.source);
810 }
811 }
812 }
813
814 fn preset_token(&self, preset_id: &str) -> CancellationToken {
818 let mut tokens = self.preset_tokens.lock().unwrap();
819 tokens.entry(preset_id.to_string()).or_default().clone()
820 }
821
822 pub(crate) fn reset_preset_token(&self, preset_id: &str) {
827 let mut tokens = self.preset_tokens.lock().unwrap();
828 tokens.insert(preset_id.to_string(), CancellationToken::new());
829 }
830
831 pub(crate) fn cancel_preset_token(&self, preset_id: &str) {
841 let token = self.preset_tokens.lock().unwrap().get(preset_id).cloned();
842 if let Some(token) = token {
843 token.cancel();
844 }
845 }
846
847 pub(crate) fn remove_preset_token(&self, preset_id: &str) {
848 self.preset_tokens.lock().unwrap().swap_remove(preset_id);
849 }
850
851 fn create_agent_token(&self, preset_id: &str, agent_id: &str) -> (u64, CancellationToken) {
855 let generation = AGENT_TOKEN_GENERATION.fetch_add(1, Ordering::Relaxed);
856 let token = self.preset_token(preset_id).child_token();
857 self.agent_tokens
858 .lock()
859 .unwrap()
860 .insert(agent_id.to_string(), (generation, token.clone()));
861 (generation, token)
862 }
863
864 fn renew_agent_token(
873 &self,
874 preset_id: &str,
875 agent_id: &str,
876 generation: u64,
877 ) -> Option<CancellationToken> {
878 let parent = self.preset_tokens.lock().unwrap().get(preset_id).cloned()?;
881 let fresh = parent.child_token();
882 let mut tokens = self.agent_tokens.lock().unwrap();
883 let slot = tokens.get_mut(agent_id)?;
884 if slot.0 != generation {
885 return None;
886 }
887 slot.1 = fresh.clone();
888 Some(fresh)
889 }
890
891 pub(crate) fn context_token(&self, ctx_id: usize) -> Arc<CancellationToken> {
898 let mut tokens = self.context_tokens.lock().unwrap();
899 if let Some(token) = tokens.get(&ctx_id).and_then(Weak::upgrade) {
900 return token;
901 }
902 if tokens.len() >= CONTEXT_TOKEN_PRUNE_THRESHOLD {
903 tokens.retain(|_, weak| weak.strong_count() > 0);
904 }
905 let token = Arc::new(CancellationToken::new());
906 tokens.insert(ctx_id, Arc::downgrade(&token));
907 token
908 }
909
910 pub fn abort_context(&self, ctx_id: usize) -> bool {
925 let token = self
926 .context_tokens
927 .lock()
928 .unwrap()
929 .get(&ctx_id)
930 .and_then(Weak::upgrade);
931 match token {
932 Some(token) => {
933 token.cancel();
934 true
935 }
936 None => {
937 log::warn!("abort_context: no live flow for context {}", ctx_id);
938 false
939 }
940 }
941 }
942
943 pub async fn start_agent(&self, agent_id: &str) -> Result<(), AgentError> {
952 let agent = {
953 let agents = self.agents.lock().unwrap();
954 let Some(a) = agents.get(agent_id) else {
955 return Err(AgentError::AgentNotFound(agent_id.to_string()));
956 };
957 a.clone()
958 };
959 let (def_name, preset_id) = {
960 let agent = agent.lock().await;
961 (agent.def_name().to_string(), agent.preset_id().to_string())
962 };
963 let uses_native_thread = {
964 let defs = self.defs.lock().unwrap();
965 let Some(def) = defs.get(&def_name) else {
966 return Err(AgentError::AgentDefinitionNotFound(agent_id.to_string()));
967 };
968 def.native_thread
969 };
970 let agent_status = {
971 let agent = agent.lock().await;
973 agent.status().clone()
974 };
975 if agent_status == AgentStatus::Init {
976 log::info!("Starting agent {}", agent_id);
977
978 let (tx, mut rx) = mpsc::channel(MESSAGE_LIMIT);
979
980 {
981 let mut agent_txs = self.agent_txs.lock().unwrap();
982 agent_txs.insert(agent_id.to_string(), tx.clone());
983 };
984
985 let agent_clone = agent.clone();
986 let agent_id_clone = agent_id.to_string();
987 let ma = self.clone();
988 let (generation, mut token) = self.create_agent_token(&preset_id, agent_id);
990
991 let agent_loop = async move {
992 let start = async {
997 let mut agent_guard = agent_clone.lock().await;
998 agent_guard.start().await
999 };
1000 tokio::select! {
1001 biased;
1002 _ = token.cancelled() => {
1003 log::info!("Start cancelled: {}", agent_id_clone);
1004 return;
1005 }
1006 r = start => {
1007 if let Err(e) = r {
1008 log::error!("Failed to start agent {}: {}", agent_id_clone, e);
1009 return;
1010 }
1011 }
1012 }
1013
1014 while let Some(message) = rx.recv().await {
1015 match message {
1016 AgentMessage::Input { ctx, port, value } => {
1017 let ctx = if ctx.cancel_token().is_none() {
1021 ctx.with_cancel_token(ma.context_token(ctx.id()))
1022 } else {
1023 ctx
1024 };
1025 let fut =
1026 async { agent_clone.lock().await.process(ctx, port, value).await };
1027 tokio::select! {
1028 biased;
1029 _ = token.cancelled() => {
1030 log::info!("Process cancelled: {}", agent_id_clone);
1031 if let Some(fresh) = ma.renew_agent_token(
1037 &preset_id,
1038 &agent_id_clone,
1039 generation,
1040 ) {
1041 token = fresh;
1042 }
1043 }
1044 r = fut => r.unwrap_or_else(|e| {
1045 log::error!("Process Error {}: {}", agent_id_clone, e);
1046 }),
1047 }
1048 }
1049 AgentMessage::Config { key, value } => {
1050 agent_clone
1051 .lock()
1052 .await
1053 .set_config(key, value)
1054 .unwrap_or_else(|e| {
1055 log::error!("Config Error {}: {}", agent_id_clone, e);
1056 });
1057 }
1058 AgentMessage::Configs { configs } => {
1059 agent_clone
1060 .lock()
1061 .await
1062 .set_configs(configs)
1063 .unwrap_or_else(|e| {
1064 log::error!("Configs Error {}: {}", agent_id_clone, e);
1065 });
1066 }
1067 AgentMessage::Stop => {
1068 rx.close();
1069 break;
1070 }
1071 }
1072 }
1073 };
1074
1075 if uses_native_thread {
1076 std::thread::spawn(move || {
1077 let rt = tokio::runtime::Builder::new_current_thread()
1078 .enable_all()
1079 .build()
1080 .unwrap();
1081 rt.block_on(agent_loop);
1082 });
1083 } else {
1084 tokio::spawn(agent_loop);
1085 }
1086 }
1087 Ok(())
1088 }
1089
1090 pub async fn stop_agent(&self, agent_id: &str) -> Result<(), AgentError> {
1095 {
1096 let mut agent_txs = self.agent_txs.lock().unwrap();
1098 if let Some(tx) = agent_txs.swap_remove(agent_id)
1099 && let Err(e) = tx.try_send(AgentMessage::Stop)
1100 {
1101 log::warn!("Failed to send stop message to agent {}: {}", agent_id, e);
1102 }
1103 }
1104
1105 let token = self.agent_tokens.lock().unwrap().swap_remove(agent_id);
1112 if let Some((_, token)) = token {
1113 token.cancel();
1114 }
1115
1116 let agent = {
1117 let agents = self.agents.lock().unwrap();
1118 let Some(a) = agents.get(agent_id) else {
1119 return Err(AgentError::AgentNotFound(agent_id.to_string()));
1120 };
1121 a.clone()
1122 };
1123 let mut agent_guard = agent.lock().await;
1124 if *agent_guard.status() == AgentStatus::Start {
1125 log::info!("Stopping agent {}", agent_id);
1126 agent_guard.stop().await?;
1127 }
1128
1129 Ok(())
1130 }
1131
1132 pub async fn set_agent_configs(
1134 &self,
1135 agent_id: String,
1136 configs: AgentConfigs,
1137 ) -> Result<(), AgentError> {
1138 let tx = {
1139 let agent_txs = self.agent_txs.lock().unwrap();
1140 agent_txs.get(&agent_id).cloned()
1141 };
1142
1143 let Some(tx) = tx else {
1144 let agent = {
1146 let agents = self.agents.lock().unwrap();
1147 let Some(a) = agents.get(&agent_id) else {
1148 return Err(AgentError::AgentNotFound(agent_id.to_string()));
1149 };
1150 a.clone()
1151 };
1152 agent.lock().await.set_configs(configs.clone())?;
1153 return Ok(());
1154 };
1155 let message = AgentMessage::Configs { configs };
1156 tx.send(message).await.map_err(|_| {
1157 AgentError::SendMessageFailed("Failed to send config message".to_string())
1158 })?;
1159 Ok(())
1160 }
1161
1162 pub fn get_global_configs(&self, def_name: &str) -> Option<AgentConfigs> {
1164 let global_configs_map = self.global_configs_map.lock().unwrap();
1165 global_configs_map.get(def_name).cloned()
1166 }
1167
1168 pub fn set_global_configs(&self, def_name: String, configs: AgentConfigs) {
1170 let mut global_configs_map = self.global_configs_map.lock().unwrap();
1171
1172 let Some(existing_configs) = global_configs_map.get_mut(&def_name) else {
1173 global_configs_map.insert(def_name, configs);
1174 return;
1175 };
1176
1177 for (key, value) in configs {
1178 existing_configs.set(key, value);
1179 }
1180 }
1181
1182 pub fn get_global_configs_map(&self) -> AgentConfigsMap {
1184 let global_configs_map = self.global_configs_map.lock().unwrap();
1185 global_configs_map.clone()
1186 }
1187
1188 pub fn set_global_configs_map(&self, new_configs_map: AgentConfigsMap) {
1190 for (agent_name, new_configs) in new_configs_map {
1191 self.set_global_configs(agent_name, new_configs);
1192 }
1193 }
1194
1195 pub(crate) async fn agent_input(
1197 &self,
1198 agent_id: String,
1199 ctx: AgentContext,
1200 port: String,
1201 value: AgentValue,
1202 ) -> Result<(), AgentError> {
1203 let message = if let Some(config_key) = port.strip_prefix("config:") {
1204 AgentMessage::Config {
1205 key: config_key.to_string(),
1206 value,
1207 }
1208 } else {
1209 AgentMessage::Input {
1210 ctx,
1211 port: port.clone(),
1212 value,
1213 }
1214 };
1215
1216 let tx = {
1217 let agent_txs = self.agent_txs.lock().unwrap();
1218 agent_txs.get(&agent_id).cloned()
1219 };
1220
1221 let Some(tx) = tx else {
1222 let agent: SharedAgent = {
1224 let agents = self.agents.lock().unwrap();
1225 let Some(a) = agents.get(&agent_id) else {
1226 return Err(AgentError::AgentNotFound(agent_id.to_string()));
1227 };
1228 a.clone()
1229 };
1230 if let AgentMessage::Config { key, value } = message {
1231 agent.lock().await.set_config(key, value)?;
1232 }
1233 return Ok(());
1234 };
1235 tx.send(message).await.map_err(|_| {
1236 AgentError::SendMessageFailed("Failed to send input message".to_string())
1237 })?;
1238
1239 self.emit_agent_input(agent_id.to_string(), port);
1240
1241 Ok(())
1242 }
1243
1244 pub async fn send_agent_out(
1246 &self,
1247 agent_id: String,
1248 ctx: AgentContext,
1249 port: String,
1250 value: AgentValue,
1251 ) -> Result<(), AgentError> {
1252 message::send_agent_out(self, agent_id, ctx, port, value).await
1253 }
1254
1255 pub fn try_send_agent_out(
1257 &self,
1258 agent_id: String,
1259 ctx: AgentContext,
1260 port: String,
1261 value: AgentValue,
1262 ) -> Result<(), AgentError> {
1263 message::try_send_agent_out(self, agent_id, ctx, port, value)
1264 }
1265
1266 pub async fn write_external_input(
1291 &self,
1292 name: String,
1293 value: AgentValue,
1294 ) -> Result<(), AgentError> {
1295 self.send_external_output(name, AgentContext::new(), value)
1296 .await
1297 }
1298
1299 pub async fn write_local_input(
1301 &self,
1302 preset_id: &str,
1303 name: &str,
1304 value: AgentValue,
1305 ) -> Result<(), AgentError> {
1306 let channel_name = format!("%{}/{}", preset_id, name);
1307 self.send_external_output(channel_name, AgentContext::new(), value)
1308 .await
1309 }
1310
1311 pub(crate) async fn send_external_output(
1312 &self,
1313 name: String,
1314 ctx: AgentContext,
1315 value: AgentValue,
1316 ) -> Result<(), AgentError> {
1317 message::send_external_output(self, name, ctx, value).await
1318 }
1319
1320 async fn spawn_message_loop(&self) -> Result<(), AgentError> {
1321 let (tx, mut rx) = mpsc::channel(4096);
1323 {
1324 let mut tx_lock = self.tx.lock().unwrap();
1325 *tx_lock = Some(tx);
1326 }
1327
1328 let ma = self.clone();
1330 tokio::spawn(async move {
1331 while let Some(message) = rx.recv().await {
1332 use AgentEventMessage::*;
1333
1334 match message {
1335 AgentOut {
1336 agent,
1337 ctx,
1338 port,
1339 value,
1340 } => {
1341 message::agent_out(&ma, agent, ctx, port, value).await;
1342 }
1343 ExternalOutput { name, ctx, value } => {
1344 message::external_input(&ma, name, ctx, value).await;
1345 }
1346 }
1347 }
1348 });
1349
1350 tokio::task::yield_now().await;
1351
1352 Ok(())
1353 }
1354
1355 pub fn subscribe(&self) -> broadcast::Receiver<ModularAgentEvent> {
1362 self.observers.subscribe()
1363 }
1364
1365 pub fn subscribe_to_event<F, T>(&self, mut filter_map: F) -> mpsc::UnboundedReceiver<T>
1407 where
1408 F: FnMut(ModularAgentEvent) -> Option<T> + Send + 'static,
1409 T: Send + 'static,
1410 {
1411 let (tx, rx) = mpsc::unbounded_channel();
1412 let mut event_rx = self.subscribe();
1413
1414 tokio::spawn(async move {
1415 loop {
1416 match event_rx.recv().await {
1417 Ok(event) => {
1418 if let Some(mapped_event) = filter_map(event)
1419 && tx.send(mapped_event).is_err()
1420 {
1421 break;
1423 }
1424 }
1425 Err(RecvError::Lagged(n)) => {
1426 log::warn!("Event subscriber lagged by {} events", n);
1427 }
1428 Err(RecvError::Closed) => {
1429 break;
1431 }
1432 }
1433 }
1434 });
1435 rx
1436 }
1437
1438 pub(crate) fn emit_agent_config_updated(
1439 &self,
1440 agent_id: String,
1441 key: String,
1442 value: AgentValue,
1443 ) {
1444 self.notify_observers(ModularAgentEvent::AgentConfigUpdated(agent_id, key, value));
1445 }
1446
1447 pub(crate) fn emit_agent_error(&self, agent_id: String, message: String) {
1448 self.notify_observers(ModularAgentEvent::AgentError(agent_id, message));
1449 }
1450
1451 pub(crate) fn emit_agent_input(&self, agent_id: String, port: String) {
1452 self.notify_observers(ModularAgentEvent::AgentIn(agent_id, port));
1453 }
1454
1455 pub(crate) fn emit_agent_spec_updated(&self, agent_id: String) {
1456 self.notify_observers(ModularAgentEvent::AgentSpecUpdated(agent_id));
1457 }
1458
1459 pub(crate) fn emit_external_output(&self, name: String, value: AgentValue) {
1460 self.notify_observers(ModularAgentEvent::ExternalOutput(name, value));
1465 }
1466
1467 fn notify_observers(&self, event: ModularAgentEvent) {
1468 let _ = self.observers.send(event);
1469 }
1470}
1471
1472#[derive(Clone, Debug)]
1494pub enum ModularAgentEvent {
1495 AgentConfigUpdated(String, String, AgentValue),
1499
1500 AgentError(String, String),
1504
1505 AgentIn(String, String),
1509
1510 AgentSpecUpdated(String),
1514
1515 ExternalOutput(String, AgentValue),
1523}
1524
1525#[cfg(test)]
1526mod tests {
1527 use super::*;
1528
1529 #[test]
1530 fn live_context_tokens_are_not_evicted_at_prune_threshold() {
1531 let ma = ModularAgent::new();
1532 let tokens: Vec<_> = (0..=CONTEXT_TOKEN_PRUNE_THRESHOLD)
1533 .map(|ctx_id| ma.context_token(ctx_id))
1534 .collect();
1535
1536 assert_eq!(tokens.len(), CONTEXT_TOKEN_PRUNE_THRESHOLD + 1);
1537 assert!(ma.abort_context(0));
1538 assert!(tokens[0].is_cancelled());
1539 }
1540}