@pellux/goodvibes-tui 1.27.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/CHANGELOG.md +116 -2
  2. package/README.md +7 -9
  3. package/bin/goodvibes +2 -2
  4. package/bin/launcher-support.js +8 -4
  5. package/docs/README.md +11 -2
  6. package/docs/foundation-artifacts/operator-contract.json +6403 -2347
  7. package/package.json +14 -25
  8. package/scripts/postinstall.js +35 -35
  9. package/src/audio/spoken-turn-model-routing.ts +1 -1
  10. package/src/cli/bundle-command.ts +5 -5
  11. package/src/cli/completions/generate.ts +1 -1
  12. package/src/cli/doctor.ts +3 -3
  13. package/src/cli/entrypoint.ts +12 -7
  14. package/src/cli/help.ts +4 -5
  15. package/src/cli/hooks-command.ts +1 -1
  16. package/src/cli/index.ts +1 -4
  17. package/src/cli/launch-auto-update.ts +59 -7
  18. package/src/cli/management-commands.ts +6 -6
  19. package/src/cli/management-utils.ts +5 -5
  20. package/src/cli/management.ts +7 -7
  21. package/src/cli/network-posture.ts +1 -1
  22. package/src/cli/package-verification.ts +7 -6
  23. package/src/cli/plugin-bundles-command.ts +1 -1
  24. package/src/cli/plugin-command.ts +2 -2
  25. package/src/cli/service-command.ts +2 -2
  26. package/src/cli/service-posture.ts +4 -4
  27. package/src/cli/status.ts +9 -8
  28. package/src/cli/surface-command.ts +4 -4
  29. package/src/cli/tui-startup.ts +3 -3
  30. package/src/cli-flags.ts +3 -3
  31. package/src/config/goodvibes-home-audit.ts +0 -2
  32. package/src/config/secret-config.ts +37 -5
  33. package/src/config/tui-extension-settings.ts +1 -1
  34. package/src/core/approval-alert.ts +2 -2
  35. package/src/core/budget-breach-notifier.ts +3 -3
  36. package/src/core/conversation-compaction-render.ts +1 -1
  37. package/src/core/conversation-line-cache.ts +1 -1
  38. package/src/core/conversation-render-context.ts +1 -1
  39. package/src/core/conversation-rendering.ts +2 -2
  40. package/src/core/conversation.ts +10 -10
  41. package/src/core/long-task-notifier.ts +2 -2
  42. package/src/core/memory-diagnostics-gateway.ts +1 -1
  43. package/src/core/scriptable-statusline.ts +1 -1
  44. package/src/core/session-auto-titler.ts +1 -1
  45. package/src/core/session-resume-core.ts +2 -2
  46. package/src/core/stream-event-wiring.ts +24 -44
  47. package/src/core/terminal-notifier.ts +3 -3
  48. package/src/core/turn-cancellation.ts +1 -1
  49. package/src/core/turn-event-wiring.ts +5 -5
  50. package/src/input/command-registry.ts +6 -6
  51. package/src/input/commands/channel-pairing.ts +1 -1
  52. package/src/input/commands/cluster-runtime.ts +1 -1
  53. package/src/input/commands/codebase-runtime.ts +1 -1
  54. package/src/input/commands/devices-runtime.ts +1 -1
  55. package/src/input/commands/hosted-runtime.ts +330 -0
  56. package/src/input/commands/local-setup-review.ts +1 -1
  57. package/src/input/commands/local-setup-transfer.ts +2 -2
  58. package/src/input/commands/local-setup.ts +3 -3
  59. package/src/input/commands/managed-runtime.ts +2 -2
  60. package/src/input/commands/operator-rpc.ts +24 -110
  61. package/src/input/commands/payment-card-intake.ts +1 -1
  62. package/src/input/commands/permissions-provenance.ts +2 -2
  63. package/src/input/commands/plugin-runtime.ts +1 -1
  64. package/src/input/commands/provider.ts +1 -1
  65. package/src/input/commands/relay-runtime.ts +18 -9
  66. package/src/input/commands/rewind-runtime.ts +1 -1
  67. package/src/input/commands/runtime-services.ts +1 -1
  68. package/src/input/commands/services-runtime.ts +1 -1
  69. package/src/input/commands/session-workflow.ts +1 -1
  70. package/src/input/commands/settings-sync-runtime.ts +2 -2
  71. package/src/input/commands/share-runtime.ts +1 -1
  72. package/src/input/commands/tasks-runtime.ts +145 -55
  73. package/src/input/commands/update-runtime.ts +31 -28
  74. package/src/input/commands/work-plan-runtime.ts +2 -2
  75. package/src/input/commands/workstream-attempts.ts +1 -1
  76. package/src/input/commands/workstream-runtime.ts +2 -2
  77. package/src/input/commands.ts +2 -0
  78. package/src/input/feature-unit-layout.ts +1 -1
  79. package/src/input/feed-context-factory.ts +3 -3
  80. package/src/input/handler-content-actions.ts +2 -2
  81. package/src/input/handler-feed-routes.ts +1 -1
  82. package/src/input/handler-feed.ts +3 -3
  83. package/src/input/handler-modal-token-routes.ts +1 -1
  84. package/src/input/handler-onboarding-daemon-adopt.ts +3 -2
  85. package/src/input/handler-onboarding.ts +1 -1
  86. package/src/input/handler-types.ts +2 -2
  87. package/src/input/handler-ui-state.ts +1 -1
  88. package/src/input/handler.ts +4 -4
  89. package/src/input/input-history.ts +1 -1
  90. package/src/input/onboarding/onboarding-feature-units.ts +2 -2
  91. package/src/input/onboarding/onboarding-runtime-status.ts +15 -1
  92. package/src/input/onboarding/onboarding-wizard-apply.ts +17 -8
  93. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +5 -3
  94. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +1 -1
  95. package/src/input/onboarding/onboarding-wizard-types.ts +1 -1
  96. package/src/input/payments-config.ts +6 -6
  97. package/src/input/search.ts +1 -1
  98. package/src/input/session-picker-modal.ts +46 -0
  99. package/src/input/settings-modal-activation.ts +1 -1
  100. package/src/input/settings-modal-behavior.ts +4 -4
  101. package/src/input/settings-modal-commit.ts +11 -0
  102. package/src/input/settings-modal-data.ts +13 -80
  103. package/src/input/settings-modal-mutations.ts +33 -0
  104. package/src/input/settings-modal-secrets.ts +34 -0
  105. package/src/input/settings-modal-subscriptions.ts +1 -1
  106. package/src/input/settings-modal-types.ts +15 -1
  107. package/src/input/settings-modal.ts +28 -3
  108. package/src/input/worktree-setup-config.ts +1 -1
  109. package/src/main.ts +11 -9
  110. package/src/panels/agent-inspector-shared.ts +2 -2
  111. package/src/panels/base-panel.ts +2 -2
  112. package/src/panels/builtin/operations.ts +6 -8
  113. package/src/panels/builtin/session.ts +17 -0
  114. package/src/panels/builtin/shared.ts +5 -5
  115. package/src/panels/builtin-modals.ts +3 -3
  116. package/src/panels/confirm-state.ts +1 -1
  117. package/src/panels/cost-tracker-panel.ts +4 -4
  118. package/src/panels/diff-panel.ts +2 -2
  119. package/src/panels/diff-review-panel.ts +2 -2
  120. package/src/panels/expandable-list-panel.ts +2 -2
  121. package/src/panels/fleet-acts.ts +1 -1
  122. package/src/panels/fleet-observed-render.ts +1 -1
  123. package/src/panels/fleet-panel-format.ts +1 -1
  124. package/src/panels/fleet-panel.ts +1 -1
  125. package/src/panels/fleet-spawn.ts +1 -1
  126. package/src/panels/fleet-steer.ts +1 -1
  127. package/src/panels/fleet-transcript.ts +1 -1
  128. package/src/panels/git-panel.ts +1 -1
  129. package/src/panels/hosted-session-feed.ts +352 -0
  130. package/src/panels/hosted-session-panel.ts +221 -0
  131. package/src/panels/local-auth-panel.ts +3 -3
  132. package/src/panels/modals/devices-modal.ts +1 -1
  133. package/src/panels/modals/local-auth-modal.ts +1 -1
  134. package/src/panels/modals/pairing-modal.ts +1 -1
  135. package/src/panels/modals/services-modal.ts +1 -1
  136. package/src/panels/modals/settings-sync-modal.ts +1 -1
  137. package/src/panels/modals/subscription-modal.ts +1 -1
  138. package/src/panels/modals/work-plan-modal.ts +1 -1
  139. package/src/panels/notifications-panel.ts +1 -1
  140. package/src/panels/panel-confirm-overlay.ts +1 -1
  141. package/src/panels/plugins-panel.ts +2 -2
  142. package/src/panels/polish-core.ts +2 -2
  143. package/src/panels/polish-tables.ts +1 -1
  144. package/src/panels/polish.ts +2 -2
  145. package/src/panels/scrollable-list-panel.ts +3 -3
  146. package/src/panels/skills-panel.ts +3 -3
  147. package/src/panels/token-budget-panel.ts +5 -5
  148. package/src/panels/types.ts +1 -2
  149. package/src/permissions/prompt.ts +1 -1
  150. package/src/renderer/autocomplete-overlay.ts +1 -1
  151. package/src/renderer/block-actions-overlay.ts +1 -1
  152. package/src/renderer/bookmark-modal.ts +1 -1
  153. package/src/renderer/buffer.ts +1 -1
  154. package/src/renderer/code-block.ts +1 -1
  155. package/src/renderer/composer-fragments.ts +1 -1
  156. package/src/renderer/compositor.ts +2 -2
  157. package/src/renderer/config-modal.ts +1 -1
  158. package/src/renderer/context-inspector.ts +1 -1
  159. package/src/renderer/conversation-layout.ts +2 -2
  160. package/src/renderer/conversation-overlays.ts +1 -1
  161. package/src/renderer/conversation-surface.ts +2 -2
  162. package/src/renderer/diff-view.ts +1 -1
  163. package/src/renderer/diff.ts +1 -1
  164. package/src/renderer/file-picker-overlay.ts +1 -1
  165. package/src/renderer/fleet-tab-strip.ts +1 -1
  166. package/src/renderer/fullscreen-primitives.ts +2 -2
  167. package/src/renderer/fullscreen-workspace.ts +1 -1
  168. package/src/renderer/header-line.ts +1 -1
  169. package/src/renderer/help-overlay.ts +1 -1
  170. package/src/renderer/history-search-overlay.ts +2 -2
  171. package/src/renderer/layout.ts +5 -8
  172. package/src/renderer/markdown-inline.ts +1 -1
  173. package/src/renderer/markdown-table.ts +1 -1
  174. package/src/renderer/markdown.ts +1 -1
  175. package/src/renderer/mcp-workspace.ts +1 -1
  176. package/src/renderer/modal-factory.ts +2 -2
  177. package/src/renderer/model-workspace.ts +2 -2
  178. package/src/renderer/onboarding/onboarding-wizard.ts +1 -1
  179. package/src/renderer/overlay-box.ts +1 -1
  180. package/src/renderer/panel-composite.ts +1 -1
  181. package/src/renderer/panel-workspace-bar.ts +1 -1
  182. package/src/renderer/process-indicator.ts +1 -1
  183. package/src/renderer/profile-picker-modal.ts +1 -1
  184. package/src/renderer/search-overlay.ts +2 -2
  185. package/src/renderer/selection-modal-overlay.ts +1 -1
  186. package/src/renderer/session-picker-modal.ts +124 -3
  187. package/src/renderer/settings-modal-helpers.ts +2 -0
  188. package/src/renderer/settings-modal.ts +2 -2
  189. package/src/renderer/shell-surface.ts +2 -2
  190. package/src/renderer/system-message.ts +1 -1
  191. package/src/renderer/tab-strip.ts +1 -1
  192. package/src/renderer/theme-mode-config.ts +1 -1
  193. package/src/renderer/theme.ts +11 -12
  194. package/src/renderer/thinking.ts +1 -1
  195. package/src/renderer/tool-call.ts +2 -2
  196. package/src/renderer/tool-result-expanded-lines.ts +1 -1
  197. package/src/renderer/turn-injection.ts +7 -8
  198. package/src/renderer/ui-factory.ts +3 -3
  199. package/src/runtime/bootstrap-command-context.ts +4 -4
  200. package/src/runtime/bootstrap-command-parts.ts +5 -5
  201. package/src/runtime/bootstrap-core.ts +30 -30
  202. package/src/runtime/bootstrap-shell.ts +4 -4
  203. package/src/runtime/bootstrap.ts +128 -157
  204. package/src/runtime/client/approval-updates.ts +82 -0
  205. package/src/runtime/client/build-floors.ts +167 -0
  206. package/src/runtime/client/daemon-attach-handshake.ts +115 -0
  207. package/src/runtime/client/fleet-union.ts +147 -0
  208. package/src/runtime/client/host-service-status.ts +71 -0
  209. package/src/runtime/client/hosted-exit.ts +86 -0
  210. package/src/runtime/client/hosted-roster.ts +110 -0
  211. package/src/runtime/client/hosted-session-stream.ts +178 -0
  212. package/src/runtime/client/hosted-sessions.ts +187 -0
  213. package/src/runtime/client/operator-endpoint.ts +213 -0
  214. package/src/runtime/client/spine-adoption.ts +99 -0
  215. package/src/runtime/cloudflare-control-plane.ts +1 -1
  216. package/src/runtime/context-accounting-source.ts +1 -1
  217. package/src/runtime/context.ts +2 -2
  218. package/src/runtime/conversation-rewind-port.ts +42 -14
  219. package/src/runtime/daemon-attach-notices.ts +55 -19
  220. package/src/runtime/diagnostics/panels/ops.ts +1 -1
  221. package/src/runtime/disposal-wiring.ts +4 -24
  222. package/src/runtime/index.ts +119 -105
  223. package/src/runtime/install-self-check-startup.ts +2 -2
  224. package/src/runtime/interaction-seams.ts +1 -1
  225. package/src/runtime/knowledge-services.ts +8 -2
  226. package/src/runtime/legacy-daemon-migration.ts +4 -3
  227. package/src/runtime/legacy-daemon-reconcile.ts +3 -2
  228. package/src/runtime/memory-spine-transport.ts +37 -267
  229. package/src/runtime/notification-dispatch.ts +2 -2
  230. package/src/runtime/onboarding/apply.ts +2 -2
  231. package/src/runtime/onboarding/derivation.ts +2 -2
  232. package/src/runtime/onboarding/markers.ts +2 -2
  233. package/src/runtime/onboarding/progress.ts +2 -2
  234. package/src/runtime/onboarding/snapshot.ts +1 -1
  235. package/src/runtime/onboarding/state.ts +2 -2
  236. package/src/runtime/onboarding/types.ts +2 -2
  237. package/src/runtime/orchestrator-core-services.ts +1 -1
  238. package/src/runtime/path-shadow-startup.ts +28 -221
  239. package/src/runtime/power-keepawake-remote.ts +3 -2
  240. package/src/runtime/process-lifecycle.ts +2 -2
  241. package/src/runtime/provider-fallback.ts +10 -66
  242. package/src/runtime/recovery-autosave.ts +1 -1
  243. package/src/runtime/recovery-offer-wiring.ts +39 -23
  244. package/src/runtime/recovery-prompt.ts +31 -17
  245. package/src/runtime/relay-reachability-bridge.ts +53 -42
  246. package/src/runtime/release-artifacts.ts +2 -1
  247. package/src/runtime/runtime-services-types.ts +82 -35
  248. package/src/runtime/services.ts +305 -300
  249. package/src/runtime/session-resume-liveness-confirm.ts +1 -1
  250. package/src/runtime/ui-services.ts +7 -6
  251. package/src/runtime/update-check.ts +23 -43
  252. package/src/runtime/workspace-checkpointing.ts +1 -1
  253. package/src/runtime/wrfc-persistence.ts +2 -2
  254. package/src/shell/ui-openers.ts +30 -7
  255. package/src/utils/clipboard.ts +1 -1
  256. package/src/verification/verification-ledger.ts +10 -8
  257. package/src/version.ts +1 -1
  258. package/bin/goodvibes-daemon +0 -90
  259. package/src/cli/config-overrides.ts +0 -267
  260. package/src/cli/endpoints.ts +0 -109
  261. package/src/cli/parser.ts +0 -395
  262. package/src/cli/redaction.ts +0 -170
  263. package/src/cli/types.ts +0 -84
  264. package/src/cluster/commands.ts +0 -382
  265. package/src/cluster/remote-daemon-target.ts +0 -225
  266. package/src/cluster/render.ts +0 -268
  267. package/src/config/atomic-write.ts +0 -70
  268. package/src/config/credential-availability.ts +0 -122
  269. package/src/config/goodvibes-home.ts +0 -149
  270. package/src/config/index.ts +0 -88
  271. package/src/config/provider-model.ts +0 -23
  272. package/src/config/read-versioned.ts +0 -228
  273. package/src/core/alert-gating.ts +0 -67
  274. package/src/core/consolidation-receipt.ts +0 -41
  275. package/src/core/focus-tracker.ts +0 -41
  276. package/src/core/history.ts +0 -45
  277. package/src/core/memory-status.ts +0 -118
  278. package/src/core/orchestrator.ts +0 -7
  279. package/src/core/pairing-devices.ts +0 -49
  280. package/src/core/pairing-handoff.ts +0 -101
  281. package/src/core/pairing-offers.ts +0 -83
  282. package/src/core/pairing-origin.ts +0 -78
  283. package/src/core/rewind-turn-anchors.ts +0 -311
  284. package/src/core/session-recovery.ts +0 -210
  285. package/src/core/stable-host.ts +0 -171
  286. package/src/core/transcript-journal.ts +0 -503
  287. package/src/daemon/cli.ts +0 -693
  288. package/src/daemon/fatal-boot-report.ts +0 -111
  289. package/src/daemon/handlers/context.ts +0 -29
  290. package/src/daemon/handlers/contracts.ts +0 -77
  291. package/src/daemon/handlers/credentials.ts +0 -139
  292. package/src/daemon/handlers/drafts/draft-store.ts +0 -427
  293. package/src/daemon/handlers/drafts/index.ts +0 -17
  294. package/src/daemon/handlers/drafts/register.ts +0 -331
  295. package/src/daemon/handlers/errors.ts +0 -18
  296. package/src/daemon/handlers/inbox/cursor-store.ts +0 -457
  297. package/src/daemon/handlers/inbox/index.ts +0 -284
  298. package/src/daemon/handlers/inbox/mapping.ts +0 -192
  299. package/src/daemon/handlers/inbox/poller.ts +0 -212
  300. package/src/daemon/handlers/inbox/provider-adapter.ts +0 -156
  301. package/src/daemon/handlers/inbox/providers/discord.ts +0 -252
  302. package/src/daemon/handlers/inbox/providers/email.ts +0 -152
  303. package/src/daemon/handlers/inbox/providers/imap-client.ts +0 -300
  304. package/src/daemon/handlers/inbox/providers/route-util.ts +0 -24
  305. package/src/daemon/handlers/inbox/providers/slack.ts +0 -263
  306. package/src/daemon/handlers/index.ts +0 -117
  307. package/src/daemon/handlers/register.ts +0 -168
  308. package/src/daemon/handlers/remote/backends/cloud-terminal.ts +0 -142
  309. package/src/daemon/handlers/remote/backends/docker.ts +0 -79
  310. package/src/daemon/handlers/remote/backends/index.ts +0 -40
  311. package/src/daemon/handlers/remote/backends/local-process.ts +0 -113
  312. package/src/daemon/handlers/remote/backends/process-runner.ts +0 -127
  313. package/src/daemon/handlers/remote/backends/ssh.ts +0 -125
  314. package/src/daemon/handlers/remote/backends/types.ts +0 -97
  315. package/src/daemon/handlers/remote/dispatcher.ts +0 -181
  316. package/src/daemon/handlers/remote/index.ts +0 -119
  317. package/src/daemon/handlers/remote/peer-registry.ts +0 -357
  318. package/src/daemon/handlers/remote/service.ts +0 -191
  319. package/src/daemon/handlers/routing/inbox-bridge.ts +0 -71
  320. package/src/daemon/handlers/routing/index.ts +0 -261
  321. package/src/daemon/handlers/routing/route-store.ts +0 -319
  322. package/src/daemon/handlers/routing/routing-resolver.ts +0 -75
  323. package/src/daemon/handlers/sqlite-store.ts +0 -278
  324. package/src/daemon/handlers/triage/index.ts +0 -57
  325. package/src/daemon/handlers/triage/integration.ts +0 -213
  326. package/src/daemon/handlers/triage/pipeline.ts +0 -274
  327. package/src/daemon/handlers/triage/scorer.ts +0 -287
  328. package/src/daemon/handlers/triage/tagger/discord.ts +0 -187
  329. package/src/daemon/handlers/triage/tagger/imap.ts +0 -384
  330. package/src/daemon/handlers/triage/tagger/index.ts +0 -184
  331. package/src/daemon/handlers/triage/tagger/shared.ts +0 -70
  332. package/src/daemon/handlers/triage/tagger/slack.ts +0 -69
  333. package/src/daemon/handlers/triage/types.ts +0 -50
  334. package/src/daemon/lifecycle.ts +0 -41
  335. package/src/daemon/provision-wake-model.ts +0 -81
  336. package/src/daemon/safe-serve.ts +0 -61
  337. package/src/daemon/send/channels.ts +0 -200
  338. package/src/daemon/send/command.ts +0 -332
  339. package/src/daemon/send/composition.ts +0 -99
  340. package/src/daemon/send/failure-text.ts +0 -93
  341. package/src/daemon/send/inert-text.ts +0 -225
  342. package/src/daemon/send/stdin.ts +0 -24
  343. package/src/daemon/service-commands.ts +0 -329
  344. package/src/export/cost-utils.ts +0 -319
  345. package/src/input/selection.ts +0 -135
  346. package/src/plugins/loader.ts +0 -15
  347. package/src/renderer/bottom-bar.ts +0 -58
  348. package/src/renderer/conversation-tree.ts +0 -228
  349. package/src/runtime/agent-graph-composition.ts +0 -78
  350. package/src/runtime/channel-composition.ts +0 -46
  351. package/src/runtime/cluster-composition.ts +0 -124
  352. package/src/runtime/cluster-group-composition.ts +0 -284
  353. package/src/runtime/code-index-services.ts +0 -167
  354. package/src/runtime/daemon-handler-composition.ts +0 -76
  355. package/src/runtime/device-posture-composition.ts +0 -110
  356. package/src/runtime/durability-housekeeping.ts +0 -183
  357. package/src/runtime/durability-services.ts +0 -155
  358. package/src/runtime/embedded-service-factories.ts +0 -74
  359. package/src/runtime/feature-settings.ts +0 -154
  360. package/src/runtime/fleet-needs-input-push.ts +0 -70
  361. package/src/runtime/fleet-services.ts +0 -41
  362. package/src/runtime/idle-power-services.ts +0 -85
  363. package/src/runtime/mail-composition.ts +0 -65
  364. package/src/runtime/memory-fold.ts +0 -55
  365. package/src/runtime/operator-token-cleanup.ts +0 -109
  366. package/src/runtime/path-shadow.ts +0 -418
  367. package/src/runtime/perf/panel-health-monitor.ts +0 -18
  368. package/src/runtime/provider-optimizer-wiring.ts +0 -55
  369. package/src/runtime/reachability-notice.ts +0 -100
  370. package/src/runtime/remote-execution-composition.ts +0 -62
  371. package/src/runtime/session-liveness-marker.ts +0 -257
  372. package/src/runtime/session-pointer-surface.ts +0 -22
  373. package/src/runtime/session-storage-services.ts +0 -41
  374. package/src/runtime/surface-feature-flags.ts +0 -96
  375. package/src/runtime/terminal-output-guard.ts +0 -235
  376. package/src/runtime/trust/workspace-registration.ts +0 -192
  377. package/src/runtime/trust/workspace-trust.ts +0 -229
  378. package/src/runtime/ui/index.ts +0 -39
  379. package/src/runtime/ui/model-picker/data-provider.ts +0 -182
  380. package/src/runtime/ui/model-picker/health-enrichment.ts +0 -231
  381. package/src/runtime/ui/model-picker/index.ts +0 -59
  382. package/src/runtime/ui/model-picker/types.ts +0 -153
  383. package/src/runtime/ui/provider-health/data-provider.ts +0 -244
  384. package/src/runtime/ui/provider-health/fallback-visualizer.ts +0 -71
  385. package/src/runtime/ui/provider-health/index.ts +0 -46
  386. package/src/runtime/ui/provider-health/types.ts +0 -146
  387. package/src/runtime/ui-events.ts +0 -1
  388. package/src/runtime/ui-read-model-helpers.ts +0 -1
  389. package/src/runtime/ui-read-models-observability-maintenance.ts +0 -1
  390. package/src/runtime/ui-read-models-observability-options.ts +0 -1
  391. package/src/runtime/ui-read-models-observability-remote.ts +0 -1
  392. package/src/runtime/ui-read-models-observability-security.ts +0 -1
  393. package/src/runtime/ui-read-models-observability-system.ts +0 -1
  394. package/src/runtime/ui-read-models-observability.ts +0 -1
  395. package/src/runtime/ui-service-queries.ts +0 -1
  396. package/src/runtime/voice-setup-services.ts +0 -98
  397. package/src/runtime/workstream-attempts-validation.ts +0 -77
  398. package/src/runtime/workstream-draft-edits.ts +0 -95
  399. package/src/runtime/workstream-draft-store.ts +0 -274
  400. package/src/runtime/workstream-draft-types.ts +0 -42
  401. package/src/runtime/workstream-services.ts +0 -373
  402. package/src/tools/index.ts +0 -1
  403. package/src/types/grid.ts +0 -48
  404. package/src/types/sql-js.d.ts +0 -15
  405. package/src/work-plans/work-plan-store.ts +0 -702
@@ -1,71 +0,0 @@
1
- /**
2
- * Fallback chain visualization data builder.
3
- *
4
- * Converts the raw fallback chain from ModelDomainState and current
5
- * provider health records into a structured FallbackChainData snapshot
6
- * suitable for UI rendering.
7
- */
8
- import type { ModelDomainState } from '@/runtime/index.ts';
9
- import type { ProviderHealthDomainState } from '@/runtime/index.ts';
10
- import type { FallbackChainData, FallbackChainNode } from '@/runtime/index.ts';
11
-
12
- /**
13
- * Build a FallbackChainData snapshot from the current model and health state.
14
- *
15
- * The primary model is always node 0. Each entry in ModelDomainState.fallbackChain
16
- * becomes a subsequent node. Health status is enriched from the provider health domain.
17
- *
18
- * @param modelState - Current model domain state.
19
- * @param healthState - Current provider health domain state.
20
- * @returns Immutable FallbackChainData for visualization.
21
- */
22
- export function buildFallbackChainData(
23
- modelState: ModelDomainState,
24
- healthState: ProviderHealthDomainState,
25
- ): FallbackChainData {
26
- const nodes: FallbackChainNode[] = [];
27
-
28
- // Node 0 — primary (active model)
29
- const primaryRecord = healthState.providers.get(modelState.activeProviderId);
30
- nodes.push({
31
- registryKey: `${modelState.activeProviderId}:${modelState.activeModelId}`,
32
- providerId: modelState.activeProviderId,
33
- modelId: modelState.activeModelId,
34
- displayName: modelState.displayName,
35
- position: 0,
36
- isCurrent: modelState.activeFallbackIndex === -1,
37
- providerStatus: primaryRecord?.status ?? 'unknown',
38
- reason: undefined,
39
- });
40
-
41
- // Nodes 1..N — fallback chain entries
42
- for (let i = 0; i < modelState.fallbackChain.length; i++) {
43
- const entry = modelState.fallbackChain[i];
44
- const record = healthState.providers.get(entry.providerId);
45
- nodes.push({
46
- registryKey: `${entry.providerId}:${entry.modelId}`,
47
- providerId: entry.providerId,
48
- modelId: entry.modelId,
49
- displayName: entry.displayName,
50
- position: i + 1,
51
- isCurrent: modelState.activeFallbackIndex === i,
52
- providerStatus: record?.status ?? 'unknown',
53
- reason: entry.reason,
54
- });
55
- }
56
-
57
- const hasUnhealthyNode = nodes.some(
58
- (n) =>
59
- n.providerStatus === 'degraded' ||
60
- n.providerStatus === 'rate_limited' ||
61
- n.providerStatus === 'unavailable' ||
62
- n.providerStatus === 'auth_error',
63
- );
64
-
65
- return {
66
- nodes,
67
- activeIndex: modelState.activeFallbackIndex,
68
- falloverCount: modelState.falloverCount,
69
- hasUnhealthyNode,
70
- };
71
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Provider health UI data surface barrel.
3
- *
4
- * Re-exports all types and the ProviderHealthDataProvider class.
5
- * Also provides the createProviderHealthData() factory for one-shot snapshots.
6
- */
7
- export type {
8
- ProviderStatus,
9
- CompositeHealthStatus,
10
- HealthTimelinePoint,
11
- HealthTimeline,
12
- ProviderHealthEntry,
13
- FallbackChainNode,
14
- FallbackChainData,
15
- ProviderHealthData,
16
- } from '@/runtime/index.ts';
17
-
18
- export { ProviderHealthDataProvider } from '@/runtime/index.ts';
19
- export { buildFallbackChainData } from '@/runtime/index.ts';
20
-
21
- import type { ProviderHealthDomainState } from '@/runtime/index.ts';
22
- import type { ModelDomainState } from '@/runtime/index.ts';
23
- import { ProviderHealthDataProvider } from '@/runtime/index.ts';
24
- import type { ProviderHealthData } from '@/runtime/index.ts';
25
-
26
- /**
27
- * Produce a one-shot ProviderHealthData snapshot without creating a long-lived provider.
28
- *
29
- * Use this when you need a single render pass and do not require change subscriptions.
30
- * For reactive/subscription-based UIs, prefer ProviderHealthDataProvider.
31
- *
32
- * @param healthState - Current provider health domain state.
33
- * @param modelState - Current model domain state.
34
- * @returns Immutable ProviderHealthData snapshot.
35
- */
36
- export function createProviderHealthData(
37
- healthState: ProviderHealthDomainState,
38
- modelState: ModelDomainState,
39
- ): ProviderHealthData {
40
- // Delegate to the data provider for consistent derivation logic,
41
- // then dispose immediately since no subscriptions are needed.
42
- const dp = new ProviderHealthDataProvider(healthState, modelState);
43
- const snapshot = dp.getSnapshot();
44
- dp.dispose();
45
- return snapshot;
46
- }
@@ -1,146 +0,0 @@
1
- /**
2
- * Provider health UI data types.
3
- *
4
- * Purely data-oriented — no rendering logic. These types are produced by
5
- * ProviderHealthDataProvider and consumed by renderers/components.
6
- */
7
- import type { ProviderStatus, CompositeHealthStatus } from '@/runtime/index.ts';
8
-
9
- // Re-exports for convenience
10
- export type { ProviderStatus, CompositeHealthStatus };
11
-
12
- /**
13
- * A single point in a provider health timeline.
14
- * Used to render charts of success rate, latency, or error rate over time.
15
- */
16
- export interface HealthTimelinePoint {
17
- /** Epoch ms timestamp of this sample. */
18
- readonly ts: number;
19
- /** Observed success rate at this point (0–1). */
20
- readonly successRate: number;
21
- /** Observed average latency in ms at this point. */
22
- readonly avgLatencyMs: number;
23
- /** Observed error rate at this point (0–1). */
24
- readonly errorRate: number;
25
- }
26
-
27
- /**
28
- * Timeline data for a single provider, for chart rendering.
29
- */
30
- export interface HealthTimeline {
31
- /** Provider ID this timeline belongs to. */
32
- readonly providerId: string;
33
- /** Ordered timeline points (oldest first). */
34
- readonly points: readonly HealthTimelinePoint[];
35
- /** The number of points retained in this timeline. */
36
- readonly length: number;
37
- }
38
-
39
- /**
40
- * Full health entry for a single provider, enriched for UI consumption.
41
- */
42
- export interface ProviderHealthEntry {
43
- // ── Identity ──────────────────────────────────────────────────────────────
44
- /** Provider identifier. */
45
- readonly providerId: string;
46
- /** Human-readable provider name. */
47
- readonly displayName: string;
48
-
49
- // ── Status ────────────────────────────────────────────────────────────────
50
- /** Current health status. */
51
- readonly status: ProviderStatus;
52
- /** True when this provider is the active selection. */
53
- readonly isActive: boolean;
54
- /** True when this provider is configured with a valid API key. */
55
- readonly isConfigured: boolean;
56
-
57
- // ── Performance metrics ───────────────────────────────────────────────────
58
- /** Success rate (0–1), derived from totalCalls and successCalls. */
59
- readonly successRate: number;
60
- /** Error rate (0–1), derived from totalCalls and errorCalls. */
61
- readonly errorRate: number;
62
- /** Approximate p95 latency in ms (max of recent observations). */
63
- readonly p95LatencyMs: number;
64
- /** Moving average latency in ms. */
65
- readonly avgLatencyMs: number;
66
- /** Total API calls recorded. */
67
- readonly totalCalls: number;
68
-
69
- // ── Cache metrics ─────────────────────────────────────────────────────────
70
- /** Cache hit rate (0–1). Present only when cache metrics are available. */
71
- readonly cacheHitRate?: number;
72
- /** Cumulative cache read tokens saved. */
73
- readonly cacheReadTokens?: number;
74
- /** Cumulative cache write tokens. */
75
- readonly cacheWriteTokens?: number;
76
-
77
- // ── Timing ────────────────────────────────────────────────────────────────
78
- /** Epoch ms of the last successful call, if any. */
79
- readonly lastSuccessAt?: number;
80
- /** Epoch ms of the last error, if any. */
81
- readonly lastErrorAt?: number;
82
- /** Most recent error message, if any. */
83
- readonly lastErrorMessage?: string;
84
- /** Epoch ms of the last health check. */
85
- readonly lastCheckedAt?: number;
86
- /** Rate limit reset time (epoch ms), if currently rate-limited. */
87
- readonly rateLimitResetAt?: number;
88
-
89
- // ── Timeline ──────────────────────────────────────────────────────────────
90
- /** Timeline data for chart rendering. */
91
- readonly timeline: HealthTimeline;
92
- }
93
-
94
- /**
95
- * A single node in the fallback chain visualization.
96
- */
97
- export interface FallbackChainNode {
98
- /** Provider identifier. */
99
- readonly providerId: string;
100
- /** Model identifier on this provider. */
101
- readonly modelId: string;
102
- /** Human-readable display label. */
103
- readonly displayName: string;
104
- /** Position in the chain (0 = primary). */
105
- readonly position: number;
106
- /** Whether this node is the currently active entry in the chain. */
107
- readonly isCurrent: boolean;
108
- /** Current health status of this provider. */
109
- readonly providerStatus: ProviderStatus;
110
- /** Reason this fallback was configured. */
111
- readonly reason?: string;
112
- }
113
-
114
- /**
115
- * Fallback chain visualization data for the current model configuration.
116
- */
117
- export interface FallbackChainData {
118
- /** Ordered nodes in the fallback chain (primary first). */
119
- readonly nodes: readonly FallbackChainNode[];
120
- /** Index of the currently active node (-1 = primary is active). */
121
- readonly activeIndex: number;
122
- /** Number of fallover events since session start. */
123
- readonly falloverCount: number;
124
- /** True when the chain has at least one degraded or unavailable node. */
125
- readonly hasUnhealthyNode: boolean;
126
- }
127
-
128
- /**
129
- * Complete data snapshot produced by ProviderHealthDataProvider.
130
- */
131
- export interface ProviderHealthData {
132
- /** All provider health entries, sorted by status priority then name. */
133
- readonly entries: readonly ProviderHealthEntry[];
134
- /** Composite health status across all providers. */
135
- readonly compositeStatus: CompositeHealthStatus;
136
- /** Number of providers currently in a non-healthy state. */
137
- readonly degradedCount: number;
138
- /** Number of providers currently unavailable. */
139
- readonly unavailableCount: number;
140
- /** Fallback chain visualization data. */
141
- readonly fallbackChain: FallbackChainData;
142
- /** Warning messages from provider discovery. */
143
- readonly warnings: readonly string[];
144
- /** Epoch ms when this snapshot was produced. */
145
- readonly snapshotAt: number;
146
- }
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1 +0,0 @@
1
- export * from '@/runtime/index.ts';
@@ -1,98 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // voice-setup-services.ts — managed local-voice provisioning composition (TUI wiring)
3
- //
4
- // Backs the daemon's voice.local.status / voice.local.install verbs. The
5
- // provisioning policy — a single-flight one-act install that provisions the
6
- // piper engine + a default voice (and whisper STT where a bundle is published),
7
- // live install-progress folded onto status(), ownership-aware preconfigure of
8
- // the voice.local.* keys (values THIS installer previously wrote update to the
9
- // new managed paths; genuinely user-set values still win; a user-cleared
10
- // installer value stays cleared), a no-network status() read, and critical-tier
11
- // admission gating — lives in the SDK's createVoiceSetupService. Earlier the
12
- // composer was not exported, so the TUI rebuilt it from the voice primitives;
13
- // SDK 1.10.1 exports it through ./platform/runtime/voice-setup, so this module
14
- // is now a thin adapter that maps the TUI's injected seams (configManager,
15
- // shellPaths, voiceProviders) onto the SDK service's flat deps.
16
- //
17
- // Extracted into its own module rather than built inline in services.ts, which
18
- // sits at the architecture check's 800-line cap (scripts/check-architecture.ts)
19
- // — new service construction gets its own module and a single wiring call there.
20
- // ---------------------------------------------------------------------------
21
-
22
- import {
23
- createVoiceSetupService,
24
- type VoiceSetupService,
25
- } from '@pellux/goodvibes-sdk/platform/runtime/voice-setup';
26
- import {
27
- startWakeBootProvisioning,
28
- type VoiceProviderRegistry,
29
- } from '@pellux/goodvibes-sdk/platform/voice';
30
- import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
31
- import { logger } from '@pellux/goodvibes-sdk/platform/utils';
32
- import type { ShellPathService } from '@/runtime/index.ts';
33
-
34
- export type { VoiceSetupService };
35
-
36
- /**
37
- * The recovery act this surface names when a boot provision degrades. The SDK
38
- * would otherwise name the control-plane verb, which is correct everywhere and
39
- * useless to someone sitting in a terminal.
40
- */
41
- export const WAKE_RECOVERY_COMMAND = '/voice wake setup';
42
-
43
- export interface VoiceSetupServicesDeps {
44
- readonly configManager: ConfigManager;
45
- readonly shellPaths: Pick<ShellPathService, 'resolveUserPath'>;
46
- readonly voiceProviders: VoiceProviderRegistry;
47
- /** Critical-tier admission gate — a provision run allocates archive + model buffers. */
48
- readonly admitExpensiveWork: (label: string) => { allowed: boolean; reason?: string | undefined };
49
- /**
50
- * Opt into boot-time wake-model provisioning and the recovery sweeper.
51
- *
52
- * Off unless a real entrypoint asks for it, matching how this graph treats the
53
- * host power seam: `daemon/cli.ts` and `runtime/bootstrap-core.ts` opt in, the
54
- * one-shot CLI commands do not, and a test composing the graph gets neither a
55
- * network fetch nor a repeating timer it did not ask for.
56
- */
57
- readonly provisionWakeModelsAtBoot?: boolean | undefined;
58
- /** Injected in tests: the boot starter, so no sweeper and no fetch are real. */
59
- readonly startBootProvisioning?: typeof startWakeBootProvisioning | undefined;
60
- }
61
-
62
- export interface VoiceSetupServices {
63
- readonly voiceSetup: VoiceSetupService;
64
- /**
65
- * Stop the wake-word recovery sweeper and cancel a pending boot provision.
66
- * A no-op when boot provisioning was not opted into. Registered on the
67
- * disposal scope (disposal-wiring.ts) — an hourly timer nothing stops is a
68
- * poller this surface leaked, which is exactly what that scope exists for.
69
- */
70
- readonly stopWakeHousekeeping: () => void;
71
- }
72
-
73
- /** Compose the managed-voice single-flight install + no-network status read. */
74
- export function wireVoiceSetup(deps: VoiceSetupServicesDeps): VoiceSetupServices {
75
- const { configManager, voiceProviders } = deps;
76
- const managedVoiceRoot = deps.shellPaths.resolveUserPath('voice');
77
- const voiceSetup = createVoiceSetupService({
78
- managedVoiceRoot,
79
- getConfig: (key) => String(configManager.get(key as Parameters<typeof configManager.get>[0]) ?? ''),
80
- setConfig: (key, value) => configManager.setDynamic(key as Parameters<typeof configManager.setDynamic>[0], value),
81
- // A successful (re-)install clears any tripped local-engine breaker.
82
- resetLocalEngineFailureState: () => voiceProviders.get('local')?.resetEngineFailureState?.(),
83
- admitExpensiveWork: deps.admitExpensiveWork,
84
- });
85
- if (deps.provisionWakeModelsAtBoot !== true) {
86
- return { voiceSetup, stopWakeHousekeeping: () => {} };
87
- }
88
- // Sweep the wake tree, then fetch whatever the install could not. The SDK owns
89
- // the policy — never throws, one plain message, reaps before it retries — and
90
- // routes the attempt through the setup service's single flight so a boot
91
- // attempt and a user typing /voice wake setup are one download.
92
- const boot = (deps.startBootProvisioning ?? startWakeBootProvisioning)({
93
- managedRoot: managedVoiceRoot,
94
- ensureProvisioned: () => voiceSetup.wakeEnsureProvisioned({ recoveryHint: WAKE_RECOVERY_COMMAND }),
95
- announce: (message) => { logger.info(message); },
96
- });
97
- return { voiceSetup, stopWakeHousekeeping: () => boot.stop() };
98
- }
@@ -1,77 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // workstream-attempts-validation.ts — plan-format validation for best-of-N.
3
- //
4
- // The plan format re-enables the per-item `attempts` field (SDK WorkItem/
5
- // WorkItemSpec.attempts — best-of-N). A best-of-N item MAY be non-leaf: it may
6
- // declare its own `dependsOn` (each sibling inherits it) and other items may
7
- // depend on it — a dependent is held by the dependency gate until the group's
8
- // winner is picked and merged, then resolves to that winner (SDK
9
- // WorkItemSpec.attempts doc). The TUI enforces the engine's genuine constraints
10
- // at plan validation rather than letting a launch silently drop the field:
11
- // • WORKTREE isolation. Best-of-N needs isolated worktrees to run and compare
12
- // the N sibling attempts; under `shared` isolation the value is ignored.
13
- // • STABLE id when depended upon. A dependency edge references an id, so a
14
- // best-of-N item that others depend on must carry a stable `id`; an
15
- // anonymous best-of-N item stays a leaf by construction (nothing can name it).
16
- // Values above the engine's MAX_ATTEMPTS (5) are clamped by the engine; the TUI
17
- // surfaces that as a note so the preview never overstates the fan-out.
18
- // ---------------------------------------------------------------------------
19
-
20
- import type { CreateWorkstreamInput } from '@pellux/goodvibes-sdk/platform/orchestration';
21
-
22
- /** The engine's hard cap on sibling attempts per work item (SDK MAX_ATTEMPTS). */
23
- export const MAX_ATTEMPTS = 5;
24
-
25
- export interface AttemptsValidation {
26
- /** True when any item requests best-of-N (attempts >= 2). */
27
- readonly hasAttempts: boolean;
28
- /** Hard rule breaks that must block a launch (worktree isolation + stable id when depended upon). */
29
- readonly violations: readonly string[];
30
- /** Non-blocking notes (e.g. an over-cap value the engine will clamp). */
31
- readonly notes: readonly string[];
32
- }
33
-
34
- function itemKey(item: CreateWorkstreamInput['items'][number]): string {
35
- return item.id ?? item.title;
36
- }
37
-
38
- /**
39
- * Validate a draft spec's best-of-N usage against the worktree-isolation and
40
- * stable-id (when depended upon) constraints. Non-leaf best-of-N is allowed.
41
- * Pure — no I/O. `violations` are hard (block launch); `notes` are advisory.
42
- */
43
- export function validateAttempts(spec: CreateWorkstreamInput): AttemptsValidation {
44
- const violations: string[] = [];
45
- const notes: string[] = [];
46
- const worktree = spec.isolation === 'worktree';
47
-
48
- // Which items are depended upon (so a best-of-N item that is a dependency
49
- // target can be checked for the stable-id requirement).
50
- const dependedUpon = new Set<string>();
51
- for (const item of spec.items) {
52
- for (const dep of item.dependsOn ?? []) dependedUpon.add(dep);
53
- }
54
-
55
- let hasAttempts = false;
56
- for (const item of spec.items) {
57
- const attempts = item.attempts ?? 1;
58
- if (attempts < 2) continue;
59
- hasAttempts = true;
60
- const label = item.title;
61
-
62
- if (!worktree) {
63
- violations.push(`"${label}" requests ${attempts} attempts, but the workstream is not worktree-isolated — best-of-N needs isolated trees. Set --isolation worktree.`);
64
- }
65
- // Non-leaf best-of-N is allowed. The only structural requirement: if other
66
- // items depend on this one, it must carry a stable id for the dependency
67
- // edge to name — an anonymous best-of-N item cannot be a dependency target.
68
- if (dependedUpon.has(itemKey(item)) && !item.id) {
69
- violations.push(`another item depends on best-of-N item "${label}", but it has no stable id — give the item an id so the dependency can resolve to the winner (an anonymous best-of-N item cannot be a dependency target).`);
70
- }
71
- if (attempts > MAX_ATTEMPTS) {
72
- notes.push(`"${label}" requests ${attempts} attempts — the engine caps best-of-N at ${MAX_ATTEMPTS}, so ${MAX_ATTEMPTS} siblings will run.`);
73
- }
74
- }
75
-
76
- return { hasAttempts, violations, notes };
77
- }
@@ -1,95 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // workstream-draft-edits.ts — pure item edits for a not-yet-launched proposal
3
- //
4
- // The plan-review gate (see /workstream, input/commands/workstream-runtime.ts)
5
- // lets a human reshape a decomposed proposal BEFORE anything is spent: rewrite
6
- // one item's brief, drop an item, or reorder items. These are pure functions
7
- // over the launchable CreateWorkstreamInput (@pellux/goodvibes-sdk/platform/
8
- // orchestration) — no engine calls, no I/O — so the same spec that is edited
9
- // here is byte-for-byte what launch hands createWorkstream. Extracted from
10
- // workstream-services.ts to keep that construction module lean (its own
11
- // header explains the 800-line-cap discipline) and to give the edits a clean
12
- // unit-test target.
13
- //
14
- // HONEST SCOPE (stated so the review surface never overstates what an edit
15
- // does):
16
- // - editItemBrief rewrites an item's `task` (the instructions its agent
17
- // runs), never its `title` (the short label). The engineer/review phase
18
- // template is unchanged — a brief is WHAT the item does, not HOW the
19
- // pipeline runs it.
20
- // - removeItem also strips the removed item's id from every other item's
21
- // `dependsOn`, so the spec never carries a dangling dependency into
22
- // fromPlanProposal's assembly (which would otherwise reject it). It
23
- // refuses to remove the last item — a workstream needs at least one.
24
- // - moveItem reorders the AUTHORING order (the ordinals shown in the review
25
- // and the tie-break when the engine has a free capacity slot and several
26
- // claimable items). It never rewrites dependencies, so it cannot create a
27
- // cycle; the engine still schedules strictly by dependency + capacity, not
28
- // by array position. Reordering is a presentation/authoring act, not a
29
- // scheduling override — and is documented as such where it is offered.
30
- // ---------------------------------------------------------------------------
31
-
32
- import type { CreateWorkstreamInput, WorkItemSpec } from '@pellux/goodvibes-sdk/platform/orchestration';
33
-
34
- /** Success carries the rewritten spec; failure carries an honest, user-facing reason. */
35
- export type DraftEditResult = { readonly spec: CreateWorkstreamInput } | { readonly error: string };
36
-
37
- /**
38
- * Resolve a user-typed item reference to a 0-based index. A reference is
39
- * either the 1-based ordinal shown in the proposal (the primary handle) or an
40
- * item id / unique id-prefix (the stable handle that survives reordering). An
41
- * ambiguous or unmatched reference resolves to undefined so the caller can
42
- * refuse honestly rather than editing the wrong item.
43
- */
44
- export function resolveItemIndex(items: readonly WorkItemSpec[], ref: string): number | undefined {
45
- const trimmed = ref.trim();
46
- const ordinal = Number(trimmed);
47
- if (Number.isInteger(ordinal) && ordinal >= 1 && ordinal <= items.length) return ordinal - 1;
48
- const matches = items
49
- .map((item, index) => ({ item, index }))
50
- .filter(({ item }) => item.id !== undefined && item.id.startsWith(trimmed));
51
- return matches.length === 1 ? matches[0]!.index : undefined;
52
- }
53
-
54
- function unresolvedError(items: readonly WorkItemSpec[], ref: string): string {
55
- return `No draft item matches "${ref}". Use a number from 1 to ${items.length}, or an item id.`;
56
- }
57
-
58
- /** Rewrite one item's brief (its `task`). Empty/whitespace briefs are refused. */
59
- export function editItemBrief(spec: CreateWorkstreamInput, ref: string, brief: string): DraftEditResult {
60
- const index = resolveItemIndex(spec.items, ref);
61
- if (index === undefined) return { error: unresolvedError(spec.items, ref) };
62
- const trimmed = brief.trim();
63
- if (!trimmed) return { error: 'A new brief cannot be empty.' };
64
- const items = spec.items.map((item, i) => (i === index ? { ...item, task: trimmed } : item));
65
- return { spec: { ...spec, items } };
66
- }
67
-
68
- /** Drop one item and strip its id from every sibling's dependsOn. Refuses to empty the plan. */
69
- export function removeItemFromSpec(spec: CreateWorkstreamInput, ref: string): DraftEditResult {
70
- const index = resolveItemIndex(spec.items, ref);
71
- if (index === undefined) return { error: unresolvedError(spec.items, ref) };
72
- if (spec.items.length <= 1) return { error: 'Cannot remove the last item — a workstream needs at least one.' };
73
- const removedId = spec.items[index]!.id;
74
- const items = spec.items
75
- .filter((_, i) => i !== index)
76
- .map((item) =>
77
- removedId && item.dependsOn?.includes(removedId)
78
- ? { ...item, dependsOn: item.dependsOn.filter((dep) => dep !== removedId) }
79
- : item,
80
- );
81
- return { spec: { ...spec, items } };
82
- }
83
-
84
- /** Reorder one item to a new 1-based authoring position. Never touches dependencies. */
85
- export function moveItemInSpec(spec: CreateWorkstreamInput, ref: string, toPosition: number): DraftEditResult {
86
- const index = resolveItemIndex(spec.items, ref);
87
- if (index === undefined) return { error: unresolvedError(spec.items, ref) };
88
- if (!Number.isInteger(toPosition) || toPosition < 1 || toPosition > spec.items.length) {
89
- return { error: `Position must be a whole number from 1 to ${spec.items.length}.` };
90
- }
91
- const items = [...spec.items];
92
- const [moved] = items.splice(index, 1);
93
- items.splice(toPosition - 1, 0, moved!);
94
- return { spec: { ...spec, items } };
95
- }