@oh-my-pi/pi-coding-agent 14.6.2 → 14.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +71 -2
  2. package/README.md +21 -0
  3. package/package.json +23 -7
  4. package/src/cli/grievances-cli.ts +89 -4
  5. package/src/commands/grievances.ts +33 -7
  6. package/src/config/prompt-templates.ts +14 -7
  7. package/src/config/settings-schema.ts +585 -100
  8. package/src/config/settings.ts +42 -0
  9. package/src/discovery/helpers.ts +13 -6
  10. package/src/edit/index.ts +3 -3
  11. package/src/edit/line-hash.ts +73 -25
  12. package/src/edit/modes/hashline.lark +10 -3
  13. package/src/edit/modes/hashline.ts +104 -38
  14. package/src/edit/renderer.ts +3 -3
  15. package/src/hindsight/backend.ts +444 -0
  16. package/src/hindsight/bank.ts +131 -0
  17. package/src/hindsight/client.ts +445 -0
  18. package/src/hindsight/config.ts +165 -0
  19. package/src/hindsight/content.ts +205 -0
  20. package/src/hindsight/index.ts +6 -0
  21. package/src/hindsight/retain-queue.ts +166 -0
  22. package/src/hindsight/transcript.ts +71 -0
  23. package/src/main.ts +7 -10
  24. package/src/memories/index.ts +1 -1
  25. package/src/memory-backend/index.ts +4 -0
  26. package/src/memory-backend/local-backend.ts +30 -0
  27. package/src/memory-backend/off-backend.ts +16 -0
  28. package/src/memory-backend/resolve.ts +24 -0
  29. package/src/memory-backend/types.ts +69 -0
  30. package/src/modes/components/settings-defs.ts +50 -451
  31. package/src/modes/components/settings-selector.ts +2 -2
  32. package/src/modes/components/status-line/presets.ts +1 -1
  33. package/src/modes/controllers/command-controller.ts +6 -5
  34. package/src/modes/controllers/event-controller.ts +12 -0
  35. package/src/modes/controllers/selector-controller.ts +3 -12
  36. package/src/modes/theme/theme.ts +4 -0
  37. package/src/prompts/tools/github.md +3 -0
  38. package/src/prompts/tools/hashline.md +20 -16
  39. package/src/prompts/tools/read.md +10 -6
  40. package/src/prompts/tools/recall.md +5 -0
  41. package/src/prompts/tools/reflect.md +5 -0
  42. package/src/prompts/tools/retain.md +5 -0
  43. package/src/prompts/tools/search.md +1 -1
  44. package/src/sdk.ts +12 -9
  45. package/src/session/agent-session.ts +75 -3
  46. package/src/slash-commands/builtin-registry.ts +2 -12
  47. package/src/tools/ast-edit.ts +14 -5
  48. package/src/tools/ast-grep.ts +12 -3
  49. package/src/tools/find.ts +47 -7
  50. package/src/tools/gh-renderer.ts +10 -1
  51. package/src/tools/gh.ts +233 -5
  52. package/src/tools/hindsight-recall.ts +70 -0
  53. package/src/tools/hindsight-reflect.ts +57 -0
  54. package/src/tools/hindsight-retain.ts +63 -0
  55. package/src/tools/index.ts +17 -0
  56. package/src/tools/path-utils.ts +55 -0
  57. package/src/tools/read.ts +1 -1
  58. package/src/tools/search.ts +45 -8
@@ -1,5 +1,6 @@
1
1
  import { THINKING_EFFORTS } from "@oh-my-pi/pi-ai";
2
2
  import { TASK_SIMPLE_MODES } from "../task/simple-mode";
3
+ import { getThinkingLevelMetadata } from "../thinking";
3
4
  import { EDIT_MODES } from "../utils/edit-mode";
4
5
 
5
6
  /** Unified settings schema - single source of truth for all settings.
@@ -23,6 +24,7 @@ export type SettingTab =
23
24
  | "model"
24
25
  | "interaction"
25
26
  | "context"
27
+ | "memory"
26
28
  | "editing"
27
29
  | "tools"
28
30
  | "tasks"
@@ -37,6 +39,7 @@ export const SETTING_TABS: SettingTab[] = [
37
39
  "model",
38
40
  "interaction",
39
41
  "context",
42
+ "memory",
40
43
  "editing",
41
44
  "tools",
42
45
  "tasks",
@@ -49,6 +52,7 @@ export const TAB_METADATA: Record<SettingTab, { label: string; icon: `tab.${stri
49
52
  model: { label: "Model", icon: "tab.model" },
50
53
  interaction: { label: "Interaction", icon: "tab.interaction" },
51
54
  context: { label: "Context", icon: "tab.context" },
55
+ memory: { label: "Memory", icon: "tab.memory" },
52
56
  editing: { label: "Editing", icon: "tab.editing" },
53
57
  tools: { label: "Tools", icon: "tab.tools" },
54
58
  tasks: { label: "Tasks", icon: "tab.tasks" },
@@ -79,51 +83,83 @@ export type StatusLineSegmentId =
79
83
  | "cache_write"
80
84
  | "session_name";
81
85
 
82
- interface UiMetadata {
86
+ /** Submenu choice metadata. */
87
+ export type SubmenuOption<V extends string = string> = {
88
+ value: V;
89
+ label: string;
90
+ description?: string;
91
+ };
92
+
93
+ interface UiBase {
83
94
  tab: SettingTab;
84
95
  label: string;
85
96
  description: string;
86
- /** For enum/submenu - display as inline toggle vs dropdown */
87
- submenu?: boolean;
88
97
  /** Condition function name - setting only shown when true */
89
98
  condition?: string;
90
99
  }
91
100
 
101
+ interface UiBoolean extends UiBase {}
102
+
103
+ interface UiEnum<T extends readonly string[]> extends UiBase {
104
+ /** Submenu options. When omitted, the enum renders as an inline toggle derived from `values`. */
105
+ options?: ReadonlyArray<SubmenuOption<T[number]>>;
106
+ }
107
+
108
+ interface UiNumber extends UiBase {
109
+ /** Submenu options. Without options, a numeric setting has no UI representation (intentional hide). */
110
+ options?: ReadonlyArray<SubmenuOption>;
111
+ }
112
+
113
+ interface UiString extends UiBase {
114
+ /**
115
+ * Submenu options.
116
+ * - Array → submenu with these choices.
117
+ * - "runtime" → submenu populated by the runtime layer (theme registry, etc.).
118
+ * - Omitted → renders as a free text input.
119
+ */
120
+ options?: ReadonlyArray<SubmenuOption> | "runtime";
121
+ }
122
+
123
+ /** Wide ui shape exposed to consumers that walk the schema generically. */
124
+ export type AnyUiMetadata = UiBase & {
125
+ options?: ReadonlyArray<SubmenuOption> | "runtime";
126
+ };
127
+
92
128
  interface BooleanDef {
93
129
  type: "boolean";
94
130
  default: boolean;
95
- ui?: UiMetadata;
131
+ ui?: UiBoolean;
96
132
  }
97
133
 
98
134
  interface StringDef {
99
135
  type: "string";
100
136
  default: string | undefined;
101
- ui?: UiMetadata;
137
+ ui?: UiString;
102
138
  }
103
139
 
104
140
  interface NumberDef {
105
141
  type: "number";
106
142
  default: number;
107
- ui?: UiMetadata;
143
+ ui?: UiNumber;
108
144
  }
109
145
 
110
146
  interface EnumDef<T extends readonly string[]> {
111
147
  type: "enum";
112
148
  values: T;
113
149
  default: T[number];
114
- ui?: UiMetadata;
150
+ ui?: UiEnum<T>;
115
151
  }
116
152
 
117
153
  interface ArrayDef<T> {
118
154
  type: "array";
119
155
  default: T[];
120
- ui?: UiMetadata;
156
+ ui?: UiBase;
121
157
  }
122
158
 
123
159
  interface RecordDef<T> {
124
160
  type: "record";
125
161
  default: Record<string, T>;
126
- ui?: UiMetadata;
162
+ ui?: UiBase;
127
163
  }
128
164
 
129
165
  type SettingDef =
@@ -153,6 +189,7 @@ const EMPTY_STRING_ARRAY: string[] = [];
153
189
  const EMPTY_STRING_RECORD: Record<string, string> = {};
154
190
  const DEFAULT_CYCLE_ORDER: string[] = ["smol", "default", "slow"];
155
191
  const EMPTY_MODEL_TAGS_RECORD: ModelTagsSettings = {};
192
+ const HINDSIGHT_RECALL_TYPES_DEFAULT: string[] = ["world", "experience"];
156
193
  export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
157
194
  {
158
195
  pattern: "^\\s*(cat|head|tail|less|more)\\s+",
@@ -218,7 +255,11 @@ export const SETTINGS_SCHEMA = {
218
255
  tab: "tools",
219
256
  label: "Marketplace Auto-Update",
220
257
  description: "Check for plugin updates on startup (off/notify/auto)",
221
- submenu: true,
258
+ options: [
259
+ { value: "off", label: "Off", description: "Don't check for plugin updates" },
260
+ { value: "notify", label: "Notify", description: "Check on startup and notify when updates are available" },
261
+ { value: "auto", label: "Auto", description: "Check on startup and auto-install updates" },
262
+ ],
222
263
  },
223
264
  },
224
265
 
@@ -248,7 +289,7 @@ export const SETTINGS_SCHEMA = {
248
289
  tab: "appearance",
249
290
  label: "Dark Theme",
250
291
  description: "Theme used when terminal has dark background",
251
- submenu: true,
292
+ options: "runtime",
252
293
  },
253
294
  },
254
295
 
@@ -259,7 +300,7 @@ export const SETTINGS_SCHEMA = {
259
300
  tab: "appearance",
260
301
  label: "Light Theme",
261
302
  description: "Theme used when terminal has light background",
262
- submenu: true,
303
+ options: "runtime",
263
304
  },
264
305
  },
265
306
 
@@ -267,7 +308,16 @@ export const SETTINGS_SCHEMA = {
267
308
  type: "enum",
268
309
  values: ["unicode", "nerd", "ascii"] as const,
269
310
  default: "unicode",
270
- ui: { tab: "appearance", label: "Symbol Preset", description: "Icon/symbol style", submenu: true },
311
+ ui: {
312
+ tab: "appearance",
313
+ label: "Symbol Preset",
314
+ description: "Icon/symbol style",
315
+ options: [
316
+ { value: "unicode", label: "Unicode", description: "Standard symbols (default)" },
317
+ { value: "nerd", label: "Nerd Font", description: "Requires Nerd Font" },
318
+ { value: "ascii", label: "ASCII", description: "Maximum compatibility" },
319
+ ],
320
+ },
271
321
  },
272
322
 
273
323
  colorBlindMode: {
@@ -289,7 +339,15 @@ export const SETTINGS_SCHEMA = {
289
339
  tab: "appearance",
290
340
  label: "Status Line Preset",
291
341
  description: "Pre-built status line configurations",
292
- submenu: true,
342
+ options: [
343
+ { value: "default", label: "Default", description: "Model, path, git, context, tokens, cost" },
344
+ { value: "minimal", label: "Minimal", description: "Path and git only" },
345
+ { value: "compact", label: "Compact", description: "Model, git, cost, context" },
346
+ { value: "full", label: "Full", description: "All segments including time" },
347
+ { value: "nerd", label: "Nerd", description: "Maximum info with Nerd Font icons" },
348
+ { value: "ascii", label: "ASCII", description: "No special characters" },
349
+ { value: "custom", label: "Custom", description: "User-defined segments" },
350
+ ],
293
351
  },
294
352
  },
295
353
 
@@ -301,7 +359,15 @@ export const SETTINGS_SCHEMA = {
301
359
  tab: "appearance",
302
360
  label: "Status Line Separator",
303
361
  description: "Style of separators between segments",
304
- submenu: true,
362
+ options: [
363
+ { value: "powerline", label: "Powerline", description: "Solid arrows (Nerd Font)" },
364
+ { value: "powerline-thin", label: "Thin chevron", description: "Thin arrows (Nerd Font)" },
365
+ { value: "slash", label: "Slash", description: "Forward slashes" },
366
+ { value: "pipe", label: "Pipe", description: "Vertical pipes" },
367
+ { value: "block", label: "Block", description: "Solid blocks" },
368
+ { value: "none", label: "None", description: "Space only" },
369
+ { value: "ascii", label: "ASCII", description: "Greater-than signs" },
370
+ ],
305
371
  },
306
372
  },
307
373
 
@@ -321,7 +387,20 @@ export const SETTINGS_SCHEMA = {
321
387
  tab: "tools",
322
388
  label: "Artifact spill threshold (KB)",
323
389
  description: "Tool output above this size is saved as an artifact; tail is kept inline",
324
- submenu: true,
390
+ options: [
391
+ { value: "1", label: "1 KB", description: "~250 tokens" },
392
+ { value: "2.5", label: "2.5 KB", description: "~625 tokens" },
393
+ { value: "5", label: "5 KB", description: "~1.25K tokens" },
394
+ { value: "10", label: "10 KB", description: "~2.5K tokens" },
395
+ { value: "20", label: "20 KB", description: "~5K tokens" },
396
+ { value: "30", label: "30 KB", description: "~7.5K tokens" },
397
+ { value: "50", label: "50 KB", description: "Default; ~12.5K tokens" },
398
+ { value: "75", label: "75 KB", description: "~19K tokens" },
399
+ { value: "100", label: "100 KB", description: "~25K tokens" },
400
+ { value: "200", label: "200 KB", description: "~50K tokens" },
401
+ { value: "500", label: "500 KB", description: "~125K tokens" },
402
+ { value: "1000", label: "1 MB", description: "~250K tokens" },
403
+ ],
325
404
  },
326
405
  },
327
406
  "tools.artifactTailBytes": {
@@ -331,7 +410,16 @@ export const SETTINGS_SCHEMA = {
331
410
  tab: "tools",
332
411
  label: "Artifact tail size (KB)",
333
412
  description: "Amount of tail content kept inline when output spills to artifact",
334
- submenu: true,
413
+ options: [
414
+ { value: "1", label: "1 KB", description: "~250 tokens" },
415
+ { value: "2.5", label: "2.5 KB", description: "~625 tokens" },
416
+ { value: "5", label: "5 KB", description: "~1.25K tokens" },
417
+ { value: "10", label: "10 KB", description: "~2.5K tokens" },
418
+ { value: "20", label: "20 KB", description: "Default; ~5K tokens" },
419
+ { value: "50", label: "50 KB", description: "~12.5K tokens" },
420
+ { value: "100", label: "100 KB", description: "~25K tokens" },
421
+ { value: "200", label: "200 KB", description: "~50K tokens" },
422
+ ],
335
423
  },
336
424
  },
337
425
  "tools.artifactTailLines": {
@@ -341,7 +429,15 @@ export const SETTINGS_SCHEMA = {
341
429
  tab: "tools",
342
430
  label: "Artifact tail lines",
343
431
  description: "Maximum lines of tail content kept inline when output spills to artifact",
344
- submenu: true,
432
+ options: [
433
+ { value: "50", label: "50 lines", description: "~250 tokens" },
434
+ { value: "100", label: "100 lines", description: "~500 tokens" },
435
+ { value: "250", label: "250 lines", description: "~1.25K tokens" },
436
+ { value: "500", label: "500 lines", description: "Default; ~2.5K tokens" },
437
+ { value: "1000", label: "1000 lines", description: "~5K tokens" },
438
+ { value: "2000", label: "2000 lines", description: "~10K tokens" },
439
+ { value: "5000", label: "5000 lines", description: "~25K tokens" },
440
+ ],
345
441
  },
346
442
  },
347
443
 
@@ -406,12 +502,6 @@ export const SETTINGS_SCHEMA = {
406
502
  "display.tabWidth": {
407
503
  type: "number",
408
504
  default: 3,
409
- ui: {
410
- tab: "appearance",
411
- label: "Tab Width",
412
- description: "Default number of spaces used when rendering tab characters",
413
- submenu: true,
414
- },
415
505
  },
416
506
 
417
507
  "display.showTokenUsage": {
@@ -453,7 +543,7 @@ export const SETTINGS_SCHEMA = {
453
543
  tab: "model",
454
544
  label: "Thinking Level",
455
545
  description: "Reasoning depth for thinking-capable models",
456
- submenu: true,
546
+ options: [...THINKING_EFFORTS.map(getThinkingLevelMetadata)],
457
547
  },
458
548
  },
459
549
 
@@ -481,7 +571,14 @@ export const SETTINGS_SCHEMA = {
481
571
  tab: "model",
482
572
  label: "Temperature",
483
573
  description: "Sampling temperature (0 = deterministic, 1 = creative, -1 = provider default)",
484
- submenu: true,
574
+ options: [
575
+ { value: "-1", label: "Default", description: "Use provider default" },
576
+ { value: "0", label: "0", description: "Deterministic" },
577
+ { value: "0.2", label: "0.2", description: "Focused" },
578
+ { value: "0.5", label: "0.5", description: "Balanced" },
579
+ { value: "0.7", label: "0.7", description: "Creative" },
580
+ { value: "1", label: "1", description: "Maximum variety" },
581
+ ],
485
582
  },
486
583
  },
487
584
 
@@ -492,7 +589,14 @@ export const SETTINGS_SCHEMA = {
492
589
  tab: "model",
493
590
  label: "Top P",
494
591
  description: "Nucleus sampling cutoff (0-1, -1 = provider default)",
495
- submenu: true,
592
+ options: [
593
+ { value: "-1", label: "Default", description: "Use provider default" },
594
+ { value: "0.1", label: "0.1", description: "Very focused" },
595
+ { value: "0.3", label: "0.3", description: "Focused" },
596
+ { value: "0.5", label: "0.5", description: "Balanced" },
597
+ { value: "0.9", label: "0.9", description: "Broad" },
598
+ { value: "1", label: "1", description: "No nucleus filtering" },
599
+ ],
496
600
  },
497
601
  },
498
602
 
@@ -503,7 +607,13 @@ export const SETTINGS_SCHEMA = {
503
607
  tab: "model",
504
608
  label: "Top K",
505
609
  description: "Sample from top-K tokens (-1 = provider default)",
506
- submenu: true,
610
+ options: [
611
+ { value: "-1", label: "Default", description: "Use provider default" },
612
+ { value: "1", label: "1", description: "Greedy top token" },
613
+ { value: "20", label: "20", description: "Focused" },
614
+ { value: "40", label: "40", description: "Balanced" },
615
+ { value: "100", label: "100", description: "Broad" },
616
+ ],
507
617
  },
508
618
  },
509
619
 
@@ -514,7 +624,12 @@ export const SETTINGS_SCHEMA = {
514
624
  tab: "model",
515
625
  label: "Min P",
516
626
  description: "Minimum probability threshold (0-1, -1 = provider default)",
517
- submenu: true,
627
+ options: [
628
+ { value: "-1", label: "Default", description: "Use provider default" },
629
+ { value: "0.01", label: "0.01", description: "Very permissive" },
630
+ { value: "0.05", label: "0.05", description: "Balanced" },
631
+ { value: "0.1", label: "0.1", description: "Strict" },
632
+ ],
518
633
  },
519
634
  },
520
635
 
@@ -525,7 +640,13 @@ export const SETTINGS_SCHEMA = {
525
640
  tab: "model",
526
641
  label: "Presence Penalty",
527
642
  description: "Penalty for introducing already-present tokens (-1 = provider default)",
528
- submenu: true,
643
+ options: [
644
+ { value: "-1", label: "Default", description: "Use provider default" },
645
+ { value: "0", label: "0", description: "No penalty" },
646
+ { value: "0.5", label: "0.5", description: "Mild novelty" },
647
+ { value: "1", label: "1", description: "Encourage novelty" },
648
+ { value: "2", label: "2", description: "Strong novelty" },
649
+ ],
529
650
  },
530
651
  },
531
652
 
@@ -536,7 +657,14 @@ export const SETTINGS_SCHEMA = {
536
657
  tab: "model",
537
658
  label: "Repetition Penalty",
538
659
  description: "Penalty for repeated tokens (-1 = provider default)",
539
- submenu: true,
660
+ options: [
661
+ { value: "-1", label: "Default", description: "Use provider default" },
662
+ { value: "0.8", label: "0.8", description: "Allow repetition" },
663
+ { value: "1", label: "1", description: "No penalty" },
664
+ { value: "1.1", label: "1.1", description: "Mild penalty" },
665
+ { value: "1.2", label: "1.2", description: "Balanced" },
666
+ { value: "1.5", label: "1.5", description: "Strong penalty" },
667
+ ],
540
668
  },
541
669
  },
542
670
 
@@ -548,7 +676,14 @@ export const SETTINGS_SCHEMA = {
548
676
  tab: "model",
549
677
  label: "Service Tier",
550
678
  description: "OpenAI processing priority (none = omit parameter)",
551
- submenu: true,
679
+ options: [
680
+ { value: "none", label: "None", description: "Omit service_tier parameter" },
681
+ { value: "auto", label: "Auto", description: "Use provider default tier selection" },
682
+ { value: "default", label: "Default", description: "Standard priority processing" },
683
+ { value: "flex", label: "Flex", description: "Use flexible capacity tier when available" },
684
+ { value: "scale", label: "Scale", description: "Use Scale Tier credits when available" },
685
+ { value: "priority", label: "Priority", description: "Use Priority processing" },
686
+ ],
552
687
  },
553
688
  },
554
689
 
@@ -562,7 +697,13 @@ export const SETTINGS_SCHEMA = {
562
697
  tab: "model",
563
698
  label: "Retry Attempts",
564
699
  description: "Maximum retry attempts on API errors",
565
- submenu: true,
700
+ options: [
701
+ { value: "1", label: "1 retry" },
702
+ { value: "2", label: "2 retries" },
703
+ { value: "3", label: "3 retries" },
704
+ { value: "5", label: "5 retries" },
705
+ { value: "10", label: "10 retries" },
706
+ ],
566
707
  },
567
708
  },
568
709
 
@@ -576,7 +717,14 @@ export const SETTINGS_SCHEMA = {
576
717
  tab: "model",
577
718
  label: "Fallback Revert Policy",
578
719
  description: "When to return to the primary model after a fallback",
579
- submenu: true,
720
+ options: [
721
+ {
722
+ value: "cooldown-expiry",
723
+ label: "Cooldown expiry",
724
+ description: "Return to the primary model after its suppression window ends",
725
+ },
726
+ { value: "never", label: "Never", description: "Stay on the fallback model until manually changed" },
727
+ ],
580
728
  },
581
729
  },
582
730
 
@@ -626,7 +774,19 @@ export const SETTINGS_SCHEMA = {
626
774
  tab: "interaction",
627
775
  label: "Loop Mode",
628
776
  description: "What happens between /loop iterations before re-submitting the prompt",
629
- submenu: true,
777
+ options: [
778
+ {
779
+ value: "prompt",
780
+ label: "Prompt",
781
+ description: "Re-submit the prompt as a follow-up message (current behavior)",
782
+ },
783
+ {
784
+ value: "compact",
785
+ label: "Compact",
786
+ description: "Compact the session context, then re-submit the prompt",
787
+ },
788
+ { value: "reset", label: "Reset", description: "Start a new session, then re-submit the prompt" },
789
+ ],
630
790
  },
631
791
  },
632
792
 
@@ -660,7 +820,14 @@ export const SETTINGS_SCHEMA = {
660
820
  tab: "interaction",
661
821
  label: "Autocomplete Items",
662
822
  description: "Max visible items in autocomplete dropdown (3-20)",
663
- submenu: true,
823
+ options: [
824
+ { value: "3", label: "3 items" },
825
+ { value: "5", label: "5 items" },
826
+ { value: "7", label: "7 items" },
827
+ { value: "10", label: "10 items" },
828
+ { value: "15", label: "15 items" },
829
+ { value: "20", label: "20 items" },
830
+ ],
664
831
  },
665
832
  },
666
833
 
@@ -705,7 +872,13 @@ export const SETTINGS_SCHEMA = {
705
872
  tab: "interaction",
706
873
  label: "Ask Timeout",
707
874
  description: "Auto-select recommended option after timeout (0 to disable)",
708
- submenu: true,
875
+ options: [
876
+ { value: "0", label: "Disabled" },
877
+ { value: "15", label: "15 seconds" },
878
+ { value: "30", label: "30 seconds" },
879
+ { value: "60", label: "60 seconds" },
880
+ { value: "120", label: "120 seconds" },
881
+ ],
709
882
  },
710
883
  },
711
884
 
@@ -726,12 +899,6 @@ export const SETTINGS_SCHEMA = {
726
899
  "stt.language": {
727
900
  type: "string",
728
901
  default: "en",
729
- ui: {
730
- tab: "interaction",
731
- label: "Speech Language",
732
- description: "Language code for transcription (e.g., en, es, fr)",
733
- submenu: true,
734
- },
735
902
  },
736
903
 
737
904
  "stt.modelName": {
@@ -742,7 +909,17 @@ export const SETTINGS_SCHEMA = {
742
909
  tab: "interaction",
743
910
  label: "Speech Model",
744
911
  description: "Whisper model size (larger = more accurate but slower)",
745
- submenu: true,
912
+ options: [
913
+ { value: "tiny", label: "tiny", description: "Multilingual; fastest, lowest accuracy" },
914
+ { value: "tiny.en", label: "tiny.en", description: "English-only; fastest" },
915
+ { value: "base", label: "base", description: "Multilingual; small and fast" },
916
+ { value: "base.en", label: "base.en", description: "English-only; default" },
917
+ { value: "small", label: "small", description: "Multilingual; balanced" },
918
+ { value: "small.en", label: "small.en", description: "English-only; balanced" },
919
+ { value: "medium", label: "medium", description: "Multilingual; accurate but slower" },
920
+ { value: "medium.en", label: "medium.en", description: "English-only; accurate but slower" },
921
+ { value: "large", label: "large", description: "Multilingual; most accurate" },
922
+ ],
746
923
  },
747
924
  },
748
925
 
@@ -780,7 +957,19 @@ export const SETTINGS_SCHEMA = {
780
957
  tab: "context",
781
958
  label: "Compaction Strategy",
782
959
  description: "Choose in-place context-full maintenance, auto-handoff, or disable auto maintenance (off)",
783
- submenu: true,
960
+ options: [
961
+ {
962
+ value: "context-full",
963
+ label: "Context-full",
964
+ description: "Summarize in-place and keep the current session",
965
+ },
966
+ { value: "handoff", label: "Handoff", description: "Generate handoff and continue in a new session" },
967
+ {
968
+ value: "off",
969
+ label: "Off",
970
+ description: "Disable automatic context maintenance (same behavior as Auto-compact off)",
971
+ },
972
+ ],
784
973
  },
785
974
  },
786
975
 
@@ -791,7 +980,21 @@ export const SETTINGS_SCHEMA = {
791
980
  tab: "context",
792
981
  label: "Compaction Threshold",
793
982
  description: "Percent threshold for context maintenance; set to Default to use legacy reserve-based behavior",
794
- submenu: true,
983
+ options: [
984
+ { value: "default", label: "Default", description: "Legacy reserve-based threshold" },
985
+ { value: "10", label: "10%", description: "Extremely early maintenance" },
986
+ { value: "20", label: "20%", description: "Very early maintenance" },
987
+ { value: "30", label: "30%", description: "Early maintenance" },
988
+ { value: "40", label: "40%", description: "Moderately early maintenance" },
989
+ { value: "50", label: "50%", description: "Halfway point" },
990
+ { value: "60", label: "60%", description: "Moderate context usage" },
991
+ { value: "70", label: "70%", description: "Balanced" },
992
+ { value: "75", label: "75%", description: "Slightly aggressive" },
993
+ { value: "80", label: "80%", description: "Typical threshold" },
994
+ { value: "85", label: "85%", description: "Aggressive context usage" },
995
+ { value: "90", label: "90%", description: "Very aggressive" },
996
+ { value: "95", label: "95%", description: "Near context limit" },
997
+ ],
795
998
  },
796
999
  },
797
1000
  "compaction.thresholdTokens": {
@@ -801,7 +1004,16 @@ export const SETTINGS_SCHEMA = {
801
1004
  tab: "context",
802
1005
  label: "Compaction Token Limit",
803
1006
  description: "Fixed token limit for context maintenance; overrides percentage if set",
804
- submenu: true,
1007
+ options: [
1008
+ { value: "default", label: "Default", description: "Use percentage-based threshold" },
1009
+ { value: "25000", label: "25K tokens", description: "Quarter of a 200K window" },
1010
+ { value: "50000", label: "50K tokens", description: "Half of a 200K window" },
1011
+ { value: "100000", label: "100K tokens", description: "Half of a 200K window" },
1012
+ { value: "150000", label: "150K tokens", description: "Three-quarters of a 200K window" },
1013
+ { value: "200000", label: "200K tokens", description: "Full standard context window" },
1014
+ { value: "300000", label: "300K tokens", description: "Large context window" },
1015
+ { value: "500000", label: "500K tokens", description: "Very large context window" },
1016
+ ],
805
1017
  },
806
1018
  },
807
1019
 
@@ -851,7 +1063,17 @@ export const SETTINGS_SCHEMA = {
851
1063
  tab: "context",
852
1064
  label: "Idle Compaction Threshold",
853
1065
  description: "Token count above which idle compaction triggers",
854
- submenu: true,
1066
+ options: [
1067
+ { value: "100000", label: "100K tokens" },
1068
+ { value: "200000", label: "200K tokens" },
1069
+ { value: "300000", label: "300K tokens" },
1070
+ { value: "400000", label: "400K tokens" },
1071
+ { value: "500000", label: "500K tokens" },
1072
+ { value: "600000", label: "600K tokens" },
1073
+ { value: "700000", label: "700K tokens" },
1074
+ { value: "800000", label: "800K tokens" },
1075
+ { value: "900000", label: "900K tokens" },
1076
+ ],
855
1077
  },
856
1078
  },
857
1079
 
@@ -862,7 +1084,14 @@ export const SETTINGS_SCHEMA = {
862
1084
  tab: "context",
863
1085
  label: "Idle Compaction Delay",
864
1086
  description: "Seconds to wait while idle before compacting",
865
- submenu: true,
1087
+ options: [
1088
+ { value: "60", label: "1 minute" },
1089
+ { value: "120", label: "2 minutes" },
1090
+ { value: "300", label: "5 minutes" },
1091
+ { value: "600", label: "10 minutes" },
1092
+ { value: "1800", label: "30 minutes" },
1093
+ { value: "3600", label: "1 hour" },
1094
+ ],
866
1095
  },
867
1096
  },
868
1097
  // Branch summaries
@@ -875,14 +1104,11 @@ export const SETTINGS_SCHEMA = {
875
1104
  "branchSummary.reserveTokens": { type: "number", default: 16384 },
876
1105
 
877
1106
  // Memories
1107
+ // Legacy local-memory enable flag kept only for back-compat migration.
1108
+ // Hidden from UI — users should use `memory.backend` instead.
878
1109
  "memories.enabled": {
879
1110
  type: "boolean",
880
1111
  default: false,
881
- ui: {
882
- tab: "context",
883
- label: "Memories",
884
- description: "Enable autonomous memory extraction and consolidation",
885
- },
886
1112
  },
887
1113
 
888
1114
  "memories.maxRolloutsPerStartup": { type: "number", default: 64 },
@@ -915,6 +1141,140 @@ export const SETTINGS_SCHEMA = {
915
1141
 
916
1142
  "memories.summaryInjectionTokenLimit": { type: "number", default: 5000 },
917
1143
 
1144
+ // Memory backend selector — picks between local memories pipeline,
1145
+ // Hindsight remote memory, or off. Legacy `memories.enabled` keeps gating
1146
+ // the local backend; see config/settings.ts migration for details.
1147
+ "memory.backend": {
1148
+ type: "enum",
1149
+ values: ["off", "local", "hindsight"] as const,
1150
+ default: "off",
1151
+ ui: {
1152
+ tab: "memory",
1153
+ label: "Memory Backend",
1154
+ description: "Off, local memory pipeline, or Hindsight remote memory",
1155
+ options: [
1156
+ { value: "off", label: "Off", description: "No memory subsystem runs" },
1157
+ { value: "local", label: "Local", description: "Local rollout summarisation pipeline (memory_summary.md)" },
1158
+ { value: "hindsight", label: "Hindsight", description: "Vectorize Hindsight remote memory service" },
1159
+ ],
1160
+ },
1161
+ },
1162
+
1163
+ // Hindsight (https://hindsight.vectorize.io)
1164
+ "hindsight.apiUrl": {
1165
+ type: "string",
1166
+ default: "http://localhost:8888",
1167
+ ui: {
1168
+ tab: "memory",
1169
+ label: "Hindsight API URL",
1170
+ description: "Hindsight server URL (Cloud or self-hosted)",
1171
+ condition: "hindsightActive",
1172
+ },
1173
+ },
1174
+
1175
+ "hindsight.apiToken": { type: "string", default: undefined },
1176
+
1177
+ "hindsight.bankId": {
1178
+ type: "string",
1179
+ default: undefined,
1180
+ ui: {
1181
+ tab: "memory",
1182
+ label: "Hindsight Bank ID",
1183
+ description: "Memory bank identifier (default: project name)",
1184
+ condition: "hindsightActive",
1185
+ },
1186
+ },
1187
+
1188
+ "hindsight.bankIdPrefix": { type: "string", default: undefined },
1189
+ "hindsight.scoping": {
1190
+ type: "enum",
1191
+ values: ["global", "per-project", "per-project-tagged"] as const,
1192
+ default: "per-project-tagged",
1193
+ ui: {
1194
+ tab: "memory",
1195
+ label: "Hindsight Scoping",
1196
+ description:
1197
+ "global = one shared bank; per-project = isolated bank per cwd; per-project-tagged = shared bank with project tags so global + project memories merge on recall",
1198
+ options: [
1199
+ {
1200
+ value: "global",
1201
+ label: "Global",
1202
+ description: "One shared bank — every project sees the same memories",
1203
+ },
1204
+ {
1205
+ value: "per-project",
1206
+ label: "Per project",
1207
+ description: "Isolated bank per cwd basename — projects cannot see each other's memories",
1208
+ },
1209
+ {
1210
+ value: "per-project-tagged",
1211
+ label: "Per project (tagged)",
1212
+ description:
1213
+ "Shared bank, retains tagged with project:<cwd>. Recall surfaces project + untagged global memories together",
1214
+ },
1215
+ ],
1216
+ condition: "hindsightActive",
1217
+ },
1218
+ },
1219
+ "hindsight.bankMission": { type: "string", default: undefined },
1220
+ "hindsight.retainMission": { type: "string", default: undefined },
1221
+
1222
+ "hindsight.autoRecall": {
1223
+ type: "boolean",
1224
+ default: true,
1225
+ ui: {
1226
+ tab: "memory",
1227
+ label: "Hindsight Auto Recall",
1228
+ description: "Recall memories on the first turn of each session",
1229
+ condition: "hindsightActive",
1230
+ },
1231
+ },
1232
+ "hindsight.autoRetain": {
1233
+ type: "boolean",
1234
+ default: true,
1235
+ ui: {
1236
+ tab: "memory",
1237
+ label: "Hindsight Auto Retain",
1238
+ description: "Retain transcript every N turns and at session boundaries",
1239
+ condition: "hindsightActive",
1240
+ },
1241
+ },
1242
+
1243
+ "hindsight.retainMode": {
1244
+ type: "enum",
1245
+ values: ["full-session", "last-turn"] as const,
1246
+ default: "full-session",
1247
+ ui: {
1248
+ tab: "memory",
1249
+ label: "Hindsight Retain Mode",
1250
+ description: "full-session = upsert one document per session, last-turn = chunked",
1251
+ options: [
1252
+ {
1253
+ value: "full-session",
1254
+ label: "Full session",
1255
+ description: "Upsert one document per session (recommended)",
1256
+ },
1257
+ { value: "last-turn", label: "Last turn", description: "Chunked retention sliced by turn boundaries" },
1258
+ ],
1259
+ condition: "hindsightActive",
1260
+ },
1261
+ },
1262
+ "hindsight.retainEveryNTurns": { type: "number", default: 3 },
1263
+ "hindsight.retainOverlapTurns": { type: "number", default: 2 },
1264
+ "hindsight.retainContext": { type: "string", default: "omp" },
1265
+
1266
+ "hindsight.recallBudget": {
1267
+ type: "enum",
1268
+ values: ["low", "mid", "high"] as const,
1269
+ default: "mid",
1270
+ },
1271
+ "hindsight.recallMaxTokens": { type: "number", default: 1024 },
1272
+ "hindsight.recallContextTurns": { type: "number", default: 1 },
1273
+ "hindsight.recallMaxQueryChars": { type: "number", default: 800 },
1274
+ "hindsight.recallTypes": { type: "array", default: HINDSIGHT_RECALL_TYPES_DEFAULT },
1275
+
1276
+ "hindsight.debug": { type: "boolean", default: false },
1277
+
918
1278
  // TTSR
919
1279
  "ttsr.enabled": {
920
1280
  type: "boolean",
@@ -945,7 +1305,12 @@ export const SETTINGS_SCHEMA = {
945
1305
  tab: "context",
946
1306
  label: "TTSR Interrupt Mode",
947
1307
  description: "When to interrupt mid-stream vs inject warning after completion",
948
- submenu: true,
1308
+ options: [
1309
+ { value: "always", label: "always", description: "Interrupt on prose and tool streams" },
1310
+ { value: "prose-only", label: "prose-only", description: "Interrupt only on reply/thinking matches" },
1311
+ { value: "tool-only", label: "tool-only", description: "Interrupt only on tool-call argument matches" },
1312
+ { value: "never", label: "never", description: "Never interrupt; inject warning after completion" },
1313
+ ],
949
1314
  },
950
1315
  },
951
1316
 
@@ -967,7 +1332,13 @@ export const SETTINGS_SCHEMA = {
967
1332
  tab: "context",
968
1333
  label: "TTSR Repeat Gap",
969
1334
  description: "Messages before a rule can trigger again",
970
- submenu: true,
1335
+ options: [
1336
+ { value: "5", label: "5 messages" },
1337
+ { value: "10", label: "10 messages" },
1338
+ { value: "15", label: "15 messages" },
1339
+ { value: "20", label: "20 messages" },
1340
+ { value: "30", label: "30 messages" },
1341
+ ],
971
1342
  },
972
1343
  },
973
1344
 
@@ -1004,7 +1375,12 @@ export const SETTINGS_SCHEMA = {
1004
1375
  tab: "editing",
1005
1376
  label: "Fuzzy Match Threshold",
1006
1377
  description: "Similarity threshold for fuzzy matches",
1007
- submenu: true,
1378
+ options: [
1379
+ { value: "0.85", label: "0.85", description: "Lenient" },
1380
+ { value: "0.90", label: "0.90", description: "Moderate" },
1381
+ { value: "0.95", label: "0.95", description: "Default" },
1382
+ { value: "0.98", label: "0.98", description: "Strict" },
1383
+ ],
1008
1384
  },
1009
1385
  },
1010
1386
 
@@ -1055,7 +1431,13 @@ export const SETTINGS_SCHEMA = {
1055
1431
  tab: "editing",
1056
1432
  label: "Default Read Limit",
1057
1433
  description: "Default number of lines returned when agent calls read without a limit",
1058
- submenu: true,
1434
+ options: [
1435
+ { value: "200", label: "200 lines" },
1436
+ { value: "300", label: "300 lines" },
1437
+ { value: "500", label: "500 lines" },
1438
+ { value: "1000", label: "1000 lines" },
1439
+ { value: "5000", label: "5000 lines" },
1440
+ ],
1059
1441
  },
1060
1442
  },
1061
1443
 
@@ -1127,24 +1509,12 @@ export const SETTINGS_SCHEMA = {
1127
1509
  "shellMinimizer.settingsPath": {
1128
1510
  type: "string",
1129
1511
  default: undefined,
1130
- ui: {
1131
- tab: "editing",
1132
- label: "Minimizer Settings Path",
1133
- description: "Optional TOML file with per-command minimizer overrides",
1134
- submenu: true,
1135
- },
1136
1512
  },
1137
1513
  "shellMinimizer.only": { type: "array", default: EMPTY_STRING_ARRAY },
1138
1514
  "shellMinimizer.except": { type: "array", default: EMPTY_STRING_ARRAY },
1139
1515
  "shellMinimizer.maxCaptureBytes": {
1140
1516
  type: "number",
1141
1517
  default: 4 * 1024 * 1024,
1142
- ui: {
1143
- tab: "editing",
1144
- label: "Minimizer Capture Limit",
1145
- description: "Maximum captured output bytes before falling back to raw streaming",
1146
- submenu: true,
1147
- },
1148
1518
  },
1149
1519
 
1150
1520
  // Eval (per-backend toggles; add more as new backends ship, e.g. eval.ts)
@@ -1214,7 +1584,12 @@ export const SETTINGS_SCHEMA = {
1214
1584
  tab: "tools",
1215
1585
  label: "Todo Reminder Limit",
1216
1586
  description: "Maximum reminders to complete todos before giving up",
1217
- submenu: true,
1587
+ options: [
1588
+ { value: "1", label: "1 reminder" },
1589
+ { value: "2", label: "2 reminders" },
1590
+ { value: "3", label: "3 reminders" },
1591
+ { value: "5", label: "5 reminders" },
1592
+ ],
1218
1593
  },
1219
1594
  },
1220
1595
 
@@ -1248,7 +1623,13 @@ export const SETTINGS_SCHEMA = {
1248
1623
  tab: "tools",
1249
1624
  label: "Search Context Before",
1250
1625
  description: "Lines of context before each search match",
1251
- submenu: true,
1626
+ options: [
1627
+ { value: "0", label: "0 lines" },
1628
+ { value: "1", label: "1 line" },
1629
+ { value: "2", label: "2 lines" },
1630
+ { value: "3", label: "3 lines" },
1631
+ { value: "5", label: "5 lines" },
1632
+ ],
1252
1633
  },
1253
1634
  },
1254
1635
 
@@ -1259,7 +1640,14 @@ export const SETTINGS_SCHEMA = {
1259
1640
  tab: "tools",
1260
1641
  label: "Search Context After",
1261
1642
  description: "Lines of context after each search match",
1262
- submenu: true,
1643
+ options: [
1644
+ { value: "0", label: "0 lines" },
1645
+ { value: "1", label: "1 line" },
1646
+ { value: "2", label: "2 lines" },
1647
+ { value: "3", label: "3 lines" },
1648
+ { value: "5", label: "5 lines" },
1649
+ { value: "10", label: "10 lines" },
1650
+ ],
1263
1651
  },
1264
1652
  },
1265
1653
 
@@ -1433,7 +1821,14 @@ export const SETTINGS_SCHEMA = {
1433
1821
  tab: "tools",
1434
1822
  label: "Max Tool Timeout",
1435
1823
  description: "Maximum timeout in seconds the agent can set for any tool (0 = no limit)",
1436
- submenu: true,
1824
+ options: [
1825
+ { value: "0", label: "No limit" },
1826
+ { value: "30", label: "30 seconds" },
1827
+ { value: "60", label: "60 seconds" },
1828
+ { value: "120", label: "120 seconds" },
1829
+ { value: "300", label: "5 minutes" },
1830
+ { value: "600", label: "10 minutes" },
1831
+ ],
1437
1832
  },
1438
1833
  },
1439
1834
 
@@ -1451,12 +1846,6 @@ export const SETTINGS_SCHEMA = {
1451
1846
  "async.maxJobs": {
1452
1847
  type: "number",
1453
1848
  default: 100,
1454
- ui: {
1455
- tab: "tools",
1456
- label: "Max Async Jobs",
1457
- description: "Maximum concurrent background jobs (1-100)",
1458
- submenu: true,
1459
- },
1460
1849
  },
1461
1850
 
1462
1851
  "async.pollWaitDuration": {
@@ -1467,7 +1856,13 @@ export const SETTINGS_SCHEMA = {
1467
1856
  tab: "tools",
1468
1857
  label: "Poll Wait Duration",
1469
1858
  description: "How long the poll tool waits for background job updates before returning the current state",
1470
- submenu: true,
1859
+ options: [
1860
+ { value: "5s", label: "5 seconds" },
1861
+ { value: "10s", label: "10 seconds" },
1862
+ { value: "30s", label: "30 seconds", description: "Default" },
1863
+ { value: "1m", label: "1 minute" },
1864
+ { value: "5m", label: "5 minutes" },
1865
+ ],
1471
1866
  },
1472
1867
  },
1473
1868
 
@@ -1484,12 +1879,6 @@ export const SETTINGS_SCHEMA = {
1484
1879
  "bash.autoBackground.thresholdMs": {
1485
1880
  type: "number",
1486
1881
  default: 60_000,
1487
- ui: {
1488
- tab: "tools",
1489
- label: "Bash Auto-Background Delay",
1490
- description: "Milliseconds to wait before a bash command is moved to the background (0 = immediately)",
1491
- submenu: true,
1492
- },
1493
1882
  },
1494
1883
 
1495
1884
  // MCP
@@ -1553,7 +1942,20 @@ export const SETTINGS_SCHEMA = {
1553
1942
  label: "Isolation Mode",
1554
1943
  description:
1555
1944
  "Isolation mode for subagents (none, git worktree, fuse-overlayfs on Unix, or ProjFS on Windows via fuse-projfs; unsupported modes fall back to worktree)",
1556
- submenu: true,
1945
+ options: [
1946
+ { value: "none", label: "None", description: "No isolation" },
1947
+ { value: "worktree", label: "Worktree", description: "Git worktree isolation" },
1948
+ {
1949
+ value: "fuse-overlay",
1950
+ label: "Fuse Overlay",
1951
+ description: "COW overlay via fuse-overlayfs (Unix only)",
1952
+ },
1953
+ {
1954
+ value: "fuse-projfs",
1955
+ label: "Fuse ProjFS",
1956
+ description: "COW overlay via ProjFS (Windows only; falls back to worktree if unavailable)",
1957
+ },
1958
+ ],
1557
1959
  },
1558
1960
  },
1559
1961
 
@@ -1565,7 +1967,10 @@ export const SETTINGS_SCHEMA = {
1565
1967
  tab: "tasks",
1566
1968
  label: "Isolation Merge Strategy",
1567
1969
  description: "How isolated task changes are integrated (patch apply or branch merge)",
1568
- submenu: true,
1970
+ options: [
1971
+ { value: "patch", label: "Patch", description: "Combine diffs and git apply" },
1972
+ { value: "branch", label: "Branch", description: "Commit per task, merge with --no-ff" },
1973
+ ],
1569
1974
  },
1570
1975
  },
1571
1976
 
@@ -1577,7 +1982,10 @@ export const SETTINGS_SCHEMA = {
1577
1982
  tab: "tasks",
1578
1983
  label: "Isolation Commit Style",
1579
1984
  description: "Commit message style for nested repo changes (generic or AI-generated)",
1580
- submenu: true,
1985
+ options: [
1986
+ { value: "generic", label: "Generic", description: "Static commit message" },
1987
+ { value: "ai", label: "AI", description: "AI-generated commit message from diff" },
1988
+ ],
1581
1989
  },
1582
1990
  },
1583
1991
 
@@ -1599,7 +2007,23 @@ export const SETTINGS_SCHEMA = {
1599
2007
  tab: "tasks",
1600
2008
  label: "Task Input Mode",
1601
2009
  description: "How much shared structure the task tool accepts (default, schema-free, or independent)",
1602
- submenu: true,
2010
+ options: [
2011
+ {
2012
+ value: "default",
2013
+ label: "Default",
2014
+ description: "Shared context and custom task schema are available",
2015
+ },
2016
+ {
2017
+ value: "schema-free",
2018
+ label: "Schema-free",
2019
+ description: "Shared context stays available, but custom task schema is disabled",
2020
+ },
2021
+ {
2022
+ value: "independent",
2023
+ label: "Independent",
2024
+ description: "No shared context or custom task schema; each task must stand alone",
2025
+ },
2026
+ ],
1603
2027
  },
1604
2028
  },
1605
2029
 
@@ -1610,7 +2034,16 @@ export const SETTINGS_SCHEMA = {
1610
2034
  tab: "tasks",
1611
2035
  label: "Max Concurrent Tasks",
1612
2036
  description: "Concurrent limit for subagents",
1613
- submenu: true,
2037
+ options: [
2038
+ { value: "0", label: "Unlimited" },
2039
+ { value: "1", label: "1 task" },
2040
+ { value: "2", label: "2 tasks" },
2041
+ { value: "4", label: "4 tasks" },
2042
+ { value: "8", label: "8 tasks" },
2043
+ { value: "16", label: "16 tasks" },
2044
+ { value: "32", label: "32 tasks" },
2045
+ { value: "64", label: "64 tasks" },
2046
+ ],
1614
2047
  },
1615
2048
  },
1616
2049
 
@@ -1621,7 +2054,13 @@ export const SETTINGS_SCHEMA = {
1621
2054
  tab: "tasks",
1622
2055
  label: "Max Task Recursion",
1623
2056
  description: "How many levels deep subagents can spawn their own subagents",
1624
- submenu: true,
2057
+ options: [
2058
+ { value: "-1", label: "Unlimited" },
2059
+ { value: "0", label: "None" },
2060
+ { value: "1", label: "Single" },
2061
+ { value: "2", label: "Double" },
2062
+ { value: "3", label: "Triple" },
2063
+ ],
1625
2064
  },
1626
2065
  },
1627
2066
 
@@ -1642,7 +2081,15 @@ export const SETTINGS_SCHEMA = {
1642
2081
  tab: "tasks",
1643
2082
  label: "Todo auto-clear delay",
1644
2083
  description: "How long to wait before removing completed/abandoned tasks from the list",
1645
- submenu: true,
2084
+ options: [
2085
+ { value: "0", label: "Instant" },
2086
+ { value: "60", label: "1 minute", description: "Default" },
2087
+ { value: "300", label: "5 minutes" },
2088
+ { value: "900", label: "15 minutes" },
2089
+ { value: "1800", label: "30 minutes" },
2090
+ { value: "3600", label: "1 hour" },
2091
+ { value: "-1", label: "Never" },
2092
+ ],
1646
2093
  },
1647
2094
  },
1648
2095
 
@@ -1736,7 +2183,29 @@ export const SETTINGS_SCHEMA = {
1736
2183
  tab: "providers",
1737
2184
  label: "Web Search Provider",
1738
2185
  description: "Provider for web search tool",
1739
- submenu: true,
2186
+ options: [
2187
+ {
2188
+ value: "auto",
2189
+ label: "Auto",
2190
+ description: "Preferred web-search provider",
2191
+ },
2192
+ { value: "exa", label: "Exa", description: "Uses Exa API when EXA_API_KEY is set; falls back to Exa MCP" },
2193
+ { value: "brave", label: "Brave", description: "Requires BRAVE_API_KEY" },
2194
+ { value: "jina", label: "Jina", description: "Requires JINA_API_KEY" },
2195
+ { value: "kimi", label: "Kimi", description: "Requires MOONSHOT_SEARCH_API_KEY or MOONSHOT_API_KEY" },
2196
+ {
2197
+ value: "perplexity",
2198
+ label: "Perplexity",
2199
+ description: "Requires PERPLEXITY_COOKIES or PERPLEXITY_API_KEY",
2200
+ },
2201
+ { value: "anthropic", label: "Anthropic", description: "Uses Anthropic web search" },
2202
+ { value: "zai", label: "Z.AI", description: "Calls Z.AI webSearchPrime MCP" },
2203
+ { value: "tavily", label: "Tavily", description: "Requires TAVILY_API_KEY" },
2204
+ { value: "kagi", label: "Kagi", description: "Requires KAGI_API_KEY and Kagi Search API beta access" },
2205
+ { value: "synthetic", label: "Synthetic", description: "Requires SYNTHETIC_API_KEY" },
2206
+ { value: "parallel", label: "Parallel", description: "Requires PARALLEL_API_KEY" },
2207
+ { value: "searxng", label: "SearXNG", description: "Requires searxng.endpoint" },
2208
+ ],
1740
2209
  },
1741
2210
  },
1742
2211
  "providers.image": {
@@ -1747,7 +2216,16 @@ export const SETTINGS_SCHEMA = {
1747
2216
  tab: "providers",
1748
2217
  label: "Image Provider",
1749
2218
  description: "Provider for image generation tool",
1750
- submenu: true,
2219
+ options: [
2220
+ {
2221
+ value: "auto",
2222
+ label: "Auto",
2223
+ description: "Priority: GPT model image tool > Antigravity > OpenRouter > Gemini",
2224
+ },
2225
+ { value: "openai", label: "OpenAI", description: "Uses the active GPT Responses/Codex model" },
2226
+ { value: "gemini", label: "Gemini", description: "Requires GEMINI_API_KEY" },
2227
+ { value: "openrouter", label: "OpenRouter", description: "Requires OPENROUTER_API_KEY" },
2228
+ ],
1751
2229
  },
1752
2230
  },
1753
2231
 
@@ -1759,7 +2237,10 @@ export const SETTINGS_SCHEMA = {
1759
2237
  tab: "providers",
1760
2238
  label: "Kimi API Format",
1761
2239
  description: "API format for Kimi Code provider",
1762
- submenu: true,
2240
+ options: [
2241
+ { value: "openai", label: "OpenAI", description: "api.kimi.com" },
2242
+ { value: "anthropic", label: "Anthropic", description: "api.moonshot.ai" },
2243
+ ],
1763
2244
  },
1764
2245
  },
1765
2246
 
@@ -1771,7 +2252,11 @@ export const SETTINGS_SCHEMA = {
1771
2252
  tab: "providers",
1772
2253
  label: "OpenAI WebSockets",
1773
2254
  description: "Websocket policy for OpenAI Codex models (auto uses model defaults, on forces, off disables)",
1774
- submenu: true,
2255
+ options: [
2256
+ { value: "auto", label: "Auto", description: "Use model/provider default websocket behavior" },
2257
+ { value: "off", label: "Off", description: "Disable websockets for OpenAI Codex models" },
2258
+ { value: "on", label: "On", description: "Force websockets for OpenAI Codex models" },
2259
+ ],
1775
2260
  },
1776
2261
  },
1777
2262
 
@@ -1916,9 +2401,9 @@ export function hasUi(path: SettingPath): boolean {
1916
2401
  }
1917
2402
 
1918
2403
  /** Get UI metadata for a path (undefined if no UI) */
1919
- export function getUi(path: SettingPath): UiMetadata | undefined {
2404
+ export function getUi(path: SettingPath): AnyUiMetadata | undefined {
1920
2405
  const def = SETTINGS_SCHEMA[path];
1921
- return "ui" in def ? (def.ui as UiMetadata) : undefined;
2406
+ return "ui" in def ? (def.ui as AnyUiMetadata) : undefined;
1922
2407
  }
1923
2408
 
1924
2409
  /** Get all paths for a specific tab */