1use crate::constants::defaults::DEFAULT_ID_WIDTH;
18use crate::constants::limits::{
19 DEFAULT_SIZE_WARNING_THRESHOLD_KB, DEFAULT_TASK_COUNT_WARNING_THRESHOLD,
20};
21use crate::constants::queue::{DEFAULT_DONE_FILE, DEFAULT_QUEUE_FILE};
22use crate::constants::timeouts::DEFAULT_SESSION_TIMEOUT_HOURS;
23use crate::contracts::model::{Model, ReasoningEffort};
24use crate::contracts::runner::{
25 ClaudePermissionMode, Runner, RunnerApprovalMode, RunnerCliConfigRoot, RunnerCliOptionsPatch,
26 RunnerOutputFormat, RunnerPlanMode, RunnerSandboxMode, RunnerVerbosity,
27 UnsupportedOptionPolicy,
28};
29use schemars::JsonSchema;
30use serde::{Deserialize, Serialize};
31use std::collections::BTreeMap;
32use std::path::PathBuf;
33
34mod agent;
36mod enums;
37mod loop_;
38mod notification;
39mod parallel;
40mod phase;
41mod plugin;
42mod queue;
43mod retry;
44#[cfg(test)]
45mod tests;
46mod webhook;
47
48pub use agent::AgentConfig;
50pub use enums::{GitRevertMode, ProjectType, ScanPromptVersion};
51pub use loop_::LoopConfig;
52pub use notification::NotificationConfig;
53pub use parallel::{ParallelConfig, default_push_backoff_ms};
54pub use phase::{PhaseOverrideConfig, PhaseOverrides};
55pub use plugin::{PluginConfig, PluginProcessorConfig, PluginRunnerConfig, PluginsConfig};
56pub use queue::{QueueAgingThresholds, QueueConfig};
57pub use retry::RunnerRetryConfig;
58pub use webhook::{WebhookConfig, WebhookEventSubscription, WebhookQueuePolicy};
59
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
71#[serde(default, deny_unknown_fields)]
72pub struct Config {
73 pub version: u32,
75
76 pub project_type: Option<ProjectType>,
78
79 pub queue: QueueConfig,
81
82 pub agent: AgentConfig,
84
85 pub parallel: ParallelConfig,
87
88 #[serde(rename = "loop")]
90 pub loop_field: LoopConfig,
91
92 pub plugins: PluginsConfig,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
100 pub profiles: Option<BTreeMap<String, AgentConfig>>,
101}
102
103impl Default for Config {
106 fn default() -> Self {
107 use std::collections::BTreeMap;
108 Self {
109 version: 1,
110 project_type: Some(ProjectType::Code),
111 queue: QueueConfig {
112 file: Some(PathBuf::from(DEFAULT_QUEUE_FILE)),
113 done_file: Some(PathBuf::from(DEFAULT_DONE_FILE)),
114 id_prefix: Some("RQ".to_string()),
115 id_width: Some(DEFAULT_ID_WIDTH as u8),
116 size_warning_threshold_kb: Some(DEFAULT_SIZE_WARNING_THRESHOLD_KB),
117 task_count_warning_threshold: Some(DEFAULT_TASK_COUNT_WARNING_THRESHOLD),
118 max_dependency_depth: Some(10),
119 auto_archive_terminal_after_days: None,
120 aging_thresholds: Some(QueueAgingThresholds {
121 warning_days: Some(7),
122 stale_days: Some(14),
123 rotten_days: Some(30),
124 }),
125 },
126 agent: AgentConfig {
127 runner: Some(Runner::Codex),
128 model: Some(Model::Gpt54),
129 reasoning_effort: Some(ReasoningEffort::Medium),
130 iterations: Some(1),
131 followup_reasoning_effort: None,
132 codex_bin: Some("codex".to_string()),
133 opencode_bin: Some("opencode".to_string()),
134 gemini_bin: Some("gemini".to_string()),
135 claude_bin: Some("claude".to_string()),
136 cursor_bin: Some("agent".to_string()),
137 kimi_bin: Some("kimi".to_string()),
138 pi_bin: Some("pi".to_string()),
139 phases: Some(3),
140 claude_permission_mode: Some(ClaudePermissionMode::BypassPermissions),
141 runner_cli: Some(RunnerCliConfigRoot {
142 defaults: RunnerCliOptionsPatch {
143 output_format: Some(RunnerOutputFormat::StreamJson),
144 verbosity: Some(RunnerVerbosity::Normal),
145 approval_mode: Some(RunnerApprovalMode::Yolo),
146 sandbox: Some(RunnerSandboxMode::Default),
147 plan_mode: Some(RunnerPlanMode::Default),
148 unsupported_option_policy: Some(UnsupportedOptionPolicy::Warn),
149 },
150 runners: BTreeMap::from([
151 (
152 Runner::Codex,
153 RunnerCliOptionsPatch {
154 sandbox: Some(RunnerSandboxMode::Disabled),
155 ..RunnerCliOptionsPatch::default()
156 },
157 ),
158 (
159 Runner::Claude,
160 RunnerCliOptionsPatch {
161 verbosity: Some(RunnerVerbosity::Verbose),
162 ..RunnerCliOptionsPatch::default()
163 },
164 ),
165 (
166 Runner::Kimi,
167 RunnerCliOptionsPatch {
168 approval_mode: Some(RunnerApprovalMode::Yolo),
169 ..RunnerCliOptionsPatch::default()
170 },
171 ),
172 (
173 Runner::Pi,
174 RunnerCliOptionsPatch {
175 approval_mode: Some(RunnerApprovalMode::Yolo),
176 ..RunnerCliOptionsPatch::default()
177 },
178 ),
179 ]),
180 }),
181 phase_overrides: None,
182 instruction_files: None,
183 repoprompt_plan_required: Some(false),
184 repoprompt_tool_injection: Some(false),
185 ci_gate_command: Some("make ci".to_string()),
186 ci_gate_enabled: Some(true),
187 git_revert_mode: Some(GitRevertMode::Ask),
188 git_commit_push_enabled: Some(true),
189 notification: NotificationConfig {
190 enabled: Some(true),
191 notify_on_complete: Some(true),
192 notify_on_fail: Some(true),
193 notify_on_loop_complete: Some(true),
194 suppress_when_active: Some(true),
195 sound_enabled: Some(false),
196 sound_path: None,
197 timeout_ms: Some(8000),
198 },
199 webhook: WebhookConfig::default(),
200 runner_retry: RunnerRetryConfig::default(),
201 session_timeout_hours: Some(DEFAULT_SESSION_TIMEOUT_HOURS),
202 scan_prompt_version: Some(ScanPromptVersion::V2),
203 },
204 parallel: ParallelConfig {
205 workers: None,
206 workspace_root: None,
207 max_push_attempts: Some(50),
208 push_backoff_ms: Some(default_push_backoff_ms()),
209 workspace_retention_hours: Some(24),
210 },
211 loop_field: LoopConfig {
212 wait_when_empty: Some(false),
213 empty_poll_ms: Some(30_000),
214 wait_when_blocked: Some(false),
215 wait_poll_ms: Some(1000),
216 wait_timeout_seconds: Some(0),
217 notify_when_unblocked: Some(false),
218 },
219 plugins: PluginsConfig::default(),
220 profiles: None,
221 }
222 }
223}