@pellux/goodvibes-tui 1.16.1 → 1.18.0

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 (257) hide show
  1. package/CHANGELOG.md +138 -9
  2. package/README.md +1 -1
  3. package/docs/foundation-artifacts/operator-contract.json +5490 -1806
  4. package/package.json +7 -7
  5. package/src/cli/completions/generate.ts +5 -4
  6. package/src/cli/config-overrides.ts +51 -11
  7. package/src/cli/doctor.ts +4 -5
  8. package/src/cli/entrypoint.ts +13 -8
  9. package/src/cli/help.ts +7 -6
  10. package/src/cli/launch-auto-update.ts +214 -0
  11. package/src/cli/management-commands.ts +82 -15
  12. package/src/cli/management.ts +0 -2
  13. package/src/cli/parser.ts +5 -0
  14. package/src/cli/status.ts +13 -0
  15. package/src/cli/surface-command.ts +5 -5
  16. package/src/cli/tui-startup.ts +60 -97
  17. package/src/cli/types.ts +2 -0
  18. package/src/config/index.ts +1 -1
  19. package/src/config/tui-extension-settings.ts +33 -0
  20. package/src/core/composer-state.ts +1 -1
  21. package/src/core/consolidation-receipt.ts +41 -0
  22. package/src/core/conversation-line-cache.ts +3 -3
  23. package/src/core/conversation-rendering.ts +18 -9
  24. package/src/core/conversation.ts +2 -2
  25. package/src/core/long-task-notifier.ts +7 -7
  26. package/src/core/memory-provenance.ts +67 -0
  27. package/src/core/pairing-devices.ts +49 -0
  28. package/src/core/pairing-handoff.ts +101 -0
  29. package/src/core/pairing-offers.ts +83 -0
  30. package/src/core/pairing-origin.ts +78 -0
  31. package/src/core/pairing-tailscale-gateway.ts +56 -0
  32. package/src/core/power-chip-source.ts +91 -0
  33. package/src/core/power-status.ts +95 -0
  34. package/src/core/stable-host.ts +171 -0
  35. package/src/core/stream-event-wiring.ts +29 -6
  36. package/src/core/system-message-noise.ts +1 -1
  37. package/src/core/system-message-router.ts +6 -6
  38. package/src/core/turn-budget-outcome.ts +60 -0
  39. package/src/core/turn-cancellation.ts +40 -1
  40. package/src/core/turn-event-wiring.ts +14 -8
  41. package/src/daemon/cli.ts +64 -58
  42. package/src/daemon/lifecycle.ts +41 -0
  43. package/src/daemon/service-commands.ts +5 -5
  44. package/src/export/cost-utils.ts +128 -6
  45. package/src/input/autocomplete.ts +2 -2
  46. package/src/input/command-registry.ts +55 -9
  47. package/src/input/commands/acp-runtime.ts +72 -0
  48. package/src/input/commands/ci-runtime.ts +5 -1
  49. package/src/input/commands/codebase-runtime.ts +6 -6
  50. package/src/input/commands/cost-runtime.ts +1 -1
  51. package/src/input/commands/devices-runtime.ts +109 -0
  52. package/src/input/commands/git-runtime.ts +28 -4
  53. package/src/input/commands/graph-runtime.ts +45 -0
  54. package/src/input/commands/health-runtime.ts +2 -2
  55. package/src/input/commands/intelligence-runtime.ts +1 -1
  56. package/src/input/commands/local-auth-runtime.ts +1 -1
  57. package/src/input/commands/local-provider-runtime.ts +49 -10
  58. package/src/input/commands/local-runtime.ts +53 -5
  59. package/src/input/commands/memory.ts +2 -2
  60. package/src/input/commands/operator-panel-runtime.ts +5 -5
  61. package/src/input/commands/operator-rpc.ts +32 -11
  62. package/src/input/commands/permissions-provenance.ts +1 -1
  63. package/src/input/commands/permissions-runtime.ts +99 -8
  64. package/src/input/commands/planning-runtime.ts +3 -3
  65. package/src/input/commands/platform-sandbox-runtime.ts +1 -1
  66. package/src/input/commands/power-runtime.ts +56 -0
  67. package/src/input/commands/provider-accounts-runtime.ts +71 -6
  68. package/src/input/commands/provider.ts +7 -5
  69. package/src/input/commands/qrcode-runtime.ts +14 -19
  70. package/src/input/commands/queue-runtime.ts +70 -0
  71. package/src/input/commands/recall-files-git.ts +13 -51
  72. package/src/input/commands/recall-files.ts +8 -11
  73. package/src/input/commands/recall-review.ts +2 -2
  74. package/src/input/commands/relay-runtime.ts +1 -1
  75. package/src/input/commands/remote-runtime.ts +1 -1
  76. package/src/input/commands/runtime-services.ts +4 -4
  77. package/src/input/commands/schedule-runtime.ts +14 -7
  78. package/src/input/commands/services-runtime.ts +1 -1
  79. package/src/input/commands/session-workflow.ts +2 -2
  80. package/src/input/commands/settings-sync-runtime.ts +2 -2
  81. package/src/input/commands/share-runtime.ts +1 -1
  82. package/src/input/commands/shell-core.ts +46 -68
  83. package/src/input/commands/subscription-runtime.ts +1 -1
  84. package/src/input/commands/update-runtime.ts +153 -96
  85. package/src/input/commands/work-plan-runtime.ts +1 -1
  86. package/src/input/commands/workstream-attempts.ts +6 -1
  87. package/src/input/commands/workstream-runtime.ts +46 -27
  88. package/src/input/commands/worktree-runtime.ts +35 -8
  89. package/src/input/commands.ts +10 -2
  90. package/src/input/config-modal-types.ts +14 -3
  91. package/src/input/config-modal.ts +75 -13
  92. package/src/input/feature-unit-layout.ts +93 -82
  93. package/src/input/feed-context-factory.ts +2 -2
  94. package/src/input/handler-command-route.ts +2 -2
  95. package/src/input/handler-content-actions.ts +1 -1
  96. package/src/input/handler-feed-routes.ts +7 -7
  97. package/src/input/handler-feed.ts +4 -4
  98. package/src/input/handler-interactions.ts +3 -2
  99. package/src/input/handler-modal-routes.ts +12 -4
  100. package/src/input/handler-modal-token-routes.ts +1 -1
  101. package/src/input/handler-onboarding-daemon-adopt.ts +2 -2
  102. package/src/input/handler-onboarding.ts +3 -2
  103. package/src/input/handler-picker-routes.ts +26 -13
  104. package/src/input/handler-shortcuts.ts +29 -6
  105. package/src/input/handler.ts +2 -2
  106. package/src/input/keybindings.ts +21 -3
  107. package/src/input/onboarding/onboarding-feature-units.ts +34 -27
  108. package/src/input/onboarding/onboarding-provider-key-fields.ts +112 -0
  109. package/src/input/onboarding/onboarding-wizard-apply.ts +22 -21
  110. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +17 -0
  111. package/src/input/onboarding/onboarding-wizard-network-adopt.ts +2 -2
  112. package/src/input/onboarding/onboarding-wizard-steps.ts +30 -42
  113. package/src/input/panel-integration-actions.ts +2 -2
  114. package/src/input/panel-mouse-geometry.ts +1 -1
  115. package/src/input/panel-paste-flood-guard.ts +1 -1
  116. package/src/input/provider-key-intake.ts +167 -0
  117. package/src/input/sandbox-exec-config.ts +2 -2
  118. package/src/input/selection-modal.ts +1 -1
  119. package/src/input/settings-modal-activation.ts +5 -0
  120. package/src/input/settings-modal-adjustment.ts +4 -31
  121. package/src/input/settings-modal-data.ts +52 -57
  122. package/src/input/settings-modal-mutations.ts +12 -68
  123. package/src/input/settings-modal-types.ts +27 -17
  124. package/src/input/settings-modal.ts +74 -55
  125. package/src/main.ts +90 -90
  126. package/src/panels/agent-inspector-shared.ts +4 -4
  127. package/src/panels/base-panel.ts +2 -2
  128. package/src/panels/builtin/agent.ts +4 -4
  129. package/src/panels/builtin/development.ts +4 -4
  130. package/src/panels/builtin/knowledge.ts +1 -1
  131. package/src/panels/builtin/operations.ts +83 -11
  132. package/src/panels/builtin/session.ts +5 -5
  133. package/src/panels/builtin/shared.ts +16 -4
  134. package/src/panels/builtin-modals.ts +59 -42
  135. package/src/panels/builtin-panels.ts +1 -1
  136. package/src/panels/cost-tracker-panel.ts +124 -7
  137. package/src/panels/diff-panel.ts +39 -6
  138. package/src/panels/eval-registry.ts +2 -2
  139. package/src/panels/fleet-acts.ts +446 -0
  140. package/src/panels/fleet-deep-link.ts +1 -1
  141. package/src/panels/fleet-gateway.ts +117 -0
  142. package/src/panels/fleet-observed-render.ts +128 -0
  143. package/src/panels/fleet-panel-format.ts +163 -11
  144. package/src/panels/fleet-panel-worktree-detail.ts +14 -0
  145. package/src/panels/fleet-panel.ts +73 -57
  146. package/src/panels/fleet-read-model.ts +84 -14
  147. package/src/panels/fleet-spawn.ts +266 -0
  148. package/src/panels/fleet-steer.ts +2 -2
  149. package/src/panels/fleet-stop.ts +41 -5
  150. package/src/panels/fleet-tabs.ts +15 -16
  151. package/src/panels/fleet-transcript.ts +5 -5
  152. package/src/panels/git-panel.ts +2 -2
  153. package/src/panels/index.ts +4 -1
  154. package/src/panels/local-auth-panel.ts +1 -1
  155. package/src/panels/memory-consolidation-gateway.ts +94 -0
  156. package/src/panels/modals/devices-modal.ts +161 -0
  157. package/src/panels/modals/hooks-modal.ts +1 -1
  158. package/src/panels/modals/keybindings-modal.ts +2 -2
  159. package/src/panels/modals/knowledge-modal.ts +1 -1
  160. package/src/panels/modals/marketplace-modal.ts +4 -4
  161. package/src/panels/modals/memory-modal.ts +153 -9
  162. package/src/panels/modals/modal-surface-helpers.ts +1 -1
  163. package/src/panels/modals/modal-theme.ts +1 -1
  164. package/src/panels/modals/pairing-modal.ts +198 -68
  165. package/src/panels/modals/planning-modal.ts +2 -2
  166. package/src/panels/modals/plugins-modal.ts +1 -1
  167. package/src/panels/modals/policy-modal.ts +1 -1
  168. package/src/panels/modals/security-modal.ts +7 -1
  169. package/src/panels/modals/skills-modal.ts +1 -1
  170. package/src/panels/modals/work-plan-modal.ts +3 -3
  171. package/src/panels/notifications-feed.ts +127 -0
  172. package/src/panels/notifications-panel.ts +160 -0
  173. package/src/panels/panel-manager.ts +9 -9
  174. package/src/panels/plugins-panel.ts +4 -4
  175. package/src/panels/project-planning-answer-actions.ts +1 -1
  176. package/src/panels/provider-health-routes.ts +2 -2
  177. package/src/panels/skills-panel.ts +4 -5
  178. package/src/panels/token-budget-panel.ts +11 -3
  179. package/src/panels/types.ts +5 -5
  180. package/src/panels/workstream-graph-render.ts +123 -0
  181. package/src/permissions/broker-approval-card.ts +201 -0
  182. package/src/permissions/hunk-selection.ts +28 -4
  183. package/src/permissions/prompt.ts +244 -29
  184. package/src/renderer/autocomplete-overlay.ts +107 -54
  185. package/src/renderer/code-block.ts +2 -2
  186. package/src/renderer/compaction-history-modal.ts +1 -1
  187. package/src/renderer/compaction-preview.ts +1 -1
  188. package/src/renderer/compaction-quality.ts +1 -1
  189. package/src/renderer/composer-fragments.ts +69 -0
  190. package/src/renderer/compositor.ts +2 -2
  191. package/src/renderer/config-modal.ts +1 -1
  192. package/src/renderer/conversation-overlays.ts +1 -1
  193. package/src/renderer/footer-tips.ts +3 -3
  194. package/src/renderer/hint-grammar.ts +1 -1
  195. package/src/renderer/modal-factory.ts +1 -1
  196. package/src/renderer/model-picker-overlay.ts +1 -1
  197. package/src/renderer/model-workspace.ts +7 -2
  198. package/src/renderer/panel-composite.ts +2 -2
  199. package/src/renderer/panel-workspace-bar.ts +1 -1
  200. package/src/renderer/process-indicator.ts +1 -1
  201. package/src/renderer/selection-modal-overlay.ts +1 -1
  202. package/src/renderer/session-picker-modal.ts +2 -2
  203. package/src/renderer/settings-modal-helpers.ts +23 -5
  204. package/src/renderer/settings-modal.ts +129 -39
  205. package/src/renderer/shell-surface.ts +6 -0
  206. package/src/renderer/terminal-bg-probe.ts +1 -1
  207. package/src/renderer/theme.ts +3 -3
  208. package/src/renderer/tool-call.ts +13 -7
  209. package/src/renderer/tool-result-summary.ts +1 -1
  210. package/src/renderer/turn-injection.ts +7 -6
  211. package/src/renderer/ui-factory.ts +38 -1
  212. package/src/runtime/bootstrap-command-context.ts +7 -2
  213. package/src/runtime/bootstrap-command-parts.ts +9 -5
  214. package/src/runtime/bootstrap-core.ts +41 -41
  215. package/src/runtime/bootstrap-hook-bridge.ts +1 -1
  216. package/src/runtime/bootstrap-shell.ts +13 -5
  217. package/src/runtime/bootstrap.ts +34 -32
  218. package/src/runtime/channel-composition.ts +46 -0
  219. package/src/runtime/code-index-services.ts +3 -4
  220. package/src/runtime/daemon-attach-notices.ts +120 -0
  221. package/src/runtime/durability-services.ts +90 -0
  222. package/src/runtime/embedded-service-factories.ts +40 -0
  223. package/src/runtime/feature-settings.ts +147 -0
  224. package/src/runtime/fleet-services.ts +41 -0
  225. package/src/runtime/idle-power-services.ts +85 -0
  226. package/src/runtime/index.ts +6 -21
  227. package/src/runtime/interaction-seams.ts +109 -0
  228. package/src/runtime/legacy-daemon-migration.ts +29 -19
  229. package/src/runtime/memory-fold.ts +1 -1
  230. package/src/runtime/notification-dispatch.ts +110 -0
  231. package/src/runtime/onboarding/apply.ts +3 -29
  232. package/src/runtime/onboarding/fast-path.ts +39 -6
  233. package/src/runtime/onboarding/provider-key-capture.ts +126 -0
  234. package/src/runtime/onboarding/snapshot.ts +0 -1
  235. package/src/runtime/onboarding/types.ts +9 -3
  236. package/src/runtime/power-keepawake-remote.ts +75 -0
  237. package/src/runtime/process-lifecycle.ts +3 -1
  238. package/src/runtime/provider-fallback.ts +55 -0
  239. package/src/runtime/provider-optimizer-wiring.ts +55 -0
  240. package/src/runtime/release-artifacts.ts +19 -103
  241. package/src/runtime/resume-notice.ts +1 -1
  242. package/src/runtime/services.ts +113 -113
  243. package/src/runtime/session-continuity-hints.ts +38 -0
  244. package/src/runtime/surface-feature-flags.ts +40 -9
  245. package/src/runtime/terminal-output-guard.ts +2 -2
  246. package/src/runtime/trust/workspace-registration.ts +10 -2
  247. package/src/runtime/trust/workspace-trust.ts +58 -31
  248. package/src/runtime/ui-services.ts +6 -1
  249. package/src/runtime/update-check.ts +15 -69
  250. package/src/runtime/workstream-services.ts +2 -2
  251. package/src/shell/blocking-input.ts +108 -132
  252. package/src/shell/recovery-input-helpers.ts +53 -166
  253. package/src/shell/ui-openers.ts +38 -6
  254. package/src/utils/format-duration.ts +1 -1
  255. package/src/verification/verification-ledger.ts +21 -4
  256. package/src/version.ts +1 -1
  257. package/src/input/commands/flags-runtime.ts +0 -328
@@ -5,9 +5,12 @@
5
5
  * - Active category (Tab to cycle)
6
6
  * - Selected setting index within category (↑↓)
7
7
  * - Editing mode for inline string/number input
8
- * - Feature flags tab with runtime toggle support
8
+ * - Feature-unit headers (each capability's real enablement row in its domain)
9
9
  *
10
- * Saves changes via configManager.set(key, value) or featureFlagManager methods.
10
+ * Every save is a plain configManager.set(key, value) on a domain settings
11
+ * key; the SDK settings bridge keeps the runtime gate manager in sync, and
12
+ * this modal re-reads the manager after enablement writes so pending-restart
13
+ * markers are shown honestly at the point of change.
11
14
  *
12
15
  * Data assembly delegates to settings-modal-data.ts (buildSettingGroups, etc.).
13
16
  * Mutation logic delegates to settings-modal-mutations.ts (applySettingValue, etc.).
@@ -41,7 +44,6 @@ import {
41
44
  } from './settings-modal-types.ts';
42
45
  import {
43
46
  buildSettingGroups,
44
- buildFlagEntries,
45
47
  buildMcpEntries,
46
48
  buildSubscriptionEntries,
47
49
  buildNetworkFilteredItems,
@@ -51,9 +53,10 @@ import {
51
53
  import { getSettingLabel } from '../renderer/settings-modal-helpers.ts';
52
54
  import {
53
55
  applySettingValue,
54
- applyFlagState,
56
+ syncFlagEntryFromManager,
55
57
  type SettingAppliedCallback,
56
58
  } from './settings-modal-mutations.ts';
59
+ import { featureEnablementWrite, isFeatureValueEnabled } from '../runtime/feature-settings.ts';
57
60
  import {
58
61
  activateSelected as _activateSelected,
59
62
  handleSubscriptionLogoutKey as _handleSubscriptionLogoutKey,
@@ -117,6 +120,13 @@ export class SettingsModal {
117
120
 
118
121
  /** Current value of the inline edit buffer. */
119
122
  public editBuffer = '';
123
+ /**
124
+ * Scroll offset (in wrapped lines) of the documentation pane for the
125
+ * selected row. The renderer clamps it to the pane's real content height
126
+ * and shows honest more-above/below markers — long feature documentation
127
+ * scrolls, it never clips. Reset whenever the selection changes.
128
+ */
129
+ public contextScroll = 0;
120
130
  /** Server awaiting explicit allow-all confirmation, if any. */
121
131
  public mcpAllowAllConfirmationTarget: string | null = null;
122
132
  /**
@@ -140,8 +150,6 @@ export class SettingsModal {
140
150
  /** Settings grouped by category. */
141
151
  public groups: Map<SettingsCategory, SettingEntry[]> = new Map();
142
152
 
143
- /** Feature flag entries (populated when flags tab is active). */
144
- public flagEntries: FlagEntry[] = [];
145
153
  /** MCP server trust entries (populated when mcp tab is active). */
146
154
  public mcpEntries: McpEntry[] = [];
147
155
  /** Provider subscription entries (populated when subscriptions tab is active). */
@@ -207,7 +215,6 @@ export class SettingsModal {
207
215
  this.mcpRegistry = mcpRegistry ?? null;
208
216
  this.onSettingApplied = options?.onSettingApplied ?? null;
209
217
  this.groups = buildSettingGroups(configManager, featureFlagManager);
210
- this.flagEntries = buildFlagEntries(featureFlagManager);
211
218
  this.mcpEntries = buildMcpEntries(this.mcpRegistry);
212
219
  this.subscriptionEntries = buildSubscriptionEntries(subscriptionManager, serviceRegistry);
213
220
  this.categoryIndex = 0;
@@ -225,9 +232,15 @@ export class SettingsModal {
225
232
  this.searchQuery = '';
226
233
  this.searchResults = [];
227
234
  this.searchFocused = false;
235
+ this.contextScroll = 0;
228
236
  this.active = true;
229
237
  }
230
238
 
239
+ /** Scroll the documentation pane by `delta` wrapped lines (renderer clamps the top end). */
240
+ scrollContext(delta: number): void {
241
+ this.contextScroll = Math.max(0, this.contextScroll + delta);
242
+ }
243
+
231
244
  close(): void {
232
245
  this.active = false;
233
246
  this.editingMode = false;
@@ -242,6 +255,7 @@ export class SettingsModal {
242
255
  this.searchQuery = '';
243
256
  this.searchResults = [];
244
257
  this.searchFocused = false;
258
+ this.contextScroll = 0;
245
259
  this.serviceRegistry = null;
246
260
  this.secretsManager = null;
247
261
  this.onSettingApplied = null;
@@ -266,6 +280,7 @@ export class SettingsModal {
266
280
  setSearchQuery(query: string): void {
267
281
  this.searchQuery = query;
268
282
  this.searchFocused = true;
283
+ this.contextScroll = 0;
269
284
  if (query.trim().length === 0) {
270
285
  this.searchResults = [];
271
286
  } else {
@@ -286,6 +301,7 @@ export class SettingsModal {
286
301
  if (this.editingMode) return;
287
302
  this.categoryIndex = (this.categoryIndex + 1) % SETTINGS_CATEGORIES.length;
288
303
  this.selectedIndex = 0;
304
+ this.contextScroll = 0;
289
305
  this.subscriptionLogoutConfirmationTarget = null;
290
306
  this._reloadTabEntries();
291
307
  }
@@ -295,6 +311,7 @@ export class SettingsModal {
295
311
  if (this.editingMode) return;
296
312
  this.categoryIndex = (this.categoryIndex - 1 + SETTINGS_CATEGORIES.length) % SETTINGS_CATEGORIES.length;
297
313
  this.selectedIndex = 0;
314
+ this.contextScroll = 0;
298
315
  this.subscriptionLogoutConfirmationTarget = null;
299
316
  this._reloadTabEntries();
300
317
  }
@@ -326,6 +343,7 @@ export class SettingsModal {
326
343
 
327
344
  moveUp(): void {
328
345
  if (this.editingMode) return;
346
+ this.contextScroll = 0;
329
347
  if (this.searchFocused) {
330
348
  if (this.searchResults.length > 0) {
331
349
  this.selectedIndex = (this.selectedIndex - 1 + this.searchResults.length) % this.searchResults.length;
@@ -334,9 +352,7 @@ export class SettingsModal {
334
352
  }
335
353
  const items = this._currentItems();
336
354
  if (items.length === 0) {
337
- if (this.currentCategory === 'flags' && this.flagEntries.length > 0) {
338
- this.selectedIndex = (this.selectedIndex - 1 + this.flagEntries.length) % this.flagEntries.length;
339
- } else if (this.currentCategory === 'mcp' && this.mcpEntries.length > 0) {
355
+ if (this.currentCategory === 'mcp' && this.mcpEntries.length > 0) {
340
356
  this.selectedIndex = (this.selectedIndex - 1 + this.mcpEntries.length) % this.mcpEntries.length;
341
357
  } else if (this.currentCategory === 'subscriptions' && this.subscriptionEntries.length > 0) {
342
358
  this.selectedIndex = (this.selectedIndex - 1 + this.subscriptionEntries.length) % this.subscriptionEntries.length;
@@ -350,6 +366,7 @@ export class SettingsModal {
350
366
 
351
367
  moveDown(): void {
352
368
  if (this.editingMode) return;
369
+ this.contextScroll = 0;
353
370
  if (this.searchFocused) {
354
371
  if (this.searchResults.length > 0) {
355
372
  this.selectedIndex = (this.selectedIndex + 1) % this.searchResults.length;
@@ -358,9 +375,7 @@ export class SettingsModal {
358
375
  }
359
376
  const items = this._currentItems();
360
377
  if (items.length === 0) {
361
- if (this.currentCategory === 'flags' && this.flagEntries.length > 0) {
362
- this.selectedIndex = (this.selectedIndex + 1) % this.flagEntries.length;
363
- } else if (this.currentCategory === 'mcp' && this.mcpEntries.length > 0) {
378
+ if (this.currentCategory === 'mcp' && this.mcpEntries.length > 0) {
364
379
  this.selectedIndex = (this.selectedIndex + 1) % this.mcpEntries.length;
365
380
  } else if (this.currentCategory === 'subscriptions' && this.subscriptionEntries.length > 0) {
366
381
  this.selectedIndex = (this.selectedIndex + 1) % this.subscriptionEntries.length;
@@ -414,6 +429,7 @@ export class SettingsModal {
414
429
  selectTarget(target?: string): void {
415
430
  const normalized = target?.trim();
416
431
  if (!normalized) return;
432
+ this.contextScroll = 0;
417
433
 
418
434
  const categoryIndex = SETTINGS_CATEGORIES.indexOf(normalized as SettingsCategory);
419
435
  if (categoryIndex >= 0) {
@@ -493,38 +509,21 @@ export class SettingsModal {
493
509
  }
494
510
 
495
511
  /**
496
- * Toggle the currently selected feature flag.
497
- *
498
- * Killed flags cannot be toggled. Non-runtimeToggleable flags toggle in config
499
- * only (require restart). runtimeToggleable flags toggle immediately.
512
+ * Toggle the currently selected feature unit through its real domain
513
+ * settings key: boolean/constant enablements flip the key, enum enablements
514
+ * jump between the feature's stock active mode and its off mode. Killed
515
+ * features cannot be toggled; constant capabilities with no off position
516
+ * (non-boolean keys) are left to their domain settings.
500
517
  */
501
518
  toggleSelectedFlag(): void {
502
519
  const entry = this.getSelected();
503
520
  if (!entry?.flag) return;
504
521
  if (entry.flag.state === 'killed') return;
505
- this._toggleFlagValue(entry.setting.key as ConfigKey, entry.flag.state !== 'enabled');
506
- }
507
-
508
- /**
509
- * Toggle the feature flag behind a `featureFlags.<id>` header row. Finds the
510
- * header entry, applies the desired state through the flag manager (runtime +
511
- * persisted override), and refreshes the header row's value/default marker in
512
- * place. Killed flags and no-op toggles are ignored.
513
- */
514
- private _toggleFlagValue(key: ConfigKey, value: unknown): void {
515
- if (!this.featureFlagManager || !this.configManager) return;
516
- let target: SettingEntry | null = null;
517
- for (const entries of this.groups.values()) {
518
- const candidate = entries.find((e) => e.setting.key === key && e.flag);
519
- if (candidate) { target = candidate; break; }
520
- }
521
- if (!target?.flag) return;
522
- if (target.flag.state === 'killed') return;
523
- const desired = value ? 'enabled' : 'disabled';
524
- if (target.flag.state === desired) return;
525
- applyFlagState(target.flag, desired, this.featureFlagManager, this.configManager);
526
- target.currentValue = target.flag.state === 'enabled';
527
- target.isDefault = target.flag.state === target.flag.flag.defaultState;
522
+ const feature = entry.flag.feature;
523
+ const currentlyOn = isFeatureValueEnabled(feature, entry.currentValue);
524
+ const write = featureEnablementWrite(feature.id, !currentlyOn);
525
+ if (!write) return;
526
+ this._setValue(write.key, write.value);
528
527
  }
529
528
 
530
529
  /**
@@ -586,6 +585,18 @@ export class SettingsModal {
586
585
  }
587
586
  }
588
587
 
588
+ if (setting.type === 'object') {
589
+ // Object-typed keys (e.g. pricing.modelPrices) edit as JSON; the
590
+ // schema's validate() below still rules on the parsed shape.
591
+ try {
592
+ parsed = JSON.parse(this.editBuffer);
593
+ } catch {
594
+ this.editingMode = false;
595
+ this.editBuffer = '';
596
+ return false;
597
+ }
598
+ }
599
+
589
600
  if (setting.validate && !setting.validate(parsed)) {
590
601
  this.editingMode = false;
591
602
  this.editBuffer = '';
@@ -685,11 +696,9 @@ export class SettingsModal {
685
696
 
686
697
  // ── Private helpers ────────────────────────────────────────────────────────────────
687
698
 
688
- /** Reload flag/mcp/subscription entries when the active tab changes. */
699
+ /** Reload mcp/subscription entries when the active tab changes. */
689
700
  private _reloadTabEntries(): void {
690
- if (this.currentCategory === 'flags') {
691
- this.flagEntries = buildFlagEntries(this.featureFlagManager);
692
- } else if (this.currentCategory === 'mcp') {
701
+ if (this.currentCategory === 'mcp') {
693
702
  this.mcpEntries = buildMcpEntries(this.mcpRegistry);
694
703
  } else if (this.currentCategory === 'subscriptions') {
695
704
  this.subscriptionEntries = buildSubscriptionEntries(this.subscriptionManager, this.serviceRegistry);
@@ -698,8 +707,8 @@ export class SettingsModal {
698
707
 
699
708
  /**
700
709
  * Returns [] for the mcp/subscriptions categories (which render their own
701
- * entry types). The 'flags' category now flows through the normal group path
702
- * — it holds the no-config feature-unit toggles (Advanced Features).
710
+ * entry types). Every other category flows through the normal group path,
711
+ * feature-unit headers included.
703
712
  */
704
713
  private _currentItems(): SettingEntry[] {
705
714
  if (
@@ -716,15 +725,6 @@ export class SettingsModal {
716
725
  private _setValue(key: ConfigKey, value: unknown): void {
717
726
  if (!this.configManager) return;
718
727
 
719
- // Feature-unit toggle headers write to `featureFlags.<id>`; route those to
720
- // the feature-flag manager (runtime toggle + persisted override) instead of
721
- // a plain config write. Reset/adjust/activate all funnel a boolean value
722
- // here (true = enabled), so this is the single flag-toggle chokepoint.
723
- if (typeof key === 'string' && key.startsWith('featureFlags.')) {
724
- this._toggleFlagValue(key, value);
725
- return;
726
- }
727
-
728
728
  const callback: SettingAppliedCallback | null = this.onSettingApplied
729
729
  ? (change) => this.onSettingApplied!(change)
730
730
  : null;
@@ -740,6 +740,13 @@ export class SettingsModal {
740
740
  },
741
741
  });
742
742
 
743
+ // Feature-unit headers write plain domain keys; the SDK settings bridge
744
+ // has already applied the change to the gate manager (live flip for
745
+ // runtime-toggleable features, pending-restart marker for startup-gated
746
+ // ones). Re-read the manager for every header bound to this key so the
747
+ // rows and the context pane show the honest state at the point of change.
748
+ this._syncFeatureHeadersForKey(key);
749
+
743
750
  if (result.restartDomain !== null) {
744
751
  this.lastSaveTriggeredRestart = result.restartDomain;
745
752
  }
@@ -749,4 +756,16 @@ export class SettingsModal {
749
756
  // No-op (result.changed === false, effectMessage === null): leave lastSettingEffectMessage untouched.
750
757
  }
751
758
 
759
+ /** Refresh live/persisted/pending state for every feature header bound to `key`. */
760
+ private _syncFeatureHeadersForKey(key: ConfigKey): void {
761
+ if (!this.featureFlagManager) return;
762
+ for (const entries of this.groups.values()) {
763
+ for (const entry of entries) {
764
+ if (entry.flag && entry.flag.feature.enablement.key === key) {
765
+ syncFlagEntryFromManager(entry.flag, this.featureFlagManager);
766
+ }
767
+ }
768
+ }
769
+ }
770
+
752
771
  }