1#![allow(
8 missing_docs,
9 reason = "fields mirror pi's TypeScript types; contracts are noted where they differ from the name"
10)]
11
12use indexmap::IndexMap;
13use serde::{Deserialize, Serialize};
14
15use crate::message::ThinkingLevel;
16
17#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
18#[serde(rename_all = "camelCase")]
19pub struct Settings {
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub last_changelog_version: Option<String>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub default_provider: Option<String>,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub default_model: Option<String>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub default_thinking_level: Option<ThinkingLevel>,
28 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub model_thinking_levels: Option<IndexMap<String, ThinkingLevel>>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub transport: Option<Transport>,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub steering_mode: Option<QueueMode>,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub follow_up_mode: Option<QueueMode>,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub theme: Option<String>,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub compaction: Option<CompactionSettings>,
41 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub branch_summary: Option<BranchSummarySettings>,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub retry: Option<RetrySettings>,
45 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub hide_thinking_block: Option<bool>,
47 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub show_cache_miss_notices: Option<bool>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub external_editor: Option<String>,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub shell_path: Option<String>,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub quiet_startup: Option<BoolOr<Header>>,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub default_project_trust: Option<DefaultProjectTrust>,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub shell_command_prefix: Option<String>,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub npm_command: Option<Vec<String>>,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub collapse_changelog: Option<bool>,
64 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub enable_install_telemetry: Option<bool>,
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub enable_analytics: Option<bool>,
68 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub tracking_id: Option<String>,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
71 pub device_id: Option<String>,
72 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub packages: Option<Vec<PackageSource>>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub extensions: Option<Vec<String>>,
77 #[serde(default, skip_serializing_if = "Option::is_none")]
78 pub skills: Option<Vec<String>>,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub prompts: Option<Vec<String>>,
81 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub themes: Option<Vec<String>>,
83 #[serde(default, skip_serializing_if = "Option::is_none")]
84 pub enable_skill_commands: Option<bool>,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub terminal: Option<TerminalSettings>,
87 #[serde(default, skip_serializing_if = "Option::is_none")]
88 pub images: Option<ImageSettings>,
89 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub enabled_models: Option<Vec<String>>,
91 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub default_tools: Option<Vec<String>>,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
95 pub double_escape_action: Option<DoubleEscapeAction>,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub tree_filter_mode: Option<TreeFilterMode>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub thinking_budgets: Option<ThinkingBudgets>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub editor_padding_x: Option<u8>,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub output_pad: Option<u8>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub autocomplete_max_visible: Option<u8>,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub show_hardware_cursor: Option<bool>,
111 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub markdown: Option<MarkdownSettings>,
113 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub warnings: Option<WarningSettings>,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub codemode: Option<CodemodeSettings>,
117 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub session_dir: Option<String>,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub http_proxy: Option<String>,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub http_idle_timeout_ms: Option<NumberOr<String>>,
124 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub cache_warming: Option<CacheWarming>,
126 #[serde(default, skip_serializing_if = "Option::is_none")]
128 pub websocket_connect_timeout_ms: Option<NumberOr<String>>,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub tui_mode: Option<TuiMode>,
131 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub fullscreen_exit_output: Option<FullscreenExitOutput>,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub fullscreen_scrollbar: Option<Scrollbar>,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
136 pub fullscreen_copy_on_select: Option<bool>,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub fullscreen_wheel_scroll_lines: Option<NumberOr<Auto>>,
139}
140
141impl Settings {
142 pub fn image_auto_resize(&self) -> bool {
144 self.images
145 .as_ref()
146 .and_then(|images| images.auto_resize)
147 .unwrap_or(true)
148 }
149}
150
151#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
153#[serde(untagged)]
154pub enum BoolOr<T> {
155 Bool(bool),
156 Other(T),
157}
158
159#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
161#[serde(untagged)]
162pub enum NumberOr<T> {
163 Number(u64),
164 Other(T),
165}
166
167#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
169#[serde(rename_all = "lowercase")]
170pub enum Auto {
171 Auto,
172}
173
174#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
176#[serde(rename_all = "lowercase")]
177pub enum Header {
178 Header,
179}
180
181#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
182#[serde(rename_all = "kebab-case")]
183pub enum Transport {
184 Sse,
185 Websocket,
186 WebsocketCached,
187 Auto,
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
191#[serde(rename_all = "kebab-case")]
192pub enum QueueMode {
193 All,
194 OneAtATime,
195}
196
197#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
198#[serde(rename_all = "camelCase")]
199pub struct CompactionSettings {
200 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub enabled: Option<bool>,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
203 pub reserve_tokens: Option<u64>,
204 #[serde(default, skip_serializing_if = "Option::is_none")]
205 pub keep_recent_tokens: Option<u64>,
206 #[serde(default, skip_serializing_if = "Option::is_none")]
208 pub model_overrides: Option<IndexMap<String, CompactionModelOverride>>,
209}
210
211#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct CompactionModelOverride {
214 #[serde(default, skip_serializing_if = "Option::is_none")]
215 pub reserve_tokens: Option<u64>,
216 #[serde(default, skip_serializing_if = "Option::is_none")]
217 pub keep_recent_tokens: Option<u64>,
218}
219
220#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct BranchSummarySettings {
223 #[serde(default, skip_serializing_if = "Option::is_none")]
224 pub reserve_tokens: Option<u64>,
225 #[serde(default, skip_serializing_if = "Option::is_none")]
226 pub skip_prompt: Option<bool>,
227}
228
229#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
230#[serde(rename_all = "camelCase")]
231pub struct RetrySettings {
232 #[serde(default, skip_serializing_if = "Option::is_none")]
233 pub enabled: Option<bool>,
234 #[serde(default, skip_serializing_if = "Option::is_none")]
235 pub max_retries: Option<u32>,
236 #[serde(default, skip_serializing_if = "Option::is_none")]
237 pub base_delay_ms: Option<u64>,
238 #[serde(default, skip_serializing_if = "Option::is_none")]
239 pub max_agent_delay_ms: Option<u64>,
240 #[serde(default, skip_serializing_if = "Option::is_none")]
241 pub provider: Option<ProviderRetrySettings>,
242}
243
244#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
245#[serde(rename_all = "camelCase")]
246pub struct ProviderRetrySettings {
247 #[serde(default, skip_serializing_if = "Option::is_none")]
248 pub timeout_ms: Option<u64>,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub max_retries: Option<u32>,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub max_retry_delay_ms: Option<u64>,
253}
254
255#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
256#[serde(rename_all = "kebab-case")]
257pub enum DefaultProjectTrust {
258 Ask,
259 Always,
260 Never,
261}
262
263#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
265#[serde(untagged)]
266pub enum PackageSource {
267 Source(String),
269 Filtered(FilteredPackage),
270}
271
272#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
273pub struct FilteredPackage {
274 pub source: String,
275 #[serde(default, skip_serializing_if = "Option::is_none")]
277 pub autoload: Option<bool>,
278 #[serde(default, skip_serializing_if = "Option::is_none")]
279 pub extensions: Option<Vec<String>>,
280 #[serde(default, skip_serializing_if = "Option::is_none")]
281 pub skills: Option<Vec<String>>,
282 #[serde(default, skip_serializing_if = "Option::is_none")]
283 pub prompts: Option<Vec<String>>,
284 #[serde(default, skip_serializing_if = "Option::is_none")]
285 pub themes: Option<Vec<String>>,
286}
287
288#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
289#[serde(rename_all = "camelCase")]
290pub struct TerminalSettings {
291 #[serde(default, skip_serializing_if = "Option::is_none")]
292 pub show_images: Option<bool>,
293 #[serde(default, skip_serializing_if = "Option::is_none")]
294 pub image_width_cells: Option<u32>,
295 #[serde(default, skip_serializing_if = "Option::is_none")]
296 pub clear_on_shrink: Option<bool>,
297 #[serde(default, skip_serializing_if = "Option::is_none")]
298 pub show_terminal_progress: Option<bool>,
299 #[serde(default, skip_serializing_if = "Option::is_none")]
300 pub hyperlinks: Option<BoolOr<Auto>>,
301 #[serde(default, skip_serializing_if = "Option::is_none")]
303 pub images: Option<BoolOr<ImageProtocol>>,
304 #[serde(default, skip_serializing_if = "Option::is_none")]
305 pub true_color: Option<BoolOr<Auto>>,
306}
307
308#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
309#[serde(rename_all = "lowercase")]
310pub enum ImageProtocol {
311 Kitty,
312 Iterm2,
313 Auto,
314}
315
316#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
317#[serde(rename_all = "camelCase")]
318pub struct ImageSettings {
319 #[serde(default, skip_serializing_if = "Option::is_none")]
320 pub auto_resize: Option<bool>,
321 #[serde(default, skip_serializing_if = "Option::is_none")]
322 pub block_images: Option<bool>,
323}
324
325#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
326#[serde(rename_all = "kebab-case")]
327pub enum DoubleEscapeAction {
328 Fork,
329 Tree,
330 None,
331}
332
333#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
334#[serde(rename_all = "kebab-case")]
335pub enum TreeFilterMode {
336 Default,
337 NoTools,
338 UserOnly,
339 LabeledOnly,
340 All,
341}
342
343#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
345pub struct ThinkingBudgets {
346 #[serde(default, skip_serializing_if = "Option::is_none")]
347 pub minimal: Option<u64>,
348 #[serde(default, skip_serializing_if = "Option::is_none")]
349 pub low: Option<u64>,
350 #[serde(default, skip_serializing_if = "Option::is_none")]
351 pub medium: Option<u64>,
352 #[serde(default, skip_serializing_if = "Option::is_none")]
353 pub high: Option<u64>,
354}
355
356#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
357#[serde(rename_all = "camelCase")]
358pub struct MarkdownSettings {
359 #[serde(default, skip_serializing_if = "Option::is_none")]
360 pub code_block_indent: Option<String>,
361 #[serde(default, skip_serializing_if = "Option::is_none")]
362 pub mermaid: Option<MermaidMode>,
363}
364
365#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
366#[serde(rename_all = "lowercase")]
367pub enum MermaidMode {
368 Off,
369 Final,
370 Streaming,
371}
372
373#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
374#[serde(rename_all = "camelCase")]
375pub struct WarningSettings {
376 #[serde(default, skip_serializing_if = "Option::is_none")]
377 pub anthropic_extra_usage: Option<bool>,
378}
379
380#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
381#[serde(rename_all = "camelCase")]
382pub struct CodemodeSettings {
383 #[serde(default, skip_serializing_if = "Option::is_none")]
384 pub mode: Option<CodemodeMode>,
385 #[serde(default, skip_serializing_if = "Option::is_none")]
386 pub inline_budget: Option<u64>,
387}
388
389#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
390#[serde(rename_all = "lowercase")]
391pub enum CodemodeMode {
392 On,
393 Only,
394}
395
396#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
397#[serde(rename_all = "lowercase")]
398pub enum CacheWarming {
399 Off,
400 Streaming,
401 Idle,
402}
403
404#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
405#[serde(rename_all = "lowercase")]
406pub enum TuiMode {
407 Regular,
408 Fullscreen,
409}
410
411#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
412#[serde(rename_all = "kebab-case")]
413pub enum FullscreenExitOutput {
414 Transcript,
415 ResumeHint,
416}
417
418#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
419#[serde(rename_all = "lowercase")]
420pub enum Scrollbar {
421 Hidden,
422 #[default]
424 Auto,
425 Always,
426}