Skip to main content

yapi_types/
settings.rs

1//! `settings.json`, global and project scope.
2//!
3//! Mirrors the `Settings` interface in `packages/coding-agent/src/core/settings-manager.ts`
4//! in pi `v1.0.0`. Every field is optional; defaults belong to the code that reads them.
5//! pi edits this file as a document and keeps unknown keys, so writes go through the
6//! document, not through [`Settings`].
7#![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    /// Keyed by `provider/modelId`.
29    #[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    /// Command for npm operations, in argv form.
60    #[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    /// Paths or patterns; `!`, `+` and `-` prefixes filter.
75    #[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    /// Tool names; `+name` and `-name` adjust the inherited list.
92    #[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    /// 0 to 3.
101    #[serde(default, skip_serializing_if = "Option::is_none")]
102    pub editor_padding_x: Option<u8>,
103    /// 0 or 1.
104    #[serde(default, skip_serializing_if = "Option::is_none")]
105    pub output_pad: Option<u8>,
106    /// 3 to 20.
107    #[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    /// Milliseconds; pi also accepts a numeric string or `"disabled"`.
122    #[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    /// Milliseconds; pi also accepts a numeric string or `"disabled"`.
127    #[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    /// pi's `getImageAutoResize`: `images.autoResize`, on when unset.
143    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/// A boolean or a specific alternative value.
152#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
153#[serde(untagged)]
154pub enum BoolOr<T> {
155    Bool(bool),
156    Other(T),
157}
158
159/// A number or a specific alternative value.
160#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
161#[serde(untagged)]
162pub enum NumberOr<T> {
163    Number(u64),
164    Other(T),
165}
166
167/// The literal `"auto"`.
168#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
169#[serde(rename_all = "lowercase")]
170pub enum Auto {
171    Auto,
172}
173
174/// The literal `"header"`.
175#[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    /// Keyed by `provider/modelId`.
207    #[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/// A package to load: a source string, or a source with resource filters.
264#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
265#[serde(untagged)]
266pub enum PackageSource {
267    /// `npm:`, `git:`, a URL, or a local path.
268    Source(String),
269    Filtered(FilteredPackage),
270}
271
272#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
273pub struct FilteredPackage {
274    pub source: String,
275    /// `false` loads only resources selected by `+` patterns.
276    #[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    /// `false` disables images.
302    #[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/// Thinking token budgets for providers that take a budget instead of a level.
344#[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    /// pi's default.
423    #[default]
424    Auto,
425    Always,
426}