@pellux/goodvibes-tui 1.27.1 → 2.0.1

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 (405) hide show
  1. package/CHANGELOG.md +116 -2
  2. package/README.md +7 -9
  3. package/bin/goodvibes +2 -2
  4. package/bin/launcher-support.js +8 -4
  5. package/docs/README.md +11 -2
  6. package/docs/foundation-artifacts/operator-contract.json +6403 -2347
  7. package/package.json +14 -25
  8. package/scripts/postinstall.js +35 -35
  9. package/src/audio/spoken-turn-model-routing.ts +1 -1
  10. package/src/cli/bundle-command.ts +5 -5
  11. package/src/cli/completions/generate.ts +1 -1
  12. package/src/cli/doctor.ts +3 -3
  13. package/src/cli/entrypoint.ts +12 -7
  14. package/src/cli/help.ts +4 -5
  15. package/src/cli/hooks-command.ts +1 -1
  16. package/src/cli/index.ts +1 -4
  17. package/src/cli/launch-auto-update.ts +59 -7
  18. package/src/cli/management-commands.ts +6 -6
  19. package/src/cli/management-utils.ts +5 -5
  20. package/src/cli/management.ts +7 -7
  21. package/src/cli/network-posture.ts +1 -1
  22. package/src/cli/package-verification.ts +7 -6
  23. package/src/cli/plugin-bundles-command.ts +1 -1
  24. package/src/cli/plugin-command.ts +2 -2
  25. package/src/cli/service-command.ts +2 -2
  26. package/src/cli/service-posture.ts +4 -4
  27. package/src/cli/status.ts +9 -8
  28. package/src/cli/surface-command.ts +4 -4
  29. package/src/cli/tui-startup.ts +3 -3
  30. package/src/cli-flags.ts +3 -3
  31. package/src/config/goodvibes-home-audit.ts +0 -2
  32. package/src/config/secret-config.ts +37 -5
  33. package/src/config/tui-extension-settings.ts +1 -1
  34. package/src/core/approval-alert.ts +2 -2
  35. package/src/core/budget-breach-notifier.ts +3 -3
  36. package/src/core/conversation-compaction-render.ts +1 -1
  37. package/src/core/conversation-line-cache.ts +1 -1
  38. package/src/core/conversation-render-context.ts +1 -1
  39. package/src/core/conversation-rendering.ts +2 -2
  40. package/src/core/conversation.ts +10 -10
  41. package/src/core/long-task-notifier.ts +2 -2
  42. package/src/core/memory-diagnostics-gateway.ts +1 -1
  43. package/src/core/scriptable-statusline.ts +1 -1
  44. package/src/core/session-auto-titler.ts +1 -1
  45. package/src/core/session-resume-core.ts +2 -2
  46. package/src/core/stream-event-wiring.ts +24 -44
  47. package/src/core/terminal-notifier.ts +3 -3
  48. package/src/core/turn-cancellation.ts +1 -1
  49. package/src/core/turn-event-wiring.ts +5 -5
  50. package/src/input/command-registry.ts +6 -6
  51. package/src/input/commands/channel-pairing.ts +1 -1
  52. package/src/input/commands/cluster-runtime.ts +1 -1
  53. package/src/input/commands/codebase-runtime.ts +1 -1
  54. package/src/input/commands/devices-runtime.ts +1 -1
  55. package/src/input/commands/hosted-runtime.ts +330 -0
  56. package/src/input/commands/local-setup-review.ts +1 -1
  57. package/src/input/commands/local-setup-transfer.ts +2 -2
  58. package/src/input/commands/local-setup.ts +3 -3
  59. package/src/input/commands/managed-runtime.ts +2 -2
  60. package/src/input/commands/operator-rpc.ts +24 -110
  61. package/src/input/commands/payment-card-intake.ts +1 -1
  62. package/src/input/commands/permissions-provenance.ts +2 -2
  63. package/src/input/commands/plugin-runtime.ts +1 -1
  64. package/src/input/commands/provider.ts +1 -1
  65. package/src/input/commands/relay-runtime.ts +18 -9
  66. package/src/input/commands/rewind-runtime.ts +1 -1
  67. package/src/input/commands/runtime-services.ts +1 -1
  68. package/src/input/commands/services-runtime.ts +1 -1
  69. package/src/input/commands/session-workflow.ts +1 -1
  70. package/src/input/commands/settings-sync-runtime.ts +2 -2
  71. package/src/input/commands/share-runtime.ts +1 -1
  72. package/src/input/commands/tasks-runtime.ts +145 -55
  73. package/src/input/commands/update-runtime.ts +31 -28
  74. package/src/input/commands/work-plan-runtime.ts +2 -2
  75. package/src/input/commands/workstream-attempts.ts +1 -1
  76. package/src/input/commands/workstream-runtime.ts +2 -2
  77. package/src/input/commands.ts +2 -0
  78. package/src/input/feature-unit-layout.ts +1 -1
  79. package/src/input/feed-context-factory.ts +3 -3
  80. package/src/input/handler-content-actions.ts +2 -2
  81. package/src/input/handler-feed-routes.ts +1 -1
  82. package/src/input/handler-feed.ts +3 -3
  83. package/src/input/handler-modal-token-routes.ts +1 -1
  84. package/src/input/handler-onboarding-daemon-adopt.ts +3 -2
  85. package/src/input/handler-onboarding.ts +1 -1
  86. package/src/input/handler-types.ts +2 -2
  87. package/src/input/handler-ui-state.ts +1 -1
  88. package/src/input/handler.ts +4 -4
  89. package/src/input/input-history.ts +1 -1
  90. package/src/input/onboarding/onboarding-feature-units.ts +2 -2
  91. package/src/input/onboarding/onboarding-runtime-status.ts +15 -1
  92. package/src/input/onboarding/onboarding-wizard-apply.ts +17 -8
  93. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +5 -3
  94. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +1 -1
  95. package/src/input/onboarding/onboarding-wizard-types.ts +1 -1
  96. package/src/input/payments-config.ts +6 -6
  97. package/src/input/search.ts +1 -1
  98. package/src/input/session-picker-modal.ts +46 -0
  99. package/src/input/settings-modal-activation.ts +1 -1
  100. package/src/input/settings-modal-behavior.ts +4 -4
  101. package/src/input/settings-modal-commit.ts +11 -0
  102. package/src/input/settings-modal-data.ts +13 -80
  103. package/src/input/settings-modal-mutations.ts +33 -0
  104. package/src/input/settings-modal-secrets.ts +34 -0
  105. package/src/input/settings-modal-subscriptions.ts +1 -1
  106. package/src/input/settings-modal-types.ts +15 -1
  107. package/src/input/settings-modal.ts +28 -3
  108. package/src/input/worktree-setup-config.ts +1 -1
  109. package/src/main.ts +11 -9
  110. package/src/panels/agent-inspector-shared.ts +2 -2
  111. package/src/panels/base-panel.ts +2 -2
  112. package/src/panels/builtin/operations.ts +6 -8
  113. package/src/panels/builtin/session.ts +17 -0
  114. package/src/panels/builtin/shared.ts +5 -5
  115. package/src/panels/builtin-modals.ts +3 -3
  116. package/src/panels/confirm-state.ts +1 -1
  117. package/src/panels/cost-tracker-panel.ts +4 -4
  118. package/src/panels/diff-panel.ts +2 -2
  119. package/src/panels/diff-review-panel.ts +2 -2
  120. package/src/panels/expandable-list-panel.ts +2 -2
  121. package/src/panels/fleet-acts.ts +1 -1
  122. package/src/panels/fleet-observed-render.ts +1 -1
  123. package/src/panels/fleet-panel-format.ts +1 -1
  124. package/src/panels/fleet-panel.ts +1 -1
  125. package/src/panels/fleet-spawn.ts +1 -1
  126. package/src/panels/fleet-steer.ts +1 -1
  127. package/src/panels/fleet-transcript.ts +1 -1
  128. package/src/panels/git-panel.ts +1 -1
  129. package/src/panels/hosted-session-feed.ts +352 -0
  130. package/src/panels/hosted-session-panel.ts +221 -0
  131. package/src/panels/local-auth-panel.ts +3 -3
  132. package/src/panels/modals/devices-modal.ts +1 -1
  133. package/src/panels/modals/local-auth-modal.ts +1 -1
  134. package/src/panels/modals/pairing-modal.ts +1 -1
  135. package/src/panels/modals/services-modal.ts +1 -1
  136. package/src/panels/modals/settings-sync-modal.ts +1 -1
  137. package/src/panels/modals/subscription-modal.ts +1 -1
  138. package/src/panels/modals/work-plan-modal.ts +1 -1
  139. package/src/panels/notifications-panel.ts +1 -1
  140. package/src/panels/panel-confirm-overlay.ts +1 -1
  141. package/src/panels/plugins-panel.ts +2 -2
  142. package/src/panels/polish-core.ts +2 -2
  143. package/src/panels/polish-tables.ts +1 -1
  144. package/src/panels/polish.ts +2 -2
  145. package/src/panels/scrollable-list-panel.ts +3 -3
  146. package/src/panels/skills-panel.ts +3 -3
  147. package/src/panels/token-budget-panel.ts +5 -5
  148. package/src/panels/types.ts +1 -2
  149. package/src/permissions/prompt.ts +1 -1
  150. package/src/renderer/autocomplete-overlay.ts +1 -1
  151. package/src/renderer/block-actions-overlay.ts +1 -1
  152. package/src/renderer/bookmark-modal.ts +1 -1
  153. package/src/renderer/buffer.ts +1 -1
  154. package/src/renderer/code-block.ts +1 -1
  155. package/src/renderer/composer-fragments.ts +1 -1
  156. package/src/renderer/compositor.ts +2 -2
  157. package/src/renderer/config-modal.ts +1 -1
  158. package/src/renderer/context-inspector.ts +1 -1
  159. package/src/renderer/conversation-layout.ts +2 -2
  160. package/src/renderer/conversation-overlays.ts +1 -1
  161. package/src/renderer/conversation-surface.ts +2 -2
  162. package/src/renderer/diff-view.ts +1 -1
  163. package/src/renderer/diff.ts +1 -1
  164. package/src/renderer/file-picker-overlay.ts +1 -1
  165. package/src/renderer/fleet-tab-strip.ts +1 -1
  166. package/src/renderer/fullscreen-primitives.ts +2 -2
  167. package/src/renderer/fullscreen-workspace.ts +1 -1
  168. package/src/renderer/header-line.ts +1 -1
  169. package/src/renderer/help-overlay.ts +1 -1
  170. package/src/renderer/history-search-overlay.ts +2 -2
  171. package/src/renderer/layout.ts +5 -8
  172. package/src/renderer/markdown-inline.ts +1 -1
  173. package/src/renderer/markdown-table.ts +1 -1
  174. package/src/renderer/markdown.ts +1 -1
  175. package/src/renderer/mcp-workspace.ts +1 -1
  176. package/src/renderer/modal-factory.ts +2 -2
  177. package/src/renderer/model-workspace.ts +2 -2
  178. package/src/renderer/onboarding/onboarding-wizard.ts +1 -1
  179. package/src/renderer/overlay-box.ts +1 -1
  180. package/src/renderer/panel-composite.ts +1 -1
  181. package/src/renderer/panel-workspace-bar.ts +1 -1
  182. package/src/renderer/process-indicator.ts +1 -1
  183. package/src/renderer/profile-picker-modal.ts +1 -1
  184. package/src/renderer/search-overlay.ts +2 -2
  185. package/src/renderer/selection-modal-overlay.ts +1 -1
  186. package/src/renderer/session-picker-modal.ts +124 -3
  187. package/src/renderer/settings-modal-helpers.ts +2 -0
  188. package/src/renderer/settings-modal.ts +2 -2
  189. package/src/renderer/shell-surface.ts +2 -2
  190. package/src/renderer/system-message.ts +1 -1
  191. package/src/renderer/tab-strip.ts +1 -1
  192. package/src/renderer/theme-mode-config.ts +1 -1
  193. package/src/renderer/theme.ts +11 -12
  194. package/src/renderer/thinking.ts +1 -1
  195. package/src/renderer/tool-call.ts +2 -2
  196. package/src/renderer/tool-result-expanded-lines.ts +1 -1
  197. package/src/renderer/turn-injection.ts +7 -8
  198. package/src/renderer/ui-factory.ts +3 -3
  199. package/src/runtime/bootstrap-command-context.ts +4 -4
  200. package/src/runtime/bootstrap-command-parts.ts +5 -5
  201. package/src/runtime/bootstrap-core.ts +30 -30
  202. package/src/runtime/bootstrap-shell.ts +4 -4
  203. package/src/runtime/bootstrap.ts +128 -157
  204. package/src/runtime/client/approval-updates.ts +82 -0
  205. package/src/runtime/client/build-floors.ts +167 -0
  206. package/src/runtime/client/daemon-attach-handshake.ts +115 -0
  207. package/src/runtime/client/fleet-union.ts +147 -0
  208. package/src/runtime/client/host-service-status.ts +71 -0
  209. package/src/runtime/client/hosted-exit.ts +86 -0
  210. package/src/runtime/client/hosted-roster.ts +110 -0
  211. package/src/runtime/client/hosted-session-stream.ts +178 -0
  212. package/src/runtime/client/hosted-sessions.ts +187 -0
  213. package/src/runtime/client/operator-endpoint.ts +213 -0
  214. package/src/runtime/client/spine-adoption.ts +99 -0
  215. package/src/runtime/cloudflare-control-plane.ts +1 -1
  216. package/src/runtime/context-accounting-source.ts +1 -1
  217. package/src/runtime/context.ts +2 -2
  218. package/src/runtime/conversation-rewind-port.ts +42 -14
  219. package/src/runtime/daemon-attach-notices.ts +55 -19
  220. package/src/runtime/diagnostics/panels/ops.ts +1 -1
  221. package/src/runtime/disposal-wiring.ts +4 -24
  222. package/src/runtime/index.ts +119 -105
  223. package/src/runtime/install-self-check-startup.ts +2 -2
  224. package/src/runtime/interaction-seams.ts +1 -1
  225. package/src/runtime/knowledge-services.ts +8 -2
  226. package/src/runtime/legacy-daemon-migration.ts +4 -3
  227. package/src/runtime/legacy-daemon-reconcile.ts +3 -2
  228. package/src/runtime/memory-spine-transport.ts +37 -267
  229. package/src/runtime/notification-dispatch.ts +2 -2
  230. package/src/runtime/onboarding/apply.ts +2 -2
  231. package/src/runtime/onboarding/derivation.ts +2 -2
  232. package/src/runtime/onboarding/markers.ts +2 -2
  233. package/src/runtime/onboarding/progress.ts +2 -2
  234. package/src/runtime/onboarding/snapshot.ts +1 -1
  235. package/src/runtime/onboarding/state.ts +2 -2
  236. package/src/runtime/onboarding/types.ts +2 -2
  237. package/src/runtime/orchestrator-core-services.ts +1 -1
  238. package/src/runtime/path-shadow-startup.ts +28 -221
  239. package/src/runtime/power-keepawake-remote.ts +3 -2
  240. package/src/runtime/process-lifecycle.ts +2 -2
  241. package/src/runtime/provider-fallback.ts +10 -66
  242. package/src/runtime/recovery-autosave.ts +1 -1
  243. package/src/runtime/recovery-offer-wiring.ts +39 -23
  244. package/src/runtime/recovery-prompt.ts +31 -17
  245. package/src/runtime/relay-reachability-bridge.ts +53 -42
  246. package/src/runtime/release-artifacts.ts +2 -1
  247. package/src/runtime/runtime-services-types.ts +82 -35
  248. package/src/runtime/services.ts +305 -300
  249. package/src/runtime/session-resume-liveness-confirm.ts +1 -1
  250. package/src/runtime/ui-services.ts +7 -6
  251. package/src/runtime/update-check.ts +23 -43
  252. package/src/runtime/workspace-checkpointing.ts +1 -1
  253. package/src/runtime/wrfc-persistence.ts +2 -2
  254. package/src/shell/ui-openers.ts +30 -7
  255. package/src/utils/clipboard.ts +1 -1
  256. package/src/verification/verification-ledger.ts +10 -8
  257. package/src/version.ts +1 -1
  258. package/bin/goodvibes-daemon +0 -90
  259. package/src/cli/config-overrides.ts +0 -267
  260. package/src/cli/endpoints.ts +0 -109
  261. package/src/cli/parser.ts +0 -395
  262. package/src/cli/redaction.ts +0 -170
  263. package/src/cli/types.ts +0 -84
  264. package/src/cluster/commands.ts +0 -382
  265. package/src/cluster/remote-daemon-target.ts +0 -225
  266. package/src/cluster/render.ts +0 -268
  267. package/src/config/atomic-write.ts +0 -70
  268. package/src/config/credential-availability.ts +0 -122
  269. package/src/config/goodvibes-home.ts +0 -149
  270. package/src/config/index.ts +0 -88
  271. package/src/config/provider-model.ts +0 -23
  272. package/src/config/read-versioned.ts +0 -228
  273. package/src/core/alert-gating.ts +0 -67
  274. package/src/core/consolidation-receipt.ts +0 -41
  275. package/src/core/focus-tracker.ts +0 -41
  276. package/src/core/history.ts +0 -45
  277. package/src/core/memory-status.ts +0 -118
  278. package/src/core/orchestrator.ts +0 -7
  279. package/src/core/pairing-devices.ts +0 -49
  280. package/src/core/pairing-handoff.ts +0 -101
  281. package/src/core/pairing-offers.ts +0 -83
  282. package/src/core/pairing-origin.ts +0 -78
  283. package/src/core/rewind-turn-anchors.ts +0 -311
  284. package/src/core/session-recovery.ts +0 -210
  285. package/src/core/stable-host.ts +0 -171
  286. package/src/core/transcript-journal.ts +0 -503
  287. package/src/daemon/cli.ts +0 -693
  288. package/src/daemon/fatal-boot-report.ts +0 -111
  289. package/src/daemon/handlers/context.ts +0 -29
  290. package/src/daemon/handlers/contracts.ts +0 -77
  291. package/src/daemon/handlers/credentials.ts +0 -139
  292. package/src/daemon/handlers/drafts/draft-store.ts +0 -427
  293. package/src/daemon/handlers/drafts/index.ts +0 -17
  294. package/src/daemon/handlers/drafts/register.ts +0 -331
  295. package/src/daemon/handlers/errors.ts +0 -18
  296. package/src/daemon/handlers/inbox/cursor-store.ts +0 -457
  297. package/src/daemon/handlers/inbox/index.ts +0 -284
  298. package/src/daemon/handlers/inbox/mapping.ts +0 -192
  299. package/src/daemon/handlers/inbox/poller.ts +0 -212
  300. package/src/daemon/handlers/inbox/provider-adapter.ts +0 -156
  301. package/src/daemon/handlers/inbox/providers/discord.ts +0 -252
  302. package/src/daemon/handlers/inbox/providers/email.ts +0 -152
  303. package/src/daemon/handlers/inbox/providers/imap-client.ts +0 -300
  304. package/src/daemon/handlers/inbox/providers/route-util.ts +0 -24
  305. package/src/daemon/handlers/inbox/providers/slack.ts +0 -263
  306. package/src/daemon/handlers/index.ts +0 -117
  307. package/src/daemon/handlers/register.ts +0 -168
  308. package/src/daemon/handlers/remote/backends/cloud-terminal.ts +0 -142
  309. package/src/daemon/handlers/remote/backends/docker.ts +0 -79
  310. package/src/daemon/handlers/remote/backends/index.ts +0 -40
  311. package/src/daemon/handlers/remote/backends/local-process.ts +0 -113
  312. package/src/daemon/handlers/remote/backends/process-runner.ts +0 -127
  313. package/src/daemon/handlers/remote/backends/ssh.ts +0 -125
  314. package/src/daemon/handlers/remote/backends/types.ts +0 -97
  315. package/src/daemon/handlers/remote/dispatcher.ts +0 -181
  316. package/src/daemon/handlers/remote/index.ts +0 -119
  317. package/src/daemon/handlers/remote/peer-registry.ts +0 -357
  318. package/src/daemon/handlers/remote/service.ts +0 -191
  319. package/src/daemon/handlers/routing/inbox-bridge.ts +0 -71
  320. package/src/daemon/handlers/routing/index.ts +0 -261
  321. package/src/daemon/handlers/routing/route-store.ts +0 -319
  322. package/src/daemon/handlers/routing/routing-resolver.ts +0 -75
  323. package/src/daemon/handlers/sqlite-store.ts +0 -278
  324. package/src/daemon/handlers/triage/index.ts +0 -57
  325. package/src/daemon/handlers/triage/integration.ts +0 -213
  326. package/src/daemon/handlers/triage/pipeline.ts +0 -274
  327. package/src/daemon/handlers/triage/scorer.ts +0 -287
  328. package/src/daemon/handlers/triage/tagger/discord.ts +0 -187
  329. package/src/daemon/handlers/triage/tagger/imap.ts +0 -384
  330. package/src/daemon/handlers/triage/tagger/index.ts +0 -184
  331. package/src/daemon/handlers/triage/tagger/shared.ts +0 -70
  332. package/src/daemon/handlers/triage/tagger/slack.ts +0 -69
  333. package/src/daemon/handlers/triage/types.ts +0 -50
  334. package/src/daemon/lifecycle.ts +0 -41
  335. package/src/daemon/provision-wake-model.ts +0 -81
  336. package/src/daemon/safe-serve.ts +0 -61
  337. package/src/daemon/send/channels.ts +0 -200
  338. package/src/daemon/send/command.ts +0 -332
  339. package/src/daemon/send/composition.ts +0 -99
  340. package/src/daemon/send/failure-text.ts +0 -93
  341. package/src/daemon/send/inert-text.ts +0 -225
  342. package/src/daemon/send/stdin.ts +0 -24
  343. package/src/daemon/service-commands.ts +0 -329
  344. package/src/export/cost-utils.ts +0 -319
  345. package/src/input/selection.ts +0 -135
  346. package/src/plugins/loader.ts +0 -15
  347. package/src/renderer/bottom-bar.ts +0 -58
  348. package/src/renderer/conversation-tree.ts +0 -228
  349. package/src/runtime/agent-graph-composition.ts +0 -78
  350. package/src/runtime/channel-composition.ts +0 -46
  351. package/src/runtime/cluster-composition.ts +0 -124
  352. package/src/runtime/cluster-group-composition.ts +0 -284
  353. package/src/runtime/code-index-services.ts +0 -167
  354. package/src/runtime/daemon-handler-composition.ts +0 -76
  355. package/src/runtime/device-posture-composition.ts +0 -110
  356. package/src/runtime/durability-housekeeping.ts +0 -183
  357. package/src/runtime/durability-services.ts +0 -155
  358. package/src/runtime/embedded-service-factories.ts +0 -74
  359. package/src/runtime/feature-settings.ts +0 -154
  360. package/src/runtime/fleet-needs-input-push.ts +0 -70
  361. package/src/runtime/fleet-services.ts +0 -41
  362. package/src/runtime/idle-power-services.ts +0 -85
  363. package/src/runtime/mail-composition.ts +0 -65
  364. package/src/runtime/memory-fold.ts +0 -55
  365. package/src/runtime/operator-token-cleanup.ts +0 -109
  366. package/src/runtime/path-shadow.ts +0 -418
  367. package/src/runtime/perf/panel-health-monitor.ts +0 -18
  368. package/src/runtime/provider-optimizer-wiring.ts +0 -55
  369. package/src/runtime/reachability-notice.ts +0 -100
  370. package/src/runtime/remote-execution-composition.ts +0 -62
  371. package/src/runtime/session-liveness-marker.ts +0 -257
  372. package/src/runtime/session-pointer-surface.ts +0 -22
  373. package/src/runtime/session-storage-services.ts +0 -41
  374. package/src/runtime/surface-feature-flags.ts +0 -96
  375. package/src/runtime/terminal-output-guard.ts +0 -235
  376. package/src/runtime/trust/workspace-registration.ts +0 -192
  377. package/src/runtime/trust/workspace-trust.ts +0 -229
  378. package/src/runtime/ui/index.ts +0 -39
  379. package/src/runtime/ui/model-picker/data-provider.ts +0 -182
  380. package/src/runtime/ui/model-picker/health-enrichment.ts +0 -231
  381. package/src/runtime/ui/model-picker/index.ts +0 -59
  382. package/src/runtime/ui/model-picker/types.ts +0 -153
  383. package/src/runtime/ui/provider-health/data-provider.ts +0 -244
  384. package/src/runtime/ui/provider-health/fallback-visualizer.ts +0 -71
  385. package/src/runtime/ui/provider-health/index.ts +0 -46
  386. package/src/runtime/ui/provider-health/types.ts +0 -146
  387. package/src/runtime/ui-events.ts +0 -1
  388. package/src/runtime/ui-read-model-helpers.ts +0 -1
  389. package/src/runtime/ui-read-models-observability-maintenance.ts +0 -1
  390. package/src/runtime/ui-read-models-observability-options.ts +0 -1
  391. package/src/runtime/ui-read-models-observability-remote.ts +0 -1
  392. package/src/runtime/ui-read-models-observability-security.ts +0 -1
  393. package/src/runtime/ui-read-models-observability-system.ts +0 -1
  394. package/src/runtime/ui-read-models-observability.ts +0 -1
  395. package/src/runtime/ui-service-queries.ts +0 -1
  396. package/src/runtime/voice-setup-services.ts +0 -98
  397. package/src/runtime/workstream-attempts-validation.ts +0 -77
  398. package/src/runtime/workstream-draft-edits.ts +0 -95
  399. package/src/runtime/workstream-draft-store.ts +0 -274
  400. package/src/runtime/workstream-draft-types.ts +0 -42
  401. package/src/runtime/workstream-services.ts +0 -373
  402. package/src/tools/index.ts +0 -1
  403. package/src/types/grid.ts +0 -48
  404. package/src/types/sql-js.d.ts +0 -15
  405. package/src/work-plans/work-plan-store.ts +0 -702
@@ -19,8 +19,8 @@ import type { UiRuntimeServices } from '../runtime/ui-services.ts';
19
19
  import type { CommandContext } from './command-registry.ts';
20
20
  import type { ConversationManager } from '../core/conversation';
21
21
  import type { ModelPickerModal, ModelPickerTarget } from './model-picker.ts';
22
- import type { SelectionManager } from './selection.ts';
23
- import type { InfiniteBuffer } from '../core/history.ts';
22
+ import type { SelectionManager } from '@pellux/goodvibes-terminal-shell';
23
+ import type { InfiniteBuffer } from '@pellux/goodvibes-terminal-shell';
24
24
  import type { AutocompleteEngine } from './autocomplete.ts';
25
25
  import type { BookmarkModal } from './bookmark-modal.ts';
26
26
  import type { SettingsModal } from './settings-modal.ts';
@@ -1,5 +1,5 @@
1
1
  import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
2
- import type { InfiniteBuffer } from '../core/history.ts';
2
+ import type { InfiniteBuffer } from '@pellux/goodvibes-terminal-shell';
3
3
  import type { SearchManager } from './search.ts';
4
4
  import type { HistorySearch } from './input-history.ts';
5
5
  import type { ConversationManager } from '../core/conversation';
@@ -9,8 +9,8 @@ import type { OnboardingRuntimePosture } from './handler-types.ts';
9
9
  import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibes-sdk/platform/config';
10
10
  import { openExternalUrl } from '@pellux/goodvibes-sdk/platform/utils';
11
11
  import { buildProviderAccountSnapshot } from '@/runtime/index.ts';
12
- import { SelectionManager } from './selection.ts';
13
- import type { InfiniteBuffer } from '../core/history.ts';
12
+ import { SelectionManager } from '@pellux/goodvibes-terminal-shell';
13
+ import type { InfiniteBuffer } from '@pellux/goodvibes-terminal-shell';
14
14
  import type { CommandRegistry, CommandContext } from './command-registry.ts';
15
15
  import { AutocompleteEngine } from './autocomplete.ts';
16
16
  import { FilePickerModal } from './file-picker.ts';
@@ -25,7 +25,7 @@ import { ContextInspectorModal } from '../renderer/context-inspector.ts';
25
25
  import { BookmarkModal } from './bookmark-modal.ts';
26
26
  import { SettingsModal } from './settings-modal.ts';
27
27
  import { McpWorkspace } from './mcp-workspace.ts';
28
- import { SessionPickerModal } from './session-picker-modal.ts';
28
+ import { SessionPickerModal, getSharedHostedSessionRoster } from './session-picker-modal.ts';
29
29
  import { ConfigModal } from './config-modal.ts';
30
30
  import { ProfilePickerModal } from './profile-picker-modal.ts';
31
31
  import type { PanelBurstGuardState } from './panel-paste-flood-guard.ts';
@@ -283,7 +283,7 @@ export class InputHandler implements InputHandlerLike {
283
283
  // retirement: ProcessModal/LiveTailModal/AgentDetailModal were removed
284
284
  // — F2 now opens the Fleet panel, which subsumes the live process tree.
285
285
  this.bookmarkModal = new BookmarkModal(uiServices.shell.bookmarkManager);
286
- this.sessionPickerModal = new SessionPickerModal(uiServices.sessions.sessionManager, uiServices.sessions.sessionBroker);
286
+ this.sessionPickerModal = new SessionPickerModal(uiServices.sessions.sessionManager, uiServices.sessions.sessionBroker, getSharedHostedSessionRoster(), () => this.requestRender()); // third source: the daemon-hosted roster
287
287
  this.profilePickerModal = new ProfilePickerModal(uiServices.shell.profileManager);
288
288
  this.selectionQueue = attachSelectionModalQueue(this); // wires exitApp teardown too — see selection-modal-queue.ts
289
289
  this.initFeedContext();
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { atomicWriteFileSync } from '@/config/atomic-write.ts';
3
+ import { atomicWriteFileSync } from '@pellux/goodvibes-sdk/platform/config';
4
4
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
5
5
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
6
6
 
@@ -14,8 +14,8 @@
14
14
  * lockstep with the SDK feature-flag set.
15
15
  */
16
16
 
17
- import type { ConfigKey } from '../../config/index.ts';
18
- import { getFeatureSetting, isFeatureDefaultEnabled } from '../../runtime/feature-settings.ts';
17
+ import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
18
+ import { getFeatureSetting, isFeatureDefaultEnabled } from '@pellux/goodvibes-terminal-shell';
19
19
  import type {
20
20
  OnboardingWizardControllerLike,
21
21
  OnboardingWizardFieldDefinition,
@@ -38,6 +38,11 @@ export function runtimePortDiagnostic(
38
38
  const version = status.version ? ` version ${status.version}` : '';
39
39
  return `An existing GoodVibes service was verified at ${status.baseUrl}${version}.`;
40
40
  }
41
+ // The only mode left after every named branch above is 'embedded', and this
42
+ // function reads either endpoint's status (see getRuntimeEndpointStatus):
43
+ // the daemon status can never carry it in this product (adoptOnly is
44
+ // hardcoded — see bootstrap.ts), but the HTTP listener's status legitimately
45
+ // does, since the SDK always reports its in-process listener as 'embedded'.
41
46
  return `An embedded GoodVibes service is running at ${status.baseUrl}.`;
42
47
  }
43
48
  if (portInUse) {
@@ -58,6 +63,11 @@ export function isRuntimeEndpointActive(
58
63
  endpoint: OnboardingRuntimeEndpoint,
59
64
  ): boolean {
60
65
  const status = getRuntimeEndpointStatus(state, endpoint);
66
+ // This helper serves both endpoints (see handler-onboarding.ts's daemon and
67
+ // httpListener calls), so the 'embedded' arm stays live for httpListener even
68
+ // though a daemon status here is never 'embedded' in this product (adoptOnly
69
+ // is hardcoded — see bootstrap.ts). Dropping the arm would mark a running
70
+ // HTTP listener as inactive.
61
71
  if (status) return status.mode === 'embedded' || status.mode === 'external';
62
72
  return endpoint === 'daemon'
63
73
  ? state?.daemonRunning === true
@@ -70,7 +80,9 @@ export function isRuntimeEndpointOccupyingConfiguredPort(
70
80
  ): boolean {
71
81
  const status = getRuntimeEndpointStatus(state, endpoint);
72
82
  // 'incompatible' means a GoodVibes daemon holds the configured port (we just
73
- // did not adopt it) — the port IS occupied, so report it as such.
83
+ // did not adopt it) — the port IS occupied, so report it as such. As with
84
+ // isRuntimeEndpointActive above, 'embedded' stays live here for httpListener
85
+ // (daemon status never carries it in this product).
74
86
  if (status) return status.mode === 'embedded' || status.mode === 'external' || status.mode === 'blocked' || status.mode === 'incompatible';
75
87
  return endpoint === 'daemon'
76
88
  ? state?.daemonRunning === true || state?.daemonPortInUse === true
@@ -87,6 +99,8 @@ export function formatRuntimeActiveSuccessMessage(
87
99
  const version = status.version ? ` version ${status.version}` : '';
88
100
  return `${label} is already running as a verified external GoodVibes service at ${status.baseUrl}${version}.`;
89
101
  }
102
+ // Reachable for httpListener (the SDK always reports its in-process listener
103
+ // as 'embedded'); a daemon status never carries this mode in this product.
90
104
  if (status?.mode === 'embedded') {
91
105
  return `${label} is running as an embedded service at ${status.baseUrl}.`;
92
106
  }
@@ -1,7 +1,7 @@
1
1
  import type { OnboardingAcknowledgementTarget, OnboardingApplyOperation, OnboardingApplyRequest } from '../../runtime/onboarding/index.ts';
2
- import { formatProviderModel } from '../../config/provider-model.ts';
3
- import { getServerSurfaceFeatureFlags } from '../../runtime/surface-feature-flags.ts';
4
- import { featureEnablementWrite } from '../../runtime/feature-settings.ts';
2
+ import { formatProviderModel } from '@pellux/goodvibes-sdk/platform/providers';
3
+ import { getServerSurfaceFeatureFlags } from '@pellux/goodvibes-sdk/platform/runtime/operations';
4
+ import { featureEnablementWrite } from '@pellux/goodvibes-terminal-shell';
5
5
  import {
6
6
  buildCloudflareApiTokenRef,
7
7
  buildCloudflareOperationalTokenRef,
@@ -303,14 +303,23 @@ function addCloudflareOperations(
303
303
  setConfig('batch.mode', batchMode);
304
304
  setConfig('batch.queueBackend', batchMode !== 'off' && components.queues ? 'cloudflare' : 'local');
305
305
  // Zero Trust Tunnel auto-enables trustProxy on both services so the
306
- // login-rate-limiter keys on the real CF-Connecting-IP rather than the tunnel
307
- // egress address. RESIDUAL RISK: until the SDK validates CF-Connecting-IP
308
- // against Cloudflare's published IP ranges (SDK handoff Item 5), a client
309
- // that reaches the listener directly can spoof the header to bypass the
310
- // per-IP limiter. The wizard surfaces this in the cloudflare step notice.
306
+ // login-rate-limiter keys on the client address the tunnel forwards rather
307
+ // than the tunnel's own egress address. On its own, that address is read
308
+ // from X-Forwarded-For, which a client reaching the listener directly can
309
+ // set for itself — so it could still choose which address the rate limiter
310
+ // and the audit log recorded.
311
+ //
312
+ // httpListener.trustCloudflare is what closes that: the listener reads
313
+ // CF-Connecting-IP instead, and only when the connecting peer is inside a
314
+ // published Cloudflare range, so a direct client cannot name its own
315
+ // address. This route puts the listener behind the tunnel, which is exactly
316
+ // the deployment the stricter read is for, so the wizard turns it on rather
317
+ // than leaving it to be found in the settings list. It is a no-op without
318
+ // trustProxy, which the same branch writes.
311
319
  if (components.zeroTrustTunnel) {
312
320
  setConfig('controlPlane.trustProxy', true);
313
321
  setConfig('httpListener.trustProxy', true);
322
+ setConfig('httpListener.trustCloudflare', true);
314
323
  }
315
324
  }
316
325
 
@@ -264,9 +264,9 @@ export function buildCloudflareStep(controller: OnboardingWizardControllerLike):
264
264
  fields.push({
265
265
  kind: 'status',
266
266
  id: 'cloudflare.trust-proxy-notice',
267
- label: 'trustProxy will be enabled for control plane and HTTP listener',
267
+ label: 'Forwarded-address handling will be configured for this route',
268
268
  defaultValue: 'Notice',
269
- hint: 'Selecting Zero Trust Tunnel auto-writes controlPlane.trustProxy=true and httpListener.trustProxy=true so the login rate-limiter keys on the real client IP (CF-Connecting-IP) rather than the tunnel egress address. RESIDUAL RISK: until the SDK validates CF-Connecting-IP against Cloudflare published IP ranges (handoff Item 5), a client that reaches the listener directly can spoof this header to bypass the per-IP rate-limiter. See docs/deployment-and-services.md for the full risk posture.',
269
+ hint: 'Selecting Zero Trust Tunnel auto-writes controlPlane.trustProxy=true and httpListener.trustProxy=true, so the login rate-limiter keys on the client address the tunnel forwards rather than the tunnel egress address. On their own those two read that address from X-Forwarded-For, which a client reaching the port directly can set for itself. It also writes httpListener.trustCloudflare=true, which is ON for this route: the HTTP listener instead reads CF-Connecting-IP and only accepts it from a peer inside a published Cloudflare range, so a direct client cannot name its own address to pick which rate-limit bucket and audit-log entry it lands in. The control plane has no equivalent setting, so keep it reachable only through the tunnel. See docs/deployment-and-services.md for the full posture.',
270
270
  });
271
271
  }
272
272
 
@@ -476,7 +476,9 @@ export function buildCloudflareStep(controller: OnboardingWizardControllerLike):
476
476
  `Components: ${enabled ? componentCount : 0} selected`,
477
477
  `Token setup: ${enabled ? setupSource : 'not used'}`,
478
478
  `Provision on final apply: ${enabled ? controller.getStringFieldValue('cloudflare.provision-on-apply', 'no') : 'no'}`,
479
- ...(enabled && components.zeroTrustTunnel ? ['trustProxy: enabled for control plane and HTTP listener (see security notice)'] : []),
479
+ ...(enabled && components.zeroTrustTunnel
480
+ ? ['trustProxy: enabled for control plane and HTTP listener; trustCloudflare: ON for the HTTP listener (see notice)']
481
+ : []),
480
482
  ],
481
483
  fields,
482
484
  };
@@ -34,7 +34,7 @@ import type {
34
34
  ChannelSetupFieldKind,
35
35
  ChannelSurface,
36
36
  } from '@pellux/goodvibes-sdk/platform/channels';
37
- import { DEFAULT_CONFIG, type ConfigKey } from '../../config/index.ts';
37
+ import { DEFAULT_CONFIG, type ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
38
38
  import type { OnboardingSnapshotState } from '../../runtime/onboarding/index.ts';
39
39
  import { TELEGRAM_MODE_OPTIONS, WHATSAPP_PROVIDER_OPTIONS } from './onboarding-wizard-constants.ts';
40
40
  import type { ExternalSurfaceSetupFieldSpec, ExternalSurfaceSpec, OnboardingWizardRadioOption } from './onboarding-wizard-types.ts';
@@ -9,7 +9,7 @@ import {
9
9
  type OnboardingStep1CapabilityItem,
10
10
  type OnboardingStepDerivationState,
11
11
  } from '../../runtime/onboarding/index.ts';
12
- import type { ConfigKey } from '../../config/index.ts';
12
+ import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
13
13
 
14
14
  export type OnboardingWizardMode = OnboardingMode;
15
15
 
@@ -26,15 +26,15 @@
26
26
  * implicit card the way this app's `/payments card` flow models it, so these
27
27
  * four fields are synthetic sub-keys under the SDK's real `payments`
28
28
  * section — the same established pattern this codebase already uses for
29
- * `tts.speed`, `behavior.notifyAfterSeconds`, etc.: a key one level under an
30
- * EXISTING section that CONFIG_SCHEMA hasn't grown a scalar entry for yet.
29
+ * `behavior.notifyAfterSeconds`, `display.themeMode`, etc.: a key one level
30
+ * under an EXISTING section that CONFIG_SCHEMA has no scalar entry for.
31
31
  *
32
32
  * That one-level-under-an-existing-section shape matters mechanically: the
33
33
  * real ConfigManager's dotted-path resolver only throws "Invalid config path"
34
34
  * when an INTERMEDIATE segment is missing (e.g. `payments.card.number`, where
35
35
  * `card` is not itself a section) — it does not throw for a final leaf that
36
- * the schema hasn't declared (`payments.cardNumber`), the same tolerance
37
- * `tts.speed` already relies on under `tts`. So these four keys are named
36
+ * the schema has not declared (`payments.cardNumber`), the same tolerance
37
+ * `display.themeMode` relies on under `display`. So these four keys are named
38
38
  * FLAT (`payments.cardNumber`, not `payments.card.number`) specifically so
39
39
  * `ConfigManager.get/setDynamic` accepts them — verified against the real
40
40
  * ConfigManager (not the fake store this module used before this round): a
@@ -62,7 +62,7 @@
62
62
  */
63
63
 
64
64
  import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
65
- import type { ConfigKey } from '../config/index.ts';
65
+ import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
66
66
  import type { SettingEntry } from './settings-modal-types.ts';
67
67
 
68
68
  /** Real ConfigManager's read surface — these four keys are defensive reads (see header comment). */
@@ -79,7 +79,7 @@ export const PAYMENTS_CVV_HANDLING_CONFIG_KEY = 'payments.cvvHandling' as Config
79
79
  function readStringField(configManager: PaymentsConfigReader, key: ConfigKey): string {
80
80
  // Defensive try/catch, same rationale as worktree-setup-config.ts's
81
81
  // readWorktreeSetupList and this codebase's other one-level-under-a-real-
82
- // section synthetic reads (tts.speed, behavior.notifyAfterSeconds): the real
82
+ // section synthetic reads (behavior.notifyAfterSeconds, display.themeMode): the real
83
83
  // ConfigManager never throws for these particular keys (verified — see
84
84
  // header comment), but degrading to empty rather than crashing the whole
85
85
  // settings modal is the same posture every synthetic setting here takes
@@ -1,4 +1,4 @@
1
- import type { InfiniteBuffer } from '../core/history.ts';
1
+ import type { InfiniteBuffer } from '@pellux/goodvibes-terminal-shell';
2
2
  import type { ConversationManager } from '../core/conversation';
3
3
 
4
4
  export interface SearchMatch {
@@ -17,6 +17,17 @@
17
17
  * selectable for load/delete. `sessionBroker` is optional so every existing
18
18
  * caller/test that only cares about local saved-session management keeps
19
19
  * working unchanged.
20
+ *
21
+ * Hosted sessions: a third, also read-only section lists the conversations
22
+ * running INSIDE the daemon (`sessions.hosted.list`, held by the shared roster
23
+ * in runtime/client/hosted-roster.ts). They belong in the place a person
24
+ * already goes to ask "what sessions are there" — a hosted session is exactly
25
+ * that, one whose loop happens to live elsewhere. Joining one is `/hosted
26
+ * attach <id>` rather than an Enter here, for the same reason cross-surface
27
+ * rows are not loadable: there is no on-disk transcript this process can
28
+ * `load()`, and attaching opens a live stream that the picker has no business
29
+ * owning. The id is rendered in full so the command can be typed straight from
30
+ * the row.
20
31
  */
21
32
 
22
33
  import { unlinkSync } from 'node:fs';
@@ -25,6 +36,20 @@ import type { SharedSessionRecord } from '@pellux/goodvibes-sdk/platform/control
25
36
  import type { CrossSurfaceView, SessionReadFacade } from '@pellux/goodvibes-sdk/platform/runtime/session-spine';
26
37
  import type { ConversationManager } from '../core/conversation';
27
38
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
39
+ import type { HostedRosterSnapshot } from '../runtime/client/hosted-roster.ts';
40
+ // Re-exported so the composition site (input/handler.ts) reaches the picker and
41
+ // its hosted roster through ONE import — that file sits on this repo's 800-line
42
+ // per-file gate and a second import line there is a real cost.
43
+ export { getSharedHostedSessionRoster } from '../runtime/client/hosted-roster.ts';
44
+
45
+ /** What the picker needs of the hosted roster: the last answer, and a way to ask again. */
46
+ export interface SessionPickerHostedRoster {
47
+ snapshot(): HostedRosterSnapshot;
48
+ refresh(): Promise<void>;
49
+ }
50
+
51
+ /** Honest default when no roster was wired: nothing has been read, and nothing is claimed. */
52
+ const UNREAD_HOSTED_ROSTER: HostedRosterSnapshot = { sessions: [], capturedAt: null, note: null };
28
53
 
29
54
  // ---------------------------------------------------------------------------
30
55
  // SessionPickerModal
@@ -51,12 +76,22 @@ export class SessionPickerModal {
51
76
  public visibleRows = 8;
52
77
  public deleteConfirmationTarget: string | null = null;
53
78
 
79
+ /** Daemon-hosted sessions (view-only) — see class doc. Never a fabricated empty list. */
80
+ public hostedRoster: HostedRosterSnapshot = UNREAD_HOSTED_ROSTER;
81
+
54
82
  /** Last status message to show in the modal (e.g. error or success). */
55
83
  public statusMessage = '';
56
84
 
57
85
  public constructor(
58
86
  private readonly sessionManager: SessionManager,
59
87
  private readonly sessionBroker?: SessionReadFacade,
88
+ private readonly hostedRosterSource?: SessionPickerHostedRoster,
89
+ /**
90
+ * Called when a late roster refresh lands, so the frame the user is looking
91
+ * at picks it up. Without it the refresh still happens; it just shows on the
92
+ * next keystroke, which is what a modal with no render hook can honestly do.
93
+ */
94
+ private readonly onRosterRefreshed?: () => void,
60
95
  ) {}
61
96
 
62
97
  /**
@@ -72,6 +107,17 @@ export class SessionPickerModal {
72
107
  this.crossSurfaceSessions = [];
73
108
  this.crossSurfaceView = DORMANT_CROSS_SURFACE_VIEW;
74
109
  }
110
+ // The roster's LAST answer renders immediately (a round trip must not delay
111
+ // the modal), and a refresh is asked for behind it. A refusal keeps the last
112
+ // rows and records why, so the section never silently empties.
113
+ this.hostedRoster = this.hostedRosterSource?.snapshot() ?? UNREAD_HOSTED_ROSTER;
114
+ if (this.hostedRosterSource) {
115
+ const source = this.hostedRosterSource;
116
+ void source.refresh().then(() => {
117
+ this.hostedRoster = source.snapshot();
118
+ this.onRosterRefreshed?.();
119
+ });
120
+ }
75
121
  this.selectedIndex = 0;
76
122
  this.scrollOffset = 0;
77
123
  this.statusMessage = '';
@@ -16,7 +16,7 @@ import { buildMcpEntries, buildSubscriptionEntries } from './settings-modal-data
16
16
  import { modelPickerLaunchForKey } from './settings-modal-behavior.ts';
17
17
  import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
18
18
  import type { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
19
- import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
19
+ import type { ServiceInspectionQuery } from '@/runtime/index.ts';
20
20
  import type { ModelPickerTarget } from './model-picker.ts';
21
21
 
22
22
  // ---------------------------------------------------------------------------
@@ -33,10 +33,10 @@ export function getNumericAdjustmentMeta(setting: ConfigSetting): {
33
33
  if (setting.key === 'wrfc.scoreThreshold') {
34
34
  return { step: 0.1, min: 0, max: 10, precision: 1 };
35
35
  }
36
- if ((setting.key as string) === 'tts.speed') {
37
- // Speed multiplier: 0.1 increments, min 0.1, no hard max (provider-defined).
38
- // tts.speed is not yet a ConfigKey in the SDK schema; cast required.
39
- return { step: 0.1, min: 0.1, precision: 1 };
36
+ if (setting.key === 'tts.speed') {
37
+ // Speed multiplier, 0.1 increments across the schema's own supported range
38
+ // (see the key's numRange in the SDK config schema).
39
+ return { step: 0.1, min: 0.25, max: 4, precision: 1 };
40
40
  }
41
41
  return { step: 1, precision: 0 };
42
42
  }
@@ -19,6 +19,7 @@ import { buildMcpEntries } from './settings-modal-data.ts';
19
19
  import { isWorktreeSetupListConfigKey, parseWorktreeSetupListInput } from './worktree-setup-config.ts';
20
20
  import { isSandboxExecListConfigKey, parseSandboxExecListInput } from './sandbox-exec-config.ts';
21
21
  import { isExecEnvScrubAllowlistConfigKey, parseExecEnvScrubAllowlistInput } from './exec-env-scrub-config.ts';
22
+ import type { SettingsDaemonCredentialWriter } from './settings-modal-secrets.ts';
22
23
  import type { McpEntry, SettingEntry } from './settings-modal-types.ts';
23
24
 
24
25
  export interface CommitEditContext {
@@ -27,6 +28,14 @@ export interface CommitEditContext {
27
28
  readonly editBuffer: string;
28
29
  readonly configManager: ConfigManager | null;
29
30
  readonly secretsManager: SettingsSecretsManager | null;
31
+ /**
32
+ * The daemon's credential write, when a daemon is adopted. A daemon-scoped
33
+ * credential must land where the daemon reads it, not in this surface's own
34
+ * tree — see settings-modal-secrets.ts.
35
+ */
36
+ readonly daemonCredentials?: SettingsDaemonCredentialWriter | null;
37
+ /** Renders a refused credential write; without it a failure would be silent. */
38
+ readonly reportError?: ((message: string) => void) | undefined;
30
39
  readonly mcpRegistry: McpRegistry | null;
31
40
  readonly mcpAllowAllConfirmationTarget: string | null;
32
41
  getSelectedMcp(): McpEntry | null;
@@ -135,7 +144,9 @@ export function commitEditValue(ctx: CommitEditContext): boolean {
135
144
  value: String(parsed ?? ''),
136
145
  configManager: ctx.configManager,
137
146
  secretsManager: ctx.secretsManager,
147
+ daemonCredentials: ctx.daemonCredentials ?? null,
138
148
  setConfigValue: (key, value) => ctx.setValue(key, value),
149
+ ...(ctx.reportError ? { onError: ctx.reportError } : {}),
139
150
  });
140
151
  } else {
141
152
  ctx.setValue(setting.key as ConfigKey, parsed);
@@ -13,8 +13,8 @@ import type { FeatureFlagManager } from '@/runtime/index.ts';
13
13
  import type { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
14
14
  import { buildSubscriptionEntries } from './settings-modal-subscriptions.ts';
15
15
  import type { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
16
- import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
17
- import { CODE_INDEX_ENABLED_CONFIG_KEY } from '../runtime/code-index-services.ts';
16
+ import type { ServiceInspectionQuery } from '@/runtime/index.ts';
17
+ import { CODE_INDEX_ENABLED_CONFIG_KEY } from '@pellux/goodvibes-sdk/platform/runtime/operations';
18
18
  import {
19
19
  WORKTREE_SETUP_CARRY_OVER_GLOBS_CONFIG_KEY,
20
20
  WORKTREE_SETUP_COMMANDS_CONFIG_KEY,
@@ -23,7 +23,7 @@ import {
23
23
  isWorktreeSetupListConfigKey,
24
24
  readWorktreeSetupList,
25
25
  } from './worktree-setup-config.ts';
26
- import { BUDGET_ALERT_USD_CONFIG_KEY, BUDGET_ALERT_USD_DEFAULT, readBudgetAlertUsd } from '../export/cost-utils.ts';
26
+ import { BUDGET_ALERT_USD_CONFIG_KEY, BUDGET_ALERT_USD_DEFAULT, readBudgetAlertUsd } from '@pellux/goodvibes-sdk/platform/providers';
27
27
  import { MEMORY_PROJECTION_DIR_CONFIG_KEY, buildMemoryProjectionDirSyntheticEntry } from './commands/recall-files-config.ts';
28
28
  import { MEMORY_SHOW_PROVENANCE_CONFIG_KEY, buildMemoryProvenanceSyntheticEntry } from '../core/memory-provenance.ts';
29
29
  import {
@@ -54,7 +54,7 @@ import {
54
54
  import { enrichRelaySettingDescriptions } from './relay-settings-descriptions.ts';
55
55
  import { enrichDaemonOwnedSettingDescriptions } from './daemon-owned-settings-descriptions.ts';
56
56
  import { applyFeatureUnitLayout } from './feature-unit-layout.ts';
57
- import { FEATURE_SETTINGS_BY_ID } from '../runtime/feature-settings.ts';
57
+ import { FEATURE_SETTINGS_BY_ID } from '@pellux/goodvibes-terminal-shell';
58
58
  import {
59
59
  buildPaymentsSyntheticEntries,
60
60
  isPaymentsSyntheticConfigKey,
@@ -172,11 +172,6 @@ export function buildSettingGroups(
172
172
  }
173
173
  }
174
174
 
175
- // Synthetic tts.speed (not yet an SDK ConfigKey) — surfaced with an honest caveat.
176
- if (ttsEntries && !ttsEntries.some((e) => e.setting.key === ('tts.speed' as ConfigKey))) {
177
- ttsEntries.push(buildTtsSpeedSyntheticEntry(configManager));
178
- }
179
-
180
175
  // Synthetic behavior.notifyAfterSeconds (TUI-local): long-task push threshold.
181
176
  const behaviorEntries = groups.get('behavior');
182
177
  if (behaviorEntries && !behaviorEntries.some((e) => e.setting.key === ('behavior.notifyAfterSeconds' as ConfigKey))) {
@@ -288,52 +283,6 @@ export function buildSettingGroups(
288
283
  return groups;
289
284
  }
290
285
 
291
- // ---------------------------------------------------------------------------
292
- // TTS_SPEED_DEFAULT — the pending-SDK default for tts.speed
293
- // ---------------------------------------------------------------------------
294
-
295
- /**
296
- * Pending default for tts.speed. Matches the value the SDK will use once
297
- * the schema field is added: 1 (normal speed, provider default).
298
- * Used for the synthetic settings-modal entry and isDefault comparisons.
299
- */
300
- export const TTS_SPEED_DEFAULT = 1;
301
-
302
- /**
303
- * The synthetic ConfigSetting descriptor for tts.speed.
304
- * `tts.speed` is not yet a ConfigKey in the SDK schema. This descriptor is
305
- * TUI-local and is injected into the tts settings group so users can see
306
- * and interact with the setting before the SDK schema catches up.
307
- *
308
- * The key is cast to ConfigKey because ConfigSetting requires it and the SDK
309
- * will add this key in a future release. The cast is safe: configManager.get
310
- * returns undefined for unknown keys rather than throwing.
311
- */
312
- export const TTS_SPEED_SYNTHETIC_SETTING: ConfigSetting = {
313
- key: 'tts.speed' as ConfigKey,
314
- type: 'number',
315
- default: TTS_SPEED_DEFAULT,
316
- description: 'Playback speed multiplier passed to the TTS provider (1.0 = normal). Takes effect immediately via the TUI bridge; SDK schema registration is pending (native typing only).',
317
- };
318
-
319
- /**
320
- * Build the synthetic SettingEntry for tts.speed.
321
- *
322
- * Reads the raw value from configManager using a cast key (tts.speed is not
323
- * yet a valid ConfigKey). If the value is absent or not a positive finite
324
- * number, falls back to TTS_SPEED_DEFAULT and marks isDefault true.
325
- */
326
- export function buildTtsSpeedSyntheticEntry(configManager: Pick<ConfigManager, 'get'>): SettingEntry {
327
- const raw = configManager.get('tts.speed' as ConfigKey);
328
- const parsed = typeof raw === 'number' ? raw : parseFloat(String(raw ?? ''));
329
- const currentValue: number = isFinite(parsed) && parsed > 0 ? parsed : TTS_SPEED_DEFAULT;
330
- return {
331
- setting: TTS_SPEED_SYNTHETIC_SETTING,
332
- currentValue,
333
- isDefault: deepEqual(currentValue, TTS_SPEED_DEFAULT),
334
- };
335
- }
336
-
337
286
  // ---------------------------------------------------------------------------
338
287
  // display.themeMode synthetic setting (light theme)
339
288
  // ---------------------------------------------------------------------------
@@ -628,16 +577,6 @@ export function buildNetworkFilteredItems(
628
577
  // refreshEntryValues — re-reads currentValue/isDefault for all loaded entries
629
578
  // ---------------------------------------------------------------------------
630
579
 
631
- /**
632
- * Normalize a raw config value for the tts.speed synthetic entry.
633
- * Returns the raw value if it is a positive finite number, otherwise falls
634
- * back to TTS_SPEED_DEFAULT. Mirrors the logic in buildTtsSpeedSyntheticEntry.
635
- */
636
- function normalizeTtsSpeedValue(raw: unknown): number {
637
- const parsed = typeof raw === 'number' ? raw : parseFloat(String(raw ?? ''));
638
- return isFinite(parsed) && parsed > 0 ? parsed : TTS_SPEED_DEFAULT;
639
- }
640
-
641
580
  export function refreshEntryValues(
642
581
  groups: Map<SettingsCategory, SettingEntry[]>,
643
582
  configManager: ConfigManager,
@@ -683,16 +622,12 @@ export function refreshEntryValues(
683
622
  continue;
684
623
  }
685
624
  const raw = configManager.get(entry.setting.key as ConfigKey);
686
- // Synthetic entries (e.g. tts.speed) that have no SDK schema key return
687
- // undefined from configManager. Normalize using the same logic used at
688
- // construction time so isDefault stays accurate.
689
- if (entry.setting.key === ('tts.speed' as ConfigKey)) {
690
- entry.currentValue = normalizeTtsSpeedValue(raw);
691
- } else if (entry.setting.key === (THEME_MODE_CONFIG_KEY as ConfigKey)) {
692
- entry.currentValue = coerceThemeModeSetting(raw);
693
- } else {
694
- entry.currentValue = raw;
695
- }
625
+ // Synthetic entries that have no SDK schema key return undefined from
626
+ // configManager. Normalize using the same logic used at construction
627
+ // time so isDefault stays accurate.
628
+ entry.currentValue = entry.setting.key === (THEME_MODE_CONFIG_KEY as ConfigKey)
629
+ ? coerceThemeModeSetting(raw)
630
+ : raw;
696
631
  entry.isDefault = deepEqual(entry.currentValue, entry.setting.default);
697
632
  }
698
633
  }
@@ -736,11 +671,9 @@ export function updateEntryForKey(
736
671
  }
737
672
  const raw = configManager.get(key);
738
673
  // Synthetic entries: normalize using the same fallback logic as construction.
739
- entry.currentValue = key === ('tts.speed' as ConfigKey)
740
- ? normalizeTtsSpeedValue(raw)
741
- : key === (THEME_MODE_CONFIG_KEY as ConfigKey)
742
- ? coerceThemeModeSetting(raw)
743
- : raw;
674
+ entry.currentValue = key === (THEME_MODE_CONFIG_KEY as ConfigKey)
675
+ ? coerceThemeModeSetting(raw)
676
+ : raw;
744
677
  entry.isDefault = deepEqual(entry.currentValue, entry.setting.default);
745
678
  }
746
679
  }
@@ -39,6 +39,21 @@ export type SettingAppliedCallback = (change: {
39
39
  // applySettingValue
40
40
  // ---------------------------------------------------------------------------
41
41
 
42
+ /**
43
+ * Where a DAEMON-owned setting is written.
44
+ *
45
+ * `surfaces.*`, `controlPlane.*`, `watchers.*`, `device.*`, `automation.*` and
46
+ * the rest are executed by the daemon, unattended, with every surface closed.
47
+ * Writing one into this surface's own settings file reports success and
48
+ * configures nothing — the daemon reads a different file and never sees it.
49
+ * So a daemon-owned write goes over `config.set`, and a write with no reachable
50
+ * daemon FAILS rather than landing somewhere harmless-looking.
51
+ */
52
+ export interface DaemonOwnedConfigWriter {
53
+ ownsKey(key: string): boolean;
54
+ set(key: string, value: unknown): Promise<void>;
55
+ }
56
+
42
57
  export function applySettingValue({
43
58
  key,
44
59
  value,
@@ -46,6 +61,8 @@ export function applySettingValue({
46
61
  groups,
47
62
  onSettingApplied,
48
63
  refreshGroups,
64
+ daemonConfig,
65
+ onAsyncError,
49
66
  }: {
50
67
  key: ConfigKey;
51
68
  value: unknown;
@@ -54,6 +71,10 @@ export function applySettingValue({
54
71
  onSettingApplied: SettingAppliedCallback | null;
55
72
  /** Called after applying the value so the caller can re-read currentValues. */
56
73
  refreshGroups: () => void;
74
+ /** Present when a daemon is configured; absent keeps every write local. */
75
+ daemonConfig?: DaemonOwnedConfigWriter | null | undefined;
76
+ /** Renders a refused daemon write, so a failed save is never silent. */
77
+ onAsyncError?: ((message: string) => void) | undefined;
57
78
  }): ApplyValueResult {
58
79
  // Defensive: a handful of TUI-local synthetic keys (see worktree-setup-config.ts)
59
80
  // live under a config section CONFIG_SCHEMA/DEFAULT_CONFIG has never populated
@@ -73,7 +94,19 @@ export function applySettingValue({
73
94
  // we detect restart-triggering keys by sub-key name heuristic below.
74
95
  const isRestartKey = ['host', 'port', 'hostMode', 'enabled'].includes(key.split('.')[1] ?? '');
75
96
 
97
+ const daemonOwned = daemonConfig?.ownsKey(key) === true;
76
98
  try {
99
+ if (daemonOwned && daemonConfig) {
100
+ // The daemon's store is the one that counts for this key. The local
101
+ // manager is still written so this session's own reads and the rows
102
+ // below reflect the edit immediately; the daemon's copy is what the
103
+ // daemon acts on, and a refusal from it is surfaced rather than
104
+ // swallowed — the local value would then be the lie.
105
+ void daemonConfig.set(key, value).catch((error) => {
106
+ logger.error('SettingsModal: the daemon refused a daemon-owned config write', { key, error: summarizeError(error) });
107
+ onAsyncError?.(`That setting is applied by the daemon and saving it there failed: ${summarizeError(error)}`);
108
+ });
109
+ }
77
110
  configManager.setDynamic(key, value);
78
111
  } catch (e) {
79
112
  logger.error('SettingsModal: failed to set config value', { key, error: summarizeError(e) });