@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,300 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Minimal, dependency-free IMAPS client (RFC 3501 subset) over node:tls.
3
- //
4
- // Implements exactly what the inbound poller needs:
5
- // LOGIN, SELECT, UID SEARCH (SINCE / ALL), UID FETCH (ENVELOPE + body peek),
6
- // LOGOUT. No external npm dependency — uses node:tls (Bun-compatible).
7
- //
8
- // This is intentionally conservative: line-buffered tagged-command protocol,
9
- // per-command timeout, and a hard cap on response size to avoid unbounded
10
- // memory growth from a hostile/large mailbox.
11
- // ---------------------------------------------------------------------------
12
-
13
- import { connect as tlsConnect } from 'node:tls';
14
- import type { TLSSocket } from 'node:tls';
15
-
16
- export interface ImapConfig {
17
- host: string;
18
- port: number; // 993 for IMAPS
19
- user: string;
20
- password: string;
21
- /** Per-command timeout in ms. */
22
- timeoutMs?: number;
23
- /** Hard cap on bytes buffered per command (defense against huge fetches). */
24
- maxResponseBytes?: number;
25
- }
26
-
27
- export interface ImapEnvelope {
28
- uid: number;
29
- from: string; // raw From header value (digested by the adapter)
30
- subject: string;
31
- date: number; // Unix ms (0 when unparseable)
32
- seen: boolean;
33
- bodyPreview: string; // first text fragment, raw (sanitized by the adapter)
34
- }
35
-
36
- const DEFAULT_TIMEOUT_MS = 20_000;
37
- const DEFAULT_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
38
-
39
- export class ImapClient {
40
- private socket: TLSSocket | null = null;
41
- private tagCounter = 0;
42
- private buffer = '';
43
- private readonly cfg: Required<ImapConfig>;
44
-
45
- constructor(cfg: ImapConfig) {
46
- this.cfg = {
47
- timeoutMs: DEFAULT_TIMEOUT_MS,
48
- maxResponseBytes: DEFAULT_MAX_RESPONSE_BYTES,
49
- ...cfg,
50
- };
51
- }
52
-
53
- async connect(): Promise<void> {
54
- await new Promise<void>((resolve, reject) => {
55
- const socket = tlsConnect(
56
- { host: this.cfg.host, port: this.cfg.port, servername: this.cfg.host },
57
- () => {
58
- resolve();
59
- },
60
- );
61
- socket.setEncoding('utf-8');
62
- socket.once('error', reject);
63
- this.socket = socket;
64
- });
65
- // Consume the server greeting (untagged * OK ...).
66
- await this.readUntil((chunk) => /\r?\n/.test(chunk), 'greeting');
67
- }
68
-
69
- async login(): Promise<void> {
70
- const user = quote(this.cfg.user);
71
- const pass = quote(this.cfg.password);
72
- await this.command(`LOGIN ${user} ${pass}`);
73
- }
74
-
75
- /** SELECT a mailbox (default INBOX). */
76
- async select(mailbox = 'INBOX'): Promise<void> {
77
- await this.command(`SELECT ${quote(mailbox)}`);
78
- }
79
-
80
- /** UID SEARCH; returns matching UIDs. `since` filters by internal date. */
81
- async searchUids(since?: number): Promise<number[]> {
82
- const criteria = since ? `SINCE ${imapDate(since)}` : 'ALL';
83
- const lines = await this.command(`UID SEARCH ${criteria}`);
84
- const uids: number[] = [];
85
- for (const line of lines) {
86
- const match = /^\* SEARCH(.*)$/i.exec(line.trim());
87
- if (match) {
88
- for (const tok of match[1]!.trim().split(/\s+/)) {
89
- const n = Number.parseInt(tok, 10);
90
- if (Number.isFinite(n)) uids.push(n);
91
- }
92
- }
93
- }
94
- return uids;
95
- }
96
-
97
- /**
98
- * UID FETCH envelope + flags + a small text body peek for the given uids.
99
- * Returns one ImapEnvelope per uid that parsed successfully.
100
- */
101
- async fetchEnvelopes(uids: readonly number[]): Promise<ImapEnvelope[]> {
102
- if (uids.length === 0) return [];
103
- const set = uids.join(',');
104
- // BODY.PEEK[HEADER.FIELDS (...)] avoids setting \Seen; TEXT peek for preview.
105
- const lines = await this.command(
106
- `UID FETCH ${set} (UID FLAGS INTERNALDATE `
107
- + `BODY.PEEK[HEADER.FIELDS (FROM SUBJECT DATE)] `
108
- + `BODY.PEEK[TEXT]<0.600>)`,
109
- );
110
- return parseFetchResponse(lines.join('\r\n'));
111
- }
112
-
113
- async logout(): Promise<void> {
114
- if (!this.socket) return;
115
- try {
116
- await this.command('LOGOUT');
117
- } catch {
118
- // ignore logout failures
119
- }
120
- }
121
-
122
- close(): void {
123
- if (this.socket) {
124
- this.socket.destroy();
125
- this.socket = null;
126
- }
127
- }
128
-
129
- // -------------------------------------------------------------------------
130
- // Protocol plumbing
131
- // -------------------------------------------------------------------------
132
-
133
- private nextTag(): string {
134
- this.tagCounter += 1;
135
- return `A${this.tagCounter.toString().padStart(4, '0')}`;
136
- }
137
-
138
- private requireSocket(): TLSSocket {
139
- if (!this.socket) throw new Error('IMAP socket not connected');
140
- return this.socket;
141
- }
142
-
143
- /** Send a tagged command and collect all response lines up to the tagged OK. */
144
- private async command(text: string): Promise<string[]> {
145
- const tag = this.nextTag();
146
- const socket = this.requireSocket();
147
- socket.write(`${tag} ${text}\r\n`);
148
- const taggedOk = new RegExp(`^${tag} (OK|NO|BAD)\\b`, 'm');
149
- const raw = await this.readUntil((buf) => taggedOk.test(buf), text);
150
- const lines = raw.split(/\r?\n/);
151
- const statusLine = lines.find((l) => new RegExp(`^${tag} `).test(l)) ?? '';
152
- const status = /^A\d+ (OK|NO|BAD)/.exec(statusLine)?.[1];
153
- if (status !== 'OK') {
154
- throw new Error(`IMAP command failed: ${redactCommand(text)} -> ${statusLine.trim()}`);
155
- }
156
- return lines.filter((l) => l.startsWith('*'));
157
- }
158
-
159
- /** Read from the socket until `predicate(buffer)` is true or timeout. */
160
- private readUntil(predicate: (buf: string) => boolean, label: string): Promise<string> {
161
- const socket = this.requireSocket();
162
- return new Promise<string>((resolve, reject) => {
163
- const onData = (chunk: string): void => {
164
- this.buffer += chunk;
165
- if (this.buffer.length > this.cfg.maxResponseBytes) {
166
- cleanup();
167
- reject(new Error(`IMAP response exceeded ${this.cfg.maxResponseBytes} bytes (${label})`));
168
- return;
169
- }
170
- if (predicate(this.buffer)) {
171
- const out = this.buffer;
172
- this.buffer = '';
173
- cleanup();
174
- resolve(out);
175
- }
176
- };
177
- const onError = (err: Error): void => {
178
- cleanup();
179
- reject(err);
180
- };
181
- const onClose = (): void => {
182
- cleanup();
183
- reject(new Error(`IMAP connection closed during ${label}`));
184
- };
185
- const timer = setTimeout(() => {
186
- cleanup();
187
- reject(new Error(`IMAP timeout after ${this.cfg.timeoutMs}ms (${label})`));
188
- }, this.cfg.timeoutMs);
189
- const cleanup = (): void => {
190
- clearTimeout(timer);
191
- socket.off('data', onData);
192
- socket.off('error', onError);
193
- socket.off('close', onClose);
194
- };
195
- socket.on('data', onData);
196
- socket.on('error', onError);
197
- socket.on('close', onClose);
198
- });
199
- }
200
- }
201
-
202
- // ---------------------------------------------------------------------------
203
- // Pure parsers (exported for unit testing)
204
- // ---------------------------------------------------------------------------
205
-
206
- /** Quote an IMAP astring, escaping backslashes and double quotes. */
207
- function quote(value: string): string {
208
- return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
209
- }
210
-
211
- /** Never echo a LOGIN password in an error message. */
212
- function redactCommand(text: string): string {
213
- return /^LOGIN\b/i.test(text) ? 'LOGIN <redacted>' : text;
214
- }
215
-
216
- /** Format a Unix-ms timestamp as an IMAP date (dd-Mon-yyyy). */
217
- export function imapDate(ms: number): string {
218
- const d = new Date(ms);
219
- const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
220
- const day = String(d.getUTCDate()).padStart(2, '0');
221
- return `${day}-${months[d.getUTCMonth()]}-${d.getUTCFullYear()}`;
222
- }
223
-
224
- const HEADER_FROM_RE = /^From:\s*(.*)$/im;
225
- const HEADER_SUBJECT_RE = /^Subject:\s*(.*)$/im;
226
- const HEADER_DATE_RE = /^Date:\s*(.*)$/im;
227
-
228
- /**
229
- * Parse a UID FETCH response block into envelopes. Robust to interleaving and
230
- * partial fields; entries that lack a UID are skipped.
231
- */
232
- export function parseFetchResponse(raw: string): ImapEnvelope[] {
233
- const envelopes: ImapEnvelope[] = [];
234
- // Split on each "* <n> FETCH (" boundary.
235
- const blocks = raw.split(/\* \d+ FETCH \(/i).slice(1);
236
- for (const block of blocks) {
237
- const uidMatch = /UID (\d+)/i.exec(block);
238
- if (!uidMatch) continue;
239
- const uid = Number.parseInt(uidMatch[1]!, 10);
240
- const seen = /FLAGS \([^)]*\\Seen/i.test(block);
241
-
242
- // Header literal: BODY[HEADER.FIELDS (...)] {n}\r\n<header bytes>
243
- const headerText = extractLiteral(block, /BODY\[HEADER\.FIELDS[^\]]*\]/i);
244
- const bodyText = extractLiteral(block, /BODY\[TEXT\](?:<\d+(?:\.\d+)?>)?/i);
245
-
246
- const from = HEADER_FROM_RE.exec(headerText)?.[1]?.trim() ?? '';
247
- const subject = decodeHeader(HEADER_SUBJECT_RE.exec(headerText)?.[1]?.trim() ?? '');
248
- const dateRaw = HEADER_DATE_RE.exec(headerText)?.[1]?.trim() ?? '';
249
- const parsedDate = dateRaw ? Date.parse(dateRaw) : NaN;
250
-
251
- envelopes.push({
252
- uid,
253
- from,
254
- subject,
255
- date: Number.isFinite(parsedDate) ? parsedDate : 0,
256
- seen,
257
- bodyPreview: bodyText,
258
- });
259
- }
260
- return envelopes;
261
- }
262
-
263
- /**
264
- * Extract a literal `{n}\r\n<bytes>` that follows a section header matched by
265
- * `sectionRe`. Returns the literal content (n bytes) or ''.
266
- */
267
- function extractLiteral(block: string, sectionRe: RegExp): string {
268
- const sectionMatch = sectionRe.exec(block);
269
- if (!sectionMatch) return '';
270
- const after = block.slice(sectionMatch.index + sectionMatch[0].length);
271
- const litMatch = /^\s*\{(\d+)\}\r?\n/.exec(after);
272
- if (!litMatch) {
273
- // Quoted-string form: BODY[...] "value"
274
- const q = /^\s*"((?:[^"\\]|\\.)*)"/.exec(after);
275
- return q ? q[1]!.replace(/\\"/g, '"') : '';
276
- }
277
- const n = Number.parseInt(litMatch[1]!, 10);
278
- const start = litMatch.index + litMatch[0].length;
279
- return after.slice(start, start + n);
280
- }
281
-
282
- /** Decode RFC 2047 encoded-word subjects (UTF-8 B/Q) best-effort. */
283
- export function decodeHeader(value: string): string {
284
- return value.replace(/=\?([^?]+)\?([BbQq])\?([^?]*)\?=/g, (_m, _charset, enc, text) => {
285
- try {
286
- if (enc.toUpperCase() === 'B') {
287
- return Buffer.from(text, 'base64').toString('utf-8');
288
- }
289
- // Q-encoding: _ -> space, =XX -> byte
290
- const replaced = String(text)
291
- .replace(/_/g, ' ')
292
- .replace(/=([0-9A-Fa-f]{2})/g, (_s: string, hex: string) =>
293
- String.fromCharCode(Number.parseInt(hex, 16)),
294
- );
295
- return replaced;
296
- } catch {
297
- return text;
298
- }
299
- });
300
- }
@@ -1,24 +0,0 @@
1
- // Shared best-effort route resolution wrapper used by all adapters.
2
- // Swallows resolver failures (routing is an optional, concurrently-wired
3
- // surface) so a route lookup can never crash a provider poll.
4
-
5
- import type { AdapterContext, InboundChannelItem } from '../provider-adapter.ts';
6
- import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
7
-
8
- export async function resolveRouteId(
9
- ctx: AdapterContext,
10
- provider: string,
11
- fromDigest: string,
12
- kind: InboundChannelItem['kind'],
13
- ): Promise<string | undefined> {
14
- if (!ctx.resolveRouteId) return undefined;
15
- try {
16
- return (await ctx.resolveRouteId({ provider, fromDigest, kind })) ?? undefined;
17
- } catch (error) {
18
- ctx.logger.warn('route resolution failed', {
19
- provider,
20
- error: summarizeError(error),
21
- });
22
- return undefined;
23
- }
24
- }
@@ -1,263 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Slack inbound adapter.
3
- //
4
- // Transport note (contract fidelity): the daemon-handoff checklist names the
5
- // Slack Events API / RTM for DM polling. Those are push transports (HTTP event
6
- // callbacks / a persistent websocket) and cannot be driven by the inbound
7
- // poller, whose contract is a stateless, cadence-driven adapter.poll() that
8
- // MUST resolve each call (see provider-adapter.ts). A websocket/event-callback
9
- // would require an inbound HTTP endpoint or a long-lived socket the poller
10
- // neither owns nor supervises. We therefore satisfy the DM-polling goal over
11
- // the supported request/response surface (the Slack Web API), which the Events
12
- // API itself documents as the canonical pull-based equivalent for reading DM
13
- // history on an interval:
14
- // conversations.list (types=im) -> open DM channels (cursor-paginated)
15
- // conversations.history -> recent messages per DM
16
- // auth.test -> our own user id (for @-mention class.);
17
- // id is digested, never emitted raw.
18
- //
19
- // Credential: surfaces.slack.botToken. Missing => 'unavailable'.
20
- // Cadence: 30s (realtime tier).
21
- // ---------------------------------------------------------------------------
22
-
23
- import type {
24
- AdapterContext,
25
- InboundChannelItem,
26
- InboundProviderAdapter,
27
- ProviderPollOptions,
28
- ProviderPollResult,
29
- } from '../provider-adapter.ts';
30
- import { POLL_CADENCE_MS } from '../provider-adapter.ts';
31
- import { digestSender, toBodyPreview, toSubjectPreview } from '../mapping.ts';
32
- import { resolveRouteId } from './route-util.ts';
33
- import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
34
-
35
- const SLACK_API = 'https://slack.com/api';
36
- export const SLACK_PROVIDER_ID = 'slack';
37
- export const SLACK_CREDENTIAL_KEY = 'surfaces.slack.botToken';
38
-
39
- interface SlackConversationsListResponse {
40
- ok: boolean;
41
- error?: string;
42
- channels?: Array<{ id: string; user?: string }>;
43
- response_metadata?: { next_cursor?: string };
44
- }
45
-
46
- interface SlackAuthTestResponse {
47
- ok: boolean;
48
- error?: string;
49
- user_id?: string;
50
- }
51
-
52
- interface SlackReaction {
53
- name?: string;
54
- count?: number;
55
- users?: string[];
56
- }
57
-
58
- interface SlackMessage {
59
- type?: string;
60
- subtype?: string;
61
- user?: string;
62
- bot_id?: string;
63
- text?: string;
64
- ts?: string; // "1700000000.000200"
65
- thread_ts?: string;
66
- reactions?: SlackReaction[];
67
- }
68
-
69
- interface SlackHistoryResponse {
70
- ok: boolean;
71
- error?: string;
72
- messages?: SlackMessage[];
73
- has_more?: boolean;
74
- response_metadata?: { next_cursor?: string };
75
- }
76
-
77
- /**
78
- * Classify a Slack message into the InboundChannelItem `kind`.
79
- * - reaction: someone reacted to OUR OWN message — a genuine inbound reaction
80
- * event. Requires the message to be authored by us (its user id
81
- * is selfUserId) AND to carry a non-empty reactions[]. A message
82
- * authored by someone else that merely carries reactions[] is a
83
- * normal DM, not a reaction event.
84
- * - mention: the message @-mentions us (text contains `<@SELF>`)
85
- * - thread: a threaded reply (thread_ts present and not the root ts)
86
- * - dm: a plain direct message
87
- * Reaction outranks mention which outranks thread/dm (most-specific first).
88
- */
89
- function classifySlackKind(
90
- msg: SlackMessage,
91
- selfUserId: string | undefined,
92
- ): InboundChannelItem['kind'] {
93
- if (
94
- selfUserId &&
95
- msg.user === selfUserId &&
96
- Array.isArray(msg.reactions) &&
97
- msg.reactions.length > 0
98
- ) {
99
- return 'reaction';
100
- }
101
- if (selfUserId && typeof msg.text === 'string' && msg.text.includes(`<@${selfUserId}>`)) {
102
- return 'mention';
103
- }
104
- if (msg.thread_ts && msg.thread_ts !== msg.ts) return 'thread';
105
- return 'dm';
106
- }
107
-
108
- /** Slack ts ("1700000000.000200") -> Unix ms. */
109
- function tsToMs(ts: string | undefined): number {
110
- if (!ts) return 0;
111
- const seconds = Number.parseFloat(ts);
112
- return Number.isFinite(seconds) ? Math.round(seconds * 1000) : 0;
113
- }
114
-
115
- async function slackGet<T>(
116
- token: string,
117
- method: string,
118
- params: Record<string, string>,
119
- ): Promise<T> {
120
- const url = new URL(`${SLACK_API}/${method}`);
121
- for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
122
- const res = await fetch(url, {
123
- method: 'GET',
124
- headers: {
125
- Authorization: `Bearer ${token}`,
126
- Accept: 'application/json',
127
- },
128
- });
129
- if (!res.ok) {
130
- throw new Error(`Slack ${method} HTTP ${res.status}`);
131
- }
132
- return (await res.json()) as T;
133
- }
134
-
135
- export function createSlackAdapter(ctx: AdapterContext): InboundProviderAdapter {
136
- return {
137
- id: SLACK_PROVIDER_ID,
138
- pollIntervalMs: POLL_CADENCE_MS.realtime,
139
- async poll(opts: ProviderPollOptions): Promise<ProviderPollResult> {
140
- let token: string | null;
141
- try {
142
- token = await ctx.credentials.resolveConfigSecret(SLACK_CREDENTIAL_KEY);
143
- } catch (error) {
144
- return unavailable(`credential lookup failed: ${errMsg(error)}`);
145
- }
146
- if (!token || token.trim().length === 0) {
147
- return unavailable('missing surfaces.slack.botToken');
148
- }
149
- if (!token.startsWith('xoxb-') && !token.startsWith('xoxp-')) {
150
- return unavailable('surfaces.slack.botToken is not a valid Slack bot/user token');
151
- }
152
-
153
- // Resolve our own user id so we can distinguish @-mentions of us from
154
- // plain DMs. Best-effort: a failure here only means mentions are not
155
- // separately classified, it never fails the provider.
156
- let selfUserId: string | undefined;
157
- try {
158
- const auth = await slackGet<SlackAuthTestResponse>(token, 'auth.test', {});
159
- if (auth.ok && auth.user_id) selfUserId = auth.user_id;
160
- } catch (error) {
161
- ctx.logger.warn('slack auth.test failed; mentions will not be classified', {
162
- error: errMsg(error),
163
- });
164
- }
165
-
166
- try {
167
- // Page through ALL open DM channels. conversations.list returns at most
168
- // `limit` channels per page and a response_metadata.next_cursor when more
169
- // remain; without following the cursor, accounts with >100 open DMs would
170
- // silently lose every channel past the first page. MAX_LIST_PAGES bounds
171
- // the loop so a misbehaving cursor can never spin forever.
172
- const MAX_LIST_PAGES = 50;
173
- const channels: Array<{ id: string; user?: string }> = [];
174
- let cursor: string | undefined;
175
- for (let page = 0; page < MAX_LIST_PAGES; page += 1) {
176
- const list = await slackGet<SlackConversationsListResponse>(token, 'conversations.list', {
177
- types: 'im',
178
- limit: '100',
179
- ...(cursor ? { cursor } : {}),
180
- });
181
- if (!list.ok) {
182
- return unavailable(`conversations.list: ${list.error ?? 'unknown_error'}`);
183
- }
184
- if (list.channels) channels.push(...list.channels);
185
- const next = list.response_metadata?.next_cursor;
186
- if (!next || next.length === 0) break;
187
- cursor = next;
188
- }
189
- const oldest = opts.since ? (opts.since / 1000).toFixed(6) : undefined;
190
- // Page through conversations.history per channel: a single call returns at
191
- // most `limit` (<=50) messages and sets has_more + a next_cursor when a DM
192
- // accrued more new messages than fit in one page. Without following the
193
- // cursor a busy DM would silently drop everything past the first page.
194
- // MAX_HISTORY_PAGES bounds the loop so a misbehaving cursor cannot spin.
195
- const MAX_HISTORY_PAGES = 20;
196
- const items: InboundChannelItem[] = [];
197
- channelLoop: for (const channel of channels) {
198
- if (items.length >= opts.limit) break;
199
- let historyCursor: string | undefined;
200
- for (let page = 0; page < MAX_HISTORY_PAGES; page += 1) {
201
- const history = await slackGet<SlackHistoryResponse>(token, 'conversations.history', {
202
- channel: channel.id,
203
- limit: String(Math.min(opts.limit, 50)),
204
- ...(oldest ? { oldest } : {}),
205
- ...(historyCursor ? { cursor: historyCursor } : {}),
206
- });
207
- if (!history.ok) {
208
- // Skip this DM but keep going; do not fail the whole provider.
209
- ctx.logger.warn('slack conversations.history failed', {
210
- channel: channel.id,
211
- error: history.error,
212
- });
213
- continue channelLoop;
214
- }
215
- for (const msg of history.messages ?? []) {
216
- if (items.length >= opts.limit) break;
217
- if (msg.subtype === 'bot_message' || msg.bot_id) continue;
218
- const senderId = msg.user ?? channel.user ?? channel.id;
219
- const receivedAt = tsToMs(msg.ts);
220
- if (opts.since && receivedAt <= opts.since) continue;
221
- // Contract: fromDigest is SHA-256 (first 16 hex == 8 bytes) of the
222
- // provider user id. Slack user ids (U...) are unique within a
223
- // workspace, and the item.id / provider fields already namespace
224
- // by provider.
225
- const fromDigest = digestSender(senderId);
226
- const kind = classifySlackKind(msg, selfUserId);
227
- const item: InboundChannelItem = {
228
- id: `slack:${channel.id}:${msg.ts ?? String(receivedAt)}`,
229
- provider: SLACK_PROVIDER_ID,
230
- kind,
231
- fromDigest,
232
- subjectPreview: toSubjectPreview(`Direct message`),
233
- bodyPreview: toBodyPreview(msg.text),
234
- receivedAt,
235
- unread: true,
236
- };
237
- const routeId = await resolveRouteId(ctx, SLACK_PROVIDER_ID, fromDigest, kind);
238
- if (routeId) item.routeId = routeId;
239
- items.push(item);
240
- }
241
- // Advance to the next history page only while the channel reported
242
- // more messages AND we still have item budget left.
243
- const nextHistory = history.response_metadata?.next_cursor;
244
- if (!history.has_more || !nextHistory || nextHistory.length === 0) break;
245
- if (items.length >= opts.limit) break;
246
- historyCursor = nextHistory;
247
- }
248
- }
249
- return { items, state: items.length > 0 ? 'ready' : 'empty' };
250
- } catch (error) {
251
- return unavailable(errMsg(error));
252
- }
253
- },
254
- };
255
- }
256
-
257
- function unavailable(error: string): ProviderPollResult {
258
- return { items: [], state: 'unavailable', error };
259
- }
260
-
261
- function errMsg(error: unknown): string {
262
- return summarizeError(error);
263
- }