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,490 @@
1
+ import type { Pool, PoolClient } from 'pg';
2
+ import { addHours, addMinutes } from 'date-fns';
3
+ import type { Agent, AgentPreferences, Hold, WorkClaim, WorkResource } from '../types';
4
+ import { determineWinnerWithReason, type ConflictRuleApplied } from './conflict';
5
+
6
+ export type WorkCheckResult = {
7
+ status: 'available' | 'would_win' | 'would_lose';
8
+ winning_claim_id: string | null;
9
+ losing_claim_ids: string[];
10
+ rule_applied: ConflictRuleApplied;
11
+ suggested_retry_at: Date | null;
12
+ };
13
+
14
+ export const MAX_WORK_LOCK_MINUTES = 4 * 60;
15
+ export const DEFAULT_WORK_LEASE_MINUTES = 45;
16
+ export const MAX_WORK_EXTENSION_MINUTES = 120;
17
+
18
+ export type WorkActor = {
19
+ actor_type?: string | null;
20
+ actor_id?: string | null;
21
+ actor_label?: string | null;
22
+ };
23
+
24
+ export type ClaimWorkSlotResult =
25
+ | {
26
+ outcome: 'blocked';
27
+ resource: WorkResource;
28
+ resource_existed: boolean;
29
+ claim: null;
30
+ blocking_claim: (WorkClaim & Record<string, unknown>) | null;
31
+ status: 'would_lose';
32
+ winning_claim_id: string | null;
33
+ losing_claim_ids: string[];
34
+ rule_applied: ConflictRuleApplied;
35
+ suggested_retry_at: Date | null;
36
+ }
37
+ | {
38
+ outcome: 'claimed';
39
+ resource: WorkResource;
40
+ resource_existed: boolean;
41
+ claim: WorkClaim;
42
+ status: 'available' | 'would_win';
43
+ winning_claim_id: null;
44
+ losing_claim_ids: string[];
45
+ rule_applied: ConflictRuleApplied;
46
+ suggested_retry_at: null;
47
+ }
48
+ | {
49
+ outcome: 'aborted';
50
+ resource: WorkResource;
51
+ resource_existed: boolean;
52
+ claim: null;
53
+ status: WorkCheckResult['status'];
54
+ winning_claim_id: string | null;
55
+ losing_claim_ids: string[];
56
+ rule_applied: ConflictRuleApplied;
57
+ suggested_retry_at: Date | null;
58
+ };
59
+
60
+ export function workWindow(input: { start_at?: string; duration_minutes?: number }) {
61
+ const startAt = input.start_at ? new Date(input.start_at) : new Date();
62
+ const duration = input.duration_minutes ?? DEFAULT_WORK_LEASE_MINUTES;
63
+ return { startAt, endAt: addMinutes(startAt, duration), duration };
64
+ }
65
+
66
+ export function maxWorkExpiresAt(startAt: Date): Date {
67
+ return addHours(startAt, 4);
68
+ }
69
+
70
+ export function initialWorkExpiresAt(startAt: Date, endAt: Date): Date {
71
+ const max = maxWorkExpiresAt(startAt);
72
+ return endAt < max ? endAt : max;
73
+ }
74
+
75
+ export function secondsUntilExpiry(expiresAt: Date): number {
76
+ return Math.max(0, Math.floor((expiresAt.getTime() - Date.now()) / 1000));
77
+ }
78
+
79
+ export async function upsertWorkResource(
80
+ client: PoolClient,
81
+ input: { org_id: string; resource_type: string; resource_key: string; label?: string },
82
+ ): Promise<WorkResource> {
83
+ const label = input.label || input.resource_key;
84
+ const result = await client.query<WorkResource>(
85
+ `INSERT INTO work_resources (org_id, resource_type, resource_key, label)
86
+ VALUES ($1, $2, $3, $4)
87
+ ON CONFLICT (org_id, resource_type, resource_key)
88
+ DO UPDATE SET label = EXCLUDED.label
89
+ RETURNING *`,
90
+ [input.org_id, input.resource_type, input.resource_key, label],
91
+ );
92
+ return result.rows[0];
93
+ }
94
+
95
+ export async function insertWorkClaimEvent(input: {
96
+ db: Pool | PoolClient;
97
+ claim_id?: string | null;
98
+ event_type: 'created' | 'blocked' | 'superseded' | 'released' | 'expired' | 'extended' | 'shadow_blocked' | 'shadow_allowed';
99
+ agent_id: string;
100
+ org_id: string;
101
+ resource_id?: string | null;
102
+ conflicting_claim_id?: string | null;
103
+ rule_applied?: string | null;
104
+ metadata?: Record<string, unknown> | null;
105
+ actor_type?: string | null;
106
+ actor_id?: string | null;
107
+ actor_label?: string | null;
108
+ }) {
109
+ await input.db.query(
110
+ `INSERT INTO work_claim_events (
111
+ claim_id,
112
+ event_type,
113
+ agent_id,
114
+ org_id,
115
+ resource_id,
116
+ conflicting_claim_id,
117
+ rule_applied,
118
+ metadata,
119
+ actor_type,
120
+ actor_id,
121
+ actor_label
122
+ )
123
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
124
+ [
125
+ input.claim_id ?? null,
126
+ input.event_type,
127
+ input.agent_id,
128
+ input.org_id,
129
+ input.resource_id ?? null,
130
+ input.conflicting_claim_id ?? null,
131
+ input.rule_applied ?? null,
132
+ JSON.stringify(input.metadata ?? {}),
133
+ input.actor_type ?? null,
134
+ input.actor_id ?? null,
135
+ input.actor_label ?? null,
136
+ ],
137
+ );
138
+ }
139
+
140
+ export async function expireOldWorkClaims(client: Pool | PoolClient, orgId: string): Promise<WorkClaim[]> {
141
+ const result = await client.query<WorkClaim>(
142
+ `UPDATE work_claims
143
+ SET status = 'expired'
144
+ WHERE org_id = $1
145
+ AND status = 'active'
146
+ AND expires_at < NOW()
147
+ RETURNING *`,
148
+ [orgId],
149
+ );
150
+ return result.rows;
151
+ }
152
+
153
+ export async function expireAndLogWorkClaims(client: Pool | PoolClient, orgId: string) {
154
+ const expired = await expireOldWorkClaims(client, orgId);
155
+ for (const claim of expired) {
156
+ await insertWorkClaimEvent({
157
+ db: client,
158
+ claim_id: claim.id,
159
+ event_type: 'expired',
160
+ agent_id: claim.agent_id,
161
+ org_id: claim.org_id,
162
+ resource_id: claim.resource_id,
163
+ actor_type: 'system',
164
+ actor_label: 'Availsync',
165
+ metadata: {
166
+ expired_at: new Date().toISOString(),
167
+ expired_by: 'expires_at',
168
+ previous_expires_at: claim.expires_at.toISOString(),
169
+ planned_end_at: claim.end_at.toISOString(),
170
+ },
171
+ });
172
+ }
173
+ return expired;
174
+ }
175
+
176
+ function asHold(claim: WorkClaim): Hold {
177
+ return {
178
+ id: claim.id,
179
+ agent_id: claim.agent_id,
180
+ org_id: claim.org_id,
181
+ start_at: claim.start_at,
182
+ end_at: claim.end_at,
183
+ reason: claim.reason,
184
+ status: claim.status === 'active' ? 'confirmed' : 'released',
185
+ metadata: claim.metadata,
186
+ created_at: claim.created_at,
187
+ };
188
+ }
189
+
190
+ export async function previewWorkClaim(
191
+ client: Pool | PoolClient,
192
+ input: {
193
+ org_id: string;
194
+ agent_id: string;
195
+ resource_id: string;
196
+ start_at: Date;
197
+ end_at: Date;
198
+ reason?: string;
199
+ metadata?: Record<string, unknown>;
200
+ },
201
+ ): Promise<WorkCheckResult> {
202
+ const conflicts = await client.query<WorkClaim>(
203
+ `SELECT *
204
+ FROM work_claims
205
+ WHERE org_id = $1
206
+ AND resource_id = $2
207
+ AND status = 'active'
208
+ AND expires_at >= NOW()
209
+ AND start_at < $4
210
+ AND expires_at > $3
211
+ ORDER BY created_at ASC`,
212
+ [input.org_id, input.resource_id, input.start_at, input.end_at],
213
+ );
214
+
215
+ if (conflicts.rows.length === 0) {
216
+ return {
217
+ status: 'available',
218
+ winning_claim_id: null,
219
+ losing_claim_ids: [],
220
+ rule_applied: 'none',
221
+ suggested_retry_at: null,
222
+ };
223
+ }
224
+
225
+ const agentIds = Array.from(new Set([input.agent_id, ...conflicts.rows.map((claim) => claim.agent_id)]));
226
+ const agents = await client.query<Agent>('SELECT * FROM agents WHERE id = ANY($1::uuid[])', [agentIds]);
227
+ const preferences = await client.query<AgentPreferences>(
228
+ 'SELECT * FROM agent_preferences WHERE agent_id = ANY($1::uuid[])',
229
+ [agentIds],
230
+ );
231
+ const newAgent = agents.rows.find((agent) => agent.id === input.agent_id);
232
+ if (!newAgent) {
233
+ throw new Error(`Agent not found: ${input.agent_id}`);
234
+ }
235
+
236
+ const syntheticClaim: WorkClaim = {
237
+ id: 'new',
238
+ agent_id: input.agent_id,
239
+ org_id: input.org_id,
240
+ resource_id: input.resource_id,
241
+ start_at: input.start_at,
242
+ end_at: input.end_at,
243
+ expires_at: input.end_at,
244
+ last_renewed_at: new Date(),
245
+ renewal_count: 0,
246
+ idempotency_key: null,
247
+ reason: input.reason,
248
+ status: 'active',
249
+ metadata: input.metadata,
250
+ created_at: new Date(8640000000000000),
251
+ };
252
+
253
+ const losingClaimIds: string[] = [];
254
+ let lastRule: ConflictRuleApplied = 'none';
255
+
256
+ for (const existingClaim of conflicts.rows) {
257
+ const existingAgent = agents.rows.find((agent) => agent.id === existingClaim.agent_id);
258
+ if (!existingAgent) {
259
+ throw new Error(`Agent not found: ${existingClaim.agent_id}`);
260
+ }
261
+
262
+ const result = determineWinnerWithReason(
263
+ asHold(syntheticClaim),
264
+ newAgent,
265
+ asHold(existingClaim),
266
+ existingAgent,
267
+ preferences.rows,
268
+ );
269
+ lastRule = result.rule_applied;
270
+
271
+ if (result.winner.id === existingClaim.id) {
272
+ return {
273
+ status: 'would_lose',
274
+ winning_claim_id: existingClaim.id,
275
+ losing_claim_ids: [],
276
+ rule_applied: lastRule,
277
+ suggested_retry_at: existingClaim.expires_at,
278
+ };
279
+ }
280
+
281
+ losingClaimIds.push(existingClaim.id);
282
+ }
283
+
284
+ return {
285
+ status: 'would_win',
286
+ winning_claim_id: null,
287
+ losing_claim_ids: losingClaimIds,
288
+ rule_applied: lastRule,
289
+ suggested_retry_at: null,
290
+ };
291
+ }
292
+
293
+ export async function claimWorkSlot(
294
+ client: PoolClient,
295
+ input: {
296
+ org_id: string;
297
+ agent_id: string;
298
+ resource_type: string;
299
+ resource_key: string;
300
+ label?: string;
301
+ start_at: Date;
302
+ end_at: Date;
303
+ reason?: string | null;
304
+ metadata?: Record<string, unknown>;
305
+ idempotency_key?: string | null;
306
+ actor?: WorkActor;
307
+ beforeMutation?: (context: {
308
+ resource: WorkResource;
309
+ resource_existed: boolean;
310
+ result: WorkCheckResult;
311
+ }) => Promise<boolean>;
312
+ },
313
+ ): Promise<ClaimWorkSlotResult> {
314
+ await client.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
315
+ `${input.org_id}:${input.resource_type}:${input.resource_key}`,
316
+ ]);
317
+ await expireAndLogWorkClaims(client, input.org_id);
318
+
319
+ const existingResource = await client.query<Pick<WorkResource, 'id'>>(
320
+ `SELECT id
321
+ FROM work_resources
322
+ WHERE org_id = $1
323
+ AND resource_type = $2
324
+ AND resource_key = $3
325
+ LIMIT 1`,
326
+ [input.org_id, input.resource_type, input.resource_key],
327
+ );
328
+ const resourceExisted = existingResource.rows.length > 0;
329
+
330
+ const resource = await upsertWorkResource(client, {
331
+ org_id: input.org_id,
332
+ resource_type: input.resource_type,
333
+ resource_key: input.resource_key,
334
+ label: input.label,
335
+ });
336
+
337
+ const result = await previewWorkClaim(client, {
338
+ org_id: input.org_id,
339
+ agent_id: input.agent_id,
340
+ resource_id: resource.id,
341
+ start_at: input.start_at,
342
+ end_at: input.end_at,
343
+ reason: input.reason ?? undefined,
344
+ metadata: input.metadata,
345
+ });
346
+
347
+ if (input.beforeMutation && !(await input.beforeMutation({ resource, resource_existed: resourceExisted, result }))) {
348
+ return {
349
+ outcome: 'aborted',
350
+ resource,
351
+ resource_existed: resourceExisted,
352
+ claim: null,
353
+ status: result.status,
354
+ winning_claim_id: result.winning_claim_id,
355
+ losing_claim_ids: result.losing_claim_ids,
356
+ rule_applied: result.rule_applied,
357
+ suggested_retry_at: result.suggested_retry_at,
358
+ };
359
+ }
360
+
361
+ const metadata = {
362
+ ...(input.metadata ?? {}),
363
+ resource_type: resource.resource_type,
364
+ resource_key: resource.resource_key,
365
+ start_at: input.start_at.toISOString(),
366
+ end_at: input.end_at.toISOString(),
367
+ };
368
+
369
+ if (result.status === 'would_lose') {
370
+ const blocking = result.winning_claim_id
371
+ ? await client.query<WorkClaim & Record<string, unknown>>(
372
+ `SELECT
373
+ work_claims.*,
374
+ work_resources.resource_type,
375
+ work_resources.resource_key,
376
+ work_resources.label AS resource_label,
377
+ agents.name AS agent_name
378
+ FROM work_claims
379
+ JOIN work_resources ON work_resources.id = work_claims.resource_id
380
+ JOIN agents ON agents.id = work_claims.agent_id
381
+ WHERE work_claims.id = $1`,
382
+ [result.winning_claim_id],
383
+ )
384
+ : { rows: [] };
385
+
386
+ await insertWorkClaimEvent({
387
+ db: client,
388
+ event_type: 'blocked',
389
+ agent_id: input.agent_id,
390
+ org_id: input.org_id,
391
+ resource_id: resource.id,
392
+ conflicting_claim_id: result.winning_claim_id,
393
+ rule_applied: result.rule_applied,
394
+ metadata,
395
+ ...(input.actor ?? {}),
396
+ });
397
+
398
+ return {
399
+ outcome: 'blocked',
400
+ resource,
401
+ resource_existed: resourceExisted,
402
+ claim: null,
403
+ blocking_claim: blocking.rows[0] ?? null,
404
+ status: 'would_lose',
405
+ winning_claim_id: result.winning_claim_id,
406
+ losing_claim_ids: result.losing_claim_ids,
407
+ rule_applied: result.rule_applied,
408
+ suggested_retry_at: result.suggested_retry_at,
409
+ };
410
+ }
411
+
412
+ if (result.losing_claim_ids.length > 0) {
413
+ await client.query(
414
+ `UPDATE work_claims
415
+ SET status = 'superseded'
416
+ WHERE id = ANY($1::uuid[])
417
+ AND org_id = $2`,
418
+ [result.losing_claim_ids, input.org_id],
419
+ );
420
+ }
421
+
422
+ const created = await client.query<WorkClaim>(
423
+ `INSERT INTO work_claims (
424
+ org_id,
425
+ agent_id,
426
+ resource_id,
427
+ start_at,
428
+ end_at,
429
+ status,
430
+ reason,
431
+ metadata,
432
+ expires_at,
433
+ last_renewed_at,
434
+ renewal_count,
435
+ idempotency_key
436
+ )
437
+ VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, NOW(), 0, $9)
438
+ RETURNING *`,
439
+ [
440
+ input.org_id,
441
+ input.agent_id,
442
+ resource.id,
443
+ input.start_at,
444
+ input.end_at,
445
+ input.reason ?? null,
446
+ JSON.stringify(input.metadata ?? {}),
447
+ initialWorkExpiresAt(input.start_at, input.end_at),
448
+ input.idempotency_key ?? null,
449
+ ],
450
+ );
451
+
452
+ await insertWorkClaimEvent({
453
+ db: client,
454
+ claim_id: created.rows[0].id,
455
+ event_type: 'created',
456
+ agent_id: input.agent_id,
457
+ org_id: input.org_id,
458
+ resource_id: resource.id,
459
+ rule_applied: result.rule_applied,
460
+ metadata,
461
+ ...(input.actor ?? {}),
462
+ });
463
+
464
+ for (const losingClaimId of result.losing_claim_ids) {
465
+ await insertWorkClaimEvent({
466
+ db: client,
467
+ claim_id: losingClaimId,
468
+ event_type: 'superseded',
469
+ agent_id: input.agent_id,
470
+ org_id: input.org_id,
471
+ resource_id: resource.id,
472
+ conflicting_claim_id: created.rows[0].id,
473
+ rule_applied: result.rule_applied,
474
+ metadata: { ...(input.metadata ?? {}), superseded_by_claim_id: created.rows[0].id },
475
+ ...(input.actor ?? {}),
476
+ });
477
+ }
478
+
479
+ return {
480
+ outcome: 'claimed',
481
+ resource,
482
+ resource_existed: resourceExisted,
483
+ claim: created.rows[0],
484
+ status: result.status,
485
+ winning_claim_id: null,
486
+ losing_claim_ids: result.losing_claim_ids,
487
+ rule_applied: result.rule_applied,
488
+ suggested_retry_at: null,
489
+ };
490
+ }
@@ -0,0 +1,17 @@
1
+ import { Pool } from 'pg';
2
+
3
+ const connectionString =
4
+ process.env.NODE_ENV === 'test'
5
+ ? process.env.TEST_DATABASE_URL || process.env.DATABASE_URL
6
+ : process.env.DATABASE_URL;
7
+ const useSSL = connectionString?.includes('sslmode=require');
8
+
9
+ const pool = new Pool({
10
+ connectionString,
11
+ max: 10,
12
+ idleTimeoutMillis: 30000,
13
+ connectionTimeoutMillis: 10000,
14
+ ssl: useSSL ? { rejectUnauthorized: false } : false,
15
+ });
16
+
17
+ export default pool;
@@ -0,0 +1,88 @@
1
+ CREATE EXTENSION IF NOT EXISTS "pgcrypto";
2
+
3
+ CREATE TABLE IF NOT EXISTS orgs (
4
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
5
+ name TEXT NOT NULL,
6
+ plan TEXT NOT NULL DEFAULT 'free' CHECK (plan IN ('free', 'individual', 'team')),
7
+ agent_limit INTEGER NOT NULL DEFAULT 3,
8
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
9
+ );
10
+
11
+ CREATE TABLE IF NOT EXISTS agents (
12
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
13
+ org_id UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
14
+ name TEXT NOT NULL,
15
+ api_key_hash TEXT NOT NULL,
16
+ agent_type TEXT NOT NULL DEFAULT 'generic'
17
+ CHECK (agent_type IN ('external_meeting', 'internal', 'focus', 'generic')),
18
+ priority INTEGER NOT NULL DEFAULT 0,
19
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
20
+ );
21
+ CREATE INDEX IF NOT EXISTS idx_agents_org_id ON agents(org_id);
22
+
23
+ CREATE TABLE IF NOT EXISTS availability_windows (
24
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
25
+ agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
26
+ start_at TIMESTAMPTZ NOT NULL,
27
+ end_at TIMESTAMPTZ NOT NULL,
28
+ window_type TEXT NOT NULL DEFAULT 'available'
29
+ CHECK (window_type IN ('available', 'focus', 'blocked')),
30
+ recurrence TEXT,
31
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
32
+ CONSTRAINT chk_window_order CHECK (end_at > start_at)
33
+ );
34
+ CREATE INDEX IF NOT EXISTS idx_windows_agent_time
35
+ ON availability_windows(agent_id, start_at, end_at);
36
+
37
+ CREATE TABLE IF NOT EXISTS holds (
38
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
39
+ agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
40
+ org_id UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
41
+ start_at TIMESTAMPTZ NOT NULL,
42
+ end_at TIMESTAMPTZ NOT NULL,
43
+ reason TEXT,
44
+ status TEXT NOT NULL DEFAULT 'confirmed'
45
+ CHECK (status IN ('confirmed', 'superseded', 'released')),
46
+ booked_by_agent_id UUID REFERENCES agents(id),
47
+ metadata JSONB,
48
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
49
+ CONSTRAINT chk_hold_order CHECK (end_at > start_at)
50
+ );
51
+ CREATE INDEX IF NOT EXISTS idx_holds_org_time
52
+ ON holds(org_id, start_at, end_at) WHERE status = 'confirmed';
53
+ CREATE INDEX IF NOT EXISTS idx_holds_agent_id ON holds(agent_id);
54
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_holds_confirmed_exact_slot
55
+ ON holds(org_id, start_at, end_at) WHERE status = 'confirmed';
56
+
57
+ CREATE TABLE IF NOT EXISTS conflict_rules (
58
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
59
+ org_id UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
60
+ rule_type TEXT NOT NULL CHECK (rule_type IN ('priority', 'buffer', 'focus')),
61
+ config JSONB NOT NULL DEFAULT '{}',
62
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
63
+ );
64
+
65
+ CREATE TABLE IF NOT EXISTS agent_preferences (
66
+ agent_id UUID PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,
67
+ buffer_minutes_after INTEGER NOT NULL DEFAULT 15,
68
+ buffer_minutes_before INTEGER NOT NULL DEFAULT 0,
69
+ focus_blocks JSONB NOT NULL DEFAULT '[]',
70
+ priority_over_agents JSONB NOT NULL DEFAULT '[]',
71
+ booking_window_days INTEGER NOT NULL DEFAULT 30,
72
+ allow_back_to_back BOOLEAN NOT NULL DEFAULT false,
73
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
74
+ );
75
+
76
+ CREATE TABLE IF NOT EXISTS hold_events (
77
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
78
+ hold_id UUID NOT NULL REFERENCES holds(id) ON DELETE CASCADE,
79
+ event_type TEXT NOT NULL
80
+ CHECK (event_type IN ('created', 'superseded', 'released', 'conflict_won', 'conflict_lost')),
81
+ agent_id UUID NOT NULL REFERENCES agents(id),
82
+ org_id UUID NOT NULL REFERENCES orgs(id),
83
+ conflicting_hold_id UUID REFERENCES holds(id),
84
+ rule_applied TEXT,
85
+ metadata JSONB,
86
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
87
+ );
88
+ CREATE INDEX IF NOT EXISTS idx_hold_events_org ON hold_events(org_id, created_at DESC);
@@ -0,0 +1,2 @@
1
+ ALTER TABLE orgs ADD COLUMN IF NOT EXISTS stripe_customer_id TEXT;
2
+ ALTER TABLE orgs ADD COLUMN IF NOT EXISTS stripe_subscription_id TEXT;
@@ -0,0 +1,19 @@
1
+ CREATE TABLE IF NOT EXISTS workspace_users (
2
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3
+ org_id UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
4
+ email TEXT NOT NULL UNIQUE,
5
+ password_hash TEXT NOT NULL,
6
+ role TEXT NOT NULL DEFAULT 'owner' CHECK (role IN ('owner', 'admin')),
7
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
8
+ );
9
+ CREATE INDEX IF NOT EXISTS idx_workspace_users_org_id ON workspace_users(org_id);
10
+
11
+ CREATE TABLE IF NOT EXISTS workspace_sessions (
12
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
13
+ user_id UUID NOT NULL REFERENCES workspace_users(id) ON DELETE CASCADE,
14
+ token_hash TEXT NOT NULL UNIQUE,
15
+ expires_at TIMESTAMPTZ NOT NULL,
16
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
17
+ );
18
+ CREATE INDEX IF NOT EXISTS idx_workspace_sessions_user_id ON workspace_sessions(user_id);
19
+ CREATE INDEX IF NOT EXISTS idx_workspace_sessions_expires_at ON workspace_sessions(expires_at);
@@ -0,0 +1,2 @@
1
+ ALTER TABLE agents ADD COLUMN IF NOT EXISTS mcp_last_seen_at TIMESTAMPTZ;
2
+ ALTER TABLE agents ADD COLUMN IF NOT EXISTS mcp_client_name TEXT;
@@ -0,0 +1,4 @@
1
+ ALTER TABLE hold_events ADD COLUMN IF NOT EXISTS actor_type TEXT;
2
+ ALTER TABLE hold_events ADD COLUMN IF NOT EXISTS actor_id TEXT;
3
+ ALTER TABLE hold_events ADD COLUMN IF NOT EXISTS actor_label TEXT;
4
+
@@ -0,0 +1,35 @@
1
+ CREATE TABLE IF NOT EXISTS agent_activity_events (
2
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3
+ org_id UUID REFERENCES orgs(id) ON DELETE CASCADE,
4
+ agent_id UUID REFERENCES agents(id) ON DELETE SET NULL,
5
+ activity_type TEXT NOT NULL,
6
+ endpoint TEXT,
7
+ method TEXT,
8
+ status_code INTEGER,
9
+ status TEXT NOT NULL DEFAULT 'success' CHECK (status IN ('success', 'error')),
10
+ actor_type TEXT,
11
+ actor_id TEXT,
12
+ actor_label TEXT,
13
+ client_name TEXT,
14
+ latency_ms INTEGER,
15
+ error_code TEXT,
16
+ error_message TEXT,
17
+ provider TEXT,
18
+ model TEXT,
19
+ input_tokens INTEGER,
20
+ output_tokens INTEGER,
21
+ total_tokens INTEGER,
22
+ estimated_cost_usd NUMERIC(12, 6),
23
+ metadata JSONB NOT NULL DEFAULT '{}',
24
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_agent_activity_org_created
28
+ ON agent_activity_events(org_id, created_at DESC);
29
+
30
+ CREATE INDEX IF NOT EXISTS idx_agent_activity_agent_created
31
+ ON agent_activity_events(agent_id, created_at DESC);
32
+
33
+ CREATE INDEX IF NOT EXISTS idx_agent_activity_org_type_created
34
+ ON agent_activity_events(org_id, activity_type, created_at DESC);
35
+