@pellux/goodvibes-tui 1.27.0 → 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 (404) hide show
  1. package/CHANGELOG.md +125 -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 +12 -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 +15 -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 -641
  288. package/src/daemon/handlers/context.ts +0 -29
  289. package/src/daemon/handlers/contracts.ts +0 -77
  290. package/src/daemon/handlers/credentials.ts +0 -139
  291. package/src/daemon/handlers/drafts/draft-store.ts +0 -427
  292. package/src/daemon/handlers/drafts/index.ts +0 -17
  293. package/src/daemon/handlers/drafts/register.ts +0 -331
  294. package/src/daemon/handlers/errors.ts +0 -18
  295. package/src/daemon/handlers/inbox/cursor-store.ts +0 -457
  296. package/src/daemon/handlers/inbox/index.ts +0 -284
  297. package/src/daemon/handlers/inbox/mapping.ts +0 -192
  298. package/src/daemon/handlers/inbox/poller.ts +0 -212
  299. package/src/daemon/handlers/inbox/provider-adapter.ts +0 -156
  300. package/src/daemon/handlers/inbox/providers/discord.ts +0 -252
  301. package/src/daemon/handlers/inbox/providers/email.ts +0 -152
  302. package/src/daemon/handlers/inbox/providers/imap-client.ts +0 -300
  303. package/src/daemon/handlers/inbox/providers/route-util.ts +0 -24
  304. package/src/daemon/handlers/inbox/providers/slack.ts +0 -263
  305. package/src/daemon/handlers/index.ts +0 -117
  306. package/src/daemon/handlers/register.ts +0 -168
  307. package/src/daemon/handlers/remote/backends/cloud-terminal.ts +0 -142
  308. package/src/daemon/handlers/remote/backends/docker.ts +0 -79
  309. package/src/daemon/handlers/remote/backends/index.ts +0 -40
  310. package/src/daemon/handlers/remote/backends/local-process.ts +0 -113
  311. package/src/daemon/handlers/remote/backends/process-runner.ts +0 -127
  312. package/src/daemon/handlers/remote/backends/ssh.ts +0 -125
  313. package/src/daemon/handlers/remote/backends/types.ts +0 -97
  314. package/src/daemon/handlers/remote/dispatcher.ts +0 -181
  315. package/src/daemon/handlers/remote/index.ts +0 -119
  316. package/src/daemon/handlers/remote/peer-registry.ts +0 -357
  317. package/src/daemon/handlers/remote/service.ts +0 -191
  318. package/src/daemon/handlers/routing/inbox-bridge.ts +0 -71
  319. package/src/daemon/handlers/routing/index.ts +0 -261
  320. package/src/daemon/handlers/routing/route-store.ts +0 -319
  321. package/src/daemon/handlers/routing/routing-resolver.ts +0 -75
  322. package/src/daemon/handlers/sqlite-store.ts +0 -278
  323. package/src/daemon/handlers/triage/index.ts +0 -57
  324. package/src/daemon/handlers/triage/integration.ts +0 -213
  325. package/src/daemon/handlers/triage/pipeline.ts +0 -274
  326. package/src/daemon/handlers/triage/scorer.ts +0 -287
  327. package/src/daemon/handlers/triage/tagger/discord.ts +0 -187
  328. package/src/daemon/handlers/triage/tagger/imap.ts +0 -384
  329. package/src/daemon/handlers/triage/tagger/index.ts +0 -184
  330. package/src/daemon/handlers/triage/tagger/shared.ts +0 -70
  331. package/src/daemon/handlers/triage/tagger/slack.ts +0 -69
  332. package/src/daemon/handlers/triage/types.ts +0 -50
  333. package/src/daemon/lifecycle.ts +0 -41
  334. package/src/daemon/provision-wake-model.ts +0 -81
  335. package/src/daemon/safe-serve.ts +0 -61
  336. package/src/daemon/send/channels.ts +0 -200
  337. package/src/daemon/send/command.ts +0 -332
  338. package/src/daemon/send/composition.ts +0 -99
  339. package/src/daemon/send/failure-text.ts +0 -93
  340. package/src/daemon/send/inert-text.ts +0 -225
  341. package/src/daemon/send/stdin.ts +0 -24
  342. package/src/daemon/service-commands.ts +0 -329
  343. package/src/export/cost-utils.ts +0 -319
  344. package/src/input/selection.ts +0 -135
  345. package/src/plugins/loader.ts +0 -15
  346. package/src/renderer/bottom-bar.ts +0 -58
  347. package/src/renderer/conversation-tree.ts +0 -228
  348. package/src/runtime/agent-graph-composition.ts +0 -78
  349. package/src/runtime/channel-composition.ts +0 -46
  350. package/src/runtime/cluster-composition.ts +0 -124
  351. package/src/runtime/cluster-group-composition.ts +0 -284
  352. package/src/runtime/code-index-services.ts +0 -167
  353. package/src/runtime/daemon-handler-composition.ts +0 -76
  354. package/src/runtime/device-posture-composition.ts +0 -110
  355. package/src/runtime/durability-housekeeping.ts +0 -183
  356. package/src/runtime/durability-services.ts +0 -155
  357. package/src/runtime/embedded-service-factories.ts +0 -58
  358. package/src/runtime/feature-settings.ts +0 -154
  359. package/src/runtime/fleet-needs-input-push.ts +0 -70
  360. package/src/runtime/fleet-services.ts +0 -41
  361. package/src/runtime/idle-power-services.ts +0 -85
  362. package/src/runtime/mail-composition.ts +0 -65
  363. package/src/runtime/memory-fold.ts +0 -55
  364. package/src/runtime/operator-token-cleanup.ts +0 -109
  365. package/src/runtime/path-shadow.ts +0 -418
  366. package/src/runtime/perf/panel-health-monitor.ts +0 -18
  367. package/src/runtime/provider-optimizer-wiring.ts +0 -55
  368. package/src/runtime/reachability-notice.ts +0 -100
  369. package/src/runtime/remote-execution-composition.ts +0 -62
  370. package/src/runtime/session-liveness-marker.ts +0 -257
  371. package/src/runtime/session-pointer-surface.ts +0 -22
  372. package/src/runtime/session-storage-services.ts +0 -41
  373. package/src/runtime/surface-feature-flags.ts +0 -96
  374. package/src/runtime/terminal-output-guard.ts +0 -235
  375. package/src/runtime/trust/workspace-registration.ts +0 -192
  376. package/src/runtime/trust/workspace-trust.ts +0 -229
  377. package/src/runtime/ui/index.ts +0 -39
  378. package/src/runtime/ui/model-picker/data-provider.ts +0 -182
  379. package/src/runtime/ui/model-picker/health-enrichment.ts +0 -231
  380. package/src/runtime/ui/model-picker/index.ts +0 -59
  381. package/src/runtime/ui/model-picker/types.ts +0 -153
  382. package/src/runtime/ui/provider-health/data-provider.ts +0 -244
  383. package/src/runtime/ui/provider-health/fallback-visualizer.ts +0 -71
  384. package/src/runtime/ui/provider-health/index.ts +0 -46
  385. package/src/runtime/ui/provider-health/types.ts +0 -146
  386. package/src/runtime/ui-events.ts +0 -1
  387. package/src/runtime/ui-read-model-helpers.ts +0 -1
  388. package/src/runtime/ui-read-models-observability-maintenance.ts +0 -1
  389. package/src/runtime/ui-read-models-observability-options.ts +0 -1
  390. package/src/runtime/ui-read-models-observability-remote.ts +0 -1
  391. package/src/runtime/ui-read-models-observability-security.ts +0 -1
  392. package/src/runtime/ui-read-models-observability-system.ts +0 -1
  393. package/src/runtime/ui-read-models-observability.ts +0 -1
  394. package/src/runtime/ui-service-queries.ts +0 -1
  395. package/src/runtime/voice-setup-services.ts +0 -98
  396. package/src/runtime/workstream-attempts-validation.ts +0 -77
  397. package/src/runtime/workstream-draft-edits.ts +0 -95
  398. package/src/runtime/workstream-draft-store.ts +0 -274
  399. package/src/runtime/workstream-draft-types.ts +0 -42
  400. package/src/runtime/workstream-services.ts +0 -373
  401. package/src/tools/index.ts +0 -1
  402. package/src/types/grid.ts +0 -48
  403. package/src/types/sql-js.d.ts +0 -15
  404. package/src/work-plans/work-plan-store.ts +0 -702
@@ -1,77 +0,0 @@
1
- /**
2
- * Single SDK-contract import seam for the daemon handler layer.
3
- *
4
- * Every other module under `src/daemon/handlers/` imports SDK contract
5
- * identifiers from HERE and nowhere else. Concentrating the SDK imports in one
6
- * concrete-submodule module keeps the rest of the layer free of barrel cycles
7
- * and guarantees the host NEVER re-declares an SDK id, descriptor, or schema —
8
- * it only attaches handlers to the descriptors the SDK already registered.
9
- */
10
-
11
- // Catalog + invocation contract types (concrete control-plane subpath, not a project barrel).
12
- export type {
13
- GatewayMethodCatalog,
14
- GatewayMethodDescriptor,
15
- GatewayMethodInvocation,
16
- GatewayMethodInvocationContext,
17
- GatewayMethodHandler,
18
- } from '@pellux/goodvibes-sdk/platform/control-plane';
19
-
20
- // Channel domain types reused in handler signatures (read-only SDK interfaces; never re-declared).
21
- export type {
22
- ChannelIdentity,
23
- ChannelResolvedTarget,
24
- ChannelAccountRecord,
25
- } from '@pellux/goodvibes-sdk/platform/channels';
26
-
27
- /**
28
- * Per-peer authentication envelope passed to peer-scoped remote routes.
29
- *
30
- * The daemon-sdk ships `RemotePeerAuth` only as an UNEXPORTED local alias
31
- * inside `@pellux/goodvibes-daemon-sdk/remote-routes` (it is `unknown` there),
32
- * so it cannot be re-exported. We mirror that exact shape here for the host
33
- * implementation. This is an implementable runtime contract, not a method
34
- * descriptor or schema — declaring it does not violate the no-re-declaration
35
- * rule for catalog methods.
36
- */
37
- export type RemotePeerAuth = unknown;
38
-
39
- /**
40
- * Remote distributed-runtime route service the HOST must implement and supply
41
- * as `RuntimeServices.distributedRuntime`. The SDK facade injects the instance
42
- * into `DaemonRemoteRouteContext.distributedRuntime` so the published
43
- * `remote.peers.*` HTTP routes can dispatch to it.
44
- *
45
- * The daemon-sdk declares this interface locally in
46
- * `@pellux/goodvibes-daemon-sdk/remote-routes` but does NOT export it (the
47
- * module ends with `export {}`), so it cannot be imported. This declaration
48
- * mirrors the SDK's exact structural shape (17 methods, verbatim signatures)
49
- * so a host implementation is assignable to the SDK's context field. The SDK
50
- * ships no docker/ssh/cloud backend — the host owns the implementation.
51
- */
52
- export interface DistributedRuntimeRouteService {
53
- listPairRequests(): unknown;
54
- approvePairRequest(requestId: string, input: Record<string, unknown>): Promise<unknown | null>;
55
- rejectPairRequest(requestId: string, input: Record<string, unknown>): Promise<unknown | null>;
56
- listPeers(): unknown;
57
- rotatePeerToken(peerId: string, input: Record<string, unknown>): Promise<unknown | null>;
58
- revokePeerToken(peerId: string, input: Record<string, unknown>): Promise<unknown | null>;
59
- disconnectPeer(peerId: string, input: Record<string, unknown>): Promise<unknown | null>;
60
- listWork(): unknown;
61
- invokePeer(input: Record<string, unknown>): Promise<unknown>;
62
- cancelWork(workId: string, input: Record<string, unknown>): Promise<unknown | null>;
63
- getNodeHostContract(): unknown;
64
- requestPairing(input: Record<string, unknown>): Promise<unknown>;
65
- verifyPairRequest(
66
- requestId: string,
67
- challenge: string,
68
- input: Record<string, unknown>,
69
- ): Promise<unknown | null>;
70
- heartbeatPeer(auth: RemotePeerAuth, input: Record<string, unknown>): Promise<unknown>;
71
- claimWork(auth: RemotePeerAuth, input: Record<string, unknown>): Promise<unknown>;
72
- completeWork(
73
- auth: RemotePeerAuth,
74
- workId: string,
75
- input: Record<string, unknown>,
76
- ): Promise<unknown | null>;
77
- }
@@ -1,139 +0,0 @@
1
- import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
2
- import type { SecretsManager, SecretScope } from '../../config/secrets.ts';
3
- import {
4
- buildGoodVibesSecretKey,
5
- isSecretReferenceValue,
6
- } from '../../config/secret-config.ts';
7
-
8
- export interface DaemonCredentialStore {
9
- /**
10
- * Resolve a goodvibes://secrets/ reference OR a raw secret key to its
11
- * plaintext value (daemon-internal only).
12
- */
13
- resolveRef(ref: string): Promise<string | null>;
14
- /**
15
- * Resolve a config-key-derived secret
16
- * (e.g. 'surfaces.slack.botToken' → GOODVIBES_SURFACES_SLACK_BOT_TOKEN).
17
- */
18
- resolveConfigSecret(configKey: string): Promise<string | null>;
19
- /**
20
- * Store a daemon-owned credential. scope defaults 'daemon'.
21
- *
22
- * This is the DAEMON's own credential store: every caller is daemon code, and
23
- * every value written through it is one the daemon itself has to read back —
24
- * including its own generated draft encryption key (createAtRestCipher below,
25
- * which calls put() with no scope at all). Defaulting to 'user' filed those
26
- * in a surface-scoped tier, so the daemon's own material lived somewhere it
27
- * only found by accident, and a value written on one surface's behalf was
28
- * invisible to the next.
29
- */
30
- put(
31
- secretKey: string,
32
- value: string,
33
- opts?: { scope?: SecretScope; medium?: 'plaintext' | 'secure' },
34
- ): Promise<void>;
35
- has(secretKey: string): Promise<boolean>;
36
- }
37
-
38
- /** Extract the trailing secret-key segment from a goodvibes://secrets/ reference. */
39
- function secretKeyFromReference(ref: string): string {
40
- const normalized = ref.trim();
41
- const segments = normalized.split('/');
42
- const last = segments[segments.length - 1] ?? '';
43
- return decodeURIComponent(last);
44
- }
45
-
46
- export function createDaemonCredentialStore(secrets: SecretsManager): DaemonCredentialStore {
47
- return {
48
- async resolveRef(ref: string): Promise<string | null> {
49
- const key = isSecretReferenceValue(ref) ? secretKeyFromReference(ref) : ref;
50
- return secrets.get(key);
51
- },
52
- async resolveConfigSecret(configKey: string): Promise<string | null> {
53
- return secrets.get(buildGoodVibesSecretKey(configKey));
54
- },
55
- async put(
56
- secretKey: string,
57
- value: string,
58
- opts?: { scope?: SecretScope; medium?: 'plaintext' | 'secure' },
59
- ): Promise<void> {
60
- await secrets.set(secretKey, value, {
61
- scope: opts?.scope ?? 'daemon',
62
- medium: opts?.medium ?? 'secure',
63
- });
64
- },
65
- async has(secretKey: string): Promise<boolean> {
66
- const value = await secrets.get(secretKey);
67
- return value !== null && value.length > 0;
68
- },
69
- };
70
- }
71
-
72
- // ---------------------------------------------------------------------------
73
- // At-rest encryption helper for drafts (draft body must be encrypted at rest).
74
- // ---------------------------------------------------------------------------
75
-
76
- export interface AtRestCipher {
77
- encrypt(plaintext: string): Promise<string>;
78
- decrypt(ciphertext: string): Promise<string>;
79
- }
80
-
81
- const DEFAULT_DRAFT_KEY_NAME = 'GOODVIBES_DAEMON_DRAFT_AESKEY';
82
- const AES_KEY_BYTES = 32; // AES-256
83
- const GCM_IV_BYTES = 12;
84
- const GCM_TAG_BYTES = 16;
85
-
86
- async function loadOrCreateKey(
87
- store: DaemonCredentialStore,
88
- keyName: string,
89
- ): Promise<Buffer> {
90
- const existing = await store.resolveRef(keyName);
91
- if (existing && existing.length > 0) {
92
- const buf = Buffer.from(existing, 'base64');
93
- if (buf.length === AES_KEY_BYTES) return buf;
94
- }
95
- const generated = randomBytes(AES_KEY_BYTES);
96
- await store.put(keyName, generated.toString('base64'), { medium: 'secure' });
97
- return generated;
98
- }
99
-
100
- /**
101
- * Create an AES-256-GCM at-rest cipher backed by a daemon-owned key.
102
- * encrypt() returns base64(iv | tag | ciphertext). Uses node:crypto (Bun-compatible).
103
- * NEVER log resolved keys or plaintext.
104
- */
105
- export function createAtRestCipher(
106
- store: DaemonCredentialStore,
107
- keyName: string = DEFAULT_DRAFT_KEY_NAME,
108
- ): AtRestCipher {
109
- let keyPromise: Promise<Buffer> | null = null;
110
- const getKey = (): Promise<Buffer> => {
111
- if (!keyPromise) keyPromise = loadOrCreateKey(store, keyName);
112
- return keyPromise;
113
- };
114
-
115
- return {
116
- async encrypt(plaintext: string): Promise<string> {
117
- const key = await getKey();
118
- const iv = randomBytes(GCM_IV_BYTES);
119
- const cipher = createCipheriv('aes-256-gcm', key, iv);
120
- const encrypted = Buffer.concat([
121
- cipher.update(plaintext, 'utf-8'),
122
- cipher.final(),
123
- ]);
124
- const tag = cipher.getAuthTag();
125
- return Buffer.concat([iv, tag, encrypted]).toString('base64');
126
- },
127
- async decrypt(ciphertext: string): Promise<string> {
128
- const key = await getKey();
129
- const raw = Buffer.from(ciphertext, 'base64');
130
- const iv = raw.subarray(0, GCM_IV_BYTES);
131
- const tag = raw.subarray(GCM_IV_BYTES, GCM_IV_BYTES + GCM_TAG_BYTES);
132
- const data = raw.subarray(GCM_IV_BYTES + GCM_TAG_BYTES);
133
- const decipher = createDecipheriv('aes-256-gcm', key, iv);
134
- decipher.setAuthTag(tag);
135
- const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
136
- return decrypted.toString('utf-8');
137
- },
138
- };
139
- }
@@ -1,427 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import type { AtRestCipher } from '../credentials.ts';
3
- import { HandlerSqliteStore } from '../sqlite-store.ts';
4
-
5
- // ---------------------------------------------------------------------------
6
- // Draft Sync Backend store (rebased onto the daemon handler foundation).
7
- //
8
- // Mirrors the agent's local draft store (channels/drafts.json) server-side so
9
- // drafts are visible across surfaces. The agent's local file remains the source
10
- // of truth; this store is a sync mirror. Conflict resolution (most-recent
11
- // updatedAt wins) is the integrator's concern — this store performs a plain
12
- // upsert and records the supplied/derived updatedAt.
13
- //
14
- // SECURITY POSTURE:
15
- // - The plaintext message body is NEVER persisted. It is encrypted at rest
16
- // via the daemon at-rest cipher (AES-256-GCM) as `bodyEnc`.
17
- // - The webhook URL is encrypted at rest (`webhookEnc`) and is ALWAYS
18
- // redacted ('[redacted]') in every list/get response — the raw URL never
19
- // leaves the store.
20
- // - `messageDigest` (sha256First(body, 12)) is computed at save time and
21
- // stored alongside, so reads never need to decrypt the body. The required
22
- // `message` field of every emitted record carries the DIGEST, never the
23
- // plaintext body.
24
- // ---------------------------------------------------------------------------
25
-
26
- /** SHA-256 of `input`, truncated to the first `hexChars` hex characters. */
27
- export function sha256First(input: string, hexChars: number): string {
28
- const digest = createHash('sha256').update(input, 'utf-8').digest('hex');
29
- return digest.slice(0, Math.max(0, hexChars));
30
- }
31
-
32
- /** Redact a stored webhook for responses: present → '[redacted]', else undefined. */
33
- export function redactWebhook(value?: string | null): string | undefined {
34
- return value === undefined || value === null || value === '' ? undefined : '[redacted]';
35
- }
36
-
37
- export type DraftStatus = 'draft' | 'queued' | 'sent' | 'failed';
38
-
39
- /** Statuses callers are permitted to write via save(). */
40
- export const WRITABLE_DRAFT_STATUSES: readonly DraftStatus[] = ['draft', 'queued'];
41
-
42
- /** All known statuses (writable + terminal states set by the send pipeline). */
43
- export const ALL_DRAFT_STATUSES: readonly DraftStatus[] = [
44
- 'draft',
45
- 'queued',
46
- 'sent',
47
- 'failed',
48
- ];
49
-
50
- export const DRAFT_MESSAGE_DIGEST_HEX = 12;
51
- export const DEFAULT_DRAFT_VERSION = 1;
52
- export const DEFAULT_DRAFT_LIST_LIMIT = 50;
53
- export const MAX_DRAFT_LIST_LIMIT = 200;
54
-
55
- /**
56
- * Public draft record returned by list/get/save. Matches the SDK
57
- * CHANNEL_DRAFT_SCHEMA: the required `message` field carries the message
58
- * DIGEST (never the plaintext body). `webhook` is always '[redacted]' when set.
59
- * `messageDigest` is emitted ONLY by `get` (whose output schema permits
60
- * additional properties); strict list/save records omit it.
61
- */
62
- export interface DraftRecord {
63
- version: number;
64
- id: string;
65
- createdAt: string; // ISO-8601
66
- updatedAt: string; // ISO-8601
67
- status: DraftStatus;
68
- message: string; // sha256First(body, 12) digest — plaintext body is NEVER transmitted
69
- title?: string;
70
- channel?: string;
71
- route?: string;
72
- webhook?: string; // '[redacted]' when a webhook is stored, else omitted
73
- link?: string;
74
- tags?: string[];
75
- sentResponseId?: string;
76
- sendError?: string;
77
- }
78
-
79
- /** Normalized input accepted by upsert(). `message` is the FULL plaintext body. */
80
- export interface DraftSaveInput {
81
- id?: string;
82
- version?: number;
83
- title?: string;
84
- message: string;
85
- channel?: string;
86
- route?: string;
87
- webhook?: string;
88
- link?: string;
89
- tags?: string[];
90
- status?: DraftStatus;
91
- createdAt?: string;
92
- /**
93
- * Caller-supplied last-modified timestamp (ISO-8601). When provided it is
94
- * persisted verbatim, enabling the sync contract's 'most recent updatedAt
95
- * wins' conflict model; when omitted the store stamps updatedAt = now().
96
- */
97
- updatedAt?: string;
98
- }
99
-
100
- export interface DraftListQuery {
101
- status?: DraftStatus;
102
- limit?: number;
103
- }
104
-
105
- export interface DraftSaveResult {
106
- draft: DraftRecord;
107
- created: boolean;
108
- }
109
-
110
- export interface DraftSyncStoreOptions {
111
- workingDirectory: string;
112
- cipher: AtRestCipher;
113
- /** Override the sqlite filename (tests). Defaults to 'drafts.sqlite'. */
114
- fileName?: string;
115
- /** UUID generator injection point (tests). Defaults to crypto.randomUUID. */
116
- generateId?: () => string;
117
- /** Clock injection point (tests). Defaults to () => new Date().toISOString(). */
118
- now?: () => string;
119
- }
120
-
121
- const SCHEMA: string[] = [
122
- `CREATE TABLE IF NOT EXISTS drafts (
123
- id TEXT PRIMARY KEY,
124
- version INTEGER NOT NULL,
125
- createdAt TEXT NOT NULL,
126
- updatedAt TEXT NOT NULL,
127
- status TEXT NOT NULL,
128
- title TEXT,
129
- bodyEnc TEXT NOT NULL,
130
- messageDigest TEXT NOT NULL,
131
- channel TEXT,
132
- route TEXT,
133
- webhookEnc TEXT,
134
- link TEXT,
135
- tags TEXT,
136
- sentResponseId TEXT,
137
- sendError TEXT
138
- )`,
139
- 'CREATE INDEX IF NOT EXISTS idx_drafts_status ON drafts (status)',
140
- 'CREATE INDEX IF NOT EXISTS idx_drafts_updatedAt ON drafts (updatedAt)',
141
- ];
142
-
143
- interface DraftRow {
144
- id: string;
145
- version: number;
146
- createdAt: string;
147
- updatedAt: string;
148
- status: string;
149
- title: string | null;
150
- messageDigest: string;
151
- channel: string | null;
152
- route: string | null;
153
- webhookEnc: string | null;
154
- link: string | null;
155
- tags: string | null;
156
- sentResponseId: string | null;
157
- sendError: string | null;
158
- }
159
-
160
- function nullable(value: string | undefined): string | null {
161
- return value === undefined ? null : value;
162
- }
163
-
164
- function parseTags(raw: string | null): string[] | undefined {
165
- if (raw === null || raw === '') return undefined;
166
- try {
167
- const parsed = JSON.parse(raw) as unknown;
168
- if (Array.isArray(parsed)) {
169
- const tags = parsed.filter((t): t is string => typeof t === 'string');
170
- return tags.length > 0 ? tags : undefined;
171
- }
172
- } catch {
173
- // Corrupt tags column — treat as absent rather than throwing on read.
174
- }
175
- return undefined;
176
- }
177
-
178
- function normalizeStatus(status: DraftStatus | undefined): DraftStatus {
179
- return status ?? 'draft';
180
- }
181
-
182
- function normalizeVersion(version: number | undefined): number {
183
- if (version === undefined || !Number.isFinite(version)) return DEFAULT_DRAFT_VERSION;
184
- const floored = Math.floor(version);
185
- return floored < 1 ? DEFAULT_DRAFT_VERSION : floored;
186
- }
187
-
188
- /**
189
- * Server-side mirror of agent drafts. Wraps HandlerSqliteStore; all body and
190
- * webhook material is encrypted at rest via the injected AtRestCipher.
191
- */
192
- export class DraftSyncStore {
193
- private readonly store: HandlerSqliteStore;
194
- private readonly cipher: AtRestCipher;
195
- private readonly generateId: () => string;
196
- private readonly now: () => string;
197
- private initialized = false;
198
-
199
- constructor(options: DraftSyncStoreOptions) {
200
- this.store = new HandlerSqliteStore({
201
- workingDirectory: options.workingDirectory,
202
- fileName: options.fileName ?? 'drafts.sqlite',
203
- schema: SCHEMA,
204
- });
205
- this.cipher = options.cipher;
206
- this.generateId = options.generateId ?? (() => crypto.randomUUID());
207
- this.now = options.now ?? (() => new Date().toISOString());
208
- }
209
-
210
- get dbPath(): string {
211
- return this.store.dbPath;
212
- }
213
-
214
- async init(): Promise<void> {
215
- if (this.initialized) return;
216
- await this.store.init();
217
- this.initialized = true;
218
- }
219
-
220
- /** Persist the in-memory db to disk (atomic tmp+rename via the base store). */
221
- async save(): Promise<void> {
222
- await this.store.save();
223
- }
224
-
225
- close(): void {
226
- this.store.close();
227
- this.initialized = false;
228
- }
229
-
230
- /**
231
- * Create (no id) or update (id present) a draft. Encrypts the body and
232
- * webhook at rest, computes messageDigest, preserves createdAt on update, and
233
- * sets updatedAt to input.updatedAt when present (enabling 'most recent
234
- * updatedAt wins') or now() otherwise.
235
- *
236
- * FULL-REPLACE SEMANTICS (intentional, per the full-snapshot sync contract):
237
- * every caller-supplied field is a complete snapshot, NOT a partial patch.
238
- * The ON CONFLICT UPDATE SET clause assigns each column from the incoming row,
239
- * so an OMITTED optional field is CLEARED on update (webhookEnc/tags/title/
240
- * channel/route/link → NULL). The ONLY exceptions are sentResponseId/
241
- * sendError, which are owned by the send pipeline and survive a save().
242
- */
243
- async upsert(input: DraftSaveInput): Promise<DraftSaveResult> {
244
- const existing = input.id !== undefined ? this.getRow(input.id) : null;
245
- const created = existing === null;
246
- const id = existing?.id ?? input.id ?? this.generateId();
247
- const timestamp = this.now();
248
- const createdAt = existing?.createdAt ?? input.createdAt ?? timestamp;
249
- const updatedAt = input.updatedAt ?? timestamp;
250
- const status = normalizeStatus(input.status);
251
- const version = normalizeVersion(input.version);
252
-
253
- const bodyEnc = await this.cipher.encrypt(input.message);
254
- const messageDigest = sha256First(input.message, DRAFT_MESSAGE_DIGEST_HEX);
255
- const webhookEnc =
256
- input.webhook !== undefined && input.webhook !== ''
257
- ? await this.cipher.encrypt(input.webhook)
258
- : null;
259
- const tagsJson =
260
- input.tags !== undefined && input.tags.length > 0
261
- ? JSON.stringify(input.tags)
262
- : null;
263
-
264
- this.store.run(
265
- `INSERT INTO drafts (
266
- id, version, createdAt, updatedAt, status, title, bodyEnc, messageDigest,
267
- channel, route, webhookEnc, link, tags, sentResponseId, sendError
268
- ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
269
- ON CONFLICT(id) DO UPDATE SET
270
- version = excluded.version,
271
- updatedAt = excluded.updatedAt,
272
- status = excluded.status,
273
- title = excluded.title,
274
- bodyEnc = excluded.bodyEnc,
275
- messageDigest = excluded.messageDigest,
276
- channel = excluded.channel,
277
- route = excluded.route,
278
- webhookEnc = excluded.webhookEnc,
279
- link = excluded.link,
280
- tags = excluded.tags`,
281
- [
282
- id,
283
- version,
284
- createdAt,
285
- updatedAt,
286
- status,
287
- nullable(input.title),
288
- bodyEnc,
289
- messageDigest,
290
- nullable(input.channel),
291
- nullable(input.route),
292
- webhookEnc,
293
- nullable(input.link),
294
- tagsJson,
295
- // sentResponseId / sendError are owned by the send pipeline, not save().
296
- // On update they are preserved (omitted from UPDATE SET); on insert NULL.
297
- null,
298
- null,
299
- ],
300
- );
301
-
302
- const row = this.getRow(id);
303
- return { draft: toRecord(row ?? this.fallbackRow(id, version, createdAt, updatedAt, status, messageDigest, webhookEnc, input)), created };
304
- }
305
-
306
- /** List drafts, newest-updated first, optionally filtered by status. */
307
- list(query: DraftListQuery = {}): DraftRecord[] {
308
- const limit = clampLimit(query.limit);
309
- const rows =
310
- query.status !== undefined
311
- ? this.store.all<DraftRow>(
312
- `SELECT id, version, createdAt, updatedAt, status, title, messageDigest,
313
- channel, route, webhookEnc, link, tags, sentResponseId, sendError
314
- FROM drafts WHERE status = ? ORDER BY updatedAt DESC, id ASC LIMIT ?`,
315
- [query.status, limit],
316
- )
317
- : this.store.all<DraftRow>(
318
- `SELECT id, version, createdAt, updatedAt, status, title, messageDigest,
319
- channel, route, webhookEnc, link, tags, sentResponseId, sendError
320
- FROM drafts ORDER BY updatedAt DESC, id ASC LIMIT ?`,
321
- [limit],
322
- );
323
- return rows.map((row) => toRecord(row));
324
- }
325
-
326
- /** Total number of stored drafts (optionally status-filtered). */
327
- count(status?: DraftStatus): number {
328
- const row =
329
- status !== undefined
330
- ? this.store.get<{ n: number }>('SELECT COUNT(*) AS n FROM drafts WHERE status = ?', [status])
331
- : this.store.get<{ n: number }>('SELECT COUNT(*) AS n FROM drafts');
332
- return row?.n ?? 0;
333
- }
334
-
335
- /** Fetch a single draft as a redacted record, or null when absent. */
336
- get(id: string): DraftRecord | null {
337
- const row = this.getRow(id);
338
- return row === null ? null : toRecord(row);
339
- }
340
-
341
- /** Delete a draft. Returns true when a row was removed. */
342
- delete(id: string): boolean {
343
- const existed = this.getRow(id) !== null;
344
- if (existed) {
345
- this.store.run('DELETE FROM drafts WHERE id = ?', [id]);
346
- }
347
- return existed;
348
- }
349
-
350
- private getRow(id: string): DraftRow | null {
351
- return this.store.get<DraftRow>(
352
- `SELECT id, version, createdAt, updatedAt, status, title, messageDigest,
353
- channel, route, webhookEnc, link, tags, sentResponseId, sendError
354
- FROM drafts WHERE id = ?`,
355
- [id],
356
- );
357
- }
358
-
359
- // Defensive synthesis if a read-back ever returns null (should not happen for
360
- // the row we just wrote); keeps upsert total even under a degraded backend.
361
- private fallbackRow(
362
- id: string,
363
- version: number,
364
- createdAt: string,
365
- updatedAt: string,
366
- status: DraftStatus,
367
- messageDigest: string,
368
- webhookEnc: string | null,
369
- input: DraftSaveInput,
370
- ): DraftRow {
371
- return {
372
- id,
373
- version,
374
- createdAt,
375
- updatedAt,
376
- status,
377
- title: nullable(input.title),
378
- messageDigest,
379
- channel: nullable(input.channel),
380
- route: nullable(input.route),
381
- webhookEnc,
382
- link: nullable(input.link),
383
- tags:
384
- input.tags !== undefined && input.tags.length > 0
385
- ? JSON.stringify(input.tags)
386
- : null,
387
- sentResponseId: null,
388
- sendError: null,
389
- };
390
- }
391
- }
392
-
393
- function clampLimit(limit: number | undefined): number {
394
- if (limit === undefined || !Number.isFinite(limit)) return DEFAULT_DRAFT_LIST_LIMIT;
395
- const floored = Math.floor(limit);
396
- if (floored < 1) return 1;
397
- if (floored > MAX_DRAFT_LIST_LIMIT) return MAX_DRAFT_LIST_LIMIT;
398
- return floored;
399
- }
400
-
401
- /**
402
- * Map a stored row to a wire record. The required `message` field carries the
403
- * DIGEST (never plaintext); the webhook is redacted; tags are parsed.
404
- */
405
- export function toRecord(row: DraftRow): DraftRecord {
406
- const record: DraftRecord = {
407
- version: row.version,
408
- id: row.id,
409
- createdAt: row.createdAt,
410
- updatedAt: row.updatedAt,
411
- status: row.status as DraftStatus,
412
- message: row.messageDigest,
413
- };
414
- if (row.title !== null) record.title = row.title;
415
- if (row.channel !== null) record.channel = row.channel;
416
- if (row.route !== null) record.route = row.route;
417
- // Webhook is encrypted at rest; reads ALWAYS redact. Presence of webhookEnc
418
- // means a webhook exists → emit '[redacted]'; the raw URL never leaves here.
419
- const redacted = redactWebhook(row.webhookEnc);
420
- if (redacted !== undefined) record.webhook = redacted;
421
- if (row.link !== null) record.link = row.link;
422
- const tags = parseTags(row.tags);
423
- if (tags !== undefined) record.tags = tags;
424
- if (row.sentResponseId !== null) record.sentResponseId = row.sentResponseId;
425
- if (row.sendError !== null) record.sendError = row.sendError;
426
- return record;
427
- }
@@ -1,17 +0,0 @@
1
- /**
2
- * channels.drafts.* handler surface barrel.
3
- *
4
- * Re-exports only the concrete register entrypoint and the store/types other
5
- * code may need. It imports concrete submodules (no project index barrels), so
6
- * it introduces no import cycle.
7
- */
8
- export { registerDraftMethods } from './register.ts';
9
- export type { RegisterDraftsOptions } from './register.ts';
10
- export { DraftSyncStore, sha256First, redactWebhook } from './draft-store.ts';
11
- export type {
12
- DraftListQuery,
13
- DraftRecord,
14
- DraftSaveInput,
15
- DraftSaveResult,
16
- DraftStatus,
17
- } from './draft-store.ts';