Skip to main content

a3s_code_core/orchestrator/
config.rs

1//! Orchestrator configuration
2
3use serde::{Deserialize, Serialize};
4
5/// Orchestrator 配置
6#[derive(Debug, Clone)]
7pub struct OrchestratorConfig {
8    /// 事件通道缓冲区大小
9    pub event_buffer_size: usize,
10
11    /// 控制信号通道缓冲区大小
12    pub control_buffer_size: usize,
13
14    /// SubAgent 最大并发数
15    pub max_concurrent_subagents: usize,
16
17    /// 事件主题前缀
18    pub subject_prefix: String,
19}
20
21impl Default for OrchestratorConfig {
22    fn default() -> Self {
23        Self {
24            event_buffer_size: 1000,
25            control_buffer_size: 100,
26            max_concurrent_subagents: 50,
27            subject_prefix: "agent".to_string(),
28        }
29    }
30}
31
32/// SubAgent 配置
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SubAgentConfig {
35    /// Agent 类型 (general, explore, plan, etc.)
36    pub agent_type: String,
37
38    /// 任务描述
39    pub description: String,
40
41    /// 执行提示词
42    pub prompt: String,
43
44    /// 是否启用宽松模式(绕过 HITL)
45    #[serde(default)]
46    pub permissive: bool,
47
48    /// Deny rules to enforce even in permissive mode (e.g., ["mcp__longvt__*"])
49    #[serde(default)]
50    pub permissive_deny: Vec<String>,
51
52    /// 最大执行步数
53    pub max_steps: Option<usize>,
54
55    /// 执行超时(毫秒)
56    pub timeout_ms: Option<u64>,
57
58    /// 父 SubAgent ID(用于嵌套)
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub parent_id: Option<String>,
61
62    /// 自定义元数据
63    #[serde(default)]
64    pub metadata: serde_json::Value,
65
66    /// Workspace directory for the SubAgent (defaults to ".")
67    #[serde(default = "default_workspace")]
68    pub workspace: String,
69
70    /// Extra directories to scan for agent definition files
71    #[serde(default)]
72    pub agent_dirs: Vec<String>,
73
74    /// Extra directories to scan for skill definition files
75    #[serde(default)]
76    pub skill_dirs: Vec<String>,
77
78    /// Lane queue configuration for External/Hybrid tool dispatch.
79    ///
80    /// When set, the SubAgent's session is created with this queue config,
81    /// enabling tools to be routed to external workers.  Any lane not
82    /// explicitly configured falls back to Internal mode.
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub lane_config: Option<crate::queue::SessionQueueConfig>,
85}
86
87/// SubAgent 信息(元数据)
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct SubAgentInfo {
90    /// SubAgent ID
91    pub id: String,
92
93    /// Agent 类型
94    pub agent_type: String,
95
96    /// 任务描述
97    pub description: String,
98
99    /// 当前状态
100    pub state: String,
101
102    /// 父 SubAgent ID
103    pub parent_id: Option<String>,
104
105    /// 创建时间(Unix 时间戳,毫秒)
106    pub created_at: u64,
107
108    /// 最后更新时间(Unix 时间戳,毫秒)
109    pub updated_at: u64,
110
111    /// 当前活动
112    pub current_activity: Option<SubAgentActivity>,
113}
114
115/// SubAgent 当前活动
116#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(tag = "type", rename_all = "snake_case")]
118pub enum SubAgentActivity {
119    /// 空闲
120    Idle,
121
122    /// 正在调用工具
123    CallingTool {
124        tool_name: String,
125        args: serde_json::Value,
126    },
127
128    /// 正在请求 LLM
129    RequestingLlm { message_count: usize },
130
131    /// 正在等待控制信号
132    WaitingForControl { reason: String },
133}
134
135impl SubAgentConfig {
136    /// 创建新的 SubAgent 配置
137    pub fn new(agent_type: impl Into<String>, prompt: impl Into<String>) -> Self {
138        Self {
139            agent_type: agent_type.into(),
140            description: String::new(),
141            prompt: prompt.into(),
142            permissive: false,
143            permissive_deny: Vec::new(),
144            max_steps: None,
145            timeout_ms: None,
146            parent_id: None,
147            metadata: serde_json::Value::Null,
148            workspace: default_workspace(),
149            agent_dirs: Vec::new(),
150            skill_dirs: Vec::new(),
151            lane_config: None,
152        }
153    }
154
155    /// 设置描述
156    pub fn with_description(mut self, description: impl Into<String>) -> Self {
157        self.description = description.into();
158        self
159    }
160
161    /// 设置宽松模式
162    pub fn with_permissive(mut self, permissive: bool) -> Self {
163        self.permissive = permissive;
164        self
165    }
166
167    /// Set deny rules to enforce even in permissive mode
168    pub fn with_permissive_deny(mut self, deny_rules: Vec<String>) -> Self {
169        self.permissive_deny = deny_rules;
170        self
171    }
172
173    /// 设置最大步数
174    pub fn with_max_steps(mut self, max_steps: usize) -> Self {
175        self.max_steps = Some(max_steps);
176        self
177    }
178
179    /// 设置超时
180    pub fn with_timeout_ms(mut self, timeout_ms: u64) -> Self {
181        self.timeout_ms = Some(timeout_ms);
182        self
183    }
184
185    /// 设置父 ID
186    pub fn with_parent_id(mut self, parent_id: impl Into<String>) -> Self {
187        self.parent_id = Some(parent_id.into());
188        self
189    }
190
191    /// 设置元数据
192    pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
193        self.metadata = metadata;
194        self
195    }
196
197    /// Set the workspace directory for this SubAgent
198    pub fn with_workspace(mut self, workspace: impl Into<String>) -> Self {
199        self.workspace = workspace.into();
200        self
201    }
202
203    /// Add extra directories to scan for agent definition files
204    pub fn with_agent_dirs(mut self, dirs: Vec<String>) -> Self {
205        self.agent_dirs = dirs;
206        self
207    }
208
209    /// Add extra directories to scan for skill definition files
210    pub fn with_skill_dirs(mut self, dirs: Vec<String>) -> Self {
211        self.skill_dirs = dirs;
212        self
213    }
214
215    /// Set lane queue configuration for External/Hybrid tool dispatch
216    pub fn with_lane_config(mut self, config: crate::queue::SessionQueueConfig) -> Self {
217        self.lane_config = Some(config);
218        self
219    }
220}
221
222fn default_workspace() -> String {
223    ".".to_string()
224}
225
226/// Unified agent slot declaration — used for both standalone subagents and team members.
227///
228/// `AgentSlot` is a superset of [`SubAgentConfig`] that also carries an optional
229/// team role.  When `role` is `None` the slot describes a standalone subagent;
230/// when set, [`crate::orchestrator::AgentOrchestrator::run_team`] uses it to
231/// build the team layout.
232#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct AgentSlot {
234    /// Agent type (general, explore, plan, etc.)
235    pub agent_type: String,
236
237    /// Team role for this slot; `None` means standalone subagent.
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub role: Option<crate::agent_teams::TeamRole>,
240
241    /// Task description
242    pub description: String,
243
244    /// Execution prompt
245    pub prompt: String,
246
247    /// Whether to bypass HITL (permissive mode)
248    #[serde(default)]
249    pub permissive: bool,
250
251    /// Deny rules to enforce even in permissive mode
252    #[serde(default)]
253    pub permissive_deny: Vec<String>,
254
255    /// Maximum execution steps
256    pub max_steps: Option<usize>,
257
258    /// Execution timeout in milliseconds
259    pub timeout_ms: Option<u64>,
260
261    /// Parent subagent ID (for nesting)
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub parent_id: Option<String>,
264
265    /// Custom metadata
266    #[serde(default)]
267    pub metadata: serde_json::Value,
268
269    /// Workspace directory for this slot (defaults to ".")
270    #[serde(default = "default_workspace")]
271    pub workspace: String,
272
273    /// Extra directories to scan for agent definition files
274    #[serde(default)]
275    pub agent_dirs: Vec<String>,
276
277    /// Extra directories to scan for skill definition files
278    #[serde(default)]
279    pub skill_dirs: Vec<String>,
280
281    /// Lane queue configuration for External/Hybrid tool dispatch
282    #[serde(skip_serializing_if = "Option::is_none")]
283    pub lane_config: Option<crate::queue::SessionQueueConfig>,
284}
285
286impl AgentSlot {
287    /// Create a new agent slot with the given agent type and prompt.
288    pub fn new(agent_type: impl Into<String>, prompt: impl Into<String>) -> Self {
289        Self {
290            agent_type: agent_type.into(),
291            role: None,
292            description: String::new(),
293            prompt: prompt.into(),
294            permissive: false,
295            permissive_deny: Vec::new(),
296            max_steps: None,
297            timeout_ms: None,
298            parent_id: None,
299            metadata: serde_json::Value::Null,
300            workspace: default_workspace(),
301            agent_dirs: Vec::new(),
302            skill_dirs: Vec::new(),
303            lane_config: None,
304        }
305    }
306
307    /// Set the team role for this slot.
308    pub fn with_role(mut self, role: crate::agent_teams::TeamRole) -> Self {
309        self.role = Some(role);
310        self
311    }
312
313    /// Set the task description.
314    pub fn with_description(mut self, description: impl Into<String>) -> Self {
315        self.description = description.into();
316        self
317    }
318
319    /// Set permissive mode (bypass HITL).
320    pub fn with_permissive(mut self, permissive: bool) -> Self {
321        self.permissive = permissive;
322        self
323    }
324
325    /// Set deny rules to enforce even in permissive mode.
326    pub fn with_permissive_deny(mut self, deny_rules: Vec<String>) -> Self {
327        self.permissive_deny = deny_rules;
328        self
329    }
330
331    /// Set the maximum number of execution steps.
332    pub fn with_max_steps(mut self, max_steps: usize) -> Self {
333        self.max_steps = Some(max_steps);
334        self
335    }
336
337    /// Set the execution timeout in milliseconds.
338    pub fn with_timeout_ms(mut self, timeout_ms: u64) -> Self {
339        self.timeout_ms = Some(timeout_ms);
340        self
341    }
342
343    /// Set the parent subagent ID.
344    pub fn with_parent_id(mut self, parent_id: impl Into<String>) -> Self {
345        self.parent_id = Some(parent_id.into());
346        self
347    }
348
349    /// Set custom metadata.
350    pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
351        self.metadata = metadata;
352        self
353    }
354
355    /// Set the workspace directory.
356    pub fn with_workspace(mut self, workspace: impl Into<String>) -> Self {
357        self.workspace = workspace.into();
358        self
359    }
360
361    /// Add extra directories to scan for agent definition files.
362    pub fn with_agent_dirs(mut self, dirs: Vec<String>) -> Self {
363        self.agent_dirs = dirs;
364        self
365    }
366
367    /// Add extra directories to scan for skill definition files.
368    pub fn with_skill_dirs(mut self, dirs: Vec<String>) -> Self {
369        self.skill_dirs = dirs;
370        self
371    }
372
373    /// Set lane queue configuration for External/Hybrid tool dispatch.
374    pub fn with_lane_config(mut self, config: crate::queue::SessionQueueConfig) -> Self {
375        self.lane_config = Some(config);
376        self
377    }
378}
379
380impl From<SubAgentConfig> for AgentSlot {
381    fn from(c: SubAgentConfig) -> Self {
382        Self {
383            agent_type: c.agent_type,
384            role: None,
385            description: c.description,
386            prompt: c.prompt,
387            permissive: c.permissive,
388            permissive_deny: c.permissive_deny,
389            max_steps: c.max_steps,
390            timeout_ms: c.timeout_ms,
391            parent_id: c.parent_id,
392            metadata: c.metadata,
393            workspace: c.workspace,
394            agent_dirs: c.agent_dirs,
395            skill_dirs: c.skill_dirs,
396            lane_config: c.lane_config,
397        }
398    }
399}
400
401impl From<AgentSlot> for SubAgentConfig {
402    fn from(s: AgentSlot) -> Self {
403        Self {
404            agent_type: s.agent_type,
405            description: s.description,
406            prompt: s.prompt,
407            permissive: s.permissive,
408            permissive_deny: s.permissive_deny,
409            max_steps: s.max_steps,
410            timeout_ms: s.timeout_ms,
411            parent_id: s.parent_id,
412            metadata: s.metadata,
413            workspace: s.workspace,
414            agent_dirs: s.agent_dirs,
415            skill_dirs: s.skill_dirs,
416            lane_config: s.lane_config,
417        }
418    }
419}