@rubytech/create-maxy-code 0.1.342 → 0.1.344

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 (221) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +4 -1
  3. package/payload/platform/neo4j/schema.cypher +9 -5
  4. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  5. package/payload/platform/plugins/admin/PLUGIN.md +4 -1
  6. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-hostname.test.d.ts +2 -0
  7. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-hostname.test.d.ts.map +1 -0
  8. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-hostname.test.js +175 -0
  9. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-hostname.test.js.map +1 -0
  10. package/payload/platform/plugins/admin/mcp/dist/index.js +33 -0
  11. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  12. package/payload/platform/plugins/admin/mcp/dist/lib/admin-hostname.d.ts +15 -0
  13. package/payload/platform/plugins/admin/mcp/dist/lib/admin-hostname.d.ts.map +1 -0
  14. package/payload/platform/plugins/admin/mcp/dist/lib/admin-hostname.js +86 -0
  15. package/payload/platform/plugins/admin/mcp/dist/lib/admin-hostname.js.map +1 -0
  16. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +90 -1
  17. package/payload/platform/plugins/cloudflare/skills/calendar-site/SKILL.md +82 -0
  18. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/functions/api/book.ts +112 -0
  19. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/public/booking.css +100 -0
  20. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/public/booking.js +155 -0
  21. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/public/index.html +47 -0
  22. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/schema.sql +18 -0
  23. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/wrangler.toml +14 -0
  24. package/payload/platform/plugins/docs/PLUGIN.md +1 -0
  25. package/payload/platform/plugins/docs/references/admin-ui.md +26 -0
  26. package/payload/platform/plugins/docs/references/calendar-booking.md +56 -0
  27. package/payload/platform/plugins/docs/references/internals.md +2 -0
  28. package/payload/platform/plugins/scheduling/PLUGIN.md +23 -0
  29. package/payload/platform/plugins/scheduling/mcp/dist/index.js +19 -5
  30. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  31. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-notify.test.d.ts +2 -0
  32. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-notify.test.d.ts.map +1 -0
  33. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-notify.test.js +45 -0
  34. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-notify.test.js.map +1 -0
  35. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.d.ts +2 -0
  36. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.d.ts.map +1 -0
  37. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js +89 -0
  38. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js.map +1 -0
  39. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-write.test.d.ts +2 -0
  40. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-write.test.d.ts.map +1 -0
  41. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-write.test.js +118 -0
  42. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/meeting-write.test.js.map +1 -0
  43. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-classify.test.d.ts +2 -0
  44. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-classify.test.d.ts.map +1 -0
  45. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-classify.test.js +59 -0
  46. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-classify.test.js.map +1 -0
  47. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-notify.d.ts +22 -0
  48. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-notify.d.ts.map +1 -0
  49. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-notify.js +31 -0
  50. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-notify.js.map +1 -0
  51. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +28 -0
  52. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -0
  53. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +70 -0
  54. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -0
  55. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.d.ts +35 -0
  56. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.d.ts.map +1 -0
  57. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.js +123 -0
  58. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.js.map +1 -0
  59. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-classify.d.ts +23 -0
  60. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-classify.d.ts.map +1 -0
  61. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-classify.js +38 -0
  62. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-classify.js.map +1 -0
  63. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts +21 -0
  64. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts.map +1 -0
  65. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +280 -0
  66. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -0
  67. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +11 -1
  68. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  69. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +34 -2
  70. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  71. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  72. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  73. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  74. package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -0
  75. package/payload/server/{chunk-5BG6CHGH.js → chunk-QAKVFSEJ.js} +15 -0
  76. package/payload/server/maxy-edge.js +1 -1
  77. package/payload/server/public/assets/{AdminLoginScreens-Brx8CmXN.js → AdminLoginScreens-CZqsFfP0.js} +1 -1
  78. package/payload/server/public/assets/AdminShell-DnhBRZv5.js +1 -0
  79. package/payload/server/public/assets/{Checkbox-aePjWzRH.js → Checkbox-D0FwyuVg.js} +1 -1
  80. package/payload/server/public/assets/{OperatorConversations-BMIZQR9t.css → OperatorConversations-BG9GrxQU.css} +1 -1
  81. package/payload/server/public/assets/OperatorConversations-dt0q8mjv.js +9 -0
  82. package/payload/server/public/assets/admin-CCzmqd1F.js +1 -0
  83. package/payload/server/public/assets/{arc-CxLB9WCE.js → arc-DpkSQNto.js} +1 -1
  84. package/payload/server/public/assets/architecture-YZFGNWBL-Csj53u5T.js +1 -0
  85. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-D-EdJPSY.js → architectureDiagram-Q4EWVU46-Bw7Yq8O9.js} +1 -1
  86. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CXx9RgCm.js → blockDiagram-DXYQGD6D-BBtJQFSz.js} +1 -1
  87. package/payload/server/public/assets/{browser-Bp5kGgyr.js → browser-kmrJq69n.js} +1 -1
  88. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-RFBXEe0B.js → c4Diagram-AHTNJAMY-JAMgxzOr.js} +1 -1
  89. package/payload/server/public/assets/calendar-BWf5qrIH.js +1 -0
  90. package/payload/server/public/assets/channel-BUQUAUts.js +1 -0
  91. package/payload/server/public/assets/chat-C2e06_q0.js +1 -0
  92. package/payload/server/public/assets/{chunk-2KRD3SAO-DBqAFwK9.js → chunk-2KRD3SAO-eDugsc2L.js} +1 -1
  93. package/payload/server/public/assets/{chunk-336JU56O-DdnFEz6R.js → chunk-336JU56O-BXfECwdC.js} +2 -2
  94. package/payload/server/public/assets/chunk-426QAEUC-CdknReB2.js +1 -0
  95. package/payload/server/public/assets/{chunk-4BX2VUAB-cnw-3Gbs.js → chunk-4BX2VUAB-CByVeKCY.js} +1 -1
  96. package/payload/server/public/assets/{chunk-4TB4RGXK-BnqJ0bDc.js → chunk-4TB4RGXK-CpY0DcUB.js} +1 -1
  97. package/payload/server/public/assets/{chunk-55IACEB6-BwyHvvzo.js → chunk-55IACEB6-C3YxgyIb.js} +1 -1
  98. package/payload/server/public/assets/{chunk-5FUZZQ4R-DXA9U02I.js → chunk-5FUZZQ4R-Bc0d7j8A.js} +1 -1
  99. package/payload/server/public/assets/{chunk-5PVQY5BW-D78bZ_8D.js → chunk-5PVQY5BW-CTUGMRIa.js} +1 -1
  100. package/payload/server/public/assets/{chunk-67CJDMHE-CtE_Mgv7.js → chunk-67CJDMHE-CbdPnpLd.js} +1 -1
  101. package/payload/server/public/assets/{chunk-7N4EOEYR-CUZDaySl.js → chunk-7N4EOEYR-Cjl2kevz.js} +1 -1
  102. package/payload/server/public/assets/{chunk-AA7GKIK3-CYQ3jO4X.js → chunk-AA7GKIK3-BgozIh5p.js} +1 -1
  103. package/payload/server/public/assets/{chunk-BSJP7CBP-BbA_HyTR.js → chunk-BSJP7CBP-6Ufq45NW.js} +1 -1
  104. package/payload/server/public/assets/{chunk-CIAEETIT-D7_YqRhG.js → chunk-CIAEETIT-BhhYuMnA.js} +1 -1
  105. package/payload/server/public/assets/{chunk-EDXVE4YY-B23LrtSw.js → chunk-EDXVE4YY-BfaJ3yDD.js} +1 -1
  106. package/payload/server/public/assets/{chunk-ENJZ2VHE-D3fTHYKN.js → chunk-ENJZ2VHE-C7-hIGVC.js} +1 -1
  107. package/payload/server/public/assets/{chunk-FMBD7UC4-DFAA2tyh.js → chunk-FMBD7UC4-BQvvyocF.js} +1 -1
  108. package/payload/server/public/assets/{chunk-FOC6F5B3-BF4IHvlI.js → chunk-FOC6F5B3-C6rBDOxH.js} +1 -1
  109. package/payload/server/public/assets/{chunk-ICPOFSXX-DBUGemHy.js → chunk-ICPOFSXX-BjOJvkVa.js} +2 -2
  110. package/payload/server/public/assets/{chunk-K5T4RW27-rMMs08Eh.js → chunk-K5T4RW27-kKowRHhn.js} +1 -1
  111. package/payload/server/public/assets/{chunk-KGLVRYIC-BD2r8R2G.js → chunk-KGLVRYIC-bAEV_5eH.js} +1 -1
  112. package/payload/server/public/assets/{chunk-LIHQZDEY-D4sYTTwM.js → chunk-LIHQZDEY-BLvsQAfg.js} +1 -1
  113. package/payload/server/public/assets/{chunk-ORNJ4GCN-CAs_uFzb.js → chunk-ORNJ4GCN-BzYZfPZ7.js} +1 -1
  114. package/payload/server/public/assets/{chunk-OYMX7WX6-Fkp-2ORc.js → chunk-OYMX7WX6-CL7WRw42.js} +1 -1
  115. package/payload/server/public/assets/chunk-QZHKN3VN-8tHoEDE9.js +1 -0
  116. package/payload/server/public/assets/{chunk-U2HBQHQK-D_pxZh25.js → chunk-U2HBQHQK-BdcVGjau.js} +1 -1
  117. package/payload/server/public/assets/{chunk-X2U36JSP-CeVmvvRf.js → chunk-X2U36JSP-C0CL_Jin.js} +1 -1
  118. package/payload/server/public/assets/{chunk-XPW4576I-iWP54fKp.js → chunk-XPW4576I-CsV7CUxH.js} +1 -1
  119. package/payload/server/public/assets/{chunk-YZCP3GAM-CNtbWJNN.js → chunk-YZCP3GAM-Cj0_fLkR.js} +1 -1
  120. package/payload/server/public/assets/{chunk-ZZ45TVLE-BmlFjU_9.js → chunk-ZZ45TVLE-DLuquyoY.js} +1 -1
  121. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BEGZ3DLg.js +1 -0
  122. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DsLxnm5U.js +1 -0
  123. package/payload/server/public/assets/clone-uiXbH_kh.js +1 -0
  124. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-C7YmF1Pg.js → cose-bilkent-S5V4N54A-BU3EOLXX.js} +1 -1
  125. package/payload/server/public/assets/{dagre-BAmiDDtR.js → dagre-BRq8C9NU.js} +1 -1
  126. package/payload/server/public/assets/{dagre-KV5264BT-Ca3ESJhc.js → dagre-KV5264BT-CoCAYhFc.js} +1 -1
  127. package/payload/server/public/assets/data-CZKWSjEH.js +1 -0
  128. package/payload/server/public/assets/{diagram-5BDNPKRD-BgT1ORf2.js → diagram-5BDNPKRD-DBy5GNaQ.js} +1 -1
  129. package/payload/server/public/assets/{diagram-G4DWMVQ6-BTaUIiem.js → diagram-G4DWMVQ6-l93MsBLv.js} +1 -1
  130. package/payload/server/public/assets/{diagram-MMDJMWI5-Bdb2g6Uo.js → diagram-MMDJMWI5-BIfXkVlL.js} +1 -1
  131. package/payload/server/public/assets/{diagram-TYMM5635-CkLGC0zZ.js → diagram-TYMM5635-BuTJLBny.js} +1 -1
  132. package/payload/server/public/assets/{dist-Bkbhs3jw.js → dist-CtUNt3w8.js} +1 -1
  133. package/payload/server/public/assets/{erDiagram-SMLLAGMA-D_sj4DMg.js → erDiagram-SMLLAGMA-BwPxWDbP.js} +1 -1
  134. package/payload/server/public/assets/{flatten-E2Sr0FeD.js → flatten-DtM0XAMt.js} +1 -1
  135. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-iLERoZTe.js → flowDiagram-DWJPFMVM-BhPtHXMR.js} +1 -1
  136. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-D60hjMBy.js → ganttDiagram-T4ZO3ILL-BlXjprdD.js} +1 -1
  137. package/payload/server/public/assets/gitGraph-7Q5UKJZL-D_2KrZoP.js +1 -0
  138. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-Di0XM6fi.js → gitGraphDiagram-UUTBAWPF-BW36BDiu.js} +1 -1
  139. package/payload/server/public/assets/{graph-Bnsvbnkf.js → graph-BxErrIf_.js} +3 -3
  140. package/payload/server/public/assets/{graph-labels-jduMtwXb.js → graph-labels-Ch5_g_JF.js} +1 -1
  141. package/payload/server/public/assets/{graphlib-C8fUP7uV.js → graphlib-Bjg3Tq-k.js} +1 -1
  142. package/payload/server/public/assets/info-OMHHGYJF-BP2TNwQ4.js +1 -0
  143. package/payload/server/public/assets/infoDiagram-42DDH7IO-D1kDtBOY.js +2 -0
  144. package/payload/server/public/assets/{isEmpty-CLz-UprQ.js → isEmpty-D__iH1WQ.js} +1 -1
  145. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CwgI4B5Z.js → ishikawaDiagram-UXIWVN3A-CmB-bA_O.js} +1 -1
  146. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CxBu-tze.js → journeyDiagram-VCZTEJTY-Bt1fJKmc.js} +1 -1
  147. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-D2qlheA5.js → kanban-definition-6JOO6SKY-DVbksz_J.js} +1 -1
  148. package/payload/server/public/assets/{line-Bdod7uzQ.js → line-BX2Ugx3B.js} +1 -1
  149. package/payload/server/public/assets/{linear-BiVe6QzC.js → linear-Bdc-QJWI.js} +1 -1
  150. package/payload/server/public/assets/{mermaid-parser.core-xZN7hC6M.js → mermaid-parser.core-qv1e-J5x.js} +2 -2
  151. package/payload/server/public/assets/{mermaid.core-D0IylAac.js → mermaid.core-C_d9YQX_.js} +3 -3
  152. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-J_Szhwf1.js → mindmap-definition-QFDTVHPH-DwKGvF14.js} +1 -1
  153. package/payload/server/public/assets/operator-CdlBsCdn.js +1 -0
  154. package/payload/server/public/assets/{ordinal-5RtyPJVt.js → ordinal-BRQ5CzZY.js} +1 -1
  155. package/payload/server/public/assets/packet-4T2RLAQJ-BDg8fde8.js +1 -0
  156. package/payload/server/public/assets/{page-BT9hkXHm.js → page-DzsVxvM6.js} +3 -3
  157. package/payload/server/public/assets/pie-ZZUOXDRM-CXDyYMkZ.js +1 -0
  158. package/payload/server/public/assets/{pieDiagram-DEJITSTG-CV5DxUVx.js → pieDiagram-DEJITSTG-dUvQC_4S.js} +1 -1
  159. package/payload/server/public/assets/{public-DvL1Zov1.js → public-BzVhn2va.js} +1 -1
  160. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-BsFMVizF.js → quadrantDiagram-34T5L4WZ-CebX_oPa.js} +1 -1
  161. package/payload/server/public/assets/radar-PYXPWWZC-xen_zv8F.js +1 -0
  162. package/payload/server/public/assets/{reduce-B3Mw_0s1.js → reduce-CkcXOmmJ.js} +1 -1
  163. package/payload/server/public/assets/{requirementDiagram-MS252O5E-C5eRRvGc.js → requirementDiagram-MS252O5E-h2euIqGj.js} +1 -1
  164. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-CJYv5gpw.js → sankeyDiagram-XADWPNL6-DCii3fkF.js} +1 -1
  165. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-B25563_2.js → sequenceDiagram-FGHM5R23-CQSGYmlq.js} +1 -1
  166. package/payload/server/public/assets/{src-BKOAoFQc.js → src-48kCpScq.js} +1 -1
  167. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-BLyNJYOA.js → stateDiagram-FHFEXIEX-CVtU0iXH.js} +1 -1
  168. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BywcXjtb.js +1 -0
  169. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-D-HgOM6Q.js → timeline-definition-GMOUNBTQ-ChvsBltI.js} +1 -1
  170. package/payload/server/public/assets/treeView-SZITEDCU-2FBf_1It.js +1 -0
  171. package/payload/server/public/assets/treemap-W4RFUUIX-BDdb30P-.js +1 -0
  172. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Cw1JElYp.js → vennDiagram-DHZGUBPP-e-EZiiba.js} +1 -1
  173. package/payload/server/public/assets/wardley-RL74JXVD-BLq5MxjR.js +1 -0
  174. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BqMWzEko.js → wardleyDiagram-NUSXRM2D-4JaRn1s5.js} +1 -1
  175. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DBp71TLT.js → xychartDiagram-5P7HB3ND-kxwmCNuM.js} +1 -1
  176. package/payload/server/public/brand/maxy-app-icon-192.png +0 -0
  177. package/payload/server/public/brand/maxy-app-icon-512.png +0 -0
  178. package/payload/server/public/brand/maxy-app-icon-maskable-512.png +0 -0
  179. package/payload/server/public/browser.html +5 -5
  180. package/payload/server/public/calendar.html +17 -0
  181. package/payload/server/public/chat.html +8 -8
  182. package/payload/server/public/data.html +5 -5
  183. package/payload/server/public/graph.html +7 -7
  184. package/payload/server/public/index.html +8 -8
  185. package/payload/server/public/operator.html +10 -10
  186. package/payload/server/public/public.html +8 -8
  187. package/payload/server/server.js +623 -190
  188. package/payload/server/public/assets/AdminShell-CHZMDX2u.js +0 -1
  189. package/payload/server/public/assets/OperatorConversations-DpjPPIOp.js +0 -9
  190. package/payload/server/public/assets/admin-DIDvfti6.js +0 -1
  191. package/payload/server/public/assets/architecture-YZFGNWBL-DN-MYxff.js +0 -1
  192. package/payload/server/public/assets/channel-y27xl53N.js +0 -1
  193. package/payload/server/public/assets/chat-C0IWx7FL.js +0 -1
  194. package/payload/server/public/assets/chunk-426QAEUC-D8Iwaegm.js +0 -1
  195. package/payload/server/public/assets/chunk-QZHKN3VN-qHqDpwz3.js +0 -1
  196. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DgVoYWhO.js +0 -1
  197. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-C0WNP3uN.js +0 -1
  198. package/payload/server/public/assets/clone-D50F_pvg.js +0 -1
  199. package/payload/server/public/assets/data-RsMye_06.js +0 -1
  200. package/payload/server/public/assets/gitGraph-7Q5UKJZL-D-L2yzYf.js +0 -1
  201. package/payload/server/public/assets/info-OMHHGYJF-BQn6jndO.js +0 -1
  202. package/payload/server/public/assets/infoDiagram-42DDH7IO-BwMdbiXg.js +0 -2
  203. package/payload/server/public/assets/operator-9K-TElDd.js +0 -1
  204. package/payload/server/public/assets/packet-4T2RLAQJ-DmkQBx4h.js +0 -1
  205. package/payload/server/public/assets/pie-ZZUOXDRM-DDnxYUBm.js +0 -1
  206. package/payload/server/public/assets/radar-PYXPWWZC-7H4k0ChM.js +0 -1
  207. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BSVuxZxz.js +0 -1
  208. package/payload/server/public/assets/treeView-SZITEDCU-BLXWTQtW.js +0 -1
  209. package/payload/server/public/assets/treemap-W4RFUUIX-DiePDF5d.js +0 -1
  210. package/payload/server/public/assets/wardley-RL74JXVD-OSUWK5LN.js +0 -1
  211. /package/payload/server/public/assets/{_baseFor-DVaNaTfF.js → _baseFor-SRtUHe7G.js} +0 -0
  212. /package/payload/server/public/assets/{admin-types-CJrGd46U.js → admin-types-Dg11L4MQ.js} +0 -0
  213. /package/payload/server/public/assets/{array-8_H1156C.js → array-BpsnM-Py.js} +0 -0
  214. /package/payload/server/public/assets/{chunk-Pqm5yXtL.js → chunk-CAM3fms7.js} +0 -0
  215. /package/payload/server/public/assets/{cytoscape.esm-h10p_6j-.js → cytoscape.esm-D4Rl1H5h.js} +0 -0
  216. /package/payload/server/public/assets/{defaultLocale-pD7tFa1r.js → defaultLocale-C7sQPl-O.js} +0 -0
  217. /package/payload/server/public/assets/{init-CwJ4b81e.js → init-BydxaDEV.js} +0 -0
  218. /package/payload/server/public/assets/{katex-DJPjIBAI.js → katex-B0fVeDx4.js} +0 -0
  219. /package/payload/server/public/assets/{path-CK8wrAxY.js → path-jlWYQ2i9.js} +0 -0
  220. /package/payload/server/public/assets/{preload-helper-Bf_JiD2A.js → preload-helper-uTix4PVD.js} +0 -0
  221. /package/payload/server/public/assets/{rough.esm-Dwml_la6.js → rough.esm-c4PR5shF.js} +0 -0
@@ -0,0 +1,89 @@
1
+ // Booking reconcile core (Task 1012). The :Meeting MERGE is keyed on the
2
+ // booking uid (real MERGE idempotency is a live-Neo4j property); these tests
3
+ // pin the contract the JS owns: correct uid/source on the write, the
4
+ // global-email-uniqueness branch (same-account link / cross-account skip / new
5
+ // stub), and the audit arithmetic.
6
+ import { describe, it, expect, vi } from "vitest";
7
+ import { reconcileOne, auditCounts, bookingUid } from "../booking-reconcile.js";
8
+ const booking = {
9
+ bookingId: "abc",
10
+ slotStart: "2026-06-22T09:00:00Z",
11
+ slotEnd: "2026-06-22T09:30:00Z",
12
+ name: "Jo Visitor",
13
+ email: "jo@example.com",
14
+ note: "hello",
15
+ };
16
+ // Fake session that routes each run() by the cypher it sees. The booker link
17
+ // is a single MERGE (p:Person {email}) whose returned `linked` is true when the
18
+ // resolved Person belongs to this account (a new node, or an existing
19
+ // same-account one) and false for a foreign-account node.
20
+ function makeSession(opts) {
21
+ const cyphers = [];
22
+ const params = [];
23
+ const run = vi.fn(async (cypher, p) => {
24
+ cyphers.push(cypher);
25
+ params.push(p);
26
+ if (cypher.includes("MERGE (m:Meeting")) {
27
+ return { records: [{ get: (k) => (k === "created" ? opts.created : "el-meeting-1") }] };
28
+ }
29
+ if (cypher.includes("MERGE (p:Person")) {
30
+ // undefined owner = freshly created under this account.
31
+ const linked = opts.personOwner === undefined || opts.personOwner === p.accountId;
32
+ return { records: [{ get: () => linked }] };
33
+ }
34
+ return { records: [] };
35
+ });
36
+ return { session: { run }, run, cyphers, params };
37
+ }
38
+ describe("reconcileOne", () => {
39
+ it("writes the booking meeting with the right uid + source and links a new booker", async () => {
40
+ const { session, cyphers, params } = makeSession({ created: true, personOwner: undefined });
41
+ const r = await reconcileOne(session, "acct-1", booking);
42
+ expect(r.created).toBe(true);
43
+ expect(r.meetingId).toBe("el-meeting-1");
44
+ expect(r.personLinked).toBe(true);
45
+ expect(r.crossAccountSkipped).toBe(false);
46
+ // uid + source asserted on the meeting MERGE params/cypher.
47
+ expect(params[0].uid).toBe(bookingUid("abc"));
48
+ expect(cyphers[0]).toMatch(/m\.source = 'booking'/);
49
+ // booker link is one atomic MERGE on the unique email, not a check-then-create.
50
+ expect(cyphers.some((c) => c.includes("MERGE (p:Person {email: $email})"))).toBe(true);
51
+ expect(cyphers.some((c) => c.includes("CREATE (p:Person"))).toBe(false);
52
+ });
53
+ it("reports created=false on the idempotent re-run", async () => {
54
+ const { session } = makeSession({ created: false, personOwner: "acct-1" });
55
+ const r = await reconcileOne(session, "acct-1", booking);
56
+ expect(r.created).toBe(false);
57
+ });
58
+ it("links a same-account existing person via ATTENDED", async () => {
59
+ const { session, cyphers } = makeSession({ created: true, personOwner: "acct-1" });
60
+ const r = await reconcileOne(session, "acct-1", booking);
61
+ expect(r.personLinked).toBe(true);
62
+ expect(r.crossAccountSkipped).toBe(false);
63
+ expect(cyphers.some((c) => c.includes("MERGE (p)-[r:ATTENDED]->(m)"))).toBe(true);
64
+ });
65
+ it("skips a cross-account person but still lands the meeting", async () => {
66
+ const { session } = makeSession({ created: true, personOwner: "other-account" });
67
+ const r = await reconcileOne(session, "acct-1", booking);
68
+ expect(r.created).toBe(true);
69
+ expect(r.personLinked).toBe(false);
70
+ expect(r.crossAccountSkipped).toBe(true);
71
+ });
72
+ });
73
+ describe("auditCounts", () => {
74
+ it("counts pending (D1 ahead) and orphans (graph ahead)", async () => {
75
+ const run = vi.fn(async () => ({
76
+ records: [{ get: () => ["booking-a", "booking-b"] }],
77
+ }));
78
+ const counts = await auditCounts({ run }, "acct-1", ["a", "c"]);
79
+ // 'c' accepted in D1 but no meeting -> pending 1; 'booking-b' meeting with no D1 row -> orphan 1.
80
+ expect(counts.pendingUnreconciled).toBe(1);
81
+ expect(counts.orphanMeetings).toBe(1);
82
+ });
83
+ it("is clean when D1 and graph agree", async () => {
84
+ const run = vi.fn(async () => ({ records: [{ get: () => ["booking-a", "booking-b"] }] }));
85
+ const counts = await auditCounts({ run }, "acct-1", ["a", "b"]);
86
+ expect(counts).toEqual({ pendingUnreconciled: 0, orphanMeetings: 0 });
87
+ });
88
+ });
89
+ //# sourceMappingURL=booking-reconcile.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"booking-reconcile.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/booking-reconcile.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=meeting-write.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meeting-write.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/meeting-write.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,118 @@
1
+ // Task 1023. Real MERGE idempotency is a live-Neo4j property; these tests pin
2
+ // the contract the JS owns: a deterministic (accountId, uid) content-hash key,
3
+ // the :Meeting MERGE shape, the PART_OF provenance link, and the attendee branch
4
+ // (same-account email link / cross-account skip / resolved nodeId / name-only).
5
+ import { describe, it, expect, vi } from "vitest";
6
+ import { writeMeeting, meetingUid } from "../meeting-write.js";
7
+ const base = {
8
+ accountId: "acct-1",
9
+ name: "Discovery call",
10
+ startDate: "2026-06-22T14:00:00Z",
11
+ endDate: "2026-06-22T14:30:00Z",
12
+ createdBy: { agent: "personal-assistant", session: "sess-1", tool: "schedule-event" },
13
+ };
14
+ // Fake session that routes each run() by the cypher it sees, capturing cyphers +
15
+ // params. `created` controls the meeting MERGE; `personOwner` controls the
16
+ // same-account/foreign branch for an email attendee; `nodeIdLinks` controls
17
+ // whether a nodeId attendee resolved under this account.
18
+ function makeSession(opts) {
19
+ const cyphers = [];
20
+ const params = [];
21
+ const run = vi.fn(async (cypher, p) => {
22
+ cyphers.push(cypher);
23
+ params.push(p);
24
+ if (cypher.includes("MERGE (m:Meeting")) {
25
+ return { records: [{ get: (k) => (k === "created" ? opts.created ?? true : "el-meeting-1") }] };
26
+ }
27
+ if (cypher.includes("MERGE (p:Person {email:")) {
28
+ const linked = opts.personOwner === undefined || opts.personOwner === p.accountId;
29
+ return { records: [{ get: () => linked }] };
30
+ }
31
+ if (cypher.includes("MATCH (p:Person) WHERE elementId(p)")) {
32
+ // The real query's same-account MATCH yields a row only when the Person
33
+ // resolves under this account; a foreign/missing nodeId returns zero rows.
34
+ return { records: opts.nodeIdLinks ? [{ get: () => true }] : [] };
35
+ }
36
+ return { records: [] };
37
+ });
38
+ return { session: { run }, run, cyphers, params };
39
+ }
40
+ describe("meetingUid", () => {
41
+ it("is deterministic and normalizes title casing/whitespace", () => {
42
+ const a = meetingUid("acct-1", "Discovery Call", "2026-06-22T14:00:00Z");
43
+ const b = meetingUid("acct-1", " discovery call ", "2026-06-22T14:00:00Z");
44
+ expect(a).toBe(b);
45
+ expect(a).toMatch(/^sched-[0-9a-f]{32}$/);
46
+ });
47
+ it("differs by account, title, and start", () => {
48
+ const u = meetingUid("acct-1", "Discovery call", "2026-06-22T14:00:00Z");
49
+ expect(u).not.toBe(meetingUid("acct-2", "Discovery call", "2026-06-22T14:00:00Z"));
50
+ expect(u).not.toBe(meetingUid("acct-1", "Other", "2026-06-22T14:00:00Z"));
51
+ expect(u).not.toBe(meetingUid("acct-1", "Discovery call", "2026-06-22T15:00:00Z"));
52
+ });
53
+ });
54
+ describe("writeMeeting", () => {
55
+ it("MERGEs the meeting on (accountId, uid) with the schedule-event source", async () => {
56
+ const { session, cyphers, params } = makeSession({ created: true });
57
+ const r = await writeMeeting(session, base);
58
+ expect(r.created).toBe(true);
59
+ expect(r.meetingId).toBe("el-meeting-1");
60
+ expect(r.uid).toBe(meetingUid(base.accountId, base.name, base.startDate));
61
+ expect(params[0].uid).toBe(r.uid);
62
+ expect(params[0].accountId).toBe("acct-1");
63
+ expect(cyphers[0]).toMatch(/MERGE \(m:Meeting \{accountId: \$accountId, uid: \$uid\}\)/);
64
+ expect(cyphers[0]).toMatch(/m\.source = 'schedule-event'/);
65
+ });
66
+ it("links PART_OF to the session Conversation when a sessionKey is given", async () => {
67
+ const { session, cyphers } = makeSession({ created: true });
68
+ await writeMeeting(session, { ...base, sessionKey: "sess-1" });
69
+ expect(cyphers.some((c) => c.includes("MERGE (m)-[r:PART_OF]->(c)"))).toBe(true);
70
+ });
71
+ it("writes no PART_OF when there is no sessionKey", async () => {
72
+ const { session, cyphers } = makeSession({ created: true });
73
+ await writeMeeting(session, base);
74
+ expect(cyphers.some((c) => c.includes("PART_OF"))).toBe(false);
75
+ });
76
+ it("links a same-account email counterparty via ATTENDED", async () => {
77
+ const { session, cyphers, params } = makeSession({ created: true, personOwner: "acct-1" });
78
+ const r = await writeMeeting(session, { ...base, attendees: [{ email: "Josh@Goff.com", name: "Josh" }] });
79
+ expect(r.attendeesLinked).toBe(1);
80
+ expect(r.crossAccountSkipped).toBe(0);
81
+ expect(cyphers.some((c) => c.includes("MERGE (p)-[r:ATTENDED]->(m)"))).toBe(true);
82
+ // email is lowercased before the MERGE.
83
+ const personCall = params.find((p) => p.email !== undefined);
84
+ expect(personCall?.email).toBe("josh@goff.com");
85
+ });
86
+ it("skips a cross-account email counterparty but still lands the meeting", async () => {
87
+ const { session } = makeSession({ created: true, personOwner: "other-account" });
88
+ const r = await writeMeeting(session, { ...base, attendees: [{ email: "foreign@x.com" }] });
89
+ expect(r.created).toBe(true);
90
+ expect(r.attendeesLinked).toBe(0);
91
+ expect(r.crossAccountSkipped).toBe(1);
92
+ });
93
+ it("links a resolved same-account nodeId counterparty", async () => {
94
+ const { session } = makeSession({ created: true, nodeIdLinks: true });
95
+ const r = await writeMeeting(session, { ...base, attendees: [{ nodeId: "el-person-9" }] });
96
+ expect(r.attendeesLinked).toBe(1);
97
+ expect(r.crossAccountSkipped).toBe(0);
98
+ });
99
+ it("skips a nodeId that does not resolve under this account", async () => {
100
+ const { session } = makeSession({ created: true, nodeIdLinks: false });
101
+ const r = await writeMeeting(session, { ...base, attendees: [{ nodeId: "el-foreign" }] });
102
+ expect(r.attendeesLinked).toBe(0);
103
+ expect(r.crossAccountSkipped).toBe(1);
104
+ });
105
+ it("writes no attendee link for a name-only counterparty", async () => {
106
+ const { session, cyphers } = makeSession({ created: true });
107
+ const r = await writeMeeting(session, { ...base, attendees: [{ name: "No Email" }] });
108
+ expect(r.attendeesLinked).toBe(0);
109
+ expect(r.crossAccountSkipped).toBe(0);
110
+ expect(cyphers.some((c) => c.includes("ATTENDED"))).toBe(false);
111
+ });
112
+ it("reports created=false on the idempotent re-run", async () => {
113
+ const { session } = makeSession({ created: false });
114
+ const r = await writeMeeting(session, base);
115
+ expect(r.created).toBe(false);
116
+ });
117
+ });
118
+ //# sourceMappingURL=meeting-write.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meeting-write.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/meeting-write.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schedule-classify.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-classify.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/schedule-classify.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ // Task 1023. The discriminator is the load-bearing decision: it picks :Meeting
2
+ // vs :Event from the schedule-event params. These pins lock each branch and the
3
+ // top-down precedence (a trigger always wins, a name-only attendee never does).
4
+ import { describe, it, expect } from "vitest";
5
+ import { classifyScheduleWrite } from "../schedule-classify.js";
6
+ describe("classifyScheduleWrite", () => {
7
+ it("recurrence makes it an Event (recurring trigger), even with an attendee + endDate", () => {
8
+ expect(classifyScheduleWrite({
9
+ recurrence: "0 8 * * 1-5",
10
+ endDate: "2026-06-22T09:00:00Z",
11
+ attendees: [{ email: "a@b.com" }],
12
+ })).toEqual({ label: "Event", reason: "recurrence" });
13
+ });
14
+ it("an action makes it an Event (dispatcher), even with an attendee", () => {
15
+ expect(classifyScheduleWrite({
16
+ action: { plugin: "memory", tool: "memory-search" },
17
+ attendees: [{ email: "a@b.com" }],
18
+ })).toEqual({ label: "Event", reason: "action" });
19
+ });
20
+ it("a counterparty with an email makes it a Meeting", () => {
21
+ expect(classifyScheduleWrite({ attendees: [{ email: "josh@goff.com", name: "Josh" }] })).toEqual({
22
+ label: "Meeting",
23
+ reason: "counterparty",
24
+ });
25
+ });
26
+ it("a counterparty with a resolved nodeId makes it a Meeting", () => {
27
+ expect(classifyScheduleWrite({ attendees: [{ nodeId: "el-person-1" }] })).toEqual({
28
+ label: "Meeting",
29
+ reason: "counterparty",
30
+ });
31
+ });
32
+ it("a bounded slot (endDate) with no counterparty is a Meeting", () => {
33
+ expect(classifyScheduleWrite({ endDate: "2026-06-22T10:00:00Z" })).toEqual({
34
+ label: "Meeting",
35
+ reason: "bounded",
36
+ });
37
+ });
38
+ it("a one-time reminder (no end, no counterparty) stays an Event", () => {
39
+ expect(classifyScheduleWrite({})).toEqual({ label: "Event", reason: "reminder" });
40
+ });
41
+ it("a name-only attendee does not, by itself, make it a Meeting", () => {
42
+ // No email and no nodeId — not resolvable to a :Person, so it falls through
43
+ // to the reminder branch rather than the counterparty branch.
44
+ expect(classifyScheduleWrite({ attendees: [{ name: "Some Person" }] })).toEqual({
45
+ label: "Event",
46
+ reason: "reminder",
47
+ });
48
+ });
49
+ it("a name-only attendee on a bounded slot is a Meeting via the bounded branch", () => {
50
+ expect(classifyScheduleWrite({ endDate: "2026-06-22T10:00:00Z", attendees: [{ name: "Some Person" }] })).toEqual({ label: "Meeting", reason: "bounded" });
51
+ });
52
+ it("treats whitespace-only attendee keys as absent", () => {
53
+ expect(classifyScheduleWrite({ attendees: [{ email: " ", nodeId: " " }] })).toEqual({
54
+ label: "Event",
55
+ reason: "reminder",
56
+ });
57
+ });
58
+ });
59
+ //# sourceMappingURL=schedule-classify.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-classify.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/schedule-classify.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { BookingRow } from "./booking-reconcile.js";
2
+ export interface NotifyDeps {
3
+ /** Admin destination address; email is skipped when absent. */
4
+ adminEmail?: string;
5
+ sendEmail?: (msg: {
6
+ to: string;
7
+ subject: string;
8
+ body: string;
9
+ }) => Promise<void>;
10
+ /** Admin WhatsApp thread; set ONLY when a thread already exists (reply-only). */
11
+ adminWhatsapp?: string;
12
+ sendWhatsapp?: (msg: {
13
+ to: string;
14
+ text: string;
15
+ }) => Promise<void>;
16
+ }
17
+ export interface NotifyResult {
18
+ channel: "email" | "whatsapp";
19
+ dispatched: boolean;
20
+ }
21
+ export declare function notifyBooking(deps: NotifyDeps, booking: BookingRow): Promise<NotifyResult[]>;
22
+ //# sourceMappingURL=booking-notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"booking-notify.d.ts","sourceRoot":"","sources":["../../src/lib/booking-notify.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;CACrB;AAOD,wBAAsB,aAAa,CACjC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,YAAY,EAAE,CAAC,CA0BzB"}
@@ -0,0 +1,31 @@
1
+ function summary(booking) {
2
+ return `${booking.name} (${booking.email}) booked ${booking.slotStart}.` +
3
+ (booking.note ? ` Note: ${booking.note}` : "");
4
+ }
5
+ export async function notifyBooking(deps, booking) {
6
+ const results = [];
7
+ if (deps.adminEmail && deps.sendEmail) {
8
+ try {
9
+ await deps.sendEmail({
10
+ to: deps.adminEmail,
11
+ subject: `New booking: ${booking.name}`,
12
+ body: summary(booking),
13
+ });
14
+ results.push({ channel: "email", dispatched: true });
15
+ }
16
+ catch {
17
+ results.push({ channel: "email", dispatched: false });
18
+ }
19
+ }
20
+ if (deps.adminWhatsapp && deps.sendWhatsapp) {
21
+ try {
22
+ await deps.sendWhatsapp({ to: deps.adminWhatsapp, text: summary(booking) });
23
+ results.push({ channel: "whatsapp", dispatched: true });
24
+ }
25
+ catch {
26
+ results.push({ channel: "whatsapp", dispatched: false });
27
+ }
28
+ }
29
+ return results;
30
+ }
31
+ //# sourceMappingURL=booking-notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"booking-notify.js","sourceRoot":"","sources":["../../src/lib/booking-notify.ts"],"names":[],"mappings":"AAwBA,SAAS,OAAO,CAAC,OAAmB;IAClC,OAAO,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,KAAK,YAAY,OAAO,CAAC,SAAS,GAAG;QACtE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAgB,EAChB,OAAmB;IAEnB,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,SAAS,CAAC;gBACnB,EAAE,EAAE,IAAI,CAAC,UAAU;gBACnB,OAAO,EAAE,gBAAgB,OAAO,CAAC,IAAI,EAAE;gBACvC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC;aACvB,CAAC,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5E,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { Session } from "neo4j-driver";
2
+ export interface BookingRow {
3
+ bookingId: string;
4
+ slotStart: string;
5
+ slotEnd: string;
6
+ name: string;
7
+ email: string;
8
+ note?: string;
9
+ }
10
+ export interface ReconcileResult {
11
+ created: boolean;
12
+ meetingId: string;
13
+ personLinked: boolean;
14
+ crossAccountSkipped: boolean;
15
+ }
16
+ export declare function bookingUid(bookingId: string): string;
17
+ export declare function reconcileOne(session: Session, accountId: string, booking: BookingRow): Promise<ReconcileResult>;
18
+ export interface AuditCounts {
19
+ pendingUnreconciled: number;
20
+ orphanMeetings: number;
21
+ }
22
+ /**
23
+ * Compare D1's accepted booking ids against the account's booking :Meeting set.
24
+ * pendingUnreconciled — accepted in D1 but no :Meeting yet (reconcile lagging).
25
+ * orphanMeetings — a booking :Meeting with no backing D1 row.
26
+ */
27
+ export declare function auditCounts(session: Session, accountId: string, d1AcceptedIds: string[]): Promise<AuditCounts>;
28
+ //# sourceMappingURL=booking-reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"booking-reconcile.d.ts","sourceRoot":"","sources":["../../src/lib/booking-reconcile.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,eAAe,CAAC,CA4D1B;AAED,MAAM,WAAW,WAAW;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,WAAW,CAAC,CAWtB"}
@@ -0,0 +1,70 @@
1
+ export function bookingUid(bookingId) {
2
+ return `booking-${bookingId}`;
3
+ }
4
+ export async function reconcileOne(session, accountId, booking) {
5
+ const uid = bookingUid(booking.bookingId);
6
+ const meetingRes = await session.run(`MERGE (m:Meeting {accountId: $accountId, uid: $uid})
7
+ ON CREATE SET m.title = $title,
8
+ m.startsAt = datetime($slotStart),
9
+ m.endsAt = datetime($slotEnd),
10
+ m.scope = 'booking',
11
+ m.source = 'booking',
12
+ m.bookerEmail = $email,
13
+ m.note = $note,
14
+ m.isAllDay = false,
15
+ m.createdAt = datetime(),
16
+ m.updatedAt = datetime(),
17
+ m.createdByAgent = 'booking-reconcile',
18
+ m.createdBySource = 'booking-reconcile',
19
+ m._created = true
20
+ ON MATCH SET m.updatedAt = datetime(),
21
+ m._created = false
22
+ WITH m, m._created AS created
23
+ REMOVE m._created
24
+ RETURN created, elementId(m) AS meetingId`, {
25
+ accountId,
26
+ uid,
27
+ title: `Booking: ${booking.name}`,
28
+ slotStart: booking.slotStart,
29
+ slotEnd: booking.slotEnd,
30
+ email: booking.email,
31
+ note: booking.note ?? "",
32
+ });
33
+ const created = meetingRes.records[0].get("created");
34
+ const meetingId = meetingRes.records[0].get("meetingId");
35
+ // Resolve the booker in one atomic statement that respects the global
36
+ // Person.email uniqueness constraint. MERGE on email matches any existing
37
+ // Person (without relabeling it — ON CREATE fires only on a fresh node) or
38
+ // creates one under this account. The ATTENDED link is written only when the
39
+ // resolved Person belongs to this account, so a foreign-account person is
40
+ // neither linked nor modified, and there is no check-then-create race that
41
+ // could throw the unique constraint and wedge the row.
42
+ const personRes = await session.run(`MATCH (m:Meeting) WHERE elementId(m) = $meetingId
43
+ MERGE (p:Person {email: $email})
44
+ ON CREATE SET p.accountId = $accountId,
45
+ p.displayName = $name,
46
+ p.scope = 'booking',
47
+ p.source = 'booking-reconcile',
48
+ p.createdAt = datetime()
49
+ FOREACH (_ IN CASE WHEN p.accountId = $accountId THEN [1] ELSE [] END |
50
+ MERGE (p)-[r:ATTENDED]->(m)
51
+ ON CREATE SET r.role = 'booker', r.createdAt = datetime(), r.createdBySource = 'booking-reconcile')
52
+ RETURN p.accountId = $accountId AS linked`, { meetingId, email: booking.email, accountId, name: booking.name });
53
+ const linked = personRes.records[0]?.get("linked") === true;
54
+ return { created, meetingId, personLinked: linked, crossAccountSkipped: !linked };
55
+ }
56
+ /**
57
+ * Compare D1's accepted booking ids against the account's booking :Meeting set.
58
+ * pendingUnreconciled — accepted in D1 but no :Meeting yet (reconcile lagging).
59
+ * orphanMeetings — a booking :Meeting with no backing D1 row.
60
+ */
61
+ export async function auditCounts(session, accountId, d1AcceptedIds) {
62
+ const res = await session.run(`MATCH (m:Meeting {accountId: $accountId, source: 'booking'}) RETURN collect(m.uid) AS uids`, { accountId });
63
+ const uids = res.records[0]?.get("uids") ?? [];
64
+ const uidSet = new Set(uids);
65
+ const idSet = new Set(d1AcceptedIds);
66
+ const pendingUnreconciled = d1AcceptedIds.filter((id) => !uidSet.has(bookingUid(id))).length;
67
+ const orphanMeetings = uids.filter((uid) => !idSet.has(uid.replace(/^booking-/, ""))).length;
68
+ return { pendingUnreconciled, orphanMeetings };
69
+ }
70
+ //# sourceMappingURL=booking-reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"booking-reconcile.js","sourceRoot":"","sources":["../../src/lib/booking-reconcile.ts"],"names":[],"mappings":"AA2BA,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,OAAO,WAAW,SAAS,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAgB,EAChB,SAAiB,EACjB,OAAmB;IAEnB,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAE1C,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC;;;;;;;;;;;;;;;;;;+CAkB2C,EAC3C;QACE,SAAS;QACT,GAAG;QACH,KAAK,EAAE,YAAY,OAAO,CAAC,IAAI,EAAE;QACjC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;KACzB,CACF,CAAC;IACF,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAY,CAAC;IAChE,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAW,CAAC;IAEnE,sEAAsE;IACtE,0EAA0E;IAC1E,2EAA2E;IAC3E,6EAA6E;IAC7E,0EAA0E;IAC1E,2EAA2E;IAC3E,uDAAuD;IACvD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC;;;;;;;;;;+CAU2C,EAC3C,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CACnE,CAAC;IACF,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAE5D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC,MAAM,EAAE,CAAC;AACpF,CAAC;AAOD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAgB,EAChB,SAAiB,EACjB,aAAuB;IAEvB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAC3B,4FAA4F,EAC5F,EAAE,SAAS,EAAE,CACd,CAAC;IACF,MAAM,IAAI,GAAa,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;IACrC,MAAM,mBAAmB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7F,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7F,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,CAAC;AACjD,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { Session } from "neo4j-driver";
2
+ import type { ScheduleAttendee } from "./schedule-classify.js";
3
+ export interface WriteMeetingParams {
4
+ accountId: string;
5
+ name: string;
6
+ startDate: string;
7
+ endDate?: string;
8
+ location?: string;
9
+ description?: string;
10
+ timezone?: string;
11
+ attendees?: ScheduleAttendee[];
12
+ sessionKey?: string;
13
+ createdBy: {
14
+ agent: string;
15
+ session: string;
16
+ tool: string;
17
+ };
18
+ }
19
+ export interface WriteMeetingResult {
20
+ meetingId: string;
21
+ uid: string;
22
+ created: boolean;
23
+ attendeesLinked: number;
24
+ crossAccountSkipped: number;
25
+ }
26
+ /**
27
+ * Deterministic uid for a conversationally-booked meeting. ICS uses the VEVENT
28
+ * UID and booking uses 'booking-<id>'; a conversational meeting has no external
29
+ * id, so the natural key is a content hash of the account, the normalized title,
30
+ * and the start. Re-running the identical request yields the same uid and the
31
+ * MERGE matches the existing node.
32
+ */
33
+ export declare function meetingUid(accountId: string, name: string, startDate: string): string;
34
+ export declare function writeMeeting(session: Session, params: WriteMeetingParams): Promise<WriteMeetingResult>;
35
+ //# sourceMappingURL=meeting-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meeting-write.d.ts","sourceRoot":"","sources":["../../src/lib/meeting-write.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGrF;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAgH7B"}