@rubytech/create-maxy-code 0.1.576 → 0.1.578

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 (259) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/active-rules/dist/index.d.ts.map +1 -1
  3. package/payload/platform/lib/active-rules/dist/index.js +32 -1
  4. package/payload/platform/lib/active-rules/dist/index.js.map +1 -1
  5. package/payload/platform/lib/active-rules/src/index.test.ts +52 -3
  6. package/payload/platform/lib/active-rules/src/index.ts +36 -2
  7. package/payload/platform/lib/dispatch-read/dist/allocate.d.ts +57 -0
  8. package/payload/platform/lib/dispatch-read/dist/allocate.d.ts.map +1 -0
  9. package/payload/platform/lib/dispatch-read/dist/allocate.js +33 -0
  10. package/payload/platform/lib/dispatch-read/dist/allocate.js.map +1 -0
  11. package/payload/platform/lib/dispatch-read/dist/index.d.ts +2 -0
  12. package/payload/platform/lib/dispatch-read/dist/index.d.ts.map +1 -1
  13. package/payload/platform/lib/dispatch-read/dist/index.js +10 -1
  14. package/payload/platform/lib/dispatch-read/dist/index.js.map +1 -1
  15. package/payload/platform/lib/dispatch-read/src/__tests__/allocate.test.ts +41 -0
  16. package/payload/platform/lib/dispatch-read/src/allocate.ts +89 -0
  17. package/payload/platform/lib/dispatch-read/src/index.ts +14 -0
  18. package/payload/platform/lib/routine-templates/dist/index.d.ts +4 -4
  19. package/payload/platform/lib/routine-templates/dist/index.js +4 -4
  20. package/payload/platform/lib/routine-templates/src/index.ts +4 -4
  21. package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-compliance.test.sh +7 -2
  22. package/payload/platform/plugins/admin/hooks/prompt-optimiser-compliance.sh +8 -2
  23. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +4 -2
  24. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +17 -0
  25. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-visit-status-write.test.ts +333 -22
  26. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-visits-scope.test.ts +262 -0
  27. package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +126 -21
  28. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/visit-status.ts +142 -2
  29. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/visits.ts +118 -6
  30. package/payload/platform/plugins/dispatch/PLUGIN.md +2 -0
  31. package/payload/platform/plugins/dispatch/mcp/dist/lib/allocate.d.ts +19 -50
  32. package/payload/platform/plugins/dispatch/mcp/dist/lib/allocate.d.ts.map +1 -1
  33. package/payload/platform/plugins/dispatch/mcp/dist/lib/allocate.js +1 -28
  34. package/payload/platform/plugins/dispatch/mcp/dist/lib/allocate.js.map +1 -1
  35. package/payload/platform/plugins/dispatch/skills/dispatch-page/SKILL.md +45 -6
  36. package/payload/platform/plugins/dispatch/skills/dispatch-page/template/planner.css +60 -0
  37. package/payload/platform/plugins/dispatch/skills/dispatch-page/template/planner.html +13 -0
  38. package/payload/platform/plugins/dispatch/skills/dispatch-page/template/planner.js +203 -5
  39. package/payload/platform/plugins/docs/references/internals.md +1 -1
  40. package/payload/platform/plugins/docs/references/platform.md +2 -0
  41. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +0 -2
  42. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  43. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-path.test.d.ts +2 -0
  44. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-path.test.d.ts.map +1 -0
  45. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-path.test.js +19 -0
  46. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-path.test.js.map +1 -0
  47. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.d.ts +2 -0
  48. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.d.ts.map +1 -0
  49. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.js +110 -0
  50. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-register.test.js.map +1 -0
  51. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.d.ts +2 -0
  52. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.d.ts.map +1 -0
  53. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.js +22 -0
  54. package/payload/platform/plugins/telegram/mcp/dist/__tests__/webhook-verdict.test.js.map +1 -0
  55. package/payload/platform/plugins/telegram/mcp/dist/index.js +19 -163
  56. package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
  57. package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts +9 -0
  58. package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts.map +1 -1
  59. package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js +11 -0
  60. package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js.map +1 -1
  61. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts +30 -3
  62. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts.map +1 -1
  63. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js +29 -4
  64. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js.map +1 -1
  65. package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.d.ts +33 -0
  66. package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.d.ts.map +1 -0
  67. package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.js +145 -0
  68. package/payload/platform/plugins/telegram/mcp/dist/tools/webhook-register.js.map +1 -0
  69. package/payload/platform/plugins/telegram/skills/configure/SKILL.md +4 -2
  70. package/payload/platform/services/telegram-channel/dist/notification.d.ts +35 -3
  71. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  72. package/payload/platform/services/telegram-channel/dist/notification.js +38 -5
  73. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  74. package/payload/platform/services/telegram-channel/dist/server.js +1 -1
  75. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
  76. package/payload/platform/services/webchat-channel/dist/notification.d.ts +2 -1
  77. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  78. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  79. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +2 -2
  80. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
  81. package/payload/server/public/activity.html +4 -4
  82. package/payload/server/public/agents.html +3 -3
  83. package/payload/server/public/assets/{AdminLoginScreens-BLGZEaKU.js → AdminLoginScreens-CAZ3DSwA.js} +1 -1
  84. package/payload/server/public/assets/AdminLoginScreens-CAZ3DSwA.js.br +0 -0
  85. package/payload/server/public/assets/AdminLoginScreens-CAZ3DSwA.js.gz +0 -0
  86. package/payload/server/public/assets/{AdminShell-C_M7YE5Z.js → AdminShell-BLs5v0qF.js} +1 -1
  87. package/payload/server/public/assets/AdminShell-BLs5v0qF.js.br +0 -0
  88. package/payload/server/public/assets/AdminShell-BLs5v0qF.js.gz +0 -0
  89. package/payload/server/public/assets/{activity-BL6qbB9a.js → activity-BwPck2yT.js} +1 -1
  90. package/payload/server/public/assets/activity-BwPck2yT.js.br +0 -0
  91. package/payload/server/public/assets/activity-BwPck2yT.js.gz +0 -0
  92. package/payload/server/public/assets/{admin-jnJ3P2u4.js → admin-DjtOoabA.js} +1 -1
  93. package/payload/server/public/assets/admin-DjtOoabA.js.br +0 -0
  94. package/payload/server/public/assets/admin-DjtOoabA.js.gz +0 -0
  95. package/payload/server/public/assets/{agents-BzhfKZDN.js → agents-IQYaRvoh.js} +1 -1
  96. package/payload/server/public/assets/agents-IQYaRvoh.js.br +0 -0
  97. package/payload/server/public/assets/agents-IQYaRvoh.js.gz +0 -0
  98. package/payload/server/public/assets/{browser-C-6io2B9.js → browser-CPG8w8rQ.js} +1 -1
  99. package/payload/server/public/assets/browser-CPG8w8rQ.js.br +0 -0
  100. package/payload/server/public/assets/browser-CPG8w8rQ.js.gz +0 -0
  101. package/payload/server/public/assets/{calendar-Dt3gS88s.js → calendar-CRwrgNdq.js} +1 -1
  102. package/payload/server/public/assets/calendar-CRwrgNdq.js.br +0 -0
  103. package/payload/server/public/assets/calendar-CRwrgNdq.js.gz +0 -0
  104. package/payload/server/public/assets/chat-B7yZ4KLl.js +1 -0
  105. package/payload/server/public/assets/chat-B7yZ4KLl.js.br +3 -0
  106. package/payload/server/public/assets/chat-B7yZ4KLl.js.gz +0 -0
  107. package/payload/server/public/assets/chevron-left-BzKUQqLy.js +1 -0
  108. package/payload/server/public/assets/chevron-left-BzKUQqLy.js.br +0 -0
  109. package/payload/server/public/assets/clock-BafWFNGo.js +1 -0
  110. package/payload/server/public/assets/clock-BafWFNGo.js.br +0 -0
  111. package/payload/server/public/assets/{copy-TkPJgllS.js → copy-JyiMTV0k.js} +1 -1
  112. package/payload/server/public/assets/copy-JyiMTV0k.js.br +0 -0
  113. package/payload/server/public/assets/copy-JyiMTV0k.js.gz +0 -0
  114. package/payload/server/public/assets/data-DHtGCVrl.js +1 -0
  115. package/payload/server/public/assets/data-DHtGCVrl.js.br +0 -0
  116. package/payload/server/public/assets/data-DHtGCVrl.js.gz +0 -0
  117. package/payload/server/public/assets/{file-text-DyAL2DCh.js → file-text-cnHv9ZS6.js} +1 -1
  118. package/payload/server/public/assets/file-text-cnHv9ZS6.js.br +0 -0
  119. package/payload/server/public/assets/file-text-cnHv9ZS6.js.gz +0 -0
  120. package/payload/server/public/assets/{graph-PiNIcCfX.js → graph-lFwEUDSH.js} +1 -1
  121. package/payload/server/public/assets/graph-lFwEUDSH.js.br +0 -0
  122. package/payload/server/public/assets/graph-lFwEUDSH.js.gz +0 -0
  123. package/payload/server/public/assets/{graph-labels-8jKFrLHn.js → graph-labels-C6qUA0vO.js} +1 -1
  124. package/payload/server/public/assets/graph-labels-C6qUA0vO.js.br +0 -0
  125. package/payload/server/public/assets/graph-labels-C6qUA0vO.js.gz +0 -0
  126. package/payload/server/public/assets/{operator-DKGKGzrb.js → operator-BdoEy5mh.js} +1 -1
  127. package/payload/server/public/assets/operator-BdoEy5mh.js.br +0 -0
  128. package/payload/server/public/assets/operator-BdoEy5mh.js.gz +0 -0
  129. package/payload/server/public/assets/{page-DXz_K10G.js → page-DMrnnXNA.js} +1 -1
  130. package/payload/server/public/assets/page-DMrnnXNA.js.br +0 -0
  131. package/payload/server/public/assets/page-DMrnnXNA.js.gz +0 -0
  132. package/payload/server/public/assets/{page-BU5vlY9Y.js → page-DeMikGYL.js} +1 -1
  133. package/payload/server/public/assets/page-DeMikGYL.js.br +0 -0
  134. package/payload/server/public/assets/page-DeMikGYL.js.gz +0 -0
  135. package/payload/server/public/assets/play-D6OAQVJ_.js +1 -0
  136. package/payload/server/public/assets/play-D6OAQVJ_.js.br +0 -0
  137. package/payload/server/public/assets/play-D6OAQVJ_.js.gz +0 -0
  138. package/payload/server/public/assets/{public-BmA3-jLj.js → public-B-X1Cxfw.js} +1 -1
  139. package/payload/server/public/assets/public-B-X1Cxfw.js.br +0 -0
  140. package/payload/server/public/assets/public-B-X1Cxfw.js.gz +0 -0
  141. package/payload/server/public/assets/{rotate-ccw-fCH0LSMq.js → rotate-ccw-DVFvQAHG.js} +1 -1
  142. package/payload/server/public/assets/rotate-ccw-DVFvQAHG.js.br +0 -0
  143. package/payload/server/public/assets/rotate-ccw-DVFvQAHG.js.gz +0 -0
  144. package/payload/server/public/assets/{routines-BeFrmkls.js → routines-CgkkGJaS.js} +1 -1
  145. package/payload/server/public/assets/routines-CgkkGJaS.js.br +0 -0
  146. package/payload/server/public/assets/routines-CgkkGJaS.js.gz +0 -0
  147. package/payload/server/public/assets/{skills-mjls5HBa.js → skills-CW7gVcaG.js} +1 -1
  148. package/payload/server/public/assets/skills-CW7gVcaG.js.br +0 -0
  149. package/payload/server/public/assets/skills-CW7gVcaG.js.gz +0 -0
  150. package/payload/server/public/assets/{tasks-p6puHHD0.js → tasks-Ddjp0Rwh.js} +1 -1
  151. package/payload/server/public/assets/tasks-Ddjp0Rwh.js.br +0 -0
  152. package/payload/server/public/assets/tasks-Ddjp0Rwh.js.gz +0 -0
  153. package/payload/server/public/assets/{triangle-alert-CrZ1ewbd.js → triangle-alert-DUrUutX4.js} +1 -1
  154. package/payload/server/public/assets/triangle-alert-DUrUutX4.js.br +0 -0
  155. package/payload/server/public/assets/triangle-alert-DUrUutX4.js.gz +0 -0
  156. package/payload/server/public/assets/{useCopyFeedback-zOQNrIwd.js → useCopyFeedback-ClVINDrQ.js} +1 -1
  157. package/payload/server/public/assets/useCopyFeedback-ClVINDrQ.js.br +0 -0
  158. package/payload/server/public/assets/useCopyFeedback-ClVINDrQ.js.gz +0 -0
  159. package/payload/server/public/assets/{useMediaQuery-BmG090FR.js → useMediaQuery-BUFkZYRs.js} +1 -1
  160. package/payload/server/public/assets/useMediaQuery-BUFkZYRs.js.br +0 -0
  161. package/payload/server/public/assets/{useMediaQuery-BmG090FR.js.gz → useMediaQuery-BUFkZYRs.js.gz} +0 -0
  162. package/payload/server/public/assets/{useVoiceRecorder-9pf6FFJ-.js → useVoiceRecorder-DnyqQFet.js} +1 -1
  163. package/payload/server/public/assets/useVoiceRecorder-DnyqQFet.js.br +0 -0
  164. package/payload/server/public/assets/useVoiceRecorder-DnyqQFet.js.gz +0 -0
  165. package/payload/server/public/assets/{wrench-k4GYG5rc.js → wrench-BW28s_d0.js} +1 -1
  166. package/payload/server/public/assets/wrench-BW28s_d0.js.br +0 -0
  167. package/payload/server/public/assets/wrench-BW28s_d0.js.gz +0 -0
  168. package/payload/server/public/browser.html +3 -3
  169. package/payload/server/public/calendar.html +6 -6
  170. package/payload/server/public/chat.html +12 -12
  171. package/payload/server/public/data.html +10 -10
  172. package/payload/server/public/graph.html +8 -8
  173. package/payload/server/public/index.html +12 -12
  174. package/payload/server/public/operator.html +13 -13
  175. package/payload/server/public/public.html +12 -12
  176. package/payload/server/public/routines.html +6 -6
  177. package/payload/server/public/skills.html +4 -4
  178. package/payload/server/public/tasks.html +5 -5
  179. package/payload/server/server.js +1014 -375
  180. package/payload/platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.d.ts +0 -6
  181. package/payload/platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.d.ts.map +0 -1
  182. package/payload/platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.js +0 -102
  183. package/payload/platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.js.map +0 -1
  184. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-routine-description.test.d.ts +0 -2
  185. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-routine-description.test.d.ts.map +0 -1
  186. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-routine-description.test.js +0 -87
  187. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-routine-description.test.js.map +0 -1
  188. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-startdate-utc.test.d.ts +0 -2
  189. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-startdate-utc.test.d.ts.map +0 -1
  190. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-startdate-utc.test.js +0 -79
  191. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-startdate-utc.test.js.map +0 -1
  192. package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.d.ts +0 -35
  193. package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.d.ts.map +0 -1
  194. package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.js +0 -56
  195. package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.js.map +0 -1
  196. package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.d.ts +0 -24
  197. package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.d.ts.map +0 -1
  198. package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.js +0 -87
  199. package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.js.map +0 -1
  200. package/payload/server/public/assets/AdminLoginScreens-BLGZEaKU.js.br +0 -0
  201. package/payload/server/public/assets/AdminLoginScreens-BLGZEaKU.js.gz +0 -0
  202. package/payload/server/public/assets/AdminShell-C_M7YE5Z.js.br +0 -0
  203. package/payload/server/public/assets/AdminShell-C_M7YE5Z.js.gz +0 -0
  204. package/payload/server/public/assets/activity-BL6qbB9a.js.br +0 -0
  205. package/payload/server/public/assets/activity-BL6qbB9a.js.gz +0 -0
  206. package/payload/server/public/assets/admin-jnJ3P2u4.js.br +0 -0
  207. package/payload/server/public/assets/admin-jnJ3P2u4.js.gz +0 -0
  208. package/payload/server/public/assets/agents-BzhfKZDN.js.br +0 -0
  209. package/payload/server/public/assets/agents-BzhfKZDN.js.gz +0 -0
  210. package/payload/server/public/assets/browser-C-6io2B9.js.br +0 -0
  211. package/payload/server/public/assets/browser-C-6io2B9.js.gz +0 -0
  212. package/payload/server/public/assets/calendar-Dt3gS88s.js.br +0 -0
  213. package/payload/server/public/assets/calendar-Dt3gS88s.js.gz +0 -0
  214. package/payload/server/public/assets/chat-IUOvVkL-.js +0 -1
  215. package/payload/server/public/assets/chat-IUOvVkL-.js.br +0 -1
  216. package/payload/server/public/assets/chat-IUOvVkL-.js.gz +0 -0
  217. package/payload/server/public/assets/chevron-left-DSzR9zR7.js +0 -1
  218. package/payload/server/public/assets/chevron-left-DSzR9zR7.js.br +0 -0
  219. package/payload/server/public/assets/clock-C5xE5p2I.js +0 -1
  220. package/payload/server/public/assets/clock-C5xE5p2I.js.br +0 -0
  221. package/payload/server/public/assets/copy-TkPJgllS.js.br +0 -0
  222. package/payload/server/public/assets/copy-TkPJgllS.js.gz +0 -0
  223. package/payload/server/public/assets/data-Bhv0Pvfl.js +0 -1
  224. package/payload/server/public/assets/data-Bhv0Pvfl.js.br +0 -0
  225. package/payload/server/public/assets/data-Bhv0Pvfl.js.gz +0 -0
  226. package/payload/server/public/assets/file-text-DyAL2DCh.js.br +0 -0
  227. package/payload/server/public/assets/file-text-DyAL2DCh.js.gz +0 -0
  228. package/payload/server/public/assets/graph-PiNIcCfX.js.br +0 -0
  229. package/payload/server/public/assets/graph-PiNIcCfX.js.gz +0 -0
  230. package/payload/server/public/assets/graph-labels-8jKFrLHn.js.br +0 -0
  231. package/payload/server/public/assets/graph-labels-8jKFrLHn.js.gz +0 -0
  232. package/payload/server/public/assets/operator-DKGKGzrb.js.br +0 -0
  233. package/payload/server/public/assets/operator-DKGKGzrb.js.gz +0 -0
  234. package/payload/server/public/assets/page-BU5vlY9Y.js.br +0 -0
  235. package/payload/server/public/assets/page-BU5vlY9Y.js.gz +0 -0
  236. package/payload/server/public/assets/page-DXz_K10G.js.br +0 -0
  237. package/payload/server/public/assets/page-DXz_K10G.js.gz +0 -0
  238. package/payload/server/public/assets/play-CmSyYrB6.js +0 -1
  239. package/payload/server/public/assets/play-CmSyYrB6.js.br +0 -0
  240. package/payload/server/public/assets/play-CmSyYrB6.js.gz +0 -0
  241. package/payload/server/public/assets/public-BmA3-jLj.js.br +0 -0
  242. package/payload/server/public/assets/public-BmA3-jLj.js.gz +0 -0
  243. package/payload/server/public/assets/rotate-ccw-fCH0LSMq.js.br +0 -0
  244. package/payload/server/public/assets/rotate-ccw-fCH0LSMq.js.gz +0 -0
  245. package/payload/server/public/assets/routines-BeFrmkls.js.br +0 -0
  246. package/payload/server/public/assets/routines-BeFrmkls.js.gz +0 -0
  247. package/payload/server/public/assets/skills-mjls5HBa.js.br +0 -0
  248. package/payload/server/public/assets/skills-mjls5HBa.js.gz +0 -0
  249. package/payload/server/public/assets/tasks-p6puHHD0.js.br +0 -0
  250. package/payload/server/public/assets/tasks-p6puHHD0.js.gz +0 -0
  251. package/payload/server/public/assets/triangle-alert-CrZ1ewbd.js.br +0 -0
  252. package/payload/server/public/assets/triangle-alert-CrZ1ewbd.js.gz +0 -0
  253. package/payload/server/public/assets/useCopyFeedback-zOQNrIwd.js.br +0 -0
  254. package/payload/server/public/assets/useCopyFeedback-zOQNrIwd.js.gz +0 -0
  255. package/payload/server/public/assets/useMediaQuery-BmG090FR.js.br +0 -0
  256. package/payload/server/public/assets/useVoiceRecorder-9pf6FFJ-.js.br +0 -0
  257. package/payload/server/public/assets/useVoiceRecorder-9pf6FFJ-.js.gz +0 -0
  258. package/payload/server/public/assets/wrench-k4GYG5rc.js.br +0 -0
  259. package/payload/server/public/assets/wrench-k4GYG5rc.js.gz +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.576",
3
+ "version": "0.1.578",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAIpE;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,CACH,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACvB,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/D;AAMD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,aAAa,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAID,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,CAAC;CACzB;AAOD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAgED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,EAAE,CAAC,CAE1B;AAKD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAI/B;AAID,wBAAsB,aAAa,CACjC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC,CAEvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAgCD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAUpE;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,CACH,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACvB,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/D;AAMD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,aAAa,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAKD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,CAAC;CACzB;AAOD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAgED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,EAAE,CAAC,CAE1B;AAKD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAI/B;AAID,wBAAsB,aAAa,CACjC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC,CAEvB"}
@@ -20,11 +20,42 @@ exports.ACTIVE_CANDIDATE_FLOOR = 0.4;
20
20
  // Render every rule verbatim. Injection has no cap (Task 1951): concision is
21
21
  // enforced at write time by the conversational-memory skill so each stored
22
22
  // value is already short, not by truncating the block here.
23
+ //
24
+ // Task 2562 — the header names the owner and the block forbids reading a
25
+ // speaker out of it. Selection is owner-only (resolveOwnerUserId then
26
+ // queryOwnerCandidates below), so every rule here belongs to one identity the
27
+ // header never used to name, and a rule written in the third person about the
28
+ // owner read as a statement about whoever was typing: on sitedesk-code nine
29
+ // owner rules reached a second admin, who was then greeted by the owner's name.
30
+ //
31
+ // The block deliberately does NOT claim a sender is named elsewhere. The same
32
+ // block feeds all three doors and only WhatsApp carries an inbound provenance
33
+ // preamble today (whatsapp-channel/src/notification.ts composeInboundProvenance);
34
+ // webchat has none and Telegram has none until Task 2561 lands. Pointing the
35
+ // agent at a block that is absent is worse than saying nothing about it.
36
+ //
37
+ // Copy constraints, all load-bearing. No apostrophe anywhere in the block:
38
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh holds its whole python
39
+ // program inside a single-quoted `python3 -c '...'` and its rules fixtures are
40
+ // single-quoted bash strings. No blank line: both strip regexes are non-greedy
41
+ // up to the blank line before `## Context`, so the body has to be one physical
42
+ // line. Operator-clean otherwise (no task numbers, repo paths or em-dashes) so
43
+ // the outbound redactor never trips on it.
44
+ //
45
+ // The header is an exact-match anchor in three places that must move with it:
46
+ // COMPOSED_ADMIN_FRAME (ui/app/lib/reader/parse-transcript.ts), the strip in
47
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh, and that hook's test
48
+ // fixtures. Nothing logs when either strip stops matching, so the verbatim
49
+ // assertions in index.test.ts are the whole reconciliation.
23
50
  function formatStandingRulesBlock(rules) {
24
51
  if (rules.length === 0)
25
52
  return "";
26
53
  const lines = rules.map((r) => `- ${r.value}`);
27
- return `## Standing rules\n${lines.join("\n")}`;
54
+ return (`## Standing rules (account owner)\n` +
55
+ `These standing rules belong to the account owner. ` +
56
+ `Some of them may name the account owner in the third person. ` +
57
+ `None of them says who sent this turn, so do not infer from them who is speaking.\n` +
58
+ lines.join("\n"));
28
59
  }
29
60
  // The account's owner identity (Task 1461). One `AdminUser {role:'owner'}` is
30
61
  // seeded per account (Task 1359); `null` when absent (a pre-seeding legacy
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -141,11 +141,60 @@ describe("resolveActiveRules — owner-scoped, uncapped, verbatim", () => {
141
141
  });
142
142
 
143
143
  describe("formatStandingRulesBlock", () => {
144
+ // Task 2562 — the header and the do-not-infer line are asserted verbatim
145
+ // because the same header string is an exact-match anchor in
146
+ // parse-transcript.ts (COMPOSED_ADMIN_FRAME) and in
147
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh. Neither logs anything
148
+ // when it stops matching, so this assertion is the only thing standing
149
+ // between a copy edit and a silently broken strip.
150
+ const HEADER = "## Standing rules (account owner)";
151
+ const BODY =
152
+ "These standing rules belong to the account owner. Some of them may name the account owner in the third person. None of them says who sent this turn, so do not infer from them who is speaking.";
153
+
154
+ const oneRule = () => [
155
+ {
156
+ preferenceId: "p1",
157
+ category: "communication",
158
+ key: "k1",
159
+ value: "use British spelling",
160
+ confidence: 0.9,
161
+ observedAt: "2026-07-01T00:00:00Z",
162
+ },
163
+ ];
164
+
144
165
  it("returns empty string for no rules", () => {
145
166
  expect(formatStandingRulesBlock([])).toBe("");
146
167
  });
147
168
 
148
- it("renders every rule, with no cap", () => {
169
+ it("opens with the attributed header and the do-not-infer line", () => {
170
+ expect(formatStandingRulesBlock(oneRule())).toBe(
171
+ `${HEADER}\n${BODY}\n- use British spelling`,
172
+ );
173
+ });
174
+
175
+ it("carries no apostrophe, so the hook and its shell fixtures keep quoting", () => {
176
+ // The rule VALUES are operator data and may contain anything; only the two
177
+ // lines of platform copy are constrained.
178
+ const platformCopy = formatStandingRulesBlock(oneRule())
179
+ .split("\n")
180
+ .slice(0, 2)
181
+ .join("\n");
182
+ expect(platformCopy).not.toContain("'");
183
+ });
184
+
185
+ it("contains no blank line, so both strip regexes still terminate at ## Context", () => {
186
+ const rules = Array.from({ length: 3 }, (_, i) => ({
187
+ preferenceId: `p${i}`,
188
+ category: "communication",
189
+ key: `k${i}`,
190
+ value: `rule ${i}`,
191
+ confidence: 1 - i * 0.01,
192
+ observedAt: "2026-07-01T00:00:00Z",
193
+ }));
194
+ expect(formatStandingRulesBlock(rules)).not.toContain("\n\n");
195
+ });
196
+
197
+ it("renders every rule, with no cap, in resolution order", () => {
149
198
  const rules = Array.from({ length: 20 }, (_, i) => ({
150
199
  preferenceId: `p${i}`,
151
200
  category: "communication",
@@ -155,7 +204,7 @@ describe("formatStandingRulesBlock", () => {
155
204
  observedAt: "2026-07-01T00:00:00Z",
156
205
  }));
157
206
  const block = formatStandingRulesBlock(rules);
158
- expect(block).toContain("## Standing rules");
159
- expect((block.match(/^- /gm) ?? []).length).toBe(20);
207
+ expect(block.startsWith(`${HEADER}\n${BODY}\n`)).toBe(true);
208
+ expect(block.split("\n").slice(2)).toEqual(rules.map((r) => `- ${r.value}`));
160
209
  });
161
210
  });
@@ -33,10 +33,43 @@ export interface ActiveRule {
33
33
  // Render every rule verbatim. Injection has no cap (Task 1951): concision is
34
34
  // enforced at write time by the conversational-memory skill so each stored
35
35
  // value is already short, not by truncating the block here.
36
+ //
37
+ // Task 2562 — the header names the owner and the block forbids reading a
38
+ // speaker out of it. Selection is owner-only (resolveOwnerUserId then
39
+ // queryOwnerCandidates below), so every rule here belongs to one identity the
40
+ // header never used to name, and a rule written in the third person about the
41
+ // owner read as a statement about whoever was typing: on sitedesk-code nine
42
+ // owner rules reached a second admin, who was then greeted by the owner's name.
43
+ //
44
+ // The block deliberately does NOT claim a sender is named elsewhere. The same
45
+ // block feeds all three doors and only WhatsApp carries an inbound provenance
46
+ // preamble today (whatsapp-channel/src/notification.ts composeInboundProvenance);
47
+ // webchat has none and Telegram has none until Task 2561 lands. Pointing the
48
+ // agent at a block that is absent is worse than saying nothing about it.
49
+ //
50
+ // Copy constraints, all load-bearing. No apostrophe anywhere in the block:
51
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh holds its whole python
52
+ // program inside a single-quoted `python3 -c '...'` and its rules fixtures are
53
+ // single-quoted bash strings. No blank line: both strip regexes are non-greedy
54
+ // up to the blank line before `## Context`, so the body has to be one physical
55
+ // line. Operator-clean otherwise (no task numbers, repo paths or em-dashes) so
56
+ // the outbound redactor never trips on it.
57
+ //
58
+ // The header is an exact-match anchor in three places that must move with it:
59
+ // COMPOSED_ADMIN_FRAME (ui/app/lib/reader/parse-transcript.ts), the strip in
60
+ // plugins/admin/hooks/prompt-optimiser-compliance.sh, and that hook's test
61
+ // fixtures. Nothing logs when either strip stops matching, so the verbatim
62
+ // assertions in index.test.ts are the whole reconciliation.
36
63
  export function formatStandingRulesBlock(rules: ActiveRule[]): string {
37
64
  if (rules.length === 0) return "";
38
65
  const lines = rules.map((r) => `- ${r.value}`);
39
- return `## Standing rules\n${lines.join("\n")}`;
66
+ return (
67
+ `## Standing rules (account owner)\n` +
68
+ `These standing rules belong to the account owner. ` +
69
+ `Some of them may name the account owner in the third person. ` +
70
+ `None of them says who sent this turn, so do not infer from them who is speaking.\n` +
71
+ lines.join("\n")
72
+ );
40
73
  }
41
74
 
42
75
  interface MinimalSession {
@@ -60,7 +93,8 @@ export interface ActiveRuleResolution {
60
93
  }
61
94
 
62
95
  // The per-turn injection payload the UI hands each channel gateway: the
63
- // formatted `## Standing rules` block plus the provenance the gateway logs.
96
+ // formatted `## Standing rules (account owner)` block plus the provenance the
97
+ // gateway logs.
64
98
  export interface StandingRulesInjection {
65
99
  block: string;
66
100
  ownerUserId: string | null;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Who can be sent to this job, and why the others cannot.
3
+ *
4
+ * The rule that matters: **a qualified worker who is already committed in the
5
+ * window is offered WITH the conflict shown, never dropped.** The office
6
+ * routinely double-books on purpose — a job runs short, a customer is on the
7
+ * way home, an engineer volunteers. A tool that silently removes the busy
8
+ * option makes that decision for them and gives no way to see it was made.
9
+ *
10
+ * Only a missing qualification excludes, and every exclusion is named with its
11
+ * worker and its reason, because `offered=0` with no explanation is the shape a
12
+ * quiet day and an unstaffable job type share.
13
+ *
14
+ * It lives in this shared lib rather than in the dispatch plugin because the
15
+ * portal visits push runs in `platform/ui` and cannot import a plugin's build
16
+ * output, and a second implementation of qualification and clash is the defect
17
+ * `functions/api/visits.ts:33-36` names: a gate written once per client is a
18
+ * gate got wrong once per client. Task 2510 moved `buildVisitKeyboard` here for
19
+ * the same reason. The dispatch plugin re-exports both from
20
+ * `mcp/src/lib/allocate.ts`, which is the import site its tools and tests
21
+ * already name.
22
+ */
23
+ import type { DispatchConfig } from "./types.js";
24
+ import type { Commitment } from "./read.js";
25
+ export type { Commitment };
26
+ export interface Candidate {
27
+ readonly workerId: string;
28
+ readonly name: string;
29
+ /** Commitments overlapping the requested window. Empty means free. */
30
+ readonly conflicts: readonly Commitment[];
31
+ }
32
+ export interface Exclusion {
33
+ readonly workerId: string;
34
+ readonly reason: "missing-qualification";
35
+ }
36
+ export interface SelectCandidatesParams {
37
+ readonly jobType: string;
38
+ readonly cfg: DispatchConfig;
39
+ /** Committed visits per workerId, already narrowed to the window. */
40
+ readonly commitments: ReadonlyMap<string, readonly Commitment[]>;
41
+ }
42
+ export interface CandidateSelection {
43
+ readonly offered: readonly Candidate[];
44
+ readonly excluded: readonly Exclusion[];
45
+ /** The qualification the job type demands, echoed for the log line. */
46
+ readonly required: string;
47
+ }
48
+ export declare function selectCandidates(params: SelectCandidatesParams): CandidateSelection;
49
+ /** `[a, b)` overlap. Touching endpoints do not overlap. */
50
+ export declare function overlaps(a: {
51
+ start: string;
52
+ end: string;
53
+ }, b: {
54
+ start: string;
55
+ end: string;
56
+ }): boolean;
57
+ //# sourceMappingURL=allocate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allocate.d.ts","sourceRoot":"","sources":["../src/allocate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;CAC1C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,kBAAkB,CAuBnF;AAED,2DAA2D;AAC3D,wBAAgB,QAAQ,CACtB,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACjC,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAChC,OAAO,CAET"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectCandidates = selectCandidates;
4
+ exports.overlaps = overlaps;
5
+ function selectCandidates(params) {
6
+ const { jobType, cfg, commitments } = params;
7
+ const type = cfg.jobTypes.find((t) => t.name === jobType);
8
+ if (!type) {
9
+ // An unknown job type is the caller's error, not a staffing outcome. Every
10
+ // worker is excluded for a reason that would be a lie, so none are: the
11
+ // caller checks the type before asking.
12
+ return { offered: [], excluded: [], required: "" };
13
+ }
14
+ const offered = [];
15
+ const excluded = [];
16
+ for (const worker of cfg.workers) {
17
+ if (!worker.qualifications.includes(type.requiredQualification)) {
18
+ excluded.push({ workerId: worker.workerId, reason: "missing-qualification" });
19
+ continue;
20
+ }
21
+ offered.push({
22
+ workerId: worker.workerId,
23
+ name: worker.name,
24
+ conflicts: commitments.get(worker.workerId) ?? [],
25
+ });
26
+ }
27
+ return { offered, excluded, required: type.requiredQualification };
28
+ }
29
+ /** `[a, b)` overlap. Touching endpoints do not overlap. */
30
+ function overlaps(a, b) {
31
+ return a.start < b.end && b.start < a.end;
32
+ }
33
+ //# sourceMappingURL=allocate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allocate.js","sourceRoot":"","sources":["../src/allocate.ts"],"names":[],"mappings":";;AAyDA,4CAuBC;AAGD,4BAKC;AA/BD,SAAgB,gBAAgB,CAAC,MAA8B;IAC7D,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,2EAA2E;QAC3E,wEAAwE;QACxE,wCAAwC;QACxC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACrD,CAAC;IACD,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAgB,EAAE,CAAC;IACjC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC9E,SAAS;QACX,CAAC;QACD,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;SAClD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,qBAAqB,EAAE,CAAC;AACrE,CAAC;AAED,2DAA2D;AAC3D,SAAgB,QAAQ,CACtB,CAAiC,EACjC,CAAiC;IAEjC,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC;AAC5C,CAAC"}
@@ -14,6 +14,8 @@ export { RECORDABLE_FIELDS, loadDispatchConfig, terminalJobStatuses, terminalVis
14
14
  export type { RecordableField } from "./config.js";
15
15
  export { CALLBACK_DATA_MAX_BYTES, buildVisitKeyboard, dispatchCallbackData, parseDispatchCallback, } from "./keyboard.js";
16
16
  export type { DispatchButton, VisitKeyboard } from "./keyboard.js";
17
+ export { overlaps, selectCandidates } from "./allocate.js";
18
+ export type { Candidate, CandidateSelection, Exclusion, SelectCandidatesParams, } from "./allocate.js";
17
19
  export { withSession } from "./session.js";
18
20
  export { resolveAccountZone } from "./zone.js";
19
21
  export { MAX_RANGE_DAYS, addDaysYmd, currentWeekYmd, daysBetween, minutesIntoDay, ymdInZone, zonedDayStartUtc, zonedRangeBounds, } from "./window.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,UAAU,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,SAAS,EACT,UAAU,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,UAAU,EACV,uBAAuB,EACvB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAInB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3F,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACvF,mBAAmB,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAQnE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3D,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,UAAU,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,SAAS,EACT,UAAU,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,UAAU,EACV,uBAAuB,EACvB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAInB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3F,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACvF,mBAAmB,YAAY,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readWorkerDay = exports.readJobList = exports.readDay = exports.readCounts = exports.nonZuluStarts = exports.geofenceVerdict = exports.distanceM = exports.readVisitsInWindow = exports.readVisitsForJob = exports.readVisitRecordState = exports.readVisit = exports.readSiteLocation = exports.readSiteHistory = exports.readPreviousVisits = exports.readOpenVisitsForWorker = exports.readNodeId = exports.readJobs = exports.readJob = exports.readFieldVisit = exports.readDrafts = exports.readDraft = exports.readCommitments = exports.projectVisit = exports.projectJob = exports.projectFieldVisit = exports.buildJobListQuery = exports.zonedRangeBounds = exports.zonedDayStartUtc = exports.ymdInZone = exports.minutesIntoDay = exports.daysBetween = exports.currentWeekYmd = exports.addDaysYmd = exports.MAX_RANGE_DAYS = exports.resolveAccountZone = exports.withSession = exports.parseDispatchCallback = exports.dispatchCallbackData = exports.buildVisitKeyboard = exports.CALLBACK_DATA_MAX_BYTES = exports.workerForTelegramUser = exports.terminalVisitStatuses = exports.terminalJobStatuses = exports.loadDispatchConfig = exports.RECORDABLE_FIELDS = void 0;
3
+ exports.readWorkerDay = exports.readJobList = exports.readDay = exports.readCounts = exports.nonZuluStarts = exports.geofenceVerdict = exports.distanceM = exports.readVisitsInWindow = exports.readVisitsForJob = exports.readVisitRecordState = exports.readVisit = exports.readSiteLocation = exports.readSiteHistory = exports.readPreviousVisits = exports.readOpenVisitsForWorker = exports.readNodeId = exports.readJobs = exports.readJob = exports.readFieldVisit = exports.readDrafts = exports.readDraft = exports.readCommitments = exports.projectVisit = exports.projectJob = exports.projectFieldVisit = exports.buildJobListQuery = exports.zonedRangeBounds = exports.zonedDayStartUtc = exports.ymdInZone = exports.minutesIntoDay = exports.daysBetween = exports.currentWeekYmd = exports.addDaysYmd = exports.MAX_RANGE_DAYS = exports.resolveAccountZone = exports.withSession = exports.selectCandidates = exports.overlaps = exports.parseDispatchCallback = exports.dispatchCallbackData = exports.buildVisitKeyboard = exports.CALLBACK_DATA_MAX_BYTES = exports.workerForTelegramUser = exports.terminalVisitStatuses = exports.terminalJobStatuses = exports.loadDispatchConfig = exports.RECORDABLE_FIELDS = void 0;
4
4
  /**
5
5
  * Dispatch reads, shared by the MCP tools and the office planner.
6
6
  *
@@ -27,6 +27,15 @@ Object.defineProperty(exports, "CALLBACK_DATA_MAX_BYTES", { enumerable: true, ge
27
27
  Object.defineProperty(exports, "buildVisitKeyboard", { enumerable: true, get: function () { return keyboard_js_1.buildVisitKeyboard; } });
28
28
  Object.defineProperty(exports, "dispatchCallbackData", { enumerable: true, get: function () { return keyboard_js_1.dispatchCallbackData; } });
29
29
  Object.defineProperty(exports, "parseDispatchCallback", { enumerable: true, get: function () { return keyboard_js_1.parseDispatchCallback; } });
30
+ // Task 2511 — qualification and clash, shared by the dispatch tools and the
31
+ // portal visits push. One implementation, for the reason allocate.ts states:
32
+ // the push runs in platform/ui and cannot import a plugin's build output, and a
33
+ // second copy of these rules is a gate got wrong once per client.
34
+ // `Commitment` is NOT re-exported here: it is `readCommitments`'s own shape,
35
+ // declared once in read.ts and already exported below.
36
+ var allocate_js_1 = require("./allocate.js");
37
+ Object.defineProperty(exports, "overlaps", { enumerable: true, get: function () { return allocate_js_1.overlaps; } });
38
+ Object.defineProperty(exports, "selectCandidates", { enumerable: true, get: function () { return allocate_js_1.selectCandidates; } });
30
39
  var session_js_1 = require("./session.js");
31
40
  Object.defineProperty(exports, "withSession", { enumerable: true, get: function () { return session_js_1.withSession; } });
32
41
  var zone_js_1 = require("./zone.js");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,yCAMqB;AALnB,8GAAA,iBAAiB,OAAA;AACjB,+GAAA,kBAAkB,OAAA;AAClB,gHAAA,mBAAmB,OAAA;AACnB,kHAAA,qBAAqB,OAAA;AACrB,kHAAA,qBAAqB,OAAA;AAIvB,6EAA6E;AAC7E,6EAA6E;AAC7E,oEAAoE;AACpE,6CAKuB;AAJrB,sHAAA,uBAAuB,OAAA;AACvB,iHAAA,kBAAkB,OAAA;AAClB,mHAAA,oBAAoB,OAAA;AACpB,oHAAA,qBAAqB,OAAA;AAIvB,2CAA2C;AAAlC,yGAAA,WAAW,OAAA;AACpB,qCAA+C;AAAtC,6GAAA,kBAAkB,OAAA;AAC3B,yCASqB;AARnB,2GAAA,cAAc,OAAA;AACd,uGAAA,UAAU,OAAA;AACV,2GAAA,cAAc,OAAA;AACd,wGAAA,WAAW,OAAA;AACX,2GAAA,cAAc,OAAA;AACd,sGAAA,SAAS,OAAA;AACT,6GAAA,gBAAgB,OAAA;AAChB,6GAAA,gBAAgB,OAAA;AAGlB,qEAAqE;AACrE,qCAoBmB;AAnBjB,4GAAA,iBAAiB,OAAA;AACjB,4GAAA,iBAAiB,OAAA;AACjB,qGAAA,UAAU,OAAA;AACV,uGAAA,YAAY,OAAA;AACZ,0GAAA,eAAe,OAAA;AACf,oGAAA,SAAS,OAAA;AACT,qGAAA,UAAU,OAAA;AACV,yGAAA,cAAc,OAAA;AACd,kGAAA,OAAO,OAAA;AACP,mGAAA,QAAQ,OAAA;AACR,qGAAA,UAAU,OAAA;AACV,kHAAA,uBAAuB,OAAA;AACvB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,2GAAA,gBAAgB,OAAA;AAChB,oGAAA,SAAS,OAAA;AACT,+GAAA,oBAAoB,OAAA;AACpB,2GAAA,gBAAgB,OAAA;AAChB,6GAAA,kBAAkB,OAAA;AAGpB,uEAAuE;AACvE,kDAAkD;AAClD,mCAAsD;AAA7C,mGAAA,SAAS,OAAA;AAAE,yGAAA,eAAe,OAAA;AAGnC,yEAAyE;AACzE,qCAA2F;AAAlF,wGAAA,aAAa,OAAA;AAAE,qGAAA,UAAU,OAAA;AAAE,kGAAA,OAAO,OAAA;AAAE,sGAAA,WAAW,OAAA;AAAE,wGAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,yCAMqB;AALnB,8GAAA,iBAAiB,OAAA;AACjB,+GAAA,kBAAkB,OAAA;AAClB,gHAAA,mBAAmB,OAAA;AACnB,kHAAA,qBAAqB,OAAA;AACrB,kHAAA,qBAAqB,OAAA;AAIvB,6EAA6E;AAC7E,6EAA6E;AAC7E,oEAAoE;AACpE,6CAKuB;AAJrB,sHAAA,uBAAuB,OAAA;AACvB,iHAAA,kBAAkB,OAAA;AAClB,mHAAA,oBAAoB,OAAA;AACpB,oHAAA,qBAAqB,OAAA;AAIvB,4EAA4E;AAC5E,6EAA6E;AAC7E,gFAAgF;AAChF,kEAAkE;AAClE,6EAA6E;AAC7E,uDAAuD;AACvD,6CAA2D;AAAlD,uGAAA,QAAQ,OAAA;AAAE,+GAAA,gBAAgB,OAAA;AAQnC,2CAA2C;AAAlC,yGAAA,WAAW,OAAA;AACpB,qCAA+C;AAAtC,6GAAA,kBAAkB,OAAA;AAC3B,yCASqB;AARnB,2GAAA,cAAc,OAAA;AACd,uGAAA,UAAU,OAAA;AACV,2GAAA,cAAc,OAAA;AACd,wGAAA,WAAW,OAAA;AACX,2GAAA,cAAc,OAAA;AACd,sGAAA,SAAS,OAAA;AACT,6GAAA,gBAAgB,OAAA;AAChB,6GAAA,gBAAgB,OAAA;AAGlB,qEAAqE;AACrE,qCAoBmB;AAnBjB,4GAAA,iBAAiB,OAAA;AACjB,4GAAA,iBAAiB,OAAA;AACjB,qGAAA,UAAU,OAAA;AACV,uGAAA,YAAY,OAAA;AACZ,0GAAA,eAAe,OAAA;AACf,oGAAA,SAAS,OAAA;AACT,qGAAA,UAAU,OAAA;AACV,yGAAA,cAAc,OAAA;AACd,kGAAA,OAAO,OAAA;AACP,mGAAA,QAAQ,OAAA;AACR,qGAAA,UAAU,OAAA;AACV,kHAAA,uBAAuB,OAAA;AACvB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,2GAAA,gBAAgB,OAAA;AAChB,oGAAA,SAAS,OAAA;AACT,+GAAA,oBAAoB,OAAA;AACpB,2GAAA,gBAAgB,OAAA;AAChB,6GAAA,kBAAkB,OAAA;AAGpB,uEAAuE;AACvE,kDAAkD;AAClD,mCAAsD;AAA7C,mGAAA,SAAS,OAAA;AAAE,yGAAA,eAAe,OAAA;AAGnC,yEAAyE;AACzE,qCAA2F;AAAlF,wGAAA,aAAa,OAAA;AAAE,qGAAA,UAAU,OAAA;AAAE,kGAAA,OAAO,OAAA;AAAE,sGAAA,WAAW,OAAA;AAAE,wGAAA,aAAa,OAAA"}
@@ -0,0 +1,41 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { selectCandidates, overlaps } from "../allocate.js";
3
+ import type { DispatchConfig } from "../types.js";
4
+
5
+ const cfg = {
6
+ statuses: { job: [], visit: [], jobTransitions: [], visitTransitions: [] },
7
+ jobTypes: [{ name: "Boiler", requiredQualification: "gas", defaultDurationMin: 60 }],
8
+ workers: [
9
+ { workerId: "w1", name: "Ann", qualifications: ["gas"], workingHours: {} },
10
+ { workerId: "w2", name: "Ben", qualifications: [], workingHours: {} },
11
+ ],
12
+ records: { checklists: {}, requiredBeforeTerminal: {} },
13
+ vocabulary: { job: "job", visit: "visit", worker: "worker" },
14
+ vehicles: { enabled: false },
15
+ } as unknown as DispatchConfig;
16
+
17
+ describe("selectCandidates, from the shared lib", () => {
18
+ it("offers a qualified worker and names why the other is excluded", () => {
19
+ const r = selectCandidates({ jobType: "Boiler", cfg, commitments: new Map() });
20
+ expect(r.offered.map((c) => c.workerId)).toEqual(["w1"]);
21
+ expect(r.excluded).toEqual([{ workerId: "w2", reason: "missing-qualification" }]);
22
+ expect(r.required).toBe("gas");
23
+ });
24
+
25
+ it("offers a busy qualified worker WITH the conflict shown, never dropped", () => {
26
+ const busy = new Map([["w1", [{ visitId: "v9", start: "a", end: "b" }]]]);
27
+ const r = selectCandidates({ jobType: "Boiler", cfg, commitments: busy });
28
+ expect(r.offered).toHaveLength(1);
29
+ expect(r.offered[0].conflicts).toHaveLength(1);
30
+ });
31
+
32
+ it("excludes nobody for an unknown job type, because every reason would be a lie", () => {
33
+ const r = selectCandidates({ jobType: "Nope", cfg, commitments: new Map() });
34
+ expect(r).toEqual({ offered: [], excluded: [], required: "" });
35
+ });
36
+
37
+ it("treats touching endpoints as no overlap", () => {
38
+ expect(overlaps({ start: "1", end: "2" }, { start: "2", end: "3" })).toBe(false);
39
+ expect(overlaps({ start: "1", end: "3" }, { start: "2", end: "4" })).toBe(true);
40
+ });
41
+ });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Who can be sent to this job, and why the others cannot.
3
+ *
4
+ * The rule that matters: **a qualified worker who is already committed in the
5
+ * window is offered WITH the conflict shown, never dropped.** The office
6
+ * routinely double-books on purpose — a job runs short, a customer is on the
7
+ * way home, an engineer volunteers. A tool that silently removes the busy
8
+ * option makes that decision for them and gives no way to see it was made.
9
+ *
10
+ * Only a missing qualification excludes, and every exclusion is named with its
11
+ * worker and its reason, because `offered=0` with no explanation is the shape a
12
+ * quiet day and an unstaffable job type share.
13
+ *
14
+ * It lives in this shared lib rather than in the dispatch plugin because the
15
+ * portal visits push runs in `platform/ui` and cannot import a plugin's build
16
+ * output, and a second implementation of qualification and clash is the defect
17
+ * `functions/api/visits.ts:33-36` names: a gate written once per client is a
18
+ * gate got wrong once per client. Task 2510 moved `buildVisitKeyboard` here for
19
+ * the same reason. The dispatch plugin re-exports both from
20
+ * `mcp/src/lib/allocate.ts`, which is the import site its tools and tests
21
+ * already name.
22
+ */
23
+ import type { DispatchConfig } from "./types.js";
24
+ // `Commitment` is `readCommitments`'s own return shape and is declared once, in
25
+ // read.ts. The dispatch plugin carried a second structurally-identical
26
+ // declaration beside its copy of this file; consolidating to one is part of the
27
+ // move, because two declarations of one contract can drift on a field.
28
+ import type { Commitment } from "./read.js";
29
+
30
+ export type { Commitment };
31
+
32
+ export interface Candidate {
33
+ readonly workerId: string;
34
+ readonly name: string;
35
+ /** Commitments overlapping the requested window. Empty means free. */
36
+ readonly conflicts: readonly Commitment[];
37
+ }
38
+
39
+ export interface Exclusion {
40
+ readonly workerId: string;
41
+ readonly reason: "missing-qualification";
42
+ }
43
+
44
+ export interface SelectCandidatesParams {
45
+ readonly jobType: string;
46
+ readonly cfg: DispatchConfig;
47
+ /** Committed visits per workerId, already narrowed to the window. */
48
+ readonly commitments: ReadonlyMap<string, readonly Commitment[]>;
49
+ }
50
+
51
+ export interface CandidateSelection {
52
+ readonly offered: readonly Candidate[];
53
+ readonly excluded: readonly Exclusion[];
54
+ /** The qualification the job type demands, echoed for the log line. */
55
+ readonly required: string;
56
+ }
57
+
58
+ export function selectCandidates(params: SelectCandidatesParams): CandidateSelection {
59
+ const { jobType, cfg, commitments } = params;
60
+ const type = cfg.jobTypes.find((t) => t.name === jobType);
61
+ if (!type) {
62
+ // An unknown job type is the caller's error, not a staffing outcome. Every
63
+ // worker is excluded for a reason that would be a lie, so none are: the
64
+ // caller checks the type before asking.
65
+ return { offered: [], excluded: [], required: "" };
66
+ }
67
+ const offered: Candidate[] = [];
68
+ const excluded: Exclusion[] = [];
69
+ for (const worker of cfg.workers) {
70
+ if (!worker.qualifications.includes(type.requiredQualification)) {
71
+ excluded.push({ workerId: worker.workerId, reason: "missing-qualification" });
72
+ continue;
73
+ }
74
+ offered.push({
75
+ workerId: worker.workerId,
76
+ name: worker.name,
77
+ conflicts: commitments.get(worker.workerId) ?? [],
78
+ });
79
+ }
80
+ return { offered, excluded, required: type.requiredQualification };
81
+ }
82
+
83
+ /** `[a, b)` overlap. Touching endpoints do not overlap. */
84
+ export function overlaps(
85
+ a: { start: string; end: string },
86
+ b: { start: string; end: string },
87
+ ): boolean {
88
+ return a.start < b.end && b.start < a.end;
89
+ }
@@ -30,6 +30,20 @@ export {
30
30
  } from "./keyboard.js";
31
31
  export type { DispatchButton, VisitKeyboard } from "./keyboard.js";
32
32
 
33
+ // Task 2511 — qualification and clash, shared by the dispatch tools and the
34
+ // portal visits push. One implementation, for the reason allocate.ts states:
35
+ // the push runs in platform/ui and cannot import a plugin's build output, and a
36
+ // second copy of these rules is a gate got wrong once per client.
37
+ // `Commitment` is NOT re-exported here: it is `readCommitments`'s own shape,
38
+ // declared once in read.ts and already exported below.
39
+ export { overlaps, selectCandidates } from "./allocate.js";
40
+ export type {
41
+ Candidate,
42
+ CandidateSelection,
43
+ Exclusion,
44
+ SelectCandidatesParams,
45
+ } from "./allocate.js";
46
+
33
47
  export { withSession } from "./session.js";
34
48
  export { resolveAccountZone } from "./zone.js";
35
49
  export {
@@ -133,10 +133,10 @@ export declare function resolveOwnerTimezone(session: SeedSessionLike, accountId
133
133
  * description-less permanently because `skip-operator-edited` takes neither
134
134
  * branch. Both halves of that argument are gone. The routines PATCH now accepts
135
135
  * `description` and stamps `operatorEdited`, so an unconditional write would
136
- * destroy the operator's own caption on the next version bump; and the
137
- * scheduling plugin's one-shot `converge-routine-description` has already given
138
- * every pre-existing row a description, so no row is left holding only a
139
- * title.
136
+ * destroy the operator's own caption on the next version bump; and every
137
+ * pre-existing row already carries a description, so no row is left holding
138
+ * only a title. (A one-shot gave them one; it was retired on 2026-08-08 along
139
+ * with the rest of the install-time migrations.)
140
140
  *
141
141
  * The gate (Task 2379) is written by a `SET` after the MERGE rather than inside
142
142
  * either branch, because both branches need `e.eventId` and only one of them
@@ -108,10 +108,10 @@ async function resolveOwnerTimezone(session, accountId) {
108
108
  * description-less permanently because `skip-operator-edited` takes neither
109
109
  * branch. Both halves of that argument are gone. The routines PATCH now accepts
110
110
  * `description` and stamps `operatorEdited`, so an unconditional write would
111
- * destroy the operator's own caption on the next version bump; and the
112
- * scheduling plugin's one-shot `converge-routine-description` has already given
113
- * every pre-existing row a description, so no row is left holding only a
114
- * title.
111
+ * destroy the operator's own caption on the next version bump; and every
112
+ * pre-existing row already carries a description, so no row is left holding
113
+ * only a title. (A one-shot gave them one; it was retired on 2026-08-08 along
114
+ * with the rest of the install-time migrations.)
115
115
  *
116
116
  * The gate (Task 2379) is written by a `SET` after the MERGE rather than inside
117
117
  * either branch, because both branches need `e.eventId` and only one of them
@@ -175,10 +175,10 @@ export async function resolveOwnerTimezone(
175
175
  * description-less permanently because `skip-operator-edited` takes neither
176
176
  * branch. Both halves of that argument are gone. The routines PATCH now accepts
177
177
  * `description` and stamps `operatorEdited`, so an unconditional write would
178
- * destroy the operator's own caption on the next version bump; and the
179
- * scheduling plugin's one-shot `converge-routine-description` has already given
180
- * every pre-existing row a description, so no row is left holding only a
181
- * title.
178
+ * destroy the operator's own caption on the next version bump; and every
179
+ * pre-existing row already carries a description, so no row is left holding
180
+ * only a title. (A one-shot gave them one; it was retired on 2026-08-08 along
181
+ * with the rest of the install-time migrations.)
182
182
  *
183
183
  * The gate (Task 2379) is written by a `SET` after the MERGE rather than inside
184
184
  * either branch, because both branches need `e.eventId` and only one of them
@@ -66,10 +66,15 @@ CHANNEL_REFRAME_MULTI='{"type":"user","isMeta":true,"message":{"role":"user","co
66
66
  # the reader anchor, so a rules-present turn is evaluated identically to a
67
67
  # rules-absent one; without the strip the turn no longer starts with ## Context
68
68
  # and the hook silently stops flagging on exactly the rule-carrying turns.
69
- CHANNEL_REFRAME_RULES='{"type":"user","isMeta":true,"message":{"role":"user","content":"<channel source=\"whatsapp-channel\">## Standing rules\n- Always greet the client by name\n- Use British spelling\n\n## Context\nplease draft the works order PDF for order 926\n\n## Instruction\nChoose the installed specialist that owns this request and dispatch it with the Agent tool.</channel>"}}'
69
+ # Task 2562 the header is now `## Standing rules (account owner)` and the block
70
+ # carries a do-not-infer line between the header and the bullets. Both fixtures
71
+ # hold the exact emitted copy, so a header change that misses the strip below
72
+ # fails here instead of silently un-flagging every rule-carrying turn again. The
73
+ # emitted copy carries no apostrophe, which is what lets these stay single-quoted.
74
+ CHANNEL_REFRAME_RULES='{"type":"user","isMeta":true,"message":{"role":"user","content":"<channel source=\"whatsapp-channel\">## Standing rules (account owner)\nThese standing rules belong to the account owner. Some of them may name the account owner in the third person. None of them says who sent this turn, so do not infer from them who is speaking.\n- Always greet the client by name\n- Use British spelling\n\n## Context\nplease draft the works order PDF for order 926\n\n## Instruction\nChoose the installed specialist that owns this request and dispatch it with the Agent tool.</channel>"}}'
70
75
  # Same rules-prefixed shape, one-word Context payload — after the prefix strip the
71
76
  # trivial filter must still read just the payload (`yes`), not the rules block.
72
- CHANNEL_REFRAME_RULES_TRIVIAL='{"type":"user","isMeta":true,"message":{"role":"user","content":"<channel source=\"whatsapp-channel\">## Standing rules\n- Always greet the client by name\n- Use British spelling\n\n## Context\nyes\n\n## Instruction\nChoose the installed specialist that owns this request and dispatch it with the Agent tool.</channel>"}}'
77
+ CHANNEL_REFRAME_RULES_TRIVIAL='{"type":"user","isMeta":true,"message":{"role":"user","content":"<channel source=\"whatsapp-channel\">## Standing rules (account owner)\nThese standing rules belong to the account owner. Some of them may name the account owner in the third person. None of them says who sent this turn, so do not infer from them who is speaking.\n- Always greet the client by name\n- Use British spelling\n\n## Context\nyes\n\n## Instruction\nChoose the installed specialist that owns this request and dispatch it with the Agent tool.</channel>"}}'
73
78
 
74
79
  assert_flagged() {
75
80
  local name="$1" t="$2" out
@@ -149,13 +149,19 @@ while inner.startswith("<channel"):
149
149
  # (`## Standing rules\n- …\n\n`) above `## Context`, so a rules-present channel turn
150
150
  # no longer begins with `## Context` and both the reframe detection and the trivial
151
151
  # strip below silently stopped firing on exactly those turns. Strip an optional
152
- # leading standing-rules block — anchored to the `## Standing rules` header (not any
152
+ # leading standing-rules block — anchored to the standing-rules header (not any
153
153
  # prefix), non-greedy up to the `\n\n` immediately before `## Context` — so both see
154
154
  # the `## Context` payload as before rules injection. Channel-only and match-gated on
155
155
  # a following `## Context`, so a rules-absent channel turn is byte-identical to before
156
156
  # and a typed (non-channel) prompt is untouched.
157
+ # Task 2562 — the header now carries the owner attribution and the block carries a
158
+ # do-not-infer line, both consumed by the same non-greedy body. The suffix is
159
+ # optional: a session spawned before the upgrade keeps composing the bare header
160
+ # until it is recycled, and this hook reads that turn at Stop time. This whole
161
+ # program is inside a single-quoted python3 -c, which is why the emitted copy
162
+ # carries no apostrophe.
157
163
  if is_channel:
158
- inner = re.sub(r"^## Standing rules\n[\s\S]*?\n\n(?=## Context\n)", "", inner, count=1)
164
+ inner = re.sub(r"^## Standing rules(?: \(account owner\))?\n[\s\S]*?\n\n(?=## Context\n)", "", inner, count=1)
159
165
 
160
166
  # Task 805 — a channel-wrapped reframe turn (the ## Context / ## Instruction
161
167
  # sections of composeAdminContent) is directive-equivalent: the directive hook suppresses