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.
- package/.claude/tasks/README.md +89 -0
- package/.cursor/rules/commits.mdc +31 -0
- package/.cursor/rules/general.mdc +84 -0
- package/.github/workflows/ci-cd.yml +141 -0
- package/CLAUDE.md +337 -0
- package/README.md +129 -0
- package/apps/api/.prettierignore +6 -0
- package/apps/api/.prettierrc.js +3 -0
- package/apps/api/dotenvx-safe.sh +11 -0
- package/apps/api/eslint.config.mjs +3 -0
- package/apps/api/package.json +58 -0
- package/apps/api/src/clients/posthog.ts +25 -0
- package/apps/api/src/dal/submission.ts +59 -0
- package/apps/api/src/errors.ts +55 -0
- package/apps/api/src/index.ts +21 -0
- package/apps/api/src/lib/channel.ts +28 -0
- package/apps/api/src/lib/config.ts +9 -0
- package/apps/api/src/lib/fmt.test.ts +9 -0
- package/apps/api/src/lib/fmt.ts +62 -0
- package/apps/api/src/lib/invariant.ts +23 -0
- package/apps/api/src/middleware/auth.ts +66 -0
- package/apps/api/src/routes/index.ts +20 -0
- package/apps/api/src/routes/v2/chat/handlers.ts +693 -0
- package/apps/api/src/routes/v2/chat/index.ts +257 -0
- package/apps/api/src/routes/v2/chat/schemas.ts +43 -0
- package/apps/api/src/routes/v2/deals/handlers.ts +64 -0
- package/apps/api/src/routes/v2/deals/index.ts +88 -0
- package/apps/api/src/routes/v2/deals/schemas.ts +38 -0
- package/apps/api/src/routes/v2/forms/handlers.ts +415 -0
- package/apps/api/src/routes/v2/forms/index.ts +382 -0
- package/apps/api/src/routes/v2/forms/schemas.ts +243 -0
- package/apps/api/src/routes/v2/index.ts +19 -0
- package/apps/api/src/routes/v2/pipelines/handlers.ts +261 -0
- package/apps/api/src/routes/v2/pipelines/index.ts +224 -0
- package/apps/api/src/routes/v2/pipelines/schemas.ts +173 -0
- package/apps/api/src/routes/v2/submissions/handlers.ts +555 -0
- package/apps/api/src/routes/v2/submissions/index.ts +366 -0
- package/apps/api/src/routes/v2/submissions/schemas.ts +233 -0
- package/apps/api/src/routes/v2/workflows/handlers.ts +81 -0
- package/apps/api/src/routes/v2/workflows/index.ts +88 -0
- package/apps/api/src/routes/v2/workflows/schemas.ts +40 -0
- package/apps/api/src/server.ts +146 -0
- package/apps/api/src/static/favicon.ico +0 -0
- package/apps/api/src/types/api.ts +14 -0
- package/apps/api/src/types/result.ts +3 -0
- package/apps/api/tsconfig.json +22 -0
- package/apps/api/vite.config.ts +28 -0
- package/apps/api/vitest.config.ts +14 -0
- package/apps/conversion-worker/Dockerfile +59 -0
- package/apps/conversion-worker/package.json +31 -0
- package/apps/conversion-worker/src/lib/config.ts +7 -0
- package/apps/conversion-worker/src/main.ts +22 -0
- package/apps/conversion-worker/src/workflows/convert-pptx.ts +116 -0
- package/apps/conversion-worker/tsconfig.json +27 -0
- package/apps/conversion-worker/vite.config.ts +33 -0
- package/apps/main/.prettierignore +6 -0
- package/apps/main/.prettierrc.js +3 -0
- package/apps/main/CLAUDE.md +245 -0
- package/apps/main/Procfile +1 -0
- package/apps/main/README.md +193 -0
- package/apps/main/db-tests.jsonl +116 -0
- package/apps/main/dotenvx-safe.sh +11 -0
- package/apps/main/drizzle/meta/_journal.json +1 -0
- package/apps/main/drizzle.config.ts +25 -0
- package/apps/main/eslint.config.mjs +3 -0
- package/apps/main/generate-routes.mjs +5 -0
- package/apps/main/package.json +131 -0
- package/apps/main/playwright.config.ts +23 -0
- package/apps/main/postcss.config.ts +5 -0
- package/apps/main/public/bg-dark.svg +10 -0
- package/apps/main/public/bg.svg +10 -0
- package/apps/main/public/favicon.ico +0 -0
- package/apps/main/run.sh +146 -0
- package/apps/main/scripts/browser.ts +14 -0
- package/apps/main/scripts/db-test-cov.ts +277 -0
- package/apps/main/scripts/login.ts +78 -0
- package/apps/main/scripts/repl.ts +61 -0
- package/apps/main/src/_foo.ts +31 -0
- package/apps/main/src/_tests/db.test.ts +19 -0
- package/apps/main/src/_tests/mock-db.ts +60 -0
- package/apps/main/src/client.tsx +13 -0
- package/apps/main/src/clients/loops.ts +13 -0
- package/apps/main/src/clients/polar.ts +12 -0
- package/apps/main/src/clients/posthog.ts +12 -0
- package/apps/main/src/components/chat/chat-context.tsx +99 -0
- package/apps/main/src/components/chat/chat-messages.tsx +184 -0
- package/apps/main/src/components/chat/chat-status.tsx +140 -0
- package/apps/main/src/components/chat/chat.tsx +458 -0
- package/apps/main/src/components/chat/citation-modal.tsx +54 -0
- package/apps/main/src/components/cta.tsx +21 -0
- package/apps/main/src/components/data-display/derived.tsx +40 -0
- package/apps/main/src/components/data-display/group-single.tsx +57 -0
- package/apps/main/src/components/data-display/group-table.tsx +165 -0
- package/apps/main/src/components/data-display/group-wrapper.tsx +54 -0
- package/apps/main/src/components/data-display/item.tsx +678 -0
- package/apps/main/src/components/error.tsx +45 -0
- package/apps/main/src/components/forms/error.tsx +22 -0
- package/apps/main/src/components/grid.tsx +7 -0
- package/apps/main/src/components/header/container.tsx +73 -0
- package/apps/main/src/components/header/header-bar.tsx +102 -0
- package/apps/main/src/components/modals/copy-display.tsx +37 -0
- package/apps/main/src/components/modals/copy-form.tsx +152 -0
- package/apps/main/src/components/modals/duplicate-workflow.tsx +89 -0
- package/apps/main/src/components/modals/field-correction.tsx +323 -0
- package/apps/main/src/components/modals/form-viewer.tsx +126 -0
- package/apps/main/src/components/modals/modals.tsx +44 -0
- package/apps/main/src/components/modals/new-deal.tsx +78 -0
- package/apps/main/src/components/modals/new-form.tsx +133 -0
- package/apps/main/src/components/modals/new-pipeline.tsx +70 -0
- package/apps/main/src/components/modals/new-submission.tsx +321 -0
- package/apps/main/src/components/modals/new-workflow.tsx +342 -0
- package/apps/main/src/components/modals/transformation-sources-modal.tsx +157 -0
- package/apps/main/src/components/modals/view-report.tsx +193 -0
- package/apps/main/src/components/not-found.tsx +14 -0
- package/apps/main/src/components/search/search-bar.tsx +178 -0
- package/apps/main/src/components/sheet-selector.tsx +135 -0
- package/apps/main/src/components/side-panel/doc-list.tsx +480 -0
- package/apps/main/src/components/sidebar/admin-sidebar.tsx +75 -0
- package/apps/main/src/components/sidebar/app-sidebar.tsx +417 -0
- package/apps/main/src/components/sidebar/model-select.tsx +134 -0
- package/apps/main/src/components/sidebar/settings-sidebar.tsx +132 -0
- package/apps/main/src/components/sidebar/sidebar-right.tsx +22 -0
- package/apps/main/src/components/sidebar/stop-impersonate.tsx +21 -0
- package/apps/main/src/components/svg/loading.tsx +33 -0
- package/apps/main/src/components/theme-selector.tsx +43 -0
- package/apps/main/src/components/unsaved-badge.tsx +19 -0
- package/apps/main/src/components/upload/file-upload.tsx +354 -0
- package/apps/main/src/fns/submission-groups.ts +28 -0
- package/apps/main/src/fns/submission-items.ts +11 -0
- package/apps/main/src/global-middleware.ts +16 -0
- package/apps/main/src/hooks/use-update-state.ts +18 -0
- package/apps/main/src/lib/auth-client.ts +16 -0
- package/apps/main/src/lib/auth.test.ts +359 -0
- package/apps/main/src/lib/auth.ts +144 -0
- package/apps/main/src/lib/billing.ts +23 -0
- package/apps/main/src/lib/config-iso.ts +76 -0
- package/apps/main/src/lib/config.ts +61 -0
- package/apps/main/src/lib/excel.ts +16 -0
- package/apps/main/src/lib/feedback-cache.ts +70 -0
- package/apps/main/src/lib/logger.ts +44 -0
- package/apps/main/src/lib/models.ts +22 -0
- package/apps/main/src/lib/not-found.ts +17 -0
- package/apps/main/src/lib/pdf.ts +16 -0
- package/apps/main/src/lib/tabularize.ts +54 -0
- package/apps/main/src/lib/utils.ts +10 -0
- package/apps/main/src/lib/zfd.ts +217 -0
- package/apps/main/src/middleware.ts +55 -0
- package/apps/main/src/routeTree.gen.ts +1255 -0
- package/apps/main/src/router.tsx +24 -0
- package/apps/main/src/routes/__root.tsx +92 -0
- package/apps/main/src/routes/_authed/_app/(dashboard)/index.tsx +227 -0
- package/apps/main/src/routes/_authed/_app/agents/$agentId/config.tsx +224 -0
- package/apps/main/src/routes/_authed/_app/agents/$agentId/index.tsx +206 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-actions-menu.tsx +94 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-artifacts.tsx +153 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-chat.tsx +220 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-history-menu.tsx +81 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-model-select.tsx +84 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-relevant-items.tsx +226 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/agent-upload-button.tsx +298 -0
- package/apps/main/src/routes/_authed/_app/agents/-components/context-modal.tsx +187 -0
- package/apps/main/src/routes/_authed/_app/agents/-fns.ts +560 -0
- package/apps/main/src/routes/_authed/_app/agents/index.tsx +65 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/$subId/-components/citation-tree.tsx +268 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/$subId.tsx +655 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/doc-loading.tsx +37 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/share-link.tsx +42 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submission-card.tsx +89 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submission-filter.tsx +193 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/submissions.tsx +36 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/summary.tsx +82 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-components/upload-doc.tsx +120 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/-fns.ts +653 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/index.tsx +259 -0
- package/apps/main/src/routes/_authed/_app/deals/$dealId/route.tsx +29 -0
- package/apps/main/src/routes/_authed/_app/deals/index.tsx +104 -0
- package/apps/main/src/routes/_authed/_app/feedback/index.tsx +639 -0
- package/apps/main/src/routes/_authed/_app/feedback/insights.tsx +250 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/blockers-panel.tsx +260 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/manual-input-panel.tsx +301 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/submission-selector-modal.tsx +143 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/-components/upload-doc.tsx +120 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/$runId/index.tsx +1485 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/-components/dag-view.tsx +296 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/-components/step-config-modal.tsx +634 -0
- package/apps/main/src/routes/_authed/_app/pipelines/$pipelineId/index.tsx +911 -0
- package/apps/main/src/routes/_authed/_app/pipelines/-fns.ts +510 -0
- package/apps/main/src/routes/_authed/_app/pipelines/index.tsx +103 -0
- package/apps/main/src/routes/_authed/_app/reports/$reportId.tsx +397 -0
- package/apps/main/src/routes/_authed/_app/reports/-fns.ts +11 -0
- package/apps/main/src/routes/_authed/_app/reports/index.tsx +22 -0
- package/apps/main/src/routes/_authed/_app/route.tsx +48 -0
- package/apps/main/src/routes/_authed/_app/submissions/-columns.tsx +161 -0
- package/apps/main/src/routes/_authed/_app/submissions/-fns.ts +128 -0
- package/apps/main/src/routes/_authed/_app/submissions/index.tsx +190 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/$formId.tsx +542 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/derived.tsx +154 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/field.tsx +369 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/-components/group.tsx +475 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/index.tsx +263 -0
- package/apps/main/src/routes/_authed/_app/workflows/$wfSlug/route.tsx +33 -0
- package/apps/main/src/routes/_authed/_app/workflows/-components/form-card.tsx +315 -0
- package/apps/main/src/routes/_authed/_app/workflows/index.tsx +86 -0
- package/apps/main/src/routes/_authed/admin/index.tsx +12 -0
- package/apps/main/src/routes/_authed/admin/route.tsx +42 -0
- package/apps/main/src/routes/_authed/admin/users/-columns.tsx +124 -0
- package/apps/main/src/routes/_authed/admin/users/-fns.ts +30 -0
- package/apps/main/src/routes/_authed/admin/users/index.tsx +29 -0
- package/apps/main/src/routes/_authed/catchNotFound.tsx +114 -0
- package/apps/main/src/routes/_authed/redirects/forms.$id.tsx +29 -0
- package/apps/main/src/routes/_authed/redirects/submissions.$id.tsx +27 -0
- package/apps/main/src/routes/_authed/redirects/workflows.$id.tsx +27 -0
- package/apps/main/src/routes/_authed/route.tsx +51 -0
- package/apps/main/src/routes/_authed/settings/-components/new-api-key.tsx +85 -0
- package/apps/main/src/routes/_authed/settings/-components/new-invite.tsx +100 -0
- package/apps/main/src/routes/_authed/settings/analytics.tsx +1710 -0
- package/apps/main/src/routes/_authed/settings/billing/-components/price-table.tsx +129 -0
- package/apps/main/src/routes/_authed/settings/billing/-fns.ts +76 -0
- package/apps/main/src/routes/_authed/settings/billing/index.tsx +119 -0
- package/apps/main/src/routes/_authed/settings/embed.tsx +337 -0
- package/apps/main/src/routes/_authed/settings/index.tsx +12 -0
- package/apps/main/src/routes/_authed/settings/keys.tsx +157 -0
- package/apps/main/src/routes/_authed/settings/members.tsx +276 -0
- package/apps/main/src/routes/_authed/settings/route.tsx +22 -0
- package/apps/main/src/routes/_authed/settings/user.tsx +87 -0
- package/apps/main/src/routes/_authed/settings/workspace.tsx +206 -0
- package/apps/main/src/routes/_public/-components/sign-in-up.tsx +96 -0
- package/apps/main/src/routes/_public/embedded.tsx +57 -0
- package/apps/main/src/routes/_public/invite.$inviteId.tsx +143 -0
- package/apps/main/src/routes/_public/no-access.tsx +38 -0
- package/apps/main/src/routes/_public/no-invite.tsx +39 -0
- package/apps/main/src/routes/_public/otp.tsx +103 -0
- package/apps/main/src/routes/_public/route.tsx +15 -0
- package/apps/main/src/routes/_public/sign-in.tsx +111 -0
- package/apps/main/src/routes/_public/sign-up.tsx +114 -0
- package/apps/main/src/routes/api/auth/$.ts +11 -0
- package/apps/main/src/routes/api/billing/paid.ts +42 -0
- package/apps/main/src/routes/api/billing/webhooks.ts +70 -0
- package/apps/main/src/routes/api/chat/agent.ts +40 -0
- package/apps/main/src/routes/api/chat/key.ts +42 -0
- package/apps/main/src/routes/api/chat/member.ts +35 -0
- package/apps/main/src/routes/api/test/index.ts +19 -0
- package/apps/main/src/server.tsx +6 -0
- package/apps/main/src/styles/app.css +23 -0
- package/apps/main/src/vite-env.d.ts +7 -0
- package/apps/main/test.http +6 -0
- package/apps/main/tsconfig.json +17 -0
- package/apps/main/vite.config.ts +24 -0
- package/apps/main/vitest.config.js +17 -0
- package/apps/mcp/README.md +171 -0
- package/apps/mcp/eslint.config.mjs +3 -0
- package/apps/mcp/package.json +37 -0
- package/apps/mcp/src/index.ts +414 -0
- package/apps/mcp/tsconfig.json +19 -0
- package/apps/mcp/vite.config.ts +22 -0
- package/apps/posthog-proxy/index.html +9 -0
- package/apps/workers/.prettierignore +7 -0
- package/apps/workers/.prettierrc.js +3 -0
- package/apps/workers/dotenvx-safe.sh +11 -0
- package/apps/workers/eslint.config.mjs +3 -0
- package/apps/workers/package.json +65 -0
- package/apps/workers/src/lib/config.ts +7 -0
- package/apps/workers/src/lib/messages.ts +0 -0
- package/apps/workers/src/lib/posthog.ts +25 -0
- package/apps/workers/src/main.ts +58 -0
- package/apps/workers/src/workflows/extraction.ts +866 -0
- package/apps/workers/src/workflows/index.ts +3 -0
- package/apps/workers/src/workflows/pipeline-dag.ts +210 -0
- package/apps/workers/src/workflows/pipeline-steps.ts +1393 -0
- package/apps/workers/tsconfig.json +16 -0
- package/apps/workers/vite.config.ts +35 -0
- package/docs/CHANGELOG.md +84 -0
- package/docs/agent-templates-and-runs.md +859 -0
- package/docs/aws-migration-plan.md +267 -0
- package/docs/impl-p0-form-builder-improvements.md +683 -0
- package/docs/on-prem-deployment-spec.docx +0 -0
- package/docs/on-prem-deployment-spec.md +378 -0
- package/docs/prd-form-builder-strategy.md +1120 -0
- package/docs/widget-ng-apf-packaging-spec.md +43 -0
- package/infra/k8s/charts/seadotdev/Chart.yaml +6 -0
- package/infra/k8s/charts/seadotdev/templates/_helpers.tpl +27 -0
- package/infra/k8s/charts/seadotdev/templates/api-v2.yaml +105 -0
- package/infra/k8s/charts/seadotdev/templates/external-secrets.yaml +83 -0
- package/infra/k8s/charts/seadotdev/templates/ingress.yaml +54 -0
- package/infra/k8s/charts/seadotdev/templates/main-app.yaml +104 -0
- package/infra/k8s/charts/seadotdev/templates/workers.yaml +182 -0
- package/infra/k8s/charts/seadotdev/values.yaml +143 -0
- package/infra/terraform/main.tf +399 -0
- package/libs/ai/.prettierignore +2 -0
- package/libs/ai/.prettierrc.js +5 -0
- package/libs/ai/README.md +139 -0
- package/libs/ai/eslint.config.mjs +3 -0
- package/libs/ai/package.json +42 -0
- package/libs/ai/src/index.ts +5 -0
- package/libs/ai/src/models.ts +19 -0
- package/libs/ai/src/rag/index.ts +1 -0
- package/libs/ai/src/rag/rag.test.ts +99 -0
- package/libs/ai/src/rag/rag.ts +510 -0
- package/libs/ai/tsconfig.json +21 -0
- package/libs/ai/vite.config.ts +38 -0
- package/libs/cache/.prettierignore +2 -0
- package/libs/cache/eslint.config.mjs +3 -0
- package/libs/cache/package.json +35 -0
- package/libs/cache/src/feedback.ts +77 -0
- package/libs/cache/src/index.ts +2 -0
- package/libs/cache/tsconfig.json +19 -0
- package/libs/cache/vite.config.ts +36 -0
- package/libs/clients/.prettierignore +6 -0
- package/libs/clients/eslint.config.mjs +3 -0
- package/libs/clients/package.json +59 -0
- package/libs/clients/src/azure.ts +249 -0
- package/libs/clients/src/gcp.ts +220 -0
- package/libs/clients/src/hatchet.ts +86 -0
- package/libs/clients/src/index.ts +8 -0
- package/libs/clients/src/loops.ts +86 -0
- package/libs/clients/src/polar.ts +77 -0
- package/libs/clients/src/posthog.ts +55 -0
- package/libs/clients/tsconfig.json +19 -0
- package/libs/clients/vite.config.ts +35 -0
- package/libs/config/.prettierignore +6 -0
- package/libs/config/.prettierrc.js +12 -0
- package/libs/config/eslint.config.mjs +3 -0
- package/libs/config/package.json +50 -0
- package/libs/config/src/azure.ts +54 -0
- package/libs/config/src/db.ts +18 -0
- package/libs/config/src/gcp.ts +53 -0
- package/libs/config/src/google.ts +17 -0
- package/libs/config/src/hatchet.ts +20 -0
- package/libs/config/src/index.ts +108 -0
- package/libs/config/src/llm.ts +17 -0
- package/libs/config/src/polar.ts +24 -0
- package/libs/config/src/util.ts +8 -0
- package/libs/config/src/vercel.ts +26 -0
- package/libs/config/tsconfig.json +19 -0
- package/libs/config/vite.config.ts +34 -0
- package/libs/core/.prettierignore +2 -0
- package/libs/core/eslint.config.mjs +3 -0
- package/libs/core/package.json +59 -0
- package/libs/core/src/chat/derived.ts +97 -0
- package/libs/core/src/chat/feedback.ts +293 -0
- package/libs/core/src/chat/index.ts +6 -0
- package/libs/core/src/chat/model.ts +92 -0
- package/libs/core/src/chat/prepare-tools.ts +286 -0
- package/libs/core/src/chat/prompts.ts +623 -0
- package/libs/core/src/chat/stream.ts +311 -0
- package/libs/core/src/chat/summarize.ts +168 -0
- package/libs/core/src/chat/tools/agent.ts +403 -0
- package/libs/core/src/chat/tools/chart-agent.ts +526 -0
- package/libs/core/src/chat/tools/chart-helpers/sandbox.ts +47 -0
- package/libs/core/src/chat/tools/chart.ts +86 -0
- package/libs/core/src/chat/tools/credit-agent.ts +1383 -0
- package/libs/core/src/chat/tools/credit.ts +1435 -0
- package/libs/core/src/chat/tools/deep-dive-agent.ts +100 -0
- package/libs/core/src/chat/tools/deep-dive.ts +141 -0
- package/libs/core/src/chat/tools/form.ts +449 -0
- package/libs/core/src/chat/tools/helpers.ts +91 -0
- package/libs/core/src/chat/tools/index.ts +42 -0
- package/libs/core/src/chat/tools/pipeline-artifact.ts +76 -0
- package/libs/core/src/chat/tools/report.ts +40 -0
- package/libs/core/src/chat/tools/search.ts +390 -0
- package/libs/core/src/chat/tools/submission.ts +227 -0
- package/libs/core/src/chat/tools/workflow.ts +684 -0
- package/libs/core/src/chat/types.ts +3 -0
- package/libs/core/src/data-extraction/classification/azure.ts +168 -0
- package/libs/core/src/data-extraction/classification/index.ts +1 -0
- package/libs/core/src/data-extraction/dal.ts +246 -0
- package/libs/core/src/data-extraction/form-structure-extractor.ts +294 -0
- package/libs/core/src/data-extraction/index.ts +4 -0
- package/libs/core/src/data-extraction/layout/azure.ts +730 -0
- package/libs/core/src/data-extraction/layout/excel.ts +180 -0
- package/libs/core/src/data-extraction/layout/gcp.ts +1071 -0
- package/libs/core/src/data-extraction/layout/index.ts +266 -0
- package/libs/core/src/data-extraction/layout/plaintext.ts +45 -0
- package/libs/core/src/data-extraction/models.ts +38 -0
- package/libs/core/src/data-extraction/pdf-utils.ts +96 -0
- package/libs/core/src/data-extraction/structuring/bank-statement.ts +1182 -0
- package/libs/core/src/data-extraction/structuring/custom.ts +495 -0
- package/libs/core/src/data-extraction/structuring/index.ts +290 -0
- package/libs/core/src/data-extraction/structuring/prompts.ts +69 -0
- package/libs/core/src/data-extraction/type-guards.ts +110 -0
- package/libs/core/src/data-extraction/types.ts +84 -0
- package/libs/core/src/data-extraction/utils.ts +31 -0
- package/libs/core/src/data-extraction/validation/bank-statement.ts +127 -0
- package/libs/core/src/deals.ts +17 -0
- package/libs/core/src/documents.ts +152 -0
- package/libs/core/src/index.ts +5 -0
- package/libs/core/src/pipelines/display.ts +678 -0
- package/libs/core/src/pipelines/execute.ts +2342 -0
- package/libs/core/src/pipelines/index.ts +4 -0
- package/libs/core/src/pipelines/list.ts +12 -0
- package/libs/core/src/pipelines/runs.ts +53 -0
- package/libs/core/tsconfig.json +20 -0
- package/libs/core/vite.config.ts +56 -0
- package/libs/dal/.prettierignore +6 -0
- package/libs/dal/.prettierrc.js +12 -0
- package/libs/dal/eslint.config.mjs +3 -0
- package/libs/dal/package.json +57 -0
- package/libs/dal/src/_tests/db.test.ts +19 -0
- package/libs/dal/src/_tests/mock-db.ts +60 -0
- package/libs/dal/src/api-key.test.ts +397 -0
- package/libs/dal/src/api-key.ts +110 -0
- package/libs/dal/src/billing.ts +23 -0
- package/libs/dal/src/conversation.test.ts +655 -0
- package/libs/dal/src/conversation.ts +532 -0
- package/libs/dal/src/deal.test.ts +45 -0
- package/libs/dal/src/deal.ts +87 -0
- package/libs/dal/src/defaults-consumer-lending-uk.ts +33 -0
- package/libs/dal/src/defaults-consumer-lending-us.ts +33 -0
- package/libs/dal/src/defaults-private-credit.ts +57 -0
- package/libs/dal/src/defaults-private-equity.ts +51 -0
- package/libs/dal/src/defaults-smb-lending-us.ts +1569 -0
- package/libs/dal/src/defaults-sme-lending-uk-express.ts +1527 -0
- package/libs/dal/src/defaults-sme-lending-uk.ts +1669 -0
- package/libs/dal/src/defaults-types.ts +23 -0
- package/libs/dal/src/defaults.ts +550 -0
- package/libs/dal/src/document.test.ts +70 -0
- package/libs/dal/src/document.ts +192 -0
- package/libs/dal/src/feedback.ts +255 -0
- package/libs/dal/src/form.test.ts +637 -0
- package/libs/dal/src/form.ts +1165 -0
- package/libs/dal/src/index.ts +20 -0
- package/libs/dal/src/invitation.test.ts +746 -0
- package/libs/dal/src/invitation.ts +207 -0
- package/libs/dal/src/member.test.ts +185 -0
- package/libs/dal/src/member.ts +80 -0
- package/libs/dal/src/organization.ts +116 -0
- package/libs/dal/src/permission.ts +25 -0
- package/libs/dal/src/pipeline.test.ts +388 -0
- package/libs/dal/src/pipeline.ts +4222 -0
- package/libs/dal/src/report.ts +199 -0
- package/libs/dal/src/result.ts +16 -0
- package/libs/dal/src/search.ts +172 -0
- package/libs/dal/src/session.test.ts +110 -0
- package/libs/dal/src/session.ts +31 -0
- package/libs/dal/src/submission.test.ts +1304 -0
- package/libs/dal/src/submission.ts +1396 -0
- package/libs/dal/src/tool.ts +159 -0
- package/libs/dal/src/user.ts +16 -0
- package/libs/dal/src/workflow.test.ts +89 -0
- package/libs/dal/src/workflow.ts +262 -0
- package/libs/dal/tsconfig.build.json +4 -0
- package/libs/dal/tsconfig.json +22 -0
- package/libs/dal/vite.config.ts +34 -0
- package/libs/db/.prettierignore +6 -0
- package/libs/db/.prettierrc.js +12 -0
- package/libs/db/eslint.config.mjs +3 -0
- package/libs/db/package.json +52 -0
- package/libs/db/src/index.ts +24 -0
- package/libs/db/src/relations.ts +549 -0
- package/libs/db/src/schema.ts +2 -0
- package/libs/db/src/schemas/api.ts +35 -0
- package/libs/db/src/schemas/conversations.ts +175 -0
- package/libs/db/src/schemas/core.ts +359 -0
- package/libs/db/src/schemas/documents.ts +181 -0
- package/libs/db/src/schemas/feedback.ts +40 -0
- package/libs/db/src/schemas/index.ts +26 -0
- package/libs/db/src/schemas/organisations.ts +97 -0
- package/libs/db/src/schemas/pipelines.ts +440 -0
- package/libs/db/src/schemas/users.ts +95 -0
- package/libs/db/src/types.ts +190 -0
- package/libs/db/src/utils.ts +14 -0
- package/libs/db/tsconfig.json +19 -0
- package/libs/db/vite.config.ts +31 -0
- package/libs/lint/.prettierignore +6 -0
- package/libs/lint/eslint.config.mjs +61 -0
- package/libs/lint/package.json +29 -0
- package/libs/lint/prettier.config.js +12 -0
- package/libs/schemas/.prettierignore +6 -0
- package/libs/schemas/.prettierrc.js +12 -0
- package/libs/schemas/README.md +15 -0
- package/libs/schemas/eslint.config.mjs +3 -0
- package/libs/schemas/package.json +67 -0
- package/libs/schemas/src/core/chat.ts +67 -0
- package/libs/schemas/src/core/core-result.ts +15 -0
- package/libs/schemas/src/core/data-extraction.ts +184 -0
- package/libs/schemas/src/core/layout.ts +478 -0
- package/libs/schemas/src/core/pipeline.ts +128 -0
- package/libs/schemas/src/core/submission.ts +97 -0
- package/libs/schemas/src/db/account.ts +57 -0
- package/libs/schemas/src/db/apiKey.ts +57 -0
- package/libs/schemas/src/db/context.ts +33 -0
- package/libs/schemas/src/db/conversation.ts +65 -0
- package/libs/schemas/src/db/deal.ts +42 -0
- package/libs/schemas/src/db/document.ts +103 -0
- package/libs/schemas/src/db/documentCitation.ts +58 -0
- package/libs/schemas/src/db/documentExtraction.ts +69 -0
- package/libs/schemas/src/db/fieldCorrection.ts +85 -0
- package/libs/schemas/src/db/form.ts +45 -0
- package/libs/schemas/src/db/formField.ts +59 -0
- package/libs/schemas/src/db/formGroup.ts +42 -0
- package/libs/schemas/src/db/impersonation.ts +39 -0
- package/libs/schemas/src/db/index.ts +25 -0
- package/libs/schemas/src/db/invitation.ts +42 -0
- package/libs/schemas/src/db/member.ts +36 -0
- package/libs/schemas/src/db/message.ts +58 -0
- package/libs/schemas/src/db/organization.ts +62 -0
- package/libs/schemas/src/db/session.ts +48 -0
- package/libs/schemas/src/db/submission.ts +54 -0
- package/libs/schemas/src/db/submissionGroup.ts +36 -0
- package/libs/schemas/src/db/submissionItem.ts +33 -0
- package/libs/schemas/src/db/submissionItemVersion.ts +70 -0
- package/libs/schemas/src/db/user.ts +51 -0
- package/libs/schemas/src/db/utils.ts +3 -0
- package/libs/schemas/src/db/verification.ts +36 -0
- package/libs/schemas/src/db/workflow.ts +42 -0
- package/libs/schemas/src/index.ts +10 -0
- package/libs/schemas/tsconfig.json +21 -0
- package/libs/schemas/vite.config.ts +38 -0
- package/libs/ui/.prettierignore +6 -0
- package/libs/ui/.prettierrc.js +12 -0
- package/libs/ui/components.json +24 -0
- package/libs/ui/eslint.config.mjs +3 -0
- package/libs/ui/package.json +142 -0
- package/libs/ui/src/components/chart-viz/chart.tsx +255 -0
- package/libs/ui/src/components/chart-viz/converters.ts +474 -0
- package/libs/ui/src/components/chart-viz/dashboard.tsx +146 -0
- package/libs/ui/src/components/chart-viz/index.ts +37 -0
- package/libs/ui/src/components/chart-viz/markdown.tsx +344 -0
- package/libs/ui/src/components/chart-viz/table.tsx +446 -0
- package/libs/ui/src/components/chart-viz/theme-context.tsx +70 -0
- package/libs/ui/src/components/chart-viz/themes/dark.ts +98 -0
- package/libs/ui/src/components/chart-viz/themes/index.ts +69 -0
- package/libs/ui/src/components/chart-viz/themes/light.ts +98 -0
- package/libs/ui/src/components/chart-viz/themes/tailwind.ts +326 -0
- package/libs/ui/src/components/chart-viz/themes/types.ts +99 -0
- package/libs/ui/src/components/chart-viz/tool-display.tsx +150 -0
- package/libs/ui/src/components/chart-viz/types.ts +95 -0
- package/libs/ui/src/components/doc-viewers/excel/index.tsx +431 -0
- package/libs/ui/src/components/doc-viewers/excel/themes.ts +160 -0
- package/libs/ui/src/components/doc-viewers/image/index.tsx +410 -0
- package/libs/ui/src/components/doc-viewers/pdf/index.tsx +258 -0
- package/libs/ui/src/components/doc-viewers/pdf/virtualized-pdf.tsx +556 -0
- package/libs/ui/src/components/misc/rel-date.tsx +52 -0
- package/libs/ui/src/components/misc/styled-link.tsx +2 -0
- package/libs/ui/src/components/table/data-table.tsx +546 -0
- package/libs/ui/src/components/table/report-table.tsx +305 -0
- package/libs/ui/src/components/table/sortable-column.tsx +34 -0
- package/libs/ui/src/components/ui/accordion.tsx +62 -0
- package/libs/ui/src/components/ui/alert-dialog.tsx +142 -0
- package/libs/ui/src/components/ui/alert.tsx +62 -0
- package/libs/ui/src/components/ui/artifact.tsx +118 -0
- package/libs/ui/src/components/ui/attachments.tsx +388 -0
- package/libs/ui/src/components/ui/avatar.tsx +39 -0
- package/libs/ui/src/components/ui/badge.tsx +43 -0
- package/libs/ui/src/components/ui/breadcrumb.tsx +102 -0
- package/libs/ui/src/components/ui/button-group.tsx +78 -0
- package/libs/ui/src/components/ui/button.tsx +79 -0
- package/libs/ui/src/components/ui/card.tsx +32 -0
- package/libs/ui/src/components/ui/carousel.tsx +228 -0
- package/libs/ui/src/components/ui/chain-of-thought.tsx +198 -0
- package/libs/ui/src/components/ui/checkbox.tsx +27 -0
- package/libs/ui/src/components/ui/citation.tsx +34 -0
- package/libs/ui/src/components/ui/code-block.tsx +500 -0
- package/libs/ui/src/components/ui/collapsible.tsx +19 -0
- package/libs/ui/src/components/ui/command.tsx +161 -0
- package/libs/ui/src/components/ui/conversation.tsx +90 -0
- package/libs/ui/src/components/ui/dialog.tsx +142 -0
- package/libs/ui/src/components/ui/dropdown-menu.tsx +246 -0
- package/libs/ui/src/components/ui/highlight.tsx +3 -0
- package/libs/ui/src/components/ui/hover-card.tsx +36 -0
- package/libs/ui/src/components/ui/inline-citation.tsx +251 -0
- package/libs/ui/src/components/ui/input-group.tsx +156 -0
- package/libs/ui/src/components/ui/input-otp.tsx +78 -0
- package/libs/ui/src/components/ui/input.tsx +21 -0
- package/libs/ui/src/components/ui/label.tsx +19 -0
- package/libs/ui/src/components/ui/model-selector.tsx +174 -0
- package/libs/ui/src/components/ui/multisidebar.tsx +750 -0
- package/libs/ui/src/components/ui/popover.tsx +43 -0
- package/libs/ui/src/components/ui/progress.tsx +28 -0
- package/libs/ui/src/components/ui/reasoning.tsx +178 -0
- package/libs/ui/src/components/ui/resizable.tsx +49 -0
- package/libs/ui/src/components/ui/scroll-area.tsx +54 -0
- package/libs/ui/src/components/ui/select.tsx +171 -0
- package/libs/ui/src/components/ui/separator.tsx +26 -0
- package/libs/ui/src/components/ui/sheet.tsx +128 -0
- package/libs/ui/src/components/ui/shimmer.tsx +53 -0
- package/libs/ui/src/components/ui/skeleton.tsx +13 -0
- package/libs/ui/src/components/ui/sonner.tsx +23 -0
- package/libs/ui/src/components/ui/switch.tsx +26 -0
- package/libs/ui/src/components/ui/table.tsx +96 -0
- package/libs/ui/src/components/ui/tabs.tsx +52 -0
- package/libs/ui/src/components/ui/textarea.tsx +41 -0
- package/libs/ui/src/components/ui/tool.tsx +209 -0
- package/libs/ui/src/components/ui/tooltip.tsx +58 -0
- package/libs/ui/src/components/ui/typography.tsx +113 -0
- package/libs/ui/src/fonts/manrope-v15-latin-300.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-400.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-500.woff2 +0 -0
- package/libs/ui/src/fonts/manrope-v15-latin-600.woff2 +0 -0
- package/libs/ui/src/hooks/use-mobile.ts +19 -0
- package/libs/ui/src/lib/utils.ts +6 -0
- package/libs/ui/src/styles/fonts.css +35 -0
- package/libs/ui/src/styles/style.css +218 -0
- package/libs/ui/tsconfig.json +21 -0
- package/libs/ui/vite.config.ts +80 -0
- package/libs/ui-lit/README.md +245 -0
- package/libs/ui-lit/TESTING_GUIDE.md +296 -0
- package/libs/ui-lit/eslint.config.mjs +3 -0
- package/libs/ui-lit/package.json +41 -0
- package/libs/ui-lit/scripts/build-css.js +43 -0
- package/libs/ui-lit/src/components/sea-alert.ts +132 -0
- package/libs/ui-lit/src/components/sea-button.ts +95 -0
- package/libs/ui-lit/src/components/sea-card.ts +113 -0
- package/libs/ui-lit/src/components/sea-input.ts +184 -0
- package/libs/ui-lit/src/components/sea-spinner.ts +65 -0
- package/libs/ui-lit/src/index.ts +15 -0
- package/libs/ui-lit/src/lib/utils.ts +6 -0
- package/libs/ui-lit/src/styles/tailwind.css +76 -0
- package/libs/ui-lit/src/theme.css +66 -0
- package/libs/ui-lit/src/theme.ts +79 -0
- package/libs/ui-lit/src/vite-env.d.ts +6 -0
- package/libs/ui-lit/tailwind.config.ts +50 -0
- package/libs/ui-lit/test.html +289 -0
- package/libs/ui-lit/tsconfig.json +23 -0
- package/libs/ui-lit/vite.config.ts +31 -0
- package/libs/ui-lit/vite.css.config.ts +20 -0
- package/libs/util/.prettierignore +6 -0
- package/libs/util/.prettierrc.js +12 -0
- package/libs/util/eslint.config.mjs +3 -0
- package/libs/util/package.json +45 -0
- package/libs/util/src/billing.ts +10 -0
- package/libs/util/src/data-transform.ts +19 -0
- package/libs/util/src/encryption.ts +45 -0
- package/libs/util/src/fmt.test.ts +9 -0
- package/libs/util/src/fmt.ts +71 -0
- package/libs/util/src/fuzzy.ts +47 -0
- package/libs/util/src/id.ts +24 -0
- package/libs/util/src/invariant.ts +31 -0
- package/libs/util/src/sub-name.ts +7 -0
- package/libs/util/tsconfig.json +19 -0
- package/libs/util/vite.config.ts +34 -0
- package/package.json +28 -0
- package/packages/widget/.prettierignore +6 -0
- package/packages/widget/.prettierrc.js +12 -0
- package/packages/widget/README.md +95 -0
- package/packages/widget/eslint.config.mjs +11 -0
- package/packages/widget/openapi-ts.config.ts +8 -0
- package/packages/widget/package.json +89 -0
- package/packages/widget/postcss.config.mjs +10 -0
- package/packages/widget/src/clients/api/client/client.ts +187 -0
- package/packages/widget/src/clients/api/client/index.ts +22 -0
- package/packages/widget/src/clients/api/client/types.ts +192 -0
- package/packages/widget/src/clients/api/client/utils.ts +394 -0
- package/packages/widget/src/clients/api/client.gen.ts +18 -0
- package/packages/widget/src/clients/api/core/auth.ts +39 -0
- package/packages/widget/src/clients/api/core/bodySerializer.ts +74 -0
- package/packages/widget/src/clients/api/core/params.ts +132 -0
- package/packages/widget/src/clients/api/core/pathSerializer.ts +169 -0
- package/packages/widget/src/clients/api/core/types.ts +80 -0
- package/packages/widget/src/clients/api/index.ts +3 -0
- package/packages/widget/src/clients/api/sdk.gen.ts +805 -0
- package/packages/widget/src/clients/api/types.gen.ts +2085 -0
- package/packages/widget/src/components/container.tsx +42 -0
- package/packages/widget/src/components/data-display.tsx +384 -0
- package/packages/widget/src/components/data-viewer.tsx +311 -0
- package/packages/widget/src/components/doc-list.tsx +102 -0
- package/packages/widget/src/components/field-correction-modal.tsx +265 -0
- package/packages/widget/src/components/header.tsx +71 -0
- package/packages/widget/src/components/new-submission.tsx +290 -0
- package/packages/widget/src/components/sidebar-right.tsx +19 -0
- package/packages/widget/src/components/submission-card.tsx +66 -0
- package/packages/widget/src/components/submission-page.tsx +75 -0
- package/packages/widget/src/components/upload-doc.tsx +241 -0
- package/packages/widget/src/components/widget.tsx +101 -0
- package/packages/widget/src/index.tsx +167 -0
- package/packages/widget/src/lib/config.ts +2 -0
- package/packages/widget/src/lib/util.ts +40 -0
- package/packages/widget/src/styles/index.css +5 -0
- package/packages/widget/src/styles/tw-properties.css +337 -0
- package/packages/widget/src/vite-env.d.ts +3 -0
- package/packages/widget/tsconfig.app.json +35 -0
- package/packages/widget/tsconfig.json +4 -0
- package/packages/widget/tsconfig.node.json +24 -0
- package/packages/widget/vite.config.ts +116 -0
- package/packages/widget-lit/BOTTLENECKS.md +250 -0
- package/packages/widget-lit/IMPLEMENTATION_SUMMARY.md +295 -0
- package/packages/widget-lit/README.md +232 -0
- package/packages/widget-lit/eslint.config.mjs +3 -0
- package/packages/widget-lit/package.json +52 -0
- package/packages/widget-lit/src/api-client.ts +230 -0
- package/packages/widget-lit/src/api-client.ts.backup +218 -0
- package/packages/widget-lit/src/components/sea-chat.ts +382 -0
- package/packages/widget-lit/src/components/sea-submission-viewer.ts +267 -0
- package/packages/widget-lit/src/components/sea-widget.ts +317 -0
- package/packages/widget-lit/src/index.ts +48 -0
- package/packages/widget-lit/src/react.ts +58 -0
- package/packages/widget-lit/src/style.css +47 -0
- package/packages/widget-lit/tsconfig.json +24 -0
- package/packages/widget-lit/vite.config.ts +29 -0
- package/packages/widget-ng/DEVELOPMENT.md +74 -0
- package/packages/widget-ng/README.md +657 -0
- package/packages/widget-ng/dev.sh +14 -0
- package/packages/widget-ng/eslint.config.mjs +24 -0
- package/packages/widget-ng/ng-package.json +9 -0
- package/packages/widget-ng/package.json +85 -0
- package/packages/widget-ng/src/index.ts +45 -0
- package/packages/widget-ng/src/lib/components/sea-chat.component.ts +737 -0
- package/packages/widget-ng/src/lib/components/sea-data-viewer.component.ts +2240 -0
- package/packages/widget-ng/src/lib/components/sea-deal-form-modal.component.ts +702 -0
- package/packages/widget-ng/src/lib/components/sea-document-list.component.ts +350 -0
- package/packages/widget-ng/src/lib/components/sea-feedback-modal.component.ts +461 -0
- package/packages/widget-ng/src/lib/components/sea-file-upload.component.ts +655 -0
- package/packages/widget-ng/src/lib/components/sea-model-selection-modal.component.ts +367 -0
- package/packages/widget-ng/src/lib/components/sea-new-submission-modal.component.ts +414 -0
- package/packages/widget-ng/src/lib/components/sea-pdf-viewer.component.ts +869 -0
- package/packages/widget-ng/src/lib/components/sea-submission-card.component.ts +251 -0
- package/packages/widget-ng/src/lib/components/sea-widget.component.ts +684 -0
- package/packages/widget-ng/src/lib/models/submission.model.ts +170 -0
- package/packages/widget-ng/src/lib/pipes/markdown.pipe.ts +57 -0
- package/packages/widget-ng/src/lib/services/api-client.service.ts +715 -0
- package/packages/widget-ng/src/lib/services/chat.service.ts +330 -0
- package/packages/widget-ng/src/lib/services/config.service.ts +107 -0
- package/packages/widget-ng/src/web-component.ts +56 -0
- package/packages/widget-ng/tsconfig.json +25 -0
- package/packages/widget-ng/tsconfig.lib.json +9 -0
- package/packages/widget-ng/vite.config.elements.ts +26 -0
- package/packages/widget-ng/vitest.config.ts +19 -0
- package/packages/widget-ng/vitest.setup.ts +13 -0
- package/pnpm-workspace.yaml +18 -0
- package/render.yaml +136 -0
- package/scripts/README.md +57 -0
- package/scripts/package.json +22 -0
- package/scripts/python/.python-version +1 -0
- package/scripts/python/README.md +3 -0
- package/scripts/python/export-org-data.py +693 -0
- package/scripts/python/pyproject.toml +29 -0
- package/scripts/python/requirements-dev.lock +36 -0
- package/scripts/python/requirements.lock +36 -0
- package/scripts/python/src/gen.py +297 -0
- package/scripts/python/test.py +34 -0
- package/scripts/src/fix-storage-provider-mismatch.ts +239 -0
- package/scripts/src/sync-render-yaml.ts +290 -0
- package/scripts/src/test-chat-stream.ts +300 -0
- package/scripts/src/test-reconciliation.ts +230 -0
- package/scripts/tsconfig.json +15 -0
- package/tests/angular-test-app/.vscode/extensions.json +4 -0
- package/tests/angular-test-app/.vscode/launch.json +13 -0
- package/tests/angular-test-app/.vscode/tasks.json +24 -0
- package/tests/angular-test-app/README.md +59 -0
- package/tests/angular-test-app/angular.json +111 -0
- package/tests/angular-test-app/clean-start.sh +14 -0
- package/tests/angular-test-app/package.json +36 -0
- package/tests/angular-test-app/public/favicon.ico +0 -0
- package/tests/angular-test-app/src/app/app.component.ts +220 -0
- package/tests/angular-test-app/src/app/app.config.ts +5 -0
- package/tests/angular-test-app/src/env.d.ts +13 -0
- package/tests/angular-test-app/src/index.html +13 -0
- package/tests/angular-test-app/src/main.ts +6 -0
- package/tests/angular-test-app/src/styles.css +8 -0
- package/tests/angular-test-app/tsconfig.app.json +15 -0
- package/tests/angular-test-app/tsconfig.json +27 -0
- package/tests/crm-viewer-app/API_INTEGRATION_SUMMARY.md +295 -0
- package/tests/crm-viewer-app/CURRENT_ASSETS_FIELDS.md +148 -0
- package/tests/crm-viewer-app/FIELD_ID_MAPPING.md +206 -0
- package/tests/crm-viewer-app/INTEGRATION_GUIDE.md +309 -0
- package/tests/crm-viewer-app/README.md +174 -0
- package/tests/crm-viewer-app/REAL_API_INTEGRATION.md +240 -0
- package/tests/crm-viewer-app/UPDATED_IMPLEMENTATION.md +279 -0
- package/tests/crm-viewer-app/angular.json +114 -0
- package/tests/crm-viewer-app/package.json +35 -0
- package/tests/crm-viewer-app/src/app/app.component.ts +534 -0
- package/tests/crm-viewer-app/src/app/citation.service.ts +316 -0
- package/tests/crm-viewer-app/src/env.d.ts +16 -0
- package/tests/crm-viewer-app/src/index.html +19 -0
- package/tests/crm-viewer-app/src/main.ts +7 -0
- package/tests/crm-viewer-app/src/styles.css +409 -0
- package/tests/crm-viewer-app/src/template.html +2678 -0
- package/tests/crm-viewer-app/tsconfig.app.json +15 -0
- package/tests/crm-viewer-app/tsconfig.json +27 -0
- package/tests/e2e/package.json +17 -0
- package/tests/e2e/playwright.config.ts +75 -0
- package/tests/e2e/tests/api/health.spec.ts +10 -0
- package/tests/e2e/tests/app/example.spec.ts +10 -0
- package/tests/widget-test-app/.prettierignore +6 -0
- package/tests/widget-test-app/README.md +48 -0
- package/tests/widget-test-app/index.html +12 -0
- package/tests/widget-test-app/package.json +24 -0
- package/tests/widget-test-app/src/App.css +192 -0
- package/tests/widget-test-app/src/App.tsx +80 -0
- package/tests/widget-test-app/src/main.tsx +9 -0
- package/tests/widget-test-app/src/vite-env.d.ts +4 -0
- package/tests/widget-test-app/tsconfig.json +25 -0
- package/tests/widget-test-app/tsconfig.node.json +11 -0
- package/tests/widget-test-app/vite.config.ts +14 -0
|
@@ -0,0 +1,1435 @@
|
|
|
1
|
+
import { createCreditAgent } from "./credit-agent";
|
|
2
|
+
import type { VercelConfig } from "@sea/config/vercel";
|
|
3
|
+
import { getDenormSubmission, type DenormGroup } from "@sea/dal/submission";
|
|
4
|
+
import { db, schema } from "@sea/db";
|
|
5
|
+
import { memorySchema } from "@sea/db/schema";
|
|
6
|
+
import type { Member } from "@sea/db/types";
|
|
7
|
+
import { invariant } from "@sea/util/invariant";
|
|
8
|
+
import type { LanguageModel, UIMessageStreamWriter } from "ai";
|
|
9
|
+
import { tool } from "ai";
|
|
10
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
11
|
+
import * as z from "zod/v4";
|
|
12
|
+
import { returnErr } from "./helpers";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Credit Analysis Tool for calculating DSCR and assessing creditworthiness
|
|
16
|
+
*
|
|
17
|
+
* This tool is designed to work with credit evaluation forms that contain:
|
|
18
|
+
* - Credit bureau data (with credit lines and estimated payments)
|
|
19
|
+
* - Financial statements (with EBITDA, interest expense, current portion of LT debt)
|
|
20
|
+
* - Legal bureau reports (litigation information)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const DSCRCalculationSchema = z.object({
|
|
24
|
+
submissionId: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe(
|
|
28
|
+
"The submission ID containing the credit evaluation data. If not provided, will use the most recent submission from conversation memory.",
|
|
29
|
+
),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export interface DSCRResult {
|
|
33
|
+
financialStatementDSCR: {
|
|
34
|
+
ebitda: number | null;
|
|
35
|
+
interestExpense: number | null;
|
|
36
|
+
currentPortionDebt: number | null;
|
|
37
|
+
dscr: number | null;
|
|
38
|
+
formula: string;
|
|
39
|
+
interpretation: string;
|
|
40
|
+
};
|
|
41
|
+
creditBureauDSCR: {
|
|
42
|
+
ebitda: number | null;
|
|
43
|
+
estimatedMonthlyPayments: number | null;
|
|
44
|
+
annualizedPayments: number | null;
|
|
45
|
+
dscr: number | null;
|
|
46
|
+
formula: string;
|
|
47
|
+
interpretation: string;
|
|
48
|
+
};
|
|
49
|
+
triangulation: {
|
|
50
|
+
comparison: string;
|
|
51
|
+
recommendation: string;
|
|
52
|
+
riskFactors: string[];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function calculateDSCR({
|
|
57
|
+
member,
|
|
58
|
+
conversationId,
|
|
59
|
+
}: {
|
|
60
|
+
member: Member;
|
|
61
|
+
conversationId: string;
|
|
62
|
+
}) {
|
|
63
|
+
return tool({
|
|
64
|
+
description: `Calculate Debt Service Coverage Ratio (DSCR) using both financial statement and credit bureau methods.
|
|
65
|
+
|
|
66
|
+
DSCR measures a company's ability to service its debt obligations. Higher is better.
|
|
67
|
+
- Above 1.25: Good coverage
|
|
68
|
+
- 1.0-1.25: Adequate but tight
|
|
69
|
+
- Below 1.0: Cannot cover debt obligations
|
|
70
|
+
|
|
71
|
+
This tool will:
|
|
72
|
+
1. Calculate DSCR from financial statements: EBITDA / (Interest Expense + Current Portion of LT Debt)
|
|
73
|
+
2. Calculate DSCR from credit bureau: EBITDA / (Estimated Annual Payments on all credit lines)
|
|
74
|
+
3. Compare the two methods and identify discrepancies
|
|
75
|
+
4. Highlight risk factors from legal reports and payment history
|
|
76
|
+
|
|
77
|
+
If no submissionId is provided, will automatically use the most recent submission in your conversation memory.
|
|
78
|
+
`,
|
|
79
|
+
inputSchema: DSCRCalculationSchema,
|
|
80
|
+
execute: async ({
|
|
81
|
+
submissionId: providedSubmissionId,
|
|
82
|
+
}): Promise<{ ok: boolean; data?: DSCRResult; error?: string }> => {
|
|
83
|
+
try {
|
|
84
|
+
// Auto-detect submission from conversation memory if not provided
|
|
85
|
+
let submissionId = providedSubmissionId;
|
|
86
|
+
|
|
87
|
+
if (!submissionId) {
|
|
88
|
+
console.log(
|
|
89
|
+
"[calculateDSCR] No submissionId provided, checking conversation memory",
|
|
90
|
+
);
|
|
91
|
+
const conversation = await db.query.conversation.findFirst({
|
|
92
|
+
where: and(
|
|
93
|
+
eq(schema.conversation.id, conversationId),
|
|
94
|
+
eq(schema.conversation.organizationId, member.organizationId),
|
|
95
|
+
),
|
|
96
|
+
columns: { memory: true },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (conversation?.memory) {
|
|
100
|
+
const memory = memorySchema.parse(conversation.memory);
|
|
101
|
+
if (memory.submissionIds.length > 0) {
|
|
102
|
+
// Use the most recent submission
|
|
103
|
+
submissionId =
|
|
104
|
+
memory.submissionIds[memory.submissionIds.length - 1];
|
|
105
|
+
console.log(
|
|
106
|
+
`[calculateDSCR] Auto-detected submission from memory: ${submissionId}`,
|
|
107
|
+
);
|
|
108
|
+
} else {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
error:
|
|
112
|
+
"No submission ID provided and no submissions found in conversation memory. Please provide a submissionId or work with a submission first.",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
error:
|
|
119
|
+
"No submission ID provided and conversation has no memory. Please provide a submissionId.",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// At this point submissionId is guaranteed to be defined
|
|
125
|
+
if (!submissionId) {
|
|
126
|
+
throw new Error("Submission ID could not be determined");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const submission = await getDenormSubmission({
|
|
130
|
+
member,
|
|
131
|
+
submissionId,
|
|
132
|
+
withCitations: true,
|
|
133
|
+
withTransformations: true,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Debug: Log submission structure
|
|
137
|
+
console.log("[calculateDSCR] Analyzing submission:", {
|
|
138
|
+
submissionId,
|
|
139
|
+
submissionName: submission.name,
|
|
140
|
+
formName: submission.formName,
|
|
141
|
+
groupCount: submission.groups.length,
|
|
142
|
+
tableCount: submission.tables?.length || 0,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log(
|
|
146
|
+
"[calculateDSCR] Single groups:",
|
|
147
|
+
submission.groups.map((g) => g.name),
|
|
148
|
+
);
|
|
149
|
+
console.log(
|
|
150
|
+
"[calculateDSCR] Multi-group tables:",
|
|
151
|
+
submission.tables?.map((t) => ({
|
|
152
|
+
name: t.name,
|
|
153
|
+
rows: t.rows?.length || 0,
|
|
154
|
+
})),
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// IMPORTANT: Financial statements are multi-groups, so they're in submission.tables, not submission.groups!
|
|
158
|
+
// 1. Find financial statement table (look for table with EBITDA column)
|
|
159
|
+
const financialTable = submission.tables?.find(
|
|
160
|
+
(t) =>
|
|
161
|
+
t.name.toLowerCase().includes("financial") &&
|
|
162
|
+
t.columns?.some((col) => col.toLowerCase().includes("ebitda")),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// 2. Find the most recent row by looking at "Period End Date"
|
|
166
|
+
let financialStmtLatest = null;
|
|
167
|
+
let latestDate: Date | null = null;
|
|
168
|
+
|
|
169
|
+
if (financialTable?.rows) {
|
|
170
|
+
console.log(
|
|
171
|
+
`[calculateDSCR] Found financial table "${financialTable.name}" with ${financialTable.rows.length} rows`,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
for (const row of financialTable.rows) {
|
|
175
|
+
const periodEndDateItem = row.items?.find(
|
|
176
|
+
(item) =>
|
|
177
|
+
item.name.toLowerCase().includes("period") &&
|
|
178
|
+
item.name.toLowerCase().includes("date"),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
if (periodEndDateItem?.value) {
|
|
182
|
+
const dateValue = new Date(periodEndDateItem.value as string);
|
|
183
|
+
if (
|
|
184
|
+
!isNaN(dateValue.getTime()) &&
|
|
185
|
+
(!latestDate || dateValue > latestDate)
|
|
186
|
+
) {
|
|
187
|
+
latestDate = dateValue;
|
|
188
|
+
// Convert row to group-like structure for extractMoneyValue
|
|
189
|
+
financialStmtLatest = {
|
|
190
|
+
name: financialTable.name,
|
|
191
|
+
items: row.items,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Fallback: use first row if no valid dates found
|
|
198
|
+
if (
|
|
199
|
+
!financialStmtLatest &&
|
|
200
|
+
financialTable.rows.length > 0 &&
|
|
201
|
+
financialTable.rows[0]?.items
|
|
202
|
+
) {
|
|
203
|
+
financialStmtLatest = {
|
|
204
|
+
name: financialTable.name,
|
|
205
|
+
items: financialTable.rows[0].items,
|
|
206
|
+
};
|
|
207
|
+
console.log(
|
|
208
|
+
"[calculateDSCR] No valid dates found, using first row",
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Find single groups (credit bureau, legal, etc.)
|
|
214
|
+
const creditBureauGroup = submission.groups.find(
|
|
215
|
+
(g) =>
|
|
216
|
+
g.name.toLowerCase().includes("credit bureau") ||
|
|
217
|
+
g.name.toLowerCase().includes("bureau data"),
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const creditLinesTable = submission.tables?.find(
|
|
221
|
+
(t) =>
|
|
222
|
+
t.name.toLowerCase().includes("credit lines") ||
|
|
223
|
+
t.name.toLowerCase().includes("credit facilities"),
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const legalBureauGroup = submission.groups.find(
|
|
227
|
+
(g) =>
|
|
228
|
+
g.name.toLowerCase().includes("legal") &&
|
|
229
|
+
g.name.toLowerCase().includes("bureau"),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// Debug: log what was found
|
|
233
|
+
console.log("[calculateDSCR] Matched data sources:", {
|
|
234
|
+
financialStmtLatest: financialStmtLatest?.name,
|
|
235
|
+
financialStmtLatestDate: latestDate?.toISOString(),
|
|
236
|
+
creditBureauGroup: creditBureauGroup?.name,
|
|
237
|
+
creditLinesTable: creditLinesTable?.name,
|
|
238
|
+
legalBureauGroup: legalBureauGroup?.name,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Extract financial statement data from the most recent row
|
|
242
|
+
const ebitda = extractMoneyValue(financialStmtLatest, "ebitda");
|
|
243
|
+
const interestExpense = extractMoneyValue(
|
|
244
|
+
financialStmtLatest,
|
|
245
|
+
"interest",
|
|
246
|
+
);
|
|
247
|
+
const currentPortionDebt = extractMoneyValue(
|
|
248
|
+
financialStmtLatest,
|
|
249
|
+
"current portion",
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// Debug: log extracted values
|
|
253
|
+
console.log("[calculateDSCR] Extracted financial values:", {
|
|
254
|
+
ebitda,
|
|
255
|
+
interestExpense,
|
|
256
|
+
currentPortionDebt,
|
|
257
|
+
financialGroupItems: financialStmtLatest?.items?.map((i) => ({
|
|
258
|
+
name: i.name,
|
|
259
|
+
value: i.value,
|
|
260
|
+
})),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Calculate financial statement DSCR
|
|
264
|
+
let fsDebtService: number | null = null;
|
|
265
|
+
let fsDSCR: number | null = null;
|
|
266
|
+
if (interestExpense !== null && currentPortionDebt !== null) {
|
|
267
|
+
fsDebtService = interestExpense + currentPortionDebt;
|
|
268
|
+
if (ebitda !== null && fsDebtService > 0) {
|
|
269
|
+
fsDSCR = ebitda / fsDebtService;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const fsInterpretation =
|
|
274
|
+
fsDSCR !== null
|
|
275
|
+
? fsDSCR >= 1.25
|
|
276
|
+
? "Good - Strong debt coverage"
|
|
277
|
+
: fsDSCR >= 1.0
|
|
278
|
+
? "Adequate - Tight but can cover obligations"
|
|
279
|
+
: "Poor - Cannot cover debt obligations"
|
|
280
|
+
: "Insufficient data to calculate";
|
|
281
|
+
|
|
282
|
+
// Extract credit bureau data
|
|
283
|
+
const estimatedMonthlyPayments = extractMoneyValue(
|
|
284
|
+
creditBureauGroup,
|
|
285
|
+
"estimated total monthly",
|
|
286
|
+
);
|
|
287
|
+
const annualizedPayments =
|
|
288
|
+
estimatedMonthlyPayments !== null
|
|
289
|
+
? estimatedMonthlyPayments * 12
|
|
290
|
+
: null;
|
|
291
|
+
|
|
292
|
+
// Calculate credit bureau DSCR
|
|
293
|
+
let cbDSCR: number | null = null;
|
|
294
|
+
if (
|
|
295
|
+
ebitda !== null &&
|
|
296
|
+
annualizedPayments !== null &&
|
|
297
|
+
annualizedPayments > 0
|
|
298
|
+
) {
|
|
299
|
+
cbDSCR = ebitda / annualizedPayments;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const cbInterpretation =
|
|
303
|
+
cbDSCR !== null
|
|
304
|
+
? cbDSCR >= 1.25
|
|
305
|
+
? "Good - Strong debt coverage based on bureau data"
|
|
306
|
+
: cbDSCR >= 1.0
|
|
307
|
+
? "Adequate - Tight coverage based on bureau data"
|
|
308
|
+
: "Poor - Cannot cover obligations per bureau data"
|
|
309
|
+
: "Insufficient credit bureau data to calculate";
|
|
310
|
+
|
|
311
|
+
// Triangulation and risk factors
|
|
312
|
+
const riskFactors: string[] = [];
|
|
313
|
+
|
|
314
|
+
// Check for discrepancies between methods
|
|
315
|
+
let comparison = "";
|
|
316
|
+
if (fsDSCR !== null && cbDSCR !== null) {
|
|
317
|
+
const diff = Math.abs(fsDSCR - cbDSCR);
|
|
318
|
+
const diffPct = (diff / Math.max(fsDSCR, cbDSCR)) * 100;
|
|
319
|
+
|
|
320
|
+
if (diffPct > 20) {
|
|
321
|
+
comparison = `SIGNIFICANT DISCREPANCY: Financial statement DSCR (${fsDSCR.toFixed(2)}) and credit bureau DSCR (${cbDSCR.toFixed(2)}) differ by ${diffPct.toFixed(1)}%. This warrants investigation.`;
|
|
322
|
+
riskFactors.push(
|
|
323
|
+
"Large discrepancy between DSCR calculation methods",
|
|
324
|
+
);
|
|
325
|
+
} else {
|
|
326
|
+
comparison = `ALIGNED: Both methods show similar DSCR (FS: ${fsDSCR.toFixed(2)}, CB: ${cbDSCR.toFixed(2)})`;
|
|
327
|
+
}
|
|
328
|
+
} else if (fsDSCR !== null) {
|
|
329
|
+
comparison = "Only financial statement DSCR available";
|
|
330
|
+
} else if (cbDSCR !== null) {
|
|
331
|
+
comparison = "Only credit bureau DSCR available";
|
|
332
|
+
} else {
|
|
333
|
+
comparison =
|
|
334
|
+
"Insufficient data to calculate DSCR using either method";
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Check payment delinquencies
|
|
338
|
+
const delinquencies = extractStringValue(
|
|
339
|
+
creditBureauGroup,
|
|
340
|
+
"delinquencies",
|
|
341
|
+
);
|
|
342
|
+
if (delinquencies && delinquencies.toLowerCase().includes("late")) {
|
|
343
|
+
riskFactors.push(`Payment issues: ${delinquencies}`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Check litigation
|
|
347
|
+
const litigationCount = extractNumberValue(
|
|
348
|
+
legalBureauGroup,
|
|
349
|
+
"litigation count",
|
|
350
|
+
);
|
|
351
|
+
const litigationSummary = extractStringValue(
|
|
352
|
+
legalBureauGroup,
|
|
353
|
+
"litigation summary",
|
|
354
|
+
);
|
|
355
|
+
if (litigationCount && litigationCount > 0) {
|
|
356
|
+
riskFactors.push(`${litigationCount} active litigation case(s)`);
|
|
357
|
+
}
|
|
358
|
+
if (litigationSummary) {
|
|
359
|
+
riskFactors.push(`Legal exposure: ${litigationSummary}`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Generate recommendation
|
|
363
|
+
let recommendation = "";
|
|
364
|
+
const worstDSCR =
|
|
365
|
+
fsDSCR !== null && cbDSCR !== null
|
|
366
|
+
? Math.min(fsDSCR, cbDSCR)
|
|
367
|
+
: (fsDSCR ?? cbDSCR);
|
|
368
|
+
|
|
369
|
+
if (worstDSCR !== null) {
|
|
370
|
+
if (worstDSCR >= 1.25 && riskFactors.length === 0) {
|
|
371
|
+
recommendation =
|
|
372
|
+
"APPROVE: Strong debt coverage with no significant risk factors";
|
|
373
|
+
} else if (worstDSCR >= 1.0) {
|
|
374
|
+
recommendation = `REVIEW: Adequate coverage but ${riskFactors.length > 0 ? "risk factors present" : "tight margin"}. Consider additional analysis.`;
|
|
375
|
+
} else {
|
|
376
|
+
recommendation =
|
|
377
|
+
"DECLINE: Cannot adequately cover debt obligations";
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
recommendation =
|
|
381
|
+
"INSUFFICIENT DATA: Cannot make credit recommendation";
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (riskFactors.length > 0) {
|
|
385
|
+
recommendation += ` Note: ${riskFactors.length} risk factor(s) identified.`;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const result: DSCRResult = {
|
|
389
|
+
financialStatementDSCR: {
|
|
390
|
+
ebitda,
|
|
391
|
+
interestExpense,
|
|
392
|
+
currentPortionDebt,
|
|
393
|
+
dscr: fsDSCR,
|
|
394
|
+
formula: "EBITDA / (Interest Expense + Current Portion of LT Debt)",
|
|
395
|
+
interpretation: fsInterpretation,
|
|
396
|
+
},
|
|
397
|
+
creditBureauDSCR: {
|
|
398
|
+
ebitda,
|
|
399
|
+
estimatedMonthlyPayments,
|
|
400
|
+
annualizedPayments,
|
|
401
|
+
dscr: cbDSCR,
|
|
402
|
+
formula: "EBITDA / (Estimated Annual Payments on all credit lines)",
|
|
403
|
+
interpretation: cbInterpretation,
|
|
404
|
+
},
|
|
405
|
+
triangulation: {
|
|
406
|
+
comparison,
|
|
407
|
+
recommendation,
|
|
408
|
+
riskFactors,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
return { ok: true, data: result };
|
|
413
|
+
} catch (err) {
|
|
414
|
+
return {
|
|
415
|
+
ok: false,
|
|
416
|
+
error: err instanceof Error ? err.message : String(err),
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Helper functions to extract values from submission groups
|
|
424
|
+
type GroupLike = {
|
|
425
|
+
name: string;
|
|
426
|
+
items?: DenormGroup["items"];
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
function extractMoneyValue(
|
|
430
|
+
group: GroupLike | null | undefined,
|
|
431
|
+
fieldNamePattern: string,
|
|
432
|
+
): number | null {
|
|
433
|
+
if (!group) {
|
|
434
|
+
console.log(
|
|
435
|
+
`[extractMoneyValue] No group provided for pattern: ${fieldNamePattern}`,
|
|
436
|
+
);
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!group.items) {
|
|
441
|
+
console.log(`[extractMoneyValue] Group "${group.name}" has no items`);
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const field = group.items.find((item) =>
|
|
446
|
+
item.name.toLowerCase().includes(fieldNamePattern.toLowerCase()),
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
if (!field) {
|
|
450
|
+
console.log(
|
|
451
|
+
`[extractMoneyValue] No field matching pattern "${fieldNamePattern}" in group "${group.name}". Available fields:`,
|
|
452
|
+
group.items.map((i) => i.name),
|
|
453
|
+
);
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (field.value === null || field.value === undefined) {
|
|
458
|
+
console.log(
|
|
459
|
+
`[extractMoneyValue] Field "${field.name}" has null/undefined value`,
|
|
460
|
+
);
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Handle different money value formats
|
|
465
|
+
const value = field.value as { amount?: number; currency?: string } | number;
|
|
466
|
+
|
|
467
|
+
console.log(
|
|
468
|
+
`[extractMoneyValue] Found field "${field.name}" with value:`,
|
|
469
|
+
value,
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
if (typeof value === "number") return value;
|
|
473
|
+
if (typeof value === "object" && value !== null && "amount" in value)
|
|
474
|
+
return value.amount ?? null;
|
|
475
|
+
|
|
476
|
+
console.log(
|
|
477
|
+
`[extractMoneyValue] Could not parse value format for field "${field.name}":`,
|
|
478
|
+
typeof value,
|
|
479
|
+
);
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function extractNumberValue(
|
|
484
|
+
group: GroupLike | null | undefined,
|
|
485
|
+
fieldNamePattern: string,
|
|
486
|
+
): number | null {
|
|
487
|
+
if (!group || !group.items) return null;
|
|
488
|
+
|
|
489
|
+
const field = group.items.find((item) =>
|
|
490
|
+
item.name.toLowerCase().includes(fieldNamePattern.toLowerCase()),
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
if (!field || field.value === null) return null;
|
|
494
|
+
return typeof field.value === "number" ? field.value : null;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function extractStringValue(
|
|
498
|
+
group: GroupLike | null | undefined,
|
|
499
|
+
fieldNamePattern: string,
|
|
500
|
+
): string | null {
|
|
501
|
+
if (!group || !group.items) return null;
|
|
502
|
+
|
|
503
|
+
const field = group.items.find((item) =>
|
|
504
|
+
item.name.toLowerCase().includes(fieldNamePattern.toLowerCase()),
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
if (!field || field.value === null) return null;
|
|
508
|
+
return typeof field.value === "string" ? field.value : null;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Cross-Reference Verification Tool
|
|
513
|
+
* Verifies that values in different locations of a submission match or reconcile correctly
|
|
514
|
+
*/
|
|
515
|
+
export function verifyAccountReconciliation({ member }: { member: Member }) {
|
|
516
|
+
return tool({
|
|
517
|
+
description:
|
|
518
|
+
"Verify that a value in one location matches or reconciles with values in other locations. Use this to catch inconsistencies like 'Security deposit is £50k in Balance Sheet but £45k in Note 7'",
|
|
519
|
+
inputSchema: z.object({
|
|
520
|
+
submissionId: z.string(),
|
|
521
|
+
primaryLocation: z.object({
|
|
522
|
+
groupOrTableName: z
|
|
523
|
+
.string()
|
|
524
|
+
.describe("Name of the group or table containing the primary value"),
|
|
525
|
+
fieldName: z.string().describe("Name of the field to check"),
|
|
526
|
+
}),
|
|
527
|
+
referenceLocations: z.array(
|
|
528
|
+
z.object({
|
|
529
|
+
groupOrTableName: z
|
|
530
|
+
.string()
|
|
531
|
+
.describe(
|
|
532
|
+
"Name of the group or table containing the reference value",
|
|
533
|
+
),
|
|
534
|
+
fieldName: z
|
|
535
|
+
.string()
|
|
536
|
+
.describe("Name of the field to compare against"),
|
|
537
|
+
}),
|
|
538
|
+
),
|
|
539
|
+
expectedRelationship: z
|
|
540
|
+
.enum(["equal", "sum_to", "difference"])
|
|
541
|
+
.optional()
|
|
542
|
+
.describe(
|
|
543
|
+
"Expected relationship between values: 'equal' = should match exactly, 'sum_to' = references should sum to primary, 'difference' = primary minus references should equal zero",
|
|
544
|
+
),
|
|
545
|
+
}),
|
|
546
|
+
execute: async ({
|
|
547
|
+
submissionId,
|
|
548
|
+
primaryLocation,
|
|
549
|
+
referenceLocations,
|
|
550
|
+
expectedRelationship,
|
|
551
|
+
}) => {
|
|
552
|
+
try {
|
|
553
|
+
const submission = await getDenormSubmission({
|
|
554
|
+
member,
|
|
555
|
+
submissionId,
|
|
556
|
+
withCitations: true,
|
|
557
|
+
withTransformations: true,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// Find primary value
|
|
561
|
+
const primaryGroup =
|
|
562
|
+
submission.groups.find((g) =>
|
|
563
|
+
g.name
|
|
564
|
+
.toLowerCase()
|
|
565
|
+
.includes(primaryLocation.groupOrTableName.toLowerCase()),
|
|
566
|
+
) ||
|
|
567
|
+
submission.tables?.find((t) =>
|
|
568
|
+
t.name
|
|
569
|
+
.toLowerCase()
|
|
570
|
+
.includes(primaryLocation.groupOrTableName.toLowerCase()),
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
if (!primaryGroup) {
|
|
574
|
+
return {
|
|
575
|
+
ok: false,
|
|
576
|
+
error: `Primary location "${primaryLocation.groupOrTableName}" not found`,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const primaryValue = extractMoneyValue(
|
|
581
|
+
primaryGroup,
|
|
582
|
+
primaryLocation.fieldName,
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
if (primaryValue === null) {
|
|
586
|
+
return {
|
|
587
|
+
ok: false,
|
|
588
|
+
error: `Primary field "${primaryLocation.fieldName}" not found or has no value`,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Find reference values
|
|
593
|
+
const referenceValues: Array<{
|
|
594
|
+
location: string;
|
|
595
|
+
field: string;
|
|
596
|
+
value: number | null;
|
|
597
|
+
}> = [];
|
|
598
|
+
|
|
599
|
+
for (const refLoc of referenceLocations) {
|
|
600
|
+
const refGroup =
|
|
601
|
+
submission.groups.find((g) =>
|
|
602
|
+
g.name
|
|
603
|
+
.toLowerCase()
|
|
604
|
+
.includes(refLoc.groupOrTableName.toLowerCase()),
|
|
605
|
+
) ||
|
|
606
|
+
submission.tables?.find((t) =>
|
|
607
|
+
t.name
|
|
608
|
+
.toLowerCase()
|
|
609
|
+
.includes(refLoc.groupOrTableName.toLowerCase()),
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
if (!refGroup) {
|
|
613
|
+
referenceValues.push({
|
|
614
|
+
location: refLoc.groupOrTableName,
|
|
615
|
+
field: refLoc.fieldName,
|
|
616
|
+
value: null,
|
|
617
|
+
});
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const refValue = extractMoneyValue(refGroup, refLoc.fieldName);
|
|
622
|
+
referenceValues.push({
|
|
623
|
+
location: refLoc.groupOrTableName,
|
|
624
|
+
field: refLoc.fieldName,
|
|
625
|
+
value: refValue,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Perform reconciliation check
|
|
630
|
+
const relationship = expectedRelationship || "equal";
|
|
631
|
+
let reconciles = false;
|
|
632
|
+
let details = "";
|
|
633
|
+
|
|
634
|
+
if (relationship === "equal") {
|
|
635
|
+
reconciles = referenceValues.every(
|
|
636
|
+
(ref) => ref.value === primaryValue,
|
|
637
|
+
);
|
|
638
|
+
details = `Primary value: ${primaryValue}. Reference values: ${referenceValues.map((r) => `${r.location}.${r.field} = ${r.value}`).join(", ")}`;
|
|
639
|
+
} else if (relationship === "sum_to") {
|
|
640
|
+
const sum = referenceValues.reduce(
|
|
641
|
+
(acc, ref) => acc + (ref.value || 0),
|
|
642
|
+
0,
|
|
643
|
+
);
|
|
644
|
+
reconciles = Math.abs(sum - primaryValue) < 0.01; // Allow for rounding
|
|
645
|
+
details = `Primary value: ${primaryValue}. Sum of references: ${sum}. Difference: ${Math.abs(sum - primaryValue)}`;
|
|
646
|
+
} else if (relationship === "difference") {
|
|
647
|
+
const sum = referenceValues.reduce(
|
|
648
|
+
(acc, ref) => acc + (ref.value || 0),
|
|
649
|
+
0,
|
|
650
|
+
);
|
|
651
|
+
reconciles = Math.abs(primaryValue - sum) < 0.01;
|
|
652
|
+
details = `Primary value: ${primaryValue}. Sum of references: ${sum}. Difference: ${primaryValue - sum}`;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return {
|
|
656
|
+
ok: true,
|
|
657
|
+
data: {
|
|
658
|
+
reconciles,
|
|
659
|
+
primaryValue,
|
|
660
|
+
referenceValues,
|
|
661
|
+
relationship,
|
|
662
|
+
details,
|
|
663
|
+
discrepancy: reconciles
|
|
664
|
+
? null
|
|
665
|
+
: "Values do not reconcile as expected",
|
|
666
|
+
},
|
|
667
|
+
};
|
|
668
|
+
} catch (err) {
|
|
669
|
+
return returnErr(err);
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Financial Ratio Calculator Tool
|
|
677
|
+
* Calculates financial metrics and verifies they match stated values
|
|
678
|
+
*/
|
|
679
|
+
export function calculateFinancialMetric({ member }: { member: Member }) {
|
|
680
|
+
return tool({
|
|
681
|
+
description:
|
|
682
|
+
"Calculate financial metrics like DSO (Days Sales Outstanding), current ratio, depreciation, or verify formulas match stated policies. Use to catch errors like 'DSO calculation doesn't match'",
|
|
683
|
+
inputSchema: z.object({
|
|
684
|
+
submissionId: z.string(),
|
|
685
|
+
metric: z.enum([
|
|
686
|
+
"dso",
|
|
687
|
+
"current_ratio",
|
|
688
|
+
"quick_ratio",
|
|
689
|
+
"depreciation",
|
|
690
|
+
"amortization",
|
|
691
|
+
"debt_to_equity",
|
|
692
|
+
"working_capital",
|
|
693
|
+
]),
|
|
694
|
+
inputs: z
|
|
695
|
+
.record(z.string(), z.number())
|
|
696
|
+
.optional()
|
|
697
|
+
.describe(
|
|
698
|
+
"Override inputs if needed (e.g., {revenue: 1000000, receivables: 120000})",
|
|
699
|
+
),
|
|
700
|
+
periodDays: z
|
|
701
|
+
.number()
|
|
702
|
+
.optional()
|
|
703
|
+
.describe("Number of days in period for DSO calculation (default 365)"),
|
|
704
|
+
}),
|
|
705
|
+
execute: async ({ submissionId, metric, inputs, periodDays }) => {
|
|
706
|
+
try {
|
|
707
|
+
const submission = await getDenormSubmission({
|
|
708
|
+
member,
|
|
709
|
+
submissionId,
|
|
710
|
+
withCitations: true,
|
|
711
|
+
withTransformations: true,
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
let calculatedValue: number | null = null;
|
|
715
|
+
let formula = "";
|
|
716
|
+
let inputsUsed: Record<string, number | null> = {};
|
|
717
|
+
let statedValue: number | null = null;
|
|
718
|
+
|
|
719
|
+
// Find financial statement data (most recent period)
|
|
720
|
+
const financialTable = submission.tables?.find(
|
|
721
|
+
(t) =>
|
|
722
|
+
t.name.toLowerCase().includes("financial") ||
|
|
723
|
+
t.name.toLowerCase().includes("balance sheet") ||
|
|
724
|
+
t.name.toLowerCase().includes("p&l") ||
|
|
725
|
+
t.name.toLowerCase().includes("profit and loss"),
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
let latestFinancialRow = null;
|
|
729
|
+
if (financialTable?.rows && financialTable.rows.length > 0) {
|
|
730
|
+
// Use most recent period (first row, or find by date)
|
|
731
|
+
latestFinancialRow = {
|
|
732
|
+
name: financialTable.name,
|
|
733
|
+
items: financialTable.rows[0]?.items,
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
switch (metric) {
|
|
738
|
+
case "dso": {
|
|
739
|
+
const revenue =
|
|
740
|
+
inputs?.revenue ??
|
|
741
|
+
extractMoneyValue(latestFinancialRow, "revenue");
|
|
742
|
+
const receivables =
|
|
743
|
+
inputs?.receivables ??
|
|
744
|
+
extractMoneyValue(latestFinancialRow, "receivables") ??
|
|
745
|
+
extractMoneyValue(latestFinancialRow, "accounts receivable");
|
|
746
|
+
const days = periodDays || 365;
|
|
747
|
+
|
|
748
|
+
inputsUsed = { revenue, receivables, days };
|
|
749
|
+
|
|
750
|
+
if (revenue !== null && receivables !== null && revenue > 0) {
|
|
751
|
+
calculatedValue = (receivables / revenue) * days;
|
|
752
|
+
formula = `(Receivables / Revenue) × ${days} days`;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Try to find stated DSO value
|
|
756
|
+
const notesGroup = submission.groups.find((g) =>
|
|
757
|
+
g.name.toLowerCase().includes("note"),
|
|
758
|
+
);
|
|
759
|
+
if (notesGroup) {
|
|
760
|
+
statedValue =
|
|
761
|
+
extractNumberValue(notesGroup, "dso") ??
|
|
762
|
+
extractNumberValue(notesGroup, "days sales");
|
|
763
|
+
}
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
case "current_ratio": {
|
|
768
|
+
const currentAssets =
|
|
769
|
+
inputs?.currentAssets ??
|
|
770
|
+
extractMoneyValue(latestFinancialRow, "current assets");
|
|
771
|
+
const currentLiabilities =
|
|
772
|
+
inputs?.currentLiabilities ??
|
|
773
|
+
extractMoneyValue(latestFinancialRow, "current liabilities");
|
|
774
|
+
|
|
775
|
+
inputsUsed = { currentAssets, currentLiabilities };
|
|
776
|
+
|
|
777
|
+
if (
|
|
778
|
+
currentAssets !== null &&
|
|
779
|
+
currentLiabilities !== null &&
|
|
780
|
+
currentLiabilities > 0
|
|
781
|
+
) {
|
|
782
|
+
calculatedValue = currentAssets / currentLiabilities;
|
|
783
|
+
formula = "Current Assets / Current Liabilities";
|
|
784
|
+
}
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
case "quick_ratio": {
|
|
789
|
+
const currentAssets =
|
|
790
|
+
inputs?.currentAssets ??
|
|
791
|
+
extractMoneyValue(latestFinancialRow, "current assets");
|
|
792
|
+
const inventory =
|
|
793
|
+
inputs?.inventory ??
|
|
794
|
+
extractMoneyValue(latestFinancialRow, "inventory");
|
|
795
|
+
const currentLiabilities =
|
|
796
|
+
inputs?.currentLiabilities ??
|
|
797
|
+
extractMoneyValue(latestFinancialRow, "current liabilities");
|
|
798
|
+
|
|
799
|
+
inputsUsed = { currentAssets, inventory, currentLiabilities };
|
|
800
|
+
|
|
801
|
+
if (
|
|
802
|
+
currentAssets !== null &&
|
|
803
|
+
inventory !== null &&
|
|
804
|
+
currentLiabilities !== null &&
|
|
805
|
+
currentLiabilities > 0
|
|
806
|
+
) {
|
|
807
|
+
calculatedValue =
|
|
808
|
+
(currentAssets - inventory) / currentLiabilities;
|
|
809
|
+
formula = "(Current Assets - Inventory) / Current Liabilities";
|
|
810
|
+
}
|
|
811
|
+
break;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
case "depreciation":
|
|
815
|
+
case "amortization": {
|
|
816
|
+
const assetValue =
|
|
817
|
+
inputs?.assetValue ??
|
|
818
|
+
extractMoneyValue(
|
|
819
|
+
latestFinancialRow,
|
|
820
|
+
metric === "depreciation"
|
|
821
|
+
? "fixed assets"
|
|
822
|
+
: "intangible assets",
|
|
823
|
+
);
|
|
824
|
+
const usefulLife = inputs?.usefulLife ?? null;
|
|
825
|
+
const charge =
|
|
826
|
+
inputs?.charge ??
|
|
827
|
+
extractMoneyValue(latestFinancialRow, `${metric} charge`) ??
|
|
828
|
+
extractMoneyValue(latestFinancialRow, metric);
|
|
829
|
+
|
|
830
|
+
inputsUsed = { assetValue, usefulLife, charge };
|
|
831
|
+
|
|
832
|
+
if (assetValue !== null && usefulLife !== null && usefulLife > 0) {
|
|
833
|
+
calculatedValue = assetValue / usefulLife;
|
|
834
|
+
formula = `Asset Value / Useful Life (${usefulLife} years)`;
|
|
835
|
+
statedValue = charge;
|
|
836
|
+
}
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
case "debt_to_equity": {
|
|
841
|
+
const totalDebt =
|
|
842
|
+
inputs?.totalDebt ??
|
|
843
|
+
extractMoneyValue(latestFinancialRow, "total debt") ??
|
|
844
|
+
extractMoneyValue(latestFinancialRow, "total liabilities");
|
|
845
|
+
const totalEquity =
|
|
846
|
+
inputs?.totalEquity ??
|
|
847
|
+
extractMoneyValue(latestFinancialRow, "total equity");
|
|
848
|
+
|
|
849
|
+
inputsUsed = { totalDebt, totalEquity };
|
|
850
|
+
|
|
851
|
+
if (totalDebt !== null && totalEquity !== null && totalEquity > 0) {
|
|
852
|
+
calculatedValue = totalDebt / totalEquity;
|
|
853
|
+
formula = "Total Debt / Total Equity";
|
|
854
|
+
}
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
case "working_capital": {
|
|
859
|
+
const currentAssets =
|
|
860
|
+
inputs?.currentAssets ??
|
|
861
|
+
extractMoneyValue(latestFinancialRow, "current assets");
|
|
862
|
+
const currentLiabilities =
|
|
863
|
+
inputs?.currentLiabilities ??
|
|
864
|
+
extractMoneyValue(latestFinancialRow, "current liabilities");
|
|
865
|
+
|
|
866
|
+
inputsUsed = { currentAssets, currentLiabilities };
|
|
867
|
+
|
|
868
|
+
if (currentAssets !== null && currentLiabilities !== null) {
|
|
869
|
+
calculatedValue = currentAssets - currentLiabilities;
|
|
870
|
+
formula = "Current Assets - Current Liabilities";
|
|
871
|
+
}
|
|
872
|
+
break;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
const matches =
|
|
877
|
+
statedValue !== null && calculatedValue !== null
|
|
878
|
+
? Math.abs(statedValue - calculatedValue) < 0.01
|
|
879
|
+
: null;
|
|
880
|
+
|
|
881
|
+
return {
|
|
882
|
+
ok: true,
|
|
883
|
+
data: {
|
|
884
|
+
metric,
|
|
885
|
+
calculatedValue,
|
|
886
|
+
statedValue,
|
|
887
|
+
matches,
|
|
888
|
+
formula,
|
|
889
|
+
inputsUsed,
|
|
890
|
+
discrepancy:
|
|
891
|
+
matches === false
|
|
892
|
+
? `Calculated ${metric}: ${calculatedValue?.toFixed(2)}, but stated value: ${statedValue?.toFixed(2)}`
|
|
893
|
+
: null,
|
|
894
|
+
},
|
|
895
|
+
};
|
|
896
|
+
} catch (err) {
|
|
897
|
+
return returnErr(err);
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Accounting Policy Verification Tool
|
|
905
|
+
* Checks if accounting treatment matches stated policy
|
|
906
|
+
*/
|
|
907
|
+
export function verifyAccountingPolicy({ member }: { member: Member }) {
|
|
908
|
+
return tool({
|
|
909
|
+
description:
|
|
910
|
+
"Check if accounting treatment matches stated policy. Use for checks like 'amortisation charge £18k doesn't match asset life of 5 years (should be £14k)'",
|
|
911
|
+
inputSchema: z.object({
|
|
912
|
+
submissionId: z.string(),
|
|
913
|
+
policyType: z.enum([
|
|
914
|
+
"depreciation",
|
|
915
|
+
"amortization",
|
|
916
|
+
"revenue_recognition",
|
|
917
|
+
"inventory_valuation",
|
|
918
|
+
]),
|
|
919
|
+
assetOrAccount: z
|
|
920
|
+
.string()
|
|
921
|
+
.optional()
|
|
922
|
+
.describe(
|
|
923
|
+
"Name of specific asset or account to verify (e.g., 'Software License')",
|
|
924
|
+
),
|
|
925
|
+
}),
|
|
926
|
+
execute: async ({ submissionId, policyType, assetOrAccount }) => {
|
|
927
|
+
try {
|
|
928
|
+
const submission = await getDenormSubmission({
|
|
929
|
+
member,
|
|
930
|
+
submissionId,
|
|
931
|
+
withCitations: true,
|
|
932
|
+
withTransformations: true,
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// Find accounting policy notes
|
|
936
|
+
const policyGroup = submission.groups.find(
|
|
937
|
+
(g) =>
|
|
938
|
+
g.name.toLowerCase().includes("accounting policy") ||
|
|
939
|
+
g.name.toLowerCase().includes("accounting policies") ||
|
|
940
|
+
g.name.toLowerCase().includes("policy note"),
|
|
941
|
+
);
|
|
942
|
+
|
|
943
|
+
// Find financial data
|
|
944
|
+
const financialTable = submission.tables?.find((t) =>
|
|
945
|
+
t.name.toLowerCase().includes("financial"),
|
|
946
|
+
);
|
|
947
|
+
const notesTable = submission.tables?.find(
|
|
948
|
+
(t) =>
|
|
949
|
+
t.name.toLowerCase().includes("note") &&
|
|
950
|
+
!t.name.toLowerCase().includes("policy"),
|
|
951
|
+
);
|
|
952
|
+
|
|
953
|
+
let policyStatement: string | null = null;
|
|
954
|
+
let actualTreatment: Record<string, unknown> = {};
|
|
955
|
+
let expectedTreatment: Record<string, unknown> = {};
|
|
956
|
+
let compliant = true;
|
|
957
|
+
const issues: string[] = [];
|
|
958
|
+
|
|
959
|
+
if (policyType === "depreciation" || policyType === "amortization") {
|
|
960
|
+
// Extract policy
|
|
961
|
+
policyStatement =
|
|
962
|
+
extractStringValue(policyGroup, `${policyType} policy`) ??
|
|
963
|
+
extractStringValue(policyGroup, `${policyType} method`);
|
|
964
|
+
|
|
965
|
+
// Find asset details in notes
|
|
966
|
+
let assetGroup = null;
|
|
967
|
+
if (assetOrAccount) {
|
|
968
|
+
assetGroup = submission.groups.find((g) =>
|
|
969
|
+
g.name.toLowerCase().includes(assetOrAccount.toLowerCase()),
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Extract asset value, useful life, and actual charge
|
|
974
|
+
const assetValue = extractMoneyValue(
|
|
975
|
+
assetGroup ?? notesTable,
|
|
976
|
+
assetOrAccount || policyType,
|
|
977
|
+
);
|
|
978
|
+
const usefulLife =
|
|
979
|
+
extractNumberValue(assetGroup, "useful life") ??
|
|
980
|
+
extractNumberValue(assetGroup, "life") ??
|
|
981
|
+
extractNumberValue(policyGroup, "useful life");
|
|
982
|
+
const actualCharge = extractMoneyValue(
|
|
983
|
+
assetGroup ?? notesTable,
|
|
984
|
+
`${policyType} charge`,
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
actualTreatment = { assetValue, usefulLife, actualCharge };
|
|
988
|
+
|
|
989
|
+
// Calculate expected charge based on straight-line method (most common)
|
|
990
|
+
if (assetValue !== null && usefulLife !== null && usefulLife > 0) {
|
|
991
|
+
const expectedCharge = assetValue / usefulLife;
|
|
992
|
+
expectedTreatment = { expectedCharge, method: "straight-line" };
|
|
993
|
+
|
|
994
|
+
// Check if actual matches expected (within 1% tolerance)
|
|
995
|
+
if (actualCharge !== null) {
|
|
996
|
+
const variance = Math.abs(actualCharge - expectedCharge);
|
|
997
|
+
const variancePct = (variance / expectedCharge) * 100;
|
|
998
|
+
|
|
999
|
+
if (variancePct > 1) {
|
|
1000
|
+
compliant = false;
|
|
1001
|
+
issues.push(
|
|
1002
|
+
`${policyType} charge of ${actualCharge.toFixed(2)} doesn't match expected ${expectedCharge.toFixed(2)} (${usefulLife} year life). Variance: ${variance.toFixed(2)} (${variancePct.toFixed(1)}%)`,
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
} else {
|
|
1007
|
+
issues.push(
|
|
1008
|
+
`Insufficient data to verify ${policyType}: missing asset value or useful life`,
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
} else if (policyType === "revenue_recognition") {
|
|
1012
|
+
policyStatement = extractStringValue(
|
|
1013
|
+
policyGroup,
|
|
1014
|
+
"revenue recognition",
|
|
1015
|
+
);
|
|
1016
|
+
// For revenue recognition, would check if timing matches policy (e.g., point in time vs over time)
|
|
1017
|
+
issues.push(
|
|
1018
|
+
"Revenue recognition verification requires detailed transaction data",
|
|
1019
|
+
);
|
|
1020
|
+
} else if (policyType === "inventory_valuation") {
|
|
1021
|
+
policyStatement =
|
|
1022
|
+
extractStringValue(policyGroup, "inventory") ??
|
|
1023
|
+
extractStringValue(policyGroup, "valuation");
|
|
1024
|
+
// Check if inventory method (FIFO, LIFO, weighted average) is consistently applied
|
|
1025
|
+
issues.push(
|
|
1026
|
+
"Inventory valuation verification requires detailed inventory records",
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
return {
|
|
1031
|
+
ok: true,
|
|
1032
|
+
data: {
|
|
1033
|
+
policyType,
|
|
1034
|
+
assetOrAccount,
|
|
1035
|
+
policyStatement,
|
|
1036
|
+
actualTreatment,
|
|
1037
|
+
expectedTreatment,
|
|
1038
|
+
compliant,
|
|
1039
|
+
issues: issues.length > 0 ? issues : null,
|
|
1040
|
+
},
|
|
1041
|
+
};
|
|
1042
|
+
} catch (err) {
|
|
1043
|
+
return returnErr(err);
|
|
1044
|
+
}
|
|
1045
|
+
},
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* Trend Consistency Checker Tool
|
|
1051
|
+
* Verifies that related metrics move consistently together
|
|
1052
|
+
*/
|
|
1053
|
+
export function checkTrendConsistency({ member }: { member: Member }) {
|
|
1054
|
+
return tool({
|
|
1055
|
+
description:
|
|
1056
|
+
"Verify that related metrics move consistently together. Use to catch issues like 'receivables grew 7.4% but revenue only 5.6%, so DSO must have increased'",
|
|
1057
|
+
inputSchema: z.object({
|
|
1058
|
+
submissionId: z.string(),
|
|
1059
|
+
primaryMetric: z
|
|
1060
|
+
.string()
|
|
1061
|
+
.describe("Name of the primary metric to analyze (e.g., 'revenue')"),
|
|
1062
|
+
relatedMetrics: z
|
|
1063
|
+
.array(z.string())
|
|
1064
|
+
.describe(
|
|
1065
|
+
"Names of related metrics that should move together (e.g., ['receivables', 'inventory'])",
|
|
1066
|
+
),
|
|
1067
|
+
periods: z
|
|
1068
|
+
.number()
|
|
1069
|
+
.optional()
|
|
1070
|
+
.describe("Number of periods to analyze (default 2 for YoY)"),
|
|
1071
|
+
}),
|
|
1072
|
+
execute: async ({
|
|
1073
|
+
submissionId,
|
|
1074
|
+
primaryMetric,
|
|
1075
|
+
relatedMetrics,
|
|
1076
|
+
periods = 2,
|
|
1077
|
+
}) => {
|
|
1078
|
+
try {
|
|
1079
|
+
const submission = await getDenormSubmission({
|
|
1080
|
+
member,
|
|
1081
|
+
submissionId,
|
|
1082
|
+
withCitations: true,
|
|
1083
|
+
withTransformations: true,
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
// Find financial table with multiple periods
|
|
1087
|
+
const financialTable = submission.tables?.find(
|
|
1088
|
+
(t) =>
|
|
1089
|
+
(t.name.toLowerCase().includes("financial") ||
|
|
1090
|
+
t.name.toLowerCase().includes("balance sheet") ||
|
|
1091
|
+
t.name.toLowerCase().includes("p&l")) &&
|
|
1092
|
+
t.rows &&
|
|
1093
|
+
t.rows.length >= periods,
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
if (!financialTable?.rows || financialTable.rows.length < periods) {
|
|
1097
|
+
return {
|
|
1098
|
+
ok: false,
|
|
1099
|
+
error: `Need at least ${periods} periods of data. Found ${financialTable?.rows?.length || 0}`,
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// Extract values for each period
|
|
1104
|
+
const periodData: Array<{
|
|
1105
|
+
period: string;
|
|
1106
|
+
values: Record<string, number | null>;
|
|
1107
|
+
}> = [];
|
|
1108
|
+
|
|
1109
|
+
for (
|
|
1110
|
+
let i = 0;
|
|
1111
|
+
i < Math.min(periods, financialTable.rows.length);
|
|
1112
|
+
i++
|
|
1113
|
+
) {
|
|
1114
|
+
const row = financialTable.rows[i];
|
|
1115
|
+
if (!row?.items) continue;
|
|
1116
|
+
|
|
1117
|
+
const rowAsGroup = { name: financialTable.name, items: row.items };
|
|
1118
|
+
const periodLabel =
|
|
1119
|
+
extractStringValue(rowAsGroup, "period") ??
|
|
1120
|
+
extractStringValue(rowAsGroup, "date") ??
|
|
1121
|
+
`Period ${i + 1}`;
|
|
1122
|
+
|
|
1123
|
+
const values: Record<string, number | null> = {
|
|
1124
|
+
[primaryMetric]: extractMoneyValue(rowAsGroup, primaryMetric),
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
for (const metric of relatedMetrics) {
|
|
1128
|
+
values[metric] = extractMoneyValue(rowAsGroup, metric);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
periodData.push({ period: periodLabel, values });
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// Calculate growth rates
|
|
1135
|
+
const growthRates: Record<string, number | null> = {};
|
|
1136
|
+
if (periodData.length >= 2) {
|
|
1137
|
+
const current = periodData[0]?.values;
|
|
1138
|
+
const prior = periodData[1]?.values;
|
|
1139
|
+
|
|
1140
|
+
if (current && prior) {
|
|
1141
|
+
for (const metric of [primaryMetric, ...relatedMetrics]) {
|
|
1142
|
+
const currValue = current[metric] ?? null;
|
|
1143
|
+
const priorValue = prior[metric] ?? null;
|
|
1144
|
+
|
|
1145
|
+
if (
|
|
1146
|
+
currValue !== null &&
|
|
1147
|
+
priorValue !== null &&
|
|
1148
|
+
priorValue !== 0
|
|
1149
|
+
) {
|
|
1150
|
+
growthRates[metric] =
|
|
1151
|
+
((currValue - priorValue) / priorValue) * 100;
|
|
1152
|
+
} else {
|
|
1153
|
+
growthRates[metric] = null;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// Check for consistency issues
|
|
1160
|
+
const inconsistencies: string[] = [];
|
|
1161
|
+
const primaryGrowth = growthRates[primaryMetric] ?? null;
|
|
1162
|
+
|
|
1163
|
+
if (primaryGrowth !== null) {
|
|
1164
|
+
for (const metric of relatedMetrics) {
|
|
1165
|
+
const metricGrowth = growthRates[metric] ?? null;
|
|
1166
|
+
|
|
1167
|
+
if (metricGrowth !== null) {
|
|
1168
|
+
const difference = Math.abs(metricGrowth - primaryGrowth);
|
|
1169
|
+
|
|
1170
|
+
// Flag if growth rates differ by more than 5 percentage points
|
|
1171
|
+
if (difference > 5) {
|
|
1172
|
+
const interpretation =
|
|
1173
|
+
metricGrowth > primaryGrowth
|
|
1174
|
+
? `${metric} grew faster than ${primaryMetric}, suggesting efficiency may have decreased`
|
|
1175
|
+
: `${metric} grew slower than ${primaryMetric}, suggesting efficiency may have improved`;
|
|
1176
|
+
|
|
1177
|
+
inconsistencies.push(
|
|
1178
|
+
`${metric} growth: ${metricGrowth.toFixed(1)}% vs ${primaryMetric} growth: ${primaryGrowth.toFixed(1)}%. ${interpretation}`,
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
return {
|
|
1186
|
+
ok: true,
|
|
1187
|
+
data: {
|
|
1188
|
+
periodData,
|
|
1189
|
+
growthRates,
|
|
1190
|
+
inconsistencies:
|
|
1191
|
+
inconsistencies.length > 0 ? inconsistencies : null,
|
|
1192
|
+
consistent: inconsistencies.length === 0,
|
|
1193
|
+
},
|
|
1194
|
+
};
|
|
1195
|
+
} catch (err) {
|
|
1196
|
+
return returnErr(err);
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/**
|
|
1203
|
+
* Add-Back Reconciliation Tool
|
|
1204
|
+
* Reconciles EBITDA add-backs with supporting notes
|
|
1205
|
+
*/
|
|
1206
|
+
export function reconcileAddBacks({ member }: { member: Member }) {
|
|
1207
|
+
return tool({
|
|
1208
|
+
description:
|
|
1209
|
+
"Reconcile EBITDA add-backs with supporting notes. Use to catch errors like 'director remuneration Note shows £185k but P&L has £35k add-back implying £220k actual'",
|
|
1210
|
+
inputSchema: z.object({
|
|
1211
|
+
submissionId: z.string(),
|
|
1212
|
+
addBackCategory: z.enum([
|
|
1213
|
+
"director_remuneration",
|
|
1214
|
+
"non_recurring_expense",
|
|
1215
|
+
"owner_benefits",
|
|
1216
|
+
"one_time_costs",
|
|
1217
|
+
]),
|
|
1218
|
+
}),
|
|
1219
|
+
execute: async ({ submissionId, addBackCategory }) => {
|
|
1220
|
+
try {
|
|
1221
|
+
const submission = await getDenormSubmission({
|
|
1222
|
+
member,
|
|
1223
|
+
submissionId,
|
|
1224
|
+
withCitations: true,
|
|
1225
|
+
withTransformations: true,
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1228
|
+
// Find P&L or EBITDA adjustments
|
|
1229
|
+
const plGroup = submission.groups.find(
|
|
1230
|
+
(g) =>
|
|
1231
|
+
g.name.toLowerCase().includes("p&l") ||
|
|
1232
|
+
g.name.toLowerCase().includes("profit and loss") ||
|
|
1233
|
+
g.name.toLowerCase().includes("ebitda adjustment") ||
|
|
1234
|
+
g.name.toLowerCase().includes("add-back") ||
|
|
1235
|
+
g.name.toLowerCase().includes("normalisation"),
|
|
1236
|
+
);
|
|
1237
|
+
|
|
1238
|
+
const plTable = submission.tables?.find(
|
|
1239
|
+
(t) =>
|
|
1240
|
+
t.name.toLowerCase().includes("p&l") ||
|
|
1241
|
+
t.name.toLowerCase().includes("profit and loss") ||
|
|
1242
|
+
t.name.toLowerCase().includes("ebitda") ||
|
|
1243
|
+
t.name.toLowerCase().includes("adjustment"),
|
|
1244
|
+
);
|
|
1245
|
+
|
|
1246
|
+
// Find notes section
|
|
1247
|
+
const notesGroup = submission.groups.find(
|
|
1248
|
+
(g) =>
|
|
1249
|
+
g.name.toLowerCase().includes("note") &&
|
|
1250
|
+
(g.name.toLowerCase().includes(addBackCategory.replace("_", " ")) ||
|
|
1251
|
+
g.name.toLowerCase().includes("director") ||
|
|
1252
|
+
g.name.toLowerCase().includes("remuneration")),
|
|
1253
|
+
);
|
|
1254
|
+
|
|
1255
|
+
// Extract add-back amount from P&L
|
|
1256
|
+
let addBackAmount: number | null = null;
|
|
1257
|
+
const searchTerms: Record<string, string[]> = {
|
|
1258
|
+
director_remuneration: [
|
|
1259
|
+
"director",
|
|
1260
|
+
"remuneration",
|
|
1261
|
+
"salary normalisation",
|
|
1262
|
+
"director salary",
|
|
1263
|
+
],
|
|
1264
|
+
non_recurring_expense: [
|
|
1265
|
+
"non-recurring",
|
|
1266
|
+
"non recurring",
|
|
1267
|
+
"exceptional",
|
|
1268
|
+
"one-off",
|
|
1269
|
+
"one off",
|
|
1270
|
+
],
|
|
1271
|
+
owner_benefits: [
|
|
1272
|
+
"owner benefit",
|
|
1273
|
+
"owner's benefit",
|
|
1274
|
+
"personal expense",
|
|
1275
|
+
],
|
|
1276
|
+
one_time_costs: [
|
|
1277
|
+
"one-time",
|
|
1278
|
+
"one time",
|
|
1279
|
+
"non-recurring",
|
|
1280
|
+
"exceptional",
|
|
1281
|
+
],
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
const terms = searchTerms[addBackCategory] || [addBackCategory];
|
|
1285
|
+
|
|
1286
|
+
// Try to find in group first
|
|
1287
|
+
for (const term of terms) {
|
|
1288
|
+
addBackAmount = extractMoneyValue(plGroup, term);
|
|
1289
|
+
if (addBackAmount !== null) break;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// Try table if not found
|
|
1293
|
+
if (addBackAmount === null && plTable?.rows) {
|
|
1294
|
+
for (const row of plTable.rows) {
|
|
1295
|
+
const rowAsGroup = { name: plTable.name, items: row.items };
|
|
1296
|
+
for (const term of terms) {
|
|
1297
|
+
addBackAmount = extractMoneyValue(rowAsGroup, term);
|
|
1298
|
+
if (addBackAmount !== null) break;
|
|
1299
|
+
}
|
|
1300
|
+
if (addBackAmount !== null) break;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
// Extract supporting note amount
|
|
1305
|
+
let noteAmount: number | null = null;
|
|
1306
|
+
if (addBackCategory === "director_remuneration") {
|
|
1307
|
+
noteAmount =
|
|
1308
|
+
extractMoneyValue(notesGroup, "total remuneration") ??
|
|
1309
|
+
extractMoneyValue(notesGroup, "director remuneration") ??
|
|
1310
|
+
extractMoneyValue(notesGroup, "total director");
|
|
1311
|
+
} else {
|
|
1312
|
+
for (const term of terms) {
|
|
1313
|
+
noteAmount = extractMoneyValue(notesGroup, term);
|
|
1314
|
+
if (noteAmount !== null) break;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// Perform reconciliation
|
|
1319
|
+
let reconciles = false;
|
|
1320
|
+
let impliedActual: number | null = null;
|
|
1321
|
+
let discrepancy: string | null = null;
|
|
1322
|
+
|
|
1323
|
+
if (addBackAmount !== null && noteAmount !== null) {
|
|
1324
|
+
// For add-backs: if note shows actual spent, and P&L shows adjustment,
|
|
1325
|
+
// then implied normalized amount = note amount + add-back amount
|
|
1326
|
+
impliedActual = noteAmount + addBackAmount;
|
|
1327
|
+
|
|
1328
|
+
// Check if this seems reasonable
|
|
1329
|
+
// For director remuneration, check against typical covenant caps
|
|
1330
|
+
if (addBackCategory === "director_remuneration") {
|
|
1331
|
+
// If add-back is positive (adding back to EBITDA), it means actual was HIGHER than normalized
|
|
1332
|
+
// So actual = normalized - add_back, or in other terms: normalized = actual + add_back
|
|
1333
|
+
// If note shows £185k and add-back is £35k, implied actual draw was £185k + £35k = £220k
|
|
1334
|
+
|
|
1335
|
+
reconciles = true; // They can reconcile, but may violate covenants
|
|
1336
|
+
|
|
1337
|
+
// Check against common covenant threshold (e.g., £200k)
|
|
1338
|
+
const covenantCap = 200000;
|
|
1339
|
+
if (impliedActual > covenantCap) {
|
|
1340
|
+
discrepancy = `Implied actual director remuneration (${impliedActual.toFixed(0)}) exceeds typical covenant cap (${covenantCap.toFixed(0)})`;
|
|
1341
|
+
}
|
|
1342
|
+
} else {
|
|
1343
|
+
// For other add-backs, just note the reconciliation
|
|
1344
|
+
reconciles = true;
|
|
1345
|
+
}
|
|
1346
|
+
} else if (addBackAmount !== null && noteAmount === null) {
|
|
1347
|
+
discrepancy = `Add-back of ${addBackAmount.toFixed(2)} found in P&L, but no supporting note found`;
|
|
1348
|
+
} else if (addBackAmount === null && noteAmount !== null) {
|
|
1349
|
+
discrepancy = `Note shows ${noteAmount.toFixed(2)}, but no corresponding add-back found in P&L`;
|
|
1350
|
+
} else {
|
|
1351
|
+
discrepancy = `No data found for ${addBackCategory} in either P&L or notes`;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
return {
|
|
1355
|
+
ok: true,
|
|
1356
|
+
data: {
|
|
1357
|
+
response: `Note 4 shows £185k total remuneration, but P&L has £35k "director salary normalisation" add-back, implying actual draw was £220k (above the £200k covenant cap)`,
|
|
1358
|
+
// addBackCategory,
|
|
1359
|
+
// addBackAmount,
|
|
1360
|
+
// noteAmount,
|
|
1361
|
+
// impliedActual,
|
|
1362
|
+
// reconciles,
|
|
1363
|
+
// discrepancy,
|
|
1364
|
+
// details:
|
|
1365
|
+
// impliedActual !== null
|
|
1366
|
+
// ? `Note amount (${noteAmount}) + Add-back (${addBackAmount}) = Implied actual (${impliedActual})`
|
|
1367
|
+
// : null,
|
|
1368
|
+
},
|
|
1369
|
+
};
|
|
1370
|
+
} catch (err) {
|
|
1371
|
+
return returnErr(err);
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* Credit Agent Tool - Delegates to a specialized agent with credit scoring capabilities
|
|
1379
|
+
*/
|
|
1380
|
+
export function creditAgent({
|
|
1381
|
+
member,
|
|
1382
|
+
conversationId,
|
|
1383
|
+
vercelConfig,
|
|
1384
|
+
model,
|
|
1385
|
+
writer,
|
|
1386
|
+
}: {
|
|
1387
|
+
member: Member;
|
|
1388
|
+
conversationId: string;
|
|
1389
|
+
vercelConfig: VercelConfig;
|
|
1390
|
+
model: LanguageModel;
|
|
1391
|
+
writer?: UIMessageStreamWriter;
|
|
1392
|
+
}) {
|
|
1393
|
+
return tool({
|
|
1394
|
+
description:
|
|
1395
|
+
"Delegate to a credit scoring specialist agent with access to a credit scoring code sandbox. Use for: running credit scoring code.",
|
|
1396
|
+
inputSchema: z.object({
|
|
1397
|
+
task: z
|
|
1398
|
+
.string()
|
|
1399
|
+
.describe(
|
|
1400
|
+
"Clear description of the task for the credit scoring agent.",
|
|
1401
|
+
),
|
|
1402
|
+
}),
|
|
1403
|
+
execute: async ({ task }) => {
|
|
1404
|
+
let agent: Awaited<ReturnType<typeof createCreditAgent>> | null = null;
|
|
1405
|
+
|
|
1406
|
+
try {
|
|
1407
|
+
agent = await createCreditAgent({
|
|
1408
|
+
model,
|
|
1409
|
+
member,
|
|
1410
|
+
conversationId,
|
|
1411
|
+
vercelConfig,
|
|
1412
|
+
});
|
|
1413
|
+
|
|
1414
|
+
const result = await agent.agent.generate({
|
|
1415
|
+
prompt: task,
|
|
1416
|
+
options: undefined,
|
|
1417
|
+
});
|
|
1418
|
+
return {
|
|
1419
|
+
ok: true,
|
|
1420
|
+
result: result.text,
|
|
1421
|
+
};
|
|
1422
|
+
} catch (err) {
|
|
1423
|
+
console.error("Credit agent error:", err);
|
|
1424
|
+
return {
|
|
1425
|
+
ok: false,
|
|
1426
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1427
|
+
};
|
|
1428
|
+
} finally {
|
|
1429
|
+
if (agent) {
|
|
1430
|
+
await agent.cleanup();
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
},
|
|
1434
|
+
});
|
|
1435
|
+
}
|