sea-dev 1.0.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 (784) hide show
  1. package/.claude/tasks/README.md +89 -0
  2. package/.cursor/rules/commits.mdc +31 -0
  3. package/.cursor/rules/general.mdc +84 -0
  4. package/.github/workflows/ci-cd.yml +141 -0
  5. package/CLAUDE.md +337 -0
  6. package/README.md +129 -0
  7. package/apps/api/.prettierignore +6 -0
  8. package/apps/api/.prettierrc.js +3 -0
  9. package/apps/api/dotenvx-safe.sh +11 -0
  10. package/apps/api/eslint.config.mjs +3 -0
  11. package/apps/api/package.json +58 -0
  12. package/apps/api/src/clients/posthog.ts +25 -0
  13. package/apps/api/src/dal/submission.ts +59 -0
  14. package/apps/api/src/errors.ts +55 -0
  15. package/apps/api/src/index.ts +21 -0
  16. package/apps/api/src/lib/channel.ts +28 -0
  17. package/apps/api/src/lib/config.ts +9 -0
  18. package/apps/api/src/lib/fmt.test.ts +9 -0
  19. package/apps/api/src/lib/fmt.ts +62 -0
  20. package/apps/api/src/lib/invariant.ts +23 -0
  21. package/apps/api/src/middleware/auth.ts +66 -0
  22. package/apps/api/src/routes/index.ts +20 -0
  23. package/apps/api/src/routes/v2/chat/handlers.ts +693 -0
  24. package/apps/api/src/routes/v2/chat/index.ts +257 -0
  25. package/apps/api/src/routes/v2/chat/schemas.ts +43 -0
  26. package/apps/api/src/routes/v2/deals/handlers.ts +64 -0
  27. package/apps/api/src/routes/v2/deals/index.ts +88 -0
  28. package/apps/api/src/routes/v2/deals/schemas.ts +38 -0
  29. package/apps/api/src/routes/v2/forms/handlers.ts +415 -0
  30. package/apps/api/src/routes/v2/forms/index.ts +382 -0
  31. package/apps/api/src/routes/v2/forms/schemas.ts +243 -0
  32. package/apps/api/src/routes/v2/index.ts +19 -0
  33. package/apps/api/src/routes/v2/pipelines/handlers.ts +261 -0
  34. package/apps/api/src/routes/v2/pipelines/index.ts +224 -0
  35. package/apps/api/src/routes/v2/pipelines/schemas.ts +173 -0
  36. package/apps/api/src/routes/v2/submissions/handlers.ts +555 -0
  37. package/apps/api/src/routes/v2/submissions/index.ts +366 -0
  38. package/apps/api/src/routes/v2/submissions/schemas.ts +233 -0
  39. package/apps/api/src/routes/v2/workflows/handlers.ts +81 -0
  40. package/apps/api/src/routes/v2/workflows/index.ts +88 -0
  41. package/apps/api/src/routes/v2/workflows/schemas.ts +40 -0
  42. package/apps/api/src/server.ts +146 -0
  43. package/apps/api/src/static/favicon.ico +0 -0
  44. package/apps/api/src/types/api.ts +14 -0
  45. package/apps/api/src/types/result.ts +3 -0
  46. package/apps/api/tsconfig.json +22 -0
  47. package/apps/api/vite.config.ts +28 -0
  48. package/apps/api/vitest.config.ts +14 -0
  49. package/apps/conversion-worker/Dockerfile +59 -0
  50. package/apps/conversion-worker/package.json +31 -0
  51. package/apps/conversion-worker/src/lib/config.ts +7 -0
  52. package/apps/conversion-worker/src/main.ts +22 -0
  53. package/apps/conversion-worker/src/workflows/convert-pptx.ts +116 -0
  54. package/apps/conversion-worker/tsconfig.json +27 -0
  55. package/apps/conversion-worker/vite.config.ts +33 -0
  56. package/apps/main/.prettierignore +6 -0
  57. package/apps/main/.prettierrc.js +3 -0
  58. package/apps/main/CLAUDE.md +245 -0
  59. package/apps/main/Procfile +1 -0
  60. package/apps/main/README.md +193 -0
  61. package/apps/main/db-tests.jsonl +116 -0
  62. package/apps/main/dotenvx-safe.sh +11 -0
  63. package/apps/main/drizzle/meta/_journal.json +1 -0
  64. package/apps/main/drizzle.config.ts +25 -0
  65. package/apps/main/eslint.config.mjs +3 -0
  66. package/apps/main/generate-routes.mjs +5 -0
  67. package/apps/main/package.json +131 -0
  68. package/apps/main/playwright.config.ts +23 -0
  69. package/apps/main/postcss.config.ts +5 -0
  70. package/apps/main/public/bg-dark.svg +10 -0
  71. package/apps/main/public/bg.svg +10 -0
  72. package/apps/main/public/favicon.ico +0 -0
  73. package/apps/main/run.sh +146 -0
  74. package/apps/main/scripts/browser.ts +14 -0
  75. package/apps/main/scripts/db-test-cov.ts +277 -0
  76. package/apps/main/scripts/login.ts +78 -0
  77. package/apps/main/scripts/repl.ts +61 -0
  78. package/apps/main/src/_foo.ts +31 -0
  79. package/apps/main/src/_tests/db.test.ts +19 -0
  80. package/apps/main/src/_tests/mock-db.ts +60 -0
  81. package/apps/main/src/client.tsx +13 -0
  82. package/apps/main/src/clients/loops.ts +13 -0
  83. package/apps/main/src/clients/polar.ts +12 -0
  84. package/apps/main/src/clients/posthog.ts +12 -0
  85. package/apps/main/src/components/chat/chat-context.tsx +99 -0
  86. package/apps/main/src/components/chat/chat-messages.tsx +184 -0
  87. package/apps/main/src/components/chat/chat-status.tsx +140 -0
  88. package/apps/main/src/components/chat/chat.tsx +458 -0
  89. package/apps/main/src/components/chat/citation-modal.tsx +54 -0
  90. package/apps/main/src/components/cta.tsx +21 -0
  91. package/apps/main/src/components/data-display/derived.tsx +40 -0
  92. package/apps/main/src/components/data-display/group-single.tsx +57 -0
  93. package/apps/main/src/components/data-display/group-table.tsx +165 -0
  94. package/apps/main/src/components/data-display/group-wrapper.tsx +54 -0
  95. package/apps/main/src/components/data-display/item.tsx +678 -0
  96. package/apps/main/src/components/error.tsx +45 -0
  97. package/apps/main/src/components/forms/error.tsx +22 -0
  98. package/apps/main/src/components/grid.tsx +7 -0
  99. package/apps/main/src/components/header/container.tsx +73 -0
  100. package/apps/main/src/components/header/header-bar.tsx +102 -0
  101. package/apps/main/src/components/modals/copy-display.tsx +37 -0
  102. package/apps/main/src/components/modals/copy-form.tsx +152 -0
  103. package/apps/main/src/components/modals/duplicate-workflow.tsx +89 -0
  104. package/apps/main/src/components/modals/field-correction.tsx +323 -0
  105. package/apps/main/src/components/modals/form-viewer.tsx +126 -0
  106. package/apps/main/src/components/modals/modals.tsx +44 -0
  107. package/apps/main/src/components/modals/new-deal.tsx +78 -0
  108. package/apps/main/src/components/modals/new-form.tsx +133 -0
  109. package/apps/main/src/components/modals/new-pipeline.tsx +70 -0
  110. package/apps/main/src/components/modals/new-submission.tsx +321 -0
  111. package/apps/main/src/components/modals/new-workflow.tsx +342 -0
  112. package/apps/main/src/components/modals/transformation-sources-modal.tsx +157 -0
  113. package/apps/main/src/components/modals/view-report.tsx +193 -0
  114. package/apps/main/src/components/not-found.tsx +14 -0
  115. package/apps/main/src/components/search/search-bar.tsx +178 -0
  116. package/apps/main/src/components/sheet-selector.tsx +135 -0
  117. package/apps/main/src/components/side-panel/doc-list.tsx +480 -0
  118. package/apps/main/src/components/sidebar/admin-sidebar.tsx +75 -0
  119. package/apps/main/src/components/sidebar/app-sidebar.tsx +417 -0
  120. package/apps/main/src/components/sidebar/model-select.tsx +134 -0
  121. package/apps/main/src/components/sidebar/settings-sidebar.tsx +132 -0
  122. package/apps/main/src/components/sidebar/sidebar-right.tsx +22 -0
  123. package/apps/main/src/components/sidebar/stop-impersonate.tsx +21 -0
  124. package/apps/main/src/components/svg/loading.tsx +33 -0
  125. package/apps/main/src/components/theme-selector.tsx +43 -0
  126. package/apps/main/src/components/unsaved-badge.tsx +19 -0
  127. package/apps/main/src/components/upload/file-upload.tsx +354 -0
  128. package/apps/main/src/fns/submission-groups.ts +28 -0
  129. package/apps/main/src/fns/submission-items.ts +11 -0
  130. package/apps/main/src/global-middleware.ts +16 -0
  131. package/apps/main/src/hooks/use-update-state.ts +18 -0
  132. package/apps/main/src/lib/auth-client.ts +16 -0
  133. package/apps/main/src/lib/auth.test.ts +359 -0
  134. package/apps/main/src/lib/auth.ts +144 -0
  135. package/apps/main/src/lib/billing.ts +23 -0
  136. package/apps/main/src/lib/config-iso.ts +76 -0
  137. package/apps/main/src/lib/config.ts +61 -0
  138. package/apps/main/src/lib/excel.ts +16 -0
  139. package/apps/main/src/lib/feedback-cache.ts +70 -0
  140. package/apps/main/src/lib/logger.ts +44 -0
  141. package/apps/main/src/lib/models.ts +22 -0
  142. package/apps/main/src/lib/not-found.ts +17 -0
  143. package/apps/main/src/lib/pdf.ts +16 -0
  144. package/apps/main/src/lib/tabularize.ts +54 -0
  145. package/apps/main/src/lib/utils.ts +10 -0
  146. package/apps/main/src/lib/zfd.ts +217 -0
  147. package/apps/main/src/middleware.ts +55 -0
  148. package/apps/main/src/routeTree.gen.ts +1255 -0
  149. package/apps/main/src/router.tsx +24 -0
  150. package/apps/main/src/routes/__root.tsx +92 -0
  151. package/apps/main/src/routes/_authed/_app/(dashboard)/index.tsx +227 -0
  152. package/apps/main/src/routes/_authed/_app/agents/$agentId/config.tsx +224 -0
  153. package/apps/main/src/routes/_authed/_app/agents/$agentId/index.tsx +206 -0
  154. package/apps/main/src/routes/_authed/_app/agents/-components/agent-actions-menu.tsx +94 -0
  155. package/apps/main/src/routes/_authed/_app/agents/-components/agent-artifacts.tsx +153 -0
  156. package/apps/main/src/routes/_authed/_app/agents/-components/agent-chat.tsx +220 -0
  157. package/apps/main/src/routes/_authed/_app/agents/-components/agent-history-menu.tsx +81 -0
  158. package/apps/main/src/routes/_authed/_app/agents/-components/agent-model-select.tsx +84 -0
  159. package/apps/main/src/routes/_authed/_app/agents/-components/agent-relevant-items.tsx +226 -0
  160. package/apps/main/src/routes/_authed/_app/agents/-components/agent-upload-button.tsx +298 -0
  161. package/apps/main/src/routes/_authed/_app/agents/-components/context-modal.tsx +187 -0
  162. package/apps/main/src/routes/_authed/_app/agents/-fns.ts +560 -0
  163. package/apps/main/src/routes/_authed/_app/agents/index.tsx +65 -0
  164. package/apps/main/src/routes/_authed/_app/deals/$dealId/$subId/-components/citation-tree.tsx +268 -0
  165. package/apps/main/src/routes/_authed/_app/deals/$dealId/$subId.tsx +655 -0
  166. package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/doc-loading.tsx +37 -0
  167. package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/share-link.tsx +42 -0
  168. package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submission-card.tsx +89 -0
  169. package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submission-filter.tsx +193 -0
  170. package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submissions.tsx +36 -0
  171. package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/summary.tsx +82 -0
  172. package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/upload-doc.tsx +120 -0
  173. package/apps/main/src/routes/_authed/_app/deals/$dealId/-fns.ts +653 -0
  174. package/apps/main/src/routes/_authed/_app/deals/$dealId/index.tsx +259 -0
  175. package/apps/main/src/routes/_authed/_app/deals/$dealId/route.tsx +29 -0
  176. package/apps/main/src/routes/_authed/_app/deals/index.tsx +104 -0
  177. package/apps/main/src/routes/_authed/_app/feedback/index.tsx +639 -0
  178. package/apps/main/src/routes/_authed/_app/feedback/insights.tsx +250 -0
  179. package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/blockers-panel.tsx +260 -0
  180. package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/manual-input-panel.tsx +301 -0
  181. package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/submission-selector-modal.tsx +143 -0
  182. package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/upload-doc.tsx +120 -0
  183. package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/index.tsx +1485 -0
  184. package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/-components/dag-view.tsx +296 -0
  185. package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/-components/step-config-modal.tsx +634 -0
  186. package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/index.tsx +911 -0
  187. package/apps/main/src/routes/_authed/_app/pipelines/-fns.ts +510 -0
  188. package/apps/main/src/routes/_authed/_app/pipelines/index.tsx +103 -0
  189. package/apps/main/src/routes/_authed/_app/reports/$reportId.tsx +397 -0
  190. package/apps/main/src/routes/_authed/_app/reports/-fns.ts +11 -0
  191. package/apps/main/src/routes/_authed/_app/reports/index.tsx +22 -0
  192. package/apps/main/src/routes/_authed/_app/route.tsx +48 -0
  193. package/apps/main/src/routes/_authed/_app/submissions/-columns.tsx +161 -0
  194. package/apps/main/src/routes/_authed/_app/submissions/-fns.ts +128 -0
  195. package/apps/main/src/routes/_authed/_app/submissions/index.tsx +190 -0
  196. package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/$formId.tsx +542 -0
  197. package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/derived.tsx +154 -0
  198. package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/field.tsx +369 -0
  199. package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/group.tsx +475 -0
  200. package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/index.tsx +263 -0
  201. package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/route.tsx +33 -0
  202. package/apps/main/src/routes/_authed/_app/workflows/-components/form-card.tsx +315 -0
  203. package/apps/main/src/routes/_authed/_app/workflows/index.tsx +86 -0
  204. package/apps/main/src/routes/_authed/admin/index.tsx +12 -0
  205. package/apps/main/src/routes/_authed/admin/route.tsx +42 -0
  206. package/apps/main/src/routes/_authed/admin/users/-columns.tsx +124 -0
  207. package/apps/main/src/routes/_authed/admin/users/-fns.ts +30 -0
  208. package/apps/main/src/routes/_authed/admin/users/index.tsx +29 -0
  209. package/apps/main/src/routes/_authed/catchNotFound.tsx +114 -0
  210. package/apps/main/src/routes/_authed/redirects/forms.$id.tsx +29 -0
  211. package/apps/main/src/routes/_authed/redirects/submissions.$id.tsx +27 -0
  212. package/apps/main/src/routes/_authed/redirects/workflows.$id.tsx +27 -0
  213. package/apps/main/src/routes/_authed/route.tsx +51 -0
  214. package/apps/main/src/routes/_authed/settings/-components/new-api-key.tsx +85 -0
  215. package/apps/main/src/routes/_authed/settings/-components/new-invite.tsx +100 -0
  216. package/apps/main/src/routes/_authed/settings/analytics.tsx +1710 -0
  217. package/apps/main/src/routes/_authed/settings/billing/-components/price-table.tsx +129 -0
  218. package/apps/main/src/routes/_authed/settings/billing/-fns.ts +76 -0
  219. package/apps/main/src/routes/_authed/settings/billing/index.tsx +119 -0
  220. package/apps/main/src/routes/_authed/settings/embed.tsx +337 -0
  221. package/apps/main/src/routes/_authed/settings/index.tsx +12 -0
  222. package/apps/main/src/routes/_authed/settings/keys.tsx +157 -0
  223. package/apps/main/src/routes/_authed/settings/members.tsx +276 -0
  224. package/apps/main/src/routes/_authed/settings/route.tsx +22 -0
  225. package/apps/main/src/routes/_authed/settings/user.tsx +87 -0
  226. package/apps/main/src/routes/_authed/settings/workspace.tsx +206 -0
  227. package/apps/main/src/routes/_public/-components/sign-in-up.tsx +96 -0
  228. package/apps/main/src/routes/_public/embedded.tsx +57 -0
  229. package/apps/main/src/routes/_public/invite.$inviteId.tsx +143 -0
  230. package/apps/main/src/routes/_public/no-access.tsx +38 -0
  231. package/apps/main/src/routes/_public/no-invite.tsx +39 -0
  232. package/apps/main/src/routes/_public/otp.tsx +103 -0
  233. package/apps/main/src/routes/_public/route.tsx +15 -0
  234. package/apps/main/src/routes/_public/sign-in.tsx +111 -0
  235. package/apps/main/src/routes/_public/sign-up.tsx +114 -0
  236. package/apps/main/src/routes/api/auth/$.ts +11 -0
  237. package/apps/main/src/routes/api/billing/paid.ts +42 -0
  238. package/apps/main/src/routes/api/billing/webhooks.ts +70 -0
  239. package/apps/main/src/routes/api/chat/agent.ts +40 -0
  240. package/apps/main/src/routes/api/chat/key.ts +42 -0
  241. package/apps/main/src/routes/api/chat/member.ts +35 -0
  242. package/apps/main/src/routes/api/test/index.ts +19 -0
  243. package/apps/main/src/server.tsx +6 -0
  244. package/apps/main/src/styles/app.css +23 -0
  245. package/apps/main/src/vite-env.d.ts +7 -0
  246. package/apps/main/test.http +6 -0
  247. package/apps/main/tsconfig.json +17 -0
  248. package/apps/main/vite.config.ts +24 -0
  249. package/apps/main/vitest.config.js +17 -0
  250. package/apps/mcp/README.md +171 -0
  251. package/apps/mcp/eslint.config.mjs +3 -0
  252. package/apps/mcp/package.json +37 -0
  253. package/apps/mcp/src/index.ts +414 -0
  254. package/apps/mcp/tsconfig.json +19 -0
  255. package/apps/mcp/vite.config.ts +22 -0
  256. package/apps/posthog-proxy/index.html +9 -0
  257. package/apps/workers/.prettierignore +7 -0
  258. package/apps/workers/.prettierrc.js +3 -0
  259. package/apps/workers/dotenvx-safe.sh +11 -0
  260. package/apps/workers/eslint.config.mjs +3 -0
  261. package/apps/workers/package.json +65 -0
  262. package/apps/workers/src/lib/config.ts +7 -0
  263. package/apps/workers/src/lib/messages.ts +0 -0
  264. package/apps/workers/src/lib/posthog.ts +25 -0
  265. package/apps/workers/src/main.ts +58 -0
  266. package/apps/workers/src/workflows/extraction.ts +866 -0
  267. package/apps/workers/src/workflows/index.ts +3 -0
  268. package/apps/workers/src/workflows/pipeline-dag.ts +210 -0
  269. package/apps/workers/src/workflows/pipeline-steps.ts +1393 -0
  270. package/apps/workers/tsconfig.json +16 -0
  271. package/apps/workers/vite.config.ts +35 -0
  272. package/docs/CHANGELOG.md +84 -0
  273. package/docs/agent-templates-and-runs.md +859 -0
  274. package/docs/aws-migration-plan.md +267 -0
  275. package/docs/impl-p0-form-builder-improvements.md +683 -0
  276. package/docs/on-prem-deployment-spec.docx +0 -0
  277. package/docs/on-prem-deployment-spec.md +378 -0
  278. package/docs/prd-form-builder-strategy.md +1120 -0
  279. package/docs/widget-ng-apf-packaging-spec.md +43 -0
  280. package/infra/k8s/charts/seadotdev/Chart.yaml +6 -0
  281. package/infra/k8s/charts/seadotdev/templates/_helpers.tpl +27 -0
  282. package/infra/k8s/charts/seadotdev/templates/api-v2.yaml +105 -0
  283. package/infra/k8s/charts/seadotdev/templates/external-secrets.yaml +83 -0
  284. package/infra/k8s/charts/seadotdev/templates/ingress.yaml +54 -0
  285. package/infra/k8s/charts/seadotdev/templates/main-app.yaml +104 -0
  286. package/infra/k8s/charts/seadotdev/templates/workers.yaml +182 -0
  287. package/infra/k8s/charts/seadotdev/values.yaml +143 -0
  288. package/infra/terraform/main.tf +399 -0
  289. package/libs/ai/.prettierignore +2 -0
  290. package/libs/ai/.prettierrc.js +5 -0
  291. package/libs/ai/README.md +139 -0
  292. package/libs/ai/eslint.config.mjs +3 -0
  293. package/libs/ai/package.json +42 -0
  294. package/libs/ai/src/index.ts +5 -0
  295. package/libs/ai/src/models.ts +19 -0
  296. package/libs/ai/src/rag/index.ts +1 -0
  297. package/libs/ai/src/rag/rag.test.ts +99 -0
  298. package/libs/ai/src/rag/rag.ts +510 -0
  299. package/libs/ai/tsconfig.json +21 -0
  300. package/libs/ai/vite.config.ts +38 -0
  301. package/libs/cache/.prettierignore +2 -0
  302. package/libs/cache/eslint.config.mjs +3 -0
  303. package/libs/cache/package.json +35 -0
  304. package/libs/cache/src/feedback.ts +77 -0
  305. package/libs/cache/src/index.ts +2 -0
  306. package/libs/cache/tsconfig.json +19 -0
  307. package/libs/cache/vite.config.ts +36 -0
  308. package/libs/clients/.prettierignore +6 -0
  309. package/libs/clients/eslint.config.mjs +3 -0
  310. package/libs/clients/package.json +59 -0
  311. package/libs/clients/src/azure.ts +249 -0
  312. package/libs/clients/src/gcp.ts +220 -0
  313. package/libs/clients/src/hatchet.ts +86 -0
  314. package/libs/clients/src/index.ts +8 -0
  315. package/libs/clients/src/loops.ts +86 -0
  316. package/libs/clients/src/polar.ts +77 -0
  317. package/libs/clients/src/posthog.ts +55 -0
  318. package/libs/clients/tsconfig.json +19 -0
  319. package/libs/clients/vite.config.ts +35 -0
  320. package/libs/config/.prettierignore +6 -0
  321. package/libs/config/.prettierrc.js +12 -0
  322. package/libs/config/eslint.config.mjs +3 -0
  323. package/libs/config/package.json +50 -0
  324. package/libs/config/src/azure.ts +54 -0
  325. package/libs/config/src/db.ts +18 -0
  326. package/libs/config/src/gcp.ts +53 -0
  327. package/libs/config/src/google.ts +17 -0
  328. package/libs/config/src/hatchet.ts +20 -0
  329. package/libs/config/src/index.ts +108 -0
  330. package/libs/config/src/llm.ts +17 -0
  331. package/libs/config/src/polar.ts +24 -0
  332. package/libs/config/src/util.ts +8 -0
  333. package/libs/config/src/vercel.ts +26 -0
  334. package/libs/config/tsconfig.json +19 -0
  335. package/libs/config/vite.config.ts +34 -0
  336. package/libs/core/.prettierignore +2 -0
  337. package/libs/core/eslint.config.mjs +3 -0
  338. package/libs/core/package.json +59 -0
  339. package/libs/core/src/chat/derived.ts +97 -0
  340. package/libs/core/src/chat/feedback.ts +293 -0
  341. package/libs/core/src/chat/index.ts +6 -0
  342. package/libs/core/src/chat/model.ts +92 -0
  343. package/libs/core/src/chat/prepare-tools.ts +286 -0
  344. package/libs/core/src/chat/prompts.ts +623 -0
  345. package/libs/core/src/chat/stream.ts +311 -0
  346. package/libs/core/src/chat/summarize.ts +168 -0
  347. package/libs/core/src/chat/tools/agent.ts +403 -0
  348. package/libs/core/src/chat/tools/chart-agent.ts +526 -0
  349. package/libs/core/src/chat/tools/chart-helpers/sandbox.ts +47 -0
  350. package/libs/core/src/chat/tools/chart.ts +86 -0
  351. package/libs/core/src/chat/tools/credit-agent.ts +1383 -0
  352. package/libs/core/src/chat/tools/credit.ts +1435 -0
  353. package/libs/core/src/chat/tools/deep-dive-agent.ts +100 -0
  354. package/libs/core/src/chat/tools/deep-dive.ts +141 -0
  355. package/libs/core/src/chat/tools/form.ts +449 -0
  356. package/libs/core/src/chat/tools/helpers.ts +91 -0
  357. package/libs/core/src/chat/tools/index.ts +42 -0
  358. package/libs/core/src/chat/tools/pipeline-artifact.ts +76 -0
  359. package/libs/core/src/chat/tools/report.ts +40 -0
  360. package/libs/core/src/chat/tools/search.ts +390 -0
  361. package/libs/core/src/chat/tools/submission.ts +227 -0
  362. package/libs/core/src/chat/tools/workflow.ts +684 -0
  363. package/libs/core/src/chat/types.ts +3 -0
  364. package/libs/core/src/data-extraction/classification/azure.ts +168 -0
  365. package/libs/core/src/data-extraction/classification/index.ts +1 -0
  366. package/libs/core/src/data-extraction/dal.ts +246 -0
  367. package/libs/core/src/data-extraction/form-structure-extractor.ts +294 -0
  368. package/libs/core/src/data-extraction/index.ts +4 -0
  369. package/libs/core/src/data-extraction/layout/azure.ts +730 -0
  370. package/libs/core/src/data-extraction/layout/excel.ts +180 -0
  371. package/libs/core/src/data-extraction/layout/gcp.ts +1071 -0
  372. package/libs/core/src/data-extraction/layout/index.ts +266 -0
  373. package/libs/core/src/data-extraction/layout/plaintext.ts +45 -0
  374. package/libs/core/src/data-extraction/models.ts +38 -0
  375. package/libs/core/src/data-extraction/pdf-utils.ts +96 -0
  376. package/libs/core/src/data-extraction/structuring/bank-statement.ts +1182 -0
  377. package/libs/core/src/data-extraction/structuring/custom.ts +495 -0
  378. package/libs/core/src/data-extraction/structuring/index.ts +290 -0
  379. package/libs/core/src/data-extraction/structuring/prompts.ts +69 -0
  380. package/libs/core/src/data-extraction/type-guards.ts +110 -0
  381. package/libs/core/src/data-extraction/types.ts +84 -0
  382. package/libs/core/src/data-extraction/utils.ts +31 -0
  383. package/libs/core/src/data-extraction/validation/bank-statement.ts +127 -0
  384. package/libs/core/src/deals.ts +17 -0
  385. package/libs/core/src/documents.ts +152 -0
  386. package/libs/core/src/index.ts +5 -0
  387. package/libs/core/src/pipelines/display.ts +678 -0
  388. package/libs/core/src/pipelines/execute.ts +2342 -0
  389. package/libs/core/src/pipelines/index.ts +4 -0
  390. package/libs/core/src/pipelines/list.ts +12 -0
  391. package/libs/core/src/pipelines/runs.ts +53 -0
  392. package/libs/core/tsconfig.json +20 -0
  393. package/libs/core/vite.config.ts +56 -0
  394. package/libs/dal/.prettierignore +6 -0
  395. package/libs/dal/.prettierrc.js +12 -0
  396. package/libs/dal/eslint.config.mjs +3 -0
  397. package/libs/dal/package.json +57 -0
  398. package/libs/dal/src/_tests/db.test.ts +19 -0
  399. package/libs/dal/src/_tests/mock-db.ts +60 -0
  400. package/libs/dal/src/api-key.test.ts +397 -0
  401. package/libs/dal/src/api-key.ts +110 -0
  402. package/libs/dal/src/billing.ts +23 -0
  403. package/libs/dal/src/conversation.test.ts +655 -0
  404. package/libs/dal/src/conversation.ts +532 -0
  405. package/libs/dal/src/deal.test.ts +45 -0
  406. package/libs/dal/src/deal.ts +87 -0
  407. package/libs/dal/src/defaults-consumer-lending-uk.ts +33 -0
  408. package/libs/dal/src/defaults-consumer-lending-us.ts +33 -0
  409. package/libs/dal/src/defaults-private-credit.ts +57 -0
  410. package/libs/dal/src/defaults-private-equity.ts +51 -0
  411. package/libs/dal/src/defaults-smb-lending-us.ts +1569 -0
  412. package/libs/dal/src/defaults-sme-lending-uk-express.ts +1527 -0
  413. package/libs/dal/src/defaults-sme-lending-uk.ts +1669 -0
  414. package/libs/dal/src/defaults-types.ts +23 -0
  415. package/libs/dal/src/defaults.ts +550 -0
  416. package/libs/dal/src/document.test.ts +70 -0
  417. package/libs/dal/src/document.ts +192 -0
  418. package/libs/dal/src/feedback.ts +255 -0
  419. package/libs/dal/src/form.test.ts +637 -0
  420. package/libs/dal/src/form.ts +1165 -0
  421. package/libs/dal/src/index.ts +20 -0
  422. package/libs/dal/src/invitation.test.ts +746 -0
  423. package/libs/dal/src/invitation.ts +207 -0
  424. package/libs/dal/src/member.test.ts +185 -0
  425. package/libs/dal/src/member.ts +80 -0
  426. package/libs/dal/src/organization.ts +116 -0
  427. package/libs/dal/src/permission.ts +25 -0
  428. package/libs/dal/src/pipeline.test.ts +388 -0
  429. package/libs/dal/src/pipeline.ts +4222 -0
  430. package/libs/dal/src/report.ts +199 -0
  431. package/libs/dal/src/result.ts +16 -0
  432. package/libs/dal/src/search.ts +172 -0
  433. package/libs/dal/src/session.test.ts +110 -0
  434. package/libs/dal/src/session.ts +31 -0
  435. package/libs/dal/src/submission.test.ts +1304 -0
  436. package/libs/dal/src/submission.ts +1396 -0
  437. package/libs/dal/src/tool.ts +159 -0
  438. package/libs/dal/src/user.ts +16 -0
  439. package/libs/dal/src/workflow.test.ts +89 -0
  440. package/libs/dal/src/workflow.ts +262 -0
  441. package/libs/dal/tsconfig.build.json +4 -0
  442. package/libs/dal/tsconfig.json +22 -0
  443. package/libs/dal/vite.config.ts +34 -0
  444. package/libs/db/.prettierignore +6 -0
  445. package/libs/db/.prettierrc.js +12 -0
  446. package/libs/db/eslint.config.mjs +3 -0
  447. package/libs/db/package.json +52 -0
  448. package/libs/db/src/index.ts +24 -0
  449. package/libs/db/src/relations.ts +549 -0
  450. package/libs/db/src/schema.ts +2 -0
  451. package/libs/db/src/schemas/api.ts +35 -0
  452. package/libs/db/src/schemas/conversations.ts +175 -0
  453. package/libs/db/src/schemas/core.ts +359 -0
  454. package/libs/db/src/schemas/documents.ts +181 -0
  455. package/libs/db/src/schemas/feedback.ts +40 -0
  456. package/libs/db/src/schemas/index.ts +26 -0
  457. package/libs/db/src/schemas/organisations.ts +97 -0
  458. package/libs/db/src/schemas/pipelines.ts +440 -0
  459. package/libs/db/src/schemas/users.ts +95 -0
  460. package/libs/db/src/types.ts +190 -0
  461. package/libs/db/src/utils.ts +14 -0
  462. package/libs/db/tsconfig.json +19 -0
  463. package/libs/db/vite.config.ts +31 -0
  464. package/libs/lint/.prettierignore +6 -0
  465. package/libs/lint/eslint.config.mjs +61 -0
  466. package/libs/lint/package.json +29 -0
  467. package/libs/lint/prettier.config.js +12 -0
  468. package/libs/schemas/.prettierignore +6 -0
  469. package/libs/schemas/.prettierrc.js +12 -0
  470. package/libs/schemas/README.md +15 -0
  471. package/libs/schemas/eslint.config.mjs +3 -0
  472. package/libs/schemas/package.json +67 -0
  473. package/libs/schemas/src/core/chat.ts +67 -0
  474. package/libs/schemas/src/core/core-result.ts +15 -0
  475. package/libs/schemas/src/core/data-extraction.ts +184 -0
  476. package/libs/schemas/src/core/layout.ts +478 -0
  477. package/libs/schemas/src/core/pipeline.ts +128 -0
  478. package/libs/schemas/src/core/submission.ts +97 -0
  479. package/libs/schemas/src/db/account.ts +57 -0
  480. package/libs/schemas/src/db/apiKey.ts +57 -0
  481. package/libs/schemas/src/db/context.ts +33 -0
  482. package/libs/schemas/src/db/conversation.ts +65 -0
  483. package/libs/schemas/src/db/deal.ts +42 -0
  484. package/libs/schemas/src/db/document.ts +103 -0
  485. package/libs/schemas/src/db/documentCitation.ts +58 -0
  486. package/libs/schemas/src/db/documentExtraction.ts +69 -0
  487. package/libs/schemas/src/db/fieldCorrection.ts +85 -0
  488. package/libs/schemas/src/db/form.ts +45 -0
  489. package/libs/schemas/src/db/formField.ts +59 -0
  490. package/libs/schemas/src/db/formGroup.ts +42 -0
  491. package/libs/schemas/src/db/impersonation.ts +39 -0
  492. package/libs/schemas/src/db/index.ts +25 -0
  493. package/libs/schemas/src/db/invitation.ts +42 -0
  494. package/libs/schemas/src/db/member.ts +36 -0
  495. package/libs/schemas/src/db/message.ts +58 -0
  496. package/libs/schemas/src/db/organization.ts +62 -0
  497. package/libs/schemas/src/db/session.ts +48 -0
  498. package/libs/schemas/src/db/submission.ts +54 -0
  499. package/libs/schemas/src/db/submissionGroup.ts +36 -0
  500. package/libs/schemas/src/db/submissionItem.ts +33 -0
  501. package/libs/schemas/src/db/submissionItemVersion.ts +70 -0
  502. package/libs/schemas/src/db/user.ts +51 -0
  503. package/libs/schemas/src/db/utils.ts +3 -0
  504. package/libs/schemas/src/db/verification.ts +36 -0
  505. package/libs/schemas/src/db/workflow.ts +42 -0
  506. package/libs/schemas/src/index.ts +10 -0
  507. package/libs/schemas/tsconfig.json +21 -0
  508. package/libs/schemas/vite.config.ts +38 -0
  509. package/libs/ui/.prettierignore +6 -0
  510. package/libs/ui/.prettierrc.js +12 -0
  511. package/libs/ui/components.json +24 -0
  512. package/libs/ui/eslint.config.mjs +3 -0
  513. package/libs/ui/package.json +142 -0
  514. package/libs/ui/src/components/chart-viz/chart.tsx +255 -0
  515. package/libs/ui/src/components/chart-viz/converters.ts +474 -0
  516. package/libs/ui/src/components/chart-viz/dashboard.tsx +146 -0
  517. package/libs/ui/src/components/chart-viz/index.ts +37 -0
  518. package/libs/ui/src/components/chart-viz/markdown.tsx +344 -0
  519. package/libs/ui/src/components/chart-viz/table.tsx +446 -0
  520. package/libs/ui/src/components/chart-viz/theme-context.tsx +70 -0
  521. package/libs/ui/src/components/chart-viz/themes/dark.ts +98 -0
  522. package/libs/ui/src/components/chart-viz/themes/index.ts +69 -0
  523. package/libs/ui/src/components/chart-viz/themes/light.ts +98 -0
  524. package/libs/ui/src/components/chart-viz/themes/tailwind.ts +326 -0
  525. package/libs/ui/src/components/chart-viz/themes/types.ts +99 -0
  526. package/libs/ui/src/components/chart-viz/tool-display.tsx +150 -0
  527. package/libs/ui/src/components/chart-viz/types.ts +95 -0
  528. package/libs/ui/src/components/doc-viewers/excel/index.tsx +431 -0
  529. package/libs/ui/src/components/doc-viewers/excel/themes.ts +160 -0
  530. package/libs/ui/src/components/doc-viewers/image/index.tsx +410 -0
  531. package/libs/ui/src/components/doc-viewers/pdf/index.tsx +258 -0
  532. package/libs/ui/src/components/doc-viewers/pdf/virtualized-pdf.tsx +556 -0
  533. package/libs/ui/src/components/misc/rel-date.tsx +52 -0
  534. package/libs/ui/src/components/misc/styled-link.tsx +2 -0
  535. package/libs/ui/src/components/table/data-table.tsx +546 -0
  536. package/libs/ui/src/components/table/report-table.tsx +305 -0
  537. package/libs/ui/src/components/table/sortable-column.tsx +34 -0
  538. package/libs/ui/src/components/ui/accordion.tsx +62 -0
  539. package/libs/ui/src/components/ui/alert-dialog.tsx +142 -0
  540. package/libs/ui/src/components/ui/alert.tsx +62 -0
  541. package/libs/ui/src/components/ui/artifact.tsx +118 -0
  542. package/libs/ui/src/components/ui/attachments.tsx +388 -0
  543. package/libs/ui/src/components/ui/avatar.tsx +39 -0
  544. package/libs/ui/src/components/ui/badge.tsx +43 -0
  545. package/libs/ui/src/components/ui/breadcrumb.tsx +102 -0
  546. package/libs/ui/src/components/ui/button-group.tsx +78 -0
  547. package/libs/ui/src/components/ui/button.tsx +79 -0
  548. package/libs/ui/src/components/ui/card.tsx +32 -0
  549. package/libs/ui/src/components/ui/carousel.tsx +228 -0
  550. package/libs/ui/src/components/ui/chain-of-thought.tsx +198 -0
  551. package/libs/ui/src/components/ui/checkbox.tsx +27 -0
  552. package/libs/ui/src/components/ui/citation.tsx +34 -0
  553. package/libs/ui/src/components/ui/code-block.tsx +500 -0
  554. package/libs/ui/src/components/ui/collapsible.tsx +19 -0
  555. package/libs/ui/src/components/ui/command.tsx +161 -0
  556. package/libs/ui/src/components/ui/conversation.tsx +90 -0
  557. package/libs/ui/src/components/ui/dialog.tsx +142 -0
  558. package/libs/ui/src/components/ui/dropdown-menu.tsx +246 -0
  559. package/libs/ui/src/components/ui/highlight.tsx +3 -0
  560. package/libs/ui/src/components/ui/hover-card.tsx +36 -0
  561. package/libs/ui/src/components/ui/inline-citation.tsx +251 -0
  562. package/libs/ui/src/components/ui/input-group.tsx +156 -0
  563. package/libs/ui/src/components/ui/input-otp.tsx +78 -0
  564. package/libs/ui/src/components/ui/input.tsx +21 -0
  565. package/libs/ui/src/components/ui/label.tsx +19 -0
  566. package/libs/ui/src/components/ui/model-selector.tsx +174 -0
  567. package/libs/ui/src/components/ui/multisidebar.tsx +750 -0
  568. package/libs/ui/src/components/ui/popover.tsx +43 -0
  569. package/libs/ui/src/components/ui/progress.tsx +28 -0
  570. package/libs/ui/src/components/ui/reasoning.tsx +178 -0
  571. package/libs/ui/src/components/ui/resizable.tsx +49 -0
  572. package/libs/ui/src/components/ui/scroll-area.tsx +54 -0
  573. package/libs/ui/src/components/ui/select.tsx +171 -0
  574. package/libs/ui/src/components/ui/separator.tsx +26 -0
  575. package/libs/ui/src/components/ui/sheet.tsx +128 -0
  576. package/libs/ui/src/components/ui/shimmer.tsx +53 -0
  577. package/libs/ui/src/components/ui/skeleton.tsx +13 -0
  578. package/libs/ui/src/components/ui/sonner.tsx +23 -0
  579. package/libs/ui/src/components/ui/switch.tsx +26 -0
  580. package/libs/ui/src/components/ui/table.tsx +96 -0
  581. package/libs/ui/src/components/ui/tabs.tsx +52 -0
  582. package/libs/ui/src/components/ui/textarea.tsx +41 -0
  583. package/libs/ui/src/components/ui/tool.tsx +209 -0
  584. package/libs/ui/src/components/ui/tooltip.tsx +58 -0
  585. package/libs/ui/src/components/ui/typography.tsx +113 -0
  586. package/libs/ui/src/fonts/manrope-v15-latin-300.woff2 +0 -0
  587. package/libs/ui/src/fonts/manrope-v15-latin-400.woff2 +0 -0
  588. package/libs/ui/src/fonts/manrope-v15-latin-500.woff2 +0 -0
  589. package/libs/ui/src/fonts/manrope-v15-latin-600.woff2 +0 -0
  590. package/libs/ui/src/hooks/use-mobile.ts +19 -0
  591. package/libs/ui/src/lib/utils.ts +6 -0
  592. package/libs/ui/src/styles/fonts.css +35 -0
  593. package/libs/ui/src/styles/style.css +218 -0
  594. package/libs/ui/tsconfig.json +21 -0
  595. package/libs/ui/vite.config.ts +80 -0
  596. package/libs/ui-lit/README.md +245 -0
  597. package/libs/ui-lit/TESTING_GUIDE.md +296 -0
  598. package/libs/ui-lit/eslint.config.mjs +3 -0
  599. package/libs/ui-lit/package.json +41 -0
  600. package/libs/ui-lit/scripts/build-css.js +43 -0
  601. package/libs/ui-lit/src/components/sea-alert.ts +132 -0
  602. package/libs/ui-lit/src/components/sea-button.ts +95 -0
  603. package/libs/ui-lit/src/components/sea-card.ts +113 -0
  604. package/libs/ui-lit/src/components/sea-input.ts +184 -0
  605. package/libs/ui-lit/src/components/sea-spinner.ts +65 -0
  606. package/libs/ui-lit/src/index.ts +15 -0
  607. package/libs/ui-lit/src/lib/utils.ts +6 -0
  608. package/libs/ui-lit/src/styles/tailwind.css +76 -0
  609. package/libs/ui-lit/src/theme.css +66 -0
  610. package/libs/ui-lit/src/theme.ts +79 -0
  611. package/libs/ui-lit/src/vite-env.d.ts +6 -0
  612. package/libs/ui-lit/tailwind.config.ts +50 -0
  613. package/libs/ui-lit/test.html +289 -0
  614. package/libs/ui-lit/tsconfig.json +23 -0
  615. package/libs/ui-lit/vite.config.ts +31 -0
  616. package/libs/ui-lit/vite.css.config.ts +20 -0
  617. package/libs/util/.prettierignore +6 -0
  618. package/libs/util/.prettierrc.js +12 -0
  619. package/libs/util/eslint.config.mjs +3 -0
  620. package/libs/util/package.json +45 -0
  621. package/libs/util/src/billing.ts +10 -0
  622. package/libs/util/src/data-transform.ts +19 -0
  623. package/libs/util/src/encryption.ts +45 -0
  624. package/libs/util/src/fmt.test.ts +9 -0
  625. package/libs/util/src/fmt.ts +71 -0
  626. package/libs/util/src/fuzzy.ts +47 -0
  627. package/libs/util/src/id.ts +24 -0
  628. package/libs/util/src/invariant.ts +31 -0
  629. package/libs/util/src/sub-name.ts +7 -0
  630. package/libs/util/tsconfig.json +19 -0
  631. package/libs/util/vite.config.ts +34 -0
  632. package/package.json +28 -0
  633. package/packages/widget/.prettierignore +6 -0
  634. package/packages/widget/.prettierrc.js +12 -0
  635. package/packages/widget/README.md +95 -0
  636. package/packages/widget/eslint.config.mjs +11 -0
  637. package/packages/widget/openapi-ts.config.ts +8 -0
  638. package/packages/widget/package.json +89 -0
  639. package/packages/widget/postcss.config.mjs +10 -0
  640. package/packages/widget/src/clients/api/client/client.ts +187 -0
  641. package/packages/widget/src/clients/api/client/index.ts +22 -0
  642. package/packages/widget/src/clients/api/client/types.ts +192 -0
  643. package/packages/widget/src/clients/api/client/utils.ts +394 -0
  644. package/packages/widget/src/clients/api/client.gen.ts +18 -0
  645. package/packages/widget/src/clients/api/core/auth.ts +39 -0
  646. package/packages/widget/src/clients/api/core/bodySerializer.ts +74 -0
  647. package/packages/widget/src/clients/api/core/params.ts +132 -0
  648. package/packages/widget/src/clients/api/core/pathSerializer.ts +169 -0
  649. package/packages/widget/src/clients/api/core/types.ts +80 -0
  650. package/packages/widget/src/clients/api/index.ts +3 -0
  651. package/packages/widget/src/clients/api/sdk.gen.ts +805 -0
  652. package/packages/widget/src/clients/api/types.gen.ts +2085 -0
  653. package/packages/widget/src/components/container.tsx +42 -0
  654. package/packages/widget/src/components/data-display.tsx +384 -0
  655. package/packages/widget/src/components/data-viewer.tsx +311 -0
  656. package/packages/widget/src/components/doc-list.tsx +102 -0
  657. package/packages/widget/src/components/field-correction-modal.tsx +265 -0
  658. package/packages/widget/src/components/header.tsx +71 -0
  659. package/packages/widget/src/components/new-submission.tsx +290 -0
  660. package/packages/widget/src/components/sidebar-right.tsx +19 -0
  661. package/packages/widget/src/components/submission-card.tsx +66 -0
  662. package/packages/widget/src/components/submission-page.tsx +75 -0
  663. package/packages/widget/src/components/upload-doc.tsx +241 -0
  664. package/packages/widget/src/components/widget.tsx +101 -0
  665. package/packages/widget/src/index.tsx +167 -0
  666. package/packages/widget/src/lib/config.ts +2 -0
  667. package/packages/widget/src/lib/util.ts +40 -0
  668. package/packages/widget/src/styles/index.css +5 -0
  669. package/packages/widget/src/styles/tw-properties.css +337 -0
  670. package/packages/widget/src/vite-env.d.ts +3 -0
  671. package/packages/widget/tsconfig.app.json +35 -0
  672. package/packages/widget/tsconfig.json +4 -0
  673. package/packages/widget/tsconfig.node.json +24 -0
  674. package/packages/widget/vite.config.ts +116 -0
  675. package/packages/widget-lit/BOTTLENECKS.md +250 -0
  676. package/packages/widget-lit/IMPLEMENTATION_SUMMARY.md +295 -0
  677. package/packages/widget-lit/README.md +232 -0
  678. package/packages/widget-lit/eslint.config.mjs +3 -0
  679. package/packages/widget-lit/package.json +52 -0
  680. package/packages/widget-lit/src/api-client.ts +230 -0
  681. package/packages/widget-lit/src/api-client.ts.backup +218 -0
  682. package/packages/widget-lit/src/components/sea-chat.ts +382 -0
  683. package/packages/widget-lit/src/components/sea-submission-viewer.ts +267 -0
  684. package/packages/widget-lit/src/components/sea-widget.ts +317 -0
  685. package/packages/widget-lit/src/index.ts +48 -0
  686. package/packages/widget-lit/src/react.ts +58 -0
  687. package/packages/widget-lit/src/style.css +47 -0
  688. package/packages/widget-lit/tsconfig.json +24 -0
  689. package/packages/widget-lit/vite.config.ts +29 -0
  690. package/packages/widget-ng/DEVELOPMENT.md +74 -0
  691. package/packages/widget-ng/README.md +657 -0
  692. package/packages/widget-ng/dev.sh +14 -0
  693. package/packages/widget-ng/eslint.config.mjs +24 -0
  694. package/packages/widget-ng/ng-package.json +9 -0
  695. package/packages/widget-ng/package.json +85 -0
  696. package/packages/widget-ng/src/index.ts +45 -0
  697. package/packages/widget-ng/src/lib/components/sea-chat.component.ts +737 -0
  698. package/packages/widget-ng/src/lib/components/sea-data-viewer.component.ts +2240 -0
  699. package/packages/widget-ng/src/lib/components/sea-deal-form-modal.component.ts +702 -0
  700. package/packages/widget-ng/src/lib/components/sea-document-list.component.ts +350 -0
  701. package/packages/widget-ng/src/lib/components/sea-feedback-modal.component.ts +461 -0
  702. package/packages/widget-ng/src/lib/components/sea-file-upload.component.ts +655 -0
  703. package/packages/widget-ng/src/lib/components/sea-model-selection-modal.component.ts +367 -0
  704. package/packages/widget-ng/src/lib/components/sea-new-submission-modal.component.ts +414 -0
  705. package/packages/widget-ng/src/lib/components/sea-pdf-viewer.component.ts +869 -0
  706. package/packages/widget-ng/src/lib/components/sea-submission-card.component.ts +251 -0
  707. package/packages/widget-ng/src/lib/components/sea-widget.component.ts +684 -0
  708. package/packages/widget-ng/src/lib/models/submission.model.ts +170 -0
  709. package/packages/widget-ng/src/lib/pipes/markdown.pipe.ts +57 -0
  710. package/packages/widget-ng/src/lib/services/api-client.service.ts +715 -0
  711. package/packages/widget-ng/src/lib/services/chat.service.ts +330 -0
  712. package/packages/widget-ng/src/lib/services/config.service.ts +107 -0
  713. package/packages/widget-ng/src/web-component.ts +56 -0
  714. package/packages/widget-ng/tsconfig.json +25 -0
  715. package/packages/widget-ng/tsconfig.lib.json +9 -0
  716. package/packages/widget-ng/vite.config.elements.ts +26 -0
  717. package/packages/widget-ng/vitest.config.ts +19 -0
  718. package/packages/widget-ng/vitest.setup.ts +13 -0
  719. package/pnpm-workspace.yaml +18 -0
  720. package/render.yaml +136 -0
  721. package/scripts/README.md +57 -0
  722. package/scripts/package.json +22 -0
  723. package/scripts/python/.python-version +1 -0
  724. package/scripts/python/README.md +3 -0
  725. package/scripts/python/export-org-data.py +693 -0
  726. package/scripts/python/pyproject.toml +29 -0
  727. package/scripts/python/requirements-dev.lock +36 -0
  728. package/scripts/python/requirements.lock +36 -0
  729. package/scripts/python/src/gen.py +297 -0
  730. package/scripts/python/test.py +34 -0
  731. package/scripts/src/fix-storage-provider-mismatch.ts +239 -0
  732. package/scripts/src/sync-render-yaml.ts +290 -0
  733. package/scripts/src/test-chat-stream.ts +300 -0
  734. package/scripts/src/test-reconciliation.ts +230 -0
  735. package/scripts/tsconfig.json +15 -0
  736. package/tests/angular-test-app/.vscode/extensions.json +4 -0
  737. package/tests/angular-test-app/.vscode/launch.json +13 -0
  738. package/tests/angular-test-app/.vscode/tasks.json +24 -0
  739. package/tests/angular-test-app/README.md +59 -0
  740. package/tests/angular-test-app/angular.json +111 -0
  741. package/tests/angular-test-app/clean-start.sh +14 -0
  742. package/tests/angular-test-app/package.json +36 -0
  743. package/tests/angular-test-app/public/favicon.ico +0 -0
  744. package/tests/angular-test-app/src/app/app.component.ts +220 -0
  745. package/tests/angular-test-app/src/app/app.config.ts +5 -0
  746. package/tests/angular-test-app/src/env.d.ts +13 -0
  747. package/tests/angular-test-app/src/index.html +13 -0
  748. package/tests/angular-test-app/src/main.ts +6 -0
  749. package/tests/angular-test-app/src/styles.css +8 -0
  750. package/tests/angular-test-app/tsconfig.app.json +15 -0
  751. package/tests/angular-test-app/tsconfig.json +27 -0
  752. package/tests/crm-viewer-app/API_INTEGRATION_SUMMARY.md +295 -0
  753. package/tests/crm-viewer-app/CURRENT_ASSETS_FIELDS.md +148 -0
  754. package/tests/crm-viewer-app/FIELD_ID_MAPPING.md +206 -0
  755. package/tests/crm-viewer-app/INTEGRATION_GUIDE.md +309 -0
  756. package/tests/crm-viewer-app/README.md +174 -0
  757. package/tests/crm-viewer-app/REAL_API_INTEGRATION.md +240 -0
  758. package/tests/crm-viewer-app/UPDATED_IMPLEMENTATION.md +279 -0
  759. package/tests/crm-viewer-app/angular.json +114 -0
  760. package/tests/crm-viewer-app/package.json +35 -0
  761. package/tests/crm-viewer-app/src/app/app.component.ts +534 -0
  762. package/tests/crm-viewer-app/src/app/citation.service.ts +316 -0
  763. package/tests/crm-viewer-app/src/env.d.ts +16 -0
  764. package/tests/crm-viewer-app/src/index.html +19 -0
  765. package/tests/crm-viewer-app/src/main.ts +7 -0
  766. package/tests/crm-viewer-app/src/styles.css +409 -0
  767. package/tests/crm-viewer-app/src/template.html +2678 -0
  768. package/tests/crm-viewer-app/tsconfig.app.json +15 -0
  769. package/tests/crm-viewer-app/tsconfig.json +27 -0
  770. package/tests/e2e/package.json +17 -0
  771. package/tests/e2e/playwright.config.ts +75 -0
  772. package/tests/e2e/tests/api/health.spec.ts +10 -0
  773. package/tests/e2e/tests/app/example.spec.ts +10 -0
  774. package/tests/widget-test-app/.prettierignore +6 -0
  775. package/tests/widget-test-app/README.md +48 -0
  776. package/tests/widget-test-app/index.html +12 -0
  777. package/tests/widget-test-app/package.json +24 -0
  778. package/tests/widget-test-app/src/App.css +192 -0
  779. package/tests/widget-test-app/src/App.tsx +80 -0
  780. package/tests/widget-test-app/src/main.tsx +9 -0
  781. package/tests/widget-test-app/src/vite-env.d.ts +4 -0
  782. package/tests/widget-test-app/tsconfig.json +25 -0
  783. package/tests/widget-test-app/tsconfig.node.json +11 -0
  784. package/tests/widget-test-app/vite.config.ts +14 -0
@@ -0,0 +1,2342 @@
1
+ import {
2
+ createPipelineRun,
3
+ createPipelineRunStep,
4
+ getPipelineWithSteps,
5
+ getPipelineStepsWithLevels,
6
+ getParentStepRuns,
7
+ getParentStepRunsWithDependencies,
8
+ getRuntimeSubmission,
9
+ getRuntimeSubmissionWithData,
10
+ storePipelineOutputData,
11
+ updatePipelineRunStatus,
12
+ updatePipelineRunStepStatus,
13
+ logRequestCall,
14
+ getSubmissionGroups,
15
+ getGroupForStepRun,
16
+ resolveStepBlockersOnCompletion,
17
+ getPipelineStepById,
18
+ getStepDocuments,
19
+ getPipelineRunWithSteps,
20
+ getStepDependenciesByParentStepId,
21
+ getStepIntegrationConfig,
22
+ } from "@sea/dal/pipeline";
23
+ import { PIPELINE_RUN_STATUS, PIPELINE_RUN_STEP_STATUS } from "@sea/db";
24
+ import {
25
+ createSubmissionWithConversation,
26
+ getDenormSubmission,
27
+ syncSubmissionToForm,
28
+ getSubmissionWithData,
29
+ type DenormSubmission,
30
+ } from "@sea/dal/submission";
31
+ import type { FormGroupHydrated, Member, PipelineRunStep } from "@sea/db/types";
32
+ import { db, schema } from "@sea/db";
33
+ import { and, asc, eq, inArray, or } from "drizzle-orm";
34
+ import type {
35
+ ExecutePipelineInput,
36
+ ExecutePipelineResult,
37
+ StepExecutionResult,
38
+ } from "@sea/schemas/core/pipeline";
39
+ import {
40
+ addAdditionalPropertiesToJsonSchema,
41
+ buildExtractionSchemaFromGroup,
42
+ } from "@sea/core/data-extraction/structuring/custom";
43
+ import {
44
+ jsonSchema,
45
+ Output,
46
+ stepCountIs,
47
+ ToolLoopAgent,
48
+ zodSchema,
49
+ type JSONSchema7,
50
+ } from "ai";
51
+ import * as z from "zod/v4";
52
+ import type { createLanguageModel } from "@sea/ai/models";
53
+ import type { DalResult } from "@sea/dal/result";
54
+ import { HatchetClientMgr } from "@sea/clients/hatchet";
55
+ import type { HatchetConfig } from "@sea/config";
56
+ import { createPipelineArtifact } from "../chat/tools/pipeline-artifact";
57
+ import pLimit from "p-limit";
58
+
59
+ // ============================================================================
60
+ // Serialization helpers
61
+ // ============================================================================
62
+
63
+ function serializeSubmission(submission: DenormSubmission): string {
64
+ const htmlParts: string[] = [];
65
+
66
+ // Add submission header
67
+ htmlParts.push(`<h2>${submission.formName}</h2>`);
68
+ if (submission.name) {
69
+ htmlParts.push(`<p><strong>Submission:</strong> ${submission.name}</p>`);
70
+ }
71
+
72
+ // Serialize single groups (definition lists)
73
+ for (const group of submission.groups) {
74
+ htmlParts.push(`<h3>${group.name}</h3>`);
75
+ htmlParts.push("<dl>");
76
+
77
+ for (const item of group.items) {
78
+ htmlParts.push(`<dt>${item.name}</dt>`);
79
+ htmlParts.push(`<dd id="${item.id}">${String(item.value ?? "")}</dd>`);
80
+ }
81
+
82
+ // Add derived fields if any
83
+ for (const derived of group.derived) {
84
+ htmlParts.push(`<dt>${derived.name} (derived)</dt>`);
85
+ htmlParts.push(
86
+ `<dd id="${derived.id}">${String(derived.value ?? "")}</dd>`,
87
+ );
88
+ }
89
+
90
+ htmlParts.push("</dl>");
91
+ }
92
+
93
+ // Serialize tables (multi groups)
94
+ for (const table of submission.tables) {
95
+ htmlParts.push(`<h3>${table.name}</h3>`);
96
+ htmlParts.push("<table>");
97
+
98
+ // Table header
99
+ htmlParts.push("<thead><tr>");
100
+ for (const col of table.columns) {
101
+ htmlParts.push(`<th>${col}</th>`);
102
+ }
103
+ htmlParts.push("</tr></thead>");
104
+
105
+ // Table body
106
+ htmlParts.push("<tbody>");
107
+ for (const row of table.rows) {
108
+ htmlParts.push("<tr>");
109
+
110
+ // Regular items
111
+ for (const item of row.items) {
112
+ htmlParts.push(`<td id="${item.id}">${String(item.value ?? "")}</td>`);
113
+ }
114
+
115
+ // Derived items
116
+ for (const derived of row.derived) {
117
+ htmlParts.push(
118
+ `<td id="${derived.id}">${String(derived.value ?? "")}</td>`,
119
+ );
120
+ }
121
+
122
+ htmlParts.push("</tr>");
123
+ }
124
+ htmlParts.push("</tbody>");
125
+ htmlParts.push("</table>");
126
+ }
127
+
128
+ return htmlParts.join("\n");
129
+ }
130
+
131
+ function serializePipelineResources(resources: DenormSubmission[]): string {
132
+ const htmlParts: string[] = [];
133
+
134
+ for (const resource of resources) {
135
+ htmlParts.push(serializeSubmission(resource));
136
+ htmlParts.push("<hr>");
137
+ }
138
+
139
+ return htmlParts.join("\n");
140
+ }
141
+
142
+ // ============================================================================
143
+ // DAG Execution Engine (Phase 1)
144
+ // ============================================================================
145
+
146
+ /**
147
+ * Execute pipeline using DAG traversal with level-by-level execution.
148
+ * This replaces the old sequential execution approach.
149
+ */
150
+ export async function executePipelineDAG({
151
+ member,
152
+ runId,
153
+ config,
154
+ }: {
155
+ member: Member;
156
+ runId: string;
157
+ config: {
158
+ model: ReturnType<typeof createLanguageModel>;
159
+ hatchet?: HatchetConfig;
160
+ };
161
+ }): Promise<ExecutePipelineResult> {
162
+ try {
163
+ // Get pipeline run via DAL
164
+ const runResult = await getPipelineRunWithSteps({ member, runId });
165
+ if (!runResult.ok) {
166
+ return { ok: false, code: 404, error: "Pipeline run not found" };
167
+ }
168
+ const run = runResult.data;
169
+
170
+ // Update run status to running
171
+ await updatePipelineRunStatus({
172
+ member,
173
+ runId,
174
+ status: PIPELINE_RUN_STEP_STATUS.RUNNING,
175
+ });
176
+
177
+ // Get execution levels for topological ordering
178
+ const levelsResult = await getPipelineStepsWithLevels({
179
+ member,
180
+ pipelineId: run.pipelineId,
181
+ });
182
+
183
+ if (!levelsResult.ok) {
184
+ await updatePipelineRunStatus({
185
+ member,
186
+ runId,
187
+ status: PIPELINE_RUN_STATUS.FAILED,
188
+ errorSummary: "Failed to compute execution levels",
189
+ });
190
+ return {
191
+ ok: false,
192
+ code: 500,
193
+ error: "Failed to compute execution levels",
194
+ };
195
+ }
196
+
197
+ const stepsWithLevels = levelsResult.data;
198
+
199
+ // Group steps by execution level
200
+ const levelMap = new Map<number, typeof stepsWithLevels>();
201
+ for (const step of stepsWithLevels) {
202
+ const level = step.executionLevel;
203
+ if (!levelMap.has(level)) {
204
+ levelMap.set(level, []);
205
+ }
206
+ levelMap.get(level)!.push(step);
207
+ }
208
+
209
+ const levels = Array.from(levelMap.keys()).sort((a, b) => a - b);
210
+
211
+ // Execute level by level
212
+ const limit = pLimit(5); // Concurrent execution limit per level
213
+ console.log(
214
+ "[executePipelineDAG] Executing levels:",
215
+ JSON.stringify(levels, null, 2),
216
+ );
217
+
218
+ for (const level of levels) {
219
+ const stepsInLevel = levelMap.get(level) || [];
220
+ console.log(
221
+ `[executePipelineDAG] Executing level ${level} with ${stepsInLevel.length} steps`,
222
+ );
223
+
224
+ // Execute all steps in this level concurrently (with limit)
225
+ const levelPromises = stepsInLevel.map((step) =>
226
+ limit(() =>
227
+ executeStepByRole({
228
+ member,
229
+ runId,
230
+ stepId: step.id,
231
+ config,
232
+ }),
233
+ ),
234
+ );
235
+
236
+ try {
237
+ // Wait for all steps in this level to complete (or be blocked/failed)
238
+ await Promise.all(levelPromises);
239
+
240
+ // After level completes, check if any steps are still blocked
241
+ // If so, we cannot proceed to the next level
242
+ const levelStepIds = stepsInLevel.map((s) => s.id);
243
+ const runSteps = await db.query.pipelineRunStep.findMany({
244
+ where: and(
245
+ eq(schema.pipelineRunStep.runId, runId),
246
+ // Filter to steps in this level
247
+ or(
248
+ ...levelStepIds.map((id) =>
249
+ eq(schema.pipelineRunStep.stepId, id),
250
+ ),
251
+ ),
252
+ ),
253
+ });
254
+
255
+ const blockedSteps = runSteps.filter(
256
+ (rs) => rs.status === PIPELINE_RUN_STEP_STATUS.BLOCKED,
257
+ );
258
+ const failedSteps = runSteps.filter(
259
+ (rs) => rs.status === PIPELINE_RUN_STEP_STATUS.FAILED,
260
+ );
261
+
262
+ if (failedSteps.length > 0) {
263
+ console.error(
264
+ `[executePipelineDAG] Level ${level} has ${failedSteps.length} failed step(s)`,
265
+ );
266
+ await updatePipelineRunStatus({
267
+ member,
268
+ runId,
269
+ status: PIPELINE_RUN_STATUS.FAILED,
270
+ errorSummary: `${failedSteps.length} step(s) failed in level ${level}`,
271
+ });
272
+ return {
273
+ ok: false,
274
+ code: 500,
275
+ error: `Level ${level} had failed steps`,
276
+ };
277
+ }
278
+
279
+ if (blockedSteps.length > 0) {
280
+ console.log(
281
+ `[executePipelineDAG] Level ${level} has ${blockedSteps.length} blocked step(s), marking run as blocked`,
282
+ );
283
+ await updatePipelineRunStatus({
284
+ member,
285
+ runId,
286
+ status: PIPELINE_RUN_STATUS.BLOCKED,
287
+ });
288
+ return {
289
+ ok: false,
290
+ code: 400,
291
+ error: `${blockedSteps.length} step(s) are blocked - resolve blockers to continue`,
292
+ };
293
+ }
294
+
295
+ // After level completes, check for spawned instances and execute them
296
+ console.log(
297
+ `[executePipelineDAG] Checking for spawned instances after level ${level}`,
298
+ );
299
+
300
+ // First check all run steps to see what's there
301
+ const allRunSteps = await db.query.pipelineRunStep.findMany({
302
+ where: eq(schema.pipelineRunStep.runId, runId),
303
+ });
304
+
305
+ console.log(
306
+ `[executePipelineDAG] All run steps (${allRunSteps.length}):`,
307
+ allRunSteps.map((rs) => ({
308
+ id: rs.id,
309
+ stepId: rs.stepId,
310
+ spawnType: rs.spawnType,
311
+ status: rs.status,
312
+ stepRunIndex: rs.stepRunIndex,
313
+ })),
314
+ );
315
+
316
+ const spawnedInstances = await db.query.pipelineRunStep.findMany({
317
+ where: and(
318
+ eq(schema.pipelineRunStep.runId, runId),
319
+ eq(schema.pipelineRunStep.spawnType, "instance"),
320
+ eq(schema.pipelineRunStep.status, "pending"),
321
+ ),
322
+ });
323
+
324
+ console.log(
325
+ `[executePipelineDAG] Spawned instances query result (${spawnedInstances.length}):`,
326
+ spawnedInstances.map((si) => ({
327
+ id: si.id,
328
+ stepId: si.stepId,
329
+ spawnType: si.spawnType,
330
+ status: si.status,
331
+ stepRunIndex: si.stepRunIndex,
332
+ })),
333
+ );
334
+
335
+ if (spawnedInstances.length > 0) {
336
+ console.log(
337
+ `[executePipelineDAG] Found ${spawnedInstances.length} spawned instance(s), executing in parallel (max 10)`,
338
+ );
339
+
340
+ // Execute spawned instances with max 10 concurrency
341
+ const instanceLimit = pLimit(10);
342
+ const instancePromises = spawnedInstances.map((instance) =>
343
+ instanceLimit(() =>
344
+ executeInstanceStep({
345
+ member,
346
+ runId,
347
+ runStepId: instance.id,
348
+ stepId: instance.stepId!,
349
+ config,
350
+ }),
351
+ ),
352
+ );
353
+
354
+ await Promise.all(instancePromises);
355
+
356
+ // Check if any instances failed or are blocked
357
+ const completedInstances = await db.query.pipelineRunStep.findMany({
358
+ where: and(
359
+ eq(schema.pipelineRunStep.runId, runId),
360
+ inArray(
361
+ schema.pipelineRunStep.id,
362
+ spawnedInstances.map((i) => i.id),
363
+ ),
364
+ ),
365
+ });
366
+
367
+ const failedInstances = completedInstances.filter(
368
+ (i) => i.status === PIPELINE_RUN_STEP_STATUS.FAILED,
369
+ );
370
+ const blockedInstances = completedInstances.filter(
371
+ (i) => i.status === PIPELINE_RUN_STEP_STATUS.BLOCKED,
372
+ );
373
+
374
+ if (failedInstances.length > 0) {
375
+ console.error(
376
+ `[executePipelineDAG] ${failedInstances.length} spawned instance(s) failed`,
377
+ );
378
+ await updatePipelineRunStatus({
379
+ member,
380
+ runId,
381
+ status: PIPELINE_RUN_STATUS.FAILED,
382
+ errorSummary: `${failedInstances.length} spawned instance(s) failed in level ${level}`,
383
+ });
384
+ return {
385
+ ok: false,
386
+ code: 500,
387
+ error: `Spawned instances failed in level ${level}`,
388
+ };
389
+ }
390
+
391
+ if (blockedInstances.length > 0) {
392
+ console.log(
393
+ `[executePipelineDAG] ${blockedInstances.length} spawned instance(s) blocked`,
394
+ );
395
+ await updatePipelineRunStatus({
396
+ member,
397
+ runId,
398
+ status: PIPELINE_RUN_STATUS.BLOCKED,
399
+ });
400
+ return {
401
+ ok: false,
402
+ code: 400,
403
+ error: `${blockedInstances.length} spawned instance(s) are blocked`,
404
+ };
405
+ }
406
+
407
+ console.log(
408
+ `[executePipelineDAG] All ${spawnedInstances.length} spawned instance(s) completed successfully`,
409
+ );
410
+ }
411
+
412
+ console.log(
413
+ `[executePipelineDAG] Level ${level} completed successfully`,
414
+ );
415
+ } catch (error) {
416
+ console.error(`[executePipelineDAG] Level ${level} failed:`, error);
417
+ await updatePipelineRunStatus({
418
+ member,
419
+ runId,
420
+ status: PIPELINE_RUN_STATUS.FAILED,
421
+ errorSummary:
422
+ error instanceof Error ? error.message : "Level execution failed",
423
+ });
424
+ return {
425
+ ok: false,
426
+ code: 500,
427
+ error: `Level ${level} execution failed`,
428
+ };
429
+ }
430
+ }
431
+
432
+ // Update run status to completed
433
+ await updatePipelineRunStatus({
434
+ member,
435
+ runId,
436
+ status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
437
+ });
438
+
439
+ return {
440
+ ok: true,
441
+ code: 200,
442
+ data: { runId, status: PIPELINE_RUN_STATUS.COMPLETED },
443
+ };
444
+ } catch (error) {
445
+ console.error("[executePipelineDAG] Error:", error);
446
+ await updatePipelineRunStatus({
447
+ member,
448
+ runId,
449
+ status: PIPELINE_RUN_STATUS.FAILED,
450
+ errorSummary: error instanceof Error ? error.message : "Unknown error",
451
+ });
452
+ return {
453
+ ok: false,
454
+ code: 500,
455
+ error: "Pipeline execution failed",
456
+ };
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Dispatcher that routes step execution based on role.
462
+ * Handles backwards compatibility for steps without a role.
463
+ */
464
+ async function executeStepByRole({
465
+ member,
466
+ runId,
467
+ stepId,
468
+ config,
469
+ }: {
470
+ member: Member;
471
+ runId: string;
472
+ stepId: string;
473
+ config: {
474
+ model: ReturnType<typeof createLanguageModel>;
475
+ hatchet?: HatchetConfig;
476
+ };
477
+ }): Promise<void> {
478
+ // Get step definition
479
+ const stepResult = await getPipelineStepById({ member, stepId });
480
+
481
+ if (!stepResult.ok) {
482
+ throw new Error(`Step ${stepId} not found`);
483
+ }
484
+
485
+ const step = stepResult.data;
486
+
487
+ // Get the existing run step (created when the run was initialized)
488
+ const existingRunStep = await db.query.pipelineRunStep.findFirst({
489
+ where: and(
490
+ eq(schema.pipelineRunStep.runId, runId),
491
+ eq(schema.pipelineRunStep.stepId, stepId),
492
+ or(
493
+ eq(schema.pipelineRunStep.spawnType, "normal"),
494
+ eq(schema.pipelineRunStep.spawnType, "template"),
495
+ ),
496
+ ),
497
+ });
498
+
499
+ if (!existingRunStep) {
500
+ throw new Error(`Run step not found for step ${stepId}`);
501
+ }
502
+
503
+ // If it's a template step, check if we should spawn instances now
504
+ if (existingRunStep.spawnType === "template") {
505
+ console.log(
506
+ `[executeStepByRole] Template step ${stepId} encountered - checking if ready to spawn instances`,
507
+ );
508
+
509
+ // Check if parent dependencies are satisfied
510
+ const parentStepRunsResult = await getParentStepRuns({
511
+ member,
512
+ runStepId: existingRunStep.id,
513
+ });
514
+
515
+ if (!parentStepRunsResult.ok) {
516
+ console.error(
517
+ `[executeStepByRole] Failed to get parent steps for template ${stepId}`,
518
+ );
519
+ return;
520
+ }
521
+
522
+ const parentStepRuns = parentStepRunsResult.data;
523
+ const incompleteParents = parentStepRuns.filter(
524
+ (p) => p.status !== PIPELINE_RUN_STEP_STATUS.COMPLETED,
525
+ );
526
+
527
+ if (incompleteParents.length > 0) {
528
+ console.log(
529
+ `[executeStepByRole] Template ${stepId} not ready - ${incompleteParents.length} parent(s) not completed`,
530
+ );
531
+ return;
532
+ }
533
+
534
+ // All parents completed - try to spawn instances
535
+ console.log(
536
+ `[executeStepByRole] All parents completed for template ${stepId} - attempting to spawn instances`,
537
+ );
538
+
539
+ await spawnInstancesForTemplate({
540
+ member,
541
+ runId,
542
+ runStep: existingRunStep,
543
+ step,
544
+ });
545
+
546
+ // Mark template as spawned
547
+ await updatePipelineRunStepStatus({
548
+ member,
549
+ runStepId: existingRunStep.id,
550
+ status: "spawned",
551
+ });
552
+
553
+ console.log(`[executeStepByRole] Template ${stepId} marked as spawned`);
554
+ return;
555
+ }
556
+
557
+ const runStep = existingRunStep;
558
+
559
+ // Check for unresolved blockers before executing
560
+ const blockers = await db.query.pipelineRunStepBlocker.findMany({
561
+ where: and(
562
+ eq(schema.pipelineRunStepBlocker.runStepId, runStep.id),
563
+ eq(schema.pipelineRunStepBlocker.resolved, false),
564
+ ),
565
+ });
566
+
567
+ if (blockers.length > 0) {
568
+ console.log(
569
+ `[executeStepByRole] Step ${stepId} has ${blockers.length} unresolved blocker(s), marking as blocked`,
570
+ );
571
+ await updatePipelineRunStepStatus({
572
+ member,
573
+ runStepId: runStep.id,
574
+ status: PIPELINE_RUN_STEP_STATUS.BLOCKED,
575
+ });
576
+ // Don't throw - just skip this step for now
577
+ return;
578
+ }
579
+
580
+ try {
581
+ // Dispatch based on role (null role = legacy analysis step)
582
+ switch (step.role) {
583
+ case "extraction":
584
+ await executeExtractionStep({ member, runStep, step, config });
585
+ break;
586
+ case "integration":
587
+ await executeIntegrationStep({ member, runStep, step, config });
588
+ break;
589
+ case "analysis":
590
+ await executeAnalysisStep({ member, runStep, step, config });
591
+ break;
592
+ case "manual":
593
+ await executeManualStep({ member, runStep, step });
594
+ break;
595
+ default:
596
+ throw new Error(`Unknown step role: ${step.role}`);
597
+ }
598
+
599
+ // After successful completion:
600
+ // 1. Check for multi-group spawning (do this BEFORE marking complete)
601
+ await checkAndSpawnMultiGroups({ member, runStep, step });
602
+
603
+ // 2. Resolve this step's blockers and cascade to children
604
+ // This will mark parent_incomplete blockers on child steps as resolved
605
+ await resolveStepBlockersOnCompletion({
606
+ member,
607
+ runStepId: runStep.id,
608
+ });
609
+
610
+ // 3. Verify output submission was created (critical for downstream steps)
611
+ const outputMapping =
612
+ await db.query.pipelineRunStepOutputSubmission.findFirst({
613
+ where: eq(schema.pipelineRunStepOutputSubmission.runStepId, runStep.id),
614
+ });
615
+
616
+ if (
617
+ step.role !== "manual" &&
618
+ step.role !== "integration" &&
619
+ !outputMapping
620
+ ) {
621
+ console.error(
622
+ `[executeStepByRole] Step ${stepId} completed but no output submission was created!`,
623
+ );
624
+ throw new Error("Step completed without creating output submission");
625
+ }
626
+
627
+ console.log(
628
+ `[executeStepByRole] Step ${stepId} completed successfully with output submission: ${outputMapping?.submissionId ?? "N/A"}`,
629
+ );
630
+ } catch (error) {
631
+ console.error(`[executeStepByRole] Step ${stepId} failed:`, error);
632
+ throw error;
633
+ }
634
+ }
635
+
636
+ /**
637
+ * Execute a spawned instance step
638
+ * Similar to executeStepByRole but operates on a specific runStepId (instance)
639
+ */
640
+ async function executeInstanceStep({
641
+ member,
642
+ runId,
643
+ runStepId,
644
+ stepId,
645
+ config,
646
+ }: {
647
+ member: Member;
648
+ runId: string;
649
+ runStepId: string;
650
+ stepId: string;
651
+ config: {
652
+ model: ReturnType<typeof createLanguageModel>;
653
+ hatchet?: HatchetConfig;
654
+ };
655
+ }): Promise<void> {
656
+ // Get step definition
657
+ const stepResult = await getPipelineStepById({ member, stepId });
658
+
659
+ if (!stepResult.ok) {
660
+ throw new Error(`Step ${stepId} not found`);
661
+ }
662
+
663
+ const step = stepResult.data;
664
+
665
+ // Get the specific instance run step
666
+ const runStep = await db.query.pipelineRunStep.findFirst({
667
+ where: eq(schema.pipelineRunStep.id, runStepId),
668
+ });
669
+
670
+ if (!runStep) {
671
+ throw new Error(`Run step ${runStepId} not found`);
672
+ }
673
+
674
+ if (runStep.spawnType !== "instance") {
675
+ throw new Error(`Run step ${runStepId} is not an instance`);
676
+ }
677
+
678
+ // Check for unresolved blockers before executing
679
+ const blockers = await db.query.pipelineRunStepBlocker.findMany({
680
+ where: and(
681
+ eq(schema.pipelineRunStepBlocker.runStepId, runStep.id),
682
+ eq(schema.pipelineRunStepBlocker.resolved, false),
683
+ ),
684
+ });
685
+
686
+ if (blockers.length > 0) {
687
+ console.log(
688
+ `[executeInstanceStep] Instance ${runStepId} has ${blockers.length} unresolved blocker(s), marking as blocked`,
689
+ );
690
+ await updatePipelineRunStepStatus({
691
+ member,
692
+ runStepId: runStep.id,
693
+ status: PIPELINE_RUN_STEP_STATUS.BLOCKED,
694
+ });
695
+ return;
696
+ }
697
+
698
+ try {
699
+ // Only analysis steps can have instances
700
+ if (step.role !== "analysis" && step.role !== null) {
701
+ throw new Error(`Step role ${step.role} cannot have instances`);
702
+ }
703
+
704
+ await executeAnalysisStep({ member, runStep, step, config });
705
+
706
+ // Resolve blockers after completion
707
+ await resolveStepBlockersOnCompletion({
708
+ member,
709
+ runStepId: runStep.id,
710
+ });
711
+
712
+ console.log(
713
+ `[executeInstanceStep] Instance ${runStepId} completed successfully`,
714
+ );
715
+ } catch (error) {
716
+ console.error(`[executeInstanceStep] Instance ${runStepId} failed:`, error);
717
+ throw error;
718
+ }
719
+ }
720
+
721
+ /**
722
+ * Execute an extraction step (document extraction)
723
+ * Triggers async Hatchet workflow for each document via pushDoc()
724
+ */
725
+ async function executeExtractionStep({
726
+ member,
727
+ runStep,
728
+ step,
729
+ config,
730
+ }: {
731
+ member: Member;
732
+ runStep: PipelineRunStep;
733
+ step: typeof schema.pipelineStep.$inferSelect & {
734
+ outputForms: Array<{
735
+ id: string;
736
+ stepId: string;
737
+ formId: string;
738
+ createdAt: Date;
739
+ }>;
740
+ };
741
+ config: {
742
+ model: ReturnType<typeof createLanguageModel>;
743
+ hatchet?: HatchetConfig;
744
+ };
745
+ }): Promise<void> {
746
+ try {
747
+ await updatePipelineRunStepStatus({
748
+ member,
749
+ runStepId: runStep.id,
750
+ status: PIPELINE_RUN_STEP_STATUS.RUNNING,
751
+ });
752
+
753
+ // 1. Get documents linked to this step run
754
+ const documentsResult = await getStepDocuments({
755
+ member,
756
+ runStepId: runStep.id,
757
+ });
758
+
759
+ if (!documentsResult.ok) {
760
+ throw new Error("Failed to load step documents");
761
+ }
762
+
763
+ if (documentsResult.data.length === 0) {
764
+ throw new Error("No documents linked to extraction step");
765
+ }
766
+
767
+ // 2. Get output form
768
+ const outputFormId = step.outputForms[0]?.formId;
769
+ if (!outputFormId) {
770
+ throw new Error("Extraction step missing output form");
771
+ }
772
+
773
+ // 3. Get pipeline run to access dealId
774
+ const pipelineRun = await db.query.pipelineRun.findFirst({
775
+ where: eq(schema.pipelineRun.id, runStep.runId),
776
+ });
777
+ if (!pipelineRun) {
778
+ throw new Error("Pipeline run not found");
779
+ }
780
+
781
+ // 4. Create output submission for extracted data
782
+ const outputSubmission = await createSubmissionWithConversation({
783
+ member,
784
+ data: {
785
+ formId: outputFormId,
786
+ dealId: pipelineRun.dealId,
787
+ name:
788
+ runStep.spawnType === "instance"
789
+ ? `${step.name} - Instance ${runStep.stepRunIndex! + 1}`
790
+ : `${step.name} output`,
791
+ status: "draft",
792
+ type: "extraction",
793
+ },
794
+ });
795
+
796
+ await syncSubmissionToForm({
797
+ member,
798
+ submissionId: outputSubmission.id,
799
+ });
800
+
801
+ // 5. For each document, create documentExtraction and trigger Hatchet workflow
802
+ if (!config.hatchet) {
803
+ throw new Error("Hatchet config required for extraction steps");
804
+ }
805
+
806
+ const hatchetClient = new HatchetClientMgr(config.hatchet);
807
+ const extractionIds: string[] = [];
808
+
809
+ for (const document of documentsResult.data) {
810
+ // Create documentExtraction record
811
+ const [documentExtraction] = await db
812
+ .insert(schema.documentExtraction)
813
+ .values({
814
+ documentId: document.id,
815
+ submissionId: outputSubmission.id,
816
+ status: "pending",
817
+ extractionModel: "general",
818
+ extractionTrigger: "api",
819
+ })
820
+ .returning({ id: schema.documentExtraction.id });
821
+
822
+ if (!documentExtraction) {
823
+ throw new Error(
824
+ `Failed to create documentExtraction for document ${document.id}`,
825
+ );
826
+ }
827
+
828
+ extractionIds.push(documentExtraction.id);
829
+
830
+ // Trigger Hatchet extraction workflow
831
+ await hatchetClient.pushDoc(
832
+ documentExtraction.id,
833
+ "general",
834
+ step.prompt || undefined,
835
+ "medium",
836
+ );
837
+
838
+ console.log(
839
+ `[executeExtractionStep] Triggered extraction for document: ${document.name} (extractionId: ${documentExtraction.id})`,
840
+ );
841
+ }
842
+
843
+ // 6. Link output submission to run step
844
+ await db.insert(schema.pipelineRunStepOutputSubmission).values({
845
+ runStepId: runStep.id,
846
+ submissionId: outputSubmission.id,
847
+ });
848
+
849
+ console.log(
850
+ `[executeExtractionStep] Waiting for ${extractionIds.length} extraction(s) to complete...`,
851
+ );
852
+
853
+ // 7. Poll for extraction completion
854
+ const MAX_WAIT_TIME_MS = 30 * 60 * 1000; // 30 minutes
855
+ const POLL_INTERVAL_MS = 5000; // 5 seconds
856
+ const startTime = Date.now();
857
+
858
+ while (true) {
859
+ // Check if we've exceeded max wait time
860
+ if (Date.now() - startTime > MAX_WAIT_TIME_MS) {
861
+ throw new Error(
862
+ `Extraction timed out after ${MAX_WAIT_TIME_MS / 1000} seconds`,
863
+ );
864
+ }
865
+
866
+ // Check status of all extractions
867
+ const extractions = await db.query.documentExtraction.findMany({
868
+ where: inArray(schema.documentExtraction.id, extractionIds),
869
+ columns: { id: true, status: true },
870
+ });
871
+
872
+ const allDone = extractions.every((e) => e.status === "done");
873
+ const anyFailed = extractions.some((e) => e.status === "failed");
874
+
875
+ if (anyFailed) {
876
+ const failedIds = extractions
877
+ .filter((e) => e.status === "failed")
878
+ .map((e) => e.id);
879
+ throw new Error(`Extraction failed for: ${failedIds.join(", ")}`);
880
+ }
881
+
882
+ if (allDone) {
883
+ console.log(
884
+ `[executeExtractionStep] All ${extractionIds.length} extraction(s) completed successfully`,
885
+ );
886
+ break;
887
+ }
888
+
889
+ // Wait before next poll
890
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
891
+ }
892
+
893
+ // 8. Mark step as completed
894
+ await updatePipelineRunStepStatus({
895
+ member,
896
+ runStepId: runStep.id,
897
+ status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
898
+ });
899
+
900
+ console.log(
901
+ `[executeExtractionStep] Completed extraction step with ${documentsResult.data.length} document(s), output submission: ${outputSubmission.id}`,
902
+ );
903
+ } catch (error) {
904
+ console.error("[executeExtractionStep] Error:", error);
905
+ await updatePipelineRunStepStatus({
906
+ member,
907
+ runStepId: runStep.id,
908
+ status: PIPELINE_RUN_STATUS.FAILED,
909
+ error: error instanceof Error ? error.message : "Unknown error",
910
+ });
911
+ throw error;
912
+ }
913
+ }
914
+
915
+ /**
916
+ * Execute an integration step (API call)
917
+ */
918
+ async function executeIntegrationStep({
919
+ member,
920
+ runStep,
921
+ step,
922
+ config,
923
+ }: {
924
+ member: Member;
925
+ runStep: PipelineRunStep;
926
+ step: typeof schema.pipelineStep.$inferSelect & {
927
+ outputForms: Array<{
928
+ id: string;
929
+ stepId: string;
930
+ formId: string;
931
+ createdAt: Date;
932
+ }>;
933
+ };
934
+ config: { model: ReturnType<typeof createLanguageModel> };
935
+ }): Promise<void> {
936
+ try {
937
+ await updatePipelineRunStepStatus({
938
+ member,
939
+ runStepId: runStep.id,
940
+ status: PIPELINE_RUN_STEP_STATUS.RUNNING,
941
+ });
942
+
943
+ // Get integration config from separate table
944
+ const integrationConfigResult = await getStepIntegrationConfig({
945
+ member,
946
+ stepId: step.id,
947
+ });
948
+
949
+ if (!integrationConfigResult.ok || !integrationConfigResult.data) {
950
+ throw new Error("Integration step missing config");
951
+ }
952
+
953
+ const integrationConfig = integrationConfigResult.data;
954
+
955
+ // Get parent step outputs to build request
956
+ const parentStepRunsResult = await getParentStepRuns({
957
+ member,
958
+ runStepId: runStep.id,
959
+ });
960
+
961
+ if (!parentStepRunsResult.ok) {
962
+ throw new Error("Failed to load parent step outputs");
963
+ }
964
+
965
+ const parentStepRuns = parentStepRunsResult.data;
966
+
967
+ // Load output submissions for each parent step run
968
+ const parentSubmissions = await Promise.all(
969
+ parentStepRuns.map(async (p) => {
970
+ const outputMapping =
971
+ await db.query.pipelineRunStepOutputSubmission.findFirst({
972
+ where: eq(schema.pipelineRunStepOutputSubmission.runStepId, p.id),
973
+ });
974
+ if (!outputMapping) return null;
975
+ return getDenormSubmission({
976
+ member,
977
+ submissionId: outputMapping.submissionId,
978
+ withCitations: true,
979
+ withTransformations: true,
980
+ });
981
+ }),
982
+ );
983
+
984
+ // Filter out nulls
985
+ const validParentSubmissions = parentSubmissions.filter(
986
+ (s): s is NonNullable<typeof s> => s !== null,
987
+ );
988
+
989
+ // Build request from template
990
+ const request = await buildHttpRequest({
991
+ config: {
992
+ method: integrationConfig.method as "GET" | "POST" | "PUT" | "DELETE",
993
+ urlTemplate: integrationConfig.urlTemplate,
994
+ headers: integrationConfig.headers,
995
+ bodyTemplate: integrationConfig.bodyTemplate ?? undefined,
996
+ },
997
+ parentData: validParentSubmissions,
998
+ });
999
+
1000
+ // Make API call
1001
+ const startTime = Date.now();
1002
+ let statusCode: number | undefined;
1003
+ let responseBody: unknown;
1004
+ let error: string | undefined;
1005
+
1006
+ try {
1007
+ const response = await fetch(request.url, {
1008
+ method: request.method,
1009
+ headers: request.headers,
1010
+ body: request.body,
1011
+ });
1012
+
1013
+ statusCode = response.status;
1014
+ responseBody = await response.json();
1015
+ } catch (err) {
1016
+ error = err instanceof Error ? err.message : "Request failed";
1017
+ }
1018
+
1019
+ const durationMs = Date.now() - startTime;
1020
+
1021
+ // Log request call
1022
+ await logRequestCall({
1023
+ member,
1024
+ runStepId: runStep.id,
1025
+ method: request.method,
1026
+ url: request.url,
1027
+ requestHeaders: request.headers,
1028
+ requestBody: request.body,
1029
+ statusCode,
1030
+ responseBody,
1031
+ durationMs,
1032
+ error,
1033
+ });
1034
+
1035
+ if (error || !statusCode || statusCode >= 400) {
1036
+ throw new Error(error || `HTTP ${statusCode}`);
1037
+ }
1038
+
1039
+ // Parse response into output form if configured
1040
+ if (step.outputForms.length > 0) {
1041
+ // TODO: Parse responseBody into form structure and store
1042
+ // For now, mark as completed without storing output
1043
+ }
1044
+
1045
+ await updatePipelineRunStepStatus({
1046
+ member,
1047
+ runStepId: runStep.id,
1048
+ status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
1049
+ });
1050
+ } catch (error) {
1051
+ await updatePipelineRunStepStatus({
1052
+ member,
1053
+ runStepId: runStep.id,
1054
+ status: PIPELINE_RUN_STATUS.FAILED,
1055
+ error: error instanceof Error ? error.message : "Integration step failed",
1056
+ });
1057
+ throw error;
1058
+ }
1059
+ }
1060
+
1061
+ /**
1062
+ * Execute an analysis/transformation step (LLM-based)
1063
+ * Enhanced to support multi-parent inputs
1064
+ */
1065
+ async function executeAnalysisStep({
1066
+ member,
1067
+ runStep,
1068
+ step,
1069
+ config,
1070
+ }: {
1071
+ member: Member;
1072
+ runStep: PipelineRunStep;
1073
+ step: typeof schema.pipelineStep.$inferSelect & {
1074
+ outputForms: Array<{
1075
+ id: string;
1076
+ stepId: string;
1077
+ formId: string;
1078
+ createdAt: Date;
1079
+ }>;
1080
+ };
1081
+ config: { model: ReturnType<typeof createLanguageModel> };
1082
+ }): Promise<void> {
1083
+ try {
1084
+ await updatePipelineRunStepStatus({
1085
+ member,
1086
+ runStepId: runStep.id,
1087
+ status: PIPELINE_RUN_STEP_STATUS.RUNNING,
1088
+ });
1089
+
1090
+ // Validate output form exists
1091
+ if (step.outputForms.length === 0) {
1092
+ throw new Error("Analysis step missing output form");
1093
+ }
1094
+
1095
+ // Get parent step runs with dependency configurations
1096
+ const parentStepRunsResult = await getParentStepRunsWithDependencies({
1097
+ member,
1098
+ runStepId: runStep.id,
1099
+ });
1100
+
1101
+ if (!parentStepRunsResult.ok) {
1102
+ throw new Error("Failed to load parent step runs");
1103
+ }
1104
+
1105
+ const parentStepRuns = parentStepRunsResult.data;
1106
+
1107
+ // Load output submissions for each parent step run
1108
+ const parentSubmissions = await Promise.all(
1109
+ parentStepRuns.map(
1110
+ async (p: {
1111
+ id: string;
1112
+ stepId: string | null;
1113
+ status: string;
1114
+ formIdMapping: Record<string, string[]> | null;
1115
+ multiFormGroupId: string | null;
1116
+ }) => {
1117
+ const outputMapping =
1118
+ await db.query.pipelineRunStepOutputSubmission.findFirst({
1119
+ where: eq(schema.pipelineRunStepOutputSubmission.runStepId, p.id),
1120
+ });
1121
+ if (!outputMapping) return null;
1122
+
1123
+ const submission = await getDenormSubmission({
1124
+ member,
1125
+ submissionId: outputMapping.submissionId,
1126
+ withCitations: true,
1127
+ withTransformations: true,
1128
+ });
1129
+
1130
+ if (!submission) return null;
1131
+
1132
+ // Apply formIdMapping filter if specified
1133
+ if (p.formIdMapping && Object.keys(p.formIdMapping).length > 0) {
1134
+ // Filter submission groups based on formIdMapping
1135
+ // formIdMapping is { [formId]: [groupId1, groupId2, ...] }
1136
+ const allowedGroupIds = new Set<string>();
1137
+ for (const groupIds of Object.values(p.formIdMapping)) {
1138
+ (groupIds as string[]).forEach((gid: string) =>
1139
+ allowedGroupIds.add(gid),
1140
+ );
1141
+ }
1142
+
1143
+ // Filter groups to only include those in formIdMapping
1144
+ const filteredGroups = submission.groups.filter((g) =>
1145
+ allowedGroupIds.has(g.formGroupId),
1146
+ );
1147
+
1148
+ return { ...submission, groups: filteredGroups };
1149
+ }
1150
+
1151
+ return submission;
1152
+ },
1153
+ ),
1154
+ );
1155
+
1156
+ // Filter out nulls
1157
+ const validParentSubmissions = parentSubmissions.filter(
1158
+ (s): s is NonNullable<typeof s> => s !== null,
1159
+ );
1160
+
1161
+ // Check if this is a multi step - if so, filter parent data to specific group
1162
+ const groupResult = await getGroupForStepRun({
1163
+ member,
1164
+ runStepId: runStep.id,
1165
+ });
1166
+
1167
+ let filteredParentSubmissions = validParentSubmissions;
1168
+ if (groupResult.ok) {
1169
+ // This is a multi step, filter to the specific group
1170
+ const targetGroup = groupResult.data;
1171
+
1172
+ // Build a map of which parent steps have the multiFormGroupId
1173
+ const parentStepsWithMultiGroup = new Set<string>();
1174
+ for (const parent of parentStepRuns) {
1175
+ if (parent.multiFormGroupId === targetGroup.formGroupId) {
1176
+ parentStepsWithMultiGroup.add(parent.id);
1177
+ }
1178
+ }
1179
+
1180
+ // Filter each parent submission to only include the specific group instance
1181
+ // BUT only if that parent has the multiFormGroupId configured
1182
+ filteredParentSubmissions = validParentSubmissions.map(
1183
+ (submission, idx) => {
1184
+ const parentStepRun = parentStepRuns[idx];
1185
+ if (!parentStepRun) return submission;
1186
+
1187
+ // If this parent doesn't have the multiFormGroupId, return full data
1188
+ if (!parentStepsWithMultiGroup.has(parentStepRun.id)) {
1189
+ return submission;
1190
+ }
1191
+
1192
+ // This parent has the multiFormGroupId, so filter to the specific group
1193
+ const matchingGroup = submission.groups.find(
1194
+ (g) => g.formGroupId === targetGroup.formGroupId,
1195
+ );
1196
+
1197
+ if (!matchingGroup) {
1198
+ // No matching group, return submission with empty groups
1199
+ return { ...submission, groups: [] };
1200
+ }
1201
+
1202
+ // Return submission with only the matching group
1203
+ return { ...submission, groups: [matchingGroup] };
1204
+ },
1205
+ );
1206
+ }
1207
+
1208
+ // Create output submission
1209
+ const sourceSubmission = validParentSubmissions[0];
1210
+ if (!sourceSubmission) {
1211
+ throw new Error("No parent submissions found");
1212
+ }
1213
+
1214
+ // Get full submission to access dealId
1215
+ const fullSubmissionResult = await getRuntimeSubmission({
1216
+ member,
1217
+ submissionId: sourceSubmission.id,
1218
+ });
1219
+
1220
+ if (!fullSubmissionResult.ok) {
1221
+ throw new Error("Failed to load source submission");
1222
+ }
1223
+
1224
+ const outputFormId = step.outputForms[0]?.formId;
1225
+ if (!outputFormId) {
1226
+ throw new Error("Analysis step missing output form");
1227
+ }
1228
+
1229
+ const outputSubmission = await createSubmissionWithConversation({
1230
+ member,
1231
+ data: {
1232
+ formId: outputFormId,
1233
+ dealId: fullSubmissionResult.data.dealId,
1234
+ name:
1235
+ runStep.spawnType === "instance"
1236
+ ? `${step.name} - Instance ${runStep.stepRunIndex! + 1}`
1237
+ : `${step.name} output`,
1238
+ status: "draft",
1239
+ type: "transformation",
1240
+ },
1241
+ });
1242
+
1243
+ await syncSubmissionToForm({
1244
+ member,
1245
+ submissionId: outputSubmission.id,
1246
+ });
1247
+
1248
+ // Get output form groups
1249
+ const outputSubmissionWithData = await getSubmissionWithData({
1250
+ member,
1251
+ submissionId: outputSubmission.id,
1252
+ typeFilter: "transformation",
1253
+ });
1254
+
1255
+ if (!outputSubmissionWithData?.form?.groups) {
1256
+ throw new Error("Failed to load output form");
1257
+ }
1258
+
1259
+ const outputFormGroups = outputSubmissionWithData.form.groups;
1260
+
1261
+ // If this is a spawned instance, extract the spawning group data
1262
+ let spawningGroupData: {
1263
+ groupName: string;
1264
+ data: DenormSubmission["groups"][0];
1265
+ } | null = null;
1266
+
1267
+ if (groupResult.ok) {
1268
+ const targetGroup = groupResult.data;
1269
+ // Find the spawning group in the filtered parent submissions
1270
+ for (const submission of filteredParentSubmissions) {
1271
+ const matchingGroup = submission.groups.find(
1272
+ (g) => g.formGroupId === targetGroup.formGroupId,
1273
+ );
1274
+ if (matchingGroup) {
1275
+ spawningGroupData = {
1276
+ groupName: matchingGroup.name,
1277
+ data: matchingGroup,
1278
+ };
1279
+ break;
1280
+ }
1281
+ }
1282
+ }
1283
+
1284
+ // Run the LLM-based analysis
1285
+ const stepResult = await runStepExecute({
1286
+ member,
1287
+ runId: runStep.runId,
1288
+ runStepId: runStep.id,
1289
+ step: {
1290
+ id: step.id,
1291
+ description: step.description,
1292
+ prompt: step.prompt,
1293
+ toolsAllowed: step.toolsAllowed,
1294
+ outputFormIds: step.outputForms.map((of) => of.formId),
1295
+ // Legacy fields - no longer used in new architecture but required by function signature
1296
+ stepOrder: 0,
1297
+ parentStepId: null,
1298
+ },
1299
+ inputSubmissions: filteredParentSubmissions,
1300
+ resourceSubmissions: [], // TODO: Load pipeline resources
1301
+ outputFormGroups,
1302
+ conversationId: outputSubmission.conversationId,
1303
+ spawningGroupData,
1304
+ config,
1305
+ });
1306
+
1307
+ // Store output
1308
+ if (stepResult.output?.data) {
1309
+ const storeResult = await storePipelineOutputData({
1310
+ member,
1311
+ outputSubmissionId: outputSubmission.id,
1312
+ outputData: stepResult.output.data,
1313
+ });
1314
+
1315
+ if (!storeResult.ok) {
1316
+ throw new Error("Failed to store pipeline output data");
1317
+ }
1318
+ }
1319
+
1320
+ // Link output submission to run step
1321
+ await db.insert(schema.pipelineRunStepOutputSubmission).values({
1322
+ runStepId: runStep.id,
1323
+ submissionId: outputSubmission.id,
1324
+ });
1325
+
1326
+ await updatePipelineRunStepStatus({
1327
+ member,
1328
+ runStepId: runStep.id,
1329
+ status: stepResult.status,
1330
+ error: stepResult.error,
1331
+ });
1332
+
1333
+ if (stepResult.status === "failed") {
1334
+ throw new Error(stepResult.error || "Analysis step failed");
1335
+ }
1336
+ } catch (error) {
1337
+ await updatePipelineRunStepStatus({
1338
+ member,
1339
+ runStepId: runStep.id,
1340
+ status: PIPELINE_RUN_STATUS.FAILED,
1341
+ error: error instanceof Error ? error.message : "Analysis step failed",
1342
+ });
1343
+ throw error;
1344
+ }
1345
+ }
1346
+
1347
+ /**
1348
+ * Execute a manual step (waits for human input)
1349
+ */
1350
+ async function executeManualStep({
1351
+ member,
1352
+ runStep,
1353
+ step,
1354
+ }: {
1355
+ member: Member;
1356
+ runStep: PipelineRunStep;
1357
+ step: typeof schema.pipelineStep.$inferSelect & {
1358
+ outputForms: Array<{
1359
+ id: string;
1360
+ stepId: string;
1361
+ formId: string;
1362
+ createdAt: Date;
1363
+ }>;
1364
+ };
1365
+ }): Promise<void> {
1366
+ try {
1367
+ await updatePipelineRunStepStatus({
1368
+ member,
1369
+ runStepId: runStep.id,
1370
+ status: PIPELINE_RUN_STEP_STATUS.RUNNING,
1371
+ });
1372
+
1373
+ // Check if manual output submission has been provided
1374
+ const outputMapping =
1375
+ await db.query.pipelineRunStepOutputSubmission.findFirst({
1376
+ where: eq(schema.pipelineRunStepOutputSubmission.runStepId, runStep.id),
1377
+ });
1378
+
1379
+ if (!outputMapping) {
1380
+ // No output submission yet - create a blocker
1381
+ console.log(
1382
+ `[executeManualStep] Step ${step.name} blocked - waiting for manual output submission`,
1383
+ );
1384
+
1385
+ // Check if blocker already exists
1386
+ const existingBlocker = await db.query.pipelineRunStepBlocker.findFirst({
1387
+ where: and(
1388
+ eq(schema.pipelineRunStepBlocker.runStepId, runStep.id),
1389
+ eq(schema.pipelineRunStepBlocker.blockerType, "missing_manual_input"),
1390
+ eq(schema.pipelineRunStepBlocker.resolved, false),
1391
+ ),
1392
+ });
1393
+
1394
+ if (!existingBlocker) {
1395
+ await db.insert(schema.pipelineRunStepBlocker).values({
1396
+ runStepId: runStep.id,
1397
+ blockerType: "missing_manual_input",
1398
+ blockerDetails: {
1399
+ type: "missing_manual_input",
1400
+ stepName: step.name,
1401
+ stepDescription: step.description,
1402
+ },
1403
+ resolved: false,
1404
+ });
1405
+ }
1406
+
1407
+ await updatePipelineRunStepStatus({
1408
+ member,
1409
+ runStepId: runStep.id,
1410
+ status: PIPELINE_RUN_STEP_STATUS.BLOCKED,
1411
+ });
1412
+
1413
+ return;
1414
+ }
1415
+
1416
+ // Output submission exists - mark as completed
1417
+ await updatePipelineRunStepStatus({
1418
+ member,
1419
+ runStepId: runStep.id,
1420
+ status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
1421
+ });
1422
+
1423
+ console.log(
1424
+ `[executeManualStep] Step ${step.name} completed - manual output submission provided`,
1425
+ );
1426
+ } catch (error) {
1427
+ console.error("[executeManualStep] Error:", error);
1428
+ await updatePipelineRunStepStatus({
1429
+ member,
1430
+ runStepId: runStep.id,
1431
+ status: PIPELINE_RUN_STATUS.FAILED,
1432
+ error: error instanceof Error ? error.message : "Manual step failed",
1433
+ });
1434
+ throw error;
1435
+ }
1436
+ }
1437
+
1438
+ /**
1439
+ * Spawn instances for a template step based on parent multi-group output
1440
+ */
1441
+ async function spawnInstancesForTemplate({
1442
+ member,
1443
+ runId,
1444
+ runStep,
1445
+ step,
1446
+ }: {
1447
+ member: Member;
1448
+ runId: string;
1449
+ runStep: PipelineRunStep;
1450
+ step: typeof schema.pipelineStep.$inferSelect & {
1451
+ outputForms: Array<{
1452
+ id: string;
1453
+ stepId: string;
1454
+ formId: string;
1455
+ createdAt: Date;
1456
+ }>;
1457
+ };
1458
+ }): Promise<void> {
1459
+ console.log(
1460
+ `[spawnInstancesForTemplate] Starting spawn for template step ${step.id}`,
1461
+ );
1462
+
1463
+ // Get parent step runs with dependency info
1464
+ const parentStepRunsResult = await getParentStepRunsWithDependencies({
1465
+ member,
1466
+ runStepId: runStep.id,
1467
+ });
1468
+
1469
+ if (!parentStepRunsResult.ok) {
1470
+ console.error(
1471
+ `[spawnInstancesForTemplate] Failed to get parent dependencies for step ${step.id}`,
1472
+ );
1473
+ return;
1474
+ }
1475
+
1476
+ const parentStepRuns = parentStepRunsResult.data;
1477
+ console.log(
1478
+ `[spawnInstancesForTemplate] Found ${parentStepRuns.length} parent step(s)`,
1479
+ );
1480
+
1481
+ // Find parent with multiFormGroupId
1482
+ const multiParent = parentStepRuns.find((p) => p.multiFormGroupId !== null);
1483
+
1484
+ if (!multiParent) {
1485
+ console.warn(
1486
+ `[spawnInstancesForTemplate] No multi-group parent found for template step ${step.id}`,
1487
+ );
1488
+ return;
1489
+ }
1490
+
1491
+ console.log(
1492
+ `[spawnInstancesForTemplate] Found multi-group parent with multiFormGroupId: ${multiParent.multiFormGroupId}`,
1493
+ );
1494
+
1495
+ // Get parent output submission
1496
+ const parentOutputMapping =
1497
+ await db.query.pipelineRunStepOutputSubmission.findFirst({
1498
+ where: eq(
1499
+ schema.pipelineRunStepOutputSubmission.runStepId,
1500
+ multiParent.id,
1501
+ ),
1502
+ });
1503
+
1504
+ if (!parentOutputMapping) {
1505
+ console.error(
1506
+ `[spawnInstancesForTemplate] No output submission found for parent step ${multiParent.id}`,
1507
+ );
1508
+ return;
1509
+ }
1510
+
1511
+ console.log(
1512
+ `[spawnInstancesForTemplate] Parent output submission: ${parentOutputMapping.submissionId}`,
1513
+ );
1514
+
1515
+ // Get submission groups using DAL function
1516
+ const groupsResult = await getSubmissionGroups({
1517
+ member,
1518
+ submissionId: parentOutputMapping.submissionId,
1519
+ formGroupId: multiParent.multiFormGroupId!,
1520
+ });
1521
+
1522
+ if (!groupsResult.ok || groupsResult.data.length === 0) {
1523
+ console.log(
1524
+ `[spawnInstancesForTemplate] No groups found for formGroupId ${multiParent.multiFormGroupId}`,
1525
+ );
1526
+ return;
1527
+ }
1528
+
1529
+ const groups = groupsResult.data;
1530
+ console.log(
1531
+ `[spawnInstancesForTemplate] Spawning ${groups.length} instance(s) for step ${step.id}`,
1532
+ );
1533
+
1534
+ // Create instance run steps for each group
1535
+ for (const [index, group] of groups.entries()) {
1536
+ console.log(
1537
+ `[spawnInstancesForTemplate] Creating instance ${index + 1}/${groups.length} for group ${group.id}`,
1538
+ );
1539
+
1540
+ await createPipelineRunStep({
1541
+ member,
1542
+ runId,
1543
+ stepId: step.id,
1544
+ spawnType: "instance",
1545
+ submissionGroupId: group.id,
1546
+ stepRunIndex: index,
1547
+ });
1548
+ }
1549
+
1550
+ console.log(
1551
+ `[spawnInstancesForTemplate] Successfully spawned ${groups.length} instance(s) for step ${step.id}`,
1552
+ );
1553
+ }
1554
+
1555
+ /**
1556
+ * Check if step output has multiple groups and spawn instance run steps for child steps
1557
+ */
1558
+ async function checkAndSpawnMultiGroups({
1559
+ member,
1560
+ runStep,
1561
+ step,
1562
+ }: {
1563
+ member: Member;
1564
+ runStep: PipelineRunStep;
1565
+ step: typeof schema.pipelineStep.$inferSelect;
1566
+ }): Promise<void> {
1567
+ try {
1568
+ // Only extraction and analysis steps can produce multi-group outputs
1569
+ if (
1570
+ step.role !== "extraction" &&
1571
+ step.role !== "analysis" &&
1572
+ step.role !== null
1573
+ ) {
1574
+ return;
1575
+ }
1576
+
1577
+ // Get output submission if exists
1578
+ const outputMapping =
1579
+ await db.query.pipelineRunStepOutputSubmission.findFirst({
1580
+ where: eq(schema.pipelineRunStepOutputSubmission.runStepId, runStep.id),
1581
+ });
1582
+
1583
+ if (!outputMapping) {
1584
+ return;
1585
+ }
1586
+
1587
+ // Get child steps that depend on this step with multiFormGroupId
1588
+ const childDepsResult = await getStepDependenciesByParentStepId({
1589
+ member,
1590
+ parentStepId: step.id,
1591
+ });
1592
+
1593
+ if (!childDepsResult.ok || childDepsResult.data.length === 0) {
1594
+ return;
1595
+ }
1596
+
1597
+ const multiDeps = childDepsResult.data.filter((d) => d.multiFormGroupId);
1598
+
1599
+ if (multiDeps.length === 0) {
1600
+ return;
1601
+ }
1602
+
1603
+ console.log(
1604
+ `[checkAndSpawnMultiGroups] Found ${multiDeps.length} multi-dependency child step(s)`,
1605
+ );
1606
+
1607
+ for (const dep of multiDeps) {
1608
+ // Find the template run step that was created at run creation
1609
+ const templateRunStep = await db.query.pipelineRunStep.findFirst({
1610
+ where: and(
1611
+ eq(schema.pipelineRunStep.runId, runStep.runId),
1612
+ eq(schema.pipelineRunStep.stepId, dep.childStepId),
1613
+ eq(schema.pipelineRunStep.spawnType, "template"),
1614
+ ),
1615
+ });
1616
+
1617
+ if (!templateRunStep) {
1618
+ console.warn(
1619
+ `[checkAndSpawnMultiGroups] Template run step not found for child step ${dep.childStepId}`,
1620
+ );
1621
+ continue;
1622
+ }
1623
+
1624
+ // Get submission groups using new DAL function
1625
+ const groupsResult = await getSubmissionGroups({
1626
+ member,
1627
+ submissionId: outputMapping.submissionId,
1628
+ formGroupId: dep.multiFormGroupId!,
1629
+ });
1630
+
1631
+ if (!groupsResult.ok || groupsResult.data.length === 0) {
1632
+ console.log(
1633
+ `[checkAndSpawnMultiGroups] No groups found for formGroupId ${dep.multiFormGroupId}`,
1634
+ );
1635
+ continue;
1636
+ }
1637
+
1638
+ const groups = groupsResult.data;
1639
+ console.log(
1640
+ `[checkAndSpawnMultiGroups] Spawning ${groups.length} instance(s) for step ${dep.childStepId}`,
1641
+ );
1642
+
1643
+ // Create instance run steps for each group
1644
+ for (const [index, group] of groups.entries()) {
1645
+ await createPipelineRunStep({
1646
+ member,
1647
+ runId: runStep.runId,
1648
+ stepId: dep.childStepId,
1649
+ spawnType: "instance",
1650
+ submissionGroupId: group.id,
1651
+ stepRunIndex: index,
1652
+ });
1653
+ }
1654
+
1655
+ // Mark template as spawned
1656
+ await updatePipelineRunStepStatus({
1657
+ member,
1658
+ runStepId: templateRunStep.id,
1659
+ status: "spawned",
1660
+ });
1661
+
1662
+ console.log(
1663
+ `[checkAndSpawnMultiGroups] Successfully spawned ${groups.length} instance(s) for step ${dep.childStepId}`,
1664
+ );
1665
+ }
1666
+ } catch (error) {
1667
+ console.error("[checkAndSpawnMultiGroups] Error:", error);
1668
+ // Don't throw - this is non-critical, log and continue
1669
+ }
1670
+ }
1671
+
1672
+ /**
1673
+ * Build HTTP request from integration config template with variable substitution
1674
+ */
1675
+ async function buildHttpRequest({
1676
+ config,
1677
+ parentData,
1678
+ }: {
1679
+ config: {
1680
+ method: "GET" | "POST" | "PUT" | "DELETE";
1681
+ urlTemplate: string;
1682
+ headers: Record<string, string>;
1683
+ bodyTemplate?: string;
1684
+ };
1685
+ parentData: DenormSubmission[];
1686
+ }): Promise<{
1687
+ method: string;
1688
+ url: string;
1689
+ headers: Record<string, string>;
1690
+ body?: string;
1691
+ }> {
1692
+ // Build variable map from parent submissions
1693
+ const variables = new Map<string, string>();
1694
+
1695
+ for (const submission of parentData) {
1696
+ // Add form-level variables
1697
+ variables.set(`form.name`, submission.formName);
1698
+
1699
+ // Add field-level variables from groups
1700
+ for (const group of submission.groups) {
1701
+ for (const item of group.items) {
1702
+ // Variable pattern: groupName.fieldName
1703
+ const key = `${group.name}.${item.name}`;
1704
+ const value = String(item.value ?? "");
1705
+ variables.set(key, value);
1706
+ }
1707
+
1708
+ // Also add derived fields
1709
+ for (const derived of group.derived) {
1710
+ const key = `${group.name}.${derived.name}`;
1711
+ const value = String(derived.value ?? "");
1712
+ variables.set(key, value);
1713
+ }
1714
+ }
1715
+
1716
+ // Add table row variables (use first row for simplicity)
1717
+ for (const table of submission.tables) {
1718
+ const firstRow = table.rows[0];
1719
+ if (firstRow) {
1720
+ for (const item of firstRow.items) {
1721
+ const key = `${table.name}.${item.name}`;
1722
+ const value = String(item.value ?? "");
1723
+ variables.set(key, value);
1724
+ }
1725
+ }
1726
+ }
1727
+ }
1728
+
1729
+ // Substitute variables in URL template
1730
+ // Pattern: {{variableName}} or {{groupName.fieldName}}
1731
+ const substituteVariables = (template: string): string => {
1732
+ return template.replace(/\{\{([^}]+)\}\}/g, (match, varName) => {
1733
+ const trimmedName = varName.trim();
1734
+ const value = variables.get(trimmedName);
1735
+
1736
+ if (value !== undefined) {
1737
+ return encodeURIComponent(value);
1738
+ }
1739
+
1740
+ console.warn(`[buildHttpRequest] Variable not found: ${trimmedName}`);
1741
+ return match; // Keep original if not found
1742
+ });
1743
+ };
1744
+
1745
+ const url = substituteVariables(config.urlTemplate);
1746
+
1747
+ // Substitute in headers
1748
+ const headers: Record<string, string> = {};
1749
+ for (const [key, value] of Object.entries(config.headers)) {
1750
+ headers[key] = substituteVariables(value);
1751
+ }
1752
+
1753
+ // Substitute in body
1754
+ const body = config.bodyTemplate
1755
+ ? substituteVariables(config.bodyTemplate)
1756
+ : undefined;
1757
+
1758
+ return {
1759
+ method: config.method,
1760
+ url,
1761
+ headers,
1762
+ body,
1763
+ };
1764
+ }
1765
+
1766
+ // ============================================================================
1767
+ // Single Step Execution (Internal)
1768
+ // ============================================================================
1769
+
1770
+ type ExecuteSingleStepResult =
1771
+ | {
1772
+ ok: true;
1773
+ data: { outputSubmissionIds: string[]; runSteps: PipelineRunStep[] };
1774
+ }
1775
+ | { ok: false; error: string };
1776
+
1777
+ export async function executeSingleStep({
1778
+ member,
1779
+ runId,
1780
+ pipelineId,
1781
+ step,
1782
+ inputSubmissionId,
1783
+ allInputSubmissionIds,
1784
+ config,
1785
+ }: {
1786
+ member: Member;
1787
+ runId: string;
1788
+ pipelineId: string;
1789
+ step: {
1790
+ id: string;
1791
+ stepOrder: number;
1792
+ type: "single" | "multi";
1793
+ description: string;
1794
+ prompt: string;
1795
+ toolsAllowed: string[] | null;
1796
+ parentStepId: string | null;
1797
+ outputFormIds: string[];
1798
+ };
1799
+ inputSubmissionId: string;
1800
+ allInputSubmissionIds?: string[];
1801
+ config: {
1802
+ model: ReturnType<typeof createLanguageModel>;
1803
+ };
1804
+ }): Promise<ExecuteSingleStepResult> {
1805
+ console.log("[executeSingleStep] Starting step execution:", {
1806
+ stepId: step.id,
1807
+ stepOrder: step.stepOrder,
1808
+ stepType: step.type,
1809
+ inputSubmissionId,
1810
+ allInputSubmissionIds,
1811
+ allInputCount: allInputSubmissionIds?.length,
1812
+ });
1813
+
1814
+ // 1. Validate step has output form
1815
+ if (!step.outputFormIds || step.outputFormIds.length === 0) {
1816
+ return { ok: false, error: `Step ${step.stepOrder} has no output form` };
1817
+ }
1818
+
1819
+ // 2. Validate input submission exists
1820
+ console.log(
1821
+ "[executeSingleStep] Validating primary input submission:",
1822
+ inputSubmissionId,
1823
+ );
1824
+ const inputSubmissionResult = await getRuntimeSubmission({
1825
+ member,
1826
+ submissionId: inputSubmissionId,
1827
+ });
1828
+ if (!inputSubmissionResult.ok) {
1829
+ console.error("[executeSingleStep] Primary input submission not found:", {
1830
+ inputSubmissionId,
1831
+ organizationId: member.organizationId,
1832
+ });
1833
+ return { ok: false, error: "Input submission not found" };
1834
+ }
1835
+ console.log("[executeSingleStep] Primary input submission found:", {
1836
+ id: inputSubmissionResult.data.id,
1837
+ name: inputSubmissionResult.data.name,
1838
+ formId: inputSubmissionResult.data.formId,
1839
+ });
1840
+ const sourceSubmission = inputSubmissionResult.data;
1841
+
1842
+ // 3. For multi-step, get the input submission data to find multi groups
1843
+ let multiGroupEntries: Array<{ order: number; data: unknown }> = [];
1844
+ if (step.type === "multi") {
1845
+ const inputSubmission = await getDenormSubmission({
1846
+ member,
1847
+ submissionId: inputSubmissionId,
1848
+ withCitations: true,
1849
+ withTransformations: true,
1850
+ });
1851
+
1852
+ // Find the first multi group in tables
1853
+ const firstMultiTable = inputSubmission.tables[0];
1854
+ if (!firstMultiTable || firstMultiTable.rows.length === 0) {
1855
+ return {
1856
+ ok: false,
1857
+ error:
1858
+ "Multi-step requires input with at least one multi group with entries",
1859
+ };
1860
+ }
1861
+
1862
+ // Create an entry for each row in the multi group
1863
+ multiGroupEntries = firstMultiTable.rows.map((row, idx) => ({
1864
+ order: idx,
1865
+ data: row,
1866
+ }));
1867
+ }
1868
+
1869
+ // 4. Determine how many output submissions to create
1870
+ const outputCount = step.type === "multi" ? multiGroupEntries.length : 1;
1871
+ const outputSubmissions: Array<{
1872
+ id: string;
1873
+ dealId: string;
1874
+ conversationId: string;
1875
+ }> = [];
1876
+ const runSteps: PipelineRunStep[] = [];
1877
+
1878
+ // 5. Create output submissions and run steps
1879
+ for (let i = 0; i < outputCount; i++) {
1880
+ const outputFormId = step.outputFormIds[0];
1881
+ if (!outputFormId) {
1882
+ return { ok: false, error: `Step ${step.stepOrder} has no output form` };
1883
+ }
1884
+
1885
+ const outputSubmission = await createSubmissionWithConversation({
1886
+ member,
1887
+ data: {
1888
+ formId: outputFormId,
1889
+ dealId: sourceSubmission.dealId,
1890
+ name:
1891
+ step.type === "multi"
1892
+ ? `Step ${step.stepOrder} - Entry ${i + 1}`
1893
+ : `Step ${step.stepOrder}`,
1894
+ status: "draft",
1895
+ type: "transformation",
1896
+ },
1897
+ });
1898
+
1899
+ // Sync submission to form to create groups and items
1900
+ await syncSubmissionToForm({
1901
+ member,
1902
+ submissionId: outputSubmission.id,
1903
+ });
1904
+
1905
+ outputSubmissions.push(outputSubmission);
1906
+
1907
+ // Create run step in DB
1908
+ const runStepResult = await createPipelineRunStep({
1909
+ member,
1910
+ runId,
1911
+ stepId: step.id,
1912
+ });
1913
+ if (!runStepResult.ok) {
1914
+ return { ok: false, error: "Failed to create run step" };
1915
+ }
1916
+ const runStep = runStepResult.data;
1917
+ runSteps.push(runStep);
1918
+
1919
+ // Link output submission to run step
1920
+ await db.insert(schema.pipelineRunStepOutputSubmission).values({
1921
+ runStepId: runStep.id,
1922
+ submissionId: outputSubmission.id,
1923
+ });
1924
+ }
1925
+
1926
+ // 6. Execute each output submission in parallel (with concurrency limit of 10)
1927
+ const CONCURRENCY_LIMIT = 10;
1928
+ const executeSubmission = async (
1929
+ outputSubmission: (typeof outputSubmissions)[number],
1930
+ i: number,
1931
+ ) => {
1932
+ const runStep = runSteps[i];
1933
+
1934
+ if (!outputSubmission || !runStep) {
1935
+ throw new Error("Internal error: missing output submission or run step");
1936
+ }
1937
+
1938
+ try {
1939
+ // Update status to running
1940
+ await updatePipelineRunStepStatus({
1941
+ member,
1942
+ runStepId: runStep.id,
1943
+ status: PIPELINE_RUN_STEP_STATUS.RUNNING,
1944
+ });
1945
+
1946
+ // Get input submission data with citations
1947
+ // For single-step after multi-step: load ALL input submissions
1948
+ const inputSubmissionsToLoad = allInputSubmissionIds || [
1949
+ inputSubmissionId,
1950
+ ];
1951
+ console.log(
1952
+ "[executeSingleStep] Loading input submissions for execution:",
1953
+ {
1954
+ count: inputSubmissionsToLoad.length,
1955
+ ids: inputSubmissionsToLoad,
1956
+ },
1957
+ );
1958
+
1959
+ const inputSubmissions = await Promise.all(
1960
+ inputSubmissionsToLoad.map(async (id) => {
1961
+ console.log("[executeSingleStep] Loading submission:", id);
1962
+ try {
1963
+ const submission = await getDenormSubmission({
1964
+ member,
1965
+ submissionId: id,
1966
+ withCitations: true,
1967
+ withTransformations: true,
1968
+ });
1969
+ console.log("[executeSingleStep] Successfully loaded submission:", {
1970
+ id,
1971
+ name: submission.name,
1972
+ });
1973
+ return submission;
1974
+ } catch (error) {
1975
+ console.error("[executeSingleStep] Failed to load submission:", {
1976
+ id,
1977
+ error: error instanceof Error ? error.message : String(error),
1978
+ });
1979
+ throw error;
1980
+ }
1981
+ }),
1982
+ );
1983
+ console.log(
1984
+ "[executeSingleStep] All input submissions loaded successfully",
1985
+ );
1986
+
1987
+ // Get output form groups
1988
+ const outputSubmissionWithData = await getSubmissionWithData({
1989
+ member,
1990
+ submissionId: outputSubmission.id,
1991
+ typeFilter: "transformation",
1992
+ });
1993
+ if (!outputSubmissionWithData?.form?.groups) {
1994
+ throw new Error("Failed to load output form");
1995
+ }
1996
+ const outputFormGroups = outputSubmissionWithData.form.groups;
1997
+
1998
+ // Run the step execution
1999
+ const stepResult = await runStepExecute({
2000
+ member,
2001
+ runId,
2002
+ runStepId: runStep.id,
2003
+ step,
2004
+ inputSubmissions,
2005
+ resourceSubmissions: [],
2006
+ outputFormGroups,
2007
+ conversationId: outputSubmission.conversationId,
2008
+ config,
2009
+ });
2010
+
2011
+ // Write output using storePipelineOutputData
2012
+ if (stepResult.output?.data) {
2013
+ const storeResult = await storePipelineOutputData({
2014
+ member,
2015
+ outputSubmissionId: outputSubmission.id,
2016
+ outputData: stepResult.output.data,
2017
+ });
2018
+ if (!storeResult.ok) {
2019
+ throw new Error("Failed to store pipeline output data");
2020
+ }
2021
+ }
2022
+
2023
+ // Update step status
2024
+ await updatePipelineRunStepStatus({
2025
+ member,
2026
+ runStepId: runStep.id,
2027
+ status: stepResult.status,
2028
+ error: stepResult.error,
2029
+ });
2030
+
2031
+ if (stepResult.status === "failed") {
2032
+ throw new Error(stepResult.error ?? "Step failed");
2033
+ }
2034
+
2035
+ return outputSubmission.id;
2036
+ } catch (error) {
2037
+ const errorMessage =
2038
+ error instanceof Error ? error.message : "Step execution failed";
2039
+ await updatePipelineRunStepStatus({
2040
+ member,
2041
+ runStepId: runStep.id,
2042
+ status: PIPELINE_RUN_STATUS.FAILED,
2043
+ error: errorMessage,
2044
+ });
2045
+ throw error;
2046
+ }
2047
+ };
2048
+
2049
+ // Execute in batches with concurrency limit
2050
+ const allOutputSubmissionIds: string[] = [];
2051
+ try {
2052
+ for (let i = 0; i < outputSubmissions.length; i += CONCURRENCY_LIMIT) {
2053
+ const batch = outputSubmissions.slice(i, i + CONCURRENCY_LIMIT);
2054
+ const batchPromises = batch.map((outputSubmission, batchIdx) =>
2055
+ executeSubmission(outputSubmission, i + batchIdx),
2056
+ );
2057
+ const batchResults = await Promise.all(batchPromises);
2058
+ allOutputSubmissionIds.push(...batchResults);
2059
+ }
2060
+
2061
+ return {
2062
+ ok: true,
2063
+ data: { outputSubmissionIds: allOutputSubmissionIds, runSteps },
2064
+ };
2065
+ } catch (error) {
2066
+ const errorMessage =
2067
+ error instanceof Error ? error.message : "Step execution failed";
2068
+ return { ok: false, error: errorMessage };
2069
+ }
2070
+ }
2071
+
2072
+ // ============================================================================
2073
+ // Step Executor
2074
+ // ============================================================================
2075
+
2076
+ function buildStepResponseSchema(outputFormGroups: FormGroupHydrated[]) {
2077
+ const extractionSchema = outputFormGroups.reduce((acc, group) => {
2078
+ const groupSchema = buildExtractionSchemaFromGroup(group);
2079
+ return z.object({
2080
+ ...acc.shape,
2081
+ ...groupSchema.shape,
2082
+ });
2083
+ }, z.object({}));
2084
+
2085
+ return z.object({
2086
+ reasoning: z.string().nullable(),
2087
+ data: extractionSchema.nullable(),
2088
+ });
2089
+ }
2090
+
2091
+ async function runStepExecute({
2092
+ member,
2093
+ runId,
2094
+ runStepId,
2095
+ step,
2096
+ inputSubmissions,
2097
+ resourceSubmissions,
2098
+ outputFormGroups,
2099
+ conversationId,
2100
+ spawningGroupData,
2101
+ config,
2102
+ }: {
2103
+ member: Member;
2104
+ runId: string;
2105
+ runStepId: string;
2106
+ step: {
2107
+ id: string;
2108
+ stepOrder: number;
2109
+ description: string;
2110
+ prompt: string;
2111
+ toolsAllowed: string[] | null;
2112
+ parentStepId: string | null;
2113
+ outputFormIds: string[];
2114
+ };
2115
+ inputSubmissions: DenormSubmission[];
2116
+ resourceSubmissions: DenormSubmission[];
2117
+ outputFormGroups: FormGroupHydrated[];
2118
+ conversationId: string;
2119
+ spawningGroupData?: {
2120
+ groupName: string;
2121
+ data: DenormSubmission["groups"][0];
2122
+ } | null;
2123
+ config: {
2124
+ model: ReturnType<typeof createLanguageModel>;
2125
+ };
2126
+ }): Promise<StepExecutionResult> {
2127
+ const { model } = config;
2128
+ try {
2129
+ // Serialize input data and resources
2130
+ // If multiple input submissions provided (parent was multi-step), serialize all of them
2131
+ const serializedInput = inputSubmissions
2132
+ .map(
2133
+ (sub, idx) => `=== Input ${idx + 1} ===\n${serializeSubmission(sub)}`,
2134
+ )
2135
+ .join("\n\n");
2136
+ const serializedResources = serializePipelineResources(resourceSubmissions);
2137
+
2138
+ // Build output schema
2139
+ const workflowStepResponseSchema =
2140
+ buildStepResponseSchema(outputFormGroups);
2141
+ const strictSchema = zodSchema(workflowStepResponseSchema);
2142
+ addAdditionalPropertiesToJsonSchema(strictSchema.jsonSchema as JSONSchema7);
2143
+
2144
+ // Build instructions (system context only)
2145
+ const instructionsParts = [
2146
+ "You are executing one step in a deterministic data workflow.",
2147
+ "Analyze the input data and produce the required output.",
2148
+ "",
2149
+ `Step: ${step.stepOrder}`,
2150
+ `Description: ${step.description}`,
2151
+ "",
2152
+ "IMPORTANT: For each primitive field (string, number, boolean), output:",
2153
+ ' "fieldName": { "value": <extracted_value>, "refId": "<element_id>" }',
2154
+ "",
2155
+ "When a value comes from multiple elements in the input data or pipeline resources, list all element IDs separated by commas in the refId field.",
2156
+ "EVERY extracted value MUST include a refId pointing to the source element(s) from the input data or pipeline resources.",
2157
+ ];
2158
+
2159
+ if (spawningGroupData) {
2160
+ instructionsParts.push(
2161
+ "",
2162
+ "NOTE: This is a spawned instance processing a specific row/entry from a multi-group table.",
2163
+ `The TARGET ROW section below contains the specific ${spawningGroupData.groupName} entry this instance is processing.`,
2164
+ "Use data from the TARGET ROW as the primary context, and reference INPUT DATA for additional context as needed.",
2165
+ );
2166
+ }
2167
+
2168
+ instructionsParts.push("", "===== STEP INSTRUCTIONS =====", step.prompt);
2169
+
2170
+ const instructions = instructionsParts.join("\n");
2171
+
2172
+ console.log("[runStepExecute] Building agent for step:", {
2173
+ runId,
2174
+ runStepId,
2175
+ stepOrder: step.stepOrder,
2176
+ stepDescription: step.description,
2177
+ promptLength: step.prompt.length,
2178
+ promptPreview: step.prompt.substring(0, 200),
2179
+ hasSpawningGroup: !!spawningGroupData,
2180
+ });
2181
+
2182
+ // Build messages with input data and resources
2183
+ const messages = [
2184
+ {
2185
+ role: "user" as const,
2186
+ content: `===== INPUT DATA =====\n${serializedInput}`,
2187
+ },
2188
+ ];
2189
+
2190
+ if (spawningGroupData) {
2191
+ // Serialize the spawning group data
2192
+ const spawningGroupHtml: string[] = [];
2193
+ spawningGroupHtml.push(`<h3>${spawningGroupData.groupName}</h3>`);
2194
+ spawningGroupHtml.push("<dl>");
2195
+
2196
+ for (const item of spawningGroupData.data.items) {
2197
+ spawningGroupHtml.push(`<dt>${item.name}</dt>`);
2198
+ spawningGroupHtml.push(
2199
+ `<dd id="${item.id}">${String(item.value ?? "")}</dd>`,
2200
+ );
2201
+ }
2202
+
2203
+ for (const derived of spawningGroupData.data.derived) {
2204
+ spawningGroupHtml.push(`<dt>${derived.name} (derived)</dt>`);
2205
+ spawningGroupHtml.push(
2206
+ `<dd id="${derived.id}">${String(derived.value ?? "")}</dd>`,
2207
+ );
2208
+ }
2209
+
2210
+ spawningGroupHtml.push("</dl>");
2211
+
2212
+ messages.push({
2213
+ role: "user" as const,
2214
+ content: `===== TARGET ROW (Spawning Group) =====\n${spawningGroupHtml.join("\n")}`,
2215
+ });
2216
+ }
2217
+
2218
+ if (resourceSubmissions.length > 0) {
2219
+ messages.push({
2220
+ role: "user" as const,
2221
+ content: `===== PIPELINE RESOURCES =====\n${serializedResources}`,
2222
+ });
2223
+ }
2224
+
2225
+ messages.push({
2226
+ role: "user" as const,
2227
+ content: "Execute the step as instructed.",
2228
+ });
2229
+
2230
+ // Load pipeline-specific tools (not agent tools)
2231
+ const tools = {
2232
+ createArtifact: createPipelineArtifact({
2233
+ member,
2234
+ runId,
2235
+ runStepId,
2236
+ }),
2237
+ };
2238
+
2239
+ console.log("[runStepExecute] Tools available:", {
2240
+ runId,
2241
+ runStepId,
2242
+ toolNames: Object.keys(tools),
2243
+ toolCount: Object.keys(tools).length,
2244
+ });
2245
+
2246
+ console.log("[runStepExecute] Full instructions being sent to agent:", {
2247
+ runId,
2248
+ runStepId,
2249
+ instructions,
2250
+ });
2251
+
2252
+ // Create agent with structured output
2253
+ const agent = new ToolLoopAgent({
2254
+ model,
2255
+ instructions,
2256
+ tools,
2257
+ stopWhen: stepCountIs(10),
2258
+ output: Output.object({
2259
+ schema: jsonSchema(strictSchema.jsonSchema),
2260
+ }),
2261
+ });
2262
+
2263
+ // Execute agent
2264
+ console.log("[runStepExecute] Starting agent execution...", {
2265
+ runId,
2266
+ runStepId,
2267
+ });
2268
+
2269
+ const result = await agent.generate({ messages });
2270
+
2271
+ console.log("[runStepExecute] Agent execution completed:", {
2272
+ runId,
2273
+ runStepId,
2274
+ hasOutput: !!result.output,
2275
+ stepCount: result.steps?.length ?? 0,
2276
+ });
2277
+
2278
+ const parsedOutput = workflowStepResponseSchema.parse(result.output);
2279
+
2280
+ return {
2281
+ status: PIPELINE_RUN_STEP_STATUS.COMPLETED,
2282
+ reasoning: parsedOutput.reasoning ?? undefined,
2283
+ output: { data: parsedOutput.data },
2284
+ };
2285
+ } catch (error) {
2286
+ console.error("[runStepExecute] Step execution failed:", {
2287
+ runId,
2288
+ runStepId,
2289
+ error: error instanceof Error ? error.message : "Unknown error",
2290
+ stack: error instanceof Error ? error.stack : undefined,
2291
+ });
2292
+
2293
+ return {
2294
+ status: PIPELINE_RUN_STATUS.FAILED,
2295
+ error: error instanceof Error ? error.message : "Step execution failed",
2296
+ output: { data: null },
2297
+ };
2298
+ }
2299
+ }
2300
+
2301
+ // ============================================================================
2302
+ // Replay Functions
2303
+ // ============================================================================
2304
+
2305
+ export async function replayPipelineRun({
2306
+ member,
2307
+ runId,
2308
+ config,
2309
+ }: {
2310
+ member: Member;
2311
+ runId: string;
2312
+ config: {
2313
+ model: ReturnType<typeof createLanguageModel>;
2314
+ };
2315
+ }): Promise<DalResult<unknown>> {
2316
+ // TODO: Implement replay functionality
2317
+ // Steps needed:
2318
+ // 1. Get run timeline with getRunTimeline
2319
+ // 2. Extract input submission IDs from resources
2320
+ // 3. Call executePipeline with same inputs
2321
+ return { ok: false, error: "not-found" };
2322
+ }
2323
+
2324
+ export async function replayPipelineRunStep({
2325
+ member,
2326
+ runStepId,
2327
+ config,
2328
+ }: {
2329
+ member: Member;
2330
+ runStepId: string;
2331
+ config: {
2332
+ model: ReturnType<typeof createLanguageModel>;
2333
+ };
2334
+ }): Promise<DalResult<unknown>> {
2335
+ // TODO: Implement step replay functionality
2336
+ // Steps needed:
2337
+ // 1. Get step details with getPipelineRunStepWithDetails
2338
+ // 2. Find the corresponding pipeline step
2339
+ // 3. Determine input (parent step output or run input)
2340
+ // 4. Either make executeSingleStep public or create wrapper
2341
+ return { ok: false, error: "not-found" };
2342
+ }