@app-vitals/agent-admin 0.0.1 → 0.1.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 (325) hide show
  1. package/Dockerfile +43 -0
  2. package/README.md +72 -2
  3. package/e2e/agents-page.e2e.ts +338 -0
  4. package/e2e/chat-degraded.e2e.ts +153 -0
  5. package/e2e/chat-page.e2e.ts +169 -0
  6. package/e2e/chat-progress.e2e.ts +206 -0
  7. package/e2e/chat-thread-page.e2e.ts +444 -0
  8. package/e2e/login-page.e2e.ts +148 -0
  9. package/e2e/test-server.ts +427 -0
  10. package/openapi.json +6098 -0
  11. package/openapi.platform.json +5553 -0
  12. package/package.json +62 -4
  13. package/playwright.config.ts +25 -0
  14. package/prisma/client/browser.ts +134 -0
  15. package/prisma/client/client.ts +158 -0
  16. package/prisma/client/commonInputTypes.ts +578 -0
  17. package/prisma/client/enums.ts +15 -0
  18. package/prisma/client/internal/class.ts +424 -0
  19. package/prisma/client/internal/prismaNamespace.ts +2898 -0
  20. package/prisma/client/internal/prismaNamespaceBrowser.ts +476 -0
  21. package/prisma/client/models/Account.ts +1774 -0
  22. package/prisma/client/models/AccountInvite.ts +1386 -0
  23. package/prisma/client/models/AccountMember.ts +1351 -0
  24. package/prisma/client/models/Agent.ts +4554 -0
  25. package/prisma/client/models/AgentAsk.ts +1373 -0
  26. package/prisma/client/models/AgentChatTokenUsageDailyByModel.ts +1638 -0
  27. package/prisma/client/models/AgentCronJob.ts +2416 -0
  28. package/prisma/client/models/AgentCronRun.ts +2858 -0
  29. package/prisma/client/models/AgentCronRunModelBreakdown.ts +1532 -0
  30. package/prisma/client/models/AgentCronRunSkillUsage.ts +1642 -0
  31. package/prisma/client/models/AgentEnv.ts +1421 -0
  32. package/prisma/client/models/AgentGitHubInstallationsSnapshot.ts +1289 -0
  33. package/prisma/client/models/AgentMember.ts +1316 -0
  34. package/prisma/client/models/AgentPhaseMethodology.ts +1355 -0
  35. package/prisma/client/models/AgentPlugin.ts +1421 -0
  36. package/prisma/client/models/AgentToken.ts +1380 -0
  37. package/prisma/client/models/AgentTool.ts +1351 -0
  38. package/prisma/client/models/AgentWorkQueueSnapshot.ts +1297 -0
  39. package/prisma/client/models/ChatThreadWatch.ts +1183 -0
  40. package/prisma/client/models/PushSubscription.ts +1233 -0
  41. package/prisma/client/models/SessionAlertState.ts +1187 -0
  42. package/prisma/client/models/SessionFollow.ts +1187 -0
  43. package/prisma/client/models/UserNotificationPrefs.ts +1221 -0
  44. package/prisma/client/models.ts +34 -0
  45. package/prisma/migrations/20260608000000_init/migration.sql +112 -0
  46. package/prisma/migrations/20260616000000_agent_members/migration.sql +18 -0
  47. package/prisma/migrations/20260624000000_add_agent_self_hosted/migration.sql +2 -0
  48. package/prisma/migrations/20260624120000_add_agent_repos/migration.sql +3 -0
  49. package/prisma/migrations/20260625000000_add_agent_cron_run/migration.sql +26 -0
  50. package/prisma/migrations/20260626000000_add_cron_run_token_fields/migration.sql +10 -0
  51. package/prisma/migrations/20260627000000_add_agent_chat_token_usage_daily/migration.sql +18 -0
  52. package/prisma/migrations/20260627010000_add_agent_chat_token_usage_fk/migration.sql +2 -0
  53. package/prisma/migrations/20260701000000_add_chat_threads_messages/migration.sql +37 -0
  54. package/prisma/migrations/20260701010000_drop_chat_threads_messages/migration.sql +17 -0
  55. package/prisma/migrations/20260701020000_add_cron_run_model_breakdown/migration.sql +23 -0
  56. package/prisma/migrations/20260701020000_chat_token_by_model/migration.sql +25 -0
  57. package/prisma/migrations/20260701030000_add_secret_flag_to_agent_env/migration.sql +2 -0
  58. package/prisma/migrations/20260701030000_drop_cron_run_legacy_columns/migration.sql +5 -0
  59. package/prisma/migrations/20260706000000_consolidate_cron_run_token_accounting/migration.sql +54 -0
  60. package/prisma/migrations/20260709000000_add_phase_to_agent_cron_run/migration.sql +8 -0
  61. package/prisma/migrations/20260717000000_add_item_tracking_to_agent_cron_run/migration.sql +10 -0
  62. package/prisma/migrations/20260717010000_add_agent_work_queue_snapshot/migration.sql +20 -0
  63. package/prisma/migrations/20260718000000_add_cron_phase_columns/migration.sql +25 -0
  64. package/prisma/migrations/20260718100000_drop_agent_cron_run_phase_column/migration.sql +8 -0
  65. package/prisma/migrations/20260724000000_add_agent_type_name/migration.sql +3 -0
  66. package/prisma/migrations/20260725232433_add_agent_author_allowlist/migration.sql +2 -0
  67. package/prisma/migrations/20260727120000_add_not_null_to_agent_author_allowlist/migration.sql +3 -0
  68. package/prisma/migrations/20260731000000_add_session_id_to_agent_cron_run/migration.sql +2 -0
  69. package/prisma/migrations/20260821000000_add_agent_restrict_slack_to_members/migration.sql +3 -0
  70. package/prisma/migrations/20260901000000_add_push_subscription_and_chat_thread_watch/migration.sql +40 -0
  71. package/prisma/migrations/20260903000000_add_agent_patch_author_allowlist/migration.sql +2 -0
  72. package/prisma/migrations/20260903120000_add_agent_review_author_allowlist/migration.sql +18 -0
  73. package/prisma/migrations/20260903150000_drop_agent_author_allowlist/migration.sql +14 -0
  74. package/prisma/migrations/20260905000000_add_started_at_index_to_agent_cron_run/migration.sql +11 -0
  75. package/prisma/migrations/20260906000000_add_session_follow_notification_prefs_alert_state/migration.sql +47 -0
  76. package/prisma/migrations/20260911000000_backfill_auto_follow_since/migration.sql +16 -0
  77. package/prisma/migrations/20260923115814_add_last_heartbeat_at_to_agent_cron_run/migration.sql +2 -0
  78. package/prisma/migrations/20260924120000_add_trial_expiry_fields_to_agent/migration.sql +6 -0
  79. package/prisma/migrations/20260929000000_add_agent_phase_methodology/migration.sql +21 -0
  80. package/prisma/migrations/20260929120000_add_agent_policy_fields/migration.sql +15 -0
  81. package/prisma/migrations/20261005000000_add_agent_github_installations_snapshot/migration.sql +21 -0
  82. package/prisma/migrations/20261008000000_add_cron_run_telemetry/migration.sql +50 -0
  83. package/prisma/migrations/20261008010000_add_account_models/migration.sql +48 -0
  84. package/prisma/migrations/20261008020000_add_cron_lockdown_disabled_at/migration.sql +3 -0
  85. package/prisma/migrations/20261009000000_chat_token_usage_bigint/migration.sql +7 -0
  86. package/prisma/migrations/20261010000000_add_cron_run_command_variant/migration.sql +2 -0
  87. package/prisma/migrations/20261019000000_add_agent_anthropic_identity/migration.sql +9 -0
  88. package/prisma/migrations/20261025000000_add_agent_ask/migration.sql +23 -0
  89. package/prisma/migrations/migration_lock.toml +3 -0
  90. package/prisma/schema/coding.prisma +85 -0
  91. package/prisma/schema/schema.prisma +506 -0
  92. package/prisma.config.ts +34 -0
  93. package/pwa-assets/icons/apple-touch-icon.png +0 -0
  94. package/pwa-assets/icons/favicon-32.png +0 -0
  95. package/pwa-assets/icons/icon-192.png +0 -0
  96. package/pwa-assets/icons/icon-512.png +0 -0
  97. package/pwa-assets/icons/icon-maskable-192.png +0 -0
  98. package/pwa-assets/icons/icon-maskable-512.png +0 -0
  99. package/src/account-agent-create.ts +42 -0
  100. package/src/account-agent-create.unit.test.ts +28 -0
  101. package/src/account-created-notifier.ts +71 -0
  102. package/src/account-created-notifier.unit.test.ts +70 -0
  103. package/src/account-email.ts +9 -0
  104. package/src/account-email.unit.test.ts +21 -0
  105. package/src/account-invites.ts +70 -0
  106. package/src/account-lifecycle.integration.test.ts +329 -0
  107. package/src/account-lifecycle.ts +206 -0
  108. package/src/account-lifecycle.unit.test.ts +108 -0
  109. package/src/account-members.ts +128 -0
  110. package/src/account-onboarding.integration.test.ts +101 -0
  111. package/src/account-onboarding.ts +95 -0
  112. package/src/account-trial-expiry-sweeper.ts +85 -0
  113. package/src/account-trial-warning-sweeper.integration.test.ts +228 -0
  114. package/src/account-trial-warning-sweeper.ts +216 -0
  115. package/src/accounts-api.integration.test.ts +196 -0
  116. package/src/accounts-api.smoke.test.ts +292 -0
  117. package/src/accounts-api.ts +289 -0
  118. package/src/accounts-api.unit.test.ts +93 -0
  119. package/src/accounts.integration.test.ts +178 -0
  120. package/src/accounts.ts +208 -0
  121. package/src/admin-boot-config.ts +574 -0
  122. package/src/admin-boot-config.unit.test.ts +28 -0
  123. package/src/admin-boot.boot-order.unit.test.ts +91 -0
  124. package/src/admin-boot.ts +690 -0
  125. package/src/admin-dev-login.smoke.test.ts +317 -0
  126. package/src/admin-extension.ts +432 -0
  127. package/src/admin-extension.unit.test.ts +532 -0
  128. package/src/admin-local-agent.smoke.test.ts +1643 -0
  129. package/src/admin-spec.smoke.test.ts +96 -0
  130. package/src/admin-spec.ts +186 -0
  131. package/src/admin-ui-account-nav.ts +20 -0
  132. package/src/admin-ui-account-pages.ts +131 -0
  133. package/src/admin-ui-account-pages.unit.test.ts +112 -0
  134. package/src/admin-ui-account.smoke.test.ts +286 -0
  135. package/src/admin-ui-account.ts +185 -0
  136. package/src/admin-ui-accounts-pages.ts +202 -0
  137. package/src/admin-ui-accounts-pages.unit.test.ts +138 -0
  138. package/src/admin-ui-accounts.smoke.test.ts +434 -0
  139. package/src/admin-ui-accounts.ts +266 -0
  140. package/src/admin-ui-layout.ts +60 -0
  141. package/src/admin-ui-layout.unit.test.ts +140 -0
  142. package/src/admin-ui-pages.ts +3489 -0
  143. package/src/admin-ui-pages.unit.test.ts +4355 -0
  144. package/src/admin-ui-push-test.smoke.test.ts +125 -0
  145. package/src/admin-ui-push-test.ts +102 -0
  146. package/src/admin-ui-styles.ts +656 -0
  147. package/src/admin-ui-styles.unit.test.ts +103 -0
  148. package/src/admin-ui.fixtures.ts +457 -0
  149. package/src/admin-ui.smoke.test.ts +7227 -0
  150. package/src/admin-ui.ts +3824 -0
  151. package/src/agent-account-assignment.ts +49 -0
  152. package/src/agent-account-assignment.unit.test.ts +99 -0
  153. package/src/agent-anthropic-identity.integration.test.ts +99 -0
  154. package/src/agent-anthropic-identity.ts +78 -0
  155. package/src/agent-anthropic-identity.unit.test.ts +77 -0
  156. package/src/agent-ask-config.ts +59 -0
  157. package/src/agent-ask-config.unit.test.ts +70 -0
  158. package/src/agent-ask.integration.test.ts +175 -0
  159. package/src/agent-ask.smoke.test.ts +457 -0
  160. package/src/agent-ask.ts +378 -0
  161. package/src/agent-chat-tokens.integration.test.ts +420 -0
  162. package/src/agent-chat-tokens.smoke.test.ts +551 -0
  163. package/src/agent-chat-tokens.ts +259 -0
  164. package/src/agent-cron-jobs.integration.test.ts +582 -0
  165. package/src/agent-cron-jobs.ts +615 -0
  166. package/src/agent-cron-jobs.unit.test.ts +420 -0
  167. package/src/agent-cron-run-stats.integration.test.ts +1551 -0
  168. package/src/agent-cron-run-stats.smoke.test.ts +953 -0
  169. package/src/agent-cron-run-stats.ts +752 -0
  170. package/src/agent-cron-runs.integration.test.ts +1491 -0
  171. package/src/agent-cron-runs.ts +489 -0
  172. package/src/agent-deletion-checklist.ts +128 -0
  173. package/src/agent-deletion-checklist.unit.test.ts +201 -0
  174. package/src/agent-deletion-per-agent.integration.test.ts +323 -0
  175. package/src/agent-deletion.integration.test.ts +323 -0
  176. package/src/agent-deletion.ts +329 -0
  177. package/src/agent-deletion.unit.test.ts +412 -0
  178. package/src/agent-envs.integration.test.ts +323 -0
  179. package/src/agent-envs.ts +244 -0
  180. package/src/agent-github-installations.integration.test.ts +115 -0
  181. package/src/agent-github-installations.ts +52 -0
  182. package/src/agent-manifest.ts +685 -0
  183. package/src/agent-manifest.unit.test.ts +870 -0
  184. package/src/agent-members.ts +69 -0
  185. package/src/agent-members.unit.test.ts +250 -0
  186. package/src/agent-patch-author-allowlist.integration.test.ts +218 -0
  187. package/src/agent-plugins.integration.test.ts +157 -0
  188. package/src/agent-plugins.ts +95 -0
  189. package/src/agent-plugins.unit.test.ts +43 -0
  190. package/src/agent-provisioner-per-agent.integration.test.ts +949 -0
  191. package/src/agent-provisioner.integration.test.ts +1245 -0
  192. package/src/agent-provisioner.ts +1650 -0
  193. package/src/agent-provisioner.unit.test.ts +1852 -0
  194. package/src/agent-tokens.integration.test.ts +150 -0
  195. package/src/agent-tokens.ts +112 -0
  196. package/src/agent-tools.integration.test.ts +129 -0
  197. package/src/agent-tools.ts +111 -0
  198. package/src/agents-api.integration.test.ts +705 -0
  199. package/src/agents-api.sentry.smoke.test.ts +478 -0
  200. package/src/agents-api.smoke.test.ts +5986 -0
  201. package/src/agents-api.ts +2826 -0
  202. package/src/agents.integration.test.ts +512 -0
  203. package/src/agents.ts +1097 -0
  204. package/src/agents.unit.test.ts +1736 -0
  205. package/src/anthropic-credential-guard.ts +44 -0
  206. package/src/anthropic-credential-guard.unit.test.ts +88 -0
  207. package/src/anthropic-federated-token-provider.integration.test.ts +248 -0
  208. package/src/anthropic-federated-token-provider.ts +249 -0
  209. package/src/anthropic-identity-client.fixtures.ts +237 -0
  210. package/src/anthropic-identity-client.integration.test.ts +342 -0
  211. package/src/anthropic-identity-client.ts +473 -0
  212. package/src/anthropic-identity-fixture-spec.integration.test.ts +169 -0
  213. package/src/anthropic-identity-name.integration.test.ts +83 -0
  214. package/src/anthropic-identity-name.ts +163 -0
  215. package/src/anthropic-identity-name.unit.test.ts +345 -0
  216. package/src/anthropic-openapi-contract.fixtures.ts +192 -0
  217. package/src/api-auth.ts +383 -0
  218. package/src/api-auth.unit.test.ts +837 -0
  219. package/src/api.smoke.test.ts +1225 -0
  220. package/src/api.ts +376 -0
  221. package/src/attachment-validation.ts +124 -0
  222. package/src/attachment-validation.unit.test.ts +218 -0
  223. package/src/caller-scope.ts +131 -0
  224. package/src/caller-scope.unit.test.ts +116 -0
  225. package/src/chat-credential-config.ts +22 -0
  226. package/src/chat-credentials-logging.unit.test.ts +92 -0
  227. package/src/chat-markers.ts +114 -0
  228. package/src/chat-markers.unit.test.ts +116 -0
  229. package/src/chat-service-provisioning-client.integration.test.ts +412 -0
  230. package/src/chat-service-provisioning-client.ts +221 -0
  231. package/src/chat.smoke.test.ts +1875 -0
  232. package/src/clock.ts +47 -0
  233. package/src/coding-task-store-provisioning-client.integration.test.ts +372 -0
  234. package/src/coding-task-store-provisioning-client.ts +156 -0
  235. package/src/crypto.ts +83 -0
  236. package/src/dev-auth-guard.ts +29 -0
  237. package/src/dev-auth-guard.unit.test.ts +51 -0
  238. package/src/errors.ts +77 -0
  239. package/src/errors.unit.test.ts +52 -0
  240. package/src/fixtures/anthropic-identity-recorded-fixtures.json +850 -0
  241. package/src/fixtures/anthropic-openapi/SDK_VERSION +1 -0
  242. package/src/fixtures/anthropic-openapi/mock-spec.json.gz +0 -0
  243. package/src/fixtures/chat-service-provisioning-cassette.json +156 -0
  244. package/src/fixtures/github-app-manifest-cassette.json +44 -0
  245. package/src/fixtures/google-auth-cassette.json +36 -0
  246. package/src/fixtures/http-chat-client-cassette.json +226 -0
  247. package/src/fixtures/k8s-cassette.json +470 -0
  248. package/src/fixtures/okta-auth-cassette.json +36 -0
  249. package/src/fixtures/slack-provision-cassette.json +12 -0
  250. package/src/fixtures/slack-provisioning-http-cassette.json +98 -0
  251. package/src/fixtures/task-store-provisioning-cassette.json +85 -0
  252. package/src/github-app-provisioning-client.integration.test.ts +149 -0
  253. package/src/github-app-provisioning-client.ts +161 -0
  254. package/src/github-app-provisioning-client.unit.test.ts +114 -0
  255. package/src/github-provisioning-service.ts +569 -0
  256. package/src/github-provisioning-service.unit.test.ts +123 -0
  257. package/src/google-auth-client.integration.test.ts +195 -0
  258. package/src/google-auth-client.ts +121 -0
  259. package/src/http-chat-client.integration.test.ts +554 -0
  260. package/src/http-chat-client.ts +533 -0
  261. package/src/http-chat-client.unit.test.ts +119 -0
  262. package/src/import-boundary.unit.test.ts +240 -0
  263. package/src/kubernetes-client.integration.test.ts +697 -0
  264. package/src/kubernetes-client.ts +1027 -0
  265. package/src/kubernetes-client.unit.test.ts +726 -0
  266. package/src/main.smoke.test.ts +91 -0
  267. package/src/main.ts +21 -0
  268. package/src/main.unit.test.ts +801 -0
  269. package/src/okta-auth-client.integration.test.ts +263 -0
  270. package/src/okta-auth-client.ts +152 -0
  271. package/src/openapi-schemas.ts +1310 -0
  272. package/src/openapi-schemas.unit.test.ts +348 -0
  273. package/src/per-agent-identity.fixtures.ts +305 -0
  274. package/src/platform-doc-route.ts +40 -0
  275. package/src/prisma-client.integration.test.ts +57 -0
  276. package/src/prisma-client.ts +187 -0
  277. package/src/prisma-client.unit.test.ts +178 -0
  278. package/src/prisma-tx.ts +23 -0
  279. package/src/push-content.ts +177 -0
  280. package/src/push-content.unit.test.ts +213 -0
  281. package/src/push-crypto.ts +135 -0
  282. package/src/push-crypto.unit.test.ts +66 -0
  283. package/src/push-sender.ts +169 -0
  284. package/src/push-sender.unit.test.ts +107 -0
  285. package/src/push-service.ts +332 -0
  286. package/src/push-service.unit.test.ts +679 -0
  287. package/src/push-toggle.ts +135 -0
  288. package/src/push-toggle.unit.test.ts +78 -0
  289. package/src/pwa-https-gating.smoke.test.ts +229 -0
  290. package/src/pwa-icons.unit.test.ts +41 -0
  291. package/src/pwa-routes.smoke.test.ts +276 -0
  292. package/src/pwa.ts +380 -0
  293. package/src/pwa.unit.test.ts +321 -0
  294. package/src/schema.unit.test.ts +122 -0
  295. package/src/self-serve-config.ts +52 -0
  296. package/src/self-serve-config.unit.test.ts +79 -0
  297. package/src/server.smoke.test.ts +273 -0
  298. package/src/server.ts +50 -0
  299. package/src/service-integration.ts +67 -0
  300. package/src/service-integration.unit.test.ts +129 -0
  301. package/src/slack-provisioning-client.integration.test.ts +308 -0
  302. package/src/slack-provisioning-client.ts +411 -0
  303. package/src/slack-provisioning-client.unit.test.ts +80 -0
  304. package/src/slack-provisioning-service.integration.test.ts +637 -0
  305. package/src/slack-provisioning-service.ts +451 -0
  306. package/src/slack-provisioning.integration.test.ts +395 -0
  307. package/src/specialty-manifest-loader.fixture.integration.test.ts +63 -0
  308. package/src/specialty-manifest-loader.ts +255 -0
  309. package/src/specialty-manifest-loader.unit.test.ts +301 -0
  310. package/src/specialty-registry.schema.unit.test.ts +35 -0
  311. package/src/specialty-registry.ts +236 -0
  312. package/src/specialty-registry.unit.test.ts +272 -0
  313. package/src/startup-policy-reconcile.ts +77 -0
  314. package/src/startup-policy-reconcile.unit.test.ts +251 -0
  315. package/src/theme-tokens.json +12 -0
  316. package/src/token-crypto.ts +60 -0
  317. package/src/token-crypto.unit.test.ts +182 -0
  318. package/src/trial-expiry-sweeper.integration.test.ts +318 -0
  319. package/src/trial-expiry-sweeper.ts +132 -0
  320. package/src/trial-expiry-sweeper.unit.test.ts +391 -0
  321. package/src/trial-expiry-warning-sweeper.integration.test.ts +468 -0
  322. package/src/trial-expiry-warning-sweeper.ts +447 -0
  323. package/src/trial-expiry-warning-sweeper.unit.test.ts +148 -0
  324. package/src/voice-contract.unit.test.ts +102 -0
  325. package/tsconfig.json +9 -0
@@ -0,0 +1,427 @@
1
+ /**
2
+ * admin/e2e/test-server.ts
3
+ * Admin UI — E2E Test Server (ADM-3.2)
4
+ *
5
+ * Starts a minimal Hono server backed by in-memory mock services so that
6
+ * Playwright tests can exercise the admin UI without a real database or
7
+ * Google OAuth credentials.
8
+ *
9
+ * Usage:
10
+ * ADMIN_E2E_PORT=3490 bun run admin/e2e/test-server.ts
11
+ * ADMIN_E2E_AGENTS_MODE=1 ADMIN_E2E_PORT=3490 bun run admin/e2e/test-server.ts
12
+ *
13
+ * The server exposes:
14
+ * GET /health → 200 OK
15
+ * GET /admin/login → login page
16
+ * GET /admin/agents → agents list (requires admin_session cookie)
17
+ * GET /admin/agents/:id → agent detail (requires admin_session cookie)
18
+ */
19
+
20
+ import { Hono } from "hono";
21
+ import { createAdminUIApp } from "../src/admin-ui.ts";
22
+ import type { AdminUIDeps } from "../src/admin-ui.ts";
23
+ import type {
24
+ ChatClient,
25
+ ChatMessage,
26
+ ChatThread,
27
+ } from "../src/http-chat-client.ts";
28
+
29
+ // ─── Config ───────────────────────────────────────────────────────────────────
30
+
31
+ const ADMIN_E2E_PORT = 3490;
32
+ const ADMIN_E2E_SESSION_SECRET =
33
+ process.env.ADMIN_E2E_SESSION_SECRET ?? "e2e-admin-test-secret-32chars!!!";
34
+
35
+ const ADMIN_E2E_AGENT = {
36
+ id: "agent-e2e-1",
37
+ name: "Test Agent",
38
+ slackId: "U999TEST",
39
+ };
40
+
41
+ // ─── Mock data ────────────────────────────────────────────────────────────────
42
+
43
+ const MOCK_CRON = {
44
+ id: "cron-e2e-1",
45
+ agentId: ADMIN_E2E_AGENT.id,
46
+ schedule: "0 9 * * 1",
47
+ prompt: "Send the weekly standup summary to the team channel",
48
+ channel: "#general",
49
+ user: null,
50
+ enabled: true,
51
+ name: null,
52
+ system: false,
53
+ silent: false,
54
+ preCheck: null,
55
+ createdAt: new Date("2024-01-01"),
56
+ updatedAt: new Date("2024-01-01"),
57
+ };
58
+
59
+ const MOCK_TOOL = {
60
+ id: "tool-e2e-1",
61
+ agentId: ADMIN_E2E_AGENT.id,
62
+ pattern: "Bash(git:*)",
63
+ enabled: true,
64
+ createdAt: new Date("2024-01-01"),
65
+ updatedAt: new Date("2024-01-01"),
66
+ };
67
+
68
+ const MOCK_TOKEN = {
69
+ id: "token-e2e-1",
70
+ agentId: ADMIN_E2E_AGENT.id,
71
+ token: "hashed-token-value",
72
+ label: "Production token",
73
+ createdAt: new Date("2024-01-01"),
74
+ revokedAt: null,
75
+ };
76
+
77
+ // ─── Chat fixtures (CFB-1.3 — chat thread page e2e) ──────────────────────────
78
+
79
+ const MOCK_CHAT_THREAD: ChatThread = {
80
+ id: "thread-e2e-1",
81
+ agentId: ADMIN_E2E_AGENT.id,
82
+ title: "E2E Test Thread",
83
+ memberId: null,
84
+ createdAt: "2024-01-01T00:00:00.000Z",
85
+ updatedAt: "2024-01-01T00:00:00.000Z",
86
+ };
87
+
88
+ const MOCK_CHAT_MESSAGES: ChatMessage[] = [
89
+ {
90
+ id: "msg-e2e-1",
91
+ threadId: MOCK_CHAT_THREAD.id,
92
+ role: "user",
93
+ // Long enough to overflow any width well under the 70% max-width cap, so
94
+ // the e2e computed-width assertion (chat-thread-page.e2e.ts) exercises
95
+ // the CSS-driven cap rather than content-driven shrink-to-fit.
96
+ body: "Hello from the e2e test suite — this message body is intentionally long so that its rendered width is capped by the chat-bubble-inner max-width rule rather than shrinking to fit its own content, which lets the e2e test assert a stable 70% width ratio against the messages container.",
97
+ createdAt: "2024-01-01T00:00:00.000Z",
98
+ claimedBy: null,
99
+ repliedAt: null,
100
+ tokens: null,
101
+ costUsd: null,
102
+ errorKind: null,
103
+ attachmentFilename: null,
104
+ attachmentSize: null,
105
+ },
106
+ ];
107
+
108
+ // ─── Live-progress fixture (CFB-2.3) ─────────────────────────────────────────
109
+ // When ADMIN_E2E_CHAT_PENDING=1, the mock thread's last message is an
110
+ // UNREPLIED user message whose progressSeq stays frozen — so the server
111
+ // renders the live status bubble and the client ticker + stall state can be
112
+ // asserted by chat-progress.e2e.ts without any real agent. createdAt is
113
+ // computed at boot so the elapsed value starts near 0 and visibly increments.
114
+ const MOCK_CHAT_PENDING_MESSAGES: ChatMessage[] = [
115
+ {
116
+ id: "msg-pending-e2e-1",
117
+ threadId: MOCK_CHAT_THREAD.id,
118
+ role: "user",
119
+ body: "Kick off a long-running task, please.",
120
+ // Anchored at boot so elapsed starts small and climbs across DOM reads.
121
+ createdAt: new Date().toISOString(),
122
+ claimedBy: "agent-e2e-1",
123
+ claimedAt: new Date().toISOString(),
124
+ heartbeatAt: new Date().toISOString(),
125
+ repliedAt: null,
126
+ tokens: null,
127
+ costUsd: null,
128
+ errorKind: null,
129
+ attachmentFilename: null,
130
+ attachmentSize: null,
131
+ // progressPhase set so a milestone label renders; progressSeq is FROZEN
132
+ // (never advances across polls) so the stall state trips once the
133
+ // (test-shortened) STALL_WARN_AFTER_MS elapses.
134
+ progressPhase: "reading",
135
+ progressSeq: 1,
136
+ cancelRequestedAt: null,
137
+ },
138
+ ];
139
+
140
+ const CHAT_PENDING_MODE = process.env.ADMIN_E2E_CHAT_PENDING === "1";
141
+
142
+ function chatMessages(): ChatMessage[] {
143
+ return CHAT_PENDING_MODE ? MOCK_CHAT_PENDING_MESSAGES : MOCK_CHAT_MESSAGES;
144
+ }
145
+
146
+ function buildMockChatClient(): ChatClient {
147
+ return {
148
+ listThreads: async () => ({
149
+ threads: [MOCK_CHAT_THREAD],
150
+ total: 1,
151
+ limit: 50,
152
+ offset: 0,
153
+ }),
154
+ getThread: async () => MOCK_CHAT_THREAD,
155
+ createThread: async () => MOCK_CHAT_THREAD,
156
+ updateThread: async () => MOCK_CHAT_THREAD,
157
+ deleteThread: async () => {},
158
+ listMessages: async () => {
159
+ const messages = chatMessages();
160
+ return {
161
+ messages,
162
+ total: messages.length,
163
+ limit: 50,
164
+ offset: 0,
165
+ };
166
+ },
167
+ createMessage: async () => chatMessages()[0],
168
+ getThreadStats: async () => {
169
+ const messages = chatMessages();
170
+ return {
171
+ messageCount: messages.length,
172
+ totalInputTokens: 0,
173
+ totalOutputTokens: 0,
174
+ totalCostUsd: 0,
175
+ };
176
+ },
177
+ };
178
+ }
179
+
180
+ // ─── Degraded-mode switch (CFB-2.3) ──────────────────────────────────────────
181
+ // When ADMIN_E2E_CHAT_CLIENT_ABSENT=1, buildTestApp() omits chatClient
182
+ // entirely — mirroring production when SHIPWRIGHT_CHAT_SERVICE_URL /
183
+ // SHIPWRIGHT_CHAT_SERVICE_ADMIN_TOKEN are unset — so the /admin/chat* routes
184
+ // fall back to the degraded alert instead of the real chat UI. This proves
185
+ // chatClient is a genuine injection point and that the other chat e2e suites
186
+ // are exercising the real UI, not a silently-degraded fallback.
187
+ const CHAT_CLIENT_ABSENT = process.env.ADMIN_E2E_CHAT_CLIENT_ABSENT === "1";
188
+
189
+ // ─── Mock deps factory ────────────────────────────────────────────────────────
190
+
191
+ function buildMockDeps(chatClient: ChatClient | undefined): AdminUIDeps {
192
+ return {
193
+ prisma: {
194
+ agent: {
195
+ findMany: async () => [
196
+ {
197
+ id: ADMIN_E2E_AGENT.id,
198
+ name: ADMIN_E2E_AGENT.name,
199
+ slackId: ADMIN_E2E_AGENT.slackId,
200
+ createdAt: new Date("2024-01-01"),
201
+ },
202
+ ],
203
+ findUnique: async () => ({
204
+ id: ADMIN_E2E_AGENT.id,
205
+ name: ADMIN_E2E_AGENT.name,
206
+ slackId: ADMIN_E2E_AGENT.slackId,
207
+ selfHosted: false,
208
+ createdAt: new Date("2024-01-01"),
209
+ updatedAt: new Date("2024-01-01"),
210
+ repos: [],
211
+ }),
212
+ create: async () => ({
213
+ id: ADMIN_E2E_AGENT.id,
214
+ name: ADMIN_E2E_AGENT.name,
215
+ slackId: ADMIN_E2E_AGENT.slackId,
216
+ createdAt: new Date("2024-01-01"),
217
+ updatedAt: new Date("2024-01-01"),
218
+ repos: [],
219
+ }),
220
+ },
221
+ agentPlugin: {
222
+ findMany: async () => [],
223
+ },
224
+ // Referenced by the agent detail page's admin-only member list and by
225
+ // assertAgentAccess's non-admin membership check. Sessions minted for
226
+ // these e2e tests explicitly set `isAdmin: true`,
227
+ // so the render path deliberately takes the admin branch
228
+ // (prisma.agentMember.findMany), never the non-admin findUnique branch.
229
+ // No members fixture exists, so both resolve to empty/not-found. A
230
+ // non-admin session case isn't exercised here — that's an intentional
231
+ // test-coverage gap, not a fixture bug.
232
+ agentMember: {
233
+ findMany: async () => [],
234
+ findUnique: async () => null,
235
+ create: async () => {
236
+ throw new Error("not used in e2e tests");
237
+ },
238
+ deleteMany: async () => ({ count: 0 }),
239
+ },
240
+ },
241
+ agentEnvService: {
242
+ getByAgentId: async () => ({
243
+ SLACK_BOT_TOKEN: "xoxb-secret-value",
244
+ OPENAI_API_KEY: "sk-secret-value",
245
+ }),
246
+ upsert: async () => {},
247
+ patch: async () => {},
248
+ deleteKey: async () => {},
249
+ getConfigBundle: async () => null,
250
+ },
251
+ agentCronJobService: {
252
+ list: async () => [MOCK_CRON],
253
+ // Mirrors AgentCronJobService.listWithRunSummary: same rows as list(),
254
+ // plus a per-cron lastRun summary (null = "never run") and a
255
+ // runCountToday count. MOCK_CRON has never run, so both are the
256
+ // "no runs yet" defaults — the agent detail page renders that as
257
+ // "never" in the cron table, which doesn't affect the schedule/prompt
258
+ // assertions the existing e2e tests make.
259
+ listWithRunSummary: async () => [
260
+ { ...MOCK_CRON, lastRun: null, runCountToday: 0 },
261
+ ],
262
+ get: async () => MOCK_CRON,
263
+ create: async () => MOCK_CRON,
264
+ update: async () => MOCK_CRON,
265
+ setEnabled: async () => MOCK_CRON,
266
+ delete: async () => {},
267
+ reconcileSystemCrons: async () => ({
268
+ created: 0,
269
+ updated: 0,
270
+ deleted: 0,
271
+ }),
272
+ },
273
+ agentToolService: {
274
+ list: async () => [MOCK_TOOL],
275
+ add: async () => MOCK_TOOL,
276
+ toggle: async () => MOCK_TOOL,
277
+ remove: async () => {},
278
+ },
279
+ agentTokenService: {
280
+ listForAgent: async () => [MOCK_TOKEN],
281
+ create: async () => ({
282
+ token: MOCK_TOKEN,
283
+ rawToken: "sw_e2erawtokenvalue",
284
+ }),
285
+ revoke: async () => MOCK_TOKEN,
286
+ },
287
+ agentPluginService: {
288
+ list: async () => [],
289
+ add: async (agentId: string, name: string) => ({
290
+ id: "plugin-e2e-1",
291
+ agentId,
292
+ name,
293
+ version: null,
294
+ enabled: true,
295
+ createdAt: new Date("2024-01-01"),
296
+ updatedAt: new Date("2024-01-01"),
297
+ }),
298
+ },
299
+ agentMemberService: {
300
+ listByEmail: async () => [],
301
+ exists: async () => false,
302
+ add: async (agentId: string, email: string) => ({
303
+ id: "m1",
304
+ agentId,
305
+ email,
306
+ }),
307
+ remove: async () => {},
308
+ listByAgentId: async () => [],
309
+ },
310
+ agentService: {
311
+ listAll: async () => [
312
+ {
313
+ id: ADMIN_E2E_AGENT.id,
314
+ name: ADMIN_E2E_AGENT.name,
315
+ slackId: ADMIN_E2E_AGENT.slackId,
316
+ selfHosted: false,
317
+ typeName: "coding",
318
+ createdAt: new Date("2024-01-01"),
319
+ updatedAt: new Date("2024-01-01"),
320
+ },
321
+ ],
322
+ listByIds: async () => [],
323
+ searchByName: async () => [],
324
+ listOptions: async () => [
325
+ { id: ADMIN_E2E_AGENT.id, name: ADMIN_E2E_AGENT.name },
326
+ ],
327
+ create: async (input: { name: string; typeName?: string }) => ({
328
+ id: ADMIN_E2E_AGENT.id,
329
+ name: input.name ?? ADMIN_E2E_AGENT.name,
330
+ slackId: ADMIN_E2E_AGENT.slackId,
331
+ selfHosted: false,
332
+ typeName: input.typeName ?? "coding",
333
+ createdAt: new Date("2024-01-01"),
334
+ updatedAt: new Date("2024-01-01"),
335
+ }),
336
+ delete: async () => {},
337
+ getDetail: async () => ({
338
+ id: ADMIN_E2E_AGENT.id,
339
+ name: ADMIN_E2E_AGENT.name,
340
+ slackId: ADMIN_E2E_AGENT.slackId,
341
+ selfHosted: false,
342
+ typeName: "coding",
343
+ createdAt: new Date("2024-01-01"),
344
+ updatedAt: new Date("2024-01-01"),
345
+ repos: [],
346
+ reviewAuthorAllowlist: [],
347
+ missingRequiredEnv: [],
348
+ }),
349
+ updateFields: async () => ({
350
+ id: ADMIN_E2E_AGENT.id,
351
+ name: ADMIN_E2E_AGENT.name,
352
+ slackId: ADMIN_E2E_AGENT.slackId,
353
+ selfHosted: false,
354
+ typeName: "coding",
355
+ createdAt: new Date("2024-01-01"),
356
+ updatedAt: new Date("2024-01-01"),
357
+ repos: [],
358
+ reviewAuthorAllowlist: [],
359
+ missingRequiredEnv: [],
360
+ }),
361
+ },
362
+ provisioner: {
363
+ canProvision: false,
364
+ provision: async () => ({
365
+ resourceName: "r",
366
+ secretName: "s",
367
+ deploymentName: "d",
368
+ }),
369
+ deprovision: async () => {},
370
+ reconcile: async () => ({
371
+ recreated: [],
372
+ updated: [],
373
+ orphans: [],
374
+ failed: [],
375
+ }),
376
+ },
377
+ sessionSecret: ADMIN_E2E_SESSION_SECRET,
378
+ googleClientId: "e2e-google-client-id",
379
+ googleClientSecret: "e2e-google-client-secret",
380
+ adminAllowedEmails: ["admin@example.com"],
381
+ googleClient: {
382
+ exchangeCode: () => Promise.reject(new Error("not used in e2e tests")),
383
+ getUserInfo: () => Promise.reject(new Error("not used in e2e tests")),
384
+ },
385
+ slackClient: {
386
+ createAppManifest: async () => ({
387
+ appId: "A_E2E_TEST",
388
+ oauthRedirectUrl: "https://slack.com/oauth/authorize?client_id=e2e",
389
+ }),
390
+ },
391
+ appBaseUrl: `http://localhost:${ADMIN_E2E_PORT}`,
392
+ chatClient,
393
+ };
394
+ }
395
+
396
+ // ─── Build app ────────────────────────────────────────────────────────────────
397
+
398
+ function buildTestApp(): Hono {
399
+ const app = new Hono();
400
+
401
+ // Health check
402
+ app.get("/health", (c) => c.text("ok"));
403
+
404
+ // Mount the admin UI
405
+ const chatClient = CHAT_CLIENT_ABSENT ? undefined : buildMockChatClient();
406
+ const adminApp = createAdminUIApp(buildMockDeps(chatClient));
407
+ app.route("/", adminApp);
408
+
409
+ return app;
410
+ }
411
+
412
+ // ─── Entrypoint ───────────────────────────────────────────────────────────────
413
+
414
+ const port = Number.parseInt(
415
+ process.env.ADMIN_E2E_PORT ?? String(ADMIN_E2E_PORT),
416
+ 10,
417
+ );
418
+
419
+ // Re-read secret at runtime in case it was overridden via env
420
+ const app = buildTestApp();
421
+ Bun.serve({ port, fetch: app.fetch });
422
+ console.log(
423
+ `[admin-e2e-server] Test server running on http://localhost:${port}`,
424
+ );
425
+
426
+ process.on("SIGTERM", () => process.exit(0));
427
+ process.on("SIGINT", () => process.exit(0));