@useatlas/create 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/README.md +231 -0
  2. package/index.ts +829 -0
  3. package/package.json +38 -0
  4. package/templates/docker/.env.example +67 -0
  5. package/templates/docker/Dockerfile +52 -0
  6. package/templates/docker/bin/__tests__/benchmark.test.ts +598 -0
  7. package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +171 -0
  8. package/templates/docker/bin/__tests__/eval.test.ts +434 -0
  9. package/templates/docker/bin/__tests__/matview-partition.test.ts +615 -0
  10. package/templates/docker/bin/__tests__/multi-source.test.ts +113 -0
  11. package/templates/docker/bin/__tests__/plugin-cli.test.ts +322 -0
  12. package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +608 -0
  13. package/templates/docker/bin/__tests__/query.test.ts +240 -0
  14. package/templates/docker/bin/__tests__/schema-drift.test.ts +542 -0
  15. package/templates/docker/bin/__tests__/view-yaml-generation.test.ts +146 -0
  16. package/templates/docker/bin/atlas.ts +5044 -0
  17. package/templates/docker/bin/benchmark.ts +695 -0
  18. package/templates/docker/bin/enrich.ts +559 -0
  19. package/templates/docker/bin/eval.ts +770 -0
  20. package/templates/docker/bin/smoke.ts +438 -0
  21. package/templates/docker/data/.gitkeep +0 -0
  22. package/templates/docker/data/cybersec.sql +1961 -0
  23. package/templates/docker/data/demo-semantic/catalog.yml +40 -0
  24. package/templates/docker/data/demo-semantic/entities/accounts.yml +170 -0
  25. package/templates/docker/data/demo-semantic/entities/companies.yml +207 -0
  26. package/templates/docker/data/demo-semantic/entities/people.yml +145 -0
  27. package/templates/docker/data/demo-semantic/glossary.yml +22 -0
  28. package/templates/docker/data/demo-semantic/metrics/accounts.yml +38 -0
  29. package/templates/docker/data/demo-semantic/metrics/companies.yml +89 -0
  30. package/templates/docker/data/demo.sql +373 -0
  31. package/templates/docker/data/ecommerce.sql +1690 -0
  32. package/templates/docker/data/init-demo-db.sql +8 -0
  33. package/templates/docker/docker-compose.yml +34 -0
  34. package/templates/docker/docs/deploy.md +390 -0
  35. package/templates/docker/eslint.config.mjs +18 -0
  36. package/templates/docker/gitignore +5 -0
  37. package/templates/docker/next.config.ts +9 -0
  38. package/templates/docker/package.json +59 -0
  39. package/templates/docker/postcss.config.mjs +8 -0
  40. package/templates/docker/public/.gitkeep +0 -0
  41. package/templates/docker/public/favicon.svg +4 -0
  42. package/templates/docker/railway.json +13 -0
  43. package/templates/docker/render.yaml +34 -0
  44. package/templates/docker/semantic/catalog.yml +5 -0
  45. package/templates/docker/semantic/entities/.gitkeep +0 -0
  46. package/templates/docker/semantic/glossary.yml +6 -0
  47. package/templates/docker/semantic/metrics/.gitkeep +0 -0
  48. package/templates/docker/sidecar/Dockerfile +28 -0
  49. package/templates/docker/sidecar/railway.json +14 -0
  50. package/templates/docker/sidecar/server.ts +188 -0
  51. package/templates/docker/src/api/__tests__/actions.test.ts +683 -0
  52. package/templates/docker/src/api/__tests__/admin.test.ts +820 -0
  53. package/templates/docker/src/api/__tests__/auth.test.ts +165 -0
  54. package/templates/docker/src/api/__tests__/chat.test.ts +376 -0
  55. package/templates/docker/src/api/__tests__/conversations.test.ts +555 -0
  56. package/templates/docker/src/api/__tests__/cors.test.ts +135 -0
  57. package/templates/docker/src/api/__tests__/health-plugin.test.ts +169 -0
  58. package/templates/docker/src/api/__tests__/health.test.ts +261 -0
  59. package/templates/docker/src/api/__tests__/query.test.ts +891 -0
  60. package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +601 -0
  61. package/templates/docker/src/api/__tests__/slack.test.ts +847 -0
  62. package/templates/docker/src/api/index.ts +117 -0
  63. package/templates/docker/src/api/routes/actions.ts +274 -0
  64. package/templates/docker/src/api/routes/admin.ts +757 -0
  65. package/templates/docker/src/api/routes/auth.ts +48 -0
  66. package/templates/docker/src/api/routes/chat.ts +465 -0
  67. package/templates/docker/src/api/routes/conversations.ts +266 -0
  68. package/templates/docker/src/api/routes/health.ts +287 -0
  69. package/templates/docker/src/api/routes/openapi.ts +390 -0
  70. package/templates/docker/src/api/routes/query.ts +318 -0
  71. package/templates/docker/src/api/routes/scheduled-tasks.ts +467 -0
  72. package/templates/docker/src/api/routes/slack.ts +611 -0
  73. package/templates/docker/src/api/server.ts +226 -0
  74. package/templates/docker/src/app/api/[...route]/route.ts +33 -0
  75. package/templates/docker/src/app/error.tsx +24 -0
  76. package/templates/docker/src/app/globals.css +126 -0
  77. package/templates/docker/src/app/layout.tsx +19 -0
  78. package/templates/docker/src/app/page.tsx +14 -0
  79. package/templates/docker/src/global.d.ts +1 -0
  80. package/templates/docker/src/lib/__tests__/agent-cache.test.ts +437 -0
  81. package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +114 -0
  82. package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
  83. package/templates/docker/src/lib/__tests__/agent-integration.test.ts +514 -0
  84. package/templates/docker/src/lib/__tests__/config-actions.test.ts +166 -0
  85. package/templates/docker/src/lib/__tests__/config.test.ts +1063 -0
  86. package/templates/docker/src/lib/__tests__/conversations.test.ts +589 -0
  87. package/templates/docker/src/lib/__tests__/errors.test.ts +256 -0
  88. package/templates/docker/src/lib/__tests__/logger.test.ts +200 -0
  89. package/templates/docker/src/lib/__tests__/providers.test.ts +99 -0
  90. package/templates/docker/src/lib/__tests__/rls.test.ts +435 -0
  91. package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
  92. package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
  93. package/templates/docker/src/lib/__tests__/semantic-index.test.ts +547 -0
  94. package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +544 -0
  95. package/templates/docker/src/lib/__tests__/semantic.test.ts +363 -0
  96. package/templates/docker/src/lib/__tests__/startup-actions.test.ts +452 -0
  97. package/templates/docker/src/lib/__tests__/startup.test.ts +465 -0
  98. package/templates/docker/src/lib/__tests__/tracing.test.ts +28 -0
  99. package/templates/docker/src/lib/action-types.ts +95 -0
  100. package/templates/docker/src/lib/agent-query.ts +178 -0
  101. package/templates/docker/src/lib/agent.ts +505 -0
  102. package/templates/docker/src/lib/api-url.ts +2 -0
  103. package/templates/docker/src/lib/auth/__tests__/audit.test.ts +418 -0
  104. package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
  105. package/templates/docker/src/lib/auth/__tests__/byot.test.ts +366 -0
  106. package/templates/docker/src/lib/auth/__tests__/detect.test.ts +190 -0
  107. package/templates/docker/src/lib/auth/__tests__/managed.test.ts +173 -0
  108. package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +456 -0
  109. package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +201 -0
  110. package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +225 -0
  111. package/templates/docker/src/lib/auth/__tests__/server.test.ts +34 -0
  112. package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +176 -0
  113. package/templates/docker/src/lib/auth/__tests__/types.test.ts +44 -0
  114. package/templates/docker/src/lib/auth/audit.ts +89 -0
  115. package/templates/docker/src/lib/auth/byot.ts +158 -0
  116. package/templates/docker/src/lib/auth/client.ts +35 -0
  117. package/templates/docker/src/lib/auth/detect.ts +83 -0
  118. package/templates/docker/src/lib/auth/managed.ts +73 -0
  119. package/templates/docker/src/lib/auth/middleware.ts +208 -0
  120. package/templates/docker/src/lib/auth/migrate.ts +111 -0
  121. package/templates/docker/src/lib/auth/permissions.ts +156 -0
  122. package/templates/docker/src/lib/auth/server.ts +142 -0
  123. package/templates/docker/src/lib/auth/simple-key.ts +92 -0
  124. package/templates/docker/src/lib/auth/types.ts +49 -0
  125. package/templates/docker/src/lib/config.ts +704 -0
  126. package/templates/docker/src/lib/conversation-types.ts +29 -0
  127. package/templates/docker/src/lib/conversations.ts +270 -0
  128. package/templates/docker/src/lib/db/__tests__/connection.test.ts +69 -0
  129. package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +141 -0
  130. package/templates/docker/src/lib/db/__tests__/internal.test.ts +387 -0
  131. package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +207 -0
  132. package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
  133. package/templates/docker/src/lib/db/__tests__/registry.test.ts +595 -0
  134. package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +339 -0
  135. package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +217 -0
  136. package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
  137. package/templates/docker/src/lib/db/connection.ts +753 -0
  138. package/templates/docker/src/lib/db/duckdb.ts +122 -0
  139. package/templates/docker/src/lib/db/internal.ts +273 -0
  140. package/templates/docker/src/lib/db/salesforce.ts +342 -0
  141. package/templates/docker/src/lib/db/source-rate-limit.ts +191 -0
  142. package/templates/docker/src/lib/errors.ts +154 -0
  143. package/templates/docker/src/lib/logger.ts +98 -0
  144. package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
  145. package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +529 -0
  146. package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +521 -0
  147. package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +346 -0
  148. package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +49 -0
  149. package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +585 -0
  150. package/templates/docker/src/lib/plugins/hooks.ts +162 -0
  151. package/templates/docker/src/lib/plugins/index.ts +9 -0
  152. package/templates/docker/src/lib/plugins/migrate.ts +309 -0
  153. package/templates/docker/src/lib/plugins/registry.ts +231 -0
  154. package/templates/docker/src/lib/plugins/tools.ts +39 -0
  155. package/templates/docker/src/lib/plugins/wiring.ts +291 -0
  156. package/templates/docker/src/lib/providers.ts +102 -0
  157. package/templates/docker/src/lib/rls.ts +321 -0
  158. package/templates/docker/src/lib/scheduled-task-types.ts +132 -0
  159. package/templates/docker/src/lib/scheduled-tasks.ts +475 -0
  160. package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
  161. package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +248 -0
  162. package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
  163. package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
  164. package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
  165. package/templates/docker/src/lib/scheduler/delivery.ts +248 -0
  166. package/templates/docker/src/lib/scheduler/engine.ts +317 -0
  167. package/templates/docker/src/lib/scheduler/executor.ts +73 -0
  168. package/templates/docker/src/lib/scheduler/format-email.ts +109 -0
  169. package/templates/docker/src/lib/scheduler/format-slack.ts +35 -0
  170. package/templates/docker/src/lib/scheduler/format-webhook.ts +37 -0
  171. package/templates/docker/src/lib/scheduler/index.ts +7 -0
  172. package/templates/docker/src/lib/security.ts +11 -0
  173. package/templates/docker/src/lib/semantic-index.ts +503 -0
  174. package/templates/docker/src/lib/semantic.ts +387 -0
  175. package/templates/docker/src/lib/sidecar-types.ts +16 -0
  176. package/templates/docker/src/lib/slack/__tests__/api.test.ts +160 -0
  177. package/templates/docker/src/lib/slack/__tests__/format.test.ts +237 -0
  178. package/templates/docker/src/lib/slack/__tests__/store.test.ts +188 -0
  179. package/templates/docker/src/lib/slack/__tests__/threads.test.ts +112 -0
  180. package/templates/docker/src/lib/slack/__tests__/verify.test.ts +111 -0
  181. package/templates/docker/src/lib/slack/api.ts +102 -0
  182. package/templates/docker/src/lib/slack/format.ts +209 -0
  183. package/templates/docker/src/lib/slack/store.ts +107 -0
  184. package/templates/docker/src/lib/slack/threads.ts +64 -0
  185. package/templates/docker/src/lib/slack/verify.ts +71 -0
  186. package/templates/docker/src/lib/startup.ts +730 -0
  187. package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
  188. package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
  189. package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
  190. package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
  191. package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
  192. package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
  193. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
  194. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
  195. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +235 -0
  196. package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
  197. package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
  198. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
  199. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
  200. package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
  201. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
  202. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +1012 -0
  203. package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
  204. package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +378 -0
  205. package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
  206. package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
  207. package/templates/docker/src/lib/tools/actions/audit.ts +47 -0
  208. package/templates/docker/src/lib/tools/actions/email.ts +191 -0
  209. package/templates/docker/src/lib/tools/actions/handler.ts +591 -0
  210. package/templates/docker/src/lib/tools/actions/index.ts +23 -0
  211. package/templates/docker/src/lib/tools/actions/jira.ts +220 -0
  212. package/templates/docker/src/lib/tools/explore-nsjail.ts +343 -0
  213. package/templates/docker/src/lib/tools/explore-sandbox.ts +264 -0
  214. package/templates/docker/src/lib/tools/explore-sidecar.ts +163 -0
  215. package/templates/docker/src/lib/tools/explore.ts +379 -0
  216. package/templates/docker/src/lib/tools/registry.ts +221 -0
  217. package/templates/docker/src/lib/tools/salesforce.ts +138 -0
  218. package/templates/docker/src/lib/tools/soql-validation.ts +172 -0
  219. package/templates/docker/src/lib/tools/sql.ts +680 -0
  220. package/templates/docker/src/lib/tracing.ts +40 -0
  221. package/templates/docker/src/lib/utils.ts +6 -0
  222. package/templates/docker/src/test-setup.ts +38 -0
  223. package/templates/docker/src/types/vercel-sandbox.d.ts +54 -0
  224. package/templates/docker/src/ui/components/actions/action-approval-card.tsx +295 -0
  225. package/templates/docker/src/ui/components/actions/action-status-badge.tsx +50 -0
  226. package/templates/docker/src/ui/components/admin/admin-layout.tsx +26 -0
  227. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +96 -0
  228. package/templates/docker/src/ui/components/admin/empty-state.tsx +24 -0
  229. package/templates/docker/src/ui/components/admin/entity-detail.tsx +233 -0
  230. package/templates/docker/src/ui/components/admin/entity-list.tsx +96 -0
  231. package/templates/docker/src/ui/components/admin/error-banner.tsx +22 -0
  232. package/templates/docker/src/ui/components/admin/feature-disabled.tsx +44 -0
  233. package/templates/docker/src/ui/components/admin/health-badge.tsx +30 -0
  234. package/templates/docker/src/ui/components/admin/loading-state.tsx +14 -0
  235. package/templates/docker/src/ui/components/admin/stat-card.tsx +32 -0
  236. package/templates/docker/src/ui/components/atlas-chat.tsx +370 -0
  237. package/templates/docker/src/ui/components/chart/chart-detection.ts +261 -0
  238. package/templates/docker/src/ui/components/chart/result-chart.tsx +375 -0
  239. package/templates/docker/src/ui/components/chat/api-key-bar.tsx +66 -0
  240. package/templates/docker/src/ui/components/chat/copy-button.tsx +25 -0
  241. package/templates/docker/src/ui/components/chat/data-table.tsx +102 -0
  242. package/templates/docker/src/ui/components/chat/error-banner.tsx +32 -0
  243. package/templates/docker/src/ui/components/chat/explore-card.tsx +41 -0
  244. package/templates/docker/src/ui/components/chat/loading-card.tsx +10 -0
  245. package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +116 -0
  246. package/templates/docker/src/ui/components/chat/markdown.tsx +72 -0
  247. package/templates/docker/src/ui/components/chat/sql-block.tsx +30 -0
  248. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +144 -0
  249. package/templates/docker/src/ui/components/chat/starter-prompts.ts +6 -0
  250. package/templates/docker/src/ui/components/chat/tool-part.tsx +40 -0
  251. package/templates/docker/src/ui/components/chat/typing-indicator.tsx +19 -0
  252. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +120 -0
  253. package/templates/docker/src/ui/components/conversations/conversation-list.tsx +66 -0
  254. package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
  255. package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +27 -0
  256. package/templates/docker/src/ui/context.tsx +78 -0
  257. package/templates/docker/src/ui/hooks/use-admin-fetch.ts +104 -0
  258. package/templates/docker/src/ui/hooks/use-conversations.ts +184 -0
  259. package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -0
  260. package/templates/docker/src/ui/lib/action-types.ts +63 -0
  261. package/templates/docker/src/ui/lib/helpers.ts +104 -0
  262. package/templates/docker/src/ui/lib/types.ts +145 -0
  263. package/templates/docker/tsconfig.json +41 -0
  264. package/templates/docker/vercel.json +3 -0
  265. package/templates/nextjs-standalone/.env.example +68 -0
  266. package/templates/nextjs-standalone/bin/__tests__/benchmark.test.ts +598 -0
  267. package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +171 -0
  268. package/templates/nextjs-standalone/bin/__tests__/eval.test.ts +434 -0
  269. package/templates/nextjs-standalone/bin/__tests__/matview-partition.test.ts +615 -0
  270. package/templates/nextjs-standalone/bin/__tests__/multi-source.test.ts +113 -0
  271. package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +322 -0
  272. package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +608 -0
  273. package/templates/nextjs-standalone/bin/__tests__/query.test.ts +240 -0
  274. package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +542 -0
  275. package/templates/nextjs-standalone/bin/__tests__/view-yaml-generation.test.ts +146 -0
  276. package/templates/nextjs-standalone/bin/atlas.ts +5044 -0
  277. package/templates/nextjs-standalone/bin/benchmark.ts +695 -0
  278. package/templates/nextjs-standalone/bin/enrich.ts +559 -0
  279. package/templates/nextjs-standalone/bin/eval.ts +770 -0
  280. package/templates/nextjs-standalone/bin/smoke.ts +438 -0
  281. package/templates/nextjs-standalone/data/.gitkeep +0 -0
  282. package/templates/nextjs-standalone/data/cybersec.sql +1961 -0
  283. package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +40 -0
  284. package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +170 -0
  285. package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +207 -0
  286. package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +145 -0
  287. package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +22 -0
  288. package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +38 -0
  289. package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +89 -0
  290. package/templates/nextjs-standalone/data/demo.sql +373 -0
  291. package/templates/nextjs-standalone/data/ecommerce.sql +1690 -0
  292. package/templates/nextjs-standalone/data/init-demo-db.sql +8 -0
  293. package/templates/nextjs-standalone/docs/deploy.md +390 -0
  294. package/templates/nextjs-standalone/eslint.config.mjs +18 -0
  295. package/templates/nextjs-standalone/gitignore +5 -0
  296. package/templates/nextjs-standalone/next.config.ts +10 -0
  297. package/templates/nextjs-standalone/package.json +63 -0
  298. package/templates/nextjs-standalone/postcss.config.mjs +8 -0
  299. package/templates/nextjs-standalone/semantic/catalog.yml +5 -0
  300. package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
  301. package/templates/nextjs-standalone/semantic/glossary.yml +6 -0
  302. package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
  303. package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +683 -0
  304. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +820 -0
  305. package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +165 -0
  306. package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +376 -0
  307. package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +555 -0
  308. package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +135 -0
  309. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +169 -0
  310. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +261 -0
  311. package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +891 -0
  312. package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +601 -0
  313. package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +847 -0
  314. package/templates/nextjs-standalone/src/api/index.ts +117 -0
  315. package/templates/nextjs-standalone/src/api/routes/actions.ts +274 -0
  316. package/templates/nextjs-standalone/src/api/routes/admin.ts +757 -0
  317. package/templates/nextjs-standalone/src/api/routes/auth.ts +48 -0
  318. package/templates/nextjs-standalone/src/api/routes/chat.ts +465 -0
  319. package/templates/nextjs-standalone/src/api/routes/conversations.ts +266 -0
  320. package/templates/nextjs-standalone/src/api/routes/health.ts +287 -0
  321. package/templates/nextjs-standalone/src/api/routes/openapi.ts +390 -0
  322. package/templates/nextjs-standalone/src/api/routes/query.ts +318 -0
  323. package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +467 -0
  324. package/templates/nextjs-standalone/src/api/routes/slack.ts +611 -0
  325. package/templates/nextjs-standalone/src/api/server.ts +226 -0
  326. package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +33 -0
  327. package/templates/nextjs-standalone/src/app/error.tsx +24 -0
  328. package/templates/nextjs-standalone/src/app/global-error.tsx +68 -0
  329. package/templates/nextjs-standalone/src/app/globals.css +126 -0
  330. package/templates/nextjs-standalone/src/app/layout.tsx +19 -0
  331. package/templates/nextjs-standalone/src/app/page.tsx +14 -0
  332. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +437 -0
  333. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +114 -0
  334. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
  335. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +514 -0
  336. package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +166 -0
  337. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +1063 -0
  338. package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +589 -0
  339. package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +256 -0
  340. package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +200 -0
  341. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +99 -0
  342. package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +435 -0
  343. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
  344. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
  345. package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +547 -0
  346. package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +544 -0
  347. package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +363 -0
  348. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +452 -0
  349. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +465 -0
  350. package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +28 -0
  351. package/templates/nextjs-standalone/src/lib/action-types.ts +95 -0
  352. package/templates/nextjs-standalone/src/lib/agent-query.ts +178 -0
  353. package/templates/nextjs-standalone/src/lib/agent.ts +505 -0
  354. package/templates/nextjs-standalone/src/lib/api-url.ts +3 -0
  355. package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +418 -0
  356. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
  357. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +366 -0
  358. package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +190 -0
  359. package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +173 -0
  360. package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +456 -0
  361. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +201 -0
  362. package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +225 -0
  363. package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +34 -0
  364. package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +176 -0
  365. package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +44 -0
  366. package/templates/nextjs-standalone/src/lib/auth/audit.ts +89 -0
  367. package/templates/nextjs-standalone/src/lib/auth/byot.ts +158 -0
  368. package/templates/nextjs-standalone/src/lib/auth/client.ts +23 -0
  369. package/templates/nextjs-standalone/src/lib/auth/detect.ts +83 -0
  370. package/templates/nextjs-standalone/src/lib/auth/managed.ts +73 -0
  371. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +208 -0
  372. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +111 -0
  373. package/templates/nextjs-standalone/src/lib/auth/permissions.ts +156 -0
  374. package/templates/nextjs-standalone/src/lib/auth/server.ts +142 -0
  375. package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +92 -0
  376. package/templates/nextjs-standalone/src/lib/auth/types.ts +49 -0
  377. package/templates/nextjs-standalone/src/lib/config.ts +704 -0
  378. package/templates/nextjs-standalone/src/lib/conversation-types.ts +29 -0
  379. package/templates/nextjs-standalone/src/lib/conversations.ts +270 -0
  380. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +69 -0
  381. package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +141 -0
  382. package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +387 -0
  383. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +207 -0
  384. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
  385. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +595 -0
  386. package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +339 -0
  387. package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +217 -0
  388. package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
  389. package/templates/nextjs-standalone/src/lib/db/connection.ts +753 -0
  390. package/templates/nextjs-standalone/src/lib/db/duckdb.ts +122 -0
  391. package/templates/nextjs-standalone/src/lib/db/internal.ts +273 -0
  392. package/templates/nextjs-standalone/src/lib/db/salesforce.ts +342 -0
  393. package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +191 -0
  394. package/templates/nextjs-standalone/src/lib/errors.ts +154 -0
  395. package/templates/nextjs-standalone/src/lib/logger.ts +98 -0
  396. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
  397. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +529 -0
  398. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +521 -0
  399. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +346 -0
  400. package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +49 -0
  401. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +585 -0
  402. package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +162 -0
  403. package/templates/nextjs-standalone/src/lib/plugins/index.ts +9 -0
  404. package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +309 -0
  405. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +231 -0
  406. package/templates/nextjs-standalone/src/lib/plugins/tools.ts +39 -0
  407. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +291 -0
  408. package/templates/nextjs-standalone/src/lib/providers.ts +102 -0
  409. package/templates/nextjs-standalone/src/lib/rls.ts +321 -0
  410. package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +132 -0
  411. package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +475 -0
  412. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
  413. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +248 -0
  414. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
  415. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
  416. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
  417. package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -0
  418. package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +317 -0
  419. package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +73 -0
  420. package/templates/nextjs-standalone/src/lib/scheduler/format-email.ts +109 -0
  421. package/templates/nextjs-standalone/src/lib/scheduler/format-slack.ts +35 -0
  422. package/templates/nextjs-standalone/src/lib/scheduler/format-webhook.ts +37 -0
  423. package/templates/nextjs-standalone/src/lib/scheduler/index.ts +7 -0
  424. package/templates/nextjs-standalone/src/lib/security.ts +11 -0
  425. package/templates/nextjs-standalone/src/lib/semantic-index.ts +503 -0
  426. package/templates/nextjs-standalone/src/lib/semantic.ts +387 -0
  427. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +16 -0
  428. package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +160 -0
  429. package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +237 -0
  430. package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +188 -0
  431. package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +112 -0
  432. package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +111 -0
  433. package/templates/nextjs-standalone/src/lib/slack/api.ts +102 -0
  434. package/templates/nextjs-standalone/src/lib/slack/format.ts +209 -0
  435. package/templates/nextjs-standalone/src/lib/slack/store.ts +107 -0
  436. package/templates/nextjs-standalone/src/lib/slack/threads.ts +64 -0
  437. package/templates/nextjs-standalone/src/lib/slack/verify.ts +71 -0
  438. package/templates/nextjs-standalone/src/lib/startup.ts +730 -0
  439. package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
  440. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
  441. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
  442. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
  443. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
  444. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
  445. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
  446. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
  447. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +235 -0
  448. package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
  449. package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
  450. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
  451. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
  452. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
  453. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
  454. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +1012 -0
  455. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
  456. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +378 -0
  457. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
  458. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
  459. package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +47 -0
  460. package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +191 -0
  461. package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +591 -0
  462. package/templates/nextjs-standalone/src/lib/tools/actions/index.ts +23 -0
  463. package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +220 -0
  464. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +343 -0
  465. package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +264 -0
  466. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +163 -0
  467. package/templates/nextjs-standalone/src/lib/tools/explore.ts +379 -0
  468. package/templates/nextjs-standalone/src/lib/tools/registry.ts +221 -0
  469. package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +138 -0
  470. package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +172 -0
  471. package/templates/nextjs-standalone/src/lib/tools/sql.ts +680 -0
  472. package/templates/nextjs-standalone/src/lib/tracing.ts +40 -0
  473. package/templates/nextjs-standalone/src/lib/utils.ts +6 -0
  474. package/templates/nextjs-standalone/src/test-setup.ts +38 -0
  475. package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +295 -0
  476. package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +50 -0
  477. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +26 -0
  478. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +96 -0
  479. package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +24 -0
  480. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +233 -0
  481. package/templates/nextjs-standalone/src/ui/components/admin/entity-list.tsx +96 -0
  482. package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +22 -0
  483. package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +44 -0
  484. package/templates/nextjs-standalone/src/ui/components/admin/health-badge.tsx +30 -0
  485. package/templates/nextjs-standalone/src/ui/components/admin/loading-state.tsx +14 -0
  486. package/templates/nextjs-standalone/src/ui/components/admin/stat-card.tsx +32 -0
  487. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +370 -0
  488. package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +261 -0
  489. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +375 -0
  490. package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +66 -0
  491. package/templates/nextjs-standalone/src/ui/components/chat/copy-button.tsx +25 -0
  492. package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +102 -0
  493. package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +32 -0
  494. package/templates/nextjs-standalone/src/ui/components/chat/explore-card.tsx +41 -0
  495. package/templates/nextjs-standalone/src/ui/components/chat/loading-card.tsx +10 -0
  496. package/templates/nextjs-standalone/src/ui/components/chat/managed-auth-card.tsx +116 -0
  497. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +72 -0
  498. package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +30 -0
  499. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +144 -0
  500. package/templates/nextjs-standalone/src/ui/components/chat/starter-prompts.ts +6 -0
  501. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +40 -0
  502. package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +19 -0
  503. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +120 -0
  504. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +66 -0
  505. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
  506. package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +27 -0
  507. package/templates/nextjs-standalone/src/ui/context.tsx +78 -0
  508. package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +104 -0
  509. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +184 -0
  510. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -0
  511. package/templates/nextjs-standalone/src/ui/lib/action-types.ts +63 -0
  512. package/templates/nextjs-standalone/src/ui/lib/helpers.ts +104 -0
  513. package/templates/nextjs-standalone/src/ui/lib/types.ts +145 -0
  514. package/templates/nextjs-standalone/tsconfig.json +32 -0
  515. package/templates/nextjs-standalone/vercel.json +4 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Shared agent query execution logic.
3
+ *
4
+ * Used by both the synchronous JSON endpoint (POST /api/v1/query) and the
5
+ * Slack bot routes to run the Atlas agent to completion and extract
6
+ * structured results from the tool calls.
7
+ */
8
+
9
+ import { runAgent } from "@atlas/api/lib/agent";
10
+ import { createLogger, withRequestContext } from "@atlas/api/lib/logger";
11
+
12
+ const log = createLogger("agent-query");
13
+
14
+ export interface PendingAction {
15
+ id: string;
16
+ type: string;
17
+ target: string;
18
+ summary: string;
19
+ }
20
+
21
+ export interface AgentQueryResult {
22
+ answer: string;
23
+ sql: string[];
24
+ data: { columns: string[]; rows: Record<string, unknown>[] }[];
25
+ steps: number;
26
+ usage: { totalTokens: number };
27
+ pendingActions?: PendingAction[];
28
+ }
29
+
30
+ /**
31
+ * Run the Atlas agent on a single question and return structured results.
32
+ *
33
+ * Creates a UIMessage from the question, optionally loads Salesforce tools,
34
+ * invokes the agent loop, and extracts SQL queries, data, and the final
35
+ * answer from tool results.
36
+ */
37
+ export async function executeAgentQuery(
38
+ question: string,
39
+ requestId?: string,
40
+ options?: { priorMessages?: Array<{ role: "user" | "assistant"; content: string }> },
41
+ ): Promise<AgentQueryResult> {
42
+ const id = requestId ?? crypto.randomUUID();
43
+
44
+ return withRequestContext({ requestId: id }, async () => {
45
+ const priorUIMessages = (options?.priorMessages ?? []).map((m, i) => ({
46
+ id: `${id}-prior-${i}`,
47
+ role: m.role as "user" | "assistant",
48
+ parts: [{ type: "text" as const, text: m.content }],
49
+ }));
50
+
51
+ const messages = [
52
+ ...priorUIMessages,
53
+ {
54
+ id,
55
+ role: "user" as const,
56
+ parts: [{ type: "text" as const, text: question }],
57
+ },
58
+ ];
59
+
60
+ // Optionally include Salesforce tools and actions
61
+ let toolRegistry;
62
+ const includeActions = process.env.ATLAS_ACTIONS_ENABLED === "true";
63
+ let includeSalesforce = false;
64
+ try {
65
+ const { listSalesforceSources } = await import(
66
+ "@atlas/api/lib/db/salesforce"
67
+ );
68
+ includeSalesforce = listSalesforceSources().length > 0;
69
+ } catch (err: unknown) {
70
+ const code = (err as { code?: string })?.code;
71
+ const isModuleNotFound =
72
+ code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND";
73
+ if (!isModuleNotFound) {
74
+ log.error(
75
+ { err: err instanceof Error ? err : new Error(String(err)) },
76
+ "Failed to initialize Salesforce tool registry — falling back to default tools",
77
+ );
78
+ }
79
+ }
80
+ if (includeSalesforce || includeActions) {
81
+ try {
82
+ const { buildRegistry } = await import(
83
+ "@atlas/api/lib/tools/registry"
84
+ );
85
+ toolRegistry = await buildRegistry({ includeSalesforce, includeActions });
86
+ } catch (err) {
87
+ log.error(
88
+ { err: err instanceof Error ? err : new Error(String(err)) },
89
+ "Failed to build tool registry — falling back to default tools",
90
+ );
91
+ }
92
+ }
93
+
94
+ const result = await runAgent({
95
+ messages,
96
+ ...(toolRegistry && { tools: toolRegistry }),
97
+ });
98
+
99
+ const [text, steps, totalUsage] = await Promise.all([
100
+ result.text,
101
+ result.steps,
102
+ result.totalUsage,
103
+ ]);
104
+
105
+ // Collect SQL queries and their data from tool results
106
+ const sqlQueries: string[] = [];
107
+ const dataResults: { columns: string[]; rows: Record<string, unknown>[] }[] = [];
108
+ const pendingActions: PendingAction[] = [];
109
+ const answer = text;
110
+
111
+ for (const step of steps) {
112
+ // No tool results in text-only steps
113
+ if (!step.toolResults) continue;
114
+ for (const tr of step.toolResults) {
115
+ if (tr.toolName === "executeSQL" && tr.output) {
116
+ const r = tr.output as {
117
+ success?: boolean;
118
+ columns?: string[];
119
+ rows?: Record<string, unknown>[];
120
+ };
121
+ const inp = tr.input as { sql?: string };
122
+ if (inp.sql) {
123
+ sqlQueries.push(inp.sql);
124
+ }
125
+ if (r.success && r.columns && r.rows) {
126
+ dataResults.push({ columns: r.columns, rows: r.rows });
127
+ } else if (r.success) {
128
+ log.warn(
129
+ { requestId: id, toolName: "executeSQL", hasColumns: !!r.columns, hasRows: !!r.rows },
130
+ "executeSQL returned success but missing columns or rows",
131
+ );
132
+ }
133
+ }
134
+ // Detect pending action approvals from any action tool
135
+ if (tr.output && typeof tr.output === "object") {
136
+ const out = tr.output as Record<string, unknown>;
137
+ if (out.status === "pending_approval") {
138
+ if (typeof out.actionId !== "string" || !out.actionId) {
139
+ log.warn(
140
+ { toolName: tr.toolName, outputKeys: Object.keys(out) },
141
+ "Tool returned pending_approval but missing or invalid actionId — skipping",
142
+ );
143
+ } else {
144
+ const actionType = typeof (tr.input as Record<string, unknown>)?.actionType === "string"
145
+ ? (tr.input as Record<string, unknown>).actionType as string
146
+ : tr.toolName;
147
+ pendingActions.push({
148
+ id: out.actionId,
149
+ type: actionType,
150
+ target: typeof out.target === "string" ? out.target : "",
151
+ summary: typeof out.summary === "string" ? out.summary : "",
152
+ });
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ if (!answer && dataResults.length > 0) {
160
+ log.warn(
161
+ { requestId: id, steps: steps.length, sqlCount: sqlQueries.length },
162
+ "Agent produced data but no text answer — model may have hit step limit before responding",
163
+ );
164
+ }
165
+
166
+ return {
167
+ answer,
168
+ sql: sqlQueries,
169
+ data: dataResults,
170
+ steps: steps.length,
171
+ usage: {
172
+ totalTokens:
173
+ (totalUsage?.inputTokens ?? 0) + (totalUsage?.outputTokens ?? 0),
174
+ },
175
+ ...(pendingActions.length > 0 && { pendingActions }),
176
+ };
177
+ });
178
+ }
@@ -0,0 +1,505 @@
1
+ /**
2
+ * The Atlas agent.
3
+ *
4
+ * Runs a single-agent loop driven by a ToolRegistry (default: explore,
5
+ * executeSQL). The loop runs until the step limit (25) is reached or
6
+ * the model stops issuing tool calls.
7
+ */
8
+
9
+ import {
10
+ convertToModelMessages,
11
+ stepCountIs,
12
+ streamText,
13
+ type ModelMessage,
14
+ type SystemModelMessage,
15
+ type UIMessage,
16
+ } from "ai";
17
+ import { getModel, getProviderType, type ProviderType } from "./providers";
18
+ import { defaultRegistry, type ToolRegistry } from "./tools/registry";
19
+ import { getContextFragments, getDialectHints } from "./plugins/tools";
20
+ import { connections, detectDBType, type ConnectionMetadata, type DBType } from "./db/connection";
21
+ import { getCrossSourceJoins, type CrossSourceJoin } from "./semantic";
22
+ import { getSemanticIndex } from "./semantic-index";
23
+ import { createLogger } from "./logger";
24
+ import { trace, SpanStatusCode } from "@opentelemetry/api";
25
+
26
+ const log = createLogger("agent");
27
+ const tracer = trace.getTracer("atlas");
28
+
29
+ const SYSTEM_PROMPT_PREFIX = `You are Atlas, an expert data analyst AI. You answer questions about data by exploring a semantic layer, writing SQL, and interpreting results.
30
+
31
+ ## Your Workflow
32
+
33
+ Follow these steps for every question:
34
+
35
+ ### 1. Understand the Question
36
+ Parse what the user is really asking. Check the Ambiguous Terms section below if the question uses terms that could have multiple meanings.`;
37
+
38
+ const SYSTEM_PROMPT_SUFFIX = `## Rules
39
+ - Use the Semantic Layer Reference below to identify tables and columns — write SQL directly when the reference has enough detail
40
+ - Use the explore tool only when you need information not in the reference (e.g., sample values, complex join SQL, query pattern SQL)
41
+ - NEVER guess table or column names — verify them against the reference or via explore
42
+ - NEVER modify data — only SELECT queries are allowed
43
+ - If you cannot answer a question with the available data, say so clearly
44
+ - Be concise but thorough in your interpretations
45
+
46
+ ## Follow-up Questions
47
+ When the user asks a follow-up question:
48
+ - Reference previous query results — don't re-explore the semantic layer if you already know the schema
49
+ - However, if the follow-up involves a different table or entity than the previous query, check the reference or re-explore the relevant entity schema
50
+ - Build on prior SQL — reuse CTEs, table aliases, and filters from earlier queries when relevant
51
+ - If the user says "break that down by X" or "now filter to Y", modify the previous query rather than starting from scratch
52
+ - Refer back to specific numbers from your previous analysis when interpreting new results
53
+
54
+ ## Ambiguous Terms
55
+ Before writing SQL, check if the user's question contains terms from the glossary that need clarification:
56
+ - If a term has status "ambiguous", ASK the user to clarify which meaning they intend before proceeding
57
+ - If a term has a "disambiguation" field (even if status is "defined"), follow its guidance — it may tell you to ask a clarifying question
58
+ - Example: if the glossary lists multiple possible_mappings for a term like "size", ask which meaning the user intends
59
+ - Only ask ONE clarifying question at a time — don't barrage the user
60
+ - If the glossary provides a default interpretation, mention it: "By 'revenue' I'll use companies.revenue (annual company revenue). Would you prefer subscription MRR from accounts.monthly_value?"
61
+
62
+ ## Error Recovery
63
+ When a SQL query fails, read the error carefully before retrying:
64
+ - **Column not found** — The error often suggests the correct name (e.g., "column 'revnue' does not exist — did you mean 'revenue'?"). Go back to the entity schema to verify the exact column name.
65
+ - **Table not found** — Re-read catalog.yml to find the correct table name. The table may use a different name than you expected.
66
+ - **Syntax error** — Check the error position hint. Common issues: missing commas, unmatched parentheses, incorrect JOIN syntax.
67
+ - **Type mismatch** — You may need to CAST a column (e.g., CAST(value AS numeric)). Check the column type in the entity schema.
68
+ - **Timeout** — Simplify the query: remove unnecessary JOINs, add WHERE filters to reduce the dataset, or break into smaller queries.
69
+ - Never retry the exact same SQL. Always fix the identified issue first.
70
+ - Max 2 retries per question — if the query still fails, explain the issue to the user.`;
71
+
72
+ const MYSQL_DIALECT_GUIDE = `
73
+
74
+ ## SQL Dialect: MySQL
75
+ This database uses MySQL. Key differences from PostgreSQL:
76
+ - Use \`YEAR(col)\` and \`MONTH(col)\` (preferred) or \`EXTRACT(YEAR FROM col)\` — both work
77
+ - Use \`DATE_FORMAT(col, '%Y-%m')\` instead of \`TO_CHAR(col, 'YYYY-MM')\`
78
+ - Use \`IFNULL(col, default)\` or \`COALESCE(col, default)\` — both work
79
+ - Use backtick quoting for identifiers: \`\\\`column\\\`\` instead of \`"column"\`
80
+ - Use \`CONCAT(a, b)\` for string concatenation — \`||\` is logical OR in MySQL
81
+ - No \`ILIKE\` — use \`WHERE col COLLATE utf8mb4_bin LIKE 'pattern'\` for case-sensitive matching
82
+ - \`GROUP_CONCAT(col SEPARATOR ', ')\` instead of \`STRING_AGG(col, ', ')\`
83
+ - No \`::type\` casting — use \`CAST(x AS SIGNED)\`, \`CAST(x AS DECIMAL)\`
84
+ - \`LIMIT offset, count\` or \`LIMIT count OFFSET offset\` — both forms work
85
+ - \`COALESCE\`, \`CASE\`, \`NULLIF\`, \`COUNT\`, \`SUM\`, \`AVG\`, \`MIN\`, \`MAX\` work identically`;
86
+
87
+ const CLICKHOUSE_DIALECT_GUIDE = `
88
+
89
+ ## SQL Dialect: ClickHouse
90
+ This database uses ClickHouse. Key differences from PostgreSQL:
91
+ - Use \`toYear(col)\` and \`toMonth(col)\` instead of \`EXTRACT(YEAR FROM col)\`
92
+ - Use \`formatDateTime(col, '%Y-%m')\` instead of \`TO_CHAR(col, 'YYYY-MM')\`
93
+ - Use \`toString(col)\` for explicit string casts — no \`::text\` casting
94
+ - Use \`toInt32(col)\`, \`toFloat64(col)\` for numeric casts — no \`::integer\` casting
95
+ - Use \`ifNull(col, default)\` or \`COALESCE(col, default)\` — both work
96
+ - \`arrayJoin(arr)\` unnests array columns into rows
97
+ - No \`UPDATE\` or \`DELETE\` — ClickHouse is append-only OLAP
98
+ - No foreign keys — joins are supported but there are no referential integrity constraints
99
+ - \`count()\` instead of \`COUNT(*)\` — both work but \`count()\` is idiomatic
100
+ - Use single quotes for strings, double quotes or backticks for identifiers
101
+ - \`argMax(col, ordering_col)\` / \`argMin\` for "latest value" queries
102
+ - Date arithmetic: \`today()\`, \`yesterday()\`, \`now()\`, \`toStartOfMonth(col)\`, \`toStartOfWeek(col)\`
103
+ - \`COALESCE\`, \`CASE\`, \`COUNT\`, \`SUM\`, \`AVG\`, \`MIN\`, \`MAX\` work identically
104
+ - Note: Atlas SQL validation uses PostgreSQL-compatible parsing. Avoid ClickHouse-only syntax (PREWHERE, LIMIT BY, WITH TOTALS) — use standard SQL equivalents instead
105
+ - \`EXPLAIN\`, \`SHOW\`, and \`DESCRIBE\` are not available — use the explore tool to read entity schema files instead`;
106
+
107
+ const SOQL_DIALECT_GUIDE = `
108
+
109
+ ## Query Language: Salesforce SOQL
110
+ This datasource uses Salesforce Object Query Language (SOQL). Key differences from SQL:
111
+ - **No JOINs** — use relationship queries instead:
112
+ - Child-to-parent: \`SELECT Account.Name FROM Contact\`
113
+ - Parent-to-child: \`SELECT Name, (SELECT LastName FROM Contacts) FROM Account\`
114
+ - **Object names** instead of table names (e.g. \`Account\`, \`Contact\`, \`Opportunity\`)
115
+ - **Field API names** — always use the API name (e.g. \`FirstName\`), not the label
116
+ - **No wildcards** — \`SELECT *\` is not supported; list fields explicitly
117
+ - **Date literals**: \`TODAY\`, \`YESTERDAY\`, \`LAST_WEEK\`, \`THIS_MONTH\`, \`LAST_N_DAYS:30\`, \`NEXT_N_DAYS:7\`
118
+ - **Date functions**: \`CALENDAR_YEAR(CloseDate)\`, \`CALENDAR_MONTH(CloseDate)\`, \`DAY_IN_MONTH(CloseDate)\`
119
+ - **Aggregate functions**: \`COUNT()\`, \`COUNT(Id)\`, \`SUM(Amount)\`, \`AVG(Amount)\`, \`MIN(Amount)\`, \`MAX(Amount)\`
120
+ - **GROUP BY** works similarly to SQL: \`SELECT StageName, COUNT(Id) FROM Opportunity GROUP BY StageName\`
121
+ - **HAVING** clause is supported for filtering aggregates
122
+ - **LIMIT** and \`OFFSET\` are supported
123
+ - **No subqueries in FROM** — subqueries only in WHERE (semi-joins): \`WHERE AccountId IN (SELECT Id FROM Account WHERE ...)\`
124
+ - **No UNION, INTERSECT, EXCEPT**
125
+ - Use the \`querySalesforce\` tool (not \`executeSQL\`) for all Salesforce queries`;
126
+
127
+ const SNOWFLAKE_DIALECT_GUIDE = `
128
+
129
+ ## SQL Dialect: Snowflake
130
+ This database uses Snowflake. Key differences from PostgreSQL:
131
+ - \`ILIKE\` works for case-insensitive matching (same as PostgreSQL)
132
+ - \`::type\` casting works (e.g. \`col::VARCHAR\`), plus \`TRY_CAST(col AS type)\` for safe casting that returns NULL on failure
133
+ - Identifiers are case-insensitive by default; double-quoted identifiers are case-sensitive
134
+ - Use \`FLATTEN()\` / \`LATERAL FLATTEN(input => col)\` to unnest semi-structured (VARIANT/ARRAY/OBJECT) data
135
+ - \`QUALIFY\` clause filters window function results directly (e.g. \`QUALIFY ROW_NUMBER() OVER (...) = 1\`)
136
+ - \`DATE_TRUNC('month', col)\` syntax (same as PostgreSQL)
137
+ - No \`LIMIT offset, count\` — use \`LIMIT count OFFSET offset\`
138
+ - Use \`LISTAGG(col, ', ')\` instead of \`STRING_AGG(col, ', ')\` or \`ARRAY_AGG(col)\` for aggregation
139
+ - \`COALESCE\`, \`CASE\`, \`NULLIF\`, \`COUNT\`, \`SUM\`, \`AVG\`, \`MIN\`, \`MAX\` work identically`;
140
+
141
+ const DUCKDB_DIALECT_GUIDE = `
142
+
143
+ ## SQL Dialect: DuckDB
144
+ This database uses DuckDB, an in-process analytical engine. DuckDB's SQL is PostgreSQL-compatible with extensions:
145
+ - \`::type\` casting works (e.g. \`col::INTEGER\`, \`col::VARCHAR\`) — same as PostgreSQL
146
+ - \`ILIKE\` works for case-insensitive matching (same as PostgreSQL)
147
+ - \`EXTRACT(YEAR FROM col)\`, \`DATE_TRUNC('month', col)\`, \`TO_CHAR(col, 'YYYY-MM')\` — same as PostgreSQL
148
+ - \`STRING_AGG(col, ', ')\` works (same as PostgreSQL)
149
+ - \`PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY col)\` works for percentile calculations
150
+ - \`LIST_AGG\`, \`ARRAY_AGG\`, \`LIST()\` for array aggregation
151
+ - \`UNNEST(list_col)\` to expand list/array columns into rows
152
+ - Note: Atlas SQL validation uses PostgreSQL-compatible parsing. Avoid DuckDB-only syntax (QUALIFY, EXCLUDE, REPLACE, STRUCT_PACK, COLUMNS(*), COLUMNS(regex), STRUCT literals with curly braces) — use standard SQL equivalents instead
153
+ - \`COALESCE\`, \`CASE\`, \`NULLIF\`, \`COUNT\`, \`SUM\`, \`AVG\`, \`MIN\`, \`MAX\` work identically`;
154
+
155
+ function dialectName(dbType: DBType): string {
156
+ switch (dbType) {
157
+ case "postgres": return "PostgreSQL";
158
+ case "mysql": return "MySQL";
159
+ case "clickhouse": return "ClickHouse";
160
+ case "snowflake": return "Snowflake";
161
+ case "duckdb": return "DuckDB";
162
+ case "salesforce": return "Salesforce (SOQL)";
163
+ default: { const _: never = dbType; return _; }
164
+ }
165
+ }
166
+
167
+ function buildMultiSourceSection(
168
+ sources: ConnectionMetadata[],
169
+ ): string {
170
+ const lines = sources.map((s) => {
171
+ const dialect = dialectName(s.dbType);
172
+ const desc = s.description ? ` — ${s.description}` : "";
173
+ const healthNote = s.health?.status === "unhealthy"
174
+ ? " (**UNAVAILABLE** — skip queries to this source)"
175
+ : s.health?.status === "degraded"
176
+ ? " (currently degraded — queries may fail)"
177
+ : "";
178
+ return `- **${s.id}** (${dialect})${desc}${healthNote}`;
179
+ });
180
+ let section = `## Available Data Sources
181
+
182
+ This environment has ${sources.length} database connections. Use the \`connectionId\` parameter in executeSQL to target the correct database.
183
+
184
+ ${lines.join("\n")}
185
+
186
+ **Important:**
187
+ - Always specify \`connectionId\` when querying a non-default source
188
+ - Check entity YAML files for the \`connection\` field to see which tables belong to which source
189
+ - Tables are scoped to their connection — a table on "warehouse" cannot be queried via "default"
190
+
191
+ **Semantic layer navigation:**
192
+ - Default connection entities are in \`entities/\` at the root
193
+ - Other sources have their own subdirectory: \`{connectionId}/entities/\`
194
+ - Start by running \`ls\` to see all available source directories
195
+ - Each source may also have its own \`metrics/\` and \`glossary.yml\``;
196
+
197
+ // Surface cross-source relationships in the system prompt so the agent
198
+ // knows upfront which tables span sources and avoids impossible cross-DB JOINs.
199
+ let crossJoins: readonly CrossSourceJoin[];
200
+ try {
201
+ crossJoins = getCrossSourceJoins();
202
+ } catch (err) {
203
+ log.warn({ err: err instanceof Error ? err.message : String(err) }, "Failed to load cross-source joins — continuing without hints");
204
+ crossJoins = [];
205
+ }
206
+ if (crossJoins.length > 0) {
207
+ const joinLines = crossJoins.map((j) => {
208
+ const desc = j.description ? `${j.description} ` : "";
209
+ return `- **${j.fromSource}.${j.fromTable}** → **${j.toSource}.${j.toTable}**: ${desc}(${j.relationship}, on: ${j.on})`;
210
+ });
211
+ section += `\n\n## Cross-Source Relationships\n\n${joinLines.join("\n")}\n\nCross-source joins cannot be done in a single SQL query. Query each source separately and combine results in your analysis.`;
212
+ }
213
+
214
+ return section;
215
+ }
216
+
217
+ /**
218
+ * Collect Salesforce source metadata via dynamic import to avoid a hard
219
+ * dependency on the salesforce module (it may not be installed).
220
+ */
221
+ function getSfSourceMeta(): ConnectionMetadata[] {
222
+ try {
223
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
224
+ const { describeSalesforceSources } = require("./db/salesforce") as {
225
+ describeSalesforceSources: () => ConnectionMetadata[];
226
+ };
227
+ return describeSalesforceSources();
228
+ } catch (err) {
229
+ // jsforce not installed — expected, don't log
230
+ if (err instanceof Error && err.message.includes("Cannot find module")) return [];
231
+ log.warn({ err: err instanceof Error ? err.message : String(err) }, "Failed to describe Salesforce sources");
232
+ return [];
233
+ }
234
+ }
235
+
236
+ function appendDialectHints(prompt: string): string {
237
+ const hints = getDialectHints();
238
+ if (hints.length === 0) return prompt;
239
+ return prompt + "\n\n## Additional SQL Dialect Notes\n\n" + hints.map((h) => h.dialect).join("\n\n");
240
+ }
241
+
242
+ function buildSystemPrompt(registry: ToolRegistry): string {
243
+ let base = SYSTEM_PROMPT_PREFIX + "\n\n" + registry.describe() + "\n\n" + SYSTEM_PROMPT_SUFFIX;
244
+
245
+ // Append the pre-indexed semantic layer summary
246
+ const semanticIndex = getSemanticIndex();
247
+ if (semanticIndex) {
248
+ base += "\n\n" + semanticIndex;
249
+ }
250
+
251
+ // Append plugin context fragments (if any)
252
+ const fragments = getContextFragments();
253
+ if (fragments.length > 0) {
254
+ base += "\n\n" + fragments.join("\n\n");
255
+ }
256
+ const connectionMeta = connections.describe();
257
+ const sfMeta = getSfSourceMeta();
258
+ const meta = [...connectionMeta, ...sfMeta];
259
+
260
+ // Single-connection: identical to pre-v0.7 behavior
261
+ if (meta.length <= 1) {
262
+ let dbType: DBType;
263
+ try {
264
+ dbType = meta.length === 1
265
+ ? meta[0].dbType
266
+ : detectDBType();
267
+ } catch (err) {
268
+ log.debug({ err: err instanceof Error ? err.message : String(err) }, "Could not detect DB type — omitting dialect guide");
269
+ return appendDialectHints(base);
270
+ }
271
+ switch (dbType) {
272
+ case "postgres": return appendDialectHints(base);
273
+ case "mysql": return appendDialectHints(base + MYSQL_DIALECT_GUIDE);
274
+ case "clickhouse": return appendDialectHints(base + CLICKHOUSE_DIALECT_GUIDE);
275
+ case "snowflake": return appendDialectHints(base + SNOWFLAKE_DIALECT_GUIDE);
276
+ case "duckdb": return appendDialectHints(base + DUCKDB_DIALECT_GUIDE);
277
+ case "salesforce": return appendDialectHints(base + SOQL_DIALECT_GUIDE);
278
+ default: { const _exhaustive: never = dbType; throw new Error(`Unknown: ${_exhaustive}`); }
279
+ }
280
+ }
281
+
282
+ // Multi-connection: list sources + include all relevant dialect guides
283
+ let prompt = base + "\n\n" + buildMultiSourceSection(meta);
284
+
285
+ const dbTypes = new Set(meta.map((m) => m.dbType));
286
+ if (dbTypes.has("mysql")) prompt += MYSQL_DIALECT_GUIDE;
287
+ if (dbTypes.has("clickhouse")) prompt += CLICKHOUSE_DIALECT_GUIDE;
288
+ if (dbTypes.has("snowflake")) prompt += SNOWFLAKE_DIALECT_GUIDE;
289
+ if (dbTypes.has("duckdb")) prompt += DUCKDB_DIALECT_GUIDE;
290
+ if (dbTypes.has("salesforce")) prompt += SOQL_DIALECT_GUIDE;
291
+
292
+ return appendDialectHints(prompt);
293
+ }
294
+
295
+ /**
296
+ * Build the system prompt with provider-appropriate cache control.
297
+ *
298
+ * The prompt body is composed from the registry's tool descriptions via
299
+ * `registry.describe()`, sandwiched between the standard prefix and suffix.
300
+ *
301
+ * - Anthropic / Bedrock-Anthropic: returns a SystemModelMessage with
302
+ * `providerOptions.anthropic.cacheControl` (~80% savings on steps 2+).
303
+ * - Bedrock (non-Anthropic): returns a SystemModelMessage with
304
+ * `providerOptions.bedrock.cachePoint`.
305
+ * - OpenAI / Ollama / Gateway: returns a plain string (OpenAI caches
306
+ * automatically for prompts >= 1024 tokens; others have no caching).
307
+ */
308
+ export function buildSystemParam(
309
+ providerType: ProviderType,
310
+ registry: ToolRegistry = defaultRegistry,
311
+ ): string | SystemModelMessage {
312
+ const content = buildSystemPrompt(registry);
313
+
314
+ switch (providerType) {
315
+ case "anthropic":
316
+ case "bedrock-anthropic":
317
+ return {
318
+ role: "system",
319
+ content,
320
+ providerOptions: {
321
+ anthropic: { cacheControl: { type: "ephemeral" } },
322
+ },
323
+ };
324
+ case "bedrock":
325
+ return {
326
+ role: "system",
327
+ content,
328
+ providerOptions: {
329
+ bedrock: { cachePoint: { type: "default" } },
330
+ },
331
+ };
332
+ case "openai":
333
+ case "ollama":
334
+ case "gateway":
335
+ return content;
336
+ default: {
337
+ const _exhaustive: never = providerType;
338
+ throw new Error(`Unknown provider type: ${_exhaustive}`);
339
+ }
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Apply prompt caching to the last message in the conversation.
345
+ *
346
+ * This marks the last message with provider-specific cache control so that
347
+ * all preceding context (system prompt + earlier messages) can be cached
348
+ * by the LLM provider on subsequent steps.
349
+ *
350
+ * - Anthropic / Bedrock-Anthropic: `providerOptions.anthropic.cacheControl`
351
+ * - Bedrock (non-Anthropic): `providerOptions.bedrock.cachePoint`
352
+ * - OpenAI / Ollama / Gateway: no-op (OpenAI caches automatically)
353
+ */
354
+ export function applyCacheControl(
355
+ messages: ModelMessage[],
356
+ providerType: ProviderType,
357
+ ): ModelMessage[] {
358
+ if (messages.length === 0) return messages;
359
+
360
+ // Only Anthropic-family and Bedrock need explicit cache markers
361
+ const lastIndex = messages.length - 1;
362
+
363
+ switch (providerType) {
364
+ case "anthropic":
365
+ case "bedrock-anthropic": {
366
+ return messages.map((message, index) => {
367
+ if (index !== lastIndex) return message;
368
+ return {
369
+ ...message,
370
+ providerOptions: {
371
+ ...message.providerOptions,
372
+ anthropic: { cacheControl: { type: "ephemeral" as const } },
373
+ },
374
+ } as typeof message;
375
+ });
376
+ }
377
+ case "bedrock": {
378
+ return messages.map((message, index) => {
379
+ if (index !== lastIndex) return message;
380
+ return {
381
+ ...message,
382
+ providerOptions: {
383
+ ...message.providerOptions,
384
+ bedrock: { cachePoint: { type: "default" as const } },
385
+ },
386
+ } as typeof message;
387
+ });
388
+ }
389
+ case "openai":
390
+ case "ollama":
391
+ case "gateway":
392
+ return messages;
393
+ default: {
394
+ const _exhaustive: never = providerType;
395
+ throw new Error(`Unknown provider type: ${_exhaustive}`);
396
+ }
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Run the Atlas agent loop.
402
+ *
403
+ * @param messages - The conversation history from the chat UI.
404
+ * @param tools - Optional custom {@link ToolRegistry}. Defaults to
405
+ * {@link defaultRegistry} (explore + executeSQL). The loop terminates
406
+ * when the step limit (25) is reached or the model stops issuing tool calls.
407
+ */
408
+ export async function runAgent({
409
+ messages,
410
+ tools: toolRegistry = defaultRegistry,
411
+ }: {
412
+ messages: UIMessage[];
413
+ tools?: ToolRegistry;
414
+ }) {
415
+ const model = getModel();
416
+ const providerType = getProviderType();
417
+
418
+ const span = tracer.startSpan("atlas.agent", {
419
+ attributes: { provider: providerType, messageCount: messages.length },
420
+ });
421
+
422
+ let spanEnded = false;
423
+ function endSpan(code: SpanStatusCode, message?: string) {
424
+ if (spanEnded) return;
425
+ spanEnded = true;
426
+ span.setStatus({ code, ...(message && { message }) });
427
+ span.end();
428
+ }
429
+
430
+ let result;
431
+ try {
432
+ result = streamText({
433
+ model,
434
+ system: buildSystemParam(providerType, toolRegistry),
435
+ messages: await convertToModelMessages(messages),
436
+ tools: toolRegistry.getAll(),
437
+ temperature: 0.2,
438
+ maxOutputTokens: 4096,
439
+ stopWhen: stepCountIs(25),
440
+ // totalMs: 180s for self-hosted (full agent loop budget).
441
+ // On Vercel, maxDuration caps the serverless function at 60s.
442
+ timeout: { totalMs: 180_000, stepMs: 30_000, chunkMs: 5_000 },
443
+
444
+ onError: ({ error }) => {
445
+ log.error(
446
+ { err: error instanceof Error ? error : new Error(String(error)) },
447
+ "stream error",
448
+ );
449
+ endSpan(
450
+ SpanStatusCode.ERROR,
451
+ error instanceof Error ? error.message : String(error),
452
+ );
453
+ },
454
+
455
+ prepareStep: ({ messages: stepMessages }) => {
456
+ return {
457
+ messages: applyCacheControl(stepMessages, providerType),
458
+ };
459
+ },
460
+
461
+ onStepFinish: ({ stepNumber, finishReason, usage }) => {
462
+ log.info(
463
+ {
464
+ step: stepNumber,
465
+ finishReason,
466
+ inputTokens: usage?.inputTokens,
467
+ outputTokens: usage?.outputTokens,
468
+ cacheRead: usage?.inputTokenDetails?.cacheReadTokens,
469
+ cacheWrite: usage?.inputTokenDetails?.cacheWriteTokens,
470
+ },
471
+ "step complete",
472
+ );
473
+ },
474
+
475
+ onFinish: ({ finishReason, totalUsage, steps }) => {
476
+ log.info(
477
+ {
478
+ finishReason,
479
+ totalSteps: steps.length,
480
+ totalInput: totalUsage?.inputTokens,
481
+ totalOutput: totalUsage?.outputTokens,
482
+ totalCacheRead: totalUsage?.inputTokenDetails?.cacheReadTokens,
483
+ totalCacheWrite: totalUsage?.inputTokenDetails?.cacheWriteTokens,
484
+ },
485
+ "agent finished",
486
+ );
487
+ span.setAttributes({
488
+ finishReason: finishReason ?? "",
489
+ totalSteps: steps.length,
490
+ totalInputTokens: totalUsage?.inputTokens ?? 0,
491
+ totalOutputTokens: totalUsage?.outputTokens ?? 0,
492
+ });
493
+ endSpan(SpanStatusCode.OK);
494
+ },
495
+ });
496
+ } catch (err) {
497
+ endSpan(
498
+ SpanStatusCode.ERROR,
499
+ err instanceof Error ? err.message : String(err),
500
+ );
501
+ throw err;
502
+ }
503
+
504
+ return result;
505
+ }
@@ -0,0 +1,3 @@
1
+ // Simplified: API is embedded via catch-all route — always same-origin.
2
+ export const API_URL = "";
3
+ export const IS_CROSS_ORIGIN = false;