@rubytech/create-maxy-code 0.1.419 → 0.1.427

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 (334) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/neo4j/schema.cypher +13 -0
  3. package/payload/platform/package-lock.json +5158 -2922
  4. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  5. package/payload/platform/plugins/admin/PLUGIN.md +5 -1
  6. package/payload/platform/plugins/admin/hooks/__tests__/datetime-inject.test.sh +119 -0
  7. package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-compliance.test.sh +22 -0
  8. package/payload/platform/plugins/admin/hooks/datetime-inject.sh +115 -0
  9. package/payload/platform/plugins/admin/hooks/prompt-optimiser-compliance.sh +14 -1
  10. package/payload/platform/plugins/admin/mcp/dist/index.js +62 -4
  11. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  12. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +44 -0
  13. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -1
  14. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +96 -0
  15. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -1
  16. package/payload/platform/plugins/admin/skills/datetime/SKILL.md +9 -2
  17. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +16 -2
  18. package/payload/platform/plugins/docs/references/cross-account-authority.md +8 -0
  19. package/payload/platform/plugins/docs/references/internals.md +4 -0
  20. package/payload/platform/plugins/docs/references/memory-guide.md +2 -0
  21. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  22. package/payload/platform/plugins/email/PLUGIN.md +2 -2
  23. package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js +73 -4
  24. package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js.map +1 -1
  25. package/payload/platform/plugins/email/mcp/dist/__tests__/mailbox-resolve.test.js +42 -1
  26. package/payload/platform/plugins/email/mcp/dist/__tests__/mailbox-resolve.test.js.map +1 -1
  27. package/payload/platform/plugins/email/mcp/dist/index.js +1 -0
  28. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  29. package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +4 -0
  30. package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -1
  31. package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +5 -0
  32. package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -1
  33. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +25 -3
  34. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  35. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +67 -12
  36. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  37. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts +1 -0
  38. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.d.ts.map +1 -1
  39. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js +1 -0
  40. package/payload/platform/plugins/email/mcp/dist/tools/email-setup.js.map +1 -1
  41. package/payload/platform/plugins/email/references/email-reference.md +8 -0
  42. package/payload/platform/plugins/memory/PLUGIN.md +3 -0
  43. package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +30 -1
  44. package/payload/platform/plugins/memory/skills/preference-audit/SKILL.md +50 -0
  45. package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
  46. package/payload/platform/plugins/scheduling/mcp/dist/index.js +5 -2
  47. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  48. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js +50 -1
  49. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js.map +1 -1
  50. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.d.ts +2 -0
  51. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.d.ts.map +1 -0
  52. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.js +22 -0
  53. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.js.map +1 -0
  54. package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts +35 -0
  55. package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts.map +1 -1
  56. package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js +36 -0
  57. package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js.map +1 -1
  58. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.d.ts +11 -0
  59. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.d.ts.map +1 -0
  60. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.js +19 -0
  61. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.js.map +1 -0
  62. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +85 -3
  63. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
  64. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/standing-audit.test.js +32 -1
  65. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/standing-audit.test.js.map +1 -1
  66. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +14 -1
  67. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  68. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +94 -4
  69. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  70. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +23 -7
  71. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  72. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.d.ts +2 -0
  73. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.d.ts.map +1 -0
  74. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.js +30 -0
  75. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.js.map +1 -0
  76. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts +29 -0
  77. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -1
  78. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +47 -2
  79. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -1
  80. package/payload/platform/plugins/whatsapp/PLUGIN.md +5 -1
  81. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/caller-scope-headers.test.d.ts +2 -0
  82. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/caller-scope-headers.test.d.ts.map +1 -0
  83. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/caller-scope-headers.test.js +20 -0
  84. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/caller-scope-headers.test.js.map +1 -0
  85. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts +2 -0
  86. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts.map +1 -0
  87. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js +15 -0
  88. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js.map +1 -0
  89. package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts +1 -1
  90. package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts.map +1 -1
  91. package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js +5 -2
  92. package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js.map +1 -1
  93. package/payload/platform/plugins/whatsapp/mcp/dist/caller-scope-headers.d.ts +9 -0
  94. package/payload/platform/plugins/whatsapp/mcp/dist/caller-scope-headers.d.ts.map +1 -0
  95. package/payload/platform/plugins/whatsapp/mcp/dist/caller-scope-headers.js +18 -0
  96. package/payload/platform/plugins/whatsapp/mcp/dist/caller-scope-headers.js.map +1 -0
  97. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +17 -14
  98. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  99. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts +7 -0
  100. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts.map +1 -0
  101. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js +13 -0
  102. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js.map +1 -0
  103. package/payload/platform/plugins/work/PLUGIN.md +1 -1
  104. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-assigned-admin-env.test.d.ts +2 -0
  105. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-assigned-admin-env.test.d.ts.map +1 -0
  106. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-assigned-admin-env.test.js +72 -0
  107. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-assigned-admin-env.test.js.map +1 -0
  108. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-client-fields.test.d.ts +2 -0
  109. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-client-fields.test.d.ts.map +1 -0
  110. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-client-fields.test.js +67 -0
  111. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-client-fields.test.js.map +1 -0
  112. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts +2 -0
  113. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts.map +1 -0
  114. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js +66 -0
  115. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js.map +1 -0
  116. package/payload/platform/plugins/work/mcp/dist/index.js +3 -0
  117. package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
  118. package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts +6 -0
  119. package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -1
  120. package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +41 -1
  121. package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -1
  122. package/payload/platform/scripts/lib/provision-account-dir.sh +2 -1
  123. package/payload/platform/services/claude-session-manager/dist/account-admin-census.d.ts +39 -0
  124. package/payload/platform/services/claude-session-manager/dist/account-admin-census.d.ts.map +1 -0
  125. package/payload/platform/services/claude-session-manager/dist/account-admin-census.js +122 -0
  126. package/payload/platform/services/claude-session-manager/dist/account-admin-census.js.map +1 -0
  127. package/payload/platform/services/claude-session-manager/dist/account-timezone.d.ts +40 -0
  128. package/payload/platform/services/claude-session-manager/dist/account-timezone.d.ts.map +1 -0
  129. package/payload/platform/services/claude-session-manager/dist/account-timezone.js +126 -0
  130. package/payload/platform/services/claude-session-manager/dist/account-timezone.js.map +1 -0
  131. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  132. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  133. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  134. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  135. package/payload/platform/services/claude-session-manager/dist/http-server.js +53 -2
  136. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  137. package/payload/platform/services/claude-session-manager/dist/index.js +52 -0
  138. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  139. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts +37 -0
  140. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts.map +1 -0
  141. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js +80 -0
  142. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js.map +1 -0
  143. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts +70 -0
  144. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts.map +1 -0
  145. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js +129 -0
  146. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js.map +1 -0
  147. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +17 -0
  148. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  149. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +40 -0
  150. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  151. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
  152. package/payload/platform/services/telegram-channel/dist/instructions.js +37 -50
  153. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
  154. package/payload/platform/services/telegram-channel/dist/notification.d.ts +22 -0
  155. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  156. package/payload/platform/services/telegram-channel/dist/notification.js +22 -1
  157. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  158. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  159. package/payload/platform/services/webchat-channel/dist/instructions.js +44 -78
  160. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  161. package/payload/platform/services/webchat-channel/dist/notification.d.ts +22 -0
  162. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  163. package/payload/platform/services/webchat-channel/dist/notification.js +22 -1
  164. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  165. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +42 -38
  166. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  167. package/payload/platform/services/whatsapp-channel/dist/notification.js +66 -82
  168. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  169. package/payload/platform/templates/account.json +2 -1
  170. package/payload/platform/templates/agents/admin/IDENTITY.md +3 -2
  171. package/payload/platform/templates/specialists/agents/content-producer.md +4 -0
  172. package/payload/server/public/assets/{AdminLoginScreens-B1PkOF66.js → AdminLoginScreens-yAIhWcGc.js} +1 -1
  173. package/payload/server/public/assets/AdminShell-Bk1AFQ4D.js +2 -0
  174. package/payload/server/public/assets/{Checkbox-Bm9Er726.js → Checkbox-B_lfjsqd.js} +1 -1
  175. package/payload/server/public/assets/Transcript-D67OZnVM.js +2 -0
  176. package/payload/server/public/assets/admin-DuOhkvrZ.js +1 -0
  177. package/payload/server/public/assets/{arc-DyzjCWmw.js → arc-dVLpR-ec.js} +1 -1
  178. package/payload/server/public/assets/architecture-YZFGNWBL-CgeA3HPD.js +1 -0
  179. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-qWfElD16.js → architectureDiagram-Q4EWVU46-C6u_CVzb.js} +1 -1
  180. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DO2fFHC3.js → blockDiagram-DXYQGD6D-Cy8F5b8O.js} +1 -1
  181. package/payload/server/public/assets/browser-CCWufeXr.js +1 -0
  182. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Ca-q5gDe.js → c4Diagram-AHTNJAMY-DRXrlepj.js} +1 -1
  183. package/payload/server/public/assets/calendar-CNk1iPrA.js +1 -0
  184. package/payload/server/public/assets/channel-B04VBWf7.js +1 -0
  185. package/payload/server/public/assets/chat-7qB-dwBq.js +1 -0
  186. package/payload/server/public/assets/chevron-left-CpcDbHIz.js +1 -0
  187. package/payload/server/public/assets/{chunk-2KRD3SAO-CX2PxuDw.js → chunk-2KRD3SAO-fEyfhRLz.js} +1 -1
  188. package/payload/server/public/assets/{chunk-336JU56O-D8l6U4Ne.js → chunk-336JU56O-NUWO4y7q.js} +2 -2
  189. package/payload/server/public/assets/chunk-426QAEUC-DZkHcXRA.js +1 -0
  190. package/payload/server/public/assets/{chunk-4BX2VUAB-CG32Lkbh.js → chunk-4BX2VUAB-BnfiNR3l.js} +1 -1
  191. package/payload/server/public/assets/{chunk-4TB4RGXK-BnAQbO-v.js → chunk-4TB4RGXK-Bj_aJOz8.js} +1 -1
  192. package/payload/server/public/assets/{chunk-55IACEB6-ejqRH2M5.js → chunk-55IACEB6-aK0DNSKb.js} +1 -1
  193. package/payload/server/public/assets/{chunk-5FUZZQ4R-ClkicQ-T.js → chunk-5FUZZQ4R-DeqAkq5C.js} +1 -1
  194. package/payload/server/public/assets/{chunk-5PVQY5BW-Be-mE_rx.js → chunk-5PVQY5BW-SShlujAQ.js} +1 -1
  195. package/payload/server/public/assets/{chunk-67CJDMHE-DPsemed2.js → chunk-67CJDMHE-CZpzQ0DL.js} +1 -1
  196. package/payload/server/public/assets/{chunk-7N4EOEYR-BQRQJETl.js → chunk-7N4EOEYR-COnjO6jy.js} +1 -1
  197. package/payload/server/public/assets/{chunk-AA7GKIK3-B1wcAVCK.js → chunk-AA7GKIK3-C9V31L2z.js} +1 -1
  198. package/payload/server/public/assets/{chunk-BSJP7CBP-DIua053h.js → chunk-BSJP7CBP-DSwnSGe3.js} +1 -1
  199. package/payload/server/public/assets/{chunk-CIAEETIT-C9GHSCS9.js → chunk-CIAEETIT-CnRUTuta.js} +1 -1
  200. package/payload/server/public/assets/{chunk-EDXVE4YY-C1T3l1xj.js → chunk-EDXVE4YY-BexiL2LH.js} +1 -1
  201. package/payload/server/public/assets/{chunk-ENJZ2VHE-8kPcXEQC.js → chunk-ENJZ2VHE-aTklWdJQ.js} +1 -1
  202. package/payload/server/public/assets/{chunk-FMBD7UC4-Bhoeq9sD.js → chunk-FMBD7UC4-svcEBQzs.js} +1 -1
  203. package/payload/server/public/assets/{chunk-FOC6F5B3-BIBpJi8I.js → chunk-FOC6F5B3-CG7R7Xri.js} +1 -1
  204. package/payload/server/public/assets/{chunk-ICPOFSXX-DELXp6fj.js → chunk-ICPOFSXX-CGN7Z4x9.js} +2 -2
  205. package/payload/server/public/assets/{chunk-K5T4RW27-DdckXldP.js → chunk-K5T4RW27-CWBh78jw.js} +1 -1
  206. package/payload/server/public/assets/{chunk-KGLVRYIC-BJhXejKT.js → chunk-KGLVRYIC-Ctiq4q-5.js} +1 -1
  207. package/payload/server/public/assets/{chunk-LIHQZDEY-BSd8nyM2.js → chunk-LIHQZDEY-BII77Q7K.js} +1 -1
  208. package/payload/server/public/assets/{chunk-ORNJ4GCN-CBi55uxq.js → chunk-ORNJ4GCN-DT7R01H8.js} +1 -1
  209. package/payload/server/public/assets/{chunk-OYMX7WX6-Dfbzl8xh.js → chunk-OYMX7WX6-y-Jkbd4C.js} +1 -1
  210. package/payload/server/public/assets/chunk-QZHKN3VN-D9Mwgzti.js +1 -0
  211. package/payload/server/public/assets/{chunk-U2HBQHQK-BN1w8_Cq.js → chunk-U2HBQHQK-DzFo6KbQ.js} +1 -1
  212. package/payload/server/public/assets/{chunk-X2U36JSP-CrRay1NJ.js → chunk-X2U36JSP-CYSwgtLV.js} +1 -1
  213. package/payload/server/public/assets/{chunk-XPW4576I-C_ZE776O.js → chunk-XPW4576I-DHlM4tOn.js} +1 -1
  214. package/payload/server/public/assets/{chunk-YZCP3GAM-CUSlqWBY.js → chunk-YZCP3GAM-B20rTmLl.js} +1 -1
  215. package/payload/server/public/assets/{chunk-ZZ45TVLE-BdPUfSiD.js → chunk-ZZ45TVLE-C7hP4J8m.js} +1 -1
  216. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CIo_TzfF.js +1 -0
  217. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-SzQYvAUZ.js +1 -0
  218. package/payload/server/public/assets/clone-BVjcjsBA.js +1 -0
  219. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-D4E9htTz.js → cose-bilkent-S5V4N54A-nKjTv9sR.js} +1 -1
  220. package/payload/server/public/assets/{dagre-D1B5TQL1.js → dagre-Cx8LiiUZ.js} +1 -1
  221. package/payload/server/public/assets/{dagre-KV5264BT-ByuvYV7N.js → dagre-KV5264BT-BNJYTnls.js} +1 -1
  222. package/payload/server/public/assets/data-BnHcS27X.js +1 -0
  223. package/payload/server/public/assets/{diagram-5BDNPKRD-B-0KduTa.js → diagram-5BDNPKRD-CoAN5_f9.js} +1 -1
  224. package/payload/server/public/assets/{diagram-G4DWMVQ6-CSQmo3eB.js → diagram-G4DWMVQ6-pRmsN4_x.js} +1 -1
  225. package/payload/server/public/assets/{diagram-MMDJMWI5-DK0kWiB3.js → diagram-MMDJMWI5-CRoylyp4.js} +1 -1
  226. package/payload/server/public/assets/{diagram-TYMM5635-CYbK2reZ.js → diagram-TYMM5635-BT3ZtG5b.js} +1 -1
  227. package/payload/server/public/assets/{dist-CADCHk0t.js → dist-CRpLAnCP.js} +1 -1
  228. package/payload/server/public/assets/{erDiagram-SMLLAGMA-C-JQHvJF.js → erDiagram-SMLLAGMA-DSKws1ZV.js} +1 -1
  229. package/payload/server/public/assets/file-download-Hf_UILaA.js +1 -0
  230. package/payload/server/public/assets/{flatten-ROVzlo1u.js → flatten-jn8bZgcI.js} +1 -1
  231. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-OTOd2czg.js → flowDiagram-DWJPFMVM-C2L38ZE5.js} +1 -1
  232. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BWJ778ou.js → ganttDiagram-T4ZO3ILL-Bm2h-vjm.js} +1 -1
  233. package/payload/server/public/assets/gitGraph-7Q5UKJZL-DzQ18i0T.js +1 -0
  234. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CkNcyISe.js → gitGraphDiagram-UUTBAWPF-BoowbF7u.js} +1 -1
  235. package/payload/server/public/assets/graph-klLugHEK.js +51 -0
  236. package/payload/server/public/assets/{graph-labels-BFG4bjXQ.js → graph-labels-BLx-0vyT.js} +1 -1
  237. package/payload/server/public/assets/{graphlib-8z59Ng6C.js → graphlib-BvT2s_o1.js} +1 -1
  238. package/payload/server/public/assets/info-OMHHGYJF-CwsoaVzV.js +1 -0
  239. package/payload/server/public/assets/infoDiagram-42DDH7IO-ieNsH2bV.js +2 -0
  240. package/payload/server/public/assets/{isEmpty-DXf_sApt.js → isEmpty-Dkk6_L-B.js} +1 -1
  241. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-Cg7pRRGp.js → ishikawaDiagram-UXIWVN3A-nxcmj-IX.js} +1 -1
  242. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CZT_4r_Z.js → journeyDiagram-VCZTEJTY-q5SNIvmS.js} +1 -1
  243. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Dp1Vd5YM.js → kanban-definition-6JOO6SKY-BugG9Q1o.js} +1 -1
  244. package/payload/server/public/assets/{line-UPAcwY66.js → line-MZCQLR-q.js} +1 -1
  245. package/payload/server/public/assets/{linear-BeeahLKw.js → linear-DmQGNk-g.js} +1 -1
  246. package/payload/server/public/assets/{maximize-2-DkZ40lSl.js → maximize-2-CnNQnj_v.js} +1 -1
  247. package/payload/server/public/assets/{mermaid-parser.core-CLKle8df.js → mermaid-parser.core-DvoeqLxw.js} +2 -2
  248. package/payload/server/public/assets/{mermaid.core-CwRj5rL5.js → mermaid.core-BLaw7iG4.js} +3 -3
  249. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CNQUY9UJ.js → mindmap-definition-QFDTVHPH-DMkt10Vc.js} +1 -1
  250. package/payload/server/public/assets/operator-CRcySYv9.js +1 -0
  251. package/payload/server/public/assets/{ordinal-B2r5L1v4.js → ordinal-DuJYM7p1.js} +1 -1
  252. package/payload/server/public/assets/packet-4T2RLAQJ-DxB-bO4L.js +1 -0
  253. package/payload/server/public/assets/page-CVo3CWIY.js +1 -0
  254. package/payload/server/public/assets/page-DPTbZRwl.js +32 -0
  255. package/payload/server/public/assets/{pdf-render-m08Xm2Nm.js → pdf-render-riHufgrn.js} +1 -1
  256. package/payload/server/public/assets/pie-ZZUOXDRM-PSdKvB8-.js +1 -0
  257. package/payload/server/public/assets/{pieDiagram-DEJITSTG-Scnq58FX.js → pieDiagram-DEJITSTG-DRp4lD2B.js} +1 -1
  258. package/payload/server/public/assets/play-yEkd7ouW.js +1 -0
  259. package/payload/server/public/assets/public-Fi6ccYaE.js +1 -0
  260. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-wPfZo_vs.js → quadrantDiagram-34T5L4WZ-DkT63Dn4.js} +1 -1
  261. package/payload/server/public/assets/radar-PYXPWWZC-D8FqRiWq.js +1 -0
  262. package/payload/server/public/assets/{reduce-CFlDlJf5.js → reduce-COyotL2V.js} +1 -1
  263. package/payload/server/public/assets/{requirementDiagram-MS252O5E-yYkH12K2.js → requirementDiagram-MS252O5E-65GGtDkc.js} +1 -1
  264. package/payload/server/public/assets/rotate-ccw-xHksz9mr.js +1 -0
  265. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-fVWRBzzV.js → sankeyDiagram-XADWPNL6-D1vRwLWd.js} +1 -1
  266. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-Dbap3X6h.js → sequenceDiagram-FGHM5R23-Bp47ZSN2.js} +1 -1
  267. package/payload/server/public/assets/square-BZqaxg7D.js +1 -0
  268. package/payload/server/public/assets/{src-Bi0fLV9t.js → src-xN1oULc8.js} +1 -1
  269. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DkPLAqIh.js → stateDiagram-FHFEXIEX-BZHHcP_e.js} +1 -1
  270. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-qVtoGx56.js +1 -0
  271. package/payload/server/public/assets/tasks-COWmfts8.js +1 -0
  272. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DabKBJyd.js → timeline-definition-GMOUNBTQ-DZ1wUyj-.js} +1 -1
  273. package/payload/server/public/assets/treeView-SZITEDCU-i-w4rJmQ.js +1 -0
  274. package/payload/server/public/assets/treemap-W4RFUUIX-Dij6AlAj.js +1 -0
  275. package/payload/server/public/assets/useSubAccountSwitcher-DADvGnr1.js +9 -0
  276. package/payload/server/public/assets/useSubAccountSwitcher-ayajZz8T.css +1 -0
  277. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CaKT7c-c.js → vennDiagram-DHZGUBPP-CttBahsn.js} +1 -1
  278. package/payload/server/public/assets/wardley-RL74JXVD-BJhOQVqW.js +1 -0
  279. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BZ5TTSOL.js → wardleyDiagram-NUSXRM2D-Y5tWieZg.js} +1 -1
  280. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CLfeklJ9.js → xychartDiagram-5P7HB3ND-C78A9qbe.js} +1 -1
  281. package/payload/server/public/browser.html +7 -5
  282. package/payload/server/public/calendar.html +8 -6
  283. package/payload/server/public/chat.html +15 -12
  284. package/payload/server/public/data.html +11 -10
  285. package/payload/server/public/graph.html +12 -10
  286. package/payload/server/public/index.html +16 -14
  287. package/payload/server/public/operator.html +17 -14
  288. package/payload/server/public/public.html +15 -12
  289. package/payload/server/public/tasks.html +18 -0
  290. package/payload/server/server.js +775 -287
  291. package/payload/server/public/assets/AdminShell-D8wNArGQ.js +0 -2
  292. package/payload/server/public/assets/Transcript-CBIyQ8T5.js +0 -2
  293. package/payload/server/public/assets/admin-CWM9G_yA.js +0 -1
  294. package/payload/server/public/assets/architecture-YZFGNWBL-XiVs-N7Z.js +0 -1
  295. package/payload/server/public/assets/browser-D8QhNffV.js +0 -1
  296. package/payload/server/public/assets/calendar-qob5K4t_.js +0 -1
  297. package/payload/server/public/assets/channel-CYBkRz7E.js +0 -1
  298. package/payload/server/public/assets/chat-BNrxVUF6.js +0 -1
  299. package/payload/server/public/assets/chevron-left-BQThFVrd.js +0 -1
  300. package/payload/server/public/assets/chunk-426QAEUC-CPhOHriU.js +0 -1
  301. package/payload/server/public/assets/chunk-QZHKN3VN-CDnAAZVJ.js +0 -1
  302. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BdXUKVrS.js +0 -1
  303. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DAq-uJr_.js +0 -1
  304. package/payload/server/public/assets/clone-DQl28-jK.js +0 -1
  305. package/payload/server/public/assets/data-C-EqNbRi.js +0 -1
  306. package/payload/server/public/assets/gitGraph-7Q5UKJZL-BM-KaDZy.js +0 -1
  307. package/payload/server/public/assets/graph-DZ2HMpIB.js +0 -51
  308. package/payload/server/public/assets/info-OMHHGYJF-CtZajyAB.js +0 -1
  309. package/payload/server/public/assets/infoDiagram-42DDH7IO-C-_WYlmI.js +0 -2
  310. package/payload/server/public/assets/operator-FrXrSsBj.js +0 -1
  311. package/payload/server/public/assets/packet-4T2RLAQJ-T-SYedp7.js +0 -1
  312. package/payload/server/public/assets/page-CHWMI9Tm.js +0 -32
  313. package/payload/server/public/assets/page-CXKV01gT.js +0 -1
  314. package/payload/server/public/assets/pie-ZZUOXDRM-BWECFVVd.js +0 -1
  315. package/payload/server/public/assets/public-2p1pJ4WF.js +0 -1
  316. package/payload/server/public/assets/radar-PYXPWWZC-Dx0YUklX.js +0 -1
  317. package/payload/server/public/assets/rotate-ccw-D3o_bMBA.js +0 -1
  318. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DceWO1u6.js +0 -1
  319. package/payload/server/public/assets/treeView-SZITEDCU-C3197Rac.js +0 -1
  320. package/payload/server/public/assets/treemap-W4RFUUIX-BPrpx29h.js +0 -1
  321. package/payload/server/public/assets/useSubAccountSwitcher-8dLvF3Ux.js +0 -9
  322. package/payload/server/public/assets/useSubAccountSwitcher-BUvlETHN.css +0 -1
  323. package/payload/server/public/assets/wardley-RL74JXVD-DW4Jlbhw.js +0 -1
  324. /package/payload/server/public/assets/{_baseFor-BVuIxZJh.js → _baseFor-Bjj9It3Q.js} +0 -0
  325. /package/payload/server/public/assets/{admin-types-hioowVct.js → admin-types-ClHBm0ni.js} +0 -0
  326. /package/payload/server/public/assets/{array-B9UHiPd-.js → array-BrrxEo12.js} +0 -0
  327. /package/payload/server/public/assets/{chunk-CAM3fms7.js → chunk-Ck5AOu5v.js} +0 -0
  328. /package/payload/server/public/assets/{cytoscape.esm-D5_TRwB-.js → cytoscape.esm-CTWJxUxo.js} +0 -0
  329. /package/payload/server/public/assets/{defaultLocale-5eAKkKJC.js → defaultLocale-CFDbRV4k.js} +0 -0
  330. /package/payload/server/public/assets/{init-DlZdxViB.js → init-dXCzGgLP.js} +0 -0
  331. /package/payload/server/public/assets/{katex-Xo0SyWvP.js → katex-CHA1JFYJ.js} +0 -0
  332. /package/payload/server/public/assets/{path-6uRLdFF7.js → path-CCRpdXhK.js} +0 -0
  333. /package/payload/server/public/assets/{preload-helper-CvXuAEud.js → preload-helper-CH6UZRzu.js} +0 -0
  334. /package/payload/server/public/assets/{rough.esm-Djo4Abte.js → rough.esm-Bud4g4fh.js} +0 -0
@@ -3006,6 +3006,22 @@ async function resolvePersonByPhone(accountId, phone) {
3006
3006
  await session.close();
3007
3007
  }
3008
3008
  }
3009
+ async function ensureManagerPerson(accountId, phone) {
3010
+ const session = getSession2();
3011
+ const normalized = normalizeE164(phone) || phone;
3012
+ try {
3013
+ const result = await session.run(
3014
+ `MERGE (p:Person {accountId: $accountId, phone: $phone})
3015
+ ON CREATE SET p.source = 'wa-passive-manager'
3016
+ RETURN elementId(p) AS personId`,
3017
+ { accountId, phone: normalized }
3018
+ );
3019
+ if (result.records.length === 0) return null;
3020
+ return result.records[0].get("personId");
3021
+ } finally {
3022
+ await session.close();
3023
+ }
3024
+ }
3009
3025
  async function enrolPerson(input) {
3010
3026
  const session = getSession2();
3011
3027
  const sliceToken = randomUUID2();
@@ -5894,8 +5910,8 @@ function webchatTurnTimeoutMs() {
5894
5910
  return Number(process.env.WEBCHAT_TURN_TIMEOUT_MS ?? String(2 * 6e4));
5895
5911
  }
5896
5912
  function createChatRoutes(deps) {
5897
- const app57 = new Hono();
5898
- app57.post("/", async (c) => {
5913
+ const app61 = new Hono();
5914
+ app61.post("/", async (c) => {
5899
5915
  console.log(`[chat-route] entered route=public method=POST`);
5900
5916
  const contentType = c.req.header("content-type") ?? "";
5901
5917
  const account = resolveAccount();
@@ -6158,7 +6174,7 @@ ${result.result.text}` : result.result.text;
6158
6174
  }
6159
6175
  });
6160
6176
  });
6161
- return app57;
6177
+ return app61;
6162
6178
  }
6163
6179
 
6164
6180
  // app/lib/channel-pty-bridge/admin-session-id.ts
@@ -6173,14 +6189,28 @@ function adminSessionIdFor(accountId, senderId, personId) {
6173
6189
  return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
6174
6190
  }
6175
6191
 
6192
+ // app/lib/channel-pty-bridge/schedule-provenance.ts
6193
+ function parseScheduleProvenance(v) {
6194
+ if (!v || typeof v !== "object") return void 0;
6195
+ const o = v;
6196
+ if (typeof o.accountId !== "string" || typeof o.eventId !== "string" || typeof o.dispatchedAt !== "string") {
6197
+ return void 0;
6198
+ }
6199
+ if (!Array.isArray(o.authoredBy)) return void 0;
6200
+ const authoredBy = o.authoredBy.filter((u) => typeof u === "string" && u.length > 0);
6201
+ const createdByTool = typeof o.createdByTool === "string" && o.createdByTool.length > 0 ? o.createdByTool : null;
6202
+ return { accountId: o.accountId, eventId: o.eventId, authoredBy, createdByTool, dispatchedAt: o.dispatchedAt };
6203
+ }
6204
+
6176
6205
  // server/routes/channel/schedule-inject.ts
6177
6206
  var TAG16 = "[schedule-inject]";
6207
+ var UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
6178
6208
  function isLoopbackAddr(addr) {
6179
6209
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
6180
6210
  }
6181
6211
  function createScheduleInjectRoutes(deps) {
6182
- const app57 = new Hono();
6183
- app57.post("/", async (c) => {
6212
+ const app61 = new Hono();
6213
+ app61.post("/", async (c) => {
6184
6214
  const env = c.env;
6185
6215
  const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
6186
6216
  if (!isLoopbackAddr(remoteAddr)) {
@@ -6206,6 +6236,8 @@ function createScheduleInjectRoutes(deps) {
6206
6236
  const destination = typeof body.destination === "string" ? body.destination : "";
6207
6237
  const prompt = typeof body.prompt === "string" ? body.prompt : "";
6208
6238
  const eventId = typeof body.eventId === "string" ? body.eventId : "";
6239
+ const scheduleProvenance = parseScheduleProvenance(body.scheduleProvenance);
6240
+ const overrideSessionId = typeof body.sessionId === "string" && UUID_V4.test(body.sessionId) ? body.sessionId : void 0;
6209
6241
  if (channel !== "whatsapp" && channel !== "telegram" || !destination || !prompt) {
6210
6242
  console.error(`${TAG16} reject reason=bad-request channel=${String(channel)} hasDestination=${Boolean(destination)} hasPrompt=${Boolean(prompt)}`);
6211
6243
  return c.json({ ok: false, error: "schedule-inject-bad-request" }, 400);
@@ -6224,7 +6256,7 @@ function createScheduleInjectRoutes(deps) {
6224
6256
  return c.json({ ok: false, error: "account-manager-unresolved" }, 403);
6225
6257
  }
6226
6258
  console.error(`${TAG16} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount2}`);
6227
- const sessionId2 = adminSessionIdFor(effectiveAccount2, destination);
6259
+ const sessionId2 = overrideSessionId ?? adminSessionIdFor(effectiveAccount2, destination);
6228
6260
  const reply2 = (text) => deps.sendWhatsAppText(houseAccountId, destination, text);
6229
6261
  try {
6230
6262
  await deps.waHandleInbound({
@@ -6241,6 +6273,8 @@ function createScheduleInjectRoutes(deps) {
6241
6273
  text: prompt,
6242
6274
  media: [],
6243
6275
  source: "schedule",
6276
+ scheduleProvenance,
6277
+ scheduledSessionId: overrideSessionId,
6244
6278
  reply: reply2
6245
6279
  });
6246
6280
  } catch (err) {
@@ -6258,7 +6292,7 @@ function createScheduleInjectRoutes(deps) {
6258
6292
  return c.json({ ok: false, error: "no-admin-bot-token" }, 400);
6259
6293
  }
6260
6294
  const chatId = Number(destination);
6261
- const sessionId = adminSessionIdFor(effectiveAccount, destination);
6295
+ const sessionId = overrideSessionId ?? adminSessionIdFor(effectiveAccount, destination);
6262
6296
  const reply = (text) => deps.sendTelegramText(botToken, chatId, text);
6263
6297
  try {
6264
6298
  await deps.tgHandleInbound({
@@ -6269,6 +6303,8 @@ function createScheduleInjectRoutes(deps) {
6269
6303
  personId: null,
6270
6304
  text: prompt,
6271
6305
  source: "schedule",
6306
+ scheduleProvenance,
6307
+ scheduledSessionId: overrideSessionId,
6272
6308
  reply
6273
6309
  });
6274
6310
  } catch (err) {
@@ -6278,7 +6314,7 @@ function createScheduleInjectRoutes(deps) {
6278
6314
  console.error(`${TAG16} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
6279
6315
  return c.json({ ok: true }, 200);
6280
6316
  });
6281
- return app57;
6317
+ return app61;
6282
6318
  }
6283
6319
 
6284
6320
  // server/index.ts
@@ -7288,9 +7324,124 @@ function reconcileCredsOnDisk(opts) {
7288
7324
  return entries;
7289
7325
  }
7290
7326
 
7327
+ // app/lib/whatsapp-reader/select-sessions.ts
7328
+ function isReaderChannelSession(role, channel) {
7329
+ return role === "admin" && (channel === "whatsapp" || channel === "telegram") || role === "public" && (channel === "whatsapp" || channel === "webchat");
7330
+ }
7331
+ function selectReaderChannelSessions(rows) {
7332
+ return rows.filter((r) => isReaderChannelSession(r.role, r.channel)).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((r) => ({
7333
+ sessionId: r.sessionId,
7334
+ projectDir: r.projectDir,
7335
+ title: r.title,
7336
+ senderId: r.senderId,
7337
+ startedAt: r.startedAt,
7338
+ channel: r.channel,
7339
+ role: r.role,
7340
+ operatorName: null,
7341
+ whatsappName: null,
7342
+ lastMessageAt: r.lastMessageAt,
7343
+ modelGated: r.modelGated,
7344
+ model: r.model,
7345
+ source: "session"
7346
+ }));
7347
+ }
7348
+ function conversationCounterpartyPhone(remoteJid, earliestSenderTelephone) {
7349
+ if (remoteJid.endsWith("@g.us")) return null;
7350
+ if (remoteJid.endsWith("@s.whatsapp.net")) return jidToE164(remoteJid) ?? earliestSenderTelephone;
7351
+ return earliestSenderTelephone;
7352
+ }
7353
+ function counterpartyBoundToSubAccount(managerPhones, counterpartyPhone) {
7354
+ return counterpartyPhone != null && managerPhones.some((mp) => phonesMatch(mp, counterpartyPhone));
7355
+ }
7356
+ function buildStoreConversationRows(accountId, summaries) {
7357
+ const rows = [];
7358
+ for (const [remoteJid, { first, last, firstNamedSender }] of summaries) {
7359
+ const scope = last.scope;
7360
+ const senderTelephone = conversationCounterpartyPhone(remoteJid, first.senderTelephone);
7361
+ rows.push({
7362
+ sessionId: "",
7363
+ projectDir: "",
7364
+ title: remoteJid,
7365
+ senderId: senderTelephone,
7366
+ startedAt: first.dateSent,
7367
+ channel: "whatsapp",
7368
+ role: scope === "admin" ? "admin" : "public",
7369
+ operatorName: null,
7370
+ whatsappName: firstNamedSender,
7371
+ lastMessageAt: last.dateSent,
7372
+ modelGated: false,
7373
+ model: null,
7374
+ source: "store",
7375
+ accountId,
7376
+ remoteJid,
7377
+ scope
7378
+ });
7379
+ }
7380
+ return rows.sort((a, b) => (b.lastMessageAt ?? "").localeCompare(a.lastMessageAt ?? ""));
7381
+ }
7382
+
7383
+ // app/lib/whatsapp/sub-account-recall.ts
7384
+ function resolveStoreProjection(params) {
7385
+ const { requestedAccountId, houseId, validAccountIds, houseAccountManagers } = params;
7386
+ const requestedIsValid = validAccountIds.includes(requestedAccountId);
7387
+ const isSubAccount = houseId !== null && validAccountIds.length > 1 && requestedIsValid && requestedAccountId !== houseId;
7388
+ if (!isSubAccount) return { storeAccountId: requestedAccountId, managerPhones: null };
7389
+ return {
7390
+ storeAccountId: houseId,
7391
+ managerPhones: managerPhonesForAccount(houseAccountManagers, requestedAccountId)
7392
+ };
7393
+ }
7394
+ function filterConversationsForSubAccount(conversations, managerPhones) {
7395
+ const out = /* @__PURE__ */ new Map();
7396
+ for (const [remoteJid, msgs] of conversations) {
7397
+ const counterparty = conversationCounterpartyPhone(remoteJid, msgs[0]?.senderTelephone ?? "");
7398
+ if (counterpartyBoundToSubAccount(managerPhones, counterparty)) out.set(remoteJid, msgs);
7399
+ }
7400
+ return out;
7401
+ }
7402
+
7403
+ // app/lib/whatsapp/authorize-recall-read.ts
7404
+ function authorizeRecallRead(input) {
7405
+ const { requestedAccountId } = input;
7406
+ const callerAccountId = input.callerAccountId && input.callerAccountId.length > 0 ? input.callerAccountId : null;
7407
+ const houseAdminScope = input.houseAdminScope && input.houseAdminScope.length > 0 ? input.houseAdminScope : null;
7408
+ if (houseAdminScope) return { authorized: true };
7409
+ if (callerAccountId && requestedAccountId === callerAccountId) return { authorized: true };
7410
+ return {
7411
+ authorized: false,
7412
+ reason: `Not authorized to read WhatsApp history for account "${requestedAccountId}". This session may only read its own account.`
7413
+ };
7414
+ }
7415
+
7291
7416
  // server/routes/whatsapp.ts
7292
7417
  var TAG21 = "[whatsapp:api]";
7293
7418
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
7419
+ function projectionFor(requestedAccountId) {
7420
+ const validAccounts = listValidAccounts();
7421
+ if (validAccounts.length <= 1) return { storeAccountId: requestedAccountId, managerPhones: null };
7422
+ let houseId = null;
7423
+ try {
7424
+ houseId = resolveHouseOrSoleAccountId(ACCOUNTS_DIR);
7425
+ } catch {
7426
+ houseId = null;
7427
+ }
7428
+ const houseAccount = houseId ? validAccounts.find((a) => a.accountId === houseId) : void 0;
7429
+ const houseAccountManagers = houseAccount ? readAccountManagers(houseAccount.accountDir) : {};
7430
+ return resolveStoreProjection({
7431
+ requestedAccountId,
7432
+ houseId,
7433
+ validAccountIds: validAccounts.map((a) => a.accountId),
7434
+ houseAccountManagers
7435
+ });
7436
+ }
7437
+ function callerScopeDenial(c, requestedAccountId) {
7438
+ const callerAccountId = c.req.header("x-maxy-caller-account")?.trim() || null;
7439
+ const houseAdminScope = c.req.header("x-maxy-house-admin-scope")?.trim() || null;
7440
+ const decision = authorizeRecallRead({ requestedAccountId, callerAccountId, houseAdminScope });
7441
+ if (decision.authorized) return null;
7442
+ console.error(`[whatsapp-read-tool] op=authz-deny requested=${requestedAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
7443
+ return c.json({ error: decision.reason }, 403);
7444
+ }
7294
7445
  var app2 = new Hono();
7295
7446
  app2.get("/status", (c) => {
7296
7447
  try {
@@ -7595,6 +7746,8 @@ app2.get("/activity", (c) => {
7595
7746
  app2.get("/conversations", (c) => {
7596
7747
  try {
7597
7748
  const accountId = validateAccountId(c.req.query("accountId") ?? null);
7749
+ const denial = callerScopeDenial(c, accountId);
7750
+ if (denial) return denial;
7598
7751
  const jids = listConversationJids(accountId);
7599
7752
  const conversations = jids.map((jid) => {
7600
7753
  const messages = getMessages(accountId, jid);
@@ -7617,6 +7770,8 @@ app2.get("/conversations", (c) => {
7617
7770
  app2.get("/messages", (c) => {
7618
7771
  try {
7619
7772
  const accountId = validateAccountId(c.req.query("accountId") ?? null);
7773
+ const denial = callerScopeDenial(c, accountId);
7774
+ if (denial) return denial;
7620
7775
  const jid = c.req.query("jid");
7621
7776
  if (!jid) {
7622
7777
  return c.json({ error: "Missing required parameter: jid" }, 400);
@@ -7638,9 +7793,14 @@ app2.get("/conversation-graph-state", async (c) => {
7638
7793
  const jid = c.req.query("jid");
7639
7794
  const accountIdQuery = c.req.query("accountId");
7640
7795
  const t0 = Date.now();
7796
+ const denial = callerScopeDenial(c, validateAccountId(accountIdQuery ?? null));
7797
+ if (denial) return denial;
7641
7798
  if (!directCacheKey && !jid) {
7642
- const accountId2 = validateAccountId(accountIdQuery ?? null);
7643
- const conversations = readAllConversations(accountId2);
7799
+ const requestedAccountId = validateAccountId(accountIdQuery ?? null);
7800
+ const projection2 = projectionFor(requestedAccountId);
7801
+ const managerPhonesBound = projection2.managerPhones?.length ?? 0;
7802
+ const allConversations = readAllConversations(projection2.storeAccountId);
7803
+ const conversations = projection2.managerPhones ? filterConversationsForSubAccount(allConversations, projection2.managerPhones) : allConversations;
7644
7804
  const rows2 = [];
7645
7805
  const nameCache = /* @__PURE__ */ new Map();
7646
7806
  let unresolvedSenders = 0;
@@ -7652,7 +7812,7 @@ app2.get("/conversation-graph-state", async (c) => {
7652
7812
  if (!nameCache.has(m.senderTelephone)) {
7653
7813
  const result = await session.run(
7654
7814
  `MATCH (p:Person {telephone: $telephone, accountId: $accountId}) RETURN p.givenName AS givenName, p.familyName AS familyName`,
7655
- { telephone: m.senderTelephone, accountId: accountId2 }
7815
+ { telephone: m.senderTelephone, accountId: projection2.storeAccountId }
7656
7816
  );
7657
7817
  const rec = result.records[0];
7658
7818
  const resolved = rec ? [rec.get("givenName"), rec.get("familyName")].filter(Boolean).join(" ") || null : null;
@@ -7674,8 +7834,8 @@ app2.get("/conversation-graph-state", async (c) => {
7674
7834
  if (session) await session.close();
7675
7835
  }
7676
7836
  const ms2 = Date.now() - t0;
7677
- console.error(`[mcp:whatsapp] tool=whatsapp-conversation-graph-state mode=all-senders accountId=${accountId2} rows=${rows2.length} unresolvedSenders=${unresolvedSenders} ms=${ms2}`);
7678
- return c.json({ accountId: accountId2, rows: rows2, unresolvedSenders, ms: ms2 });
7837
+ console.error(`[whatsapp-read-tool] op=query scope=${requestedAccountId} storeAccount=${projection2.storeAccountId} managerPhonesBound=${managerPhonesBound} resultCount=${rows2.length} unresolvedSenders=${unresolvedSenders} ms=${ms2}`);
7838
+ return c.json({ accountId: requestedAccountId, rows: rows2, unresolvedSenders, ms: ms2, managerPhonesBound });
7679
7839
  }
7680
7840
  let remoteJid;
7681
7841
  let accountId;
@@ -7690,7 +7850,13 @@ app2.get("/conversation-graph-state", async (c) => {
7690
7850
  accountId = validateAccountId(accountIdQuery ?? null);
7691
7851
  remoteJid = jid;
7692
7852
  cacheKey = isGroupJid(remoteJid) ? deriveCacheKey({ agentType: "public", accountId, senderPhone: "", isGroup: true, groupJid: remoteJid }) : deriveCacheKey({ agentType: "public", accountId, senderPhone: remoteJid.replace(/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i, "$1"), isGroup: false });
7693
- const messages = readConversation(accountId, remoteJid);
7853
+ const projection = projectionFor(accountId);
7854
+ const storeMessages = readConversation(projection.storeAccountId, remoteJid);
7855
+ const counterpartyBound = projection.managerPhones == null || counterpartyBoundToSubAccount(
7856
+ projection.managerPhones,
7857
+ conversationCounterpartyPhone(remoteJid, storeMessages[0]?.senderTelephone ?? "")
7858
+ );
7859
+ const messages = counterpartyBound ? storeMessages : [];
7694
7860
  const sessionId = messages[0]?.sessionId ?? null;
7695
7861
  const rows = messages.map((m) => ({
7696
7862
  messageId: m.messageId,
@@ -7700,12 +7866,12 @@ app2.get("/conversation-graph-state", async (c) => {
7700
7866
  body: m.body
7701
7867
  }));
7702
7868
  const ms = Date.now() - t0;
7703
- console.error(`[mcp:whatsapp] tool=whatsapp-conversation-graph-state cacheKey=${cacheKey} graphRows=${rows.length} sessionId=${sessionId ?? "null"} ms=${ms}`);
7869
+ console.error(`[whatsapp-read-tool] op=query mode=single scope=${accountId} storeAccount=${projection.storeAccountId} graphRows=${rows.length} sessionId=${sessionId ?? "null"} ms=${ms}`);
7704
7870
  return c.json({
7705
7871
  cacheKey,
7706
7872
  sessionId,
7707
7873
  graphRows: rows,
7708
- cypher: `on-disk read: ~/.<brand>/data/whatsapp-messages/${accountId}/${remoteJid}.jsonl`,
7874
+ cypher: `on-disk read: ~/.<brand>/data/whatsapp-messages/${projection.storeAccountId}/${remoteJid}.jsonl`,
7709
7875
  ms
7710
7876
  });
7711
7877
  } catch (err) {
@@ -7755,57 +7921,6 @@ import { basename as basename5, dirname as dirname4, isAbsolute, join as join14,
7755
7921
  // server/routes/admin/sidebar-sessions.ts
7756
7922
  import { readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
7757
7923
  import { basename as basename4, dirname as dirname3, join as join11, resolve as resolve11 } from "path";
7758
-
7759
- // app/lib/whatsapp-reader/select-sessions.ts
7760
- function isReaderChannelSession(role, channel) {
7761
- return role === "admin" && (channel === "whatsapp" || channel === "telegram") || role === "public" && (channel === "whatsapp" || channel === "webchat");
7762
- }
7763
- function selectReaderChannelSessions(rows) {
7764
- return rows.filter((r) => isReaderChannelSession(r.role, r.channel)).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((r) => ({
7765
- sessionId: r.sessionId,
7766
- projectDir: r.projectDir,
7767
- title: r.title,
7768
- senderId: r.senderId,
7769
- startedAt: r.startedAt,
7770
- channel: r.channel,
7771
- role: r.role,
7772
- operatorName: null,
7773
- whatsappName: null,
7774
- lastMessageAt: r.lastMessageAt,
7775
- modelGated: r.modelGated,
7776
- model: r.model,
7777
- source: "session"
7778
- }));
7779
- }
7780
- function buildStoreConversationRows(accountId, summaries) {
7781
- const rows = [];
7782
- for (const [remoteJid, { first, last, firstNamedSender }] of summaries) {
7783
- const scope = last.scope;
7784
- const isGroup = remoteJid.endsWith("@g.us");
7785
- const senderTelephone = isGroup ? null : remoteJid.endsWith("@s.whatsapp.net") ? jidToE164(remoteJid) ?? first.senderTelephone : first.senderTelephone;
7786
- rows.push({
7787
- sessionId: "",
7788
- projectDir: "",
7789
- title: remoteJid,
7790
- senderId: senderTelephone,
7791
- startedAt: first.dateSent,
7792
- channel: "whatsapp",
7793
- role: scope === "admin" ? "admin" : "public",
7794
- operatorName: null,
7795
- whatsappName: firstNamedSender,
7796
- lastMessageAt: last.dateSent,
7797
- modelGated: false,
7798
- model: null,
7799
- source: "store",
7800
- accountId,
7801
- remoteJid,
7802
- scope
7803
- });
7804
- }
7805
- return rows.sort((a, b) => (b.lastMessageAt ?? "").localeCompare(a.lastMessageAt ?? ""));
7806
- }
7807
-
7808
- // server/routes/admin/sidebar-sessions.ts
7809
7924
  var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
7810
7925
  function lastAssistantModel(body) {
7811
7926
  const lines = body.split("\n");
@@ -8279,7 +8394,7 @@ function unwrapChannel(text) {
8279
8394
  }
8280
8395
  return source ? { text: inner, source } : { text: inner };
8281
8396
  }
8282
- var COMPOSED_ADMIN_FRAME = /^## Context\n([\s\S]*)\n\n## Instruction\n[\s\S]*$/;
8397
+ var COMPOSED_ADMIN_FRAME = /^(?:## Standing rules\n[\s\S]*?\n\n)?## Context\n([\s\S]*)\n\n## Instruction\n[\s\S]*$/;
8283
8398
  var UPLOAD_ANNOTATION = /(?:\n\n)?\[The operator uploaded \d+ file\(s\): [\s\S]*\]$/;
8284
8399
  function rawFromComposedAdminFrame(text) {
8285
8400
  const context = text.match(COMPOSED_ADMIN_FRAME)?.[1];
@@ -8646,7 +8761,7 @@ app4.get("/conversations", requireAdminSession, async (c) => {
8646
8761
  managerPhonesBound = managerPhones.length;
8647
8762
  if (managerPhones.length > 0) {
8648
8763
  managedRows = buildStoreConversationRows(houseId, readConversationSummaries(houseId)).filter(
8649
- (r) => r.senderId != null && managerPhones.some((mp) => phonesMatch(mp, r.senderId))
8764
+ (r) => counterpartyBoundToSubAccount(managerPhones, r.senderId)
8650
8765
  );
8651
8766
  }
8652
8767
  }
@@ -9874,8 +9989,8 @@ async function reapplyLeversViaManager() {
9874
9989
  var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
9875
9990
  var sendSeq = 0;
9876
9991
  function createWebchatRoutes(deps) {
9877
- const app57 = new Hono();
9878
- app57.post("/send", requireAdminSession, async (c) => {
9992
+ const app61 = new Hono();
9993
+ app61.post("/send", requireAdminSession, async (c) => {
9879
9994
  let accountId;
9880
9995
  try {
9881
9996
  accountId = resolvePlatformAccountId();
@@ -10075,7 +10190,7 @@ ${note}` : note;
10075
10190
  if (turnTimer.unref) turnTimer.unref();
10076
10191
  return c.json({ ok: true });
10077
10192
  });
10078
- app57.post("/interrupt", requireAdminSession, async (c) => {
10193
+ app61.post("/interrupt", requireAdminSession, async (c) => {
10079
10194
  const cacheKey = c.get("cacheKey");
10080
10195
  const requesterUserId = (cacheKey ? getUserIdForSession(cacheKey) : void 0) ?? null;
10081
10196
  const primaryUserId = resolvePrimaryAdminUserId();
@@ -10103,7 +10218,7 @@ ${note}` : note;
10103
10218
  }
10104
10219
  return c.json({ ok: true, stopped: outcome.stopped, intId: outcome.intId, deadChild: outcome.deadChild }, 200);
10105
10220
  });
10106
- app57.post("/settings", requireAdminSession, async (c) => {
10221
+ app61.post("/settings", requireAdminSession, async (c) => {
10107
10222
  const body = await c.req.json().catch(() => null);
10108
10223
  const op = body?.op;
10109
10224
  const value = body?.value;
@@ -10143,7 +10258,7 @@ ${note}` : note;
10143
10258
  console.log(`[webchat:settings] op=${op} outcome=accepted value=${value} settingsApplied=${settingsApplied}`);
10144
10259
  return c.json({ ok: true, settingsApplied });
10145
10260
  });
10146
- app57.get("/session", requireAdminSession, async (c) => {
10261
+ app61.get("/session", requireAdminSession, async (c) => {
10147
10262
  let accountId;
10148
10263
  try {
10149
10264
  accountId = resolvePlatformAccountId();
@@ -10221,7 +10336,7 @@ ${note}` : note;
10221
10336
  const indicators = await fetchComposerIndicators(sessionId);
10222
10337
  return c.json({ sessionId, projectDir, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
10223
10338
  });
10224
- app57.post("/permission-verdict", requireAdminSession, async (c) => {
10339
+ app61.post("/permission-verdict", requireAdminSession, async (c) => {
10225
10340
  const body = await c.req.json().catch(() => null);
10226
10341
  const sessionId = body?.sessionId;
10227
10342
  const requestId = body?.request_id;
@@ -10238,7 +10353,7 @@ ${note}` : note;
10238
10353
  const ok = deps.resolvePermissionVerdict?.(`session:${sessionId}`, requestId, behavior) ?? false;
10239
10354
  return c.json({ ok });
10240
10355
  });
10241
- return app57;
10356
+ return app61;
10242
10357
  }
10243
10358
 
10244
10359
  // server/routes/webchat-greeting.ts
@@ -13275,18 +13390,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
13275
13390
  ]);
13276
13391
  var app20 = new Hono();
13277
13392
  app20.post("/", async (c) => {
13278
- const TAG39 = "[admin:events]";
13393
+ const TAG44 = "[admin:events]";
13279
13394
  let body;
13280
13395
  try {
13281
13396
  body = await c.req.json();
13282
13397
  } catch (err) {
13283
13398
  const detail = err instanceof Error ? err.message : String(err);
13284
- console.error(`${TAG39} reject reason=body-not-json detail=${detail}`);
13399
+ console.error(`${TAG44} reject reason=body-not-json detail=${detail}`);
13285
13400
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
13286
13401
  }
13287
13402
  const event = typeof body.event === "string" ? body.event : "";
13288
13403
  if (!ALLOWED_EVENTS.has(event)) {
13289
- console.error(`${TAG39} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
13404
+ console.error(`${TAG44} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
13290
13405
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
13291
13406
  }
13292
13407
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -19221,52 +19336,307 @@ app45.get("/booking-link", requireAdminSession, (c) => {
19221
19336
  });
19222
19337
  var calendar_default = app45;
19223
19338
 
19224
- // server/routes/admin/index.ts
19339
+ // app/lib/time-entry-format.ts
19340
+ function secondsBetween(startIso, endIso) {
19341
+ const s = Date.parse(startIso);
19342
+ const e = Date.parse(endIso);
19343
+ if (!Number.isFinite(s) || !Number.isFinite(e)) return 0;
19344
+ return Math.max(0, Math.round((e - s) / 1e3));
19345
+ }
19346
+ function toNum(v) {
19347
+ if (v == null) return 0;
19348
+ if (typeof v === "number") return v;
19349
+ if (typeof v === "object" && typeof v.toNumber === "function") {
19350
+ return v.toNumber();
19351
+ }
19352
+ const n = Number(v);
19353
+ return Number.isFinite(n) ? n : 0;
19354
+ }
19355
+
19356
+ // server/routes/admin/tasks-list.ts
19357
+ var TAG33 = "[task-timer]";
19358
+ var COMPLETED = /* @__PURE__ */ new Set(["completed"]);
19359
+ var HIDDEN_FROM_OPEN = /* @__PURE__ */ new Set(["completed", "cancelled"]);
19225
19360
  var app46 = new Hono();
19226
- app46.route("/session", session_default);
19227
- app46.route("/accounts", accounts_default);
19228
- app46.route("/logs", logs_default);
19229
- app46.route("/claude-info", claude_info_default);
19230
- app46.route("/attachment", attachment_default);
19231
- app46.route("/agents", agents_default);
19232
- app46.route("/sessions", sessions_default);
19233
- app46.route("/claude-sessions", claude_sessions_default);
19234
- app46.route("/log-ingest", log_ingest_default);
19235
- app46.route("/events", events_default);
19236
- app46.route("/files", files_default);
19237
- app46.route("/graph-search", graph_search_default);
19238
- app46.route("/graph-subgraph", graph_subgraph_default);
19239
- app46.route("/graph-delete", graph_delete_default);
19240
- app46.route("/graph-restore", graph_restore_default);
19241
- app46.route("/graph-labels-in-graph", graph_labels_in_graph_default);
19242
- app46.route("/graph-default-view", graph_default_view_default);
19243
- app46.route("/sidebar-artefacts", sidebar_artefacts_default);
19244
- app46.route("/sidebar-sessions", sidebar_sessions_default);
19245
- app46.route("/session-delete", session_delete_default);
19246
- app46.route("/session-stop", session_stop_default);
19247
- app46.route("/session-archive", session_archive_default);
19248
- app46.route("/session-rename", session_rename_default);
19249
- app46.route("/session-usage", session_usage_default);
19250
- app46.route("/browser", browser_default);
19251
- app46.route("/session-rc-spawn", session_rc_spawn_default);
19252
- app46.route("/session-reseat", session_reseat_default);
19253
- app46.route("/system-stats", system_stats_default);
19254
- app46.route("/health-brand", health_default2);
19255
- app46.route("/linkedin-ingest", linkedin_ingest_default);
19256
- app46.route("/post-turn-context", post_turn_context_default);
19257
- app46.route("/public-session-context", public_session_context_default);
19258
- app46.route("/public-session-exit", public_session_exit_default);
19259
- app46.route("/access-session-evict", access_session_evict_default);
19260
- app46.route("/enrol-person", enrol_person_default);
19261
- app46.route("/calendar", calendar_default);
19262
- var admin_default = app46;
19361
+ app46.get("/", requireAdminSession, async (c) => {
19362
+ const cacheKey = c.var.cacheKey;
19363
+ const accountId = getAccountIdForSession(cacheKey);
19364
+ if (!accountId) {
19365
+ console.error(`${TAG33} op=list auth-rejected reason="no account for session"`);
19366
+ return c.json({ error: "Account not found for session" }, 401);
19367
+ }
19368
+ const session = getSession();
19369
+ try {
19370
+ const result = await session.run(
19371
+ `MATCH (t:Task {accountId: $accountId})
19372
+ OPTIONAL MATCH (te:TimeEntry {taskId: t.taskId, accountId: $accountId})
19373
+ WHERE te.endedAt IS NULL
19374
+ WITH t, te ORDER BY te.startedAt
19375
+ WITH t, head(collect(te)) AS openTe
19376
+ RETURN t.taskId AS taskId, t.name AS name, t.clientName AS clientName,
19377
+ t.propertyAddress AS propertyAddress, t.fee AS fee, t.status AS status,
19378
+ coalesce(t.secondsLogged, 0) AS secondsLogged,
19379
+ openTe.startedAt AS runningStartedAt
19380
+ ORDER BY t.createdAt DESC`,
19381
+ { accountId }
19382
+ );
19383
+ const open = [];
19384
+ const completed = [];
19385
+ for (const rec of result.records) {
19386
+ const status = rec.get("status") ?? "pending";
19387
+ const runningStartedAt = rec.get("runningStartedAt") ?? null;
19388
+ const fee = rec.get("fee");
19389
+ const row = {
19390
+ taskId: rec.get("taskId"),
19391
+ name: rec.get("name") ?? "",
19392
+ clientName: rec.get("clientName") ?? null,
19393
+ propertyAddress: rec.get("propertyAddress") ?? null,
19394
+ fee: fee == null ? null : toNum(fee),
19395
+ status,
19396
+ secondsLogged: toNum(rec.get("secondsLogged")),
19397
+ running: runningStartedAt !== null,
19398
+ runningStartedAt
19399
+ };
19400
+ if (COMPLETED.has(status)) completed.push(row);
19401
+ else if (!HIDDEN_FROM_OPEN.has(status)) open.push(row);
19402
+ }
19403
+ console.log(`${TAG33} op=list account=${accountId} open=${open.length} completed=${completed.length}`);
19404
+ return c.json({ open, completed });
19405
+ } catch (err) {
19406
+ console.error(`${TAG33} op=list error="${err instanceof Error ? err.message : String(err)}"`);
19407
+ return c.json({ error: "tasks-list-failed" }, 500);
19408
+ } finally {
19409
+ await session.close();
19410
+ }
19411
+ });
19412
+ var tasks_list_default = app46;
19413
+
19414
+ // server/routes/admin/task-timer-start.ts
19415
+ import { randomUUID as randomUUID13 } from "crypto";
19416
+ import neo4j5 from "neo4j-driver";
19417
+ var TAG34 = "[task-timer]";
19418
+ var app47 = new Hono();
19419
+ app47.post("/", requireAdminSession, async (c) => {
19420
+ const cacheKey = c.var.cacheKey;
19421
+ const accountId = getAccountIdForSession(cacheKey);
19422
+ if (!accountId) return c.json({ error: "Account not found for session" }, 401);
19423
+ let body;
19424
+ try {
19425
+ body = await c.req.json();
19426
+ } catch {
19427
+ return c.json({ error: "invalid-json" }, 400);
19428
+ }
19429
+ const taskId = typeof body.taskId === "string" ? body.taskId : "";
19430
+ if (!taskId) return c.json({ error: "taskId required" }, 400);
19431
+ const now = (/* @__PURE__ */ new Date()).toISOString();
19432
+ const session = getSession();
19433
+ try {
19434
+ const target = await session.run(
19435
+ `MATCH (t:Task {taskId: $taskId, accountId: $accountId}) RETURN t.taskId AS taskId LIMIT 1`,
19436
+ { taskId, accountId }
19437
+ );
19438
+ if (target.records.length === 0) {
19439
+ console.error(`${TAG34} op=start taskId=${taskId} accountId=${accountId} result=task-not-found`);
19440
+ return c.json({ error: "task-not-found" }, 404);
19441
+ }
19442
+ const open = await session.run(
19443
+ `MATCH (te:TimeEntry {accountId: $accountId}) WHERE te.endedAt IS NULL
19444
+ RETURN te.entryId AS entryId, te.taskId AS taskId, te.startedAt AS startedAt
19445
+ ORDER BY te.startedAt LIMIT 1`,
19446
+ { accountId }
19447
+ );
19448
+ const priorEntryId = open.records[0]?.get("entryId");
19449
+ const priorTaskId = open.records[0]?.get("taskId");
19450
+ const priorStartedAt = open.records[0]?.get("startedAt");
19451
+ console.log(`${TAG34} op=start taskId=${taskId} accountId=${accountId} priorOpen=${priorTaskId ?? "none"}`);
19452
+ if (priorEntryId && priorTaskId === taskId) {
19453
+ return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
19454
+ }
19455
+ if (priorEntryId && priorStartedAt && priorTaskId) {
19456
+ const seconds = secondsBetween(priorStartedAt, now);
19457
+ await session.run(
19458
+ `MATCH (te:TimeEntry {entryId: $priorEntryId}) WHERE te.endedAt IS NULL
19459
+ SET te.endedAt = $now, te.seconds = $seconds
19460
+ WITH te
19461
+ MATCH (t:Task {taskId: $priorTaskId, accountId: $accountId})
19462
+ SET t.secondsLogged = coalesce(t.secondsLogged, 0) + $seconds`,
19463
+ { priorEntryId, priorTaskId, accountId, now, seconds: neo4j5.int(seconds) }
19464
+ );
19465
+ console.log(`${TAG34} op=autostop taskId=${priorTaskId} entryId=${priorEntryId} seconds=${seconds}`);
19466
+ }
19467
+ const entryId = randomUUID13();
19468
+ await session.run(
19469
+ `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
19470
+ CREATE (te:TimeEntry {entryId: $entryId, taskId: $taskId, accountId: $accountId, startedAt: $now, endedAt: null})
19471
+ CREATE (te)-[:LOGGED_AGAINST]->(t)`,
19472
+ { taskId, accountId, entryId, now }
19473
+ );
19474
+ console.log(`${TAG34} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
19475
+ return c.json({ ok: true, running: true, entryId, startedAt: now });
19476
+ } catch (err) {
19477
+ console.error(`${TAG34} op=start error="${err instanceof Error ? err.message : String(err)}"`);
19478
+ return c.json({ error: "task-timer-start-failed" }, 500);
19479
+ } finally {
19480
+ await session.close();
19481
+ }
19482
+ });
19483
+ var task_timer_start_default = app47;
19484
+
19485
+ // server/routes/admin/task-timer-stop.ts
19486
+ import neo4j6 from "neo4j-driver";
19487
+ var TAG35 = "[task-timer]";
19488
+ var app48 = new Hono();
19489
+ app48.post("/", requireAdminSession, async (c) => {
19490
+ const cacheKey = c.var.cacheKey;
19491
+ const accountId = getAccountIdForSession(cacheKey);
19492
+ if (!accountId) return c.json({ error: "Account not found for session" }, 401);
19493
+ let body;
19494
+ try {
19495
+ body = await c.req.json();
19496
+ } catch {
19497
+ return c.json({ error: "invalid-json" }, 400);
19498
+ }
19499
+ const taskId = typeof body.taskId === "string" ? body.taskId : "";
19500
+ if (!taskId) return c.json({ error: "taskId required" }, 400);
19501
+ const now = (/* @__PURE__ */ new Date()).toISOString();
19502
+ const session = getSession();
19503
+ try {
19504
+ const open = await session.run(
19505
+ `MATCH (te:TimeEntry {taskId: $taskId, accountId: $accountId}) WHERE te.endedAt IS NULL
19506
+ RETURN te.entryId AS entryId, te.startedAt AS startedAt
19507
+ ORDER BY te.startedAt LIMIT 1`,
19508
+ { taskId, accountId }
19509
+ );
19510
+ const entryId = open.records[0]?.get("entryId");
19511
+ const startedAt = open.records[0]?.get("startedAt");
19512
+ if (!entryId || !startedAt) {
19513
+ const cur = await session.run(
19514
+ `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
19515
+ RETURN coalesce(t.secondsLogged, 0) AS secondsLogged`,
19516
+ { taskId, accountId }
19517
+ );
19518
+ const secondsLogged2 = toNum(cur.records[0]?.get("secondsLogged"));
19519
+ console.log(`${TAG35} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
19520
+ return c.json({ ok: true, running: false, seconds: 0, secondsLogged: secondsLogged2 });
19521
+ }
19522
+ const seconds = secondsBetween(startedAt, now);
19523
+ await session.run(
19524
+ `MATCH (te:TimeEntry {entryId: $entryId}) WHERE te.endedAt IS NULL
19525
+ SET te.endedAt = $now, te.seconds = $seconds
19526
+ WITH te
19527
+ MATCH (t:Task {taskId: $taskId, accountId: $accountId})
19528
+ SET t.secondsLogged = coalesce(t.secondsLogged, 0) + $seconds`,
19529
+ { entryId, taskId, accountId, now, seconds: neo4j6.int(seconds) }
19530
+ );
19531
+ const verify = await session.run(
19532
+ `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
19533
+ OPTIONAL MATCH (te:TimeEntry {taskId: $taskId, accountId: $accountId}) WHERE te.endedAt IS NOT NULL
19534
+ RETURN coalesce(sum(te.seconds), 0) AS sumEntries, coalesce(t.secondsLogged, 0) AS secondsLogged`,
19535
+ { taskId, accountId }
19536
+ );
19537
+ const sumEntries = toNum(verify.records[0]?.get("sumEntries"));
19538
+ const secondsLogged = toNum(verify.records[0]?.get("secondsLogged"));
19539
+ console.log(`${TAG35} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
19540
+ console.log(`${TAG35} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} secondsLogged=${secondsLogged}`);
19541
+ return c.json({ ok: true, running: false, seconds, secondsLogged });
19542
+ } catch (err) {
19543
+ console.error(`${TAG35} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
19544
+ return c.json({ error: "task-timer-stop-failed" }, 500);
19545
+ } finally {
19546
+ await session.close();
19547
+ }
19548
+ });
19549
+ var task_timer_stop_default = app48;
19550
+
19551
+ // server/routes/admin/task-complete.ts
19552
+ var TAG36 = "[task-timer]";
19553
+ var app49 = new Hono();
19554
+ app49.post("/", requireAdminSession, async (c) => {
19555
+ const cacheKey = c.var.cacheKey;
19556
+ const accountId = getAccountIdForSession(cacheKey);
19557
+ if (!accountId) return c.json({ error: "Account not found for session" }, 401);
19558
+ let body;
19559
+ try {
19560
+ body = await c.req.json();
19561
+ } catch {
19562
+ return c.json({ error: "invalid-json" }, 400);
19563
+ }
19564
+ const taskId = typeof body.taskId === "string" ? body.taskId : "";
19565
+ if (!taskId) return c.json({ error: "taskId required" }, 400);
19566
+ const now = (/* @__PURE__ */ new Date()).toISOString();
19567
+ const session = getSession();
19568
+ try {
19569
+ const result = await session.run(
19570
+ `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
19571
+ SET t.status = 'completed', t.updatedAt = $now
19572
+ RETURN t.taskId AS taskId, coalesce(t.secondsLogged, 0) AS secondsLogged`,
19573
+ { taskId, accountId, now }
19574
+ );
19575
+ if (result.records.length === 0) {
19576
+ return c.json({ error: "task-not-found" }, 404);
19577
+ }
19578
+ const secondsLogged = toNum(result.records[0].get("secondsLogged"));
19579
+ console.log(`${TAG36} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
19580
+ return c.json({ ok: true, status: "completed", secondsLogged });
19581
+ } catch (err) {
19582
+ console.error(`${TAG36} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
19583
+ return c.json({ error: "task-complete-failed" }, 500);
19584
+ } finally {
19585
+ await session.close();
19586
+ }
19587
+ });
19588
+ var task_complete_default = app49;
19589
+
19590
+ // server/routes/admin/index.ts
19591
+ var app50 = new Hono();
19592
+ app50.route("/session", session_default);
19593
+ app50.route("/accounts", accounts_default);
19594
+ app50.route("/logs", logs_default);
19595
+ app50.route("/claude-info", claude_info_default);
19596
+ app50.route("/attachment", attachment_default);
19597
+ app50.route("/agents", agents_default);
19598
+ app50.route("/sessions", sessions_default);
19599
+ app50.route("/claude-sessions", claude_sessions_default);
19600
+ app50.route("/log-ingest", log_ingest_default);
19601
+ app50.route("/events", events_default);
19602
+ app50.route("/files", files_default);
19603
+ app50.route("/graph-search", graph_search_default);
19604
+ app50.route("/graph-subgraph", graph_subgraph_default);
19605
+ app50.route("/graph-delete", graph_delete_default);
19606
+ app50.route("/graph-restore", graph_restore_default);
19607
+ app50.route("/graph-labels-in-graph", graph_labels_in_graph_default);
19608
+ app50.route("/graph-default-view", graph_default_view_default);
19609
+ app50.route("/sidebar-artefacts", sidebar_artefacts_default);
19610
+ app50.route("/sidebar-sessions", sidebar_sessions_default);
19611
+ app50.route("/session-delete", session_delete_default);
19612
+ app50.route("/session-stop", session_stop_default);
19613
+ app50.route("/session-archive", session_archive_default);
19614
+ app50.route("/session-rename", session_rename_default);
19615
+ app50.route("/session-usage", session_usage_default);
19616
+ app50.route("/browser", browser_default);
19617
+ app50.route("/session-rc-spawn", session_rc_spawn_default);
19618
+ app50.route("/session-reseat", session_reseat_default);
19619
+ app50.route("/system-stats", system_stats_default);
19620
+ app50.route("/health-brand", health_default2);
19621
+ app50.route("/linkedin-ingest", linkedin_ingest_default);
19622
+ app50.route("/post-turn-context", post_turn_context_default);
19623
+ app50.route("/public-session-context", public_session_context_default);
19624
+ app50.route("/public-session-exit", public_session_exit_default);
19625
+ app50.route("/access-session-evict", access_session_evict_default);
19626
+ app50.route("/enrol-person", enrol_person_default);
19627
+ app50.route("/calendar", calendar_default);
19628
+ app50.route("/tasks-list", tasks_list_default);
19629
+ app50.route("/task-timer-start", task_timer_start_default);
19630
+ app50.route("/task-timer-stop", task_timer_stop_default);
19631
+ app50.route("/task-complete", task_complete_default);
19632
+ var admin_default = app50;
19263
19633
 
19264
19634
  // server/routes/access/verify-token.ts
19265
- var TAG33 = "[access-verify]";
19635
+ var TAG37 = "[access-verify]";
19266
19636
  var MINT_TAG = "[access-session-mint]";
19267
19637
  var COOKIE_NAME = "__access_session";
19268
- var app47 = new Hono();
19269
- app47.post("/", async (c) => {
19638
+ var app51 = new Hono();
19639
+ app51.post("/", async (c) => {
19270
19640
  const ip = c.var.clientIp || "unknown";
19271
19641
  let body;
19272
19642
  try {
@@ -19281,39 +19651,39 @@ app47.post("/", async (c) => {
19281
19651
  }
19282
19652
  const rateMsg = checkAccessRateLimit(ip, agentSlug);
19283
19653
  if (rateMsg) {
19284
- console.error(`${TAG33} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
19654
+ console.error(`${TAG37} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
19285
19655
  return c.json({ error: rateMsg }, 429);
19286
19656
  }
19287
19657
  const grant = await findGrantByMagicToken(token);
19288
19658
  if (!grant) {
19289
19659
  recordAccessFailedAttempt(ip, agentSlug);
19290
- console.error(`${TAG33} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
19660
+ console.error(`${TAG37} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
19291
19661
  return c.json({ error: "invalid-or-expired-link" }, 401);
19292
19662
  }
19293
19663
  if (grant.agentSlug !== agentSlug) {
19294
19664
  recordAccessFailedAttempt(ip, agentSlug);
19295
19665
  console.error(
19296
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
19666
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
19297
19667
  );
19298
19668
  return c.json({ error: "invalid-or-expired-link" }, 401);
19299
19669
  }
19300
19670
  if (grant.status === "expired" || grant.status === "revoked") {
19301
19671
  recordAccessFailedAttempt(ip, agentSlug);
19302
19672
  console.error(
19303
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
19673
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
19304
19674
  );
19305
19675
  return c.json({ error: "access-no-longer-valid" }, 401);
19306
19676
  }
19307
19677
  if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
19308
19678
  recordAccessFailedAttempt(ip, agentSlug);
19309
19679
  console.error(
19310
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
19680
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
19311
19681
  );
19312
19682
  return c.json({ error: "access-no-longer-valid" }, 401);
19313
19683
  }
19314
19684
  if (!grant.sliceToken) {
19315
19685
  console.error(
19316
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
19686
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
19317
19687
  );
19318
19688
  return c.json({ error: "grant-misconfigured" }, 500);
19319
19689
  }
@@ -19329,12 +19699,12 @@ app47.post("/", async (c) => {
19329
19699
  await consumeMagicTokenAndActivate(grant.grantId);
19330
19700
  } catch (err) {
19331
19701
  console.error(
19332
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
19702
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
19333
19703
  );
19334
19704
  return c.json({ error: "verification-failed" }, 500);
19335
19705
  }
19336
19706
  clearAccessRateLimit(ip, agentSlug);
19337
- console.log(`${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
19707
+ console.log(`${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
19338
19708
  console.log(
19339
19709
  `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
19340
19710
  );
@@ -19348,7 +19718,7 @@ app47.post("/", async (c) => {
19348
19718
  displayName: grant.displayName
19349
19719
  });
19350
19720
  });
19351
- var verify_token_default = app47;
19721
+ var verify_token_default = app51;
19352
19722
 
19353
19723
  // app/lib/access-email.ts
19354
19724
  import { spawn as spawn2 } from "child_process";
@@ -19410,10 +19780,10 @@ async function sendMagicLinkEmail(payload) {
19410
19780
  }
19411
19781
 
19412
19782
  // server/routes/access/request-magic-link.ts
19413
- var TAG34 = "[access-request-link]";
19414
- var app48 = new Hono();
19783
+ var TAG38 = "[access-request-link]";
19784
+ var app52 = new Hono();
19415
19785
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
19416
- app48.post("/", async (c) => {
19786
+ app52.post("/", async (c) => {
19417
19787
  let body;
19418
19788
  try {
19419
19789
  body = await c.req.json();
@@ -19427,18 +19797,18 @@ app48.post("/", async (c) => {
19427
19797
  }
19428
19798
  const rateMsg = checkRequestLinkRateLimit(contactValue);
19429
19799
  if (rateMsg) {
19430
- console.error(`${TAG34} contactValue=${maskContact(contactValue)} result=rate-limited`);
19800
+ console.error(`${TAG38} contactValue=${maskContact(contactValue)} result=rate-limited`);
19431
19801
  return c.json({ error: rateMsg }, 429);
19432
19802
  }
19433
19803
  recordRequestLinkAttempt(contactValue);
19434
19804
  const accountId = process.env.ACCOUNT_ID ?? "";
19435
19805
  if (!accountId) {
19436
- console.error(`${TAG34} contactValue=${maskContact(contactValue)} result=no-account-id`);
19806
+ console.error(`${TAG38} contactValue=${maskContact(contactValue)} result=no-account-id`);
19437
19807
  return c.json({ message: VISITOR_MESSAGE }, 200);
19438
19808
  }
19439
19809
  const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
19440
19810
  if (!grant) {
19441
- console.log(`${TAG34} contactValue=${maskContact(contactValue)} result=notfound`);
19811
+ console.log(`${TAG38} contactValue=${maskContact(contactValue)} result=notfound`);
19442
19812
  return c.json({ message: VISITOR_MESSAGE }, 200);
19443
19813
  }
19444
19814
  let token;
@@ -19446,7 +19816,7 @@ app48.post("/", async (c) => {
19446
19816
  token = await generateNewMagicToken(grant.grantId);
19447
19817
  } catch (err) {
19448
19818
  console.error(
19449
- `${TAG34} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
19819
+ `${TAG38} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
19450
19820
  );
19451
19821
  return c.json({ message: VISITOR_MESSAGE }, 200);
19452
19822
  }
@@ -19478,22 +19848,22 @@ app48.post("/", async (c) => {
19478
19848
  });
19479
19849
  if (!sendResult.ok) {
19480
19850
  console.error(
19481
- `${TAG34} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
19851
+ `${TAG38} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
19482
19852
  );
19483
19853
  return c.json({ message: VISITOR_MESSAGE }, 200);
19484
19854
  }
19485
19855
  console.log(
19486
- `${TAG34} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
19856
+ `${TAG38} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
19487
19857
  );
19488
19858
  return c.json({ message: VISITOR_MESSAGE }, 200);
19489
19859
  });
19490
- var request_magic_link_default = app48;
19860
+ var request_magic_link_default = app52;
19491
19861
 
19492
19862
  // server/routes/access/index.ts
19493
- var app49 = new Hono();
19494
- app49.route("/verify-token", verify_token_default);
19495
- app49.route("/request-magic-link", request_magic_link_default);
19496
- var access_default = app49;
19863
+ var app53 = new Hono();
19864
+ app53.route("/verify-token", verify_token_default);
19865
+ app53.route("/request-magic-link", request_magic_link_default);
19866
+ var access_default = app53;
19497
19867
 
19498
19868
  // server/routes/sites.ts
19499
19869
  import { existsSync as existsSync28, readFileSync as readFileSync31, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
@@ -19528,8 +19898,8 @@ function getExt(p) {
19528
19898
  if (idx < p.lastIndexOf("/")) return "";
19529
19899
  return p.slice(idx).toLowerCase();
19530
19900
  }
19531
- var app50 = new Hono();
19532
- app50.get("/:rel{.*}", (c) => {
19901
+ var app54 = new Hono();
19902
+ app54.get("/:rel{.*}", (c) => {
19533
19903
  const reqPath = c.req.path;
19534
19904
  const rawRel = c.req.param("rel") ?? "";
19535
19905
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -19632,7 +20002,7 @@ app50.get("/:rel{.*}", (c) => {
19632
20002
  "X-Content-Type-Options": "nosniff"
19633
20003
  });
19634
20004
  });
19635
- var sites_default = app50;
20005
+ var sites_default = app54;
19636
20006
 
19637
20007
  // app/lib/visitor-token.ts
19638
20008
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
@@ -19732,7 +20102,7 @@ function readBrandConfig() {
19732
20102
  }
19733
20103
 
19734
20104
  // server/routes/visitor-consent.ts
19735
- var app51 = new Hono();
20105
+ var app55 = new Hono();
19736
20106
  var CONSENT_COOKIE_NAME = "mxy_consent";
19737
20107
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
19738
20108
  var DEFAULT_CONSENT_COPY = {
@@ -19777,17 +20147,17 @@ function siteSlugFromReferer(referer) {
19777
20147
  return "";
19778
20148
  }
19779
20149
  }
19780
- app51.options("/consent", (c) => {
20150
+ app55.options("/consent", (c) => {
19781
20151
  const origin = getOrigin(c);
19782
20152
  setCorsHeaders(c, origin);
19783
20153
  return c.body(null, 204);
19784
20154
  });
19785
- app51.options("/brand-config", (c) => {
20155
+ app55.options("/brand-config", (c) => {
19786
20156
  const origin = getOrigin(c);
19787
20157
  setCorsHeaders(c, origin);
19788
20158
  return c.body(null, 204);
19789
20159
  });
19790
- app51.post("/consent", async (c) => {
20160
+ app55.post("/consent", async (c) => {
19791
20161
  const origin = getOrigin(c);
19792
20162
  setCorsHeaders(c, origin);
19793
20163
  let raw;
@@ -19827,7 +20197,7 @@ app51.post("/consent", async (c) => {
19827
20197
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
19828
20198
  return c.body(null, 204);
19829
20199
  });
19830
- app51.get("/brand-config", (c) => {
20200
+ app55.get("/brand-config", (c) => {
19831
20201
  const origin = getOrigin(c);
19832
20202
  setCorsHeaders(c, origin);
19833
20203
  const brand = readBrandConfig();
@@ -19837,7 +20207,7 @@ app51.get("/brand-config", (c) => {
19837
20207
  c.header("Cache-Control", "public, max-age=300");
19838
20208
  return c.json({ consent: { copy, palette } });
19839
20209
  });
19840
- var visitor_consent_default = app51;
20210
+ var visitor_consent_default = app55;
19841
20211
 
19842
20212
  // server/routes/listings.ts
19843
20213
  function getCookie(headerValue, name) {
@@ -19864,8 +20234,8 @@ function appendConsentParams(pageUrl, token) {
19864
20234
  }
19865
20235
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
19866
20236
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
19867
- var app52 = new Hono();
19868
- app52.get("/:slug/click", async (c) => {
20237
+ var app56 = new Hono();
20238
+ app56.get("/:slug/click", async (c) => {
19869
20239
  const slug = c.req.param("slug") ?? "";
19870
20240
  const rawSession = c.req.query("session") ?? "";
19871
20241
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -19929,10 +20299,10 @@ app52.get("/:slug/click", async (c) => {
19929
20299
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
19930
20300
  return c.redirect(redirectUrl, 302);
19931
20301
  });
19932
- var listings_default = app52;
20302
+ var listings_default = app56;
19933
20303
 
19934
20304
  // server/routes/visitor-event.ts
19935
- var app53 = new Hono();
20305
+ var app57 = new Hono();
19936
20306
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
19937
20307
  var buckets = /* @__PURE__ */ new Map();
19938
20308
  var RATE_LIMIT = 60;
@@ -19999,12 +20369,12 @@ function originAllowed(origin, allowlist) {
19999
20369
  return false;
20000
20370
  }
20001
20371
  }
20002
- app53.options("/event", (c) => {
20372
+ app57.options("/event", (c) => {
20003
20373
  const origin = getOrigin2(c);
20004
20374
  setCorsHeaders2(c, origin);
20005
20375
  return c.body(null, 204);
20006
20376
  });
20007
- app53.post("/event", async (c) => {
20377
+ app57.post("/event", async (c) => {
20008
20378
  const origin = getOrigin2(c);
20009
20379
  setCorsHeaders2(c, origin);
20010
20380
  const ua = c.req.header("user-agent") ?? "";
@@ -20209,7 +20579,7 @@ async function writeEvent(opts) {
20209
20579
  );
20210
20580
  }
20211
20581
  }
20212
- var visitor_event_default = app53;
20582
+ var visitor_event_default = app57;
20213
20583
 
20214
20584
  // server/routes/session.ts
20215
20585
  import { resolve as resolve27 } from "path";
@@ -20255,8 +20625,8 @@ function withVisitorCookie(response, visitorId) {
20255
20625
  headers
20256
20626
  });
20257
20627
  }
20258
- var app54 = new Hono();
20259
- app54.post("/", async (c) => {
20628
+ var app58 = new Hono();
20629
+ app58.post("/", async (c) => {
20260
20630
  let body;
20261
20631
  try {
20262
20632
  body = await c.req.json();
@@ -20403,7 +20773,7 @@ app54.post("/", async (c) => {
20403
20773
  newVisitorId
20404
20774
  );
20405
20775
  });
20406
- var session_default2 = app54;
20776
+ var session_default2 = app58;
20407
20777
 
20408
20778
  // server/lib/calendar-slots.ts
20409
20779
  var WEEKDAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
@@ -20486,17 +20856,17 @@ function computeOpenSlots(config, busy, fromISO, toISO) {
20486
20856
  }
20487
20857
 
20488
20858
  // server/routes/calendar-public.ts
20489
- var app55 = new Hono();
20859
+ var app59 = new Hono();
20490
20860
  function setCors(c) {
20491
20861
  c.header("Access-Control-Allow-Origin", "*");
20492
20862
  c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
20493
20863
  c.header("Access-Control-Allow-Headers", "content-type");
20494
20864
  }
20495
- app55.options("/free-busy", (c) => {
20865
+ app59.options("/free-busy", (c) => {
20496
20866
  setCors(c);
20497
20867
  return c.body(null, 204);
20498
20868
  });
20499
- app55.get("/free-busy", async (c) => {
20869
+ app59.get("/free-busy", async (c) => {
20500
20870
  setCors(c);
20501
20871
  const from = c.req.query("from");
20502
20872
  const to = c.req.query("to");
@@ -20538,7 +20908,79 @@ app55.get("/free-busy", async (c) => {
20538
20908
  await session.close();
20539
20909
  }
20540
20910
  });
20541
- var calendar_public_default = app55;
20911
+ var calendar_public_default = app59;
20912
+
20913
+ // app/lib/timeentry-census.ts
20914
+ var TAG39 = "[timeentry-census]";
20915
+ function reconcileTimeEntries(rows, now, horizonMs) {
20916
+ const openEntries = rows.openEntries.length;
20917
+ let oldestOpenAgeMs = 0;
20918
+ const perAccount = /* @__PURE__ */ new Map();
20919
+ for (const e of rows.openEntries) {
20920
+ oldestOpenAgeMs = Math.max(oldestOpenAgeMs, now - e.startedAtMs);
20921
+ perAccount.set(e.accountId, (perAccount.get(e.accountId) ?? 0) + 1);
20922
+ }
20923
+ const maxOpenPerAccount = perAccount.size === 0 ? 0 : Math.max(...perAccount.values());
20924
+ const driftTasks = rows.taskSums.filter((t) => t.secondsLogged !== t.sumEntries).length;
20925
+ const oldestOpenAgeMin = Math.floor(oldestOpenAgeMs / 6e4);
20926
+ const alarm = oldestOpenAgeMs > horizonMs || maxOpenPerAccount > 1 || driftTasks > 0;
20927
+ return { openEntries, oldestOpenAgeMin, maxOpenPerAccount, driftTasks, alarm };
20928
+ }
20929
+ async function runTimeEntryCensusOnce(session, now, horizonMs) {
20930
+ try {
20931
+ const openRes = await session.run(
20932
+ `MATCH (te:TimeEntry) WHERE te.endedAt IS NULL
20933
+ RETURN te.accountId AS accountId, te.startedAt AS startedAt`
20934
+ );
20935
+ const openEntries = openRes.records.map((r) => ({
20936
+ accountId: r.get("accountId") ?? "unknown",
20937
+ startedAtMs: Date.parse(r.get("startedAt") ?? "") || now
20938
+ }));
20939
+ const sumRes = await session.run(
20940
+ `MATCH (t:Task) WHERE t.secondsLogged IS NOT NULL
20941
+ OPTIONAL MATCH (te:TimeEntry {taskId: t.taskId}) WHERE te.endedAt IS NOT NULL
20942
+ RETURN t.taskId AS taskId, coalesce(t.secondsLogged, 0) AS secondsLogged, coalesce(sum(te.seconds), 0) AS sumEntries`
20943
+ );
20944
+ const taskSums = sumRes.records.map((r) => ({
20945
+ taskId: r.get("taskId") ?? "",
20946
+ secondsLogged: toNum(r.get("secondsLogged")),
20947
+ sumEntries: toNum(r.get("sumEntries"))
20948
+ }));
20949
+ const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
20950
+ const line = `${TAG39} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
20951
+ if (finding.alarm) console.error(`${line} alarm=true`);
20952
+ else console.log(line);
20953
+ return finding;
20954
+ } catch (err) {
20955
+ console.error(`${TAG39} error="${err instanceof Error ? err.message : String(err)}"`);
20956
+ return null;
20957
+ }
20958
+ }
20959
+ var DEFAULT_INTERVAL_MS = 15 * 60 * 1e3;
20960
+ var DEFAULT_HORIZON_MS = 8 * 60 * 60 * 1e3;
20961
+ function startTimeEntryCensus(openSession, opts = {}) {
20962
+ const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
20963
+ const horizonMs = opts.horizonMs ?? DEFAULT_HORIZON_MS;
20964
+ const tick = async () => {
20965
+ const session = openSession();
20966
+ try {
20967
+ await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
20968
+ } finally {
20969
+ try {
20970
+ await session.close();
20971
+ } catch {
20972
+ }
20973
+ }
20974
+ };
20975
+ void tick();
20976
+ const id = setInterval(() => {
20977
+ void tick();
20978
+ }, intervalMs);
20979
+ if (typeof id.unref === "function") {
20980
+ id.unref();
20981
+ }
20982
+ return () => clearInterval(id);
20983
+ }
20542
20984
 
20543
20985
  // app/lib/graph-health.ts
20544
20986
  var import_dist4 = __toESM(require_dist4(), 1);
@@ -21307,8 +21749,8 @@ var streamSSE = (c, cb, onError) => {
21307
21749
 
21308
21750
  // app/lib/whatsapp/gateway/routes.ts
21309
21751
  function createWaChannelRoutes(deps) {
21310
- const app57 = new Hono();
21311
- app57.get("/wa-channel/inbound", (c) => {
21752
+ const app61 = new Hono();
21753
+ app61.get("/wa-channel/inbound", (c) => {
21312
21754
  const senderId = c.req.query("senderId");
21313
21755
  if (!senderId) return c.json({ error: "senderId required" }, 400);
21314
21756
  return streamSSE(c, async (stream2) => {
@@ -21326,7 +21768,7 @@ function createWaChannelRoutes(deps) {
21326
21768
  }
21327
21769
  });
21328
21770
  });
21329
- app57.post("/wa-channel/reply", async (c) => {
21771
+ app61.post("/wa-channel/reply", async (c) => {
21330
21772
  const body = await c.req.json().catch(() => null);
21331
21773
  const senderId = body?.senderId;
21332
21774
  const text = body?.text;
@@ -21347,7 +21789,7 @@ function createWaChannelRoutes(deps) {
21347
21789
  console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
21348
21790
  return c.json({ ok: true });
21349
21791
  });
21350
- app57.post("/wa-channel/reply-document", async (c) => {
21792
+ app61.post("/wa-channel/reply-document", async (c) => {
21351
21793
  const body = await c.req.json().catch(() => null);
21352
21794
  const senderId = body?.senderId;
21353
21795
  const files = body?.files;
@@ -21386,7 +21828,7 @@ function createWaChannelRoutes(deps) {
21386
21828
  }
21387
21829
  return c.json({ ok: true, results });
21388
21830
  });
21389
- app57.post("/wa-channel/ready", async (c) => {
21831
+ app61.post("/wa-channel/ready", async (c) => {
21390
21832
  const body = await c.req.json().catch(() => null);
21391
21833
  const senderId = body?.senderId;
21392
21834
  if (typeof senderId !== "string") {
@@ -21395,7 +21837,7 @@ function createWaChannelRoutes(deps) {
21395
21837
  deps.onReady?.(senderId);
21396
21838
  return c.json({ ok: true });
21397
21839
  });
21398
- app57.post("/wa-channel/received", async (c) => {
21840
+ app61.post("/wa-channel/received", async (c) => {
21399
21841
  const body = await c.req.json().catch(() => null);
21400
21842
  const senderId = body?.senderId;
21401
21843
  const waMessageId = body?.waMessageId;
@@ -21405,7 +21847,7 @@ function createWaChannelRoutes(deps) {
21405
21847
  deps.onReceived?.(senderId, waMessageId);
21406
21848
  return c.json({ ok: true });
21407
21849
  });
21408
- app57.post("/wa-channel/turn-end", async (c) => {
21850
+ app61.post("/wa-channel/turn-end", async (c) => {
21409
21851
  const body = await c.req.json().catch(() => null);
21410
21852
  const senderId = body?.senderId;
21411
21853
  const sessionId = body?.sessionId;
@@ -21421,7 +21863,7 @@ function createWaChannelRoutes(deps) {
21421
21863
  console.error(`[whatsapp-native] op=turn-end sessionId=${sid} replied=no delivered=none`);
21422
21864
  return c.json({ ok: true, delivered: "none" });
21423
21865
  });
21424
- return app57;
21866
+ return app61;
21425
21867
  }
21426
21868
 
21427
21869
  // app/lib/whatsapp/gateway/wa-gateway.ts
@@ -21526,7 +21968,8 @@ var WaGateway = class {
21526
21968
  waMessageId: `wa-${++this.seq}`,
21527
21969
  media: input.media,
21528
21970
  source,
21529
- standingRules
21971
+ standingRules,
21972
+ scheduleProvenance: input.scheduleProvenance
21530
21973
  },
21531
21974
  Date.now()
21532
21975
  );
@@ -21544,7 +21987,9 @@ var WaGateway = class {
21544
21987
  role: input.role ?? "admin",
21545
21988
  personId: input.personId ?? null,
21546
21989
  gatewayUrl: this.deps.gatewayUrl,
21547
- serverPath: this.deps.serverPath
21990
+ serverPath: this.deps.serverPath,
21991
+ // Task 1526 — a scheduled firing's per-firing seat id; absent on real inbound.
21992
+ sessionIdOverride: input.scheduledSessionId
21548
21993
  });
21549
21994
  } finally {
21550
21995
  this.spawning.delete(input.senderId);
@@ -21570,7 +22015,7 @@ var WaGateway = class {
21570
22015
  function buildWaSpawnRequest(input) {
21571
22016
  const personId = input.role === "public" && input.personId ? input.personId : void 0;
21572
22017
  return {
21573
- sessionId: adminSessionIdFor(input.accountId, input.senderId, personId),
22018
+ sessionId: input.sessionIdOverride && input.sessionIdOverride.length > 0 ? input.sessionIdOverride : adminSessionIdFor(input.accountId, input.senderId, personId),
21574
22019
  role: input.role,
21575
22020
  channel: "whatsapp",
21576
22021
  personId,
@@ -21754,8 +22199,8 @@ var import_dist6 = __toESM(require_dist(), 1);
21754
22199
 
21755
22200
  // app/lib/webchat/gateway/routes.ts
21756
22201
  function createWebchatChannelRoutes(deps) {
21757
- const app57 = new Hono();
21758
- app57.get("/webchat-channel/inbound", (c) => {
22202
+ const app61 = new Hono();
22203
+ app61.get("/webchat-channel/inbound", (c) => {
21759
22204
  const key = c.req.query("key");
21760
22205
  if (!key) return c.json({ error: "key required" }, 400);
21761
22206
  return streamSSE(c, async (stream2) => {
@@ -21773,7 +22218,7 @@ function createWebchatChannelRoutes(deps) {
21773
22218
  }
21774
22219
  });
21775
22220
  });
21776
- app57.post("/webchat-channel/reply", async (c) => {
22221
+ app61.post("/webchat-channel/reply", async (c) => {
21777
22222
  const body = await c.req.json().catch(() => null);
21778
22223
  const key = body?.key;
21779
22224
  const text = body?.text;
@@ -21783,7 +22228,7 @@ function createWebchatChannelRoutes(deps) {
21783
22228
  deps.onReply?.(key, text);
21784
22229
  return c.json({ ok: true });
21785
22230
  });
21786
- app57.post("/webchat-channel/connector-auth", async (c) => {
22231
+ app61.post("/webchat-channel/connector-auth", async (c) => {
21787
22232
  const body = await c.req.json().catch(() => null);
21788
22233
  const key = body?.key;
21789
22234
  const sessionId = body?.sessionId;
@@ -21796,7 +22241,7 @@ function createWebchatChannelRoutes(deps) {
21796
22241
  const result = await deps.onConnectorAuth({ key, sessionId, name, completed });
21797
22242
  return c.json(result);
21798
22243
  });
21799
- app57.post("/webchat-channel/ready", async (c) => {
22244
+ app61.post("/webchat-channel/ready", async (c) => {
21800
22245
  const body = await c.req.json().catch(() => null);
21801
22246
  const key = body?.key;
21802
22247
  if (typeof key !== "string") {
@@ -21805,7 +22250,7 @@ function createWebchatChannelRoutes(deps) {
21805
22250
  deps.onReady?.(key);
21806
22251
  return c.json({ ok: true });
21807
22252
  });
21808
- app57.post("/webchat-channel/permission-request", async (c) => {
22253
+ app61.post("/webchat-channel/permission-request", async (c) => {
21809
22254
  const body = await c.req.json().catch(() => null);
21810
22255
  const key = body?.key;
21811
22256
  const requestId = body?.request_id;
@@ -21819,7 +22264,7 @@ function createWebchatChannelRoutes(deps) {
21819
22264
  const verdict = await deps.awaitPermissionVerdict({ key, requestId, toolName, description, inputPreview });
21820
22265
  return c.json(verdict);
21821
22266
  });
21822
- app57.post("/webchat-channel/received", async (c) => {
22267
+ app61.post("/webchat-channel/received", async (c) => {
21823
22268
  const body = await c.req.json().catch(() => null);
21824
22269
  const key = body?.key;
21825
22270
  const messageId = body?.messageId;
@@ -21829,7 +22274,7 @@ function createWebchatChannelRoutes(deps) {
21829
22274
  deps.onReceived?.(key, messageId);
21830
22275
  return c.json({ ok: true });
21831
22276
  });
21832
- app57.post("/webchat-channel/turn-end", async (c) => {
22277
+ app61.post("/webchat-channel/turn-end", async (c) => {
21833
22278
  const body = await c.req.json().catch(() => null);
21834
22279
  const key = body?.key;
21835
22280
  const sessionId = body?.sessionId;
@@ -21849,7 +22294,7 @@ function createWebchatChannelRoutes(deps) {
21849
22294
  console.error(`[webchat-native] op=turn-undelivered channel=webchat key=${k} sessionId=${sid} bytes=${finalBytes}`);
21850
22295
  return c.json({ ok: true, delivered: "undelivered" });
21851
22296
  });
21852
- return app57;
22297
+ return app61;
21853
22298
  }
21854
22299
 
21855
22300
  // app/lib/webchat/gateway/webchat-gateway.ts
@@ -22480,7 +22925,7 @@ function makeFileDelivery(opts) {
22480
22925
  }
22481
22926
 
22482
22927
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
22483
- var TAG35 = "[whatsapp-adaptor]";
22928
+ var TAG40 = "[whatsapp-adaptor]";
22484
22929
  var SEND_USER_FILE2 = "SendUserFile";
22485
22930
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
22486
22931
  function platformRoot() {
@@ -22498,7 +22943,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
22498
22943
  });
22499
22944
  if (result.ok) return { ok: true };
22500
22945
  console.error(
22501
- `${TAG35} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
22946
+ `${TAG40} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
22502
22947
  );
22503
22948
  return { ok: false, error: result.error };
22504
22949
  };
@@ -22506,7 +22951,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
22506
22951
  function makeWhatsAppFileDelivery(entry, maxyAccountId) {
22507
22952
  const shared = makeFileDelivery({
22508
22953
  entry,
22509
- tag: TAG35,
22954
+ tag: TAG40,
22510
22955
  channel: "whatsapp",
22511
22956
  sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
22512
22957
  });
@@ -22541,7 +22986,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
22541
22986
  if (!delivered) {
22542
22987
  const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
22543
22988
  console.error(
22544
- `${TAG35} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
22989
+ `${TAG40} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
22545
22990
  );
22546
22991
  }
22547
22992
  }
@@ -22718,8 +23163,8 @@ var import_dist7 = __toESM(require_dist(), 1);
22718
23163
 
22719
23164
  // app/lib/telegram/gateway/routes.ts
22720
23165
  function createTelegramChannelRoutes(deps) {
22721
- const app57 = new Hono();
22722
- app57.get("/tg-channel/inbound", (c) => {
23166
+ const app61 = new Hono();
23167
+ app61.get("/tg-channel/inbound", (c) => {
22723
23168
  const key = c.req.query("key");
22724
23169
  if (!key) return c.json({ error: "key required" }, 400);
22725
23170
  return streamSSE(c, async (stream2) => {
@@ -22737,7 +23182,7 @@ function createTelegramChannelRoutes(deps) {
22737
23182
  }
22738
23183
  });
22739
23184
  });
22740
- app57.post("/tg-channel/reply", async (c) => {
23185
+ app61.post("/tg-channel/reply", async (c) => {
22741
23186
  const body = await c.req.json().catch(() => null);
22742
23187
  const key = body?.key;
22743
23188
  const text = body?.text;
@@ -22753,7 +23198,7 @@ function createTelegramChannelRoutes(deps) {
22753
23198
  console.error(`[telegram-native] op=reply key=${key} bytes=${Buffer.byteLength(text, "utf8")}`);
22754
23199
  return c.json({ ok: true });
22755
23200
  });
22756
- app57.post("/tg-channel/ready", async (c) => {
23201
+ app61.post("/tg-channel/ready", async (c) => {
22757
23202
  const body = await c.req.json().catch(() => null);
22758
23203
  const key = body?.key;
22759
23204
  if (typeof key !== "string") {
@@ -22762,7 +23207,7 @@ function createTelegramChannelRoutes(deps) {
22762
23207
  deps.onReady?.(key);
22763
23208
  return c.json({ ok: true });
22764
23209
  });
22765
- app57.post("/tg-channel/received", async (c) => {
23210
+ app61.post("/tg-channel/received", async (c) => {
22766
23211
  const body = await c.req.json().catch(() => null);
22767
23212
  const key = body?.key;
22768
23213
  const messageId = body?.messageId;
@@ -22772,7 +23217,7 @@ function createTelegramChannelRoutes(deps) {
22772
23217
  deps.onReceived?.(key, messageId);
22773
23218
  return c.json({ ok: true });
22774
23219
  });
22775
- app57.post("/tg-channel/turn-end", async (c) => {
23220
+ app61.post("/tg-channel/turn-end", async (c) => {
22776
23221
  const body = await c.req.json().catch(() => null);
22777
23222
  const key = body?.key;
22778
23223
  const sessionId = body?.sessionId;
@@ -22788,7 +23233,7 @@ function createTelegramChannelRoutes(deps) {
22788
23233
  console.error(`[telegram-native] op=turn-end key=${key} sessionId=${sid} replied=no delivered=none`);
22789
23234
  return c.json({ ok: true, delivered: "none" });
22790
23235
  });
22791
- return app57;
23236
+ return app61;
22792
23237
  }
22793
23238
 
22794
23239
  // app/lib/telegram/gateway/telegram-gateway.ts
@@ -22855,7 +23300,7 @@ var TelegramGateway = class {
22855
23300
  );
22856
23301
  }
22857
23302
  this.hub.deliver(
22858
- { key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules },
23303
+ { key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules, scheduleProvenance: input.scheduleProvenance },
22859
23304
  Date.now()
22860
23305
  );
22861
23306
  console.error(
@@ -22872,7 +23317,9 @@ var TelegramGateway = class {
22872
23317
  personId: input.personId ?? null,
22873
23318
  chatId: input.chatId,
22874
23319
  gatewayUrl: this.deps.gatewayUrl,
22875
- serverPath: this.deps.serverPath
23320
+ serverPath: this.deps.serverPath,
23321
+ // Task 1526 — a scheduled firing's per-firing seat id; absent on real inbound.
23322
+ sessionIdOverride: input.scheduledSessionId
22876
23323
  });
22877
23324
  } finally {
22878
23325
  this.spawning.delete(input.senderId);
@@ -22890,7 +23337,7 @@ var TelegramGateway = class {
22890
23337
  function buildTelegramSpawnRequest(input) {
22891
23338
  const personId = input.role === "public" && input.personId ? input.personId : void 0;
22892
23339
  return {
22893
- sessionId: adminSessionIdFor(input.accountId, input.senderId, personId),
23340
+ sessionId: input.sessionIdOverride && input.sessionIdOverride.length > 0 ? input.sessionIdOverride : adminSessionIdFor(input.accountId, input.senderId, personId),
22894
23341
  role: input.role,
22895
23342
  channel: "telegram",
22896
23343
  personId,
@@ -22909,7 +23356,7 @@ function buildTelegramSpawnRequest(input) {
22909
23356
  import { realpathSync as realpathSync7 } from "fs";
22910
23357
  import { readFile as readFile7, stat as stat7 } from "fs/promises";
22911
23358
  import { resolve as resolve30, basename as basename12 } from "path";
22912
- var TAG36 = "[telegram:outbound]";
23359
+ var TAG41 = "[telegram:outbound]";
22913
23360
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
22914
23361
  async function sendTelegramDocument(input) {
22915
23362
  const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot3 } = input;
@@ -22925,16 +23372,16 @@ async function sendTelegramDocument(input) {
22925
23372
  resolvedPath = realpathSync7(filePath);
22926
23373
  const accountResolved = realpathSync7(accountDir);
22927
23374
  if (!resolvedPath.startsWith(accountResolved + "/")) {
22928
- console.error(`${TAG36} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
23375
+ console.error(`${TAG41} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
22929
23376
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
22930
23377
  }
22931
23378
  } catch (err) {
22932
23379
  const code = err.code;
22933
23380
  if (code === "ENOENT") {
22934
- console.error(`${TAG36} document ENOENT path=${filePath}`);
23381
+ console.error(`${TAG41} document ENOENT path=${filePath}`);
22935
23382
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
22936
23383
  }
22937
- console.error(`${TAG36} document path error: ${String(err)}`);
23384
+ console.error(`${TAG41} document path error: ${String(err)}`);
22938
23385
  return { ok: false, status: 500, error: String(err) };
22939
23386
  }
22940
23387
  const fileStat = await stat7(resolvedPath);
@@ -22967,13 +23414,13 @@ async function sendTelegramDocument(input) {
22967
23414
  error = e instanceof Error ? e.message : String(e);
22968
23415
  }
22969
23416
  console.error(
22970
- `${TAG36} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
23417
+ `${TAG41} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
22971
23418
  );
22972
23419
  return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
22973
23420
  }
22974
23421
 
22975
23422
  // app/lib/telegram/outbound/file-delivery.ts
22976
- var TAG37 = "[telegram:outbound]";
23423
+ var TAG42 = "[telegram:outbound]";
22977
23424
  function platformRoot2() {
22978
23425
  return process.env.MAXY_PLATFORM_ROOT || "";
22979
23426
  }
@@ -22985,11 +23432,11 @@ function makeTelegramSendFile(entry) {
22985
23432
  botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
22986
23433
  }
22987
23434
  if (!botToken) {
22988
- console.error(`${TAG37} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
23435
+ console.error(`${TAG42} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
22989
23436
  return { ok: false, error: "no-bot-token" };
22990
23437
  }
22991
23438
  if (entry.replyTarget == null) {
22992
- console.error(`${TAG37} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
23439
+ console.error(`${TAG42} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
22993
23440
  return { ok: false, error: "no-reply-target" };
22994
23441
  }
22995
23442
  const result = await sendTelegramDocument({
@@ -23006,7 +23453,7 @@ function makeTelegramSendFile(entry) {
23006
23453
  function makeTelegramFileDelivery(entry) {
23007
23454
  return makeFileDelivery({
23008
23455
  entry,
23009
- tag: TAG37,
23456
+ tag: TAG42,
23010
23457
  channel: "telegram",
23011
23458
  sendFile: makeTelegramSendFile(entry)
23012
23459
  });
@@ -23055,8 +23502,8 @@ function telegramServerPath() {
23055
23502
  }
23056
23503
 
23057
23504
  // app/lib/channel-pty-bridge/public-session-end-review.ts
23058
- import { randomUUID as randomUUID13 } from "crypto";
23059
- var TAG38 = "[public-session-review]";
23505
+ import { randomUUID as randomUUID14 } from "crypto";
23506
+ var TAG43 = "[public-session-review]";
23060
23507
  var CONSUMED_CAP = 500;
23061
23508
  var consumed = /* @__PURE__ */ new Set();
23062
23509
  function consumeOnce(sessionId) {
@@ -23083,7 +23530,7 @@ async function fetchJsonl(sessionId) {
23083
23530
  return await res.text();
23084
23531
  } catch (err) {
23085
23532
  console.error(
23086
- `${TAG38} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
23533
+ `${TAG43} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
23087
23534
  );
23088
23535
  return null;
23089
23536
  }
@@ -23107,7 +23554,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
23107
23554
  const res = await fetch(url);
23108
23555
  if (!res.ok) {
23109
23556
  console.error(
23110
- `${TAG38} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
23557
+ `${TAG43} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
23111
23558
  );
23112
23559
  return [];
23113
23560
  }
@@ -23115,7 +23562,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
23115
23562
  return Array.isArray(body.writes) ? body.writes : [];
23116
23563
  } catch (err) {
23117
23564
  console.error(
23118
- `${TAG38} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
23565
+ `${TAG43} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
23119
23566
  );
23120
23567
  return [];
23121
23568
  }
@@ -23143,8 +23590,8 @@ function composeInitialMessage(input) {
23143
23590
  ].join("\n");
23144
23591
  }
23145
23592
  async function dispatchReviewer(input, initialMessage) {
23146
- const sessionId = randomUUID13();
23147
- console.log(`${TAG38} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
23593
+ const sessionId = randomUUID14();
23594
+ console.log(`${TAG43} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
23148
23595
  const spawned = await managerRcSpawn({
23149
23596
  sessionId,
23150
23597
  initialMessage,
@@ -23164,21 +23611,21 @@ async function firePublicSessionEndReview(input) {
23164
23611
  const dispatchedAt = Date.now();
23165
23612
  if (consumed.has(input.sessionId)) {
23166
23613
  console.log(
23167
- `${TAG38} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
23614
+ `${TAG43} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
23168
23615
  );
23169
23616
  return;
23170
23617
  }
23171
23618
  const jsonl = await fetchJsonl(input.sessionId);
23172
23619
  if (!jsonl) {
23173
23620
  console.log(
23174
- `${TAG38} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
23621
+ `${TAG43} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
23175
23622
  );
23176
23623
  return;
23177
23624
  }
23178
23625
  const operatorTurns = countOperatorTurns(jsonl);
23179
23626
  if (operatorTurns === 0) {
23180
23627
  console.log(
23181
- `${TAG38} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
23628
+ `${TAG43} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
23182
23629
  );
23183
23630
  return;
23184
23631
  }
@@ -23192,19 +23639,19 @@ async function firePublicSessionEndReview(input) {
23192
23639
  });
23193
23640
  if (!consumeOnce(input.sessionId)) {
23194
23641
  console.log(
23195
- `${TAG38} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
23642
+ `${TAG43} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
23196
23643
  );
23197
23644
  return;
23198
23645
  }
23199
23646
  const dispatched = await dispatchReviewer(input, initialMessage);
23200
23647
  if (!dispatched.ok) {
23201
23648
  console.error(
23202
- `${TAG38} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
23649
+ `${TAG43} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
23203
23650
  );
23204
23651
  return;
23205
23652
  }
23206
23653
  console.log(
23207
- `${TAG38} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
23654
+ `${TAG43} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
23208
23655
  );
23209
23656
  }
23210
23657
 
@@ -23619,7 +24066,7 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
23619
24066
  function isPublicHost(host) {
23620
24067
  return host.startsWith("public.") || aliasDomains.has(host);
23621
24068
  }
23622
- var app56 = new Hono();
24069
+ var app60 = new Hono();
23623
24070
  var nativeFileFollowers = /* @__PURE__ */ new Map();
23624
24071
  async function fetchAccountStandingRules(accountId) {
23625
24072
  const session = getSession();
@@ -23635,6 +24082,26 @@ async function fetchAccountStandingRules(accountId) {
23635
24082
  await session.close();
23636
24083
  }
23637
24084
  }
24085
+ async function recordPassiveDispatch(input) {
24086
+ const session = getSession();
24087
+ try {
24088
+ await session.run(
24089
+ `CREATE (d:PassiveDispatch {
24090
+ accountId: $accountId,
24091
+ sessionId: $sessionId,
24092
+ msgId: $msgId,
24093
+ dispatchedAt: $dispatchedAt
24094
+ })`,
24095
+ { ...input, dispatchedAt: (/* @__PURE__ */ new Date()).toISOString() }
24096
+ );
24097
+ } catch (err) {
24098
+ console.error(
24099
+ `[wa-passive] op=ledger-write-failed account=${input.accountId} sessionId=${input.sessionId} error=${err instanceof Error ? err.message : String(err)}`
24100
+ );
24101
+ } finally {
24102
+ await session.close();
24103
+ }
24104
+ }
23638
24105
  var waGateway = new WaGateway({
23639
24106
  fetchStandingRules: fetchAccountStandingRules,
23640
24107
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
@@ -23657,7 +24124,7 @@ var waGateway = new WaGateway({
23657
24124
  });
23658
24125
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
23659
24126
  },
23660
- ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId }) => {
24127
+ ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, sessionIdOverride }) => {
23661
24128
  if (passive) {
23662
24129
  const phoneHash = createHash6("sha256").update(senderId).digest("hex").slice(0, 12);
23663
24130
  const prompt = (text ?? "").trim();
@@ -23665,7 +24132,17 @@ var waGateway = new WaGateway({
23665
24132
  console.error(`[wa-passive] op=dropped account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} reason=empty-text`);
23666
24133
  return;
23667
24134
  }
23668
- console.error(`[wa-passive] op=dispatch account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"}`);
24135
+ let raisedByPersonId;
24136
+ try {
24137
+ raisedByPersonId = await ensureManagerPerson(effectiveAccountId, senderId) ?? void 0;
24138
+ } catch (err) {
24139
+ console.error(`[wa-passive] op=raised-by-error account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} error=${err instanceof Error ? err.message : String(err)}`);
24140
+ }
24141
+ if (!raisedByPersonId) {
24142
+ console.error(`[wa-passive] op=dropped account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} reason=raised-by-unresolved`);
24143
+ return;
24144
+ }
24145
+ console.error(`[wa-passive] op=dispatch account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} raisedBy=${raisedByPersonId}`);
23669
24146
  const result2 = await managerSpawn({
23670
24147
  senderId,
23671
24148
  role: "public",
@@ -23674,18 +24151,28 @@ var waGateway = new WaGateway({
23674
24151
  targetAccountId: effectiveAccountId,
23675
24152
  agentSlug: "passive",
23676
24153
  initialMessage: prompt,
23677
- passive: true
24154
+ passive: true,
24155
+ // Task 1493 — the manager's elementId rides the personId field
24156
+ // end-to-end (managerSpawn → /public-spawn → spawn args → work env as
24157
+ // RAISED_BY_PERSON_ID). It is also this passive session's identity.
24158
+ personId: raisedByPersonId
23678
24159
  });
23679
24160
  if ("error" in result2) {
23680
24161
  console.error(`[wa-passive] op=dispatch-failed account=${effectiveAccountId} msgId=${msgId ?? "none"} error=${result2.error}`);
24162
+ return;
23681
24163
  }
24164
+ await recordPassiveDispatch({
24165
+ accountId: effectiveAccountId,
24166
+ sessionId: result2.sessionId,
24167
+ msgId: msgId ?? ""
24168
+ });
23682
24169
  return;
23683
24170
  }
23684
24171
  if (role === "admin") {
23685
24172
  const managesAccount = effectiveAccountId !== accountId ? effectiveAccountId : "none";
23686
24173
  console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
23687
24174
  }
23688
- const req = buildWaSpawnRequest({ accountId: effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath });
24175
+ const req = buildWaSpawnRequest({ accountId: effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, sessionIdOverride });
23689
24176
  const userId = role === "admin" ? resolveAdminUserId({ accountId: effectiveAccountId, senderPhone: senderId }) ?? void 0 : void 0;
23690
24177
  if (role === "admin") {
23691
24178
  console.error(`[whatsapp-native] op=admin-identity senderId=${senderId} userId=${userId ?? "owner-fallback"}`);
@@ -23714,7 +24201,7 @@ var waGateway = new WaGateway({
23714
24201
  nativeFileFollowers.set(senderId, ac);
23715
24202
  }
23716
24203
  });
23717
- app56.route("/", waGateway.routes());
24204
+ app60.route("/", waGateway.routes());
23718
24205
  waGateway.startSweeper();
23719
24206
  var webchatGateway = new WebchatGateway({
23720
24207
  fetchStandingRules: fetchAccountStandingRules,
@@ -23798,7 +24285,7 @@ var webchatGateway = new WebchatGateway({
23798
24285
  firePublicSessionEndReview: (input) => firePublicSessionEndReview(input)
23799
24286
  });
23800
24287
  setWebchatGateway(webchatGateway);
23801
- app56.route("/", webchatGateway.routes());
24288
+ app60.route("/", webchatGateway.routes());
23802
24289
  webchatGateway.startSweeper();
23803
24290
  webchatGateway.startPublicReaper();
23804
24291
  var telegramFileFollowers = /* @__PURE__ */ new Map();
@@ -23806,8 +24293,8 @@ var telegramGateway = new TelegramGateway({
23806
24293
  fetchStandingRules: fetchAccountStandingRules,
23807
24294
  gatewayUrl: telegramGatewayUrl(),
23808
24295
  serverPath: telegramServerPath(),
23809
- ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath }) => {
23810
- const req = buildTelegramSpawnRequest({ accountId, senderId, role, personId, gatewayUrl, serverPath });
24296
+ ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath, sessionIdOverride }) => {
24297
+ const req = buildTelegramSpawnRequest({ accountId, senderId, role, personId, gatewayUrl, serverPath, sessionIdOverride });
23811
24298
  const result = await managerRcSpawn(req);
23812
24299
  if ("error" in result) {
23813
24300
  console.error(`[telegram-native] spawn-failed senderId=${senderId} role=${role} error=${result.error} status=${result.status}`);
@@ -23832,7 +24319,7 @@ var telegramGateway = new TelegramGateway({
23832
24319
  }
23833
24320
  });
23834
24321
  setTelegramGateway(telegramGateway);
23835
- app56.route("/", telegramGateway.routes());
24322
+ app60.route("/", telegramGateway.routes());
23836
24323
  telegramGateway.startSweeper();
23837
24324
  var chatRoutes = createChatRoutes({
23838
24325
  handleInbound: (input) => webchatGateway.handleInbound(input),
@@ -23865,19 +24352,19 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
23865
24352
  return managed.account;
23866
24353
  }
23867
24354
  });
23868
- app56.route("/api/channel/schedule-inject", scheduleInjectRoutes);
23869
- app56.use("*", clientIpMiddleware);
24355
+ app60.route("/api/channel/schedule-inject", scheduleInjectRoutes);
24356
+ app60.use("*", clientIpMiddleware);
23870
24357
  function allowsSameOriginFraming(path2) {
23871
24358
  return path2 === "/vnc-viewer.html" || path2.startsWith("/api/admin/attachment/") || path2.startsWith("/api/public-reader/attachment/") || path2 === "/api/admin/files/download";
23872
24359
  }
23873
- app56.use("*", async (c, next) => {
24360
+ app60.use("*", async (c, next) => {
23874
24361
  await next();
23875
24362
  c.header("X-Content-Type-Options", "nosniff");
23876
24363
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
23877
24364
  c.header("X-Frame-Options", allowsSameOriginFraming(c.req.path) ? "SAMEORIGIN" : "DENY");
23878
24365
  });
23879
24366
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
23880
- app56.use("*", async (c, next) => {
24367
+ app60.use("*", async (c, next) => {
23881
24368
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
23882
24369
  await next();
23883
24370
  return;
@@ -23895,7 +24382,7 @@ app56.use("*", async (c, next) => {
23895
24382
  });
23896
24383
  }
23897
24384
  });
23898
- app56.use("*", async (c, next) => {
24385
+ app60.use("*", async (c, next) => {
23899
24386
  const host = (c.req.header("host") ?? "").split(":")[0];
23900
24387
  if (isOperatorHost(host, getOperatorDomains()) || !isPublicHost(host)) {
23901
24388
  await next();
@@ -23933,7 +24420,7 @@ function resolveRemoteAuthOpts(c) {
23933
24420
  return { ...brandLoginOpts, origin };
23934
24421
  }
23935
24422
  var MAX_LOGIN_BODY = 8 * 1024;
23936
- app56.post("/__remote-auth/login", async (c) => {
24423
+ app60.post("/__remote-auth/login", async (c) => {
23937
24424
  const client = clientFrom(c);
23938
24425
  const clientIp = client.ip || "unknown";
23939
24426
  if (!requestIsTlsTerminated(c)) {
@@ -23978,7 +24465,7 @@ app56.post("/__remote-auth/login", async (c) => {
23978
24465
  }
23979
24466
  });
23980
24467
  });
23981
- app56.get("/__remote-auth/logout", (c) => {
24468
+ app60.get("/__remote-auth/logout", (c) => {
23982
24469
  const client = clientFrom(c);
23983
24470
  const clientIp = client.ip || "unknown";
23984
24471
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -23991,7 +24478,7 @@ app56.get("/__remote-auth/logout", (c) => {
23991
24478
  }
23992
24479
  });
23993
24480
  });
23994
- app56.post("/__remote-auth/change-password", async (c) => {
24481
+ app60.post("/__remote-auth/change-password", async (c) => {
23995
24482
  const client = clientFrom(c);
23996
24483
  const clientIp = client.ip || "unknown";
23997
24484
  const rateLimited = checkRateLimit(client);
@@ -24050,13 +24537,13 @@ app56.post("/__remote-auth/change-password", async (c) => {
24050
24537
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "change", changeError: "Failed to save password", redirect }), 200);
24051
24538
  }
24052
24539
  });
24053
- app56.get("/__remote-auth/setup", (c) => {
24540
+ app60.get("/__remote-auth/setup", (c) => {
24054
24541
  if (isRemoteAuthConfigured()) {
24055
24542
  return c.redirect("/");
24056
24543
  }
24057
24544
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup" }), 200);
24058
24545
  });
24059
- app56.post("/__remote-auth/set-initial-password", async (c) => {
24546
+ app60.post("/__remote-auth/set-initial-password", async (c) => {
24060
24547
  if (isRemoteAuthConfigured()) {
24061
24548
  return c.redirect("/");
24062
24549
  }
@@ -24094,10 +24581,10 @@ app56.post("/__remote-auth/set-initial-password", async (c) => {
24094
24581
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
24095
24582
  }
24096
24583
  });
24097
- app56.get("/api/remote-auth/status", (c) => {
24584
+ app60.get("/api/remote-auth/status", (c) => {
24098
24585
  return c.json({ configured: isRemoteAuthConfigured() });
24099
24586
  });
24100
- app56.post("/api/remote-auth/set-password", async (c) => {
24587
+ app60.post("/api/remote-auth/set-password", async (c) => {
24101
24588
  let body;
24102
24589
  try {
24103
24590
  body = await c.req.json();
@@ -24136,10 +24623,10 @@ app56.post("/api/remote-auth/set-password", async (c) => {
24136
24623
  return c.json({ error: "Failed to save password" }, 500);
24137
24624
  }
24138
24625
  });
24139
- app56.route("/api/_client-error", client_error_default);
24626
+ app60.route("/api/_client-error", client_error_default);
24140
24627
  console.log("[client-error-route] mounted");
24141
24628
  var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
24142
- app56.use("*", async (c, next) => {
24629
+ app60.use("*", async (c, next) => {
24143
24630
  const host = (c.req.header("host") ?? "").split(":")[0];
24144
24631
  const path2 = c.req.path;
24145
24632
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
@@ -24183,26 +24670,26 @@ app56.use("*", async (c, next) => {
24183
24670
  }
24184
24671
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path2 }), 200);
24185
24672
  });
24186
- app56.route("/api/health", health_default);
24187
- app56.route("/api/chat", chatRoutes);
24188
- app56.route("/api/whatsapp", whatsapp_default);
24189
- app56.route("/api/whatsapp-reader", whatsapp_reader_default);
24190
- app56.route("/api/public-reader", public_reader_default);
24191
- app56.route("/api/webchat", createWebchatRoutes({
24673
+ app60.route("/api/health", health_default);
24674
+ app60.route("/api/chat", chatRoutes);
24675
+ app60.route("/api/whatsapp", whatsapp_default);
24676
+ app60.route("/api/whatsapp-reader", whatsapp_reader_default);
24677
+ app60.route("/api/public-reader", public_reader_default);
24678
+ app60.route("/api/webchat", createWebchatRoutes({
24192
24679
  handleInbound: (input) => webchatGateway.handleInbound(input),
24193
24680
  // Task 940 — the permission relay's pointer read + verdict write.
24194
24681
  pendingPromptFor: (key) => webchatGateway.pendingPromptFor(key),
24195
24682
  deliveryFailureFor: (key) => webchatGateway.deliveryFailureFor(key),
24196
24683
  resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
24197
24684
  }));
24198
- app56.route("/api/webchat/greeting", webchat_greeting_default);
24199
- app56.route("/api/telegram", telegram_default);
24200
- app56.route("/api/quickbooks", quickbooks_default);
24201
- app56.route("/api/onboarding", onboarding_default);
24202
- app56.route("/api/admin", admin_default);
24203
- app56.route("/api/access", access_default);
24204
- app56.route("/api/session", session_default2);
24205
- app56.route("/api/calendar", calendar_public_default);
24685
+ app60.route("/api/webchat/greeting", webchat_greeting_default);
24686
+ app60.route("/api/telegram", telegram_default);
24687
+ app60.route("/api/quickbooks", quickbooks_default);
24688
+ app60.route("/api/onboarding", onboarding_default);
24689
+ app60.route("/api/admin", admin_default);
24690
+ app60.route("/api/access", access_default);
24691
+ app60.route("/api/session", session_default2);
24692
+ app60.route("/api/calendar", calendar_public_default);
24206
24693
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
24207
24694
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
24208
24695
  var IMAGE_MIME = {
@@ -24214,7 +24701,7 @@ var IMAGE_MIME = {
24214
24701
  ".svg": "image/svg+xml",
24215
24702
  ".ico": "image/x-icon"
24216
24703
  };
24217
- app56.get("/agent-assets/:slug/:filename", (c) => {
24704
+ app60.get("/agent-assets/:slug/:filename", (c) => {
24218
24705
  const slug = c.req.param("slug");
24219
24706
  const filename = c.req.param("filename");
24220
24707
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -24249,7 +24736,7 @@ app56.get("/agent-assets/:slug/:filename", (c) => {
24249
24736
  "Cache-Control": "public, max-age=3600"
24250
24737
  });
24251
24738
  });
24252
- app56.get("/generated/:filename", (c) => {
24739
+ app60.get("/generated/:filename", (c) => {
24253
24740
  const filename = c.req.param("filename");
24254
24741
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
24255
24742
  console.error(`[generated] serve file=${filename} status=403`);
@@ -24279,10 +24766,10 @@ app56.get("/generated/:filename", (c) => {
24279
24766
  "Cache-Control": "public, max-age=86400"
24280
24767
  });
24281
24768
  });
24282
- app56.route("/sites", sites_default);
24283
- app56.route("/listings", listings_default);
24284
- app56.route("/v", visitor_event_default);
24285
- app56.route("/v", visitor_consent_default);
24769
+ app60.route("/sites", sites_default);
24770
+ app60.route("/listings", listings_default);
24771
+ app60.route("/v", visitor_event_default);
24772
+ app60.route("/v", visitor_consent_default);
24286
24773
  var htmlCache = /* @__PURE__ */ new Map();
24287
24774
  var brandLogoPath = "/brand/maxy-monochrome.png";
24288
24775
  var brandIconPath = "/brand/maxy-monochrome.png";
@@ -24437,7 +24924,7 @@ function brandedPublicHtml(agentSlug) {
24437
24924
  function agentUnavailableHtml() {
24438
24925
  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>`;
24439
24926
  }
24440
- app56.get("/", (c) => {
24927
+ app60.get("/", (c) => {
24441
24928
  const host = (c.req.header("host") ?? "").split(":")[0];
24442
24929
  const klass = classifyHost(host, getOperatorDomains(), isPublicHost);
24443
24930
  if (klass === "operator") {
@@ -24459,12 +24946,12 @@ app56.get("/", (c) => {
24459
24946
  console.log(`[host-class] host=${host} class=admin served=index.html`);
24460
24947
  return c.html(cachedHtml("index.html"));
24461
24948
  });
24462
- app56.get("/public", (c) => {
24949
+ app60.get("/public", (c) => {
24463
24950
  const host = (c.req.header("host") ?? "").split(":")[0];
24464
24951
  if (isPublicHost(host)) return c.text("Not found", 404);
24465
24952
  return c.html(cachedHtml("public.html"));
24466
24953
  });
24467
- app56.get("/public-chat", (c) => {
24954
+ app60.get("/public-chat", (c) => {
24468
24955
  const host = (c.req.header("host") ?? "").split(":")[0];
24469
24956
  if (isPublicHost(host)) return c.text("Not found", 404);
24470
24957
  return c.html(cachedHtml("public.html"));
@@ -24483,9 +24970,9 @@ async function logViewerFetch(c, next) {
24483
24970
  duration_ms: Date.now() - start
24484
24971
  });
24485
24972
  }
24486
- app56.use("/vnc-viewer.html", logViewerFetch);
24487
- app56.use("/vnc-popout.html", logViewerFetch);
24488
- app56.get("/vnc-popout.html", (c) => {
24973
+ app60.use("/vnc-viewer.html", logViewerFetch);
24974
+ app60.use("/vnc-popout.html", logViewerFetch);
24975
+ app60.get("/vnc-popout.html", (c) => {
24489
24976
  let html = htmlCache.get("vnc-popout.html");
24490
24977
  if (!html) {
24491
24978
  html = readFileSync35(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
@@ -24498,7 +24985,7 @@ app56.get("/vnc-popout.html", (c) => {
24498
24985
  }
24499
24986
  return c.html(html);
24500
24987
  });
24501
- app56.post("/api/vnc/client-event", async (c) => {
24988
+ app60.post("/api/vnc/client-event", async (c) => {
24502
24989
  let body;
24503
24990
  try {
24504
24991
  body = await c.req.json();
@@ -24519,11 +25006,11 @@ app56.post("/api/vnc/client-event", async (c) => {
24519
25006
  });
24520
25007
  return c.json({ ok: true });
24521
25008
  });
24522
- app56.get("/g/:slug", (c) => {
25009
+ app60.get("/g/:slug", (c) => {
24523
25010
  return c.html(brandedPublicHtml(resolveDefaultSlug() ?? void 0));
24524
25011
  });
24525
25012
  for (const pwa of PWA_SURFACES) {
24526
- app56.get(pwa.manifestPath, (c) => {
25013
+ app60.get(pwa.manifestPath, (c) => {
24527
25014
  const manifest = buildManifest(pwa, {
24528
25015
  productName: BRAND.productName,
24529
25016
  appIcon192: brandAppIcon192Path,
@@ -24539,7 +25026,7 @@ for (const pwa of PWA_SURFACES) {
24539
25026
  return c.body(JSON.stringify(manifest));
24540
25027
  });
24541
25028
  }
24542
- app56.get("/sw.js", (c) => {
25029
+ app60.get("/sw.js", (c) => {
24543
25030
  if (SW_SOURCE == null) {
24544
25031
  console.error("[pwa] op=sw status=500 reason=sw-source-missing");
24545
25032
  return c.text("Service worker unavailable", 500);
@@ -24548,12 +25035,12 @@ app56.get("/sw.js", (c) => {
24548
25035
  console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
24549
25036
  return c.body(SW_SOURCE);
24550
25037
  });
24551
- app56.get("/graph", (c) => {
25038
+ app60.get("/graph", (c) => {
24552
25039
  const host = (c.req.header("host") ?? "").split(":")[0];
24553
25040
  if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
24554
25041
  return c.html(cachedHtml("graph.html"));
24555
25042
  });
24556
- app56.get("/chat", (c) => {
25043
+ app60.get("/chat", (c) => {
24557
25044
  const host = (c.req.header("host") ?? "").split(":")[0];
24558
25045
  if (isOperatorHost(host, getOperatorDomains())) {
24559
25046
  console.log(`[host-class] host=${host} class=operator served=operator.html`);
@@ -24562,22 +25049,22 @@ app56.get("/chat", (c) => {
24562
25049
  if (isPublicHost(host)) return c.text("Not found", 404);
24563
25050
  return c.html(cachedHtml("chat.html"));
24564
25051
  });
24565
- app56.get("/data", (c) => {
25052
+ app60.get("/data", (c) => {
24566
25053
  const host = (c.req.header("host") ?? "").split(":")[0];
24567
25054
  if (isPublicHost(host)) return c.text("Not found", 404);
24568
25055
  return c.html(cachedHtml("data.html"));
24569
25056
  });
24570
- app56.get("/calendar", (c) => {
25057
+ app60.get("/calendar", (c) => {
24571
25058
  const host = (c.req.header("host") ?? "").split(":")[0];
24572
25059
  if (isPublicHost(host)) return c.text("Not found", 404);
24573
25060
  return c.html(cachedHtml("calendar.html"));
24574
25061
  });
24575
- app56.get("/browser", (c) => {
25062
+ app60.get("/browser", (c) => {
24576
25063
  const host = (c.req.header("host") ?? "").split(":")[0];
24577
25064
  if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
24578
25065
  return c.html(cachedHtml("browser.html"));
24579
25066
  });
24580
- app56.get("/:slug", async (c, next) => {
25067
+ app60.get("/:slug", async (c, next) => {
24581
25068
  const slug = c.req.param("slug");
24582
25069
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
24583
25070
  const account = resolveAccount();
@@ -24592,13 +25079,13 @@ app56.get("/:slug", async (c, next) => {
24592
25079
  await next();
24593
25080
  });
24594
25081
  if (brandFaviconPath !== "/favicon.ico") {
24595
- app56.get("/favicon.ico", (c) => {
25082
+ app60.get("/favicon.ico", (c) => {
24596
25083
  c.header("Cache-Control", "public, max-age=300");
24597
25084
  return c.redirect(brandFaviconPath, 302);
24598
25085
  });
24599
25086
  }
24600
- app56.use("/*", serveStatic({ root: "./public" }));
24601
- app56.all("*", (c) => {
25087
+ app60.use("/*", serveStatic({ root: "./public" }));
25088
+ app60.all("*", (c) => {
24602
25089
  const host = (c.req.header("host") ?? "").split(":")[0];
24603
25090
  const path2 = c.req.path;
24604
25091
  if (isPublicHost(host)) {
@@ -24612,7 +25099,7 @@ app56.all("*", (c) => {
24612
25099
  });
24613
25100
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
24614
25101
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
24615
- var httpServer = serve({ fetch: app56.fetch, port, hostname });
25102
+ var httpServer = serve({ fetch: app60.fetch, port, hostname });
24616
25103
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
24617
25104
  {
24618
25105
  const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join35(__dirname, "..");
@@ -24636,6 +25123,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
24636
25123
  const loop = setInterval(runReconcile, RECONCILE_INTERVAL_MS);
24637
25124
  loop.unref();
24638
25125
  }
25126
+ startTimeEntryCensus(() => getSession());
24639
25127
  {
24640
25128
  const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join35(__dirname, "..");
24641
25129
  const auditScript = resolve33(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
@@ -24712,7 +25200,7 @@ for (const m of SUBAPP_MANIFEST) {
24712
25200
  }
24713
25201
  try {
24714
25202
  const registered = [];
24715
- for (const r of app56.routes ?? []) {
25203
+ for (const r of app60.routes ?? []) {
24716
25204
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
24717
25205
  if (AGENT_SLUG_PATTERN.test(r.path)) {
24718
25206
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });