@@ -23,6 +23,11 @@ use a3s_code_core::agent_teams::{
2323 TeamRole as RustTeamRole , TeamRunner as RustTeamRunner , TeamTask as RustTeamTask ,
2424 TeamTaskBoard as RustTeamTaskBoard ,
2525} ;
26+ use a3s_code_core:: orchestrator:: {
27+ AgentOrchestrator as RustOrchestrator , ControlSignal as RustControlSignal ,
28+ OrchestratorEvent as RustOrchestratorEvent , SubAgentConfig as RustSubAgentConfig ,
29+ SubAgentHandle as RustSubAgentHandle , SubAgentState as RustSubAgentState ,
30+ } ;
2631use a3s_code_core:: config:: {
2732 SearchConfig as RustSearchConfig , SearchEngineConfig as RustSearchEngineConfig ,
2833 SearchHealthConfig as RustSearchHealthConfig ,
@@ -2245,3 +2250,150 @@ impl From<SearchConfig> for RustSearchConfig {
22452250 }
22462251 }
22472252}
2253+
2254+ // ============================================================================
2255+ // Agent Orchestrator - Main-Sub Agent Coordination
2256+ // ============================================================================
2257+
2258+ /// SubAgent configuration for orchestrator.
2259+ #[ napi( object) ]
2260+ #[ derive( Clone ) ]
2261+ pub struct SubAgentConfig {
2262+ /// Agent type (general, explore, plan, etc.)
2263+ pub agent_type : String ,
2264+ /// Task description
2265+ pub description : String ,
2266+ /// Execution prompt
2267+ pub prompt : String ,
2268+ /// Enable permissive mode (bypass HITL)
2269+ pub permissive : bool ,
2270+ /// Maximum execution steps
2271+ pub max_steps : Option < u32 > ,
2272+ /// Execution timeout (milliseconds)
2273+ pub timeout_ms : Option < u32 > ,
2274+ /// Parent SubAgent ID (for nesting)
2275+ pub parent_id : Option < String > ,
2276+ }
2277+
2278+ impl From < SubAgentConfig > for RustSubAgentConfig {
2279+ fn from ( c : SubAgentConfig ) -> Self {
2280+ let mut config = RustSubAgentConfig :: new ( c. agent_type , c. prompt ) ;
2281+ if !c. description . is_empty ( ) {
2282+ config = config. with_description ( c. description ) ;
2283+ }
2284+ config = config. with_permissive ( c. permissive ) ;
2285+ if let Some ( steps) = c. max_steps {
2286+ config = config. with_max_steps ( steps as usize ) ;
2287+ }
2288+ if let Some ( timeout) = c. timeout_ms {
2289+ config = config. with_timeout_ms ( timeout as u64 ) ;
2290+ }
2291+ if let Some ( parent) = c. parent_id {
2292+ config = config. with_parent_id ( parent) ;
2293+ }
2294+ config
2295+ }
2296+ }
2297+
2298+ /// SubAgent handle for control and monitoring.
2299+ #[ napi]
2300+ pub struct SubAgentHandle {
2301+ inner : Arc < tokio:: sync:: Mutex < RustSubAgentHandle > > ,
2302+ }
2303+
2304+ #[ napi]
2305+ impl SubAgentHandle {
2306+ /// Get SubAgent ID
2307+ #[ napi( getter) ]
2308+ pub fn id ( & self ) -> napi:: Result < String > {
2309+ let handle = self . inner . clone ( ) ;
2310+ Ok ( get_runtime ( ) . block_on ( async move {
2311+ let h = handle. lock ( ) . await ;
2312+ h. id . clone ( )
2313+ } ) )
2314+ }
2315+
2316+ /// Get current state (non-blocking)
2317+ #[ napi]
2318+ pub fn state ( & self ) -> napi:: Result < String > {
2319+ let handle = self . inner . clone ( ) ;
2320+ Ok ( get_runtime ( ) . block_on ( async move {
2321+ let h = handle. lock ( ) . await ;
2322+ let state = h. state_async ( ) . await ;
2323+ format ! ( "{:?}" , state)
2324+ } ) )
2325+ }
2326+
2327+ /// Pause execution
2328+ #[ napi]
2329+ pub fn pause ( & self ) -> napi:: Result < ( ) > {
2330+ let handle = self . inner . clone ( ) ;
2331+ get_runtime ( )
2332+ . block_on ( async move { handle. lock ( ) . await . pause ( ) . await } )
2333+ . map_err ( |e| napi:: Error :: from_reason ( format ! ( "Pause failed: {}" , e) ) )
2334+ }
2335+
2336+ /// Resume execution
2337+ #[ napi]
2338+ pub fn resume ( & self ) -> napi:: Result < ( ) > {
2339+ let handle = self . inner . clone ( ) ;
2340+ get_runtime ( )
2341+ . block_on ( async move { handle. lock ( ) . await . resume ( ) . await } )
2342+ . map_err ( |e| napi:: Error :: from_reason ( format ! ( "Resume failed: {}" , e) ) )
2343+ }
2344+
2345+ /// Cancel execution
2346+ #[ napi]
2347+ pub fn cancel ( & self ) -> napi:: Result < ( ) > {
2348+ let handle = self . inner . clone ( ) ;
2349+ get_runtime ( )
2350+ . block_on ( async move { handle. lock ( ) . await . cancel ( ) . await } )
2351+ . map_err ( |e| napi:: Error :: from_reason ( format ! ( "Cancel failed: {}" , e) ) )
2352+ }
2353+
2354+ /// Wait for completion and get result
2355+ #[ napi]
2356+ pub fn wait ( & self ) -> napi:: Result < String > {
2357+ let handle = self . inner . clone ( ) ;
2358+ get_runtime ( )
2359+ . block_on ( async move { handle. lock ( ) . await . wait ( ) . await } )
2360+ . map_err ( |e| napi:: Error :: from_reason ( format ! ( "Wait failed: {}" , e) ) )
2361+ }
2362+ }
2363+
2364+ /// Agent Orchestrator for main-sub agent coordination.
2365+ #[ napi]
2366+ pub struct Orchestrator {
2367+ inner : Arc < tokio:: sync:: Mutex < RustOrchestrator > > ,
2368+ }
2369+
2370+ #[ napi]
2371+ impl Orchestrator {
2372+ /// Create a new orchestrator with memory-based event communication
2373+ #[ napi( factory) ]
2374+ pub fn create ( ) -> Self {
2375+ Self {
2376+ inner : Arc :: new ( tokio:: sync:: Mutex :: new ( RustOrchestrator :: new_memory ( ) ) ) ,
2377+ }
2378+ }
2379+
2380+ /// Spawn a new SubAgent
2381+ #[ napi]
2382+ pub fn spawn_subagent ( & self , config : SubAgentConfig ) -> napi:: Result < SubAgentHandle > {
2383+ let orch = self . inner . clone ( ) ;
2384+ let cfg: RustSubAgentConfig = config. into ( ) ;
2385+ let handle = get_runtime ( )
2386+ . block_on ( async move { orch. lock ( ) . await . spawn_subagent ( cfg) . await } )
2387+ . map_err ( |e| napi:: Error :: from_reason ( format ! ( "Spawn failed: {}" , e) ) ) ?;
2388+ Ok ( SubAgentHandle {
2389+ inner : Arc :: new ( tokio:: sync:: Mutex :: new ( handle) ) ,
2390+ } )
2391+ }
2392+
2393+ /// Get active SubAgent count
2394+ #[ napi]
2395+ pub fn active_count ( & self ) -> napi:: Result < u32 > {
2396+ let orch = self . inner . clone ( ) ;
2397+ Ok ( get_runtime ( ) . block_on ( async move { orch. lock ( ) . await . active_count ( ) . await } ) as u32 )
2398+ }
2399+ }
0 commit comments