@rubytech/create-sitedesk-code 0.1.519 → 0.1.521

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 (317) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-07-28-task-2097-email-signature-set.md +65 -0
  3. package/payload/platform/docs/superpowers/specs/2026-07-28-task-2097-email-signature-set-design.md +155 -0
  4. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +4 -2
  5. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
  6. package/payload/platform/plugins/cloudflare/bin/portal-assemble.mjs +187 -0
  7. package/payload/platform/plugins/cloudflare/bin/portal-enrol.mjs +46 -5
  8. package/payload/platform/plugins/cloudflare/mcp/__tests__/auth-route.test.ts +19 -7
  9. package/payload/platform/plugins/cloudflare/mcp/__tests__/authorize.test.ts +154 -38
  10. package/payload/platform/plugins/cloudflare/mcp/__tests__/file-routes.test.ts +100 -29
  11. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-assemble.test.ts +143 -0
  12. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-delete-withdraw.test.ts +12 -5
  13. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-directory-listing.test.ts +15 -3
  14. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +105 -11
  15. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-indexed-download.test.ts +15 -2
  16. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-moved-download.test.ts +9 -5
  17. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-schema-shape.test.ts +47 -5
  18. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-session-grant.test.ts +26 -2
  19. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-target.test.ts +12 -4
  20. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-upload-multipart.test.ts +23 -15
  21. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-upload-target.test.ts +23 -15
  22. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-uploads-listing.test.ts +8 -0
  23. package/payload/platform/plugins/cloudflare/mcp/__tests__/skill-contract.test.ts +23 -6
  24. package/payload/platform/plugins/cloudflare/mcp/__tests__/upload-route.test.ts +36 -20
  25. package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +85 -22
  26. package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +47 -7
  27. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/authorize.ts +27 -18
  28. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/ratelimit.ts +10 -3
  29. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/session.ts +27 -11
  30. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/target.ts +18 -9
  31. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/types.ts +16 -0
  32. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/auth.ts +16 -4
  33. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/delete.ts +26 -11
  34. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +14 -10
  35. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +13 -9
  36. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/upload.ts +42 -22
  37. package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +17 -4
  38. package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
  39. package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
  40. package/payload/platform/plugins/email/PLUGIN.md +20 -1
  41. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-metadata-calendar.test.js +46 -1
  42. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-metadata-calendar.test.js.map +1 -1
  43. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list-tool.test.d.ts +2 -0
  44. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list-tool.test.d.ts.map +1 -0
  45. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list-tool.test.js +142 -0
  46. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list-tool.test.js.map +1 -0
  47. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list.test.d.ts +2 -0
  48. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list.test.d.ts.map +1 -0
  49. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list.test.js +153 -0
  50. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list.test.js.map +1 -0
  51. package/payload/platform/plugins/email/mcp/dist/__tests__/email-signature-set.test.d.ts +2 -0
  52. package/payload/platform/plugins/email/mcp/dist/__tests__/email-signature-set.test.d.ts.map +1 -0
  53. package/payload/platform/plugins/email/mcp/dist/__tests__/email-signature-set.test.js +203 -0
  54. package/payload/platform/plugins/email/mcp/dist/__tests__/email-signature-set.test.js.map +1 -0
  55. package/payload/platform/plugins/email/mcp/dist/index.js +69 -0
  56. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  57. package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts.map +1 -1
  58. package/payload/platform/plugins/email/mcp/dist/lib/compose.js +5 -7
  59. package/payload/platform/plugins/email/mcp/dist/lib/compose.js.map +1 -1
  60. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +56 -0
  61. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  62. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +111 -4
  63. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  64. package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts +38 -0
  65. package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts.map +1 -1
  66. package/payload/platform/plugins/email/mcp/dist/lib/signature.js +67 -2
  67. package/payload/platform/plugins/email/mcp/dist/lib/signature.js.map +1 -1
  68. package/payload/platform/plugins/email/mcp/dist/tools/email-draft-list.d.ts +10 -0
  69. package/payload/platform/plugins/email/mcp/dist/tools/email-draft-list.d.ts.map +1 -0
  70. package/payload/platform/plugins/email/mcp/dist/tools/email-draft-list.js +73 -0
  71. package/payload/platform/plugins/email/mcp/dist/tools/email-draft-list.js.map +1 -0
  72. package/payload/platform/plugins/email/mcp/dist/tools/email-signature-set.d.ts +18 -0
  73. package/payload/platform/plugins/email/mcp/dist/tools/email-signature-set.d.ts.map +1 -0
  74. package/payload/platform/plugins/email/mcp/dist/tools/email-signature-set.js +38 -0
  75. package/payload/platform/plugins/email/mcp/dist/tools/email-signature-set.js.map +1 -0
  76. package/payload/platform/plugins/email/references/email-reference.md +15 -3
  77. package/payload/platform/plugins/email/skills/email-composition/SKILL.md +10 -0
  78. package/payload/platform/plugins/email/skills/email-signature/SKILL.md +61 -0
  79. package/payload/platform/plugins/scheduling/PLUGIN.md +8 -0
  80. package/payload/platform/plugins/scheduling/mcp/dist/index.js +26 -15
  81. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  82. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.d.ts +2 -0
  83. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.d.ts.map +1 -0
  84. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js +48 -0
  85. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js.map +1 -0
  86. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +24 -0
  87. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -0
  88. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +33 -0
  89. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -0
  90. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +22 -1
  91. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
  92. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel-meeting.test.d.ts +2 -0
  93. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel-meeting.test.d.ts.map +1 -0
  94. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel-meeting.test.js +30 -0
  95. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel-meeting.test.js.map +1 -0
  96. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-meeting.test.d.ts +2 -0
  97. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-meeting.test.d.ts.map +1 -0
  98. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-meeting.test.js +43 -0
  99. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-meeting.test.js.map +1 -0
  100. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.d.ts +2 -0
  101. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.d.ts.map +1 -0
  102. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js +87 -0
  103. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js.map +1 -0
  104. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-meeting.test.d.ts +2 -0
  105. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-meeting.test.d.ts.map +1 -0
  106. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-meeting.test.js +48 -0
  107. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-meeting.test.js.map +1 -0
  108. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -1
  109. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +14 -1
  110. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -1
  111. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts +4 -1
  112. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -1
  113. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +39 -2
  114. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -1
  115. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts +4 -1
  116. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts.map +1 -1
  117. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js +50 -1
  118. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -1
  119. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  120. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +43 -1
  121. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  122. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +3 -1
  123. package/payload/platform/scripts/check-architecture-skill-no-drift.mjs +20 -6
  124. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  125. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  126. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  127. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +17 -5
  128. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  129. package/payload/platform/services/whatsapp-channel/dist/notification.js +30 -6
  130. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  131. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  132. package/payload/platform/services/whatsapp-channel/dist/server.js +53 -21
  133. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  134. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +16 -0
  135. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +1 -1
  136. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +41 -2
  137. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +1 -1
  138. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  139. package/payload/server/{chunk-TIIWMF5G.js → chunk-AZLT4LVO.js} +40 -7
  140. package/payload/server/{manager-B6OFOXSW.js → manager-CQBSUJXR.js} +1 -1
  141. package/payload/server/public/activity.html +5 -5
  142. package/payload/server/public/agents.html +4 -4
  143. package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js +1 -0
  144. package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.br +0 -0
  145. package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.gz +0 -0
  146. package/payload/server/public/assets/AdminShell-CpaI2F_p.js +2 -0
  147. package/payload/server/public/assets/AdminShell-CpaI2F_p.js.br +0 -0
  148. package/payload/server/public/assets/AdminShell-CpaI2F_p.js.gz +0 -0
  149. package/payload/server/public/assets/{activity-DOsfKzT3.js → activity-B1rt4GIy.js} +1 -1
  150. package/payload/server/public/assets/activity-B1rt4GIy.js.br +0 -0
  151. package/payload/server/public/assets/activity-B1rt4GIy.js.gz +0 -0
  152. package/payload/server/public/assets/admin-DE3lLNje.js +1 -0
  153. package/payload/server/public/assets/admin-DE3lLNje.js.br +0 -0
  154. package/payload/server/public/assets/admin-DE3lLNje.js.gz +0 -0
  155. package/payload/server/public/assets/{agents-DqDnZTH_.js → agents-BXq8woVf.js} +1 -1
  156. package/payload/server/public/assets/agents-BXq8woVf.js.br +0 -0
  157. package/payload/server/public/assets/agents-BXq8woVf.js.gz +0 -0
  158. package/payload/server/public/assets/{browser-FL17Dfp0.js → browser-1QSCPl3X.js} +1 -1
  159. package/payload/server/public/assets/browser-1QSCPl3X.js.br +0 -0
  160. package/payload/server/public/assets/browser-1QSCPl3X.js.gz +0 -0
  161. package/payload/server/public/assets/{calendar-CLfLvsnV.js → calendar-D009qWVV.js} +1 -1
  162. package/payload/server/public/assets/calendar-D009qWVV.js.br +0 -0
  163. package/payload/server/public/assets/calendar-D009qWVV.js.gz +0 -0
  164. package/payload/server/public/assets/chat-CUg1fTqJ.js +1 -0
  165. package/payload/server/public/assets/chat-CUg1fTqJ.js.br +1 -0
  166. package/payload/server/public/assets/chat-CUg1fTqJ.js.gz +0 -0
  167. package/payload/server/public/assets/chevron-left-C2JEy7uU.js +1 -0
  168. package/payload/server/public/assets/chevron-left-C2JEy7uU.js.br +0 -0
  169. package/payload/server/public/assets/chevron-right-DWa047Ys.js +1 -0
  170. package/payload/server/public/assets/chevron-right-DWa047Ys.js.br +0 -0
  171. package/payload/server/public/assets/clock-C3OqfCnn.js +1 -0
  172. package/payload/server/public/assets/clock-C3OqfCnn.js.br +0 -0
  173. package/payload/server/public/assets/clock-C3OqfCnn.js.gz +0 -0
  174. package/payload/server/public/assets/data-Bu_yCScC.js +1 -0
  175. package/payload/server/public/assets/data-Bu_yCScC.js.br +0 -0
  176. package/payload/server/public/assets/data-Bu_yCScC.js.gz +0 -0
  177. package/payload/server/public/assets/{file-text-DNm0AzUz.js → file-text-DwGAfCsd.js} +1 -1
  178. package/payload/server/public/assets/file-text-DwGAfCsd.js.br +0 -0
  179. package/payload/server/public/assets/file-text-DwGAfCsd.js.gz +0 -0
  180. package/payload/server/public/assets/{graph-fAvmlVzh.js → graph-D-DGlfhW.js} +2 -2
  181. package/payload/server/public/assets/graph-D-DGlfhW.js.br +0 -0
  182. package/payload/server/public/assets/graph-D-DGlfhW.js.gz +0 -0
  183. package/payload/server/public/assets/{graph-labels-CwdkCxfs.js → graph-labels-BbX3fXzl.js} +1 -1
  184. package/payload/server/public/assets/graph-labels-BbX3fXzl.js.br +0 -0
  185. package/payload/server/public/assets/graph-labels-BbX3fXzl.js.gz +0 -0
  186. package/payload/server/public/assets/{maximize-2-B6tAPhC2.js → maximize-2-C_-KapxH.js} +1 -1
  187. package/payload/server/public/assets/maximize-2-C_-KapxH.js.br +0 -0
  188. package/payload/server/public/assets/maximize-2-C_-KapxH.js.gz +0 -0
  189. package/payload/server/public/assets/{operator-CTKvBVjk.js → operator-CFrZVcv6.js} +1 -1
  190. package/payload/server/public/assets/operator-CFrZVcv6.js.br +0 -0
  191. package/payload/server/public/assets/operator-CFrZVcv6.js.gz +0 -0
  192. package/payload/server/public/assets/{page-O5HkHQw4.js → page-CHt62f1_.js} +1 -1
  193. package/payload/server/public/assets/page-CHt62f1_.js.br +0 -0
  194. package/payload/server/public/assets/page-CHt62f1_.js.gz +0 -0
  195. package/payload/server/public/assets/page-E74MYB18.js +32 -0
  196. package/payload/server/public/assets/page-E74MYB18.js.br +0 -0
  197. package/payload/server/public/assets/page-E74MYB18.js.gz +0 -0
  198. package/payload/server/public/assets/{public-BF1rnVPa.js → public-CI6K1sNW.js} +1 -1
  199. package/payload/server/public/assets/public-CI6K1sNW.js.br +0 -0
  200. package/payload/server/public/assets/public-CI6K1sNW.js.gz +0 -0
  201. package/payload/server/public/assets/{rotate-ccw-B8TqfCy4.js → rotate-ccw-PWu_QGrD.js} +1 -1
  202. package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.br +0 -0
  203. package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.gz +0 -0
  204. package/payload/server/public/assets/routines-Cp3_XS7S.js +1 -0
  205. package/payload/server/public/assets/routines-Cp3_XS7S.js.br +0 -0
  206. package/payload/server/public/assets/routines-Cp3_XS7S.js.gz +0 -0
  207. package/payload/server/public/assets/skills-g8HvunPA.js +1 -0
  208. package/payload/server/public/assets/skills-g8HvunPA.js.br +0 -0
  209. package/payload/server/public/assets/skills-g8HvunPA.js.gz +0 -0
  210. package/payload/server/public/assets/{tasks-DePyOrRk.js → tasks-CMC_jDVm.js} +1 -1
  211. package/payload/server/public/assets/tasks-CMC_jDVm.js.br +0 -0
  212. package/payload/server/public/assets/tasks-CMC_jDVm.js.gz +0 -0
  213. package/payload/server/public/assets/{time-entry-format-D9gtnG01.js → time-entry-format-BXWuQ6Sw.js} +1 -1
  214. package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.br +2 -0
  215. package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.gz +0 -0
  216. package/payload/server/public/assets/{triangle-alert-DAtm2mLL.js → triangle-alert-BNshRC13.js} +1 -1
  217. package/payload/server/public/assets/triangle-alert-BNshRC13.js.br +0 -0
  218. package/payload/server/public/assets/triangle-alert-BNshRC13.js.gz +0 -0
  219. package/payload/server/public/assets/{useCopyFeedback-uMJT-p6i.js → useCopyFeedback-CwTZ9DqH.js} +1 -1
  220. package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.br +0 -0
  221. package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.gz +0 -0
  222. package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js +14 -0
  223. package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.br +0 -0
  224. package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.gz +0 -0
  225. package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css +1 -0
  226. package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.br +0 -0
  227. package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.gz +0 -0
  228. package/payload/server/public/assets/{useVoiceRecorder-BFrPC-s7.js → useVoiceRecorder-BGqV5R6u.js} +1 -1
  229. package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.br +0 -0
  230. package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.gz +0 -0
  231. package/payload/server/public/assets/{wrench-CYgaN7K9.js → wrench-DQ045Pyk.js} +1 -1
  232. package/payload/server/public/assets/wrench-DQ045Pyk.js.br +0 -0
  233. package/payload/server/public/assets/wrench-DQ045Pyk.js.gz +0 -0
  234. package/payload/server/public/browser.html +4 -4
  235. package/payload/server/public/calendar.html +7 -7
  236. package/payload/server/public/chat.html +13 -13
  237. package/payload/server/public/data.html +11 -11
  238. package/payload/server/public/graph.html +9 -9
  239. package/payload/server/public/index.html +14 -14
  240. package/payload/server/public/operator.html +14 -14
  241. package/payload/server/public/public.html +13 -13
  242. package/payload/server/public/routines.html +6 -6
  243. package/payload/server/public/skills.html +5 -5
  244. package/payload/server/public/tasks.html +6 -6
  245. package/payload/server/server.js +687 -400
  246. package/payload/server/public/assets/AdminLoginScreens-BZPbCk8g.js +0 -1
  247. package/payload/server/public/assets/AdminLoginScreens-BZPbCk8g.js.br +0 -0
  248. package/payload/server/public/assets/AdminLoginScreens-BZPbCk8g.js.gz +0 -0
  249. package/payload/server/public/assets/AdminShell-Bpo_wgxm.js +0 -2
  250. package/payload/server/public/assets/AdminShell-Bpo_wgxm.js.br +0 -0
  251. package/payload/server/public/assets/AdminShell-Bpo_wgxm.js.gz +0 -0
  252. package/payload/server/public/assets/activity-DOsfKzT3.js.br +0 -0
  253. package/payload/server/public/assets/activity-DOsfKzT3.js.gz +0 -0
  254. package/payload/server/public/assets/admin-COgD1z5a.js +0 -1
  255. package/payload/server/public/assets/admin-COgD1z5a.js.br +0 -0
  256. package/payload/server/public/assets/admin-COgD1z5a.js.gz +0 -0
  257. package/payload/server/public/assets/agents-DqDnZTH_.js.br +0 -0
  258. package/payload/server/public/assets/agents-DqDnZTH_.js.gz +0 -0
  259. package/payload/server/public/assets/browser-FL17Dfp0.js.br +0 -0
  260. package/payload/server/public/assets/browser-FL17Dfp0.js.gz +0 -0
  261. package/payload/server/public/assets/calendar-CLfLvsnV.js.br +0 -0
  262. package/payload/server/public/assets/calendar-CLfLvsnV.js.gz +0 -0
  263. package/payload/server/public/assets/chat-DYq4FxMG.js +0 -1
  264. package/payload/server/public/assets/chat-DYq4FxMG.js.br +0 -0
  265. package/payload/server/public/assets/chat-DYq4FxMG.js.gz +0 -0
  266. package/payload/server/public/assets/chevron-left-DWt5cdUo.js +0 -1
  267. package/payload/server/public/assets/chevron-right-B9suVrYy.js +0 -1
  268. package/payload/server/public/assets/chevron-right-B9suVrYy.js.br +0 -0
  269. package/payload/server/public/assets/clock-DW6qVXtv.js +0 -1
  270. package/payload/server/public/assets/clock-DW6qVXtv.js.br +0 -0
  271. package/payload/server/public/assets/clock-DW6qVXtv.js.gz +0 -0
  272. package/payload/server/public/assets/data-C8rbOgds.js +0 -1
  273. package/payload/server/public/assets/data-C8rbOgds.js.br +0 -0
  274. package/payload/server/public/assets/data-C8rbOgds.js.gz +0 -0
  275. package/payload/server/public/assets/file-text-DNm0AzUz.js.br +0 -0
  276. package/payload/server/public/assets/file-text-DNm0AzUz.js.gz +0 -0
  277. package/payload/server/public/assets/graph-fAvmlVzh.js.br +0 -0
  278. package/payload/server/public/assets/graph-fAvmlVzh.js.gz +0 -0
  279. package/payload/server/public/assets/graph-labels-CwdkCxfs.js.br +0 -0
  280. package/payload/server/public/assets/graph-labels-CwdkCxfs.js.gz +0 -0
  281. package/payload/server/public/assets/maximize-2-B6tAPhC2.js.br +0 -0
  282. package/payload/server/public/assets/maximize-2-B6tAPhC2.js.gz +0 -0
  283. package/payload/server/public/assets/operator-CTKvBVjk.js.br +0 -0
  284. package/payload/server/public/assets/operator-CTKvBVjk.js.gz +0 -0
  285. package/payload/server/public/assets/page-BWoNW3sa.js +0 -32
  286. package/payload/server/public/assets/page-BWoNW3sa.js.br +0 -0
  287. package/payload/server/public/assets/page-BWoNW3sa.js.gz +0 -0
  288. package/payload/server/public/assets/page-O5HkHQw4.js.br +0 -0
  289. package/payload/server/public/assets/page-O5HkHQw4.js.gz +0 -0
  290. package/payload/server/public/assets/public-BF1rnVPa.js.br +0 -0
  291. package/payload/server/public/assets/public-BF1rnVPa.js.gz +0 -0
  292. package/payload/server/public/assets/rotate-ccw-B8TqfCy4.js.br +0 -0
  293. package/payload/server/public/assets/rotate-ccw-B8TqfCy4.js.gz +0 -0
  294. package/payload/server/public/assets/routines-4R7BLZyH.js +0 -1
  295. package/payload/server/public/assets/routines-4R7BLZyH.js.br +0 -0
  296. package/payload/server/public/assets/routines-4R7BLZyH.js.gz +0 -0
  297. package/payload/server/public/assets/skills-CQ9Qs2tg.js +0 -1
  298. package/payload/server/public/assets/skills-CQ9Qs2tg.js.br +0 -0
  299. package/payload/server/public/assets/skills-CQ9Qs2tg.js.gz +0 -0
  300. package/payload/server/public/assets/tasks-DePyOrRk.js.br +0 -0
  301. package/payload/server/public/assets/tasks-DePyOrRk.js.gz +0 -0
  302. package/payload/server/public/assets/time-entry-format-D9gtnG01.js.br +0 -0
  303. package/payload/server/public/assets/time-entry-format-D9gtnG01.js.gz +0 -0
  304. package/payload/server/public/assets/triangle-alert-DAtm2mLL.js.br +0 -0
  305. package/payload/server/public/assets/triangle-alert-DAtm2mLL.js.gz +0 -0
  306. package/payload/server/public/assets/useCopyFeedback-uMJT-p6i.js.br +0 -0
  307. package/payload/server/public/assets/useCopyFeedback-uMJT-p6i.js.gz +0 -0
  308. package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js +0 -14
  309. package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js.br +0 -0
  310. package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js.gz +0 -0
  311. package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css +0 -1
  312. package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css.br +0 -0
  313. package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css.gz +0 -0
  314. package/payload/server/public/assets/useVoiceRecorder-BFrPC-s7.js.br +0 -0
  315. package/payload/server/public/assets/useVoiceRecorder-BFrPC-s7.js.gz +0 -0
  316. package/payload/server/public/assets/wrench-CYgaN7K9.js.br +0 -1
  317. package/payload/server/public/assets/wrench-CYgaN7K9.js.gz +0 -0
@@ -163,7 +163,6 @@ import {
163
163
  sendMediaMessage,
164
164
  sendPaused,
165
165
  sendTextMessage,
166
- senderJidFromTelephone,
167
166
  sessionScratchpadRoot,
168
167
  shouldDispatchInbound,
169
168
  shutdown,
@@ -178,7 +177,7 @@ import {
178
177
  validateFilePathInAccount,
179
178
  waitForConnection,
180
179
  warnNonUuidAccount
181
- } from "./chunk-TIIWMF5G.js";
180
+ } from "./chunk-AZLT4LVO.js";
182
181
  import {
183
182
  ACCOUNTS_DIR,
184
183
  BIN_DIR,
@@ -1719,10 +1718,10 @@ var require_dist5 = __commonJS({
1719
1718
  });
1720
1719
 
1721
1720
  // server/index.ts
1722
- import { readFileSync as readFileSync43, existsSync as existsSync47, watchFile, readdirSync as readdirSync28, statSync as statSync24 } from "fs";
1721
+ import { readFileSync as readFileSync44, existsSync as existsSync48, watchFile, readdirSync as readdirSync28, statSync as statSync24 } from "fs";
1723
1722
  import { spawn as spawn3 } from "child_process";
1724
1723
  import { createHash as createHash7 } from "crypto";
1725
- import { resolve as resolve42, join as join49, basename as basename15 } from "path";
1724
+ import { resolve as resolve42, join as join50, basename as basename15 } from "path";
1726
1725
  import { homedir as homedir4 } from "os";
1727
1726
 
1728
1727
  // server/lib/route-cache-watchers.ts
@@ -2288,11 +2287,11 @@ function staticCacheHeaders(root) {
2288
2287
  }
2289
2288
 
2290
2289
  // server/lib/static-mounts.ts
2291
- function mountStaticFiles(app72, root) {
2292
- app72.use("/*", staticCacheHeaders(root));
2293
- app72.use("/assets/*", assetsVary);
2294
- app72.use("/assets/*", serveStatic({ root, precompressed: true }));
2295
- app72.use("/*", serveStatic({ root }));
2290
+ function mountStaticFiles(app73, root) {
2291
+ app73.use("/*", staticCacheHeaders(root));
2292
+ app73.use("/assets/*", assetsVary);
2293
+ app73.use("/assets/*", serveStatic({ root, precompressed: true }));
2294
+ app73.use("/*", serveStatic({ root }));
2296
2295
  }
2297
2296
  var assetsVary = async (c, next) => {
2298
2297
  await next();
@@ -3503,8 +3502,8 @@ function webchatTurnTimeoutMs() {
3503
3502
  return Number(process.env.WEBCHAT_TURN_TIMEOUT_MS ?? String(2 * 6e4));
3504
3503
  }
3505
3504
  function createChatRoutes(deps) {
3506
- const app72 = new Hono();
3507
- app72.post("/", async (c) => {
3505
+ const app73 = new Hono();
3506
+ app73.post("/", async (c) => {
3508
3507
  console.log(`[chat-route] entered route=public method=POST`);
3509
3508
  const contentType = c.req.header("content-type") ?? "";
3510
3509
  const account = resolveAccount();
@@ -3776,7 +3775,7 @@ ${result.result.text}` : result.result.text;
3776
3775
  }
3777
3776
  });
3778
3777
  });
3779
- return app72;
3778
+ return app73;
3780
3779
  }
3781
3780
 
3782
3781
  // app/lib/channel-pty-bridge/admin-session-id.ts
@@ -3810,8 +3809,8 @@ function isLoopbackAddr(addr) {
3810
3809
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
3811
3810
  }
3812
3811
  function createScheduleInjectRoutes(deps) {
3813
- const app72 = new Hono();
3814
- app72.post("/", async (c) => {
3812
+ const app73 = new Hono();
3813
+ app73.post("/", async (c) => {
3815
3814
  const env = c.env;
3816
3815
  const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
3817
3816
  if (!isLoopbackAddr(remoteAddr)) {
@@ -3912,14 +3911,14 @@ function createScheduleInjectRoutes(deps) {
3912
3911
  console.error(`${TAG5} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
3913
3912
  return c.json({ ok: true }, 200);
3914
3913
  });
3915
- return app72;
3914
+ return app73;
3916
3915
  }
3917
3916
 
3918
3917
  // server/index.ts
3919
3918
  var import_dist9 = __toESM(require_dist(), 1);
3920
3919
 
3921
3920
  // app/lib/whatsapp/avatar-store.ts
3922
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
3921
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, readdirSync as readdirSync2, statSync as statSync4, rmSync } from "fs";
3923
3922
  import { join as join6 } from "path";
3924
3923
  function safeName(jid) {
3925
3924
  if (jid.length === 0 || jid.includes("/") || jid.includes("\\") || jid.includes("..")) {
@@ -3964,30 +3963,41 @@ function writeAvatarAbsent(accountId, jid) {
3964
3963
  const dir = dirFor(accountId);
3965
3964
  const base = join6(dir, safeName(jid));
3966
3965
  mkdirSync2(dir, { recursive: true });
3966
+ rmSync(`${base}.bin`, { force: true });
3967
3967
  const meta = { jid, state: "absent", fetchedAt: (/* @__PURE__ */ new Date()).toISOString() };
3968
3968
  writeFileSync3(`${base}.json`, JSON.stringify(meta));
3969
3969
  }
3970
- function listAvatarJids(accountId) {
3970
+ function removeAvatar(accountId, jid) {
3971
+ const base = join6(dirFor(accountId), safeName(jid));
3972
+ rmSync(`${base}.bin`, { force: true });
3973
+ rmSync(`${base}.json`, { force: true });
3974
+ }
3975
+ function listAvatarEntries(accountId) {
3971
3976
  const dir = dirFor(accountId);
3972
3977
  if (!existsSync3(dir)) return [];
3973
3978
  const out = [];
3974
3979
  for (const f of readdirSync2(dir)) {
3975
3980
  if (!f.endsWith(".json")) continue;
3976
3981
  const meta = readMeta(join6(dir, f));
3977
- if (meta?.jid) out.push(meta.jid);
3982
+ if (!meta?.jid) continue;
3983
+ let bytes = 0;
3984
+ if (meta.state === "present") {
3985
+ try {
3986
+ bytes = statSync4(join6(dir, `${f.slice(0, -".json".length)}.bin`)).size;
3987
+ } catch {
3988
+ bytes = 0;
3989
+ }
3990
+ }
3991
+ out.push({ jid: meta.jid, state: meta.state, bytes, fetchedAt: meta.fetchedAt });
3978
3992
  }
3979
3993
  return out;
3980
3994
  }
3995
+ function listAvatarJids(accountId) {
3996
+ return listAvatarEntries(accountId).map((e) => e.jid);
3997
+ }
3981
3998
  function avatarCensus(accountId) {
3982
- const dir = dirFor(accountId);
3983
- if (!existsSync3(dir)) return { entries: 0, bytes: 0 };
3984
- let entries2 = 0;
3985
- let bytes = 0;
3986
- for (const f of readdirSync2(dir)) {
3987
- if (f.endsWith(".json")) entries2++;
3988
- else if (f.endsWith(".bin")) bytes += statSync4(join6(dir, f)).size;
3989
- }
3990
- return { entries: entries2, bytes };
3999
+ const entries2 = listAvatarEntries(accountId);
4000
+ return { entries: entries2.length, bytes: entries2.reduce((n, e) => n + e.bytes, 0) };
3991
4001
  }
3992
4002
 
3993
4003
  // app/lib/whatsapp/avatar-fetch.ts
@@ -4007,7 +4017,7 @@ async function defaultHttpGet(url) {
4007
4017
  }
4008
4018
  }
4009
4019
  async function defaultResolve(accountId) {
4010
- const { resolveSocket: resolveSocket2 } = await import("./manager-B6OFOXSW.js");
4020
+ const { resolveSocket: resolveSocket2 } = await import("./manager-CQBSUJXR.js");
4011
4021
  return resolveSocket2(accountId);
4012
4022
  }
4013
4023
  async function fetchAvatar(accountId, jid, deps = {}) {
@@ -4055,25 +4065,66 @@ async function fetchAvatar(accountId, jid, deps = {}) {
4055
4065
  // app/lib/whatsapp/avatar-sweep.ts
4056
4066
  var TAG7 = "[wa-avatar]";
4057
4067
  var CENSUS_TAG = "[wa-avatar-census]";
4068
+ var AVATAR_STORE_MAX_BYTES = 16 * 1024 * 1024;
4058
4069
  function liveJidsForAccount(accountId) {
4059
4070
  const live = /* @__PURE__ */ new Set();
4060
4071
  for (const [remoteJid, records] of readAllConversations(accountId)) {
4061
- live.add(remoteJid);
4062
- for (const r of records) {
4063
- if (r.fromMe) continue;
4064
- const jid = senderJidFromTelephone(r.senderTelephone);
4065
- if (jid) live.add(jid);
4066
- }
4072
+ for (const jid of avatarJidsForConversation(remoteJid, records)) live.add(jid);
4067
4073
  }
4068
4074
  return live;
4069
4075
  }
4076
+ function avatarJidsForConversation(remoteJid, records) {
4077
+ const jids = /* @__PURE__ */ new Set([remoteJid]);
4078
+ if (!isGroupJid(remoteJid)) return jids;
4079
+ for (const r of records) {
4080
+ if (r.fromMe) continue;
4081
+ if (r.senderJid && isUserJid(r.senderJid)) jids.add(r.senderJid);
4082
+ }
4083
+ return jids;
4084
+ }
4085
+ function evictAvatars(accountId, live) {
4086
+ let unreferenced = 0;
4087
+ let overBound = 0;
4088
+ let freedBytes = 0;
4089
+ const kept = [];
4090
+ for (const e of listAvatarEntries(accountId)) {
4091
+ if (live.has(e.jid)) {
4092
+ kept.push(e);
4093
+ continue;
4094
+ }
4095
+ removeAvatar(accountId, e.jid);
4096
+ unreferenced++;
4097
+ freedBytes += e.bytes;
4098
+ }
4099
+ let bytes = kept.reduce((n, e) => n + e.bytes, 0);
4100
+ if (bytes > AVATAR_STORE_MAX_BYTES) {
4101
+ const oldestFirst = kept.filter((e) => e.state === "present").sort((a, b) => a.fetchedAt.localeCompare(b.fetchedAt));
4102
+ for (const e of oldestFirst) {
4103
+ if (bytes <= AVATAR_STORE_MAX_BYTES) break;
4104
+ removeAvatar(accountId, e.jid);
4105
+ overBound++;
4106
+ freedBytes += e.bytes;
4107
+ bytes -= e.bytes;
4108
+ }
4109
+ }
4110
+ return { unreferenced, overBound, freedBytes };
4111
+ }
4070
4112
  async function sweepAvatars() {
4071
4113
  const started2 = Date.now();
4072
4114
  let jids = 0;
4073
4115
  let refetched = 0;
4074
4116
  let skipped = 0;
4075
4117
  let failed = 0;
4118
+ let evicted = 0;
4076
4119
  for (const accountId of listStoredAccountIds()) {
4120
+ const live = liveJidsForAccount(accountId);
4121
+ const ev = evictAvatars(accountId, live);
4122
+ evicted += ev.unreferenced + ev.overBound;
4123
+ if (ev.unreferenced > 0 || ev.overBound > 0) {
4124
+ console.log(
4125
+ `${TAG7} op=evict account=${accountId.slice(0, 8)} unreferenced=${ev.unreferenced} overBound=${ev.overBound} freedBytes=${ev.freedBytes}`
4126
+ );
4127
+ }
4077
4128
  const cached3 = listAvatarJids(accountId);
4078
4129
  for (const jid of cached3) {
4079
4130
  jids++;
@@ -4089,11 +4140,10 @@ async function sweepAvatars() {
4089
4140
  else if (outcome === "failed") failed++;
4090
4141
  else skipped++;
4091
4142
  }
4092
- const live = liveJidsForAccount(accountId);
4093
4143
  const census = avatarCensus(accountId);
4094
4144
  console.log(`${CENSUS_TAG} account=${accountId.slice(0, 8)} entries=${census.entries} bytes=${census.bytes} live=${live.size}`);
4095
4145
  }
4096
- const note = `jids=${jids} refetched=${refetched} skipped=${skipped} failed=${failed} ms=${Date.now() - started2}`;
4146
+ const note = `jids=${jids} refetched=${refetched} skipped=${skipped} failed=${failed} evicted=${evicted} ms=${Date.now() - started2}`;
4097
4147
  console.log(`${TAG7} op=sweep ${note}`);
4098
4148
  return note;
4099
4149
  }
@@ -4104,7 +4154,9 @@ var OFFENDER_CAP = 10;
4104
4154
  function auditGroupOutbound() {
4105
4155
  let convos = 0;
4106
4156
  let outbound = 0;
4107
- let unmarked = 0;
4157
+ let operator = 0;
4158
+ let agent = 0;
4159
+ let unstamped = 0;
4108
4160
  const offenders = [];
4109
4161
  for (const accountId of listStoredAccountIds()) {
4110
4162
  for (const [remoteJid, records] of readAllConversations(accountId)) {
@@ -4113,18 +4165,20 @@ function auditGroupOutbound() {
4113
4165
  for (const r of records) {
4114
4166
  if (!r.fromMe) continue;
4115
4167
  outbound++;
4116
- if (r.origin === "operator-manual") continue;
4117
- unmarked++;
4118
- if (offenders.length < OFFENDER_CAP) offenders.push(r.msgKeyId);
4168
+ if (r.origin === "operator-manual") operator++;
4169
+ else if (r.origin === "agent") {
4170
+ agent++;
4171
+ if (offenders.length < OFFENDER_CAP) offenders.push(r.msgKeyId);
4172
+ } else unstamped++;
4119
4173
  }
4120
4174
  }
4121
4175
  }
4122
- return { convos, outbound, unmarked, offenders };
4176
+ return { convos, outbound, operator, agent, unstamped, offenders };
4123
4177
  }
4124
4178
  function auditGroupOutboundNote() {
4125
4179
  const a = auditGroupOutbound();
4126
- const tail = a.unmarked > 0 ? ` offenders=${a.offenders.join(",")}` : "";
4127
- const note = `convos=${a.convos} outbound=${a.outbound} unmarked=${a.unmarked}${tail}`;
4180
+ const tail = a.agent > 0 ? ` offenders=${a.offenders.join(",")}` : "";
4181
+ const note = `convos=${a.convos} outbound=${a.outbound} operator=${a.operator} agent=${a.agent} unstamped=${a.unstamped}${tail}`;
4128
4182
  console.log(`${TAG8} op=group-outbound-audit ${note}`);
4129
4183
  return note;
4130
4184
  }
@@ -5868,6 +5922,7 @@ app2.post("/reply", async (c) => {
5868
5922
  quotedId: null,
5869
5923
  quotedSender: null,
5870
5924
  scope: template.scope,
5925
+ origin: "agent",
5871
5926
  createdAt: nowIso
5872
5927
  };
5873
5928
  appendMessage(storeKey, rec);
@@ -6813,7 +6868,51 @@ function countConfigAbsent(platformRoot5, present) {
6813
6868
  return 0;
6814
6869
  }
6815
6870
  }
6816
- function discoverPortalAccounts(accountsDir) {
6871
+ var SAFE_AUDIT_ACCOUNT_ID = /^[a-z0-9-]+$/;
6872
+ function auditAccountClause(portal) {
6873
+ if (portal.storageModel !== "shared") {
6874
+ return { manifestWhere: "", directoryWhere: "", objectPrefix: void 0 };
6875
+ }
6876
+ if (!SAFE_AUDIT_ACCOUNT_ID.test(portal.accountId)) {
6877
+ throw new Error(
6878
+ `storage-broker: refusing to audit unsafe accountId "${portal.accountId}"`
6879
+ );
6880
+ }
6881
+ return {
6882
+ manifestWhere: ` WHERE accountId = '${portal.accountId}'`,
6883
+ directoryWhere: ` WHERE d.accountId = '${portal.accountId}'`,
6884
+ objectPrefix: `${portal.accountId}/`
6885
+ };
6886
+ }
6887
+ function readSharedStoreNames(platformRootDir) {
6888
+ let raw;
6889
+ try {
6890
+ raw = readFileSync8(join10(platformRootDir, "config", "cloudflare-house.env"), "utf-8");
6891
+ } catch {
6892
+ return null;
6893
+ }
6894
+ const env = {};
6895
+ for (const line of raw.split("\n")) {
6896
+ const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
6897
+ if (m) env[m[1]] = m[2].trim();
6898
+ }
6899
+ const dbName = env.DATA_PORTAL_DB_NAME;
6900
+ const bucket = env.DATA_PORTAL_BUCKET;
6901
+ return dbName && bucket ? { dbName, bucket } : null;
6902
+ }
6903
+ function resolveStorageModel(cfg, shared, onDisagree) {
6904
+ const labelled = cfg.storageModel === "shared" ? "shared" : "dedicated";
6905
+ if (!shared) return labelled;
6906
+ const namesShared = cfg.portalDbName === shared.dbName || cfg.bucketName === shared.bucket;
6907
+ const derived = namesShared ? "shared" : "dedicated";
6908
+ if (derived !== labelled && onDisagree) {
6909
+ onDisagree(
6910
+ `[storage-broker] op=storage-model result=overridden labelled=${labelled} derived=${derived} db=${cfg.portalDbName} bucket=${cfg.bucketName}`
6911
+ );
6912
+ }
6913
+ return derived;
6914
+ }
6915
+ function discoverPortalAccounts(accountsDir, shared = null) {
6817
6916
  if (!existsSync6(accountsDir)) return [];
6818
6917
  const found = [];
6819
6918
  for (const accountId of readdirSync5(accountsDir)) {
@@ -6822,7 +6921,20 @@ function discoverPortalAccounts(accountsDir) {
6822
6921
  try {
6823
6922
  const cfg = JSON.parse(readFileSync8(configPath2, "utf-8"));
6824
6923
  if (typeof cfg.portalDbName === "string" && cfg.portalDbName.length > 0 && typeof cfg.bucketName === "string" && cfg.bucketName.length > 0) {
6825
- found.push({ accountId, portalDbName: cfg.portalDbName, bucketName: cfg.bucketName });
6924
+ found.push({
6925
+ accountId,
6926
+ portalDbName: cfg.portalDbName,
6927
+ bucketName: cfg.bucketName,
6928
+ storageModel: resolveStorageModel(
6929
+ {
6930
+ portalDbName: cfg.portalDbName,
6931
+ bucketName: cfg.bucketName,
6932
+ storageModel: cfg.storageModel
6933
+ },
6934
+ shared,
6935
+ (line) => console.error(`${line} account=${accountId}`)
6936
+ )
6937
+ });
6826
6938
  }
6827
6939
  } catch {
6828
6940
  }
@@ -6830,7 +6942,10 @@ function discoverPortalAccounts(accountsDir) {
6830
6942
  return found;
6831
6943
  }
6832
6944
  async function runDataPortalAudit(root) {
6833
- const portals = discoverPortalAccounts(resolve7(root, "..", "data", "accounts"));
6945
+ const portals = discoverPortalAccounts(
6946
+ resolve7(root, "..", "data", "accounts"),
6947
+ readSharedStoreNames(root)
6948
+ );
6834
6949
  const configAbsent = countConfigAbsent(root, portals);
6835
6950
  if (portals.length === 0) {
6836
6951
  if (configAbsent > 0) {
@@ -6841,13 +6956,14 @@ async function runDataPortalAudit(root) {
6841
6956
  const cf = await makeHouseCfExec(root);
6842
6957
  let degraded = 0;
6843
6958
  for (const portal of portals) {
6959
+ const scope = auditAccountClause(portal);
6844
6960
  let rows = null;
6845
6961
  let d1Error = null;
6846
6962
  try {
6847
6963
  rows = parseManifestRows(
6848
6964
  await cf.d1Query(
6849
6965
  portal.portalDbName,
6850
- "SELECT objectKey, ingested, uploadedAt, devicePath, deleted FROM manifest"
6966
+ "SELECT objectKey, ingested, uploadedAt, devicePath, deleted FROM manifest" + scope.manifestWhere
6851
6967
  )
6852
6968
  );
6853
6969
  } catch (err) {
@@ -6857,7 +6973,7 @@ async function runDataPortalAudit(root) {
6857
6973
  try {
6858
6974
  const published = await cf.d1Query(
6859
6975
  portal.portalDbName,
6860
- "SELECT d.relPath AS relPath FROM directory d JOIN directory_state s ON s.accountId = d.accountId AND s.currentGeneration = d.generation"
6976
+ "SELECT d.relPath AS relPath FROM directory d JOIN directory_state s ON s.accountId = d.accountId AND s.currentGeneration = d.generation" + scope.directoryWhere
6861
6977
  );
6862
6978
  const envelope = Array.isArray(published) ? published[0] : null;
6863
6979
  const results = envelope?.results;
@@ -6872,7 +6988,7 @@ async function runDataPortalAudit(root) {
6872
6988
  let objects = null;
6873
6989
  let r2Error = null;
6874
6990
  try {
6875
- objects = await cf.r2ObjectList(portal.bucketName);
6991
+ objects = await cf.r2ObjectList(portal.bucketName, scope.objectPrefix);
6876
6992
  } catch (err) {
6877
6993
  r2Error = err.message;
6878
6994
  }
@@ -7157,7 +7273,17 @@ function exposedOrOwnUploads(segments, exposed, ownerId) {
7157
7273
  if (exposed.includes(segments[0])) return true;
7158
7274
  return segments.length > 2 && segments[0] === "uploads" && segments[1] === ownerId;
7159
7275
  }
7160
- var PENDING_SELECT = "SELECT fileId, ownerId, filename, objectKey, size, targetPath, devicePath, deleted FROM manifest WHERE ingested = 0 OR deleted = 1";
7276
+ var PENDING_COLUMNS = "SELECT fileId, ownerId, filename, objectKey, size, targetPath, devicePath, deleted FROM manifest WHERE (ingested = 0 OR deleted = 1)";
7277
+ var SAFE_ACCOUNT_ID = /^[a-z0-9-]+$/;
7278
+ function pendingSelect(portal) {
7279
+ if (portal.storageModel !== "shared") return PENDING_COLUMNS;
7280
+ if (!SAFE_ACCOUNT_ID.test(portal.accountId)) {
7281
+ throw new Error(
7282
+ `portal-uploads-pull: refusing to build a query for unsafe accountId "${portal.accountId}"`
7283
+ );
7284
+ }
7285
+ return `${PENDING_COLUMNS} AND accountId = '${portal.accountId}'`;
7286
+ }
7161
7287
  function parsePendingRows(raw) {
7162
7288
  const envelope = Array.isArray(raw) ? raw[0] : null;
7163
7289
  const results = envelope?.results;
@@ -7208,7 +7334,7 @@ async function readExposedDirs(accountDir) {
7208
7334
  }
7209
7335
  async function runPortalUploadsPull(root) {
7210
7336
  const accountsRoot = resolve8(root, "..", "data", "accounts");
7211
- const portals = discoverPortalAccounts(accountsRoot);
7337
+ const portals = discoverPortalAccounts(accountsRoot, readSharedStoreNames(root));
7212
7338
  if (portals.length === 0) return "portals=0 pulled=0";
7213
7339
  const cf = await makeHouseCfExec(root);
7214
7340
  let pulledTotal = 0;
@@ -7217,7 +7343,7 @@ async function runPortalUploadsPull(root) {
7217
7343
  for (const portal of portals) {
7218
7344
  try {
7219
7345
  const { rows, malformed } = parsePendingRows(
7220
- await cf.d1Query(portal.portalDbName, PENDING_SELECT)
7346
+ await cf.d1Query(portal.portalDbName, pendingSelect(portal))
7221
7347
  );
7222
7348
  if (malformed > 0) {
7223
7349
  console.error(`${TAG14} op=malformed account=${portal.accountId} rows=${malformed}`);
@@ -7949,12 +8075,7 @@ function resolveReplyAppendAccount(args) {
7949
8075
  return args.resolve(args.sendAccountId, args.counterpartyPhone) || args.sendAccountId;
7950
8076
  }
7951
8077
  function warmConversationAvatars(accountId, remoteJid, records) {
7952
- const jids = /* @__PURE__ */ new Set([remoteJid]);
7953
- for (const r of records) {
7954
- if (r.fromMe) continue;
7955
- const jid = senderJidFromTelephone(r.senderTelephone);
7956
- if (jid) jids.add(jid);
7957
- }
8078
+ const jids = avatarJidsForConversation(remoteJid, records);
7958
8079
  console.log(`[wa-avatar] op=request account=${accountId.slice(0, 8)} convo=${remoteJid.slice(0, 12)} jids=${jids.size} reason=reader-open`);
7959
8080
  for (const jid of jids) {
7960
8081
  void fetchAvatar(accountId, jid).catch((e) => {
@@ -8025,6 +8146,10 @@ app4.get("/store-stream", requireAdminSession, (c) => {
8025
8146
  // Task 2081 — the group transcript's per-sender colour key. Stable across a
8026
8147
  // display-name change, which `senderName` is not.
8027
8148
  senderTelephone: r.senderTelephone,
8149
+ // Task 2096 — the sender's wire JID, for the per-bubble avatar. Omitted
8150
+ // rather than sent null when the record has none, so the client's own
8151
+ // presence check is the whole test. Pre-2096 records have none.
8152
+ ...r.senderJid ? { senderJid: r.senderJid } : {},
8028
8153
  origin: r.origin,
8029
8154
  msgKeyId: r.msgKeyId,
8030
8155
  // Task 1619 — folded inbound reactions for this bubble (omitted when none).
@@ -11009,8 +11134,8 @@ async function reapplyLeversViaManager() {
11009
11134
  var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
11010
11135
  var sendSeq = 0;
11011
11136
  function createWebchatRoutes(deps) {
11012
- const app72 = new Hono();
11013
- app72.post("/send", requireAdminSession, async (c) => {
11137
+ const app73 = new Hono();
11138
+ app73.post("/send", requireAdminSession, async (c) => {
11014
11139
  let accountId;
11015
11140
  try {
11016
11141
  accountId = resolvePlatformAccountId();
@@ -11208,7 +11333,7 @@ ${note}` : note;
11208
11333
  if (turnTimer.unref) turnTimer.unref();
11209
11334
  return c.json({ ok: true });
11210
11335
  });
11211
- app72.post("/interrupt", requireAdminSession, async (c) => {
11336
+ app73.post("/interrupt", requireAdminSession, async (c) => {
11212
11337
  const cacheKey = c.get("cacheKey");
11213
11338
  const requesterUserId = (cacheKey ? getUserIdForSession(cacheKey) : void 0) ?? null;
11214
11339
  const primaryUserId = resolvePrimaryAdminUserId();
@@ -11236,7 +11361,7 @@ ${note}` : note;
11236
11361
  }
11237
11362
  return c.json({ ok: true, stopped: outcome.stopped, intId: outcome.intId, deadChild: outcome.deadChild }, 200);
11238
11363
  });
11239
- app72.post("/settings", requireAdminSession, async (c) => {
11364
+ app73.post("/settings", requireAdminSession, async (c) => {
11240
11365
  const body = await c.req.json().catch(() => null);
11241
11366
  const op = body?.op;
11242
11367
  const value = body?.value;
@@ -11272,7 +11397,7 @@ ${note}` : note;
11272
11397
  console.log(`[webchat:settings] op=${op} outcome=accepted accountId=${account.accountId.slice(0, 8)} value=${value} settingsApplied=${settingsApplied}`);
11273
11398
  return c.json({ ok: true, settingsApplied });
11274
11399
  });
11275
- app72.get("/session", requireAdminSession, async (c) => {
11400
+ app73.get("/session", requireAdminSession, async (c) => {
11276
11401
  let accountId;
11277
11402
  try {
11278
11403
  accountId = resolvePlatformAccountId();
@@ -11345,7 +11470,7 @@ ${note}` : note;
11345
11470
  const indicators = await fetchComposerIndicators(sessionId);
11346
11471
  return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
11347
11472
  });
11348
- app72.post("/permission-verdict", requireAdminSession, async (c) => {
11473
+ app73.post("/permission-verdict", requireAdminSession, async (c) => {
11349
11474
  const body = await c.req.json().catch(() => null);
11350
11475
  const sessionId = body?.sessionId;
11351
11476
  const requestId = body?.request_id;
@@ -11362,7 +11487,7 @@ ${note}` : note;
11362
11487
  const ok = deps.resolvePermissionVerdict?.(`session:${sessionId}`, requestId, behavior) ?? false;
11363
11488
  return c.json({ ok });
11364
11489
  });
11365
- return app72;
11490
+ return app73;
11366
11491
  }
11367
11492
 
11368
11493
  // server/routes/webchat-greeting.ts
@@ -11656,7 +11781,7 @@ var telegram_default = app10;
11656
11781
 
11657
11782
  // server/routes/quickbooks.ts
11658
11783
  import { join as join28 } from "path";
11659
- import { existsSync as existsSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync9, mkdirSync as mkdirSync4, rmSync, renameSync as renameSync5 } from "fs";
11784
+ import { existsSync as existsSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync9, mkdirSync as mkdirSync4, rmSync as rmSync2, renameSync as renameSync5 } from "fs";
11660
11785
  var TAG18 = "[quickbooks]";
11661
11786
  var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
11662
11787
  var STATE_RE = /^[A-Za-z0-9_-]+$/;
@@ -11737,7 +11862,7 @@ async function completeConsent(args) {
11737
11862
  console.error(`${TAG18} op=token-exchange realmId=${realmId} persisted=${persisted}`);
11738
11863
  return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
11739
11864
  } finally {
11740
- rmSync(claimFile, { force: true });
11865
+ rmSync2(claimFile, { force: true });
11741
11866
  }
11742
11867
  }
11743
11868
  function page(title, detail) {
@@ -13172,7 +13297,7 @@ var session_upload_default = app19;
13172
13297
 
13173
13298
  // server/routes/admin/agents.ts
13174
13299
  import { resolve as resolve20 } from "path";
13175
- import { readdirSync as readdirSync21, readFileSync as readFileSync28, existsSync as existsSync30, rmSync as rmSync2, renameSync as renameSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync12 } from "fs";
13300
+ import { readdirSync as readdirSync21, readFileSync as readFileSync28, existsSync as existsSync30, rmSync as rmSync3, renameSync as renameSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync12 } from "fs";
13176
13301
  import { execFileSync as execFileSync6 } from "child_process";
13177
13302
 
13178
13303
  // ../services/claude-session-manager/src/tool-surface.ts
@@ -14065,7 +14190,7 @@ app20.delete("/:slug", async (c) => {
14065
14190
  return c.json({ error: `Graph cleanup failed: ${msg}` }, 500);
14066
14191
  }
14067
14192
  try {
14068
- rmSync2(agentDir, { recursive: true, force: true });
14193
+ rmSync3(agentDir, { recursive: true, force: true });
14069
14194
  console.log(`[admin/agents] op=delete accountId=${account.accountId.slice(0, 8)} slug=${slug} outcome=ok`);
14070
14195
  return c.json({ ok: true });
14071
14196
  } catch (err) {
@@ -20163,6 +20288,7 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID7, adm
20163
20288
  const operator = isOperatorHost(host, operatorDomains);
20164
20289
  const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
20165
20290
  const name = typeof body.name === "string" && body.name.length > 0 ? body.name : void 0;
20291
+ const rawTitle = typeof body.title === "string" ? body.title.trim() : "";
20166
20292
  const webchatBind = resumeId === void 0 || operator;
20167
20293
  const kind = resumeId === void 0 ? "new" : "resume";
20168
20294
  const origin = operator ? "operator" : "admin";
@@ -20193,13 +20319,25 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID7, adm
20193
20319
  payload.sessionId = resumeId;
20194
20320
  if (name) payload.name = name;
20195
20321
  }
20196
- return { webchatBind, origin, kind, sessionId, payload, model: pickedModel, effort: pickedEffort, paramsSource };
20322
+ return {
20323
+ webchatBind,
20324
+ origin,
20325
+ kind,
20326
+ sessionId,
20327
+ payload,
20328
+ model: pickedModel,
20329
+ effort: pickedEffort,
20330
+ paramsSource,
20331
+ // Never rides `payload`: the manager's rc-spawn takes no title. The route
20332
+ // applies it through the rename endpoint once the session id exists.
20333
+ title: kind === "new" && webchatBind && rawTitle.length > 0 ? rawTitle : null
20334
+ };
20197
20335
  }
20198
- function shapeWebchatResponse(sessionId, manager) {
20336
+ function shapeWebchatResponse(sessionId, manager, titleSource = "none") {
20199
20337
  const outcome = typeof manager?.outcome === "string" ? manager.outcome : "bound";
20200
20338
  const redirected = manager?.redirected === true && typeof manager?.sessionId === "string";
20201
20339
  const landed = redirected ? manager.sessionId : sessionId;
20202
- return { sessionId: landed, outcome, target: `/chat?session=${landed}` };
20340
+ return { sessionId: landed, outcome, target: `/chat?session=${landed}`, titleSource };
20203
20341
  }
20204
20342
  var app38 = new Hono();
20205
20343
  app38.post("/", requireAdminSession, async (c) => {
@@ -20278,9 +20416,40 @@ app38.post("/", requireAdminSession, async (c) => {
20278
20416
  );
20279
20417
  }
20280
20418
  }
20419
+ let titleSource = "none";
20420
+ if (plan.kind === "new") {
20421
+ if (plan.title === null) {
20422
+ console.log(
20423
+ `[chat-spawn] op=title spawnReqId=${spawnReqId} sessionId=${plan.sessionId} titleLen=0 source=absent outcome=skipped`
20424
+ );
20425
+ } else {
20426
+ try {
20427
+ const tRes = await fetch(`${managerBase("session-rc-spawn:title")}/${plan.sessionId}/rename`, {
20428
+ method: "POST",
20429
+ headers: { "Content-Type": "application/json" },
20430
+ body: JSON.stringify({ title: plan.title })
20431
+ });
20432
+ if (tRes.status === 200) {
20433
+ titleSource = "user";
20434
+ console.log(
20435
+ `[chat-spawn] op=title spawnReqId=${spawnReqId} sessionId=${plan.sessionId} titleLen=${plan.title.length} source=modal outcome=applied`
20436
+ );
20437
+ } else {
20438
+ const reason = (await tRes.json().catch(() => ({}))).reason ?? `status-${tRes.status}`;
20439
+ console.error(
20440
+ `[chat-spawn] op=title spawnReqId=${spawnReqId} sessionId=${plan.sessionId} titleLen=${plan.title.length} source=modal outcome=rejected reason=${reason}`
20441
+ );
20442
+ }
20443
+ } catch (err) {
20444
+ console.error(
20445
+ `[chat-spawn] op=title spawnReqId=${spawnReqId} sessionId=${plan.sessionId} titleLen=${plan.title.length} source=modal outcome=failed reason=${err instanceof Error ? err.message : String(err)}`
20446
+ );
20447
+ }
20448
+ }
20449
+ }
20281
20450
  console.log(`[chat-spawn] op=navigate spawnReqId=${spawnReqId} target=${target}`);
20282
20451
  console.log(`[chat-spawn] op=exit spawnReqId=${spawnReqId} outcome=bound`);
20283
- return c.json(shapeWebchatResponse(plan.sessionId, parsed));
20452
+ return c.json(shapeWebchatResponse(plan.sessionId, parsed, titleSource));
20284
20453
  }
20285
20454
  return c.json(parsed ?? {});
20286
20455
  });
@@ -21877,10 +22046,40 @@ app49.get("/booking-link", requireAdminSession, (c) => {
21877
22046
  });
21878
22047
  var calendar_default = app49;
21879
22048
 
22049
+ // server/routes/admin/data-portal.ts
22050
+ import { existsSync as existsSync38, readFileSync as readFileSync35 } from "fs";
22051
+ import { join as join44 } from "path";
22052
+ var app50 = new Hono();
22053
+ var PORTAL_FILENAME = "data-portal.json";
22054
+ app50.get("/portal-link", requireAdminSession, (c) => {
22055
+ const account = accountDirForSession(c.var.cacheKey);
22056
+ if (!account) {
22057
+ console.error("[data-portal] op=portal-link auth-rejected reason=no-account-for-session accountId=none");
22058
+ return c.json({ error: "no account for session", reason: "no-account-for-session" }, 401);
22059
+ }
22060
+ const id82 = account.accountId.slice(0, 8);
22061
+ try {
22062
+ const path = join44(account.accountDir, PORTAL_FILENAME);
22063
+ if (!existsSync38(path)) {
22064
+ console.log(`[data-portal] op=portal-link accountId=${id82} portalDomain=none source=data-portal-config`);
22065
+ return c.json({ portalDomain: null });
22066
+ }
22067
+ const cfg = JSON.parse(readFileSync35(path, "utf-8"));
22068
+ const portalDomain = typeof cfg.portalDomain === "string" && cfg.portalDomain.trim() ? cfg.portalDomain.trim() : null;
22069
+ console.log(`[data-portal] op=portal-link accountId=${id82} portalDomain=${portalDomain ?? "none"} source=data-portal-config`);
22070
+ return c.json({ portalDomain });
22071
+ } catch (err) {
22072
+ const message = err instanceof Error ? err.message : String(err);
22073
+ console.error(`[data-portal] op=portal-link-fail accountId=${id82} err="${message}"`);
22074
+ return c.json({ portalDomain: null });
22075
+ }
22076
+ });
22077
+ var data_portal_default = app50;
22078
+
21880
22079
  // server/routes/admin/routines.ts
21881
22080
  import cronParser from "cron-parser";
21882
22081
  var { parseExpression } = cronParser;
21883
- var app50 = new Hono();
22082
+ var app51 = new Hono();
21884
22083
  var AUTOMATION_WHERE = "(e.recurrence IS NOT NULL OR e.agentChannel IS NOT NULL OR e.actionPlugin IS NOT NULL)";
21885
22084
  var ROUTINE_COLS = `e.eventId AS eventId, e.name AS name, e.eventStatus AS eventStatus,
21886
22085
  e.recurrence AS recurrence, e.nextRun AS nextRun, e.lastTriggered AS lastTriggered,
@@ -21910,7 +22109,7 @@ function mapRoutine(r) {
21910
22109
  lastDispatchAt: g("lastDispatchAt")
21911
22110
  };
21912
22111
  }
21913
- app50.get("/", requireAdminSession, async (c) => {
22112
+ app51.get("/", requireAdminSession, async (c) => {
21914
22113
  const accountId = getAccountIdForSession(c.var.cacheKey);
21915
22114
  if (!accountId) {
21916
22115
  console.error('[admin:routines] op=list auth-rejected reason="no account for session"');
@@ -21936,7 +22135,7 @@ app50.get("/", requireAdminSession, async (c) => {
21936
22135
  await session.close();
21937
22136
  }
21938
22137
  });
21939
- app50.get("/:eventId", requireAdminSession, async (c) => {
22138
+ app51.get("/:eventId", requireAdminSession, async (c) => {
21940
22139
  const accountId = getAccountIdForSession(c.var.cacheKey);
21941
22140
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
21942
22141
  const eventId = c.req.param("eventId");
@@ -21960,7 +22159,7 @@ app50.get("/:eventId", requireAdminSession, async (c) => {
21960
22159
  await session.close();
21961
22160
  }
21962
22161
  });
21963
- app50.patch("/:eventId", requireAdminSession, async (c) => {
22162
+ app51.patch("/:eventId", requireAdminSession, async (c) => {
21964
22163
  const accountId = getAccountIdForSession(c.var.cacheKey);
21965
22164
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
21966
22165
  const eventId = c.req.param("eventId");
@@ -22030,7 +22229,7 @@ app50.patch("/:eventId", requireAdminSession, async (c) => {
22030
22229
  await session.close();
22031
22230
  }
22032
22231
  });
22033
- app50.delete("/:eventId", requireAdminSession, async (c) => {
22232
+ app51.delete("/:eventId", requireAdminSession, async (c) => {
22034
22233
  const accountId = getAccountIdForSession(c.var.cacheKey);
22035
22234
  if (!accountId) {
22036
22235
  console.error('[admin:routines] op=delete auth-rejected reason="no account for session"');
@@ -22058,12 +22257,12 @@ app50.delete("/:eventId", requireAdminSession, async (c) => {
22058
22257
  await session.close();
22059
22258
  }
22060
22259
  });
22061
- var routines_default = app50;
22260
+ var routines_default = app51;
22062
22261
 
22063
22262
  // server/routes/admin/skills.ts
22064
- import { existsSync as existsSync38, readdirSync as readdirSync24, readFileSync as readFileSync35, rmSync as rmSync3 } from "fs";
22263
+ import { existsSync as existsSync39, readdirSync as readdirSync24, readFileSync as readFileSync36, rmSync as rmSync4 } from "fs";
22065
22264
  import { resolve as resolve30 } from "path";
22066
- var app51 = new Hono();
22265
+ var app52 = new Hono();
22067
22266
  var NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
22068
22267
  function splitSkillMd(text) {
22069
22268
  const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
@@ -22085,14 +22284,14 @@ function stripQuotes(s) {
22085
22284
  return s;
22086
22285
  }
22087
22286
  function subdirs2(dir) {
22088
- if (!existsSync38(dir)) return [];
22287
+ if (!existsSync39(dir)) return [];
22089
22288
  try {
22090
22289
  return readdirSync24(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
22091
22290
  } catch {
22092
22291
  return [];
22093
22292
  }
22094
22293
  }
22095
- app51.get("/", requireAdminSession, async (c) => {
22294
+ app52.get("/", requireAdminSession, async (c) => {
22096
22295
  const acct = accountDirForSession(c.var.cacheKey);
22097
22296
  if (!acct) {
22098
22297
  console.error('[admin:skills] op=list auth-rejected reason="no account for session"');
@@ -22108,11 +22307,11 @@ app51.get("/", requireAdminSession, async (c) => {
22108
22307
  const skillsDir = resolve30(pluginsDir, plugin, "skills");
22109
22308
  for (const skill of subdirs2(skillsDir)) {
22110
22309
  const skillMd = resolve30(skillsDir, skill, "SKILL.md");
22111
- if (!existsSync38(skillMd)) continue;
22310
+ if (!existsSync39(skillMd)) continue;
22112
22311
  try {
22113
- const { frontmatter } = splitSkillMd(readFileSync35(skillMd, "utf-8"));
22312
+ const { frontmatter } = splitSkillMd(readFileSync36(skillMd, "utf-8"));
22114
22313
  const refsDir = resolve30(skillsDir, skill, "references");
22115
- const refCount = existsSync38(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).length : 0;
22314
+ const refCount = existsSync39(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).length : 0;
22116
22315
  skills.push({ plugin, skill, description: frontmatter.description, refCount });
22117
22316
  } catch (err) {
22118
22317
  console.error(`[admin:skills] op=list-skip plugin=${plugin} skill=${skill} reason="${err instanceof Error ? err.message : String(err)}"`);
@@ -22125,7 +22324,7 @@ app51.get("/", requireAdminSession, async (c) => {
22125
22324
  console.log(`[admin:skills] op=list accountId=${acct.accountId.slice(0, 8)} plugins=${value.pluginCount} skills=${value.skills.length} cache=${state}`);
22126
22325
  return c.json({ skills: value.skills });
22127
22326
  });
22128
- app51.get("/:plugin/:skill", requireAdminSession, async (c) => {
22327
+ app52.get("/:plugin/:skill", requireAdminSession, async (c) => {
22129
22328
  const acct = accountDirForSession(c.var.cacheKey);
22130
22329
  if (!acct) return c.json({ error: "Account not found for session" }, 401);
22131
22330
  const plugin = c.req.param("plugin");
@@ -22136,13 +22335,13 @@ app51.get("/:plugin/:skill", requireAdminSession, async (c) => {
22136
22335
  }
22137
22336
  const skillDir = resolve30(acct.accountDir, "plugins", plugin, "skills", skill);
22138
22337
  const skillMd = resolve30(skillDir, "SKILL.md");
22139
- const found = existsSync38(skillMd);
22338
+ const found = existsSync39(skillMd);
22140
22339
  console.log(`[admin:skills] op=get plugin=${plugin} skill=${skill} found=${found}`);
22141
22340
  if (!found) return c.json({ error: "Skill not found" }, 404);
22142
22341
  try {
22143
- const { frontmatter, body } = splitSkillMd(readFileSync35(skillMd, "utf-8"));
22342
+ const { frontmatter, body } = splitSkillMd(readFileSync36(skillMd, "utf-8"));
22144
22343
  const refsDir = resolve30(skillDir, "references");
22145
- const references = existsSync38(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
22344
+ const references = existsSync39(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
22146
22345
  return c.json({ skill: { plugin, skill, frontmatter, body, references } });
22147
22346
  } catch (err) {
22148
22347
  const message = err instanceof Error ? err.message : String(err);
@@ -22150,7 +22349,7 @@ app51.get("/:plugin/:skill", requireAdminSession, async (c) => {
22150
22349
  return c.json({ error: `Skill unavailable: ${message}` }, 503);
22151
22350
  }
22152
22351
  });
22153
- app51.delete("/:plugin/:skill", requireAdminSession, async (c) => {
22352
+ app52.delete("/:plugin/:skill", requireAdminSession, async (c) => {
22154
22353
  const acct = accountDirForSession(c.var.cacheKey);
22155
22354
  if (!acct) return c.json({ error: "Account not found for session" }, 401);
22156
22355
  const plugin = c.req.param("plugin");
@@ -22162,15 +22361,15 @@ app51.delete("/:plugin/:skill", requireAdminSession, async (c) => {
22162
22361
  const pluginDir = resolve30(acct.accountDir, "plugins", plugin);
22163
22362
  const skillsDir = resolve30(pluginDir, "skills");
22164
22363
  const skillDir = resolve30(skillsDir, skill);
22165
- if (!existsSync38(resolve30(skillDir, "SKILL.md"))) {
22364
+ if (!existsSync39(resolve30(skillDir, "SKILL.md"))) {
22166
22365
  console.error(`[admin:skills] op=delete outcome=rejected reason=not-found plugin=${plugin} skill=${skill}`);
22167
22366
  return c.json({ error: "Skill not found" }, 404);
22168
22367
  }
22169
22368
  try {
22170
- rmSync3(skillDir, { recursive: true, force: true });
22369
+ rmSync4(skillDir, { recursive: true, force: true });
22171
22370
  const pluginDirRemoved = subdirs2(skillsDir).length === 0;
22172
- if (pluginDirRemoved) rmSync3(pluginDir, { recursive: true, force: true });
22173
- const skillDirGone = !existsSync38(skillDir);
22371
+ if (pluginDirRemoved) rmSync4(pluginDir, { recursive: true, force: true });
22372
+ const skillDirGone = !existsSync39(skillDir);
22174
22373
  if (!skillDirGone) {
22175
22374
  console.error(`[admin:skills] op=delete plugin=${plugin} skill=${skill} outcome=rejected reason=rm-incomplete skillDirGone=false`);
22176
22375
  return c.json({ error: "Delete did not remove the skill directory" }, 503);
@@ -22183,7 +22382,7 @@ app51.delete("/:plugin/:skill", requireAdminSession, async (c) => {
22183
22382
  return c.json({ error: `Delete failed: ${message}` }, 503);
22184
22383
  }
22185
22384
  });
22186
- var skills_default = app51;
22385
+ var skills_default = app52;
22187
22386
 
22188
22387
  // app/lib/time-entry-format.ts
22189
22388
  function secondsBetween(startIso, endIso) {
@@ -22206,8 +22405,8 @@ function toNum(v) {
22206
22405
  var TAG27 = "[task-timer]";
22207
22406
  var COMPLETED = /* @__PURE__ */ new Set(["completed"]);
22208
22407
  var HIDDEN_FROM_OPEN = /* @__PURE__ */ new Set(["completed", "cancelled"]);
22209
- var app52 = new Hono();
22210
- app52.get("/", requireAdminSession, async (c) => {
22408
+ var app53 = new Hono();
22409
+ app53.get("/", requireAdminSession, async (c) => {
22211
22410
  const cacheKey = c.var.cacheKey;
22212
22411
  const accountId = getAccountIdForSession(cacheKey);
22213
22412
  if (!accountId) {
@@ -22313,13 +22512,13 @@ async function computeTaskList(accountId) {
22313
22512
  await session.close();
22314
22513
  }
22315
22514
  }
22316
- var tasks_list_default = app52;
22515
+ var tasks_list_default = app53;
22317
22516
 
22318
22517
  // server/routes/admin/task-timer-start.ts
22319
22518
  import { randomUUID as randomUUID10 } from "crypto";
22320
22519
  var TAG28 = "[task-timer]";
22321
- var app53 = new Hono();
22322
- app53.post("/", requireAdminSession, async (c) => {
22520
+ var app54 = new Hono();
22521
+ app54.post("/", requireAdminSession, async (c) => {
22323
22522
  const cacheKey = c.var.cacheKey;
22324
22523
  const accountId = getAccountIdForSession(cacheKey);
22325
22524
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
@@ -22376,13 +22575,13 @@ app53.post("/", requireAdminSession, async (c) => {
22376
22575
  await session.close();
22377
22576
  }
22378
22577
  });
22379
- var task_timer_start_default = app53;
22578
+ var task_timer_start_default = app54;
22380
22579
 
22381
22580
  // server/routes/admin/task-timer-stop.ts
22382
22581
  import neo4j4 from "neo4j-driver";
22383
22582
  var TAG29 = "[task-timer]";
22384
- var app54 = new Hono();
22385
- app54.post("/", requireAdminSession, async (c) => {
22583
+ var app55 = new Hono();
22584
+ app55.post("/", requireAdminSession, async (c) => {
22386
22585
  const cacheKey = c.var.cacheKey;
22387
22586
  const accountId = getAccountIdForSession(cacheKey);
22388
22587
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
@@ -22451,12 +22650,12 @@ app54.post("/", requireAdminSession, async (c) => {
22451
22650
  await session.close();
22452
22651
  }
22453
22652
  });
22454
- var task_timer_stop_default = app54;
22653
+ var task_timer_stop_default = app55;
22455
22654
 
22456
22655
  // server/routes/admin/task-complete.ts
22457
22656
  var TAG30 = "[task-timer]";
22458
- var app55 = new Hono();
22459
- app55.post("/", requireAdminSession, async (c) => {
22657
+ var app56 = new Hono();
22658
+ app56.post("/", requireAdminSession, async (c) => {
22460
22659
  const cacheKey = c.var.cacheKey;
22461
22660
  const accountId = getAccountIdForSession(cacheKey);
22462
22661
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
@@ -22495,14 +22694,14 @@ app55.post("/", requireAdminSession, async (c) => {
22495
22694
  await session.close();
22496
22695
  }
22497
22696
  });
22498
- var task_complete_default = app55;
22697
+ var task_complete_default = app56;
22499
22698
 
22500
22699
  // server/routes/admin/task-time-adjust.ts
22501
22700
  import { randomUUID as randomUUID11 } from "crypto";
22502
22701
  import neo4j5 from "neo4j-driver";
22503
22702
  var TAG31 = "[task-time-adjust]";
22504
- var app56 = new Hono();
22505
- app56.post("/", requireAdminSession, async (c) => {
22703
+ var app57 = new Hono();
22704
+ app57.post("/", requireAdminSession, async (c) => {
22506
22705
  const cacheKey = c.var.cacheKey;
22507
22706
  const accountId = getAccountIdForSession(cacheKey);
22508
22707
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
@@ -22577,10 +22776,10 @@ app56.post("/", requireAdminSession, async (c) => {
22577
22776
  await session.close();
22578
22777
  }
22579
22778
  });
22580
- var task_time_adjust_default = app56;
22779
+ var task_time_adjust_default = app57;
22581
22780
 
22582
22781
  // app/lib/admin-roster.ts
22583
- import { existsSync as existsSync39, readdirSync as readdirSync25, readFileSync as readFileSync36 } from "fs";
22782
+ import { existsSync as existsSync40, readdirSync as readdirSync25, readFileSync as readFileSync37 } from "fs";
22584
22783
  import { resolve as resolve31 } from "path";
22585
22784
  function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
22586
22785
  let entries2;
@@ -22591,9 +22790,9 @@ function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
22591
22790
  }
22592
22791
  for (const entry of entries2) {
22593
22792
  const cfgPath = resolve31(accountsDir, entry, "account.json");
22594
- if (!existsSync39(cfgPath)) continue;
22793
+ if (!existsSync40(cfgPath)) continue;
22595
22794
  try {
22596
- const cfg = JSON.parse(readFileSync36(cfgPath, "utf-8"));
22795
+ const cfg = JSON.parse(readFileSync37(cfgPath, "utf-8"));
22597
22796
  if (cfg.role !== "house") continue;
22598
22797
  if (!Array.isArray(cfg.admins)) return [];
22599
22798
  return cfg.admins.map(
@@ -22665,8 +22864,8 @@ async function resolveAssignableAdmins(accountId, accountsDir = ACCOUNTS_DIR) {
22665
22864
 
22666
22865
  // server/routes/admin/task-assign.ts
22667
22866
  var TAG32 = "[task-assign]";
22668
- var app57 = new Hono();
22669
- app57.post("/", requireAdminSession, async (c) => {
22867
+ var app58 = new Hono();
22868
+ app58.post("/", requireAdminSession, async (c) => {
22670
22869
  const cacheKey = c.var.cacheKey;
22671
22870
  const accountId = getAccountIdForSession(cacheKey);
22672
22871
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
@@ -22748,12 +22947,12 @@ app57.post("/", requireAdminSession, async (c) => {
22748
22947
  await session.close();
22749
22948
  }
22750
22949
  });
22751
- var task_assign_default = app57;
22950
+ var task_assign_default = app58;
22752
22951
 
22753
22952
  // server/routes/admin/admin-users-list.ts
22754
22953
  var TAG33 = "[admin-users]";
22755
- var app58 = new Hono();
22756
- app58.get("/", requireAdminSession, async (c) => {
22954
+ var app59 = new Hono();
22955
+ app59.get("/", requireAdminSession, async (c) => {
22757
22956
  const cacheKey = c.var.cacheKey;
22758
22957
  const accountId = getAccountIdForSession(cacheKey);
22759
22958
  if (!accountId) {
@@ -22769,11 +22968,11 @@ app58.get("/", requireAdminSession, async (c) => {
22769
22968
  return c.json({ error: "admin-users-list-failed" }, 500);
22770
22969
  }
22771
22970
  });
22772
- var admin_users_list_default = app58;
22971
+ var admin_users_list_default = app59;
22773
22972
 
22774
22973
  // server/routes/admin/activity.ts
22775
22974
  import { readdirSync as readdirSync26 } from "fs";
22776
- var app59 = new Hono();
22975
+ var app60 = new Hono();
22777
22976
  var TAG34 = "[activity]";
22778
22977
  function resolveHouseAccountId(accounts) {
22779
22978
  const houses = accounts.filter((a) => a.config.role === "house");
@@ -22795,7 +22994,7 @@ function countAccountDirsOnDisk() {
22795
22994
  return null;
22796
22995
  }
22797
22996
  }
22798
- app59.get("/", requireAdminSession, async (c) => {
22997
+ app60.get("/", requireAdminSession, async (c) => {
22799
22998
  const req = requestToken();
22800
22999
  const cacheKey = c.var.cacheKey;
22801
23000
  const caller2 = getAccountIdForSession(cacheKey) ?? null;
@@ -22904,65 +23103,66 @@ app59.get("/", requireAdminSession, async (c) => {
22904
23103
  }
22905
23104
  });
22906
23105
  });
22907
- var activity_default = app59;
23106
+ var activity_default = app60;
22908
23107
 
22909
23108
  // server/routes/admin/index.ts
22910
- var app60 = new Hono();
22911
- app60.route("/session", session_default);
22912
- app60.route("/accounts", accounts_default);
22913
- app60.route("/logs", logs_default);
22914
- app60.route("/claude-info", claude_info_default);
22915
- app60.route("/attachment", attachment_default);
22916
- app60.route("/session-upload", session_upload_default);
22917
- app60.route("/agents", agents_default);
22918
- app60.route("/sessions", sessions_default);
22919
- app60.route("/claude-sessions", claude_sessions_default);
22920
- app60.route("/log-ingest", log_ingest_default);
22921
- app60.route("/events", events_default);
22922
- app60.route("/files", files_default);
22923
- app60.route("/graph-search", graph_search_default);
22924
- app60.route("/graph-subgraph", graph_subgraph_default);
22925
- app60.route("/graph-delete", graph_delete_default);
22926
- app60.route("/graph-restore", graph_restore_default);
22927
- app60.route("/graph-labels-in-graph", graph_labels_in_graph_default);
22928
- app60.route("/graph-default-view", graph_default_view_default);
22929
- app60.route("/sidebar-artefacts", sidebar_artefacts_default);
22930
- app60.route("/sidebar-sessions", sidebar_sessions_default);
22931
- app60.route("/session-delete", session_delete_default);
22932
- app60.route("/session-stop", session_stop_default);
22933
- app60.route("/session-archive", session_archive_default);
22934
- app60.route("/session-rename", session_rename_default);
22935
- app60.route("/session-usage", session_usage_default);
22936
- app60.route("/browser", browser_default);
22937
- app60.route("/session-rc-spawn", session_rc_spawn_default);
22938
- app60.route("/session-reseat", session_reseat_default);
22939
- app60.route("/system-stats", system_stats_default);
22940
- app60.route("/health-brand", health_default2);
22941
- app60.route("/linkedin-ingest", linkedin_ingest_default);
22942
- app60.route("/post-turn-context", post_turn_context_default);
22943
- app60.route("/public-session-context", public_session_context_default);
22944
- app60.route("/public-session-exit", public_session_exit_default);
22945
- app60.route("/access-session-evict", access_session_evict_default);
22946
- app60.route("/enrol-person", enrol_person_default);
22947
- app60.route("/calendar", calendar_default);
22948
- app60.route("/routines", routines_default);
22949
- app60.route("/skills", skills_default);
22950
- app60.route("/tasks-list", tasks_list_default);
22951
- app60.route("/task-timer-start", task_timer_start_default);
22952
- app60.route("/task-timer-stop", task_timer_stop_default);
22953
- app60.route("/task-complete", task_complete_default);
22954
- app60.route("/task-time-adjust", task_time_adjust_default);
22955
- app60.route("/task-assign", task_assign_default);
22956
- app60.route("/admin-users-list", admin_users_list_default);
22957
- app60.route("/activity", activity_default);
22958
- var admin_default = app60;
23109
+ var app61 = new Hono();
23110
+ app61.route("/session", session_default);
23111
+ app61.route("/accounts", accounts_default);
23112
+ app61.route("/logs", logs_default);
23113
+ app61.route("/claude-info", claude_info_default);
23114
+ app61.route("/attachment", attachment_default);
23115
+ app61.route("/session-upload", session_upload_default);
23116
+ app61.route("/agents", agents_default);
23117
+ app61.route("/sessions", sessions_default);
23118
+ app61.route("/claude-sessions", claude_sessions_default);
23119
+ app61.route("/log-ingest", log_ingest_default);
23120
+ app61.route("/events", events_default);
23121
+ app61.route("/files", files_default);
23122
+ app61.route("/graph-search", graph_search_default);
23123
+ app61.route("/graph-subgraph", graph_subgraph_default);
23124
+ app61.route("/graph-delete", graph_delete_default);
23125
+ app61.route("/graph-restore", graph_restore_default);
23126
+ app61.route("/graph-labels-in-graph", graph_labels_in_graph_default);
23127
+ app61.route("/graph-default-view", graph_default_view_default);
23128
+ app61.route("/sidebar-artefacts", sidebar_artefacts_default);
23129
+ app61.route("/sidebar-sessions", sidebar_sessions_default);
23130
+ app61.route("/session-delete", session_delete_default);
23131
+ app61.route("/session-stop", session_stop_default);
23132
+ app61.route("/session-archive", session_archive_default);
23133
+ app61.route("/session-rename", session_rename_default);
23134
+ app61.route("/session-usage", session_usage_default);
23135
+ app61.route("/browser", browser_default);
23136
+ app61.route("/session-rc-spawn", session_rc_spawn_default);
23137
+ app61.route("/session-reseat", session_reseat_default);
23138
+ app61.route("/system-stats", system_stats_default);
23139
+ app61.route("/health-brand", health_default2);
23140
+ app61.route("/linkedin-ingest", linkedin_ingest_default);
23141
+ app61.route("/post-turn-context", post_turn_context_default);
23142
+ app61.route("/public-session-context", public_session_context_default);
23143
+ app61.route("/public-session-exit", public_session_exit_default);
23144
+ app61.route("/access-session-evict", access_session_evict_default);
23145
+ app61.route("/enrol-person", enrol_person_default);
23146
+ app61.route("/calendar", calendar_default);
23147
+ app61.route("/data-portal", data_portal_default);
23148
+ app61.route("/routines", routines_default);
23149
+ app61.route("/skills", skills_default);
23150
+ app61.route("/tasks-list", tasks_list_default);
23151
+ app61.route("/task-timer-start", task_timer_start_default);
23152
+ app61.route("/task-timer-stop", task_timer_stop_default);
23153
+ app61.route("/task-complete", task_complete_default);
23154
+ app61.route("/task-time-adjust", task_time_adjust_default);
23155
+ app61.route("/task-assign", task_assign_default);
23156
+ app61.route("/admin-users-list", admin_users_list_default);
23157
+ app61.route("/activity", activity_default);
23158
+ var admin_default = app61;
22959
23159
 
22960
23160
  // server/routes/access/verify-token.ts
22961
23161
  var TAG35 = "[access-verify]";
22962
23162
  var MINT_TAG = "[access-session-mint]";
22963
23163
  var COOKIE_NAME = "__access_session";
22964
- var app61 = new Hono();
22965
- app61.post("/", async (c) => {
23164
+ var app62 = new Hono();
23165
+ app62.post("/", async (c) => {
22966
23166
  const ip = c.var.clientIp || "unknown";
22967
23167
  let body;
22968
23168
  try {
@@ -23044,7 +23244,7 @@ app61.post("/", async (c) => {
23044
23244
  displayName: grant.displayName
23045
23245
  });
23046
23246
  });
23047
- var verify_token_default = app61;
23247
+ var verify_token_default = app62;
23048
23248
 
23049
23249
  // app/lib/access-email.ts
23050
23250
  import { spawn as spawn2 } from "child_process";
@@ -23107,9 +23307,9 @@ async function sendMagicLinkEmail(payload) {
23107
23307
 
23108
23308
  // server/routes/access/request-magic-link.ts
23109
23309
  var TAG36 = "[access-request-link]";
23110
- var app62 = new Hono();
23310
+ var app63 = new Hono();
23111
23311
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
23112
- app62.post("/", async (c) => {
23312
+ app63.post("/", async (c) => {
23113
23313
  let body;
23114
23314
  try {
23115
23315
  body = await c.req.json();
@@ -23183,16 +23383,16 @@ app62.post("/", async (c) => {
23183
23383
  );
23184
23384
  return c.json({ message: VISITOR_MESSAGE }, 200);
23185
23385
  });
23186
- var request_magic_link_default = app62;
23386
+ var request_magic_link_default = app63;
23187
23387
 
23188
23388
  // server/routes/access/index.ts
23189
- var app63 = new Hono();
23190
- app63.route("/verify-token", verify_token_default);
23191
- app63.route("/request-magic-link", request_magic_link_default);
23192
- var access_default = app63;
23389
+ var app64 = new Hono();
23390
+ app64.route("/verify-token", verify_token_default);
23391
+ app64.route("/request-magic-link", request_magic_link_default);
23392
+ var access_default = app64;
23193
23393
 
23194
23394
  // server/routes/sites.ts
23195
- import { existsSync as existsSync40, readFileSync as readFileSync37, realpathSync as realpathSync8, statSync as statSync21 } from "fs";
23395
+ import { existsSync as existsSync41, readFileSync as readFileSync38, realpathSync as realpathSync8, statSync as statSync21 } from "fs";
23196
23396
  import { resolve as resolve33 } from "path";
23197
23397
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
23198
23398
  var MIME = {
@@ -23224,8 +23424,8 @@ function getExt(p) {
23224
23424
  if (idx < p.lastIndexOf("/")) return "";
23225
23425
  return p.slice(idx).toLowerCase();
23226
23426
  }
23227
- var app64 = new Hono();
23228
- app64.get("/:rel{.*}", (c) => {
23427
+ var app65 = new Hono();
23428
+ app65.get("/:rel{.*}", (c) => {
23229
23429
  const reqPath = c.req.path;
23230
23430
  const rawRel = c.req.param("rel") ?? "";
23231
23431
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -23258,7 +23458,7 @@ app64.get("/:rel{.*}", (c) => {
23258
23458
  }
23259
23459
  let stat11;
23260
23460
  try {
23261
- stat11 = existsSync40(filePath) ? statSync21(filePath) : null;
23461
+ stat11 = existsSync41(filePath) ? statSync21(filePath) : null;
23262
23462
  } catch {
23263
23463
  stat11 = null;
23264
23464
  }
@@ -23277,7 +23477,7 @@ app64.get("/:rel{.*}", (c) => {
23277
23477
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
23278
23478
  return c.text("Forbidden", 403);
23279
23479
  }
23280
- if (!existsSync40(filePath)) {
23480
+ if (!existsSync41(filePath)) {
23281
23481
  console.error(`[sites] not-found path=${reqPath} status=404`);
23282
23482
  return c.text("Not found", 404);
23283
23483
  }
@@ -23296,7 +23496,7 @@ app64.get("/:rel{.*}", (c) => {
23296
23496
  }
23297
23497
  let body;
23298
23498
  try {
23299
- body = readFileSync37(realPath);
23499
+ body = readFileSync38(realPath);
23300
23500
  } catch (err) {
23301
23501
  const code = err?.code;
23302
23502
  if (code === "EISDIR") {
@@ -23328,11 +23528,11 @@ app64.get("/:rel{.*}", (c) => {
23328
23528
  "X-Content-Type-Options": "nosniff"
23329
23529
  });
23330
23530
  });
23331
- var sites_default = app64;
23531
+ var sites_default = app65;
23332
23532
 
23333
23533
  // app/lib/visitor-token.ts
23334
23534
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
23335
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync38, writeFileSync as writeFileSync14 } from "fs";
23535
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync39, writeFileSync as writeFileSync14 } from "fs";
23336
23536
  import { dirname as dirname15 } from "path";
23337
23537
  var TOKEN_PREFIX = "v1.";
23338
23538
  var SECRET_BYTES = 32;
@@ -23341,7 +23541,7 @@ var cachedSecret = null;
23341
23541
  function getSecret() {
23342
23542
  if (cachedSecret) return cachedSecret;
23343
23543
  try {
23344
- const hex2 = readFileSync38(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
23544
+ const hex2 = readFileSync39(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
23345
23545
  if (hex2.length === SECRET_BYTES * 2) {
23346
23546
  cachedSecret = Buffer.from(hex2, "hex");
23347
23547
  return cachedSecret;
@@ -23355,7 +23555,7 @@ function getSecret() {
23355
23555
  console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
23356
23556
  } catch {
23357
23557
  }
23358
- const hex = readFileSync38(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
23558
+ const hex = readFileSync39(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
23359
23559
  cachedSecret = Buffer.from(hex, "hex");
23360
23560
  return cachedSecret;
23361
23561
  }
@@ -23408,8 +23608,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
23408
23608
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
23409
23609
 
23410
23610
  // app/lib/brand-config.ts
23411
- import { existsSync as existsSync41, readFileSync as readFileSync39 } from "fs";
23412
- import { join as join44 } from "path";
23611
+ import { existsSync as existsSync42, readFileSync as readFileSync40 } from "fs";
23612
+ import { join as join45 } from "path";
23413
23613
  var cached2 = null;
23414
23614
  var cachedAttempted = false;
23415
23615
  function readBrandConfig() {
@@ -23417,10 +23617,10 @@ function readBrandConfig() {
23417
23617
  cachedAttempted = true;
23418
23618
  const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
23419
23619
  if (!platformRoot5) return null;
23420
- const brandPath = join44(platformRoot5, "config", "brand.json");
23421
- if (!existsSync41(brandPath)) return null;
23620
+ const brandPath = join45(platformRoot5, "config", "brand.json");
23621
+ if (!existsSync42(brandPath)) return null;
23422
23622
  try {
23423
- cached2 = JSON.parse(readFileSync39(brandPath, "utf-8"));
23623
+ cached2 = JSON.parse(readFileSync40(brandPath, "utf-8"));
23424
23624
  return cached2;
23425
23625
  } catch {
23426
23626
  return null;
@@ -23428,7 +23628,7 @@ function readBrandConfig() {
23428
23628
  }
23429
23629
 
23430
23630
  // server/routes/visitor-consent.ts
23431
- var app65 = new Hono();
23631
+ var app66 = new Hono();
23432
23632
  var CONSENT_COOKIE_NAME = "mxy_consent";
23433
23633
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
23434
23634
  var DEFAULT_CONSENT_COPY = {
@@ -23473,17 +23673,17 @@ function siteSlugFromReferer(referer) {
23473
23673
  return "";
23474
23674
  }
23475
23675
  }
23476
- app65.options("/consent", (c) => {
23676
+ app66.options("/consent", (c) => {
23477
23677
  const origin = getOrigin(c);
23478
23678
  setCorsHeaders(c, origin);
23479
23679
  return c.body(null, 204);
23480
23680
  });
23481
- app65.options("/brand-config", (c) => {
23681
+ app66.options("/brand-config", (c) => {
23482
23682
  const origin = getOrigin(c);
23483
23683
  setCorsHeaders(c, origin);
23484
23684
  return c.body(null, 204);
23485
23685
  });
23486
- app65.post("/consent", async (c) => {
23686
+ app66.post("/consent", async (c) => {
23487
23687
  const origin = getOrigin(c);
23488
23688
  setCorsHeaders(c, origin);
23489
23689
  let raw;
@@ -23523,7 +23723,7 @@ app65.post("/consent", async (c) => {
23523
23723
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
23524
23724
  return c.body(null, 204);
23525
23725
  });
23526
- app65.get("/brand-config", (c) => {
23726
+ app66.get("/brand-config", (c) => {
23527
23727
  const origin = getOrigin(c);
23528
23728
  setCorsHeaders(c, origin);
23529
23729
  const brand = readBrandConfig();
@@ -23533,7 +23733,7 @@ app65.get("/brand-config", (c) => {
23533
23733
  c.header("Cache-Control", "public, max-age=300");
23534
23734
  return c.json({ consent: { copy, palette } });
23535
23735
  });
23536
- var visitor_consent_default = app65;
23736
+ var visitor_consent_default = app66;
23537
23737
 
23538
23738
  // server/routes/listings.ts
23539
23739
  function getCookie(headerValue, name) {
@@ -23560,8 +23760,8 @@ function appendConsentParams(pageUrl, token) {
23560
23760
  }
23561
23761
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
23562
23762
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
23563
- var app66 = new Hono();
23564
- app66.get("/:slug/click", async (c) => {
23763
+ var app67 = new Hono();
23764
+ app67.get("/:slug/click", async (c) => {
23565
23765
  const slug = c.req.param("slug") ?? "";
23566
23766
  const rawSession = c.req.query("session") ?? "";
23567
23767
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -23625,10 +23825,10 @@ app66.get("/:slug/click", async (c) => {
23625
23825
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
23626
23826
  return c.redirect(redirectUrl, 302);
23627
23827
  });
23628
- var listings_default = app66;
23828
+ var listings_default = app67;
23629
23829
 
23630
23830
  // server/routes/visitor-event.ts
23631
- var app67 = new Hono();
23831
+ var app68 = new Hono();
23632
23832
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
23633
23833
  var buckets = /* @__PURE__ */ new Map();
23634
23834
  var RATE_LIMIT = 60;
@@ -23704,12 +23904,12 @@ function originAllowed(origin, allowlist) {
23704
23904
  return false;
23705
23905
  }
23706
23906
  }
23707
- app67.options("/event", (c) => {
23907
+ app68.options("/event", (c) => {
23708
23908
  const origin = getOrigin2(c);
23709
23909
  setCorsHeaders2(c, origin);
23710
23910
  return c.body(null, 204);
23711
23911
  });
23712
- app67.post("/event", async (c) => {
23912
+ app68.post("/event", async (c) => {
23713
23913
  const origin = getOrigin2(c);
23714
23914
  setCorsHeaders2(c, origin);
23715
23915
  const ua = c.req.header("user-agent") ?? "";
@@ -23914,11 +24114,11 @@ async function writeEvent(opts) {
23914
24114
  );
23915
24115
  }
23916
24116
  }
23917
- var visitor_event_default = app67;
24117
+ var visitor_event_default = app68;
23918
24118
 
23919
24119
  // server/routes/session.ts
23920
24120
  import { resolve as resolve34 } from "path";
23921
- import { existsSync as existsSync42, writeFileSync as writeFileSync15, mkdirSync as mkdirSync9 } from "fs";
24121
+ import { existsSync as existsSync43, writeFileSync as writeFileSync15, mkdirSync as mkdirSync9 } from "fs";
23922
24122
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
23923
24123
  function writeBrandingCache(accountId, agentSlug, branding) {
23924
24124
  try {
@@ -23960,8 +24160,8 @@ function withVisitorCookie(response, visitorId) {
23960
24160
  headers
23961
24161
  });
23962
24162
  }
23963
- var app68 = new Hono();
23964
- app68.post("/", async (c) => {
24163
+ var app69 = new Hono();
24164
+ app69.post("/", async (c) => {
23965
24165
  let body;
23966
24166
  try {
23967
24167
  body = await c.req.json();
@@ -24002,7 +24202,7 @@ app68.post("/", async (c) => {
24002
24202
  let agentConfig = null;
24003
24203
  if (account) {
24004
24204
  const agentDir = resolve34(account.accountDir, "agents", agentSlug);
24005
- if (!existsSync42(agentDir) || !existsSync42(resolve34(agentDir, "config.json"))) {
24205
+ if (!existsSync43(agentDir) || !existsSync43(resolve34(agentDir, "config.json"))) {
24006
24206
  return c.json({ error: "Agent not found" }, 404);
24007
24207
  }
24008
24208
  agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
@@ -24108,7 +24308,7 @@ app68.post("/", async (c) => {
24108
24308
  newVisitorId
24109
24309
  );
24110
24310
  });
24111
- var session_default2 = app68;
24311
+ var session_default2 = app69;
24112
24312
 
24113
24313
  // server/lib/calendar-slots.ts
24114
24314
  var WEEKDAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
@@ -24191,17 +24391,17 @@ function computeOpenSlots(config, busy, fromISO, toISO) {
24191
24391
  }
24192
24392
 
24193
24393
  // server/routes/calendar-public.ts
24194
- var app69 = new Hono();
24394
+ var app70 = new Hono();
24195
24395
  function setCors(c) {
24196
24396
  c.header("Access-Control-Allow-Origin", "*");
24197
24397
  c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
24198
24398
  c.header("Access-Control-Allow-Headers", "content-type");
24199
24399
  }
24200
- app69.options("/free-busy", (c) => {
24400
+ app70.options("/free-busy", (c) => {
24201
24401
  setCors(c);
24202
24402
  return c.body(null, 204);
24203
24403
  });
24204
- app69.get("/free-busy", async (c) => {
24404
+ app70.get("/free-busy", async (c) => {
24205
24405
  setCors(c);
24206
24406
  const from = c.req.query("from");
24207
24407
  const to = c.req.query("to");
@@ -24257,14 +24457,14 @@ app69.get("/free-busy", async (c) => {
24257
24457
  await session.close();
24258
24458
  }
24259
24459
  });
24260
- var calendar_public_default = app69;
24460
+ var calendar_public_default = app70;
24261
24461
 
24262
24462
  // server/routes/portal-fetch.ts
24263
24463
  import { createReadStream as createReadStream3 } from "fs";
24264
24464
  import { stat as stat8, readFile as readFile11, realpath } from "fs/promises";
24265
- import { join as join45, resolve as resolve35, sep as sep11 } from "path";
24465
+ import { join as join46, resolve as resolve35, sep as sep11 } from "path";
24266
24466
  import { Readable as Readable3 } from "stream";
24267
- var app70 = new Hono();
24467
+ var app71 = new Hono();
24268
24468
  var TAG37 = "[portal-fetch]";
24269
24469
  async function verifySignature(secret, accountId, relPath, expiresAtMs, signature, nowMs) {
24270
24470
  if (!secret || !signature) return false;
@@ -24335,7 +24535,7 @@ function isValidAccountId2(value) {
24335
24535
  }
24336
24536
  async function readExposeFolders2(accountDir) {
24337
24537
  try {
24338
- const cfg = JSON.parse(await readFile11(join45(accountDir, "data-portal.json"), "utf8"));
24538
+ const cfg = JSON.parse(await readFile11(join46(accountDir, "data-portal.json"), "utf8"));
24339
24539
  return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
24340
24540
  } catch {
24341
24541
  return [];
@@ -24344,7 +24544,7 @@ async function readExposeFolders2(accountDir) {
24344
24544
  async function resolveFetchTarget(accountDir, relPath, ownerId) {
24345
24545
  let schemaText = null;
24346
24546
  try {
24347
- schemaText = await readFile11(join45(accountDir, "SCHEMA.md"), "utf8");
24547
+ schemaText = await readFile11(join46(accountDir, "SCHEMA.md"), "utf8");
24348
24548
  } catch {
24349
24549
  schemaText = null;
24350
24550
  }
@@ -24427,9 +24627,9 @@ async function handle(c, headOnly) {
24427
24627
  }
24428
24628
  });
24429
24629
  }
24430
- app70.get("/fetch", (c) => handle(c, false));
24431
- app70.on("HEAD", "/fetch", (c) => handle(c, true));
24432
- var portal_fetch_default = app70;
24630
+ app71.get("/fetch", (c) => handle(c, false));
24631
+ app71.on("HEAD", "/fetch", (c) => handle(c, true));
24632
+ var portal_fetch_default = app71;
24433
24633
 
24434
24634
  // app/lib/timeentry-census.ts
24435
24635
  var TAG38 = "[timeentry-census]";
@@ -24514,8 +24714,8 @@ function startTimeEntryCensus(openSession, opts = {}) {
24514
24714
  }
24515
24715
 
24516
24716
  // app/lib/ledger-census.ts
24517
- import { readdirSync as readdirSync27, readFileSync as readFileSync40, statSync as statSync22 } from "fs";
24518
- import { join as join46 } from "path";
24717
+ import { readdirSync as readdirSync27, readFileSync as readFileSync41, statSync as statSync22 } from "fs";
24718
+ import { join as join47 } from "path";
24519
24719
 
24520
24720
  // ../lib/ledger-core/dist/reconcile.js
24521
24721
  var LEDGER_INDEX_NAMES = [
@@ -24569,10 +24769,10 @@ function countWriteRejects24h(logDir, now) {
24569
24769
  }
24570
24770
  let total = 0;
24571
24771
  for (const name of entries2) {
24572
- const path = join46(logDir, name);
24772
+ const path = join47(logDir, name);
24573
24773
  try {
24574
24774
  if (now - statSync22(path).mtimeMs > DAY_MS) continue;
24575
- for (const line of readFileSync40(path, "utf8").split("\n")) {
24775
+ for (const line of readFileSync41(path, "utf8").split("\n")) {
24576
24776
  if (line.includes("[graph-write] reject")) total += 1;
24577
24777
  }
24578
24778
  } catch {
@@ -25067,8 +25267,8 @@ async function migrateUploads(opts = {}) {
25067
25267
 
25068
25268
  // app/lib/migrate-admin-webchat-sidecars.ts
25069
25269
  import { readdir as readdir7, readFile as readFile12, rename as rename5, writeFile as writeFile4, unlink as unlink4 } from "fs/promises";
25070
- import { existsSync as existsSync43 } from "fs";
25071
- import { join as join47 } from "path";
25270
+ import { existsSync as existsSync44 } from "fs";
25271
+ import { join as join48 } from "path";
25072
25272
  var ADMIN_ROLE2 = "admin";
25073
25273
  var WEBCHAT_CHANNEL2 = "webchat";
25074
25274
  var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
@@ -25097,7 +25297,7 @@ async function writeRaw(path, obj) {
25097
25297
  await rename5(tmp, path);
25098
25298
  } catch (err) {
25099
25299
  try {
25100
- if (existsSync43(tmp)) await unlink4(tmp);
25300
+ if (existsSync44(tmp)) await unlink4(tmp);
25101
25301
  } catch {
25102
25302
  }
25103
25303
  throw err;
@@ -25113,7 +25313,7 @@ async function collectLiveSidecars(projectsRoot) {
25113
25313
  }
25114
25314
  for (const slug of slugs) {
25115
25315
  if (!slug.isDirectory()) continue;
25116
- const slugDir = join47(projectsRoot, slug.name);
25316
+ const slugDir = join48(projectsRoot, slug.name);
25117
25317
  let entries2;
25118
25318
  try {
25119
25319
  entries2 = await readdir7(slugDir, { withFileTypes: true });
@@ -25122,9 +25322,9 @@ async function collectLiveSidecars(projectsRoot) {
25122
25322
  }
25123
25323
  for (const entry of entries2) {
25124
25324
  if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
25125
- out.push(join47(slugDir, entry.name));
25325
+ out.push(join48(slugDir, entry.name));
25126
25326
  } else if (entry.isDirectory() && entry.name === "subagents") {
25127
- const subDir = join47(slugDir, entry.name);
25327
+ const subDir = join48(slugDir, entry.name);
25128
25328
  let subs;
25129
25329
  try {
25130
25330
  subs = await readdir7(subDir, { withFileTypes: true });
@@ -25132,7 +25332,7 @@ async function collectLiveSidecars(projectsRoot) {
25132
25332
  continue;
25133
25333
  }
25134
25334
  for (const s of subs) {
25135
- if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join47(subDir, s.name));
25335
+ if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join48(subDir, s.name));
25136
25336
  }
25137
25337
  }
25138
25338
  }
@@ -25144,7 +25344,7 @@ function shortId(path) {
25144
25344
  return base.slice(0, 8);
25145
25345
  }
25146
25346
  async function migrateAdminWebchatSidecars(opts = {}) {
25147
- const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join47(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
25347
+ const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join48(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
25148
25348
  const usersFile = opts.usersFile ?? USERS_FILE;
25149
25349
  const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
25150
25350
  const resolveName = opts.resolveName ?? defaultResolveName;
@@ -25220,8 +25420,8 @@ async function migrateAdminWebchatSidecars(opts = {}) {
25220
25420
 
25221
25421
  // app/lib/migrate-sidecar-account-ids.ts
25222
25422
  import { readdir as readdir8, readFile as readFile13, rename as rename6, writeFile as writeFile5, unlink as unlink5 } from "fs/promises";
25223
- import { existsSync as existsSync44 } from "fs";
25224
- import { join as join48 } from "path";
25423
+ import { existsSync as existsSync45 } from "fs";
25424
+ import { join as join49 } from "path";
25225
25425
  var SESSION_META_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
25226
25426
  function projectSlugForCwd(cwd) {
25227
25427
  return cwd.replace(/\//g, "-");
@@ -25247,7 +25447,7 @@ async function writeRaw2(path, obj) {
25247
25447
  await rename6(tmp, path);
25248
25448
  } catch (err) {
25249
25449
  try {
25250
- if (existsSync44(tmp)) await unlink5(tmp);
25450
+ if (existsSync45(tmp)) await unlink5(tmp);
25251
25451
  } catch {
25252
25452
  }
25253
25453
  throw err;
@@ -25267,7 +25467,7 @@ async function collectSidecars(projectsRoot) {
25267
25467
  }
25268
25468
  for (const slug of slugs) {
25269
25469
  if (!slug.isDirectory()) continue;
25270
- const slugDir = join48(projectsRoot, slug.name);
25470
+ const slugDir = join49(projectsRoot, slug.name);
25271
25471
  let entries2;
25272
25472
  try {
25273
25473
  entries2 = await readdir8(slugDir, { withFileTypes: true });
@@ -25276,9 +25476,9 @@ async function collectSidecars(projectsRoot) {
25276
25476
  }
25277
25477
  for (const entry of entries2) {
25278
25478
  if (entry.isFile() && SESSION_META_RE2.test(entry.name)) {
25279
- out.push({ path: join48(slugDir, entry.name), slug: slug.name });
25479
+ out.push({ path: join49(slugDir, entry.name), slug: slug.name });
25280
25480
  } else if (entry.isDirectory() && (entry.name === "subagents" || entry.name === "archive")) {
25281
- const subDir = join48(slugDir, entry.name);
25481
+ const subDir = join49(slugDir, entry.name);
25282
25482
  let subs;
25283
25483
  try {
25284
25484
  subs = await readdir8(subDir, { withFileTypes: true });
@@ -25287,7 +25487,7 @@ async function collectSidecars(projectsRoot) {
25287
25487
  }
25288
25488
  for (const s of subs) {
25289
25489
  if (s.isFile() && SESSION_META_RE2.test(s.name)) {
25290
- out.push({ path: join48(subDir, s.name), slug: slug.name });
25490
+ out.push({ path: join49(subDir, s.name), slug: slug.name });
25291
25491
  }
25292
25492
  }
25293
25493
  }
@@ -25296,7 +25496,7 @@ async function collectSidecars(projectsRoot) {
25296
25496
  return out;
25297
25497
  }
25298
25498
  async function migrateSidecarAccountIds(opts = {}) {
25299
- const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join48(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
25499
+ const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join49(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
25300
25500
  const accounts = opts.accounts ?? listValidAccounts();
25301
25501
  const result = {
25302
25502
  scanned: 0,
@@ -25609,8 +25809,8 @@ var streamSSE = (c, cb, onError) => {
25609
25809
 
25610
25810
  // app/lib/whatsapp/gateway/routes.ts
25611
25811
  function createWaChannelRoutes(deps) {
25612
- const app72 = new Hono();
25613
- app72.get("/wa-channel/inbound", (c) => {
25812
+ const app73 = new Hono();
25813
+ app73.get("/wa-channel/inbound", (c) => {
25614
25814
  const senderId = c.req.query("senderId");
25615
25815
  if (!senderId) return c.json({ error: "senderId required" }, 400);
25616
25816
  return streamSSE(c, async (stream2) => {
@@ -25628,7 +25828,7 @@ function createWaChannelRoutes(deps) {
25628
25828
  }
25629
25829
  });
25630
25830
  });
25631
- app72.post("/wa-channel/reply", async (c) => {
25831
+ app73.post("/wa-channel/reply", async (c) => {
25632
25832
  const body = await c.req.json().catch(() => null);
25633
25833
  const senderId = body?.senderId;
25634
25834
  const text = body?.text;
@@ -25647,9 +25847,10 @@ function createWaChannelRoutes(deps) {
25647
25847
  return c.json({ error: message, terminal }, 500);
25648
25848
  }
25649
25849
  console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
25850
+ deps.onReplyDispatch?.(senderId, Date.now());
25650
25851
  return c.json({ ok: true });
25651
25852
  });
25652
- app72.post("/wa-channel/reply-document", async (c) => {
25853
+ app73.post("/wa-channel/reply-document", async (c) => {
25653
25854
  const body = await c.req.json().catch(() => null);
25654
25855
  const senderId = body?.senderId;
25655
25856
  const files = body?.files;
@@ -25688,7 +25889,7 @@ function createWaChannelRoutes(deps) {
25688
25889
  }
25689
25890
  return c.json({ ok: true, results });
25690
25891
  });
25691
- app72.post("/wa-channel/ready", async (c) => {
25892
+ app73.post("/wa-channel/ready", async (c) => {
25692
25893
  const body = await c.req.json().catch(() => null);
25693
25894
  const senderId = body?.senderId;
25694
25895
  if (typeof senderId !== "string") {
@@ -25697,7 +25898,7 @@ function createWaChannelRoutes(deps) {
25697
25898
  deps.onReady?.(senderId);
25698
25899
  return c.json({ ok: true });
25699
25900
  });
25700
- app72.post("/wa-channel/received", async (c) => {
25901
+ app73.post("/wa-channel/received", async (c) => {
25701
25902
  const body = await c.req.json().catch(() => null);
25702
25903
  const senderId = body?.senderId;
25703
25904
  const waMessageId = body?.waMessageId;
@@ -25707,7 +25908,7 @@ function createWaChannelRoutes(deps) {
25707
25908
  deps.onReceived?.(senderId, waMessageId);
25708
25909
  return c.json({ ok: true });
25709
25910
  });
25710
- app72.post("/wa-channel/turn-end", async (c) => {
25911
+ app73.post("/wa-channel/turn-end", async (c) => {
25711
25912
  const body = await c.req.json().catch(() => null);
25712
25913
  const senderId = body?.senderId;
25713
25914
  const sessionId = body?.sessionId;
@@ -25721,9 +25922,10 @@ function createWaChannelRoutes(deps) {
25721
25922
  const finalBytes = num(body?.finalBytes);
25722
25923
  const queueDepth = num(body?.queueDepth);
25723
25924
  const pendingTurns = num(body?.pendingTurns);
25925
+ const consumed2 = num(body?.consumed);
25724
25926
  const sid = sessionId.slice(0, 8);
25725
25927
  const deliver = interactive && !replied && finalText.trim().length > 0;
25726
- const decision = `sessionId=${sid} replied=${replied ? "yes" : "no"} interactive=${interactive ? "yes" : "no"} finalBytes=${finalBytes} queueDepth=${queueDepth} pendingTurns=${pendingTurns}`;
25928
+ const decision = `sessionId=${sid} replied=${replied ? "yes" : "no"} interactive=${interactive ? "yes" : "no"} finalBytes=${finalBytes} queueDepth=${queueDepth} pendingTurns=${pendingTurns} consumed=${consumed2}`;
25727
25929
  if (!replied && finalBytes > 0 && !deliver) {
25728
25930
  const op = interactive ? "turn-answer-dropped" : "turn-answer-withheld";
25729
25931
  console.error(
@@ -25742,6 +25944,7 @@ function createWaChannelRoutes(deps) {
25742
25944
  return c.json({ ok: true, delivered: "undelivered" });
25743
25945
  }
25744
25946
  console.error(`[whatsapp-native] op=turn-end ${decision} delivered=fallback bytes=${bytes}`);
25947
+ deps.onReplyDispatch?.(senderId, Date.now());
25745
25948
  return c.json({ ok: true, delivered: "fallback" });
25746
25949
  }
25747
25950
  if (replied) {
@@ -25751,13 +25954,14 @@ function createWaChannelRoutes(deps) {
25751
25954
  console.error(`[whatsapp-native] op=turn-end ${decision} delivered=none`);
25752
25955
  return c.json({ ok: true, delivered: "none" });
25753
25956
  });
25754
- return app72;
25957
+ return app73;
25755
25958
  }
25756
25959
 
25757
25960
  // app/lib/whatsapp/gateway/wa-gateway.ts
25758
25961
  var QUEUED_THRESHOLD_MS = 3e4;
25759
25962
  var ACK_THRESHOLD_MS = 3e4;
25760
25963
  var SWEEP_INTERVAL_MS = 15e3;
25964
+ var MEDIA_STANZA_STALE_MS = 12e4;
25761
25965
  var WaGateway = class {
25762
25966
  constructor(deps) {
25763
25967
  this.deps = deps;
@@ -25772,6 +25976,66 @@ var WaGateway = class {
25772
25976
  docContexts = /* @__PURE__ */ new Map();
25773
25977
  spawning = /* @__PURE__ */ new Set();
25774
25978
  seq = 0;
25979
+ // Task 2110 — senderId -> the arrival ms of every still-unemitted non-audio
25980
+ // media stanza for that sender, oldest first. This is the race window: a reply
25981
+ // dispatched while the list is non-empty was composed by a turn that could not
25982
+ // yet see an incoming file, and the window is measured from the OLDEST entry.
25983
+ //
25984
+ // One entry per stanza, not one per sender. Downloads finish out of order and
25985
+ // the debouncer flushes whatever has landed, so a small file can be emitted
25986
+ // while a large one is still downloading. Holding a single timestamp meant that
25987
+ // first inbound closed the window and the still-blind replies that followed
25988
+ // were never counted, with the census reading zero throughout.
25989
+ pendingMediaStanza = /* @__PURE__ */ new Map();
25990
+ // Counters for the per-sweep census, reset on every print.
25991
+ mediaRaces = 0;
25992
+ mediaRaceSenders = /* @__PURE__ */ new Set();
25993
+ mediaStanzaExpired = 0;
25994
+ /** Task 2110 — the WhatsApp manager reports that a non-audio media stanza for
25995
+ * this sender has started downloading. The gateway cannot derive this itself:
25996
+ * it first learns of the media when the completed inbound arrives, which is
25997
+ * the CLOSE of the window, not its open. Audio is excluded by the caller —
25998
+ * its transcript rides in as `text`, so no turn is ever blind to a voice note.
25999
+ * The key is canonicalised on write, the same rule handleInbound applies, so
26000
+ * a "+"-prefixed value and a digits-only one address one window. */
26001
+ noteMediaStanza(senderId, atMs) {
26002
+ const key = normalizeTarget(senderId) ?? senderId;
26003
+ const inFlight2 = this.pendingMediaStanza.get(key);
26004
+ if (inFlight2) inFlight2.push(atMs);
26005
+ else this.pendingMediaStanza.set(key, [atMs]);
26006
+ }
26007
+ /** Task 2110 — drop every entry past the staleness cap, oldest first, counting
26008
+ * each into the census. Entries are appended in arrival order, so the scan
26009
+ * stops at the first one still inside the cap. A download that never produced
26010
+ * an inbound leaves its entry here; without this it would hold the window open
26011
+ * forever and make every later reply to that sender read as a race. */
26012
+ expireStaleStanzas(key, inFlight2, now) {
26013
+ while (inFlight2.length > 0 && now - inFlight2[0] > MEDIA_STANZA_STALE_MS) {
26014
+ inFlight2.shift();
26015
+ this.mediaStanzaExpired++;
26016
+ }
26017
+ if (inFlight2.length === 0) this.pendingMediaStanza.delete(key);
26018
+ }
26019
+ /** Task 2110 — a reply was dispatched to this sender. When a media stanza for
26020
+ * the same sender is in flight, the turn that composed this reply could not
26021
+ * see the incoming file, so the exposure is recorded whether or not the agent
26022
+ * actually misspoke: a steering clause has no machine-checkable
26023
+ * post-condition, and this counts entry into the window, never the phrasing.
26024
+ * A window past the staleness cap is a download that never produced an
26025
+ * inbound; it is dropped and counted as expired, not reported as a race. */
26026
+ noteReplyDispatch(senderId, atMs) {
26027
+ const key = normalizeTarget(senderId) ?? senderId;
26028
+ const inFlight2 = this.pendingMediaStanza.get(key);
26029
+ if (inFlight2 === void 0) return;
26030
+ this.expireStaleStanzas(key, inFlight2, atMs);
26031
+ if (inFlight2.length === 0) return;
26032
+ const stanzaAt = inFlight2[0];
26033
+ this.mediaRaces++;
26034
+ this.mediaRaceSenders.add(key);
26035
+ console.error(
26036
+ `[whatsapp-native] op=media-race senderId=${key} replyAt=${new Date(atMs).toISOString()} mediaStanzaAt=${new Date(stanzaAt).toISOString()} gapMs=${atMs - stanzaAt}`
26037
+ );
26038
+ }
25775
26039
  /** Hono sub-app exposing the loopback channel routes; mount on the UI app.
25776
26040
  * Task 1836 — the process-local unseated-sender census that used to live here
25777
26041
  * was retired. Its quiet state was ambiguous (a UI restart emptied it, so
@@ -25793,7 +26057,8 @@ var WaGateway = class {
25793
26057
  console.error(
25794
26058
  `[whatsapp-native] op=channel-received senderId=${senderId} waMessageId=${waMessageId}${found ? "" : " note=unknown"}`
25795
26059
  );
25796
- }
26060
+ },
26061
+ onReplyDispatch: (senderId, atMs) => this.noteReplyDispatch(senderId, atMs)
25797
26062
  });
25798
26063
  }
25799
26064
  /** Run one reconciliation pass: log every terminal delivery event the sweep
@@ -25813,6 +26078,15 @@ var WaGateway = class {
25813
26078
  `[whatsapp-native] op=${e.kind} senderId=${e.senderId} waMessageId=${e.waMessageId} ageMs=${e.ageMs}`
25814
26079
  );
25815
26080
  }
26081
+ for (const [key, inFlight2] of [...this.pendingMediaStanza]) {
26082
+ this.expireStaleStanzas(key, inFlight2, now);
26083
+ }
26084
+ console.error(
26085
+ `[whatsapp-native] op=media-race-census races=${this.mediaRaces} senders=${this.mediaRaceSenders.size} expired=${this.mediaStanzaExpired}`
26086
+ );
26087
+ this.mediaRaces = 0;
26088
+ this.mediaRaceSenders.clear();
26089
+ this.mediaStanzaExpired = 0;
25816
26090
  return events;
25817
26091
  }
25818
26092
  /** Arm the periodic reconciliation sweep in the long-lived UI process.
@@ -25844,6 +26118,14 @@ var WaGateway = class {
25844
26118
  if (senderId !== input.senderId) {
25845
26119
  console.error(`[whatsapp-native] op=sender-key raw=${input.senderId} canonical=${senderId}`);
25846
26120
  }
26121
+ const arrived = input.media.filter((m) => m.type !== "audio").length;
26122
+ if (arrived > 0) {
26123
+ const inFlight2 = this.pendingMediaStanza.get(senderId);
26124
+ if (inFlight2) {
26125
+ inFlight2.splice(0, arrived);
26126
+ if (inFlight2.length === 0) this.pendingMediaStanza.delete(senderId);
26127
+ }
26128
+ }
25847
26129
  if (input.passive) {
25848
26130
  const msgId = `wa-${++this.seq}`;
25849
26131
  await this.deps.ensureChannelSession({
@@ -26181,8 +26463,8 @@ var InboundHub2 = class {
26181
26463
 
26182
26464
  // app/lib/webchat/gateway/routes.ts
26183
26465
  function createWebchatChannelRoutes(deps) {
26184
- const app72 = new Hono();
26185
- app72.get("/webchat-channel/inbound", (c) => {
26466
+ const app73 = new Hono();
26467
+ app73.get("/webchat-channel/inbound", (c) => {
26186
26468
  const key = c.req.query("key");
26187
26469
  if (!key) return c.json({ error: "key required" }, 400);
26188
26470
  const role = c.req.query("role");
@@ -26207,7 +26489,7 @@ function createWebchatChannelRoutes(deps) {
26207
26489
  }
26208
26490
  });
26209
26491
  });
26210
- app72.post("/webchat-channel/reply", async (c) => {
26492
+ app73.post("/webchat-channel/reply", async (c) => {
26211
26493
  const body = await c.req.json().catch(() => null);
26212
26494
  const key = body?.key;
26213
26495
  const text = body?.text;
@@ -26217,7 +26499,7 @@ function createWebchatChannelRoutes(deps) {
26217
26499
  deps.onReply?.(key, text);
26218
26500
  return c.json({ ok: true });
26219
26501
  });
26220
- app72.post("/webchat-channel/connector-auth", async (c) => {
26502
+ app73.post("/webchat-channel/connector-auth", async (c) => {
26221
26503
  const body = await c.req.json().catch(() => null);
26222
26504
  const key = body?.key;
26223
26505
  const sessionId = body?.sessionId;
@@ -26230,7 +26512,7 @@ function createWebchatChannelRoutes(deps) {
26230
26512
  const result = await deps.onConnectorAuth({ key, sessionId, name, completed });
26231
26513
  return c.json(result);
26232
26514
  });
26233
- app72.post("/webchat-channel/ready", async (c) => {
26515
+ app73.post("/webchat-channel/ready", async (c) => {
26234
26516
  const body = await c.req.json().catch(() => null);
26235
26517
  const key = body?.key;
26236
26518
  if (typeof key !== "string") {
@@ -26239,7 +26521,7 @@ function createWebchatChannelRoutes(deps) {
26239
26521
  deps.onReady?.(key);
26240
26522
  return c.json({ ok: true });
26241
26523
  });
26242
- app72.post("/webchat-channel/permission-request", async (c) => {
26524
+ app73.post("/webchat-channel/permission-request", async (c) => {
26243
26525
  const body = await c.req.json().catch(() => null);
26244
26526
  const key = body?.key;
26245
26527
  const requestId = body?.request_id;
@@ -26253,7 +26535,7 @@ function createWebchatChannelRoutes(deps) {
26253
26535
  const verdict = await deps.awaitPermissionVerdict({ key, requestId, toolName, description, inputPreview });
26254
26536
  return c.json(verdict);
26255
26537
  });
26256
- app72.post("/webchat-channel/received", async (c) => {
26538
+ app73.post("/webchat-channel/received", async (c) => {
26257
26539
  const body = await c.req.json().catch(() => null);
26258
26540
  const key = body?.key;
26259
26541
  const messageId = body?.messageId;
@@ -26263,7 +26545,7 @@ function createWebchatChannelRoutes(deps) {
26263
26545
  deps.onReceived?.(key, messageId);
26264
26546
  return c.json({ ok: true });
26265
26547
  });
26266
- app72.post("/webchat-channel/turn-end", async (c) => {
26548
+ app73.post("/webchat-channel/turn-end", async (c) => {
26267
26549
  const body = await c.req.json().catch(() => null);
26268
26550
  const key = body?.key;
26269
26551
  const sessionId = body?.sessionId;
@@ -26283,7 +26565,7 @@ function createWebchatChannelRoutes(deps) {
26283
26565
  deps.onTurnEnd?.(key, role);
26284
26566
  return c.json({ ok: true });
26285
26567
  });
26286
- return app72;
26568
+ return app73;
26287
26569
  }
26288
26570
 
26289
26571
  // app/lib/webchat/gateway/webchat-gateway.ts
@@ -27379,8 +27661,8 @@ var InboundHub3 = class {
27379
27661
 
27380
27662
  // app/lib/telegram/gateway/routes.ts
27381
27663
  function createTelegramChannelRoutes(deps) {
27382
- const app72 = new Hono();
27383
- app72.get("/tg-channel/inbound", (c) => {
27664
+ const app73 = new Hono();
27665
+ app73.get("/tg-channel/inbound", (c) => {
27384
27666
  const key = c.req.query("key");
27385
27667
  if (!key) return c.json({ error: "key required" }, 400);
27386
27668
  return streamSSE(c, async (stream2) => {
@@ -27398,7 +27680,7 @@ function createTelegramChannelRoutes(deps) {
27398
27680
  }
27399
27681
  });
27400
27682
  });
27401
- app72.post("/tg-channel/reply", async (c) => {
27683
+ app73.post("/tg-channel/reply", async (c) => {
27402
27684
  const body = await c.req.json().catch(() => null);
27403
27685
  const key = body?.key;
27404
27686
  const text = body?.text;
@@ -27414,7 +27696,7 @@ function createTelegramChannelRoutes(deps) {
27414
27696
  console.error(`[telegram-native] op=reply key=${key} bytes=${Buffer.byteLength(text, "utf8")}`);
27415
27697
  return c.json({ ok: true });
27416
27698
  });
27417
- app72.post("/tg-channel/ready", async (c) => {
27699
+ app73.post("/tg-channel/ready", async (c) => {
27418
27700
  const body = await c.req.json().catch(() => null);
27419
27701
  const key = body?.key;
27420
27702
  if (typeof key !== "string") {
@@ -27423,7 +27705,7 @@ function createTelegramChannelRoutes(deps) {
27423
27705
  deps.onReady?.(key);
27424
27706
  return c.json({ ok: true });
27425
27707
  });
27426
- app72.post("/tg-channel/received", async (c) => {
27708
+ app73.post("/tg-channel/received", async (c) => {
27427
27709
  const body = await c.req.json().catch(() => null);
27428
27710
  const key = body?.key;
27429
27711
  const messageId = body?.messageId;
@@ -27433,7 +27715,7 @@ function createTelegramChannelRoutes(deps) {
27433
27715
  deps.onReceived?.(key, messageId);
27434
27716
  return c.json({ ok: true });
27435
27717
  });
27436
- app72.post("/tg-channel/turn-end", async (c) => {
27718
+ app73.post("/tg-channel/turn-end", async (c) => {
27437
27719
  const body = await c.req.json().catch(() => null);
27438
27720
  const key = body?.key;
27439
27721
  const sessionId = body?.sessionId;
@@ -27466,7 +27748,7 @@ function createTelegramChannelRoutes(deps) {
27466
27748
  console.error(`[telegram-native] op=turn-end key=${key} sessionId=${sid} replied=no delivered=none`);
27467
27749
  return c.json({ ok: true, delivered: "none" });
27468
27750
  });
27469
- return app72;
27751
+ return app73;
27470
27752
  }
27471
27753
 
27472
27754
  // app/lib/telegram/gateway/telegram-gateway.ts
@@ -28035,14 +28317,14 @@ function resolveScheduledEffectiveAccountForInstall(accountDir, destination) {
28035
28317
  }
28036
28318
 
28037
28319
  // server/lib/booking-site-accounts.ts
28038
- import { existsSync as existsSync45, readFileSync as readFileSync41 } from "fs";
28320
+ import { existsSync as existsSync46, readFileSync as readFileSync42 } from "fs";
28039
28321
  import { resolve as resolve40 } from "path";
28040
28322
  function listBookingSiteAccounts(accounts) {
28041
28323
  return accounts.filter((a) => {
28042
28324
  const availPath = resolve40(a.accountDir, "calendar-availability.json");
28043
- if (!existsSync45(availPath)) return false;
28325
+ if (!existsSync46(availPath)) return false;
28044
28326
  try {
28045
- const cfg = JSON.parse(readFileSync41(availPath, "utf-8"));
28327
+ const cfg = JSON.parse(readFileSync42(availPath, "utf-8"));
28046
28328
  return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
28047
28329
  } catch {
28048
28330
  return false;
@@ -28080,7 +28362,7 @@ function broadcastAdminShutdown(reason) {
28080
28362
 
28081
28363
  // ../lib/entitlement/src/index.ts
28082
28364
  import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
28083
- import { existsSync as existsSync46, readFileSync as readFileSync42, statSync as statSync23 } from "fs";
28365
+ import { existsSync as existsSync47, readFileSync as readFileSync43, statSync as statSync23 } from "fs";
28084
28366
  import { resolve as resolve41 } from "path";
28085
28367
 
28086
28368
  // ../lib/entitlement/src/canonicalize.ts
@@ -28129,7 +28411,7 @@ function resolveEntitlement(brand, account) {
28129
28411
  return logResolved(implicitTrust(account), null);
28130
28412
  }
28131
28413
  const entitlementPath = resolve41(brand.configDir, "entitlement.json");
28132
- if (!existsSync46(entitlementPath)) {
28414
+ if (!existsSync47(entitlementPath)) {
28133
28415
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
28134
28416
  }
28135
28417
  const stat11 = statSync23(entitlementPath);
@@ -28144,7 +28426,7 @@ function resolveEntitlement(brand, account) {
28144
28426
  function verifyAndResolve(brand, entitlementPath, account) {
28145
28427
  let pubkeyPem;
28146
28428
  try {
28147
- pubkeyPem = readFileSync42(pubkeyPath(brand), "utf-8");
28429
+ pubkeyPem = readFileSync43(pubkeyPath(brand), "utf-8");
28148
28430
  } catch (err) {
28149
28431
  return logResolved(anonymousFallback("pubkey-missing"), {
28150
28432
  reason: "pubkey-missing"
@@ -28158,7 +28440,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
28158
28440
  }
28159
28441
  let envelope;
28160
28442
  try {
28161
- envelope = JSON.parse(readFileSync42(entitlementPath, "utf-8"));
28443
+ envelope = JSON.parse(readFileSync43(entitlementPath, "utf-8"));
28162
28444
  } catch {
28163
28445
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
28164
28446
  }
@@ -28320,14 +28602,14 @@ function clientFrom(c) {
28320
28602
  }
28321
28603
  installMediaDownloadGuard();
28322
28604
  var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT || "";
28323
- var BRAND_JSON_PATH = PLATFORM_ROOT6 ? join49(PLATFORM_ROOT6, "config", "brand.json") : "";
28605
+ var BRAND_JSON_PATH = PLATFORM_ROOT6 ? join50(PLATFORM_ROOT6, "config", "brand.json") : "";
28324
28606
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
28325
- if (BRAND_JSON_PATH && !existsSync47(BRAND_JSON_PATH)) {
28607
+ if (BRAND_JSON_PATH && !existsSync48(BRAND_JSON_PATH)) {
28326
28608
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
28327
28609
  }
28328
- if (BRAND_JSON_PATH && existsSync47(BRAND_JSON_PATH)) {
28610
+ if (BRAND_JSON_PATH && existsSync48(BRAND_JSON_PATH)) {
28329
28611
  try {
28330
- const parsed = JSON.parse(readFileSync43(BRAND_JSON_PATH, "utf-8"));
28612
+ const parsed = JSON.parse(readFileSync44(BRAND_JSON_PATH, "utf-8"));
28331
28613
  BRAND = { ...BRAND, ...parsed };
28332
28614
  } catch (err) {
28333
28615
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -28359,11 +28641,11 @@ var brandLoginOpts = {
28359
28641
  appleTouchIconPath: brandAppIcon512Path,
28360
28642
  themeColor: BRAND.defaultColors?.primary
28361
28643
  };
28362
- var ALIAS_DOMAINS_PATH = join49(homedir4(), BRAND.configDir, "alias-domains.json");
28644
+ var ALIAS_DOMAINS_PATH = join50(homedir4(), BRAND.configDir, "alias-domains.json");
28363
28645
  function loadAliasDomains() {
28364
28646
  try {
28365
- if (!existsSync47(ALIAS_DOMAINS_PATH)) return null;
28366
- const parsed = JSON.parse(readFileSync43(ALIAS_DOMAINS_PATH, "utf-8"));
28647
+ if (!existsSync48(ALIAS_DOMAINS_PATH)) return null;
28648
+ const parsed = JSON.parse(readFileSync44(ALIAS_DOMAINS_PATH, "utf-8"));
28367
28649
  if (!Array.isArray(parsed)) {
28368
28650
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
28369
28651
  return null;
@@ -28389,7 +28671,7 @@ startManagerEventsSubscriber();
28389
28671
  function isPublicHost(host) {
28390
28672
  return host.startsWith("public.") || aliasDomains.has(host);
28391
28673
  }
28392
- var app71 = new Hono();
28674
+ var app72 = new Hono();
28393
28675
  function resolveChannelOverride(accountId, channel, senderId) {
28394
28676
  try {
28395
28677
  const dir = listValidAccounts().find((a) => a.accountId === accountId)?.accountDir;
@@ -28438,7 +28720,7 @@ var waGateway = new WaGateway({
28438
28720
  fetchStandingRules: fetchAccountStandingRules,
28439
28721
  resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
28440
28722
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
28441
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve42(process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
28723
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve42(process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
28442
28724
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
28443
28725
  // path-validation scope) is the sender's effective SESSION account, resolved
28444
28726
  // once at the inbound gate and threaded here via the gateway's per-sender doc
@@ -28453,7 +28735,7 @@ var waGateway = new WaGateway({
28453
28735
  caption,
28454
28736
  accountId,
28455
28737
  maxyAccountId,
28456
- platformRoot: resolve42(process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, ".."))
28738
+ platformRoot: resolve42(process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, ".."))
28457
28739
  });
28458
28740
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
28459
28741
  },
@@ -28566,13 +28848,13 @@ var waGateway = new WaGateway({
28566
28848
  nativeFileFollowers.set(senderId, ac);
28567
28849
  }
28568
28850
  });
28569
- app71.route("/", waGateway.routes());
28851
+ app72.route("/", waGateway.routes());
28570
28852
  waGateway.startSweeper();
28571
28853
  function runDuplicateSenderAudit() {
28572
28854
  try {
28573
28855
  const configDir = process.env.CLAUDE_CONFIG_DIR;
28574
28856
  if (!configDir) return;
28575
- const projectsRoot = join49(configDir, "projects");
28857
+ const projectsRoot = join50(configDir, "projects");
28576
28858
  const records = [];
28577
28859
  for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
28578
28860
  if (isSubagent || archived) continue;
@@ -28696,7 +28978,7 @@ var webchatGateway = new WebchatGateway({
28696
28978
  firePublicSessionEndReview: (input) => firePublicSessionEndReview(input)
28697
28979
  });
28698
28980
  setWebchatGateway(webchatGateway);
28699
- app71.route("/", webchatGateway.routes());
28981
+ app72.route("/", webchatGateway.routes());
28700
28982
  webchatGateway.startSweeper();
28701
28983
  webchatGateway.startPublicReaper();
28702
28984
  var telegramFileFollowers = /* @__PURE__ */ new Map();
@@ -28740,7 +29022,7 @@ var telegramGateway = new TelegramGateway({
28740
29022
  }
28741
29023
  });
28742
29024
  setTelegramGateway(telegramGateway);
28743
- app71.route("/", telegramGateway.routes());
29025
+ app72.route("/", telegramGateway.routes());
28744
29026
  telegramGateway.startSweeper();
28745
29027
  var chatRoutes = createChatRoutes({
28746
29028
  handleInbound: (input) => webchatGateway.handleInbound(input),
@@ -28774,19 +29056,19 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
28774
29056
  // and why the real dependencies are bound there rather than assembled here.
28775
29057
  effectiveAccountFor: resolveScheduledEffectiveAccountForInstall
28776
29058
  });
28777
- app71.route("/api/channel/schedule-inject", scheduleInjectRoutes);
28778
- app71.use("*", clientIpMiddleware);
29059
+ app72.route("/api/channel/schedule-inject", scheduleInjectRoutes);
29060
+ app72.use("*", clientIpMiddleware);
28779
29061
  function allowsSameOriginFraming(path) {
28780
29062
  return path === "/vnc-viewer.html" || path.startsWith("/api/admin/attachment/") || path.startsWith("/api/public-reader/attachment/") || path === "/api/admin/files/download" || path === "/api/admin/session-upload";
28781
29063
  }
28782
- app71.use("*", async (c, next) => {
29064
+ app72.use("*", async (c, next) => {
28783
29065
  await next();
28784
29066
  c.header("X-Content-Type-Options", "nosniff");
28785
29067
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
28786
29068
  c.header("X-Frame-Options", allowsSameOriginFraming(c.req.path) ? "SAMEORIGIN" : "DENY");
28787
29069
  });
28788
29070
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
28789
- app71.use("*", async (c, next) => {
29071
+ app72.use("*", async (c, next) => {
28790
29072
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
28791
29073
  await next();
28792
29074
  return;
@@ -28804,7 +29086,7 @@ app71.use("*", async (c, next) => {
28804
29086
  });
28805
29087
  }
28806
29088
  });
28807
- app71.use("*", async (c, next) => {
29089
+ app72.use("*", async (c, next) => {
28808
29090
  const host = (c.req.header("host") ?? "").split(":")[0];
28809
29091
  if (isOperatorHost(host, getOperatorDomains()) || !isPublicHost(host)) {
28810
29092
  await next();
@@ -28842,7 +29124,7 @@ function resolveRemoteAuthOpts(c) {
28842
29124
  return { ...brandLoginOpts, origin };
28843
29125
  }
28844
29126
  var MAX_LOGIN_BODY = 8 * 1024;
28845
- app71.post("/__remote-auth/login", async (c) => {
29127
+ app72.post("/__remote-auth/login", async (c) => {
28846
29128
  const client = clientFrom(c);
28847
29129
  const clientIp = client.ip || "unknown";
28848
29130
  if (!requestIsTlsTerminated(c)) {
@@ -28887,7 +29169,7 @@ app71.post("/__remote-auth/login", async (c) => {
28887
29169
  }
28888
29170
  });
28889
29171
  });
28890
- app71.get("/__remote-auth/logout", (c) => {
29172
+ app72.get("/__remote-auth/logout", (c) => {
28891
29173
  const client = clientFrom(c);
28892
29174
  const clientIp = client.ip || "unknown";
28893
29175
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -28900,7 +29182,7 @@ app71.get("/__remote-auth/logout", (c) => {
28900
29182
  }
28901
29183
  });
28902
29184
  });
28903
- app71.post("/__remote-auth/change-password", async (c) => {
29185
+ app72.post("/__remote-auth/change-password", async (c) => {
28904
29186
  const client = clientFrom(c);
28905
29187
  const clientIp = client.ip || "unknown";
28906
29188
  const rateLimited = checkRateLimit(client);
@@ -28959,13 +29241,13 @@ app71.post("/__remote-auth/change-password", async (c) => {
28959
29241
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "change", changeError: "Failed to save password", redirect }), 200);
28960
29242
  }
28961
29243
  });
28962
- app71.get("/__remote-auth/setup", (c) => {
29244
+ app72.get("/__remote-auth/setup", (c) => {
28963
29245
  if (isRemoteAuthConfigured()) {
28964
29246
  return c.redirect("/");
28965
29247
  }
28966
29248
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup" }), 200);
28967
29249
  });
28968
- app71.post("/__remote-auth/set-initial-password", async (c) => {
29250
+ app72.post("/__remote-auth/set-initial-password", async (c) => {
28969
29251
  if (isRemoteAuthConfigured()) {
28970
29252
  return c.redirect("/");
28971
29253
  }
@@ -29003,10 +29285,10 @@ app71.post("/__remote-auth/set-initial-password", async (c) => {
29003
29285
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
29004
29286
  }
29005
29287
  });
29006
- app71.get("/api/remote-auth/status", (c) => {
29288
+ app72.get("/api/remote-auth/status", (c) => {
29007
29289
  return c.json({ configured: isRemoteAuthConfigured() });
29008
29290
  });
29009
- app71.post("/api/remote-auth/set-password", async (c) => {
29291
+ app72.post("/api/remote-auth/set-password", async (c) => {
29010
29292
  let body;
29011
29293
  try {
29012
29294
  body = await c.req.json();
@@ -29045,10 +29327,10 @@ app71.post("/api/remote-auth/set-password", async (c) => {
29045
29327
  return c.json({ error: "Failed to save password" }, 500);
29046
29328
  }
29047
29329
  });
29048
- app71.route("/api/_client-error", client_error_default);
29330
+ app72.route("/api/_client-error", client_error_default);
29049
29331
  console.log("[client-error-route] mounted");
29050
29332
  var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
29051
- app71.use("*", async (c, next) => {
29333
+ app72.use("*", async (c, next) => {
29052
29334
  const host = (c.req.header("host") ?? "").split(":")[0];
29053
29335
  const path = c.req.path;
29054
29336
  if (path === "/favicon.ico" || path.startsWith("/assets/") || path.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
@@ -29092,30 +29374,30 @@ app71.use("*", async (c, next) => {
29092
29374
  }
29093
29375
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path }), 200);
29094
29376
  });
29095
- app71.route("/api/health", health_default);
29096
- app71.route("/api/chat", chatRoutes);
29097
- app71.route("/api/whatsapp", whatsapp_default);
29098
- app71.route("/api/storage", storage_broker_default);
29099
- app71.route("/api/whatsapp-reader", whatsapp_reader_default);
29100
- app71.route("/api/session-reader", session_reader_default);
29101
- app71.route("/api/operator-conversations", operator_conversations_default);
29102
- app71.route("/api/public-reader", public_reader_default);
29103
- app71.route("/api/webchat", createWebchatRoutes({
29377
+ app72.route("/api/health", health_default);
29378
+ app72.route("/api/chat", chatRoutes);
29379
+ app72.route("/api/whatsapp", whatsapp_default);
29380
+ app72.route("/api/storage", storage_broker_default);
29381
+ app72.route("/api/whatsapp-reader", whatsapp_reader_default);
29382
+ app72.route("/api/session-reader", session_reader_default);
29383
+ app72.route("/api/operator-conversations", operator_conversations_default);
29384
+ app72.route("/api/public-reader", public_reader_default);
29385
+ app72.route("/api/webchat", createWebchatRoutes({
29104
29386
  handleInbound: (input) => webchatGateway.handleInbound(input),
29105
29387
  // Task 940 — the permission relay's pointer read + verdict write.
29106
29388
  pendingPromptFor: (key) => webchatGateway.pendingPromptFor(key),
29107
29389
  deliveryFailureFor: (key) => webchatGateway.deliveryFailureFor(key),
29108
29390
  resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
29109
29391
  }));
29110
- app71.route("/api/webchat/greeting", webchat_greeting_default);
29111
- app71.route("/api/telegram", telegram_default);
29112
- app71.route("/api/quickbooks", quickbooks_default);
29113
- app71.route("/api/onboarding", onboarding_default);
29114
- app71.route("/api/admin", admin_default);
29115
- app71.route("/api/access", access_default);
29116
- app71.route("/api/session", session_default2);
29117
- app71.route("/api/calendar", calendar_public_default);
29118
- app71.route("/api/portal", portal_fetch_default);
29392
+ app72.route("/api/webchat/greeting", webchat_greeting_default);
29393
+ app72.route("/api/telegram", telegram_default);
29394
+ app72.route("/api/quickbooks", quickbooks_default);
29395
+ app72.route("/api/onboarding", onboarding_default);
29396
+ app72.route("/api/admin", admin_default);
29397
+ app72.route("/api/access", access_default);
29398
+ app72.route("/api/session", session_default2);
29399
+ app72.route("/api/calendar", calendar_public_default);
29400
+ app72.route("/api/portal", portal_fetch_default);
29119
29401
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
29120
29402
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
29121
29403
  var IMAGE_MIME = {
@@ -29127,7 +29409,7 @@ var IMAGE_MIME = {
29127
29409
  ".svg": "image/svg+xml",
29128
29410
  ".ico": "image/x-icon"
29129
29411
  };
29130
- app71.get("/agent-assets/:slug/:filename", (c) => {
29412
+ app72.get("/agent-assets/:slug/:filename", (c) => {
29131
29413
  const slug = c.req.param("slug");
29132
29414
  const filename = c.req.param("filename");
29133
29415
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -29149,20 +29431,20 @@ app71.get("/agent-assets/:slug/:filename", (c) => {
29149
29431
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
29150
29432
  return c.text("Forbidden", 403);
29151
29433
  }
29152
- if (!existsSync47(filePath)) {
29434
+ if (!existsSync48(filePath)) {
29153
29435
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
29154
29436
  return c.text("Not found", 404);
29155
29437
  }
29156
29438
  const ext = "." + filename.split(".").pop()?.toLowerCase();
29157
29439
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
29158
29440
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
29159
- const body = readFileSync43(filePath);
29441
+ const body = readFileSync44(filePath);
29160
29442
  return c.body(body, 200, {
29161
29443
  "Content-Type": contentType,
29162
29444
  "Cache-Control": "public, max-age=3600"
29163
29445
  });
29164
29446
  });
29165
- app71.get("/generated/:filename", (c) => {
29447
+ app72.get("/generated/:filename", (c) => {
29166
29448
  const filename = c.req.param("filename");
29167
29449
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
29168
29450
  console.error(`[generated] serve file=${filename} status=403`);
@@ -29179,29 +29461,29 @@ app71.get("/generated/:filename", (c) => {
29179
29461
  console.error(`[generated] serve file=${filename} status=403`);
29180
29462
  return c.text("Forbidden", 403);
29181
29463
  }
29182
- if (!existsSync47(filePath)) {
29464
+ if (!existsSync48(filePath)) {
29183
29465
  console.error(`[generated] serve file=${filename} status=404`);
29184
29466
  return c.text("Not found", 404);
29185
29467
  }
29186
29468
  const ext = "." + filename.split(".").pop()?.toLowerCase();
29187
29469
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
29188
29470
  console.log(`[generated] serve file=${filename} status=200`);
29189
- const body = readFileSync43(filePath);
29471
+ const body = readFileSync44(filePath);
29190
29472
  return c.body(body, 200, {
29191
29473
  "Content-Type": contentType,
29192
29474
  "Cache-Control": "public, max-age=86400"
29193
29475
  });
29194
29476
  });
29195
- app71.route("/sites", sites_default);
29196
- app71.route("/listings", listings_default);
29197
- app71.route("/v", visitor_event_default);
29198
- app71.route("/v", visitor_consent_default);
29477
+ app72.route("/sites", sites_default);
29478
+ app72.route("/listings", listings_default);
29479
+ app72.route("/v", visitor_event_default);
29480
+ app72.route("/v", visitor_consent_default);
29199
29481
  var htmlCache = /* @__PURE__ */ new Map();
29200
29482
  var brandLogoPath = "/brand/maxy-monochrome.png";
29201
29483
  var brandIconPath = "/brand/maxy-monochrome.png";
29202
- if (BRAND_JSON_PATH && existsSync47(BRAND_JSON_PATH)) {
29484
+ if (BRAND_JSON_PATH && existsSync48(BRAND_JSON_PATH)) {
29203
29485
  try {
29204
- const fullBrand = JSON.parse(readFileSync43(BRAND_JSON_PATH, "utf-8"));
29486
+ const fullBrand = JSON.parse(readFileSync44(BRAND_JSON_PATH, "utf-8"));
29205
29487
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
29206
29488
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
29207
29489
  } catch {
@@ -29228,11 +29510,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
29228
29510
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
29229
29511
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
29230
29512
  var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
29231
- (p) => existsSync47(resolve42(process.cwd(), "public", p.replace(/^\//, "")))
29513
+ (p) => existsSync48(resolve42(process.cwd(), "public", p.replace(/^\//, "")))
29232
29514
  );
29233
29515
  var SW_SOURCE = (() => {
29234
29516
  try {
29235
- return readFileSync43(resolve42(process.cwd(), "public", "sw.js"), "utf-8");
29517
+ return readFileSync44(resolve42(process.cwd(), "public", "sw.js"), "utf-8");
29236
29518
  } catch {
29237
29519
  return null;
29238
29520
  }
@@ -29240,9 +29522,9 @@ var SW_SOURCE = (() => {
29240
29522
  function readInstalledVersion() {
29241
29523
  try {
29242
29524
  if (!PLATFORM_ROOT6) return "unknown";
29243
- const versionFile = join49(PLATFORM_ROOT6, "config", `.${BRAND.hostname}-version`);
29244
- if (!existsSync47(versionFile)) return "unknown";
29245
- const content = readFileSync43(versionFile, "utf-8").trim();
29525
+ const versionFile = join50(PLATFORM_ROOT6, "config", `.${BRAND.hostname}-version`);
29526
+ if (!existsSync48(versionFile)) return "unknown";
29527
+ const content = readFileSync44(versionFile, "utf-8").trim();
29246
29528
  return content || "unknown";
29247
29529
  } catch {
29248
29530
  return "unknown";
@@ -29253,7 +29535,7 @@ var versionScript = `<script>window.__MAXY_VERSION__=${JSON.stringify(INSTALLED_
29253
29535
  function cachedHtml(file) {
29254
29536
  let html = htmlCache.get(file);
29255
29537
  if (!html) {
29256
- html = readFileSync43(resolve42(process.cwd(), "public", file), "utf-8");
29538
+ html = readFileSync44(resolve42(process.cwd(), "public", file), "utf-8");
29257
29539
  const productNameEsc = escapeHtml(BRAND.productName);
29258
29540
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
29259
29541
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -29270,13 +29552,13 @@ ${clientErrorReporterScript}
29270
29552
  return html;
29271
29553
  }
29272
29554
  function loadBrandingCache(agentSlug) {
29273
- const configDir = join49(homedir4(), BRAND.configDir);
29555
+ const configDir = join50(homedir4(), BRAND.configDir);
29274
29556
  try {
29275
29557
  const accountId = getDefaultAccountId();
29276
29558
  if (!accountId) return null;
29277
- const cachePath = join49(configDir, "branding-cache", accountId, `${agentSlug}.json`);
29278
- if (!existsSync47(cachePath)) return null;
29279
- return JSON.parse(readFileSync43(cachePath, "utf-8"));
29559
+ const cachePath = join50(configDir, "branding-cache", accountId, `${agentSlug}.json`);
29560
+ if (!existsSync48(cachePath)) return null;
29561
+ return JSON.parse(readFileSync44(cachePath, "utf-8"));
29280
29562
  } catch {
29281
29563
  return null;
29282
29564
  }
@@ -29320,7 +29602,7 @@ function brandedPublicHtml(agentSlug) {
29320
29602
  function agentUnavailableHtml() {
29321
29603
  return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
29322
29604
  }
29323
- app71.get("/", (c) => {
29605
+ app72.get("/", (c) => {
29324
29606
  const host = (c.req.header("host") ?? "").split(":")[0];
29325
29607
  const klass = classifyHost(host, getOperatorDomains(), isPublicHost);
29326
29608
  if (klass === "operator") {
@@ -29342,12 +29624,12 @@ app71.get("/", (c) => {
29342
29624
  console.log(`[host-class] host=${host} class=admin served=index.html`);
29343
29625
  return c.html(cachedHtml("index.html"));
29344
29626
  });
29345
- app71.get("/public", (c) => {
29627
+ app72.get("/public", (c) => {
29346
29628
  const host = (c.req.header("host") ?? "").split(":")[0];
29347
29629
  if (isPublicHost(host)) return c.text("Not found", 404);
29348
29630
  return c.html(cachedHtml("public.html"));
29349
29631
  });
29350
- app71.get("/public-chat", (c) => {
29632
+ app72.get("/public-chat", (c) => {
29351
29633
  const host = (c.req.header("host") ?? "").split(":")[0];
29352
29634
  if (isPublicHost(host)) return c.text("Not found", 404);
29353
29635
  return c.html(cachedHtml("public.html"));
@@ -29366,12 +29648,12 @@ async function logViewerFetch(c, next) {
29366
29648
  duration_ms: Date.now() - start
29367
29649
  });
29368
29650
  }
29369
- app71.use("/vnc-viewer.html", logViewerFetch);
29370
- app71.use("/vnc-popout.html", logViewerFetch);
29371
- app71.get("/vnc-popout.html", (c) => {
29651
+ app72.use("/vnc-viewer.html", logViewerFetch);
29652
+ app72.use("/vnc-popout.html", logViewerFetch);
29653
+ app72.get("/vnc-popout.html", (c) => {
29372
29654
  let html = htmlCache.get("vnc-popout.html");
29373
29655
  if (!html) {
29374
- html = readFileSync43(resolve42(process.cwd(), "public", "vnc-popout.html"), "utf-8");
29656
+ html = readFileSync44(resolve42(process.cwd(), "public", "vnc-popout.html"), "utf-8");
29375
29657
  const name = escapeHtml(BRAND.productName);
29376
29658
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
29377
29659
  html = html.replace("</head>", ` ${brandScript}
@@ -29381,7 +29663,7 @@ app71.get("/vnc-popout.html", (c) => {
29381
29663
  }
29382
29664
  return c.html(html);
29383
29665
  });
29384
- app71.post("/api/vnc/client-event", async (c) => {
29666
+ app72.post("/api/vnc/client-event", async (c) => {
29385
29667
  let body;
29386
29668
  try {
29387
29669
  body = await c.req.json();
@@ -29402,11 +29684,11 @@ app71.post("/api/vnc/client-event", async (c) => {
29402
29684
  });
29403
29685
  return c.json({ ok: true });
29404
29686
  });
29405
- app71.get("/g/:slug", (c) => {
29687
+ app72.get("/g/:slug", (c) => {
29406
29688
  return c.html(brandedPublicHtml(resolveDefaultSlug() ?? void 0));
29407
29689
  });
29408
29690
  for (const pwa of PWA_SURFACES) {
29409
- app71.get(pwa.manifestPath, (c) => {
29691
+ app72.get(pwa.manifestPath, (c) => {
29410
29692
  const manifest = buildManifest(pwa, {
29411
29693
  productName: BRAND.productName,
29412
29694
  appIcon192: brandAppIcon192Path,
@@ -29422,7 +29704,7 @@ for (const pwa of PWA_SURFACES) {
29422
29704
  return c.body(JSON.stringify(manifest));
29423
29705
  });
29424
29706
  }
29425
- app71.get("/sw.js", (c) => {
29707
+ app72.get("/sw.js", (c) => {
29426
29708
  if (SW_SOURCE == null) {
29427
29709
  console.error("[pwa] op=sw status=500 reason=sw-source-missing");
29428
29710
  return c.text("Service worker unavailable", 500);
@@ -29431,12 +29713,12 @@ app71.get("/sw.js", (c) => {
29431
29713
  console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
29432
29714
  return c.body(SW_SOURCE);
29433
29715
  });
29434
- app71.get("/graph", (c) => {
29716
+ app72.get("/graph", (c) => {
29435
29717
  const host = (c.req.header("host") ?? "").split(":")[0];
29436
29718
  if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
29437
29719
  return c.html(cachedHtml("graph.html"));
29438
29720
  });
29439
- app71.get("/chat", (c) => {
29721
+ app72.get("/chat", (c) => {
29440
29722
  const host = (c.req.header("host") ?? "").split(":")[0];
29441
29723
  if (isOperatorHost(host, getOperatorDomains())) {
29442
29724
  console.log(`[host-class] host=${host} class=operator served=operator.html`);
@@ -29445,47 +29727,47 @@ app71.get("/chat", (c) => {
29445
29727
  if (isPublicHost(host)) return c.text("Not found", 404);
29446
29728
  return c.html(cachedHtml("chat.html"));
29447
29729
  });
29448
- app71.get("/data", (c) => {
29730
+ app72.get("/data", (c) => {
29449
29731
  const host = (c.req.header("host") ?? "").split(":")[0];
29450
29732
  if (isPublicHost(host)) return c.text("Not found", 404);
29451
29733
  return c.html(cachedHtml("data.html"));
29452
29734
  });
29453
- app71.get("/tasks", (c) => {
29735
+ app72.get("/tasks", (c) => {
29454
29736
  const host = (c.req.header("host") ?? "").split(":")[0];
29455
29737
  if (isPublicHost(host)) return c.text("Not found", 404);
29456
29738
  return c.html(cachedHtml("tasks.html"));
29457
29739
  });
29458
- app71.get("/activity", (c) => {
29740
+ app72.get("/activity", (c) => {
29459
29741
  const host = (c.req.header("host") ?? "").split(":")[0];
29460
29742
  if (isPublicHost(host)) return c.text("Not found", 404);
29461
29743
  return c.html(cachedHtml("activity.html"));
29462
29744
  });
29463
- app71.get("/calendar", (c) => {
29745
+ app72.get("/calendar", (c) => {
29464
29746
  const host = (c.req.header("host") ?? "").split(":")[0];
29465
29747
  if (isPublicHost(host)) return c.text("Not found", 404);
29466
29748
  return c.html(cachedHtml("calendar.html"));
29467
29749
  });
29468
- app71.get("/routines", (c) => {
29750
+ app72.get("/routines", (c) => {
29469
29751
  const host = (c.req.header("host") ?? "").split(":")[0];
29470
29752
  if (isPublicHost(host)) return c.text("Not found", 404);
29471
29753
  return c.html(cachedHtml("routines.html"));
29472
29754
  });
29473
- app71.get("/skills", (c) => {
29755
+ app72.get("/skills", (c) => {
29474
29756
  const host = (c.req.header("host") ?? "").split(":")[0];
29475
29757
  if (isPublicHost(host)) return c.text("Not found", 404);
29476
29758
  return c.html(cachedHtml("skills.html"));
29477
29759
  });
29478
- app71.get("/agents", (c) => {
29760
+ app72.get("/agents", (c) => {
29479
29761
  const host = (c.req.header("host") ?? "").split(":")[0];
29480
29762
  if (isPublicHost(host)) return c.text("Not found", 404);
29481
29763
  return c.html(cachedHtml("agents.html"));
29482
29764
  });
29483
- app71.get("/browser", (c) => {
29765
+ app72.get("/browser", (c) => {
29484
29766
  const host = (c.req.header("host") ?? "").split(":")[0];
29485
29767
  if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
29486
29768
  return c.html(cachedHtml("browser.html"));
29487
29769
  });
29488
- app71.get("/:slug", async (c, next) => {
29770
+ app72.get("/:slug", async (c, next) => {
29489
29771
  const slug = c.req.param("slug");
29490
29772
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
29491
29773
  const account = resolveAccount();
@@ -29500,13 +29782,13 @@ app71.get("/:slug", async (c, next) => {
29500
29782
  await next();
29501
29783
  });
29502
29784
  if (brandFaviconPath !== "/favicon.ico") {
29503
- app71.get("/favicon.ico", (c) => {
29785
+ app72.get("/favicon.ico", (c) => {
29504
29786
  c.header("Cache-Control", "public, max-age=300");
29505
29787
  return c.redirect(brandFaviconPath, 302);
29506
29788
  });
29507
29789
  }
29508
- mountStaticFiles(app71, "./public");
29509
- app71.all("*", (c) => {
29790
+ mountStaticFiles(app72, "./public");
29791
+ app72.all("*", (c) => {
29510
29792
  const host = (c.req.header("host") ?? "").split(":")[0];
29511
29793
  const path = c.req.path;
29512
29794
  if (isPublicHost(host)) {
@@ -29520,13 +29802,13 @@ app71.all("*", (c) => {
29520
29802
  });
29521
29803
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
29522
29804
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
29523
- var httpServer = serve({ fetch: app71.fetch, port, hostname });
29805
+ var httpServer = serve({ fetch: app72.fetch, port, hostname });
29524
29806
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29525
29807
  {
29526
- const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
29808
+ const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
29527
29809
  const reconcileScript = resolve42(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
29528
29810
  const runReconcile = (ctx) => {
29529
- if (!existsSync47(reconcileScript)) return;
29811
+ if (!existsSync48(reconcileScript)) return;
29530
29812
  try {
29531
29813
  const child = spawn3(process.execPath, [reconcileScript], {
29532
29814
  env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
@@ -29546,7 +29828,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29546
29828
  detached: true,
29547
29829
  run: runReconcile
29548
29830
  });
29549
- const reconcileStatePath = join49(MAXY_DIR, "booking-reconcile-state.json");
29831
+ const reconcileStatePath = join50(MAXY_DIR, "booking-reconcile-state.json");
29550
29832
  registerLoop({
29551
29833
  name: "calendar-reconcile-liveness",
29552
29834
  intervalMs: 6e5,
@@ -29564,7 +29846,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29564
29846
  }
29565
29847
  {
29566
29848
  const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
29567
- const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
29849
+ const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
29568
29850
  const dreamSweepScript = resolve42(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
29569
29851
  registerLoop({
29570
29852
  name: "dream-cycle-sweep",
@@ -29572,7 +29854,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29572
29854
  firstRunDelayMs: 3e4,
29573
29855
  detached: true,
29574
29856
  run: (ctx) => {
29575
- if (!existsSync47(dreamSweepScript)) return;
29857
+ if (!existsSync48(dreamSweepScript)) return;
29576
29858
  try {
29577
29859
  const child = spawn3(process.execPath, [dreamSweepScript], {
29578
29860
  env: { ...process.env, PLATFORM_ROOT: dreamPlatformRoot },
@@ -29588,11 +29870,11 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29588
29870
  });
29589
29871
  }
29590
29872
  {
29591
- const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
29873
+ const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
29592
29874
  const outlookScript = resolve42(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
29593
29875
  const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
29594
29876
  const runOutlookComplete = (ctx) => {
29595
- if (!existsSync47(outlookScript)) return;
29877
+ if (!existsSync48(outlookScript)) return;
29596
29878
  try {
29597
29879
  const child = spawn3(process.execPath, [outlookScript], {
29598
29880
  env: { ...process.env, PLATFORM_ROOT: outlookPlatformRoot },
@@ -29614,19 +29896,19 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29614
29896
  });
29615
29897
  }
29616
29898
  {
29617
- const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
29899
+ const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
29618
29900
  const strandedAccountsDir = resolve42(auditRoot, "..", "data/accounts");
29619
29901
  const STRANDED_AUDIT_INTERVAL_MS = 3e5;
29620
29902
  const STRANDED_AGE_MS = 16 * 6e4;
29621
29903
  const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
29622
29904
  const runStrandedAudit = () => {
29623
29905
  try {
29624
- if (!existsSync47(strandedAccountsDir)) return;
29906
+ if (!existsSync48(strandedAccountsDir)) return;
29625
29907
  const now = Date.now();
29626
29908
  for (const name of readdirSync28(strandedAccountsDir)) {
29627
29909
  if (!STRANDED_UUID_RE.test(name)) continue;
29628
29910
  const pendingPath2 = resolve42(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
29629
- if (!existsSync47(pendingPath2)) continue;
29911
+ if (!existsSync48(pendingPath2)) continue;
29630
29912
  const ageMs2 = now - statSync24(pendingPath2).mtimeMs;
29631
29913
  if (ageMs2 > STRANDED_AGE_MS) {
29632
29914
  console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs2 / 1e3)}`);
@@ -29644,7 +29926,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29644
29926
  });
29645
29927
  }
29646
29928
  {
29647
- const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
29929
+ const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
29648
29930
  const googleAccountsDir = resolve42(googleRoot, "..", "data/accounts");
29649
29931
  const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
29650
29932
  const runGooglePendingAuditSafe = () => {
@@ -29663,7 +29945,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29663
29945
  const googleAuditScript = resolve42(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
29664
29946
  const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
29665
29947
  const runGoogleAccountAudit = (ctx) => {
29666
- if (!existsSync47(googleAuditScript)) return;
29948
+ if (!existsSync48(googleAuditScript)) return;
29667
29949
  try {
29668
29950
  const child = spawn3(process.execPath, [googleAuditScript], {
29669
29951
  env: { ...process.env, PLATFORM_ROOT: googleRoot },
@@ -29688,7 +29970,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29688
29970
  });
29689
29971
  }
29690
29972
  {
29691
- const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
29973
+ const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
29692
29974
  const STORAGE_AUDIT_INTERVAL_MS = 3e5;
29693
29975
  const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
29694
29976
  console.error(`[storage-audit] error="${err.message}"`);
@@ -29749,7 +30031,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29749
30031
  });
29750
30032
  }
29751
30033
  {
29752
- const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
30034
+ const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
29753
30035
  const publishScript = resolve42(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
29754
30036
  const PUBLISH_INTERVAL_MS = 3e5;
29755
30037
  const bookingAccounts = () => {
@@ -29761,7 +30043,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29761
30043
  }
29762
30044
  };
29763
30045
  const spawnPublish = (account, ctx) => {
29764
- if (!existsSync47(publishScript)) return;
30046
+ if (!existsSync48(publishScript)) return;
29765
30047
  try {
29766
30048
  const child = spawn3(process.execPath, [publishScript], {
29767
30049
  env: {
@@ -29783,8 +30065,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29783
30065
  const availPath = resolve42(account.accountDir, "calendar-availability.json");
29784
30066
  let hasBookingSite = false;
29785
30067
  try {
29786
- if (existsSync47(availPath)) {
29787
- const cfg = JSON.parse(readFileSync43(availPath, "utf-8"));
30068
+ if (existsSync48(availPath)) {
30069
+ const cfg = JSON.parse(readFileSync44(availPath, "utf-8"));
29788
30070
  hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
29789
30071
  }
29790
30072
  } catch {
@@ -29792,9 +30074,9 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29792
30074
  if (!hasBookingSite) return;
29793
30075
  const statePath = resolve42(account.accountDir, "state", "booking-availability", "last-publish.json");
29794
30076
  let lastSuccessAt = null;
29795
- if (existsSync47(statePath)) {
30077
+ if (existsSync48(statePath)) {
29796
30078
  try {
29797
- const rec = JSON.parse(readFileSync43(statePath, "utf-8"));
30079
+ const rec = JSON.parse(readFileSync44(statePath, "utf-8"));
29798
30080
  lastSuccessAt = rec.lastSuccessAt ?? null;
29799
30081
  } catch {
29800
30082
  console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
@@ -29826,12 +30108,12 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29826
30108
  startTimeEntryCensus(() => getSession());
29827
30109
  startLedgerCensus(() => getSession());
29828
30110
  {
29829
- const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..");
30111
+ const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
29830
30112
  const auditScript = resolve42(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
29831
30113
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
29832
30114
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
29833
30115
  const runConnectorAudit = (ctx) => {
29834
- if (!existsSync47(auditScript)) return;
30116
+ if (!existsSync48(auditScript)) return;
29835
30117
  try {
29836
30118
  const child = spawn3(process.execPath, [auditScript], {
29837
30119
  env: { ...process.env, PLATFORM_ROOT: auditPlatformRoot, LOG_DIR: auditLogDir },
@@ -29911,7 +30193,7 @@ for (const m of SUBAPP_MANIFEST) {
29911
30193
  }
29912
30194
  try {
29913
30195
  const registered = [];
29914
- for (const r of app71.routes ?? []) {
30196
+ for (const r of app72.routes ?? []) {
29915
30197
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
29916
30198
  if (AGENT_SLUG_PATTERN.test(r.path)) {
29917
30199
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -29951,11 +30233,11 @@ try {
29951
30233
  var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
29952
30234
  async function runAdminUserReconcileTick() {
29953
30235
  try {
29954
- if (!existsSync47(USERS_FILE)) {
30236
+ if (!existsSync48(USERS_FILE)) {
29955
30237
  console.error("[adminuser-self-heal] skip reason=no-users-file");
29956
30238
  return;
29957
30239
  }
29958
- const usersRaw = readFileSync43(USERS_FILE, "utf-8").trim();
30240
+ const usersRaw = readFileSync44(USERS_FILE, "utf-8").trim();
29959
30241
  if (!usersRaw) {
29960
30242
  console.error("[adminuser-self-heal] skip reason=empty-users-file");
29961
30243
  return;
@@ -30007,8 +30289,8 @@ registerLoop({
30007
30289
  });
30008
30290
  var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
30009
30291
  function countUsersRows() {
30010
- if (!existsSync47(USERS_FILE)) return 0;
30011
- const raw = readFileSync43(USERS_FILE, "utf-8").trim();
30292
+ if (!existsSync48(USERS_FILE)) return 0;
30293
+ const raw = readFileSync44(USERS_FILE, "utf-8").trim();
30012
30294
  if (!raw) return 0;
30013
30295
  const users = JSON.parse(raw);
30014
30296
  return users.filter((u) => typeof u.userId === "string").length;
@@ -30142,7 +30424,7 @@ if (bootAccountConfig?.whatsapp) {
30142
30424
  }
30143
30425
  init({
30144
30426
  configDir: configDirForWhatsApp,
30145
- platformRoot: resolve42(process.env.MAXY_PLATFORM_ROOT ?? join49(__dirname, "..")),
30427
+ platformRoot: resolve42(process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..")),
30146
30428
  accountConfig: bootAccountConfig,
30147
30429
  // Task 2074 — enrich with the sub-account name and fan out to every open
30148
30430
  // admin SSE stream. Fire-and-forget: the manager's persist path must not
@@ -30174,6 +30456,11 @@ init({
30174
30456
  );
30175
30457
  });
30176
30458
  },
30459
+ // Task 2110 — the manager is the only place that sees a media stanza start
30460
+ // downloading; the gateway first sees the media on the completed inbound,
30461
+ // which is the race window's close. Wiring it here keeps the manager free of
30462
+ // any gateway import, exactly as onMessage and onNotify are wired.
30463
+ onMediaStanza: (senderId, atMs) => waGateway.noteMediaStanza(senderId, atMs),
30177
30464
  onMessage: async (msg) => {
30178
30465
  if (isObserveAccount(msg.accountId)) {
30179
30466
  console.error(`[whatsapp:observe] op=BREACH account=${msg.accountId} sender=${msg.senderPhone} \u2014 dispatch reached the router from an observe socket`);