availsync 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (460) hide show
  1. package/.adal/skills/stripe-best-practices/SKILL.md +42 -0
  2. package/.adal/skills/stripe-best-practices/references/billing.md +36 -0
  3. package/.adal/skills/stripe-best-practices/references/connect.md +48 -0
  4. package/.adal/skills/stripe-best-practices/references/payments.md +79 -0
  5. package/.adal/skills/stripe-best-practices/references/security.md +109 -0
  6. package/.adal/skills/stripe-best-practices/references/treasury.md +16 -0
  7. package/.adal/skills/stripe-projects/SKILL.md +139 -0
  8. package/.adal/skills/upgrade-stripe/SKILL.md +185 -0
  9. package/.agents/skills/stripe-best-practices/SKILL.md +42 -0
  10. package/.agents/skills/stripe-best-practices/references/billing.md +36 -0
  11. package/.agents/skills/stripe-best-practices/references/connect.md +48 -0
  12. package/.agents/skills/stripe-best-practices/references/payments.md +79 -0
  13. package/.agents/skills/stripe-best-practices/references/security.md +109 -0
  14. package/.agents/skills/stripe-best-practices/references/treasury.md +16 -0
  15. package/.agents/skills/stripe-projects/SKILL.md +139 -0
  16. package/.agents/skills/upgrade-stripe/SKILL.md +185 -0
  17. package/.augment/skills/stripe-best-practices/SKILL.md +42 -0
  18. package/.augment/skills/stripe-best-practices/references/billing.md +36 -0
  19. package/.augment/skills/stripe-best-practices/references/connect.md +48 -0
  20. package/.augment/skills/stripe-best-practices/references/payments.md +79 -0
  21. package/.augment/skills/stripe-best-practices/references/security.md +109 -0
  22. package/.augment/skills/stripe-best-practices/references/treasury.md +16 -0
  23. package/.augment/skills/stripe-projects/SKILL.md +139 -0
  24. package/.augment/skills/upgrade-stripe/SKILL.md +185 -0
  25. package/.bob/skills/stripe-best-practices/SKILL.md +42 -0
  26. package/.bob/skills/stripe-best-practices/references/billing.md +36 -0
  27. package/.bob/skills/stripe-best-practices/references/connect.md +48 -0
  28. package/.bob/skills/stripe-best-practices/references/payments.md +79 -0
  29. package/.bob/skills/stripe-best-practices/references/security.md +109 -0
  30. package/.bob/skills/stripe-best-practices/references/treasury.md +16 -0
  31. package/.bob/skills/stripe-projects/SKILL.md +139 -0
  32. package/.bob/skills/upgrade-stripe/SKILL.md +185 -0
  33. package/.claude/settings.local.json +7 -0
  34. package/.claude/skills/stripe-best-practices/SKILL.md +42 -0
  35. package/.claude/skills/stripe-best-practices/references/billing.md +36 -0
  36. package/.claude/skills/stripe-best-practices/references/connect.md +48 -0
  37. package/.claude/skills/stripe-best-practices/references/payments.md +79 -0
  38. package/.claude/skills/stripe-best-practices/references/security.md +109 -0
  39. package/.claude/skills/stripe-best-practices/references/treasury.md +16 -0
  40. package/.claude/skills/stripe-projects/SKILL.md +139 -0
  41. package/.claude/skills/upgrade-stripe/SKILL.md +185 -0
  42. package/.codebuddy/skills/stripe-best-practices/SKILL.md +42 -0
  43. package/.codebuddy/skills/stripe-best-practices/references/billing.md +36 -0
  44. package/.codebuddy/skills/stripe-best-practices/references/connect.md +48 -0
  45. package/.codebuddy/skills/stripe-best-practices/references/payments.md +79 -0
  46. package/.codebuddy/skills/stripe-best-practices/references/security.md +109 -0
  47. package/.codebuddy/skills/stripe-best-practices/references/treasury.md +16 -0
  48. package/.codebuddy/skills/stripe-projects/SKILL.md +139 -0
  49. package/.codebuddy/skills/upgrade-stripe/SKILL.md +185 -0
  50. package/.commandcode/skills/stripe-best-practices/SKILL.md +42 -0
  51. package/.commandcode/skills/stripe-best-practices/references/billing.md +36 -0
  52. package/.commandcode/skills/stripe-best-practices/references/connect.md +48 -0
  53. package/.commandcode/skills/stripe-best-practices/references/payments.md +79 -0
  54. package/.commandcode/skills/stripe-best-practices/references/security.md +109 -0
  55. package/.commandcode/skills/stripe-best-practices/references/treasury.md +16 -0
  56. package/.commandcode/skills/stripe-projects/SKILL.md +139 -0
  57. package/.commandcode/skills/upgrade-stripe/SKILL.md +185 -0
  58. package/.continue/skills/stripe-best-practices/SKILL.md +42 -0
  59. package/.continue/skills/stripe-best-practices/references/billing.md +36 -0
  60. package/.continue/skills/stripe-best-practices/references/connect.md +48 -0
  61. package/.continue/skills/stripe-best-practices/references/payments.md +79 -0
  62. package/.continue/skills/stripe-best-practices/references/security.md +109 -0
  63. package/.continue/skills/stripe-best-practices/references/treasury.md +16 -0
  64. package/.continue/skills/stripe-projects/SKILL.md +139 -0
  65. package/.continue/skills/upgrade-stripe/SKILL.md +185 -0
  66. package/.cortex/skills/stripe-best-practices/SKILL.md +42 -0
  67. package/.cortex/skills/stripe-best-practices/references/billing.md +36 -0
  68. package/.cortex/skills/stripe-best-practices/references/connect.md +48 -0
  69. package/.cortex/skills/stripe-best-practices/references/payments.md +79 -0
  70. package/.cortex/skills/stripe-best-practices/references/security.md +109 -0
  71. package/.cortex/skills/stripe-best-practices/references/treasury.md +16 -0
  72. package/.cortex/skills/stripe-projects/SKILL.md +139 -0
  73. package/.cortex/skills/upgrade-stripe/SKILL.md +185 -0
  74. package/.crush/skills/stripe-best-practices/SKILL.md +42 -0
  75. package/.crush/skills/stripe-best-practices/references/billing.md +36 -0
  76. package/.crush/skills/stripe-best-practices/references/connect.md +48 -0
  77. package/.crush/skills/stripe-best-practices/references/payments.md +79 -0
  78. package/.crush/skills/stripe-best-practices/references/security.md +109 -0
  79. package/.crush/skills/stripe-best-practices/references/treasury.md +16 -0
  80. package/.crush/skills/stripe-projects/SKILL.md +139 -0
  81. package/.crush/skills/upgrade-stripe/SKILL.md +185 -0
  82. package/.env.example +20 -0
  83. package/.factory/skills/stripe-best-practices/SKILL.md +42 -0
  84. package/.factory/skills/stripe-best-practices/references/billing.md +36 -0
  85. package/.factory/skills/stripe-best-practices/references/connect.md +48 -0
  86. package/.factory/skills/stripe-best-practices/references/payments.md +79 -0
  87. package/.factory/skills/stripe-best-practices/references/security.md +109 -0
  88. package/.factory/skills/stripe-best-practices/references/treasury.md +16 -0
  89. package/.factory/skills/stripe-projects/SKILL.md +139 -0
  90. package/.factory/skills/upgrade-stripe/SKILL.md +185 -0
  91. package/.goose/skills/stripe-best-practices/SKILL.md +42 -0
  92. package/.goose/skills/stripe-best-practices/references/billing.md +36 -0
  93. package/.goose/skills/stripe-best-practices/references/connect.md +48 -0
  94. package/.goose/skills/stripe-best-practices/references/payments.md +79 -0
  95. package/.goose/skills/stripe-best-practices/references/security.md +109 -0
  96. package/.goose/skills/stripe-best-practices/references/treasury.md +16 -0
  97. package/.goose/skills/stripe-projects/SKILL.md +139 -0
  98. package/.goose/skills/upgrade-stripe/SKILL.md +185 -0
  99. package/.iflow/skills/stripe-best-practices/SKILL.md +42 -0
  100. package/.iflow/skills/stripe-best-practices/references/billing.md +36 -0
  101. package/.iflow/skills/stripe-best-practices/references/connect.md +48 -0
  102. package/.iflow/skills/stripe-best-practices/references/payments.md +79 -0
  103. package/.iflow/skills/stripe-best-practices/references/security.md +109 -0
  104. package/.iflow/skills/stripe-best-practices/references/treasury.md +16 -0
  105. package/.iflow/skills/stripe-projects/SKILL.md +139 -0
  106. package/.iflow/skills/upgrade-stripe/SKILL.md +185 -0
  107. package/.junie/skills/stripe-best-practices/SKILL.md +42 -0
  108. package/.junie/skills/stripe-best-practices/references/billing.md +36 -0
  109. package/.junie/skills/stripe-best-practices/references/connect.md +48 -0
  110. package/.junie/skills/stripe-best-practices/references/payments.md +79 -0
  111. package/.junie/skills/stripe-best-practices/references/security.md +109 -0
  112. package/.junie/skills/stripe-best-practices/references/treasury.md +16 -0
  113. package/.junie/skills/stripe-projects/SKILL.md +139 -0
  114. package/.junie/skills/upgrade-stripe/SKILL.md +185 -0
  115. package/.kilocode/skills/stripe-best-practices/SKILL.md +42 -0
  116. package/.kilocode/skills/stripe-best-practices/references/billing.md +36 -0
  117. package/.kilocode/skills/stripe-best-practices/references/connect.md +48 -0
  118. package/.kilocode/skills/stripe-best-practices/references/payments.md +79 -0
  119. package/.kilocode/skills/stripe-best-practices/references/security.md +109 -0
  120. package/.kilocode/skills/stripe-best-practices/references/treasury.md +16 -0
  121. package/.kilocode/skills/stripe-projects/SKILL.md +139 -0
  122. package/.kilocode/skills/upgrade-stripe/SKILL.md +185 -0
  123. package/.kiro/skills/stripe-best-practices/SKILL.md +42 -0
  124. package/.kiro/skills/stripe-best-practices/references/billing.md +36 -0
  125. package/.kiro/skills/stripe-best-practices/references/connect.md +48 -0
  126. package/.kiro/skills/stripe-best-practices/references/payments.md +79 -0
  127. package/.kiro/skills/stripe-best-practices/references/security.md +109 -0
  128. package/.kiro/skills/stripe-best-practices/references/treasury.md +16 -0
  129. package/.kiro/skills/stripe-projects/SKILL.md +139 -0
  130. package/.kiro/skills/upgrade-stripe/SKILL.md +185 -0
  131. package/.kode/skills/stripe-best-practices/SKILL.md +42 -0
  132. package/.kode/skills/stripe-best-practices/references/billing.md +36 -0
  133. package/.kode/skills/stripe-best-practices/references/connect.md +48 -0
  134. package/.kode/skills/stripe-best-practices/references/payments.md +79 -0
  135. package/.kode/skills/stripe-best-practices/references/security.md +109 -0
  136. package/.kode/skills/stripe-best-practices/references/treasury.md +16 -0
  137. package/.kode/skills/stripe-projects/SKILL.md +139 -0
  138. package/.kode/skills/upgrade-stripe/SKILL.md +185 -0
  139. package/.mcpjam/skills/stripe-best-practices/SKILL.md +42 -0
  140. package/.mcpjam/skills/stripe-best-practices/references/billing.md +36 -0
  141. package/.mcpjam/skills/stripe-best-practices/references/connect.md +48 -0
  142. package/.mcpjam/skills/stripe-best-practices/references/payments.md +79 -0
  143. package/.mcpjam/skills/stripe-best-practices/references/security.md +109 -0
  144. package/.mcpjam/skills/stripe-best-practices/references/treasury.md +16 -0
  145. package/.mcpjam/skills/stripe-projects/SKILL.md +139 -0
  146. package/.mcpjam/skills/upgrade-stripe/SKILL.md +185 -0
  147. package/.mux/skills/stripe-best-practices/SKILL.md +42 -0
  148. package/.mux/skills/stripe-best-practices/references/billing.md +36 -0
  149. package/.mux/skills/stripe-best-practices/references/connect.md +48 -0
  150. package/.mux/skills/stripe-best-practices/references/payments.md +79 -0
  151. package/.mux/skills/stripe-best-practices/references/security.md +109 -0
  152. package/.mux/skills/stripe-best-practices/references/treasury.md +16 -0
  153. package/.mux/skills/stripe-projects/SKILL.md +139 -0
  154. package/.mux/skills/upgrade-stripe/SKILL.md +185 -0
  155. package/.neovate/skills/stripe-best-practices/SKILL.md +42 -0
  156. package/.neovate/skills/stripe-best-practices/references/billing.md +36 -0
  157. package/.neovate/skills/stripe-best-practices/references/connect.md +48 -0
  158. package/.neovate/skills/stripe-best-practices/references/payments.md +79 -0
  159. package/.neovate/skills/stripe-best-practices/references/security.md +109 -0
  160. package/.neovate/skills/stripe-best-practices/references/treasury.md +16 -0
  161. package/.neovate/skills/stripe-projects/SKILL.md +139 -0
  162. package/.neovate/skills/upgrade-stripe/SKILL.md +185 -0
  163. package/.nixpacksignore +14 -0
  164. package/.openhands/skills/stripe-best-practices/SKILL.md +42 -0
  165. package/.openhands/skills/stripe-best-practices/references/billing.md +36 -0
  166. package/.openhands/skills/stripe-best-practices/references/connect.md +48 -0
  167. package/.openhands/skills/stripe-best-practices/references/payments.md +79 -0
  168. package/.openhands/skills/stripe-best-practices/references/security.md +109 -0
  169. package/.openhands/skills/stripe-best-practices/references/treasury.md +16 -0
  170. package/.openhands/skills/stripe-projects/SKILL.md +139 -0
  171. package/.openhands/skills/upgrade-stripe/SKILL.md +185 -0
  172. package/.pi/skills/stripe-best-practices/SKILL.md +42 -0
  173. package/.pi/skills/stripe-best-practices/references/billing.md +36 -0
  174. package/.pi/skills/stripe-best-practices/references/connect.md +48 -0
  175. package/.pi/skills/stripe-best-practices/references/payments.md +79 -0
  176. package/.pi/skills/stripe-best-practices/references/security.md +109 -0
  177. package/.pi/skills/stripe-best-practices/references/treasury.md +16 -0
  178. package/.pi/skills/stripe-projects/SKILL.md +139 -0
  179. package/.pi/skills/upgrade-stripe/SKILL.md +185 -0
  180. package/.pochi/skills/stripe-best-practices/SKILL.md +42 -0
  181. package/.pochi/skills/stripe-best-practices/references/billing.md +36 -0
  182. package/.pochi/skills/stripe-best-practices/references/connect.md +48 -0
  183. package/.pochi/skills/stripe-best-practices/references/payments.md +79 -0
  184. package/.pochi/skills/stripe-best-practices/references/security.md +109 -0
  185. package/.pochi/skills/stripe-best-practices/references/treasury.md +16 -0
  186. package/.pochi/skills/stripe-projects/SKILL.md +139 -0
  187. package/.pochi/skills/upgrade-stripe/SKILL.md +185 -0
  188. package/.qoder/skills/stripe-best-practices/SKILL.md +42 -0
  189. package/.qoder/skills/stripe-best-practices/references/billing.md +36 -0
  190. package/.qoder/skills/stripe-best-practices/references/connect.md +48 -0
  191. package/.qoder/skills/stripe-best-practices/references/payments.md +79 -0
  192. package/.qoder/skills/stripe-best-practices/references/security.md +109 -0
  193. package/.qoder/skills/stripe-best-practices/references/treasury.md +16 -0
  194. package/.qoder/skills/stripe-projects/SKILL.md +139 -0
  195. package/.qoder/skills/upgrade-stripe/SKILL.md +185 -0
  196. package/.qwen/skills/stripe-best-practices/SKILL.md +42 -0
  197. package/.qwen/skills/stripe-best-practices/references/billing.md +36 -0
  198. package/.qwen/skills/stripe-best-practices/references/connect.md +48 -0
  199. package/.qwen/skills/stripe-best-practices/references/payments.md +79 -0
  200. package/.qwen/skills/stripe-best-practices/references/security.md +109 -0
  201. package/.qwen/skills/stripe-best-practices/references/treasury.md +16 -0
  202. package/.qwen/skills/stripe-projects/SKILL.md +139 -0
  203. package/.qwen/skills/upgrade-stripe/SKILL.md +185 -0
  204. package/.roo/skills/stripe-best-practices/SKILL.md +42 -0
  205. package/.roo/skills/stripe-best-practices/references/billing.md +36 -0
  206. package/.roo/skills/stripe-best-practices/references/connect.md +48 -0
  207. package/.roo/skills/stripe-best-practices/references/payments.md +79 -0
  208. package/.roo/skills/stripe-best-practices/references/security.md +109 -0
  209. package/.roo/skills/stripe-best-practices/references/treasury.md +16 -0
  210. package/.roo/skills/stripe-projects/SKILL.md +139 -0
  211. package/.roo/skills/upgrade-stripe/SKILL.md +185 -0
  212. package/.trae/skills/stripe-best-practices/SKILL.md +42 -0
  213. package/.trae/skills/stripe-best-practices/references/billing.md +36 -0
  214. package/.trae/skills/stripe-best-practices/references/connect.md +48 -0
  215. package/.trae/skills/stripe-best-practices/references/payments.md +79 -0
  216. package/.trae/skills/stripe-best-practices/references/security.md +109 -0
  217. package/.trae/skills/stripe-best-practices/references/treasury.md +16 -0
  218. package/.trae/skills/stripe-projects/SKILL.md +139 -0
  219. package/.trae/skills/upgrade-stripe/SKILL.md +185 -0
  220. package/.vibe/skills/stripe-best-practices/SKILL.md +42 -0
  221. package/.vibe/skills/stripe-best-practices/references/billing.md +36 -0
  222. package/.vibe/skills/stripe-best-practices/references/connect.md +48 -0
  223. package/.vibe/skills/stripe-best-practices/references/payments.md +79 -0
  224. package/.vibe/skills/stripe-best-practices/references/security.md +109 -0
  225. package/.vibe/skills/stripe-best-practices/references/treasury.md +16 -0
  226. package/.vibe/skills/stripe-projects/SKILL.md +139 -0
  227. package/.vibe/skills/upgrade-stripe/SKILL.md +185 -0
  228. package/.windsurf/skills/stripe-best-practices/SKILL.md +42 -0
  229. package/.windsurf/skills/stripe-best-practices/references/billing.md +36 -0
  230. package/.windsurf/skills/stripe-best-practices/references/connect.md +48 -0
  231. package/.windsurf/skills/stripe-best-practices/references/payments.md +79 -0
  232. package/.windsurf/skills/stripe-best-practices/references/security.md +109 -0
  233. package/.windsurf/skills/stripe-best-practices/references/treasury.md +16 -0
  234. package/.windsurf/skills/stripe-projects/SKILL.md +139 -0
  235. package/.windsurf/skills/upgrade-stripe/SKILL.md +185 -0
  236. package/.zencoder/skills/stripe-best-practices/SKILL.md +42 -0
  237. package/.zencoder/skills/stripe-best-practices/references/billing.md +36 -0
  238. package/.zencoder/skills/stripe-best-practices/references/connect.md +48 -0
  239. package/.zencoder/skills/stripe-best-practices/references/payments.md +79 -0
  240. package/.zencoder/skills/stripe-best-practices/references/security.md +109 -0
  241. package/.zencoder/skills/stripe-best-practices/references/treasury.md +16 -0
  242. package/.zencoder/skills/stripe-projects/SKILL.md +139 -0
  243. package/.zencoder/skills/upgrade-stripe/SKILL.md +185 -0
  244. package/AUDIT.md +95 -0
  245. package/BLOCKERS.md +0 -0
  246. package/COOLIFY.md +51 -0
  247. package/MCP_SETUP.md +23 -0
  248. package/PRODUCTION_CHECKLIST.md +246 -0
  249. package/README.md +47 -0
  250. package/ROADMAP.md +91 -0
  251. package/docs/superpowers/plans/2026-05-11-availsync-frontend-sales-flow.md +2445 -0
  252. package/frontend/.env.example +2 -0
  253. package/frontend/app/admin/layout.tsx +13 -0
  254. package/frontend/app/admin/page.tsx +747 -0
  255. package/frontend/app/app/activity/page.tsx +257 -0
  256. package/frontend/app/app/agents/[agentId]/page.tsx +21 -0
  257. package/frontend/app/app/agents/page.tsx +1155 -0
  258. package/frontend/app/app/audit/page.tsx +225 -0
  259. package/frontend/app/app/availability/page.tsx +840 -0
  260. package/frontend/app/app/holds/page.tsx +262 -0
  261. package/frontend/app/app/layout.tsx +19 -0
  262. package/frontend/app/app/onboarding/page.tsx +10 -0
  263. package/frontend/app/app/onboarding/verify/page.tsx +309 -0
  264. package/frontend/app/app/page.tsx +508 -0
  265. package/frontend/app/app/settings/page.tsx +399 -0
  266. package/frontend/app/app/work/page.tsx +426 -0
  267. package/frontend/app/changelog/page.tsx +93 -0
  268. package/frontend/app/checkout/page.tsx +25 -0
  269. package/frontend/app/docs/api/page.tsx +157 -0
  270. package/frontend/app/docs/page.tsx +296 -0
  271. package/frontend/app/docs/pilot/page.tsx +127 -0
  272. package/frontend/app/docs/quickstart/page.tsx +318 -0
  273. package/frontend/app/docs/reliability/page.tsx +78 -0
  274. package/frontend/app/docs/sdk/node/page.tsx +166 -0
  275. package/frontend/app/globals.css +57 -0
  276. package/frontend/app/icon.png +0 -0
  277. package/frontend/app/layout.tsx +87 -0
  278. package/frontend/app/login/page.tsx +14 -0
  279. package/frontend/app/page.tsx +47 -0
  280. package/frontend/app/pricing/page.tsx +66 -0
  281. package/frontend/app/privacy/page.tsx +52 -0
  282. package/frontend/app/robots.ts +26 -0
  283. package/frontend/app/security/page.tsx +74 -0
  284. package/frontend/app/signup/page.tsx +14 -0
  285. package/frontend/app/sitemap.ts +14 -0
  286. package/frontend/app/terms/page.tsx +51 -0
  287. package/frontend/components/brand/AvailsyncLogo.tsx +56 -0
  288. package/frontend/components/checkout/CheckoutClient.tsx +100 -0
  289. package/frontend/components/dashboard/AgentForm.tsx +59 -0
  290. package/frontend/components/dashboard/AppShell.tsx +291 -0
  291. package/frontend/components/dashboard/AvailabilityChecker.tsx +117 -0
  292. package/frontend/components/dashboard/AvailabilityWindowForm.tsx +40 -0
  293. package/frontend/components/dashboard/HoldForm.tsx +133 -0
  294. package/frontend/components/dashboard/MetricCard.tsx +10 -0
  295. package/frontend/components/login/LoginForm.tsx +95 -0
  296. package/frontend/components/marketing/AgentCoordinationStory.tsx +1530 -0
  297. package/frontend/components/marketing/Faq.tsx +41 -0
  298. package/frontend/components/marketing/Hero.tsx +73 -0
  299. package/frontend/components/marketing/HowItWorks.tsx +28 -0
  300. package/frontend/components/marketing/ObserveModeTeaser.tsx +41 -0
  301. package/frontend/components/marketing/PricingTeaser.tsx +23 -0
  302. package/frontend/components/marketing/ProblemSolution.tsx +36 -0
  303. package/frontend/components/marketing/SiteFooter.tsx +59 -0
  304. package/frontend/components/marketing/SiteHeader.tsx +45 -0
  305. package/frontend/components/marketing/UseCases.tsx +27 -0
  306. package/frontend/components/onboarding/OnboardingClient.tsx +278 -0
  307. package/frontend/components/pricing/PricingCards.tsx +65 -0
  308. package/frontend/components/privacy/CookieConsent.tsx +230 -0
  309. package/frontend/components/privacy/CookieSettingsButton.tsx +15 -0
  310. package/frontend/components/seo/JsonLd.tsx +10 -0
  311. package/frontend/components/signup/SignupForm.tsx +55 -0
  312. package/frontend/components/ui/Badge.tsx +23 -0
  313. package/frontend/components/ui/Button.tsx +37 -0
  314. package/frontend/components/ui/Card.tsx +11 -0
  315. package/frontend/components/ui/ConfirmDialog.tsx +77 -0
  316. package/frontend/components/ui/EmptyState.tsx +24 -0
  317. package/frontend/components/ui/Input.tsx +14 -0
  318. package/frontend/components/ui/KeyDisplay.tsx +49 -0
  319. package/frontend/components/ui/Select.tsx +14 -0
  320. package/frontend/components/ui/Skeleton.tsx +24 -0
  321. package/frontend/components/ui/Tabs.tsx +19 -0
  322. package/frontend/components/ui/Textarea.tsx +14 -0
  323. package/frontend/components/ui/Toast.tsx +78 -0
  324. package/frontend/components/waitlist/WaitlistDialog.tsx +128 -0
  325. package/frontend/lib/api.ts +1282 -0
  326. package/frontend/lib/billing.ts +6 -0
  327. package/frontend/lib/cookieConsent.ts +113 -0
  328. package/frontend/lib/format.ts +16 -0
  329. package/frontend/lib/plans.ts +62 -0
  330. package/frontend/lib/schemas.ts +108 -0
  331. package/frontend/lib/seo.ts +376 -0
  332. package/frontend/lib/setupGuides.ts +630 -0
  333. package/frontend/lib/storage.ts +30 -0
  334. package/frontend/next-env.d.ts +6 -0
  335. package/frontend/next.config.mjs +13 -0
  336. package/frontend/package-lock.json +14409 -0
  337. package/frontend/package.json +41 -0
  338. package/frontend/playwright.config.ts +20 -0
  339. package/frontend/postcss.config.mjs +8 -0
  340. package/frontend/public/.gitkeep +0 -0
  341. package/frontend/public/brand/availsync-logo-board.png +0 -0
  342. package/frontend/public/brand/availsync-logo-dark.png +0 -0
  343. package/frontend/public/brand/availsync-mark-dark.png +0 -0
  344. package/frontend/public/brand/availsync-wordmark-dark.png +0 -0
  345. package/frontend/public/marketing/hero-agent-coordination.png +0 -0
  346. package/frontend/tailwind.config.ts +53 -0
  347. package/frontend/tests/smoke.spec.ts +89 -0
  348. package/frontend/tsconfig.json +23 -0
  349. package/jest.config.js +7 -0
  350. package/nixpacks.toml +11 -0
  351. package/package.json +53 -0
  352. package/packages/mcp/LICENSE +21 -0
  353. package/packages/mcp/README.md +60 -0
  354. package/packages/mcp/jest.config.cjs +8 -0
  355. package/packages/mcp/package.json +54 -0
  356. package/packages/mcp/src/helpers.ts +38 -0
  357. package/packages/mcp/src/index.test.ts +60 -0
  358. package/packages/mcp/src/index.ts +387 -0
  359. package/packages/mcp/tsconfig.json +20 -0
  360. package/packages/mcp/tsconfig.test.json +12 -0
  361. package/packages/node/LICENSE +21 -0
  362. package/packages/node/README.md +120 -0
  363. package/packages/node/jest.config.cjs +8 -0
  364. package/packages/node/package.json +46 -0
  365. package/packages/node/src/index.test.ts +360 -0
  366. package/packages/node/src/index.ts +402 -0
  367. package/packages/node/tsconfig.json +20 -0
  368. package/packages/node/tsconfig.test.json +12 -0
  369. package/plan.md +923 -0
  370. package/skills/stripe-best-practices/SKILL.md +42 -0
  371. package/skills/stripe-best-practices/references/billing.md +36 -0
  372. package/skills/stripe-best-practices/references/connect.md +48 -0
  373. package/skills/stripe-best-practices/references/payments.md +79 -0
  374. package/skills/stripe-best-practices/references/security.md +109 -0
  375. package/skills/stripe-best-practices/references/treasury.md +16 -0
  376. package/skills/stripe-projects/SKILL.md +139 -0
  377. package/skills/upgrade-stripe/SKILL.md +185 -0
  378. package/skills-lock.json +20 -0
  379. package/src/core/availability.ts +178 -0
  380. package/src/core/conflict.ts +209 -0
  381. package/src/core/work.ts +490 -0
  382. package/src/db/client.ts +17 -0
  383. package/src/db/migrations/001_init.sql +88 -0
  384. package/src/db/migrations/002_stripe.sql +2 -0
  385. package/src/db/migrations/003_workspace_auth.sql +19 -0
  386. package/src/db/migrations/004_agent_mcp_status.sql +2 -0
  387. package/src/db/migrations/005_hold_event_actor.sql +4 -0
  388. package/src/db/migrations/006_agent_activity.sql +35 -0
  389. package/src/db/migrations/007_work_coordination.sql +60 -0
  390. package/src/db/migrations/008_work_claim_leases.sql +20 -0
  391. package/src/db/migrations/009_billing_subscription_state.sql +23 -0
  392. package/src/db/migrations/010_agent_api_key_prefix.sql +10 -0
  393. package/src/db/migrations/011_org_verified_and_work_event_retention.sql +11 -0
  394. package/src/db/migrations/012_agent_enforcement_mode.sql +12 -0
  395. package/src/db/migrations/013_support_tickets.sql +21 -0
  396. package/src/db/migrations/014_paid_plan_waitlist.sql +23 -0
  397. package/src/db/migrations/015_agent_last_seen.sql +2 -0
  398. package/src/db/migrations.ts +164 -0
  399. package/src/db/run-migrations.ts +13 -0
  400. package/src/index.ts +183 -0
  401. package/src/lib/activity.ts +137 -0
  402. package/src/lib/apiKeys.ts +32 -0
  403. package/src/lib/appInfo.ts +26 -0
  404. package/src/lib/billingConfig.ts +3 -0
  405. package/src/lib/env.ts +75 -0
  406. package/src/lib/logger.ts +8 -0
  407. package/src/lib/plans.ts +204 -0
  408. package/src/mcp/server.js +5 -0
  409. package/src/mcp/server.ts +350 -0
  410. package/src/middleware/auth.ts +342 -0
  411. package/src/middleware/requestId.ts +16 -0
  412. package/src/routes/account.ts +168 -0
  413. package/src/routes/activity.ts +126 -0
  414. package/src/routes/admin.ts +514 -0
  415. package/src/routes/audit.ts +68 -0
  416. package/src/routes/auth.ts +203 -0
  417. package/src/routes/availability.ts +325 -0
  418. package/src/routes/billing.ts +406 -0
  419. package/src/routes/conflicts.ts +131 -0
  420. package/src/routes/holds.ts +437 -0
  421. package/src/routes/mcp.ts +57 -0
  422. package/src/routes/metrics.ts +39 -0
  423. package/src/routes/onboarding.ts +273 -0
  424. package/src/routes/orgs.ts +981 -0
  425. package/src/routes/preferences.ts +132 -0
  426. package/src/routes/session.ts +16 -0
  427. package/src/routes/support.ts +77 -0
  428. package/src/routes/value.ts +186 -0
  429. package/src/routes/waitlist.ts +63 -0
  430. package/src/routes/work.ts +1578 -0
  431. package/src/server.ts +36 -0
  432. package/src/types/index.ts +109 -0
  433. package/tests/integration/activity.route.test.ts +103 -0
  434. package/tests/integration/admin.route.test.ts +143 -0
  435. package/tests/integration/agent-keys.route.test.ts +237 -0
  436. package/tests/integration/availability.route.test.ts +125 -0
  437. package/tests/integration/billing.route.test.ts +393 -0
  438. package/tests/integration/conflicts.route.test.ts +131 -0
  439. package/tests/integration/flows.test.ts +154 -0
  440. package/tests/integration/helpers.ts +134 -0
  441. package/tests/integration/holds.route.test.ts +185 -0
  442. package/tests/integration/metrics.route.test.ts +100 -0
  443. package/tests/integration/onboarding.verify.route.test.ts +163 -0
  444. package/tests/integration/preferences.route.test.ts +53 -0
  445. package/tests/integration/session.route.test.ts +97 -0
  446. package/tests/integration/system.route.test.ts +92 -0
  447. package/tests/integration/value.route.test.ts +235 -0
  448. package/tests/integration/work.route.test.ts +745 -0
  449. package/tests/setup.ts +4 -0
  450. package/tests/smoke.sh +62 -0
  451. package/tests/unit/auth.test.ts +114 -0
  452. package/tests/unit/availability.test.ts +149 -0
  453. package/tests/unit/conflict.test.ts +118 -0
  454. package/tests/unit/env.test.ts +69 -0
  455. package/tests/unit/migrations.test.ts +135 -0
  456. package/tests/unit/request-id.test.ts +37 -0
  457. package/tmp-mobile-agents.png +0 -0
  458. package/tmp-next-mobile.err.log +10 -0
  459. package/tmp-next-mobile.log +5 -0
  460. package/tsconfig.json +16 -0
@@ -0,0 +1,163 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from '@jest/globals';
2
+ import pool from '../../src/db/client';
3
+ import {
4
+ api,
5
+ closeDatabase,
6
+ createAgent,
7
+ createOrgAndAgent,
8
+ getWorkspaceCookie,
9
+ migrateTestDatabase,
10
+ resetDatabase,
11
+ } from './helpers';
12
+
13
+ describe('onboarding verification', () => {
14
+ beforeAll(async () => {
15
+ await migrateTestDatabase();
16
+ await resetDatabase();
17
+ });
18
+
19
+ afterAll(async () => {
20
+ await resetDatabase();
21
+ await closeDatabase();
22
+ });
23
+
24
+ test('verifies an org by showing external meeting agent winning over internal agent', async () => {
25
+ const org = await createOrgAndAgent('Sales Bot', 'external_meeting');
26
+ const internal = await createAgent(org.orgId, 'Recruiting Bot', 'internal');
27
+
28
+ const response = await api
29
+ .post('/v1/onboarding/verify')
30
+ .set('Cookie', getWorkspaceCookie(org.orgId))
31
+ .send({ agent_a_id: org.agentId, agent_b_id: internal.agentId })
32
+ .expect(200);
33
+
34
+ expect(response.body).toMatchObject({
35
+ resource_key: expect.stringMatching(/^verify-/),
36
+ winner: {
37
+ agent_id: org.agentId,
38
+ agent_name: 'Sales Bot',
39
+ agent_type: 'external_meeting',
40
+ outcome: 'claimed',
41
+ },
42
+ loser: {
43
+ agent_id: internal.agentId,
44
+ agent_name: 'Recruiting Bot',
45
+ agent_type: 'internal',
46
+ claim_id: null,
47
+ outcome: 'blocked',
48
+ },
49
+ rule_applied: 'agent_type',
50
+ });
51
+ expect(response.body.rule_explanation).toContain("external_meeting");
52
+ expect(response.body.verified_at).toEqual(expect.any(String));
53
+
54
+ const stored = await pool.query('SELECT verified_at FROM orgs WHERE id = $1', [org.orgId]);
55
+ expect(stored.rows[0].verified_at.toISOString()).toBe(response.body.verified_at);
56
+ });
57
+
58
+ test('verification can run again with a new resource key while preserving original verified_at', async () => {
59
+ const org = await createOrgAndAgent('Sales Bot 2', 'external_meeting');
60
+ const internal = await createAgent(org.orgId, 'Recruiting Bot 2', 'internal');
61
+
62
+ const first = await api
63
+ .post('/v1/onboarding/verify')
64
+ .set('Cookie', getWorkspaceCookie(org.orgId))
65
+ .send({ agent_a_id: org.agentId, agent_b_id: internal.agentId })
66
+ .expect(200);
67
+
68
+ const second = await api
69
+ .post('/v1/onboarding/verify')
70
+ .set('Cookie', getWorkspaceCookie(org.orgId))
71
+ .send({ agent_a_id: org.agentId, agent_b_id: internal.agentId })
72
+ .expect(200);
73
+
74
+ expect(second.body.resource_key).not.toBe(first.body.resource_key);
75
+ expect(second.body.verified_at).toBe(first.body.verified_at);
76
+ });
77
+
78
+ test('returns insufficient_agents when org has only one agent', async () => {
79
+ const org = await createOrgAndAgent('Solo Bot', 'generic');
80
+
81
+ const response = await api
82
+ .post('/v1/onboarding/verify')
83
+ .set('Cookie', getWorkspaceCookie(org.orgId))
84
+ .send({})
85
+ .expect(422);
86
+
87
+ expect(response.body).toMatchObject({
88
+ error: 'insufficient_agents',
89
+ message: 'Create at least 2 agents to run verification',
90
+ });
91
+ });
92
+
93
+ test('returns tie warning when agents tie on type and priority', async () => {
94
+ const org = await createOrgAndAgent('Generic A', 'generic', 0);
95
+ const genericB = await createAgent(org.orgId, 'Generic B', 'generic', 0);
96
+
97
+ const response = await api
98
+ .post('/v1/onboarding/verify')
99
+ .set('Cookie', getWorkspaceCookie(org.orgId))
100
+ .send({ agent_a_id: org.agentId, agent_b_id: genericB.agentId })
101
+ .expect(200);
102
+
103
+ expect(response.body).toMatchObject({
104
+ rule_applied: 'first_created',
105
+ tie_warning: expect.stringContaining('Both agents tied on priority'),
106
+ winner: { agent_id: org.agentId },
107
+ loser: { agent_id: genericB.agentId },
108
+ });
109
+ });
110
+
111
+ test('cleanup removes verification resource and keeps work events', async () => {
112
+ const org = await createOrgAndAgent('Sales Bot 3', 'external_meeting');
113
+ const internal = await createAgent(org.orgId, 'Recruiting Bot 3', 'internal');
114
+
115
+ const response = await api
116
+ .post('/v1/onboarding/verify')
117
+ .set('Cookie', getWorkspaceCookie(org.orgId))
118
+ .send({ agent_a_id: org.agentId, agent_b_id: internal.agentId })
119
+ .expect(200);
120
+
121
+ const resource = await pool.query(
122
+ `SELECT COUNT(*)::int AS count
123
+ FROM work_resources
124
+ WHERE org_id = $1
125
+ AND resource_key = $2`,
126
+ [org.orgId, response.body.resource_key],
127
+ );
128
+ expect(resource.rows[0].count).toBe(0);
129
+
130
+ const activeClaims = await pool.query(
131
+ `SELECT COUNT(*)::int AS count
132
+ FROM work_claims
133
+ WHERE org_id = $1
134
+ AND metadata->>'resource_key' = $2`,
135
+ [org.orgId, response.body.resource_key],
136
+ );
137
+ expect(activeClaims.rows[0].count).toBe(0);
138
+
139
+ const events = await pool.query(
140
+ `SELECT event_type, metadata
141
+ FROM work_claim_events
142
+ WHERE org_id = $1
143
+ AND metadata->>'source' = 'onboarding_verification'
144
+ AND metadata->>'resource_key' = $2
145
+ ORDER BY created_at ASC`,
146
+ [org.orgId, response.body.resource_key],
147
+ );
148
+ expect(events.rows.map((row) => row.event_type).sort()).toEqual(['blocked', 'created', 'released']);
149
+ });
150
+
151
+ test('rejects selected agents outside the workspace', async () => {
152
+ const org = await createOrgAndAgent('Local Bot', 'external_meeting');
153
+ const otherOrg = await createOrgAndAgent('Foreign Bot', 'internal');
154
+
155
+ const response = await api
156
+ .post('/v1/onboarding/verify')
157
+ .set('Cookie', getWorkspaceCookie(org.orgId))
158
+ .send({ agent_a_id: org.agentId, agent_b_id: otherOrg.agentId })
159
+ .expect(403);
160
+
161
+ expect(response.body).toMatchObject({ error: 'forbidden' });
162
+ });
163
+ });
@@ -0,0 +1,53 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from '@jest/globals';
2
+ import {
3
+ api,
4
+ closeDatabase,
5
+ createOrgAndAgent,
6
+ migrateTestDatabase,
7
+ resetDatabase,
8
+ } from './helpers';
9
+
10
+ describe('preferences routes', () => {
11
+ let agentId: string;
12
+ let apiKey: string;
13
+
14
+ beforeAll(async () => {
15
+ await migrateTestDatabase();
16
+ await resetDatabase();
17
+ const created = await createOrgAndAgent();
18
+ agentId = created.agentId;
19
+ apiKey = created.apiKey;
20
+ });
21
+
22
+ afterAll(async () => {
23
+ await resetDatabase();
24
+ await closeDatabase();
25
+ });
26
+
27
+ test('GET /v1/preferences returns defaults', async () => {
28
+ const response = await api
29
+ .get(`/v1/preferences/${agentId}`)
30
+ .set('Authorization', `Bearer ${apiKey}`)
31
+ .expect(200);
32
+
33
+ expect(response.body.preferences.buffer_minutes_after).toBe(15);
34
+ });
35
+
36
+ test('PUT /v1/preferences updates buffer_minutes_after', async () => {
37
+ const response = await api
38
+ .put(`/v1/preferences/${agentId}`)
39
+ .set('Authorization', `Bearer ${apiKey}`)
40
+ .send({ buffer_minutes_after: 30 })
41
+ .expect(200);
42
+
43
+ expect(response.body.preferences.buffer_minutes_after).toBe(30);
44
+ });
45
+
46
+ test('PUT /v1/preferences with invalid weekday returns 422', async () => {
47
+ await api
48
+ .put(`/v1/preferences/${agentId}`)
49
+ .set('Authorization', `Bearer ${apiKey}`)
50
+ .send({ focus_blocks: [{ weekday: 7, start_time: '09:00', end_time: '10:00' }] })
51
+ .expect(422);
52
+ });
53
+ });
@@ -0,0 +1,97 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from '@jest/globals';
2
+ import { api, closeDatabase, migrateTestDatabase, resetDatabase } from './helpers';
3
+
4
+ describe('session routes', () => {
5
+ beforeAll(async () => {
6
+ await migrateTestDatabase();
7
+ await resetDatabase();
8
+ });
9
+
10
+ afterAll(async () => {
11
+ await resetDatabase();
12
+ await closeDatabase();
13
+ });
14
+
15
+ test('signup creates a workspace session and GET /v1/session restores workspace context', async () => {
16
+ const response = await api
17
+ .post('/v1/auth/signup')
18
+ .send({
19
+ workspace_name: 'Session Org',
20
+ email: 'session@example.com',
21
+ password: 'password123',
22
+ agent: { name: 'Session Bot', agent_type: 'generic' },
23
+ })
24
+ .expect(201);
25
+ const cookie = response.headers['set-cookie'];
26
+
27
+ const session = await api
28
+ .get('/v1/session')
29
+ .set('Cookie', cookie)
30
+ .expect(200);
31
+
32
+ expect(session.body.org.id).toBe(response.body.org.id);
33
+ expect(session.body.user.email).toBe('session@example.com');
34
+ expect(session.body.user.password_hash).toBeUndefined();
35
+ expect(response.body.api_key).toBeTruthy();
36
+ });
37
+
38
+ test('login and logout manage workspace sessions', async () => {
39
+ const signup = await api
40
+ .post('/v1/auth/signup')
41
+ .send({
42
+ workspace_name: 'Login Org',
43
+ email: 'login@example.com',
44
+ password: 'password123',
45
+ agent: { name: 'Login Bot', agent_type: 'generic' },
46
+ })
47
+ .expect(201);
48
+
49
+ const login = await api
50
+ .post('/v1/auth/login')
51
+ .send({ email: 'login@example.com', password: 'password123' })
52
+ .expect(200);
53
+
54
+ await api.get('/v1/session').set('Cookie', login.headers['set-cookie']).expect(200);
55
+ await api.post('/v1/auth/logout').set('Cookie', signup.headers['set-cookie']).expect(200);
56
+ });
57
+
58
+ test('GET /v1/session rejects missing auth', async () => {
59
+ await api.get('/v1/session').expect(401);
60
+ });
61
+
62
+ test('agent management requires a workspace session', async () => {
63
+ const signup = await api
64
+ .post('/v1/auth/signup')
65
+ .send({
66
+ workspace_name: 'Agents Org',
67
+ email: 'agents@example.com',
68
+ password: 'password123',
69
+ agent: { name: 'First Agent', agent_type: 'generic' },
70
+ })
71
+ .expect(201);
72
+
73
+ const orgId = signup.body.org.id;
74
+ const apiKey = signup.body.api_key;
75
+
76
+ await api.get(`/v1/orgs/${orgId}/agents`).expect(401);
77
+ await api
78
+ .post(`/v1/orgs/${orgId}/agents`)
79
+ .set('Authorization', `Bearer ${apiKey}`)
80
+ .send({ name: 'Blocked Agent', agent_type: 'generic' })
81
+ .expect(401);
82
+
83
+ const created = await api
84
+ .post(`/v1/orgs/${orgId}/agents`)
85
+ .set('Cookie', signup.headers['set-cookie'])
86
+ .send({ name: 'Second Agent', agent_type: 'generic' })
87
+ .expect(201);
88
+
89
+ const agents = await api
90
+ .get(`/v1/orgs/${orgId}/agents`)
91
+ .set('Cookie', signup.headers['set-cookie'])
92
+ .expect(200);
93
+
94
+ expect(created.body.api_key).toBeTruthy();
95
+ expect(agents.body.agents).toHaveLength(2);
96
+ });
97
+ });
@@ -0,0 +1,92 @@
1
+ import { afterAll, afterEach, beforeEach, describe, expect, test } from '@jest/globals';
2
+ import pool from '../../src/db/client';
3
+ import {
4
+ api,
5
+ closeDatabase,
6
+ createOrgAndAgent,
7
+ getWorkspaceCookie,
8
+ migrateTestDatabase,
9
+ resetDatabase,
10
+ } from './helpers';
11
+
12
+ async function ownerEmail(orgId: string): Promise<string> {
13
+ const result = await pool.query(
14
+ `SELECT email
15
+ FROM workspace_users
16
+ WHERE org_id = $1
17
+ ORDER BY created_at ASC
18
+ LIMIT 1`,
19
+ [orgId],
20
+ );
21
+ return result.rows[0].email;
22
+ }
23
+
24
+ describe('system diagnostics routes', () => {
25
+ const previousAdminEmails = process.env.ADMIN_EMAILS;
26
+
27
+ beforeEach(async () => {
28
+ await migrateTestDatabase();
29
+ await resetDatabase();
30
+ process.env.ADMIN_EMAILS = '';
31
+ });
32
+
33
+ afterEach(() => {
34
+ process.env.ADMIN_EMAILS = previousAdminEmails;
35
+ });
36
+
37
+ afterAll(async () => {
38
+ await resetDatabase();
39
+ await closeDatabase();
40
+ });
41
+
42
+ test('health returns public database and migration status', async () => {
43
+ const response = await api.get('/health').expect((res) => {
44
+ expect([200, 503]).toContain(res.status);
45
+ });
46
+
47
+ expect(response.headers['x-request-id']).toEqual(expect.any(String));
48
+ expect(response.body).toMatchObject({
49
+ db: 'connected',
50
+ version: expect.any(String),
51
+ migrations: {
52
+ total: expect.any(Number),
53
+ applied: expect.any(Number),
54
+ failed: expect.any(Number),
55
+ },
56
+ });
57
+ });
58
+
59
+ test('admin system requires admin session and redacts env values', async () => {
60
+ const { orgId } = await createOrgAndAgent('System Admin', 'generic', 0);
61
+
62
+ await api.get('/v1/admin/system').expect(401);
63
+ await api.get('/v1/admin/system').set('Cookie', getWorkspaceCookie(orgId)).expect(403);
64
+
65
+ process.env.ADMIN_EMAILS = await ownerEmail(orgId);
66
+ process.env.STRIPE_SECRET_KEY = 'sk_test_should_not_leak';
67
+ const response = await api
68
+ .get('/v1/admin/system')
69
+ .set('Cookie', getWorkspaceCookie(orgId))
70
+ .expect(200);
71
+
72
+ expect(response.body).toMatchObject({
73
+ app: {
74
+ version: expect.any(String),
75
+ node_version: expect.any(String),
76
+ },
77
+ database: { connected: true },
78
+ env: {
79
+ required: expect.any(Array),
80
+ missing: expect.any(Array),
81
+ },
82
+ stripe: {
83
+ configured: true,
84
+ },
85
+ migrations: {
86
+ total: expect.any(Number),
87
+ applied: expect.any(Number),
88
+ },
89
+ });
90
+ expect(JSON.stringify(response.body)).not.toContain('sk_test_should_not_leak');
91
+ });
92
+ });
@@ -0,0 +1,235 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from '@jest/globals';
2
+ import {
3
+ api,
4
+ closeDatabase,
5
+ createAgent,
6
+ createOrgAndAgent,
7
+ getWorkspaceCookie,
8
+ migrateTestDatabase,
9
+ resetDatabase,
10
+ } from './helpers';
11
+ import pool from '../../src/db/client';
12
+
13
+ describe('value health route', () => {
14
+ beforeAll(async () => {
15
+ await migrateTestDatabase();
16
+ });
17
+
18
+ beforeEach(async () => {
19
+ await resetDatabase();
20
+ });
21
+
22
+ afterAll(async () => {
23
+ await resetDatabase();
24
+ await closeDatabase();
25
+ });
26
+
27
+ test('excludes setup and verification traffic from real activity and value metrics', async () => {
28
+ const workspace = await createOrgAndAgent('Setup Only Agent', 'generic');
29
+ await pool.query('UPDATE orgs SET verified_at = NOW() WHERE id = $1', [workspace.orgId]);
30
+
31
+ await pool.query(
32
+ `INSERT INTO agent_activity_events (org_id, agent_id, activity_type, status, metadata)
33
+ VALUES
34
+ ($1, $2, 'setup_test_work_claim', 'success', '{"source":"setup_test"}'),
35
+ ($1, $2, 'verification_run', 'success', '{"source":"onboarding_verification"}'),
36
+ ($1, $2, 'connection_test', 'success', '{}')`,
37
+ [workspace.orgId, workspace.agentId],
38
+ );
39
+ await pool.query(
40
+ `INSERT INTO work_claim_events (event_type, agent_id, org_id, metadata)
41
+ VALUES ('blocked', $1, $2, '{"source":"setup_test"}')`,
42
+ [workspace.agentId, workspace.orgId],
43
+ );
44
+
45
+ const response = await api
46
+ .get('/v1/value/health')
47
+ .set('Cookie', getWorkspaceCookie(workspace.orgId))
48
+ .expect(200);
49
+
50
+ expect(response.body.summary).toMatchObject({
51
+ conflicts_prevented_7d: 0,
52
+ blocked_work_runs_7d: 0,
53
+ calendar_conflicts_7d: 0,
54
+ api_calls_7d: 0,
55
+ errors_7d: 0,
56
+ latest_real_activity_at: null,
57
+ });
58
+ expect(response.body.agent_health[0]).toMatchObject({
59
+ agent_id: workspace.agentId,
60
+ status: 'never_connected',
61
+ });
62
+ expect(response.body.next_best_action).toBe('connect_first_agent');
63
+ });
64
+
65
+ test('counts real blocked work runs, calendar conflicts, and recent API activity', async () => {
66
+ const workspace = await createOrgAndAgent('Real Agent', 'external_meeting');
67
+ await pool.query('UPDATE orgs SET verified_at = NOW() WHERE id = $1', [workspace.orgId]);
68
+
69
+ await pool.query(
70
+ `INSERT INTO agent_activity_events (org_id, agent_id, activity_type, status, metadata)
71
+ VALUES ($1, $2, 'work_check', 'success', '{}')`,
72
+ [workspace.orgId, workspace.agentId],
73
+ );
74
+ await pool.query(
75
+ `INSERT INTO work_claim_events (event_type, agent_id, org_id, metadata)
76
+ VALUES ('blocked', $1, $2, '{}')`,
77
+ [workspace.agentId, workspace.orgId],
78
+ );
79
+ const hold = await pool.query<{ id: string }>(
80
+ `INSERT INTO holds (agent_id, org_id, start_at, end_at, status)
81
+ VALUES ($1, $2, NOW() + INTERVAL '1 day', NOW() + INTERVAL '1 day 30 minutes', 'confirmed')
82
+ RETURNING id`,
83
+ [workspace.agentId, workspace.orgId],
84
+ );
85
+ await pool.query(
86
+ `INSERT INTO hold_events (hold_id, event_type, agent_id, org_id, metadata)
87
+ VALUES ($1, 'conflict_won', $2, $3, '{}')`,
88
+ [hold.rows[0].id, workspace.agentId, workspace.orgId],
89
+ );
90
+
91
+ const response = await api
92
+ .get('/v1/value/health')
93
+ .set('Cookie', getWorkspaceCookie(workspace.orgId))
94
+ .expect(200);
95
+
96
+ expect(response.body.summary).toMatchObject({
97
+ conflicts_prevented_7d: 2,
98
+ blocked_work_runs_7d: 1,
99
+ would_have_blocked_runs_7d: 0,
100
+ calendar_conflicts_7d: 1,
101
+ api_calls_7d: 1,
102
+ errors_7d: 0,
103
+ });
104
+ expect(response.body.summary.latest_real_activity_at).toEqual(expect.any(String));
105
+ expect(response.body.agent_health[0]).toMatchObject({
106
+ agent_id: workspace.agentId,
107
+ status: 'healthy',
108
+ mcp_status: 'offline',
109
+ });
110
+ expect(response.body.next_best_action).toBe('none');
111
+ });
112
+
113
+ test('counts shadow blocked runs separately from prevented conflicts', async () => {
114
+ const workspace = await createOrgAndAgent('Shadow Agent', 'generic');
115
+ await pool.query('UPDATE orgs SET verified_at = NOW() WHERE id = $1', [workspace.orgId]);
116
+
117
+ await pool.query(
118
+ `INSERT INTO agent_activity_events (org_id, agent_id, activity_type, status, metadata)
119
+ VALUES ($1, $2, 'automation_shadow_blocked', 'success', '{"source":"shadow_mode"}')`,
120
+ [workspace.orgId, workspace.agentId],
121
+ );
122
+ await pool.query(
123
+ `INSERT INTO work_claim_events (event_type, agent_id, org_id, metadata)
124
+ VALUES ('shadow_blocked', $1, $2, '{"source":"shadow_mode"}')`,
125
+ [workspace.agentId, workspace.orgId],
126
+ );
127
+
128
+ const response = await api
129
+ .get('/v1/value/health')
130
+ .set('Cookie', getWorkspaceCookie(workspace.orgId))
131
+ .expect(200);
132
+
133
+ expect(response.body.summary).toMatchObject({
134
+ conflicts_prevented_7d: 0,
135
+ blocked_work_runs_7d: 0,
136
+ would_have_blocked_runs_7d: 1,
137
+ api_calls_7d: 1,
138
+ });
139
+ expect(response.body.agent_health[0]).toMatchObject({
140
+ agent_id: workspace.agentId,
141
+ status: 'healthy',
142
+ });
143
+ });
144
+
145
+ test('classifies quiet, error, and never connected agents', async () => {
146
+ const workspace = await createOrgAndAgent('Quiet Agent', 'generic');
147
+ const errorAgent = await createAgent(workspace.orgId, 'Error Agent', 'generic');
148
+ const neverAgent = await createAgent(workspace.orgId, 'Never Agent', 'generic');
149
+ await pool.query('UPDATE orgs SET verified_at = NOW() WHERE id = $1', [workspace.orgId]);
150
+
151
+ await pool.query(
152
+ `INSERT INTO agent_activity_events (org_id, agent_id, activity_type, status, created_at, metadata)
153
+ VALUES
154
+ ($1, $2, 'work_check', 'success', NOW() - INTERVAL '4 days', '{}'),
155
+ ($1, $3, 'work_claim', 'error', NOW() - INTERVAL '1 hour', '{}')`,
156
+ [workspace.orgId, workspace.agentId, errorAgent.agentId],
157
+ );
158
+
159
+ const response = await api
160
+ .get('/v1/value/health')
161
+ .set('Cookie', getWorkspaceCookie(workspace.orgId))
162
+ .expect(200);
163
+
164
+ const byId = new Map(response.body.agent_health.map((agent: { agent_id: string; status: string }) => [agent.agent_id, agent.status]));
165
+ expect(byId.get(workspace.agentId)).toBe('quiet');
166
+ expect(byId.get(errorAgent.agentId)).toBe('error');
167
+ expect(byId.get(neverAgent.agentId)).toBe('never_connected');
168
+ expect(response.body.next_best_action).toBe('review_errors');
169
+ });
170
+
171
+ test('uses agent last_seen_at for SDK and REST connectivity without requiring MCP heartbeat', async () => {
172
+ const workspace = await createOrgAndAgent('SDK Agent', 'generic');
173
+ await pool.query('UPDATE orgs SET verified_at = NOW() WHERE id = $1', [workspace.orgId]);
174
+ await pool.query('UPDATE agents SET last_seen_at = NOW() WHERE id = $1', [workspace.agentId]);
175
+
176
+ const response = await api
177
+ .get('/v1/value/health')
178
+ .set('Cookie', getWorkspaceCookie(workspace.orgId))
179
+ .expect(200);
180
+
181
+ expect(response.body.agent_health[0]).toMatchObject({
182
+ agent_id: workspace.agentId,
183
+ status: 'healthy',
184
+ last_seen_at: expect.any(String),
185
+ last_activity_at: null,
186
+ mcp_status: 'offline',
187
+ });
188
+ });
189
+
190
+ test('classifies stale last_seen_at as quiet when no newer activity exists', async () => {
191
+ const workspace = await createOrgAndAgent('Old SDK Agent', 'generic');
192
+ await pool.query('UPDATE orgs SET verified_at = NOW() WHERE id = $1', [workspace.orgId]);
193
+ await pool.query("UPDATE agents SET last_seen_at = NOW() - INTERVAL '4 days' WHERE id = $1", [workspace.agentId]);
194
+
195
+ const response = await api
196
+ .get('/v1/value/health')
197
+ .set('Cookie', getWorkspaceCookie(workspace.orgId))
198
+ .expect(200);
199
+
200
+ expect(response.body.agent_health[0]).toMatchObject({
201
+ agent_id: workspace.agentId,
202
+ status: 'quiet',
203
+ mcp_status: 'offline',
204
+ });
205
+ });
206
+
207
+ test('returns check_inactive_agents when workspace only has stale activity', async () => {
208
+ const workspace = await createOrgAndAgent('Stale Agent', 'generic');
209
+ await pool.query('UPDATE orgs SET verified_at = NOW() WHERE id = $1', [workspace.orgId]);
210
+ await pool.query(
211
+ `INSERT INTO agent_activity_events (org_id, agent_id, activity_type, status, created_at, metadata)
212
+ VALUES ($1, $2, 'work_check', 'success', NOW() - INTERVAL '4 days', '{}')`,
213
+ [workspace.orgId, workspace.agentId],
214
+ );
215
+
216
+ const response = await api
217
+ .get('/v1/value/health')
218
+ .set('Cookie', getWorkspaceCookie(workspace.orgId))
219
+ .expect(200);
220
+
221
+ expect(response.body.agent_health[0].status).toBe('quiet');
222
+ expect(response.body.next_best_action).toBe('check_inactive_agents');
223
+ });
224
+
225
+ test('prioritizes onboarding verification before other next actions', async () => {
226
+ const workspace = await createOrgAndAgent('Unverified Agent', 'generic');
227
+
228
+ const response = await api
229
+ .get('/v1/value/health')
230
+ .set('Cookie', getWorkspaceCookie(workspace.orgId))
231
+ .expect(200);
232
+
233
+ expect(response.body.next_best_action).toBe('run_verification');
234
+ });
235
+ });