@rubytech/create-sitedesk-code 0.1.521 → 0.1.523

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 (416) hide show
  1. package/dist/index.js +17 -3
  2. package/package.json +1 -1
  3. package/payload/platform/lib/account-schema-regions/package-lock.json +12 -0
  4. package/payload/platform/lib/active-rules/package-lock.json +12 -0
  5. package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/graph-style/dist/index.js +28 -0
  7. package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
  8. package/payload/platform/lib/graph-style/src/__tests__/caption-coverage.test.ts +7 -0
  9. package/payload/platform/lib/graph-style/src/index.ts +24 -0
  10. package/payload/platform/lib/graph-write/dist/__tests__/merge-on.test.d.ts.map +1 -0
  11. package/payload/platform/lib/graph-write/dist/__tests__/merge-on.test.js.map +1 -0
  12. package/payload/platform/lib/graph-write/dist/index.d.ts +16 -0
  13. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  14. package/payload/platform/lib/graph-write/dist/index.js +83 -6
  15. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  16. package/payload/platform/lib/graph-write/src/index.ts +117 -10
  17. package/payload/platform/lib/mcp-lifeline/package-lock.json +12 -0
  18. package/payload/platform/lib/routine-templates/dist/index.d.ts +151 -0
  19. package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -0
  20. package/payload/platform/lib/routine-templates/dist/index.js +207 -0
  21. package/payload/platform/lib/routine-templates/dist/index.js.map +1 -0
  22. package/payload/platform/lib/routine-templates/dist/roster.d.ts +57 -0
  23. package/payload/platform/lib/routine-templates/dist/roster.d.ts.map +1 -0
  24. package/payload/platform/lib/routine-templates/dist/roster.js +182 -0
  25. package/payload/platform/lib/routine-templates/dist/roster.js.map +1 -0
  26. package/payload/platform/lib/routine-templates/dist/seed-cli.d.ts +2 -0
  27. package/payload/platform/lib/routine-templates/dist/seed-cli.d.ts.map +1 -0
  28. package/payload/platform/lib/routine-templates/dist/seed-cli.js +64 -0
  29. package/payload/platform/lib/routine-templates/dist/seed-cli.js.map +1 -0
  30. package/payload/platform/lib/routine-templates/package.json +6 -0
  31. package/payload/platform/lib/routine-templates/src/__tests__/roster.test.ts +170 -0
  32. package/payload/platform/lib/routine-templates/src/__tests__/seed.test.ts +298 -0
  33. package/payload/platform/lib/routine-templates/src/index.ts +299 -0
  34. package/payload/platform/lib/routine-templates/src/roster.ts +206 -0
  35. package/payload/platform/lib/routine-templates/src/seed-cli.ts +65 -0
  36. package/payload/platform/lib/routine-templates/tsconfig.json +9 -0
  37. package/payload/platform/lib/routine-templates/vitest.config.ts +9 -0
  38. package/payload/platform/neo4j/edge-annotations.json +2 -2
  39. package/payload/platform/neo4j/schema.cypher +25 -1
  40. package/payload/platform/package.json +3 -2
  41. package/payload/platform/plugins/admin/PLUGIN.md +2 -2
  42. package/payload/platform/plugins/admin/mcp/dist/index.js +39 -1
  43. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  44. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +319 -66
  45. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
  46. package/payload/platform/plugins/docs/references/admin-ui.md +275 -59
  47. package/payload/platform/plugins/docs/references/internals.md +4 -0
  48. package/payload/platform/plugins/docs/references/memory-guide.md +11 -1
  49. package/payload/platform/plugins/docs/references/outlook-guide.md +27 -4
  50. package/payload/platform/plugins/graph/.claude-plugin/plugin.json +1 -1
  51. package/payload/platform/plugins/graph/PLUGIN.md +1 -1
  52. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  53. package/payload/platform/plugins/memory/mcp/dist/index.js +12 -5
  54. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.d.ts +2 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.d.ts.map +1 -0
  57. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.js +197 -0
  58. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.js.map +1 -0
  59. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +55 -9
  60. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -1
  61. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +96 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -1
  63. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts +7 -0
  64. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts.map +1 -1
  65. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.d.ts +29 -0
  66. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.d.ts.map +1 -0
  67. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.js +126 -0
  68. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.js.map +1 -0
  69. package/payload/platform/plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js +7 -4
  70. package/payload/platform/plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js.map +1 -1
  71. package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts +1 -0
  72. package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts.map +1 -1
  73. package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js +18 -1
  74. package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js.map +1 -1
  75. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +7 -0
  76. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  77. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +4 -3
  78. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  79. package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
  80. package/payload/platform/plugins/memory/references/schema-estate-agent.md +2 -2
  81. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +4 -4
  82. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  83. package/payload/platform/plugins/outlook/PLUGIN.md +9 -1
  84. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts +2 -0
  85. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts.map +1 -0
  86. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js +151 -0
  87. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js.map +1 -0
  88. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts +2 -0
  89. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts.map +1 -0
  90. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js +151 -0
  91. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js.map +1 -0
  92. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +38 -4
  93. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js.map +1 -1
  94. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts +2 -0
  95. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts.map +1 -0
  96. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js +146 -0
  97. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js.map +1 -0
  98. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts +2 -0
  99. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts.map +1 -0
  100. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js +120 -0
  101. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js.map +1 -0
  102. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js +8 -8
  103. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js.map +1 -1
  104. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts +2 -0
  105. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts.map +1 -0
  106. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js +27 -0
  107. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js.map +1 -0
  108. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts +2 -0
  109. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts.map +1 -0
  110. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js +70 -0
  111. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js.map +1 -0
  112. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts +2 -0
  113. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts.map +1 -0
  114. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js +152 -0
  115. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js.map +1 -0
  116. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts +57 -0
  117. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts.map +1 -0
  118. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js +163 -0
  119. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js.map +1 -0
  120. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +12 -0
  121. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts.map +1 -1
  122. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js +21 -1
  123. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -1
  124. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts +35 -4
  125. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts.map +1 -1
  126. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js +92 -10
  127. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js.map +1 -1
  128. package/payload/platform/plugins/outlook/mcp/dist/index.js +106 -3
  129. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  130. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +49 -0
  131. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
  132. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +126 -8
  133. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
  134. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +13 -0
  135. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -1
  136. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +9 -0
  137. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -1
  138. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts +42 -8
  139. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts.map +1 -1
  140. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js +59 -19
  141. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js.map +1 -1
  142. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts +47 -0
  143. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts.map +1 -0
  144. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js +67 -0
  145. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js.map +1 -0
  146. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts +12 -0
  147. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts.map +1 -0
  148. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js +18 -0
  149. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js.map +1 -0
  150. package/payload/platform/plugins/outlook/references/auth.md +15 -2
  151. package/payload/platform/plugins/outlook/references/graph-surfaces.md +8 -0
  152. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +21 -1
  153. package/payload/platform/plugins/quickbooks/PLUGIN.md +10 -10
  154. package/payload/platform/plugins/scheduling/PLUGIN.md +49 -1
  155. package/payload/platform/plugins/scheduling/mcp/dist/index.js +48 -0
  156. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  157. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.d.ts +2 -0
  158. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.d.ts.map +1 -0
  159. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.js +78 -0
  160. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.js.map +1 -0
  161. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.d.ts +2 -0
  162. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.d.ts.map +1 -0
  163. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js +457 -0
  164. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js.map +1 -0
  165. package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.d.ts +35 -0
  166. package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.d.ts.map +1 -0
  167. package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.js +39 -0
  168. package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.js.map +1 -0
  169. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +175 -0
  170. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -0
  171. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js +533 -0
  172. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -0
  173. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +123 -9
  174. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
  175. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.d.ts +2 -0
  176. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.d.ts.map +1 -0
  177. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.js +189 -0
  178. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.js.map +1 -0
  179. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.d.ts +2 -0
  180. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.d.ts.map +1 -0
  181. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.js +95 -0
  182. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.js.map +1 -0
  183. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.d.ts +2 -0
  184. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.d.ts.map +1 -0
  185. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.js +114 -0
  186. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.js.map +1 -0
  187. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +109 -2
  188. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  189. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +340 -9
  190. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  191. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts +9 -1
  192. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts.map +1 -1
  193. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +210 -45
  194. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  195. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +43 -1
  196. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
  197. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-agent.test.js +55 -7
  198. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-agent.test.js.map +1 -1
  199. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js +45 -1
  200. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js.map +1 -1
  201. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.d.ts +2 -0
  202. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.d.ts.map +1 -0
  203. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.js +96 -0
  204. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.js.map +1 -0
  205. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -1
  206. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +2 -1
  207. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -1
  208. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts +5 -0
  209. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -1
  210. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +13 -0
  211. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -1
  212. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts +4 -0
  213. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts.map +1 -1
  214. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js +24 -3
  215. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -1
  216. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.d.ts +33 -0
  217. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.d.ts.map +1 -0
  218. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.js +51 -0
  219. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.js.map +1 -0
  220. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  221. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +12 -2
  222. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  223. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +30 -2
  224. package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +1 -1
  225. package/payload/platform/scripts/check-specialist-tool-surface.mjs +92 -16
  226. package/payload/platform/scripts/seed-neo4j.sh +41 -0
  227. package/payload/platform/scripts/verify-skill-tool-surface.sh +91 -2
  228. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  229. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +3 -0
  230. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  231. package/payload/platform/templates/specialists/agents/librarian.md +1 -1
  232. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  233. package/payload/server/{chunk-AZLT4LVO.js → chunk-VUXKXOJU.js} +755 -335
  234. package/payload/server/{chunk-UX2FRNO7.js → chunk-WT2PFGY4.js} +37 -265
  235. package/payload/server/{chunk-PXNY2TAP.js → chunk-XCR5D2G3.js} +239 -10
  236. package/payload/server/{manager-CQBSUJXR.js → manager-4TJK3YOY.js} +4 -2
  237. package/payload/server/maxy-edge.js +2 -2
  238. package/payload/server/public/activity.html +5 -5
  239. package/payload/server/public/agents.html +4 -4
  240. package/payload/server/public/assets/{AdminLoginScreens-Dar_MMJm.js → AdminLoginScreens-uknqh2Pm.js} +1 -1
  241. package/payload/server/public/assets/AdminLoginScreens-uknqh2Pm.js.br +0 -0
  242. package/payload/server/public/assets/AdminLoginScreens-uknqh2Pm.js.gz +0 -0
  243. package/payload/server/public/assets/AdminShell-GqtMImyQ.js +2 -0
  244. package/payload/server/public/assets/AdminShell-GqtMImyQ.js.br +0 -0
  245. package/payload/server/public/assets/AdminShell-GqtMImyQ.js.gz +0 -0
  246. package/payload/server/public/assets/{activity-B1rt4GIy.js → activity-B4KfMJqO.js} +1 -1
  247. package/payload/server/public/assets/activity-B4KfMJqO.js.br +0 -0
  248. package/payload/server/public/assets/activity-B4KfMJqO.js.gz +0 -0
  249. package/payload/server/public/assets/admin-DxCZeVw0.js +1 -0
  250. package/payload/server/public/assets/admin-DxCZeVw0.js.br +0 -0
  251. package/payload/server/public/assets/admin-DxCZeVw0.js.gz +0 -0
  252. package/payload/server/public/assets/agents-BtwBuO4G.js +1 -0
  253. package/payload/server/public/assets/agents-BtwBuO4G.js.br +0 -0
  254. package/payload/server/public/assets/agents-BtwBuO4G.js.gz +0 -0
  255. package/payload/server/public/assets/{browser-1QSCPl3X.js → browser-B3hSzI3O.js} +1 -1
  256. package/payload/server/public/assets/browser-B3hSzI3O.js.br +0 -0
  257. package/payload/server/public/assets/browser-B3hSzI3O.js.gz +0 -0
  258. package/payload/server/public/assets/{calendar-D009qWVV.js → calendar-BggxXxhS.js} +1 -1
  259. package/payload/server/public/assets/calendar-BggxXxhS.js.br +0 -0
  260. package/payload/server/public/assets/calendar-BggxXxhS.js.gz +0 -0
  261. package/payload/server/public/assets/chat-flivHt0t.js +1 -0
  262. package/payload/server/public/assets/chat-flivHt0t.js.br +0 -0
  263. package/payload/server/public/assets/chat-flivHt0t.js.gz +0 -0
  264. package/payload/server/public/assets/chevron-left-nVRThX7f.js +1 -0
  265. package/payload/server/public/assets/chevron-right-CLqfrkRA.js +1 -0
  266. package/payload/server/public/assets/chevron-right-CLqfrkRA.js.br +0 -0
  267. package/payload/server/public/assets/clock-Dj3SRZKo.js +1 -0
  268. package/payload/server/public/assets/clock-Dj3SRZKo.js.br +0 -0
  269. package/payload/server/public/assets/clock-Dj3SRZKo.js.gz +0 -0
  270. package/payload/server/public/assets/data-Ckjkne54.js +1 -0
  271. package/payload/server/public/assets/data-Ckjkne54.js.br +3 -0
  272. package/payload/server/public/assets/data-Ckjkne54.js.gz +0 -0
  273. package/payload/server/public/assets/{file-text-DwGAfCsd.js → file-text-BKs9eyiw.js} +1 -1
  274. package/payload/server/public/assets/file-text-BKs9eyiw.js.br +0 -0
  275. package/payload/server/public/assets/file-text-BKs9eyiw.js.gz +0 -0
  276. package/payload/server/public/assets/{graph-D-DGlfhW.js → graph-DTi71NaZ.js} +3 -3
  277. package/payload/server/public/assets/graph-DTi71NaZ.js.br +0 -0
  278. package/payload/server/public/assets/graph-DTi71NaZ.js.gz +0 -0
  279. package/payload/server/public/assets/graph-labels-BT3S6e9H.js +1 -0
  280. package/payload/server/public/assets/graph-labels-BT3S6e9H.js.br +0 -0
  281. package/payload/server/public/assets/graph-labels-BT3S6e9H.js.gz +0 -0
  282. package/payload/server/public/assets/{maximize-2-C_-KapxH.js → maximize-2-zC94tsxY.js} +1 -1
  283. package/payload/server/public/assets/maximize-2-zC94tsxY.js.br +0 -0
  284. package/payload/server/public/assets/maximize-2-zC94tsxY.js.gz +0 -0
  285. package/payload/server/public/assets/operator-BtWNEMh-.js +1 -0
  286. package/payload/server/public/assets/operator-BtWNEMh-.js.br +0 -0
  287. package/payload/server/public/assets/operator-BtWNEMh-.js.gz +0 -0
  288. package/payload/server/public/assets/page-BFm91p0e.js +1 -0
  289. package/payload/server/public/assets/page-BFm91p0e.js.br +0 -0
  290. package/payload/server/public/assets/page-BFm91p0e.js.gz +0 -0
  291. package/payload/server/public/assets/{page-E74MYB18.js → page-Ccl0FazM.js} +3 -3
  292. package/payload/server/public/assets/page-Ccl0FazM.js.br +0 -0
  293. package/payload/server/public/assets/page-Ccl0FazM.js.gz +0 -0
  294. package/payload/server/public/assets/{public-CI6K1sNW.js → public-BpC6cn9S.js} +1 -1
  295. package/payload/server/public/assets/public-BpC6cn9S.js.br +0 -0
  296. package/payload/server/public/assets/public-BpC6cn9S.js.gz +0 -0
  297. package/payload/server/public/assets/{rotate-ccw-PWu_QGrD.js → rotate-ccw-BNcqbHkZ.js} +1 -1
  298. package/payload/server/public/assets/rotate-ccw-BNcqbHkZ.js.br +0 -0
  299. package/payload/server/public/assets/rotate-ccw-BNcqbHkZ.js.gz +0 -0
  300. package/payload/server/public/assets/routines-D5iia-XG.js +1 -0
  301. package/payload/server/public/assets/routines-D5iia-XG.js.br +0 -0
  302. package/payload/server/public/assets/routines-D5iia-XG.js.gz +0 -0
  303. package/payload/server/public/assets/{skills-g8HvunPA.js → skills-COLAw_jt.js} +1 -1
  304. package/payload/server/public/assets/skills-COLAw_jt.js.br +0 -0
  305. package/payload/server/public/assets/skills-COLAw_jt.js.gz +0 -0
  306. package/payload/server/public/assets/tasks-CZDQI4nB.js +1 -0
  307. package/payload/server/public/assets/tasks-CZDQI4nB.js.br +0 -0
  308. package/payload/server/public/assets/tasks-CZDQI4nB.js.gz +0 -0
  309. package/payload/server/public/assets/{time-entry-format-BXWuQ6Sw.js → time-entry-format-laTFzawg.js} +1 -1
  310. package/payload/server/public/assets/time-entry-format-laTFzawg.js.br +0 -0
  311. package/payload/server/public/assets/time-entry-format-laTFzawg.js.gz +0 -0
  312. package/payload/server/public/assets/{triangle-alert-BNshRC13.js → triangle-alert-DrwqhEog.js} +1 -1
  313. package/payload/server/public/assets/triangle-alert-DrwqhEog.js.br +0 -0
  314. package/payload/server/public/assets/triangle-alert-DrwqhEog.js.gz +0 -0
  315. package/payload/server/public/assets/{useCopyFeedback-CwTZ9DqH.js → useCopyFeedback-Cxme5GK6.js} +1 -1
  316. package/payload/server/public/assets/useCopyFeedback-Cxme5GK6.js.br +0 -0
  317. package/payload/server/public/assets/useCopyFeedback-Cxme5GK6.js.gz +0 -0
  318. package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css +1 -0
  319. package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css.br +0 -0
  320. package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css.gz +0 -0
  321. package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js +14 -0
  322. package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js.br +0 -0
  323. package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js.gz +0 -0
  324. package/payload/server/public/assets/{useVoiceRecorder-BGqV5R6u.js → useVoiceRecorder-DUBpVa2T.js} +1 -1
  325. package/payload/server/public/assets/useVoiceRecorder-DUBpVa2T.js.br +0 -0
  326. package/payload/server/public/assets/useVoiceRecorder-DUBpVa2T.js.gz +0 -0
  327. package/payload/server/public/assets/{wrench-DQ045Pyk.js → wrench-DWuxPnzF.js} +1 -1
  328. package/payload/server/public/assets/wrench-DWuxPnzF.js.br +0 -0
  329. package/payload/server/public/assets/wrench-DWuxPnzF.js.gz +0 -0
  330. package/payload/server/public/browser.html +4 -4
  331. package/payload/server/public/calendar.html +7 -7
  332. package/payload/server/public/chat.html +13 -13
  333. package/payload/server/public/data.html +11 -11
  334. package/payload/server/public/graph.html +9 -9
  335. package/payload/server/public/index.html +14 -14
  336. package/payload/server/public/operator.html +14 -14
  337. package/payload/server/public/public.html +13 -13
  338. package/payload/server/public/routines.html +6 -6
  339. package/payload/server/public/skills.html +5 -5
  340. package/payload/server/public/tasks.html +6 -6
  341. package/payload/server/server.js +6631 -5690
  342. package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.br +0 -0
  343. package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.gz +0 -0
  344. package/payload/server/public/assets/AdminShell-CpaI2F_p.js +0 -2
  345. package/payload/server/public/assets/AdminShell-CpaI2F_p.js.br +0 -0
  346. package/payload/server/public/assets/AdminShell-CpaI2F_p.js.gz +0 -0
  347. package/payload/server/public/assets/activity-B1rt4GIy.js.br +0 -0
  348. package/payload/server/public/assets/activity-B1rt4GIy.js.gz +0 -0
  349. package/payload/server/public/assets/admin-DE3lLNje.js +0 -1
  350. package/payload/server/public/assets/admin-DE3lLNje.js.br +0 -0
  351. package/payload/server/public/assets/admin-DE3lLNje.js.gz +0 -0
  352. package/payload/server/public/assets/agents-BXq8woVf.js +0 -1
  353. package/payload/server/public/assets/agents-BXq8woVf.js.br +0 -0
  354. package/payload/server/public/assets/agents-BXq8woVf.js.gz +0 -0
  355. package/payload/server/public/assets/browser-1QSCPl3X.js.br +0 -0
  356. package/payload/server/public/assets/browser-1QSCPl3X.js.gz +0 -0
  357. package/payload/server/public/assets/calendar-D009qWVV.js.br +0 -0
  358. package/payload/server/public/assets/calendar-D009qWVV.js.gz +0 -0
  359. package/payload/server/public/assets/chat-CUg1fTqJ.js +0 -1
  360. package/payload/server/public/assets/chat-CUg1fTqJ.js.br +0 -1
  361. package/payload/server/public/assets/chat-CUg1fTqJ.js.gz +0 -0
  362. package/payload/server/public/assets/chevron-left-C2JEy7uU.js +0 -1
  363. package/payload/server/public/assets/chevron-left-C2JEy7uU.js.br +0 -0
  364. package/payload/server/public/assets/chevron-right-DWa047Ys.js +0 -1
  365. package/payload/server/public/assets/chevron-right-DWa047Ys.js.br +0 -0
  366. package/payload/server/public/assets/clock-C3OqfCnn.js +0 -1
  367. package/payload/server/public/assets/clock-C3OqfCnn.js.br +0 -0
  368. package/payload/server/public/assets/clock-C3OqfCnn.js.gz +0 -0
  369. package/payload/server/public/assets/data-Bu_yCScC.js +0 -1
  370. package/payload/server/public/assets/data-Bu_yCScC.js.br +0 -0
  371. package/payload/server/public/assets/data-Bu_yCScC.js.gz +0 -0
  372. package/payload/server/public/assets/file-text-DwGAfCsd.js.br +0 -0
  373. package/payload/server/public/assets/file-text-DwGAfCsd.js.gz +0 -0
  374. package/payload/server/public/assets/graph-D-DGlfhW.js.br +0 -0
  375. package/payload/server/public/assets/graph-D-DGlfhW.js.gz +0 -0
  376. package/payload/server/public/assets/graph-labels-BbX3fXzl.js +0 -1
  377. package/payload/server/public/assets/graph-labels-BbX3fXzl.js.br +0 -0
  378. package/payload/server/public/assets/graph-labels-BbX3fXzl.js.gz +0 -0
  379. package/payload/server/public/assets/maximize-2-C_-KapxH.js.br +0 -0
  380. package/payload/server/public/assets/maximize-2-C_-KapxH.js.gz +0 -0
  381. package/payload/server/public/assets/operator-CFrZVcv6.js +0 -1
  382. package/payload/server/public/assets/operator-CFrZVcv6.js.br +0 -0
  383. package/payload/server/public/assets/operator-CFrZVcv6.js.gz +0 -0
  384. package/payload/server/public/assets/page-CHt62f1_.js +0 -1
  385. package/payload/server/public/assets/page-CHt62f1_.js.br +0 -0
  386. package/payload/server/public/assets/page-CHt62f1_.js.gz +0 -0
  387. package/payload/server/public/assets/page-E74MYB18.js.br +0 -0
  388. package/payload/server/public/assets/page-E74MYB18.js.gz +0 -0
  389. package/payload/server/public/assets/public-CI6K1sNW.js.br +0 -0
  390. package/payload/server/public/assets/public-CI6K1sNW.js.gz +0 -0
  391. package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.br +0 -0
  392. package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.gz +0 -0
  393. package/payload/server/public/assets/routines-Cp3_XS7S.js +0 -1
  394. package/payload/server/public/assets/routines-Cp3_XS7S.js.br +0 -0
  395. package/payload/server/public/assets/routines-Cp3_XS7S.js.gz +0 -0
  396. package/payload/server/public/assets/skills-g8HvunPA.js.br +0 -0
  397. package/payload/server/public/assets/skills-g8HvunPA.js.gz +0 -0
  398. package/payload/server/public/assets/tasks-CMC_jDVm.js +0 -1
  399. package/payload/server/public/assets/tasks-CMC_jDVm.js.br +0 -0
  400. package/payload/server/public/assets/tasks-CMC_jDVm.js.gz +0 -0
  401. package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.br +0 -2
  402. package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.gz +0 -0
  403. package/payload/server/public/assets/triangle-alert-BNshRC13.js.br +0 -0
  404. package/payload/server/public/assets/triangle-alert-BNshRC13.js.gz +0 -0
  405. package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.br +0 -0
  406. package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.gz +0 -0
  407. package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js +0 -14
  408. package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.br +0 -0
  409. package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.gz +0 -0
  410. package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css +0 -1
  411. package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.br +0 -0
  412. package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.gz +0 -0
  413. package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.br +0 -0
  414. package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.gz +0 -0
  415. package/payload/server/public/assets/wrench-DQ045Pyk.js.br +0 -0
  416. package/payload/server/public/assets/wrench-DQ045Pyk.js.gz +0 -0
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Task 2130 — the seed's own behaviour, plus the structural guards on the
3
+ * Cypher it issues.
4
+ *
5
+ * The Cypher's runtime semantics belong to Neo4j and are proven on a real
6
+ * install (the task's laptop verification). What IS testable here, and what a
7
+ * future edit would otherwise break silently, is the SHAPE of the statement:
8
+ * which properties `ON CREATE` writes and — the load-bearing one — which
9
+ * properties `ON MATCH` is allowed to touch. A re-seed that writes
10
+ * `eventStatus` on match would re-suspend every routine the operator had
11
+ * enabled, on every upgrade, and nothing would report it.
12
+ */
13
+ import { readFileSync } from "node:fs";
14
+ import { resolve } from "node:path";
15
+ import { describe, expect, it } from "vitest";
16
+ import {
17
+ MERGE_CYPHER,
18
+ ROUTINE_ROSTER,
19
+ ROUTINE_TEMPLATE_VERSION,
20
+ VERIFY_CYPHER,
21
+ resolveOwnerTimezone,
22
+ seedRoutineRoster,
23
+ type RoutineMergeAction,
24
+ type SeedSessionLike,
25
+ } from "../index.js";
26
+
27
+ interface FakeCall {
28
+ query: string;
29
+ params: Record<string, unknown>;
30
+ }
31
+
32
+ /** A session that answers the two statements the seed issues, in order:
33
+ * the timezone read, the MERGE, then the verify read. */
34
+ function fakeSession(opts: {
35
+ timezone?: { timezone: string; timezoneSource: string } | null;
36
+ merges: Array<{ key: string; eventId: string; action: RoutineMergeAction }>;
37
+ verify: { actual: number; suspended: number; nextRunNull: number };
38
+ }): { session: SeedSessionLike; calls: FakeCall[] } {
39
+ const calls: FakeCall[] = [];
40
+ const rec = (obj: Record<string, unknown>) => ({ get: (k: string) => obj[k] });
41
+ const session: SeedSessionLike = {
42
+ async run(query: string, params: Record<string, unknown> = {}) {
43
+ calls.push({ query, params });
44
+ if (query.includes("UserProfile")) {
45
+ return { records: opts.timezone ? [rec({ timezone: opts.timezone.timezone })] : [] };
46
+ }
47
+ if (query.includes("MERGE (e:Event")) {
48
+ return { records: opts.merges.map((m) => rec({ ...m })) };
49
+ }
50
+ return { records: [rec(opts.verify)] };
51
+ },
52
+ };
53
+ return { session, calls };
54
+ }
55
+
56
+ const allUnchanged = ROUTINE_ROSTER.map((r) => ({
57
+ key: r.key,
58
+ eventId: `id-${r.key}`,
59
+ action: "unchanged" as RoutineMergeAction,
60
+ }));
61
+
62
+ const allCreated = ROUTINE_ROSTER.map((r) => ({
63
+ key: r.key,
64
+ eventId: `id-${r.key}`,
65
+ action: "create" as RoutineMergeAction,
66
+ }));
67
+
68
+ const cleanVerify = { actual: 10, suspended: 10, nextRunNull: 10 };
69
+
70
+ describe("MERGE statement shape", () => {
71
+ /** The property names assigned inside a `ON CREATE SET` / `ON MATCH SET`
72
+ * block, up to the next clause keyword. */
73
+ function propertiesSetIn(clause: "ON CREATE SET" | "ON MATCH SET"): string[] {
74
+ const start = MERGE_CYPHER.indexOf(clause);
75
+ expect(start, `${clause} must be present`).toBeGreaterThan(-1);
76
+ const after = MERGE_CYPHER.slice(start + clause.length);
77
+ const end = after.search(/\n\s*(ON MATCH SET|RETURN|WITH)\b/);
78
+ const body = end === -1 ? after : after.slice(0, end);
79
+ return [...body.matchAll(/\be\.([a-zA-Z]+)\s*=/g)].map((m) => m[1]);
80
+ }
81
+
82
+ it("ON CREATE writes the full suspended shape", () => {
83
+ const props = new Set(propertiesSetIn("ON CREATE SET"));
84
+ for (const required of [
85
+ "eventId",
86
+ "name",
87
+ "agentPrompt",
88
+ "recurrence",
89
+ "routineTemplateVersion",
90
+ "eventStatus",
91
+ "nextRun",
92
+ "agentChannel",
93
+ "agentDestination",
94
+ "actionPlugin",
95
+ "sourcePlugin",
96
+ "timezone",
97
+ ]) {
98
+ expect(props, `ON CREATE must set ${required}`).toContain(required);
99
+ }
100
+ });
101
+
102
+ it("ON CREATE seeds suspended, with no nextRun, channel, destination or action", () => {
103
+ const start = MERGE_CYPHER.indexOf("ON CREATE SET");
104
+ const body = MERGE_CYPHER.slice(start, MERGE_CYPHER.indexOf("ON MATCH SET"));
105
+ expect(body).toContain("e.eventStatus = 'suspended'");
106
+ expect(body).toContain("e.nextRun = null");
107
+ expect(body).toContain("e.agentChannel = null");
108
+ expect(body).toContain("e.agentDestination = null");
109
+ expect(body).toContain("e.actionPlugin = null");
110
+ });
111
+
112
+ it("ON CREATE leaves sourcePlugin null so the lifecycle sweep never cancels an enabled routine", () => {
113
+ // check-due-events.ts cancels any 'scheduled' Event whose sourcePlugin is
114
+ // absent from enabledPlugins. A roster routine spans many plugins, so any
115
+ // value here would cancel it the moment an operator enabled it.
116
+ const start = MERGE_CYPHER.indexOf("ON CREATE SET");
117
+ const body = MERGE_CYPHER.slice(start, MERGE_CYPHER.indexOf("ON MATCH SET"));
118
+ expect(body).toContain("e.sourcePlugin = null");
119
+ });
120
+
121
+ it("ON MATCH touches ONLY the prompt, name, version and updatedAt", () => {
122
+ expect(new Set(propertiesSetIn("ON MATCH SET"))).toEqual(
123
+ new Set(["agentPrompt", "name", "routineTemplateVersion", "updatedAt"]),
124
+ );
125
+ });
126
+
127
+ it("ON MATCH never writes anything the operator controls", () => {
128
+ const start = MERGE_CYPHER.indexOf("ON MATCH SET");
129
+ const body = MERGE_CYPHER.slice(start, MERGE_CYPHER.indexOf("RETURN"));
130
+ for (const operatorOwned of ["eventStatus", "agentChannel", "agentDestination", "recurrence", "timezone"]) {
131
+ expect(body, `ON MATCH must not write ${operatorOwned}`).not.toContain(`e.${operatorOwned} =`);
132
+ }
133
+ });
134
+
135
+ it("refreshes only a behind-version row with no operator edit", () => {
136
+ expect(MERGE_CYPHER).toContain("(existed AND NOT edited AND priorVersion < $version) AS refreshing");
137
+ });
138
+
139
+ it("reads the operator-edit guard from a property both edit paths actually write", () => {
140
+ // The guard is only as real as its producer. Shipping the reader without a
141
+ // writer would leave `edited` false for every row, so the first version
142
+ // bump would silently overwrite every customised prompt while the code
143
+ // comment promised the opposite. Both paths that let an operator change a
144
+ // routine's prompt stamp it: the scheduling plugin's schedule-update tool,
145
+ // and the Routines page PATCH route.
146
+ expect(MERGE_CYPHER).toContain("pre.operatorEdited IS NOT NULL AS edited");
147
+ const writers = [
148
+ resolve(process.cwd(), "../../plugins/scheduling/mcp/src/tools/schedule-update.ts"),
149
+ resolve(process.cwd(), "../../ui/server/routes/admin/routines.ts"),
150
+ ];
151
+ for (const writer of writers) {
152
+ expect(readFileSync(writer, "utf-8"), `${writer} must stamp operatorEdited`).toMatch(
153
+ /e\.operatorEdited = \$/,
154
+ );
155
+ }
156
+ });
157
+
158
+ it("MERGEs on the account plus the template key", () => {
159
+ expect(MERGE_CYPHER).toContain("MERGE (e:Event {accountId: $accountId, routineTemplate: row.key})");
160
+ });
161
+
162
+ it("verify reads the roster back out of the graph rather than counting writes", () => {
163
+ expect(VERIFY_CYPHER).toContain("MATCH (e:Event {accountId: $accountId})");
164
+ expect(VERIFY_CYPHER).toContain("e.routineTemplate IS NOT NULL");
165
+ expect(VERIFY_CYPHER).toContain("count(e) AS actual");
166
+ });
167
+ });
168
+
169
+ describe("resolveOwnerTimezone", () => {
170
+ it("accepts an owner-set zone", async () => {
171
+ const { session } = fakeSession({
172
+ timezone: { timezone: "Europe/Lisbon", timezoneSource: "owner-set" },
173
+ merges: allUnchanged,
174
+ verify: cleanVerify,
175
+ });
176
+ expect(await resolveOwnerTimezone(session, "acct-1")).toEqual({
177
+ timezone: "Europe/Lisbon",
178
+ source: "owner-profile",
179
+ });
180
+ });
181
+
182
+ it("reports absent rather than defaulting to the box's zone", async () => {
183
+ const { session } = fakeSession({ timezone: null, merges: allUnchanged, verify: cleanVerify });
184
+ expect(await resolveOwnerTimezone(session, "acct-1")).toEqual({ timezone: null, source: "absent" });
185
+ });
186
+
187
+ it("only reads a profile marked owner-set", async () => {
188
+ const { session, calls } = fakeSession({ timezone: null, merges: allUnchanged, verify: cleanVerify });
189
+ await resolveOwnerTimezone(session, "acct-1");
190
+ expect(calls[0].query).toContain("up.timezoneSource = 'owner-set'");
191
+ });
192
+ });
193
+
194
+ describe("seedRoutineRoster", () => {
195
+ const seed = async (over: Partial<Parameters<typeof seedRoutineRoster>[0]> = {}, fake = {
196
+ timezone: { timezone: "Europe/Lisbon", timezoneSource: "owner-set" },
197
+ merges: allUnchanged,
198
+ verify: cleanVerify,
199
+ }) => {
200
+ const lines: string[] = [];
201
+ const { session, calls } = fakeSession(fake);
202
+ let tick = 0;
203
+ const report = await seedRoutineRoster({
204
+ session,
205
+ accountId: "abcd1234-0000-0000-0000-000000000000",
206
+ now: "2026-07-29T00:00:00.000Z",
207
+ log: (l) => lines.push(l),
208
+ clock: () => (tick++ === 0 ? 1000 : 1042),
209
+ ...over,
210
+ });
211
+ return { report, lines, calls };
212
+ };
213
+
214
+ it("passes every roster row to the MERGE", async () => {
215
+ const { calls } = await seed();
216
+ const mergeCall = calls.find((c) => c.query.includes("MERGE (e:Event"))!;
217
+ expect((mergeCall.params.rows as unknown[]).length).toBe(ROUTINE_ROSTER.length);
218
+ expect(mergeCall.params.version).toBe(ROUTINE_TEMPLATE_VERSION);
219
+ expect(mergeCall.params.timezone).toBe("Europe/Lisbon");
220
+ });
221
+
222
+ it("passes a null timezone through rather than substituting one", async () => {
223
+ const { calls } = await seed({}, { timezone: null, merges: allUnchanged, verify: cleanVerify });
224
+ const mergeCall = calls.find((c) => c.query.includes("MERGE (e:Event"))!;
225
+ expect(mergeCall.params.timezone).toBeNull();
226
+ });
227
+
228
+ it("emits the full lifecycle on a run that changes nothing", async () => {
229
+ const { lines } = await seed();
230
+ expect(lines.filter((l) => l.includes("op=begin"))).toHaveLength(1);
231
+ expect(lines.filter((l) => l.includes("op=tz-resolved"))).toHaveLength(1);
232
+ expect(lines.filter((l) => l.includes("op=merge"))).toHaveLength(ROUTINE_ROSTER.length);
233
+ expect(lines.filter((l) => l.includes("op=verify"))).toHaveLength(1);
234
+ expect(lines.filter((l) => l.includes("op=end"))).toHaveLength(1);
235
+ });
236
+
237
+ it("carries the account correlation key on every line", async () => {
238
+ const { lines } = await seed();
239
+ expect(lines.every((l) => l.includes("acct=abcd1234"))).toBe(true);
240
+ });
241
+
242
+ it("reports the verify counts measured back out of the graph", async () => {
243
+ // One row short of the roster: the drift the verify pass exists to report.
244
+ // Derived from ROUTINE_ROSTER rather than typed, so adding a routine does
245
+ // not silently turn this into an assertion about a stale count.
246
+ const expected = ROUTINE_ROSTER.length;
247
+ const short = expected - 1;
248
+ const { report, lines } = await seed({}, {
249
+ timezone: { timezone: "Europe/Lisbon", timezoneSource: "owner-set" },
250
+ merges: allUnchanged,
251
+ verify: { actual: short, suspended: short, nextRunNull: short },
252
+ });
253
+ expect(report.verify).toEqual({ expected, actual: short, suspended: short, nextRunNull: short });
254
+ expect(lines).toContainEqual(
255
+ `[routine-seed] op=verify acct=abcd1234 expected=${expected} actual=${short} suspended=${short} nextRunNull=${short}`,
256
+ );
257
+ });
258
+
259
+ it("logs tz-resolved as null when no owner-set profile exists", async () => {
260
+ const { lines } = await seed({}, { timezone: null, merges: allUnchanged, verify: cleanVerify });
261
+ expect(lines).toContainEqual("[routine-seed] op=tz-resolved acct=abcd1234 tz=null source=absent");
262
+ });
263
+
264
+ it("counts each merge action separately", async () => {
265
+ const mixed = [
266
+ { key: "start-of-day", eventId: "e1", action: "create" as RoutineMergeAction },
267
+ { key: "inbound-check", eventId: "e2", action: "refresh-version" as RoutineMergeAction },
268
+ { key: "end-of-day", eventId: "e3", action: "skip-operator-edited" as RoutineMergeAction },
269
+ { key: "weekly-digest", eventId: "e4", action: "unchanged" as RoutineMergeAction },
270
+ ];
271
+ const { report } = await seed({}, {
272
+ timezone: null,
273
+ merges: mixed,
274
+ verify: { actual: 4, suspended: 3, nextRunNull: 3 },
275
+ });
276
+ expect(report.created).toBe(1);
277
+ expect(report.refreshed).toBe(1);
278
+ expect(report.skippedOperatorEdited).toBe(1);
279
+ expect(report.unchanged).toBe(1);
280
+ });
281
+
282
+ it("names the action and eventId on every merge line, so a clobber is greppable", async () => {
283
+ const { lines } = await seed({}, {
284
+ timezone: null,
285
+ merges: allCreated,
286
+ verify: cleanVerify,
287
+ });
288
+ expect(lines).toContainEqual(
289
+ "[routine-seed] op=merge acct=abcd1234 key=start-of-day action=create eventId=id-start-of-day",
290
+ );
291
+ });
292
+
293
+ it("reports elapsed time on op=end", async () => {
294
+ const { report, lines } = await seed();
295
+ expect(report.ranMs).toBe(42);
296
+ expect(lines.find((l) => l.includes("op=end"))).toContain("ranMs=42");
297
+ });
298
+ });
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Task 2130 — the routine-roster seed.
3
+ *
4
+ * One module, two callers: `platform/scripts/seed-neo4j.sh` reaches it through
5
+ * `dist/seed-cli.js` on every install and upgrade (which is what covers the
6
+ * existing fleet), and the admin plugin's `account_create` imports it directly
7
+ * for a new client sub-account. Sharing the MERGE rather than writing it twice
8
+ * is the point: two copies drift, and the drift would only ever be noticed as
9
+ * "the sub-accounts have different routines from the house".
10
+ *
11
+ * The seed is inert by construction. Every row lands `eventStatus:'suspended'`
12
+ * with `nextRun` NULL, and all four selecting queries in `check-due-events.ts`
13
+ * admit only `'scheduled'`: the one-time dispatch (`:686-690`, `WHERE
14
+ * e.eventStatus = 'scheduled' AND e.recurrence IS NULL`), the recurring
15
+ * dispatch (`:739-744`), the lifecycle activate/heal probe (`:491`) and the
16
+ * lifecycle deactivation sweep (`:550-551`). NULL `nextRun` is the second,
17
+ * independent guard: the recurring query also requires `e.nextRun IS NOT NULL`
18
+ * (`:742`). Neither guard depends on the other holding.
19
+ *
20
+ * `nextRun` is left NULL rather than computed, and that is not tidiness. The
21
+ * recurring loop advances from the STORED value, not from now
22
+ * (`check-due-events.ts:778`, `currentDate: new Date(currentNextRun)`), so a
23
+ * row seeded with a computed `nextRun` and enabled a month later advances one
24
+ * occurrence per tick and dispatches on every one of them. A quarter-hourly
25
+ * routine would replay roughly 2,880 turns. The only safe value is no value,
26
+ * until an operator supplies a recurrence through `schedule-update`, which
27
+ * recomputes from now.
28
+ *
29
+ * Each line number above is paired with the construct it points at. Bare
30
+ * numbers drift on the next edit to that file and then send a reader somewhere
31
+ * unrelated, which is worse than no citation.
32
+ */
33
+ import { ROUTINE_ROSTER, ROUTINE_TEMPLATE_VERSION, type RoutineTemplate } from "./roster.js";
34
+
35
+ export { ROUTINE_ROSTER, ROUTINE_ROSTER_KEYS, ROUTINE_TEMPLATE_VERSION } from "./roster.js";
36
+ export type { RoutineTemplate } from "./roster.js";
37
+
38
+ /** The minimal Neo4j session surface the seed needs. Both the admin plugin's
39
+ * `getSession()` and a driver session from `seed-cli` satisfy it; the tests
40
+ * pass an in-memory fake. Deliberately not `neo4j-driver`'s `Session` type, so
41
+ * this module stays importable by a consumer that has no driver types. */
42
+ export interface SeedSessionLike {
43
+ run(query: string, params?: Record<string, unknown>): Promise<{ records: Array<{ get(key: string): unknown }> }>;
44
+ }
45
+
46
+ /** What the seed did to one routine.
47
+ * - `create` — the row did not exist. On an already-seeded account this is the
48
+ * clobber signature: something deleted the row, or the MERGE key changed.
49
+ * - `refresh-version` — the stored template version was behind and no operator
50
+ * had edited the row, so the prompt and name were refreshed.
51
+ * - `skip-operator-edited` — a refresh was due and was withheld because the row
52
+ * carries `operatorEdited`. The operator's prompt wins, permanently.
53
+ * - `unchanged` — the row is already at this version. The steady state. */
54
+ export type RoutineMergeAction = "create" | "refresh-version" | "skip-operator-edited" | "unchanged";
55
+
56
+ export interface RoutineMergeRow {
57
+ key: string;
58
+ eventId: string;
59
+ action: RoutineMergeAction;
60
+ }
61
+
62
+ /** The post-condition, measured back out of the graph rather than counted from
63
+ * the statements issued. A seed that ran and wrote nothing must not be able to
64
+ * report success, which is exactly what counting its own writes would do.
65
+ *
66
+ * `expected === actual` is the health signal. `suspended` and `nextRunNull`
67
+ * equal `actual` on a fresh seed and legitimately fall below it once an
68
+ * operator enables a routine — they are measurements, not assertions. */
69
+ export interface RoutineVerifyCounts {
70
+ expected: number;
71
+ actual: number;
72
+ suspended: number;
73
+ nextRunNull: number;
74
+ }
75
+
76
+ export interface RoutineSeedReport {
77
+ accountId: string;
78
+ version: number;
79
+ timezone: string | null;
80
+ timezoneSource: "owner-profile" | "absent";
81
+ merges: RoutineMergeRow[];
82
+ created: number;
83
+ refreshed: number;
84
+ skippedOperatorEdited: number;
85
+ unchanged: number;
86
+ verify: RoutineVerifyCounts;
87
+ ranMs: number;
88
+ }
89
+
90
+ /** Neo4j returns integers as its own `Integer` type over bolt and as plain
91
+ * numbers through a test fake. Normalise both without importing the driver. */
92
+ function toInt(raw: unknown): number {
93
+ if (typeof raw === "number") return raw;
94
+ if (raw && typeof (raw as { toNumber?: () => number }).toNumber === "function") {
95
+ return (raw as { toNumber: () => number }).toNumber();
96
+ }
97
+ return Number(raw ?? 0);
98
+ }
99
+
100
+ /** First eight characters of the accountId — the correlation key every
101
+ * `[routine-seed]` and `[schedule-audit]` line carries. */
102
+ export function acctTag(accountId: string): string {
103
+ return accountId.slice(0, 8);
104
+ }
105
+
106
+ /**
107
+ * The owner's IANA timezone, read from the owner `:UserProfile` and accepted
108
+ * only when it is owner-set.
109
+ *
110
+ * A profile whose `timezoneSource` is anything else was backfilled from the
111
+ * box's own zone, and seeding that onto a routine would give the operator a
112
+ * routine that fires at the server's hour while looking entirely healthy. NULL
113
+ * is the honest value in that case, and the heartbeat's `op=null-timezone`
114
+ * count is what surfaces it — on occasion one, rather than at the first
115
+ * mis-timed fire.
116
+ */
117
+ export async function resolveOwnerTimezone(
118
+ session: SeedSessionLike,
119
+ accountId: string,
120
+ ): Promise<{ timezone: string | null; source: "owner-profile" | "absent" }> {
121
+ const res = await session.run(
122
+ `MATCH (up:UserProfile {accountId: $accountId})
123
+ WHERE up.timezoneSource = 'owner-set' AND up.timezone IS NOT NULL AND trim(up.timezone) <> ''
124
+ RETURN up.timezone AS timezone
125
+ LIMIT 1`,
126
+ { accountId },
127
+ );
128
+ const raw = res.records[0]?.get("timezone");
129
+ if (typeof raw === "string" && raw.trim() !== "") {
130
+ return { timezone: raw.trim(), source: "owner-profile" };
131
+ }
132
+ return { timezone: null, source: "absent" };
133
+ }
134
+
135
+ /** The MERGE. One statement over the whole roster.
136
+ *
137
+ * The pre-state is captured by an OPTIONAL MATCH before the MERGE, because the
138
+ * MERGE itself cannot report which branch it took. Without it the seed could
139
+ * only log what it intended; `action=create` on an already-seeded account
140
+ * would be indistinguishable from `action=unchanged`, and the clobber it
141
+ * signals would be invisible.
142
+ *
143
+ * ON MATCH writes nothing an operator controls: not `eventStatus`, not
144
+ * `agentChannel`, not `agentDestination`, not `recurrence`, not `timezone`. A
145
+ * re-seed on upgrade must not re-suspend an enabled routine, unbind a bound
146
+ * destination, or reset a retimed cron. Only the prompt, the name and the
147
+ * version move, and only when the row is behind and unedited. */
148
+ export const MERGE_CYPHER = `
149
+ UNWIND $rows AS row
150
+ OPTIONAL MATCH (pre:Event {accountId: $accountId, routineTemplate: row.key})
151
+ WITH row,
152
+ pre IS NOT NULL AS existed,
153
+ coalesce(pre.routineTemplateVersion, 0) AS priorVersion,
154
+ pre.operatorEdited IS NOT NULL AS edited
155
+ WITH row, existed, priorVersion, edited,
156
+ (existed AND NOT edited AND priorVersion < $version) AS refreshing
157
+ MERGE (e:Event {accountId: $accountId, routineTemplate: row.key})
158
+ ON CREATE SET e.eventId = randomUUID(),
159
+ e.name = row.name,
160
+ e.agentPrompt = row.prompt,
161
+ e.recurrence = row.recurrence,
162
+ e.routineTemplateVersion = $version,
163
+ e.eventStatus = 'suspended',
164
+ e.nextRun = null,
165
+ e.agentChannel = null,
166
+ e.agentDestination = null,
167
+ e.actionPlugin = null,
168
+ e.actionTool = null,
169
+ e.actionArgs = null,
170
+ e.sourcePlugin = null,
171
+ e.timezone = $timezone,
172
+ e.createdBySource = 'installer',
173
+ e.createdByAgent = 'system',
174
+ e.createdByTool = 'routine-roster-seed',
175
+ e.createdAt = $now,
176
+ e.updatedAt = $now
177
+ ON MATCH SET e.agentPrompt = CASE WHEN refreshing THEN row.prompt ELSE e.agentPrompt END,
178
+ e.name = CASE WHEN refreshing THEN row.name ELSE e.name END,
179
+ e.routineTemplateVersion = CASE WHEN refreshing THEN $version ELSE e.routineTemplateVersion END,
180
+ e.updatedAt = CASE WHEN refreshing THEN $now ELSE e.updatedAt END
181
+ RETURN row.key AS key,
182
+ e.eventId AS eventId,
183
+ CASE WHEN NOT existed THEN 'create'
184
+ WHEN priorVersion >= $version THEN 'unchanged'
185
+ WHEN edited THEN 'skip-operator-edited'
186
+ ELSE 'refresh-version' END AS action`;
187
+
188
+ /** The post-condition read. Counts every roster row the account actually holds,
189
+ * independently of what the MERGE above believes it wrote. */
190
+ export const VERIFY_CYPHER = `
191
+ MATCH (e:Event {accountId: $accountId})
192
+ WHERE e.routineTemplate IS NOT NULL
193
+ RETURN count(e) AS actual,
194
+ sum(CASE WHEN e.eventStatus = 'suspended' THEN 1 ELSE 0 END) AS suspended,
195
+ sum(CASE WHEN e.nextRun IS NULL THEN 1 ELSE 0 END) AS nextRunNull`;
196
+
197
+ export interface SeedRoutineRosterArgs {
198
+ session: SeedSessionLike;
199
+ accountId: string;
200
+ /** Defaults to `ROUTINE_TEMPLATE_VERSION`. Injected by the tests. */
201
+ version?: number;
202
+ /** ISO timestamp stamped on created rows. Injected by the tests. */
203
+ now?: string;
204
+ /** Defaults to `ROUTINE_ROSTER`. Injected by the tests. */
205
+ roster?: readonly RoutineTemplate[];
206
+ /** Where the `[routine-seed]` lines go. Defaults to stderr. */
207
+ log?: (line: string) => void;
208
+ /** Elapsed milliseconds for `op=end`. Injected by the tests so the line is
209
+ * assertable; `Date.now()` either side of the work otherwise. */
210
+ clock?: () => number;
211
+ }
212
+
213
+ /**
214
+ * Seed (or re-seed) the roster for one account and report what happened.
215
+ *
216
+ * Emits the full `[routine-seed]` lifecycle on every run, including a run that
217
+ * changes nothing: `op=begin`, `op=tz-resolved`, one `op=merge` per routine,
218
+ * `op=verify`, `op=end`. A no-op run that logged nothing would be
219
+ * indistinguishable from a seed that never ran, and "never ran" is the failure
220
+ * this logging exists to make visible.
221
+ *
222
+ * Throws on a Neo4j failure. Both callers catch: the installer path exits
223
+ * non-zero, and `account_create` reports a created-but-routine-less account
224
+ * with the recovery action rather than a generic create failure.
225
+ */
226
+ export async function seedRoutineRoster(args: SeedRoutineRosterArgs): Promise<RoutineSeedReport> {
227
+ const {
228
+ session,
229
+ accountId,
230
+ version = ROUTINE_TEMPLATE_VERSION,
231
+ now = new Date().toISOString(),
232
+ roster = ROUTINE_ROSTER,
233
+ log = (line: string) => process.stderr.write(`${line}\n`),
234
+ clock = () => Date.now(),
235
+ } = args;
236
+
237
+ const acct = acctTag(accountId);
238
+ const startedAt = clock();
239
+ log(`[routine-seed] op=begin acct=${acct} roster=${roster.length} version=${version}`);
240
+
241
+ const { timezone, source } = await resolveOwnerTimezone(session, accountId);
242
+ log(`[routine-seed] op=tz-resolved acct=${acct} tz=${timezone ?? "null"} source=${source}`);
243
+
244
+ const mergeRes = await session.run(MERGE_CYPHER, {
245
+ accountId,
246
+ version,
247
+ timezone,
248
+ now,
249
+ rows: roster.map((r) => ({ key: r.key, name: r.name, prompt: r.prompt, recurrence: r.recurrence })),
250
+ });
251
+
252
+ const merges: RoutineMergeRow[] = mergeRes.records.map((rec) => ({
253
+ key: String(rec.get("key")),
254
+ eventId: String(rec.get("eventId")),
255
+ action: String(rec.get("action")) as RoutineMergeAction,
256
+ }));
257
+
258
+ for (const m of merges) {
259
+ log(`[routine-seed] op=merge acct=${acct} key=${m.key} action=${m.action} eventId=${m.eventId}`);
260
+ }
261
+
262
+ const verifyRes = await session.run(VERIFY_CYPHER, { accountId });
263
+ const verifyRec = verifyRes.records[0];
264
+ const verify: RoutineVerifyCounts = {
265
+ expected: roster.length,
266
+ actual: toInt(verifyRec?.get("actual")),
267
+ suspended: toInt(verifyRec?.get("suspended")),
268
+ nextRunNull: toInt(verifyRec?.get("nextRunNull")),
269
+ };
270
+ log(
271
+ `[routine-seed] op=verify acct=${acct} expected=${verify.expected} actual=${verify.actual} ` +
272
+ `suspended=${verify.suspended} nextRunNull=${verify.nextRunNull}`,
273
+ );
274
+
275
+ const created = merges.filter((m) => m.action === "create").length;
276
+ const refreshed = merges.filter((m) => m.action === "refresh-version").length;
277
+ const skippedOperatorEdited = merges.filter((m) => m.action === "skip-operator-edited").length;
278
+ const unchanged = merges.filter((m) => m.action === "unchanged").length;
279
+ const ranMs = clock() - startedAt;
280
+
281
+ log(
282
+ `[routine-seed] op=end acct=${acct} ranMs=${ranMs} created=${created} ` +
283
+ `skipped=${skippedOperatorEdited} refreshed=${refreshed} unchanged=${unchanged}`,
284
+ );
285
+
286
+ return {
287
+ accountId,
288
+ version,
289
+ timezone,
290
+ timezoneSource: source,
291
+ merges,
292
+ created,
293
+ refreshed,
294
+ skippedOperatorEdited,
295
+ unchanged,
296
+ verify,
297
+ ranMs,
298
+ };
299
+ }