@pellux/goodvibes-agent 0.1.0

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 (398) hide show
  1. package/.goodvibes/GOODVIBES.md +35 -0
  2. package/.goodvibes/agents/reviewer.md +48 -0
  3. package/.goodvibes/skills/add-provider/SKILL.md +199 -0
  4. package/CHANGELOG.md +25 -0
  5. package/README.md +74 -0
  6. package/bin/goodvibes-agent.ts +2 -0
  7. package/docs/README.md +23 -0
  8. package/docs/deployment-and-services.md +57 -0
  9. package/docs/getting-started.md +53 -0
  10. package/docs/release-and-publishing.md +46 -0
  11. package/package.json +134 -0
  12. package/scripts/check-bun.sh +20 -0
  13. package/src/audio/player.ts +156 -0
  14. package/src/audio/spoken-turn-controller.ts +203 -0
  15. package/src/audio/spoken-turn-model-routing.ts +117 -0
  16. package/src/audio/spoken-turn-wiring.ts +44 -0
  17. package/src/audio/text-chunker.ts +110 -0
  18. package/src/cli/bundle-command.ts +227 -0
  19. package/src/cli/completion.ts +90 -0
  20. package/src/cli/config-overrides.ts +159 -0
  21. package/src/cli/endpoints.ts +63 -0
  22. package/src/cli/entrypoint.ts +172 -0
  23. package/src/cli/help.ts +299 -0
  24. package/src/cli/index.ts +11 -0
  25. package/src/cli/management-commands.ts +426 -0
  26. package/src/cli/management.ts +744 -0
  27. package/src/cli/network-posture.ts +46 -0
  28. package/src/cli/package-verification.ts +123 -0
  29. package/src/cli/parser.ts +369 -0
  30. package/src/cli/provider-auth-routes.ts +22 -0
  31. package/src/cli/provider-classification.ts +107 -0
  32. package/src/cli/redaction.ts +105 -0
  33. package/src/cli/service-command.ts +26 -0
  34. package/src/cli/service-posture.ts +482 -0
  35. package/src/cli/status.ts +383 -0
  36. package/src/cli/surface-command.ts +247 -0
  37. package/src/cli/tui-startup.ts +32 -0
  38. package/src/cli/types.ts +69 -0
  39. package/src/cli-flags.ts +21 -0
  40. package/src/config/goodvibes-home-audit.ts +465 -0
  41. package/src/config/index.ts +57 -0
  42. package/src/config/provider-model.ts +23 -0
  43. package/src/config/secret-config.ts +119 -0
  44. package/src/config/secrets.ts +71 -0
  45. package/src/config/surface.ts +1 -0
  46. package/src/core/composer-state.ts +61 -0
  47. package/src/core/conversation-rendering.ts +359 -0
  48. package/src/core/conversation.ts +551 -0
  49. package/src/core/history.ts +45 -0
  50. package/src/core/orchestrator.ts +7 -0
  51. package/src/core/system-message-router.ts +171 -0
  52. package/src/daemon/cli.ts +55 -0
  53. package/src/daemon/safe-serve.ts +61 -0
  54. package/src/input/agent-workspace.ts +428 -0
  55. package/src/input/autocomplete.ts +96 -0
  56. package/src/input/bookmark-modal.ts +115 -0
  57. package/src/input/command-args-hint.ts +36 -0
  58. package/src/input/command-registry.ts +329 -0
  59. package/src/input/commands/agent-externalized-tui.ts +73 -0
  60. package/src/input/commands/agent-workspace-runtime.ts +17 -0
  61. package/src/input/commands/branch-runtime.ts +72 -0
  62. package/src/input/commands/cloudflare-runtime.ts +370 -0
  63. package/src/input/commands/config.ts +18 -0
  64. package/src/input/commands/control-room-runtime.ts +255 -0
  65. package/src/input/commands/conversation-runtime.ts +207 -0
  66. package/src/input/commands/discovery-runtime.ts +52 -0
  67. package/src/input/commands/eval.ts +204 -0
  68. package/src/input/commands/experience-runtime.ts +278 -0
  69. package/src/input/commands/guidance-runtime.ts +106 -0
  70. package/src/input/commands/health-runtime.ts +434 -0
  71. package/src/input/commands/hooks-runtime.ts +148 -0
  72. package/src/input/commands/incident-runtime.ts +95 -0
  73. package/src/input/commands/integration-runtime.ts +394 -0
  74. package/src/input/commands/intelligence-runtime.ts +223 -0
  75. package/src/input/commands/knowledge.ts +531 -0
  76. package/src/input/commands/local-auth-runtime.ts +105 -0
  77. package/src/input/commands/local-provider-runtime.ts +170 -0
  78. package/src/input/commands/local-runtime.ts +392 -0
  79. package/src/input/commands/local-setup-review.ts +199 -0
  80. package/src/input/commands/local-setup-transfer.ts +135 -0
  81. package/src/input/commands/local-setup.ts +282 -0
  82. package/src/input/commands/managed-runtime.ts +209 -0
  83. package/src/input/commands/marketplace-runtime.ts +290 -0
  84. package/src/input/commands/mcp-runtime.ts +432 -0
  85. package/src/input/commands/memory-product-runtime.ts +111 -0
  86. package/src/input/commands/memory.ts +151 -0
  87. package/src/input/commands/notify-runtime.ts +83 -0
  88. package/src/input/commands/onboarding-runtime.ts +14 -0
  89. package/src/input/commands/operator-panel-runtime.ts +146 -0
  90. package/src/input/commands/operator-runtime.ts +392 -0
  91. package/src/input/commands/planning-runtime.ts +205 -0
  92. package/src/input/commands/platform-access-runtime.ts +422 -0
  93. package/src/input/commands/platform-services-runtime.ts +246 -0
  94. package/src/input/commands/policy-dispatch.ts +339 -0
  95. package/src/input/commands/policy.ts +17 -0
  96. package/src/input/commands/product-runtime.ts +351 -0
  97. package/src/input/commands/profile-sync-runtime.ts +99 -0
  98. package/src/input/commands/provider-accounts-runtime.ts +113 -0
  99. package/src/input/commands/provider.ts +363 -0
  100. package/src/input/commands/qrcode-runtime.ts +20 -0
  101. package/src/input/commands/quit-shared.ts +162 -0
  102. package/src/input/commands/recall-bundle.ts +132 -0
  103. package/src/input/commands/recall-capture.ts +152 -0
  104. package/src/input/commands/recall-query.ts +229 -0
  105. package/src/input/commands/recall-review.ts +98 -0
  106. package/src/input/commands/recall-shared.ts +22 -0
  107. package/src/input/commands/remote-runtime-pool.ts +106 -0
  108. package/src/input/commands/remote-runtime-setup.ts +199 -0
  109. package/src/input/commands/remote-runtime.ts +431 -0
  110. package/src/input/commands/replay-runtime.ts +18 -0
  111. package/src/input/commands/runtime-services.ts +291 -0
  112. package/src/input/commands/schedule-runtime.ts +91 -0
  113. package/src/input/commands/services-runtime.ts +209 -0
  114. package/src/input/commands/session-content.ts +408 -0
  115. package/src/input/commands/session-workflow.ts +464 -0
  116. package/src/input/commands/session.ts +375 -0
  117. package/src/input/commands/settings-sync-runtime.ts +174 -0
  118. package/src/input/commands/share-runtime.ts +119 -0
  119. package/src/input/commands/shell-core.ts +307 -0
  120. package/src/input/commands/skills-runtime.ts +221 -0
  121. package/src/input/commands/subscription-runtime.ts +434 -0
  122. package/src/input/commands/tasks-runtime.ts +230 -0
  123. package/src/input/commands/teamwork-runtime.ts +339 -0
  124. package/src/input/commands/teleport-runtime.ts +57 -0
  125. package/src/input/commands/tts-runtime.ts +29 -0
  126. package/src/input/commands/work-plan-runtime.ts +169 -0
  127. package/src/input/commands.ts +131 -0
  128. package/src/input/feed-context-factory.ts +254 -0
  129. package/src/input/file-picker.ts +192 -0
  130. package/src/input/handler-command-route.ts +180 -0
  131. package/src/input/handler-content-actions.ts +497 -0
  132. package/src/input/handler-feed-routes.ts +648 -0
  133. package/src/input/handler-feed.ts +452 -0
  134. package/src/input/handler-interactions.ts +281 -0
  135. package/src/input/handler-modal-routes.ts +418 -0
  136. package/src/input/handler-modal-stack.ts +263 -0
  137. package/src/input/handler-modal-token-routes.ts +329 -0
  138. package/src/input/handler-onboarding-cloudflare.ts +391 -0
  139. package/src/input/handler-onboarding.ts +620 -0
  140. package/src/input/handler-picker-routes.ts +472 -0
  141. package/src/input/handler-prompt-buffer.ts +320 -0
  142. package/src/input/handler-shortcuts.ts +213 -0
  143. package/src/input/handler-ui-state.ts +372 -0
  144. package/src/input/handler.ts +729 -0
  145. package/src/input/input-history.ts +297 -0
  146. package/src/input/keybindings.ts +292 -0
  147. package/src/input/mcp-workspace.ts +554 -0
  148. package/src/input/model-picker-provider-filter.ts +28 -0
  149. package/src/input/model-picker-types.ts +137 -0
  150. package/src/input/model-picker.ts +797 -0
  151. package/src/input/onboarding/handler-onboarding-routes.ts +125 -0
  152. package/src/input/onboarding/onboarding-runtime-status.ts +87 -0
  153. package/src/input/onboarding/onboarding-wizard-apply.ts +277 -0
  154. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +494 -0
  155. package/src/input/onboarding/onboarding-wizard-cloudflare.ts +204 -0
  156. package/src/input/onboarding/onboarding-wizard-constants.ts +158 -0
  157. package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +130 -0
  158. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +762 -0
  159. package/src/input/onboarding/onboarding-wizard-helpers.ts +167 -0
  160. package/src/input/onboarding/onboarding-wizard-rules.ts +256 -0
  161. package/src/input/onboarding/onboarding-wizard-state.ts +365 -0
  162. package/src/input/onboarding/onboarding-wizard-steps.ts +798 -0
  163. package/src/input/onboarding/onboarding-wizard-types.ts +195 -0
  164. package/src/input/onboarding/onboarding-wizard.ts +711 -0
  165. package/src/input/panel-integration-actions.ts +78 -0
  166. package/src/input/profile-picker-modal.ts +222 -0
  167. package/src/input/search.ts +100 -0
  168. package/src/input/selection-modal.ts +163 -0
  169. package/src/input/selection.ts +135 -0
  170. package/src/input/session-picker-modal.ts +136 -0
  171. package/src/input/settings-modal-behavior.ts +37 -0
  172. package/src/input/settings-modal-secrets.ts +41 -0
  173. package/src/input/settings-modal-subscriptions.ts +95 -0
  174. package/src/input/settings-modal-types.ts +91 -0
  175. package/src/input/settings-modal.ts +793 -0
  176. package/src/input/submission-intent.ts +17 -0
  177. package/src/input/submission-router.ts +59 -0
  178. package/src/input/tts-settings-actions.ts +100 -0
  179. package/src/main.ts +792 -0
  180. package/src/mcp/runtime-reload.ts +81 -0
  181. package/src/panels/agent-inspector-panel.ts +521 -0
  182. package/src/panels/agent-inspector-shared.ts +94 -0
  183. package/src/panels/agent-logs-panel.ts +559 -0
  184. package/src/panels/agent-logs-shared.ts +129 -0
  185. package/src/panels/approval-panel.ts +150 -0
  186. package/src/panels/automation-control-panel.ts +212 -0
  187. package/src/panels/base-panel.ts +254 -0
  188. package/src/panels/builtin/agent.ts +117 -0
  189. package/src/panels/builtin/development.ts +31 -0
  190. package/src/panels/builtin/knowledge.ts +26 -0
  191. package/src/panels/builtin/operations.ts +349 -0
  192. package/src/panels/builtin/session.ts +129 -0
  193. package/src/panels/builtin/shared.ts +274 -0
  194. package/src/panels/builtin-panels.ts +23 -0
  195. package/src/panels/cockpit-panel.ts +183 -0
  196. package/src/panels/communication-panel.ts +153 -0
  197. package/src/panels/confirm-state.ts +61 -0
  198. package/src/panels/context-visualizer-panel.ts +204 -0
  199. package/src/panels/control-plane-panel.ts +211 -0
  200. package/src/panels/cost-tracker-panel.ts +444 -0
  201. package/src/panels/debug-panel.ts +432 -0
  202. package/src/panels/diff-panel.ts +520 -0
  203. package/src/panels/docs-panel.ts +283 -0
  204. package/src/panels/eval-panel.ts +399 -0
  205. package/src/panels/file-explorer-panel.ts +584 -0
  206. package/src/panels/file-preview-panel.ts +434 -0
  207. package/src/panels/forensics-panel.ts +364 -0
  208. package/src/panels/git-panel.ts +638 -0
  209. package/src/panels/hooks-panel.ts +239 -0
  210. package/src/panels/incident-review-panel.ts +197 -0
  211. package/src/panels/index.ts +46 -0
  212. package/src/panels/intelligence-panel.ts +176 -0
  213. package/src/panels/knowledge-panel.ts +345 -0
  214. package/src/panels/local-auth-panel.ts +130 -0
  215. package/src/panels/marketplace-panel.ts +212 -0
  216. package/src/panels/memory-panel.ts +225 -0
  217. package/src/panels/ops-control-panel.ts +150 -0
  218. package/src/panels/ops-strategy-panel.ts +235 -0
  219. package/src/panels/orchestration-panel.ts +273 -0
  220. package/src/panels/panel-list-panel.ts +509 -0
  221. package/src/panels/panel-manager.ts +570 -0
  222. package/src/panels/panel-picker.ts +106 -0
  223. package/src/panels/plan-dashboard-panel.ts +274 -0
  224. package/src/panels/plugins-panel.ts +178 -0
  225. package/src/panels/policy-panel.ts +308 -0
  226. package/src/panels/polish.ts +717 -0
  227. package/src/panels/project-planning-panel.ts +711 -0
  228. package/src/panels/provider-account-snapshot.ts +259 -0
  229. package/src/panels/provider-accounts-panel.ts +218 -0
  230. package/src/panels/provider-health-domains.ts +215 -0
  231. package/src/panels/provider-health-panel.ts +727 -0
  232. package/src/panels/provider-health-tracker.ts +115 -0
  233. package/src/panels/provider-stats-panel.ts +366 -0
  234. package/src/panels/qr-panel.ts +182 -0
  235. package/src/panels/remote-panel.ts +449 -0
  236. package/src/panels/routes-panel.ts +178 -0
  237. package/src/panels/sandbox-panel.ts +283 -0
  238. package/src/panels/schedule-panel.ts +329 -0
  239. package/src/panels/scrollable-list-panel.ts +491 -0
  240. package/src/panels/search-focus.ts +32 -0
  241. package/src/panels/security-panel.ts +295 -0
  242. package/src/panels/services-panel.ts +231 -0
  243. package/src/panels/session-browser-panel.ts +400 -0
  244. package/src/panels/session-maintenance.ts +125 -0
  245. package/src/panels/settings-sync-panel.ts +120 -0
  246. package/src/panels/skills-panel.ts +431 -0
  247. package/src/panels/subscription-panel.ts +263 -0
  248. package/src/panels/symbol-outline-panel.ts +486 -0
  249. package/src/panels/system-messages-panel.ts +230 -0
  250. package/src/panels/tasks-panel.ts +399 -0
  251. package/src/panels/thinking-panel.ts +304 -0
  252. package/src/panels/token-budget-panel.ts +475 -0
  253. package/src/panels/tool-inspector-panel.ts +429 -0
  254. package/src/panels/types.ts +54 -0
  255. package/src/panels/watchers-panel.ts +193 -0
  256. package/src/panels/work-plan-panel.ts +175 -0
  257. package/src/panels/worktree-panel.ts +182 -0
  258. package/src/panels/wrfc-panel.ts +609 -0
  259. package/src/permissions/prompt.ts +165 -0
  260. package/src/planning/project-planning-coordinator.ts +543 -0
  261. package/src/plugins/loader.ts +15 -0
  262. package/src/renderer/agent-detail-modal.ts +331 -0
  263. package/src/renderer/agent-workspace.ts +238 -0
  264. package/src/renderer/ansi-sanitize.ts +76 -0
  265. package/src/renderer/autocomplete-overlay.ts +154 -0
  266. package/src/renderer/block-actions.ts +76 -0
  267. package/src/renderer/bookmark-modal.ts +101 -0
  268. package/src/renderer/bottom-bar.ts +58 -0
  269. package/src/renderer/buffer.ts +113 -0
  270. package/src/renderer/code-block.ts +373 -0
  271. package/src/renderer/compositor.ts +283 -0
  272. package/src/renderer/context-inspector.ts +219 -0
  273. package/src/renderer/conversation-layout.ts +67 -0
  274. package/src/renderer/conversation-overlays.ts +140 -0
  275. package/src/renderer/conversation-surface.ts +260 -0
  276. package/src/renderer/diff-view.ts +132 -0
  277. package/src/renderer/diff.ts +130 -0
  278. package/src/renderer/file-picker-overlay.ts +101 -0
  279. package/src/renderer/file-tree.ts +153 -0
  280. package/src/renderer/fullscreen-primitives.ts +130 -0
  281. package/src/renderer/fullscreen-workspace.ts +199 -0
  282. package/src/renderer/git-status.ts +89 -0
  283. package/src/renderer/help-overlay.ts +267 -0
  284. package/src/renderer/history-search-overlay.ts +73 -0
  285. package/src/renderer/layout-engine.ts +97 -0
  286. package/src/renderer/layout.ts +32 -0
  287. package/src/renderer/live-tail-modal.ts +156 -0
  288. package/src/renderer/markdown.ts +635 -0
  289. package/src/renderer/mcp-workspace.ts +237 -0
  290. package/src/renderer/modal-factory.ts +467 -0
  291. package/src/renderer/modal-utils.ts +24 -0
  292. package/src/renderer/model-picker-overlay.ts +473 -0
  293. package/src/renderer/model-workspace.ts +488 -0
  294. package/src/renderer/onboarding/onboarding-wizard.ts +615 -0
  295. package/src/renderer/overlay-box.ts +146 -0
  296. package/src/renderer/overlay-viewport.ts +104 -0
  297. package/src/renderer/panel-composite.ts +158 -0
  298. package/src/renderer/panel-picker-overlay.ts +202 -0
  299. package/src/renderer/panel-tab-bar.ts +69 -0
  300. package/src/renderer/panel-workspace-bar.ts +42 -0
  301. package/src/renderer/process-indicator.ts +96 -0
  302. package/src/renderer/process-modal.ts +656 -0
  303. package/src/renderer/process-summary.ts +67 -0
  304. package/src/renderer/profile-picker-modal.ts +129 -0
  305. package/src/renderer/progress.ts +98 -0
  306. package/src/renderer/qr-renderer.ts +120 -0
  307. package/src/renderer/search-overlay.ts +54 -0
  308. package/src/renderer/selection-modal-overlay.ts +214 -0
  309. package/src/renderer/semantic-diff.ts +369 -0
  310. package/src/renderer/session-picker-modal.ts +127 -0
  311. package/src/renderer/settings-modal-helpers.ts +193 -0
  312. package/src/renderer/settings-modal.ts +537 -0
  313. package/src/renderer/shell-surface.ts +88 -0
  314. package/src/renderer/status-glyphs.ts +21 -0
  315. package/src/renderer/status-token.ts +67 -0
  316. package/src/renderer/surface-layout.ts +101 -0
  317. package/src/renderer/syntax-highlighter.ts +542 -0
  318. package/src/renderer/system-message.ts +83 -0
  319. package/src/renderer/tab-strip.ts +108 -0
  320. package/src/renderer/text-layout.ts +31 -0
  321. package/src/renderer/thinking.ts +17 -0
  322. package/src/renderer/tool-call.ts +234 -0
  323. package/src/renderer/ui-factory.ts +524 -0
  324. package/src/renderer/ui-primitives.ts +96 -0
  325. package/src/runtime/bootstrap-command-context.ts +278 -0
  326. package/src/runtime/bootstrap-command-parts.ts +386 -0
  327. package/src/runtime/bootstrap-core.ts +540 -0
  328. package/src/runtime/bootstrap-hook-bridge.ts +112 -0
  329. package/src/runtime/bootstrap-shell.ts +283 -0
  330. package/src/runtime/bootstrap.ts +575 -0
  331. package/src/runtime/cloudflare-control-plane.ts +349 -0
  332. package/src/runtime/context.ts +142 -0
  333. package/src/runtime/diagnostics/panels/index.ts +24 -0
  334. package/src/runtime/diagnostics/panels/ops.ts +156 -0
  335. package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
  336. package/src/runtime/diagnostics/panels/policy.ts +177 -0
  337. package/src/runtime/index.ts +662 -0
  338. package/src/runtime/onboarding/apply.ts +642 -0
  339. package/src/runtime/onboarding/derivation.ts +534 -0
  340. package/src/runtime/onboarding/index.ts +7 -0
  341. package/src/runtime/onboarding/markers.ts +148 -0
  342. package/src/runtime/onboarding/snapshot.ts +406 -0
  343. package/src/runtime/onboarding/state.ts +141 -0
  344. package/src/runtime/onboarding/types.ts +404 -0
  345. package/src/runtime/onboarding/verify.ts +171 -0
  346. package/src/runtime/operator-token-cleanup.ts +27 -0
  347. package/src/runtime/perf/panel-contracts.ts +32 -0
  348. package/src/runtime/perf/panel-health-monitor.ts +18 -0
  349. package/src/runtime/sandbox-public-gaps.ts +358 -0
  350. package/src/runtime/services.ts +670 -0
  351. package/src/runtime/store/domains/domain-read-matrix.ts +15 -0
  352. package/src/runtime/store/domains/index.ts +222 -0
  353. package/src/runtime/store/domains/panels.ts +117 -0
  354. package/src/runtime/store/domains/ui-perf.ts +103 -0
  355. package/src/runtime/store/index.ts +305 -0
  356. package/src/runtime/store/selectors/index.ts +359 -0
  357. package/src/runtime/store/state.ts +145 -0
  358. package/src/runtime/surface-feature-flags.ts +65 -0
  359. package/src/runtime/terminal-output-guard.ts +228 -0
  360. package/src/runtime/ui/index.ts +39 -0
  361. package/src/runtime/ui/model-picker/data-provider.ts +182 -0
  362. package/src/runtime/ui/model-picker/health-enrichment.ts +228 -0
  363. package/src/runtime/ui/model-picker/index.ts +59 -0
  364. package/src/runtime/ui/model-picker/types.ts +149 -0
  365. package/src/runtime/ui/provider-health/data-provider.ts +244 -0
  366. package/src/runtime/ui/provider-health/fallback-visualizer.ts +71 -0
  367. package/src/runtime/ui/provider-health/index.ts +46 -0
  368. package/src/runtime/ui/provider-health/types.ts +146 -0
  369. package/src/runtime/ui-events.ts +1 -0
  370. package/src/runtime/ui-read-model-helpers.ts +1 -0
  371. package/src/runtime/ui-read-models-observability-maintenance.ts +1 -0
  372. package/src/runtime/ui-read-models-observability-options.ts +1 -0
  373. package/src/runtime/ui-read-models-observability-remote.ts +1 -0
  374. package/src/runtime/ui-read-models-observability-security.ts +1 -0
  375. package/src/runtime/ui-read-models-observability-system.ts +1 -0
  376. package/src/runtime/ui-read-models-observability.ts +1 -0
  377. package/src/runtime/ui-read-models.ts +61 -0
  378. package/src/runtime/ui-service-queries.ts +1 -0
  379. package/src/runtime/ui-services.ts +190 -0
  380. package/src/scripts/process-messages.ts +42 -0
  381. package/src/shell/blocking-input.ts +98 -0
  382. package/src/shell/service-settings-sync.ts +273 -0
  383. package/src/shell/ui-openers.ts +352 -0
  384. package/src/tools/index.ts +1 -0
  385. package/src/tools/wrfc-agent-guard.ts +49 -0
  386. package/src/types/grid.ts +48 -0
  387. package/src/types/sql-js.d.ts +15 -0
  388. package/src/utils/clipboard.ts +22 -0
  389. package/src/utils/splash-lines.ts +46 -0
  390. package/src/utils/terminal-width.ts +185 -0
  391. package/src/verification/live-verifier.ts +430 -0
  392. package/src/verification/verification-ledger.ts +242 -0
  393. package/src/version.ts +17 -0
  394. package/src/widget/index.ts +2 -0
  395. package/src/widget/types.ts +9 -0
  396. package/src/widget/widget.ts +8 -0
  397. package/src/work-plans/work-plan-store.ts +374 -0
  398. package/tsconfig.json +18 -0
@@ -0,0 +1,434 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import type { CommandContext, CommandRegistry } from '../command-registry.ts';
4
+ import { createOAuthLocalListener } from '@pellux/goodvibes-sdk/platform/config';
5
+ import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibes-sdk/platform/config';
6
+ import type { OAuthProviderConfig, ProviderSubscription } from '@pellux/goodvibes-sdk/platform/config';
7
+ import { getSubscriptionProviderConfig, listAvailableSubscriptionProviders } from '@pellux/goodvibes-sdk/platform/config';
8
+ import { inspectProviderAuth } from '@/runtime/index.ts';
9
+ import { openExternalUrl } from '@pellux/goodvibes-sdk/platform/utils';
10
+ import { requireSecretsManager, requireServiceRegistry, requireShellPaths, requireSubscriptionManager } from './runtime-services.ts';
11
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
12
+
13
+ interface SubscriptionBundle {
14
+ readonly version: 1;
15
+ readonly exportedAt: number;
16
+ readonly subscriptions: readonly ProviderSubscription[];
17
+ }
18
+
19
+ function buildReviewText(ctx: CommandContext): string {
20
+ const subscriptions = requireSubscriptionManager(ctx).list();
21
+ const available = listAvailableSubscriptionProviders(requireServiceRegistry(ctx).getAll());
22
+ if (subscriptions.length === 0) {
23
+ return [
24
+ 'Subscription Review',
25
+ ' No provider subscriptions stored yet.',
26
+ ...(available.length > 0 ? [` available providers: ${available.map((entry) => entry.provider).join(', ')}`] : []),
27
+ ].join('\n');
28
+ }
29
+ return [
30
+ `Subscription Review`,
31
+ ...subscriptions.map((subscription) => (
32
+ ` ${subscription.provider} ${subscription.authMode} token=${subscription.tokenType} expires=${subscription.expiresAt ? new Date(subscription.expiresAt).toISOString() : 'n/a'}`
33
+ )),
34
+ ].join('\n');
35
+ }
36
+
37
+ function inspectBundle(path: string): string {
38
+ const bundle = JSON.parse(readFileSync(path, 'utf-8')) as SubscriptionBundle;
39
+ return [
40
+ 'Subscription Bundle Review',
41
+ ` exportedAt: ${new Date(bundle.exportedAt).toISOString()}`,
42
+ ` subscriptions: ${bundle.subscriptions.length}`,
43
+ ...bundle.subscriptions.map((subscription) => ` ${subscription.provider} ${subscription.authMode}`),
44
+ ].join('\n');
45
+ }
46
+
47
+ function extractAuthorizationCode(input: string): string | null {
48
+ const trimmed = input.trim();
49
+ if (!trimmed) return null;
50
+
51
+ try {
52
+ const url = new URL(trimmed);
53
+ return url.searchParams.get('code');
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ function resolveLoginConfig(config: OAuthProviderConfig, useLocalCallback: boolean): OAuthProviderConfig {
60
+ if (!useLocalCallback || !config.localCallback) {
61
+ return config.manualRedirectUri
62
+ ? { ...config, redirectUri: config.manualRedirectUri }
63
+ : config;
64
+ }
65
+ return config;
66
+ }
67
+
68
+ function describePrecedence(record: Pick<ProviderSubscription, 'overrideAmbientApiKeys'>): string {
69
+ return record.overrideAmbientApiKeys
70
+ ? ' precedence: this now overrides ambient API keys for the provider'
71
+ : ' precedence: stored for subscription-backed flows only; ambient API keys are unchanged';
72
+ }
73
+
74
+ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry): void {
75
+ registry.register({
76
+ name: 'subscription',
77
+ aliases: ['subs'],
78
+ description: 'Manage provider subscription sessions and, when supported, let them override ambient API keys for matching providers',
79
+ usage: '[review|list|providers|inspect <provider>|login <provider> start [--no-browser] [--manual]|finish <code-or-url>|logout <provider>|bundle export <path>|bundle inspect <path>]',
80
+ async handler(args, ctx) {
81
+ const shellPaths = requireShellPaths(ctx);
82
+ if (args.length === 0 && ctx.openSubscriptionPanel) {
83
+ ctx.openSubscriptionPanel();
84
+ return;
85
+ }
86
+ const sub = (args[0] ?? 'review').toLowerCase();
87
+ const manager = requireSubscriptionManager(ctx);
88
+ const services = requireServiceRegistry(ctx);
89
+
90
+ if (sub === 'review' || sub === 'list') {
91
+ ctx.print(buildReviewText(ctx));
92
+ return;
93
+ }
94
+
95
+ if (sub === 'providers') {
96
+ const available = listAvailableSubscriptionProviders(services.getAll());
97
+ if (available.length === 0) {
98
+ ctx.print('No subscription-capable providers are currently configured or built in.');
99
+ return;
100
+ }
101
+ ctx.print([
102
+ 'Available Subscription Providers',
103
+ ...available.map((provider) => (
104
+ ` ${provider.provider} source=${provider.source} redirect=${provider.oauth.redirectUri}`
105
+ )),
106
+ ].join('\n'));
107
+ return;
108
+ }
109
+
110
+ if (sub === 'inspect') {
111
+ const provider = args[1];
112
+ if (!provider) {
113
+ ctx.print('Usage: /subscription inspect <provider>');
114
+ return;
115
+ }
116
+ const resolved = getSubscriptionProviderConfig(provider, services.get(provider));
117
+ if (!resolved && !manager.get(provider) && !manager.getPending(provider)) {
118
+ ctx.print(`No stored or available subscription provider named ${provider}.`);
119
+ return;
120
+ }
121
+ const inspection = await inspectProviderAuth(provider, {
122
+ serviceRegistry: services,
123
+ subscriptionManager: manager,
124
+ secretsManager: requireSecretsManager(ctx),
125
+ });
126
+ ctx.print([
127
+ `Subscription ${provider}`,
128
+ ` configured: ${inspection.configured ? 'yes' : 'no'}`,
129
+ ` freshness: ${inspection.freshness}`,
130
+ ` callbackMode: ${inspection.callbackMode}`,
131
+ ...(resolved ? [
132
+ ` source: ${resolved.source}`,
133
+ ` redirectUri: ${resolved.oauth.redirectUri}`,
134
+ ` authUrl: ${resolved.oauth.authUrl}`,
135
+ ` tokenUrl: ${resolved.oauth.tokenUrl}`,
136
+ ...(inspection.localCallback ? [` localCallback: ${inspection.localCallback}`] : []),
137
+ ] : []),
138
+ ...(inspection.activeSubscription ? [
139
+ ` authMode: ${manager.get(provider)?.authMode ?? 'oauth'}`,
140
+ ` tokenType: ${inspection.tokenType ?? 'n/a'}`,
141
+ ` createdAt: ${manager.get(provider)?.createdAt ? new Date(manager.get(provider)!.createdAt).toISOString() : 'n/a'}`,
142
+ ` updatedAt: ${manager.get(provider)?.updatedAt ? new Date(manager.get(provider)!.updatedAt).toISOString() : 'n/a'}`,
143
+ ` expiresAt: ${inspection.expiresAt ? new Date(inspection.expiresAt).toISOString() : 'n/a'}`,
144
+ ` refreshToken: ${manager.get(provider)?.refreshToken ? 'present' : 'absent'}`,
145
+ describePrecedence(manager.get(provider)!),
146
+ ] : [
147
+ ` state: ${inspection.freshness === 'pending' ? 'pending login' : 'available for login'}`,
148
+ ]),
149
+ ...inspection.issues.map((issue) => ` issue: ${issue}`),
150
+ ...inspection.nextActions.map((action) => ` next: ${action}`),
151
+ ].join('\n'));
152
+ return;
153
+ }
154
+
155
+ if (sub === 'login') {
156
+ const provider = args[1];
157
+ const mode = args[2]?.toLowerCase();
158
+ if (!provider || !mode) {
159
+ ctx.print('Usage: /subscription login <provider> start|finish <code>');
160
+ return;
161
+ }
162
+ const service = services.get(provider);
163
+ const resolved = getSubscriptionProviderConfig(provider, service);
164
+ if (!resolved) {
165
+ ctx.print([
166
+ `OAuth is not configured for ${provider}.`,
167
+ 'Add an oauth block to .goodvibes/agent/services.json for that provider, for example:',
168
+ ` { "name": "${provider}", "authType": "oauth", "tokenKey": "${provider.toUpperCase()}_API_KEY", "providerId": "${provider}", "oauth": { "authUrl": "...", "tokenUrl": "...", "clientId": "...", "redirectUri": "http://127.0.0.1/callback", "scopes": ["..."] } }`,
169
+ ].join('\n'));
170
+ return;
171
+ }
172
+ if (mode === 'start') {
173
+ const flags = new Set(args.slice(3));
174
+ const openBrowser = !flags.has('--no-browser');
175
+ const useManualMode = flags.has('--manual');
176
+ if (provider === 'openai' && resolved.source === 'builtin') {
177
+ const started = await beginOpenAICodexLogin();
178
+ manager.savePending({
179
+ provider,
180
+ state: started.state,
181
+ verifier: started.verifier,
182
+ redirectUri: started.redirectUri,
183
+ createdAt: Date.now(),
184
+ });
185
+
186
+ let listener: Awaited<ReturnType<typeof createOAuthLocalListener>> | null = null;
187
+ if (!useManualMode) {
188
+ try {
189
+ listener = await createOAuthLocalListener({
190
+ expectedState: started.state,
191
+ host: '127.0.0.1',
192
+ port: 1455,
193
+ path: '/auth/callback',
194
+ }).catch(() => null); // best-effort: listener creation is optional; null triggers manual flow
195
+ } catch {
196
+ listener = null;
197
+ }
198
+ }
199
+
200
+ const browserOpened = openBrowser
201
+ ? await openExternalUrl(started.authorizationUrl)
202
+ : false;
203
+
204
+ if (listener && browserOpened) {
205
+ try {
206
+ const callback = await listener.waitForCode();
207
+ const token = await exchangeOpenAICodexCode(callback.code, started.verifier);
208
+ const now = Date.now();
209
+ const record = manager.saveSubscription({
210
+ provider,
211
+ accessToken: token.accessToken,
212
+ refreshToken: token.refreshToken,
213
+ tokenType: token.tokenType,
214
+ expiresAt: token.expiresAt,
215
+ ...(token.scopes ? { scopes: token.scopes } : {}),
216
+ authMode: 'oauth',
217
+ overrideAmbientApiKeys: false,
218
+ createdAt: manager.get(provider)?.createdAt ?? now,
219
+ updatedAt: now,
220
+ });
221
+ ctx.print([
222
+ `Subscription OAuth Start: ${provider}`,
223
+ ` source: ${resolved.source}`,
224
+ ` state: ${started.state}`,
225
+ ` redirectUri: ${started.redirectUri}`,
226
+ ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
227
+ ' authorizationUrl:',
228
+ ` ${started.authorizationUrl}`,
229
+ `Subscription OAuth Complete: ${provider}`,
230
+ ` tokenType: ${record.tokenType}`,
231
+ ` expiresAt: ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
232
+ describePrecedence(record),
233
+ ].join('\n'));
234
+ return;
235
+ } catch (error) {
236
+ listener.close();
237
+ ctx.print([
238
+ `Subscription OAuth Start: ${provider}`,
239
+ ` source: ${resolved.source}`,
240
+ ` state: ${started.state}`,
241
+ ` redirectUri: ${started.redirectUri}`,
242
+ ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
243
+ ` next: /subscription login ${provider} finish <code-or-url>`,
244
+ ` listener: ${summarizeError(error)}`,
245
+ ' authorizationUrl:',
246
+ ` ${started.authorizationUrl}`,
247
+ ].join('\n'));
248
+ return;
249
+ }
250
+ }
251
+
252
+ listener?.close();
253
+ ctx.print([
254
+ `Subscription OAuth Start: ${provider}`,
255
+ ` source: ${resolved.source}`,
256
+ ` state: ${started.state}`,
257
+ ` redirectUri: ${started.redirectUri}`,
258
+ ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
259
+ ` next: /subscription login ${provider} finish <code-or-url>`,
260
+ ' authorizationUrl:',
261
+ ` ${started.authorizationUrl}`,
262
+ ].join('\n'));
263
+ return;
264
+ }
265
+ const useLocalCallback = Boolean(resolved.oauth.localCallback) && !flags.has('--manual');
266
+ let activeConfig = resolveLoginConfig(resolved.oauth, useLocalCallback);
267
+ let listener: Awaited<ReturnType<typeof createOAuthLocalListener>> | null = null;
268
+
269
+ if (useLocalCallback && resolved.oauth.localCallback) {
270
+ listener = await createOAuthLocalListener({
271
+ expectedState: '',
272
+ host: resolved.oauth.localCallback.host,
273
+ port: resolved.oauth.localCallback.port,
274
+ path: resolved.oauth.localCallback.path,
275
+ }).catch(() => null); // best-effort: local callback listener is optional; null falls back to manual redirect
276
+ }
277
+
278
+ if (listener) {
279
+ activeConfig = { ...activeConfig, redirectUri: listener.redirectUri };
280
+ }
281
+
282
+ const started = await manager.beginOAuthLogin(provider, activeConfig);
283
+ if (listener) {
284
+ listener.setExpectedState(started.pending.state);
285
+ }
286
+
287
+ const browserOpened = openBrowser
288
+ ? await openExternalUrl(started.authorizationUrl)
289
+ : false;
290
+
291
+ const shouldAutoComplete = Boolean(listener) && (resolved.oauth.localCallback?.autoComplete ?? true) && browserOpened;
292
+
293
+ if (shouldAutoComplete && listener) {
294
+ try {
295
+ const callback = await listener.waitForCode();
296
+ const record = await manager.completeOAuthLogin(provider, activeConfig, callback.code);
297
+ ctx.print([
298
+ `Subscription OAuth Start: ${provider}`,
299
+ ` source: ${resolved.source}`,
300
+ ` state: ${started.pending.state}`,
301
+ ` redirectUri: ${activeConfig.redirectUri}`,
302
+ ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
303
+ ' authorizationUrl:',
304
+ ` ${started.authorizationUrl}`,
305
+ `Subscription OAuth Complete: ${provider}`,
306
+ ` tokenType: ${record.tokenType}`,
307
+ ` expiresAt: ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
308
+ describePrecedence(record),
309
+ ].join('\n'));
310
+ return;
311
+ } catch (error) {
312
+ listener.close();
313
+ ctx.print([
314
+ `Subscription OAuth Start: ${provider}`,
315
+ ` source: ${resolved.source}`,
316
+ ` state: ${started.pending.state}`,
317
+ ` redirectUri: ${activeConfig.redirectUri}`,
318
+ ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
319
+ ` next: /subscription login ${provider} finish <code-or-url>`,
320
+ ` listener: ${summarizeError(error)}`,
321
+ ' authorizationUrl:',
322
+ ` ${started.authorizationUrl}`,
323
+ ].join('\n'));
324
+ return;
325
+ }
326
+ }
327
+
328
+ listener?.close();
329
+
330
+ ctx.print([
331
+ `Subscription OAuth Start: ${provider}`,
332
+ ` source: ${resolved.source}`,
333
+ ` state: ${started.pending.state}`,
334
+ ` redirectUri: ${activeConfig.redirectUri}`,
335
+ ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
336
+ ` next: /subscription login ${provider} finish <code-or-url>`,
337
+ ' authorizationUrl:',
338
+ ` ${started.authorizationUrl}`,
339
+ ].join('\n'));
340
+ return;
341
+ }
342
+ if (mode === 'finish') {
343
+ const codeInput = args[3];
344
+ if (!codeInput) {
345
+ ctx.print(`Usage: /subscription login ${provider} finish <code-or-url>`);
346
+ return;
347
+ }
348
+ const code = extractAuthorizationCode(codeInput) ?? codeInput;
349
+ if (provider === 'openai' && resolved.source === 'builtin') {
350
+ const pending = manager.getPending(provider);
351
+ if (!pending) {
352
+ ctx.print(`No pending OAuth login for ${provider}. Start with /subscription login ${provider} start.`);
353
+ return;
354
+ }
355
+ const token = await exchangeOpenAICodexCode(code, pending.verifier);
356
+ const now = Date.now();
357
+ const record = manager.saveSubscription({
358
+ provider,
359
+ accessToken: token.accessToken,
360
+ refreshToken: token.refreshToken,
361
+ tokenType: token.tokenType,
362
+ expiresAt: token.expiresAt,
363
+ ...(token.scopes ? { scopes: token.scopes } : {}),
364
+ authMode: 'oauth',
365
+ overrideAmbientApiKeys: false,
366
+ createdAt: manager.get(provider)?.createdAt ?? now,
367
+ updatedAt: now,
368
+ });
369
+ ctx.print([
370
+ `Stored subscription session for ${provider}.`,
371
+ ` tokenType: ${record.tokenType}`,
372
+ ` expiresAt: ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
373
+ describePrecedence(record),
374
+ ].join('\n'));
375
+ return;
376
+ }
377
+ const activeConfig = resolveLoginConfig(resolved.oauth, false);
378
+ const record = await manager.completeOAuthLogin(provider, activeConfig, code);
379
+ ctx.print([
380
+ `Stored subscription session for ${provider}.`,
381
+ ` tokenType: ${record.tokenType}`,
382
+ ` expiresAt: ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
383
+ describePrecedence(record),
384
+ ].join('\n'));
385
+ return;
386
+ }
387
+ ctx.print('Usage: /subscription login <provider> start|finish <code-or-url>');
388
+ return;
389
+ }
390
+
391
+ if (sub === 'logout') {
392
+ const provider = args[1];
393
+ if (!provider) {
394
+ ctx.print('Usage: /subscription logout <provider>');
395
+ return;
396
+ }
397
+ const removed = manager.logout(provider);
398
+ ctx.print(removed
399
+ ? `Logged out of ${provider}. Ambient API key resolution will apply again if configured.`
400
+ : `No stored subscription session existed for ${provider}.`);
401
+ return;
402
+ }
403
+
404
+ if (sub === 'bundle') {
405
+ const mode = args[1]?.toLowerCase();
406
+ const pathArg = args[2];
407
+ if (!mode || !pathArg) {
408
+ ctx.print('Usage: /subscription bundle <export|inspect> <path>');
409
+ return;
410
+ }
411
+ const targetPath = shellPaths.resolveWorkspacePath(pathArg);
412
+ if (mode === 'export') {
413
+ const bundle: SubscriptionBundle = {
414
+ version: 1,
415
+ exportedAt: Date.now(),
416
+ subscriptions: manager.list(),
417
+ };
418
+ mkdirSync(dirname(targetPath), { recursive: true });
419
+ writeFileSync(targetPath, `${JSON.stringify(bundle, null, 2)}\n`, 'utf-8');
420
+ ctx.print(`Subscription bundle exported to ${targetPath}`);
421
+ return;
422
+ }
423
+ if (mode === 'inspect') {
424
+ ctx.print(inspectBundle(targetPath));
425
+ return;
426
+ }
427
+ ctx.print('Usage: /subscription bundle <export|inspect> <path>');
428
+ return;
429
+ }
430
+
431
+ ctx.print('Usage: /subscription [review|list|providers|inspect <provider>|login <provider> start [--no-browser] [--manual]|finish <code-or-url>|logout <provider>|bundle export <path>|bundle inspect <path>]');
432
+ },
433
+ });
434
+ }
@@ -0,0 +1,230 @@
1
+ import type { CommandRegistry } from '../command-registry.ts';
2
+ import type { RuntimeTask, TaskLifecycleState } from '@/runtime/index.ts';
3
+ import { reviewWorktreeAttachments } from '@/runtime/index.ts';
4
+ import { requireOperatorClient, requireOpsApi, requirePanelManager, requireShellPaths } from './runtime-services.ts';
5
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
6
+
7
+ function sortRuntimeTasks(tasks: RuntimeTask[]): RuntimeTask[] {
8
+ const statusOrder: TaskLifecycleState[] = ['running', 'queued', 'blocked', 'failed', 'completed', 'cancelled'];
9
+ const ranking = new Map(statusOrder.map((status, index) => [status, index] as const));
10
+ return [...tasks].sort((a, b) => {
11
+ const rankDelta = (ranking.get(a.status) ?? 99) - (ranking.get(b.status) ?? 99);
12
+ if (rankDelta !== 0) return rankDelta;
13
+ const aWhen = a.startedAt ?? a.queuedAt;
14
+ const bWhen = b.startedAt ?? b.queuedAt;
15
+ return bWhen - aWhen;
16
+ });
17
+ }
18
+
19
+ function summarizeTaskResult(task: RuntimeTask): string {
20
+ const payload = (
21
+ typeof task.result === 'string'
22
+ ? task.result
23
+ : task.error
24
+ ?? (task.result !== undefined ? JSON.stringify(task.result) : task.description)
25
+ ?? task.title
26
+ );
27
+ const normalized = String(payload).replace(/\s+/g, ' ').trim();
28
+ return normalized.length <= 140 ? normalized : `${normalized.slice(0, 137)}...`;
29
+ }
30
+
31
+ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
32
+ registry.register({
33
+ name: 'tasks',
34
+ aliases: ['task'],
35
+ description: 'Inspect and control runtime tasks',
36
+ usage: '[list [status|kind] | show <taskId> | output <taskId> | create <kind> <owner> <title...> | update <taskId> <title|description|result> <value...> | complete <taskId> [result] | fail <taskId> <error...> | cancel <taskId> [note] | pause <taskId> [note] | resume <taskId> [note] | retry <taskId> [note]]',
37
+ handler(args, ctx) {
38
+ if (args.length === 0) {
39
+ if (ctx.showPanel) ctx.showPanel('tasks');
40
+ else {
41
+ const panelManager = requirePanelManager(ctx);
42
+ panelManager.open('tasks');
43
+ panelManager.show();
44
+ ctx.renderRequest();
45
+ }
46
+ return;
47
+ }
48
+
49
+ const operatorClient = requireOperatorClient(ctx);
50
+ const tasks = sortRuntimeTasks([...operatorClient.tasks.list(500)]);
51
+ const subcommand = args[0]?.toLowerCase() ?? 'list';
52
+
53
+ if (subcommand === 'list') {
54
+ const filter = args[1]?.toLowerCase();
55
+ const filtered = tasks.filter((task) => !filter || task.status === filter || task.kind === filter);
56
+ if (filtered.length === 0) {
57
+ ctx.print(filter ? `No tasks matched "${filter}".` : 'No tasks recorded yet.');
58
+ return;
59
+ }
60
+ ctx.print([
61
+ `Runtime Tasks (${filtered.length})`,
62
+ ...filtered.slice(0, 20).map((task) => ` ${task.id} ${task.status.padEnd(9)} ${task.kind.padEnd(11)} ${task.owner} ${task.title}`),
63
+ ].join('\n'));
64
+ return;
65
+ }
66
+
67
+ if (subcommand === 'show') {
68
+ const taskId = args[1];
69
+ if (!taskId) {
70
+ ctx.print('Usage: /tasks show <taskId>');
71
+ return;
72
+ }
73
+ const task = operatorClient.tasks.get(taskId);
74
+ if (!task) {
75
+ ctx.print(`Unknown task: ${taskId}`);
76
+ return;
77
+ }
78
+ ctx.print([
79
+ `Task ${task.id}`,
80
+ ` title: ${task.title}`,
81
+ ` kind: ${task.kind}`,
82
+ ` status: ${task.status}`,
83
+ ` owner: ${task.owner}`,
84
+ ` cancellable: ${task.cancellable ? 'yes' : 'no'}`,
85
+ ` queuedAt: ${new Date(task.queuedAt).toISOString()}`,
86
+ ` startedAt: ${task.startedAt ? new Date(task.startedAt).toISOString() : 'n/a'}`,
87
+ ` endedAt: ${task.endedAt ? new Date(task.endedAt).toISOString() : 'n/a'}`,
88
+ ` parent: ${task.parentTaskId ?? 'none'}`,
89
+ ` children: ${task.childTaskIds.join(', ') || '(none)'}`,
90
+ ` correlationId: ${task.correlationId ?? 'n/a'}`,
91
+ ...(() => {
92
+ const shellPaths = requireShellPaths(ctx);
93
+ const worktrees = reviewWorktreeAttachments('task', task.id, {
94
+ workingDirectory: shellPaths.workingDirectory,
95
+ });
96
+ return worktrees.total > 0
97
+ ? [
98
+ ` worktrees: ${worktrees.total} tracked (${worktrees.active} active / ${worktrees.paused} paused / ${worktrees.pendingCleanup} cleanup)`,
99
+ ` worktree next: /worktree task ${task.id}`,
100
+ ]
101
+ : [];
102
+ })(),
103
+ ` summary: ${summarizeTaskResult(task)}`,
104
+ ].join('\n'));
105
+ return;
106
+ }
107
+
108
+ if (subcommand === 'output') {
109
+ const taskId = args[1];
110
+ if (!taskId) {
111
+ ctx.print('Usage: /tasks output <taskId>');
112
+ return;
113
+ }
114
+ const task = operatorClient.tasks.get(taskId);
115
+ if (!task) {
116
+ ctx.print(`Unknown task: ${taskId}`);
117
+ return;
118
+ }
119
+ const payload = typeof task.result === 'string'
120
+ ? task.result
121
+ : task.result !== undefined
122
+ ? JSON.stringify(task.result, null, 2)
123
+ : task.error ?? task.description ?? task.title;
124
+ ctx.print(String(payload));
125
+ return;
126
+ }
127
+
128
+ if (subcommand === 'create') {
129
+ const opsApi = requireOpsApi(ctx);
130
+ const kind = args[1];
131
+ const owner = args[2];
132
+ const title = args.slice(3).join(' ').trim();
133
+ if (!kind || !owner || !title) {
134
+ ctx.print('Usage: /tasks create <kind> <owner> <title...>');
135
+ return;
136
+ }
137
+ const validKinds = new Set(['exec', 'agent', 'acp', 'scheduler', 'daemon', 'mcp', 'plugin', 'integration']);
138
+ if (!validKinds.has(kind)) {
139
+ ctx.print(`Unknown task kind: ${kind}`);
140
+ return;
141
+ }
142
+ const task = opsApi.tasks.create({
143
+ kind: kind as import('@/runtime/index.ts').TaskKind,
144
+ owner,
145
+ title,
146
+ description: title,
147
+ });
148
+ ctx.print(`Created task ${task.id} (${task.kind}) for ${task.owner}.`);
149
+ return;
150
+ }
151
+
152
+ if (subcommand === 'update') {
153
+ const opsApi = requireOpsApi(ctx);
154
+ const taskId = args[1];
155
+ const field = args[2];
156
+ const value = args.slice(3).join(' ').trim();
157
+ if (!taskId || !field || !value) {
158
+ ctx.print('Usage: /tasks update <taskId> <title|description|result> <value...>');
159
+ return;
160
+ }
161
+ if (field !== 'title' && field !== 'description' && field !== 'result') {
162
+ ctx.print(`Unsupported task update field: ${field}`);
163
+ return;
164
+ }
165
+ opsApi.tasks.update(taskId, field === 'result' ? { result: value } : { [field]: value });
166
+ ctx.print(`Updated task ${taskId} field ${field}.`);
167
+ return;
168
+ }
169
+
170
+ if (subcommand === 'complete') {
171
+ const opsApi = requireOpsApi(ctx);
172
+ const taskId = args[1];
173
+ if (!taskId) {
174
+ ctx.print('Usage: /tasks complete <taskId> [result]');
175
+ return;
176
+ }
177
+ const result = args.slice(2).join(' ').trim() || undefined;
178
+ opsApi.tasks.complete(taskId, result);
179
+ ctx.print(`Completed task ${taskId}.`);
180
+ return;
181
+ }
182
+
183
+ if (subcommand === 'fail') {
184
+ const opsApi = requireOpsApi(ctx);
185
+ const taskId = args[1];
186
+ const errorText = args.slice(2).join(' ').trim();
187
+ if (!taskId || !errorText) {
188
+ ctx.print('Usage: /tasks fail <taskId> <error...>');
189
+ return;
190
+ }
191
+ opsApi.tasks.fail(taskId, { error: errorText });
192
+ ctx.print(`Failed task ${taskId}.`);
193
+ return;
194
+ }
195
+
196
+ const taskId = args[1];
197
+ const note = args.slice(2).join(' ').trim() || undefined;
198
+ if (!taskId) {
199
+ ctx.print(`Usage: /tasks ${subcommand} <taskId> [note]`);
200
+ return;
201
+ }
202
+ const opsApi = requireOpsApi(ctx);
203
+ try {
204
+ switch (subcommand) {
205
+ case 'cancel':
206
+ opsApi.tasks.cancel(taskId, note);
207
+ ctx.print(`Cancelled task ${taskId}.`);
208
+ return;
209
+ case 'pause':
210
+ opsApi.tasks.pause(taskId, note);
211
+ ctx.print(`Paused task ${taskId}.`);
212
+ return;
213
+ case 'resume':
214
+ opsApi.tasks.resume(taskId, note);
215
+ ctx.print(`Resumed task ${taskId}.`);
216
+ return;
217
+ case 'retry':
218
+ opsApi.tasks.retry(taskId, note);
219
+ ctx.print(`Re-queued task ${taskId}.`);
220
+ return;
221
+ default:
222
+ ctx.print(`Unknown tasks subcommand: ${subcommand}`);
223
+ return;
224
+ }
225
+ } catch (error) {
226
+ ctx.print(summarizeError(error));
227
+ }
228
+ },
229
+ });
230
+ }