Skip to main content

claude_pool/
types.rs

1//! Core types for claude-pool.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6// Re-export shared types from claude-wrapper so consumers don't need
7// to depend on both crates for basic config.
8pub use claude_wrapper::types::{Effort, PermissionMode};
9
10// ── Identifiers ──────────────────────────────────────────────────────
11
12/// Unique identifier for a task.
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub struct TaskId(pub String);
15
16/// Unique identifier for a slot.
17#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
18pub struct SlotId(pub String);
19
20// ── Slot types ─────────────────────────────────────────────────────
21
22/// Slot persistence mode.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
24#[serde(rename_all = "snake_case")]
25pub enum SlotMode {
26    /// Persistent slots stay alive across tasks, resuming sessions.
27    #[default]
28    Persistent,
29    /// Ephemeral slots are created per task and destroyed after.
30    Ephemeral,
31}
32
33/// Configuration for dynamic slot pool scaling.
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ScalingConfig {
36    /// Minimum number of slots (default: 1).
37    pub min_slots: usize,
38    /// Maximum number of slots (default: 16).
39    pub max_slots: usize,
40}
41
42impl Default for ScalingConfig {
43    fn default() -> Self {
44        Self {
45            min_slots: 1,
46            max_slots: 16,
47        }
48    }
49}
50
51/// Configuration that applies to all slots by default.
52///
53/// Individual slots can override any of these fields via [`SlotConfig`].
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct PoolConfig {
56    /// Claude model to use (e.g. "claude-haiku-4-5-20251001").
57    pub model: Option<String>,
58
59    /// Permission mode for slots.
60    pub permission_mode: Option<PermissionMode>,
61
62    /// Maximum turns per task.
63    pub max_turns: Option<u32>,
64
65    /// System prompt prepended to all slot tasks.
66    pub system_prompt: Option<String>,
67
68    /// Allowed tools for slots.
69    pub allowed_tools: Vec<String>,
70
71    /// MCP servers available to slots.
72    pub mcp_servers: HashMap<String, serde_json::Value>,
73
74    /// Default effort level for slots (maps to `--effort`).
75    pub effort: Option<Effort>,
76
77    /// Total budget cap for the pool in microdollars.
78    /// When cumulative spend across all slots reaches this limit,
79    /// new tasks are rejected with [`crate::Error::BudgetExhausted`].
80    pub budget_microdollars: Option<u64>,
81
82    /// Default slot mode.
83    pub slot_mode: SlotMode,
84
85    /// Maximum number of restarts per slot before marking as errored.
86    pub max_restarts: u32,
87
88    /// Enable git worktree isolation for slots.
89    pub worktree_isolation: bool,
90
91    /// Maximum time to wait for an idle slot before failing a task (in seconds).
92    pub slot_assignment_timeout_secs: u64,
93
94    /// Dynamic scaling configuration (min/max bounds).
95    pub scaling: ScalingConfig,
96
97    /// Enable unattended mode: use stricter permission defaults to prevent prompts.
98    /// When true, defaults to `DontAsk` permission mode if not explicitly set.
99    pub unattended_mode: bool,
100
101    /// If true, detect permission prompt patterns in stderr and provide actionable errors.
102    pub detect_permission_prompts: bool,
103}
104
105impl Default for PoolConfig {
106    fn default() -> Self {
107        Self {
108            model: None,
109            permission_mode: Some(PermissionMode::Plan),
110            max_turns: None,
111            system_prompt: None,
112            allowed_tools: Vec::new(),
113            mcp_servers: HashMap::new(),
114            effort: None,
115            budget_microdollars: None,
116            slot_mode: SlotMode::default(),
117            max_restarts: 3,
118            worktree_isolation: false,
119            slot_assignment_timeout_secs: 300,
120            scaling: ScalingConfig::default(),
121            unattended_mode: false,
122            detect_permission_prompts: true,
123        }
124    }
125}
126
127/// Per-slot configuration overrides.
128///
129/// Any `Some` field here takes precedence over the corresponding field
130/// in [`PoolConfig`].
131#[derive(Debug, Clone, Default, Serialize, Deserialize)]
132pub struct SlotConfig {
133    /// Override model for this slot.
134    pub model: Option<String>,
135
136    /// Override permission mode for this slot.
137    pub permission_mode: Option<PermissionMode>,
138
139    /// Override max turns for this slot.
140    pub max_turns: Option<u32>,
141
142    /// Override system prompt for this slot.
143    pub system_prompt: Option<String>,
144
145    /// Additional allowed tools (merged with global).
146    pub allowed_tools: Option<Vec<String>>,
147
148    /// Additional MCP servers (merged with global).
149    pub mcp_servers: Option<HashMap<String, serde_json::Value>>,
150
151    /// Override effort level for this slot.
152    pub effort: Option<Effort>,
153
154    /// Optional name/role for this slot (e.g. "reviewer", "coder").
155    pub role: Option<String>,
156
157    /// Optional human-readable name for the slot (e.g. "reviewer", "writer").
158    pub name: Option<String>,
159
160    /// Optional description of the slot's purpose or responsibilities.
161    pub description: Option<String>,
162
163    /// Override slot assignment timeout (in seconds).
164    pub slot_assignment_timeout_secs: Option<u64>,
165}
166
167/// Current state of a slot.
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
169#[serde(rename_all = "snake_case")]
170pub enum SlotState {
171    /// Slot is ready to accept a task.
172    Idle,
173    /// Slot is currently executing a task.
174    Busy,
175    /// Slot process has exited or been stopped.
176    Stopped,
177    /// Slot encountered an error and needs attention.
178    Errored,
179}
180
181/// Record of a slot in the pool.
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct SlotRecord {
184    /// Unique slot identifier.
185    pub id: SlotId,
186
187    /// Current state.
188    pub state: SlotState,
189
190    /// Per-slot config overrides.
191    pub config: SlotConfig,
192
193    /// The task currently being executed, if any.
194    pub current_task: Option<TaskId>,
195
196    /// Claude session ID for session resumption.
197    pub session_id: Option<String>,
198
199    /// Number of tasks completed by this slot.
200    pub tasks_completed: u64,
201
202    /// Cumulative cost in microdollars.
203    pub cost_microdollars: u64,
204
205    /// Number of times this slot has been restarted.
206    pub restart_count: u32,
207
208    /// Git worktree path, if worktree isolation is enabled.
209    pub worktree_path: Option<String>,
210}
211
212// ── Task types ───────────────────────────────────────────────────────
213
214/// Current state of a task.
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
216#[serde(rename_all = "snake_case")]
217pub enum TaskState {
218    /// Task is waiting for a slot.
219    Pending,
220    /// Task is being executed by a slot.
221    Running,
222    /// Task completed successfully.
223    Completed,
224    /// Task failed.
225    Failed,
226    /// Task was cancelled.
227    Cancelled,
228}
229
230/// A task submitted to the pool.
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct TaskRecord {
233    /// Unique task identifier.
234    pub id: TaskId,
235
236    /// The prompt/instruction for the task.
237    pub prompt: String,
238
239    /// Current state.
240    pub state: TaskState,
241
242    /// Slot assigned to this task.
243    pub slot_id: Option<SlotId>,
244
245    /// Task result, available when state is `Completed` or `Failed`.
246    pub result: Option<TaskResult>,
247
248    /// Optional tags for filtering and grouping.
249    pub tags: Vec<String>,
250
251    /// Per-task config overrides (takes precedence over slot and global config).
252    pub config: Option<SlotConfig>,
253}
254
255/// The result of a completed task.
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct TaskResult {
258    /// The text output from Claude.
259    pub output: String,
260
261    /// Whether the task succeeded.
262    pub success: bool,
263
264    /// Cost in microdollars.
265    pub cost_microdollars: u64,
266
267    /// Number of turns used.
268    pub turns_used: u32,
269
270    /// Session ID from the execution.
271    pub session_id: Option<String>,
272}
273
274/// Filter criteria for listing tasks.
275#[derive(Debug, Clone, Default, Serialize, Deserialize)]
276pub struct TaskFilter {
277    /// Filter by state.
278    pub state: Option<TaskState>,
279
280    /// Filter by slot.
281    pub slot_id: Option<SlotId>,
282
283    /// Filter by tags (any match).
284    pub tags: Option<Vec<String>>,
285}