@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,164 @@
1
+ /**
2
+ * Tests for agent buildSystemParam health annotations in the multi-source section.
3
+ *
4
+ * When connections have health status, buildMultiSourceSection (called by
5
+ * buildSystemParam) annotates unhealthy/degraded sources in the system prompt.
6
+ */
7
+
8
+ import { describe, expect, test, beforeEach, mock } from "bun:test";
9
+ import type { ConnectionMetadata, HealthCheckResult } from "../db/connection";
10
+
11
+ // --- Mocks ---
12
+
13
+ const mockDBConnection = {
14
+ query: async () => ({ columns: [], rows: [] }),
15
+ close: async () => {},
16
+ };
17
+
18
+ // Stateful mock — tests push entries to simulate different connection states
19
+ const mockEntries: ConnectionMetadata[] = [];
20
+
21
+ function resetMockEntries() {
22
+ mockEntries.length = 0;
23
+ }
24
+
25
+ mock.module("@atlas/api/lib/db/connection", () => ({
26
+ getDB: () => mockDBConnection,
27
+ connections: {
28
+ get: () => mockDBConnection,
29
+ getDefault: () => mockDBConnection,
30
+ getDBType: (id: string) => {
31
+ const entry = mockEntries.find((e) => e.id === id);
32
+ return entry?.dbType ?? ("postgres" as const);
33
+ },
34
+ getValidator: () => undefined,
35
+ list: () => mockEntries.map((e) => e.id),
36
+ describe: () =>
37
+ mockEntries.map((e) => ({
38
+ id: e.id,
39
+ dbType: e.dbType,
40
+ description: e.description,
41
+ ...(e.health ? { health: e.health } : {}),
42
+ })),
43
+ _reset: () => {
44
+ mockEntries.length = 0;
45
+ },
46
+ },
47
+ detectDBType: () => "postgres" as const,
48
+ ConnectionRegistry: class {},
49
+ }));
50
+
51
+ mock.module("@atlas/api/lib/semantic", () => ({
52
+ getWhitelistedTables: () => new Set(["companies"]),
53
+ _resetWhitelists: () => {},
54
+ getCrossSourceJoins: () => [],
55
+ }));
56
+
57
+ const { buildSystemParam } = await import("@atlas/api/lib/agent");
58
+
59
+ // --- Helpers ---
60
+
61
+ function getContent(result: string | { content: string }): string {
62
+ return typeof result === "string" ? result : result.content;
63
+ }
64
+
65
+ function makeHealthy(): HealthCheckResult {
66
+ return { status: "healthy", latencyMs: 5, checkedAt: new Date() };
67
+ }
68
+
69
+ function makeDegraded(message?: string): HealthCheckResult {
70
+ return {
71
+ status: "degraded",
72
+ latencyMs: 2000,
73
+ message: message ?? "High latency",
74
+ checkedAt: new Date(),
75
+ };
76
+ }
77
+
78
+ function makeUnhealthy(message?: string): HealthCheckResult {
79
+ return {
80
+ status: "unhealthy",
81
+ latencyMs: 5000,
82
+ message: message ?? "Connection refused",
83
+ checkedAt: new Date(),
84
+ };
85
+ }
86
+
87
+ // --- Tests ---
88
+
89
+ describe("buildSystemParam health annotations", () => {
90
+ beforeEach(() => {
91
+ resetMockEntries();
92
+ });
93
+
94
+ test("unhealthy source is annotated with UNAVAILABLE", () => {
95
+ mockEntries.push(
96
+ { id: "default", dbType: "postgres", health: makeHealthy() },
97
+ { id: "warehouse", dbType: "postgres", health: makeUnhealthy() },
98
+ );
99
+
100
+ const content = getContent(buildSystemParam("openai"));
101
+
102
+ expect(content).toContain("UNAVAILABLE");
103
+ expect(content).toContain("warehouse");
104
+ expect(content).toContain("skip queries to this source");
105
+ });
106
+
107
+ test("degraded source is annotated with 'currently degraded'", () => {
108
+ mockEntries.push(
109
+ { id: "default", dbType: "postgres", health: makeHealthy() },
110
+ { id: "warehouse", dbType: "postgres", health: makeDegraded() },
111
+ );
112
+
113
+ const content = getContent(buildSystemParam("openai"));
114
+
115
+ expect(content).toContain("currently degraded");
116
+ expect(content).toContain("queries may fail");
117
+ });
118
+
119
+ test("healthy source has no health annotation", () => {
120
+ mockEntries.push(
121
+ { id: "default", dbType: "postgres", health: makeHealthy() },
122
+ { id: "warehouse", dbType: "postgres", health: makeHealthy() },
123
+ );
124
+
125
+ const content = getContent(buildSystemParam("openai"));
126
+
127
+ expect(content).toContain("**default** (PostgreSQL)");
128
+ expect(content).toContain("**warehouse** (PostgreSQL)");
129
+ expect(content).not.toContain("UNAVAILABLE");
130
+ expect(content).not.toContain("currently degraded");
131
+ });
132
+
133
+ test("only the unhealthy source gets the UNAVAILABLE annotation, not healthy ones", () => {
134
+ mockEntries.push(
135
+ { id: "default", dbType: "postgres", health: makeHealthy() },
136
+ { id: "broken", dbType: "mysql", health: makeUnhealthy("Connection refused") },
137
+ );
138
+
139
+ const content = getContent(buildSystemParam("openai"));
140
+
141
+ // The "broken" source should have the annotation
142
+ expect(content).toContain("**broken** (MySQL)");
143
+ expect(content).toContain("UNAVAILABLE");
144
+
145
+ // The "default" line should NOT have the annotation
146
+ const defaultLine = content.split("\n").find((l: string) => l.includes("**default**"));
147
+ expect(defaultLine).toBeDefined();
148
+ expect(defaultLine!).not.toContain("UNAVAILABLE");
149
+ expect(defaultLine!).not.toContain("degraded");
150
+ });
151
+
152
+ test("source without health field has no annotation", () => {
153
+ mockEntries.push(
154
+ { id: "default", dbType: "postgres" },
155
+ { id: "warehouse", dbType: "postgres" },
156
+ );
157
+
158
+ const content = getContent(buildSystemParam("openai"));
159
+
160
+ expect(content).toContain("Available Data Sources");
161
+ expect(content).not.toContain("UNAVAILABLE");
162
+ expect(content).not.toContain("currently degraded");
163
+ });
164
+ });
@@ -0,0 +1,514 @@
1
+ /**
2
+ * Integration tests for the agent loop (runAgent).
3
+ *
4
+ * Exercises the full streamText → tool execution → multi-step flow with a
5
+ * MockLanguageModelV3 that returns pre-configured tool calls per step.
6
+ *
7
+ * All external dependencies are mocked:
8
+ * - LLM provider → MockLanguageModelV3 from ai/test
9
+ * - Database → canned query results
10
+ * - Semantic layer → fixed whitelist
11
+ * - Shell backend (just-bash) → canned command outputs
12
+ */
13
+
14
+ import { describe, expect, it, beforeEach, mock } from "bun:test";
15
+ import {
16
+ MockLanguageModelV3,
17
+ convertArrayToReadableStream,
18
+ } from "ai/test";
19
+ import type { LanguageModelV3StreamPart } from "@ai-sdk/provider";
20
+ import type { UIMessage } from "ai";
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Environment — must be set before module imports
24
+ // ---------------------------------------------------------------------------
25
+
26
+ process.env.ATLAS_DATASOURCE_URL = "postgresql://test:test@localhost:5432/test";
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Module-level mock model — each test assigns its own MockLanguageModelV3
30
+ // ---------------------------------------------------------------------------
31
+
32
+ let mockModel: InstanceType<typeof MockLanguageModelV3>;
33
+
34
+ mock.module("@atlas/api/lib/providers", () => ({
35
+ getModel: () => mockModel,
36
+ getProviderType: () => "anthropic" as const,
37
+ }));
38
+
39
+ mock.module("@atlas/api/lib/semantic", () => ({
40
+ getWhitelistedTables: () => new Set(["companies", "people"]),
41
+ _resetWhitelists: () => {},
42
+ getCrossSourceJoins: () => [],
43
+ }));
44
+
45
+ let mockDBQuery: (sql: string, timeout?: number) => Promise<{ columns: string[]; rows: Record<string, unknown>[] }> =
46
+ async () => ({ columns: ["id", "name"], rows: [{ id: 1, name: "Acme" }] });
47
+
48
+ const mockDBConnectionObj = {
49
+ query: (...args: [string, number?]) => mockDBQuery(...args),
50
+ close: async () => {},
51
+ };
52
+ mock.module("@atlas/api/lib/db/connection", () => ({
53
+ getDB: () => mockDBConnectionObj,
54
+ connections: {
55
+ get: () => mockDBConnectionObj,
56
+ getDefault: () => mockDBConnectionObj,
57
+ getDBType: () => "postgres" as const,
58
+ getTargetHost: () => "localhost:5432",
59
+ getValidator: () => undefined,
60
+ list: () => ["default"],
61
+ describe: () => [{ id: "default", dbType: "postgres" as const }],
62
+ },
63
+ detectDBType: () => "postgres" as const,
64
+ }));
65
+
66
+ // Mock just-bash to avoid OverlayFs/filesystem dependency in CI.
67
+ // The mock Bash.exec() returns canned stdout for known commands and
68
+ // simulates path-traversal rejection for `../` patterns.
69
+ mock.module("just-bash", () => ({
70
+ Bash: class MockBash {
71
+ constructor(_: unknown) {}
72
+ async exec(command: string) {
73
+ if (command.includes("../")) {
74
+ return {
75
+ stdout: "",
76
+ stderr: `cat: ${command.split(" ").pop()}: No such file or directory`,
77
+ exitCode: 1,
78
+ };
79
+ }
80
+ if (/^ls(\s|$)/.test(command)) {
81
+ return {
82
+ stdout: "catalog.yml\nentities/\nmetrics/\nglossary.yml\n",
83
+ stderr: "",
84
+ exitCode: 0,
85
+ };
86
+ }
87
+ if (command.includes("cat catalog.yml")) {
88
+ return {
89
+ stdout: "entities:\n - companies\n - people\n",
90
+ stderr: "",
91
+ exitCode: 0,
92
+ };
93
+ }
94
+ throw new Error(`MockBash: unhandled command "${command}". Add a case to the mock.`);
95
+ }
96
+ },
97
+ OverlayFs: class MockOverlayFs {
98
+ constructor(_: unknown) {}
99
+ },
100
+ }));
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Imports — after mocks so modules resolve to mocked versions
104
+ // ---------------------------------------------------------------------------
105
+
106
+ const { runAgent } = await import("@atlas/api/lib/agent");
107
+ const { invalidateExploreBackend } = await import("@atlas/api/lib/tools/explore");
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // Helpers
111
+ // ---------------------------------------------------------------------------
112
+
113
+ /** Subset of executeSQL tool output fields used in assertions. */
114
+ interface SQLOutput {
115
+ success: boolean;
116
+ error?: string;
117
+ row_count?: number;
118
+ columns?: string[];
119
+ rows?: Record<string, unknown>[];
120
+ }
121
+
122
+ let callId = 0;
123
+ function nextId(): string {
124
+ return `call-${++callId}`;
125
+ }
126
+
127
+ /** V3-format usage object for finish chunks. */
128
+ const MOCK_USAGE = {
129
+ inputTokens: { total: 10, noCache: 10, cacheRead: undefined, cacheWrite: undefined },
130
+ outputTokens: { total: 20, text: 20, reasoning: undefined },
131
+ };
132
+
133
+ /** Build stream chunks for a single tool call step. */
134
+ function makeToolStepChunks(
135
+ toolName: string,
136
+ args: Record<string, unknown>,
137
+ ): LanguageModelV3StreamPart[] {
138
+ return [
139
+ { type: "tool-call", toolCallId: nextId(), toolName, input: JSON.stringify(args) },
140
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "tool-calls", raw: "tool_use" } },
141
+ ];
142
+ }
143
+
144
+ /** Build a simple user message array for runAgent. */
145
+ function userMessages(content: string): UIMessage[] {
146
+ return [
147
+ {
148
+ id: "msg-1",
149
+ role: "user" as const,
150
+ parts: [{ type: "text" as const, text: content }],
151
+ },
152
+ ];
153
+ }
154
+
155
+ /**
156
+ * Find all tool result outputs for a given tool name across all steps.
157
+ *
158
+ * The agent may place tool calls and their results in different steps,
159
+ * or insert text-only steps between them, so hard-coding step indices
160
+ * like `steps[0].toolResults[0]` is fragile. This helper searches
161
+ * every step's `toolResults` array by `toolName` and returns all
162
+ * matching `.output` values in order.
163
+ */
164
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
165
+ function findToolResults(steps: any[], toolName: string): any[] {
166
+ const results: unknown[] = [];
167
+ for (const step of steps) {
168
+ if (!step.toolResults) continue;
169
+ for (const tr of step.toolResults) {
170
+ if (tr.toolName === toolName) {
171
+ results.push(tr.output);
172
+ }
173
+ }
174
+ }
175
+ return results;
176
+ }
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Tests
180
+ // ---------------------------------------------------------------------------
181
+
182
+ describe("agent integration", () => {
183
+ beforeEach(() => {
184
+ callId = 0;
185
+ invalidateExploreBackend();
186
+ process.env.ATLAS_DATASOURCE_URL = "postgresql://test:test@localhost:5432/test";
187
+ delete process.env.ATLAS_TABLE_WHITELIST;
188
+ mockDBQuery = async () => ({ columns: ["id", "name"], rows: [{ id: 1, name: "Acme" }] });
189
+ });
190
+
191
+ // -----------------------------------------------------------------------
192
+ // 1. Happy path — explore → executeSQL
193
+ // -----------------------------------------------------------------------
194
+
195
+ it("happy path — explore → executeSQL", async () => {
196
+ let streamIdx = 0;
197
+ mockModel = new MockLanguageModelV3({
198
+ doStream: async () => {
199
+ const allSteps: LanguageModelV3StreamPart[][] = [
200
+ makeToolStepChunks("explore", { command: "cat catalog.yml" }),
201
+ makeToolStepChunks("executeSQL", { sql: "SELECT id, name FROM companies LIMIT 10", explanation: "List companies" }),
202
+ // Final step: text-only response (model stops calling tools → loop ends)
203
+ [
204
+ { type: "text-delta", id: "text-0", delta: "Found 1 company: Acme." },
205
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "stop", raw: "end_turn" } },
206
+ ],
207
+ ];
208
+ if (streamIdx >= allSteps.length) {
209
+ return { stream: convertArrayToReadableStream(allSteps[allSteps.length - 1]) };
210
+ }
211
+ return { stream: convertArrayToReadableStream(allSteps[streamIdx++]) };
212
+ },
213
+ });
214
+
215
+ const result = await runAgent({ messages: userMessages("Show me all companies") });
216
+ const steps = await result.steps;
217
+
218
+ // Loop terminated naturally after 3 steps (explore + executeSQL + text-only)
219
+ expect(steps.length).toBe(3);
220
+
221
+ // Both tools were called
222
+ const exploreResults = findToolResults(steps, "explore");
223
+ const sqlResults = findToolResults(steps, "executeSQL");
224
+
225
+ expect(exploreResults.length).toBe(1);
226
+ expect(sqlResults.length).toBe(1);
227
+
228
+ // executeSQL succeeded
229
+ expect(sqlResults[0]).toMatchObject({
230
+ success: true,
231
+ row_count: 1,
232
+ });
233
+ });
234
+
235
+ // -----------------------------------------------------------------------
236
+ // 2. SQL error recovery — bad table → retry → success
237
+ // -----------------------------------------------------------------------
238
+
239
+ it("SQL error recovery — bad table → retry → success", async () => {
240
+ let streamIdx = 0;
241
+ mockModel = new MockLanguageModelV3({
242
+ doStream: async () => {
243
+ const allSteps: LanguageModelV3StreamPart[][] = [
244
+ makeToolStepChunks("executeSQL", { sql: "SELECT * FROM nonexistent_table", explanation: "Try bad table" }),
245
+ makeToolStepChunks("executeSQL", { sql: "SELECT id FROM companies", explanation: "Retry with correct table" }),
246
+ [
247
+ { type: "text-delta", id: "text-0", delta: "Found data." },
248
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "stop", raw: "end_turn" } },
249
+ ],
250
+ ];
251
+ if (streamIdx >= allSteps.length) {
252
+ return { stream: convertArrayToReadableStream(allSteps[allSteps.length - 1]) };
253
+ }
254
+ return { stream: convertArrayToReadableStream(allSteps[streamIdx++]) };
255
+ },
256
+ });
257
+
258
+ const result = await runAgent({ messages: userMessages("List companies") });
259
+ const steps = await result.steps;
260
+
261
+ const sqlResults = findToolResults(steps, "executeSQL") as SQLOutput[];
262
+ expect(sqlResults.length).toBe(2);
263
+
264
+ // First SQL call returns error (whitelist rejection)
265
+ expect(sqlResults[0].success).toBe(false);
266
+ expect(sqlResults[0].error).toContain("not in the allowed list");
267
+
268
+ // Second SQL call succeeds
269
+ expect(sqlResults[1].success).toBe(true);
270
+ expect(sqlResults[1].row_count).toBe(1);
271
+ });
272
+
273
+ // -----------------------------------------------------------------------
274
+ // 3. Step limit enforcement (25 steps)
275
+ // -----------------------------------------------------------------------
276
+
277
+ it("step limit enforcement — stops after 25 steps", async () => {
278
+ // Model always returns explore ls — never stops calling tools
279
+ mockModel = new MockLanguageModelV3({
280
+ doStream: async () => ({
281
+ stream: convertArrayToReadableStream(
282
+ makeToolStepChunks("explore", { command: "ls" }),
283
+ ),
284
+ }),
285
+ });
286
+
287
+ const result = await runAgent({
288
+ messages: userMessages("What data do you have?"),
289
+ });
290
+ const steps = await result.steps;
291
+
292
+ // stopWhen: stepCountIs(25) should cap the loop
293
+ expect(steps.length).toBe(25);
294
+ });
295
+
296
+ // -----------------------------------------------------------------------
297
+ // 4. Explore path traversal rejection
298
+ // -----------------------------------------------------------------------
299
+
300
+ it("explore rejects path traversal (../../.env)", async () => {
301
+ let streamIdx = 0;
302
+ mockModel = new MockLanguageModelV3({
303
+ doStream: async () => {
304
+ const allSteps: LanguageModelV3StreamPart[][] = [
305
+ makeToolStepChunks("explore", { command: "cat ../../.env" }),
306
+ [
307
+ { type: "text-delta", id: "text-0", delta: "Could not access file." },
308
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "stop", raw: "end_turn" } },
309
+ ],
310
+ ];
311
+ if (streamIdx >= allSteps.length) {
312
+ return { stream: convertArrayToReadableStream(allSteps[allSteps.length - 1]) };
313
+ }
314
+ return { stream: convertArrayToReadableStream(allSteps[streamIdx++]) };
315
+ },
316
+ });
317
+
318
+ const result = await runAgent({ messages: userMessages("Read .env") });
319
+ const steps = await result.steps;
320
+
321
+ // NOTE: Protection comes from OverlayFs in production; here we test that
322
+ // the explore tool surfaces backend errors correctly (not file contents).
323
+ const exploreResults = findToolResults(steps, "explore");
324
+ expect(exploreResults.length).toBe(1);
325
+ const exploreResult = exploreResults[0];
326
+ expect(typeof exploreResult).toBe("string");
327
+ expect(exploreResult as string).toInclude("Error (exit 1)");
328
+ expect(exploreResult as string).toInclude("No such file or directory");
329
+ expect(exploreResult as string).not.toInclude("DATABASE_URL");
330
+ expect(exploreResult as string).not.toInclude("ATLAS_DATASOURCE_URL");
331
+ });
332
+
333
+ // -----------------------------------------------------------------------
334
+ // 5. SQL whitelist enforcement
335
+ // -----------------------------------------------------------------------
336
+
337
+ it("SQL whitelist blocks queries on non-whitelisted tables", async () => {
338
+ let streamIdx = 0;
339
+ mockModel = new MockLanguageModelV3({
340
+ doStream: async () => {
341
+ const allSteps: LanguageModelV3StreamPart[][] = [
342
+ makeToolStepChunks("executeSQL", { sql: "SELECT * FROM secret_table", explanation: "Access secret" }),
343
+ [
344
+ { type: "text-delta", id: "text-0", delta: "Table not available." },
345
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "stop", raw: "end_turn" } },
346
+ ],
347
+ ];
348
+ if (streamIdx >= allSteps.length) {
349
+ return { stream: convertArrayToReadableStream(allSteps[allSteps.length - 1]) };
350
+ }
351
+ return { stream: convertArrayToReadableStream(allSteps[streamIdx++]) };
352
+ },
353
+ });
354
+
355
+ const result = await runAgent({ messages: userMessages("Show secret data") });
356
+ const steps = await result.steps;
357
+
358
+ const sqlResults = findToolResults(steps, "executeSQL") as SQLOutput[];
359
+ expect(sqlResults.length).toBe(1);
360
+ expect(sqlResults[0].success).toBe(false);
361
+ expect(sqlResults[0].error).toContain("not in the allowed list");
362
+ });
363
+
364
+ // -----------------------------------------------------------------------
365
+ // 6. Empty / malicious SQL rejection
366
+ // -----------------------------------------------------------------------
367
+
368
+ it("rejects DDL (DROP TABLE) and empty SQL", async () => {
369
+ let streamIdx = 0;
370
+ mockModel = new MockLanguageModelV3({
371
+ doStream: async () => {
372
+ const allSteps: LanguageModelV3StreamPart[][] = [
373
+ makeToolStepChunks("executeSQL", { sql: "DROP TABLE companies", explanation: "Drop attempt" }),
374
+ makeToolStepChunks("executeSQL", { sql: "", explanation: "Empty query" }),
375
+ [
376
+ { type: "text-delta", id: "text-0", delta: "Blocked." },
377
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "stop", raw: "end_turn" } },
378
+ ],
379
+ ];
380
+ if (streamIdx >= allSteps.length) {
381
+ return { stream: convertArrayToReadableStream(allSteps[allSteps.length - 1]) };
382
+ }
383
+ return { stream: convertArrayToReadableStream(allSteps[streamIdx++]) };
384
+ },
385
+ });
386
+
387
+ const result = await runAgent({
388
+ messages: userMessages("Do something bad"),
389
+ });
390
+ const steps = await result.steps;
391
+
392
+ // The agent may insert text-only steps between tool calls, so search
393
+ // across all steps by tool name instead of hard-coding step indices.
394
+ const sqlResults = findToolResults(steps, "executeSQL") as SQLOutput[];
395
+ expect(sqlResults.length).toBeGreaterThanOrEqual(2);
396
+
397
+ // DDL blocked by regex guard
398
+ expect(sqlResults[0].success).toBe(false);
399
+ expect(sqlResults[0].error).toContain("Forbidden");
400
+
401
+ // Empty SQL blocked
402
+ expect(sqlResults[1].success).toBe(false);
403
+ expect(sqlResults[1].error).toContain("Empty");
404
+ });
405
+
406
+ // -----------------------------------------------------------------------
407
+ // 7. Database error — sensitive errors are redacted
408
+ // -----------------------------------------------------------------------
409
+
410
+ it("database error — sensitive errors are redacted", async () => {
411
+ // Override mock DB to throw a sensitive error (matches the "connection string" pattern)
412
+ mockDBQuery = async () => {
413
+ throw new Error("invalid connection string: postgresql://admin:secret@db.example.com:5432/prod");
414
+ };
415
+
416
+ let streamIdx = 0;
417
+ mockModel = new MockLanguageModelV3({
418
+ doStream: async () => {
419
+ const allSteps: LanguageModelV3StreamPart[][] = [
420
+ makeToolStepChunks("executeSQL", { sql: "SELECT id FROM companies", explanation: "Test query" }),
421
+ [
422
+ { type: "text-delta", id: "text-0", delta: "Failed." },
423
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "stop", raw: "end_turn" } },
424
+ ],
425
+ ];
426
+ if (streamIdx >= allSteps.length) {
427
+ return { stream: convertArrayToReadableStream(allSteps[allSteps.length - 1]) };
428
+ }
429
+ return { stream: convertArrayToReadableStream(allSteps[streamIdx++]) };
430
+ },
431
+ });
432
+
433
+ const result = await runAgent({ messages: userMessages("Test") });
434
+ const steps = await result.steps;
435
+ const sqlResults = findToolResults(steps, "executeSQL") as SQLOutput[];
436
+ expect(sqlResults.length).toBe(1);
437
+ expect(sqlResults[0].success).toBe(false);
438
+ expect(sqlResults[0].error).toContain("Database query failed");
439
+ // Must NOT leak the connection string
440
+ expect(sqlResults[0].error).not.toContain("db.example.com");
441
+ });
442
+
443
+ // -----------------------------------------------------------------------
444
+ // 8. Database error — non-sensitive errors surface detail for self-correction
445
+ // -----------------------------------------------------------------------
446
+
447
+ it("database error — non-sensitive errors surface hint and position for self-correction", async () => {
448
+ mockDBQuery = async () => {
449
+ const err = new Error('column "nonexistent" does not exist') as Error & { hint?: string; position?: string };
450
+ err.hint = 'Perhaps you meant to reference the column "name".';
451
+ err.position = "8";
452
+ throw err;
453
+ };
454
+
455
+ let streamIdx = 0;
456
+ mockModel = new MockLanguageModelV3({
457
+ doStream: async () => {
458
+ const allSteps: LanguageModelV3StreamPart[][] = [
459
+ makeToolStepChunks("executeSQL", { sql: "SELECT nonexistent FROM companies", explanation: "Bad column" }),
460
+ [
461
+ { type: "text-delta", id: "text-0", delta: "Failed." },
462
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "stop", raw: "end_turn" } },
463
+ ],
464
+ ];
465
+ if (streamIdx >= allSteps.length) {
466
+ return { stream: convertArrayToReadableStream(allSteps[allSteps.length - 1]) };
467
+ }
468
+ return { stream: convertArrayToReadableStream(allSteps[streamIdx++]) };
469
+ },
470
+ });
471
+
472
+ const result = await runAgent({ messages: userMessages("Test") });
473
+ const steps = await result.steps;
474
+ const sqlResults = findToolResults(steps, "executeSQL") as SQLOutput[];
475
+ expect(sqlResults.length).toBe(1);
476
+ expect(sqlResults[0].success).toBe(false);
477
+ expect(sqlResults[0].error).toContain("does not exist");
478
+ expect(sqlResults[0].error).toContain("Hint:");
479
+ expect(sqlResults[0].error).toContain("at character 8");
480
+ });
481
+
482
+ // -----------------------------------------------------------------------
483
+ // 9. Auto-LIMIT injection — verify LIMIT is appended
484
+ // -----------------------------------------------------------------------
485
+
486
+ it("auto-LIMIT — LIMIT is appended to queries without one", async () => {
487
+ let capturedSQL = "";
488
+ mockDBQuery = async (sql: string) => {
489
+ capturedSQL = sql;
490
+ return { columns: ["id"], rows: [{ id: 1 }] };
491
+ };
492
+
493
+ let streamIdx = 0;
494
+ mockModel = new MockLanguageModelV3({
495
+ doStream: async () => {
496
+ const allSteps: LanguageModelV3StreamPart[][] = [
497
+ makeToolStepChunks("executeSQL", { sql: "SELECT id FROM companies", explanation: "No limit" }),
498
+ [
499
+ { type: "text-delta", id: "text-0", delta: "Done." },
500
+ { type: "finish", usage: MOCK_USAGE, finishReason: { unified: "stop", raw: "end_turn" } },
501
+ ],
502
+ ];
503
+ if (streamIdx >= allSteps.length) {
504
+ return { stream: convertArrayToReadableStream(allSteps[allSteps.length - 1]) };
505
+ }
506
+ return { stream: convertArrayToReadableStream(allSteps[streamIdx++]) };
507
+ },
508
+ });
509
+
510
+ const result = await runAgent({ messages: userMessages("Test") });
511
+ await result.steps;
512
+ expect(capturedSQL).toContain("LIMIT 1000");
513
+ });
514
+ });