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,1485 @@
1
+ import { SubmissionGroupWrapper } from "@/components/data-display/group-wrapper";
2
+ import { Container } from "@/components/header/container";
3
+ import { DocList } from "@/components/side-panel/doc-list";
4
+ import { SidebarRight } from "@/components/sidebar/sidebar-right";
5
+ import { membershipMiddleware } from "@/middleware";
6
+ import { downloadDocumentFn } from "@/routes/_authed/_app/deals/$dealId/-fns";
7
+ import type { ExtractionInfo } from "@sea/core/data-extraction/types";
8
+ import { getSubmissionWithFormAndExtractions } from "@sea/dal/submission";
9
+ import type {
10
+ DocumentCitation,
11
+ PipelineRunStepWithDetails,
12
+ PipelineRunTimeline,
13
+ } from "@sea/db/types";
14
+ import type { ApiReconciliation } from "@sea/schemas/core/submission";
15
+ import { PdfViewer } from "@sea/ui/components/doc-viewers/pdf";
16
+ import { RelDate } from "@sea/ui/components/misc/rel-date";
17
+ import {
18
+ Accordion,
19
+ AccordionContent,
20
+ AccordionItem,
21
+ AccordionTrigger,
22
+ } from "@sea/ui/components/ui/accordion";
23
+ import { Badge } from "@sea/ui/components/ui/badge";
24
+ import { Button } from "@sea/ui/components/ui/button";
25
+ import { Card, CardContent } from "@sea/ui/components/ui/card";
26
+ import {
27
+ Dialog,
28
+ DialogContent,
29
+ DialogDescription,
30
+ DialogFooter,
31
+ DialogHeader,
32
+ DialogTitle,
33
+ DialogTrigger,
34
+ } from "@sea/ui/components/ui/dialog";
35
+ import {
36
+ ResizableHandle,
37
+ ResizablePanel,
38
+ ResizablePanelGroup,
39
+ } from "@sea/ui/components/ui/resizable";
40
+ import { H3 } from "@sea/ui/components/ui/typography";
41
+ import { convertItemsToRecord, groupBy } from "@sea/util/data-transform";
42
+ import { createFileRoute, useRouter } from "@tanstack/react-router";
43
+ import { createServerFn, useServerFn } from "@tanstack/react-start";
44
+ import {
45
+ DownloadIcon,
46
+ FileTextIcon,
47
+ PlayIcon,
48
+ RotateCcwIcon,
49
+ XCircleIcon,
50
+ XIcon,
51
+ } from "lucide-react";
52
+ import { useCallback, useEffect, useMemo, useState } from "react";
53
+ import Markdown from "react-markdown";
54
+ import remarkGfm from "remark-gfm";
55
+ import { toast } from "sonner";
56
+ import { DAGView } from "../-components/dag-view";
57
+ import {
58
+ getRunBlockersFn,
59
+ replayPipelineRunFn,
60
+ replayPipelineRunStepFn,
61
+ resolveBlockerWithManualInputFn,
62
+ startPipelineRunFn,
63
+ } from "../../-fns";
64
+ import { BlockersPanel } from "./-components/blockers-panel";
65
+ import { SubmissionSelectorModal } from "./-components/submission-selector-modal";
66
+ import { UploadDocForPipeline } from "./-components/upload-doc";
67
+
68
+ // Helper functions for artifact download
69
+ function downloadBlob(blob: Blob, filename: string) {
70
+ const url = URL.createObjectURL(blob);
71
+ const link = document.createElement("a");
72
+ link.href = url;
73
+ link.download = filename;
74
+ link.click();
75
+ URL.revokeObjectURL(url);
76
+ }
77
+
78
+ function escapeHtml(text: string) {
79
+ return text
80
+ .replace(/&/g, "&")
81
+ .replace(/</g, "&lt;")
82
+ .replace(/>/g, "&gt;")
83
+ .replace(/"/g, "&quot;")
84
+ .replace(/'/g, "&#039;");
85
+ }
86
+
87
+ // Server functions for loader
88
+ const getRunTimelineServerFn = createServerFn({ method: "GET" })
89
+ .middleware([membershipMiddleware])
90
+ .validator((data: { runId: string }) => data)
91
+ .handler(async ({ context: { member }, data: { runId } }) => {
92
+ const { getRunTimeline } = await import("@sea/dal/pipeline");
93
+ return await getRunTimeline({ member, runId });
94
+ });
95
+
96
+ const getPipelineServerFn = createServerFn({ method: "GET" })
97
+ .middleware([membershipMiddleware])
98
+ .validator((data: { pipelineId: string }) => data)
99
+ .handler(async ({ context: { member }, data: { pipelineId } }) => {
100
+ const { getPipelineDetail } = await import("@sea/dal/pipeline");
101
+ const result = await getPipelineDetail({ member, pipelineId: pipelineId });
102
+ if (!result.ok) return null;
103
+ return result.data;
104
+ });
105
+
106
+ const getSubmissionDataFn = createServerFn({ method: "GET" })
107
+ .middleware([membershipMiddleware])
108
+ .validator((data: { submissionId: string }) => data)
109
+ .handler(async ({ context: { member }, data: { submissionId } }) => {
110
+ return await getSubmissionWithFormAndExtractions({ member, submissionId });
111
+ });
112
+
113
+ const getAvailableSubmissionsFn = createServerFn({ method: "GET" })
114
+ .middleware([membershipMiddleware])
115
+ .validator((data: Record<string, never>) => data)
116
+ .handler(async ({ context: { member } }) => {
117
+ // Get all submissions in the organization
118
+ const { getSubmissions } = await import("@sea/dal/submission");
119
+ return await getSubmissions({ member, typeFilter: "all" });
120
+ });
121
+
122
+ const getRunOutputSubmissionsFn = createServerFn({ method: "GET" })
123
+ .middleware([membershipMiddleware])
124
+ .validator((data: { runId: string }) => data)
125
+ .handler(async ({ context: { member }, data: { runId } }) => {
126
+ const { getRunOutputSubmissions } = await import("@sea/dal/pipeline");
127
+ const result = await getRunOutputSubmissions({ member, runId });
128
+ if (!result.ok) return {};
129
+ // Convert Map to plain object for serialization
130
+ return Object.fromEntries(result.data);
131
+ });
132
+
133
+ const getSubmissionsByIdsFn = createServerFn({ method: "GET" })
134
+ .middleware([membershipMiddleware])
135
+ .validator((data: { submissionIds: string[] }) => data)
136
+ .handler(async ({ context: { member }, data: { submissionIds } }) => {
137
+ const results = await Promise.all(
138
+ submissionIds.map((id) => getSubmissionWithFormAndExtractions({ member, submissionId: id })),
139
+ );
140
+ return results;
141
+ });
142
+
143
+ export const Route = createFileRoute("/_authed/_app/pipelines/$pipelineId/$runId/")({
144
+ loader: async ({
145
+ params: { pipelineId, runId },
146
+ }): Promise<{
147
+ timeline: PipelineRunTimeline;
148
+ pipeline: Awaited<ReturnType<typeof getPipelineServerFn>>;
149
+ inputSubmissionData: Map<string, Awaited<ReturnType<typeof getSubmissionDataFn>>>;
150
+ outputSubmissionData: Map<string, Awaited<ReturnType<typeof getSubmissionDataFn>>[]>;
151
+ blockers: Awaited<ReturnType<typeof getRunBlockersFn>>;
152
+ availableSubmissions: Awaited<ReturnType<typeof getAvailableSubmissionsFn>>;
153
+ }> => {
154
+ const timelineResult = await getRunTimelineServerFn({ data: { runId } });
155
+ if (!timelineResult.ok) {
156
+ throw new Error("Failed to load run timeline");
157
+ }
158
+ const timeline = timelineResult.data;
159
+
160
+ // Fetch pipeline data
161
+ const pipeline = await getPipelineServerFn({ data: { pipelineId } });
162
+
163
+ // Load output submissions for each run step
164
+ const outputSubmissionIdsObj = await getRunOutputSubmissionsFn({ data: { runId } });
165
+ const outputSubmissionData = new Map<
166
+ string,
167
+ Awaited<ReturnType<typeof getSubmissionDataFn>>[]
168
+ >();
169
+
170
+ // Load submission data for all output submissions
171
+ for (const [runStepId, submissionIds] of Object.entries(outputSubmissionIdsObj)) {
172
+ if (submissionIds.length > 0) {
173
+ const submissions = await Promise.all(
174
+ submissionIds.map((submissionId) => getSubmissionDataFn({ data: { submissionId } })),
175
+ );
176
+ outputSubmissionData.set(runStepId, submissions);
177
+ }
178
+ }
179
+
180
+ // TODO: Load input submissions from pipeline run trigger data
181
+ // Input submissions architecture is still being determined
182
+ const inputSubmissionData = new Map<string, Awaited<ReturnType<typeof getSubmissionDataFn>>>();
183
+
184
+ // Fetch blockers for the run
185
+ const blockers = await getRunBlockersFn({ data: { runId } });
186
+
187
+ // Fetch available submissions for manual inputs
188
+ const availableSubmissions = await getAvailableSubmissionsFn({ data: {} });
189
+
190
+ return {
191
+ timeline,
192
+ pipeline,
193
+ inputSubmissionData,
194
+ outputSubmissionData,
195
+ blockers,
196
+ availableSubmissions,
197
+ };
198
+ },
199
+ component: RouteComponent,
200
+ });
201
+
202
+ type ProcessFlowStepData = PipelineRunStepWithDetails;
203
+
204
+ // Helper component for the process flow
205
+ function ProcessFlowStep({
206
+ step,
207
+ isSelected,
208
+ isFirst,
209
+ isLast,
210
+ stepNumber,
211
+ onClick,
212
+ }: {
213
+ step: ProcessFlowStepData;
214
+ isSelected: boolean;
215
+ isFirst: boolean;
216
+ isLast: boolean;
217
+ stepNumber: number;
218
+ onClick: () => void;
219
+ }) {
220
+ const isCompleted = step.status === "completed" || step.status === "success";
221
+ const isFailed = step.status === "failed" || step.status === "error";
222
+ const isRunning = step.status === "running";
223
+ const isPending = !isCompleted && !isFailed && !isRunning;
224
+
225
+ const getStatusIcon = () => {
226
+ if (isCompleted)
227
+ return (
228
+ <svg
229
+ viewBox="0 0 24 24"
230
+ fill="currentColor"
231
+ aria-hidden="true"
232
+ className="size-6 text-white"
233
+ >
234
+ <path
235
+ d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z"
236
+ clipRule="evenodd"
237
+ fillRule="evenodd"
238
+ />
239
+ </svg>
240
+ );
241
+ if (isFailed) return <XCircleIcon className="size-6 text-white" />;
242
+ if (isRunning) return <PlayIcon className="size-5 text-white" />;
243
+ return (
244
+ <span className="text-gray-500 dark:text-gray-400">
245
+ {stepNumber.toString().padStart(2, "0")}
246
+ </span>
247
+ );
248
+ };
249
+
250
+ const getDescription = () => {
251
+ if (step.startedAt && step.completedAt) {
252
+ const durationMs = new Date(step.completedAt).getTime() - new Date(step.startedAt).getTime();
253
+ if (durationMs > 0) {
254
+ return `${(durationMs / 1000).toFixed(1)}s`;
255
+ }
256
+ }
257
+ return "Processing step";
258
+ };
259
+
260
+ return (
261
+ <li className="relative overflow-hidden lg:flex-1">
262
+ <div className="overflow-hidden">
263
+ <button
264
+ type="button"
265
+ onClick={onClick}
266
+ className="group w-full text-left"
267
+ aria-current={isSelected ? "step" : undefined}
268
+ >
269
+ {/* Active indicator bar */}
270
+ <span
271
+ aria-hidden="true"
272
+ className={`absolute top-0 left-0 h-full w-1 lg:top-auto lg:bottom-0 lg:h-1 lg:w-full ${
273
+ isSelected
274
+ ? isCompleted
275
+ ? "bg-indigo-600 dark:bg-indigo-500"
276
+ : isFailed
277
+ ? "bg-red-600 dark:bg-red-500"
278
+ : isRunning
279
+ ? "bg-blue-600 dark:bg-blue-500"
280
+ : "bg-gray-300 dark:bg-white/15"
281
+ : "bg-transparent group-hover:bg-gray-200 dark:group-hover:bg-white/20"
282
+ }`}
283
+ />
284
+
285
+ <span
286
+ className={`flex items-start px-6 py-5 text-sm font-medium ${!isFirst ? "lg:pl-9" : ""}`}
287
+ >
288
+ <span className="shrink-0">
289
+ {isCompleted ? (
290
+ <span className="flex size-10 items-center justify-center rounded-full bg-indigo-600 dark:bg-indigo-500">
291
+ {getStatusIcon()}
292
+ </span>
293
+ ) : isFailed ? (
294
+ <span className="flex size-10 items-center justify-center rounded-full bg-red-600 dark:bg-red-500">
295
+ {getStatusIcon()}
296
+ </span>
297
+ ) : isRunning ? (
298
+ <span className="flex size-10 items-center justify-center rounded-full bg-blue-600 dark:bg-blue-500">
299
+ {getStatusIcon()}
300
+ </span>
301
+ ) : isSelected ? (
302
+ <span className="flex size-10 items-center justify-center rounded-full border-2 border-indigo-600 dark:border-indigo-500">
303
+ <span className="text-indigo-600 dark:text-indigo-400">{getStatusIcon()}</span>
304
+ </span>
305
+ ) : (
306
+ <span className="flex size-10 items-center justify-center rounded-full border-2 border-gray-300 dark:border-white/15">
307
+ {getStatusIcon()}
308
+ </span>
309
+ )}
310
+ </span>
311
+ <span className="mt-0.5 ml-4 flex min-w-0 flex-col">
312
+ <span
313
+ className={`text-sm font-medium ${
314
+ isSelected
315
+ ? isCompleted || isRunning
316
+ ? "text-indigo-600 dark:text-indigo-400"
317
+ : isFailed
318
+ ? "text-red-600 dark:text-red-400"
319
+ : "text-indigo-600 dark:text-indigo-400"
320
+ : "text-gray-500 dark:text-gray-400"
321
+ }`}
322
+ >
323
+ Step {stepNumber}
324
+ </span>
325
+ <span className="text-sm font-medium text-gray-500 dark:text-gray-400">
326
+ {getDescription()}
327
+ </span>
328
+ </span>
329
+ </span>
330
+ </button>
331
+
332
+ {/* Chevron Separator */}
333
+ {!isFirst && (
334
+ <div aria-hidden="true" className="absolute inset-0 top-0 left-0 hidden w-3 lg:block">
335
+ <svg
336
+ viewBox="0 0 12 82"
337
+ fill="none"
338
+ preserveAspectRatio="none"
339
+ className="size-full text-gray-300 dark:text-white/15"
340
+ >
341
+ <path
342
+ d="M0.5 0V31L10.5 41L0.5 51V82"
343
+ stroke="currentcolor"
344
+ vectorEffect="non-scaling-stroke"
345
+ />
346
+ </svg>
347
+ </div>
348
+ )}
349
+ </div>
350
+ </li>
351
+ );
352
+ }
353
+
354
+ // Component to display submission data
355
+ function SubmissionDataDisplay({
356
+ submission,
357
+ activeCitationId,
358
+ onAnnotationSelect,
359
+ boundingKey,
360
+ setBoundingKey,
361
+ }: {
362
+ submission: Awaited<ReturnType<typeof getSubmissionDataFn>> | null;
363
+ activeCitationId: string | undefined;
364
+ onAnnotationSelect: (id: string | undefined) => void;
365
+ boundingKey: string | undefined;
366
+ setBoundingKey: (key: string | undefined) => void;
367
+ }) {
368
+ // Transform groups to match the structure expected by SubmissionGroupWrapper
369
+ const groups = useMemo(() => {
370
+ if (!submission?.groups) return [];
371
+
372
+ return submission.groups.map((group) => {
373
+ const items = group.items.map((item) => {
374
+ // Use selectedVersion from DAL instead of versions[0]
375
+ // The DAL attaches transformationSource to selectedVersion
376
+ const version = item.selectedVersion ?? item.versions[0];
377
+
378
+ return {
379
+ ...item,
380
+ selectedVersion: version ?? null,
381
+ };
382
+ });
383
+
384
+ return {
385
+ ...group,
386
+ items: [...items].sort((a, b) => a.formField.order - b.formField.order),
387
+ itemsMap: convertItemsToRecord(items, "formFieldId"),
388
+ derivedMap: convertItemsToRecord(group.derived, "formDerivedId"),
389
+ };
390
+ });
391
+ }, [submission]);
392
+
393
+ const grouped = useMemo(() => {
394
+ if (!submission?.form?.groups) return [];
395
+
396
+ const groupObj = groupBy(groups, "formGroupId");
397
+
398
+ return submission.form.groups.map((formGroup) => ({
399
+ formGroup,
400
+ groups: groupObj[formGroup.id] ?? [],
401
+ }));
402
+ }, [submission, groups]);
403
+
404
+ const noop = () => {};
405
+
406
+ if (!submission) {
407
+ return (
408
+ <div className="p-4">
409
+ <p className="text-muted-foreground text-sm">Submission data not available</p>
410
+ </div>
411
+ );
412
+ }
413
+
414
+ return (
415
+ <div className="space-y-4">
416
+ {grouped.length > 0 ? (
417
+ <div className="space-y-4">
418
+ {grouped.map(({ formGroup, groups }) => (
419
+ <SubmissionGroupWrapper
420
+ key={formGroup.id}
421
+ formGroup={formGroup}
422
+ groups={groups}
423
+ setUnsaved={noop}
424
+ disableInput={false}
425
+ annotationId={activeCitationId}
426
+ setAnnotationId={onAnnotationSelect}
427
+ boundingKey={boundingKey}
428
+ setBoundingKey={setBoundingKey}
429
+ submissionId={submission.id}
430
+ />
431
+ ))}
432
+ </div>
433
+ ) : (
434
+ <div className="p-4">
435
+ <p className="text-muted-foreground text-sm">No data available</p>
436
+ </div>
437
+ )}
438
+ </div>
439
+ );
440
+ }
441
+
442
+ // Helper component for step details
443
+ function StepDetails({
444
+ step,
445
+ allStepsForThisStep,
446
+ outputSubmissionData,
447
+ onReplay,
448
+ isReplaying,
449
+ activeCitationId,
450
+ onAnnotationSelect,
451
+ boundingKey,
452
+ setBoundingKey,
453
+ }: {
454
+ step: PipelineRunStepWithDetails;
455
+ allStepsForThisStep: PipelineRunStepWithDetails[];
456
+ outputSubmissionData: Map<string, Awaited<ReturnType<typeof getSubmissionDataFn>>[]>;
457
+ onReplay: (stepId: string) => void;
458
+ isReplaying: boolean;
459
+ activeCitationId: string | undefined;
460
+ onAnnotationSelect: (id: string | undefined) => void;
461
+ boundingKey: string | undefined;
462
+ setBoundingKey: (key: string | undefined) => void;
463
+ }) {
464
+ const [selectedSubmissionId, setSelectedSubmissionId] = useState<string | undefined>(undefined);
465
+
466
+ // Collect all submission items to display (from all run steps)
467
+ const submissionItems = allStepsForThisStep.flatMap((runStep, runStepIdx) => {
468
+ const submissions = outputSubmissionData.get(runStep.id) ?? [];
469
+ return submissions.map((submission, submissionIdx) => ({
470
+ runStep,
471
+ runStepIdx,
472
+ submission,
473
+ submissionIdx,
474
+ key: `${runStep.id}-${submission?.id ?? submissionIdx}`,
475
+ label:
476
+ allStepsForThisStep.length > 1
477
+ ? submissions.length > 1
478
+ ? `Instance ${runStepIdx + 1} - ${submission?.form?.name ?? `Form ${submissionIdx + 1}`}`
479
+ : `Instance ${runStepIdx + 1}`
480
+ : submissions.length > 1
481
+ ? (submission?.form?.name ?? `Form ${submissionIdx + 1}`)
482
+ : null,
483
+ }));
484
+ });
485
+
486
+ // If there are multiple submissions (from multiple run steps OR multiple forms), show accordion
487
+ const isMulti = submissionItems.length > 1;
488
+
489
+ if (isMulti) {
490
+ return (
491
+ <div className="space-y-4">
492
+ <div className="text-muted-foreground mb-2 text-sm">
493
+ {submissionItems.length} output{submissionItems.length !== 1 ? "s" : ""}
494
+ </div>
495
+ <Accordion
496
+ type="single"
497
+ collapsible
498
+ className="space-y-2"
499
+ value={selectedSubmissionId}
500
+ onValueChange={setSelectedSubmissionId}
501
+ >
502
+ {submissionItems.map((item, idx) => {
503
+ return (
504
+ <AccordionItem key={item.key} value={item.key} className="rounded-lg border">
505
+ <AccordionTrigger className="px-4 hover:no-underline">
506
+ <div className="flex min-w-0 flex-1 items-center justify-between gap-3">
507
+ <div className="flex min-w-0 flex-1 items-center gap-3">
508
+ <div className="flex size-8 shrink-0 items-center justify-center rounded-full bg-indigo-600 text-sm font-medium text-white dark:bg-indigo-500">
509
+ {idx + 1}
510
+ </div>
511
+ <div className="min-w-0 flex-1 text-left">
512
+ <div className="truncate font-medium">
513
+ {item.label ?? `Entry ${idx + 1}`}
514
+ </div>
515
+ {item.submission && (
516
+ <div className="text-muted-foreground truncate text-xs">
517
+ {item.submission.form.name}
518
+ </div>
519
+ )}
520
+ </div>
521
+ </div>
522
+ <Badge
523
+ variant={
524
+ item.runStep.status === "completed"
525
+ ? "green"
526
+ : item.runStep.status === "failed"
527
+ ? "destructive"
528
+ : "secondary"
529
+ }
530
+ className="ml-2"
531
+ >
532
+ {item.runStep.status}
533
+ </Badge>
534
+ </div>
535
+ </AccordionTrigger>
536
+ <AccordionContent className="px-4 pb-4">
537
+ {item.submission ? (
538
+ <SubmissionDataDisplay
539
+ submission={item.submission}
540
+ activeCitationId={activeCitationId}
541
+ onAnnotationSelect={onAnnotationSelect}
542
+ boundingKey={boundingKey}
543
+ setBoundingKey={setBoundingKey}
544
+ />
545
+ ) : (
546
+ <div className="text-muted-foreground py-4 text-center text-sm">
547
+ No submission data available
548
+ </div>
549
+ )}
550
+ </AccordionContent>
551
+ </AccordionItem>
552
+ );
553
+ })}
554
+ </Accordion>
555
+ </div>
556
+ );
557
+ }
558
+
559
+ // Single submission - show directly
560
+ const singleItem = submissionItems[0];
561
+ const submission = singleItem?.submission ?? null;
562
+
563
+ return (
564
+ <div>
565
+ <SubmissionDataDisplay
566
+ submission={submission}
567
+ activeCitationId={activeCitationId}
568
+ onAnnotationSelect={onAnnotationSelect}
569
+ boundingKey={boundingKey}
570
+ setBoundingKey={setBoundingKey}
571
+ />
572
+ </div>
573
+ );
574
+ }
575
+
576
+ function RouteComponent() {
577
+ const { pipelineId, runId } = Route.useParams();
578
+ const {
579
+ timeline: initialTimeline,
580
+ pipeline,
581
+ inputSubmissionData: initialInputSubmissionData,
582
+ outputSubmissionData: initialOutputSubmissionData,
583
+ blockers: initialBlockers,
584
+ availableSubmissions: initialAvailableSubmissions,
585
+ } = Route.useLoaderData();
586
+ const router = useRouter();
587
+ const downloadDocument = useServerFn(downloadDocumentFn);
588
+ const replayRun = useServerFn(replayPipelineRunFn);
589
+ const replayStep = useServerFn(replayPipelineRunStepFn);
590
+ const startRun = useServerFn(startPipelineRunFn);
591
+ const [selectedStepId, setSelectedStepId] = useState<string | undefined>(undefined);
592
+ const [timeline, setTimeline] = useState(initialTimeline);
593
+ const [inputSubmissionData, setInputSubmissionData] = useState(initialInputSubmissionData);
594
+ const [outputSubmissionData, setOutputSubmissionData] = useState(initialOutputSubmissionData);
595
+ const [blockers, setBlockers] = useState(initialBlockers);
596
+ const [availableSubmissions, setAvailableSubmissions] = useState(initialAvailableSubmissions);
597
+ const [isReplaying, setIsReplaying] = useState(false);
598
+ const [isStarting, setIsStarting] = useState(false);
599
+ const [viewMode, setViewMode] = useState<"flow" | "dag">("flow");
600
+ const crumbs = [
601
+ { href: "/pipelines", label: "Pipelines" },
602
+ { href: `/pipelines/${pipelineId}`, label: pipeline?.name ?? pipelineId },
603
+ { href: `/pipelines/${pipelineId}/${runId}`, label: runId.slice(0, 12) + "..." },
604
+ ];
605
+
606
+ // Create unique steps for process flow (one per stepId), ordered by execution level
607
+ const allSteps = useMemo<ProcessFlowStepData[]>(() => {
608
+ if (!timeline?.steps || !pipeline?.steps) return [];
609
+
610
+ // Group by stepId and take the first run step for each
611
+ const uniqueSteps = new Map<string, PipelineRunStepWithDetails>();
612
+ for (const runStep of timeline.steps) {
613
+ if (runStep.stepId && !uniqueSteps.has(runStep.stepId)) {
614
+ uniqueSteps.set(runStep.stepId, runStep);
615
+ }
616
+ }
617
+
618
+ // Pipeline steps are already sorted by execution level in getPipelineDetail
619
+ // Map pipeline step order to get the correct ordering
620
+ const steps: PipelineRunStepWithDetails[] = [];
621
+ for (const pipelineStep of pipeline.steps) {
622
+ const runStep = uniqueSteps.get(pipelineStep.id);
623
+ if (runStep) {
624
+ steps.push(runStep);
625
+ }
626
+ }
627
+
628
+ return steps;
629
+ }, [timeline, pipeline]);
630
+
631
+ // Sync loader data when it changes (but don't reset selected step on polls)
632
+ useEffect(() => {
633
+ setTimeline(initialTimeline);
634
+ setInputSubmissionData(initialInputSubmissionData);
635
+ setOutputSubmissionData(initialOutputSubmissionData);
636
+ setBlockers(initialBlockers);
637
+ setAvailableSubmissions(initialAvailableSubmissions);
638
+ }, [
639
+ initialTimeline,
640
+ initialInputSubmissionData,
641
+ initialOutputSubmissionData,
642
+ initialBlockers,
643
+ initialAvailableSubmissions,
644
+ ]);
645
+
646
+ // Set initial selected step when steps load
647
+ useEffect(() => {
648
+ if (allSteps.length > 0 && !selectedStepId) {
649
+ setSelectedStepId(allSteps[0]?.stepId ?? undefined);
650
+ }
651
+ }, [allSteps, selectedStepId]);
652
+
653
+ // Poll for updates if run is not complete
654
+ const isRunActive = timeline?.status === "pending" || timeline?.status === "running";
655
+
656
+ useEffect(() => {
657
+ if (!isRunActive) return;
658
+
659
+ const interval = setInterval(() => {
660
+ router.invalidate();
661
+ }, 1000); // Poll every second
662
+
663
+ return () => clearInterval(interval);
664
+ }, [isRunActive, router]);
665
+
666
+ // Group run steps by their stepId
667
+ const stepsByStepId = useMemo(() => {
668
+ if (!timeline?.steps) return new Map<string, PipelineRunStepWithDetails[]>();
669
+ const grouped = new Map<string, PipelineRunStepWithDetails[]>();
670
+ for (const runStep of timeline.steps) {
671
+ const stepId = runStep.stepId;
672
+ if (!stepId) continue;
673
+ if (!grouped.has(stepId)) {
674
+ grouped.set(stepId, []);
675
+ }
676
+ grouped.get(stepId)!.push(runStep);
677
+ }
678
+ return grouped;
679
+ }, [timeline]);
680
+
681
+ // Find the first run step for the selected stepId (for display purposes)
682
+ const selectedStep = timeline?.steps?.find((s) => s.stepId === selectedStepId);
683
+ // Get all run steps for the selected stepId (for multi-step display)
684
+ const allStepsForSelected = selectedStepId ? (stepsByStepId.get(selectedStepId) ?? []) : [];
685
+
686
+ const getStatusBadgeVariant = (
687
+ status: string,
688
+ ): "default" | "secondary" | "destructive" | "green" => {
689
+ if (status === "completed" || status === "success") return "green";
690
+ if (status === "failed" || status === "error") return "destructive";
691
+ if (status === "running" || status === "pending") return "secondary";
692
+ return "default";
693
+ };
694
+
695
+ const getDuration = () => {
696
+ if (!timeline?.completedAt) return null;
697
+ return new Date(timeline.completedAt).getTime() - new Date(timeline.startedAt).getTime();
698
+ };
699
+
700
+ const formatDuration = (ms: number | null) => {
701
+ if (!ms) return null;
702
+ const seconds = ms / 1000;
703
+ if (seconds < 60) return `${seconds.toFixed(1)}s`;
704
+ const minutes = Math.floor(seconds / 60);
705
+ const remainingSeconds = (seconds % 60).toFixed(0);
706
+ return `${minutes}m ${remainingSeconds}s`;
707
+ };
708
+
709
+ const handleStartRun = async () => {
710
+ if (!timeline || isStarting) return;
711
+ setIsStarting(true);
712
+ try {
713
+ await startRun({ data: { runId, pipelineId } });
714
+ // Invalidate to refresh the page and show updated status
715
+ router.invalidate();
716
+ } catch (error) {
717
+ console.error("Failed to start run:", error);
718
+ alert("Failed to start run. Please try again.");
719
+ } finally {
720
+ setIsStarting(false);
721
+ }
722
+ };
723
+
724
+ const handleReplayRun = async () => {
725
+ if (!timeline || isReplaying) return;
726
+ setIsReplaying(true);
727
+ try {
728
+ const result = await replayRun({ data: { runId } });
729
+ // Navigate to the new run
730
+ // router.navigate({
731
+ // to: "/pipelines/$pipelineId/$runId",
732
+ // params: { pipelineId, runId: result.runId },
733
+ // });
734
+ } catch (error) {
735
+ console.error("Failed to replay run:", error);
736
+ alert("Failed to replay run. Please try again.");
737
+ } finally {
738
+ setIsReplaying(false);
739
+ }
740
+ };
741
+
742
+ const handleReplayStep = async (stepId: string) => {
743
+ if (isReplaying) return;
744
+ setIsReplaying(true);
745
+ try {
746
+ const result = await replayStep({ data: { runStepId: stepId } });
747
+ // Navigate to the new run
748
+ // router.navigate({
749
+ // to: "/pipelines/$pipelineId/$runId",
750
+ // params: { pipelineId, runId: result.runId },
751
+ // });
752
+ } catch (error) {
753
+ console.error("Failed to replay step:", error);
754
+ alert("Failed to replay step. Please try again.");
755
+ } finally {
756
+ setIsReplaying(false);
757
+ }
758
+ };
759
+
760
+ const extractionInfo = useMemo<(ExtractionInfo & { extractionTrigger: "web" | "api" })[]>(() => {
761
+ // Collect all unique extractions from input and output submissions
762
+ const extractionsById = new Map<
763
+ string,
764
+ ExtractionInfo & { extractionTrigger: "web" | "api" }
765
+ >();
766
+
767
+ // Helper to add extractions from a submission
768
+ const addExtractionsFromSubmission = (
769
+ submission: Awaited<ReturnType<typeof getSubmissionDataFn>> | null,
770
+ label: string,
771
+ ) => {
772
+ if (!submission?.extractions) {
773
+ return;
774
+ }
775
+ submission.extractions.forEach((e) => {
776
+ // Filter out deleted documents and extractions
777
+ if (e.deletedAt || e.document.deletedAt) {
778
+ return;
779
+ }
780
+ if (!extractionsById.has(e.id)) {
781
+ extractionsById.set(e.id, {
782
+ documentId: e.document.id,
783
+ documentExtractionId: e.id,
784
+ url: e.document.blobUrl,
785
+ urlExpiry: e.document.blobUrlExpiry,
786
+ uploaded: e.document.uploaded,
787
+ name: e.document.name,
788
+ composite: e.document.composite,
789
+ parentId: e.document.parentId,
790
+ classificationLabel: e.document.classificationLabel,
791
+ status: e.status,
792
+ displayType: e.document.displayType,
793
+ extractionModel: e.extractionModel ?? undefined,
794
+ extractionTrigger: e.extractionTrigger,
795
+ extractedData: e.data,
796
+ reconciliationData: e.reconciliation as ApiReconciliation,
797
+ citations: [] as DocumentCitation[],
798
+ });
799
+ }
800
+ });
801
+ };
802
+
803
+ // Helper to add citations from a submission's groups
804
+ const addCitationsFromSubmission = (
805
+ submission: Awaited<ReturnType<typeof getSubmissionDataFn>> | null,
806
+ label: string,
807
+ ) => {
808
+ if (!submission?.groups) {
809
+ return;
810
+ }
811
+ submission.groups.forEach((group) => {
812
+ group.items.forEach((item) => {
813
+ const version = item.selectedVersion;
814
+ if (!version) return;
815
+
816
+ // Recursive helper to collect document citations from transformation chains
817
+ const collectDocumentCitations = (v: typeof version): DocumentCitation[] => {
818
+ const citations: DocumentCitation[] = [];
819
+
820
+ // If this version has a direct document source, add it
821
+ if (v.documentSource) {
822
+ citations.push(v.documentSource);
823
+ }
824
+
825
+ // If this version has transformation sources, recursively collect their citations
826
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
827
+ const transformationSource = (v as any).transformationSource as
828
+ | Array<typeof version>
829
+ | undefined;
830
+ if (transformationSource && Array.isArray(transformationSource)) {
831
+ for (const sourceVersion of transformationSource) {
832
+ citations.push(...collectDocumentCitations(sourceVersion));
833
+ }
834
+ }
835
+
836
+ return citations;
837
+ };
838
+
839
+ const citations = collectDocumentCitations(version);
840
+
841
+ if (citations.length > 0) {
842
+ citations.forEach((citation) => {
843
+ const extraction = extractionsById.get(citation.documentExtractionId);
844
+ if (extraction) {
845
+ extraction.citations.push(citation);
846
+ } else {
847
+ }
848
+ });
849
+ }
850
+ });
851
+ });
852
+ };
853
+
854
+ // Add extractions from all input submissions
855
+ if (inputSubmissionData) {
856
+ let index = 0;
857
+ inputSubmissionData.forEach((submission, submissionId) => {
858
+ const label = `Input[${index}:${submissionId}]`;
859
+ addExtractionsFromSubmission(submission, label);
860
+ addCitationsFromSubmission(submission, label);
861
+ index++;
862
+ });
863
+ }
864
+
865
+ // Add extractions and citations from all output submissions
866
+ if (outputSubmissionData) {
867
+ let index = 0;
868
+ outputSubmissionData.forEach((submissions, runStepId) => {
869
+ submissions.forEach((submission, submissionIdx) => {
870
+ const label = `Output[${index}:${runStepId}:${submissionIdx}]`;
871
+ addExtractionsFromSubmission(submission, label);
872
+ addCitationsFromSubmission(submission, label);
873
+ index++;
874
+ });
875
+ });
876
+ }
877
+
878
+ return Array.from(extractionsById.values());
879
+ }, [inputSubmissionData, outputSubmissionData, timeline]);
880
+
881
+ const [visibleExtractionId, setDocVisible] = useState<string | undefined>(undefined);
882
+ const [activeCitationId, setActiveCitationId] = useState<string | undefined>(undefined);
883
+ const [boundingKey, setBoundingKey] = useState<string | undefined>(undefined);
884
+
885
+ // Filter extraction info to only show documents from the selected step's output submissions
886
+ const selectedStepExtractionInfo = useMemo(() => {
887
+ if (!selectedStepId) return extractionInfo;
888
+
889
+ // Get all run steps for the selected stepId
890
+ const runStepsForSelected = stepsByStepId.get(selectedStepId) ?? [];
891
+ const runStepIds = new Set(runStepsForSelected.map((s) => s.id));
892
+
893
+ // Collect extraction IDs from output submissions of these run steps
894
+ const allowedExtractionIds = new Set<string>();
895
+ runStepIds.forEach((runStepId) => {
896
+ const submissions = outputSubmissionData.get(runStepId) ?? [];
897
+ submissions.forEach((submission) => {
898
+ submission?.extractions?.forEach((extraction) => {
899
+ allowedExtractionIds.add(extraction.id);
900
+ });
901
+ });
902
+ });
903
+
904
+ // Filter to only include extractions from the selected step's output submissions
905
+ return extractionInfo.filter((ext) => allowedExtractionIds.has(ext.documentExtractionId));
906
+ }, [extractionInfo, selectedStepId, stepsByStepId, outputSubmissionData]);
907
+
908
+ const activeDocument = useMemo(
909
+ () =>
910
+ visibleExtractionId
911
+ ? extractionInfo.find((p) => p.documentExtractionId === visibleExtractionId && !!p.url)
912
+ : undefined,
913
+ [visibleExtractionId, extractionInfo],
914
+ );
915
+
916
+ const focusDocumentForCitation = useCallback(
917
+ (citationId: string | undefined) => {
918
+ if (citationId === undefined) return;
919
+ const selectedPdf = extractionInfo.find((p) => p.citations.find((c) => c.id === citationId));
920
+ if (!selectedPdf) return;
921
+ setDocVisible(selectedPdf.documentExtractionId);
922
+ },
923
+ [extractionInfo],
924
+ );
925
+
926
+ const setActiveAnnotation = useCallback(
927
+ (id: string | undefined, options: { focus?: boolean } = {}) => {
928
+ const { focus = true } = options;
929
+ setActiveCitationId(id);
930
+ if (focus && id) {
931
+ focusDocumentForCitation(id);
932
+ }
933
+ },
934
+ [focusDocumentForCitation],
935
+ );
936
+
937
+ const handleFormAnnotationSelect = useCallback(
938
+ (id: string | undefined) => {
939
+ setActiveAnnotation(id, { focus: true });
940
+ },
941
+ [setActiveAnnotation],
942
+ );
943
+
944
+ const handleSelectCitation = useCallback(
945
+ (citationId?: string, key?: string) => {
946
+ setBoundingKey(key);
947
+ setActiveAnnotation(citationId, { focus: true });
948
+
949
+ // Scroll to the form field containing this citation
950
+ setTimeout(() => {
951
+ const element = document.querySelector(`[data-citation-id="${citationId}"]`);
952
+ if (element) {
953
+ element.scrollIntoView({ behavior: "smooth", block: "center" });
954
+ }
955
+ }, 100);
956
+ },
957
+ [setActiveAnnotation],
958
+ );
959
+
960
+ const refreshPdfUrl = useCallback(() => {
961
+ if (activeDocument) {
962
+ return downloadDocument({ data: { documentId: activeDocument.documentId } }).then(
963
+ ({ blobUrl }) => {
964
+ // Update the URL in extractionInfo if needed
965
+ const extraction = extractionInfo.find(
966
+ (e) => e.documentExtractionId === activeDocument.documentExtractionId,
967
+ );
968
+ if (extraction) {
969
+ extraction.url = blobUrl;
970
+ }
971
+ },
972
+ );
973
+ }
974
+ return Promise.resolve();
975
+ }, [activeDocument, extractionInfo, downloadDocument]);
976
+
977
+ const handleDownload = async (documentId: string) => {
978
+ const { blobUrl } = await downloadDocument({ data: { documentId } });
979
+ const [domain, docPath] = blobUrl.split("/documents/");
980
+ if (domain && docPath) {
981
+ const newUrl = new URL(docPath, "https://ph.sea.dev/doc/").toString();
982
+ window.open(newUrl, "_blank");
983
+ } else {
984
+ window.open(blobUrl, "_blank");
985
+ }
986
+ };
987
+
988
+ // State for upload modal
989
+ const [uploadModalOpen, setUploadModalOpen] = useState(false);
990
+ const [selectedRunStepId, setSelectedRunStepId] = useState<string | null>(null);
991
+
992
+ // State for submission selector modal
993
+ const [submissionSelectorOpen, setSubmissionSelectorOpen] = useState(false);
994
+ const [selectedBlockerDetails, setSelectedBlockerDetails] = useState<{
995
+ blockerId: string;
996
+ runStepId: string;
997
+ stepName: string;
998
+ requiredFormId?: string;
999
+ requiredFormName?: string;
1000
+ } | null>(null);
1001
+
1002
+ const handleResolveBlocker = useCallback(
1003
+ (blockerId: string, blockerType: string, runStepId: string) => {
1004
+ switch (blockerType) {
1005
+ case "missing_upload":
1006
+ // Open upload modal
1007
+ setSelectedRunStepId(runStepId);
1008
+ setUploadModalOpen(true);
1009
+ break;
1010
+ case "missing_manual_input": {
1011
+ // Find the blocker details to get form info
1012
+ const stepBlockers = blockers?.find((b) => b.runStepId === runStepId);
1013
+ const blocker = stepBlockers?.blockers.find((b) => b.id === blockerId);
1014
+ const runStep = timeline?.steps.find((s) => s.id === runStepId);
1015
+ const pipelineStep = pipeline?.steps.find((s) => s.id === runStep?.stepId);
1016
+
1017
+ const details = blocker?.blockerDetails;
1018
+ const formId =
1019
+ details && "requiredFormId" in details ? details.requiredFormId : undefined;
1020
+ const formName =
1021
+ details && "requiredFormName" in details ? details.requiredFormName : undefined;
1022
+
1023
+ setSelectedBlockerDetails({
1024
+ blockerId,
1025
+ runStepId,
1026
+ stepName: pipelineStep?.name ?? "Unknown Step",
1027
+ requiredFormId: formId,
1028
+ requiredFormName: formName,
1029
+ });
1030
+ setSubmissionSelectorOpen(true);
1031
+ break;
1032
+ }
1033
+ case "missing_config":
1034
+ // Navigate to pipeline configuration
1035
+ toast.info("Navigate to pipeline configuration to fix the step setup", {
1036
+ action: {
1037
+ label: "Go to Config",
1038
+ onClick: () => {
1039
+ router.navigate({
1040
+ to: "/pipelines/$pipelineId",
1041
+ params: { pipelineId },
1042
+ });
1043
+ },
1044
+ },
1045
+ });
1046
+ break;
1047
+ default:
1048
+ // Unknown blocker type
1049
+ break;
1050
+ }
1051
+ },
1052
+ [pipelineId, router, blockers, timeline, pipeline],
1053
+ );
1054
+
1055
+ const handleSubmitManualInput = async (submissionIds: string[]) => {
1056
+ if (!selectedBlockerDetails) return;
1057
+
1058
+ try {
1059
+ await resolveBlockerWithManualInputFn({
1060
+ data: {
1061
+ blockerId: selectedBlockerDetails.blockerId,
1062
+ runStepId: selectedBlockerDetails.runStepId,
1063
+ submissionIds,
1064
+ },
1065
+ });
1066
+ toast.success("Manual input added successfully");
1067
+ router.invalidate();
1068
+ } catch (error) {
1069
+ console.error("Failed to add manual input:", error);
1070
+ toast.error("Failed to add manual input");
1071
+ throw error;
1072
+ }
1073
+ };
1074
+
1075
+ const sidebar = (
1076
+ <SidebarRight className="border-none">
1077
+ {activeDocument && (
1078
+ <div className="flex h-full flex-col">
1079
+ <div className="relative flex w-full items-center justify-between gap-4 border-b p-2">
1080
+ <H3>{activeDocument.name}</H3>
1081
+ <div className="flex items-center gap-1">
1082
+ <Button
1083
+ size="icon"
1084
+ variant="ghost"
1085
+ onClick={() => {
1086
+ setDocVisible(undefined);
1087
+ setBoundingKey(undefined);
1088
+ }}
1089
+ >
1090
+ <XIcon />
1091
+ <span className="sr-only">Close</span>
1092
+ </Button>
1093
+ </div>
1094
+ </div>
1095
+ <PdfViewer
1096
+ pdfInfo={activeDocument}
1097
+ activeAnnotationKey={boundingKey}
1098
+ activeCitationId={activeCitationId}
1099
+ onSelectCitation={handleSelectCitation}
1100
+ refreshPdfUrl={refreshPdfUrl}
1101
+ />
1102
+ </div>
1103
+ )}
1104
+ {!activeDocument && (
1105
+ <ResizablePanelGroup direction="vertical">
1106
+ <ResizablePanel defaultSize={60}>
1107
+ <div className="flex h-full min-h-0 flex-col gap-2 p-2">
1108
+ <H3>Artifacts</H3>
1109
+ <div className="h-full min-h-0 space-y-2 overflow-y-auto">
1110
+ {timeline?.artifacts && timeline.artifacts.length > 0 ? (
1111
+ timeline.artifacts.map((artifact) => {
1112
+ const markdownContent =
1113
+ typeof artifact.content === "object" &&
1114
+ artifact.content !== null &&
1115
+ "markdown" in artifact.content
1116
+ ? String(artifact.content.markdown)
1117
+ : typeof artifact.content === "string"
1118
+ ? artifact.content
1119
+ : JSON.stringify(artifact.content, null, 2);
1120
+
1121
+ const markdownFileName = `${artifact.title}.md`;
1122
+ const wordFileName = `${artifact.title}.doc`;
1123
+
1124
+ return (
1125
+ <Dialog key={artifact.id}>
1126
+ <div className="group/item bg-muted/50 hover:bg-muted/70 flex items-center justify-between gap-2 rounded-md">
1127
+ <DialogTrigger asChild>
1128
+ <button className="flex grow cursor-pointer items-center justify-between gap-2 p-2 text-left">
1129
+ <span className="flex min-w-0 items-center gap-2">
1130
+ <FileTextIcon className="text-primary h-4 w-4 shrink-0" />
1131
+ <span className="truncate text-sm">{artifact.title}</span>
1132
+ </span>
1133
+ <Badge variant="secondary" className="shrink-0 text-xs">
1134
+ {artifact.type}
1135
+ </Badge>
1136
+ </button>
1137
+ </DialogTrigger>
1138
+ </div>
1139
+ <DialogContent className="h-[90vh] sm:max-w-6xl">
1140
+ <DialogHeader>
1141
+ <DialogTitle>{artifact.title}</DialogTitle>
1142
+ </DialogHeader>
1143
+ <div className="prose prose-sm dark:prose-invert max-h-[60vh] max-w-full overflow-y-auto">
1144
+ {artifact.type === "markdown" ? (
1145
+ <Markdown remarkPlugins={[remarkGfm]}>{markdownContent}</Markdown>
1146
+ ) : (
1147
+ <pre className="text-xs">{markdownContent}</pre>
1148
+ )}
1149
+ </div>
1150
+ <DialogFooter className="flex w-full flex-wrap items-center justify-between gap-2">
1151
+ <div className="text-muted-foreground text-xs">
1152
+ Created <RelDate date={artifact.createdAt} />
1153
+ </div>
1154
+ <div className="flex gap-2">
1155
+ <Button
1156
+ variant="secondary"
1157
+ size="sm"
1158
+ onClick={() =>
1159
+ downloadBlob(
1160
+ new Blob([markdownContent], { type: "text/markdown" }),
1161
+ markdownFileName,
1162
+ )
1163
+ }
1164
+ >
1165
+ <DownloadIcon className="mr-2 h-4 w-4" />
1166
+ Download Markdown
1167
+ </Button>
1168
+ <Button
1169
+ variant="outline"
1170
+ size="sm"
1171
+ onClick={() =>
1172
+ downloadBlob(
1173
+ new Blob(
1174
+ [
1175
+ `<!DOCTYPE html><html><head><meta charset="utf-8" /></head><body><pre>${escapeHtml(
1176
+ markdownContent,
1177
+ )}</pre></body></html>`,
1178
+ ],
1179
+ { type: "application/msword" },
1180
+ ),
1181
+ wordFileName,
1182
+ )
1183
+ }
1184
+ >
1185
+ <DownloadIcon className="mr-2 h-4 w-4" />
1186
+ Download Word
1187
+ </Button>
1188
+ </div>
1189
+ </DialogFooter>
1190
+ </DialogContent>
1191
+ </Dialog>
1192
+ );
1193
+ })
1194
+ ) : (
1195
+ <div className="text-muted-foreground flex items-center justify-center py-8">
1196
+ No artifacts yet
1197
+ </div>
1198
+ )}
1199
+ </div>
1200
+ </div>
1201
+ </ResizablePanel>
1202
+ <ResizableHandle withHandle={true} />
1203
+ <ResizablePanel defaultSize={40}>
1204
+ <DocList
1205
+ docs={selectedStepExtractionInfo}
1206
+ setDocVisible={setDocVisible}
1207
+ canExtract={true}
1208
+ download={handleDownload}
1209
+ />
1210
+ </ResizablePanel>
1211
+ </ResizablePanelGroup>
1212
+ )}
1213
+ </SidebarRight>
1214
+ );
1215
+
1216
+ return (
1217
+ <Container crumbs={crumbs} sidebar={sidebar}>
1218
+ <div className="space-y-6">
1219
+ {/* Header with all run metadata */}
1220
+ <div className="space-y-3">
1221
+ <div className="flex items-center justify-between">
1222
+ <h1 className="font-mono text-2xl font-semibold">{runId}</h1>
1223
+ <div className="flex items-center gap-2">
1224
+ {/* View Mode Toggle */}
1225
+ {timeline && allSteps.length > 0 && pipeline && (
1226
+ <div className="flex gap-1 rounded-md border p-1">
1227
+ <button
1228
+ type="button"
1229
+ onClick={() => setViewMode("flow")}
1230
+ className={`rounded px-3 py-0.5 text-sm font-medium transition-colors ${
1231
+ viewMode === "flow" ? "bg-primary text-primary-foreground" : "hover:bg-muted"
1232
+ }`}
1233
+ >
1234
+ Flow
1235
+ </button>
1236
+ <button
1237
+ type="button"
1238
+ onClick={() => setViewMode("dag")}
1239
+ className={`rounded px-3 py-0.5 text-sm font-medium transition-colors ${
1240
+ viewMode === "dag" ? "bg-primary text-primary-foreground" : "hover:bg-muted"
1241
+ }`}
1242
+ >
1243
+ DAG
1244
+ </button>
1245
+ </div>
1246
+ )}
1247
+ {timeline && timeline.status === "pending" && (
1248
+ <Button
1249
+ variant="default"
1250
+ size="sm"
1251
+ onClick={handleStartRun}
1252
+ disabled={
1253
+ isStarting ||
1254
+ (blockers?.some((b) => b.blockers.some((blocker) => !blocker.resolved)) ??
1255
+ false)
1256
+ }
1257
+ >
1258
+ <PlayIcon className="mr-2 size-4" />
1259
+ {isStarting
1260
+ ? "Starting..."
1261
+ : (blockers?.some((b) => b.blockers.some((blocker) => !blocker.resolved)) ??
1262
+ false)
1263
+ ? "Resolve Blockers First"
1264
+ : "Start Run"}
1265
+ </Button>
1266
+ )}
1267
+ {timeline && (
1268
+ <Button
1269
+ variant="outline"
1270
+ size="sm"
1271
+ onClick={handleReplayRun}
1272
+ disabled={isReplaying}
1273
+ >
1274
+ <RotateCcwIcon className="size-4" />
1275
+ </Button>
1276
+ )}
1277
+ </div>
1278
+ </div>
1279
+
1280
+ {timeline && (
1281
+ <div className="flex flex-wrap items-center gap-x-6 gap-y-2 text-sm">
1282
+ <div className="flex items-center gap-2">
1283
+ <span className="text-muted-foreground">Created</span>
1284
+ <span>
1285
+ <RelDate date={timeline.createdAt} />
1286
+ </span>
1287
+ </div>
1288
+ <span className="text-muted-foreground">|</span>
1289
+ <div className="flex items-center gap-2">
1290
+ <span className="text-muted-foreground">Started</span>
1291
+ <span>
1292
+ <RelDate date={timeline.startedAt} />
1293
+ </span>
1294
+ </div>
1295
+ <span className="text-muted-foreground">|</span>
1296
+ {timeline.completedAt && (
1297
+ <>
1298
+ <div className="flex items-center gap-2">
1299
+ <span className="text-muted-foreground">
1300
+ {timeline.status === "completed" ? "Succeeded" : "Finished"}
1301
+ </span>
1302
+ <span>
1303
+ <RelDate date={timeline.completedAt} />
1304
+ </span>
1305
+ </div>
1306
+ <span className="text-muted-foreground">|</span>
1307
+ </>
1308
+ )}
1309
+ {getDuration() && (
1310
+ <div className="flex items-center gap-2">
1311
+ <span className="text-muted-foreground">Run took</span>
1312
+ <span className="font-medium">{formatDuration(getDuration())}</span>
1313
+ </div>
1314
+ )}
1315
+ </div>
1316
+ )}
1317
+ </div>
1318
+
1319
+ {/* Blockers Section - Only in Flow View */}
1320
+ {viewMode === "flow" && timeline && allSteps.length > 0 && (
1321
+ <Accordion type="multiple">
1322
+ <AccordionItem value="blockers">
1323
+ <AccordionTrigger className="px-4">
1324
+ <H3 className="text-base font-semibold">Dependencies</H3>
1325
+ </AccordionTrigger>
1326
+ <AccordionContent className="px-4 pb-4">
1327
+ <BlockersPanel
1328
+ steps={timeline.steps.map((step) => {
1329
+ const pipelineStep = pipeline?.steps.find((s) => s.id === step.stepId);
1330
+ const stepBlockerGroup = blockers?.find((b) => b.runStepId === step.id);
1331
+ const stepBlockers = stepBlockerGroup?.blockers ?? [];
1332
+ const hasUnresolvedBlockers = stepBlockers.some((b) => !b.resolved);
1333
+ return {
1334
+ id: step.id,
1335
+ stepId: step.id, // Use run step ID for blocker resolution
1336
+ stepName: pipelineStep?.name ?? "Unknown Step",
1337
+ status: step.status as "pending" | "running" | "completed" | "failed",
1338
+ blockers: stepBlockers,
1339
+ hasUnresolvedBlockers,
1340
+ };
1341
+ })}
1342
+ onResolveBlocker={handleResolveBlocker}
1343
+ />
1344
+ </AccordionContent>
1345
+ </AccordionItem>
1346
+ </Accordion>
1347
+ )}
1348
+
1349
+ {/* Pipeline Visualization */}
1350
+ {timeline && allSteps.length > 0 && pipeline && (
1351
+ <>
1352
+ {/* Flow View */}
1353
+ {viewMode === "flow" && (
1354
+ <div className="overflow-hidden rounded-lg border border-gray-200 dark:border-white/15">
1355
+ <nav aria-label="Progress">
1356
+ <ol role="list" className="lg:flex">
1357
+ {allSteps.map((step, idx) => {
1358
+ const stepKey = step.stepId;
1359
+ return (
1360
+ <ProcessFlowStep
1361
+ key={stepKey}
1362
+ step={step}
1363
+ isSelected={selectedStepId === stepKey}
1364
+ isFirst={idx === 0}
1365
+ isLast={idx === allSteps.length - 1}
1366
+ stepNumber={idx + 1}
1367
+ onClick={() => setSelectedStepId(stepKey ?? undefined)}
1368
+ />
1369
+ );
1370
+ })}
1371
+ </ol>
1372
+ </nav>
1373
+ </div>
1374
+ )}
1375
+
1376
+ {/* DAG View */}
1377
+ {viewMode === "dag" && (
1378
+ <div className="h-[500px] rounded-lg border border-gray-200 dark:border-white/15">
1379
+ <DAGView
1380
+ steps={pipeline.steps.map((step) => ({
1381
+ id: step.id,
1382
+ name: step.name,
1383
+ description: step.description ?? "",
1384
+ role: step.role,
1385
+ type: step.type,
1386
+ stepOrder: 0,
1387
+ }))}
1388
+ dependencies={pipeline.steps.flatMap(
1389
+ (step) =>
1390
+ step.parentDependencies?.map((dep) => ({
1391
+ id: dep.id,
1392
+ childStepId: step.id,
1393
+ parentStepId: dep.parentStepId,
1394
+ order: undefined,
1395
+ })) ?? [],
1396
+ )}
1397
+ onNodeClick={(stepId) => {
1398
+ // Find the run step for this pipeline step
1399
+ const runStep = timeline.steps.find((s) => s.stepId === stepId);
1400
+ if (runStep) {
1401
+ setSelectedStepId(stepId);
1402
+ setViewMode("flow"); // Switch to flow view to show details
1403
+ }
1404
+ }}
1405
+ />
1406
+ </div>
1407
+ )}
1408
+ </>
1409
+ )}
1410
+
1411
+ {/* Step Details - Only in Flow View */}
1412
+ {viewMode === "flow" && selectedStep && (
1413
+ <div>
1414
+ <StepDetails
1415
+ step={selectedStep}
1416
+ allStepsForThisStep={allStepsForSelected}
1417
+ outputSubmissionData={outputSubmissionData}
1418
+ onReplay={handleReplayStep}
1419
+ isReplaying={isReplaying}
1420
+ activeCitationId={activeCitationId}
1421
+ onAnnotationSelect={handleFormAnnotationSelect}
1422
+ boundingKey={boundingKey}
1423
+ setBoundingKey={setBoundingKey}
1424
+ />
1425
+ </div>
1426
+ )}
1427
+
1428
+ {/* Loading state */}
1429
+ {!timeline && (
1430
+ <Card>
1431
+ <CardContent className="py-12">
1432
+ <p className="text-muted-foreground text-center">Loading run details...</p>
1433
+ </CardContent>
1434
+ </Card>
1435
+ )}
1436
+ </div>
1437
+
1438
+ {/* Upload Documents Modal */}
1439
+ {uploadModalOpen && selectedRunStepId && (
1440
+ <Dialog open={uploadModalOpen} onOpenChange={setUploadModalOpen}>
1441
+ <DialogContent className="sm:max-w-lg">
1442
+ <DialogHeader>
1443
+ <DialogTitle>Upload Documents</DialogTitle>
1444
+ <DialogDescription>
1445
+ Upload documents to resolve the blocker and continue pipeline execution
1446
+ </DialogDescription>
1447
+ </DialogHeader>
1448
+ <UploadDocForPipeline
1449
+ runId={runId}
1450
+ runStepId={selectedRunStepId}
1451
+ isExperimental={false}
1452
+ onUploadComplete={() => {
1453
+ setUploadModalOpen(false);
1454
+ setSelectedRunStepId(null);
1455
+ }}
1456
+ />
1457
+ </DialogContent>
1458
+ </Dialog>
1459
+ )}
1460
+
1461
+ {/* Submission Selector Modal */}
1462
+ {selectedBlockerDetails && (
1463
+ <SubmissionSelectorModal
1464
+ open={submissionSelectorOpen}
1465
+ onClose={() => {
1466
+ setSubmissionSelectorOpen(false);
1467
+ setSelectedBlockerDetails(null);
1468
+ }}
1469
+ onSubmit={handleSubmitManualInput}
1470
+ availableSubmissions={availableSubmissions.map((sub) => ({
1471
+ id: sub.id,
1472
+ name: sub.name,
1473
+ formId: sub.formId,
1474
+ formName: sub.form.name,
1475
+ status: sub.status,
1476
+ updatedAt: sub.updatedAt,
1477
+ }))}
1478
+ requiredFormId={selectedBlockerDetails.requiredFormId}
1479
+ requiredFormName={selectedBlockerDetails.requiredFormName}
1480
+ stepName={selectedBlockerDetails.stepName}
1481
+ />
1482
+ )}
1483
+ </Container>
1484
+ );
1485
+ }