@oh-my-pi/pi-coding-agent 14.6.1 → 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 (63) hide show
  1. package/CHANGELOG.md +82 -1
  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 +595 -100
  8. package/src/config/settings.ts +46 -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 +4 -2
  32. package/src/modes/components/status-line/presets.ts +1 -1
  33. package/src/modes/components/status-line.ts +4 -1
  34. package/src/modes/controllers/command-controller.ts +6 -5
  35. package/src/modes/controllers/event-controller.ts +12 -0
  36. package/src/modes/controllers/mcp-command-controller.ts +23 -0
  37. package/src/modes/controllers/selector-controller.ts +10 -12
  38. package/src/modes/interactive-mode.ts +3 -2
  39. package/src/modes/theme/theme.ts +4 -0
  40. package/src/prompts/tools/github.md +3 -0
  41. package/src/prompts/tools/hashline.md +20 -16
  42. package/src/prompts/tools/read.md +10 -6
  43. package/src/prompts/tools/recall.md +5 -0
  44. package/src/prompts/tools/reflect.md +5 -0
  45. package/src/prompts/tools/retain.md +5 -0
  46. package/src/prompts/tools/search.md +1 -1
  47. package/src/sdk.ts +12 -9
  48. package/src/session/agent-session.ts +75 -3
  49. package/src/slash-commands/builtin-registry.ts +2 -12
  50. package/src/ssh/connection-manager.ts +1 -1
  51. package/src/tools/ast-edit.ts +14 -5
  52. package/src/tools/ast-grep.ts +12 -3
  53. package/src/tools/find.ts +47 -7
  54. package/src/tools/gh-renderer.ts +10 -1
  55. package/src/tools/gh.ts +233 -5
  56. package/src/tools/hindsight-recall.ts +70 -0
  57. package/src/tools/hindsight-reflect.ts +57 -0
  58. package/src/tools/hindsight-retain.ts +63 -0
  59. package/src/tools/index.ts +17 -0
  60. package/src/tools/output-meta.ts +1 -0
  61. package/src/tools/path-utils.ts +55 -0
  62. package/src/tools/read.ts +1 -1
  63. 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,25 @@ 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
+ ],
371
+ },
372
+ },
373
+
374
+ "statusLine.sessionAccent": {
375
+ type: "boolean",
376
+ default: true,
377
+ ui: {
378
+ tab: "appearance",
379
+ label: "Session Accent",
380
+ description: "Use the session name color for the editor border and status line gap",
305
381
  },
306
382
  },
307
383
  "tools.artifactSpillThreshold": {
@@ -311,7 +387,20 @@ export const SETTINGS_SCHEMA = {
311
387
  tab: "tools",
312
388
  label: "Artifact spill threshold (KB)",
313
389
  description: "Tool output above this size is saved as an artifact; tail is kept inline",
314
- 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
+ ],
315
404
  },
316
405
  },
317
406
  "tools.artifactTailBytes": {
@@ -321,7 +410,16 @@ export const SETTINGS_SCHEMA = {
321
410
  tab: "tools",
322
411
  label: "Artifact tail size (KB)",
323
412
  description: "Amount of tail content kept inline when output spills to artifact",
324
- 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
+ ],
325
423
  },
326
424
  },
327
425
  "tools.artifactTailLines": {
@@ -331,7 +429,15 @@ export const SETTINGS_SCHEMA = {
331
429
  tab: "tools",
332
430
  label: "Artifact tail lines",
333
431
  description: "Maximum lines of tail content kept inline when output spills to artifact",
334
- 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
+ ],
335
441
  },
336
442
  },
337
443
 
@@ -396,12 +502,6 @@ export const SETTINGS_SCHEMA = {
396
502
  "display.tabWidth": {
397
503
  type: "number",
398
504
  default: 3,
399
- ui: {
400
- tab: "appearance",
401
- label: "Tab Width",
402
- description: "Default number of spaces used when rendering tab characters",
403
- submenu: true,
404
- },
405
505
  },
406
506
 
407
507
  "display.showTokenUsage": {
@@ -443,7 +543,7 @@ export const SETTINGS_SCHEMA = {
443
543
  tab: "model",
444
544
  label: "Thinking Level",
445
545
  description: "Reasoning depth for thinking-capable models",
446
- submenu: true,
546
+ options: [...THINKING_EFFORTS.map(getThinkingLevelMetadata)],
447
547
  },
448
548
  },
449
549
 
@@ -471,7 +571,14 @@ export const SETTINGS_SCHEMA = {
471
571
  tab: "model",
472
572
  label: "Temperature",
473
573
  description: "Sampling temperature (0 = deterministic, 1 = creative, -1 = provider default)",
474
- 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
+ ],
475
582
  },
476
583
  },
477
584
 
@@ -482,7 +589,14 @@ export const SETTINGS_SCHEMA = {
482
589
  tab: "model",
483
590
  label: "Top P",
484
591
  description: "Nucleus sampling cutoff (0-1, -1 = provider default)",
485
- 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
+ ],
486
600
  },
487
601
  },
488
602
 
@@ -493,7 +607,13 @@ export const SETTINGS_SCHEMA = {
493
607
  tab: "model",
494
608
  label: "Top K",
495
609
  description: "Sample from top-K tokens (-1 = provider default)",
496
- 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
+ ],
497
617
  },
498
618
  },
499
619
 
@@ -504,7 +624,12 @@ export const SETTINGS_SCHEMA = {
504
624
  tab: "model",
505
625
  label: "Min P",
506
626
  description: "Minimum probability threshold (0-1, -1 = provider default)",
507
- 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
+ ],
508
633
  },
509
634
  },
510
635
 
@@ -515,7 +640,13 @@ export const SETTINGS_SCHEMA = {
515
640
  tab: "model",
516
641
  label: "Presence Penalty",
517
642
  description: "Penalty for introducing already-present tokens (-1 = provider default)",
518
- 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
+ ],
519
650
  },
520
651
  },
521
652
 
@@ -526,7 +657,14 @@ export const SETTINGS_SCHEMA = {
526
657
  tab: "model",
527
658
  label: "Repetition Penalty",
528
659
  description: "Penalty for repeated tokens (-1 = provider default)",
529
- 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
+ ],
530
668
  },
531
669
  },
532
670
 
@@ -538,7 +676,14 @@ export const SETTINGS_SCHEMA = {
538
676
  tab: "model",
539
677
  label: "Service Tier",
540
678
  description: "OpenAI processing priority (none = omit parameter)",
541
- 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
+ ],
542
687
  },
543
688
  },
544
689
 
@@ -552,7 +697,13 @@ export const SETTINGS_SCHEMA = {
552
697
  tab: "model",
553
698
  label: "Retry Attempts",
554
699
  description: "Maximum retry attempts on API errors",
555
- 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
+ ],
556
707
  },
557
708
  },
558
709
 
@@ -566,7 +717,14 @@ export const SETTINGS_SCHEMA = {
566
717
  tab: "model",
567
718
  label: "Fallback Revert Policy",
568
719
  description: "When to return to the primary model after a fallback",
569
- 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
+ ],
570
728
  },
571
729
  },
572
730
 
@@ -616,7 +774,19 @@ export const SETTINGS_SCHEMA = {
616
774
  tab: "interaction",
617
775
  label: "Loop Mode",
618
776
  description: "What happens between /loop iterations before re-submitting the prompt",
619
- 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
+ ],
620
790
  },
621
791
  },
622
792
 
@@ -650,7 +820,14 @@ export const SETTINGS_SCHEMA = {
650
820
  tab: "interaction",
651
821
  label: "Autocomplete Items",
652
822
  description: "Max visible items in autocomplete dropdown (3-20)",
653
- 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
+ ],
654
831
  },
655
832
  },
656
833
 
@@ -695,7 +872,13 @@ export const SETTINGS_SCHEMA = {
695
872
  tab: "interaction",
696
873
  label: "Ask Timeout",
697
874
  description: "Auto-select recommended option after timeout (0 to disable)",
698
- 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
+ ],
699
882
  },
700
883
  },
701
884
 
@@ -716,12 +899,6 @@ export const SETTINGS_SCHEMA = {
716
899
  "stt.language": {
717
900
  type: "string",
718
901
  default: "en",
719
- ui: {
720
- tab: "interaction",
721
- label: "Speech Language",
722
- description: "Language code for transcription (e.g., en, es, fr)",
723
- submenu: true,
724
- },
725
902
  },
726
903
 
727
904
  "stt.modelName": {
@@ -732,7 +909,17 @@ export const SETTINGS_SCHEMA = {
732
909
  tab: "interaction",
733
910
  label: "Speech Model",
734
911
  description: "Whisper model size (larger = more accurate but slower)",
735
- 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
+ ],
736
923
  },
737
924
  },
738
925
 
@@ -770,7 +957,19 @@ export const SETTINGS_SCHEMA = {
770
957
  tab: "context",
771
958
  label: "Compaction Strategy",
772
959
  description: "Choose in-place context-full maintenance, auto-handoff, or disable auto maintenance (off)",
773
- 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
+ ],
774
973
  },
775
974
  },
776
975
 
@@ -781,7 +980,21 @@ export const SETTINGS_SCHEMA = {
781
980
  tab: "context",
782
981
  label: "Compaction Threshold",
783
982
  description: "Percent threshold for context maintenance; set to Default to use legacy reserve-based behavior",
784
- 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
+ ],
785
998
  },
786
999
  },
787
1000
  "compaction.thresholdTokens": {
@@ -791,7 +1004,16 @@ export const SETTINGS_SCHEMA = {
791
1004
  tab: "context",
792
1005
  label: "Compaction Token Limit",
793
1006
  description: "Fixed token limit for context maintenance; overrides percentage if set",
794
- 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
+ ],
795
1017
  },
796
1018
  },
797
1019
 
@@ -841,7 +1063,17 @@ export const SETTINGS_SCHEMA = {
841
1063
  tab: "context",
842
1064
  label: "Idle Compaction Threshold",
843
1065
  description: "Token count above which idle compaction triggers",
844
- 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
+ ],
845
1077
  },
846
1078
  },
847
1079
 
@@ -852,7 +1084,14 @@ export const SETTINGS_SCHEMA = {
852
1084
  tab: "context",
853
1085
  label: "Idle Compaction Delay",
854
1086
  description: "Seconds to wait while idle before compacting",
855
- 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
+ ],
856
1095
  },
857
1096
  },
858
1097
  // Branch summaries
@@ -865,14 +1104,11 @@ export const SETTINGS_SCHEMA = {
865
1104
  "branchSummary.reserveTokens": { type: "number", default: 16384 },
866
1105
 
867
1106
  // Memories
1107
+ // Legacy local-memory enable flag kept only for back-compat migration.
1108
+ // Hidden from UI — users should use `memory.backend` instead.
868
1109
  "memories.enabled": {
869
1110
  type: "boolean",
870
1111
  default: false,
871
- ui: {
872
- tab: "context",
873
- label: "Memories",
874
- description: "Enable autonomous memory extraction and consolidation",
875
- },
876
1112
  },
877
1113
 
878
1114
  "memories.maxRolloutsPerStartup": { type: "number", default: 64 },
@@ -905,6 +1141,140 @@ export const SETTINGS_SCHEMA = {
905
1141
 
906
1142
  "memories.summaryInjectionTokenLimit": { type: "number", default: 5000 },
907
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
+
908
1278
  // TTSR
909
1279
  "ttsr.enabled": {
910
1280
  type: "boolean",
@@ -935,7 +1305,12 @@ export const SETTINGS_SCHEMA = {
935
1305
  tab: "context",
936
1306
  label: "TTSR Interrupt Mode",
937
1307
  description: "When to interrupt mid-stream vs inject warning after completion",
938
- 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
+ ],
939
1314
  },
940
1315
  },
941
1316
 
@@ -957,7 +1332,13 @@ export const SETTINGS_SCHEMA = {
957
1332
  tab: "context",
958
1333
  label: "TTSR Repeat Gap",
959
1334
  description: "Messages before a rule can trigger again",
960
- 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
+ ],
961
1342
  },
962
1343
  },
963
1344
 
@@ -994,7 +1375,12 @@ export const SETTINGS_SCHEMA = {
994
1375
  tab: "editing",
995
1376
  label: "Fuzzy Match Threshold",
996
1377
  description: "Similarity threshold for fuzzy matches",
997
- 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
+ ],
998
1384
  },
999
1385
  },
1000
1386
 
@@ -1045,7 +1431,13 @@ export const SETTINGS_SCHEMA = {
1045
1431
  tab: "editing",
1046
1432
  label: "Default Read Limit",
1047
1433
  description: "Default number of lines returned when agent calls read without a limit",
1048
- 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
+ ],
1049
1441
  },
1050
1442
  },
1051
1443
 
@@ -1117,24 +1509,12 @@ export const SETTINGS_SCHEMA = {
1117
1509
  "shellMinimizer.settingsPath": {
1118
1510
  type: "string",
1119
1511
  default: undefined,
1120
- ui: {
1121
- tab: "editing",
1122
- label: "Minimizer Settings Path",
1123
- description: "Optional TOML file with per-command minimizer overrides",
1124
- submenu: true,
1125
- },
1126
1512
  },
1127
1513
  "shellMinimizer.only": { type: "array", default: EMPTY_STRING_ARRAY },
1128
1514
  "shellMinimizer.except": { type: "array", default: EMPTY_STRING_ARRAY },
1129
1515
  "shellMinimizer.maxCaptureBytes": {
1130
1516
  type: "number",
1131
1517
  default: 4 * 1024 * 1024,
1132
- ui: {
1133
- tab: "editing",
1134
- label: "Minimizer Capture Limit",
1135
- description: "Maximum captured output bytes before falling back to raw streaming",
1136
- submenu: true,
1137
- },
1138
1518
  },
1139
1519
 
1140
1520
  // Eval (per-backend toggles; add more as new backends ship, e.g. eval.ts)
@@ -1204,7 +1584,12 @@ export const SETTINGS_SCHEMA = {
1204
1584
  tab: "tools",
1205
1585
  label: "Todo Reminder Limit",
1206
1586
  description: "Maximum reminders to complete todos before giving up",
1207
- 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
+ ],
1208
1593
  },
1209
1594
  },
1210
1595
 
@@ -1238,7 +1623,13 @@ export const SETTINGS_SCHEMA = {
1238
1623
  tab: "tools",
1239
1624
  label: "Search Context Before",
1240
1625
  description: "Lines of context before each search match",
1241
- 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
+ ],
1242
1633
  },
1243
1634
  },
1244
1635
 
@@ -1249,7 +1640,14 @@ export const SETTINGS_SCHEMA = {
1249
1640
  tab: "tools",
1250
1641
  label: "Search Context After",
1251
1642
  description: "Lines of context after each search match",
1252
- 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
+ ],
1253
1651
  },
1254
1652
  },
1255
1653
 
@@ -1423,7 +1821,14 @@ export const SETTINGS_SCHEMA = {
1423
1821
  tab: "tools",
1424
1822
  label: "Max Tool Timeout",
1425
1823
  description: "Maximum timeout in seconds the agent can set for any tool (0 = no limit)",
1426
- 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
+ ],
1427
1832
  },
1428
1833
  },
1429
1834
 
@@ -1441,12 +1846,6 @@ export const SETTINGS_SCHEMA = {
1441
1846
  "async.maxJobs": {
1442
1847
  type: "number",
1443
1848
  default: 100,
1444
- ui: {
1445
- tab: "tools",
1446
- label: "Max Async Jobs",
1447
- description: "Maximum concurrent background jobs (1-100)",
1448
- submenu: true,
1449
- },
1450
1849
  },
1451
1850
 
1452
1851
  "async.pollWaitDuration": {
@@ -1457,7 +1856,13 @@ export const SETTINGS_SCHEMA = {
1457
1856
  tab: "tools",
1458
1857
  label: "Poll Wait Duration",
1459
1858
  description: "How long the poll tool waits for background job updates before returning the current state",
1460
- 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
+ ],
1461
1866
  },
1462
1867
  },
1463
1868
 
@@ -1474,12 +1879,6 @@ export const SETTINGS_SCHEMA = {
1474
1879
  "bash.autoBackground.thresholdMs": {
1475
1880
  type: "number",
1476
1881
  default: 60_000,
1477
- ui: {
1478
- tab: "tools",
1479
- label: "Bash Auto-Background Delay",
1480
- description: "Milliseconds to wait before a bash command is moved to the background (0 = immediately)",
1481
- submenu: true,
1482
- },
1483
1882
  },
1484
1883
 
1485
1884
  // MCP
@@ -1543,7 +1942,20 @@ export const SETTINGS_SCHEMA = {
1543
1942
  label: "Isolation Mode",
1544
1943
  description:
1545
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)",
1546
- 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
+ ],
1547
1959
  },
1548
1960
  },
1549
1961
 
@@ -1555,7 +1967,10 @@ export const SETTINGS_SCHEMA = {
1555
1967
  tab: "tasks",
1556
1968
  label: "Isolation Merge Strategy",
1557
1969
  description: "How isolated task changes are integrated (patch apply or branch merge)",
1558
- 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
+ ],
1559
1974
  },
1560
1975
  },
1561
1976
 
@@ -1567,7 +1982,10 @@ export const SETTINGS_SCHEMA = {
1567
1982
  tab: "tasks",
1568
1983
  label: "Isolation Commit Style",
1569
1984
  description: "Commit message style for nested repo changes (generic or AI-generated)",
1570
- 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
+ ],
1571
1989
  },
1572
1990
  },
1573
1991
 
@@ -1589,7 +2007,23 @@ export const SETTINGS_SCHEMA = {
1589
2007
  tab: "tasks",
1590
2008
  label: "Task Input Mode",
1591
2009
  description: "How much shared structure the task tool accepts (default, schema-free, or independent)",
1592
- 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
+ ],
1593
2027
  },
1594
2028
  },
1595
2029
 
@@ -1600,7 +2034,16 @@ export const SETTINGS_SCHEMA = {
1600
2034
  tab: "tasks",
1601
2035
  label: "Max Concurrent Tasks",
1602
2036
  description: "Concurrent limit for subagents",
1603
- 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
+ ],
1604
2047
  },
1605
2048
  },
1606
2049
 
@@ -1611,7 +2054,13 @@ export const SETTINGS_SCHEMA = {
1611
2054
  tab: "tasks",
1612
2055
  label: "Max Task Recursion",
1613
2056
  description: "How many levels deep subagents can spawn their own subagents",
1614
- 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
+ ],
1615
2064
  },
1616
2065
  },
1617
2066
 
@@ -1632,7 +2081,15 @@ export const SETTINGS_SCHEMA = {
1632
2081
  tab: "tasks",
1633
2082
  label: "Todo auto-clear delay",
1634
2083
  description: "How long to wait before removing completed/abandoned tasks from the list",
1635
- 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
+ ],
1636
2093
  },
1637
2094
  },
1638
2095
 
@@ -1726,7 +2183,29 @@ export const SETTINGS_SCHEMA = {
1726
2183
  tab: "providers",
1727
2184
  label: "Web Search Provider",
1728
2185
  description: "Provider for web search tool",
1729
- 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
+ ],
1730
2209
  },
1731
2210
  },
1732
2211
  "providers.image": {
@@ -1737,7 +2216,16 @@ export const SETTINGS_SCHEMA = {
1737
2216
  tab: "providers",
1738
2217
  label: "Image Provider",
1739
2218
  description: "Provider for image generation tool",
1740
- 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
+ ],
1741
2229
  },
1742
2230
  },
1743
2231
 
@@ -1749,7 +2237,10 @@ export const SETTINGS_SCHEMA = {
1749
2237
  tab: "providers",
1750
2238
  label: "Kimi API Format",
1751
2239
  description: "API format for Kimi Code provider",
1752
- submenu: true,
2240
+ options: [
2241
+ { value: "openai", label: "OpenAI", description: "api.kimi.com" },
2242
+ { value: "anthropic", label: "Anthropic", description: "api.moonshot.ai" },
2243
+ ],
1753
2244
  },
1754
2245
  },
1755
2246
 
@@ -1761,7 +2252,11 @@ export const SETTINGS_SCHEMA = {
1761
2252
  tab: "providers",
1762
2253
  label: "OpenAI WebSockets",
1763
2254
  description: "Websocket policy for OpenAI Codex models (auto uses model defaults, on forces, off disables)",
1764
- 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
+ ],
1765
2260
  },
1766
2261
  },
1767
2262
 
@@ -1906,9 +2401,9 @@ export function hasUi(path: SettingPath): boolean {
1906
2401
  }
1907
2402
 
1908
2403
  /** Get UI metadata for a path (undefined if no UI) */
1909
- export function getUi(path: SettingPath): UiMetadata | undefined {
2404
+ export function getUi(path: SettingPath): AnyUiMetadata | undefined {
1910
2405
  const def = SETTINGS_SCHEMA[path];
1911
- return "ui" in def ? (def.ui as UiMetadata) : undefined;
2406
+ return "ui" in def ? (def.ui as AnyUiMetadata) : undefined;
1912
2407
  }
1913
2408
 
1914
2409
  /** Get all paths for a specific tab */