@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,231 +0,0 @@
1
- /**
2
- * Health enrichment for model picker entries.
3
- *
4
- * Joins ModelDefinition records from the provider registry with health
5
- * telemetry from ProviderHealthDomainState to produce enriched
6
- * ModelPickerEntry objects ready for UI consumption.
7
- */
8
- import type { ModelDefinition } from '@pellux/goodvibes-sdk/platform/providers';
9
- import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
10
- import type { BenchmarkStore } from '@pellux/goodvibes-sdk/platform/providers';
11
- import type { ProviderHealthDomainState, ProviderHealthRecord } from '@/runtime/index.ts';
12
- import type { ModelDomainState } from '@/runtime/index.ts';
13
- import { detectFamily, tierToCategoryFilter } from '../../../input/model-picker.ts';
14
- import { getQualityTier, getQualityTierFromScore, compositeScore } from '@pellux/goodvibes-sdk/platform/providers';
15
- import type {
16
- ModelPickerEntry,
17
- ModelPickerGroup,
18
- ProviderHealthContext,
19
- CapabilityFlags,
20
- ProviderLatencyStats,
21
- } from '@/runtime/index.ts';
22
-
23
- /** Status sort priority (lower = shown first). */
24
- const STATUS_ORDER: Record<string, number> = {
25
- healthy: 0,
26
- unknown: 1,
27
- degraded: 2,
28
- rate_limited: 3,
29
- auth_error: 4,
30
- unavailable: 5,
31
- };
32
-
33
- /**
34
- * Derive ProviderHealthContext from a ProviderHealthRecord.
35
- * Returns a safe default when the record is absent (provider not yet observed).
36
- */
37
- function buildHealthContext(record: ProviderHealthRecord | undefined): ProviderHealthContext {
38
- if (!record) {
39
- return {
40
- status: 'unknown',
41
- isConfigured: false,
42
- };
43
- }
44
-
45
- let latency: ProviderLatencyStats | undefined;
46
- if (record.stats.totalCalls > 0) {
47
- latency = {
48
- avgMs: record.stats.avgLatencyMs,
49
- p95Ms: record.stats.maxLatencyMs,
50
- minMs: record.stats.minLatencyMs,
51
- };
52
- }
53
-
54
- return {
55
- status: record.status,
56
- latency,
57
- cacheHitRate: record.cacheMetrics?.hitRate,
58
- isConfigured: record.isConfigured,
59
- rateLimitResetAt: record.rateLimitResetAt,
60
- };
61
- }
62
-
63
- /**
64
- * Derive CapabilityFlags from a ModelDefinition.
65
- */
66
- function buildCapabilityFlags(model: ModelDefinition): CapabilityFlags {
67
- return {
68
- reasoning: model.capabilities?.reasoning ?? false,
69
- caching: false, // caching capability is provider-level; enriched separately if needed
70
- toolCalling: model.capabilities?.toolCalling ?? false,
71
- multimodal: model.capabilities?.multimodal ?? false,
72
- codeEditing: model.capabilities?.codeEditing ?? false,
73
- };
74
- }
75
-
76
- /**
77
- * Derive quality tier and benchmark score for a model.
78
- * Handles synthetic models (catalog-backed composite scores) and standard models.
79
- */
80
- function buildQualityInfo(
81
- model: ModelDefinition,
82
- benchmarkStore: Pick<BenchmarkStore, 'getBenchmarks'>,
83
- providerRegistry: Pick<ProviderRegistry, 'getSyntheticModelInfoFromCatalog'>,
84
- ): { qualityTier?: string; benchmarkScore?: number } {
85
- if (model.provider === 'synthetic') {
86
- const info = providerRegistry.getSyntheticModelInfoFromCatalog(model.id);
87
- if (info?.bestCompositeScore != null) {
88
- return {
89
- qualityTier: getQualityTierFromScore(info.bestCompositeScore),
90
- benchmarkScore: info.bestCompositeScore,
91
- };
92
- }
93
- return {};
94
- }
95
-
96
- const benchmarks = benchmarkStore.getBenchmarks(model.id) ?? benchmarkStore.getBenchmarks(model.displayName);
97
- if (!benchmarks) return {};
98
-
99
- return {
100
- qualityTier: getQualityTier(benchmarks.benchmarks),
101
- benchmarkScore: compositeScore(benchmarks.benchmarks) ?? undefined,
102
- };
103
- }
104
-
105
- /**
106
- * Build the set of model IDs in the current fallback chain.
107
- * Returns a Map from modelId to its position (0 = primary).
108
- */
109
- function buildFallbackPositionMap(modelState: ModelDomainState): Map<string, number> {
110
- const map = new Map<string, number>();
111
- // Position 0 is always the primary active model
112
- map.set(modelState.activeModelId, 0);
113
- for (let i = 0; i < modelState.fallbackChain.length; i++) {
114
- const entry = modelState.fallbackChain[i];
115
- if (!map.has(entry.modelId)) {
116
- map.set(entry.modelId, i + 1);
117
- }
118
- }
119
- return map;
120
- }
121
-
122
- /**
123
- * Enrich a flat list of ModelDefinitions with health and state data.
124
- *
125
- * @param models - All selectable models from the registry.
126
- * @param healthState - Current provider health domain state.
127
- * @param modelState - Current model domain state.
128
- * @param pinnedIds - Set of pinned/favorited model IDs.
129
- * @returns Sorted, enriched ModelPickerEntry array.
130
- */
131
- export function enrichModelEntries(
132
- models: readonly ModelDefinition[],
133
- healthState: ProviderHealthDomainState,
134
- modelState: ModelDomainState,
135
- pinnedIds: ReadonlySet<string>,
136
- benchmarkStore: Pick<BenchmarkStore, 'getBenchmarks'>,
137
- providerRegistry: Pick<ProviderRegistry, 'getSyntheticModelInfoFromCatalog' | 'getContextWindowForModel'>,
138
- ): ModelPickerEntry[] {
139
- const fallbackPositions = buildFallbackPositionMap(modelState);
140
-
141
- const entries: ModelPickerEntry[] = models.map((model) => {
142
- const record = healthState.providers.get(model.provider);
143
- const health = buildHealthContext(record);
144
- const { qualityTier, benchmarkScore } = buildQualityInfo(model, benchmarkStore, providerRegistry);
145
- const isProviderDegraded =
146
- health.status === 'degraded' || health.status === 'rate_limited';
147
- const isProviderUnavailable =
148
- health.status === 'unavailable' || health.status === 'auth_error';
149
-
150
- const fallbackPosition = fallbackPositions.get(model.id);
151
-
152
- // Resolve effective context window and determine display source label.
153
- const effectiveContextWindow = providerRegistry.getContextWindowForModel(model);
154
- // Determine source: custom/local providers carry provenance on ModelDefinition;
155
- // for catalog models, if getContextWindowForModel returned more than the
156
- // static contextWindow it came from OpenRouter, else it's the registry value.
157
- // NOTE: This is a best-effort heuristic. When the OpenRouter live value equals
158
- // the static registry value the badge shows 'registry' even though OpenRouter
159
- // was consulted. The contextWindow value itself is always accurate.
160
- let contextWindowSource: ModelPickerEntry['contextWindowSource'];
161
- if (model.contextWindowProvenance) {
162
- contextWindowSource = model.contextWindowProvenance;
163
- } else if (effectiveContextWindow !== model.contextWindow) {
164
- contextWindowSource = 'openrouter';
165
- } else {
166
- contextWindowSource = 'registry';
167
- }
168
-
169
- return {
170
- modelId: model.id,
171
- providerId: model.provider,
172
- displayName: model.displayName,
173
- family: detectFamily(model),
174
- pricingTier: tierToCategoryFilter(model.tier),
175
- qualityTier,
176
- benchmarkScore,
177
- capabilities: buildCapabilityFlags(model),
178
- health,
179
- contextWindow: effectiveContextWindow,
180
- contextWindowSource,
181
- isPinned: pinnedIds.has(model.id),
182
- isActive: model.id === modelState.activeModelId,
183
- isProviderDegraded,
184
- isProviderUnavailable,
185
- isInFallbackChain: fallbackPosition !== undefined,
186
- fallbackPosition,
187
- };
188
- });
189
-
190
- // Sort: healthy providers first, then degraded, then unavailable.
191
- // Within each status bucket: pinned first, then active, then by display name.
192
- entries.sort((a, b) => {
193
- const statusDiff =
194
- (STATUS_ORDER[a.health.status] ?? 1) - (STATUS_ORDER[b.health.status] ?? 1);
195
- if (statusDiff !== 0) return statusDiff;
196
-
197
- // Pinned before unpinned
198
- if (a.isPinned !== b.isPinned) return a.isPinned ? -1 : 1;
199
- // Active before inactive
200
- if (a.isActive !== b.isActive) return a.isActive ? -1 : 1;
201
-
202
- return a.displayName.localeCompare(b.displayName);
203
- });
204
-
205
- return entries;
206
- }
207
-
208
- /**
209
- * Group a sorted list of ModelPickerEntry records by their provider ID.
210
- * Preserves the sort order of entries within each group.
211
- *
212
- * @param entries - Pre-sorted enriched entries.
213
- * @returns Groups ordered by first appearance of each provider in entries.
214
- */
215
- export function groupEntriesByProvider(entries: readonly ModelPickerEntry[]): ModelPickerGroup[] {
216
- const groupMap = new Map<string, ModelPickerEntry[]>();
217
- const groupOrder: string[] = [];
218
-
219
- for (const entry of entries) {
220
- if (!groupMap.has(entry.providerId)) {
221
- groupMap.set(entry.providerId, []);
222
- groupOrder.push(entry.providerId);
223
- }
224
- groupMap.get(entry.providerId)!.push(entry);
225
- }
226
-
227
- return groupOrder.map((providerId) => ({
228
- label: providerId,
229
- entries: groupMap.get(providerId)!,
230
- }));
231
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * Model picker UI data surface barrel.
3
- *
4
- * Re-exports all types and the ModelPickerDataProvider class.
5
- * Also provides the createModelPickerData() factory for one-shot snapshots.
6
- */
7
- export type {
8
- CapabilityFlags,
9
- ProviderLatencyStats,
10
- ProviderHealthContext,
11
- ModelPickerEntry,
12
- ModelPickerGroup,
13
- ModelPickerData,
14
- } from '@/runtime/index.ts';
15
- // ProviderStatus re-exported from types for convenience
16
- export type { ProviderStatus } from '@/runtime/index.ts';
17
-
18
- export { ModelPickerDataProvider } from '@/runtime/index.ts';
19
- export type { ModelPickerDataProviderOptions } from '@/runtime/index.ts';
20
-
21
- import type { ModelDefinition } from '@pellux/goodvibes-sdk/platform/providers';
22
- import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
23
- import type { BenchmarkStore } from '@pellux/goodvibes-sdk/platform/providers';
24
- import type { ProviderHealthDomainState } from '@/runtime/index.ts';
25
- import type { ModelDomainState } from '@/runtime/index.ts';
26
- import type { ModelPickerData } from '@/runtime/index.ts';
27
- import { ModelPickerDataProvider } from '@/runtime/index.ts';
28
-
29
- /**
30
- * Produce a one-shot ModelPickerData snapshot without creating a long-lived provider.
31
- *
32
- * Use this when you need a single render pass and do not require change subscriptions.
33
- * For reactive/subscription-based UIs, prefer ModelPickerDataProvider.
34
- *
35
- * @param models - All selectable models from the registry.
36
- * @param healthState - Current provider health domain state.
37
- * @param modelState - Current model domain state.
38
- * @param pinnedIds - Set of pinned/favorited model IDs.
39
- * @returns Immutable ModelPickerData snapshot.
40
- */
41
- export function createModelPickerData(
42
- models: readonly ModelDefinition[],
43
- healthState: ProviderHealthDomainState,
44
- modelState: ModelDomainState,
45
- benchmarkStore: Pick<BenchmarkStore, 'getBenchmarks'>,
46
- providerRegistry: Pick<ProviderRegistry, 'getSyntheticModelInfoFromCatalog' | 'getContextWindowForModel'>,
47
- pinnedIds: ReadonlySet<string> = new Set(),
48
- ): ModelPickerData {
49
- // Delegate to the data provider for consistent derivation logic,
50
- // then dispose immediately since no subscriptions are needed.
51
- const dp = new ModelPickerDataProvider(models, healthState, modelState, {
52
- pinnedIds,
53
- benchmarkStore,
54
- providerRegistry,
55
- });
56
- const snapshot = dp.getSnapshot();
57
- dp.dispose();
58
- return snapshot;
59
- }
@@ -1,153 +0,0 @@
1
- /**
2
- * Model picker UI data types.
3
- *
4
- * Purely data-oriented — no rendering logic. These types are produced by
5
- * ModelPickerDataProvider and consumed by renderers/components.
6
- */
7
- import type { ProviderStatus } from '@/runtime/index.ts';
8
- import type { ModelFamily, CategoryFilter } from '../../../input/model-picker.ts';
9
-
10
- // Re-export for convenience
11
- export type { ProviderStatus };
12
-
13
- /**
14
- * Capability flags indicating what a model can do.
15
- */
16
- export interface CapabilityFlags {
17
- /** Model supports extended reasoning / chain-of-thought. */
18
- readonly reasoning: boolean;
19
- /** Model supports prompt caching. */
20
- readonly caching: boolean;
21
- /** Model supports tool/function calling. */
22
- readonly toolCalling: boolean;
23
- /** Model supports vision/multimodal inputs. */
24
- readonly multimodal: boolean;
25
- /** Model supports code editing operations. */
26
- readonly codeEditing: boolean;
27
- }
28
-
29
- /**
30
- * Latency statistics for a provider, derived from health telemetry.
31
- */
32
- export interface ProviderLatencyStats {
33
- /** Moving average latency in ms (last N calls). */
34
- readonly avgMs: number;
35
- /** Approximate 95th-percentile latency in ms (max of recent observations). */
36
- readonly p95Ms: number;
37
- /** Minimum observed latency in ms. */
38
- readonly minMs: number;
39
- }
40
-
41
- /**
42
- * Health context for a single provider, enriched into model picker entries.
43
- */
44
- export interface ProviderHealthContext {
45
- /** Current health status of the provider. */
46
- readonly status: ProviderStatus;
47
- /** Latency stats. Present only when the provider has call history. */
48
- readonly latency?: ProviderLatencyStats;
49
- /** Cache hit rate (0–1). Present only when cache metrics are available. */
50
- readonly cacheHitRate?: number;
51
- /** True when the provider is configured with a valid API key. */
52
- readonly isConfigured: boolean;
53
- /** Rate limit reset time (epoch ms), if the provider is currently rate-limited. */
54
- readonly rateLimitResetAt?: number;
55
- }
56
-
57
- /**
58
- * A single entry in the model picker list, enriched with health and benchmark data.
59
- */
60
- export interface ModelPickerEntry {
61
- // ── Identity ──────────────────────────────────────────────────────────────
62
- /** Unique model identifier. */
63
- readonly modelId: string;
64
- /** Provider identifier. */
65
- readonly providerId: string;
66
- /** Human-readable display name. */
67
- readonly displayName: string;
68
- /** Model family (GPT, Claude, Gemini, …). */
69
- readonly family: ModelFamily;
70
- /** Pricing tier bucket. */
71
- readonly pricingTier: CategoryFilter;
72
-
73
- // ── Quality ───────────────────────────────────────────────────────────────
74
- /** Quality tier badge (S/A/B/C), derived from benchmark composite score. */
75
- readonly qualityTier?: string;
76
- /** Benchmark composite score (0–1). */
77
- readonly benchmarkScore?: number;
78
-
79
- // ── Capabilities ──────────────────────────────────────────────────────────
80
- /** Capability flags for this model. */
81
- readonly capabilities: CapabilityFlags;
82
-
83
- // ── Health ────────────────────────────────────────────────────────────────
84
- /** Health context for the model's provider. */
85
- readonly health: ProviderHealthContext;
86
-
87
- // ── Context window ─────────────────────────────────────────────────────
88
- /**
89
- * Effective context window in tokens.
90
- * Use this for display and budgeting — it is the authoritative value.
91
- */
92
- readonly contextWindow: number;
93
- /**
94
- * How `contextWindow` was determined.
95
- * - `provider_api` — reported by the provider's /v1/models endpoint
96
- * - `configured_cap` — set explicitly in the provider config file
97
- * - `fallback` — default constant (no config or API source)
98
- * - `openrouter` — sourced from OpenRouter model data (built-in catalog models)
99
- * - `registry` — static value in the built-in model registry
100
- *
101
- * For built-in catalog models this badge is best-effort: when the OpenRouter
102
- * resolved value equals the static registry value the source shows 'registry'
103
- * even if OpenRouter was consulted at runtime.
104
- */
105
- readonly contextWindowSource:
106
- | 'provider_api'
107
- | 'configured_cap'
108
- | 'fallback'
109
- | 'openrouter'
110
- | 'registry';
111
-
112
- // ── Display state ─────────────────────────────────────────────────────────
113
- /** True if this model is in the user's favorites/pinned list. */
114
- readonly isPinned: boolean;
115
- /** True if this is the currently active model. */
116
- readonly isActive: boolean;
117
- /** True when the provider is currently in a degraded or critical state. */
118
- readonly isProviderDegraded: boolean;
119
- /** True when the provider is unavailable or in auth error. */
120
- readonly isProviderUnavailable: boolean;
121
- /** True if this model is part of the current fallback chain. */
122
- readonly isInFallbackChain: boolean;
123
- /** Position in the fallback chain (0 = primary, 1+ = fallback index). */
124
- readonly fallbackPosition?: number;
125
- }
126
-
127
- /**
128
- * A visual group in the model picker list.
129
- */
130
- export interface ModelPickerGroup {
131
- /** Group label (e.g. provider name, family name, quality tier). */
132
- readonly label: string;
133
- /** Entries belonging to this group, sorted by health then favorites. */
134
- readonly entries: readonly ModelPickerEntry[];
135
- }
136
-
137
- /**
138
- * Complete data snapshot produced by ModelPickerDataProvider.
139
- */
140
- export interface ModelPickerData {
141
- /** All enriched model entries (flat, pre-sorted). */
142
- readonly entries: readonly ModelPickerEntry[];
143
- /** Grouped view for rendering with section headers. */
144
- readonly groups: readonly ModelPickerGroup[];
145
- /** IDs of providers that are currently degraded. */
146
- readonly degradedProviderIds: readonly string[];
147
- /** IDs of providers that are currently unavailable. */
148
- readonly unavailableProviderIds: readonly string[];
149
- /** Model ID currently at the head of the fallback chain (active model). */
150
- readonly activeModelId: string;
151
- /** Epoch ms when this snapshot was produced. */
152
- readonly snapshotAt: number;
153
- }
@@ -1,244 +0,0 @@
1
- /**
2
- * ProviderHealthDataProvider — enriched provider health data surface.
3
- *
4
- * Combines ProviderHealthDomainState and ModelDomainState into a single,
5
- * sorted ProviderHealthData snapshot for UI consumption.
6
- *
7
- * This class is a data provider only — it contains no rendering logic.
8
- * Subscribe to change notifications and call getSnapshot() to render.
9
- */
10
- import type { ProviderHealthDomainState, ProviderHealthRecord } from '@/runtime/index.ts';
11
- import type { ModelDomainState } from '@/runtime/index.ts';
12
- import { buildFallbackChainData } from '@/runtime/index.ts';
13
- import type {
14
- ProviderHealthData,
15
- ProviderHealthEntry,
16
- HealthTimeline,
17
- HealthTimelinePoint,
18
- } from '@/runtime/index.ts';
19
-
20
- /**
21
- * Number of timeline points retained per provider.
22
- * Each point represents a snapshot at the time of a state update.
23
- */
24
- const TIMELINE_MAX_POINTS = 60;
25
-
26
- /** Status sort priority (lower = shown first in list). */
27
- const STATUS_ORDER: Record<string, number> = {
28
- degraded: 0,
29
- rate_limited: 1,
30
- auth_error: 2,
31
- unavailable: 3,
32
- unknown: 4,
33
- healthy: 5,
34
- };
35
-
36
- /**
37
- * Internal mutable timeline buffer for a provider.
38
- */
39
- interface TimelineBuffer {
40
- readonly points: HealthTimelinePoint[];
41
- }
42
-
43
- /**
44
- * ProviderHealthDataProvider produces enriched provider health data snapshots.
45
- *
46
- * Usage:
47
- * ```ts
48
- * const provider = new ProviderHealthDataProvider(healthState, modelState);
49
- * const unsub = provider.subscribe(() => {
50
- * const data = provider.getSnapshot();
51
- * // render data.entries, data.fallbackChain, etc.
52
- * });
53
- * // When state changes:
54
- * provider.updateHealthState(newHealthState);
55
- * provider.updateModelState(newModelState);
56
- * // Cleanup:
57
- * unsub();
58
- * provider.dispose();
59
- * ```
60
- */
61
- export class ProviderHealthDataProvider {
62
- private _healthState: ProviderHealthDomainState;
63
- private _modelState: ModelDomainState;
64
- private _snapshot: ProviderHealthData;
65
- private readonly _subscribers = new Set<() => void>();
66
- /** Per-provider timeline buffers, keyed by providerId. */
67
- private readonly _timelines = new Map<string, TimelineBuffer>();
68
-
69
- constructor(healthState: ProviderHealthDomainState, modelState: ModelDomainState) {
70
- this._healthState = healthState;
71
- this._modelState = modelState;
72
- this._seedTimelines(healthState);
73
- this._snapshot = this._buildSnapshot();
74
- }
75
-
76
- /**
77
- * Return the current enriched provider health data snapshot.
78
- * Updated synchronously when state changes via update methods.
79
- */
80
- public getSnapshot(): ProviderHealthData {
81
- return this._snapshot;
82
- }
83
-
84
- /**
85
- * Register a callback invoked whenever the snapshot changes.
86
- * @returns An unsubscribe function.
87
- */
88
- public subscribe(callback: () => void): () => void {
89
- this._subscribers.add(callback);
90
- return () => this._subscribers.delete(callback);
91
- }
92
-
93
- /**
94
- * Update provider health state and rebuild the snapshot.
95
- * Appends a new timeline point for each provider.
96
- */
97
- public updateHealthState(healthState: ProviderHealthDomainState): void {
98
- this._healthState = healthState;
99
- this._appendTimelinePoints(healthState);
100
- this._rebuild();
101
- }
102
-
103
- /**
104
- * Update model domain state (e.g. active model or fallback chain change).
105
- * Triggers a snapshot rebuild.
106
- */
107
- public updateModelState(modelState: ModelDomainState): void {
108
- this._modelState = modelState;
109
- this._rebuild();
110
- }
111
-
112
- /**
113
- * Release all subscriber references.
114
- * Does not clear internal state — getSnapshot() remains usable after disposal.
115
- */
116
- public dispose(): void {
117
- this._subscribers.clear();
118
- }
119
-
120
- // ── Private ────────────────────────────────────────────────────────────────
121
-
122
- /**
123
- * Seed timeline buffers from the initial health state.
124
- * Appends one point per known provider to initialize the timeline.
125
- */
126
- private _seedTimelines(healthState: ProviderHealthDomainState): void {
127
- for (const [id, record] of healthState.providers) {
128
- const buffer: TimelineBuffer = { points: [] };
129
- this._timelines.set(id, buffer);
130
- this._appendPoint(buffer, record);
131
- }
132
- }
133
-
134
- /**
135
- * Append a new timeline point for every provider in the updated state.
136
- * Creates a new buffer for providers seen for the first time.
137
- */
138
- private _appendTimelinePoints(healthState: ProviderHealthDomainState): void {
139
- for (const [id, record] of healthState.providers) {
140
- if (!this._timelines.has(id)) {
141
- this._timelines.set(id, { points: [] });
142
- }
143
- this._appendPoint(this._timelines.get(id)!, record);
144
- }
145
- }
146
-
147
- /** Append a single timeline point to a buffer, capping at TIMELINE_MAX_POINTS. */
148
- private _appendPoint(buffer: TimelineBuffer, record: ProviderHealthRecord): void {
149
- const total = record.stats.totalCalls;
150
- const successRate = total > 0 ? record.stats.successCalls / total : 1;
151
- const errorRate = total > 0 ? record.stats.errorCalls / total : 0;
152
-
153
- buffer.points.push({
154
- ts: Date.now(),
155
- successRate,
156
- avgLatencyMs: record.stats.avgLatencyMs,
157
- errorRate,
158
- });
159
-
160
- if (buffer.points.length > TIMELINE_MAX_POINTS) {
161
- // Array.shift() is O(n) but acceptable: TIMELINE_MAX_POINTS is capped at 60,
162
- // so the cost is negligible and the simplicity outweighs a ring-buffer implementation.
163
- buffer.points.shift();
164
- }
165
- }
166
-
167
- /** Build a HealthTimeline from a buffer for a given provider. */
168
- private _buildTimeline(providerId: string): HealthTimeline {
169
- const buffer = this._timelines.get(providerId);
170
- const points: readonly HealthTimelinePoint[] = buffer ? [...buffer.points] : [];
171
- return {
172
- providerId,
173
- points,
174
- length: points.length,
175
- };
176
- }
177
-
178
- private _rebuild(): void {
179
- this._snapshot = this._buildSnapshot();
180
- this._notify();
181
- }
182
-
183
- private _buildSnapshot(): ProviderHealthData {
184
- const entries: ProviderHealthEntry[] = [];
185
-
186
- for (const [id, record] of this._healthState.providers) {
187
- const total = record.stats.totalCalls;
188
- const successRate = total > 0 ? record.stats.successCalls / total : 1;
189
- const errorRate = total > 0 ? record.stats.errorCalls / total : 0;
190
-
191
- entries.push({
192
- providerId: id,
193
- displayName: record.displayName,
194
- status: record.status,
195
- isActive: record.isActive,
196
- isConfigured: record.isConfigured,
197
- successRate,
198
- errorRate,
199
- p95LatencyMs: record.stats.maxLatencyMs,
200
- avgLatencyMs: record.stats.avgLatencyMs,
201
- totalCalls: total,
202
- // cacheHitRate is populated only when cache-capability is wired to the provider record.
203
- // Until then this will always be undefined — intentionally unsupported at this stage.
204
- cacheHitRate: record.cacheMetrics?.hitRate,
205
- cacheReadTokens: record.cacheMetrics?.cacheReadTokens,
206
- cacheWriteTokens: record.cacheMetrics?.cacheWriteTokens,
207
- lastSuccessAt: record.stats.lastSuccessAt,
208
- lastErrorAt: record.stats.lastErrorAt,
209
- lastErrorMessage: record.stats.lastErrorMessage,
210
- lastCheckedAt: record.lastCheckedAt,
211
- rateLimitResetAt: record.rateLimitResetAt,
212
- timeline: this._buildTimeline(id),
213
- });
214
- }
215
-
216
- // Sort: degraded/unavailable first (needs attention), then healthy, then unknown
217
- entries.sort((a, b) => {
218
- const diff = (STATUS_ORDER[a.status] ?? 4) - (STATUS_ORDER[b.status] ?? 4);
219
- return diff !== 0 ? diff : a.displayName.localeCompare(b.displayName);
220
- });
221
-
222
- const fallbackChain = buildFallbackChainData(this._modelState, this._healthState);
223
-
224
- return {
225
- entries,
226
- compositeStatus: this._healthState.compositeStatus,
227
- degradedCount: this._healthState.degradedCount,
228
- unavailableCount: this._healthState.unavailableCount,
229
- fallbackChain,
230
- warnings: this._healthState.warnings,
231
- snapshotAt: Date.now(),
232
- };
233
- }
234
-
235
- private _notify(): void {
236
- for (const cb of this._subscribers) {
237
- try {
238
- cb();
239
- } catch {
240
- // Non-fatal: subscriber errors must not crash the provider
241
- }
242
- }
243
- }
244
- }