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