@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.
- package/CHANGELOG.md +82 -1
- package/README.md +21 -0
- package/package.json +23 -7
- package/src/cli/grievances-cli.ts +89 -4
- package/src/commands/grievances.ts +33 -7
- package/src/config/prompt-templates.ts +14 -7
- package/src/config/settings-schema.ts +595 -100
- package/src/config/settings.ts +46 -0
- package/src/discovery/helpers.ts +13 -6
- package/src/edit/index.ts +3 -3
- package/src/edit/line-hash.ts +73 -25
- package/src/edit/modes/hashline.lark +10 -3
- package/src/edit/modes/hashline.ts +104 -38
- package/src/edit/renderer.ts +3 -3
- package/src/hindsight/backend.ts +444 -0
- package/src/hindsight/bank.ts +131 -0
- package/src/hindsight/client.ts +445 -0
- package/src/hindsight/config.ts +165 -0
- package/src/hindsight/content.ts +205 -0
- package/src/hindsight/index.ts +6 -0
- package/src/hindsight/retain-queue.ts +166 -0
- package/src/hindsight/transcript.ts +71 -0
- package/src/main.ts +7 -10
- package/src/memories/index.ts +1 -1
- package/src/memory-backend/index.ts +4 -0
- package/src/memory-backend/local-backend.ts +30 -0
- package/src/memory-backend/off-backend.ts +16 -0
- package/src/memory-backend/resolve.ts +24 -0
- package/src/memory-backend/types.ts +69 -0
- package/src/modes/components/settings-defs.ts +50 -451
- package/src/modes/components/settings-selector.ts +4 -2
- package/src/modes/components/status-line/presets.ts +1 -1
- package/src/modes/components/status-line.ts +4 -1
- package/src/modes/controllers/command-controller.ts +6 -5
- package/src/modes/controllers/event-controller.ts +12 -0
- package/src/modes/controllers/mcp-command-controller.ts +23 -0
- package/src/modes/controllers/selector-controller.ts +10 -12
- package/src/modes/interactive-mode.ts +3 -2
- package/src/modes/theme/theme.ts +4 -0
- package/src/prompts/tools/github.md +3 -0
- package/src/prompts/tools/hashline.md +20 -16
- package/src/prompts/tools/read.md +10 -6
- package/src/prompts/tools/recall.md +5 -0
- package/src/prompts/tools/reflect.md +5 -0
- package/src/prompts/tools/retain.md +5 -0
- package/src/prompts/tools/search.md +1 -1
- package/src/sdk.ts +12 -9
- package/src/session/agent-session.ts +75 -3
- package/src/slash-commands/builtin-registry.ts +2 -12
- package/src/ssh/connection-manager.ts +1 -1
- package/src/tools/ast-edit.ts +14 -5
- package/src/tools/ast-grep.ts +12 -3
- package/src/tools/find.ts +47 -7
- package/src/tools/gh-renderer.ts +10 -1
- package/src/tools/gh.ts +233 -5
- package/src/tools/hindsight-recall.ts +70 -0
- package/src/tools/hindsight-reflect.ts +57 -0
- package/src/tools/hindsight-retain.ts +63 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/output-meta.ts +1 -0
- package/src/tools/path-utils.ts +55 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/search.ts +45 -8
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* UI adapter over the schema. Reads `ui.options` declared inline in
|
|
3
|
+
* settings-schema.ts and produces typed widget definitions for the
|
|
4
|
+
* settings selector.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 2. That's it - it appears in the UI automatically
|
|
6
|
+
* To add a new setting to the UI: declare it in `settings-schema.ts`
|
|
7
|
+
* with a `ui` block. If it needs a submenu, include `options: [...]`
|
|
8
|
+
* (or `options: "runtime"` for runtime-injected lists like themes).
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import { THINKING_EFFORTS } from "@oh-my-pi/pi-ai";
|
|
11
11
|
import { TERMINAL } from "@oh-my-pi/pi-tui";
|
|
12
|
+
import { Settings } from "../../config/settings";
|
|
12
13
|
import {
|
|
14
|
+
type AnyUiMetadata,
|
|
13
15
|
getDefault,
|
|
14
16
|
getEnumValues,
|
|
15
17
|
getPathsForTab,
|
|
@@ -18,8 +20,8 @@ import {
|
|
|
18
20
|
SETTING_TABS,
|
|
19
21
|
type SettingPath,
|
|
20
22
|
type SettingTab,
|
|
23
|
+
type SubmenuOption,
|
|
21
24
|
} from "../../config/settings-schema";
|
|
22
|
-
import { getThinkingLevelMetadata } from "../../thinking";
|
|
23
25
|
|
|
24
26
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
25
27
|
// UI Definition Types
|
|
@@ -32,11 +34,16 @@ interface BaseSettingDef {
|
|
|
32
34
|
label: string;
|
|
33
35
|
description: string;
|
|
34
36
|
tab: SettingTab;
|
|
37
|
+
/**
|
|
38
|
+
* Optional visibility predicate. When supplied and returning false, the
|
|
39
|
+
* setting is hidden from the UI. Applies to every variant — booleans,
|
|
40
|
+
* enums, submenus, and text inputs.
|
|
41
|
+
*/
|
|
42
|
+
condition?: () => boolean;
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
export interface BooleanSettingDef extends BaseSettingDef {
|
|
38
46
|
type: "boolean";
|
|
39
|
-
condition?: () => boolean;
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
export interface EnumSettingDef extends BaseSettingDef {
|
|
@@ -44,9 +51,11 @@ export interface EnumSettingDef extends BaseSettingDef {
|
|
|
44
51
|
values: readonly string[];
|
|
45
52
|
}
|
|
46
53
|
|
|
54
|
+
type OptionList = ReadonlyArray<SubmenuOption>;
|
|
55
|
+
|
|
47
56
|
export interface SubmenuSettingDef extends BaseSettingDef {
|
|
48
57
|
type: "submenu";
|
|
49
|
-
|
|
58
|
+
options: OptionList;
|
|
50
59
|
onPreview?: (value: string) => void;
|
|
51
60
|
onPreviewCancel?: (originalValue: string) => void;
|
|
52
61
|
}
|
|
@@ -63,472 +72,62 @@ export type SettingDef = BooleanSettingDef | EnumSettingDef | SubmenuSettingDef
|
|
|
63
72
|
|
|
64
73
|
const CONDITIONS: Record<string, () => boolean> = {
|
|
65
74
|
hasImageProtocol: () => !!TERMINAL.imageProtocol,
|
|
75
|
+
hindsightActive: () => {
|
|
76
|
+
try {
|
|
77
|
+
return Settings.instance.get("memory.backend") === "hindsight";
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
},
|
|
66
82
|
};
|
|
67
83
|
|
|
68
84
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
|
-
//
|
|
85
|
+
// Schema to UI Conversion
|
|
70
86
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
71
87
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// Context maintenance strategy
|
|
77
|
-
"compaction.strategy": [
|
|
78
|
-
{ value: "context-full", label: "Context-full", description: "Summarize in-place and keep the current session" },
|
|
79
|
-
{ value: "handoff", label: "Handoff", description: "Generate handoff and continue in a new session" },
|
|
80
|
-
{
|
|
81
|
-
value: "off",
|
|
82
|
-
label: "Off",
|
|
83
|
-
description: "Disable automatic context maintenance (same behavior as Auto-compact off)",
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
// Context maintenance threshold
|
|
87
|
-
"compaction.thresholdPercent": [
|
|
88
|
-
{ value: "default", label: "Default", description: "Legacy reserve-based threshold" },
|
|
89
|
-
{ value: "10", label: "10%", description: "Extremely early maintenance" },
|
|
90
|
-
{ value: "20", label: "20%", description: "Very early maintenance" },
|
|
91
|
-
{ value: "30", label: "30%", description: "Early maintenance" },
|
|
92
|
-
{ value: "40", label: "40%", description: "Moderately early maintenance" },
|
|
93
|
-
{ value: "50", label: "50%", description: "Halfway point" },
|
|
94
|
-
{ value: "60", label: "60%", description: "Moderate context usage" },
|
|
95
|
-
{ value: "70", label: "70%", description: "Balanced" },
|
|
96
|
-
{ value: "75", label: "75%", description: "Slightly aggressive" },
|
|
97
|
-
{ value: "80", label: "80%", description: "Typical threshold" },
|
|
98
|
-
{ value: "85", label: "85%", description: "Aggressive context usage" },
|
|
99
|
-
{ value: "90", label: "90%", description: "Very aggressive" },
|
|
100
|
-
{ value: "95", label: "95%", description: "Near context limit" },
|
|
101
|
-
],
|
|
102
|
-
"compaction.thresholdTokens": [
|
|
103
|
-
{ value: "default", label: "Default", description: "Use percentage-based threshold" },
|
|
104
|
-
{ value: "25000", label: "25K tokens", description: "Quarter of a 200K window" },
|
|
105
|
-
{ value: "50000", label: "50K tokens", description: "Half of a 200K window" },
|
|
106
|
-
{ value: "100000", label: "100K tokens", description: "Half of a 200K window" },
|
|
107
|
-
{ value: "150000", label: "150K tokens", description: "Three-quarters of a 200K window" },
|
|
108
|
-
{ value: "200000", label: "200K tokens", description: "Full standard context window" },
|
|
109
|
-
{ value: "300000", label: "300K tokens", description: "Large context window" },
|
|
110
|
-
{ value: "500000", label: "500K tokens", description: "Very large context window" },
|
|
111
|
-
],
|
|
112
|
-
"compaction.idleThresholdTokens": [
|
|
113
|
-
{ value: "100000", label: "100K tokens" },
|
|
114
|
-
{ value: "200000", label: "200K tokens" },
|
|
115
|
-
{ value: "300000", label: "300K tokens" },
|
|
116
|
-
{ value: "400000", label: "400K tokens" },
|
|
117
|
-
{ value: "500000", label: "500K tokens" },
|
|
118
|
-
{ value: "600000", label: "600K tokens" },
|
|
119
|
-
{ value: "700000", label: "700K tokens" },
|
|
120
|
-
{ value: "800000", label: "800K tokens" },
|
|
121
|
-
{ value: "900000", label: "900K tokens" },
|
|
122
|
-
],
|
|
123
|
-
"compaction.idleTimeoutSeconds": [
|
|
124
|
-
{ value: "60", label: "1 minute" },
|
|
125
|
-
{ value: "120", label: "2 minutes" },
|
|
126
|
-
{ value: "300", label: "5 minutes" },
|
|
127
|
-
{ value: "600", label: "10 minutes" },
|
|
128
|
-
{ value: "1800", label: "30 minutes" },
|
|
129
|
-
{ value: "3600", label: "1 hour" },
|
|
130
|
-
],
|
|
131
|
-
// Retry max retries
|
|
132
|
-
"retry.maxRetries": [
|
|
133
|
-
{ value: "1", label: "1 retry" },
|
|
134
|
-
{ value: "2", label: "2 retries" },
|
|
135
|
-
{ value: "3", label: "3 retries" },
|
|
136
|
-
{ value: "5", label: "5 retries" },
|
|
137
|
-
{ value: "10", label: "10 retries" },
|
|
138
|
-
],
|
|
139
|
-
// Retry fallback revert policy
|
|
140
|
-
"retry.fallbackRevertPolicy": [
|
|
141
|
-
{
|
|
142
|
-
value: "cooldown-expiry",
|
|
143
|
-
label: "Cooldown expiry",
|
|
144
|
-
description: "Return to the primary model after its suppression window ends",
|
|
145
|
-
},
|
|
146
|
-
{ value: "never", label: "Never", description: "Stay on the fallback model until manually changed" },
|
|
147
|
-
],
|
|
148
|
-
// Task input mode
|
|
149
|
-
"task.simple": [
|
|
150
|
-
{
|
|
151
|
-
value: "default",
|
|
152
|
-
label: "Default",
|
|
153
|
-
description: "Shared context and custom task schema are available",
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
value: "schema-free",
|
|
157
|
-
label: "Schema-free",
|
|
158
|
-
description: "Shared context stays available, but custom task schema is disabled",
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
value: "independent",
|
|
162
|
-
label: "Independent",
|
|
163
|
-
description: "No shared context or custom task schema; each task must stand alone",
|
|
164
|
-
},
|
|
165
|
-
],
|
|
166
|
-
// Task max concurrency
|
|
167
|
-
"task.maxConcurrency": [
|
|
168
|
-
{ value: "0", label: "Unlimited" },
|
|
169
|
-
{ value: "1", label: "1 task" },
|
|
170
|
-
{ value: "2", label: "2 tasks" },
|
|
171
|
-
{ value: "4", label: "4 tasks" },
|
|
172
|
-
{ value: "8", label: "8 tasks" },
|
|
173
|
-
{ value: "16", label: "16 tasks" },
|
|
174
|
-
{ value: "32", label: "32 tasks" },
|
|
175
|
-
{ value: "64", label: "64 tasks" },
|
|
176
|
-
],
|
|
177
|
-
// Task max recursion depth
|
|
178
|
-
"task.maxRecursionDepth": [
|
|
179
|
-
{ value: "-1", label: "Unlimited" },
|
|
180
|
-
{ value: "0", label: "None" },
|
|
181
|
-
{ value: "1", label: "Single" },
|
|
182
|
-
{ value: "2", label: "Double" },
|
|
183
|
-
{ value: "3", label: "Triple" },
|
|
184
|
-
],
|
|
185
|
-
// Task isolation mode
|
|
186
|
-
"task.isolation.mode": [
|
|
187
|
-
{ value: "none", label: "None", description: "No isolation" },
|
|
188
|
-
{ value: "worktree", label: "Worktree", description: "Git worktree isolation" },
|
|
189
|
-
{
|
|
190
|
-
value: "fuse-overlay",
|
|
191
|
-
label: "Fuse Overlay",
|
|
192
|
-
description: "COW overlay via fuse-overlayfs (Unix only)",
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
value: "fuse-projfs",
|
|
196
|
-
label: "Fuse ProjFS",
|
|
197
|
-
description: "COW overlay via ProjFS (Windows only; falls back to worktree if unavailable)",
|
|
198
|
-
},
|
|
199
|
-
],
|
|
200
|
-
// Task isolation merge strategy
|
|
201
|
-
"task.isolation.merge": [
|
|
202
|
-
{ value: "patch", label: "Patch", description: "Combine diffs and git apply" },
|
|
203
|
-
{ value: "branch", label: "Branch", description: "Commit per task, merge with --no-ff" },
|
|
204
|
-
],
|
|
205
|
-
// Task isolation commit messages
|
|
206
|
-
"task.isolation.commits": [
|
|
207
|
-
{ value: "generic", label: "Generic", description: "Static commit message" },
|
|
208
|
-
{ value: "ai", label: "AI", description: "AI-generated commit message from diff" },
|
|
209
|
-
],
|
|
210
|
-
// Todo max reminders
|
|
211
|
-
"todo.reminders.max": [
|
|
212
|
-
{ value: "1", label: "1 reminder" },
|
|
213
|
-
{ value: "2", label: "2 reminders" },
|
|
214
|
-
{ value: "3", label: "3 reminders" },
|
|
215
|
-
{ value: "5", label: "5 reminders" },
|
|
216
|
-
],
|
|
217
|
-
// Search context
|
|
218
|
-
"search.contextBefore": [
|
|
219
|
-
{ value: "0", label: "0 lines" },
|
|
220
|
-
{ value: "1", label: "1 line" },
|
|
221
|
-
{ value: "2", label: "2 lines" },
|
|
222
|
-
{ value: "3", label: "3 lines" },
|
|
223
|
-
{ value: "5", label: "5 lines" },
|
|
224
|
-
],
|
|
225
|
-
"search.contextAfter": [
|
|
226
|
-
{ value: "0", label: "0 lines" },
|
|
227
|
-
{ value: "1", label: "1 line" },
|
|
228
|
-
{ value: "2", label: "2 lines" },
|
|
229
|
-
{ value: "3", label: "3 lines" },
|
|
230
|
-
{ value: "5", label: "5 lines" },
|
|
231
|
-
{ value: "10", label: "10 lines" },
|
|
232
|
-
],
|
|
233
|
-
// Autocomplete max visible
|
|
234
|
-
autocompleteMaxVisible: [
|
|
235
|
-
{ value: "3", label: "3 items" },
|
|
236
|
-
{ value: "5", label: "5 items" },
|
|
237
|
-
{ value: "7", label: "7 items" },
|
|
238
|
-
{ value: "10", label: "10 items" },
|
|
239
|
-
{ value: "15", label: "15 items" },
|
|
240
|
-
{ value: "20", label: "20 items" },
|
|
241
|
-
],
|
|
242
|
-
// Ask timeout
|
|
243
|
-
"ask.timeout": [
|
|
244
|
-
{ value: "0", label: "Disabled" },
|
|
245
|
-
{ value: "15", label: "15 seconds" },
|
|
246
|
-
{ value: "30", label: "30 seconds" },
|
|
247
|
-
{ value: "60", label: "60 seconds" },
|
|
248
|
-
{ value: "120", label: "120 seconds" },
|
|
249
|
-
],
|
|
250
|
-
// Global tool timeout ceiling
|
|
251
|
-
"tools.maxTimeout": [
|
|
252
|
-
{ value: "0", label: "No limit" },
|
|
253
|
-
{ value: "30", label: "30 seconds" },
|
|
254
|
-
{ value: "60", label: "60 seconds" },
|
|
255
|
-
{ value: "120", label: "120 seconds" },
|
|
256
|
-
{ value: "300", label: "5 minutes" },
|
|
257
|
-
{ value: "600", label: "10 minutes" },
|
|
258
|
-
],
|
|
259
|
-
// Artifact spill settings
|
|
260
|
-
"tools.artifactSpillThreshold": [
|
|
261
|
-
{ value: "1", label: "1 KB", description: "~250 tokens" },
|
|
262
|
-
{ value: "2.5", label: "2.5 KB", description: "~625 tokens" },
|
|
263
|
-
{ value: "5", label: "5 KB", description: "~1.25K tokens" },
|
|
264
|
-
{ value: "10", label: "10 KB", description: "~2.5K tokens" },
|
|
265
|
-
{ value: "20", label: "20 KB", description: "~5K tokens" },
|
|
266
|
-
{ value: "30", label: "30 KB", description: "~7.5K tokens" },
|
|
267
|
-
{ value: "50", label: "50 KB", description: "Default; ~12.5K tokens" },
|
|
268
|
-
{ value: "75", label: "75 KB", description: "~19K tokens" },
|
|
269
|
-
{ value: "100", label: "100 KB", description: "~25K tokens" },
|
|
270
|
-
{ value: "200", label: "200 KB", description: "~50K tokens" },
|
|
271
|
-
{ value: "500", label: "500 KB", description: "~125K tokens" },
|
|
272
|
-
{ value: "1000", label: "1 MB", description: "~250K tokens" },
|
|
273
|
-
],
|
|
274
|
-
"tools.artifactTailBytes": [
|
|
275
|
-
{ value: "1", label: "1 KB", description: "~250 tokens" },
|
|
276
|
-
{ value: "2.5", label: "2.5 KB", description: "~625 tokens" },
|
|
277
|
-
{ value: "5", label: "5 KB", description: "~1.25K tokens" },
|
|
278
|
-
{ value: "10", label: "10 KB", description: "~2.5K tokens" },
|
|
279
|
-
{ value: "20", label: "20 KB", description: "Default; ~5K tokens" },
|
|
280
|
-
{ value: "50", label: "50 KB", description: "~12.5K tokens" },
|
|
281
|
-
{ value: "100", label: "100 KB", description: "~25K tokens" },
|
|
282
|
-
{ value: "200", label: "200 KB", description: "~50K tokens" },
|
|
283
|
-
],
|
|
284
|
-
"tools.artifactTailLines": [
|
|
285
|
-
{ value: "50", label: "50 lines", description: "~250 tokens" },
|
|
286
|
-
{ value: "100", label: "100 lines", description: "~500 tokens" },
|
|
287
|
-
{ value: "250", label: "250 lines", description: "~1.25K tokens" },
|
|
288
|
-
{ value: "500", label: "500 lines", description: "Default; ~2.5K tokens" },
|
|
289
|
-
{ value: "1000", label: "1000 lines", description: "~5K tokens" },
|
|
290
|
-
{ value: "2000", label: "2000 lines", description: "~10K tokens" },
|
|
291
|
-
{ value: "5000", label: "5000 lines", description: "~25K tokens" },
|
|
292
|
-
],
|
|
293
|
-
// Read line limit
|
|
294
|
-
"read.defaultLimit": [
|
|
295
|
-
{ value: "200", label: "200 lines" },
|
|
296
|
-
{ value: "300", label: "300 lines" },
|
|
297
|
-
{ value: "500", label: "500 lines" },
|
|
298
|
-
{ value: "1000", label: "1000 lines" },
|
|
299
|
-
{ value: "5000", label: "5000 lines" },
|
|
300
|
-
],
|
|
301
|
-
// Todo auto-clear delay
|
|
302
|
-
"tasks.todoClearDelay": [
|
|
303
|
-
{ value: "0", label: "Instant" },
|
|
304
|
-
{ value: "60", label: "1 minute", description: "Default" },
|
|
305
|
-
{ value: "300", label: "5 minutes" },
|
|
306
|
-
{ value: "900", label: "15 minutes" },
|
|
307
|
-
{ value: "1800", label: "30 minutes" },
|
|
308
|
-
{ value: "3600", label: "1 hour" },
|
|
309
|
-
{ value: "-1", label: "Never" },
|
|
310
|
-
],
|
|
311
|
-
|
|
312
|
-
// Edit fuzzy threshold
|
|
313
|
-
"edit.fuzzyThreshold": [
|
|
314
|
-
{ value: "0.85", label: "0.85", description: "Lenient" },
|
|
315
|
-
{ value: "0.90", label: "0.90", description: "Moderate" },
|
|
316
|
-
{ value: "0.95", label: "0.95", description: "Default" },
|
|
317
|
-
{ value: "0.98", label: "0.98", description: "Strict" },
|
|
318
|
-
],
|
|
319
|
-
// TTSR repeat gap
|
|
320
|
-
"ttsr.repeatGap": [
|
|
321
|
-
{ value: "5", label: "5 messages" },
|
|
322
|
-
{ value: "10", label: "10 messages" },
|
|
323
|
-
{ value: "15", label: "15 messages" },
|
|
324
|
-
{ value: "20", label: "20 messages" },
|
|
325
|
-
{ value: "30", label: "30 messages" },
|
|
326
|
-
],
|
|
327
|
-
"ttsr.interruptMode": [
|
|
328
|
-
{ value: "always", label: "always", description: "Interrupt on prose and tool streams" },
|
|
329
|
-
{ value: "prose-only", label: "prose-only", description: "Interrupt only on reply/thinking matches" },
|
|
330
|
-
{ value: "tool-only", label: "tool-only", description: "Interrupt only on tool-call argument matches" },
|
|
331
|
-
{ value: "never", label: "never", description: "Never interrupt; inject warning after completion" },
|
|
332
|
-
],
|
|
333
|
-
// Provider options
|
|
334
|
-
"providers.webSearch": [
|
|
335
|
-
{
|
|
336
|
-
value: "auto",
|
|
337
|
-
label: "Auto",
|
|
338
|
-
description: "Preferred web-search provider",
|
|
339
|
-
},
|
|
340
|
-
{ value: "exa", label: "Exa", description: "Uses Exa API when EXA_API_KEY is set; falls back to Exa MCP" },
|
|
341
|
-
{ value: "brave", label: "Brave", description: "Requires BRAVE_API_KEY" },
|
|
342
|
-
{ value: "jina", label: "Jina", description: "Requires JINA_API_KEY" },
|
|
343
|
-
{ value: "kimi", label: "Kimi", description: "Requires MOONSHOT_SEARCH_API_KEY or MOONSHOT_API_KEY" },
|
|
344
|
-
{ value: "perplexity", label: "Perplexity", description: "Requires PERPLEXITY_COOKIES or PERPLEXITY_API_KEY" },
|
|
345
|
-
{ value: "anthropic", label: "Anthropic", description: "Uses Anthropic web search" },
|
|
346
|
-
{ value: "zai", label: "Z.AI", description: "Calls Z.AI webSearchPrime MCP" },
|
|
347
|
-
{ value: "tavily", label: "Tavily", description: "Requires TAVILY_API_KEY" },
|
|
348
|
-
{ value: "kagi", label: "Kagi", description: "Requires KAGI_API_KEY and Kagi Search API beta access" },
|
|
349
|
-
{ value: "synthetic", label: "Synthetic", description: "Requires SYNTHETIC_API_KEY" },
|
|
350
|
-
{ value: "parallel", label: "Parallel", description: "Requires PARALLEL_API_KEY" },
|
|
351
|
-
{ value: "searxng", label: "SearXNG", description: "Requires searxng.endpoint" },
|
|
352
|
-
],
|
|
353
|
-
"providers.image": [
|
|
354
|
-
{
|
|
355
|
-
value: "auto",
|
|
356
|
-
label: "Auto",
|
|
357
|
-
description: "Priority: GPT model image tool > Antigravity > OpenRouter > Gemini",
|
|
358
|
-
},
|
|
359
|
-
{ value: "openai", label: "OpenAI", description: "Uses the active GPT Responses/Codex model" },
|
|
360
|
-
{ value: "gemini", label: "Gemini", description: "Requires GEMINI_API_KEY" },
|
|
361
|
-
{ value: "openrouter", label: "OpenRouter", description: "Requires OPENROUTER_API_KEY" },
|
|
362
|
-
],
|
|
363
|
-
"providers.kimiApiFormat": [
|
|
364
|
-
{ value: "openai", label: "OpenAI", description: "api.kimi.com" },
|
|
365
|
-
{ value: "anthropic", label: "Anthropic", description: "api.moonshot.ai" },
|
|
366
|
-
],
|
|
367
|
-
"providers.openaiWebsockets": [
|
|
368
|
-
{ value: "auto", label: "Auto", description: "Use model/provider default websocket behavior" },
|
|
369
|
-
{ value: "off", label: "Off", description: "Disable websockets for OpenAI Codex models" },
|
|
370
|
-
{ value: "on", label: "On", description: "Force websockets for OpenAI Codex models" },
|
|
371
|
-
],
|
|
372
|
-
// Default thinking level
|
|
373
|
-
defaultThinkingLevel: [...THINKING_EFFORTS.map(getThinkingLevelMetadata)],
|
|
374
|
-
// Temperature
|
|
375
|
-
temperature: [
|
|
376
|
-
{ value: "-1", label: "Default", description: "Use provider default" },
|
|
377
|
-
{ value: "0", label: "0", description: "Deterministic" },
|
|
378
|
-
{ value: "0.2", label: "0.2", description: "Focused" },
|
|
379
|
-
{ value: "0.5", label: "0.5", description: "Balanced" },
|
|
380
|
-
{ value: "0.7", label: "0.7", description: "Creative" },
|
|
381
|
-
{ value: "1", label: "1", description: "Maximum variety" },
|
|
382
|
-
],
|
|
383
|
-
topP: [
|
|
384
|
-
{ value: "-1", label: "Default", description: "Use provider default" },
|
|
385
|
-
{ value: "0.1", label: "0.1", description: "Very focused" },
|
|
386
|
-
{ value: "0.3", label: "0.3", description: "Focused" },
|
|
387
|
-
{ value: "0.5", label: "0.5", description: "Balanced" },
|
|
388
|
-
{ value: "0.9", label: "0.9", description: "Broad" },
|
|
389
|
-
{ value: "1", label: "1", description: "No nucleus filtering" },
|
|
390
|
-
],
|
|
391
|
-
topK: [
|
|
392
|
-
{ value: "-1", label: "Default", description: "Use provider default" },
|
|
393
|
-
{ value: "1", label: "1", description: "Greedy top token" },
|
|
394
|
-
{ value: "20", label: "20", description: "Focused" },
|
|
395
|
-
{ value: "40", label: "40", description: "Balanced" },
|
|
396
|
-
{ value: "100", label: "100", description: "Broad" },
|
|
397
|
-
],
|
|
398
|
-
minP: [
|
|
399
|
-
{ value: "-1", label: "Default", description: "Use provider default" },
|
|
400
|
-
{ value: "0.01", label: "0.01", description: "Very permissive" },
|
|
401
|
-
{ value: "0.05", label: "0.05", description: "Balanced" },
|
|
402
|
-
{ value: "0.1", label: "0.1", description: "Strict" },
|
|
403
|
-
],
|
|
404
|
-
presencePenalty: [
|
|
405
|
-
{ value: "-1", label: "Default", description: "Use provider default" },
|
|
406
|
-
{ value: "0", label: "0", description: "No penalty" },
|
|
407
|
-
{ value: "0.5", label: "0.5", description: "Mild novelty" },
|
|
408
|
-
{ value: "1", label: "1", description: "Encourage novelty" },
|
|
409
|
-
{ value: "2", label: "2", description: "Strong novelty" },
|
|
410
|
-
],
|
|
411
|
-
repetitionPenalty: [
|
|
412
|
-
{ value: "-1", label: "Default", description: "Use provider default" },
|
|
413
|
-
{ value: "0.8", label: "0.8", description: "Allow repetition" },
|
|
414
|
-
{ value: "1", label: "1", description: "No penalty" },
|
|
415
|
-
{ value: "1.1", label: "1.1", description: "Mild penalty" },
|
|
416
|
-
{ value: "1.2", label: "1.2", description: "Balanced" },
|
|
417
|
-
{ value: "1.5", label: "1.5", description: "Strong penalty" },
|
|
418
|
-
],
|
|
419
|
-
serviceTier: [
|
|
420
|
-
{ value: "none", label: "None", description: "Omit service_tier parameter" },
|
|
421
|
-
{ value: "auto", label: "Auto", description: "Use provider default tier selection" },
|
|
422
|
-
{ value: "default", label: "Default", description: "Standard priority processing" },
|
|
423
|
-
{ value: "flex", label: "Flex", description: "Use flexible capacity tier when available" },
|
|
424
|
-
{ value: "scale", label: "Scale", description: "Use Scale Tier credits when available" },
|
|
425
|
-
{ value: "priority", label: "Priority", description: "Use Priority processing" },
|
|
426
|
-
],
|
|
427
|
-
// Symbol preset
|
|
428
|
-
symbolPreset: [
|
|
429
|
-
{ value: "unicode", label: "Unicode", description: "Standard symbols (default)" },
|
|
430
|
-
{ value: "nerd", label: "Nerd Font", description: "Requires Nerd Font" },
|
|
431
|
-
{ value: "ascii", label: "ASCII", description: "Maximum compatibility" },
|
|
432
|
-
],
|
|
433
|
-
// Status line preset
|
|
434
|
-
"statusLine.preset": [
|
|
435
|
-
{ value: "default", label: "Default", description: "Model, path, git, context, tokens, cost" },
|
|
436
|
-
{ value: "minimal", label: "Minimal", description: "Path and git only" },
|
|
437
|
-
{ value: "compact", label: "Compact", description: "Model, git, cost, context" },
|
|
438
|
-
{ value: "full", label: "Full", description: "All segments including time" },
|
|
439
|
-
{ value: "nerd", label: "Nerd", description: "Maximum info with Nerd Font icons" },
|
|
440
|
-
{ value: "ascii", label: "ASCII", description: "No special characters" },
|
|
441
|
-
{ value: "custom", label: "Custom", description: "User-defined segments" },
|
|
442
|
-
],
|
|
443
|
-
// Status line separator
|
|
444
|
-
"statusLine.separator": [
|
|
445
|
-
{ value: "powerline", label: "Powerline", description: "Solid arrows (Nerd Font)" },
|
|
446
|
-
{ value: "powerline-thin", label: "Thin chevron", description: "Thin arrows (Nerd Font)" },
|
|
447
|
-
{ value: "slash", label: "Slash", description: "Forward slashes" },
|
|
448
|
-
{ value: "pipe", label: "Pipe", description: "Vertical pipes" },
|
|
449
|
-
{ value: "block", label: "Block", description: "Solid blocks" },
|
|
450
|
-
{ value: "none", label: "None", description: "Space only" },
|
|
451
|
-
{ value: "ascii", label: "ASCII", description: "Greater-than signs" },
|
|
452
|
-
],
|
|
453
|
-
// Loop mode
|
|
454
|
-
"loop.mode": [
|
|
455
|
-
{
|
|
456
|
-
value: "prompt",
|
|
457
|
-
label: "Prompt",
|
|
458
|
-
description: "Re-submit the prompt as a follow-up message (current behavior)",
|
|
459
|
-
},
|
|
460
|
-
{ value: "compact", label: "Compact", description: "Compact the session context, then re-submit the prompt" },
|
|
461
|
-
{ value: "reset", label: "Reset", description: "Start a new session, then re-submit the prompt" },
|
|
462
|
-
],
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
function createSubmenuSettingDef(base: Omit<SettingDef, "type" | "options">, provider: OptionProvider): SettingDef {
|
|
466
|
-
if (typeof provider === "function") {
|
|
467
|
-
return {
|
|
468
|
-
...base,
|
|
469
|
-
type: "submenu",
|
|
470
|
-
get options() {
|
|
471
|
-
return provider();
|
|
472
|
-
},
|
|
473
|
-
};
|
|
474
|
-
} else {
|
|
475
|
-
return {
|
|
476
|
-
...base,
|
|
477
|
-
type: "submenu",
|
|
478
|
-
options: provider,
|
|
479
|
-
};
|
|
480
|
-
}
|
|
88
|
+
function resolveOptions(ui: AnyUiMetadata): OptionList | "runtime" | undefined {
|
|
89
|
+
if (!ui.options) return undefined;
|
|
90
|
+
if (ui.options === "runtime") return "runtime";
|
|
91
|
+
return ui.options;
|
|
481
92
|
}
|
|
482
93
|
|
|
483
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
484
|
-
// Schema to UI Conversion
|
|
485
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
486
|
-
|
|
487
94
|
function pathToSettingDef(path: SettingPath): SettingDef | null {
|
|
488
95
|
const ui = getUi(path);
|
|
489
96
|
if (!ui) return null;
|
|
490
97
|
|
|
491
98
|
const schemaType = getType(path);
|
|
492
|
-
const base = { path, label: ui.label, description: ui.description, tab: ui.tab };
|
|
493
|
-
|
|
494
|
-
// Check for condition
|
|
495
99
|
const condition = ui.condition ? CONDITIONS[ui.condition] : undefined;
|
|
100
|
+
const base = { path, label: ui.label, description: ui.description, tab: ui.tab, condition };
|
|
496
101
|
|
|
497
102
|
if (schemaType === "boolean") {
|
|
498
|
-
return { ...base, type: "boolean"
|
|
103
|
+
return { ...base, type: "boolean" };
|
|
499
104
|
}
|
|
500
105
|
|
|
501
|
-
|
|
502
|
-
const values = getEnumValues(path) ?? [];
|
|
503
|
-
|
|
504
|
-
// If marked as submenu, use submenu type
|
|
505
|
-
if (ui.submenu) {
|
|
506
|
-
const provider = OPTION_PROVIDERS[path];
|
|
507
|
-
if (!provider) return null;
|
|
508
|
-
return createSubmenuSettingDef(base, provider);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
return { ...base, type: "enum", values };
|
|
512
|
-
}
|
|
106
|
+
const options = resolveOptions(ui);
|
|
513
107
|
|
|
514
|
-
if (schemaType === "
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
return createSubmenuSettingDef(base, provider);
|
|
108
|
+
if (schemaType === "enum") {
|
|
109
|
+
if (options === undefined) {
|
|
110
|
+
return { ...base, type: "enum", values: getEnumValues(path) ?? [] };
|
|
518
111
|
}
|
|
112
|
+
// "runtime" is not a valid sentinel for enums — schema types prevent this,
|
|
113
|
+
// but treat defensively as an empty submenu.
|
|
114
|
+
return { ...base, type: "submenu", options: options === "runtime" ? [] : options };
|
|
519
115
|
}
|
|
520
116
|
|
|
521
|
-
if (schemaType === "
|
|
522
|
-
|
|
523
|
-
if (
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
-
// For theme etc, options will be injected at runtime
|
|
527
|
-
return createSubmenuSettingDef(base, []);
|
|
117
|
+
if (schemaType === "number") {
|
|
118
|
+
// Numbers without options are intentionally hidden from the UI.
|
|
119
|
+
if (!options || options === "runtime") return null;
|
|
120
|
+
return { ...base, type: "submenu", options };
|
|
528
121
|
}
|
|
529
122
|
|
|
530
|
-
// Plain string setting — free-text input field
|
|
531
123
|
if (schemaType === "string") {
|
|
124
|
+
if (options === "runtime") {
|
|
125
|
+
// Empty list now; the selector layer (theme handling, etc.) injects choices.
|
|
126
|
+
return { ...base, type: "submenu", options: [] };
|
|
127
|
+
}
|
|
128
|
+
if (options) {
|
|
129
|
+
return { ...base, type: "submenu", options };
|
|
130
|
+
}
|
|
532
131
|
return { ...base, type: "text" };
|
|
533
132
|
}
|
|
534
133
|
|
|
@@ -194,6 +194,7 @@ export interface StatusLinePreviewSettings {
|
|
|
194
194
|
leftSegments?: StatusLineSegmentId[];
|
|
195
195
|
rightSegments?: StatusLineSegmentId[];
|
|
196
196
|
separator?: StatusLineSeparatorStyle;
|
|
197
|
+
sessionAccent?: boolean;
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
export interface SettingsCallbacks {
|
|
@@ -287,8 +288,8 @@ export class SettingsSelectorComponent extends Container {
|
|
|
287
288
|
* Convert a setting definition to a SettingItem for the UI.
|
|
288
289
|
*/
|
|
289
290
|
#defToItem(def: SettingDef): SettingItem | null {
|
|
290
|
-
// Check condition
|
|
291
|
-
if (def.
|
|
291
|
+
// Check condition: applies to every variant — booleans, enums, submenus, text inputs.
|
|
292
|
+
if (def.condition && !def.condition()) {
|
|
292
293
|
return null;
|
|
293
294
|
}
|
|
294
295
|
|
|
@@ -563,6 +564,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
563
564
|
leftSegments: settings.get("statusLine.leftSegments"),
|
|
564
565
|
rightSegments: settings.get("statusLine.rightSegments"),
|
|
565
566
|
separator: settings.get("statusLine.separator"),
|
|
567
|
+
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
566
568
|
};
|
|
567
569
|
this.callbacks.onStatusLinePreview?.(statusLineSettings);
|
|
568
570
|
this.#updateStatusPreview();
|
|
@@ -2,7 +2,7 @@ import type { PresetDef, StatusLinePreset } from "./types";
|
|
|
2
2
|
|
|
3
3
|
export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
4
4
|
default: {
|
|
5
|
-
leftSegments: ["pi", "model", "mode", "path", "git", "pr", "context_pct", "
|
|
5
|
+
leftSegments: ["pi", "model", "mode", "path", "git", "pr", "context_pct", "cost"],
|
|
6
6
|
rightSegments: ["session_name"],
|
|
7
7
|
separator: "powerline-thin",
|
|
8
8
|
segmentOptions: {
|
|
@@ -36,6 +36,7 @@ export interface StatusLineSettings {
|
|
|
36
36
|
separator?: StatusLineSeparatorStyle;
|
|
37
37
|
segmentOptions?: StatusLineSegmentOptions;
|
|
38
38
|
showHookStatus?: boolean;
|
|
39
|
+
sessionAccent?: boolean;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -80,6 +81,7 @@ export class StatusLineComponent implements Component {
|
|
|
80
81
|
separator: settings.get("statusLine.separator"),
|
|
81
82
|
showHookStatus: settings.get("statusLine.showHookStatus"),
|
|
82
83
|
segmentOptions: settings.getGroup("statusLine").segmentOptions,
|
|
84
|
+
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
83
85
|
};
|
|
84
86
|
}
|
|
85
87
|
|
|
@@ -514,7 +516,8 @@ export class StatusLineComponent implements Component {
|
|
|
514
516
|
leftWidth = groupWidth(left, leftCapWidth, leftSepWidth);
|
|
515
517
|
rightWidth = groupWidth(right, rightCapWidth, rightSepWidth);
|
|
516
518
|
const gapWidth = Math.max(1, topFillWidth - leftWidth - rightWidth);
|
|
517
|
-
const sessionName =
|
|
519
|
+
const sessionName =
|
|
520
|
+
effectiveSettings.sessionAccent !== false ? this.session.sessionManager?.getSessionName() : undefined;
|
|
518
521
|
const accentHex = sessionName ? getSessionAccentHex(sessionName) : undefined;
|
|
519
522
|
const gapColor = getSessionAccentAnsi(accentHex) ?? theme.getFgAnsi("border");
|
|
520
523
|
const gapFill = `${gapColor}${theme.boxRound.horizontal.repeat(gapWidth)}\x1b[39m`;
|