stagent 0.9.6 → 0.11.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 (396) hide show
  1. package/README.md +20 -44
  2. package/dist/cli.js +66 -18
  3. package/docs/.coverage-gaps.json +144 -56
  4. package/docs/.last-generated +1 -1
  5. package/docs/features/agent-intelligence.md +12 -2
  6. package/docs/features/chat.md +40 -5
  7. package/docs/features/cost-usage.md +1 -1
  8. package/docs/features/documents.md +5 -2
  9. package/docs/features/inbox-notifications.md +10 -2
  10. package/docs/features/keyboard-navigation.md +12 -3
  11. package/docs/features/provider-runtimes.md +20 -2
  12. package/docs/features/schedules.md +32 -4
  13. package/docs/features/settings.md +28 -5
  14. package/docs/features/shared-components.md +7 -3
  15. package/docs/features/tables.md +11 -2
  16. package/docs/features/tool-permissions.md +6 -2
  17. package/docs/features/workflows.md +14 -4
  18. package/docs/index.md +1 -1
  19. package/docs/journeys/developer.md +39 -2
  20. package/docs/journeys/personal-use.md +32 -8
  21. package/docs/journeys/power-user.md +45 -14
  22. package/docs/journeys/work-use.md +17 -8
  23. package/docs/manifest.json +15 -15
  24. package/docs/superpowers/plans/2026-04-07-instance-bootstrap.md +1691 -0
  25. package/docs/superpowers/plans/2026-04-08-schedule-orchestration.md +2983 -0
  26. package/docs/superpowers/plans/2026-04-11-schedule-maxturns-api-control.md +551 -0
  27. package/docs/superpowers/plans/2026-04-11-task-create-profile-validation.md +864 -0
  28. package/docs/superpowers/plans/2026-04-11-task-runtime-stagent-mcp-injection.md +739 -0
  29. package/docs/superpowers/plans/2026-04-14-chat-command-namespace-refactor.md +1390 -0
  30. package/docs/superpowers/plans/2026-04-14-chat-environment-integration.md +1561 -0
  31. package/docs/superpowers/plans/2026-04-14-chat-polish-bundle-v1.md +1219 -0
  32. package/docs/superpowers/plans/2026-04-14-chat-session-persistence-provider-closeout.md +399 -0
  33. package/docs/superpowers/specs/2026-04-08-chat-sse-resilience-hotfix-design.md +201 -0
  34. package/docs/superpowers/specs/2026-04-08-schedule-orchestration-design.md +371 -0
  35. package/docs/superpowers/specs/2026-04-08-swarm-visibility-design.md +213 -0
  36. package/next.config.mjs +1 -0
  37. package/package.json +3 -2
  38. package/src/__tests__/instrumentation-smoke.test.ts +15 -0
  39. package/src/app/analytics/page.tsx +1 -21
  40. package/src/app/api/chat/conversations/[id]/messages/route.ts +22 -1
  41. package/src/app/api/chat/conversations/[id]/skills/__tests__/activate.test.ts +141 -0
  42. package/src/app/api/chat/conversations/[id]/skills/activate/route.ts +74 -0
  43. package/src/app/api/chat/conversations/[id]/skills/deactivate/route.ts +33 -0
  44. package/src/app/api/chat/export/route.ts +52 -0
  45. package/src/app/api/chat/files/search/route.ts +50 -0
  46. package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
  47. package/src/app/api/environment/rescan-if-stale/__tests__/route.test.ts +45 -0
  48. package/src/app/api/environment/rescan-if-stale/route.ts +23 -0
  49. package/src/app/api/environment/skills/route.ts +13 -0
  50. package/src/app/api/instance/config/route.ts +41 -0
  51. package/src/app/api/instance/init/route.ts +34 -0
  52. package/src/app/api/instance/upgrade/check/route.ts +26 -0
  53. package/src/app/api/instance/upgrade/route.ts +96 -0
  54. package/src/app/api/instance/upgrade/status/route.ts +35 -0
  55. package/src/app/api/memory/route.ts +0 -11
  56. package/src/app/api/notifications/route.ts +4 -2
  57. package/src/app/api/projects/[id]/route.ts +5 -155
  58. package/src/app/api/projects/__tests__/delete-project.test.ts +10 -19
  59. package/src/app/api/schedules/[id]/execute/route.ts +111 -0
  60. package/src/app/api/schedules/[id]/route.ts +9 -1
  61. package/src/app/api/schedules/__tests__/execute-route.test.ts +118 -0
  62. package/src/app/api/schedules/route.ts +3 -12
  63. package/src/app/api/settings/chat/pins/route.ts +94 -0
  64. package/src/app/api/settings/chat/saved-searches/__tests__/route.test.ts +119 -0
  65. package/src/app/api/settings/chat/saved-searches/route.ts +79 -0
  66. package/src/app/api/settings/environment/route.ts +26 -0
  67. package/src/app/api/settings/openai/login/route.ts +22 -0
  68. package/src/app/api/settings/openai/logout/route.ts +7 -0
  69. package/src/app/api/settings/openai/route.ts +21 -1
  70. package/src/app/api/settings/providers/route.ts +35 -8
  71. package/src/app/api/tables/[id]/enrich/__tests__/route.test.ts +153 -0
  72. package/src/app/api/tables/[id]/enrich/plan/route.ts +98 -0
  73. package/src/app/api/tables/[id]/enrich/route.ts +147 -0
  74. package/src/app/api/tables/[id]/enrich/runs/route.ts +25 -0
  75. package/src/app/api/tasks/[id]/execute/route.ts +52 -33
  76. package/src/app/api/tasks/[id]/respond/route.ts +31 -15
  77. package/src/app/api/tasks/[id]/resume/route.ts +24 -3
  78. package/src/app/api/workflows/[id]/resume/route.ts +59 -0
  79. package/src/app/api/workflows/[id]/status/route.ts +22 -8
  80. package/src/app/api/workspace/context/route.ts +2 -0
  81. package/src/app/api/workspace/fix-data-dir/route.ts +81 -0
  82. package/src/app/chat/page.tsx +11 -0
  83. package/src/app/documents/page.tsx +4 -1
  84. package/src/app/inbox/page.tsx +12 -5
  85. package/src/app/layout.tsx +42 -21
  86. package/src/app/page.tsx +0 -2
  87. package/src/app/settings/page.tsx +8 -9
  88. package/src/components/chat/__tests__/capability-banner.test.tsx +38 -0
  89. package/src/components/chat/__tests__/chat-session-provider.test.tsx +573 -0
  90. package/src/components/chat/__tests__/skill-row.test.tsx +91 -0
  91. package/src/components/chat/capability-banner.tsx +68 -0
  92. package/src/components/chat/chat-command-popover.tsx +670 -49
  93. package/src/components/chat/chat-input.tsx +104 -10
  94. package/src/components/chat/chat-message.tsx +12 -3
  95. package/src/components/chat/chat-session-provider.tsx +790 -0
  96. package/src/components/chat/chat-shell.tsx +151 -401
  97. package/src/components/chat/command-tab-bar.tsx +68 -0
  98. package/src/components/chat/conversation-template-picker.tsx +421 -0
  99. package/src/components/chat/help-dialog.tsx +39 -0
  100. package/src/components/chat/skill-composition-conflict-dialog.tsx +96 -0
  101. package/src/components/chat/skill-row.tsx +147 -0
  102. package/src/components/documents/document-browser.tsx +37 -19
  103. package/src/components/instance/__tests__/instance-section.test.tsx +125 -0
  104. package/src/components/instance/instance-section.tsx +382 -0
  105. package/src/components/instance/upgrade-badge.tsx +219 -0
  106. package/src/components/notifications/__tests__/batch-proposal-review.test.tsx +95 -0
  107. package/src/components/notifications/__tests__/notification-item.test.tsx +106 -0
  108. package/src/components/notifications/__tests__/permission-response-actions.test.tsx +70 -0
  109. package/src/components/notifications/batch-proposal-review.tsx +20 -5
  110. package/src/components/notifications/inbox-list.tsx +11 -2
  111. package/src/components/notifications/notification-item.tsx +56 -2
  112. package/src/components/notifications/pending-approval-host.tsx +56 -37
  113. package/src/components/notifications/permission-response-actions.tsx +155 -1
  114. package/src/components/schedules/schedule-create-sheet.tsx +19 -1
  115. package/src/components/schedules/schedule-edit-sheet.tsx +20 -1
  116. package/src/components/schedules/schedule-form.tsx +31 -0
  117. package/src/components/settings/__tests__/providers-runtimes-section.test.tsx +149 -0
  118. package/src/components/settings/auth-method-selector.tsx +19 -4
  119. package/src/components/settings/auth-status-badge.tsx +28 -3
  120. package/src/components/settings/environment-section.tsx +102 -0
  121. package/src/components/settings/openai-chatgpt-auth-control.tsx +278 -0
  122. package/src/components/settings/openai-runtime-section.tsx +7 -1
  123. package/src/components/settings/providers-runtimes-section.tsx +138 -19
  124. package/src/components/shared/__tests__/filter-hint.test.tsx +40 -0
  125. package/src/components/shared/__tests__/saved-searches-manager.test.tsx +147 -0
  126. package/src/components/shared/app-sidebar.tsx +4 -3
  127. package/src/components/shared/command-palette.tsx +266 -7
  128. package/src/components/shared/filter-hint.tsx +70 -0
  129. package/src/components/shared/filter-input.tsx +59 -0
  130. package/src/components/shared/saved-searches-manager.tsx +199 -0
  131. package/src/components/shared/theme-toggle.tsx +5 -24
  132. package/src/components/shared/workspace-indicator.tsx +61 -2
  133. package/src/components/tables/__tests__/table-enrichment-sheet.test.tsx +130 -0
  134. package/src/components/tables/table-create-sheet.tsx +4 -0
  135. package/src/components/tables/table-enrichment-runs.tsx +103 -0
  136. package/src/components/tables/table-enrichment-sheet.tsx +538 -0
  137. package/src/components/tables/table-spreadsheet.tsx +29 -5
  138. package/src/components/tables/table-toolbar.tsx +10 -1
  139. package/src/components/tasks/kanban-board.tsx +1 -0
  140. package/src/components/tasks/kanban-column.tsx +53 -14
  141. package/src/components/tasks/task-bento-grid.tsx +31 -2
  142. package/src/components/tasks/task-card.tsx +29 -3
  143. package/src/components/tasks/task-chip-bar.tsx +54 -1
  144. package/src/components/tasks/task-result-renderer.tsx +1 -1
  145. package/src/components/workflows/delay-step-body.tsx +109 -0
  146. package/src/components/workflows/hooks/use-workflow-status.ts +50 -0
  147. package/src/components/workflows/loop-status-view.tsx +1 -1
  148. package/src/components/workflows/shared/step-result.tsx +78 -0
  149. package/src/components/workflows/shared/workflow-header.tsx +141 -0
  150. package/src/components/workflows/shared/workflow-loading-skeleton.tsx +36 -0
  151. package/src/components/workflows/swarm-dashboard.tsx +2 -15
  152. package/src/components/workflows/views/loop-pattern-view.tsx +137 -0
  153. package/src/components/workflows/views/sequence-pattern-view.tsx +511 -0
  154. package/src/components/workflows/workflow-form-view.tsx +133 -16
  155. package/src/components/workflows/workflow-status-view.tsx +30 -740
  156. package/src/hooks/__tests__/use-chat-autocomplete-tabs.test.ts +47 -0
  157. package/src/hooks/__tests__/use-saved-searches.test.ts +70 -0
  158. package/src/hooks/use-active-skills.ts +110 -0
  159. package/src/hooks/use-chat-autocomplete.ts +120 -7
  160. package/src/hooks/use-enriched-skills.ts +19 -0
  161. package/src/hooks/use-pinned-entries.ts +104 -0
  162. package/src/hooks/use-recent-user-messages.ts +19 -0
  163. package/src/hooks/use-saved-searches.ts +142 -0
  164. package/src/instrumentation-node.ts +94 -0
  165. package/src/instrumentation.ts +4 -48
  166. package/src/lib/agents/__tests__/claude-agent-sdk-options.test.ts +56 -0
  167. package/src/lib/agents/__tests__/claude-agent.test.ts +212 -0
  168. package/src/lib/agents/__tests__/execution-manager.test.ts +1 -27
  169. package/src/lib/agents/__tests__/failure-reason.test.ts +68 -0
  170. package/src/lib/agents/__tests__/learned-context.test.ts +0 -11
  171. package/src/lib/agents/__tests__/learning-session.test.ts +158 -0
  172. package/src/lib/agents/__tests__/pattern-extractor.test.ts +48 -0
  173. package/src/lib/agents/__tests__/task-dispatch.test.ts +166 -0
  174. package/src/lib/agents/__tests__/tool-permissions.test.ts +60 -0
  175. package/src/lib/agents/claude-agent.ts +217 -21
  176. package/src/lib/agents/execution-manager.ts +0 -35
  177. package/src/lib/agents/handoff/bus.ts +2 -2
  178. package/src/lib/agents/learned-context.ts +0 -12
  179. package/src/lib/agents/learning-session.ts +18 -5
  180. package/src/lib/agents/profiles/__tests__/list-fused-profiles.test.ts +110 -0
  181. package/src/lib/agents/profiles/__tests__/registry.test.ts +53 -4
  182. package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +97 -0
  183. package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +36 -0
  184. package/src/lib/agents/profiles/list-fused-profiles.ts +104 -0
  185. package/src/lib/agents/profiles/registry.ts +18 -0
  186. package/src/lib/agents/profiles/types.ts +7 -1
  187. package/src/lib/agents/router.ts +3 -6
  188. package/src/lib/agents/runtime/__tests__/catalog.test.ts +130 -0
  189. package/src/lib/agents/runtime/__tests__/execution-target.test.ts +183 -0
  190. package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
  191. package/src/lib/agents/runtime/anthropic-direct.ts +8 -0
  192. package/src/lib/agents/runtime/catalog.ts +121 -0
  193. package/src/lib/agents/runtime/claude-sdk.ts +32 -0
  194. package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
  195. package/src/lib/agents/runtime/execution-target.ts +456 -0
  196. package/src/lib/agents/runtime/index.ts +4 -0
  197. package/src/lib/agents/runtime/launch-failure.ts +101 -0
  198. package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
  199. package/src/lib/agents/runtime/openai-codex.ts +64 -60
  200. package/src/lib/agents/runtime/openai-direct.ts +8 -0
  201. package/src/lib/agents/runtime/types.ts +8 -0
  202. package/src/lib/agents/task-dispatch.ts +220 -0
  203. package/src/lib/agents/tool-permissions.ts +16 -1
  204. package/src/lib/book/chapter-mapping.ts +11 -0
  205. package/src/lib/book/content.ts +10 -0
  206. package/src/lib/chat/__tests__/active-skill-injection.test.ts +261 -0
  207. package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
  208. package/src/lib/chat/__tests__/clean-filter-input.test.ts +68 -0
  209. package/src/lib/chat/__tests__/command-tabs.test.ts +68 -0
  210. package/src/lib/chat/__tests__/context-builder-files.test.ts +112 -0
  211. package/src/lib/chat/__tests__/dismissals.test.ts +65 -0
  212. package/src/lib/chat/__tests__/engine-sdk-options.test.ts +117 -0
  213. package/src/lib/chat/__tests__/finalize-safety-net.test.ts +139 -0
  214. package/src/lib/chat/__tests__/reconcile.test.ts +137 -0
  215. package/src/lib/chat/__tests__/skill-conflict.test.ts +35 -0
  216. package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
  217. package/src/lib/chat/__tests__/types.test.ts +28 -0
  218. package/src/lib/chat/active-skills.ts +31 -0
  219. package/src/lib/chat/active-streams.ts +27 -0
  220. package/src/lib/chat/clean-filter-input.ts +30 -0
  221. package/src/lib/chat/codex-engine.ts +46 -24
  222. package/src/lib/chat/command-tabs.ts +61 -0
  223. package/src/lib/chat/context-builder.ts +146 -4
  224. package/src/lib/chat/dismissals.ts +73 -0
  225. package/src/lib/chat/engine.ts +159 -18
  226. package/src/lib/chat/files/__tests__/search.test.ts +135 -0
  227. package/src/lib/chat/files/expand-mention.ts +76 -0
  228. package/src/lib/chat/files/search.ts +99 -0
  229. package/src/lib/chat/reconcile.ts +117 -0
  230. package/src/lib/chat/skill-composition.ts +210 -0
  231. package/src/lib/chat/skill-conflict.ts +105 -0
  232. package/src/lib/chat/stagent-tools.ts +7 -19
  233. package/src/lib/chat/stream-telemetry.ts +137 -0
  234. package/src/lib/chat/suggested-prompts.ts +28 -1
  235. package/src/lib/chat/system-prompt.ts +48 -1
  236. package/src/lib/chat/tool-catalog.ts +35 -4
  237. package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
  238. package/src/lib/chat/tools/__tests__/profile-tools.test.ts +51 -0
  239. package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
  240. package/src/lib/chat/tools/__tests__/settings-tools.test.ts +294 -0
  241. package/src/lib/chat/tools/__tests__/skill-tools.test.ts +474 -0
  242. package/src/lib/chat/tools/__tests__/task-tools.test.ts +399 -0
  243. package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +351 -0
  244. package/src/lib/chat/tools/blueprint-tools.ts +190 -0
  245. package/src/lib/chat/tools/document-tools.ts +29 -13
  246. package/src/lib/chat/tools/helpers.ts +41 -0
  247. package/src/lib/chat/tools/notification-tools.ts +9 -5
  248. package/src/lib/chat/tools/profile-tools.ts +120 -23
  249. package/src/lib/chat/tools/project-tools.ts +33 -0
  250. package/src/lib/chat/tools/schedule-tools.ts +44 -11
  251. package/src/lib/chat/tools/skill-tools.ts +183 -0
  252. package/src/lib/chat/tools/table-tools.ts +71 -0
  253. package/src/lib/chat/tools/task-tools.ts +89 -21
  254. package/src/lib/chat/tools/workflow-tools.ts +275 -32
  255. package/src/lib/chat/types.ts +15 -0
  256. package/src/lib/constants/settings.ts +10 -18
  257. package/src/lib/data/__tests__/clear.test.ts +56 -2
  258. package/src/lib/data/clear.ts +17 -16
  259. package/src/lib/data/delete-project.ts +171 -0
  260. package/src/lib/db/__tests__/bootstrap.test.ts +1 -1
  261. package/src/lib/db/bootstrap.ts +62 -16
  262. package/src/lib/db/index.ts +5 -0
  263. package/src/lib/db/migrations/0009_add_app_instances.sql +25 -0
  264. package/src/lib/db/migrations/0024_add_workflow_resume_at.sql +10 -0
  265. package/src/lib/db/migrations/0025_drop_app_instances.sql +3 -0
  266. package/src/lib/db/migrations/0026_drop_license.sql +3 -0
  267. package/src/lib/db/migrations/meta/_journal.json +21 -0
  268. package/src/lib/db/schema.ts +94 -23
  269. package/src/lib/environment/__tests__/auto-promote.test.ts +132 -0
  270. package/src/lib/environment/__tests__/list-skills-enriched.test.ts +55 -0
  271. package/src/lib/environment/__tests__/skill-enrichment.test.ts +129 -0
  272. package/src/lib/environment/__tests__/skill-recommendations.test.ts +87 -0
  273. package/src/lib/environment/data.ts +9 -0
  274. package/src/lib/environment/list-skills.ts +176 -0
  275. package/src/lib/environment/parsers/__tests__/skill.test.ts +54 -0
  276. package/src/lib/environment/parsers/skill.ts +26 -5
  277. package/src/lib/environment/profile-generator.ts +54 -0
  278. package/src/lib/environment/skill-enrichment.ts +106 -0
  279. package/src/lib/environment/skill-recommendations.ts +66 -0
  280. package/src/lib/environment/workspace-context.ts +13 -1
  281. package/src/lib/filters/__tests__/parse.quoted.test.ts +40 -0
  282. package/src/lib/filters/__tests__/parse.test.ts +135 -0
  283. package/src/lib/filters/parse.ts +86 -0
  284. package/src/lib/import/dedup.ts +4 -54
  285. package/src/lib/instance/__tests__/bootstrap.test.ts +362 -0
  286. package/src/lib/instance/__tests__/detect.test.ts +115 -0
  287. package/src/lib/instance/__tests__/fingerprint.test.ts +48 -0
  288. package/src/lib/instance/__tests__/git-ops.test.ts +95 -0
  289. package/src/lib/instance/__tests__/settings.test.ts +83 -0
  290. package/src/lib/instance/__tests__/upgrade-poller.test.ts +181 -0
  291. package/src/lib/instance/bootstrap.ts +270 -0
  292. package/src/lib/instance/detect.ts +49 -0
  293. package/src/lib/instance/fingerprint.ts +76 -0
  294. package/src/lib/instance/git-ops.ts +95 -0
  295. package/src/lib/instance/settings.ts +61 -0
  296. package/src/lib/instance/types.ts +77 -0
  297. package/src/lib/instance/upgrade-poller.ts +205 -0
  298. package/src/lib/notifications/__tests__/visibility.test.ts +51 -0
  299. package/src/lib/notifications/visibility.ts +33 -0
  300. package/src/lib/schedules/__tests__/collision-check.test.ts +93 -0
  301. package/src/lib/schedules/__tests__/config.test.ts +62 -0
  302. package/src/lib/schedules/__tests__/firing-metrics.test.ts +99 -0
  303. package/src/lib/schedules/__tests__/integration.test.ts +82 -0
  304. package/src/lib/schedules/__tests__/slot-claim.test.ts +242 -0
  305. package/src/lib/schedules/__tests__/tick-scheduler.test.ts +102 -0
  306. package/src/lib/schedules/__tests__/turn-budget.test.ts +228 -0
  307. package/src/lib/schedules/collision-check.ts +105 -0
  308. package/src/lib/schedules/config.ts +53 -0
  309. package/src/lib/schedules/scheduler.ts +236 -17
  310. package/src/lib/schedules/slot-claim.ts +105 -0
  311. package/src/lib/settings/__tests__/openai-auth.test.ts +101 -0
  312. package/src/lib/settings/__tests__/openai-login-manager.test.ts +64 -0
  313. package/src/lib/settings/__tests__/runtime-setup.test.ts +33 -0
  314. package/src/lib/settings/openai-auth.ts +105 -10
  315. package/src/lib/settings/openai-login-manager.ts +260 -0
  316. package/src/lib/settings/runtime-setup.ts +14 -4
  317. package/src/lib/tables/__tests__/enrichment-planner.test.ts +124 -0
  318. package/src/lib/tables/__tests__/enrichment.test.ts +147 -0
  319. package/src/lib/tables/enrichment-planner.ts +454 -0
  320. package/src/lib/tables/enrichment.ts +328 -0
  321. package/src/lib/tables/query-builder.ts +5 -2
  322. package/src/lib/tables/trigger-evaluator.ts +3 -2
  323. package/src/lib/theme.ts +71 -0
  324. package/src/lib/usage/ledger.ts +2 -18
  325. package/src/lib/util/__tests__/similarity.test.ts +106 -0
  326. package/src/lib/util/similarity.ts +77 -0
  327. package/src/lib/utils/format-timestamp.ts +24 -0
  328. package/src/lib/utils/stagent-paths.ts +12 -0
  329. package/src/lib/validators/__tests__/blueprint.test.ts +172 -0
  330. package/src/lib/validators/__tests__/settings.test.ts +10 -0
  331. package/src/lib/validators/blueprint.ts +70 -9
  332. package/src/lib/validators/profile.ts +2 -2
  333. package/src/lib/validators/settings.ts +3 -1
  334. package/src/lib/workflows/__tests__/delay.test.ts +196 -0
  335. package/src/lib/workflows/__tests__/engine.test.ts +8 -0
  336. package/src/lib/workflows/__tests__/loop-executor.test.ts +54 -0
  337. package/src/lib/workflows/__tests__/post-action.test.ts +108 -0
  338. package/src/lib/workflows/blueprints/__tests__/render-prompt.test.ts +124 -0
  339. package/src/lib/workflows/blueprints/instantiator.ts +22 -1
  340. package/src/lib/workflows/blueprints/render-prompt.ts +71 -0
  341. package/src/lib/workflows/blueprints/types.ts +16 -2
  342. package/src/lib/workflows/delay.ts +106 -0
  343. package/src/lib/workflows/engine.ts +212 -7
  344. package/src/lib/workflows/loop-executor.ts +349 -24
  345. package/src/lib/workflows/post-action.ts +91 -0
  346. package/src/lib/workflows/types.ts +166 -1
  347. package/src/test/setup.ts +10 -0
  348. package/src/app/api/license/checkout/route.ts +0 -28
  349. package/src/app/api/license/portal/route.ts +0 -26
  350. package/src/app/api/license/route.ts +0 -89
  351. package/src/app/api/license/usage/route.ts +0 -63
  352. package/src/app/api/marketplace/browse/route.ts +0 -15
  353. package/src/app/api/marketplace/import/route.ts +0 -28
  354. package/src/app/api/marketplace/publish/route.ts +0 -40
  355. package/src/app/api/onboarding/email/route.ts +0 -53
  356. package/src/app/api/settings/telemetry/route.ts +0 -14
  357. package/src/app/api/sync/export/route.ts +0 -54
  358. package/src/app/api/sync/restore/route.ts +0 -37
  359. package/src/app/api/sync/sessions/route.ts +0 -24
  360. package/src/app/auth/callback/route.ts +0 -73
  361. package/src/app/marketplace/page.tsx +0 -19
  362. package/src/components/analytics/analytics-gate-card.tsx +0 -101
  363. package/src/components/marketplace/blueprint-card.tsx +0 -61
  364. package/src/components/marketplace/marketplace-browser.tsx +0 -131
  365. package/src/components/onboarding/email-capture-card.tsx +0 -104
  366. package/src/components/settings/activation-form.tsx +0 -95
  367. package/src/components/settings/cloud-account-section.tsx +0 -147
  368. package/src/components/settings/cloud-sync-section.tsx +0 -155
  369. package/src/components/settings/subscription-section.tsx +0 -410
  370. package/src/components/settings/telemetry-section.tsx +0 -80
  371. package/src/components/shared/premium-gate-overlay.tsx +0 -50
  372. package/src/components/shared/schedule-gate-dialog.tsx +0 -64
  373. package/src/components/shared/upgrade-banner.tsx +0 -112
  374. package/src/hooks/use-supabase-auth.ts +0 -79
  375. package/src/lib/billing/email.ts +0 -54
  376. package/src/lib/billing/products.ts +0 -80
  377. package/src/lib/billing/stripe.ts +0 -101
  378. package/src/lib/cloud/supabase-browser.ts +0 -32
  379. package/src/lib/cloud/supabase-client.ts +0 -56
  380. package/src/lib/license/__tests__/features.test.ts +0 -56
  381. package/src/lib/license/__tests__/key-format.test.ts +0 -88
  382. package/src/lib/license/__tests__/manager.test.ts +0 -64
  383. package/src/lib/license/__tests__/tier-limits.test.ts +0 -79
  384. package/src/lib/license/cloud-validation.ts +0 -60
  385. package/src/lib/license/features.ts +0 -44
  386. package/src/lib/license/key-format.ts +0 -101
  387. package/src/lib/license/limit-check.ts +0 -111
  388. package/src/lib/license/limit-queries.ts +0 -51
  389. package/src/lib/license/manager.ts +0 -345
  390. package/src/lib/license/notifications.ts +0 -59
  391. package/src/lib/license/tier-limits.ts +0 -71
  392. package/src/lib/marketplace/marketplace-client.ts +0 -107
  393. package/src/lib/sync/cloud-sync.ts +0 -235
  394. package/src/lib/telemetry/conversion-events.ts +0 -71
  395. package/src/lib/telemetry/queue.ts +0 -122
  396. package/src/lib/validators/license.ts +0 -33
@@ -0,0 +1,511 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { Button } from "@/components/ui/button";
7
+ import { Card, CardContent } from "@/components/ui/card";
8
+ import { Checkbox } from "@/components/ui/checkbox";
9
+ import {
10
+ CheckCircle,
11
+ Circle,
12
+ Loader2,
13
+ XCircle,
14
+ ShieldQuestion,
15
+ Clock3,
16
+ GitBranch,
17
+ MessageSquareMore,
18
+ FileText,
19
+ Paperclip,
20
+ ArrowRight,
21
+ } from "lucide-react";
22
+ import { toast } from "sonner";
23
+ import { SwarmDashboard } from "../swarm-dashboard";
24
+ import { WorkflowFullOutput } from "../workflow-full-output";
25
+ import { DelayStepBody } from "../delay-step-body";
26
+ import { WorkflowHeader } from "../shared/workflow-header";
27
+ import { ExpandableResult, DocumentList } from "../shared/step-result";
28
+ import type {
29
+ WorkflowStatusResponse,
30
+ WorkflowStatusDocument,
31
+ StepWithState,
32
+ } from "@/lib/workflows/types";
33
+
34
+ const stepStatusIcons: Record<string, React.ReactNode> = {
35
+ pending: <Circle className="h-4 w-4 text-muted-foreground" />,
36
+ running: <Loader2 className="h-4 w-4 text-status-running animate-spin" />,
37
+ completed: <CheckCircle className="h-4 w-4 text-status-completed" />,
38
+ failed: <XCircle className="h-4 w-4 text-destructive" />,
39
+ waiting_approval: <ShieldQuestion className="h-4 w-4 text-status-warning" />,
40
+ waiting_dependencies: <Clock3 className="h-4 w-4 text-status-warning" />,
41
+ delayed: <Clock3 className="h-4 w-4 text-status-warning" />,
42
+ };
43
+
44
+ /**
45
+ * Non-loop workflow subview. Consumes the sequence/parallel/swarm/
46
+ * planner-executor/checkpoint arm of the status API discriminated union.
47
+ * Renders either a sequential step list (with delay-step support), a parallel
48
+ * branches + synthesis layout, or delegates to SwarmDashboard — all three
49
+ * share the same `steps: StepWithState[]` shape, so branching stays inside
50
+ * this subview.
51
+ *
52
+ * Optimistic Execute/Rerun state is owned here so the Execute button can flip
53
+ * immediately without a round-trip through the router. The hook's `setData`
54
+ * is the mechanism for pushing optimistic state back into the shared polling
55
+ * cache; subsequent poll ticks will overwrite it with authoritative data.
56
+ *
57
+ * Per TDR-031, this subview never touches `.state` on anything except
58
+ * `StepWithState` (which by the union's type guarantees `.state` is present).
59
+ */
60
+ export function SequencePatternView({
61
+ data,
62
+ setData,
63
+ onRefresh,
64
+ onRequestDelete,
65
+ }: {
66
+ data: Extract<WorkflowStatusResponse, { pattern: Exclude<WorkflowStatusResponse["pattern"], "loop"> }>;
67
+ setData: (updater: (current: WorkflowStatusResponse | null) => WorkflowStatusResponse | null) => void;
68
+ onRefresh: () => Promise<void>;
69
+ onRequestDelete: () => void;
70
+ }) {
71
+ const [executing, setExecuting] = useState(false);
72
+
73
+ const handleExecute = useCallback(async () => {
74
+ setExecuting(true);
75
+ // Optimistic update — immediately show "active" status so the UI doesn't
76
+ // feel laggy while the POST and next poll tick complete.
77
+ setData((current) => {
78
+ if (!current || current.pattern === "loop") return current;
79
+ return {
80
+ ...current,
81
+ status: "active",
82
+ steps: current.steps.map((step, index): StepWithState => {
83
+ if (current.pattern === "swarm") {
84
+ const lastIndex = current.steps.length - 1;
85
+ return {
86
+ ...step,
87
+ state: {
88
+ ...step.state,
89
+ status:
90
+ index === 0
91
+ ? "running"
92
+ : index === lastIndex
93
+ ? "waiting_dependencies"
94
+ : "pending",
95
+ },
96
+ };
97
+ }
98
+ if (current.pattern === "parallel") {
99
+ const isJoin = !!step.dependsOn?.length;
100
+ return {
101
+ ...step,
102
+ state: {
103
+ ...step.state,
104
+ status: isJoin
105
+ ? "waiting_dependencies"
106
+ : index === 0
107
+ ? "running"
108
+ : "pending",
109
+ },
110
+ };
111
+ }
112
+ return index === 0
113
+ ? { ...step, state: { ...step.state, status: "running" } }
114
+ : step;
115
+ }),
116
+ };
117
+ });
118
+ try {
119
+ const res = await fetch(`/api/workflows/${data.id}/execute`, { method: "POST" });
120
+ if (res.ok) {
121
+ toast.success("Workflow started");
122
+ await onRefresh();
123
+ } else {
124
+ const err = await res.json().catch(() => null);
125
+ toast.error(err?.error ?? "Failed to start workflow");
126
+ await onRefresh(); // Revert optimistic update on failure
127
+ }
128
+ } finally {
129
+ setExecuting(false);
130
+ }
131
+ }, [data.id, onRefresh, setData]);
132
+
133
+ const handleRerun = useCallback(async () => {
134
+ setExecuting(true);
135
+ try {
136
+ const res = await fetch(`/api/workflows/${data.id}/execute`, { method: "POST" });
137
+ if (res.ok) {
138
+ toast.success("Workflow re-started");
139
+ await onRefresh();
140
+ } else {
141
+ const err = await res.json().catch(() => null);
142
+ toast.error(err?.error ?? "Failed to re-run workflow");
143
+ }
144
+ } finally {
145
+ setExecuting(false);
146
+ }
147
+ }, [data.id, onRefresh]);
148
+
149
+ // At this point on the non-loop arm, `state` is guaranteed present — no
150
+ // optional chaining needed. This is the AC that PR #6's optional chaining
151
+ // patched; the discriminated union makes the patch unnecessary.
152
+ const completedStepOutputs = data.steps
153
+ .filter((s) => s.state.result && s.state.status === "completed")
154
+ .map((s) => ({ name: s.name, result: s.state.result! }));
155
+
156
+ const hasStepDocs = !!data.stepDocuments && Object.keys(data.stepDocuments).length > 0;
157
+ const hasParentDocs = !!data.parentDocuments && data.parentDocuments.length > 0;
158
+
159
+ const parallelBranches =
160
+ data.pattern === "parallel"
161
+ ? data.steps.filter((step) => !step.dependsOn?.length)
162
+ : [];
163
+ const synthesisStep =
164
+ data.pattern === "parallel"
165
+ ? (data.steps.find((step) => step.dependsOn?.length) ?? null)
166
+ : null;
167
+
168
+ return (
169
+ <div className="space-y-6">
170
+ <Card>
171
+ <WorkflowHeader
172
+ data={data}
173
+ executing={executing}
174
+ canExecute={true}
175
+ onExecute={handleExecute}
176
+ onRerun={handleRerun}
177
+ onDelete={onRequestDelete}
178
+ />
179
+ <CardContent>
180
+ {data.pattern === "swarm" ? (
181
+ <SwarmDashboard
182
+ workflowId={data.id}
183
+ workflowStatus={data.status}
184
+ steps={data.steps}
185
+ swarmConfig={data.swarmConfig}
186
+ onRefresh={onRefresh}
187
+ stepStatusIcons={stepStatusIcons}
188
+ />
189
+ ) : (
190
+ <div className="space-y-4" aria-live="polite">
191
+ {data.pattern === "parallel" && parallelBranches.length > 0 ? (
192
+ <>
193
+ <section className="space-y-3">
194
+ <div className="flex items-center gap-2">
195
+ <GitBranch className="h-4 w-4 text-muted-foreground" />
196
+ <p className="text-sm font-medium">Parallel Branches</p>
197
+ <Badge variant="secondary" className="text-xs">
198
+ {parallelBranches.length}
199
+ </Badge>
200
+ </div>
201
+ <div className="grid gap-3 md:grid-cols-2">
202
+ {parallelBranches.map((step, index) => (
203
+ <div
204
+ key={step.id}
205
+ className="surface-card-muted rounded-lg border border-border/50 p-4"
206
+ >
207
+ <div className="flex items-start gap-3">
208
+ <div className="mt-0.5">
209
+ {stepStatusIcons[step.state.status] ?? stepStatusIcons.pending}
210
+ </div>
211
+ <div className="min-w-0 flex-1">
212
+ <div className="flex items-center gap-2">
213
+ <Badge variant="secondary" className="text-[11px]">
214
+ Branch {index + 1}
215
+ </Badge>
216
+ <span className="text-sm font-medium">{step.name}</span>
217
+ </div>
218
+ <p className="mt-1 text-xs text-muted-foreground line-clamp-2">
219
+ {step.prompt}
220
+ </p>
221
+ {step.state.error && (
222
+ <p className="mt-2 text-xs text-destructive">
223
+ {step.state.error}
224
+ </p>
225
+ )}
226
+ {step.state.result && step.state.status === "completed" && (
227
+ <ExpandableResult result={step.state.result} />
228
+ )}
229
+ {step.state.taskId && data.stepDocuments?.[step.state.taskId] && (
230
+ <DocumentList
231
+ docs={data.stepDocuments[step.state.taskId]}
232
+ label="Generated Files"
233
+ />
234
+ )}
235
+ </div>
236
+ </div>
237
+ </div>
238
+ ))}
239
+ </div>
240
+ </section>
241
+
242
+ {synthesisStep && (
243
+ <section className="space-y-3">
244
+ <div className="flex items-center gap-2">
245
+ <MessageSquareMore className="h-4 w-4 text-muted-foreground" />
246
+ <p className="text-sm font-medium">Synthesis Step</p>
247
+ </div>
248
+ <div className="surface-card-muted rounded-lg border border-border/50 p-4">
249
+ <div className="flex items-start gap-3">
250
+ <div className="mt-0.5">
251
+ {stepStatusIcons[synthesisStep.state.status] ??
252
+ stepStatusIcons.pending}
253
+ </div>
254
+ <div className="min-w-0 flex-1">
255
+ <div className="flex items-center gap-2">
256
+ <Badge variant="outline" className="text-[11px]">
257
+ join
258
+ </Badge>
259
+ <span className="text-sm font-medium">
260
+ {synthesisStep.name}
261
+ </span>
262
+ </div>
263
+ <p className="mt-1 text-xs text-muted-foreground line-clamp-2">
264
+ {synthesisStep.prompt}
265
+ </p>
266
+ <p className="mt-2 text-xs text-muted-foreground">
267
+ Waits for all {parallelBranches.length} branches before running.
268
+ </p>
269
+ {synthesisStep.state.error && (
270
+ <p className="mt-2 text-xs text-destructive">
271
+ {synthesisStep.state.error}
272
+ </p>
273
+ )}
274
+ {synthesisStep.state.result &&
275
+ synthesisStep.state.status === "completed" && (
276
+ <ExpandableResult result={synthesisStep.state.result} />
277
+ )}
278
+ {synthesisStep.state.taskId && data.stepDocuments?.[synthesisStep.state.taskId] && (
279
+ <DocumentList
280
+ docs={data.stepDocuments[synthesisStep.state.taskId]}
281
+ label="Generated Files"
282
+ />
283
+ )}
284
+ </div>
285
+ </div>
286
+ </div>
287
+ </section>
288
+ )}
289
+ </>
290
+ ) : (
291
+ <div className="space-y-3">
292
+ {data.steps.map((step, index) => {
293
+ const isDelayStep = !!step.delayDuration;
294
+ const isActiveDelay = isDelayStep && step.state.status === "delayed";
295
+ return (
296
+ <div key={`${step.id}-${index}`} className="flex items-start gap-3">
297
+ <div className="mt-0.5 flex flex-col items-center">
298
+ {isDelayStep && step.state.status === "pending" ? (
299
+ <Clock3 className="h-4 w-4 text-muted-foreground" />
300
+ ) : (
301
+ stepStatusIcons[step.state.status] ?? stepStatusIcons.pending
302
+ )}
303
+ {index < data.steps.length - 1 && (
304
+ <div className="mt-1 h-6 w-px bg-border" />
305
+ )}
306
+ </div>
307
+ <div className="flex-1 min-w-0">
308
+ <div className="flex items-center gap-2">
309
+ <span className="text-sm font-medium">{step.name}</span>
310
+ {isDelayStep && (
311
+ <Badge variant="secondary" className="text-[10px] uppercase tracking-wide">
312
+ Delay
313
+ </Badge>
314
+ )}
315
+ {step.requiresApproval && !isDelayStep && (
316
+ <Badge variant="outline" className="text-xs">
317
+ checkpoint
318
+ </Badge>
319
+ )}
320
+ </div>
321
+ {isDelayStep ? (
322
+ <DelayStepBody
323
+ workflowId={data.id}
324
+ delayDuration={step.delayDuration!}
325
+ stepStatus={step.state.status}
326
+ resumeAt={isActiveDelay ? data.resumeAt ?? null : null}
327
+ />
328
+ ) : (
329
+ <div className="flex items-center gap-1.5 mt-0.5">
330
+ <p className="text-xs text-muted-foreground truncate">
331
+ {step.prompt.slice(0, 100)}
332
+ {step.prompt.length > 100 ? "..." : ""}
333
+ </p>
334
+ {step.state.taskId && (
335
+ <a
336
+ href={`/tasks/${step.state.taskId}`}
337
+ className="text-[10px] text-primary hover:underline shrink-0"
338
+ >
339
+ view task
340
+ </a>
341
+ )}
342
+ </div>
343
+ )}
344
+ {hasParentDocs && index === 0 && !isDelayStep && (
345
+ <div className="flex items-center gap-1 mt-1">
346
+ <Badge variant="outline" className="text-[10px] px-1.5 py-0">
347
+ {data.parentDocuments!.length} doc{data.parentDocuments!.length !== 1 ? "s" : ""} attached
348
+ </Badge>
349
+ </div>
350
+ )}
351
+ {step.state.error && (
352
+ <p className="text-xs text-destructive mt-1">
353
+ {step.state.error}
354
+ </p>
355
+ )}
356
+ {step.state.result && step.state.status === "completed" && !isDelayStep && (
357
+ <ExpandableResult result={step.state.result} />
358
+ )}
359
+ {step.state.taskId && data.stepDocuments?.[step.state.taskId] && (
360
+ <DocumentList
361
+ docs={data.stepDocuments[step.state.taskId]}
362
+ label="Generated Files"
363
+ />
364
+ )}
365
+ </div>
366
+ </div>
367
+ );
368
+ })}
369
+ </div>
370
+ )}
371
+ </div>
372
+ )}
373
+
374
+ {(hasParentDocs || hasStepDocs) && (
375
+ <div className="mt-6 pt-4 border-t border-border/50">
376
+ <div className="flex items-center gap-2 mb-3">
377
+ <Paperclip className="h-4 w-4 text-muted-foreground" />
378
+ <p className="text-sm font-medium">Documents</p>
379
+ </div>
380
+ {hasParentDocs && (
381
+ <DocumentList docs={data.parentDocuments!} label="Input Files" />
382
+ )}
383
+ {hasStepDocs &&
384
+ Object.entries(data.stepDocuments!).map(([taskId, docs]) => {
385
+ const step = data.steps.find((s) => s.state.taskId === taskId);
386
+ return (
387
+ <DocumentList
388
+ key={taskId}
389
+ docs={docs}
390
+ label={step ? `Output: ${step.name}` : "Output Files"}
391
+ />
392
+ );
393
+ })}
394
+ </div>
395
+ )}
396
+ </CardContent>
397
+ </Card>
398
+
399
+ {data.status === "completed" && completedStepOutputs.length > 0 && (
400
+ <WorkflowFullOutput workflowName={data.name} steps={completedStepOutputs} />
401
+ )}
402
+
403
+ {data.status === "completed" && hasStepDocs && (
404
+ <OutputDock stepDocuments={data.stepDocuments!} steps={data.steps} />
405
+ )}
406
+ </div>
407
+ );
408
+ }
409
+
410
+ /** Output Dock — selectable output documents for chaining into a new workflow */
411
+ function OutputDock({
412
+ stepDocuments,
413
+ steps,
414
+ }: {
415
+ stepDocuments: Record<string, WorkflowStatusDocument[]>;
416
+ steps: StepWithState[];
417
+ }) {
418
+ const router = useRouter();
419
+ const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
420
+
421
+ const allOutputDocs = Object.entries(stepDocuments).flatMap(([taskId, docs]) => {
422
+ const step = steps.find((s) => s.state.taskId === taskId);
423
+ return docs.map((doc) => ({
424
+ ...doc,
425
+ stepName: step?.name ?? "Unknown Step",
426
+ }));
427
+ });
428
+
429
+ if (allOutputDocs.length === 0) return null;
430
+
431
+ function toggleDoc(id: string) {
432
+ setSelectedIds((prev) => {
433
+ const next = new Set(prev);
434
+ if (next.has(id)) next.delete(id);
435
+ else next.add(id);
436
+ return next;
437
+ });
438
+ }
439
+
440
+ function selectAll() {
441
+ setSelectedIds(new Set(allOutputDocs.map((d) => d.id)));
442
+ }
443
+
444
+ function chainIntoNewWorkflow() {
445
+ if (selectedIds.size === 0) return;
446
+ const params = new URLSearchParams({ inputDocs: [...selectedIds].join(",") });
447
+ router.push(`/workflows/new?${params}`);
448
+ }
449
+
450
+ return (
451
+ <Card>
452
+ <div className="px-6 pt-6 pb-3">
453
+ <div className="flex items-center justify-between">
454
+ <div className="flex items-center gap-2 text-sm font-medium">
455
+ <ArrowRight className="h-4 w-4" />
456
+ Chain Output Documents
457
+ </div>
458
+ <Button variant="ghost" size="sm" onClick={selectAll} className="text-xs">
459
+ Select All
460
+ </Button>
461
+ </div>
462
+ <p className="text-xs text-muted-foreground mt-1">
463
+ Select output documents to use as inputs in a new workflow
464
+ </p>
465
+ </div>
466
+ <CardContent>
467
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-2 mb-4">
468
+ {allOutputDocs.map((doc) => {
469
+ const isChecked = selectedIds.has(doc.id);
470
+ return (
471
+ <div
472
+ key={doc.id}
473
+ role="button"
474
+ tabIndex={0}
475
+ onClick={() => toggleDoc(doc.id)}
476
+ onKeyDown={(e) => {
477
+ if (e.key === "Enter" || e.key === " ") {
478
+ e.preventDefault();
479
+ toggleDoc(doc.id);
480
+ }
481
+ }}
482
+ className={`flex items-center gap-3 p-3 rounded-lg text-left transition-colors border cursor-pointer ${
483
+ isChecked
484
+ ? "bg-accent/50 border-accent"
485
+ : "hover:bg-muted/50 border-border/50"
486
+ }`}
487
+ >
488
+ <Checkbox
489
+ checked={isChecked}
490
+ onCheckedChange={() => toggleDoc(doc.id)}
491
+ />
492
+ <FileText className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
493
+ <div className="flex-1 min-w-0">
494
+ <p className="text-sm font-medium truncate">{doc.originalName}</p>
495
+ <p className="text-xs text-muted-foreground truncate">{doc.stepName}</p>
496
+ </div>
497
+ </div>
498
+ );
499
+ })}
500
+ </div>
501
+
502
+ {selectedIds.size > 0 && (
503
+ <Button onClick={chainIntoNewWorkflow} className="w-full gap-2" size="sm">
504
+ <ArrowRight className="h-4 w-4" />
505
+ Chain {selectedIds.size} Document{selectedIds.size !== 1 ? "s" : ""} Into New Workflow
506
+ </Button>
507
+ )}
508
+ </CardContent>
509
+ </Card>
510
+ );
511
+ }