ralph/contracts/config/
agent.rs1use crate::contracts::config::{
11 GitRevertMode, NotificationConfig, PhaseOverrides, RunnerRetryConfig, ScanPromptVersion,
12 WebhookConfig,
13};
14use crate::contracts::model::{Model, ReasoningEffort};
15use crate::contracts::runner::{ClaudePermissionMode, Runner, RunnerCliConfigRoot};
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18use std::path::PathBuf;
19
20#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
22#[serde(default, deny_unknown_fields)]
23pub struct AgentConfig {
24 pub runner: Option<Runner>,
26
27 pub model: Option<Model>,
29
30 pub reasoning_effort: Option<ReasoningEffort>,
32
33 #[schemars(range(min = 1))]
35 pub iterations: Option<u8>,
36
37 pub followup_reasoning_effort: Option<ReasoningEffort>,
40
41 pub codex_bin: Option<String>,
43
44 pub opencode_bin: Option<String>,
46
47 pub gemini_bin: Option<String>,
49
50 pub claude_bin: Option<String>,
52
53 pub cursor_bin: Option<String>,
57
58 pub kimi_bin: Option<String>,
60
61 pub pi_bin: Option<String>,
63
64 pub claude_permission_mode: Option<ClaudePermissionMode>,
68
69 pub runner_cli: Option<RunnerCliConfigRoot>,
73
74 pub phase_overrides: Option<PhaseOverrides>,
79
80 pub instruction_files: Option<Vec<PathBuf>>,
85
86 pub repoprompt_plan_required: Option<bool>,
88
89 pub repoprompt_tool_injection: Option<bool>,
91
92 pub ci_gate_command: Option<String>,
94
95 pub ci_gate_enabled: Option<bool>,
97
98 pub git_revert_mode: Option<GitRevertMode>,
100
101 pub git_commit_push_enabled: Option<bool>,
103
104 #[schemars(range(min = 1, max = 3))]
107 pub phases: Option<u8>,
108
109 pub notification: NotificationConfig,
111
112 pub webhook: WebhookConfig,
114
115 #[schemars(range(min = 1))]
119 pub session_timeout_hours: Option<u64>,
120
121 pub scan_prompt_version: Option<ScanPromptVersion>,
123
124 pub runner_retry: RunnerRetryConfig,
126}
127
128impl AgentConfig {
129 pub fn merge_from(&mut self, other: Self) {
130 if other.runner.is_some() {
131 self.runner = other.runner;
132 }
133 if other.model.is_some() {
134 self.model = other.model;
135 }
136 if other.reasoning_effort.is_some() {
137 self.reasoning_effort = other.reasoning_effort;
138 }
139 if other.iterations.is_some() {
140 self.iterations = other.iterations;
141 }
142 if other.followup_reasoning_effort.is_some() {
143 self.followup_reasoning_effort = other.followup_reasoning_effort;
144 }
145 if other.codex_bin.is_some() {
146 self.codex_bin = other.codex_bin;
147 }
148 if other.opencode_bin.is_some() {
149 self.opencode_bin = other.opencode_bin;
150 }
151 if other.gemini_bin.is_some() {
152 self.gemini_bin = other.gemini_bin;
153 }
154 if other.claude_bin.is_some() {
155 self.claude_bin = other.claude_bin;
156 }
157 if other.cursor_bin.is_some() {
158 self.cursor_bin = other.cursor_bin;
159 }
160 if other.kimi_bin.is_some() {
161 self.kimi_bin = other.kimi_bin;
162 }
163 if other.pi_bin.is_some() {
164 self.pi_bin = other.pi_bin;
165 }
166 if other.phases.is_some() {
167 self.phases = other.phases;
168 }
169 if other.claude_permission_mode.is_some() {
170 self.claude_permission_mode = other.claude_permission_mode;
171 }
172 if let Some(other_runner_cli) = other.runner_cli {
173 match &mut self.runner_cli {
174 Some(existing) => existing.merge_from(other_runner_cli),
175 None => self.runner_cli = Some(other_runner_cli),
176 }
177 }
178 if let Some(other_phase_overrides) = other.phase_overrides {
180 match &mut self.phase_overrides {
181 Some(existing) => existing.merge_from(other_phase_overrides),
182 None => self.phase_overrides = Some(other_phase_overrides),
183 }
184 }
185 if other.instruction_files.is_some() {
186 self.instruction_files = other.instruction_files;
187 }
188 if other.repoprompt_plan_required.is_some() {
189 self.repoprompt_plan_required = other.repoprompt_plan_required;
190 }
191 if other.repoprompt_tool_injection.is_some() {
192 self.repoprompt_tool_injection = other.repoprompt_tool_injection;
193 }
194 if other.ci_gate_command.is_some() {
195 self.ci_gate_command = other.ci_gate_command;
196 }
197 if other.ci_gate_enabled.is_some() {
198 self.ci_gate_enabled = other.ci_gate_enabled;
199 }
200 if other.git_revert_mode.is_some() {
201 self.git_revert_mode = other.git_revert_mode;
202 }
203 if other.git_commit_push_enabled.is_some() {
204 self.git_commit_push_enabled = other.git_commit_push_enabled;
205 }
206 self.notification.merge_from(other.notification);
207 self.webhook.merge_from(other.webhook);
208 if other.session_timeout_hours.is_some() {
209 self.session_timeout_hours = other.session_timeout_hours;
210 }
211 if other.scan_prompt_version.is_some() {
212 self.scan_prompt_version = other.scan_prompt_version;
213 }
214 self.runner_retry.merge_from(other.runner_retry);
215 }
216}