@rubytech/create-maxy-code 0.1.601 → 0.1.603

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 (257) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/neo4j/schema.cypher +7 -0
  3. package/payload/platform/plugins/admin/hooks/video-frames-inject.sh +141 -0
  4. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +19 -1
  5. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
  6. package/payload/platform/plugins/docs/references/admin-ui.md +2 -0
  7. package/payload/platform/plugins/docs/references/telegram-guide.md +16 -0
  8. package/payload/platform/plugins/scheduling/PLUGIN.md +2 -0
  9. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js +67 -1
  10. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js.map +1 -1
  11. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js +89 -1
  12. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js.map +1 -1
  13. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/outage-marker.test.d.ts +2 -0
  14. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/outage-marker.test.d.ts.map +1 -0
  15. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/outage-marker.test.js +160 -0
  16. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/outage-marker.test.js.map +1 -0
  17. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js +16 -0
  18. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js.map +1 -1
  19. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +31 -1
  20. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -1
  21. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js +42 -4
  22. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -1
  23. package/payload/platform/plugins/scheduling/mcp/dist/lib/outage-marker.d.ts +101 -0
  24. package/payload/platform/plugins/scheduling/mcp/dist/lib/outage-marker.d.ts.map +1 -0
  25. package/payload/platform/plugins/scheduling/mcp/dist/lib/outage-marker.js +130 -0
  26. package/payload/platform/plugins/scheduling/mcp/dist/lib/outage-marker.js.map +1 -0
  27. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +10 -1
  28. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -1
  29. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -1
  30. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +24 -0
  31. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
  32. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +89 -0
  33. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
  34. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +67 -1
  35. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
  36. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js +31 -0
  37. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js.map +1 -1
  38. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +10 -1
  39. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  40. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +46 -1
  41. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  42. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +72 -4
  43. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  44. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +81 -4
  45. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
  46. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +50 -0
  47. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
  48. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +48 -7
  49. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
  50. package/payload/platform/plugins/telegram/PLUGIN.md +4 -0
  51. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin-registry.test.js +33 -1
  52. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin-registry.test.js.map +1 -1
  53. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin.test.js +166 -1
  54. package/payload/platform/plugins/telegram/mcp/dist/__tests__/public-pin.test.js.map +1 -1
  55. package/payload/platform/plugins/telegram/mcp/dist/index.js +39 -14
  56. package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
  57. package/payload/platform/plugins/telegram/mcp/dist/lib/public-pin.d.ts +95 -0
  58. package/payload/platform/plugins/telegram/mcp/dist/lib/public-pin.d.ts.map +1 -1
  59. package/payload/platform/plugins/telegram/mcp/dist/lib/public-pin.js +125 -0
  60. package/payload/platform/plugins/telegram/mcp/dist/lib/public-pin.js.map +1 -1
  61. package/payload/platform/plugins/telegram/skills/configure/SKILL.md +15 -1
  62. package/payload/platform/services/telegram-channel/dist/notification.d.ts +6 -0
  63. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  64. package/payload/platform/services/telegram-channel/dist/notification.js +12 -5
  65. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  66. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +5 -0
  67. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  68. package/payload/platform/services/whatsapp-channel/dist/notification.js +9 -3
  69. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  70. package/payload/platform/templates/account-settings.json +6 -0
  71. package/payload/server/chunk-CYU75QAV.js +377 -0
  72. package/payload/server/{chunk-34FPJYHN.js → chunk-GI5AVCRG.js} +87 -146
  73. package/payload/server/chunk-KUVNCAE4.js +77 -0
  74. package/payload/server/{manager-7BXVB76I.js → manager-VDHT3YYI.js} +2 -1
  75. package/payload/server/maxy-edge.js +33 -1
  76. package/payload/server/public/activity.html +6 -6
  77. package/payload/server/public/agents.html +5 -5
  78. package/payload/server/public/assets/{AdminLoginScreens-B3W6J6Bl.js → AdminLoginScreens-DI5bNlX8.js} +1 -1
  79. package/payload/server/public/assets/AdminLoginScreens-DI5bNlX8.js.br +0 -0
  80. package/payload/server/public/assets/AdminLoginScreens-DI5bNlX8.js.gz +0 -0
  81. package/payload/server/public/assets/{AdminShell-C3R-vMZ5.js → AdminShell-B-iX9ahJ.js} +1 -1
  82. package/payload/server/public/assets/AdminShell-B-iX9ahJ.js.br +0 -0
  83. package/payload/server/public/assets/AdminShell-B-iX9ahJ.js.gz +0 -0
  84. package/payload/server/public/assets/{activity-tzv68iBs.js → activity-CqgFwANo.js} +1 -1
  85. package/payload/server/public/assets/activity-CqgFwANo.js.br +0 -0
  86. package/payload/server/public/assets/activity-CqgFwANo.js.gz +0 -0
  87. package/payload/server/public/assets/admin-K63n6ybW.js +1 -0
  88. package/payload/server/public/assets/admin-K63n6ybW.js.br +0 -0
  89. package/payload/server/public/assets/admin-K63n6ybW.js.gz +0 -0
  90. package/payload/server/public/assets/{agents-DUZfeY3X.js → agents-cue5Zf8X.js} +1 -1
  91. package/payload/server/public/assets/agents-cue5Zf8X.js.br +0 -0
  92. package/payload/server/public/assets/agents-cue5Zf8X.js.gz +0 -0
  93. package/payload/server/public/assets/{browser-DwLaXqjO.js → browser-CqDBcKwU.js} +1 -1
  94. package/payload/server/public/assets/browser-CqDBcKwU.js.br +0 -0
  95. package/payload/server/public/assets/browser-CqDBcKwU.js.gz +0 -0
  96. package/payload/server/public/assets/{calendar-Cli8Qjl9.js → calendar-BgzuW5Rt.js} +1 -1
  97. package/payload/server/public/assets/calendar-BgzuW5Rt.js.br +0 -0
  98. package/payload/server/public/assets/calendar-BgzuW5Rt.js.gz +0 -0
  99. package/payload/server/public/assets/chat-HYZViu2u.js +1 -0
  100. package/payload/server/public/assets/chat-HYZViu2u.js.br +3 -0
  101. package/payload/server/public/assets/chat-HYZViu2u.js.gz +0 -0
  102. package/payload/server/public/assets/chevron-left-C5FXH9r_.js +1 -0
  103. package/payload/server/public/assets/chevron-left-C5FXH9r_.js.br +0 -0
  104. package/payload/server/public/assets/clock-DxPtyejC.js +1 -0
  105. package/payload/server/public/assets/clock-DxPtyejC.js.br +0 -0
  106. package/payload/server/public/assets/clock-DxPtyejC.js.gz +0 -0
  107. package/payload/server/public/assets/{copy-yhhwfEbQ.js → copy-BNhZ0flH.js} +1 -1
  108. package/payload/server/public/assets/copy-BNhZ0flH.js.br +0 -0
  109. package/payload/server/public/assets/copy-BNhZ0flH.js.gz +0 -0
  110. package/payload/server/public/assets/data-kCBB8Z4Z.js +1 -0
  111. package/payload/server/public/assets/data-kCBB8Z4Z.js.br +0 -0
  112. package/payload/server/public/assets/data-kCBB8Z4Z.js.gz +0 -0
  113. package/payload/server/public/assets/{field-jg0YQNDy.js → field-X4OVA3vx.js} +1 -1
  114. package/payload/server/public/assets/field-X4OVA3vx.js.br +0 -0
  115. package/payload/server/public/assets/field-X4OVA3vx.js.gz +0 -0
  116. package/payload/server/public/assets/{file-text-D0EdW7h4.js → file-text-BddO_2EI.js} +1 -1
  117. package/payload/server/public/assets/file-text-BddO_2EI.js.br +0 -0
  118. package/payload/server/public/assets/file-text-BddO_2EI.js.gz +0 -0
  119. package/payload/server/public/assets/{graph--AUr4uBc.js → graph-l7tzHqvm.js} +1 -1
  120. package/payload/server/public/assets/graph-l7tzHqvm.js.br +0 -0
  121. package/payload/server/public/assets/graph-l7tzHqvm.js.gz +0 -0
  122. package/payload/server/public/assets/{graph-labels-DM0CiAX1.js → graph-labels-CtFFq5W2.js} +1 -1
  123. package/payload/server/public/assets/graph-labels-CtFFq5W2.js.br +0 -0
  124. package/payload/server/public/assets/graph-labels-CtFFq5W2.js.gz +0 -0
  125. package/payload/server/public/assets/jsx-runtime-bq0g4NlA.css +1 -0
  126. package/payload/server/public/assets/jsx-runtime-bq0g4NlA.css.br +0 -0
  127. package/payload/server/public/assets/jsx-runtime-bq0g4NlA.css.gz +0 -0
  128. package/payload/server/public/assets/{operator-CNYKStFX.js → operator-Dg-dwf7a.js} +1 -1
  129. package/payload/server/public/assets/operator-Dg-dwf7a.js.br +7 -0
  130. package/payload/server/public/assets/operator-Dg-dwf7a.js.gz +0 -0
  131. package/payload/server/public/assets/page-Ci1bfIbs.js +1 -0
  132. package/payload/server/public/assets/page-Ci1bfIbs.js.br +0 -0
  133. package/payload/server/public/assets/page-Ci1bfIbs.js.gz +0 -0
  134. package/payload/server/public/assets/page-Y9JmFq0Q.js +32 -0
  135. package/payload/server/public/assets/page-Y9JmFq0Q.js.br +0 -0
  136. package/payload/server/public/assets/page-Y9JmFq0Q.js.gz +0 -0
  137. package/payload/server/public/assets/play-BSZVGpQT.js +1 -0
  138. package/payload/server/public/assets/play-BSZVGpQT.js.br +0 -0
  139. package/payload/server/public/assets/play-BSZVGpQT.js.gz +0 -0
  140. package/payload/server/public/assets/{public-7gXDqCog.js → public-m66FAza_.js} +1 -1
  141. package/payload/server/public/assets/public-m66FAza_.js.br +0 -0
  142. package/payload/server/public/assets/public-m66FAza_.js.gz +0 -0
  143. package/payload/server/public/assets/{rotate-ccw-DhqNTPpo.js → rotate-ccw-DTOJmOxB.js} +1 -1
  144. package/payload/server/public/assets/rotate-ccw-DTOJmOxB.js.br +0 -0
  145. package/payload/server/public/assets/rotate-ccw-DTOJmOxB.js.gz +0 -0
  146. package/payload/server/public/assets/{routines-DN_yDtEj.js → routines-6fxZNacQ.js} +1 -1
  147. package/payload/server/public/assets/routines-6fxZNacQ.js.br +0 -0
  148. package/payload/server/public/assets/routines-6fxZNacQ.js.gz +0 -0
  149. package/payload/server/public/assets/{skills-pb_3BdOl.js → skills-51mqx-s5.js} +1 -1
  150. package/payload/server/public/assets/skills-51mqx-s5.js.br +0 -0
  151. package/payload/server/public/assets/skills-51mqx-s5.js.gz +0 -0
  152. package/payload/server/public/assets/{tasks-BQm4boat.js → tasks-D4y-Cwa3.js} +1 -1
  153. package/payload/server/public/assets/tasks-D4y-Cwa3.js.br +0 -0
  154. package/payload/server/public/assets/tasks-D4y-Cwa3.js.gz +0 -0
  155. package/payload/server/public/assets/{triangle-alert-d7yjaIqv.js → triangle-alert-BzCjDFts.js} +1 -1
  156. package/payload/server/public/assets/triangle-alert-BzCjDFts.js.br +0 -0
  157. package/payload/server/public/assets/triangle-alert-BzCjDFts.js.gz +0 -0
  158. package/payload/server/public/assets/{useCopyFeedback-CZ6Qhfkj.js → useCopyFeedback-BG7h37Rx.js} +1 -1
  159. package/payload/server/public/assets/useCopyFeedback-BG7h37Rx.js.br +2 -0
  160. package/payload/server/public/assets/useCopyFeedback-BG7h37Rx.js.gz +0 -0
  161. package/payload/server/public/assets/{useMediaQuery-R1smYLx4.js → useMediaQuery-CqwktQr_.js} +1 -1
  162. package/payload/server/public/assets/useMediaQuery-CqwktQr_.js.br +0 -0
  163. package/payload/server/public/assets/useMediaQuery-CqwktQr_.js.gz +0 -0
  164. package/payload/server/public/assets/useVoiceRecorder-CAZcUc1s.js +2 -0
  165. package/payload/server/public/assets/useVoiceRecorder-CAZcUc1s.js.br +0 -0
  166. package/payload/server/public/assets/useVoiceRecorder-CAZcUc1s.js.gz +0 -0
  167. package/payload/server/public/assets/{wrench-DL1pbaCs.js → wrench-B2WKLLQG.js} +1 -1
  168. package/payload/server/public/assets/wrench-B2WKLLQG.js.br +0 -0
  169. package/payload/server/public/assets/wrench-B2WKLLQG.js.gz +0 -0
  170. package/payload/server/public/browser.html +5 -5
  171. package/payload/server/public/calendar.html +8 -8
  172. package/payload/server/public/chat.html +14 -14
  173. package/payload/server/public/data.html +12 -12
  174. package/payload/server/public/field.html +3 -3
  175. package/payload/server/public/graph.html +10 -10
  176. package/payload/server/public/index.html +14 -14
  177. package/payload/server/public/operator.html +15 -15
  178. package/payload/server/public/public.html +14 -14
  179. package/payload/server/public/routines.html +8 -8
  180. package/payload/server/public/skills.html +6 -6
  181. package/payload/server/public/tasks.html +7 -7
  182. package/payload/server/server.js +1067 -783
  183. package/payload/server/video-extract.js +36 -0
  184. package/payload/server/public/assets/AdminLoginScreens-B3W6J6Bl.js.br +0 -0
  185. package/payload/server/public/assets/AdminLoginScreens-B3W6J6Bl.js.gz +0 -0
  186. package/payload/server/public/assets/AdminShell-C3R-vMZ5.js.br +0 -0
  187. package/payload/server/public/assets/AdminShell-C3R-vMZ5.js.gz +0 -0
  188. package/payload/server/public/assets/activity-tzv68iBs.js.br +0 -0
  189. package/payload/server/public/assets/activity-tzv68iBs.js.gz +0 -0
  190. package/payload/server/public/assets/admin-CzW_9jm3.js +0 -1
  191. package/payload/server/public/assets/admin-CzW_9jm3.js.br +0 -0
  192. package/payload/server/public/assets/admin-CzW_9jm3.js.gz +0 -0
  193. package/payload/server/public/assets/agents-DUZfeY3X.js.br +0 -0
  194. package/payload/server/public/assets/agents-DUZfeY3X.js.gz +0 -0
  195. package/payload/server/public/assets/browser-DwLaXqjO.js.br +0 -0
  196. package/payload/server/public/assets/browser-DwLaXqjO.js.gz +0 -0
  197. package/payload/server/public/assets/calendar-Cli8Qjl9.js.br +0 -0
  198. package/payload/server/public/assets/calendar-Cli8Qjl9.js.gz +0 -0
  199. package/payload/server/public/assets/chat-ByAgBgm1.js +0 -1
  200. package/payload/server/public/assets/chat-ByAgBgm1.js.br +0 -1
  201. package/payload/server/public/assets/chat-ByAgBgm1.js.gz +0 -0
  202. package/payload/server/public/assets/chevron-left-BIYVnwvk.js +0 -1
  203. package/payload/server/public/assets/chevron-left-BIYVnwvk.js.br +0 -0
  204. package/payload/server/public/assets/clock-Dzy79hq3.js +0 -1
  205. package/payload/server/public/assets/clock-Dzy79hq3.js.br +0 -0
  206. package/payload/server/public/assets/clock-Dzy79hq3.js.gz +0 -0
  207. package/payload/server/public/assets/copy-yhhwfEbQ.js.br +0 -0
  208. package/payload/server/public/assets/copy-yhhwfEbQ.js.gz +0 -0
  209. package/payload/server/public/assets/data-CsMMki2A.js +0 -1
  210. package/payload/server/public/assets/data-CsMMki2A.js.br +0 -0
  211. package/payload/server/public/assets/data-CsMMki2A.js.gz +0 -0
  212. package/payload/server/public/assets/field-jg0YQNDy.js.br +0 -0
  213. package/payload/server/public/assets/field-jg0YQNDy.js.gz +0 -0
  214. package/payload/server/public/assets/file-text-D0EdW7h4.js.br +0 -0
  215. package/payload/server/public/assets/file-text-D0EdW7h4.js.gz +0 -0
  216. package/payload/server/public/assets/graph--AUr4uBc.js.br +0 -0
  217. package/payload/server/public/assets/graph--AUr4uBc.js.gz +0 -0
  218. package/payload/server/public/assets/graph-labels-DM0CiAX1.js.br +0 -0
  219. package/payload/server/public/assets/graph-labels-DM0CiAX1.js.gz +0 -0
  220. package/payload/server/public/assets/jsx-runtime-lfcjzaWi.css +0 -1
  221. package/payload/server/public/assets/jsx-runtime-lfcjzaWi.css.br +0 -0
  222. package/payload/server/public/assets/jsx-runtime-lfcjzaWi.css.gz +0 -0
  223. package/payload/server/public/assets/operator-CNYKStFX.js.br +0 -0
  224. package/payload/server/public/assets/operator-CNYKStFX.js.gz +0 -0
  225. package/payload/server/public/assets/page-DSsEpl8J.js +0 -32
  226. package/payload/server/public/assets/page-DSsEpl8J.js.br +0 -0
  227. package/payload/server/public/assets/page-DSsEpl8J.js.gz +0 -0
  228. package/payload/server/public/assets/page-DjhmwxnY.js +0 -1
  229. package/payload/server/public/assets/page-DjhmwxnY.js.br +0 -0
  230. package/payload/server/public/assets/page-DjhmwxnY.js.gz +0 -0
  231. package/payload/server/public/assets/play-DhZlml5b.js +0 -1
  232. package/payload/server/public/assets/play-DhZlml5b.js.br +0 -0
  233. package/payload/server/public/assets/play-DhZlml5b.js.gz +0 -0
  234. package/payload/server/public/assets/public-7gXDqCog.js.br +0 -0
  235. package/payload/server/public/assets/public-7gXDqCog.js.gz +0 -0
  236. package/payload/server/public/assets/rotate-ccw-DhqNTPpo.js.br +0 -0
  237. package/payload/server/public/assets/rotate-ccw-DhqNTPpo.js.gz +0 -0
  238. package/payload/server/public/assets/routines-DN_yDtEj.js.br +0 -0
  239. package/payload/server/public/assets/routines-DN_yDtEj.js.gz +0 -0
  240. package/payload/server/public/assets/skills-pb_3BdOl.js.br +0 -0
  241. package/payload/server/public/assets/skills-pb_3BdOl.js.gz +0 -0
  242. package/payload/server/public/assets/tasks-BQm4boat.js.br +0 -0
  243. package/payload/server/public/assets/tasks-BQm4boat.js.gz +0 -0
  244. package/payload/server/public/assets/triangle-alert-d7yjaIqv.js.br +0 -0
  245. package/payload/server/public/assets/triangle-alert-d7yjaIqv.js.gz +0 -0
  246. package/payload/server/public/assets/useCopyFeedback-CZ6Qhfkj.js.br +0 -0
  247. package/payload/server/public/assets/useCopyFeedback-CZ6Qhfkj.js.gz +0 -0
  248. package/payload/server/public/assets/useMediaQuery-R1smYLx4.js.br +0 -0
  249. package/payload/server/public/assets/useMediaQuery-R1smYLx4.js.gz +0 -0
  250. package/payload/server/public/assets/useVoiceRecorder-DM1v6sRa.js +0 -2
  251. package/payload/server/public/assets/useVoiceRecorder-DM1v6sRa.js.br +0 -0
  252. package/payload/server/public/assets/useVoiceRecorder-DM1v6sRa.js.gz +0 -0
  253. package/payload/server/public/assets/wrench-DL1pbaCs.js.br +0 -0
  254. package/payload/server/public/assets/wrench-DL1pbaCs.js.gz +0 -0
  255. /package/payload/server/public/assets/{jsx-runtime-BtDK2X5t.js → jsx-runtime-UK6hgBWO.js} +0 -0
  256. /package/payload/server/public/assets/{jsx-runtime-BtDK2X5t.js.br → jsx-runtime-UK6hgBWO.js.br} +0 -0
  257. /package/payload/server/public/assets/{jsx-runtime-BtDK2X5t.js.gz → jsx-runtime-UK6hgBWO.js.gz} +0 -0
@@ -71,6 +71,11 @@ import {
71
71
  vncLog,
72
72
  walkPremiumBundles
73
73
  } from "./chunk-MNN2RWJR.js";
74
+ import {
75
+ audioExtension,
76
+ isAudioMime,
77
+ isVideoMime
78
+ } from "./chunk-KUVNCAE4.js";
74
79
  import {
75
80
  ACCOUNTS_ROOT,
76
81
  DATA_ROOT,
@@ -210,13 +215,12 @@ import {
210
215
  storeMediaBuffer,
211
216
  storePublicAttachment,
212
217
  toWhatsappJid,
213
- transcribe,
214
218
  validateAccountIdEnv,
215
219
  validateFilePathInAccount,
216
220
  waitForConnection,
217
221
  warnNonUuidAccount,
218
222
  writeChatMetadataMerged
219
- } from "./chunk-34FPJYHN.js";
223
+ } from "./chunk-GI5AVCRG.js";
220
224
  import {
221
225
  ACCOUNTS_DIR,
222
226
  AGENT_SLUG_PATTERN,
@@ -282,6 +286,10 @@ import {
282
286
  verifyConversationOwnership,
283
287
  writeAdminUserAndPerson
284
288
  } from "./chunk-6AQYMT6C.js";
289
+ import {
290
+ extractVideo,
291
+ transcribe
292
+ } from "./chunk-CYU75QAV.js";
285
293
  import {
286
294
  D1_MAX_SQL_STATEMENT_BYTES,
287
295
  D1_QUERY_MAX_BODY_BYTES,
@@ -604,8 +612,289 @@ var require_dist4 = __commonJS({
604
612
  }
605
613
  });
606
614
 
607
- // ../lib/telegram-button-labels/dist/index.js
615
+ // ../lib/graph-trash/dist/index.js
608
616
  var require_dist5 = __commonJS({
617
+ "../lib/graph-trash/dist/index.js"(exports) {
618
+ "use strict";
619
+ Object.defineProperty(exports, "__esModule", { value: true });
620
+ exports.TRASH_METADATA_PROPS = void 0;
621
+ exports.trashNode = trashNode2;
622
+ exports.restoreNode = restoreNode2;
623
+ exports.emptyTrash = emptyTrash;
624
+ exports.notTrashed = notTrashed4;
625
+ exports.uniqueKeysForLabels = uniqueKeysForLabels;
626
+ var UNIQUE_KEYS_BY_LABEL2 = {
627
+ Person: ["email", "telephone"],
628
+ Service: ["serviceId"],
629
+ LocalBusiness: ["accountId"],
630
+ Task: ["taskId"],
631
+ Event: ["eventId"],
632
+ // :KnowledgeDocument is keyed on `attachmentId` (document writer path —
633
+ // file/URL/obsidian/contract attachment + legacy Gmail-thread shape under
634
+ // `source:'email'`). Task 397 split the conversation transcripts off into
635
+ // `:ConversationArchive`, which carries `conversationIdentity`.
636
+ KnowledgeDocument: ["attachmentId"],
637
+ ConversationArchive: ["conversationIdentity"],
638
+ DigitalDocument: ["attachmentId"],
639
+ Conversation: ["sessionId"],
640
+ Message: ["messageId"],
641
+ Workflow: ["workflowId"],
642
+ WorkflowStep: ["stepId"],
643
+ WorkflowRun: ["runId"],
644
+ Preference: ["preferenceId"],
645
+ Email: ["emailId", "messageId"],
646
+ AdminUser: ["userId"],
647
+ ToolCall: ["callId"],
648
+ // Composite component nulls — frees the composite constraint:
649
+ AccessGrant: ["contactValue"],
650
+ // composite (contactValue, agentSlug, accountId)
651
+ UserProfile: ["userId"]
652
+ // composite (accountId, userId)
653
+ };
654
+ var TRASH_PROP_NAMES = [
655
+ "trashedAt",
656
+ "trashedBy",
657
+ "trashReason",
658
+ "_trashedKeys"
659
+ ];
660
+ async function trashNode2(params) {
661
+ const { session, accountId, elementId, by, reason } = params;
662
+ const lookup = await session.run(`MATCH (n) WHERE elementId(n) = $eid AND n.accountId = $accountId
663
+ RETURN labels(n) AS labels, properties(n) AS props`, { eid: elementId, accountId });
664
+ if (lookup.records.length === 0) {
665
+ throw new Error(`trashNode: node not found (elementId=${elementId} accountId=${accountId.slice(0, 8)}\u2026)`);
666
+ }
667
+ const allLabels = lookup.records[0].get("labels");
668
+ const props = lookup.records[0].get("props");
669
+ const baseLabels = allLabels.filter((l) => l !== "Trashed");
670
+ if (allLabels.includes("Trashed")) {
671
+ return {
672
+ trashed: false,
673
+ alreadyTrashed: true,
674
+ nodeId: elementId,
675
+ labels: baseLabels,
676
+ trashedAt: String(props.trashedAt ?? ""),
677
+ originalKeys: {}
678
+ };
679
+ }
680
+ const uniqueKeys = /* @__PURE__ */ new Set();
681
+ for (const label of baseLabels) {
682
+ for (const key2 of UNIQUE_KEYS_BY_LABEL2[label] ?? [])
683
+ uniqueKeys.add(key2);
684
+ }
685
+ const originalKeys = {};
686
+ for (const k of uniqueKeys) {
687
+ if (props[k] !== void 0 && props[k] !== null)
688
+ originalKeys[k] = props[k];
689
+ }
690
+ const setNullClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = null`).join(", ");
691
+ const setNullSuffix = setNullClauses ? `, ${setNullClauses}` : "";
692
+ const trashedAt = (/* @__PURE__ */ new Date()).toISOString();
693
+ const isConversation = baseLabels.includes("Conversation");
694
+ const messageUniqueKeys = UNIQUE_KEYS_BY_LABEL2["Message"] ?? [];
695
+ let cascadedMessageCount = 0;
696
+ await session.executeWrite(async (tx) => {
697
+ await tx.run(`MATCH (n) WHERE elementId(n) = $eid
698
+ SET n:Trashed,
699
+ n.trashedAt = datetime($trashedAt),
700
+ n.trashedBy = $by,
701
+ n.trashReason = $reason,
702
+ n._trashedKeys = $trashedKeysJson${setNullSuffix}`, {
703
+ eid: elementId,
704
+ trashedAt,
705
+ by,
706
+ reason: reason ?? null,
707
+ trashedKeysJson: JSON.stringify(originalKeys)
708
+ });
709
+ if (isConversation) {
710
+ const collectKeys = messageUniqueKeys.length > 0 ? messageUniqueKeys.map((k) => `\`${k}\`: m.\`${k}\``).join(", ") : "";
711
+ const collectMsgProps = collectKeys ? `, {${collectKeys}}` : ", {}";
712
+ const collected = await tx.run(`MATCH (c) WHERE elementId(c) = $eid
713
+ MATCH (m:Message)-[:PART_OF]->(c)
714
+ WHERE NOT m:Trashed
715
+ RETURN elementId(m) AS meid${collectMsgProps} AS keys`, { eid: elementId });
716
+ for (const rec of collected.records) {
717
+ const meid = rec.get("meid");
718
+ const keys = rec.get("keys");
719
+ const liveKeys = {};
720
+ for (const k of messageUniqueKeys) {
721
+ if (keys[k] !== void 0 && keys[k] !== null)
722
+ liveKeys[k] = keys[k];
723
+ }
724
+ const msgSetNulls = Object.keys(liveKeys).length > 0 ? ", " + Object.keys(liveKeys).map((k) => `m.\`${k}\` = null`).join(", ") : "";
725
+ await tx.run(`MATCH (m) WHERE elementId(m) = $meid
726
+ SET m:Trashed,
727
+ m.trashedAt = datetime($trashedAt),
728
+ m.trashedBy = $by,
729
+ m.trashReason = $reason,
730
+ m._trashedKeys = $trashedKeysJson${msgSetNulls}`, {
731
+ meid,
732
+ trashedAt,
733
+ by: `${by}:cascade-from-conversation`,
734
+ reason: reason ?? `cascade from Conversation ${elementId}`,
735
+ trashedKeysJson: JSON.stringify(liveKeys)
736
+ });
737
+ }
738
+ cascadedMessageCount = collected.records.length;
739
+ }
740
+ });
741
+ process.stderr.write(`[trash:marked] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")} by=${by} reason=${reason ?? "null"}
742
+ `);
743
+ if (isConversation) {
744
+ process.stderr.write(`[trash:cascaded] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount} by=${by}
745
+ `);
746
+ }
747
+ return {
748
+ trashed: true,
749
+ alreadyTrashed: false,
750
+ nodeId: elementId,
751
+ labels: baseLabels,
752
+ trashedAt,
753
+ originalKeys
754
+ };
755
+ }
756
+ async function restoreNode2(params) {
757
+ const { session, accountId, elementId } = params;
758
+ const lookup = await session.run(`MATCH (n:Trashed) WHERE elementId(n) = $eid
759
+ RETURN labels(n) AS labels, n._trashedKeys AS keysJson`, { eid: elementId });
760
+ if (lookup.records.length === 0) {
761
+ throw new Error(`restoreNode: trashed node not found (elementId=${elementId})`);
762
+ }
763
+ const allLabels = lookup.records[0].get("labels");
764
+ const baseLabels = allLabels.filter((l) => l !== "Trashed");
765
+ const keysJson = lookup.records[0].get("keysJson");
766
+ const originalKeys = keysJson ? JSON.parse(keysJson) : {};
767
+ for (const label of baseLabels) {
768
+ const uniqueKeys = UNIQUE_KEYS_BY_LABEL2[label] ?? [];
769
+ for (const k of uniqueKeys) {
770
+ const v = originalKeys[k];
771
+ if (v === void 0 || v === null)
772
+ continue;
773
+ const conflict = await session.run(`MATCH (other:\`${label}\`)
774
+ WHERE elementId(other) <> $eid
775
+ AND NOT other:Trashed
776
+ AND other.\`${k}\` = $val
777
+ RETURN elementId(other) AS otherId LIMIT 1`, { eid: elementId, val: v });
778
+ if (conflict.records.length > 0) {
779
+ const otherId = conflict.records[0].get("otherId");
780
+ throw new Error(`restoreNode: cannot restore ${label} elementId=${elementId} \u2014 active node elementId=${otherId} already holds ${k}=${JSON.stringify(v)}`);
781
+ }
782
+ }
783
+ }
784
+ const setClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = $val_${k}`).join(", ");
785
+ const setSuffix = setClauses ? `, ${setClauses}` : "";
786
+ const setParams = { eid: elementId };
787
+ for (const [k, v] of Object.entries(originalKeys))
788
+ setParams[`val_${k}`] = v;
789
+ const isConversation = baseLabels.includes("Conversation");
790
+ let cascadedMessageCount = 0;
791
+ await session.executeWrite(async (tx) => {
792
+ await tx.run(`MATCH (n:Trashed) WHERE elementId(n) = $eid
793
+ REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
794
+ SET n.restoredAt = datetime()${setSuffix}`, setParams);
795
+ if (isConversation) {
796
+ const collected = await tx.run(`MATCH (c) WHERE elementId(c) = $eid
797
+ MATCH (m:Trashed:Message)-[:PART_OF]->(c)
798
+ WHERE m.trashedBy ENDS WITH ':cascade-from-conversation'
799
+ RETURN elementId(m) AS meid, m._trashedKeys AS keysJson`, { eid: elementId });
800
+ for (const rec of collected.records) {
801
+ const meid = rec.get("meid");
802
+ const keysJson2 = rec.get("keysJson");
803
+ const keys = keysJson2 ? JSON.parse(keysJson2) : {};
804
+ const setClause = Object.keys(keys).length > 0 ? ", " + Object.keys(keys).map((k) => `m.\`${k}\` = $val_${k}`).join(", ") : "";
805
+ const msgParams = { meid };
806
+ for (const [k, v] of Object.entries(keys))
807
+ msgParams[`val_${k}`] = v;
808
+ await tx.run(`MATCH (m) WHERE elementId(m) = $meid
809
+ REMOVE m:Trashed, m.trashedAt, m.trashedBy, m.trashReason, m._trashedKeys
810
+ SET m.restoredAt = datetime()${setClause}`, msgParams);
811
+ }
812
+ cascadedMessageCount = collected.records.length;
813
+ }
814
+ });
815
+ process.stderr.write(`[trash:restored] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")}
816
+ `);
817
+ if (isConversation) {
818
+ process.stderr.write(`[trash:cascade-restored] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount}
819
+ `);
820
+ }
821
+ return {
822
+ restored: true,
823
+ nodeId: elementId,
824
+ labels: baseLabels,
825
+ restoredKeys: originalKeys
826
+ };
827
+ }
828
+ async function emptyTrash(params) {
829
+ const t0 = Date.now();
830
+ const { session, accountId, graceDays = 30, dryRun = false, labels: labelFilter, onEmpty } = params;
831
+ const cutoff = new Date(Date.now() - graceDays * 24 * 60 * 60 * 1e3).toISOString();
832
+ const labelClause = labelFilter && labelFilter.length > 0 ? `AND ANY(l IN labels(n) WHERE l IN $labelFilter)` : "";
833
+ const candidatesResult = await session.run(`MATCH (n:Trashed)
834
+ WHERE n.trashedAt < datetime($cutoff)
835
+ ${labelClause}
836
+ RETURN elementId(n) AS eid,
837
+ labels(n) AS labels,
838
+ toString(n.trashedAt) AS trashedAt,
839
+ n.trashedBy AS trashedBy,
840
+ n._trashedKeys AS keysJson
841
+ ORDER BY n.trashedAt ASC`, { cutoff, ...labelFilter ? { labelFilter } : {} });
842
+ const candidates = candidatesResult.records.map((r) => {
843
+ const keysJson = r.get("keysJson");
844
+ return {
845
+ elementId: r.get("eid"),
846
+ labels: r.get("labels").filter((l) => l !== "Trashed"),
847
+ trashedAt: String(r.get("trashedAt")),
848
+ trashedBy: r.get("trashedBy") ?? null,
849
+ trashedKeys: keysJson ? JSON.parse(keysJson) : {}
850
+ };
851
+ });
852
+ process.stderr.write(`[trash:empty-run] accountId=${accountId} graceDays=${graceDays} dryRun=${dryRun} candidates=${candidates.length}
853
+ `);
854
+ if (dryRun || candidates.length === 0) {
855
+ process.stderr.write(`[graph:trash:summary] accountId=${accountId} trashedNow=0 emptiedThisRun=0 graceDays=${graceDays} dryRun=${dryRun} elapsedMs=${Date.now() - t0}
856
+ `);
857
+ return { graceDays, dryRun, candidates, emptied: 0 };
858
+ }
859
+ let emptied = 0;
860
+ for (const c of candidates) {
861
+ if (onEmpty) {
862
+ try {
863
+ await onEmpty(c);
864
+ } catch (err) {
865
+ const id83 = c.elementId.slice(0, 8);
866
+ const message = err instanceof Error ? err.message : String(err);
867
+ process.stderr.write(`[trash:empty-run] reason=onEmpty-failed elementId=${id83} labels=${c.labels.join(",")} message=${message}
868
+ `);
869
+ continue;
870
+ }
871
+ }
872
+ await session.run(`MATCH (n) WHERE elementId(n) = $eid DETACH DELETE n`, { eid: c.elementId });
873
+ const ageDays = Math.floor((Date.now() - new Date(c.trashedAt).getTime()) / (24 * 60 * 60 * 1e3));
874
+ process.stderr.write(`[trash:emptied] accountId=${accountId} elementId=${c.elementId} labels=${c.labels.join(",")} trashedAt=${c.trashedAt} ageDays=${ageDays}
875
+ `);
876
+ emptied++;
877
+ }
878
+ process.stderr.write(`[graph:trash:summary] accountId=${accountId} trashedNow=0 emptiedThisRun=${emptied} graceDays=${graceDays} dryRun=${dryRun} elapsedMs=${Date.now() - t0}
879
+ `);
880
+ return { graceDays, dryRun, candidates, emptied };
881
+ }
882
+ function notTrashed4(alias) {
883
+ return `(NOT \`${alias}\`:Trashed AND \`${alias}\`.deletedAt IS NULL)`;
884
+ }
885
+ function uniqueKeysForLabels(labels) {
886
+ const out = /* @__PURE__ */ new Set();
887
+ for (const l of labels)
888
+ for (const k of UNIQUE_KEYS_BY_LABEL2[l] ?? [])
889
+ out.add(k);
890
+ return [...out];
891
+ }
892
+ exports.TRASH_METADATA_PROPS = TRASH_PROP_NAMES;
893
+ }
894
+ });
895
+
896
+ // ../lib/telegram-button-labels/dist/index.js
897
+ var require_dist6 = __commonJS({
609
898
  "../lib/telegram-button-labels/dist/index.js"(exports) {
610
899
  "use strict";
611
900
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1575,7 +1864,7 @@ var require_geo = __commonJS({
1575
1864
  });
1576
1865
 
1577
1866
  // ../lib/dispatch-read/dist/index.js
1578
- var require_dist6 = __commonJS({
1867
+ var require_dist7 = __commonJS({
1579
1868
  "../lib/dispatch-read/dist/index.js"(exports) {
1580
1869
  "use strict";
1581
1870
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1734,7 +2023,7 @@ var require_dist6 = __commonJS({
1734
2023
  });
1735
2024
 
1736
2025
  // ../lib/account-schema-regions/dist/index.js
1737
- var require_dist7 = __commonJS({
2026
+ var require_dist8 = __commonJS({
1738
2027
  "../lib/account-schema-regions/dist/index.js"(exports) {
1739
2028
  "use strict";
1740
2029
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1803,287 +2092,6 @@ var require_dist7 = __commonJS({
1803
2092
  }
1804
2093
  });
1805
2094
 
1806
- // ../lib/graph-trash/dist/index.js
1807
- var require_dist8 = __commonJS({
1808
- "../lib/graph-trash/dist/index.js"(exports) {
1809
- "use strict";
1810
- Object.defineProperty(exports, "__esModule", { value: true });
1811
- exports.TRASH_METADATA_PROPS = void 0;
1812
- exports.trashNode = trashNode2;
1813
- exports.restoreNode = restoreNode2;
1814
- exports.emptyTrash = emptyTrash;
1815
- exports.notTrashed = notTrashed2;
1816
- exports.uniqueKeysForLabels = uniqueKeysForLabels;
1817
- var UNIQUE_KEYS_BY_LABEL2 = {
1818
- Person: ["email", "telephone"],
1819
- Service: ["serviceId"],
1820
- LocalBusiness: ["accountId"],
1821
- Task: ["taskId"],
1822
- Event: ["eventId"],
1823
- // :KnowledgeDocument is keyed on `attachmentId` (document writer path —
1824
- // file/URL/obsidian/contract attachment + legacy Gmail-thread shape under
1825
- // `source:'email'`). Task 397 split the conversation transcripts off into
1826
- // `:ConversationArchive`, which carries `conversationIdentity`.
1827
- KnowledgeDocument: ["attachmentId"],
1828
- ConversationArchive: ["conversationIdentity"],
1829
- DigitalDocument: ["attachmentId"],
1830
- Conversation: ["sessionId"],
1831
- Message: ["messageId"],
1832
- Workflow: ["workflowId"],
1833
- WorkflowStep: ["stepId"],
1834
- WorkflowRun: ["runId"],
1835
- Preference: ["preferenceId"],
1836
- Email: ["emailId", "messageId"],
1837
- AdminUser: ["userId"],
1838
- ToolCall: ["callId"],
1839
- // Composite component nulls — frees the composite constraint:
1840
- AccessGrant: ["contactValue"],
1841
- // composite (contactValue, agentSlug, accountId)
1842
- UserProfile: ["userId"]
1843
- // composite (accountId, userId)
1844
- };
1845
- var TRASH_PROP_NAMES = [
1846
- "trashedAt",
1847
- "trashedBy",
1848
- "trashReason",
1849
- "_trashedKeys"
1850
- ];
1851
- async function trashNode2(params) {
1852
- const { session, accountId, elementId, by, reason } = params;
1853
- const lookup = await session.run(`MATCH (n) WHERE elementId(n) = $eid AND n.accountId = $accountId
1854
- RETURN labels(n) AS labels, properties(n) AS props`, { eid: elementId, accountId });
1855
- if (lookup.records.length === 0) {
1856
- throw new Error(`trashNode: node not found (elementId=${elementId} accountId=${accountId.slice(0, 8)}\u2026)`);
1857
- }
1858
- const allLabels = lookup.records[0].get("labels");
1859
- const props = lookup.records[0].get("props");
1860
- const baseLabels = allLabels.filter((l) => l !== "Trashed");
1861
- if (allLabels.includes("Trashed")) {
1862
- return {
1863
- trashed: false,
1864
- alreadyTrashed: true,
1865
- nodeId: elementId,
1866
- labels: baseLabels,
1867
- trashedAt: String(props.trashedAt ?? ""),
1868
- originalKeys: {}
1869
- };
1870
- }
1871
- const uniqueKeys = /* @__PURE__ */ new Set();
1872
- for (const label of baseLabels) {
1873
- for (const key2 of UNIQUE_KEYS_BY_LABEL2[label] ?? [])
1874
- uniqueKeys.add(key2);
1875
- }
1876
- const originalKeys = {};
1877
- for (const k of uniqueKeys) {
1878
- if (props[k] !== void 0 && props[k] !== null)
1879
- originalKeys[k] = props[k];
1880
- }
1881
- const setNullClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = null`).join(", ");
1882
- const setNullSuffix = setNullClauses ? `, ${setNullClauses}` : "";
1883
- const trashedAt = (/* @__PURE__ */ new Date()).toISOString();
1884
- const isConversation = baseLabels.includes("Conversation");
1885
- const messageUniqueKeys = UNIQUE_KEYS_BY_LABEL2["Message"] ?? [];
1886
- let cascadedMessageCount = 0;
1887
- await session.executeWrite(async (tx) => {
1888
- await tx.run(`MATCH (n) WHERE elementId(n) = $eid
1889
- SET n:Trashed,
1890
- n.trashedAt = datetime($trashedAt),
1891
- n.trashedBy = $by,
1892
- n.trashReason = $reason,
1893
- n._trashedKeys = $trashedKeysJson${setNullSuffix}`, {
1894
- eid: elementId,
1895
- trashedAt,
1896
- by,
1897
- reason: reason ?? null,
1898
- trashedKeysJson: JSON.stringify(originalKeys)
1899
- });
1900
- if (isConversation) {
1901
- const collectKeys = messageUniqueKeys.length > 0 ? messageUniqueKeys.map((k) => `\`${k}\`: m.\`${k}\``).join(", ") : "";
1902
- const collectMsgProps = collectKeys ? `, {${collectKeys}}` : ", {}";
1903
- const collected = await tx.run(`MATCH (c) WHERE elementId(c) = $eid
1904
- MATCH (m:Message)-[:PART_OF]->(c)
1905
- WHERE NOT m:Trashed
1906
- RETURN elementId(m) AS meid${collectMsgProps} AS keys`, { eid: elementId });
1907
- for (const rec of collected.records) {
1908
- const meid = rec.get("meid");
1909
- const keys = rec.get("keys");
1910
- const liveKeys = {};
1911
- for (const k of messageUniqueKeys) {
1912
- if (keys[k] !== void 0 && keys[k] !== null)
1913
- liveKeys[k] = keys[k];
1914
- }
1915
- const msgSetNulls = Object.keys(liveKeys).length > 0 ? ", " + Object.keys(liveKeys).map((k) => `m.\`${k}\` = null`).join(", ") : "";
1916
- await tx.run(`MATCH (m) WHERE elementId(m) = $meid
1917
- SET m:Trashed,
1918
- m.trashedAt = datetime($trashedAt),
1919
- m.trashedBy = $by,
1920
- m.trashReason = $reason,
1921
- m._trashedKeys = $trashedKeysJson${msgSetNulls}`, {
1922
- meid,
1923
- trashedAt,
1924
- by: `${by}:cascade-from-conversation`,
1925
- reason: reason ?? `cascade from Conversation ${elementId}`,
1926
- trashedKeysJson: JSON.stringify(liveKeys)
1927
- });
1928
- }
1929
- cascadedMessageCount = collected.records.length;
1930
- }
1931
- });
1932
- process.stderr.write(`[trash:marked] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")} by=${by} reason=${reason ?? "null"}
1933
- `);
1934
- if (isConversation) {
1935
- process.stderr.write(`[trash:cascaded] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount} by=${by}
1936
- `);
1937
- }
1938
- return {
1939
- trashed: true,
1940
- alreadyTrashed: false,
1941
- nodeId: elementId,
1942
- labels: baseLabels,
1943
- trashedAt,
1944
- originalKeys
1945
- };
1946
- }
1947
- async function restoreNode2(params) {
1948
- const { session, accountId, elementId } = params;
1949
- const lookup = await session.run(`MATCH (n:Trashed) WHERE elementId(n) = $eid
1950
- RETURN labels(n) AS labels, n._trashedKeys AS keysJson`, { eid: elementId });
1951
- if (lookup.records.length === 0) {
1952
- throw new Error(`restoreNode: trashed node not found (elementId=${elementId})`);
1953
- }
1954
- const allLabels = lookup.records[0].get("labels");
1955
- const baseLabels = allLabels.filter((l) => l !== "Trashed");
1956
- const keysJson = lookup.records[0].get("keysJson");
1957
- const originalKeys = keysJson ? JSON.parse(keysJson) : {};
1958
- for (const label of baseLabels) {
1959
- const uniqueKeys = UNIQUE_KEYS_BY_LABEL2[label] ?? [];
1960
- for (const k of uniqueKeys) {
1961
- const v = originalKeys[k];
1962
- if (v === void 0 || v === null)
1963
- continue;
1964
- const conflict = await session.run(`MATCH (other:\`${label}\`)
1965
- WHERE elementId(other) <> $eid
1966
- AND NOT other:Trashed
1967
- AND other.\`${k}\` = $val
1968
- RETURN elementId(other) AS otherId LIMIT 1`, { eid: elementId, val: v });
1969
- if (conflict.records.length > 0) {
1970
- const otherId = conflict.records[0].get("otherId");
1971
- throw new Error(`restoreNode: cannot restore ${label} elementId=${elementId} \u2014 active node elementId=${otherId} already holds ${k}=${JSON.stringify(v)}`);
1972
- }
1973
- }
1974
- }
1975
- const setClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = $val_${k}`).join(", ");
1976
- const setSuffix = setClauses ? `, ${setClauses}` : "";
1977
- const setParams = { eid: elementId };
1978
- for (const [k, v] of Object.entries(originalKeys))
1979
- setParams[`val_${k}`] = v;
1980
- const isConversation = baseLabels.includes("Conversation");
1981
- let cascadedMessageCount = 0;
1982
- await session.executeWrite(async (tx) => {
1983
- await tx.run(`MATCH (n:Trashed) WHERE elementId(n) = $eid
1984
- REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
1985
- SET n.restoredAt = datetime()${setSuffix}`, setParams);
1986
- if (isConversation) {
1987
- const collected = await tx.run(`MATCH (c) WHERE elementId(c) = $eid
1988
- MATCH (m:Trashed:Message)-[:PART_OF]->(c)
1989
- WHERE m.trashedBy ENDS WITH ':cascade-from-conversation'
1990
- RETURN elementId(m) AS meid, m._trashedKeys AS keysJson`, { eid: elementId });
1991
- for (const rec of collected.records) {
1992
- const meid = rec.get("meid");
1993
- const keysJson2 = rec.get("keysJson");
1994
- const keys = keysJson2 ? JSON.parse(keysJson2) : {};
1995
- const setClause = Object.keys(keys).length > 0 ? ", " + Object.keys(keys).map((k) => `m.\`${k}\` = $val_${k}`).join(", ") : "";
1996
- const msgParams = { meid };
1997
- for (const [k, v] of Object.entries(keys))
1998
- msgParams[`val_${k}`] = v;
1999
- await tx.run(`MATCH (m) WHERE elementId(m) = $meid
2000
- REMOVE m:Trashed, m.trashedAt, m.trashedBy, m.trashReason, m._trashedKeys
2001
- SET m.restoredAt = datetime()${setClause}`, msgParams);
2002
- }
2003
- cascadedMessageCount = collected.records.length;
2004
- }
2005
- });
2006
- process.stderr.write(`[trash:restored] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")}
2007
- `);
2008
- if (isConversation) {
2009
- process.stderr.write(`[trash:cascade-restored] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount}
2010
- `);
2011
- }
2012
- return {
2013
- restored: true,
2014
- nodeId: elementId,
2015
- labels: baseLabels,
2016
- restoredKeys: originalKeys
2017
- };
2018
- }
2019
- async function emptyTrash(params) {
2020
- const t0 = Date.now();
2021
- const { session, accountId, graceDays = 30, dryRun = false, labels: labelFilter, onEmpty } = params;
2022
- const cutoff = new Date(Date.now() - graceDays * 24 * 60 * 60 * 1e3).toISOString();
2023
- const labelClause = labelFilter && labelFilter.length > 0 ? `AND ANY(l IN labels(n) WHERE l IN $labelFilter)` : "";
2024
- const candidatesResult = await session.run(`MATCH (n:Trashed)
2025
- WHERE n.trashedAt < datetime($cutoff)
2026
- ${labelClause}
2027
- RETURN elementId(n) AS eid,
2028
- labels(n) AS labels,
2029
- toString(n.trashedAt) AS trashedAt,
2030
- n.trashedBy AS trashedBy,
2031
- n._trashedKeys AS keysJson
2032
- ORDER BY n.trashedAt ASC`, { cutoff, ...labelFilter ? { labelFilter } : {} });
2033
- const candidates = candidatesResult.records.map((r) => {
2034
- const keysJson = r.get("keysJson");
2035
- return {
2036
- elementId: r.get("eid"),
2037
- labels: r.get("labels").filter((l) => l !== "Trashed"),
2038
- trashedAt: String(r.get("trashedAt")),
2039
- trashedBy: r.get("trashedBy") ?? null,
2040
- trashedKeys: keysJson ? JSON.parse(keysJson) : {}
2041
- };
2042
- });
2043
- process.stderr.write(`[trash:empty-run] accountId=${accountId} graceDays=${graceDays} dryRun=${dryRun} candidates=${candidates.length}
2044
- `);
2045
- if (dryRun || candidates.length === 0) {
2046
- process.stderr.write(`[graph:trash:summary] accountId=${accountId} trashedNow=0 emptiedThisRun=0 graceDays=${graceDays} dryRun=${dryRun} elapsedMs=${Date.now() - t0}
2047
- `);
2048
- return { graceDays, dryRun, candidates, emptied: 0 };
2049
- }
2050
- let emptied = 0;
2051
- for (const c of candidates) {
2052
- if (onEmpty) {
2053
- try {
2054
- await onEmpty(c);
2055
- } catch (err) {
2056
- const id83 = c.elementId.slice(0, 8);
2057
- const message = err instanceof Error ? err.message : String(err);
2058
- process.stderr.write(`[trash:empty-run] reason=onEmpty-failed elementId=${id83} labels=${c.labels.join(",")} message=${message}
2059
- `);
2060
- continue;
2061
- }
2062
- }
2063
- await session.run(`MATCH (n) WHERE elementId(n) = $eid DETACH DELETE n`, { eid: c.elementId });
2064
- const ageDays = Math.floor((Date.now() - new Date(c.trashedAt).getTime()) / (24 * 60 * 60 * 1e3));
2065
- process.stderr.write(`[trash:emptied] accountId=${accountId} elementId=${c.elementId} labels=${c.labels.join(",")} trashedAt=${c.trashedAt} ageDays=${ageDays}
2066
- `);
2067
- emptied++;
2068
- }
2069
- process.stderr.write(`[graph:trash:summary] accountId=${accountId} trashedNow=0 emptiedThisRun=${emptied} graceDays=${graceDays} dryRun=${dryRun} elapsedMs=${Date.now() - t0}
2070
- `);
2071
- return { graceDays, dryRun, candidates, emptied };
2072
- }
2073
- function notTrashed2(alias) {
2074
- return `(NOT \`${alias}\`:Trashed AND \`${alias}\`.deletedAt IS NULL)`;
2075
- }
2076
- function uniqueKeysForLabels(labels) {
2077
- const out = /* @__PURE__ */ new Set();
2078
- for (const l of labels)
2079
- for (const k of UNIQUE_KEYS_BY_LABEL2[l] ?? [])
2080
- out.add(k);
2081
- return [...out];
2082
- }
2083
- exports.TRASH_METADATA_PROPS = TRASH_PROP_NAMES;
2084
- }
2085
- });
2086
-
2087
2095
  // ../lib/graph-style/dist/index.js
2088
2096
  var require_dist9 = __commonJS({
2089
2097
  "../lib/graph-style/dist/index.js"(exports) {
@@ -4252,6 +4260,74 @@ var TELEGRAM_CARD_TOOLS = /* @__PURE__ */ new Set([
4252
4260
  "mcp__telegram__telegram-card",
4253
4261
  "mcp__plugin_telegram_telegram__telegram-card"
4254
4262
  ]);
4263
+ var TELEGRAM_MEDIA_SEND_TOOLS = /* @__PURE__ */ new Set([
4264
+ "mcp__telegram__telegram-media-send",
4265
+ "mcp__plugin_telegram_telegram__telegram-media-send"
4266
+ ]);
4267
+ var TELEGRAM_ASK_TOOLS = /* @__PURE__ */ new Set([
4268
+ "mcp__telegram__telegram-ask",
4269
+ "mcp__plugin_telegram_telegram__telegram-ask"
4270
+ ]);
4271
+ var TELEGRAM_API_TOOLS = /* @__PURE__ */ new Set([
4272
+ "mcp__telegram__telegram-api",
4273
+ "mcp__plugin_telegram_telegram__telegram-api"
4274
+ ]);
4275
+ var TELEGRAM_API_TRANSMIT_METHODS = /* @__PURE__ */ new Set(["sendMessage", "editMessageText"]);
4276
+ var TELEGRAM_SEND_TOOLS = /* @__PURE__ */ new Set([
4277
+ ...TELEGRAM_CARD_TOOLS,
4278
+ ...TELEGRAM_MEDIA_SEND_TOOLS,
4279
+ ...TELEGRAM_ASK_TOOLS,
4280
+ ...TELEGRAM_API_TOOLS
4281
+ ]);
4282
+ function chatIdOf(value) {
4283
+ if (typeof value === "string") return value;
4284
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
4285
+ return "";
4286
+ }
4287
+ function cardOutcome(text, isError) {
4288
+ const v = /card-result: outcome=(\w+)/.exec(text)?.[1];
4289
+ if (v === "edited" || v === "unchanged" || v === "sent") return v;
4290
+ if (v === "none") return "failed";
4291
+ return isError ? "failed" : "sent";
4292
+ }
4293
+ function pendingSendOf(name, input) {
4294
+ const i = input ?? {};
4295
+ if (TELEGRAM_CARD_TOOLS.has(name)) {
4296
+ return {
4297
+ kind: "telegram-card",
4298
+ text: typeof i.text === "string" ? i.text : "",
4299
+ buttons: parseCardButtonsFromInput(input),
4300
+ chatId: chatIdOf(i.chatId),
4301
+ ts: null
4302
+ };
4303
+ }
4304
+ if (TELEGRAM_MEDIA_SEND_TOOLS.has(name) || TELEGRAM_ASK_TOOLS.has(name)) {
4305
+ const kind = typeof i.kind === "string" ? i.kind : "";
4306
+ const text = TELEGRAM_MEDIA_SEND_TOOLS.has(name) ? typeof i.caption === "string" ? i.caption : "" : typeof i.question === "string" ? i.question : "";
4307
+ return {
4308
+ kind: "telegram-send",
4309
+ label: kind,
4310
+ text,
4311
+ chatId: chatIdOf(i.chatId),
4312
+ edit: kind.startsWith("edit") || kind.startsWith("stop"),
4313
+ ts: null
4314
+ };
4315
+ }
4316
+ if (TELEGRAM_API_TOOLS.has(name)) {
4317
+ const method = typeof i.method === "string" ? i.method : "";
4318
+ if (!TELEGRAM_API_TRANSMIT_METHODS.has(method)) return null;
4319
+ const params = i.params ?? {};
4320
+ return {
4321
+ kind: "telegram-send",
4322
+ label: method,
4323
+ text: typeof params.text === "string" ? params.text : "",
4324
+ chatId: chatIdOf(params.chat_id),
4325
+ edit: method === "editMessageText",
4326
+ ts: null
4327
+ };
4328
+ }
4329
+ return null;
4330
+ }
4255
4331
  function parseCardButtonsFromInput(input) {
4256
4332
  const rows = input?.buttons;
4257
4333
  if (!Array.isArray(rows)) return [];
@@ -4396,7 +4472,29 @@ function erroredToolUseIds(lines) {
4396
4472
  }
4397
4473
  return ids;
4398
4474
  }
4399
- function assistantTurns(content, ts, promoteFinal = false, erroredIds = /* @__PURE__ */ new Set(), uuid) {
4475
+ function answeredToolUseIds(lines) {
4476
+ const ids = /* @__PURE__ */ new Set();
4477
+ for (const line of lines) {
4478
+ if (!line || line[0] !== "{" || !line.includes("tool_use_id")) continue;
4479
+ let row;
4480
+ try {
4481
+ const parsed = JSON.parse(line);
4482
+ if (!parsed || typeof parsed !== "object") continue;
4483
+ row = parsed;
4484
+ } catch {
4485
+ continue;
4486
+ }
4487
+ const content = row.message?.content;
4488
+ if (!Array.isArray(content)) continue;
4489
+ for (const block of content) {
4490
+ if (!block || typeof block !== "object") continue;
4491
+ const b = block;
4492
+ if (b.type === "tool_result" && typeof b.tool_use_id === "string") ids.add(b.tool_use_id);
4493
+ }
4494
+ }
4495
+ return ids;
4496
+ }
4497
+ function assistantTurns(content, ts, promoteFinal = false, erroredIds = /* @__PURE__ */ new Set(), uuid, answeredIds = /* @__PURE__ */ new Set(), pending = /* @__PURE__ */ new Map(), fullRead = true) {
4400
4498
  if (!Array.isArray(content)) return [];
4401
4499
  const out = [];
4402
4500
  for (const block of content) {
@@ -4430,21 +4528,21 @@ function assistantTurns(content, ts, promoteFinal = false, erroredIds = /* @__PU
4430
4528
  } else {
4431
4529
  out.push({ kind: "agent-file", files, caption, ts });
4432
4530
  }
4433
- } else if (b.type === "tool_use" && typeof b.name === "string" && TELEGRAM_CARD_TOOLS.has(b.name)) {
4434
- const input = b.input ?? {};
4435
- out.push({
4436
- kind: "telegram-card",
4437
- text: typeof input.text === "string" ? input.text : "",
4438
- buttons: parseCardButtonsFromInput(b.input),
4439
- ts
4440
- });
4531
+ } else if (b.type === "tool_use" && typeof b.name === "string" && TELEGRAM_SEND_TOOLS.has(b.name)) {
4532
+ const send = pendingSendOf(b.name, b.input);
4533
+ const id = typeof b.id === "string" ? b.id : null;
4534
+ if (send !== null && id !== null && (answeredIds.has(id) || !fullRead)) {
4535
+ pending.set(id, { ...send, ts });
4536
+ } else {
4537
+ out.push({ kind: "tool-call", name: b.name, input: b.input ?? null, ts });
4538
+ }
4441
4539
  } else if (b.type === "tool_use" && typeof b.name === "string") {
4442
4540
  out.push({ kind: "tool-call", name: b.name, input: b.input ?? null, ts });
4443
4541
  }
4444
4542
  }
4445
4543
  return out;
4446
4544
  }
4447
- function toolResultTurns(content, ts) {
4545
+ function toolResultTurns(content, ts, pending = /* @__PURE__ */ new Map()) {
4448
4546
  if (!Array.isArray(content)) return [];
4449
4547
  const out = [];
4450
4548
  for (const block of content) {
@@ -4455,6 +4553,15 @@ function toolResultTurns(content, ts) {
4455
4553
  (c) => c && typeof c === "object" && typeof c.text === "string" ? c.text : ""
4456
4554
  ).join("") : "";
4457
4555
  out.push({ kind: "tool-result", text, isError: b.is_error === true, ts });
4556
+ const id = b.tool_use_id;
4557
+ if (typeof id !== "string") continue;
4558
+ const send = pending.get(id);
4559
+ if (send === void 0) continue;
4560
+ pending.delete(id);
4561
+ const outcome = send.kind === "telegram-card" ? cardOutcome(text, b.is_error === true) : b.is_error === true ? "failed" : send.edit ? "edited" : "sent";
4562
+ out.push(
4563
+ send.kind === "telegram-card" ? { kind: "telegram-card", text: send.text, buttons: send.buttons, chatId: send.chatId, outcome, ts: send.ts } : { kind: "telegram-send", label: send.label, text: send.text, chatId: send.chatId, outcome, ts: send.ts }
4564
+ );
4458
4565
  }
4459
4566
  return out;
4460
4567
  }
@@ -4479,9 +4586,11 @@ function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, uuid,
4479
4586
  out.push(operatorInboundTurn(u, ts, uuid, census));
4480
4587
  }
4481
4588
  }
4482
- function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(), turnCtx = {}) {
4589
+ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(), turnCtx = {}, fullRead = true) {
4483
4590
  const out = [];
4591
+ const pendingSends = turnCtx.pendingTelegramSends ??= /* @__PURE__ */ new Map();
4484
4592
  const erroredIds = erroredToolUseIds(lines);
4593
+ const answeredIds = lines.some((l) => l.includes("telegram-")) ? answeredToolUseIds(lines) : /* @__PURE__ */ new Set();
4485
4594
  for (const line of lines) {
4486
4595
  if (!line || line[0] !== "{") continue;
4487
4596
  let row;
@@ -4505,7 +4614,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
4505
4614
  }
4506
4615
  const isEnd = msg?.stop_reason === "end_turn";
4507
4616
  const promoteFinal = isEnd && turnCtx.lastChannelSource !== void 0;
4508
- out.push(...assistantTurns(msg?.content, ts, promoteFinal, erroredIds, asString(row.uuid) ?? void 0));
4617
+ out.push(...assistantTurns(msg?.content, ts, promoteFinal, erroredIds, asString(row.uuid) ?? void 0, answeredIds, pendingSends, fullRead));
4509
4618
  continue;
4510
4619
  }
4511
4620
  if (row.type === "queue-operation" && row.operation === "enqueue") {
@@ -4538,7 +4647,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
4538
4647
  continue;
4539
4648
  }
4540
4649
  if (Array.isArray(msg.content)) {
4541
- out.push(...toolResultTurns(msg.content, ts));
4650
+ out.push(...toolResultTurns(msg.content, ts, pendingSends));
4542
4651
  continue;
4543
4652
  }
4544
4653
  const text = asString(msg.content);
@@ -5650,48 +5759,6 @@ function logPublicAddress(slug, verdict) {
5650
5759
  import { writeFile, mkdtemp, rm } from "fs/promises";
5651
5760
  import { tmpdir as tmpdir2 } from "os";
5652
5761
  import { join as join7 } from "path";
5653
-
5654
- // app/lib/stt/audio-mime.ts
5655
- var AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
5656
- "audio/ogg",
5657
- "audio/opus",
5658
- "audio/webm",
5659
- "audio/mp4",
5660
- "audio/x-m4a",
5661
- "audio/mpeg",
5662
- "audio/wav",
5663
- "video/mp4"
5664
- ]);
5665
- function normalizeMime(mimeType) {
5666
- return mimeType.split(";")[0].trim().toLowerCase();
5667
- }
5668
- function isAudioMime(mimeType) {
5669
- return AUDIO_MIME_TYPES.has(normalizeMime(mimeType));
5670
- }
5671
- function audioExtension(mimeType) {
5672
- switch (normalizeMime(mimeType)) {
5673
- case "audio/ogg":
5674
- return ".ogg";
5675
- case "audio/opus":
5676
- return ".opus";
5677
- case "audio/webm":
5678
- return ".webm";
5679
- case "audio/mp4":
5680
- return ".m4a";
5681
- case "audio/x-m4a":
5682
- return ".m4a";
5683
- case "audio/mpeg":
5684
- return ".mp3";
5685
- case "audio/wav":
5686
- return ".wav";
5687
- case "video/mp4":
5688
- return ".mp4";
5689
- default:
5690
- return ".audio";
5691
- }
5692
- }
5693
-
5694
- // app/lib/stt/voice-note.ts
5695
5762
  var TAG4 = "[voice]";
5696
5763
  async function transcribeVoiceNote(file, source) {
5697
5764
  const startMs = Date.now();
@@ -7385,7 +7452,7 @@ function createScheduleInjectRoutes(deps) {
7385
7452
  }
7386
7453
 
7387
7454
  // server/index.ts
7388
- var import_dist18 = __toESM(require_dist3(), 1);
7455
+ var import_dist19 = __toESM(require_dist3(), 1);
7389
7456
 
7390
7457
  // app/lib/whatsapp/avatar-store.ts
7391
7458
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync6, statSync as statSync8, rmSync as rmSync3 } from "fs";
@@ -7487,7 +7554,7 @@ async function defaultHttpGet(url) {
7487
7554
  }
7488
7555
  }
7489
7556
  async function defaultResolve(accountId) {
7490
- const { resolveSocket: resolveSocket2 } = await import("./manager-7BXVB76I.js");
7557
+ const { resolveSocket: resolveSocket2 } = await import("./manager-VDHT3YYI.js");
7491
7558
  return resolveSocket2(accountId);
7492
7559
  }
7493
7560
  async function fetchAvatar(accountId, jid, deps = {}) {
@@ -15841,7 +15908,7 @@ data: ${JSON.stringify(obj)}
15841
15908
  const { lines, nextOffset } = splitNewLines(buf, offset);
15842
15909
  offset = nextOffset;
15843
15910
  let n = 0;
15844
- for (const turn of parseTranscript(lines, queuedPendingSuppress, turnCtx)) {
15911
+ for (const turn of parseTranscript(lines, queuedPendingSuppress, turnCtx, false)) {
15845
15912
  send(controller, enrichAdminAttachments(enrichDownloads(turn, scratchpadRoot), sessionId), offset);
15846
15913
  actx.lastParentTurns.push(turn);
15847
15914
  if (turn.kind === "agent-error") {
@@ -16987,6 +17054,19 @@ async function authorizeAccountWrite(c, accountId) {
16987
17054
  return { ok: true, via: "session" };
16988
17055
  }
16989
17056
 
17057
+ // app/lib/video/webchat-video.ts
17058
+ var TAG26 = "[video]";
17059
+ async function extractWebchatVideo(file, extractImpl = extractVideo) {
17060
+ if (!isVideoMime(file.mimeType)) return void 0;
17061
+ try {
17062
+ return await extractImpl(file.storagePath, file.mimeType);
17063
+ } catch (err) {
17064
+ const reason = err instanceof Error ? err.message : String(err);
17065
+ console.error(`${TAG26} webchat extract failed error=${reason} path=${file.storagePath}`);
17066
+ return void 0;
17067
+ }
17068
+ }
17069
+
16990
17070
  // ../lib/models/src/index.ts
16991
17071
  var OPUS_MODEL = "claude-opus-5[1m]";
16992
17072
  var SONNET_MODEL = "claude-sonnet-5";
@@ -17422,6 +17502,18 @@ async function reapplyLeversViaManager() {
17422
17502
  }
17423
17503
  var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
17424
17504
  var sendSeq = 0;
17505
+ var transcribingKeys = /* @__PURE__ */ new Map();
17506
+ function markTranscribing(key2) {
17507
+ transcribingKeys.set(key2, (transcribingKeys.get(key2) ?? 0) + 1);
17508
+ }
17509
+ function clearTranscribing(key2) {
17510
+ const left = (transcribingKeys.get(key2) ?? 1) - 1;
17511
+ if (left > 0) transcribingKeys.set(key2, left);
17512
+ else transcribingKeys.delete(key2);
17513
+ }
17514
+ function isTranscribing(key2) {
17515
+ return (transcribingKeys.get(key2) ?? 0) > 0;
17516
+ }
17425
17517
  function createWebchatRoutes(deps) {
17426
17518
  const app81 = new Hono();
17427
17519
  app81.post("/send", requireAdminSession, async (c) => {
@@ -17532,13 +17624,17 @@ function createWebchatRoutes(deps) {
17532
17624
  deliveryKey = `session:${deliverySessionId}`;
17533
17625
  deliveryAccountId = bootstrapAccountId;
17534
17626
  }
17627
+ let audioFile;
17628
+ let kind = "files";
17629
+ const stored = [];
17630
+ const sendId = sendIdFromResolve ?? ++sendSeq;
17535
17631
  if (isMultipart) {
17536
- const kind = files.some((f) => isAudioMime(f.type)) ? "voice" : "files";
17632
+ kind = files.some((f) => isAudioMime(f.type)) ? "voice" : "files";
17537
17633
  if (files.length > MAX_FILES_PER_MESSAGE) {
17538
17634
  console.error(`[webchat:inbound] op=send-refused kind=${kind} reason=too-many-files count=${files.length}`);
17539
17635
  return c.json({ error: `Maximum ${MAX_FILES_PER_MESSAGE} files per message.` }, 422);
17540
17636
  }
17541
- const audioFile = files.find((f) => isAudioMime(f.type));
17637
+ audioFile = files.find((f) => isAudioMime(f.type));
17542
17638
  const regularFiles = files.filter((f) => !isAudioMime(f.type));
17543
17639
  for (const file of regularFiles) {
17544
17640
  try {
@@ -17552,7 +17648,6 @@ function createWebchatRoutes(deps) {
17552
17648
  return c.json({ error: detail }, 422);
17553
17649
  }
17554
17650
  }
17555
- const stored = [];
17556
17651
  if (audioFile) {
17557
17652
  if (audioFile.size > MAX_FILE_SIZE_BYTES) {
17558
17653
  console.error(`[webchat:inbound] op=send-refused kind=voice reason=oversize bytes=${audioFile.size}`);
@@ -17565,14 +17660,6 @@ function createWebchatRoutes(deps) {
17565
17660
  console.error(`[webchat:inbound] op=send-refused kind=voice reason=audio-store-failed detail=${detail}`);
17566
17661
  return c.json({ error: "failed to store voice recording" }, 500);
17567
17662
  }
17568
- const result = await transcribeVoiceNote(audioFile, "admin");
17569
- if (result.ok) {
17570
- text = text.length > 0 ? `${text}
17571
-
17572
- ${result.result.text}` : result.result.text;
17573
- } else {
17574
- console.error(`[webchat:inbound] op=stt-failed kind=voice detail=${result.error}`);
17575
- }
17576
17663
  }
17577
17664
  try {
17578
17665
  for (const file of regularFiles) {
@@ -17588,51 +17675,87 @@ ${result.result.text}` : result.result.text;
17588
17675
  const ext = dot >= 0 ? s.filename.slice(dot).toLowerCase() : "";
17589
17676
  console.log(`[attach-ingest] mime=${s.mimeType} ext=${ext} converted=${!!s.readableTextPath} path=${s.storagePath}`);
17590
17677
  }
17591
- if (stored.length > 0) {
17592
- const listing = stored.map((s) => `${s.filename} (${s.readableTextPath ?? s.storagePath})`).join(", ");
17593
- const note = `[The operator uploaded ${stored.length} file(s): ${listing}]`;
17594
- text = text.length > 0 ? `${text}
17595
-
17596
- ${note}` : note;
17597
- }
17598
- if (text.length === 0) {
17678
+ if (text.length === 0 && stored.length === 0) {
17599
17679
  console.error(`[webchat:inbound] op=send-refused kind=${kind} reason=empty-text`);
17600
17680
  return c.json({ error: "text required" }, 400);
17601
17681
  }
17602
- console.log(`[webchat:inbound] op=send-parsed kind=${kind} files=${stored.length} bytes=${Buffer.byteLength(text, "utf8")}`);
17603
- }
17604
- if (target !== void 0 && await managerActivity(deliverySessionId) === "busy") {
17605
- console.log(`[webchat:inbound] op=queued-busy sessionId=${deliverySessionId.slice(0, 8)} key=${deliveryKey}`);
17606
17682
  }
17607
- const adminUserId = requesterUserId ?? void 0;
17608
- const inboundStartedAt = Date.now();
17609
- const sendId = sendIdFromResolve ?? ++sendSeq;
17610
- const replyContext = resolveQuoted(deliverySessionId, quotedUuid, sendId);
17611
- const inbound = await deps.handleInbound({
17612
- role: "admin",
17613
- accountId: deliveryAccountId,
17614
- key: deliveryKey,
17615
- text,
17616
- sessionId: deliverySessionId,
17617
- adminUserId,
17618
- ...replyContext ? { replyContext } : {}
17619
- });
17620
- const turnSessionId = deliverySessionId;
17621
17683
  const keyDisplay2 = deliveryKey.slice(0, "session:".length + 8);
17622
- const baseline = jsonlSizeBytes(locateSession(turnSessionId).projectDir, turnSessionId);
17623
- console.error(
17624
- `[webchat:inbound] op=send-accepted key=${keyDisplay2} sendId=${sendId} baselineBytes=${baseline ?? "absent"} acceptMs=${Date.now() - inboundStartedAt} spawn=${inbound.spawn !== null ? "triggered" : "none"}`
17625
- );
17626
- const turnTimer = setTimeout(() => {
17627
- const now = jsonlSizeBytes(locateSession(turnSessionId).projectDir, turnSessionId);
17628
- const grew = now !== null && (baseline === null || now > baseline);
17629
- if (grew) {
17630
- console.error(`[webchat:inbound] op=send-turned key=${keyDisplay2} sendId=${sendId} bytes=${now}`);
17631
- } else {
17632
- console.error(`[webchat:inbound] op=send-no-turn key=${keyDisplay2} sendId=${sendId} baselineBytes=${baseline ?? "absent"}`);
17684
+ const completeSend = async () => {
17685
+ if (isMultipart) {
17686
+ if (audioFile) {
17687
+ const result = await transcribeVoiceNote(audioFile, "admin");
17688
+ if (result.ok) {
17689
+ text = text.length > 0 ? `${text}
17690
+
17691
+ ${result.result.text}` : result.result.text;
17692
+ } else {
17693
+ console.error(`[webchat:inbound] op=stt-failed kind=voice detail=${result.error}`);
17694
+ }
17695
+ }
17696
+ if (stored.length > 0) {
17697
+ const listing = stored.map((s) => `${s.filename} (${s.readableTextPath ?? s.storagePath})`).join(", ");
17698
+ const note = `[The operator uploaded ${stored.length} file(s): ${listing}]`;
17699
+ text = text.length > 0 ? `${text}
17700
+
17701
+ ${note}` : note;
17702
+ }
17703
+ for (const s of stored) {
17704
+ const timeline = await extractWebchatVideo(s);
17705
+ if (timeline) text = `${text}
17706
+
17707
+ ${timeline}`;
17708
+ }
17709
+ console.log(`[webchat:inbound] op=send-parsed kind=${kind} files=${stored.length} bytes=${Buffer.byteLength(text, "utf8")}`);
17633
17710
  }
17634
- }, WEBCHAT_SEND_TURN_WINDOW_MS);
17635
- if (turnTimer.unref) turnTimer.unref();
17711
+ if (target !== void 0 && await managerActivity(deliverySessionId) === "busy") {
17712
+ console.log(`[webchat:inbound] op=queued-busy sessionId=${deliverySessionId.slice(0, 8)} key=${deliveryKey}`);
17713
+ }
17714
+ const adminUserId = requesterUserId ?? void 0;
17715
+ const inboundStartedAt = Date.now();
17716
+ const replyContext = resolveQuoted(deliverySessionId, quotedUuid, sendId);
17717
+ const inbound = await deps.handleInbound({
17718
+ role: "admin",
17719
+ accountId: deliveryAccountId,
17720
+ key: deliveryKey,
17721
+ text,
17722
+ sessionId: deliverySessionId,
17723
+ adminUserId,
17724
+ ...replyContext ? { replyContext } : {}
17725
+ });
17726
+ const turnSessionId = deliverySessionId;
17727
+ const baseline = jsonlSizeBytes(locateSession(turnSessionId).projectDir, turnSessionId);
17728
+ console.error(
17729
+ `[webchat:inbound] op=send-accepted key=${keyDisplay2} sendId=${sendId} baselineBytes=${baseline ?? "absent"} acceptMs=${Date.now() - inboundStartedAt} spawn=${inbound.spawn !== null ? "triggered" : "none"}`
17730
+ );
17731
+ const turnTimer = setTimeout(() => {
17732
+ const now = jsonlSizeBytes(locateSession(turnSessionId).projectDir, turnSessionId);
17733
+ const grew = now !== null && (baseline === null || now > baseline);
17734
+ if (grew) {
17735
+ console.error(`[webchat:inbound] op=send-turned key=${keyDisplay2} sendId=${sendId} bytes=${now}`);
17736
+ } else {
17737
+ console.error(`[webchat:inbound] op=send-no-turn key=${keyDisplay2} sendId=${sendId} baselineBytes=${baseline ?? "absent"}`);
17738
+ }
17739
+ }, WEBCHAT_SEND_TURN_WINDOW_MS);
17740
+ if (turnTimer.unref) turnTimer.unref();
17741
+ };
17742
+ const deferred = audioFile !== void 0 || stored.some((s) => isVideoMime(s.mimeType));
17743
+ if (deferred) {
17744
+ markTranscribing(deliveryKey);
17745
+ notify("webchat-pointer", deliveryAccountId);
17746
+ const stagedBytes = stored.reduce((n, s) => n + s.sizeBytes, 0);
17747
+ console.log(`[webchat:inbound] op=send-staged kind=${kind} key=${keyDisplay2} bytes=${stagedBytes}`);
17748
+ void completeSend().catch((err) => {
17749
+ const detail = err instanceof Error ? err.message : String(err);
17750
+ console.error(`[webchat:inbound] op=send-continuation-failed key=${keyDisplay2} detail=${detail}`);
17751
+ deps.markDeliveryFailure?.(deliveryKey, `send-${sendId}`);
17752
+ }).finally(() => {
17753
+ clearTranscribing(deliveryKey);
17754
+ notify("webchat-pointer", deliveryAccountId);
17755
+ });
17756
+ return c.json({ ok: true, transcribing: true });
17757
+ }
17758
+ await completeSend();
17636
17759
  return c.json({ ok: true });
17637
17760
  });
17638
17761
  app81.post("/interrupt", requireAdminSession, async (c) => {
@@ -17742,7 +17865,7 @@ ${note}` : note;
17742
17865
  }
17743
17866
  const known = resolvedProjectDir !== null || sessionSidecarExists(target);
17744
17867
  const indicators2 = await fetchComposerIndicators(target);
17745
- return c.json({ sessionId: target, projectDir: resolvedProjectDir, transcriptSessionId, channelBinding: conflictingBinding(channel), known, sizeBytes: jsonlSizeBytes(projectDir2, target), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${target}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${target}`) ?? null, ...indicators2, ...readLevers(scopeAccountId ? validateAccountId2(scopeAccountId) : null) });
17868
+ return c.json({ sessionId: target, projectDir: resolvedProjectDir, transcriptSessionId, channelBinding: conflictingBinding(channel), known, sizeBytes: jsonlSizeBytes(projectDir2, target), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${target}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${target}`) ?? null, transcribing: isTranscribing(`session:${target}`), ...indicators2, ...readLevers(scopeAccountId ? validateAccountId2(scopeAccountId) : null) });
17746
17869
  }
17747
17870
  const account = scopeAccountId ? validateAccountId2(scopeAccountId) : null;
17748
17871
  const { sessionId, source } = resolveCanonical(accountId, account?.accountDir ?? null, requesterUserId, primaryUserId, scopeAccountId, multiAccount);
@@ -17770,7 +17893,7 @@ ${note}` : note;
17770
17893
  }
17771
17894
  }
17772
17895
  const indicators = await fetchComposerIndicators(sessionId);
17773
- return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
17896
+ return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, transcribing: isTranscribing(`session:${sessionId}`), ...indicators, ...readLevers(account) });
17774
17897
  });
17775
17898
  app81.post("/permission-verdict", requireAdminSession, async (c) => {
17776
17899
  const body = await c.req.json().catch(() => null);
@@ -18997,7 +19120,7 @@ function routeTelegramUpdate(input) {
18997
19120
  // app/lib/telegram/inbound/media.ts
18998
19121
  var import_dist5 = __toESM(require_dist4(), 1);
18999
19122
  import { join as join37 } from "path";
19000
- var TAG26 = "[telegram-inbound]";
19123
+ var TAG27 = "[telegram-inbound]";
19001
19124
  var TELEGRAM_MEDIA_DIR = "/tmp/maxy-media";
19002
19125
  var DOWNLOAD_TIMEOUT_MS = 45e3;
19003
19126
  async function downloadTelegramMedia(input) {
@@ -19046,17 +19169,55 @@ async function downloadTelegramMedia(input) {
19046
19169
  result = { ...base, failure: err instanceof Error ? err.message : String(err) };
19047
19170
  }
19048
19171
  console.error(
19049
- `${TAG26} op=media-download botId=${input.botId} type=${item.type} ok=${result.path ? "yes" : "no"} bytes=${bytes} path=${result.path ?? "-"} reason=${result.failure ?? "-"}`
19172
+ `${TAG27} op=media-download botId=${input.botId} type=${item.type} ok=${result.path ? "yes" : "no"} bytes=${bytes} path=${result.path ?? "-"} reason=${result.failure ?? "-"}`
19050
19173
  );
19051
19174
  out.push(result);
19052
19175
  }
19053
19176
  return out;
19054
19177
  }
19055
19178
 
19179
+ // app/lib/telegram/resolve-sender-name.ts
19180
+ var import_dist6 = __toESM(require_dist5(), 1);
19181
+ function composeName(row) {
19182
+ return [row.givenName ?? "", row.familyName ?? ""].join(" ").trim().replace(/\s+/g, " ");
19183
+ }
19184
+ function classifySenderName(rows) {
19185
+ if (rows.length > 1) return { kind: "ambiguous", count: rows.length };
19186
+ const first = rows[0];
19187
+ if (!first) return { kind: "none" };
19188
+ const display = composeName(first);
19189
+ return display.length > 0 ? { kind: "resolved", display } : { kind: "none" };
19190
+ }
19191
+ async function resolveTelegramSenderName(params) {
19192
+ if (!/^\d+$/.test(params.senderId)) return { verdict: { kind: "none" } };
19193
+ const telegramUserId = Number(params.senderId);
19194
+ if (!Number.isSafeInteger(telegramUserId)) return { verdict: { kind: "none" } };
19195
+ let session;
19196
+ try {
19197
+ session = getSession();
19198
+ const result = await session.run(
19199
+ `MATCH (p:Person {accountId: $accountId})
19200
+ WHERE p.telegramUserId = $telegramUserId AND ${(0, import_dist6.notTrashed)("p")}
19201
+ RETURN p.givenName AS givenName, p.familyName AS familyName`,
19202
+ { accountId: params.accountId, telegramUserId },
19203
+ { timeout: 2e3 }
19204
+ );
19205
+ const rows = result.records.map((r) => ({
19206
+ givenName: r.get("givenName") ?? null,
19207
+ familyName: r.get("familyName") ?? null
19208
+ }));
19209
+ return { verdict: classifySenderName(rows) };
19210
+ } catch (err) {
19211
+ return { verdict: { kind: "none" }, error: err instanceof Error ? err.message : String(err) };
19212
+ } finally {
19213
+ await session?.close();
19214
+ }
19215
+ }
19216
+
19056
19217
  // app/lib/telegram/inbound/servable.ts
19057
19218
  import { readFile as readFile6 } from "fs/promises";
19058
19219
  import { basename as basename12 } from "path";
19059
- var TAG27 = "[telegram-inbound]";
19220
+ var TAG28 = "[telegram-inbound]";
19060
19221
  async function storeTelegramServable(input) {
19061
19222
  const out = [];
19062
19223
  for (const [index, item] of input.media.entries()) {
@@ -19070,12 +19231,12 @@ async function storeTelegramServable(input) {
19070
19231
  }
19071
19232
  out.push(attachmentId);
19072
19233
  console.error(
19073
- `${TAG27} op=servable-store ok=true type=${item.type} id=${attachmentId}`
19234
+ `${TAG28} op=servable-store ok=true type=${item.type} id=${attachmentId}`
19074
19235
  );
19075
19236
  } catch (err) {
19076
19237
  const reason = err instanceof Error ? err.message : String(err);
19077
19238
  console.error(
19078
- `${TAG27} op=servable-store ok=false type=${item.type} id=${attachmentId} reason=${reason.slice(0, 100)}`
19239
+ `${TAG28} op=servable-store ok=false type=${item.type} id=${attachmentId} reason=${reason.slice(0, 100)}`
19079
19240
  );
19080
19241
  }
19081
19242
  }
@@ -19083,7 +19244,7 @@ async function storeTelegramServable(input) {
19083
19244
  }
19084
19245
 
19085
19246
  // app/lib/telegram/inbound/transcribe.ts
19086
- var TAG28 = "[telegram-inbound]";
19247
+ var TAG29 = "[telegram-inbound]";
19087
19248
  async function transcribeTelegramAudio(input) {
19088
19249
  const doTranscribe = input.transcribeImpl ?? transcribe;
19089
19250
  const out = [];
@@ -19095,7 +19256,7 @@ async function transcribeTelegramAudio(input) {
19095
19256
  }
19096
19257
  if (!m.path) {
19097
19258
  console.error(
19098
- `${TAG28} op=transcribe botId=${input.botId} ok=no chars=0 ms=0 reason=${m.failure ?? "no local file"}`
19259
+ `${TAG29} op=transcribe botId=${input.botId} ok=no chars=0 ms=0 reason=${m.failure ?? "no local file"}`
19099
19260
  );
19100
19261
  out.push(m);
19101
19262
  continue;
@@ -19112,7 +19273,7 @@ async function transcribeTelegramAudio(input) {
19112
19273
  reason = err instanceof Error ? err.message : String(err);
19113
19274
  }
19114
19275
  console.error(
19115
- `${TAG28} op=transcribe botId=${input.botId} ok=${heard ? "yes" : "no"} chars=${heard?.length ?? 0} ms=${Date.now() - startedMs} reason=${reason}`
19276
+ `${TAG29} op=transcribe botId=${input.botId} ok=${heard ? "yes" : "no"} chars=${heard?.length ?? 0} ms=${Date.now() - startedMs} reason=${reason}`
19116
19277
  );
19117
19278
  if (heard) {
19118
19279
  transcripts.push(heard);
@@ -19129,6 +19290,33 @@ async function transcribeTelegramAudio(input) {
19129
19290
  ${body}` : body, media: out };
19130
19291
  }
19131
19292
 
19293
+ // app/lib/telegram/inbound/video.ts
19294
+ var TAG30 = "[telegram-inbound]";
19295
+ async function extractTelegramVideo(input) {
19296
+ const doExtract = input.extractImpl ?? extractVideo;
19297
+ const out = [];
19298
+ for (const m of input.media) {
19299
+ if (m.type !== "video" || !m.path) {
19300
+ out.push(m);
19301
+ continue;
19302
+ }
19303
+ const startedMs = Date.now();
19304
+ let timeline;
19305
+ let reason = "-";
19306
+ try {
19307
+ timeline = await doExtract(m.path, m.mimetype ?? "");
19308
+ if (!timeline) reason = "no timeline returned";
19309
+ } catch (err) {
19310
+ reason = err instanceof Error ? err.message : String(err);
19311
+ }
19312
+ console.error(
19313
+ `${TAG30} op=video-extract botId=${input.botId} ok=${timeline ? "yes" : "no"} chars=${timeline?.length ?? 0} ms=${Date.now() - startedMs} reason=${reason}`
19314
+ );
19315
+ out.push(timeline ? { ...m, timeline } : m);
19316
+ }
19317
+ return { media: out };
19318
+ }
19319
+
19132
19320
  // app/lib/telegram/gateway/telegram-inbound-census.ts
19133
19321
  var ANSWER_THRESHOLD_MS = 3e4;
19134
19322
  var GAP_NAME_LIMIT2 = 10;
@@ -19195,7 +19383,7 @@ function runTelegramNotifyReconcile(now) {
19195
19383
  }
19196
19384
 
19197
19385
  // server/routes/telegram.ts
19198
- var import_dist8 = __toESM(require_dist5(), 1);
19386
+ var import_dist9 = __toESM(require_dist6(), 1);
19199
19387
 
19200
19388
  // app/lib/telegram/outbound/answer-callback.ts
19201
19389
  async function answerTelegramCallback(botToken, callbackQueryId, text) {
@@ -19289,7 +19477,7 @@ function runTelegramNotifyPublishCensus(now) {
19289
19477
  }
19290
19478
 
19291
19479
  // ../lib/dispatch-write/src/lifecycle.ts
19292
- var import_dist6 = __toESM(require_dist6());
19480
+ var import_dist7 = __toESM(require_dist7());
19293
19481
  function checkTransition(kind, from, to, cfg) {
19294
19482
  const edges = kind === "job" ? cfg.statuses.jobTransitions : cfg.statuses.visitTransitions;
19295
19483
  const legal = edges.some(([f, t]) => f === from && t === to);
@@ -19319,7 +19507,7 @@ function firstMissingRequiredField(toStatus, cfg, state) {
19319
19507
  const required = cfg.records.requiredBeforeTerminal[toStatus];
19320
19508
  if (!required) return null;
19321
19509
  for (const field2 of required) {
19322
- if (!import_dist6.RECORDABLE_FIELDS.includes(field2)) continue;
19510
+ if (!import_dist7.RECORDABLE_FIELDS.includes(field2)) continue;
19323
19511
  if (!fieldSatisfied(field2, state)) {
19324
19512
  return field2;
19325
19513
  }
@@ -19331,12 +19519,12 @@ function formatTransitionRefused(p) {
19331
19519
  }
19332
19520
 
19333
19521
  // ../lib/dispatch-write/src/write.ts
19334
- var import_dist7 = __toESM(require_dist6());
19522
+ var import_dist8 = __toESM(require_dist7());
19335
19523
  function refuse(reason, message) {
19336
19524
  return { ok: false, reason, message };
19337
19525
  }
19338
19526
  async function visitRecord(p) {
19339
- const visit = await (0, import_dist7.readVisit)(p.session, p.accountId, p.visitId);
19527
+ const visit = await (0, import_dist8.readVisit)(p.session, p.accountId, p.visitId);
19340
19528
  if (!visit) {
19341
19529
  return refuse(
19342
19530
  "not-found",
@@ -19433,7 +19621,7 @@ async function visitRecord(p) {
19433
19621
  arrivalParams
19434
19622
  );
19435
19623
  }
19436
- const state = await (0, import_dist7.readVisitRecordState)(p.session, p.accountId, p.visitId);
19624
+ const state = await (0, import_dist8.readVisitRecordState)(p.session, p.accountId, p.visitId);
19437
19625
  p.emit(
19438
19626
  `[dispatch] op=visit-record visitId=${p.visitId} notes=${state.notes} media=${state.media} parts=${state.parts} checklist=${state.checklistDone}/${state.checklistTotal} signatures=${state.signatures}`
19439
19627
  );
@@ -19446,7 +19634,7 @@ async function visitRecord(p) {
19446
19634
  `[dispatch] op=arrival-observed visitId=${p.visitId} distanceM=${p.arrivedObservedDistanceM ?? "none"} observedAt=${p.arrivedObservedAt} pressedAt=${pressedAt ?? "none"} deltaS=${deltaS ?? "none"}`
19447
19635
  );
19448
19636
  }
19449
- const after = await (0, import_dist7.readVisit)(p.session, p.accountId, p.visitId);
19637
+ const after = await (0, import_dist8.readVisit)(p.session, p.accountId, p.visitId);
19450
19638
  return {
19451
19639
  ok: true,
19452
19640
  value: {
@@ -19461,7 +19649,7 @@ async function visitRecord(p) {
19461
19649
  };
19462
19650
  }
19463
19651
  async function visitStatusSet(p) {
19464
- const visit = await (0, import_dist7.readVisit)(p.session, p.accountId, p.visitId);
19652
+ const visit = await (0, import_dist8.readVisit)(p.session, p.accountId, p.visitId);
19465
19653
  if (!visit) {
19466
19654
  return refuse(
19467
19655
  "not-found",
@@ -19501,7 +19689,7 @@ async function visitStatusSet(p) {
19501
19689
  `This account allows no move from "${visit.status}" to "${p.to}". From "${visit.status}" it allows: ${legalNext.join(", ") || "nothing"}.`
19502
19690
  );
19503
19691
  }
19504
- const state = await (0, import_dist7.readVisitRecordState)(p.session, p.accountId, p.visitId);
19692
+ const state = await (0, import_dist8.readVisitRecordState)(p.session, p.accountId, p.visitId);
19505
19693
  const missing = firstMissingRequiredField(p.to, p.cfg, state);
19506
19694
  if (missing) {
19507
19695
  p.emit(
@@ -19530,7 +19718,7 @@ async function visitStatusSet(p) {
19530
19718
  );
19531
19719
  let jobClosed = null;
19532
19720
  if (terminal && visit.jobId) {
19533
- const siblings = await (0, import_dist7.readVisitsForJob)(p.session, p.accountId, visit.jobId);
19721
+ const siblings = await (0, import_dist8.readVisitsForJob)(p.session, p.accountId, visit.jobId);
19534
19722
  const allTerminal = siblings.every(
19535
19723
  (v) => v.visitId === p.visitId || isTerminal("visit", v.status, p.cfg)
19536
19724
  );
@@ -19571,7 +19759,7 @@ async function visitStatusSet(p) {
19571
19759
  }
19572
19760
 
19573
19761
  // app/lib/telegram/dispatch/press.ts
19574
- var TAG29 = "[dispatch-tg]";
19762
+ var TAG31 = "[dispatch-tg]";
19575
19763
  function logValue(v) {
19576
19764
  return v.replace(/[\r\n]+/g, " ");
19577
19765
  }
@@ -19632,12 +19820,12 @@ async function handleDispatchPress(p) {
19632
19820
  status: ${status}`;
19633
19821
  const done = await p.edit(p.token, p.chatId, p.messageId, text, toBotApiKeyboard(kb.rows));
19634
19822
  p.emit(
19635
- `${TAG29} op=card-redrawn visitId=${logVisitId} messageId=${p.messageId} buttons=${kb.rows.flat().length} drawnFor=${logValue(status)}` + (done.ok ? "" : ` editError="${logValue(done.error ?? "unknown")}"`)
19823
+ `${TAG31} op=card-redrawn visitId=${logVisitId} messageId=${p.messageId} buttons=${kb.rows.flat().length} drawnFor=${logValue(status)}` + (done.ok ? "" : ` editError="${logValue(done.error ?? "unknown")}"`)
19636
19824
  );
19637
19825
  };
19638
19826
  if (visit.status !== parsed.drawnFor) {
19639
19827
  p.emit(
19640
- `${TAG29} op=callback-stale updateId=${u} visitId=${logVisitId} expected=${logDrawnFor} actual=${logValue(visit.status)}`
19828
+ `${TAG31} op=callback-stale updateId=${u} visitId=${logVisitId} expected=${logDrawnFor} actual=${logValue(visit.status)}`
19641
19829
  );
19642
19830
  await redraw(visit.status);
19643
19831
  return {
@@ -19664,7 +19852,7 @@ status: ${status}`;
19664
19852
  return { kind: "refused", reason: out.reason, answer: out.message };
19665
19853
  }
19666
19854
  p.emit(
19667
- `${TAG29} op=callback-applied updateId=${u} visitId=${logVisitId} from=${logValue(out.value.from)} to=${logValue(out.value.to)} tool=dispatch-visit-status-set ms=${Date.now() - startedAt}`
19855
+ `${TAG31} op=callback-applied updateId=${u} visitId=${logVisitId} from=${logValue(out.value.from)} to=${logValue(out.value.to)} tool=dispatch-visit-status-set ms=${Date.now() - startedAt}`
19668
19856
  );
19669
19857
  await redraw(out.value.to);
19670
19858
  return {
@@ -19691,7 +19879,7 @@ async function bindChatToVisit(p) {
19691
19879
  }
19692
19880
 
19693
19881
  // app/lib/telegram/dispatch/location.ts
19694
- var TAG30 = "[dispatch-tg]";
19882
+ var TAG32 = "[dispatch-tg]";
19695
19883
  async function handleDispatchLocation(p) {
19696
19884
  const bound = await bindChatToVisit({
19697
19885
  session: p.session,
@@ -19708,7 +19896,7 @@ async function handleDispatchLocation(p) {
19708
19896
  at: { latitude: p.latitude, longitude: p.longitude }
19709
19897
  });
19710
19898
  p.emit(
19711
- `${TAG30} op=location-update visitId=${bound.visit.visitId} workerId=${bound.worker.workerId} distanceM=${v.distanceM ?? "none"} inside=${v.inside} siteHasCoords=${v.siteHasCoords} radiusM=${v.radiusM ?? "none"}`
19899
+ `${TAG32} op=location-update visitId=${bound.visit.visitId} workerId=${bound.worker.workerId} distanceM=${v.distanceM ?? "none"} inside=${v.inside} siteHasCoords=${v.siteHasCoords} radiusM=${v.radiusM ?? "none"}`
19712
19900
  );
19713
19901
  if (!v.siteHasCoords) return { kind: "no-coords" };
19714
19902
  if (v.radiusM === null) return { kind: "no-radius" };
@@ -19726,12 +19914,12 @@ async function handleDispatchLocation(p) {
19726
19914
  });
19727
19915
  if (!rec.ok) {
19728
19916
  p.emit(
19729
- `${TAG30} op=geofence-refused visitId=${bound.visit.visitId} workerId=${bound.worker.workerId} distanceM=${v.distanceM} reason=${rec.reason}`
19917
+ `${TAG32} op=geofence-refused visitId=${bound.visit.visitId} workerId=${bound.worker.workerId} distanceM=${v.distanceM} reason=${rec.reason}`
19730
19918
  );
19731
19919
  return { kind: "not-recorded" };
19732
19920
  }
19733
19921
  p.emit(
19734
- `${TAG30} op=geofence-enter visitId=${bound.visit.visitId} observedAt=${observedAt} distanceM=${v.distanceM} pressedAt=${bound.visit.arrivedPressedAt ?? "none"}`
19922
+ `${TAG32} op=geofence-enter visitId=${bound.visit.visitId} observedAt=${observedAt} distanceM=${v.distanceM} pressedAt=${bound.visit.arrivedPressedAt ?? "none"}`
19735
19923
  );
19736
19924
  return { kind: "recorded" };
19737
19925
  }
@@ -19763,7 +19951,7 @@ function readTgActivity(accountId) {
19763
19951
  }
19764
19952
 
19765
19953
  // app/lib/telegram/dispatch/media.ts
19766
- var TAG31 = "[dispatch-tg]";
19954
+ var TAG33 = "[dispatch-tg]";
19767
19955
  async function handleDispatchMedia(p) {
19768
19956
  const bound = await bindChatToVisit({
19769
19957
  session: p.session,
@@ -19774,14 +19962,14 @@ async function handleDispatchMedia(p) {
19774
19962
  if (bound.kind !== "bound") {
19775
19963
  const tell = bound.kind === "no-open-visit" ? `You have no open ${p.cfg.vocabulary.visit}, so I have kept this in the chat. Open one and send it again.` : bound.kind === "many-open-visits" ? `You have ${bound.visits.length} open ${p.cfg.vocabulary.visit}s, so I have kept this in the chat rather than guessing. Open the one you mean in the job sheet and send it there.` : null;
19776
19964
  p.emit(
19777
- `${TAG31} op=unbound-inbound chatId=${p.telegramUserId} kind=${p.kind} reason=${bound.kind} told=${tell !== null}`
19965
+ `${TAG33} op=unbound-inbound chatId=${p.telegramUserId} kind=${p.kind} reason=${bound.kind} told=${tell !== null}`
19778
19966
  );
19779
19967
  return { kind: "unattached", reason: bound.kind, tell };
19780
19968
  }
19781
19969
  const visitId = bound.visit.visitId;
19782
19970
  if (p.kind === "voice") {
19783
19971
  p.emit(
19784
- `${TAG31} op=voice-received visitId=${visitId} durationS=${p.durationS ?? "none"} bytes=${p.bytes ?? "none"}`
19972
+ `${TAG33} op=voice-received visitId=${visitId} durationS=${p.durationS ?? "none"} bytes=${p.bytes ?? "none"}`
19785
19973
  );
19786
19974
  noteVoiceReceived(p.accountId, visitId);
19787
19975
  if (p.transcript === null || p.transcript.length === 0) {
@@ -19801,7 +19989,7 @@ async function handleDispatchMedia(p) {
19801
19989
  note: p.transcript
19802
19990
  });
19803
19991
  p.emit(
19804
- `${TAG31} op=voice-transcribed visitId=${visitId} chars=${p.transcript.length} ms=${Date.now() - startedAt}`
19992
+ `${TAG33} op=voice-transcribed visitId=${visitId} chars=${p.transcript.length} ms=${Date.now() - startedAt}`
19805
19993
  );
19806
19994
  noteVoiceTranscribed(p.accountId, visitId);
19807
19995
  return { kind: "note", visitId };
@@ -19821,14 +20009,14 @@ async function handleDispatchMedia(p) {
19821
20009
  visitId,
19822
20010
  mediaRef: p.path
19823
20011
  });
19824
- p.emit(`${TAG31} op=media-attached visitId=${visitId} kind=${p.kind} path=${p.path}`);
20012
+ p.emit(`${TAG33} op=media-attached visitId=${visitId} kind=${p.kind} path=${p.path}`);
19825
20013
  return { kind: "media", visitId };
19826
20014
  }
19827
20015
 
19828
20016
  // app/lib/telegram/live-location.ts
19829
20017
  import { existsSync as existsSync26, mkdirSync as mkdirSync6, readFileSync as readFileSync30, readdirSync as readdirSync20, statSync as statSync19, writeFileSync as writeFileSync13 } from "fs";
19830
20018
  import { join as join38 } from "path";
19831
- var TAG32 = "[telegram-inbound]";
20019
+ var TAG34 = "[telegram-inbound]";
19832
20020
  var LIVE_LOCATION_FILE = "telegram-live-location.json";
19833
20021
  var LIVE_LOCATION_CENSUS_TAG = "[telegram-live-location-census]";
19834
20022
  function liveLocationKey(botId, senderId) {
@@ -19864,12 +20052,12 @@ function writeLiveLocation(input) {
19864
20052
  const livePeriodS = input.livePeriodS ?? stored[key2]?.livePeriodS ?? null;
19865
20053
  if (livePeriodS === null) {
19866
20054
  emit(
19867
- `${TAG32} op=live-location op=skipped-write senderId=${senderId} botId=${botId} reason=no-live-period`
20055
+ `${TAG34} op=live-location op=skipped-write senderId=${senderId} botId=${botId} reason=no-live-period`
19868
20056
  );
19869
20057
  return { written: false, reason: "no-live-period" };
19870
20058
  }
19871
20059
  if (input.atS === null) {
19872
- emit(`${TAG32} op=live-location op=skipped-write senderId=${senderId} botId=${botId} reason=no-date`);
20060
+ emit(`${TAG34} op=live-location op=skipped-write senderId=${senderId} botId=${botId} reason=no-date`);
19873
20061
  return { written: false, reason: "no-date" };
19874
20062
  }
19875
20063
  const next = {
@@ -19887,7 +20075,7 @@ function writeLiveLocation(input) {
19887
20075
  mkdirSync6(accountDir, { recursive: true });
19888
20076
  writeFileSync13(path, JSON.stringify(kept, null, 2) + "\n", "utf-8");
19889
20077
  emit(
19890
- `${TAG32} op=live-location op=updated senderId=${senderId} botId=${botId} ageMs=${now - next.at} livePeriodS=${livePeriodS}`
20078
+ `${TAG34} op=live-location op=updated senderId=${senderId} botId=${botId} ageMs=${now - next.at} livePeriodS=${livePeriodS}`
19891
20079
  );
19892
20080
  return { written: true };
19893
20081
  }
@@ -19931,7 +20119,7 @@ function liveLocationCensusLine(accountsRoot, now) {
19931
20119
  // app/lib/telegram/business-connection.ts
19932
20120
  import { existsSync as existsSync27, mkdirSync as mkdirSync7, readFileSync as readFileSync31, writeFileSync as writeFileSync14 } from "fs";
19933
20121
  import { join as join39 } from "path";
19934
- var TAG33 = "[telegram-inbound]";
20122
+ var TAG35 = "[telegram-inbound]";
19935
20123
  var BUSINESS_CONNECTION_FILE = "telegram-business-connection.json";
19936
20124
  function truncateConnectionId(id) {
19937
20125
  return `${id.slice(0, 6)}\u2026`;
@@ -19964,7 +20152,7 @@ function writeBusinessConnection(input) {
19964
20152
  const canReply = rights?.can_reply === true;
19965
20153
  const say = (stored, reason) => {
19966
20154
  emit(
19967
- `${TAG33} op=business-connection botId=${botId} connectionId=${id === null ? "none" : truncateConnectionId(id)} userId=${userId ?? "none"} isEnabled=${isEnabled} canReply=${canReply} stored=${stored} reason=${reason}`
20155
+ `${TAG35} op=business-connection botId=${botId} connectionId=${id === null ? "none" : truncateConnectionId(id)} userId=${userId ?? "none"} isEnabled=${isEnabled} canReply=${canReply} stored=${stored} reason=${reason}`
19968
20156
  );
19969
20157
  };
19970
20158
  if (id === null) {
@@ -20043,7 +20231,7 @@ async function editTelegramMessage(botToken, chatId, messageId, text, keyboard)
20043
20231
  }
20044
20232
 
20045
20233
  // server/routes/telegram.ts
20046
- var TAG34 = "[telegram-inbound]";
20234
+ var TAG36 = "[telegram-inbound]";
20047
20235
  var PROVISION_TAG = "[telegram-provision]";
20048
20236
  function entryActive(entry3, accountDir, botId, noteSpecialist) {
20049
20237
  if (entry3.role === "admin") return true;
@@ -20053,7 +20241,7 @@ function entryActive(entry3, accountDir, botId, noteSpecialist) {
20053
20241
  noteSpecialist?.(rosterValid);
20054
20242
  if (rosterValid) return true;
20055
20243
  console.error(
20056
- `${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(entry3.specialist)} reason=specialist-unknown`
20244
+ `${TAG36} op=specialist-refused botId=${botId} specialist=${logValue2(entry3.specialist)} reason=specialist-unknown`
20057
20245
  );
20058
20246
  return false;
20059
20247
  }
@@ -20104,13 +20292,13 @@ function accountConfig(accountDir) {
20104
20292
  function raiseChannelTurn(input) {
20105
20293
  if (!input.active) {
20106
20294
  console.error(
20107
- `${TAG34} op=reject reason=agent-inactive botId=${input.botId} agent=${input.agentSlug}`
20295
+ `${TAG36} op=reject reason=agent-inactive botId=${input.botId} agent=${input.agentSlug}`
20108
20296
  );
20109
20297
  return;
20110
20298
  }
20111
20299
  const gateway = getTelegramGateway();
20112
20300
  if (!gateway) {
20113
- console.error(`${TAG34} op=reply-dropped botId=${input.botId} reason=gateway-not-ready`);
20301
+ console.error(`${TAG36} op=reply-dropped botId=${input.botId} reason=gateway-not-ready`);
20114
20302
  return;
20115
20303
  }
20116
20304
  const reply = async (replyText) => {
@@ -20131,8 +20319,8 @@ function raiseChannelTurn(input) {
20131
20319
  origin: "agent",
20132
20320
  createdAt: at
20133
20321
  });
20134
- console.error(`${TAG34} op=reply-sent botId=${input.botId} ok=true`);
20135
- } else console.error(`${TAG34} op=reply-dropped botId=${input.botId} reason=${sent.error}`);
20322
+ console.error(`${TAG36} op=reply-sent botId=${input.botId} ok=true`);
20323
+ } else console.error(`${TAG36} op=reply-dropped botId=${input.botId} reason=${sent.error}`);
20136
20324
  };
20137
20325
  void gateway.handleInbound({
20138
20326
  accountId: input.accountId,
@@ -20157,34 +20345,34 @@ var app11 = new Hono();
20157
20345
  app11.post("/", async (c) => {
20158
20346
  const botId = c.req.query("bot");
20159
20347
  if (!botId) {
20160
- console.error(`${TAG34} op=reject reason=missing-bot-param`);
20348
+ console.error(`${TAG36} op=reject reason=missing-bot-param`);
20161
20349
  return c.json({ ok: false }, 400);
20162
20350
  }
20163
- console.error(`${TAG34} op=received botId=${botId} path=/api/telegram/`);
20351
+ console.error(`${TAG36} op=received botId=${botId} path=/api/telegram/`);
20164
20352
  const resolved = resolveBotEntry(listValidAccounts(), botId);
20165
20353
  if (resolved.kind === "none") {
20166
- console.error(`${TAG34} op=reject reason=unknown-bot botId=${botId}`);
20354
+ console.error(`${TAG36} op=reject reason=unknown-bot botId=${botId}`);
20167
20355
  return c.json({ ok: false }, 401);
20168
20356
  }
20169
20357
  if (resolved.kind === "duplicate") {
20170
- console.error(`${TAG34} op=reject reason=duplicate-bot-id botId=${botId} accounts=${resolved.count}`);
20358
+ console.error(`${TAG36} op=reject reason=duplicate-bot-id botId=${botId} accounts=${resolved.count}`);
20171
20359
  return c.json({ ok: false }, 401);
20172
20360
  }
20173
20361
  const { accountId, accountDir, entry: entry3 } = resolved;
20174
20362
  const agentSlug = entryAgentSlug(entry3);
20175
- console.error(`${TAG34} op=entry botId=${botId} accountId=${accountId} role=${entry3.role} agent=${agentSlug}`);
20363
+ console.error(`${TAG36} op=entry botId=${botId} accountId=${accountId} role=${entry3.role} agent=${agentSlug}`);
20176
20364
  const sp = secretPath(botId);
20177
20365
  if (!existsSync28(sp)) {
20178
- console.error(`${TAG34} op=secret botId=${botId} result=missing-file`);
20366
+ console.error(`${TAG36} op=secret botId=${botId} result=missing-file`);
20179
20367
  return c.json({ ok: false }, 401);
20180
20368
  }
20181
20369
  const expected = readFileSync32(sp, "utf-8").trim();
20182
20370
  const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
20183
20371
  if (got !== expected) {
20184
- console.error(`${TAG34} op=secret botId=${botId} result=mismatch`);
20372
+ console.error(`${TAG36} op=secret botId=${botId} result=mismatch`);
20185
20373
  return c.json({ ok: false }, 401);
20186
20374
  }
20187
- console.error(`${TAG34} op=secret botId=${botId} result=ok`);
20375
+ console.error(`${TAG36} op=secret botId=${botId} result=ok`);
20188
20376
  let update;
20189
20377
  try {
20190
20378
  update = await c.req.json();
@@ -20193,7 +20381,7 @@ app11.post("/", async (c) => {
20193
20381
  }
20194
20382
  const shape = describeTelegramUpdate(update);
20195
20383
  console.error(
20196
- `${TAG34} op=inbound-kind botId=${botId} update=${shape.envelope} content=${shape.content} chatType=${shape.chatType} isEdit=${shape.isEdit ? "yes" : "no"}`
20384
+ `${TAG36} op=inbound-kind botId=${botId} update=${shape.envelope} content=${shape.content} chatType=${shape.chatType} isEdit=${shape.isEdit ? "yes" : "no"}`
20197
20385
  );
20198
20386
  if (update.business_connection) {
20199
20387
  writeBusinessConnection({
@@ -20207,13 +20395,13 @@ app11.post("/", async (c) => {
20207
20395
  if (shape.chatType === "group" || shape.chatType === "supergroup") {
20208
20396
  const allowlisted = entry3.role === "public" && (entry3.groupPolicy ?? "disabled") === "allowlist" && shape.chatId !== null && (entry3.allowGroups ?? []).includes(shape.chatId);
20209
20397
  console.error(
20210
- `${TAG34} op=group-inbound botId=${botId} chatType=${shape.chatType} chatId=${shape.chatId ?? "none"} senderId=${shape.senderId ?? "none"} threadId=${shape.threadId ?? "none"} allowlisted=${allowlisted ? "yes" : "no"}`
20398
+ `${TAG36} op=group-inbound botId=${botId} chatType=${shape.chatType} chatId=${shape.chatId ?? "none"} senderId=${shape.senderId ?? "none"} threadId=${shape.threadId ?? "none"} allowlisted=${allowlisted ? "yes" : "no"}`
20211
20399
  );
20212
20400
  }
20213
20401
  if (shape.chatType === "channel") {
20214
20402
  const allowlisted = entry3.role === "public" && (entry3.channelPolicy ?? "disabled") === "allowlist" && shape.chatId !== null && (entry3.allowChannels ?? []).includes(shape.chatId);
20215
20403
  console.error(
20216
- `${TAG34} op=channel-post botId=${botId} chatId=${shape.chatId ?? "none"} messageId=${shape.messageId ?? "none"} senderChat=${shape.senderChat ?? "none"} isEdit=${shape.isEdit ? "yes" : "no"} allowlisted=${allowlisted ? "yes" : "no"}`
20404
+ `${TAG36} op=channel-post botId=${botId} chatId=${shape.chatId ?? "none"} messageId=${shape.messageId ?? "none"} senderChat=${shape.senderChat ?? "none"} isEdit=${shape.isEdit ? "yes" : "no"} allowlisted=${allowlisted ? "yes" : "no"}`
20217
20405
  );
20218
20406
  }
20219
20407
  const hostBots = update.managed_bot ? listBotEntries(accountConfig(accountDir).telegram) : [];
@@ -20234,56 +20422,68 @@ app11.post("/", async (c) => {
20234
20422
  decision.kind === "managed-bot" && decision.authorised && decision.event === "created";
20235
20423
  noteInboundKind(shape.primaryKind, raisesTurn, shape.envelope);
20236
20424
  if (decision.kind === "ignore") {
20237
- console.error(`${TAG34} op=ignore botId=${botId} reason=${decision.reason}`);
20425
+ console.error(`${TAG36} op=ignore botId=${botId} reason=${decision.reason}`);
20238
20426
  return c.json({ ok: true }, 200);
20239
20427
  }
20240
20428
  if (decision.kind === "denied") {
20241
20429
  if (decision.reason === "admin-entry-no-groups" || decision.reason === "not-in-allowGroups" || decision.reason === "group-policy-disabled") {
20242
20430
  console.error(
20243
- `${TAG34} op=group-refused botId=${botId} chatId=${shape.chatId ?? "none"} reason=${decision.reason}`
20431
+ `${TAG36} op=group-refused botId=${botId} chatId=${shape.chatId ?? "none"} reason=${decision.reason}`
20244
20432
  );
20245
20433
  }
20246
20434
  if (decision.reason === "admin-entry-no-channels" || decision.reason === "specialist-entry-no-channels" || decision.reason === "unbound-entry-no-channels" || decision.reason === "not-in-allowChannels" || decision.reason === "channel-policy-disabled") {
20247
20435
  console.error(
20248
- `${TAG34} op=channel-refused botId=${botId} chatId=${shape.chatId ?? "none"} reason=${decision.reason}`
20436
+ `${TAG36} op=channel-refused botId=${botId} chatId=${shape.chatId ?? "none"} reason=${decision.reason}`
20249
20437
  );
20250
20438
  }
20251
- console.error(`${TAG34} op=access botId=${botId} senderId=- allowed=false reason=${decision.reason}`);
20439
+ console.error(`${TAG36} op=access botId=${botId} senderId=- allowed=false reason=${decision.reason}`);
20252
20440
  if (entry3.role === "specialist") {
20253
20441
  console.error(
20254
- `${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(entry3.specialist)} reason=${decision.reason}`
20442
+ `${TAG36} op=specialist-refused botId=${botId} specialist=${logValue2(entry3.specialist)} reason=${decision.reason}`
20255
20443
  );
20256
20444
  }
20257
20445
  return c.json({ ok: true }, 200);
20258
20446
  }
20259
20447
  if (!isBindableEntry(entry3)) {
20260
- console.error(`${TAG34} op=ignore botId=${botId} reason=bot-unbound`);
20448
+ console.error(`${TAG36} op=ignore botId=${botId} reason=bot-unbound`);
20261
20449
  return c.json({ ok: true }, 200);
20262
20450
  }
20451
+ const decisionSenderId = "senderId" in decision ? decision.senderId : void 0;
20452
+ const decisionSenderDisplay = "senderDisplay" in decision ? decision.senderDisplay : void 0;
20453
+ let provenanceDisplay = decisionSenderDisplay;
20454
+ if (entry3.role === "admin" && typeof decisionSenderId === "string" && decisionSenderId.length > 0) {
20455
+ const startedMs = Date.now();
20456
+ const { verdict, error } = await resolveTelegramSenderName({ accountId, senderId: decisionSenderId });
20457
+ const source = verdict.kind === "resolved" ? "contact" : typeof decisionSenderDisplay === "string" && decisionSenderDisplay.length > 0 ? "profile" : "id";
20458
+ if (verdict.kind === "resolved") provenanceDisplay = verdict.display;
20459
+ console.error(
20460
+ `[telegram:reframe] op=sender-name senderId=${decisionSenderId} accountId=${accountId.slice(0, 8)} source=${source} resolvedMs=${Date.now() - startedMs}` + (verdict.kind === "ambiguous" ? ` reason=ambiguous count=${verdict.count}` : "") + (error ? ` error=${error}` : "")
20461
+ );
20462
+ }
20263
20463
  if (decision.kind === "chat-member") {
20264
20464
  console.error(
20265
- `${TAG34} op=chat-member-group botId=${botId} chatId=${decision.chatId} userId=${decision.userId || "none"} status=${logValue2(decision.status)}`
20465
+ `${TAG36} op=chat-member-group botId=${botId} chatId=${decision.chatId} userId=${decision.userId || "none"} status=${logValue2(decision.status)}`
20266
20466
  );
20267
20467
  return c.json({ ok: true }, 200);
20268
20468
  }
20269
20469
  if (decision.kind === "recorded") {
20270
20470
  console.error(
20271
- `${TAG34} op=recorded botId=${botId} envelope=${decision.envelope} chatId=${decision.chatId ?? "none"} messageId=${decision.messageId ?? "none"}`
20471
+ `${TAG36} op=recorded botId=${botId} envelope=${decision.envelope} chatId=${decision.chatId ?? "none"} messageId=${decision.messageId ?? "none"}`
20272
20472
  );
20273
20473
  return c.json({ ok: true }, 200);
20274
20474
  }
20275
20475
  if (decision.kind === "comment-root") {
20276
20476
  console.error(
20277
- `${TAG34} op=channel-comment-root botId=${botId} groupChatId=${decision.groupChatId} rootMessageId=${decision.rootMessageId} channelChatId=${decision.channelChatId}`
20477
+ `${TAG36} op=channel-comment-root botId=${botId} groupChatId=${decision.groupChatId} rootMessageId=${decision.rootMessageId} channelChatId=${decision.channelChatId}`
20278
20478
  );
20279
20479
  return c.json({ ok: true }, 200);
20280
20480
  }
20281
20481
  if (decision.kind === "callback") {
20282
20482
  console.error(
20283
- `${TAG34} op=callback-received botId=${botId} callbackId=${decision.callbackId} chatId=${decision.chatId} messageId=${decision.messageId ?? "none"} data=${decision.data === null ? "none" : logValue2(decision.data)}`
20483
+ `${TAG36} op=callback-received botId=${botId} callbackId=${decision.callbackId} chatId=${decision.chatId} messageId=${decision.messageId ?? "none"} data=${decision.data === null ? "none" : logValue2(decision.data)}`
20284
20484
  );
20285
20485
  console.error(
20286
- `${TAG34} op=callback-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20486
+ `${TAG36} op=callback-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20287
20487
  );
20288
20488
  const cbSubject = telegramConversationSubject(decision.chatType, decision.chatId, decision.senderId);
20289
20489
  if (decision.allowed) {
@@ -20291,12 +20491,12 @@ app11.post("/", async (c) => {
20291
20491
  const cbAt = (/* @__PURE__ */ new Date()).toISOString();
20292
20492
  let cbBody = `[button: ${decision.data ?? "no-data"}]`;
20293
20493
  if (decision.data !== null) {
20294
- const label = (0, import_dist8.readButtonLabel)(accountDir, (0, import_dist8.buttonLabelKey)(botId, decision.chatId, decision.data));
20494
+ const label = (0, import_dist9.readButtonLabel)(accountDir, (0, import_dist9.buttonLabelKey)(botId, decision.chatId, decision.data));
20295
20495
  if (label !== null) {
20296
20496
  cbBody = label;
20297
20497
  } else {
20298
20498
  console.error(
20299
- `${TAG34} op=button-label-miss botId=${botId} chatId=${decision.chatId} data=${logValue2(decision.data)}`
20499
+ `${TAG36} op=button-label-miss botId=${botId} chatId=${decision.chatId} data=${logValue2(decision.data)}`
20300
20500
  );
20301
20501
  }
20302
20502
  }
@@ -20357,11 +20557,11 @@ app11.post("/", async (c) => {
20357
20557
  const answered4 = await answerTelegramCallback(entry3.token, decision.callbackId, answerText);
20358
20558
  const outcome = !answered4.ok ? "error" : decision.allowed ? "stored" : "refused";
20359
20559
  console.error(
20360
- `${TAG34} op=callback-answered botId=${botId} callbackId=${decision.callbackId} ms=${Date.now() - answerStartedAt} outcome=${outcome}${answered4.error ? ` error=${logValue2(answered4.error)}` : ""}`
20560
+ `${TAG36} op=callback-answered botId=${botId} callbackId=${decision.callbackId} ms=${Date.now() - answerStartedAt} outcome=${outcome}${answered4.error ? ` error=${logValue2(answered4.error)}` : ""}`
20361
20561
  );
20362
20562
  if (decision.allowed && parseDispatchCallback(decision.data) === null) {
20363
20563
  const cbActive = entryActive(entry3, accountDir, botId);
20364
- console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${cbActive}`);
20564
+ console.error(`${TAG36} op=agent botId=${botId} slug=${agentSlug} active=${cbActive}`);
20365
20565
  raiseChannelTurn({
20366
20566
  accountId,
20367
20567
  accountDir,
@@ -20372,7 +20572,7 @@ app11.post("/", async (c) => {
20372
20572
  specialist: entry3.role === "specialist" ? entry3.specialist : null,
20373
20573
  active: cbActive,
20374
20574
  senderId: decision.senderId,
20375
- senderDisplay: decision.senderDisplay,
20575
+ senderDisplay: provenanceDisplay,
20376
20576
  chatId: decision.chatId,
20377
20577
  // The same body the store row above carries, so the operator's reader and
20378
20578
  // the agent's turn say the same thing about one press.
@@ -20388,10 +20588,10 @@ app11.post("/", async (c) => {
20388
20588
  if (decision.kind === "poll") {
20389
20589
  const options = decision.optionIds.length > 0 ? decision.optionIds.join(",") : "retracted";
20390
20590
  console.error(
20391
- `${TAG34} op=poll-received botId=${botId} updateId=${decision.updateId ?? "none"} senderId=${decision.senderId} pollId=${logValue2(decision.pollId)} options=${options}`
20591
+ `${TAG36} op=poll-received botId=${botId} updateId=${decision.updateId ?? "none"} senderId=${decision.senderId} pollId=${logValue2(decision.pollId)} options=${options}`
20392
20592
  );
20393
20593
  console.error(
20394
- `${TAG34} op=poll-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20594
+ `${TAG36} op=poll-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20395
20595
  );
20396
20596
  if (!decision.allowed) return c.json({ ok: true }, 200);
20397
20597
  const pollKey = telegramChannelKey(botId, decision.senderId);
@@ -20414,7 +20614,7 @@ app11.post("/", async (c) => {
20414
20614
  createdAt: pollAt
20415
20615
  });
20416
20616
  const pollActive = entryActive(entry3, accountDir, botId);
20417
- console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${pollActive}`);
20617
+ console.error(`${TAG36} op=agent botId=${botId} slug=${agentSlug} active=${pollActive}`);
20418
20618
  raiseChannelTurn({
20419
20619
  accountId,
20420
20620
  accountDir,
@@ -20425,7 +20625,7 @@ app11.post("/", async (c) => {
20425
20625
  specialist: entry3.role === "specialist" ? entry3.specialist : null,
20426
20626
  active: pollActive,
20427
20627
  senderId: decision.senderId,
20428
- senderDisplay: decision.senderDisplay,
20628
+ senderDisplay: provenanceDisplay,
20429
20629
  chatId: decision.chatId,
20430
20630
  text: pollBody,
20431
20631
  channelKey: pollKey,
@@ -20440,10 +20640,10 @@ app11.post("/", async (c) => {
20440
20640
  const quotedBody = findInboundBody(accountId, rKey, decision.messageId);
20441
20641
  const shown = decision.newReaction.length > 0 ? decision.newReaction.join(",") : "none";
20442
20642
  console.error(
20443
- `${TAG34} op=reaction-received botId=${botId} chatId=${decision.chatId} messageId=${decision.messageId} senderId=${decision.senderId} old=${decision.oldReaction.join(",") || "none"} new=${shown} targetHeld=${quotedBody === null ? "no" : "yes"}`
20643
+ `${TAG36} op=reaction-received botId=${botId} chatId=${decision.chatId} messageId=${decision.messageId} senderId=${decision.senderId} old=${decision.oldReaction.join(",") || "none"} new=${shown} targetHeld=${quotedBody === null ? "no" : "yes"}`
20444
20644
  );
20445
20645
  console.error(
20446
- `${TAG34} op=reaction-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20646
+ `${TAG36} op=reaction-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20447
20647
  );
20448
20648
  if (!decision.allowed) return c.json({ ok: true }, 200);
20449
20649
  const rScope = entry3.role === "admin" ? "admin" : "public";
@@ -20466,7 +20666,7 @@ app11.post("/", async (c) => {
20466
20666
  createdAt: rAt
20467
20667
  });
20468
20668
  const rActive = entryActive(entry3, accountDir, botId);
20469
- console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${rActive}`);
20669
+ console.error(`${TAG36} op=agent botId=${botId} slug=${agentSlug} active=${rActive}`);
20470
20670
  raiseChannelTurn({
20471
20671
  accountId,
20472
20672
  accountDir,
@@ -20477,7 +20677,7 @@ app11.post("/", async (c) => {
20477
20677
  specialist: entry3.role === "specialist" ? entry3.specialist : null,
20478
20678
  active: rActive,
20479
20679
  senderId: decision.senderId,
20480
- senderDisplay: decision.senderDisplay,
20680
+ senderDisplay: provenanceDisplay,
20481
20681
  chatId: decision.chatId,
20482
20682
  text: rBody,
20483
20683
  channelKey: rKey,
@@ -20490,25 +20690,25 @@ app11.post("/", async (c) => {
20490
20690
  if (decision.kind === "checkout") {
20491
20691
  if (decision.query === "pre-checkout") {
20492
20692
  console.error(
20493
- `${TAG34} op=precheckout-received botId=${botId} queryId=${decision.queryId} payload=${logValue2(decision.payload)} amount=${decision.amount ?? "none"} currency=${decision.currency ?? "none"}`
20693
+ `${TAG36} op=precheckout-received botId=${botId} queryId=${decision.queryId} payload=${logValue2(decision.payload)} amount=${decision.amount ?? "none"} currency=${decision.currency ?? "none"}`
20494
20694
  );
20495
20695
  noteCheckoutArrived(botId, decision.queryId, Date.now());
20496
20696
  } else {
20497
20697
  console.error(
20498
- `${TAG34} op=shipping-received botId=${botId} queryId=${decision.queryId} payload=${logValue2(decision.payload)}`
20698
+ `${TAG36} op=shipping-received botId=${botId} queryId=${decision.queryId} payload=${logValue2(decision.payload)}`
20499
20699
  );
20500
20700
  }
20501
20701
  console.error(
20502
- `${TAG34} op=checkout-ignored botId=${botId} senderId=${decision.senderId} queryId=${decision.queryId} query=${decision.query} allowed=${decision.allowed} reason=payments-removed`
20702
+ `${TAG36} op=checkout-ignored botId=${botId} senderId=${decision.senderId} queryId=${decision.queryId} query=${decision.query} allowed=${decision.allowed} reason=payments-removed`
20503
20703
  );
20504
20704
  return c.json({ ok: true }, 200);
20505
20705
  }
20506
20706
  if (decision.kind === "subscription") {
20507
20707
  console.error(
20508
- `${TAG34} op=subscription botId=${botId} userId=${decision.senderId} payload=${logValue2(decision.payload)} state=${decision.state}`
20708
+ `${TAG36} op=subscription botId=${botId} userId=${decision.senderId} payload=${logValue2(decision.payload)} state=${decision.state}`
20509
20709
  );
20510
20710
  console.error(
20511
- `${TAG34} op=subscription-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20711
+ `${TAG36} op=subscription-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20512
20712
  );
20513
20713
  if (!decision.allowed) return c.json({ ok: true }, 200);
20514
20714
  const sKey = telegramChannelKey(botId, decision.senderId);
@@ -20531,7 +20731,7 @@ app11.post("/", async (c) => {
20531
20731
  createdAt: sAt
20532
20732
  });
20533
20733
  const sActive = entryActive(entry3, accountDir, botId);
20534
- console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${sActive}`);
20734
+ console.error(`${TAG36} op=agent botId=${botId} slug=${agentSlug} active=${sActive}`);
20535
20735
  raiseChannelTurn({
20536
20736
  accountId,
20537
20737
  accountDir,
@@ -20542,7 +20742,7 @@ app11.post("/", async (c) => {
20542
20742
  specialist: entry3.role === "specialist" ? entry3.specialist : null,
20543
20743
  active: sActive,
20544
20744
  senderId: decision.senderId,
20545
- senderDisplay: decision.senderDisplay,
20745
+ senderDisplay: provenanceDisplay,
20546
20746
  chatId: decision.chatId,
20547
20747
  text: sBody,
20548
20748
  channelKey: sKey,
@@ -20555,7 +20755,7 @@ app11.post("/", async (c) => {
20555
20755
  if (!decision.isClosed) return c.json({ ok: true }, 200);
20556
20756
  const where = findPollConversation(accountId, decision.pollId);
20557
20757
  console.error(
20558
- `${TAG34} op=poll-closed botId=${botId} pollId=${logValue2(decision.pollId)} options=${decision.options.length} totalVotes=${decision.totalVotes} targetHeld=${where === null ? "no" : "yes"}`
20758
+ `${TAG36} op=poll-closed botId=${botId} pollId=${logValue2(decision.pollId)} options=${decision.options.length} totalVotes=${decision.totalVotes} targetHeld=${where === null ? "no" : "yes"}`
20559
20759
  );
20560
20760
  if (where === null) return c.json({ ok: true }, 200);
20561
20761
  const plScope = entry3.role === "admin" ? "admin" : "public";
@@ -20578,7 +20778,7 @@ app11.post("/", async (c) => {
20578
20778
  createdAt: plAt
20579
20779
  });
20580
20780
  const plActive = entryActive(entry3, accountDir, botId);
20581
- console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${plActive}`);
20781
+ console.error(`${TAG36} op=agent botId=${botId} slug=${agentSlug} active=${plActive}`);
20582
20782
  raiseChannelTurn({
20583
20783
  accountId,
20584
20784
  accountDir,
@@ -20599,7 +20799,7 @@ app11.post("/", async (c) => {
20599
20799
  }
20600
20800
  if (decision.kind === "member") {
20601
20801
  console.error(
20602
- `${TAG34} op=chat-member botId=${botId} chatId=${decision.chatId} status=${decision.status}`
20802
+ `${TAG36} op=chat-member botId=${botId} chatId=${decision.chatId} status=${decision.status}`
20603
20803
  );
20604
20804
  return c.json({ ok: true }, 200);
20605
20805
  }
@@ -20607,13 +20807,13 @@ app11.post("/", async (c) => {
20607
20807
  const { newBotId } = decision;
20608
20808
  if (!decision.authorised) {
20609
20809
  console.error(
20610
- `${TAG34} op=managed-bot botId=${botId} event=${decision.event} newBotId=${newBotId} result=refused reason=public-entry-no-provision`
20810
+ `${TAG36} op=managed-bot botId=${botId} event=${decision.event} newBotId=${newBotId} result=refused reason=public-entry-no-provision`
20611
20811
  );
20612
20812
  return c.json({ ok: true }, 200);
20613
20813
  }
20614
20814
  const { event } = decision;
20615
20815
  console.error(
20616
- `${TAG34} op=managed-bot botId=${botId} event=${event} newBotId=${newBotId} creator=${decision.creatorId ?? "none"}`
20816
+ `${TAG36} op=managed-bot botId=${botId} event=${event} newBotId=${newBotId} creator=${decision.creatorId ?? "none"}`
20617
20817
  );
20618
20818
  const fetched = await getManagedBotToken(entry3.token, Number(newBotId));
20619
20819
  console.error(
@@ -20669,11 +20869,11 @@ app11.post("/", async (c) => {
20669
20869
  const matched = bizStored !== null && bizStored.id === decision.businessConnectionId;
20670
20870
  const bizNowS = Math.floor(Date.now() / 1e3);
20671
20871
  console.error(
20672
- `${TAG34} op=business-message botId=${botId} connectionId=${truncateConnectionId(decision.businessConnectionId)} chatId=${decision.chatId} senderId=${decision.senderId} messageId=${decision.messageId ?? "none"} kind=${shape.primaryKind} ageS=${decision.date === null ? "none" : bizNowS - decision.date} matched=${matched ? "yes" : "no"}`
20872
+ `${TAG36} op=business-message botId=${botId} connectionId=${truncateConnectionId(decision.businessConnectionId)} chatId=${decision.chatId} senderId=${decision.senderId} messageId=${decision.messageId ?? "none"} kind=${shape.primaryKind} ageS=${decision.date === null ? "none" : bizNowS - decision.date} matched=${matched ? "yes" : "no"}`
20673
20873
  );
20674
20874
  if (!matched) {
20675
20875
  console.error(
20676
- `${TAG34} op=business-refused botId=${botId} connectionId=${truncateConnectionId(decision.businessConnectionId)} reason=${bizStored === null ? "no-record" : "mismatch"}`
20876
+ `${TAG36} op=business-refused botId=${botId} connectionId=${truncateConnectionId(decision.businessConnectionId)} reason=${bizStored === null ? "no-record" : "mismatch"}`
20677
20877
  );
20678
20878
  return c.json({ ok: true }, 200);
20679
20879
  }
@@ -20735,7 +20935,7 @@ app11.post("/", async (c) => {
20735
20935
  }
20736
20936
  const bizGateway = getTelegramGateway();
20737
20937
  if (!bizGateway) {
20738
- console.error(`${TAG34} op=reply-dropped botId=${botId} reason=gateway-not-ready`);
20938
+ console.error(`${TAG36} op=reply-dropped botId=${botId} reason=gateway-not-ready`);
20739
20939
  return c.json({ ok: true }, 200);
20740
20940
  }
20741
20941
  const bizConnectionId = bizStored.id;
@@ -20749,7 +20949,7 @@ app11.post("/", async (c) => {
20749
20949
  );
20750
20950
  if (!verdict.allowed) {
20751
20951
  console.error(
20752
- `${TAG34} op=reply-dropped botId=${botId} reason=business-window-${verdict.reason} detail="${logValue2(businessWindowRefusal(decision.chatId, verdict.ageS))}"`
20952
+ `${TAG36} op=reply-dropped botId=${botId} reason=business-window-${verdict.reason} detail="${logValue2(businessWindowRefusal(decision.chatId, verdict.ageS))}"`
20753
20953
  );
20754
20954
  return;
20755
20955
  }
@@ -20776,8 +20976,8 @@ app11.post("/", async (c) => {
20776
20976
  origin: "agent",
20777
20977
  createdAt: outAt
20778
20978
  });
20779
- console.error(`${TAG34} op=reply-sent botId=${botId} ok=true`);
20780
- } else console.error(`${TAG34} op=reply-dropped botId=${botId} reason=${sent.error}`);
20979
+ console.error(`${TAG36} op=reply-sent botId=${botId} ok=true`);
20980
+ } else console.error(`${TAG36} op=reply-dropped botId=${botId} reason=${sent.error}`);
20781
20981
  };
20782
20982
  void bizGateway.handleInbound({
20783
20983
  accountId,
@@ -20808,7 +21008,7 @@ app11.post("/", async (c) => {
20808
21008
  const delStored = readBusinessConnection(accountDir, botId);
20809
21009
  if (delStored === null || delStored.id !== decision.businessConnectionId) {
20810
21010
  console.error(
20811
- `${TAG34} op=business-refused botId=${botId} connectionId=${truncateConnectionId(decision.businessConnectionId)} reason=${delStored === null ? "no-record" : "mismatch"}`
21011
+ `${TAG36} op=business-refused botId=${botId} connectionId=${truncateConnectionId(decision.businessConnectionId)} reason=${delStored === null ? "no-record" : "mismatch"}`
20812
21012
  );
20813
21013
  return c.json({ ok: true }, 200);
20814
21014
  }
@@ -20825,12 +21025,12 @@ app11.post("/", async (c) => {
20825
21025
  if (res.created) marked += 1;
20826
21026
  }
20827
21027
  console.error(
20828
- `${TAG34} op=business-deleted botId=${botId} chatId=${decision.chatId} ids=${decision.messageIds.length} marked=${marked}`
21028
+ `${TAG36} op=business-deleted botId=${botId} chatId=${decision.chatId} ids=${decision.messageIds.length} marked=${marked}`
20829
21029
  );
20830
21030
  return c.json({ ok: true }, 200);
20831
21031
  }
20832
21032
  const { senderId, chatId, text } = decision;
20833
- console.error(`${TAG34} op=access botId=${botId} senderId=${senderId} allowed=true reason=${decision.reason}`);
21033
+ console.error(`${TAG36} op=access botId=${botId} senderId=${senderId} allowed=true reason=${decision.reason}`);
20834
21034
  const subject = telegramConversationSubject(decision.chatType, chatId, senderId);
20835
21035
  const channelKey = telegramChannelKey(botId, subject);
20836
21036
  const scope = entry3.role === "public" ? "public" : "admin";
@@ -20917,17 +21117,17 @@ app11.post("/", async (c) => {
20917
21117
  const active = entryActive(entry3, accountDir, botId, (rosterValid) => {
20918
21118
  if (entry3.role !== "specialist") return;
20919
21119
  console.error(
20920
- `${TAG34} op=specialist-inbound botId=${botId} specialist=${logValue2(entry3.specialist)} senderId=${senderId} sessionId=${sessionId} rosterValid=${rosterValid ? "yes" : "no"}`
21120
+ `${TAG36} op=specialist-inbound botId=${botId} specialist=${logValue2(entry3.specialist)} senderId=${senderId} sessionId=${sessionId} rosterValid=${rosterValid ? "yes" : "no"}`
20921
21121
  );
20922
21122
  });
20923
- console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${active}`);
21123
+ console.error(`${TAG36} op=agent botId=${botId} slug=${agentSlug} active=${active}`);
20924
21124
  if (!active) {
20925
- console.error(`${TAG34} op=reject reason=agent-inactive botId=${botId} agent=${agentSlug}`);
21125
+ console.error(`${TAG36} op=reject reason=agent-inactive botId=${botId} agent=${agentSlug}`);
20926
21126
  return c.json({ ok: true }, 200);
20927
21127
  }
20928
21128
  const gateway = getTelegramGateway();
20929
21129
  if (!gateway) {
20930
- console.error(`${TAG34} op=reply-dropped botId=${botId} reason=gateway-not-ready`);
21130
+ console.error(`${TAG36} op=reply-dropped botId=${botId} reason=gateway-not-ready`);
20931
21131
  return c.json({ ok: true }, 200);
20932
21132
  }
20933
21133
  const replyThreadId = decision.threadId ?? void 0;
@@ -20949,8 +21149,8 @@ app11.post("/", async (c) => {
20949
21149
  origin: "agent",
20950
21150
  createdAt: at
20951
21151
  });
20952
- console.error(`${TAG34} op=reply-sent botId=${botId} ok=true`);
20953
- } else console.error(`${TAG34} op=reply-dropped botId=${botId} reason=${sent.error}`);
21152
+ console.error(`${TAG36} op=reply-sent botId=${botId} ok=true`);
21153
+ } else console.error(`${TAG36} op=reply-dropped botId=${botId} reason=${sent.error}`);
20954
21154
  };
20955
21155
  const media = await downloadTelegramMedia({ token: entry3.token, botId, items: decision.media });
20956
21156
  if (media.length > 0) {
@@ -21010,7 +21210,7 @@ app11.post("/", async (c) => {
21010
21210
  if (written.written && !decision.isEdit) noteShareStarted();
21011
21211
  } catch (err) {
21012
21212
  console.error(
21013
- `${TAG34} op=live-location op=write-failed botId=${botId} senderId=${senderId} detail="${logValue2(err instanceof Error ? err.message : String(err))}"`
21213
+ `${TAG36} op=live-location op=write-failed botId=${botId} senderId=${senderId} detail="${logValue2(err instanceof Error ? err.message : String(err))}"`
21014
21214
  );
21015
21215
  }
21016
21216
  }
@@ -21022,6 +21222,8 @@ app11.post("/", async (c) => {
21022
21222
  textIsPlaceholder: decision.textIsPlaceholder,
21023
21223
  media
21024
21224
  });
21225
+ const seen = await extractTelegramVideo({ botId, media: heard.media });
21226
+ heard.media = seen.media;
21025
21227
  if (heard.text !== text) {
21026
21228
  const patched = appendUpdate(accountId, channelKey, {
21027
21229
  kind: "update",
@@ -21030,7 +21232,7 @@ app11.post("/", async (c) => {
21030
21232
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
21031
21233
  });
21032
21234
  console.error(
21033
- `${TAG34} op=transcript-patch botId=${botId} target=${inboundMessageId} ok=${patched.created}`
21235
+ `${TAG36} op=transcript-patch botId=${botId} target=${inboundMessageId} ok=${patched.created}`
21034
21236
  );
21035
21237
  }
21036
21238
  {
@@ -21066,7 +21268,7 @@ app11.post("/", async (c) => {
21066
21268
  }
21067
21269
  if (decision.isEdit && decision.location) {
21068
21270
  console.error(
21069
- `${TAG34} op=live-location op=suppressed-turn botId=${botId} senderId=${senderId} reason=live-edit`
21271
+ `${TAG36} op=live-location op=suppressed-turn botId=${botId} senderId=${senderId} reason=live-edit`
21070
21272
  );
21071
21273
  return;
21072
21274
  }
@@ -21082,7 +21284,8 @@ app11.post("/", async (c) => {
21082
21284
  agentSlug,
21083
21285
  senderId,
21084
21286
  // Task 2616 — the name beside the id on the turn's provenance line.
21085
- senderDisplay: decision.senderDisplay,
21287
+ // Task 2705 — the account's own contact name when one holds this id.
21288
+ senderDisplay: provenanceDisplay,
21086
21289
  // Task 2616 — a group is ONE conversation. The gateway's hub key, reply
21087
21290
  // map, spawn guard and session id all take this, so five members of one
21088
21291
  // room reach one channel server rather than opening five sessions whose
@@ -21117,7 +21320,7 @@ function mountTelegramRoutes(app81, telegramRoutes) {
21117
21320
  // server/routes/quickbooks.ts
21118
21321
  import { join as join41 } from "path";
21119
21322
  import { existsSync as existsSync29, readFileSync as readFileSync33, writeFileSync as writeFileSync15, mkdirSync as mkdirSync8, rmSync as rmSync4, renameSync as renameSync5 } from "fs";
21120
- var TAG35 = "[quickbooks]";
21323
+ var TAG37 = "[quickbooks]";
21121
21324
  var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
21122
21325
  var STATE_RE = /^[A-Za-z0-9_-]+$/;
21123
21326
  function pendingPath(accountDir, state) {
@@ -21130,14 +21333,14 @@ async function completeConsent(args) {
21130
21333
  const { accountDir, code, realmId } = args;
21131
21334
  const state = args.state;
21132
21335
  if (!state || !STATE_RE.test(state) || !existsSync29(pendingPath(accountDir, state))) {
21133
- console.error(`${TAG35} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
21336
+ console.error(`${TAG37} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
21134
21337
  return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
21135
21338
  }
21136
21339
  const claimFile = `${pendingPath(accountDir, state)}.claimed`;
21137
21340
  try {
21138
21341
  renameSync5(pendingPath(accountDir, state), claimFile);
21139
21342
  } catch {
21140
- console.error(`${TAG35} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
21343
+ console.error(`${TAG37} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
21141
21344
  return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
21142
21345
  }
21143
21346
  try {
@@ -21145,14 +21348,14 @@ async function completeConsent(args) {
21145
21348
  try {
21146
21349
  pending = JSON.parse(readFileSync33(claimFile, "utf-8"));
21147
21350
  } catch {
21148
- console.error(`${TAG35} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
21351
+ console.error(`${TAG37} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
21149
21352
  return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
21150
21353
  }
21151
21354
  if (Date.now() > pending.expiresAt) {
21152
- console.error(`${TAG35} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
21355
+ console.error(`${TAG37} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
21153
21356
  return { ok: false, status: 400, message: "Authorization request expired. Generate a new consent link.", stateValid: false };
21154
21357
  }
21155
- console.error(`${TAG35} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
21358
+ console.error(`${TAG37} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
21156
21359
  if (!code || !realmId) {
21157
21360
  return { ok: false, status: 400, message: "Missing code or realmId in the callback.", stateValid: true };
21158
21361
  }
@@ -21176,12 +21379,12 @@ async function completeConsent(args) {
21176
21379
  });
21177
21380
  const text = await res.text();
21178
21381
  if (!res.ok) {
21179
- console.error(`${TAG35} op=token-exchange realmId=${realmId} persisted=false`);
21382
+ console.error(`${TAG37} op=token-exchange realmId=${realmId} persisted=false`);
21180
21383
  return { ok: false, status: 502, message: `Token exchange failed (${res.status}). Generate a new consent link to retry.`, stateValid: true };
21181
21384
  }
21182
21385
  const body = JSON.parse(text);
21183
21386
  if (!body.refresh_token) {
21184
- console.error(`${TAG35} op=token-exchange realmId=${realmId} persisted=false`);
21387
+ console.error(`${TAG37} op=token-exchange realmId=${realmId} persisted=false`);
21185
21388
  return { ok: false, status: 502, message: "Token exchange returned no refresh token.", stateValid: true };
21186
21389
  }
21187
21390
  const now = Date.now();
@@ -21194,7 +21397,7 @@ async function completeConsent(args) {
21194
21397
  mkdirSync8(join41(accountDir, "secrets"), { recursive: true });
21195
21398
  writeFileSync15(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
21196
21399
  const persisted = JSON.parse(readFileSync33(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
21197
- console.error(`${TAG35} op=token-exchange realmId=${realmId} persisted=${persisted}`);
21400
+ console.error(`${TAG37} op=token-exchange realmId=${realmId} persisted=${persisted}`);
21198
21401
  return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
21199
21402
  } finally {
21200
21403
  rmSync4(claimFile, { force: true });
@@ -21207,7 +21410,7 @@ var app12 = new Hono();
21207
21410
  app12.get("/callback", async (c) => {
21208
21411
  const account = resolveAccount();
21209
21412
  if (!account) {
21210
- console.error(`${TAG35} op=callback stateValid=false realmId= reason=no-account`);
21413
+ console.error(`${TAG37} op=callback stateValid=false realmId= reason=no-account`);
21211
21414
  return c.html(page("QuickBooks", "This install has no account configured."), 500);
21212
21415
  }
21213
21416
  const result = await completeConsent({
@@ -22172,7 +22375,7 @@ function startDispatchFieldCensus(getSession3, intervalMs = FIFTEEN_MIN_MS) {
22172
22375
  }
22173
22376
 
22174
22377
  // server/routes/field.ts
22175
- var TAG36 = "[dispatch-field]";
22378
+ var TAG38 = "[dispatch-field]";
22176
22379
  var MAX_INITDATA_AGE_S = 3600;
22177
22380
  var ZONE_FALLBACK2 = "UTC";
22178
22381
  var SITE_HISTORY_LIMIT = 20;
@@ -22221,7 +22424,7 @@ function gate(c) {
22221
22424
  Date.now()
22222
22425
  );
22223
22426
  console.log(
22224
- `${TAG36} op=initdata-verify workerId=${auth.ok ? auth.worker.workerId : "none"} ok=${auth.ok} reason=${auth.ok ? "ok" : auth.reason} ageS=${auth.ageS}`
22427
+ `${TAG38} op=initdata-verify workerId=${auth.ok ? auth.worker.workerId : "none"} ok=${auth.ok} reason=${auth.ok ? "ok" : auth.reason} ageS=${auth.ageS}`
22225
22428
  );
22226
22429
  if (auth.ok) {
22227
22430
  noteFieldContact(auth.accountId, auth.worker.workerId, Date.now());
@@ -22243,7 +22446,7 @@ async function loadSheet(auth, visitId) {
22243
22446
  if (!visit) return { ok: false, status: 404, reason: "not-found" };
22244
22447
  if (visit.workerId !== auth.worker.workerId) {
22245
22448
  console.error(
22246
- `${TAG36} op=visit-refused visitId=${visitId} workerId=${auth.worker.workerId} reason=not-assigned`
22449
+ `${TAG38} op=visit-refused visitId=${visitId} workerId=${auth.worker.workerId} reason=not-assigned`
22247
22450
  );
22248
22451
  return { ok: false, status: 403, reason: "not-assigned" };
22249
22452
  }
@@ -22264,7 +22467,7 @@ async function loadSheet(auth, visitId) {
22264
22467
  }
22265
22468
  function readFailed(reason, err, ctx) {
22266
22469
  console.error(
22267
- `${TAG36} op=read-failed ${ctx} error="${err instanceof Error ? err.message : String(err)}" reason=${reason}`
22470
+ `${TAG38} op=read-failed ${ctx} error="${err instanceof Error ? err.message : String(err)}" reason=${reason}`
22268
22471
  );
22269
22472
  }
22270
22473
  var app14 = new Hono();
@@ -22280,7 +22483,7 @@ app14.get("/session", async (c) => {
22280
22483
  return c.json({ reason: "read-failed" }, 503);
22281
22484
  }
22282
22485
  console.log(
22283
- `${TAG36} op=session-open workerId=${auth.worker.workerId} visits=${visits} source=network`
22486
+ `${TAG38} op=session-open workerId=${auth.worker.workerId} visits=${visits} source=network`
22284
22487
  );
22285
22488
  return c.json({
22286
22489
  worker: { workerId: auth.worker.workerId, name: auth.worker.name },
@@ -22316,7 +22519,7 @@ app14.get("/visit/:visitId", async (c) => {
22316
22519
  const loaded = await loadSheet(auth, visitId);
22317
22520
  if (!loaded.ok) return c.json({ reason: loaded.reason }, loaded.status);
22318
22521
  console.log(
22319
- `${TAG36} op=sheet-open visitId=${visitId} workerId=${auth.worker.workerId} historyVisits=${loaded.history.length} source=network`
22522
+ `${TAG38} op=sheet-open visitId=${visitId} workerId=${auth.worker.workerId} historyVisits=${loaded.history.length} source=network`
22320
22523
  );
22321
22524
  return c.json({
22322
22525
  visit: loaded.visit,
@@ -22352,7 +22555,7 @@ app14.post("/visit/:visitId/record", async (c) => {
22352
22555
  );
22353
22556
  if (!out.ok) return c.json({ reason: out.reason, detail: out.message }, 400);
22354
22557
  const kind = body.checklistItem !== void 0 ? "checklist" : body.partId !== void 0 ? "part" : body.signature !== void 0 ? "signature" : body.mediaRef !== void 0 ? "media" : "note";
22355
- console.log(`${TAG36} op=edit-recorded visitId=${visitId} workerId=${auth.worker.workerId} kind=${kind}`);
22558
+ console.log(`${TAG38} op=edit-recorded visitId=${visitId} workerId=${auth.worker.workerId} kind=${kind}`);
22356
22559
  return c.json(out.value);
22357
22560
  });
22358
22561
  app14.post("/visit/:visitId/status", async (c) => {
@@ -22379,7 +22582,7 @@ app14.post("/visit/:visitId/status", async (c) => {
22379
22582
  const prefix = "missing-required-field:";
22380
22583
  if (out.reason.startsWith(prefix)) {
22381
22584
  const missing = out.reason.slice(prefix.length);
22382
- console.error(`${TAG36} op=complete-refused visitId=${visitId} missing=${missing}`);
22585
+ console.error(`${TAG38} op=complete-refused visitId=${visitId} missing=${missing}`);
22383
22586
  return c.json({ reason: "missing-required-field", missing, detail: out.message }, 409);
22384
22587
  }
22385
22588
  return c.json({ reason: out.reason, detail: out.message }, 409);
@@ -22391,7 +22594,7 @@ app14.post("/visit/:visitId/status", async (c) => {
22391
22594
  Math.round((Date.parse(out.value.at) - Date.parse(dueToLeaveAt)) / 1e3)
22392
22595
  );
22393
22596
  console.log(
22394
- `${TAG36} op=complete visitId=${visitId} checklist=${state.checklistDone}/${state.checklistTotal} notes=${state.notes} media=${state.media} parts=${state.parts} signatures=${state.signatures} lagS=${lagS}`
22597
+ `${TAG38} op=complete visitId=${visitId} checklist=${state.checklistDone}/${state.checklistTotal} notes=${state.notes} media=${state.media} parts=${state.parts} signatures=${state.signatures} lagS=${lagS}`
22395
22598
  );
22396
22599
  return c.json(out.value);
22397
22600
  });
@@ -22400,10 +22603,10 @@ app14.post("/probe", async (c) => {
22400
22603
  if (!auth.ok) return c.json({ reason: auth.reason }, auth.status);
22401
22604
  const p = await c.req.json();
22402
22605
  console.log(
22403
- `${TAG36} op=storage-probe platform=${p.platform} indexedDB=${p.indexedDB} localStorage=${p.localStorage} cacheStorage=${p.cacheStorage} serviceWorker=${p.serviceWorker} persisted=${p.persisted} workerId=${auth.worker.workerId}`
22606
+ `${TAG38} op=storage-probe platform=${p.platform} indexedDB=${p.indexedDB} localStorage=${p.localStorage} cacheStorage=${p.cacheStorage} serviceWorker=${p.serviceWorker} persisted=${p.persisted} workerId=${auth.worker.workerId}`
22404
22607
  );
22405
22608
  console.log(
22406
- `${TAG36} op=launch-params workerId=${auth.worker.workerId} keys=${(p.launchKeys ?? []).join(",") || "none"}`
22609
+ `${TAG38} op=launch-params workerId=${auth.worker.workerId} keys=${(p.launchKeys ?? []).join(",") || "none"}`
22407
22610
  );
22408
22611
  const anyUsable = p.indexedDB || p.localStorage || p.cacheStorage;
22409
22612
  noteFieldStorage(auth.accountId, auth.worker.workerId, anyUsable, Date.now());
@@ -22530,6 +22733,14 @@ app15.post("/", async (c) => {
22530
22733
  // the standard fields.
22531
22734
  "graph-fullscreen-not-covering",
22532
22735
  "graph-fullscreen-canvas-mismatch",
22736
+ // Task 2439 — the admin composer's own send failure. The inline reporter in
22737
+ // server/index.ts hooks only `error` and `unhandledrejection`, so a caught
22738
+ // fetch rejection in send() reached no sink at all. Without this entry the
22739
+ // POST collapses to kind=unknown and the log loses the source, which is the
22740
+ // whole point of the report. Deliberately NOT in ERROR_KINDS: it fires at
22741
+ // most once per send press, so it cannot exhaust a budget, and leaving it in
22742
+ // the telemetry class keeps the protected error budget for uncaught errors.
22743
+ "webchat-send-failed",
22533
22744
  "event"
22534
22745
  ]);
22535
22746
  const kind = allowedKinds.has(kindRaw) ? kindRaw : "unknown";
@@ -25309,7 +25520,7 @@ function resolveTunnelUrl() {
25309
25520
  if (!state) return null;
25310
25521
  return `https://${hostname2}.${state.domain}`;
25311
25522
  }
25312
- var TAG37 = "[claude-session-manager:wrapper]";
25523
+ var TAG39 = "[claude-session-manager:wrapper]";
25313
25524
  async function refuseIfClaudeAuthDead(c, route, sessionId) {
25314
25525
  const auth = await ensureAuth();
25315
25526
  if (auth.status !== "dead" && auth.status !== "missing") return null;
@@ -25327,12 +25538,12 @@ async function performSpawnWithInitialMessage(args) {
25327
25538
  const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
25328
25539
  const ownerMs = Date.now() - ownerStart;
25329
25540
  const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
25330
- console.log(`${TAG37} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
25541
+ console.log(`${TAG39} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
25331
25542
  const dormantPlugins = computeDormantPlugins(args.senderId);
25332
25543
  const activePlugins = computeActivePlugins(args.senderId);
25333
25544
  const specialistDomains = computeSpecialistDomains(args.senderId);
25334
25545
  const tunnelUrl = resolveTunnelUrl();
25335
- console.log(`${TAG37} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
25546
+ console.log(`${TAG39} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
25336
25547
  const upstreamPayload = JSON.stringify({
25337
25548
  senderId: args.senderId,
25338
25549
  // Task 205 — pass userId through to the manager so it lands as
@@ -25359,24 +25570,24 @@ async function performSpawnWithInitialMessage(args) {
25359
25570
  // unshapely values.
25360
25571
  conversationNodeId: args.conversationNodeId
25361
25572
  });
25362
- console.log(`${TAG37} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
25573
+ console.log(`${TAG39} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
25363
25574
  const forwardStart = Date.now();
25364
25575
  const upstream = await fetch(`${managerBase("claude-session-manager:wrapper")}/public-spawn`, {
25365
25576
  method: "POST",
25366
25577
  headers: { "content-type": "application/json" },
25367
25578
  body: upstreamPayload
25368
25579
  }).catch((err) => {
25369
- console.error(`${TAG37} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
25580
+ console.error(`${TAG39} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
25370
25581
  return null;
25371
25582
  });
25372
25583
  if (!upstream) return {
25373
25584
  response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
25374
25585
  claudeSessionId: null
25375
25586
  };
25376
- console.log(`${TAG37} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
25587
+ console.log(`${TAG39} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
25377
25588
  if (args.initialMessage) {
25378
25589
  const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
25379
- console.log(`${TAG37} initial-message-inlined bytes=${inputBytes}`);
25590
+ console.log(`${TAG39} initial-message-inlined bytes=${inputBytes}`);
25380
25591
  }
25381
25592
  const bodyText = await upstream.text().catch(() => "");
25382
25593
  let claudeSessionId = null;
@@ -25411,7 +25622,7 @@ app24.post("/", async (c) => {
25411
25622
  if (refusal) return refusal;
25412
25623
  const senderId = getAccountIdForSession(cacheKey) ?? "";
25413
25624
  if (!senderId) {
25414
- console.error(`${TAG37} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
25625
+ console.error(`${TAG39} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
25415
25626
  return c.json({ error: "admin-account-not-resolved" }, 500);
25416
25627
  }
25417
25628
  const userId = getUserIdForSession(cacheKey) ?? void 0;
@@ -25420,7 +25631,7 @@ app24.post("/", async (c) => {
25420
25631
  const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
25421
25632
  const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
25422
25633
  const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
25423
- console.log(`${TAG37} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
25634
+ console.log(`${TAG39} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
25424
25635
  const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
25425
25636
  const { response, claudeSessionId } = await performSpawnWithInitialMessage({
25426
25637
  senderId,
@@ -25439,13 +25650,13 @@ app24.post("/", async (c) => {
25439
25650
  claudeSessionId,
25440
25651
  senderId
25441
25652
  );
25442
- console.log(`${TAG37} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
25653
+ console.log(`${TAG39} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
25443
25654
  return response;
25444
25655
  });
25445
25656
  var claude_sessions_default = app24;
25446
25657
 
25447
25658
  // server/routes/admin/log-ingest.ts
25448
- var TAG38 = "[log-ingest]";
25659
+ var TAG40 = "[log-ingest]";
25449
25660
  var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
25450
25661
  var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
25451
25662
  var MAX_LINE_BYTES = 4096;
@@ -25456,7 +25667,7 @@ function isLoopbackAddr2(addr) {
25456
25667
  app25.post("/", async (c) => {
25457
25668
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
25458
25669
  if (!isLoopbackAddr2(remoteAddr)) {
25459
- console.error(`${TAG38} reject reason=non-loopback remoteAddr=${remoteAddr}`);
25670
+ console.error(`${TAG40} reject reason=non-loopback remoteAddr=${remoteAddr}`);
25460
25671
  return c.json({ error: "log-ingest-loopback-only" }, 403);
25461
25672
  }
25462
25673
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -25465,7 +25676,7 @@ app25.post("/", async (c) => {
25465
25676
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
25466
25677
  const offender = tokens.find((t) => !isLoopbackAddr2(t));
25467
25678
  if (offender !== void 0) {
25468
- console.error(`${TAG38} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
25679
+ console.error(`${TAG40} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
25469
25680
  return c.json({ error: "log-ingest-loopback-only" }, 403);
25470
25681
  }
25471
25682
  }
@@ -25500,7 +25711,7 @@ var log_ingest_default = app25;
25500
25711
 
25501
25712
  // server/routes/admin/session-blocked.ts
25502
25713
  import { join as join48 } from "path";
25503
- var TAG39 = "[blocked-tool-notify]";
25714
+ var TAG41 = "[blocked-tool-notify]";
25504
25715
  function isLoopbackAddr3(addr) {
25505
25716
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
25506
25717
  }
@@ -25518,18 +25729,18 @@ Answer the permission card in the session's PTY or in Claude Desktop. Some check
25518
25729
  async function sendWhatsApp(account, to, text) {
25519
25730
  const res = resolveSocket(account.accountId);
25520
25731
  if (!res.ok) {
25521
- console.error(`${TAG39} op=socket-unavailable accountId=${account.accountId} reason=${res.reason}`);
25732
+ console.error(`${TAG41} op=socket-unavailable accountId=${account.accountId} reason=${res.reason}`);
25522
25733
  return false;
25523
25734
  }
25524
25735
  const sent = await sendTextMessage(res.sock, to, text, { accountId: res.accountId });
25525
- if (!sent.success) console.error(`${TAG39} op=send-failed to=${to} error=${sent.error ?? "unknown"}`);
25736
+ if (!sent.success) console.error(`${TAG41} op=send-failed to=${to} error=${sent.error ?? "unknown"}`);
25526
25737
  return sent.success === true;
25527
25738
  }
25528
25739
  var app26 = new Hono();
25529
25740
  app26.post("/", async (c) => {
25530
25741
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
25531
25742
  if (!isLoopbackAddr3(remoteAddr)) {
25532
- console.error(`${TAG39} op=reject reason=non-loopback remoteAddr=${remoteAddr}`);
25743
+ console.error(`${TAG41} op=reject reason=non-loopback remoteAddr=${remoteAddr}`);
25533
25744
  return c.json({ error: "session-blocked-loopback-only" }, 403);
25534
25745
  }
25535
25746
  let body = {};
@@ -25550,13 +25761,13 @@ app26.post("/", async (c) => {
25550
25761
  const account = validateAccountId2(meta.accountId ?? "");
25551
25762
  if (!account) {
25552
25763
  console.error(
25553
- `${TAG39} op=account-unresolved sessionId=${sessionId.slice(0, 8)} sidecarAccountId=${meta.accountId ?? "none"}`
25764
+ `${TAG41} op=account-unresolved sessionId=${sessionId.slice(0, 8)} sidecarAccountId=${meta.accountId ?? "none"}`
25554
25765
  );
25555
25766
  return c.json({ delivered: false, channel: "none" }, 200);
25556
25767
  }
25557
25768
  if (meta.role === "admin" && meta.channel === "whatsapp" && meta.senderId) {
25558
25769
  const delivered2 = await sendWhatsApp(account, meta.senderId, text);
25559
- console.error(`${TAG39} op=notify sessionId=${sessionId.slice(0, 8)} channel=whatsapp delivered=${delivered2}`);
25770
+ console.error(`${TAG41} op=notify sessionId=${sessionId.slice(0, 8)} channel=whatsapp delivered=${delivered2}`);
25560
25771
  return c.json({ delivered: delivered2, channel: "whatsapp" }, 200);
25561
25772
  }
25562
25773
  if (meta.role === "admin" && meta.channel === "telegram" && meta.senderId) {
@@ -25566,11 +25777,11 @@ app26.post("/", async (c) => {
25566
25777
  const chatId = Number(parsed.senderId);
25567
25778
  if (resolved.kind === "one" && Number.isFinite(chatId)) {
25568
25779
  const res = await sendTelegramText(resolved.entry.token, chatId, text);
25569
- console.error(`${TAG39} op=notify sessionId=${sessionId.slice(0, 8)} channel=telegram delivered=${res.ok}`);
25780
+ console.error(`${TAG41} op=notify sessionId=${sessionId.slice(0, 8)} channel=telegram delivered=${res.ok}`);
25570
25781
  return c.json({ delivered: res.ok, channel: "telegram" }, 200);
25571
25782
  }
25572
25783
  console.error(
25573
- `${TAG39} op=telegram-unresolved sessionId=${sessionId.slice(0, 8)} botId=${parsed.botId} kind=${resolved.kind}`
25784
+ `${TAG41} op=telegram-unresolved sessionId=${sessionId.slice(0, 8)} botId=${parsed.botId} kind=${resolved.kind}`
25574
25785
  );
25575
25786
  }
25576
25787
  }
@@ -25580,7 +25791,7 @@ app26.post("/", async (c) => {
25580
25791
  if (await sendWhatsApp(account, phone, text)) delivered = true;
25581
25792
  }
25582
25793
  console.error(
25583
- `${TAG39} op=notify sessionId=${sessionId.slice(0, 8)} channel=house-admin phones=${phones.length} delivered=${delivered}`
25794
+ `${TAG41} op=notify sessionId=${sessionId.slice(0, 8)} channel=house-admin phones=${phones.length} delivered=${delivered}`
25584
25795
  );
25585
25796
  return c.json({ delivered, channel: "house-admin" }, 200);
25586
25797
  });
@@ -25613,18 +25824,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
25613
25824
  ]);
25614
25825
  var app27 = new Hono();
25615
25826
  app27.post("/", async (c) => {
25616
- const TAG75 = "[admin:events]";
25827
+ const TAG78 = "[admin:events]";
25617
25828
  let body;
25618
25829
  try {
25619
25830
  body = await c.req.json();
25620
25831
  } catch (err) {
25621
25832
  const detail = err instanceof Error ? err.message : String(err);
25622
- console.error(`${TAG75} reject reason=body-not-json detail=${detail}`);
25833
+ console.error(`${TAG78} reject reason=body-not-json detail=${detail}`);
25623
25834
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
25624
25835
  }
25625
25836
  const event = typeof body.event === "string" ? body.event : "";
25626
25837
  if (!ALLOWED_EVENTS.has(event)) {
25627
- console.error(`${TAG75} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
25838
+ console.error(`${TAG78} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
25628
25839
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
25629
25840
  }
25630
25841
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -26580,13 +26791,13 @@ function buildZip(entries2) {
26580
26791
  }
26581
26792
 
26582
26793
  // server/lib/account-root-groups.ts
26583
- var import_dist9 = __toESM(require_dist7(), 1);
26794
+ var import_dist10 = __toESM(require_dist8(), 1);
26584
26795
  function classifyAccountRoot(schemaMd, rootEntries) {
26585
- const regions = (0, import_dist9.parseSchemaRegions)(schemaMd);
26796
+ const regions = (0, import_dist10.parseSchemaRegions)(schemaMd);
26586
26797
  if (!regions.parsed) {
26587
26798
  return { home: [], system: [...rootEntries], unknown: [], parsed: false, reason: regions.reason };
26588
26799
  }
26589
- const homeOrder = [...regions.ontologyRoots, ...import_dist9.HOME_FIXED].filter((d, i, a) => a.indexOf(d) === i);
26800
+ const homeOrder = [...regions.ontologyRoots, ...import_dist10.HOME_FIXED].filter((d, i, a) => a.indexOf(d) === i);
26590
26801
  const homeSet = new Set(homeOrder);
26591
26802
  const present = new Set(rootEntries);
26592
26803
  const home = homeOrder.filter((d) => present.has(d));
@@ -27729,7 +27940,7 @@ app28.post("/rename", requireAdminSession, async (c) => {
27729
27940
  var files_default = app28;
27730
27941
 
27731
27942
  // ../lib/graph-search/src/index.ts
27732
- var import_dist10 = __toESM(require_dist8());
27943
+ var import_dist11 = __toESM(require_dist5());
27733
27944
  import { int } from "neo4j-driver";
27734
27945
 
27735
27946
  // ../lib/graph-search/src/rrf-fusion.ts
@@ -27979,7 +28190,7 @@ async function bm25Only(session, params) {
27979
28190
  ${scopeClause}
27980
28191
  ${agentClause}
27981
28192
  ${labelClause}
27982
- AND ${(0, import_dist10.notTrashed)("node")}
28193
+ AND ${(0, import_dist11.notTrashed)("node")}
27983
28194
  ${kwClause}
27984
28195
  RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
27985
28196
  ORDER BY score DESC
@@ -28141,7 +28352,7 @@ async function hybrid(session, embed2, params) {
28141
28352
  WHERE node.accountId = $accountId
28142
28353
  ${scopeClause}
28143
28354
  ${agentClause}
28144
- AND ${(0, import_dist10.notTrashed)("node")}
28355
+ AND ${(0, import_dist11.notTrashed)("node")}
28145
28356
  ${keywordClause}
28146
28357
  RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
28147
28358
  ORDER BY score DESC
@@ -28240,7 +28451,7 @@ async function hybrid(session, embed2, params) {
28240
28451
  const propResult = await session.run(
28241
28452
  `MATCH (node)
28242
28453
  WHERE node.accountId = $accountId
28243
- AND ${(0, import_dist10.notTrashed)("node")}
28454
+ AND ${(0, import_dist11.notTrashed)("node")}
28244
28455
  AND node.keywords IS NOT NULL
28245
28456
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
28246
28457
  ${propScope.clause}
@@ -28292,7 +28503,7 @@ async function hybrid(session, embed2, params) {
28292
28503
  const propResult = await session.run(
28293
28504
  `MATCH (node)
28294
28505
  WHERE node.accountId = $accountId
28295
- AND ${(0, import_dist10.notTrashed)("node")}
28506
+ AND ${(0, import_dist11.notTrashed)("node")}
28296
28507
  AND node.keywords IS NOT NULL
28297
28508
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
28298
28509
  ${propScope.clause}
@@ -28399,7 +28610,7 @@ async function hybrid(session, embed2, params) {
28399
28610
  `UNWIND $nodeIds AS nid
28400
28611
  MATCH (n)-[r]-(related)
28401
28612
  WHERE elementId(n) = nid
28402
- AND ${(0, import_dist10.notTrashed)("related")}
28613
+ AND ${(0, import_dist11.notTrashed)("related")}
28403
28614
  ${expandScopeClause}
28404
28615
  ${expandAgentClause}
28405
28616
  WITH nid, n, r, related
@@ -28627,8 +28838,8 @@ var graph_search_default = app29;
28627
28838
  import neo4j2 from "neo4j-driver";
28628
28839
 
28629
28840
  // app/lib/graph-labels.ts
28630
- var import_dist11 = __toESM(require_dist9(), 1);
28631
28841
  var import_dist12 = __toESM(require_dist9(), 1);
28842
+ var import_dist13 = __toESM(require_dist9(), 1);
28632
28843
  var HIDDEN_BY_DEFAULT_LABELS = Object.freeze(
28633
28844
  /* @__PURE__ */ new Set(["Chunk", "GraphPreference"])
28634
28845
  );
@@ -28712,14 +28923,14 @@ var EXCLUDED_EDGE_TYPES = Object.freeze(
28712
28923
  ])
28713
28924
  );
28714
28925
  function isKnownLabel(label) {
28715
- return Object.prototype.hasOwnProperty.call(import_dist12.GRAPH_LABEL_COLOURS, label);
28926
+ return Object.prototype.hasOwnProperty.call(import_dist13.GRAPH_LABEL_COLOURS, label);
28716
28927
  }
28717
28928
  function isHiddenByDefault(label) {
28718
28929
  return HIDDEN_BY_DEFAULT_LABELS.has(label);
28719
28930
  }
28720
28931
 
28721
28932
  // server/lib/top-level-labels.ts
28722
- var import_dist13 = __toESM(require_dist9(), 1);
28933
+ var import_dist14 = __toESM(require_dist9(), 1);
28723
28934
  import { readdirSync as readdirSync26, readFileSync as readFileSync41 } from "fs";
28724
28935
  import { join as join51, resolve as resolve33 } from "path";
28725
28936
  var STATIC_TOP_LEVEL_LABELS = Object.freeze(
@@ -28856,7 +29067,7 @@ function getTopLevelLabelAllowlist(opts = {}) {
28856
29067
  const referencesDir = opts.referencesDir ?? resolveReferencesDir();
28857
29068
  const { labels: derived, contributingFiles } = parseTableTopLevelLabels(referencesDir);
28858
29069
  const excluded = [];
28859
- for (const label of import_dist13.ADDITIONAL_BASE_LABELS) {
29070
+ for (const label of import_dist14.ADDITIONAL_BASE_LABELS) {
28860
29071
  if (derived.delete(label)) excluded.push(label);
28861
29072
  }
28862
29073
  const derivedCount = derived.size;
@@ -31189,7 +31400,7 @@ function managerLogFollowUrl(sessionId, opts) {
31189
31400
  }
31190
31401
 
31191
31402
  // server/routes/admin/linkedin-ingest.ts
31192
- var TAG40 = "[linkedin-ingest-route]";
31403
+ var TAG42 = "[linkedin-ingest-route]";
31193
31404
  var UUID4 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
31194
31405
  var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
31195
31406
  var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
@@ -31314,28 +31525,28 @@ app45.post("/", requireIngestToken, async (c) => {
31314
31525
  try {
31315
31526
  body = await c.req.json();
31316
31527
  } catch {
31317
- console.error(TAG40 + " rejected status=400 reason=schema:body-not-json");
31528
+ console.error(TAG42 + " rejected status=400 reason=schema:body-not-json");
31318
31529
  return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
31319
31530
  }
31320
31531
  const v = validate2(body);
31321
31532
  if (!v.ok) {
31322
- console.error(TAG40 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
31533
+ console.error(TAG42 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
31323
31534
  return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
31324
31535
  }
31325
31536
  const envelope = v.envelope;
31326
31537
  const senderId = c.var.ingestAccountId ?? "";
31327
31538
  if (!senderId) {
31328
- console.error(TAG40 + " rejected status=500 reason=admin-account-not-resolved");
31539
+ console.error(TAG42 + " rejected status=500 reason=admin-account-not-resolved");
31329
31540
  return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
31330
31541
  }
31331
31542
  const payloadBytes = JSON.stringify(envelope).length;
31332
31543
  console.log(
31333
- TAG40 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
31544
+ TAG42 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
31334
31545
  );
31335
31546
  const initialMessage = buildInitialMessage(envelope);
31336
31547
  const spawnStart = Date.now();
31337
31548
  const sessionId = randomUUID9();
31338
- console.log(TAG40 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
31549
+ console.log(TAG42 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
31339
31550
  const spawned = await managerRcSpawn({
31340
31551
  sessionId,
31341
31552
  initialMessage,
@@ -31348,12 +31559,12 @@ app45.post("/", requireIngestToken, async (c) => {
31348
31559
  });
31349
31560
  if ("error" in spawned) {
31350
31561
  console.error(
31351
- TAG40 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
31562
+ TAG42 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
31352
31563
  );
31353
31564
  return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
31354
31565
  }
31355
31566
  console.log(
31356
- TAG40 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
31567
+ TAG42 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
31357
31568
  );
31358
31569
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
31359
31570
  });
@@ -31361,7 +31572,7 @@ var linkedin_ingest_default = app45;
31361
31572
 
31362
31573
  // server/routes/admin/post-turn-context.ts
31363
31574
  import neo4j3 from "neo4j-driver";
31364
- var TAG41 = "[post-turn-context]";
31575
+ var TAG43 = "[post-turn-context]";
31365
31576
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
31366
31577
  "embedding",
31367
31578
  "passwordHash",
@@ -31403,7 +31614,7 @@ var app46 = new Hono();
31403
31614
  app46.get("/", async (c) => {
31404
31615
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
31405
31616
  if (!isLoopbackAddr4(remoteAddr)) {
31406
- console.error(`${TAG41} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31617
+ console.error(`${TAG43} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31407
31618
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
31408
31619
  }
31409
31620
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -31412,7 +31623,7 @@ app46.get("/", async (c) => {
31412
31623
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
31413
31624
  const offender = tokens.find((t) => !isLoopbackAddr4(t));
31414
31625
  if (offender !== void 0) {
31415
- console.error(`${TAG41} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31626
+ console.error(`${TAG43} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31416
31627
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
31417
31628
  }
31418
31629
  }
@@ -31444,7 +31655,7 @@ app46.get("/", async (c) => {
31444
31655
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
31445
31656
  const total = Date.now() - started2;
31446
31657
  console.log(
31447
- `${TAG41} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
31658
+ `${TAG43} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
31448
31659
  );
31449
31660
  return c.json({
31450
31661
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -31453,7 +31664,7 @@ app46.get("/", async (c) => {
31453
31664
  const elapsed = Date.now() - started2;
31454
31665
  const message = err instanceof Error ? err.message : String(err);
31455
31666
  console.error(
31456
- `${TAG41} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
31667
+ `${TAG43} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
31457
31668
  );
31458
31669
  return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
31459
31670
  } finally {
@@ -31566,7 +31777,7 @@ function formatPreviousContext(writes) {
31566
31777
  }
31567
31778
 
31568
31779
  // server/routes/admin/public-session-context.ts
31569
- var TAG42 = "[public-session-context]";
31780
+ var TAG44 = "[public-session-context]";
31570
31781
  function isLoopbackAddr5(addr) {
31571
31782
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
31572
31783
  }
@@ -31574,7 +31785,7 @@ var app47 = new Hono();
31574
31785
  app47.get("/", async (c) => {
31575
31786
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
31576
31787
  if (!isLoopbackAddr5(remoteAddr)) {
31577
- console.error(`${TAG42} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31788
+ console.error(`${TAG44} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31578
31789
  return c.json({ error: "public-session-context-loopback-only" }, 403);
31579
31790
  }
31580
31791
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -31583,7 +31794,7 @@ app47.get("/", async (c) => {
31583
31794
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
31584
31795
  const offender = tokens.find((t) => !isLoopbackAddr5(t));
31585
31796
  if (offender !== void 0) {
31586
- console.error(`${TAG42} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31797
+ console.error(`${TAG44} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31587
31798
  return c.json({ error: "public-session-context-loopback-only" }, 403);
31588
31799
  }
31589
31800
  }
@@ -31597,14 +31808,14 @@ app47.get("/", async (c) => {
31597
31808
  const writes = await fetchSliceWrites(session, sliceToken, accountId);
31598
31809
  const total = Date.now() - started2;
31599
31810
  console.log(
31600
- `${TAG42} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
31811
+ `${TAG44} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
31601
31812
  );
31602
31813
  return c.json({ writes });
31603
31814
  } catch (err) {
31604
31815
  const elapsed = Date.now() - started2;
31605
31816
  const message = err instanceof Error ? err.message : String(err);
31606
31817
  console.error(
31607
- `${TAG42} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
31818
+ `${TAG44} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
31608
31819
  );
31609
31820
  return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
31610
31821
  } finally {
@@ -31623,7 +31834,7 @@ function getWebchatGateway() {
31623
31834
  }
31624
31835
 
31625
31836
  // server/routes/admin/public-session-exit.ts
31626
- var TAG43 = "[public-session-exit-route]";
31837
+ var TAG45 = "[public-session-exit-route]";
31627
31838
  function isLoopbackAddr6(addr) {
31628
31839
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
31629
31840
  }
@@ -31631,7 +31842,7 @@ var app48 = new Hono();
31631
31842
  app48.post("/", async (c) => {
31632
31843
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
31633
31844
  if (!isLoopbackAddr6(remoteAddr)) {
31634
- console.error(`${TAG43} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31845
+ console.error(`${TAG45} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31635
31846
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
31636
31847
  }
31637
31848
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -31640,7 +31851,7 @@ app48.post("/", async (c) => {
31640
31851
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
31641
31852
  const offender = tokens.find((t) => !isLoopbackAddr6(t));
31642
31853
  if (offender !== void 0) {
31643
- console.error(`${TAG43} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31854
+ console.error(`${TAG45} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31644
31855
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
31645
31856
  }
31646
31857
  }
@@ -31654,7 +31865,7 @@ app48.post("/", async (c) => {
31654
31865
  if (!sessionId) return c.json({ error: "sessionId required" }, 400);
31655
31866
  const gateway = getWebchatGateway();
31656
31867
  if (!gateway) {
31657
- console.error(`${TAG43} reject reason=gateway-unset sessionId=${sessionId.slice(0, 8)}`);
31868
+ console.error(`${TAG45} reject reason=gateway-unset sessionId=${sessionId.slice(0, 8)}`);
31658
31869
  return c.json({ error: "webchat-gateway-unset" }, 503);
31659
31870
  }
31660
31871
  gateway.handlePublicSessionExit(sessionId);
@@ -31663,7 +31874,7 @@ app48.post("/", async (c) => {
31663
31874
  var public_session_exit_default = app48;
31664
31875
 
31665
31876
  // server/routes/admin/access-session-evict.ts
31666
- var TAG44 = "[access-session-evict]";
31877
+ var TAG46 = "[access-session-evict]";
31667
31878
  function isLoopbackAddr7(addr) {
31668
31879
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
31669
31880
  }
@@ -31671,7 +31882,7 @@ var app49 = new Hono();
31671
31882
  app49.post("/", async (c) => {
31672
31883
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
31673
31884
  if (!isLoopbackAddr7(remoteAddr)) {
31674
- console.error(`${TAG44} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31885
+ console.error(`${TAG46} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31675
31886
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
31676
31887
  }
31677
31888
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -31680,7 +31891,7 @@ app49.post("/", async (c) => {
31680
31891
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
31681
31892
  const offender = tokens.find((t) => !isLoopbackAddr7(t));
31682
31893
  if (offender !== void 0) {
31683
- console.error(`${TAG44} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31894
+ console.error(`${TAG46} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31684
31895
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
31685
31896
  }
31686
31897
  }
@@ -31693,13 +31904,13 @@ app49.post("/", async (c) => {
31693
31904
  const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
31694
31905
  if (!grantId) return c.json({ error: "grantId required" }, 400);
31695
31906
  const dropped = evictAccessSessionsByGrant(grantId);
31696
- console.log(`${TAG44} grantId=${grantId} dropped=${dropped}`);
31907
+ console.log(`${TAG46} grantId=${grantId} dropped=${dropped}`);
31697
31908
  return c.json({ ok: true, dropped });
31698
31909
  });
31699
31910
  var access_session_evict_default = app49;
31700
31911
 
31701
31912
  // server/routes/admin/enrol-person.ts
31702
- var TAG45 = "[enrol]";
31913
+ var TAG47 = "[enrol]";
31703
31914
  function isLoopbackAddr8(addr) {
31704
31915
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
31705
31916
  }
@@ -31708,7 +31919,7 @@ var app50 = new Hono();
31708
31919
  app50.post("/", async (c) => {
31709
31920
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
31710
31921
  if (!isLoopbackAddr8(remoteAddr)) {
31711
- console.error(`${TAG45} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31922
+ console.error(`${TAG47} reject reason=non-loopback remoteAddr=${remoteAddr}`);
31712
31923
  return c.json({ error: "enrol-person-loopback-only" }, 403);
31713
31924
  }
31714
31925
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -31717,7 +31928,7 @@ app50.post("/", async (c) => {
31717
31928
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
31718
31929
  const offender = tokens.find((t) => !isLoopbackAddr8(t));
31719
31930
  if (offender !== void 0) {
31720
- console.error(`${TAG45} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31931
+ console.error(`${TAG47} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
31721
31932
  return c.json({ error: "enrol-person-loopback-only" }, 403);
31722
31933
  }
31723
31934
  }
@@ -31742,7 +31953,7 @@ app50.post("/", async (c) => {
31742
31953
  }
31743
31954
  const accountId = process.env.ACCOUNT_ID ?? "";
31744
31955
  if (!accountId) {
31745
- console.error(`${TAG45} op=person result=no-account-id`);
31956
+ console.error(`${TAG47} op=person result=no-account-id`);
31746
31957
  return c.json({ error: "no-account-id" }, 500);
31747
31958
  }
31748
31959
  let personId;
@@ -31750,7 +31961,7 @@ app50.post("/", async (c) => {
31750
31961
  personId = await enrolPerson({ accountId, phone, email, agentSlug });
31751
31962
  } catch (err) {
31752
31963
  console.error(
31753
- `${TAG45} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
31964
+ `${TAG47} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
31754
31965
  );
31755
31966
  return c.json({ error: "enrol-failed" }, 500);
31756
31967
  }
@@ -31760,11 +31971,11 @@ app50.post("/", async (c) => {
31760
31971
  if (g) grant = { grantId: g.grantId, status: g.status, contactValue: g.contactValue };
31761
31972
  } catch (err) {
31762
31973
  console.error(
31763
- `${TAG45} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
31974
+ `${TAG47} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
31764
31975
  );
31765
31976
  }
31766
31977
  console.log(
31767
- `${TAG45} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
31978
+ `${TAG47} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
31768
31979
  );
31769
31980
  return c.json({ personId, grant }, 200);
31770
31981
  });
@@ -33832,7 +34043,7 @@ function toNum(v) {
33832
34043
  }
33833
34044
 
33834
34045
  // server/routes/admin/tasks-list.ts
33835
- var TAG46 = "[task-timer]";
34046
+ var TAG48 = "[task-timer]";
33836
34047
  var COMPLETED = /* @__PURE__ */ new Set(["completed"]);
33837
34048
  var HIDDEN_FROM_OPEN = /* @__PURE__ */ new Set(["completed", "cancelled"]);
33838
34049
  var app57 = new Hono();
@@ -33840,12 +34051,12 @@ app57.get("/", requireAdminSession, async (c) => {
33840
34051
  const cacheKey = c.var.cacheKey;
33841
34052
  const accountId = getAccountIdForSession(cacheKey);
33842
34053
  if (!accountId) {
33843
- console.error(`${TAG46} op=list auth-rejected reason="no account for session"`);
34054
+ console.error(`${TAG48} op=list auth-rejected reason="no account for session"`);
33844
34055
  return c.json({ error: "Account not found for session" }, 401);
33845
34056
  }
33846
34057
  const cacheStart = Date.now();
33847
34058
  const cached3 = await getOrCompute("tasks-list", accountId, () => computeTaskList(accountId));
33848
- console.log(`${TAG46} op=list-cache cache=${cached3.state} account=${accountId} ms=${Date.now() - cacheStart}`);
34059
+ console.log(`${TAG48} op=list-cache cache=${cached3.state} account=${accountId} ms=${Date.now() - cacheStart}`);
33849
34060
  if (cached3.value === null) {
33850
34061
  return c.json({ error: "tasks-list-failed" }, 500);
33851
34062
  }
@@ -33946,13 +34157,13 @@ async function computeTaskList(accountId) {
33946
34157
  orphans = orphanResult.records.length > 0 ? toNum(orphanResult.records[0].get("orphans")) : 0;
33947
34158
  } catch (err) {
33948
34159
  console.error(
33949
- `${TAG46} op=session-orphans-census-failed account=${accountId} error="${err instanceof Error ? err.message : String(err)}"`
34160
+ `${TAG48} op=session-orphans-census-failed account=${accountId} error="${err instanceof Error ? err.message : String(err)}"`
33950
34161
  );
33951
34162
  orphans = -1;
33952
34163
  }
33953
34164
  if (orphans > 0) {
33954
34165
  console.error(
33955
- `${TAG46} op=session-orphans account=${accountId} orphans=${orphans} detail="task marked for a session spawn that never linked; the spawned session is unreachable from its row"`
34166
+ `${TAG48} op=session-orphans account=${accountId} orphans=${orphans} detail="task marked for a session spawn that never linked; the spawned session is unreachable from its row"`
33956
34167
  );
33957
34168
  }
33958
34169
  let orphanEntries = 0;
@@ -33971,14 +34182,14 @@ async function computeTaskList(accountId) {
33971
34182
  orphanAdjustments = orphanTime.records.length > 0 ? toNum(orphanTime.records[0].get("orphanAdjustments")) : 0;
33972
34183
  } catch (err) {
33973
34184
  console.error(
33974
- `${TAG46} op=orphan-time-records-census-failed account=${accountId} error="${err instanceof Error ? err.message : String(err)}"`
34185
+ `${TAG48} op=orphan-time-records-census-failed account=${accountId} error="${err instanceof Error ? err.message : String(err)}"`
33975
34186
  );
33976
34187
  orphanEntries = -1;
33977
34188
  orphanAdjustments = -1;
33978
34189
  }
33979
34190
  if (orphanEntries > 0 || orphanAdjustments > 0) {
33980
34191
  console.error(
33981
- `${TAG46} op=orphan-time-records account=${accountId} orphanEntries=${orphanEntries} orphanAdjustments=${orphanAdjustments} detail="time records whose task no longer exists; something removed the task without its records"`
34192
+ `${TAG48} op=orphan-time-records account=${accountId} orphanEntries=${orphanEntries} orphanAdjustments=${orphanAdjustments} detail="time records whose task no longer exists; something removed the task without its records"`
33982
34193
  );
33983
34194
  }
33984
34195
  const linkedIds = [...open3, ...completed].map((r) => r.sessionId).filter((s) => s !== null);
@@ -33991,7 +34202,7 @@ async function computeTaskList(accountId) {
33991
34202
  const walk = enumerateJsonlsWithSkips(join60(cfg, "projects"));
33992
34203
  if (walk.skipped > 0) {
33993
34204
  console.error(
33994
- `${TAG46} op=session-dangling-census-incomplete account=${accountId} skipped=${walk.skipped} detail="a slug the walk could not read contributes no session ids, so its links cannot be judged"`
34205
+ `${TAG48} op=session-dangling-census-incomplete account=${accountId} skipped=${walk.skipped} detail="a slug the walk could not read contributes no session ids, so its links cannot be judged"`
33995
34206
  );
33996
34207
  dangling = null;
33997
34208
  } else {
@@ -34000,28 +34211,28 @@ async function computeTaskList(accountId) {
34000
34211
  }
34001
34212
  } catch (err) {
34002
34213
  console.error(
34003
- `${TAG46} op=session-dangling-census-failed account=${accountId} error="${err instanceof Error ? err.message : String(err)}"`
34214
+ `${TAG48} op=session-dangling-census-failed account=${accountId} error="${err instanceof Error ? err.message : String(err)}"`
34004
34215
  );
34005
34216
  dangling = null;
34006
34217
  }
34007
34218
  }
34008
34219
  if (dangling !== null && dangling > 0) {
34009
34220
  console.error(
34010
- `${TAG46} op=session-dangling account=${accountId} dangling=${dangling} linked=${linkedIds.length} detail="a task links a session with no JSONL on disk; clicking it will not resume"`
34221
+ `${TAG48} op=session-dangling account=${accountId} dangling=${dangling} linked=${linkedIds.length} detail="a task links a session with no JSONL on disk; clicking it will not resume"`
34011
34222
  );
34012
34223
  }
34013
34224
  const returned = result.records.length;
34014
34225
  if (returned < graphTotal) {
34015
34226
  console.error(
34016
- `${TAG46} op=list row-count-shortfall account=${accountId} returned=${returned} graphTotal=${graphTotal} missing=${graphTotal - returned}`
34227
+ `${TAG48} op=list row-count-shortfall account=${accountId} returned=${returned} graphTotal=${graphTotal} missing=${graphTotal - returned}`
34017
34228
  );
34018
34229
  }
34019
34230
  console.log(
34020
- `${TAG46} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${open3.length} completed=${completed.length} missingTaskId=${missingTaskId} missingCreatedAt=${missingCreatedAt} assigned=${assigned} sessionLinked=${sessionLinked} sessionOrphans=${orphans < 0 ? "n/a" : orphans} sessionDangling=${dangling === null ? "n/a" : dangling}`
34231
+ `${TAG48} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${open3.length} completed=${completed.length} missingTaskId=${missingTaskId} missingCreatedAt=${missingCreatedAt} assigned=${assigned} sessionLinked=${sessionLinked} sessionOrphans=${orphans < 0 ? "n/a" : orphans} sessionDangling=${dangling === null ? "n/a" : dangling}`
34021
34232
  );
34022
34233
  return { open: open3, completed };
34023
34234
  } catch (err) {
34024
- console.error(`${TAG46} op=list error="${err instanceof Error ? err.message : String(err)}"`);
34235
+ console.error(`${TAG48} op=list error="${err instanceof Error ? err.message : String(err)}"`);
34025
34236
  return null;
34026
34237
  } finally {
34027
34238
  await session.close();
@@ -34031,7 +34242,7 @@ var tasks_list_default = app57;
34031
34242
 
34032
34243
  // server/routes/admin/task-timer-start.ts
34033
34244
  import { randomUUID as randomUUID11 } from "crypto";
34034
- var TAG47 = "[task-timer]";
34245
+ var TAG49 = "[task-timer]";
34035
34246
  var app58 = new Hono();
34036
34247
  app58.post("/", requireAdminSession, async (c) => {
34037
34248
  const cacheKey = c.var.cacheKey;
@@ -34046,7 +34257,7 @@ app58.post("/", requireAdminSession, async (c) => {
34046
34257
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
34047
34258
  if (!taskId) {
34048
34259
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
34049
- console.error(`${TAG47} op=timer-start rejected reason=${reason} account=${accountId}`);
34260
+ console.error(`${TAG49} op=timer-start rejected reason=${reason} account=${accountId}`);
34050
34261
  return c.json({ error: "taskId required" }, 400);
34051
34262
  }
34052
34263
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -34057,7 +34268,7 @@ app58.post("/", requireAdminSession, async (c) => {
34057
34268
  { taskId, accountId }
34058
34269
  );
34059
34270
  if (target.records.length === 0) {
34060
- console.error(`${TAG47} op=start taskId=${taskId} accountId=${accountId} result=task-not-found`);
34271
+ console.error(`${TAG49} op=start taskId=${taskId} accountId=${accountId} result=task-not-found`);
34061
34272
  return c.json({ error: "task-not-found" }, 404);
34062
34273
  }
34063
34274
  const open3 = await session.run(
@@ -34069,7 +34280,7 @@ app58.post("/", requireAdminSession, async (c) => {
34069
34280
  const priorEntryId = open3.records[0]?.get("entryId");
34070
34281
  const priorStartedAt = open3.records[0]?.get("startedAt");
34071
34282
  if (priorEntryId) {
34072
- console.log(`${TAG47} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
34283
+ console.log(`${TAG49} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
34073
34284
  invalidate("tasks-list", accountId);
34074
34285
  return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
34075
34286
  }
@@ -34080,11 +34291,11 @@ app58.post("/", requireAdminSession, async (c) => {
34080
34291
  CREATE (te)-[:LOGGED_AGAINST]->(t)`,
34081
34292
  { taskId, accountId, entryId, now }
34082
34293
  );
34083
- console.log(`${TAG47} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
34294
+ console.log(`${TAG49} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
34084
34295
  invalidate("tasks-list", accountId);
34085
34296
  return c.json({ ok: true, running: true, entryId, startedAt: now });
34086
34297
  } catch (err) {
34087
- console.error(`${TAG47} op=start error="${err instanceof Error ? err.message : String(err)}"`);
34298
+ console.error(`${TAG49} op=start error="${err instanceof Error ? err.message : String(err)}"`);
34088
34299
  return c.json({ error: "task-timer-start-failed" }, 500);
34089
34300
  } finally {
34090
34301
  await session.close();
@@ -34094,7 +34305,7 @@ var task_timer_start_default = app58;
34094
34305
 
34095
34306
  // server/routes/admin/task-timer-stop.ts
34096
34307
  import neo4j5 from "neo4j-driver";
34097
- var TAG48 = "[task-timer]";
34308
+ var TAG50 = "[task-timer]";
34098
34309
  var app59 = new Hono();
34099
34310
  app59.post("/", requireAdminSession, async (c) => {
34100
34311
  const cacheKey = c.var.cacheKey;
@@ -34109,7 +34320,7 @@ app59.post("/", requireAdminSession, async (c) => {
34109
34320
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
34110
34321
  if (!taskId) {
34111
34322
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
34112
- console.error(`${TAG48} op=timer-stop rejected reason=${reason} account=${accountId}`);
34323
+ console.error(`${TAG50} op=timer-stop rejected reason=${reason} account=${accountId}`);
34113
34324
  return c.json({ error: "taskId required" }, 400);
34114
34325
  }
34115
34326
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -34130,7 +34341,7 @@ app59.post("/", requireAdminSession, async (c) => {
34130
34341
  { taskId, accountId }
34131
34342
  );
34132
34343
  const secondsLogged2 = toNum(cur.records[0]?.get("secondsLogged"));
34133
- console.log(`${TAG48} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
34344
+ console.log(`${TAG50} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
34134
34345
  invalidate("tasks-list", accountId);
34135
34346
  return c.json({ ok: true, running: false, seconds: 0, secondsLogged: secondsLogged2 });
34136
34347
  }
@@ -34154,12 +34365,12 @@ app59.post("/", requireAdminSession, async (c) => {
34154
34365
  const sumEntries = toNum(verify.records[0]?.get("sumEntries"));
34155
34366
  const sumAdjustments = toNum(verify.records[0]?.get("sumAdjustments"));
34156
34367
  const secondsLogged = toNum(verify.records[0]?.get("secondsLogged"));
34157
- console.log(`${TAG48} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
34158
- console.log(`${TAG48} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} sumAdjustments=${sumAdjustments} secondsLogged=${secondsLogged}`);
34368
+ console.log(`${TAG50} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
34369
+ console.log(`${TAG50} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} sumAdjustments=${sumAdjustments} secondsLogged=${secondsLogged}`);
34159
34370
  invalidate("tasks-list", accountId);
34160
34371
  return c.json({ ok: true, running: false, seconds, secondsLogged });
34161
34372
  } catch (err) {
34162
- console.error(`${TAG48} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
34373
+ console.error(`${TAG50} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
34163
34374
  return c.json({ error: "task-timer-stop-failed" }, 500);
34164
34375
  } finally {
34165
34376
  await session.close();
@@ -34168,7 +34379,7 @@ app59.post("/", requireAdminSession, async (c) => {
34168
34379
  var task_timer_stop_default = app59;
34169
34380
 
34170
34381
  // server/routes/admin/task-complete.ts
34171
- var TAG49 = "[task-timer]";
34382
+ var TAG51 = "[task-timer]";
34172
34383
  var app60 = new Hono();
34173
34384
  app60.post("/", requireAdminSession, async (c) => {
34174
34385
  const cacheKey = c.var.cacheKey;
@@ -34183,7 +34394,7 @@ app60.post("/", requireAdminSession, async (c) => {
34183
34394
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
34184
34395
  if (!taskId) {
34185
34396
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
34186
- console.error(`${TAG49} op=complete rejected reason=${reason} account=${accountId}`);
34397
+ console.error(`${TAG51} op=complete rejected reason=${reason} account=${accountId}`);
34187
34398
  return c.json({ error: "taskId required" }, 400);
34188
34399
  }
34189
34400
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -34199,11 +34410,11 @@ app60.post("/", requireAdminSession, async (c) => {
34199
34410
  return c.json({ error: "task-not-found" }, 404);
34200
34411
  }
34201
34412
  const secondsLogged = toNum(result.records[0].get("secondsLogged"));
34202
- console.log(`${TAG49} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
34413
+ console.log(`${TAG51} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
34203
34414
  invalidate("tasks-list", accountId);
34204
34415
  return c.json({ ok: true, status: "completed", secondsLogged });
34205
34416
  } catch (err) {
34206
- console.error(`${TAG49} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
34417
+ console.error(`${TAG51} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
34207
34418
  return c.json({ error: "task-complete-failed" }, 500);
34208
34419
  } finally {
34209
34420
  await session.close();
@@ -34214,7 +34425,7 @@ var task_complete_default = app60;
34214
34425
  // server/routes/admin/task-time-adjust.ts
34215
34426
  import { randomUUID as randomUUID12 } from "crypto";
34216
34427
  import neo4j6 from "neo4j-driver";
34217
- var TAG50 = "[task-time-adjust]";
34428
+ var TAG52 = "[task-time-adjust]";
34218
34429
  var app61 = new Hono();
34219
34430
  app61.post("/", requireAdminSession, async (c) => {
34220
34431
  const cacheKey = c.var.cacheKey;
@@ -34224,23 +34435,23 @@ app61.post("/", requireAdminSession, async (c) => {
34224
34435
  try {
34225
34436
  body = await c.req.json();
34226
34437
  } catch {
34227
- console.error(`${TAG50} op=reject reason=bad-json`);
34438
+ console.error(`${TAG52} op=reject reason=bad-json`);
34228
34439
  return c.json({ error: "invalid-json" }, 400);
34229
34440
  }
34230
34441
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
34231
34442
  if (!taskId) {
34232
34443
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
34233
- console.error(`${TAG50} op=time-adjust rejected reason=${reason} account=${accountId}`);
34444
+ console.error(`${TAG52} op=time-adjust rejected reason=${reason} account=${accountId}`);
34234
34445
  return c.json({ error: "taskId required" }, 400);
34235
34446
  }
34236
34447
  const newSeconds = body.newSeconds;
34237
34448
  if (typeof newSeconds !== "number" || !Number.isInteger(newSeconds) || newSeconds < 0) {
34238
- console.error(`${TAG50} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
34449
+ console.error(`${TAG52} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
34239
34450
  return c.json({ error: "newSeconds must be a non-negative integer" }, 400);
34240
34451
  }
34241
34452
  const adjustmentId = randomUUID12();
34242
34453
  const now = (/* @__PURE__ */ new Date()).toISOString();
34243
- console.log(`${TAG50} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
34454
+ console.log(`${TAG52} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
34244
34455
  const session = getSession();
34245
34456
  try {
34246
34457
  const guard = await session.run(
@@ -34250,12 +34461,12 @@ app61.post("/", requireAdminSession, async (c) => {
34250
34461
  { taskId, accountId }
34251
34462
  );
34252
34463
  if (guard.records.length === 0) {
34253
- console.error(`${TAG50} op=reject reason=no-task adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
34464
+ console.error(`${TAG52} op=reject reason=no-task adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
34254
34465
  return c.json({ error: "task-not-found" }, 404);
34255
34466
  }
34256
34467
  const openCount = toNum(guard.records[0].get("openCount"));
34257
34468
  if (openCount > 0) {
34258
- console.error(`${TAG50} op=reject reason=running adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
34469
+ console.error(`${TAG52} op=reject reason=running adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
34259
34470
  return c.json({ error: "task-timer-running" }, 409);
34260
34471
  }
34261
34472
  const previousSeconds = toNum(guard.records[0].get("previousSeconds"));
@@ -34281,11 +34492,11 @@ app61.post("/", requireAdminSession, async (c) => {
34281
34492
  }
34282
34493
  );
34283
34494
  const secondsLoggedAfter = toNum(write.records[0]?.get("secondsLoggedAfter"));
34284
- console.log(`${TAG50} op=persisted adjustmentId=${adjustmentId} previousSeconds=${previousSeconds} newSeconds=${newSeconds} delta=${delta} secondsLoggedAfter=${secondsLoggedAfter}`);
34495
+ console.log(`${TAG52} op=persisted adjustmentId=${adjustmentId} previousSeconds=${previousSeconds} newSeconds=${newSeconds} delta=${delta} secondsLoggedAfter=${secondsLoggedAfter}`);
34285
34496
  invalidate("tasks-list", accountId);
34286
34497
  return c.json({ ok: true, secondsLogged: secondsLoggedAfter });
34287
34498
  } catch (err) {
34288
- console.error(`${TAG50} op=error adjustmentId=${adjustmentId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
34499
+ console.error(`${TAG52} op=error adjustmentId=${adjustmentId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
34289
34500
  return c.json({ error: "task-time-adjust-failed" }, 500);
34290
34501
  } finally {
34291
34502
  await session.close();
@@ -34295,7 +34506,7 @@ var task_time_adjust_default = app61;
34295
34506
 
34296
34507
  // server/routes/admin/task-delete.ts
34297
34508
  import { randomUUID as randomUUID13 } from "crypto";
34298
- var TAG51 = "[task-delete]";
34509
+ var TAG53 = "[task-delete]";
34299
34510
  var app62 = new Hono();
34300
34511
  app62.post("/", requireAdminSession, async (c) => {
34301
34512
  const cacheKey = c.var.cacheKey;
@@ -34305,18 +34516,18 @@ app62.post("/", requireAdminSession, async (c) => {
34305
34516
  try {
34306
34517
  body = await c.req.json();
34307
34518
  } catch {
34308
- console.error(`${TAG51} op=reject reason=bad-json`);
34519
+ console.error(`${TAG53} op=reject reason=bad-json`);
34309
34520
  return c.json({ error: "invalid-json" }, 400);
34310
34521
  }
34311
34522
  const deletionId = randomUUID13();
34312
34523
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
34313
34524
  if (!taskId) {
34314
34525
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
34315
- console.error(`${TAG51} op=reject reason=${reason} deletionId=${deletionId} accountId=${accountId}`);
34526
+ console.error(`${TAG53} op=reject reason=${reason} deletionId=${deletionId} accountId=${accountId}`);
34316
34527
  return c.json({ error: "taskId required" }, 400);
34317
34528
  }
34318
34529
  console.log(
34319
- `${TAG51} op=request deletionId=${deletionId} taskId=${taskId} accountId=${accountId} confirmed=${body.confirmed === true}`
34530
+ `${TAG53} op=request deletionId=${deletionId} taskId=${taskId} accountId=${accountId} confirmed=${body.confirmed === true}`
34320
34531
  );
34321
34532
  const session = getSession();
34322
34533
  try {
@@ -34332,18 +34543,18 @@ app62.post("/", requireAdminSession, async (c) => {
34332
34543
  { taskId, accountId }
34333
34544
  );
34334
34545
  if (guard.records.length === 0) {
34335
- console.error(`${TAG51} op=reject reason=no-task deletionId=${deletionId} taskId=${taskId} accountId=${accountId}`);
34546
+ console.error(`${TAG53} op=reject reason=no-task deletionId=${deletionId} taskId=${taskId} accountId=${accountId}`);
34336
34547
  return c.json({ error: "task-not-found" }, 404);
34337
34548
  }
34338
34549
  const timeEntries = toNum(guard.records[0].get("timeEntries"));
34339
34550
  const openCount = toNum(guard.records[0].get("openCount"));
34340
34551
  const adjustments = toNum(guard.records[0].get("adjustments"));
34341
34552
  console.log(
34342
- `${TAG51} op=guard deletionId=${deletionId} taskId=${taskId} openCount=${openCount} timeEntries=${timeEntries} adjustments=${adjustments}`
34553
+ `${TAG53} op=guard deletionId=${deletionId} taskId=${taskId} openCount=${openCount} timeEntries=${timeEntries} adjustments=${adjustments}`
34343
34554
  );
34344
34555
  if (openCount > 0) {
34345
34556
  console.error(
34346
- `${TAG51} op=reject reason=running deletionId=${deletionId} taskId=${taskId} openCount=${openCount}`
34557
+ `${TAG53} op=reject reason=running deletionId=${deletionId} taskId=${taskId} openCount=${openCount}`
34347
34558
  );
34348
34559
  return c.json({ error: "task-timer-running" }, 409);
34349
34560
  }
@@ -34379,18 +34590,18 @@ app62.post("/", requireAdminSession, async (c) => {
34379
34590
  const adjustmentsLeft = toNum(after.records[0]?.get("adjustmentsLeft"));
34380
34591
  const remaining = taskLeft + entriesLeft + adjustmentsLeft;
34381
34592
  console.log(
34382
- `${TAG51} op=persisted deletionId=${deletionId} taskId=${taskId} tasksDeleted=${tasksDeleted} timeEntriesDeleted=${timeEntriesDeleted} adjustmentsDeleted=${adjustmentsDeleted} remaining=${remaining}`
34593
+ `${TAG53} op=persisted deletionId=${deletionId} taskId=${taskId} tasksDeleted=${tasksDeleted} timeEntriesDeleted=${timeEntriesDeleted} adjustmentsDeleted=${adjustmentsDeleted} remaining=${remaining}`
34383
34594
  );
34384
34595
  if (remaining > 0) {
34385
34596
  console.error(
34386
- `${TAG51} op=incomplete deletionId=${deletionId} taskId=${taskId} taskLeft=${taskLeft} entriesLeft=${entriesLeft} adjustmentsLeft=${adjustmentsLeft} detail="the delete returned but rows for this taskId survive"`
34597
+ `${TAG53} op=incomplete deletionId=${deletionId} taskId=${taskId} taskLeft=${taskLeft} entriesLeft=${entriesLeft} adjustmentsLeft=${adjustmentsLeft} detail="the delete returned but rows for this taskId survive"`
34387
34598
  );
34388
34599
  }
34389
34600
  invalidate("tasks-list", accountId);
34390
34601
  return c.json({ ok: true, tasksDeleted, timeEntriesDeleted, adjustmentsDeleted, remaining });
34391
34602
  } catch (err) {
34392
34603
  console.error(
34393
- `${TAG51} op=error deletionId=${deletionId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`
34604
+ `${TAG53} op=error deletionId=${deletionId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`
34394
34605
  );
34395
34606
  return c.json({ error: "task-delete-failed" }, 500);
34396
34607
  } finally {
@@ -34484,7 +34695,7 @@ async function resolveAssignableAdmins(accountId, accountsDir = ACCOUNTS_DIR) {
34484
34695
  }
34485
34696
 
34486
34697
  // server/routes/admin/task-assign.ts
34487
- var TAG52 = "[task-assign]";
34698
+ var TAG54 = "[task-assign]";
34488
34699
  var app63 = new Hono();
34489
34700
  app63.post("/", requireAdminSession, async (c) => {
34490
34701
  const cacheKey = c.var.cacheKey;
@@ -34499,7 +34710,7 @@ app63.post("/", requireAdminSession, async (c) => {
34499
34710
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
34500
34711
  if (!taskId) {
34501
34712
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
34502
- console.error(`${TAG52} op=assign rejected reason=${reason} account=${accountId}`);
34713
+ console.error(`${TAG54} op=assign rejected reason=${reason} account=${accountId}`);
34503
34714
  return c.json({ error: "taskId required" }, 400);
34504
34715
  }
34505
34716
  const rawAssignee = body.assigneeUserId;
@@ -34511,7 +34722,7 @@ app63.post("/", requireAdminSession, async (c) => {
34511
34722
  const { ids } = await resolveAllowedAssigneeIds(accountId);
34512
34723
  if (!ids.has(assigneeUserId)) {
34513
34724
  console.error(
34514
- `${TAG52} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=not-in-roster`
34725
+ `${TAG54} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=not-in-roster`
34515
34726
  );
34516
34727
  return c.json({ error: "assignee-not-in-roster" }, 403);
34517
34728
  }
@@ -34533,7 +34744,7 @@ app63.post("/", requireAdminSession, async (c) => {
34533
34744
  `MATCH (t:Task {taskId: $taskId, accountId: $accountId}) SET t.updatedAt = $now`,
34534
34745
  { taskId, accountId, now }
34535
34746
  );
34536
- console.log(`${TAG52} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=none assigned=false source=route`);
34747
+ console.log(`${TAG54} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=none assigned=false source=route`);
34537
34748
  invalidate("tasks-list", accountId);
34538
34749
  return c.json({ ok: true, assigneeUserId: null, assigneeName: null });
34539
34750
  }
@@ -34549,7 +34760,7 @@ app63.post("/", requireAdminSession, async (c) => {
34549
34760
  );
34550
34761
  if (set.records.length === 0) {
34551
34762
  console.error(
34552
- `${TAG52} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=no-adminuser-node`
34763
+ `${TAG54} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=no-adminuser-node`
34553
34764
  );
34554
34765
  return c.json({ error: "assignee-no-admin-node" }, 409);
34555
34766
  }
@@ -34557,12 +34768,12 @@ app63.post("/", requireAdminSession, async (c) => {
34557
34768
  const family = set.records[0].get("familyName") ?? null;
34558
34769
  const assigneeName = given && given.trim().length > 0 ? family && family.trim().length > 0 ? `${given.trim()} ${family.trim()}` : given.trim() : null;
34559
34770
  console.log(
34560
- `${TAG52} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=true source=route`
34771
+ `${TAG54} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=true source=route`
34561
34772
  );
34562
34773
  invalidate("tasks-list", accountId);
34563
34774
  return c.json({ ok: true, assigneeUserId, assigneeName });
34564
34775
  } catch (err) {
34565
- console.error(`${TAG52} op=assign error="${err instanceof Error ? err.message : String(err)}"`);
34776
+ console.error(`${TAG54} op=assign error="${err instanceof Error ? err.message : String(err)}"`);
34566
34777
  return c.json({ error: "task-assign-failed" }, 500);
34567
34778
  } finally {
34568
34779
  await session.close();
@@ -34571,13 +34782,13 @@ app63.post("/", requireAdminSession, async (c) => {
34571
34782
  var task_assign_default = app63;
34572
34783
 
34573
34784
  // server/routes/admin/task-session-open.ts
34574
- var TAG53 = "[task-session]";
34785
+ var TAG55 = "[task-session]";
34575
34786
  var app64 = new Hono();
34576
34787
  app64.post("/", requireAdminSession, async (c) => {
34577
34788
  const cacheKey = c.var.cacheKey;
34578
34789
  const accountId = getAccountIdForSession(cacheKey);
34579
34790
  if (!accountId) {
34580
- console.error(`${TAG53} op=request auth-rejected reason="no account for session"`);
34791
+ console.error(`${TAG55} op=request auth-rejected reason="no account for session"`);
34581
34792
  return c.json({ error: "Account not found for session" }, 401);
34582
34793
  }
34583
34794
  let body;
@@ -34589,7 +34800,7 @@ app64.post("/", requireAdminSession, async (c) => {
34589
34800
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
34590
34801
  if (!taskId) {
34591
34802
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
34592
- console.error(`${TAG53} op=request rejected reason=${reason} account=${accountId}`);
34803
+ console.error(`${TAG55} op=request rejected reason=${reason} account=${accountId}`);
34593
34804
  return c.json({ error: "taskId required" }, 400);
34594
34805
  }
34595
34806
  const session = getSession();
@@ -34603,14 +34814,14 @@ app64.post("/", requireAdminSession, async (c) => {
34603
34814
  { taskId, accountId }
34604
34815
  );
34605
34816
  if (found.records.length === 0) {
34606
- console.error(`${TAG53} op=request taskId=${taskId} account=${accountId} result=task-not-found`);
34817
+ console.error(`${TAG55} op=request taskId=${taskId} account=${accountId} result=task-not-found`);
34607
34818
  return c.json({ error: "task-not-found" }, 404);
34608
34819
  }
34609
34820
  const title = found.records[0].get("name") ?? "";
34610
34821
  const sessionId = found.records[0].get("sessionId") ?? null;
34611
34822
  const description = found.records[0].get("description") ?? "";
34612
34823
  const notes = found.records[0].get("notes") ?? "";
34613
- console.log(`${TAG53} op=request taskId=${taskId} account=${accountId} linked=${sessionId ?? "none"}`);
34824
+ console.log(`${TAG55} op=request taskId=${taskId} account=${accountId} linked=${sessionId ?? "none"}`);
34614
34825
  if (sessionId !== null) return c.json({ sessionId, title, description, notes });
34615
34826
  const now = (/* @__PURE__ */ new Date()).toISOString();
34616
34827
  const stamped = await session.run(
@@ -34620,10 +34831,10 @@ app64.post("/", requireAdminSession, async (c) => {
34620
34831
  { taskId, accountId, now }
34621
34832
  );
34622
34833
  const at = stamped.records[0]?.get("at") ?? void 0;
34623
- console.log(`${TAG53} op=marker taskId=${taskId} stamped=${at !== void 0}`);
34834
+ console.log(`${TAG55} op=marker taskId=${taskId} stamped=${at !== void 0}`);
34624
34835
  return c.json({ sessionId: null, title, description, notes });
34625
34836
  } catch (err) {
34626
- console.error(`${TAG53} op=request taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
34837
+ console.error(`${TAG55} op=request taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
34627
34838
  return c.json({ error: "task-session-open-failed" }, 500);
34628
34839
  } finally {
34629
34840
  await session.close();
@@ -34632,13 +34843,13 @@ app64.post("/", requireAdminSession, async (c) => {
34632
34843
  var task_session_open_default = app64;
34633
34844
 
34634
34845
  // server/routes/admin/task-session-link.ts
34635
- var TAG54 = "[task-session]";
34846
+ var TAG56 = "[task-session]";
34636
34847
  var app65 = new Hono();
34637
34848
  app65.post("/", requireAdminSession, async (c) => {
34638
34849
  const cacheKey = c.var.cacheKey;
34639
34850
  const accountId = getAccountIdForSession(cacheKey);
34640
34851
  if (!accountId) {
34641
- console.error(`${TAG54} op=link auth-rejected reason="no account for session"`);
34852
+ console.error(`${TAG56} op=link auth-rejected reason="no account for session"`);
34642
34853
  return c.json({ error: "Account not found for session" }, 401);
34643
34854
  }
34644
34855
  let body;
@@ -34650,12 +34861,12 @@ app65.post("/", requireAdminSession, async (c) => {
34650
34861
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
34651
34862
  if (!taskId) {
34652
34863
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
34653
- console.error(`${TAG54} op=link rejected reason=${reason} account=${accountId}`);
34864
+ console.error(`${TAG56} op=link rejected reason=${reason} account=${accountId}`);
34654
34865
  return c.json({ error: "taskId required" }, 400);
34655
34866
  }
34656
34867
  const sessionId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : "";
34657
34868
  if (!sessionId) {
34658
- console.error(`${TAG54} op=link rejected reason=missing-sessionId taskId=${taskId} account=${accountId}`);
34869
+ console.error(`${TAG56} op=link rejected reason=missing-sessionId taskId=${taskId} account=${accountId}`);
34659
34870
  return c.json({ error: "sessionId required" }, 400);
34660
34871
  }
34661
34872
  const session = getSession();
@@ -34669,13 +34880,13 @@ app65.post("/", requireAdminSession, async (c) => {
34669
34880
  { taskId, accountId, sessionId }
34670
34881
  );
34671
34882
  if (state.records.length === 0) {
34672
- console.error(`${TAG54} op=link taskId=${taskId} account=${accountId} result=task-not-found`);
34883
+ console.error(`${TAG56} op=link taskId=${taskId} account=${accountId} result=task-not-found`);
34673
34884
  return c.json({ error: "task-not-found" }, 404);
34674
34885
  }
34675
34886
  const conversationExists = state.records[0].get("conversationExists") === true;
34676
34887
  const priorSessionIds = (state.records[0].get("priorSessionIds") ?? []).filter((s) => typeof s === "string");
34677
34888
  if (!conversationExists) {
34678
- console.error(`${TAG54} op=link-failed taskId=${taskId} sessionId=${sessionId} reason=conversation-absent`);
34889
+ console.error(`${TAG56} op=link-failed taskId=${taskId} sessionId=${sessionId} reason=conversation-absent`);
34679
34890
  return c.json({ linked: false, created: false, repointedFrom: null });
34680
34891
  }
34681
34892
  const stale = priorSessionIds.filter((s) => s !== sessionId);
@@ -34701,18 +34912,18 @@ app65.post("/", requireAdminSession, async (c) => {
34701
34912
  const repointedFrom = stale.length > 0 ? stale[0] : null;
34702
34913
  if (!linked) {
34703
34914
  console.error(
34704
- `${TAG54} op=link-failed taskId=${taskId} sessionId=${sessionId} reason=edge-absent-after-merge linkedTo=${linkedTo ?? "none"}`
34915
+ `${TAG56} op=link-failed taskId=${taskId} sessionId=${sessionId} reason=edge-absent-after-merge linkedTo=${linkedTo ?? "none"}`
34705
34916
  );
34706
34917
  return c.json({ linked: false, created: false, repointedFrom });
34707
34918
  }
34708
34919
  console.log(
34709
- `${TAG54} op=link taskId=${taskId} sessionId=${sessionId} created=${created} repointedFrom=${repointedFrom ?? "none"}`
34920
+ `${TAG56} op=link taskId=${taskId} sessionId=${sessionId} created=${created} repointedFrom=${repointedFrom ?? "none"}`
34710
34921
  );
34711
34922
  invalidate("tasks-list", accountId);
34712
34923
  return c.json({ linked: true, created, repointedFrom });
34713
34924
  } catch (err) {
34714
34925
  console.error(
34715
- `${TAG54} op=link-failed taskId=${taskId} sessionId=${sessionId} reason="${err instanceof Error ? err.message : String(err)}"`
34926
+ `${TAG56} op=link-failed taskId=${taskId} sessionId=${sessionId} reason="${err instanceof Error ? err.message : String(err)}"`
34716
34927
  );
34717
34928
  return c.json({ error: "task-session-link-failed" }, 500);
34718
34929
  } finally {
@@ -34722,21 +34933,21 @@ app65.post("/", requireAdminSession, async (c) => {
34722
34933
  var task_session_link_default = app65;
34723
34934
 
34724
34935
  // server/routes/admin/admin-users-list.ts
34725
- var TAG55 = "[admin-users]";
34936
+ var TAG57 = "[admin-users]";
34726
34937
  var app66 = new Hono();
34727
34938
  app66.get("/", requireAdminSession, async (c) => {
34728
34939
  const cacheKey = c.var.cacheKey;
34729
34940
  const accountId = getAccountIdForSession(cacheKey);
34730
34941
  if (!accountId) {
34731
- console.error(`${TAG55} op=list auth-rejected reason="no account for session"`);
34942
+ console.error(`${TAG57} op=list auth-rejected reason="no account for session"`);
34732
34943
  return c.json({ error: "Account not found for session" }, 401);
34733
34944
  }
34734
34945
  try {
34735
34946
  const users = await resolveAssignableAdmins(accountId);
34736
- console.log(`${TAG55} op=list account=${accountId} count=${users.length}`);
34947
+ console.log(`${TAG57} op=list account=${accountId} count=${users.length}`);
34737
34948
  return c.json({ ok: true, users });
34738
34949
  } catch (err) {
34739
- console.error(`${TAG55} op=list error="${err instanceof Error ? err.message : String(err)}"`);
34950
+ console.error(`${TAG57} op=list error="${err instanceof Error ? err.message : String(err)}"`);
34740
34951
  return c.json({ error: "admin-users-list-failed" }, 500);
34741
34952
  }
34742
34953
  });
@@ -34744,7 +34955,7 @@ var admin_users_list_default = app66;
34744
34955
 
34745
34956
  // server/routes/admin/dispatch-destinations.ts
34746
34957
  import { resolve as resolve43 } from "path";
34747
- var TAG56 = "[admin:routines]";
34958
+ var TAG58 = "[admin:routines]";
34748
34959
  function scopeFor(d, houseDir, houseAccountId) {
34749
34960
  if (d.channel !== "whatsapp") return null;
34750
34961
  if (d.label === "Account manager") return boundSubAccountFor(houseDir, d.destination);
@@ -34762,7 +34973,7 @@ var app67 = new Hono();
34762
34973
  app67.get("/", requireAdminSession, async (c) => {
34763
34974
  const accountId = getAccountIdForSession(c.var.cacheKey);
34764
34975
  if (!accountId) {
34765
- console.error(`${TAG56} op=destinations auth-rejected reason="no account for session"`);
34976
+ console.error(`${TAG58} op=destinations auth-rejected reason="no account for session"`);
34766
34977
  return c.json({ error: "Account not found for session" }, 401);
34767
34978
  }
34768
34979
  let houseAccountId = null;
@@ -34770,7 +34981,7 @@ app67.get("/", requireAdminSession, async (c) => {
34770
34981
  houseAccountId = resolveHouseOrSoleAccountId(ACCOUNTS_DIR);
34771
34982
  } catch (err) {
34772
34983
  const message = err instanceof Error ? err.message : String(err);
34773
- console.error(`${TAG56} op=destinations account=${accountId.slice(0, 8)} outcome=house-unresolved reason="${message}"`);
34984
+ console.error(`${TAG58} op=destinations account=${accountId.slice(0, 8)} outcome=house-unresolved reason="${message}"`);
34774
34985
  }
34775
34986
  const destinations = listDispatchDestinations({ accountsDir: ACCOUNTS_DIR, houseAccountId, accountId });
34776
34987
  const houseDir = houseAccountId === null ? null : resolve43(ACCOUNTS_DIR, houseAccountId);
@@ -34821,7 +35032,7 @@ app67.get("/", requireAdminSession, async (c) => {
34821
35032
  const scopes = [...byScope.keys()];
34822
35033
  const nameScope = scopes.length === 0 ? "none" : scopes.every((s) => s === houseAccountId) ? "house" : scopes.some((s) => s === houseAccountId) ? "mixed" : "account";
34823
35034
  console.log(
34824
- `${TAG56} op=destinations account=${accountId.slice(0, 8)} count=${destinations.length} named=${named} unnamed=${destinations.length - named} nameScope=${nameScope}` + (houseAccountId === null ? " outcome=house-unresolved" : "") + (namesUnresolved === null ? "" : ` outcome=names-unresolved reason="${namesUnresolved}"`)
35035
+ `${TAG58} op=destinations account=${accountId.slice(0, 8)} count=${destinations.length} named=${named} unnamed=${destinations.length - named} nameScope=${nameScope}` + (houseAccountId === null ? " outcome=house-unresolved" : "") + (namesUnresolved === null ? "" : ` outcome=names-unresolved reason="${namesUnresolved}"`)
34825
35036
  );
34826
35037
  return c.json({ destinations });
34827
35038
  });
@@ -34830,7 +35041,7 @@ var dispatch_destinations_default = app67;
34830
35041
  // server/routes/admin/activity.ts
34831
35042
  import { readdirSync as readdirSync30 } from "fs";
34832
35043
  var app68 = new Hono();
34833
- var TAG57 = "[activity]";
35044
+ var TAG59 = "[activity]";
34834
35045
  function resolveHouseAccountId2(accounts) {
34835
35046
  const houses = accounts.filter((a) => a.config.role === "house");
34836
35047
  if (houses.length === 1) return { accountId: houses[0].accountId, reason: "house" };
@@ -34859,19 +35070,19 @@ app68.get("/", requireAdminSession, async (c) => {
34859
35070
  const house = resolveHouseAccountId2(valid);
34860
35071
  const isHouse = caller2 !== null && house.accountId !== null && caller2 === house.accountId;
34861
35072
  console.log(
34862
- `${TAG57} op=request req=${req} caller=${short(caller2)} house=${isHouse} houseVia=${house.reason}`
35073
+ `${TAG59} op=request req=${req} caller=${short(caller2)} house=${isHouse} houseVia=${house.reason}`
34863
35074
  );
34864
35075
  if (!isHouse) {
34865
35076
  const reason = house.accountId === null ? house.reason : "not-house";
34866
35077
  console.error(
34867
- `${TAG57} op=gate req=${req} decision=deny reason=${reason} caller=${short(caller2)} houseAcct=${short(house.accountId)} accounts=${valid.length}`
35078
+ `${TAG59} op=gate req=${req} decision=deny reason=${reason} caller=${short(caller2)} houseAcct=${short(house.accountId)} accounts=${valid.length}`
34868
35079
  );
34869
35080
  return c.json({ error: "house-only" }, 403);
34870
35081
  }
34871
- console.log(`${TAG57} op=gate req=${req} decision=allow caller=${short(caller2)} via=${house.reason}`);
35082
+ console.log(`${TAG59} op=gate req=${req} decision=allow caller=${short(caller2)} via=${house.reason}`);
34872
35083
  const onDisk = countAccountDirsOnDisk();
34873
35084
  console.log(
34874
- `${TAG57} op=accounts req=${req} onDisk=${onDisk ?? "unknown"} valid=${valid.length} ids=${valid.map((a) => short(a.accountId)).join(",")}`
35085
+ `${TAG59} op=accounts req=${req} onDisk=${onDisk ?? "unknown"} valid=${valid.length} ids=${valid.map((a) => short(a.accountId)).join(",")}`
34875
35086
  );
34876
35087
  const start = Date.now();
34877
35088
  let res;
@@ -34887,17 +35098,17 @@ app68.get("/", requireAdminSession, async (c) => {
34887
35098
  });
34888
35099
  } catch (err) {
34889
35100
  console.error(
34890
- `${TAG57} op=manager-fail req=${req} reason=unreachable err=${err instanceof Error ? err.message : String(err)}`
35101
+ `${TAG59} op=manager-fail req=${req} reason=unreachable err=${err instanceof Error ? err.message : String(err)}`
34891
35102
  );
34892
35103
  return c.json({ error: "manager-unreachable" }, 502);
34893
35104
  }
34894
35105
  if (res.status !== 200) {
34895
- console.error(`${TAG57} op=manager-fail req=${req} reason=status status=${res.status}`);
35106
+ console.error(`${TAG59} op=manager-fail req=${req} reason=status status=${res.status}`);
34896
35107
  return c.json({ error: `manager-status-${res.status}` }, 502);
34897
35108
  }
34898
35109
  const body = await res.json().catch(() => null);
34899
35110
  if (!body || !Array.isArray(body.rows)) {
34900
- console.error(`${TAG57} op=manager-fail req=${req} reason=bad-json`);
35111
+ console.error(`${TAG59} op=manager-fail req=${req} reason=bad-json`);
34901
35112
  return c.json({ error: "manager-bad-json" }, 502);
34902
35113
  }
34903
35114
  const rows = body.rows;
@@ -34908,10 +35119,10 @@ app68.get("/", requireAdminSession, async (c) => {
34908
35119
  const ageNull = rows.filter((r) => r.ageMs === null).length;
34909
35120
  const sidecarNull = rows.filter((r) => r.sidecarMissing).length;
34910
35121
  console.log(
34911
- `${TAG57} op=census req=${req} ageMs=${censusAgeMs ?? "none"} live=${census?.live ?? "none"} cpuNull=${census?.cpuNull ?? "none"} rows=${rows.length} ageNull=${ageNull} sidecarNull=${sidecarNull}`
35122
+ `${TAG59} op=census req=${req} ageMs=${censusAgeMs ?? "none"} live=${census?.live ?? "none"} cpuNull=${census?.cpuNull ?? "none"} rows=${rows.length} ageNull=${ageNull} sidecarNull=${sidecarNull}`
34912
35123
  );
34913
35124
  console.log(
34914
- `${TAG57} op=response req=${req} rows=${rows.length} accounts=${valid.length} untagged=${untaggedCount} meteringFailed=${meteringFailed} ms=${Date.now() - start}`
35125
+ `${TAG59} op=response req=${req} rows=${rows.length} accounts=${valid.length} untagged=${untaggedCount} meteringFailed=${meteringFailed} ms=${Date.now() - start}`
34915
35126
  );
34916
35127
  const names = {};
34917
35128
  await Promise.all(
@@ -34930,7 +35141,7 @@ app68.get("/", requireAdminSession, async (c) => {
34930
35141
  return acc;
34931
35142
  }, {});
34932
35143
  console.log(
34933
- `${TAG57} op=loops req=${req} armed=${armed2} rendered=${loopRows.length} procPct=${cpu.processPct?.toFixed(1) ?? "null"} attributedPct=${cpu.attributedPct?.toFixed(1) ?? "null"} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
35144
+ `${TAG59} op=loops req=${req} armed=${armed2} rendered=${loopRows.length} procPct=${cpu.processPct?.toFixed(1) ?? "null"} attributedPct=${cpu.attributedPct?.toFixed(1) ?? "null"} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
34934
35145
  );
34935
35146
  return c.json({
34936
35147
  rows,
@@ -35021,7 +35232,7 @@ app69.route("/activity", activity_default);
35021
35232
  var admin_default = app69;
35022
35233
 
35023
35234
  // server/routes/access/verify-token.ts
35024
- var TAG58 = "[access-verify]";
35235
+ var TAG60 = "[access-verify]";
35025
35236
  var MINT_TAG = "[access-session-mint]";
35026
35237
  var COOKIE_NAME = "__access_session";
35027
35238
  var app70 = new Hono();
@@ -35040,39 +35251,39 @@ app70.post("/", async (c) => {
35040
35251
  }
35041
35252
  const rateMsg = checkAccessRateLimit(ip, agentSlug);
35042
35253
  if (rateMsg) {
35043
- console.error(`${TAG58} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
35254
+ console.error(`${TAG60} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
35044
35255
  return c.json({ error: rateMsg }, 429);
35045
35256
  }
35046
35257
  const grant = await findGrantByMagicToken(token);
35047
35258
  if (!grant) {
35048
35259
  recordAccessFailedAttempt(ip, agentSlug);
35049
- console.error(`${TAG58} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
35260
+ console.error(`${TAG60} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
35050
35261
  return c.json({ error: "invalid-or-expired-link" }, 401);
35051
35262
  }
35052
35263
  if (grant.agentSlug !== agentSlug) {
35053
35264
  recordAccessFailedAttempt(ip, agentSlug);
35054
35265
  console.error(
35055
- `${TAG58} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
35266
+ `${TAG60} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
35056
35267
  );
35057
35268
  return c.json({ error: "invalid-or-expired-link" }, 401);
35058
35269
  }
35059
35270
  if (grant.status === "expired" || grant.status === "revoked") {
35060
35271
  recordAccessFailedAttempt(ip, agentSlug);
35061
35272
  console.error(
35062
- `${TAG58} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
35273
+ `${TAG60} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
35063
35274
  );
35064
35275
  return c.json({ error: "access-no-longer-valid" }, 401);
35065
35276
  }
35066
35277
  if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
35067
35278
  recordAccessFailedAttempt(ip, agentSlug);
35068
35279
  console.error(
35069
- `${TAG58} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
35280
+ `${TAG60} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
35070
35281
  );
35071
35282
  return c.json({ error: "access-no-longer-valid" }, 401);
35072
35283
  }
35073
35284
  if (!grant.sliceToken) {
35074
35285
  console.error(
35075
- `${TAG58} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
35286
+ `${TAG60} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
35076
35287
  );
35077
35288
  return c.json({ error: "grant-misconfigured" }, 500);
35078
35289
  }
@@ -35088,12 +35299,12 @@ app70.post("/", async (c) => {
35088
35299
  await consumeMagicTokenAndActivate(grant.grantId);
35089
35300
  } catch (err) {
35090
35301
  console.error(
35091
- `${TAG58} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
35302
+ `${TAG60} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
35092
35303
  );
35093
35304
  return c.json({ error: "verification-failed" }, 500);
35094
35305
  }
35095
35306
  clearAccessRateLimit(ip, agentSlug);
35096
- console.log(`${TAG58} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
35307
+ console.log(`${TAG60} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
35097
35308
  console.log(
35098
35309
  `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
35099
35310
  );
@@ -35169,7 +35380,7 @@ async function sendMagicLinkEmail(payload) {
35169
35380
  }
35170
35381
 
35171
35382
  // server/routes/access/request-magic-link.ts
35172
- var TAG59 = "[access-request-link]";
35383
+ var TAG61 = "[access-request-link]";
35173
35384
  var app71 = new Hono();
35174
35385
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
35175
35386
  app71.post("/", async (c) => {
@@ -35186,18 +35397,18 @@ app71.post("/", async (c) => {
35186
35397
  }
35187
35398
  const rateMsg = checkRequestLinkRateLimit(contactValue);
35188
35399
  if (rateMsg) {
35189
- console.error(`${TAG59} contactValue=${maskContact(contactValue)} result=rate-limited`);
35400
+ console.error(`${TAG61} contactValue=${maskContact(contactValue)} result=rate-limited`);
35190
35401
  return c.json({ error: rateMsg }, 429);
35191
35402
  }
35192
35403
  recordRequestLinkAttempt(contactValue);
35193
35404
  const accountId = process.env.ACCOUNT_ID ?? "";
35194
35405
  if (!accountId) {
35195
- console.error(`${TAG59} contactValue=${maskContact(contactValue)} result=no-account-id`);
35406
+ console.error(`${TAG61} contactValue=${maskContact(contactValue)} result=no-account-id`);
35196
35407
  return c.json({ message: VISITOR_MESSAGE }, 200);
35197
35408
  }
35198
35409
  const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
35199
35410
  if (!grant) {
35200
- console.log(`${TAG59} contactValue=${maskContact(contactValue)} result=notfound`);
35411
+ console.log(`${TAG61} contactValue=${maskContact(contactValue)} result=notfound`);
35201
35412
  return c.json({ message: VISITOR_MESSAGE }, 200);
35202
35413
  }
35203
35414
  let token;
@@ -35205,7 +35416,7 @@ app71.post("/", async (c) => {
35205
35416
  token = await generateNewMagicToken(grant.grantId);
35206
35417
  } catch (err) {
35207
35418
  console.error(
35208
- `${TAG59} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
35419
+ `${TAG61} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
35209
35420
  );
35210
35421
  return c.json({ message: VISITOR_MESSAGE }, 200);
35211
35422
  }
@@ -35237,12 +35448,12 @@ app71.post("/", async (c) => {
35237
35448
  });
35238
35449
  if (!sendResult.ok) {
35239
35450
  console.error(
35240
- `${TAG59} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
35451
+ `${TAG61} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
35241
35452
  );
35242
35453
  return c.json({ message: VISITOR_MESSAGE }, 200);
35243
35454
  }
35244
35455
  console.log(
35245
- `${TAG59} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
35456
+ `${TAG61} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
35246
35457
  );
35247
35458
  return c.json({ message: VISITOR_MESSAGE }, 200);
35248
35459
  });
@@ -36252,7 +36463,7 @@ import { stat as stat8, readFile as readFile12, realpath } from "fs/promises";
36252
36463
  import { join as join62, resolve as resolve47, sep as sep11 } from "path";
36253
36464
  import { Readable as Readable3 } from "stream";
36254
36465
  var app79 = new Hono();
36255
- var TAG60 = "[portal-fetch]";
36466
+ var TAG62 = "[portal-fetch]";
36256
36467
  async function verifySignature(secret, accountId, relPath, expiresAtMs, signature, nowMs) {
36257
36468
  if (!secret || !signature) return false;
36258
36469
  if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) return false;
@@ -36307,14 +36518,14 @@ async function readAccountSecret(accountId) {
36307
36518
  try {
36308
36519
  text = await readFile12(path, "utf8");
36309
36520
  } catch {
36310
- console.error(`${TAG60} op=no-secret account="${accountId}" path="${path}"`);
36521
+ console.error(`${TAG62} op=no-secret account="${accountId}" path="${path}"`);
36311
36522
  return "";
36312
36523
  }
36313
36524
  for (const line of text.split("\n")) {
36314
36525
  const m = line.match(/^PORTAL_FETCH_SECRET=(.*)$/);
36315
36526
  if (m) return m[1].trim();
36316
36527
  }
36317
- console.error(`${TAG60} op=no-secret account="${accountId}" reason=key-absent path="${path}"`);
36528
+ console.error(`${TAG62} op=no-secret account="${accountId}" reason=key-absent path="${path}"`);
36318
36529
  return "";
36319
36530
  }
36320
36531
  function isValidAccountId2(value) {
@@ -36374,16 +36585,16 @@ async function handle(c, headOnly) {
36374
36585
  const sig = c.req.query("sig") ?? "";
36375
36586
  const owner = c.req.query("owner") ?? "";
36376
36587
  console.log(
36377
- `${TAG60} op=request account=${q(accountId)} path=${q(relPath)} owner=${q(owner)} head=${headOnly}`
36588
+ `${TAG62} op=request account=${q(accountId)} path=${q(relPath)} owner=${q(owner)} head=${headOnly}`
36378
36589
  );
36379
36590
  if (!isValidAccountId2(accountId)) {
36380
- console.error(`${TAG60} op=denied account=${q(accountId)} reason=account-id`);
36591
+ console.error(`${TAG62} op=denied account=${q(accountId)} reason=account-id`);
36381
36592
  return c.json({ ok: false, error: "denied" }, 401);
36382
36593
  }
36383
36594
  const secret = await readAccountSecret(accountId);
36384
36595
  const verified = owner ? await verifyOwnUploadSignature(secret, accountId, owner, relPath, exp, sig, Date.now()) : await verifySignature(secret, accountId, relPath, exp, sig, Date.now());
36385
36596
  if (!verified) {
36386
- console.error(`${TAG60} op=denied account=${q(accountId)} reason=secret`);
36597
+ console.error(`${TAG62} op=denied account=${q(accountId)} reason=secret`);
36387
36598
  return c.json({ ok: false, error: "denied" }, 401);
36388
36599
  }
36389
36600
  const target = await resolveFetchTarget(
@@ -36393,14 +36604,14 @@ async function handle(c, headOnly) {
36393
36604
  );
36394
36605
  if (!target.ok) {
36395
36606
  const op = target.reason === "enoent" ? "miss" : "denied";
36396
- console.error(`${TAG60} op=${op} account=${accountId} reason=${target.reason}`);
36607
+ console.error(`${TAG62} op=${op} account=${accountId} reason=${target.reason}`);
36397
36608
  return c.json({ ok: false, error: "not found" }, 404);
36398
36609
  }
36399
36610
  if (headOnly) {
36400
- console.log(`${TAG60} op=served account=${accountId} bytes=${target.sizeBytes} head=true`);
36611
+ console.log(`${TAG62} op=served account=${accountId} bytes=${target.sizeBytes} head=true`);
36401
36612
  return c.body(null, 200);
36402
36613
  }
36403
- console.log(`${TAG60} op=served account=${accountId} bytes=${target.sizeBytes}`);
36614
+ console.log(`${TAG62} op=served account=${accountId} bytes=${target.sizeBytes}`);
36404
36615
  return new Response(Readable3.toWeb(createReadStream3(target.absolute)), {
36405
36616
  status: 200,
36406
36617
  headers: {
@@ -36419,7 +36630,7 @@ app79.on("HEAD", "/fetch", (c) => handle(c, true));
36419
36630
  var portal_fetch_default = app79;
36420
36631
 
36421
36632
  // app/lib/timeentry-census.ts
36422
- var TAG61 = "[timeentry-census]";
36633
+ var TAG63 = "[timeentry-census]";
36423
36634
  function reconcileTimeEntries(rows, now, horizonMs) {
36424
36635
  const openEntries = rows.openEntries.length;
36425
36636
  let oldestOpenAgeMs = 0;
@@ -36459,12 +36670,12 @@ async function runTimeEntryCensusOnce(session, now, horizonMs) {
36459
36670
  sumAdjustments: toNum(r.get("sumAdjustments"))
36460
36671
  }));
36461
36672
  const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
36462
- const line = `${TAG61} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
36673
+ const line = `${TAG63} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
36463
36674
  if (finding.alarm) console.error(`${line} alarm=true`);
36464
36675
  else console.log(line);
36465
36676
  return finding;
36466
36677
  } catch (err) {
36467
- console.error(`${TAG61} error="${err instanceof Error ? err.message : String(err)}"`);
36678
+ console.error(`${TAG63} error="${err instanceof Error ? err.message : String(err)}"`);
36468
36679
  return null;
36469
36680
  }
36470
36681
  }
@@ -36478,13 +36689,13 @@ function startTimeEntryCensus(openSession, opts = {}) {
36478
36689
  try {
36479
36690
  session = openSession();
36480
36691
  } catch (err) {
36481
- console.error(`${TAG61} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
36692
+ console.error(`${TAG63} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
36482
36693
  return;
36483
36694
  }
36484
36695
  try {
36485
36696
  await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
36486
36697
  } catch (err) {
36487
- console.error(`${TAG61} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
36698
+ console.error(`${TAG63} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
36488
36699
  } finally {
36489
36700
  try {
36490
36701
  await session.close();
@@ -36545,7 +36756,7 @@ function formatCensusLine(f) {
36545
36756
  }
36546
36757
 
36547
36758
  // app/lib/ledger-census.ts
36548
- var TAG62 = "[ledger-census]";
36759
+ var TAG64 = "[ledger-census]";
36549
36760
  var DAY_MS = 864e5;
36550
36761
  function countWriteRejects24h(logDir, now) {
36551
36762
  let entries2;
@@ -36621,7 +36832,7 @@ async function runLedgerCensusOnce(session, now) {
36621
36832
  else console.log(line);
36622
36833
  return finding;
36623
36834
  } catch (err) {
36624
- console.error(`${TAG62} error="${err instanceof Error ? err.message : String(err)}"`);
36835
+ console.error(`${TAG64} error="${err instanceof Error ? err.message : String(err)}"`);
36625
36836
  return null;
36626
36837
  }
36627
36838
  }
@@ -36634,7 +36845,7 @@ function startLedgerCensus(openSession, opts = {}) {
36634
36845
  session = openSession();
36635
36846
  } catch (err) {
36636
36847
  console.error(
36637
- `${TAG62} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`
36848
+ `${TAG64} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`
36638
36849
  );
36639
36850
  return;
36640
36851
  }
@@ -36642,7 +36853,7 @@ function startLedgerCensus(openSession, opts = {}) {
36642
36853
  await runLedgerCensusOnce(session, Date.now());
36643
36854
  } catch (err) {
36644
36855
  console.error(
36645
- `${TAG62} tick-failed error="${err instanceof Error ? err.message : String(err)}"`
36856
+ `${TAG64} tick-failed error="${err instanceof Error ? err.message : String(err)}"`
36646
36857
  );
36647
36858
  } finally {
36648
36859
  try {
@@ -36987,7 +37198,7 @@ function startDispatchTgCensus(getSession3, intervalMs = FIFTEEN_MIN_MS3) {
36987
37198
  }
36988
37199
 
36989
37200
  // app/lib/conversation-audit.ts
36990
- var TAG63 = "[conversation-audit]";
37201
+ var TAG65 = "[conversation-audit]";
36991
37202
  async function runConversationAudit() {
36992
37203
  const session = getSession();
36993
37204
  try {
@@ -37024,12 +37235,12 @@ async function runConversationAudit() {
37024
37235
  multiAnchored: Number(multiRec?.get("multiAnchored") ?? 0)
37025
37236
  };
37026
37237
  console.error(
37027
- `${TAG63} channel=whatsapp total=${result.total} senders=${result.senders} multiAnchored=${result.multiAnchored} unreconciled=${result.unreconciled} unpairable=${result.unpairable}`
37238
+ `${TAG65} channel=whatsapp total=${result.total} senders=${result.senders} multiAnchored=${result.multiAnchored} unreconciled=${result.unreconciled} unpairable=${result.unpairable}`
37028
37239
  );
37029
37240
  return result;
37030
37241
  } catch (err) {
37031
37242
  const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
37032
- console.error(`${TAG63} op=failed reason=${reason}`);
37243
+ console.error(`${TAG65} op=failed reason=${reason}`);
37033
37244
  return null;
37034
37245
  } finally {
37035
37246
  await session.close();
@@ -37037,7 +37248,7 @@ async function runConversationAudit() {
37037
37248
  }
37038
37249
 
37039
37250
  // app/lib/graph-health.ts
37040
- var import_dist15 = __toESM(require_dist10(), 1);
37251
+ var import_dist16 = __toESM(require_dist10(), 1);
37041
37252
  var HOUR_MS2 = 60 * 60 * 1e3;
37042
37253
  function renderLabelTop(rows) {
37043
37254
  return rows.map((b) => {
@@ -37082,7 +37293,7 @@ async function runGraphHealthTick() {
37082
37293
  console.error(
37083
37294
  `[graph-health] userprofile-multi accounts=${upAccounts} top=${upTop.length > 0 ? upTop.join(",") : "none"}`
37084
37295
  );
37085
- const indexed = [...import_dist15.VECTOR_INDEXED_LABELS];
37296
+ const indexed = [...import_dist16.VECTOR_INDEXED_LABELS];
37086
37297
  const embCount = await session.run(
37087
37298
  `MATCH (n) WHERE n.embedding IS NULL AND any(l IN labels(n) WHERE l IN $indexed)
37088
37299
  RETURN count(n) AS total`,
@@ -37156,8 +37367,8 @@ function startGraphHealthTimer() {
37156
37367
  // app/lib/shared-folder-census.ts
37157
37368
  import { existsSync as existsSync54, statSync as statSync29, readdirSync as readdirSync32 } from "fs";
37158
37369
  import { join as join65 } from "path";
37159
- var import_dist16 = __toESM(require_dist(), 1);
37160
- var TAG64 = "[shared-census]";
37370
+ var import_dist17 = __toESM(require_dist(), 1);
37371
+ var TAG66 = "[shared-census]";
37161
37372
  var WIRED_SURFACES = 3;
37162
37373
  function countFiles(dir) {
37163
37374
  let n = 0;
@@ -37174,7 +37385,7 @@ function countFiles(dir) {
37174
37385
  return n;
37175
37386
  }
37176
37387
  function runSharedFolderCensus(dataRoot = DATA_ROOT) {
37177
- const root = (0, import_dist16.sharedRoot)(dataRoot);
37388
+ const root = (0, import_dist17.sharedRoot)(dataRoot);
37178
37389
  const exists = existsSync54(root);
37179
37390
  let isDir = false;
37180
37391
  if (exists) {
@@ -37185,8 +37396,8 @@ function runSharedFolderCensus(dataRoot = DATA_ROOT) {
37185
37396
  }
37186
37397
  }
37187
37398
  const files = isDir ? countFiles(root) : 0;
37188
- console.error(`${TAG64} op=root exists=${exists} isDir=${isDir} files=${files}`);
37189
- console.error(`${TAG64} op=wiring surfaces=${WIRED_SURFACES}/3`);
37399
+ console.error(`${TAG66} op=root exists=${exists} isDir=${isDir} files=${files}`);
37400
+ console.error(`${TAG66} op=wiring surfaces=${WIRED_SURFACES}/3`);
37190
37401
  return `shared root ${exists ? "present" : "ABSENT"}, ${files} file(s)`;
37191
37402
  }
37192
37403
  function startSharedFolderCensus() {
@@ -37202,7 +37413,7 @@ function startSharedFolderCensus() {
37202
37413
  import { readdirSync as readdirSync33, readFileSync as readFileSync57 } from "fs";
37203
37414
  import { homedir as homedir4 } from "os";
37204
37415
  import { join as join66 } from "path";
37205
- var TAG65 = "[claude-auth]";
37416
+ var TAG67 = "[claude-auth]";
37206
37417
  function refreshTokenLength(raw) {
37207
37418
  let data;
37208
37419
  try {
@@ -37246,7 +37457,7 @@ function runCredRefreshCensus(home = homedir4()) {
37246
37457
  const blank = rows.filter(isBlank).length;
37247
37458
  const detail = rows.map((r) => `${r.brand}:${r.refreshTokenLen === null ? "unreadable" : r.refreshTokenLen}`).join(",");
37248
37459
  console.log(
37249
- `${TAG65} op=cred-refresh-census result=${censusResult(rows)} brands=${rows.length} blank=${blank} detail=${detail}`
37460
+ `${TAG67} op=cred-refresh-census result=${censusResult(rows)} brands=${rows.length} blank=${blank} detail=${detail}`
37250
37461
  );
37251
37462
  return `${rows.length} brand(s), ${blank} blank`;
37252
37463
  }
@@ -39745,6 +39956,16 @@ var WebchatGateway = class _WebchatGateway {
39745
39956
  clearDeliveryFailure(key2) {
39746
39957
  this.deliveryFailures.delete(key2);
39747
39958
  }
39959
+ /** Task 2439 — mark a key undelivered from OUTSIDE the drain path. Every other
39960
+ * writer above fires because a message the hub already holds could not be
39961
+ * delivered. The send route's deferred continuation is the one case where no
39962
+ * message ever reached the hub: it dies before handleInbound runs, so nothing
39963
+ * is queued, nothing can go stale, and no drain can fail. Without this the
39964
+ * client's pending window (Task 2465 reads exactly this marker) never gets a
39965
+ * verdict and the composer stays disabled until the page is reloaded. */
39966
+ markDeliveryFailure(key2, messageId) {
39967
+ this.deliveryFailures.set(key2, { messageId, at: Date.now() });
39968
+ }
39748
39969
  /** Task 2465 — record which session a conversation key's child is running.
39749
39970
  * Public so the audit below can be driven directly in tests without standing
39750
39971
  * up the channel-server HTTP surface; production writes come from runSpawn and
@@ -40437,14 +40658,14 @@ function makeFileDelivery(opts) {
40437
40658
  }
40438
40659
 
40439
40660
  // app/lib/webchat/file-delivery.ts
40440
- var TAG66 = "[webchat-adaptor]";
40661
+ var TAG68 = "[webchat-adaptor]";
40441
40662
  function platformRoot2() {
40442
40663
  return process.env.MAXY_PLATFORM_ROOT || "";
40443
40664
  }
40444
40665
  function makeWebchatSendFile(entry3) {
40445
40666
  return async (filePath) => {
40446
40667
  if (!entry3.accountId) {
40447
- console.error(`${TAG66} file-delivery reject reason=no-account sender=${entry3.senderId}`);
40668
+ console.error(`${TAG68} file-delivery reject reason=no-account sender=${entry3.senderId}`);
40448
40669
  return { ok: false, error: "no-account" };
40449
40670
  }
40450
40671
  const accountDir = resolve50(platformRoot2(), "..", "data/accounts", entry3.accountId);
@@ -40452,14 +40673,14 @@ function makeWebchatSendFile(entry3) {
40452
40673
  const resolved = realpathSync9(filePath);
40453
40674
  const accountResolved = realpathSync9(accountDir);
40454
40675
  if (!resolved.startsWith(accountResolved + "/")) {
40455
- console.error(`${TAG66} file-delivery reject reason=outside_account_directory sender=${entry3.senderId}`);
40676
+ console.error(`${TAG68} file-delivery reject reason=outside_account_directory sender=${entry3.senderId}`);
40456
40677
  return { ok: false, error: "outside-account" };
40457
40678
  }
40458
40679
  return { ok: true };
40459
40680
  } catch (err) {
40460
40681
  const code = err.code;
40461
40682
  console.error(
40462
- `${TAG66} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry3.senderId}`
40683
+ `${TAG68} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry3.senderId}`
40463
40684
  );
40464
40685
  return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
40465
40686
  }
@@ -40468,7 +40689,7 @@ function makeWebchatSendFile(entry3) {
40468
40689
  function makeWebchatFileDelivery(entry3) {
40469
40690
  return makeFileDelivery({
40470
40691
  entry: entry3,
40471
- tag: TAG66,
40692
+ tag: TAG68,
40472
40693
  channel: "webchat",
40473
40694
  sendFile: makeWebchatSendFile(entry3),
40474
40695
  deferUntilVerdict: true
@@ -40547,7 +40768,7 @@ function buildPublicWebchatSpawnRequest(input) {
40547
40768
  }
40548
40769
 
40549
40770
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
40550
- var TAG67 = "[whatsapp-adaptor]";
40771
+ var TAG69 = "[whatsapp-adaptor]";
40551
40772
  var SEND_USER_FILE2 = "SendUserFile";
40552
40773
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
40553
40774
  function platformRoot3() {
@@ -40565,7 +40786,7 @@ function makeWhatsAppSendFile(entry3, maxyAccountId) {
40565
40786
  });
40566
40787
  if (result.ok) return { ok: true };
40567
40788
  console.error(
40568
- `${TAG67} file-delivery reject reason=send-failed sender=${entry3.senderId} status=${result.status} message=${result.error}`
40789
+ `${TAG69} file-delivery reject reason=send-failed sender=${entry3.senderId} status=${result.status} message=${result.error}`
40569
40790
  );
40570
40791
  return { ok: false, error: result.error };
40571
40792
  };
@@ -40573,7 +40794,7 @@ function makeWhatsAppSendFile(entry3, maxyAccountId) {
40573
40794
  function makeWhatsAppFileDelivery(entry3, maxyAccountId) {
40574
40795
  const shared = makeFileDelivery({
40575
40796
  entry: entry3,
40576
- tag: TAG67,
40797
+ tag: TAG69,
40577
40798
  channel: "whatsapp",
40578
40799
  sendFile: makeWhatsAppSendFile(entry3, maxyAccountId)
40579
40800
  });
@@ -40608,7 +40829,7 @@ function makeWhatsAppFileDelivery(entry3, maxyAccountId) {
40608
40829
  if (!delivered) {
40609
40830
  const fileField = call2.filePath !== void 0 ? ` file=${call2.filePath}` : "";
40610
40831
  console.error(
40611
- `${TAG67} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
40832
+ `${TAG69} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
40612
40833
  );
40613
40834
  }
40614
40835
  }
@@ -40774,7 +40995,7 @@ function whatsappDoorLabel(doorId) {
40774
40995
  }
40775
40996
 
40776
40997
  // app/lib/channel-delivery/refusal-notice.ts
40777
- var TAG68 = "[channel-delivery]";
40998
+ var TAG70 = "[channel-delivery]";
40778
40999
  var notified = /* @__PURE__ */ new Set();
40779
41000
  function refusalNoticeText(reason) {
40780
41001
  return reason === "claude-auth-dead" ? "Your message arrived but no assistant could be started: the Claude sign-in on this install is not active. An operator has to sign in again; send this again once they have." : `Your message arrived but no assistant could be started (${reason}). Nothing was lost; try again shortly.`;
@@ -40783,19 +41004,19 @@ async function sendRefusalNotice(input) {
40783
41004
  if (notified.has(input.doorId)) return;
40784
41005
  const fields = `channel=${input.channel} sender=${input.sender} reason=${input.reason}`;
40785
41006
  if (!input.send) {
40786
- console.error(`${TAG68} op=refusal-notice-failed ${fields} error=no-target`);
41007
+ console.error(`${TAG70} op=refusal-notice-failed ${fields} error=no-target`);
40787
41008
  return;
40788
41009
  }
40789
41010
  try {
40790
41011
  await input.send(guardOutbound(input.sender, refusalNoticeText(input.reason)));
40791
41012
  } catch (e) {
40792
41013
  console.error(
40793
- `${TAG68} op=refusal-notice-failed ${fields} error=${e instanceof Error ? e.message : String(e)}`
41014
+ `${TAG70} op=refusal-notice-failed ${fields} error=${e instanceof Error ? e.message : String(e)}`
40794
41015
  );
40795
41016
  return;
40796
41017
  }
40797
41018
  notified.add(input.doorId);
40798
- console.error(`${TAG68} op=refusal-notice ${fields} status=${input.status}`);
41019
+ console.error(`${TAG70} op=refusal-notice ${fields} status=${input.status}`);
40799
41020
  }
40800
41021
  function clearRefusalNotice(doorId) {
40801
41022
  notified.delete(doorId);
@@ -41455,10 +41676,10 @@ function buildTelegramSpawnRequest(input) {
41455
41676
  }
41456
41677
 
41457
41678
  // server/telegram-ensure-session.ts
41458
- var TAG69 = "[telegram-inbound]";
41679
+ var TAG71 = "[telegram-inbound]";
41459
41680
  function reportReused(botId, channelKey, result) {
41460
41681
  if (!result.reused) return;
41461
- console.error(`${TAG69} op=spawn-reused botId=${botId} key=${channelKey} sessionId=${result.sessionId}`);
41682
+ console.error(`${TAG71} op=spawn-reused botId=${botId} key=${channelKey} sessionId=${result.sessionId}`);
41462
41683
  }
41463
41684
  function buildTelegramEnsureSession(deps) {
41464
41685
  return async (input) => {
@@ -41522,10 +41743,10 @@ function buildTelegramEnsureSession(deps) {
41522
41743
  return;
41523
41744
  }
41524
41745
  console.error(
41525
- `${TAG69} op=telegram-spawn-door botId=${input.botId} role=specialist door=rc specialist=${input.specialist}`
41746
+ `${TAG71} op=telegram-spawn-door botId=${input.botId} role=specialist door=rc specialist=${input.specialist}`
41526
41747
  );
41527
41748
  console.error(
41528
- `${TAG69} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=specialist specialist=${input.specialist}`
41749
+ `${TAG71} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=specialist specialist=${input.specialist}`
41529
41750
  );
41530
41751
  reportReused(input.botId, req.channelKey, result2);
41531
41752
  noteSpawnOk2(input.botId);
@@ -41560,10 +41781,10 @@ function buildTelegramEnsureSession(deps) {
41560
41781
  return;
41561
41782
  }
41562
41783
  console.error(
41563
- `${TAG69} op=telegram-spawn-door botId=${input.botId} role=public door=rc agent=${input.agentSlug}`
41784
+ `${TAG71} op=telegram-spawn-door botId=${input.botId} role=public door=rc agent=${input.agentSlug}`
41564
41785
  );
41565
41786
  console.error(
41566
- `${TAG69} op=spawn botId=${input.botId} sessionId=${result2.sessionId} targetAccountId=${input.accountId} role=public`
41787
+ `${TAG71} op=spawn botId=${input.botId} sessionId=${result2.sessionId} targetAccountId=${input.accountId} role=public`
41567
41788
  );
41568
41789
  reportReused(input.botId, req.channelKey, result2);
41569
41790
  noteSpawnOk2(input.botId);
@@ -41587,7 +41808,7 @@ function buildTelegramEnsureSession(deps) {
41587
41808
  return;
41588
41809
  }
41589
41810
  console.error(
41590
- `${TAG69} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=admin`
41811
+ `${TAG71} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=admin`
41591
41812
  );
41592
41813
  reportReused(input.botId, req.channelKey, result);
41593
41814
  noteSpawnOk2(input.botId);
@@ -41606,7 +41827,7 @@ function buildTelegramEnsureSession(deps) {
41606
41827
  import { realpathSync as realpathSync10 } from "fs";
41607
41828
  import { readFile as readFile15, stat as stat10 } from "fs/promises";
41608
41829
  import { resolve as resolve51, basename as basename18 } from "path";
41609
- var TAG70 = "[telegram:outbound]";
41830
+ var TAG72 = "[telegram:outbound]";
41610
41831
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
41611
41832
  async function sendTelegramDocument(input) {
41612
41833
  const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
@@ -41623,10 +41844,10 @@ async function sendTelegramDocument(input) {
41623
41844
  } catch (err) {
41624
41845
  const code = err.code;
41625
41846
  if (code === "ENOENT") {
41626
- console.error(`${TAG70} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
41847
+ console.error(`${TAG72} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
41627
41848
  return { ok: false, status: 500, error: `Account directory not resolvable for ${maxyAccountId}` };
41628
41849
  }
41629
- console.error(`${TAG70} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
41850
+ console.error(`${TAG72} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
41630
41851
  return { ok: false, status: 500, error: String(err) };
41631
41852
  }
41632
41853
  let resolvedPath;
@@ -41635,14 +41856,14 @@ async function sendTelegramDocument(input) {
41635
41856
  } catch (err) {
41636
41857
  const code = err.code;
41637
41858
  if (code === "ENOENT") {
41638
- console.error(`${TAG70} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
41859
+ console.error(`${TAG72} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
41639
41860
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
41640
41861
  }
41641
- console.error(`${TAG70} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
41862
+ console.error(`${TAG72} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
41642
41863
  return { ok: false, status: 500, error: String(err) };
41643
41864
  }
41644
41865
  if (!resolvedPath.startsWith(accountResolved + "/")) {
41645
- console.error(`${TAG70} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
41866
+ console.error(`${TAG72} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
41646
41867
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
41647
41868
  }
41648
41869
  const fileStat = await stat10(resolvedPath);
@@ -41675,24 +41896,24 @@ async function sendTelegramDocument(input) {
41675
41896
  error = e instanceof Error ? e.message : String(e);
41676
41897
  }
41677
41898
  console.error(
41678
- `${TAG70} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok2}` + (messageId !== void 0 ? ` id=${messageId}` : "")
41899
+ `${TAG72} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok2}` + (messageId !== void 0 ? ` id=${messageId}` : "")
41679
41900
  );
41680
41901
  return ok2 ? { ok: true, messageId } : { ok: false, status: 500, error };
41681
41902
  }
41682
41903
 
41683
41904
  // app/lib/telegram/outbound/file-delivery.ts
41684
- var TAG71 = "[telegram:outbound]";
41905
+ var TAG73 = "[telegram:outbound]";
41685
41906
  function platformRoot4() {
41686
41907
  return process.env.MAXY_PLATFORM_ROOT || "";
41687
41908
  }
41688
41909
  function makeTelegramSendFile(entry3, botToken) {
41689
41910
  return async (filePath, caption) => {
41690
41911
  if (!botToken) {
41691
- console.error(`${TAG71} file-delivery reject reason=no-bot-token sender=${entry3.senderId} role=${entry3.role}`);
41912
+ console.error(`${TAG73} file-delivery reject reason=no-bot-token sender=${entry3.senderId} role=${entry3.role}`);
41692
41913
  return { ok: false, error: "no-bot-token" };
41693
41914
  }
41694
41915
  if (entry3.replyTarget == null) {
41695
- console.error(`${TAG71} file-delivery reject reason=no-reply-target sender=${entry3.senderId} role=${entry3.role}`);
41916
+ console.error(`${TAG73} file-delivery reject reason=no-reply-target sender=${entry3.senderId} role=${entry3.role}`);
41696
41917
  return { ok: false, error: "no-reply-target" };
41697
41918
  }
41698
41919
  const result = await sendTelegramDocument({
@@ -41709,7 +41930,7 @@ function makeTelegramSendFile(entry3, botToken) {
41709
41930
  function makeTelegramFileDelivery(entry3, botToken) {
41710
41931
  return makeFileDelivery({
41711
41932
  entry: entry3,
41712
- tag: TAG71,
41933
+ tag: TAG73,
41713
41934
  channel: "telegram",
41714
41935
  sendFile: makeTelegramSendFile(entry3, botToken)
41715
41936
  });
@@ -42120,7 +42341,7 @@ function adminFanTargets(p) {
42120
42341
 
42121
42342
  // app/lib/channel-pty-bridge/public-session-end-review.ts
42122
42343
  import { randomUUID as randomUUID14 } from "crypto";
42123
- var TAG72 = "[public-session-review]";
42344
+ var TAG74 = "[public-session-review]";
42124
42345
  var CONSUMED_CAP = 500;
42125
42346
  var consumed = /* @__PURE__ */ new Set();
42126
42347
  function consumeOnce(sessionId) {
@@ -42147,7 +42368,7 @@ async function fetchJsonl(sessionId) {
42147
42368
  return await res.text();
42148
42369
  } catch (err) {
42149
42370
  console.error(
42150
- `${TAG72} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
42371
+ `${TAG74} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
42151
42372
  );
42152
42373
  return null;
42153
42374
  }
@@ -42171,7 +42392,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
42171
42392
  const res = await fetch(url);
42172
42393
  if (!res.ok) {
42173
42394
  console.error(
42174
- `${TAG72} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
42395
+ `${TAG74} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
42175
42396
  );
42176
42397
  return [];
42177
42398
  }
@@ -42179,7 +42400,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
42179
42400
  return Array.isArray(body.writes) ? body.writes : [];
42180
42401
  } catch (err) {
42181
42402
  console.error(
42182
- `${TAG72} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
42403
+ `${TAG74} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
42183
42404
  );
42184
42405
  return [];
42185
42406
  }
@@ -42208,7 +42429,7 @@ function composeInitialMessage(input) {
42208
42429
  }
42209
42430
  async function dispatchReviewer(input, initialMessage) {
42210
42431
  const sessionId = randomUUID14();
42211
- console.log(`${TAG72} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
42432
+ console.log(`${TAG74} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
42212
42433
  const spawned = await managerRcSpawn({
42213
42434
  sessionId,
42214
42435
  initialMessage,
@@ -42232,21 +42453,21 @@ async function firePublicSessionEndReview(input) {
42232
42453
  const dispatchedAt = Date.now();
42233
42454
  if (consumed.has(input.sessionId)) {
42234
42455
  console.log(
42235
- `${TAG72} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
42456
+ `${TAG74} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
42236
42457
  );
42237
42458
  return;
42238
42459
  }
42239
42460
  const jsonl = await fetchJsonl(input.sessionId);
42240
42461
  if (!jsonl) {
42241
42462
  console.log(
42242
- `${TAG72} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
42463
+ `${TAG74} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
42243
42464
  );
42244
42465
  return;
42245
42466
  }
42246
42467
  const operatorTurns = countOperatorTurns(jsonl);
42247
42468
  if (operatorTurns === 0) {
42248
42469
  console.log(
42249
- `${TAG72} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
42470
+ `${TAG74} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
42250
42471
  );
42251
42472
  return;
42252
42473
  }
@@ -42260,28 +42481,28 @@ async function firePublicSessionEndReview(input) {
42260
42481
  });
42261
42482
  if (!consumeOnce(input.sessionId)) {
42262
42483
  console.log(
42263
- `${TAG72} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
42484
+ `${TAG74} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
42264
42485
  );
42265
42486
  return;
42266
42487
  }
42267
42488
  const dispatched = await dispatchReviewer(input, initialMessage);
42268
42489
  if (!dispatched.ok) {
42269
42490
  console.error(
42270
- `${TAG72} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
42491
+ `${TAG74} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
42271
42492
  );
42272
42493
  return;
42273
42494
  }
42274
42495
  console.log(
42275
- `${TAG72} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
42496
+ `${TAG74} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
42276
42497
  );
42277
42498
  }
42278
42499
 
42279
42500
  // app/lib/whatsapp/inbound/resolve-client-graph-owner.ts
42280
- var import_dist17 = __toESM(require_dist3(), 1);
42501
+ var import_dist18 = __toESM(require_dist3(), 1);
42281
42502
  async function resolveClientOwnerUserId(accountId, deps = {}) {
42282
42503
  const listAccounts = deps.listAccounts ?? listValidAccounts;
42283
42504
  const getSession3 = deps.getSession ?? getSession;
42284
- const resolveOwner2 = deps.resolveOwner ?? import_dist17.resolveOwnerUserId;
42505
+ const resolveOwner2 = deps.resolveOwner ?? import_dist18.resolveOwnerUserId;
42285
42506
  const role = listAccounts().find((a) => a.accountId === accountId)?.config.role;
42286
42507
  if (role !== "client") return null;
42287
42508
  const session = getSession3();
@@ -42541,10 +42762,10 @@ function* walkTelegramSidecars() {
42541
42762
  }
42542
42763
 
42543
42764
  // server/telegram-group-census.ts
42544
- var TAG73 = "[telegram-audit]";
42765
+ var TAG75 = "[telegram-audit]";
42545
42766
  var PRESENT_STATUSES = /* @__PURE__ */ new Set(["member", "administrator", "creator"]);
42546
42767
  function buildGroupCensusLine(c) {
42547
- return `${TAG73} op=group-census bots=${c.bots} groups=${c.groups} present=${c.present} absent=${c.absent} privacyOff=${c.privacyOff}`;
42768
+ return `${TAG75} op=group-census bots=${c.bots} groups=${c.groups} present=${c.present} absent=${c.absent} privacyOff=${c.privacyOff}`;
42548
42769
  }
42549
42770
  async function runTelegramGroupCensus(accounts, deps) {
42550
42771
  const counts = { bots: 0, groups: 0, present: 0, absent: 0, privacyOff: 0 };
@@ -42575,10 +42796,10 @@ async function runTelegramGroupCensus(accounts, deps) {
42575
42796
  }
42576
42797
 
42577
42798
  // server/telegram-channel-census.ts
42578
- var TAG74 = "[telegram-audit]";
42799
+ var TAG76 = "[telegram-audit]";
42579
42800
  var ADMIN_STATUSES = /* @__PURE__ */ new Set(["administrator", "creator"]);
42580
42801
  function buildChannelCensusLine(c) {
42581
- return `${TAG74} op=channel-census bots=${c.bots} channels=${c.channels} admin=${c.admin} notAdmin=${c.notAdmin} noPostRight=${c.noPostRight}`;
42802
+ return `${TAG76} op=channel-census bots=${c.bots} channels=${c.channels} admin=${c.admin} notAdmin=${c.notAdmin} noPostRight=${c.noPostRight}`;
42582
42803
  }
42583
42804
  async function runTelegramChannelCensus(accounts, deps) {
42584
42805
  const counts = { bots: 0, channels: 0, admin: 0, notAdmin: 0, noPostRight: 0 };
@@ -42609,6 +42830,41 @@ async function runTelegramChannelCensus(accounts, deps) {
42609
42830
  return [buildChannelCensusLine(counts)];
42610
42831
  }
42611
42832
 
42833
+ // server/telegram-identity-census.ts
42834
+ var TAG77 = "[telegram-audit]";
42835
+ function buildIdentityCensusLine(c) {
42836
+ return `${TAG77} op=identity-census bots=${c.bots} ids=${c.ids} bound=${c.bound} unbound=${c.unbound} ambiguous=${c.ambiguous}`;
42837
+ }
42838
+ async function runTelegramIdentityCensus(accounts, deps) {
42839
+ const counts = { bots: 0, ids: 0, bound: 0, unbound: 0, ambiguous: 0 };
42840
+ for (const account of accounts) {
42841
+ const adminEntries = listBotEntries(account.telegram).filter((e) => e.role === "admin");
42842
+ if (adminEntries.length === 0) continue;
42843
+ counts.bots += adminEntries.length;
42844
+ const ids = adminEntries.flatMap((e) => e.adminUsers ?? []);
42845
+ counts.ids += ids.length;
42846
+ if (ids.length === 0) continue;
42847
+ let found;
42848
+ try {
42849
+ found = await deps.boundIds(account.accountId, [...new Set(ids)]);
42850
+ } catch {
42851
+ counts.unbound += ids.length;
42852
+ continue;
42853
+ }
42854
+ for (const id of ids) {
42855
+ const n = found.get(id) ?? 0;
42856
+ if (n === 1) counts.bound += 1;
42857
+ else if (n > 1) counts.ambiguous += 1;
42858
+ else counts.unbound += 1;
42859
+ }
42860
+ }
42861
+ if (counts.bots === 0) return [];
42862
+ return [buildIdentityCensusLine(counts)];
42863
+ }
42864
+
42865
+ // server/index.ts
42866
+ var import_dist20 = __toESM(require_dist5(), 1);
42867
+
42612
42868
  // app/lib/telegram/gateway/origin-target.ts
42613
42869
  function answeringOriginTarget(send, key2, noteAnswered3) {
42614
42870
  if (!send) return null;
@@ -43282,8 +43538,8 @@ var webchatFileFollowers = /* @__PURE__ */ new Map();
43282
43538
  async function fetchAccountStandingRules(accountId) {
43283
43539
  const session = getSession();
43284
43540
  try {
43285
- const res = await (0, import_dist18.resolveActiveRules)(session, accountId);
43286
- return { block: (0, import_dist18.formatStandingRulesBlock)(res.rules), ownerUserId: res.ownerUserId, source: res.source };
43541
+ const res = await (0, import_dist19.resolveActiveRules)(session, accountId);
43542
+ return { block: (0, import_dist19.formatStandingRulesBlock)(res.rules), ownerUserId: res.ownerUserId, source: res.source };
43287
43543
  } catch (err) {
43288
43544
  console.error(
43289
43545
  `[preference-inject] op=fetch-failed accountId=${accountId} error=${err instanceof Error ? err.message : String(err)}`
@@ -44204,6 +44460,9 @@ app80.route("/api/webchat", createWebchatRoutes({
44204
44460
  // Task 940 — the permission relay's pointer read + verdict write.
44205
44461
  pendingPromptFor: (key2) => webchatGateway.pendingPromptFor(key2),
44206
44462
  deliveryFailureFor: (key2) => webchatGateway.deliveryFailureFor(key2),
44463
+ // Task 2439 — the deferred send continuation's only way to report that a
44464
+ // staged upload will never produce a turn.
44465
+ markDeliveryFailure: (key2, messageId) => webchatGateway.markDeliveryFailure(key2, messageId),
44207
44466
  resolvePermissionVerdict: (key2, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key2, requestId, behavior)
44208
44467
  }));
44209
44468
  app80.route("/api/webchat/greeting", webchat_greeting_default);
@@ -44909,6 +45168,31 @@ registerLoop({
44909
45168
  `[telegram-native] op=telegram-spawn-census-rejected detail=${err instanceof Error ? err.message : String(err)}`
44910
45169
  );
44911
45170
  }
45171
+ try {
45172
+ for (const line of await runTelegramIdentityCensus(accounts, {
45173
+ boundIds: async (accountId, ids) => {
45174
+ const session = getSession();
45175
+ try {
45176
+ const result = await session.run(
45177
+ `MATCH (p:Person {accountId: $accountId})
45178
+ WHERE p.telegramUserId IN $ids AND ${(0, import_dist20.notTrashed)("p")}
45179
+ RETURN p.telegramUserId AS id, count(*) AS n`,
45180
+ { accountId, ids },
45181
+ { timeout: 5e3 }
45182
+ );
45183
+ return new Map(result.records.map((r) => [Number(r.get("id")), Number(r.get("n"))]));
45184
+ } finally {
45185
+ await session.close();
45186
+ }
45187
+ }
45188
+ })) {
45189
+ console.error(line);
45190
+ }
45191
+ } catch (err) {
45192
+ console.error(
45193
+ `[telegram-native] op=identity-census-rejected detail=${err instanceof Error ? err.message : String(err)}`
45194
+ );
45195
+ }
44912
45196
  };
44913
45197
  registerLoop({
44914
45198
  name: "telegram-audit",