@rubytech/create-sitedesk-code 0.1.608 → 0.1.609

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 (241) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.js +86 -0
  3. package/payload/platform/lib/graph-write/dist/__tests__/account-id-gate.test.js.map +1 -1
  4. package/payload/platform/lib/graph-write/dist/index.d.ts +30 -9
  5. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/graph-write/dist/index.js +22 -2
  7. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  8. package/payload/platform/lib/graph-write/src/__tests__/account-id-gate.test.ts +99 -1
  9. package/payload/platform/lib/graph-write/src/index.ts +47 -12
  10. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +45 -1
  11. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +8 -0
  12. package/payload/platform/plugins/docs/references/telegram-guide.md +44 -0
  13. package/payload/platform/plugins/memory/mcp/dist/index.js +65 -0
  14. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  15. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/xacct-counters.test.d.ts +2 -0
  16. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/xacct-counters.test.d.ts.map +1 -0
  17. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/xacct-counters.test.js +169 -0
  18. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/xacct-counters.test.js.map +1 -0
  19. package/payload/platform/plugins/memory/mcp/dist/lib/xacct-counters.d.ts +63 -0
  20. package/payload/platform/plugins/memory/mcp/dist/lib/xacct-counters.d.ts.map +1 -0
  21. package/payload/platform/plugins/memory/mcp/dist/lib/xacct-counters.js +114 -0
  22. package/payload/platform/plugins/memory/mcp/dist/lib/xacct-counters.js.map +1 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-cross-account.test.d.ts +2 -0
  24. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-cross-account.test.d.ts.map +1 -0
  25. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-cross-account.test.js +123 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-cross-account.test.js.map +1 -0
  27. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/xacct-write-gate.test.d.ts +2 -0
  28. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/xacct-write-gate.test.d.ts.map +1 -0
  29. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/xacct-write-gate.test.js +110 -0
  30. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/xacct-write-gate.test.js.map +1 -0
  31. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +7 -0
  33. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  34. package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
  35. package/payload/platform/services/claude-session-manager/dist/claude-trust-preflight.d.ts +43 -0
  36. package/payload/platform/services/claude-session-manager/dist/claude-trust-preflight.d.ts.map +1 -0
  37. package/payload/platform/services/claude-session-manager/dist/claude-trust-preflight.js +88 -0
  38. package/payload/platform/services/claude-session-manager/dist/claude-trust-preflight.js.map +1 -0
  39. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  40. package/payload/platform/services/claude-session-manager/dist/http-server.js +125 -4
  41. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  42. package/payload/platform/services/claude-session-manager/dist/index.js +26 -1
  43. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  44. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +1 -1
  45. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
  46. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +26 -0
  47. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
  48. package/payload/platform/services/claude-session-manager/dist/recover-account.d.ts +54 -0
  49. package/payload/platform/services/claude-session-manager/dist/recover-account.d.ts.map +1 -0
  50. package/payload/platform/services/claude-session-manager/dist/recover-account.js +141 -0
  51. package/payload/platform/services/claude-session-manager/dist/recover-account.js.map +1 -0
  52. package/payload/platform/services/claude-session-manager/dist/session-account-census.d.ts +30 -0
  53. package/payload/platform/services/claude-session-manager/dist/session-account-census.d.ts.map +1 -0
  54. package/payload/platform/services/claude-session-manager/dist/session-account-census.js +96 -0
  55. package/payload/platform/services/claude-session-manager/dist/session-account-census.js.map +1 -0
  56. package/payload/platform/services/telegram-channel/dist/instructions.d.ts +9 -0
  57. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
  58. package/payload/platform/services/telegram-channel/dist/instructions.js +15 -0
  59. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
  60. package/payload/platform/services/telegram-channel/dist/notification.d.ts +6 -4
  61. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  62. package/payload/platform/services/telegram-channel/dist/notification.js +15 -1
  63. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  64. package/payload/server/{chunk-GI5AVCRG.js → chunk-DRDU74ZM.js} +1 -1
  65. package/payload/server/{chunk-MNN2RWJR.js → chunk-IOPUA7QV.js} +1 -1
  66. package/payload/server/{chunk-6AQYMT6C.js → chunk-T6NXRWMG.js} +43 -8
  67. package/payload/server/{manager-VDHT3YYI.js → manager-O5L2SKW7.js} +2 -2
  68. package/payload/server/maxy-edge.js +2 -2
  69. package/payload/server/public/activity.html +6 -6
  70. package/payload/server/public/agents.html +5 -5
  71. package/payload/server/public/assets/{AdminLoginScreens-DuQ3ax5U.js → AdminLoginScreens-BBKEIEjY.js} +1 -1
  72. package/payload/server/public/assets/AdminLoginScreens-BBKEIEjY.js.br +0 -0
  73. package/payload/server/public/assets/AdminLoginScreens-BBKEIEjY.js.gz +0 -0
  74. package/payload/server/public/assets/{AdminShell-1HyATV_t.js → AdminShell-CrCdEMNW.js} +1 -1
  75. package/payload/server/public/assets/AdminShell-CrCdEMNW.js.br +0 -0
  76. package/payload/server/public/assets/AdminShell-CrCdEMNW.js.gz +0 -0
  77. package/payload/server/public/assets/{activity-C2qWlcJ5.js → activity-CbR3vaq1.js} +1 -1
  78. package/payload/server/public/assets/activity-CbR3vaq1.js.br +0 -0
  79. package/payload/server/public/assets/activity-CbR3vaq1.js.gz +0 -0
  80. package/payload/server/public/assets/{admin-DqpfY8-t.js → admin-D8qKnmhE.js} +1 -1
  81. package/payload/server/public/assets/admin-D8qKnmhE.js.br +0 -0
  82. package/payload/server/public/assets/admin-D8qKnmhE.js.gz +0 -0
  83. package/payload/server/public/assets/{agents-DoGVAjIT.js → agents-8gAQjRqI.js} +1 -1
  84. package/payload/server/public/assets/agents-8gAQjRqI.js.br +0 -0
  85. package/payload/server/public/assets/agents-8gAQjRqI.js.gz +0 -0
  86. package/payload/server/public/assets/{browser-DjqS897k.js → browser-jAje5ILy.js} +1 -1
  87. package/payload/server/public/assets/browser-jAje5ILy.js.br +0 -0
  88. package/payload/server/public/assets/browser-jAje5ILy.js.gz +0 -0
  89. package/payload/server/public/assets/{calendar-DJeOm0WS.js → calendar-xEmCJif9.js} +1 -1
  90. package/payload/server/public/assets/calendar-xEmCJif9.js.br +0 -0
  91. package/payload/server/public/assets/calendar-xEmCJif9.js.gz +0 -0
  92. package/payload/server/public/assets/chat-Ct5RDmv6.js +1 -0
  93. package/payload/server/public/assets/chat-Ct5RDmv6.js.br +0 -0
  94. package/payload/server/public/assets/chat-Ct5RDmv6.js.gz +0 -0
  95. package/payload/server/public/assets/{chevron-left-D9z1aK3p.js → chevron-left-DV8gIVC9.js} +1 -1
  96. package/payload/server/public/assets/chevron-left-DV8gIVC9.js.br +0 -0
  97. package/payload/server/public/assets/clock-PnR4Tlg9.js +1 -0
  98. package/payload/server/public/assets/clock-PnR4Tlg9.js.br +0 -0
  99. package/payload/server/public/assets/{copy-p9ZM9fnn.js → copy-b6MpzRz9.js} +1 -1
  100. package/payload/server/public/assets/copy-b6MpzRz9.js.br +0 -0
  101. package/payload/server/public/assets/copy-b6MpzRz9.js.gz +0 -0
  102. package/payload/server/public/assets/data-BxFc9wPl.js +1 -0
  103. package/payload/server/public/assets/data-BxFc9wPl.js.br +2 -0
  104. package/payload/server/public/assets/data-BxFc9wPl.js.gz +0 -0
  105. package/payload/server/public/assets/{field-WRk67lvr.js → field-CiIsn-47.js} +1 -1
  106. package/payload/server/public/assets/field-CiIsn-47.js.br +0 -0
  107. package/payload/server/public/assets/field-CiIsn-47.js.gz +0 -0
  108. package/payload/server/public/assets/{file-text-BJDCb334.js → file-text-Cy-IjCf8.js} +1 -1
  109. package/payload/server/public/assets/file-text-Cy-IjCf8.js.br +0 -0
  110. package/payload/server/public/assets/file-text-Cy-IjCf8.js.gz +0 -0
  111. package/payload/server/public/assets/{graph-CAU2xh2Z.js → graph-BdC2IGOT.js} +1 -1
  112. package/payload/server/public/assets/graph-BdC2IGOT.js.br +0 -0
  113. package/payload/server/public/assets/graph-BdC2IGOT.js.gz +0 -0
  114. package/payload/server/public/assets/{graph-labels-CEDmX7qW.js → graph-labels-3bstML8n.js} +1 -1
  115. package/payload/server/public/assets/graph-labels-3bstML8n.js.br +0 -0
  116. package/payload/server/public/assets/graph-labels-3bstML8n.js.gz +0 -0
  117. package/payload/server/public/assets/{jsx-runtime-hPFGo4rC.css → jsx-runtime-JxxB_2S6.css} +1 -1
  118. package/payload/server/public/assets/jsx-runtime-JxxB_2S6.css.br +0 -0
  119. package/payload/server/public/assets/{jsx-runtime-hPFGo4rC.css.gz → jsx-runtime-JxxB_2S6.css.gz} +0 -0
  120. package/payload/server/public/assets/{operator-1WFztEQA.js → operator-D7L2SA16.js} +1 -1
  121. package/payload/server/public/assets/operator-D7L2SA16.js.br +0 -0
  122. package/payload/server/public/assets/operator-D7L2SA16.js.gz +0 -0
  123. package/payload/server/public/assets/{page-B72XigcU.js → page-DspGbDgK.js} +1 -1
  124. package/payload/server/public/assets/page-DspGbDgK.js.br +0 -0
  125. package/payload/server/public/assets/page-DspGbDgK.js.gz +0 -0
  126. package/payload/server/public/assets/{page-nT8l_cX1.js → page-ObXxk8ol.js} +1 -1
  127. package/payload/server/public/assets/page-ObXxk8ol.js.br +0 -0
  128. package/payload/server/public/assets/page-ObXxk8ol.js.gz +0 -0
  129. package/payload/server/public/assets/play-BL4ztD3m.js +1 -0
  130. package/payload/server/public/assets/play-BL4ztD3m.js.br +0 -0
  131. package/payload/server/public/assets/play-BL4ztD3m.js.gz +0 -0
  132. package/payload/server/public/assets/{public-DAea6w7w.js → public-CEXKl8wg.js} +1 -1
  133. package/payload/server/public/assets/public-CEXKl8wg.js.br +0 -0
  134. package/payload/server/public/assets/public-CEXKl8wg.js.gz +0 -0
  135. package/payload/server/public/assets/{rotate-ccw-CqlRlGsY.js → rotate-ccw-ckXDebK2.js} +1 -1
  136. package/payload/server/public/assets/rotate-ccw-ckXDebK2.js.br +0 -0
  137. package/payload/server/public/assets/rotate-ccw-ckXDebK2.js.gz +0 -0
  138. package/payload/server/public/assets/{routines-vy5xsyqx.js → routines-Dwc2meN2.js} +1 -1
  139. package/payload/server/public/assets/routines-Dwc2meN2.js.br +0 -0
  140. package/payload/server/public/assets/routines-Dwc2meN2.js.gz +0 -0
  141. package/payload/server/public/assets/{skills-X7FTilVy.js → skills-DdHKHl4Z.js} +1 -1
  142. package/payload/server/public/assets/skills-DdHKHl4Z.js.br +0 -0
  143. package/payload/server/public/assets/skills-DdHKHl4Z.js.gz +0 -0
  144. package/payload/server/public/assets/{tasks-BXx_jk_p.js → tasks-C_yWRvOG.js} +1 -1
  145. package/payload/server/public/assets/tasks-C_yWRvOG.js.br +0 -0
  146. package/payload/server/public/assets/tasks-C_yWRvOG.js.gz +0 -0
  147. package/payload/server/public/assets/{triangle-alert-CuWcGaRo.js → triangle-alert-C-7A00P0.js} +1 -1
  148. package/payload/server/public/assets/triangle-alert-C-7A00P0.js.br +0 -0
  149. package/payload/server/public/assets/triangle-alert-C-7A00P0.js.gz +0 -0
  150. package/payload/server/public/assets/{useCopyFeedback-DLSKs6r2.js → useCopyFeedback-CWvSi-Qe.js} +1 -1
  151. package/payload/server/public/assets/useCopyFeedback-CWvSi-Qe.js.br +2 -0
  152. package/payload/server/public/assets/useCopyFeedback-CWvSi-Qe.js.gz +0 -0
  153. package/payload/server/public/assets/{useMediaQuery-C_1CV_iW.js → useMediaQuery-CLVRXJMp.js} +1 -1
  154. package/payload/server/public/assets/useMediaQuery-CLVRXJMp.js.br +0 -0
  155. package/payload/server/public/assets/useMediaQuery-CLVRXJMp.js.gz +0 -0
  156. package/payload/server/public/assets/{useVoiceRecorder-DBzM_HIx.js → useVoiceRecorder-BbJad9CQ.js} +1 -1
  157. package/payload/server/public/assets/useVoiceRecorder-BbJad9CQ.js.br +0 -0
  158. package/payload/server/public/assets/useVoiceRecorder-BbJad9CQ.js.gz +0 -0
  159. package/payload/server/public/assets/{wrench-nDMQ6x-6.js → wrench-Cl6TUm2T.js} +1 -1
  160. package/payload/server/public/assets/wrench-Cl6TUm2T.js.br +0 -0
  161. package/payload/server/public/assets/wrench-Cl6TUm2T.js.gz +0 -0
  162. package/payload/server/public/browser.html +5 -5
  163. package/payload/server/public/calendar.html +8 -8
  164. package/payload/server/public/chat.html +14 -14
  165. package/payload/server/public/data.html +12 -12
  166. package/payload/server/public/field.html +3 -3
  167. package/payload/server/public/graph.html +10 -10
  168. package/payload/server/public/index.html +14 -14
  169. package/payload/server/public/operator.html +16 -16
  170. package/payload/server/public/public.html +14 -14
  171. package/payload/server/public/routines.html +8 -8
  172. package/payload/server/public/skills.html +6 -6
  173. package/payload/server/public/tasks.html +7 -7
  174. package/payload/server/server.js +511 -283
  175. package/payload/server/public/assets/AdminLoginScreens-DuQ3ax5U.js.br +0 -0
  176. package/payload/server/public/assets/AdminLoginScreens-DuQ3ax5U.js.gz +0 -0
  177. package/payload/server/public/assets/AdminShell-1HyATV_t.js.br +0 -0
  178. package/payload/server/public/assets/AdminShell-1HyATV_t.js.gz +0 -0
  179. package/payload/server/public/assets/activity-C2qWlcJ5.js.br +0 -0
  180. package/payload/server/public/assets/activity-C2qWlcJ5.js.gz +0 -0
  181. package/payload/server/public/assets/admin-DqpfY8-t.js.br +0 -0
  182. package/payload/server/public/assets/admin-DqpfY8-t.js.gz +0 -0
  183. package/payload/server/public/assets/agents-DoGVAjIT.js.br +0 -0
  184. package/payload/server/public/assets/agents-DoGVAjIT.js.gz +0 -0
  185. package/payload/server/public/assets/browser-DjqS897k.js.br +0 -0
  186. package/payload/server/public/assets/browser-DjqS897k.js.gz +0 -0
  187. package/payload/server/public/assets/calendar-DJeOm0WS.js.br +0 -0
  188. package/payload/server/public/assets/calendar-DJeOm0WS.js.gz +0 -0
  189. package/payload/server/public/assets/chat-BrRE0Dck.js +0 -1
  190. package/payload/server/public/assets/chat-BrRE0Dck.js.br +0 -0
  191. package/payload/server/public/assets/chat-BrRE0Dck.js.gz +0 -0
  192. package/payload/server/public/assets/chevron-left-D9z1aK3p.js.br +0 -0
  193. package/payload/server/public/assets/clock-BqHWMICF.js +0 -1
  194. package/payload/server/public/assets/clock-BqHWMICF.js.br +0 -0
  195. package/payload/server/public/assets/clock-BqHWMICF.js.gz +0 -0
  196. package/payload/server/public/assets/copy-p9ZM9fnn.js.br +0 -0
  197. package/payload/server/public/assets/copy-p9ZM9fnn.js.gz +0 -0
  198. package/payload/server/public/assets/data-CPsqW0qW.js +0 -1
  199. package/payload/server/public/assets/data-CPsqW0qW.js.br +0 -0
  200. package/payload/server/public/assets/data-CPsqW0qW.js.gz +0 -0
  201. package/payload/server/public/assets/field-WRk67lvr.js.br +0 -0
  202. package/payload/server/public/assets/field-WRk67lvr.js.gz +0 -0
  203. package/payload/server/public/assets/file-text-BJDCb334.js.br +0 -0
  204. package/payload/server/public/assets/file-text-BJDCb334.js.gz +0 -0
  205. package/payload/server/public/assets/graph-CAU2xh2Z.js.br +0 -0
  206. package/payload/server/public/assets/graph-CAU2xh2Z.js.gz +0 -0
  207. package/payload/server/public/assets/graph-labels-CEDmX7qW.js.br +0 -0
  208. package/payload/server/public/assets/graph-labels-CEDmX7qW.js.gz +0 -0
  209. package/payload/server/public/assets/jsx-runtime-hPFGo4rC.css.br +0 -0
  210. package/payload/server/public/assets/operator-1WFztEQA.js.br +0 -0
  211. package/payload/server/public/assets/operator-1WFztEQA.js.gz +0 -0
  212. package/payload/server/public/assets/page-B72XigcU.js.br +0 -0
  213. package/payload/server/public/assets/page-B72XigcU.js.gz +0 -0
  214. package/payload/server/public/assets/page-nT8l_cX1.js.br +0 -0
  215. package/payload/server/public/assets/page-nT8l_cX1.js.gz +0 -0
  216. package/payload/server/public/assets/play-CYqOCqm6.js +0 -1
  217. package/payload/server/public/assets/play-CYqOCqm6.js.br +0 -0
  218. package/payload/server/public/assets/play-CYqOCqm6.js.gz +0 -0
  219. package/payload/server/public/assets/public-DAea6w7w.js.br +0 -0
  220. package/payload/server/public/assets/public-DAea6w7w.js.gz +0 -0
  221. package/payload/server/public/assets/rotate-ccw-CqlRlGsY.js.br +0 -0
  222. package/payload/server/public/assets/rotate-ccw-CqlRlGsY.js.gz +0 -0
  223. package/payload/server/public/assets/routines-vy5xsyqx.js.br +0 -0
  224. package/payload/server/public/assets/routines-vy5xsyqx.js.gz +0 -0
  225. package/payload/server/public/assets/skills-X7FTilVy.js.br +0 -0
  226. package/payload/server/public/assets/skills-X7FTilVy.js.gz +0 -0
  227. package/payload/server/public/assets/tasks-BXx_jk_p.js.br +0 -0
  228. package/payload/server/public/assets/tasks-BXx_jk_p.js.gz +0 -0
  229. package/payload/server/public/assets/triangle-alert-CuWcGaRo.js.br +0 -0
  230. package/payload/server/public/assets/triangle-alert-CuWcGaRo.js.gz +0 -0
  231. package/payload/server/public/assets/useCopyFeedback-DLSKs6r2.js.br +0 -0
  232. package/payload/server/public/assets/useCopyFeedback-DLSKs6r2.js.gz +0 -0
  233. package/payload/server/public/assets/useMediaQuery-C_1CV_iW.js.br +0 -0
  234. package/payload/server/public/assets/useMediaQuery-C_1CV_iW.js.gz +0 -0
  235. package/payload/server/public/assets/useVoiceRecorder-DBzM_HIx.js.br +0 -0
  236. package/payload/server/public/assets/useVoiceRecorder-DBzM_HIx.js.gz +0 -0
  237. package/payload/server/public/assets/wrench-nDMQ6x-6.js.br +0 -0
  238. package/payload/server/public/assets/wrench-nDMQ6x-6.js.gz +0 -0
  239. /package/payload/server/public/assets/{jsx-runtime-CvVXOszc.js → jsx-runtime-BCAZhYqz.js} +0 -0
  240. /package/payload/server/public/assets/{jsx-runtime-CvVXOszc.js.br → jsx-runtime-BCAZhYqz.js.br} +0 -0
  241. /package/payload/server/public/assets/{jsx-runtime-CvVXOszc.js.gz → jsx-runtime-BCAZhYqz.js.gz} +0 -0
@@ -70,7 +70,7 @@ import {
70
70
  verifyRemotePassword,
71
71
  vncLog,
72
72
  walkPremiumBundles
73
- } from "./chunk-MNN2RWJR.js";
73
+ } from "./chunk-IOPUA7QV.js";
74
74
  import {
75
75
  audioExtension,
76
76
  isAudioMime,
@@ -220,7 +220,7 @@ import {
220
220
  waitForConnection,
221
221
  warnNonUuidAccount,
222
222
  writeChatMetadataMerged
223
- } from "./chunk-GI5AVCRG.js";
223
+ } from "./chunk-DRDU74ZM.js";
224
224
  import {
225
225
  ACCOUNTS_DIR,
226
226
  AGENT_SLUG_PATTERN,
@@ -243,6 +243,7 @@ import {
243
243
  VNC_DISPLAY,
244
244
  WEBSOCKIFY_PORT,
245
245
  armedCount,
246
+ censusAccountDirs,
246
247
  createNewAdminConversation,
247
248
  deleteAgentProjection,
248
249
  deleteConversation,
@@ -285,7 +286,7 @@ import {
285
286
  verifyAndGetConversationUpdatedAt,
286
287
  verifyConversationOwnership,
287
288
  writeAdminUserAndPerson
288
- } from "./chunk-6AQYMT6C.js";
289
+ } from "./chunk-T6NXRWMG.js";
289
290
  import {
290
291
  extractVideo,
291
292
  transcribe
@@ -361,15 +362,15 @@ var require_dist2 = __commonJS({
361
362
  return out;
362
363
  }
363
364
  function scanStore(storeDir) {
364
- const newest = /* @__PURE__ */ new Map();
365
+ const newest2 = /* @__PURE__ */ new Map();
365
366
  let storeRows = 0;
366
367
  if (!storeDir || !(0, node_fs_1.existsSync)(storeDir))
367
- return { newest, storeRows };
368
+ return { newest: newest2, storeRows };
368
369
  let entries2;
369
370
  try {
370
371
  entries2 = (0, node_fs_1.readdirSync)(storeDir);
371
372
  } catch {
372
- return { newest, storeRows };
373
+ return { newest: newest2, storeRows };
373
374
  }
374
375
  for (const entry3 of entries2) {
375
376
  if (!entry3.endsWith(".jsonl"))
@@ -401,10 +402,10 @@ var require_dist2 = __commonJS({
401
402
  if (chatId !== senderId)
402
403
  continue;
403
404
  const key2 = `${botId}:${senderId}`;
404
- const prev = newest.get(key2);
405
+ const prev = newest2.get(key2);
405
406
  if (prev !== void 0 && prev.dateSent.localeCompare(dateSent) >= 0)
406
407
  continue;
407
- newest.set(key2, {
408
+ newest2.set(key2, {
408
409
  chatId,
409
410
  senderId,
410
411
  senderName: typeof row.senderName === "string" ? row.senderName : null,
@@ -412,16 +413,16 @@ var require_dist2 = __commonJS({
412
413
  });
413
414
  }
414
415
  }
415
- return { newest, storeRows };
416
+ return { newest: newest2, storeRows };
416
417
  }
417
418
  function collectTelegramReach2(input) {
418
- const { newest, storeRows } = scanStore(input.storeDir);
419
+ const { newest: newest2, storeRows } = scanStore(input.storeDir);
419
420
  const bots = input.bots.map((bot) => {
420
421
  const adminUsers = bot.adminUsers ?? [];
421
422
  const adminSet = new Set(adminUsers.map((u) => String(u)));
422
423
  const chats = [];
423
424
  const seen = /* @__PURE__ */ new Set();
424
- for (const [key2, row] of newest) {
425
+ for (const [key2, row] of newest2) {
425
426
  if (key2.slice(0, key2.indexOf(":")) !== bot.id)
426
427
  continue;
427
428
  seen.add(row.senderId);
@@ -2923,7 +2924,8 @@ var require_dist10 = __commonJS({
2923
2924
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
2924
2925
  };
2925
2926
  Object.defineProperty(exports, "__esModule", { value: true });
2926
- exports.DOCUMENT_ARCHIVE_LABELS = exports.VECTOR_INDEXED_LABELS = exports.PROVENANCE_SOURCE_LABELS = exports.ACTION_PROVENANCE_LABELS = void 0;
2927
+ exports.INVALID_ACCOUNT_ID_REASON = exports.DOCUMENT_ARCHIVE_LABELS = exports.VECTOR_INDEXED_LABELS = exports.PROVENANCE_SOURCE_LABELS = exports.ACTION_PROVENANCE_LABELS = void 0;
2928
+ exports.isInvalidAccountIdError = isInvalidAccountIdError;
2927
2929
  exports.stampCreatedBy = stampCreatedBy;
2928
2930
  exports.writeNodeWithEdges = writeNodeWithEdges;
2929
2931
  __exportStar(require_audit(), exports);
@@ -3027,6 +3029,15 @@ var require_dist10 = __commonJS({
3027
3029
  function findProvenanceCandidates(relationships) {
3028
3030
  return relationships.filter((r) => r.type === "PRODUCED" && r.direction === "incoming");
3029
3031
  }
3032
+ exports.INVALID_ACCOUNT_ID_REASON = "invalid-account-id";
3033
+ function invalidAccountIdError(slice) {
3034
+ const err = new Error(`Write doctrine violated: ${exports.INVALID_ACCOUNT_ID_REASON} (${slice}) \u2014 accountId must equal the account this write is authorised for: \`expectedAccountId\` when the caller resolves authority per call, otherwise the ACCOUNT_ID set by the spawning process. See .docs/neo4j.md "Account isolation invariant".`);
3035
+ err.reason = exports.INVALID_ACCOUNT_ID_REASON;
3036
+ return err;
3037
+ }
3038
+ function isInvalidAccountIdError(err) {
3039
+ return err instanceof Error && err.reason === exports.INVALID_ACCOUNT_ID_REASON;
3040
+ }
3030
3041
  var MERGE_CREATE_ONLY_PROPS = /* @__PURE__ */ new Set([
3031
3042
  "createdAt",
3032
3043
  "createdByAgent",
@@ -3055,7 +3066,7 @@ var require_dist10 = __commonJS({
3055
3066
  const slice = typeof accountId === "string" ? accountId.slice(0, 8) : "missing";
3056
3067
  process.stderr.write(`[graph-write] reject reason=invalid-account-id accountId=${slice} writer=${agentLabel}
3057
3068
  `);
3058
- throw new Error(`Write doctrine violated: invalid-account-id (${slice}) \u2014 accountId must equal ACCOUNT_ID set by the spawning process. See .docs/neo4j.md "Account isolation invariant".`);
3069
+ throw invalidAccountIdError(slice);
3059
3070
  }
3060
3071
  }
3061
3072
  const reviewDigestActionTool = typeof props.actionTool === "string" && props.actionTool === "review-digest-compose";
@@ -3459,15 +3470,15 @@ function readRows(accountId, channelKey) {
3459
3470
  }
3460
3471
  }
3461
3472
  function newestInboundTelegramDate(accountId, channelKey) {
3462
- let newest = null;
3473
+ let newest2 = null;
3463
3474
  for (const row of readRows(accountId, channelKey)) {
3464
3475
  if (isUpdateRecord(row)) continue;
3465
3476
  if (row.fromMe) continue;
3466
3477
  const d = row.telegramDate;
3467
3478
  if (typeof d !== "number" || !Number.isFinite(d)) continue;
3468
- if (newest === null || d > newest) newest = d;
3479
+ if (newest2 === null || d > newest2) newest2 = d;
3469
3480
  }
3470
- return newest;
3481
+ return newest2;
3471
3482
  }
3472
3483
  function readConversation2(accountId, channelKey) {
3473
3484
  if (!(0, import_dist2.isStorableChannelKey)(channelKey)) return [];
@@ -3516,9 +3527,9 @@ function findPollConversation(accountId, pollId) {
3516
3527
  }
3517
3528
  var summaryCache = /* @__PURE__ */ new Map();
3518
3529
  function summariseRecords(rows) {
3519
- const records4 = messageRows(rows);
3520
- if (records4.length === 0) return null;
3521
- const sorted = records4.slice().sort(byDateSentAsc);
3530
+ const records5 = messageRows(rows);
3531
+ if (records5.length === 0) return null;
3532
+ const sorted = records5.slice().sort(byDateSentAsc);
3522
3533
  return { first: sorted[0], last: sorted[sorted.length - 1] };
3523
3534
  }
3524
3535
  function readConversationSummaries2(accountId) {
@@ -4078,9 +4089,9 @@ var idCache = /* @__PURE__ */ new Map();
4078
4089
  function persistedMsgKeyIds() {
4079
4090
  const ids = /* @__PURE__ */ new Set();
4080
4091
  for (const accountId of listStoredAccountIds()) {
4081
- for (const [remoteJid, records4] of readAllConversationsCached(accountId)) {
4092
+ for (const [remoteJid, records5] of readAllConversationsCached(accountId)) {
4082
4093
  void remoteJid;
4083
- for (const id of records4) ids.add(id);
4094
+ for (const id of records5) ids.add(id);
4084
4095
  }
4085
4096
  }
4086
4097
  return ids;
@@ -4579,9 +4590,9 @@ function isModelGated(turns) {
4579
4590
  return turns.some((t) => t.kind === "agent-error" && t.code === "model_unavailable");
4580
4591
  }
4581
4592
  function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, uuid, census) {
4582
- const armed2 = queuedPendingSuppress.get(u.text) ?? 0;
4583
- if (armed2 > 0) {
4584
- queuedPendingSuppress.set(u.text, armed2 - 1);
4593
+ const armed3 = queuedPendingSuppress.get(u.text) ?? 0;
4594
+ if (armed3 > 0) {
4595
+ queuedPendingSuppress.set(u.text, armed3 - 1);
4585
4596
  } else {
4586
4597
  out.push(operatorInboundTurn(u, ts, uuid, census));
4587
4598
  }
@@ -7948,7 +7959,7 @@ async function defaultHttpGet(url) {
7948
7959
  }
7949
7960
  }
7950
7961
  async function defaultResolve(accountId) {
7951
- const { resolveSocket: resolveSocket2 } = await import("./manager-VDHT3YYI.js");
7962
+ const { resolveSocket: resolveSocket2 } = await import("./manager-O5L2SKW7.js");
7952
7963
  return resolveSocket2(accountId);
7953
7964
  }
7954
7965
  async function fetchAvatar(accountId, jid, deps = {}) {
@@ -7999,15 +8010,15 @@ var CENSUS_TAG = "[wa-avatar-census]";
7999
8010
  var AVATAR_STORE_MAX_BYTES = 16 * 1024 * 1024;
8000
8011
  function liveJidsForAccount(accountId) {
8001
8012
  const live = /* @__PURE__ */ new Set();
8002
- for (const [remoteJid, records4] of readAllConversations(accountId)) {
8003
- for (const jid of avatarJidsForConversation(remoteJid, records4)) live.add(jid);
8013
+ for (const [remoteJid, records5] of readAllConversations(accountId)) {
8014
+ for (const jid of avatarJidsForConversation(remoteJid, records5)) live.add(jid);
8004
8015
  }
8005
8016
  return live;
8006
8017
  }
8007
- function avatarJidsForConversation(remoteJid, records4) {
8018
+ function avatarJidsForConversation(remoteJid, records5) {
8008
8019
  const jids = /* @__PURE__ */ new Set([remoteJid]);
8009
8020
  if (!isGroupJid(remoteJid)) return jids;
8010
- for (const r of records4) {
8021
+ for (const r of records5) {
8011
8022
  if (r.fromMe) continue;
8012
8023
  if (r.senderJid && isUserJid(r.senderJid)) jids.add(r.senderJid);
8013
8024
  }
@@ -8142,10 +8153,10 @@ function auditGroupOutbound() {
8142
8153
  let unstamped = 0;
8143
8154
  const offenders = [];
8144
8155
  for (const accountId of listStoredAccountIds()) {
8145
- for (const [remoteJid, records4] of readAllConversations(accountId)) {
8156
+ for (const [remoteJid, records5] of readAllConversations(accountId)) {
8146
8157
  if (!isGroupJid(remoteJid)) continue;
8147
8158
  convos++;
8148
- for (const r of records4) {
8159
+ for (const r of records5) {
8149
8160
  if (!r.fromMe) continue;
8150
8161
  outbound++;
8151
8162
  if (r.origin === "operator-manual") operator++;
@@ -14686,11 +14697,11 @@ function emitGauge() {
14686
14697
  counts[reg.channel]++;
14687
14698
  oldestMs = Math.max(oldestMs, now - reg.startedAt);
14688
14699
  }
14689
- const armed2 = counts.reader + counts.store;
14700
+ const armed3 = counts.reader + counts.store;
14690
14701
  console.log(
14691
- `[wa-stream] op=gauge armed=${armed2} reader=${counts.reader} store=${counts.store} oldestSec=${Math.floor(oldestMs / 1e3)} stale=${stale}`
14702
+ `[wa-stream] op=gauge armed=${armed3} reader=${counts.reader} store=${counts.store} oldestSec=${Math.floor(oldestMs / 1e3)} stale=${stale}`
14692
14703
  );
14693
- if (armed2 === 0) stopGauge();
14704
+ if (armed3 === 0) stopGauge();
14694
14705
  }
14695
14706
  function startGauge() {
14696
14707
  if (gaugeTimer) return;
@@ -14857,8 +14868,8 @@ function resolveReplyAppendAccount(args) {
14857
14868
  if (args.isGroup) return args.sendAccountId;
14858
14869
  return args.resolve(args.sendAccountId, args.counterpartyPhone) || args.sendAccountId;
14859
14870
  }
14860
- function warmConversationAvatars(accountId, remoteJid, records4) {
14861
- const jids = avatarJidsForConversation(remoteJid, records4);
14871
+ function warmConversationAvatars(accountId, remoteJid, records5) {
14872
+ const jids = avatarJidsForConversation(remoteJid, records5);
14862
14873
  console.log(`[wa-avatar] op=request account=${accountId.slice(0, 8)} convo=${remoteJid.slice(0, 12)} jids=${jids.size} reason=reader-open`);
14863
14874
  for (const jid of jids) {
14864
14875
  void fetchAvatar(accountId, jid).catch((e) => {
@@ -14930,7 +14941,7 @@ app5.get("/store-backfill", requireAdminSession, (c) => {
14930
14941
  return c.json({ turns: [], from: 0, reachedStart: true });
14931
14942
  }
14932
14943
  const { from, aligned } = backfillRangeStart(file, to, TAIL_WINDOW_BYTES);
14933
- const records4 = [];
14944
+ const records5 = [];
14934
14945
  const reactionRecords = [];
14935
14946
  let reactionScanBytes = 0;
14936
14947
  try {
@@ -14944,7 +14955,7 @@ app5.get("/store-backfill", requireAdminSession, (c) => {
14944
14955
  reactionRecords.push(parsed);
14945
14956
  continue;
14946
14957
  }
14947
- records4.push(parsed);
14958
+ records5.push(parsed);
14948
14959
  } catch {
14949
14960
  }
14950
14961
  }
@@ -14963,11 +14974,11 @@ app5.get("/store-backfill", requireAdminSession, (c) => {
14963
14974
  console.error(`[wa-reader] op=store-backfill remoteJid=${remoteJid} from=${from} to=${to} outcome=read-failed`);
14964
14975
  return c.json({ error: "read-failed" }, 500);
14965
14976
  }
14966
- records4.sort((a, b) => a.dateSent.localeCompare(b.dateSent));
14977
+ records5.sort((a, b) => a.dateSent.localeCompare(b.dateSent));
14967
14978
  const byMsgKeyId = /* @__PURE__ */ new Map();
14968
- for (const m of records4) byMsgKeyId.set(m.msgKeyId, m);
14979
+ for (const m of records5) byMsgKeyId.set(m.msgKeyId, m);
14969
14980
  const wanted = /* @__PURE__ */ new Set();
14970
- for (const m of records4) {
14981
+ for (const m of records5) {
14971
14982
  if (m.quotedId && !byMsgKeyId.has(m.quotedId)) wanted.add(m.quotedId);
14972
14983
  }
14973
14984
  const q2 = resolveQuoteTargets(file, from, wanted);
@@ -14983,7 +14994,7 @@ app5.get("/store-backfill", requireAdminSession, (c) => {
14983
14994
  const uploadsBase = resolve18(DATA_ROOT, "accounts", accountId, "uploads");
14984
14995
  const toTurn2 = makeToTurn({ byMsgKeyId, uploadsBase });
14985
14996
  const fold = foldReactions(reactionRecords);
14986
- const turns = records4.map((m) => toTurn2(m, fold.get(m.msgKeyId)));
14997
+ const turns = records5.map((m) => toTurn2(m, fold.get(m.msgKeyId)));
14987
14998
  const reachedStart = from === 0;
14988
14999
  noteBackfill(remoteJid.slice(0, 12));
14989
15000
  console.log(
@@ -15060,7 +15071,7 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
15060
15071
 
15061
15072
  `)
15062
15073
  );
15063
- const records4 = [];
15074
+ const records5 = [];
15064
15075
  try {
15065
15076
  const { buf, end } = readFrom(file, alignedFrom);
15066
15077
  offset = end;
@@ -15072,18 +15083,18 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
15072
15083
  reactionRecords.push(parsed);
15073
15084
  continue;
15074
15085
  }
15075
- records4.push(parsed);
15086
+ records5.push(parsed);
15076
15087
  } catch {
15077
15088
  }
15078
15089
  }
15079
15090
  } catch {
15080
15091
  offset = fileEnd0;
15081
15092
  }
15082
- records4.sort((a, b) => a.dateSent.localeCompare(b.dateSent));
15083
- const scope = records4.length ? records4[records4.length - 1].scope : "none";
15084
- for (const m of records4) byMsgKeyId.set(m.msgKeyId, m);
15093
+ records5.sort((a, b) => a.dateSent.localeCompare(b.dateSent));
15094
+ const scope = records5.length ? records5[records5.length - 1].scope : "none";
15095
+ for (const m of records5) byMsgKeyId.set(m.msgKeyId, m);
15085
15096
  const wantedQuotes = /* @__PURE__ */ new Set();
15086
- for (const m of records4) {
15097
+ for (const m of records5) {
15087
15098
  if (m.quotedId && !byMsgKeyId.has(m.quotedId)) wantedQuotes.add(m.quotedId);
15088
15099
  }
15089
15100
  const q2 = resolveQuoteTargets(file, alignedFrom, wantedQuotes);
@@ -15097,14 +15108,14 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
15097
15108
  });
15098
15109
  }
15099
15110
  const backlogFold = foldReactions(reactionRecords);
15100
- for (const m of records4) send(toTurn2(m, backlogFold.get(m.msgKeyId)), offset);
15111
+ for (const m of records5) send(toTurn2(m, backlogFold.get(m.msgKeyId)), offset);
15101
15112
  console.log(
15102
- `[wa-reader] op=open remoteJid=${remoteJid} scope=${scope} messages=${records4.length} source=store`
15113
+ `[wa-reader] op=open remoteJid=${remoteJid} scope=${scope} messages=${records5.length} source=store`
15103
15114
  );
15104
15115
  console.log(
15105
- `[wa-reader] op=store-window remoteJid=${remoteJid} fileBytes=${fileEnd0} budget=${TAIL_WINDOW_BYTES} rawFrom=${rawFrom} alignedFrom=${alignedFrom} aligned=${aligned} source=${alignedFrom > 0 ? "windowed" : "full"} messages=${records4.length} quoteWanted=${wantedQuotes.size} quoteResolved=${q2.found.size} quoteMissing=${wantedQuotes.size - q2.found.size} quoteScanBytes=${q2.scannedBytes} quoteCapped=${q2.capped}`
15116
+ `[wa-reader] op=store-window remoteJid=${remoteJid} fileBytes=${fileEnd0} budget=${TAIL_WINDOW_BYTES} rawFrom=${rawFrom} alignedFrom=${alignedFrom} aligned=${aligned} source=${alignedFrom > 0 ? "windowed" : "full"} messages=${records5.length} quoteWanted=${wantedQuotes.size} quoteResolved=${q2.found.size} quoteMissing=${wantedQuotes.size - q2.found.size} quoteScanBytes=${q2.scannedBytes} quoteCapped=${q2.capped}`
15106
15117
  );
15107
- warmConversationAvatars(accountId, remoteJid, records4);
15118
+ warmConversationAvatars(accountId, remoteJid, records5);
15108
15119
  } else {
15109
15120
  reactionRecords.push(...readReactions(accountId, remoteJid));
15110
15121
  }
@@ -15421,7 +15432,7 @@ var whatsapp_reader_default = app5;
15421
15432
 
15422
15433
  // server/routes/telegram-reader.ts
15423
15434
  import { statSync as statSync14, watch as watch3 } from "fs";
15424
- import { join as join27, resolve as resolve19 } from "path";
15435
+ import { join as join28, resolve as resolve19 } from "path";
15425
15436
 
15426
15437
  // app/lib/telegram/outbound/get-chat.ts
15427
15438
  async function getTelegramChat(botToken, chatId) {
@@ -15439,6 +15450,164 @@ async function getTelegramChat(botToken, chatId) {
15439
15450
  }
15440
15451
  }
15441
15452
 
15453
+ // app/lib/telegram/manual-send-census.ts
15454
+ var RECORD_THRESHOLD_MS = 3e4;
15455
+ var SUPPRESSION_THRESHOLD_MS = 6e5;
15456
+ var WINDOW_MS3 = 36e5;
15457
+ var records3 = [];
15458
+ function prune2(now) {
15459
+ records3 = records3.filter((r) => now - r.at <= WINDOW_MS3);
15460
+ }
15461
+ function newest(key2) {
15462
+ for (let i = records3.length - 1; i >= 0; i--) if (records3[i].key === key2) return records3[i];
15463
+ return void 0;
15464
+ }
15465
+ function noteManualSent(key2, now) {
15466
+ records3.push({ key: key2, at: now, recorded: false, skipped: null, suppressed: false });
15467
+ }
15468
+ function noteManualRecorded(key2, _now) {
15469
+ const r = newest(key2);
15470
+ if (r) r.recorded = true;
15471
+ }
15472
+ function noteManualSkipped(key2, reason, _now) {
15473
+ const r = newest(key2);
15474
+ if (r) r.skipped = reason;
15475
+ }
15476
+ function noteManualSuppressed(key2, _now) {
15477
+ const r = records3.find((x) => x.key === key2 && x.recorded && !x.suppressed);
15478
+ if (r) r.suppressed = true;
15479
+ }
15480
+ function readManualSendCensus(now) {
15481
+ prune2(now);
15482
+ let recorded = 0;
15483
+ let suppressed = 0;
15484
+ let unrecorded = 0;
15485
+ let skipped = 0;
15486
+ let leaked = 0;
15487
+ for (const r of records3) {
15488
+ if (r.recorded) recorded += 1;
15489
+ else if (r.skipped !== null) skipped += 1;
15490
+ else if (now - r.at > RECORD_THRESHOLD_MS) unrecorded += 1;
15491
+ if (r.suppressed) suppressed += 1;
15492
+ else if (r.recorded && now - r.at > SUPPRESSION_THRESHOLD_MS) leaked += 1;
15493
+ }
15494
+ return { sent: records3.length, recorded, suppressed, unrecorded, skipped, leaked };
15495
+ }
15496
+ function buildManualSendCensusLine(c) {
15497
+ return `[telegram-manual-census] sent=${c.sent} recorded=${c.recorded} suppressed=${c.suppressed} unrecorded=${c.unrecorded} skipped=${c.skipped} leaked=${c.leaked} window=60m`;
15498
+ }
15499
+ function runTelegramManualSendCensus(now) {
15500
+ console.error(buildManualSendCensusLine(readManualSendCensus(now)));
15501
+ }
15502
+
15503
+ // app/lib/telegram/manual-send-context.ts
15504
+ async function recordManualSendContext(input, deps) {
15505
+ const refuse2 = (reason) => {
15506
+ noteManualSkipped(input.channelKey, reason, deps.now());
15507
+ return { recorded: false, reason };
15508
+ };
15509
+ if (input.entry.role !== "public") return refuse2("not-public-bot");
15510
+ if (!deps.entryActive(input.entry, input.accountDir, input.botId)) return refuse2("agent-inactive");
15511
+ const businessKey = telegramBusinessChannelKey(input.botId, String(input.chatId));
15512
+ if (deps.newestInboundTelegramDate(input.accountId, businessKey) !== null) {
15513
+ return refuse2("business-chat");
15514
+ }
15515
+ if (deps.findSessionProjectDir(input.sessionId) === null) return refuse2("no-transcript");
15516
+ try {
15517
+ await deps.handleInbound({
15518
+ accountId: input.accountId,
15519
+ accountDir: input.accountDir,
15520
+ botId: input.botId,
15521
+ botToken: input.entry.token,
15522
+ agentSlug: input.entry.agent,
15523
+ senderId: input.senderId,
15524
+ // The route admits only a private chat, where the conversation's subject IS
15525
+ // the sender. Passed explicitly so the gateway's key and its session id are
15526
+ // derived from the same value the store row already carries.
15527
+ conversationSubject: input.senderId,
15528
+ businessChatId: null,
15529
+ role: "public",
15530
+ personId: null,
15531
+ specialist: null,
15532
+ chatId: String(input.chatId),
15533
+ text: input.text,
15534
+ // No file by construction: this is text the operator typed into the send
15535
+ // box.
15536
+ media: [],
15537
+ source: "operator-sent",
15538
+ // The webhook's own closure, rebuilt with the same parameters. The gateway
15539
+ // stores it as this key's live delivery target, so a closure that differed
15540
+ // would re-route every LATER answer on this conversation through it.
15541
+ reply: deps.buildReply({
15542
+ accountId: input.accountId,
15543
+ sessionId: input.sessionId,
15544
+ channelKey: input.channelKey,
15545
+ chatId: input.chatId,
15546
+ senderId: input.senderId,
15547
+ botToken: input.entry.token
15548
+ })
15549
+ });
15550
+ } catch (err) {
15551
+ return refuse2(`inject-error:${err instanceof Error ? err.message : String(err)}`);
15552
+ }
15553
+ return { recorded: true, reason: "-" };
15554
+ }
15555
+
15556
+ // app/lib/claude-agent/specialist-roster.ts
15557
+ import { existsSync as existsSync19, readFileSync as readFileSync22, readdirSync as readdirSync14 } from "fs";
15558
+ import { join as join27 } from "path";
15559
+ function listSpecialistCardNames(specialistsDir) {
15560
+ if (!existsSync19(specialistsDir)) return [];
15561
+ return readdirSync14(specialistsDir).filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3)).sort();
15562
+ }
15563
+ function field(fm, name) {
15564
+ const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
15565
+ if (!m) return null;
15566
+ let value = m[1].trim();
15567
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
15568
+ value = value.slice(1, -1);
15569
+ }
15570
+ return value || null;
15571
+ }
15572
+ function readSpecialistRoster(specialistsDir) {
15573
+ if (!existsSync19(specialistsDir)) return { specialists: [], skipped: [] };
15574
+ const entries2 = readdirSync14(specialistsDir);
15575
+ const specialists = [];
15576
+ const skipped = [];
15577
+ for (const file of entries2.sort()) {
15578
+ if (!file.endsWith(".md")) continue;
15579
+ let raw;
15580
+ try {
15581
+ raw = readFileSync22(join27(specialistsDir, file), "utf-8");
15582
+ } catch (err) {
15583
+ skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
15584
+ continue;
15585
+ }
15586
+ const fm = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
15587
+ if (!fm) {
15588
+ skipped.push({ file, reason: "no-frontmatter" });
15589
+ continue;
15590
+ }
15591
+ const name = field(fm[1], "name");
15592
+ if (!name) {
15593
+ skipped.push({ file, reason: "missing-name" });
15594
+ continue;
15595
+ }
15596
+ const summary = field(fm[1], "summary") ?? field(fm[1], "description") ?? "";
15597
+ specialists.push({ name, summary });
15598
+ }
15599
+ return { specialists, skipped };
15600
+ }
15601
+
15602
+ // app/lib/telegram/gateway/instance.ts
15603
+ var instance = null;
15604
+ function setTelegramGateway(gw) {
15605
+ instance = gw;
15606
+ }
15607
+ function getTelegramGateway() {
15608
+ return instance;
15609
+ }
15610
+
15442
15611
  // server/routes/telegram-reader.ts
15443
15612
  var TAG27 = "[telegram-reader]";
15444
15613
  var app6 = new Hono();
@@ -15454,7 +15623,7 @@ function foldPatches(rows, uploadsBase) {
15454
15623
  for (const id of row.attachmentIds) {
15455
15624
  if (already.has(id)) continue;
15456
15625
  already.add(id);
15457
- const meta = readAttachmentMeta(join27(uploadsBase, id));
15626
+ const meta = readAttachmentMeta(join28(uploadsBase, id));
15458
15627
  if (meta) metas.push(meta);
15459
15628
  }
15460
15629
  seen.set(row.targetMessageId, already);
@@ -15576,13 +15745,13 @@ app6.get("/store-backfill", requireAdminSession, (c) => {
15576
15745
  patchScanBytes = Math.max(0, buf.length - pageEnd);
15577
15746
  } catch {
15578
15747
  }
15579
- const records4 = messagesIn(pageRows);
15748
+ const records5 = messagesIn(pageRows);
15580
15749
  const patches = foldPatches(patchRows, uploadsBaseFor(v.accountId));
15581
15750
  console.log(
15582
- `${TAG27} op=backfill channelKey=${v.channelKey} from=${from} to=${to} turns=${records4.length} patched=${patches.size} patchScanBytes=${patchScanBytes} done=${from === 0}`
15751
+ `${TAG27} op=backfill channelKey=${v.channelKey} from=${from} to=${to} turns=${records5.length} patched=${patches.size} patchScanBytes=${patchScanBytes} done=${from === 0}`
15583
15752
  );
15584
15753
  return c.json({
15585
- turns: records4.map((r) => toTurn(r, patches.get(r.messageId))),
15754
+ turns: records5.map((r) => toTurn(r, patches.get(r.messageId))),
15586
15755
  from,
15587
15756
  done: from === 0
15588
15757
  });
@@ -15635,11 +15804,11 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
15635
15804
  } catch {
15636
15805
  offset = fileEnd0;
15637
15806
  }
15638
- const records4 = messagesIn(rows);
15807
+ const records5 = messagesIn(rows);
15639
15808
  const patches = foldPatches(rows, uploadsBase);
15640
- for (const r of records4) send(toTurn(r, patches.get(r.messageId)), offset);
15809
+ for (const r of records5) send(toTurn(r, patches.get(r.messageId)), offset);
15641
15810
  console.log(
15642
- `${TAG27} op=open channelKey=${channelKey} messages=${records4.length} fileBytes=${fileEnd0} alignedFrom=${alignedFrom} aligned=${aligned} source=${alignedFrom > 0 ? "windowed" : "full"}`
15811
+ `${TAG27} op=open channelKey=${channelKey} messages=${records5.length} fileBytes=${fileEnd0} alignedFrom=${alignedFrom} aligned=${aligned} source=${alignedFrom > 0 ? "windowed" : "full"}`
15643
15812
  );
15644
15813
  }
15645
15814
  const drain = () => {
@@ -15719,6 +15888,43 @@ data: ${JSON.stringify({
15719
15888
  });
15720
15889
  });
15721
15890
  var MANUAL_TAG2 = "[telegram-manual]";
15891
+ function manualSendContextDeps() {
15892
+ return {
15893
+ // The same definition the webhook applies, from the same module: an agent the
15894
+ // operator switched off must not be resurrected by a manual send.
15895
+ entryActive: (entry3, accountDir, botId) => entryActive(entry3, accountDir, botId, {
15896
+ isActiveAgentSlug,
15897
+ specialistCards: (dir) => listSpecialistCardNames(dir),
15898
+ log: (line) => console.error(line)
15899
+ }),
15900
+ newestInboundTelegramDate,
15901
+ findSessionProjectDir,
15902
+ handleInbound: async (inbound) => {
15903
+ const gateway = getTelegramGateway();
15904
+ if (!gateway) throw new Error("gateway-not-ready");
15905
+ await gateway.handleInbound(inbound);
15906
+ },
15907
+ // One builder, shared with the webhook and the inject door, so no two doors
15908
+ // can disagree about which conversation an answer's row files under.
15909
+ buildReply: ({ accountId, sessionId, channelKey, chatId, senderId, botToken }) => buildTelegramReplyClosure({
15910
+ accountId,
15911
+ channelKey,
15912
+ sessionId,
15913
+ senderId,
15914
+ chatId,
15915
+ // The hook refuses every non-public entry above, so this is the only
15916
+ // scope reachable here.
15917
+ scope: "public",
15918
+ botToken,
15919
+ send: sendTelegramText,
15920
+ append: appendMessage2,
15921
+ log: (line) => console.error(line),
15922
+ now: () => /* @__PURE__ */ new Date()
15923
+ }),
15924
+ log: (line) => console.error(line),
15925
+ now: () => Date.now()
15926
+ };
15927
+ }
15722
15928
  app6.post("/send", requireAdminSession, async (c) => {
15723
15929
  const sendId = process.hrtime.bigint().toString(36).slice(-8);
15724
15930
  const t0 = Date.now();
@@ -15816,6 +16022,25 @@ app6.post("/send", requireAdminSession, async (c) => {
15816
16022
  console.error(
15817
16023
  `${MANUAL_TAG2} op=recorded sendId=${sendId} records=${after.length} reread=${reread}`
15818
16024
  );
16025
+ const contextSessionId = telegramAdminSessionId(resolution.accountId, parsed.senderId, parsed.botId);
16026
+ noteManualSent(channelKey, Date.now());
16027
+ const ctx = await recordManualSendContext(
16028
+ {
16029
+ accountId: resolution.accountId,
16030
+ accountDir: resolution.accountDir,
16031
+ entry: resolution.entry,
16032
+ botId: parsed.botId,
16033
+ senderId: parsed.senderId,
16034
+ chatId,
16035
+ channelKey,
16036
+ text,
16037
+ sessionId: contextSessionId
16038
+ },
16039
+ manualSendContextDeps()
16040
+ );
16041
+ console.error(
16042
+ `${MANUAL_TAG2} op=context-recorded sendId=${sendId} chatId=${chatId} sessionId=${contextSessionId} suppressed=${ctx.recorded ? "yes" : "no"} reason=${ctx.reason}`
16043
+ );
15819
16044
  exit("delivered");
15820
16045
  return c.json({ ok: true, recorded: reread }, 200);
15821
16046
  });
@@ -15838,10 +16063,10 @@ function collectTelegramStoreCensus() {
15838
16063
  const summaries = readConversationSummaries2(accountId);
15839
16064
  for (const channelKey of summaries.keys()) {
15840
16065
  out.conversations += 1;
15841
- const records4 = readConversation2(accountId, channelKey);
15842
- if (records4.length > 0) out.served += 1;
15843
- out.records += records4.length;
15844
- for (const r of records4) {
16066
+ const records5 = readConversation2(accountId, channelKey);
16067
+ if (records5.length > 0) out.served += 1;
16068
+ out.records += records5.length;
16069
+ for (const r of records5) {
15845
16070
  if (r.origin === "inbound") out.inbound += 1;
15846
16071
  else if (r.origin === "agent") out.agent += 1;
15847
16072
  else if (r.origin === "operator-manual") out.operatorManual += 1;
@@ -15860,8 +16085,8 @@ function runTelegramStoreCensus() {
15860
16085
 
15861
16086
  // app/lib/telegram/reach-census.ts
15862
16087
  var import_dist4 = __toESM(require_dist2(), 1);
15863
- import { existsSync as existsSync19 } from "fs";
15864
- import { join as join28 } from "path";
16088
+ import { existsSync as existsSync20 } from "fs";
16089
+ import { join as join29 } from "path";
15865
16090
  function collectTelegramReachCensus(accounts) {
15866
16091
  const out = {
15867
16092
  accounts: accounts.length,
@@ -15871,7 +16096,7 @@ function collectTelegramReachCensus(accounts) {
15871
16096
  };
15872
16097
  for (const account of accounts) {
15873
16098
  const reach = (0, import_dist4.collectTelegramReach)({
15874
- storeDir: join28(STORE_ROOT2(), account.accountId),
16099
+ storeDir: join29(STORE_ROOT2(), account.accountId),
15875
16100
  bots: account.bots
15876
16101
  });
15877
16102
  for (const bot of reach.bots) {
@@ -15887,7 +16112,7 @@ function runTelegramReachCensus(accounts) {
15887
16112
  const c = collectTelegramReachCensus(accounts);
15888
16113
  const root = STORE_ROOT2();
15889
16114
  console.error(
15890
- `[telegram-reach-census] accounts=${c.accounts} bots=${c.bots} chatsKnown=${c.chatsKnown} botsWithNoChats=${c.botsWithNoChats} storeRoot=${root} exists=${existsSync19(root)}`
16115
+ `[telegram-reach-census] accounts=${c.accounts} bots=${c.bots} chatsKnown=${c.chatsKnown} botsWithNoChats=${c.botsWithNoChats} storeRoot=${root} exists=${existsSync20(root)}`
15891
16116
  );
15892
16117
  return c;
15893
16118
  }
@@ -15925,7 +16150,7 @@ function runTelegramCheckoutCensus(bots, now) {
15925
16150
  }
15926
16151
 
15927
16152
  // app/lib/telegram/media-census.ts
15928
- import { join as join29, resolve as resolve20 } from "path";
16153
+ import { join as join30, resolve as resolve20 } from "path";
15929
16154
  function idsByTarget(rows) {
15930
16155
  const out = /* @__PURE__ */ new Map();
15931
16156
  for (const row of rows) {
@@ -16006,8 +16231,8 @@ function readWaOperatorTurns() {
16006
16231
  }
16007
16232
 
16008
16233
  // server/routes/session-reader.ts
16009
- import { readFileSync as readFileSync22, watch as watch4, statSync as statSync15, existsSync as existsSync20, readdirSync as readdirSync14, realpathSync as realpathSync5 } from "fs";
16010
- import { isAbsolute as isAbsolute2, join as join30, relative as relative3, resolve as resolve21, sep as sep5 } from "path";
16234
+ import { readFileSync as readFileSync23, watch as watch4, statSync as statSync15, existsSync as existsSync21, readdirSync as readdirSync15, realpathSync as realpathSync5 } from "fs";
16235
+ import { isAbsolute as isAbsolute2, join as join31, relative as relative3, resolve as resolve21, sep as sep5 } from "path";
16011
16236
 
16012
16237
  // server/routes/admin/manager-client.ts
16013
16238
  var SESSION_ID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -16023,10 +16248,10 @@ function managerBase(tag) {
16023
16248
  function pickActivity(entries2, grown, nowMs, concurrencyWindowMs) {
16024
16249
  const candidates = entries2.filter((e) => grown.has(e.hex));
16025
16250
  if (candidates.length === 0) return null;
16026
- let newest = candidates[0];
16027
- for (const e of candidates) if (e.mtimeMs > newest.mtimeMs) newest = e;
16251
+ let newest2 = candidates[0];
16252
+ for (const e of candidates) if (e.mtimeMs > newest2.mtimeMs) newest2 = e;
16028
16253
  const count = candidates.filter((e) => nowMs - e.mtimeMs < concurrencyWindowMs).length;
16029
- return { hex: newest.hex, lastEmitAt: newest.mtimeMs, count };
16254
+ return { hex: newest2.hex, lastEmitAt: newest2.mtimeMs, count };
16030
16255
  }
16031
16256
 
16032
16257
  // server/routes/session-reader.ts
@@ -16112,7 +16337,7 @@ function openTaskKey(turns) {
16112
16337
  }
16113
16338
  function readSubagentMeta(dir, hex) {
16114
16339
  try {
16115
- const m = JSON.parse(readFileSync22(join30(dir, `agent-${hex}.meta.json`), "utf8"));
16340
+ const m = JSON.parse(readFileSync23(join31(dir, `agent-${hex}.meta.json`), "utf8"));
16116
16341
  if (typeof m.agentType === "string" && typeof m.description === "string" && typeof m.toolUseId === "string") {
16117
16342
  return { agentType: m.agentType, description: m.description, toolUseId: m.toolUseId };
16118
16343
  }
@@ -16135,7 +16360,7 @@ function runActivityTick(c) {
16135
16360
  c.parentGrewThisTick = false;
16136
16361
  let names = [];
16137
16362
  try {
16138
- names = readdirSync14(c.subagentsDir).filter((n) => AGENT_JSONL_RE.test(n));
16363
+ names = readdirSync15(c.subagentsDir).filter((n) => AGENT_JSONL_RE.test(n));
16139
16364
  } catch {
16140
16365
  }
16141
16366
  const entries2 = [];
@@ -16144,7 +16369,7 @@ function runActivityTick(c) {
16144
16369
  let size = 0;
16145
16370
  let mtimeMs = 0;
16146
16371
  try {
16147
- const st = statSync15(join30(c.subagentsDir, name));
16372
+ const st = statSync15(join31(c.subagentsDir, name));
16148
16373
  size = st.size;
16149
16374
  mtimeMs = st.mtimeMs;
16150
16375
  } catch {
@@ -16189,11 +16414,11 @@ app7.get("/stream", requireAdminSession, (c) => {
16189
16414
  const sessionId = c.req.query("sessionId") ?? "";
16190
16415
  const projectDir = c.req.query("projectDir") ?? "";
16191
16416
  const cfg = claudeConfigDir();
16192
- const projectsRoot = cfg ? resolve21(join30(cfg, "projects")) : null;
16417
+ const projectsRoot = cfg ? resolve21(join31(cfg, "projects")) : null;
16193
16418
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
16194
16419
  return c.json({ error: "bad session reference" }, 400);
16195
16420
  }
16196
- const jsonlPath = resolve21(join30(projectDir, `${sessionId}.jsonl`));
16421
+ const jsonlPath = resolve21(join31(projectDir, `${sessionId}.jsonl`));
16197
16422
  if (!jsonlPath.startsWith(projectsRoot + sep5)) {
16198
16423
  return c.json({ error: "bad session reference" }, 400);
16199
16424
  }
@@ -16221,9 +16446,9 @@ data: ${JSON.stringify(turn)}
16221
16446
  let offset = resumeOffset(lastEventId, fileEnd0);
16222
16447
  console.log(`[wa-stream] op=open conn=${connId} sessionId=${sessionId} channel=reader resume=${offset}`);
16223
16448
  const sessionKey = c.req.query("session_key") ?? "";
16224
- const subagentsDir = resolve21(join30(projectDir, sessionId, "subagents"));
16449
+ const subagentsDir = resolve21(join31(projectDir, sessionId, "subagents"));
16225
16450
  console.log(
16226
- `[webchat-activity] op=watch-subagents key=${sessionKey} dir=${subagentsDir} exists=${existsSync20(subagentsDir)}`
16451
+ `[webchat-activity] op=watch-subagents key=${sessionKey} dir=${subagentsDir} exists=${existsSync21(subagentsDir)}`
16227
16452
  );
16228
16453
  const actx = makeActivityCtx({
16229
16454
  subagentsDir,
@@ -16374,11 +16599,11 @@ app7.get("/backfill", requireAdminSession, (c) => {
16374
16599
  const beforeRaw = c.req.query("before") ?? "";
16375
16600
  const before = Number(beforeRaw);
16376
16601
  const cfg = claudeConfigDir();
16377
- const projectsRoot = cfg ? resolve21(join30(cfg, "projects")) : null;
16602
+ const projectsRoot = cfg ? resolve21(join31(cfg, "projects")) : null;
16378
16603
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId) || beforeRaw === "" || !Number.isFinite(before) || before < 0) {
16379
16604
  return c.json({ error: "bad session reference" }, 400);
16380
16605
  }
16381
- const jsonlPath = resolve21(join30(projectDir, `${sessionId}.jsonl`));
16606
+ const jsonlPath = resolve21(join31(projectDir, `${sessionId}.jsonl`));
16382
16607
  if (!jsonlPath.startsWith(projectsRoot + sep5)) {
16383
16608
  return c.json({ error: "bad session reference" }, 400);
16384
16609
  }
@@ -16430,10 +16655,10 @@ app7.get("/directives", requireAdminSession, (c) => {
16430
16655
  const sessionId = c.req.query("sessionId") ?? "";
16431
16656
  if (!SESSION_ID_RE2.test(sessionId)) return c.json({ error: "bad session reference" }, 400);
16432
16657
  const dir = directiveStoreDir(sessionId);
16433
- if (!dir || !existsSync20(dir)) return c.json({ entries: [] });
16658
+ if (!dir || !existsSync21(dir)) return c.json({ entries: [] });
16434
16659
  let names;
16435
16660
  try {
16436
- names = readdirSync14(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
16661
+ names = readdirSync15(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
16437
16662
  } catch {
16438
16663
  return c.json({ entries: [] });
16439
16664
  }
@@ -16443,7 +16668,7 @@ app7.get("/directives", requireAdminSession, (c) => {
16443
16668
  const pid = Number(pidPart);
16444
16669
  let len = 0;
16445
16670
  try {
16446
- len = statSync15(join30(dir, name)).size;
16671
+ len = statSync15(join31(dir, name)).size;
16447
16672
  } catch {
16448
16673
  }
16449
16674
  return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
@@ -16458,11 +16683,11 @@ app7.get("/directive", requireAdminSession, (c) => {
16458
16683
  }
16459
16684
  const dir = directiveStoreDir(sessionId);
16460
16685
  if (!dir) return c.json({ error: "no account" }, 404);
16461
- const path = resolve21(join30(dir, name));
16686
+ const path = resolve21(join31(dir, name));
16462
16687
  if (!path.startsWith(dir + sep5)) return c.json({ error: "bad reference" }, 400);
16463
16688
  let body;
16464
16689
  try {
16465
- body = readFileSync22(path, "utf8");
16690
+ body = readFileSync23(path, "utf8");
16466
16691
  } catch {
16467
16692
  return c.json({ error: "not found" }, 404);
16468
16693
  }
@@ -16471,8 +16696,8 @@ app7.get("/directive", requireAdminSession, (c) => {
16471
16696
  var session_reader_default = app7;
16472
16697
 
16473
16698
  // server/routes/operator-conversations.ts
16474
- import { readFileSync as readFileSync23 } from "fs";
16475
- import { basename as basename9, dirname as dirname9, join as join31 } from "path";
16699
+ import { readFileSync as readFileSync24 } from "fs";
16700
+ import { basename as basename9, dirname as dirname9, join as join32 } from "path";
16476
16701
 
16477
16702
  // app/lib/reader/operator-conversations.ts
16478
16703
  function operatorConvName(row) {
@@ -16560,7 +16785,7 @@ app8.get("/conversations", requireAdminSession, async (c) => {
16560
16785
  }
16561
16786
  const cfg = claudeConfigDir();
16562
16787
  if (!cfg) return c.json({ conversations: [] });
16563
- const projectsRoot = join31(cfg, "projects");
16788
+ const projectsRoot = join32(cfg, "projects");
16564
16789
  const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
16565
16790
  const rows = [];
16566
16791
  let sessionRowsExcludedUntagged = 0;
@@ -16578,7 +16803,7 @@ app8.get("/conversations", requireAdminSession, async (c) => {
16578
16803
  enumeratedCount += 1;
16579
16804
  const sessionId = basename9(path).replace(/\.jsonl$/, "");
16580
16805
  const projectDir = dirname9(path);
16581
- const meta = readSidecarMeta(join31(projectDir, `${sessionId}.meta.json`));
16806
+ const meta = readSidecarMeta(join32(projectDir, `${sessionId}.meta.json`));
16582
16807
  if (!isReaderChannelSession(meta.role, meta.channel)) continue;
16583
16808
  if (multiAccount) {
16584
16809
  if (meta.accountId === null) {
@@ -16590,7 +16815,7 @@ app8.get("/conversations", requireAdminSession, async (c) => {
16590
16815
  let body = "";
16591
16816
  let bodyRead = false;
16592
16817
  try {
16593
- body = readFileSync23(path, "utf8");
16818
+ body = readFileSync24(path, "utf8");
16594
16819
  bodyRead = true;
16595
16820
  bodyReadCount += 1;
16596
16821
  } catch (err) {
@@ -16766,7 +16991,7 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
16766
16991
  const { value, state } = await getOrCompute("conversations-all", houseId, async () => {
16767
16992
  const cfg = claudeConfigDir();
16768
16993
  if (!cfg) return { conversations: [] };
16769
- const projectsRoot = join31(cfg, "projects");
16994
+ const projectsRoot = join32(cfg, "projects");
16770
16995
  const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
16771
16996
  const validAccounts = listValidAccounts();
16772
16997
  const validIds = new Set(validAccounts.map((a) => a.accountId));
@@ -16786,7 +17011,7 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
16786
17011
  enumeratedCount += 1;
16787
17012
  const sessionId = basename9(path).replace(/\.jsonl$/, "");
16788
17013
  const projectDir = dirname9(path);
16789
- const meta = readSidecarMeta(join31(projectDir, `${sessionId}.meta.json`));
17014
+ const meta = readSidecarMeta(join32(projectDir, `${sessionId}.meta.json`));
16790
17015
  if (!isReaderChannelSession(meta.role, meta.channel)) continue;
16791
17016
  if (meta.accountId === null) {
16792
17017
  untaggedCount += 1;
@@ -16797,7 +17022,7 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
16797
17022
  let body = "";
16798
17023
  let bodyRead = false;
16799
17024
  try {
16800
- body = readFileSync23(path, "utf8");
17025
+ body = readFileSync24(path, "utf8");
16801
17026
  bodyRead = true;
16802
17027
  bodyReadCount += 1;
16803
17028
  } catch (err) {
@@ -16922,8 +17147,8 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
16922
17147
  var operator_conversations_default = app8;
16923
17148
 
16924
17149
  // server/routes/public-reader.ts
16925
- import { statSync as statSync17, watch as watch6, readFileSync as readFileSync24, readdirSync as readdirSync16 } from "fs";
16926
- import { basename as basename10, dirname as dirname10, join as join32, resolve as resolve23, sep as sep7 } from "path";
17150
+ import { statSync as statSync17, watch as watch6, readFileSync as readFileSync25, readdirSync as readdirSync17 } from "fs";
17151
+ import { basename as basename10, dirname as dirname10, join as join33, resolve as resolve23, sep as sep7 } from "path";
16927
17152
 
16928
17153
  // app/lib/reader/delivered-kinds.ts
16929
17154
  var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
@@ -17012,10 +17237,10 @@ function enumeratePublicRows() {
17012
17237
  const cfg = claudeConfigDir();
17013
17238
  if (!cfg) return [];
17014
17239
  const rows = [];
17015
- for (const { path } of enumerateJsonls(join32(cfg, "projects"))) {
17240
+ for (const { path } of enumerateJsonls(join33(cfg, "projects"))) {
17016
17241
  const sessionId = basename10(path).replace(/\.jsonl$/, "");
17017
17242
  const projectDir = dirname10(path);
17018
- const meta = readSidecarMeta(join32(projectDir, `${sessionId}.meta.json`));
17243
+ const meta = readSidecarMeta(join33(projectDir, `${sessionId}.meta.json`));
17019
17244
  rows.push({
17020
17245
  sessionId,
17021
17246
  projectDir,
@@ -17082,15 +17307,15 @@ app9.get("/backfill", (c) => {
17082
17307
  const beforeRaw = c.req.query("before") ?? "";
17083
17308
  const before = Number(beforeRaw);
17084
17309
  const cfg = claudeConfigDir();
17085
- const projectsRoot = cfg ? resolve23(join32(cfg, "projects")) : null;
17310
+ const projectsRoot = cfg ? resolve23(join33(cfg, "projects")) : null;
17086
17311
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId) || beforeRaw === "" || !Number.isFinite(before) || before < 0) {
17087
17312
  return c.json({ error: "bad session reference" }, 400);
17088
17313
  }
17089
- const jsonlPath = resolve23(join32(projectDir, `${sessionId}.jsonl`));
17314
+ const jsonlPath = resolve23(join33(projectDir, `${sessionId}.jsonl`));
17090
17315
  if (!jsonlPath.startsWith(projectsRoot + sep7)) {
17091
17316
  return c.json({ error: "bad session reference" }, 400);
17092
17317
  }
17093
- const meta = readSidecarMeta(join32(projectDir, `${sessionId}.meta.json`));
17318
+ const meta = readSidecarMeta(join33(projectDir, `${sessionId}.meta.json`));
17094
17319
  const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
17095
17320
  const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
17096
17321
  if (!(isPublicWebchat && owns)) {
@@ -17149,15 +17374,15 @@ app9.get("/stream", (c) => {
17149
17374
  const sessionId = c.req.query("sessionId") ?? "";
17150
17375
  const projectDir = c.req.query("projectDir") ?? "";
17151
17376
  const cfg = claudeConfigDir();
17152
- const projectsRoot = cfg ? resolve23(join32(cfg, "projects")) : null;
17377
+ const projectsRoot = cfg ? resolve23(join33(cfg, "projects")) : null;
17153
17378
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
17154
17379
  return c.json({ error: "bad session reference" }, 400);
17155
17380
  }
17156
- const jsonlPath = resolve23(join32(projectDir, `${sessionId}.jsonl`));
17381
+ const jsonlPath = resolve23(join33(projectDir, `${sessionId}.jsonl`));
17157
17382
  if (!jsonlPath.startsWith(projectsRoot + sep7)) {
17158
17383
  return c.json({ error: "bad session reference" }, 400);
17159
17384
  }
17160
- const meta = readSidecarMeta(join32(projectDir, `${sessionId}.meta.json`));
17385
+ const meta = readSidecarMeta(join33(projectDir, `${sessionId}.meta.json`));
17161
17386
  const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
17162
17387
  const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
17163
17388
  if (!(isPublicWebchat && owns)) {
@@ -17301,9 +17526,9 @@ app9.get("/attachment/:attachmentId", (c) => {
17301
17526
  let dataFile;
17302
17527
  let buffer;
17303
17528
  try {
17304
- dataFile = readdirSync16(dir).find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
17529
+ dataFile = readdirSync17(dir).find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
17305
17530
  if (!dataFile) throw new Error("no data file");
17306
- buffer = readFileSync24(resolve23(dir, dataFile));
17531
+ buffer = readFileSync25(resolve23(dir, dataFile));
17307
17532
  } catch {
17308
17533
  console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
17309
17534
  return new Response("Not found", { status: 404 });
@@ -17325,20 +17550,20 @@ var public_reader_default = app9;
17325
17550
 
17326
17551
  // server/routes/webchat.ts
17327
17552
  import { basename as basename11, dirname as dirname11 } from "path";
17328
- import { join as join35 } from "path";
17329
- import { existsSync as existsSync23, readdirSync as readdirSync18, readFileSync as readFileSync27, renameSync as renameSync4, statSync as statSync18, writeFileSync as writeFileSync11 } from "fs";
17553
+ import { join as join36 } from "path";
17554
+ import { existsSync as existsSync24, readdirSync as readdirSync19, readFileSync as readFileSync28, renameSync as renameSync4, statSync as statSync18, writeFileSync as writeFileSync11 } from "fs";
17330
17555
 
17331
17556
  // server/canonical-webchat-override.ts
17332
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync9, renameSync as renameSync2 } from "fs";
17333
- import { join as join33 } from "path";
17557
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync9, renameSync as renameSync2 } from "fs";
17558
+ import { join as join34 } from "path";
17334
17559
  var FILE = "canonical-webchat-session.json";
17335
17560
  var UUID2 = /^[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}$/;
17336
17561
  function canonicalOverridePath(accountDir) {
17337
- return join33(accountDir, FILE);
17562
+ return join34(accountDir, FILE);
17338
17563
  }
17339
17564
  function readRaw(accountDir) {
17340
17565
  try {
17341
- return JSON.parse(readFileSync25(canonicalOverridePath(accountDir), "utf8"));
17566
+ return JSON.parse(readFileSync26(canonicalOverridePath(accountDir), "utf8"));
17342
17567
  } catch {
17343
17568
  return null;
17344
17569
  }
@@ -17451,8 +17676,8 @@ function isNewSessionEffortLevel(value) {
17451
17676
  }
17452
17677
 
17453
17678
  // ../services/claude-session-manager/src/sidecar-store.ts
17454
- import { existsSync as existsSync22, mkdirSync as mkdirSync4, readdirSync as readdirSync17, readFileSync as readFileSync26, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
17455
- import { join as join34 } from "path";
17679
+ import { existsSync as existsSync23, mkdirSync as mkdirSync4, readdirSync as readdirSync18, readFileSync as readFileSync27, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
17680
+ import { join as join35 } from "path";
17456
17681
  function escapeRegExp(s) {
17457
17682
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17458
17683
  }
@@ -17460,7 +17685,7 @@ function createSidecarStore(config) {
17460
17685
  const { suffix, validate: validate3 } = config;
17461
17686
  const suffixRe = new RegExp(`${escapeRegExp(suffix)}$`);
17462
17687
  function pathFor(sessionsDir, id) {
17463
- return join34(sessionsDir, `${id}${suffix}`);
17688
+ return join35(sessionsDir, `${id}${suffix}`);
17464
17689
  }
17465
17690
  function write(sessionsDir, id, record) {
17466
17691
  mkdirSync4(sessionsDir, { recursive: true });
@@ -17472,7 +17697,7 @@ function createSidecarStore(config) {
17472
17697
  return { ok: true };
17473
17698
  } catch (error) {
17474
17699
  try {
17475
- if (existsSync22(tmp)) unlinkSync2(tmp);
17700
+ if (existsSync23(tmp)) unlinkSync2(tmp);
17476
17701
  } catch {
17477
17702
  }
17478
17703
  return { ok: false, error };
@@ -17481,7 +17706,7 @@ function createSidecarStore(config) {
17481
17706
  function readFileAt(path) {
17482
17707
  let raw;
17483
17708
  try {
17484
- raw = readFileSync26(path, "utf8");
17709
+ raw = readFileSync27(path, "utf8");
17485
17710
  } catch {
17486
17711
  return null;
17487
17712
  }
@@ -17497,17 +17722,17 @@ function createSidecarStore(config) {
17497
17722
  function readAll3(sessionsDir, onSkip) {
17498
17723
  let names;
17499
17724
  try {
17500
- names = readdirSync17(sessionsDir, { withFileTypes: true }).filter((entry3) => entry3.isFile()).map((entry3) => entry3.name);
17725
+ names = readdirSync18(sessionsDir, { withFileTypes: true }).filter((entry3) => entry3.isFile()).map((entry3) => entry3.name);
17501
17726
  } catch {
17502
17727
  return [];
17503
17728
  }
17504
17729
  const out = [];
17505
17730
  for (const name of names) {
17506
17731
  if (!suffixRe.test(name)) continue;
17507
- const path = join34(sessionsDir, name);
17732
+ const path = join35(sessionsDir, name);
17508
17733
  let raw;
17509
17734
  try {
17510
- raw = readFileSync26(path, "utf8");
17735
+ raw = readFileSync27(path, "utf8");
17511
17736
  } catch {
17512
17737
  onSkip?.(name, "unreadable");
17513
17738
  continue;
@@ -17528,7 +17753,7 @@ function createSidecarStore(config) {
17528
17753
  function clear(sessionsDir, id) {
17529
17754
  const path = pathFor(sessionsDir, id);
17530
17755
  try {
17531
- if (existsSync22(path)) {
17756
+ if (existsSync23(path)) {
17532
17757
  unlinkSync2(path);
17533
17758
  return { ok: true, removed: true };
17534
17759
  }
@@ -17580,7 +17805,7 @@ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9
17580
17805
  function locateSession(sessionId) {
17581
17806
  const dir = findSessionProjectDir(sessionId);
17582
17807
  if (!dir) return { projectDir: null, channel: null, accountId: null };
17583
- const meta = readSidecarMeta(join35(dir, `${sessionId}.meta.json`));
17808
+ const meta = readSidecarMeta(join36(dir, `${sessionId}.meta.json`));
17584
17809
  return { projectDir: dir, channel: meta.channel, accountId: meta.accountId };
17585
17810
  }
17586
17811
  function resolveQuoted(sessionId, quotedUuid, sendId) {
@@ -17598,7 +17823,7 @@ function resolveQuoted(sessionId, quotedUuid, sendId) {
17598
17823
  let line;
17599
17824
  try {
17600
17825
  const needle = `"uuid":"${quotedUuid}"`;
17601
- line = readFileSync27(join35(projectDir, `${sessionId}.jsonl`), "utf8").split("\n").find((l) => l.includes(needle));
17826
+ line = readFileSync28(join36(projectDir, `${sessionId}.jsonl`), "utf8").split("\n").find((l) => l.includes(needle));
17602
17827
  } catch {
17603
17828
  log2(false, 0);
17604
17829
  return void 0;
@@ -17615,16 +17840,16 @@ function resolveQuoted(sessionId, quotedUuid, sendId) {
17615
17840
  function locateSidecar(sessionId) {
17616
17841
  const cfg = claudeConfigDir();
17617
17842
  if (!cfg) return null;
17618
- const projectsRoot = join35(cfg, "projects");
17843
+ const projectsRoot = join36(cfg, "projects");
17619
17844
  let slugs;
17620
17845
  try {
17621
- slugs = readdirSync18(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
17846
+ slugs = readdirSync19(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
17622
17847
  } catch {
17623
17848
  return null;
17624
17849
  }
17625
17850
  for (const slug of slugs) {
17626
- const metaPath = join35(projectsRoot, slug, `${sessionId}.meta.json`);
17627
- if (existsSync23(metaPath)) return readSidecarMeta(metaPath);
17851
+ const metaPath = join36(projectsRoot, slug, `${sessionId}.meta.json`);
17852
+ if (existsSync24(metaPath)) return readSidecarMeta(metaPath);
17628
17853
  }
17629
17854
  return null;
17630
17855
  }
@@ -17633,7 +17858,7 @@ function readTargetOwner(sessionId) {
17633
17858
  if (sidecar !== null) return { found: true, owner: sidecar.adminUserId };
17634
17859
  const { projectDir } = locateSession(sessionId);
17635
17860
  if (projectDir !== null) {
17636
- return { found: true, owner: readSidecarMeta(join35(projectDir, `${sessionId}.meta.json`)).adminUserId };
17861
+ return { found: true, owner: readSidecarMeta(join36(projectDir, `${sessionId}.meta.json`)).adminUserId };
17637
17862
  }
17638
17863
  return { found: false, owner: null };
17639
17864
  }
@@ -17641,7 +17866,7 @@ function readSourceRowMeta(sessionId) {
17641
17866
  const sidecar = locateSidecar(sessionId);
17642
17867
  if (sidecar !== null) return sidecar;
17643
17868
  const { projectDir } = locateSession(sessionId);
17644
- if (projectDir !== null) return readSidecarMeta(join35(projectDir, `${sessionId}.meta.json`));
17869
+ if (projectDir !== null) return readSidecarMeta(join36(projectDir, `${sessionId}.meta.json`));
17645
17870
  return null;
17646
17871
  }
17647
17872
  function ownedByRequester(owner, requesterUserId, primaryUserId) {
@@ -17654,7 +17879,7 @@ function sessionSidecarExists(sessionId) {
17654
17879
  function jsonlSizeBytes(projectDir, sessionId) {
17655
17880
  if (projectDir === null) return null;
17656
17881
  try {
17657
- return statSync18(join35(projectDir, `${sessionId}.jsonl`)).size;
17882
+ return statSync18(join36(projectDir, `${sessionId}.jsonl`)).size;
17658
17883
  } catch {
17659
17884
  return null;
17660
17885
  }
@@ -17782,10 +18007,10 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccoun
17782
18007
  const cfg = claudeConfigDir();
17783
18008
  if (!cfg) return null;
17784
18009
  let best = null;
17785
- for (const { path, isSubagent, archived } of enumerateJsonls(join35(cfg, "projects"))) {
18010
+ for (const { path, isSubagent, archived } of enumerateJsonls(join36(cfg, "projects"))) {
17786
18011
  if (isSubagent || archived) continue;
17787
18012
  const id = basename11(path).slice(0, -".jsonl".length);
17788
- const meta = readSidecarMeta(join35(dirname11(path), `${id}.meta.json`));
18013
+ const meta = readSidecarMeta(join36(dirname11(path), `${id}.meta.json`));
17789
18014
  if (meta.role !== "admin" || meta.channel !== "webchat") continue;
17790
18015
  if (multiAccount && meta.accountId !== scopeAccountId) continue;
17791
18016
  let mtimeMs;
@@ -18145,9 +18370,9 @@ ${timeline}`;
18145
18370
  console.error(`[webchat:settings] op=${op} outcome=refused value=${value} accountId=none reason=no-account-for-session`);
18146
18371
  return c.json({ error: "account unresolved" }, 503);
18147
18372
  }
18148
- const accountJsonPath = join35(account.accountDir, "account.json");
18373
+ const accountJsonPath = join36(account.accountDir, "account.json");
18149
18374
  try {
18150
- const parsed = JSON.parse(readFileSync27(accountJsonPath, "utf8"));
18375
+ const parsed = JSON.parse(readFileSync28(accountJsonPath, "utf8"));
18151
18376
  if (op === "model") parsed.adminModel = value;
18152
18377
  else if (op === "effort") parsed.effort = value;
18153
18378
  else parsed.adminPermissionMode = value;
@@ -18193,7 +18418,7 @@ ${timeline}`;
18193
18418
  const cfg2 = claudeConfigDir();
18194
18419
  if (cfg2) {
18195
18420
  const source2 = resolveBridgeSource(
18196
- join35(cfg2, "sessions"),
18421
+ join36(cfg2, "sessions"),
18197
18422
  target,
18198
18423
  (id) => locateSession(id).projectDir !== null
18199
18424
  );
@@ -18220,7 +18445,7 @@ ${timeline}`;
18220
18445
  const cfg = claudeConfigDir();
18221
18446
  let projectDir = null;
18222
18447
  if (cfg) {
18223
- for (const { path } of enumerateJsonls(join35(cfg, "projects"))) {
18448
+ for (const { path } of enumerateJsonls(join36(cfg, "projects"))) {
18224
18449
  if (basename11(path) === `${sessionId}.jsonl`) {
18225
18450
  projectDir = dirname11(path);
18226
18451
  break;
@@ -18228,7 +18453,7 @@ ${timeline}`;
18228
18453
  }
18229
18454
  }
18230
18455
  if (source === "latest" && requesterUserId !== null) {
18231
- const owner = readSidecarMeta(join35(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
18456
+ const owner = readSidecarMeta(join36(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
18232
18457
  if (owner !== null && owner !== requesterUserId && requesterUserId !== primaryUserId) {
18233
18458
  console.error(`[webchat:inbound] op=session-resolve-foreign key=${sessionId.slice(0, 8)} owner=${owner.slice(0, 8)} requester=${requesterUserId.slice(0, 8)}`);
18234
18459
  }
@@ -18258,54 +18483,6 @@ ${timeline}`;
18258
18483
 
18259
18484
  // server/routes/webchat-greeting.ts
18260
18485
  import { resolve as resolve24 } from "path";
18261
-
18262
- // app/lib/claude-agent/specialist-roster.ts
18263
- import { existsSync as existsSync24, readFileSync as readFileSync28, readdirSync as readdirSync19 } from "fs";
18264
- import { join as join36 } from "path";
18265
- function listSpecialistCardNames(specialistsDir) {
18266
- if (!existsSync24(specialistsDir)) return [];
18267
- return readdirSync19(specialistsDir).filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3)).sort();
18268
- }
18269
- function field(fm, name) {
18270
- const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
18271
- if (!m) return null;
18272
- let value = m[1].trim();
18273
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
18274
- value = value.slice(1, -1);
18275
- }
18276
- return value || null;
18277
- }
18278
- function readSpecialistRoster(specialistsDir) {
18279
- if (!existsSync24(specialistsDir)) return { specialists: [], skipped: [] };
18280
- const entries2 = readdirSync19(specialistsDir);
18281
- const specialists = [];
18282
- const skipped = [];
18283
- for (const file of entries2.sort()) {
18284
- if (!file.endsWith(".md")) continue;
18285
- let raw;
18286
- try {
18287
- raw = readFileSync28(join36(specialistsDir, file), "utf-8");
18288
- } catch (err) {
18289
- skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
18290
- continue;
18291
- }
18292
- const fm = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
18293
- if (!fm) {
18294
- skipped.push({ file, reason: "no-frontmatter" });
18295
- continue;
18296
- }
18297
- const name = field(fm[1], "name");
18298
- if (!name) {
18299
- skipped.push({ file, reason: "missing-name" });
18300
- continue;
18301
- }
18302
- const summary = field(fm[1], "summary") ?? field(fm[1], "description") ?? "";
18303
- specialists.push({ name, summary });
18304
- }
18305
- return { specialists, skipped };
18306
- }
18307
-
18308
- // server/routes/webchat-greeting.ts
18309
18486
  var OUTSTANDING_TASK_STATUSES = ["pending", "active", "running"];
18310
18487
  var UPCOMING_EVENT_STATUSES = ["scheduled", "due"];
18311
18488
  var TASK_ITEMS_CAP = 5;
@@ -18563,15 +18740,6 @@ async function setManagedBotAccessSettings(managingBotToken, userId, isAccessRes
18563
18740
  return r.ok ? { ok: true } : { ok: false, error: r.error };
18564
18741
  }
18565
18742
 
18566
- // app/lib/telegram/gateway/instance.ts
18567
- var instance = null;
18568
- function setTelegramGateway(gw) {
18569
- instance = gw;
18570
- }
18571
- function getTelegramGateway() {
18572
- return instance;
18573
- }
18574
-
18575
18743
  // app/lib/telegram/access-control.ts
18576
18744
  function checkTelegramAccess(params) {
18577
18745
  const { senderId, entry: entry3, chatId, chatType } = params;
@@ -20432,7 +20600,7 @@ function noteLocationTurn() {
20432
20600
  locationTurns += 1;
20433
20601
  }
20434
20602
  function liveLocationCensusLine(accountsRoot, now) {
20435
- let records4 = 0;
20603
+ let records5 = 0;
20436
20604
  let current = 0;
20437
20605
  let expired = 0;
20438
20606
  let oldestAgeMs = 0;
@@ -20441,7 +20609,7 @@ function liveLocationCensusLine(accountsRoot, now) {
20441
20609
  const path = join39(accountsRoot, entry3, LIVE_LOCATION_FILE);
20442
20610
  if (!existsSync26(path) || !statSync19(path).isFile()) continue;
20443
20611
  for (const rec of Object.values(readAll(path))) {
20444
- records4 += 1;
20612
+ records5 += 1;
20445
20613
  if (isExpired(rec, now)) expired += 1;
20446
20614
  else current += 1;
20447
20615
  oldestAgeMs = Math.max(oldestAgeMs, now - rec.at);
@@ -20454,7 +20622,7 @@ function liveLocationCensusLine(accountsRoot, now) {
20454
20622
  sharesStarted = 0;
20455
20623
  locationTurns = 0;
20456
20624
  const ratio = shares === 0 ? "none" : String(turns / shares);
20457
- return `${LIVE_LOCATION_CENSUS_TAG} records=${records4} current=${current} expired=${expired} oldestAgeMs=${oldestAgeMs} shares=${shares} turnsPerShare=${ratio}`;
20625
+ return `${LIVE_LOCATION_CENSUS_TAG} records=${records5} current=${current} expired=${expired} oldestAgeMs=${oldestAgeMs} shares=${shares} turnsPerShare=${ratio}`;
20458
20626
  }
20459
20627
 
20460
20628
  // app/lib/telegram/business-connection.ts
@@ -22950,7 +23118,7 @@ var MAX_URL_LEN = 500;
22950
23118
  var MAX_UA_LEN = 400;
22951
23119
  var MAX_FILE_LEN = 300;
22952
23120
  var RATE_LIMIT_PER_MIN = 20;
22953
- var WINDOW_MS3 = 60 * 1e3;
23121
+ var WINDOW_MS4 = 60 * 1e3;
22954
23122
  var rateHits = /* @__PURE__ */ new Map();
22955
23123
  var ERROR_KINDS = /* @__PURE__ */ new Set(["uncaught", "unhandled-rejection", "subresource"]);
22956
23124
  function rateKey(ip, cls) {
@@ -22960,7 +23128,7 @@ function checkRateLimit2(ip, cls) {
22960
23128
  const key2 = rateKey(ip, cls);
22961
23129
  const now = Date.now();
22962
23130
  const rec = rateHits.get(key2);
22963
- if (!rec || now - rec.windowStart >= WINDOW_MS3) {
23131
+ if (!rec || now - rec.windowStart >= WINDOW_MS4) {
22964
23132
  rateHits.set(key2, { windowStart: now, count: 1, blocked: 0, lastLoggedBlocked: 0 });
22965
23133
  return { allow: true, blocked: 0, shouldLog: false };
22966
23134
  }
@@ -22975,12 +23143,12 @@ function checkRateLimit2(ip, cls) {
22975
23143
  }
22976
23144
  registerLoop({
22977
23145
  name: "client-error-rate-cleanup",
22978
- intervalMs: WINDOW_MS3,
23146
+ intervalMs: WINDOW_MS4,
22979
23147
  run: () => {
22980
23148
  const now = Date.now();
22981
23149
  let purged = 0;
22982
23150
  for (const [key2, rec] of rateHits) {
22983
- if (now - rec.windowStart >= WINDOW_MS3 * 2) {
23151
+ if (now - rec.windowStart >= WINDOW_MS4 * 2) {
22984
23152
  rateHits.delete(key2);
22985
23153
  purged += 1;
22986
23154
  }
@@ -26148,18 +26316,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
26148
26316
  ]);
26149
26317
  var app27 = new Hono();
26150
26318
  app27.post("/", async (c) => {
26151
- const TAG82 = "[admin:events]";
26319
+ const TAG83 = "[admin:events]";
26152
26320
  let body;
26153
26321
  try {
26154
26322
  body = await c.req.json();
26155
26323
  } catch (err) {
26156
26324
  const detail = err instanceof Error ? err.message : String(err);
26157
- console.error(`${TAG82} reject reason=body-not-json detail=${detail}`);
26325
+ console.error(`${TAG83} reject reason=body-not-json detail=${detail}`);
26158
26326
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
26159
26327
  }
26160
26328
  const event = typeof body.event === "string" ? body.event : "";
26161
26329
  if (!ALLOWED_EVENTS.has(event)) {
26162
- console.error(`${TAG82} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
26330
+ console.error(`${TAG83} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
26163
26331
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
26164
26332
  }
26165
26333
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -35459,13 +35627,13 @@ app68.get("/", requireAdminSession, async (c) => {
35459
35627
  sampleCpu(sampledAt);
35460
35628
  const cpu = sampleProcessCpu(sampledAt);
35461
35629
  const loopRows = listLoops();
35462
- const armed2 = armedCount();
35630
+ const armed3 = armedCount();
35463
35631
  const byState = loopRows.reduce((acc, r) => {
35464
35632
  acc[r.state] = (acc[r.state] ?? 0) + 1;
35465
35633
  return acc;
35466
35634
  }, {});
35467
35635
  console.log(
35468
- `${TAG62} 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(" ")
35636
+ `${TAG62} op=loops req=${req} armed=${armed3} rendered=${loopRows.length} procPct=${cpu.processPct?.toFixed(1) ?? "null"} attributedPct=${cpu.attributedPct?.toFixed(1) ?? "null"} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
35469
35637
  );
35470
35638
  return c.json({
35471
35639
  rows,
@@ -35477,7 +35645,7 @@ app68.get("/", requireAdminSession, async (c) => {
35477
35645
  // vocabulary renders as a dash meaning "not measured". The check runs both
35478
35646
  // ways: a key the page does not know about fails as an excess property.
35479
35647
  loops: {
35480
- armed: armed2,
35648
+ armed: armed3,
35481
35649
  rows: loopRows,
35482
35650
  // The server's own CPU beside what the registry can explain. The GAP is
35483
35651
  // the point: loops summing to 2% against a process at 60% says the cost
@@ -37733,11 +37901,29 @@ function startSharedFolderCensus() {
37733
37901
  });
37734
37902
  }
37735
37903
 
37904
+ // app/lib/accounts-state-census.ts
37905
+ var TAG70 = "[accounts-state]";
37906
+ function runAccountsStateCensus(accountsDir, nowMs) {
37907
+ const { dirs, provisioning, abandoned } = censusAccountDirs(accountsDir, nowMs);
37908
+ console.error(
37909
+ `${TAG70} op=stub-census dirs=${dirs} provisioning=${provisioning.length} abandoned=${abandoned.length} abandonedIds=${abandoned.length > 0 ? abandoned.join(",") : "none"}`
37910
+ );
37911
+ return `${dirs} account dir(s), ${abandoned.length} abandoned`;
37912
+ }
37913
+ function startAccountsStateCensus() {
37914
+ registerLoop({
37915
+ name: "accounts-state-census",
37916
+ intervalMs: 60 * 60 * 1e3,
37917
+ firstRunDelayMs: 0,
37918
+ run: () => runAccountsStateCensus()
37919
+ });
37920
+ }
37921
+
37736
37922
  // app/lib/cred-refresh-census.ts
37737
37923
  import { readdirSync as readdirSync33, readFileSync as readFileSync57 } from "fs";
37738
37924
  import { homedir as homedir4 } from "os";
37739
37925
  import { join as join67 } from "path";
37740
- var TAG70 = "[claude-auth]";
37926
+ var TAG71 = "[claude-auth]";
37741
37927
  function refreshTokenLength(raw) {
37742
37928
  let data;
37743
37929
  try {
@@ -37781,7 +37967,7 @@ function runCredRefreshCensus(home = homedir4()) {
37781
37967
  const blank = rows.filter(isBlank).length;
37782
37968
  const detail = rows.map((r) => `${r.brand}:${r.refreshTokenLen === null ? "unreadable" : r.refreshTokenLen}`).join(",");
37783
37969
  console.log(
37784
- `${TAG70} op=cred-refresh-census result=${censusResult(rows)} brands=${rows.length} blank=${blank} detail=${detail}`
37970
+ `${TAG71} op=cred-refresh-census result=${censusResult(rows)} brands=${rows.length} blank=${blank} detail=${detail}`
37785
37971
  );
37786
37972
  return `${rows.length} brand(s), ${blank} blank`;
37787
37973
  }
@@ -38707,14 +38893,14 @@ async function fanOut(p) {
38707
38893
  }
38708
38894
 
38709
38895
  // app/lib/channel-delivery/delivery-census.ts
38710
- var WINDOW_MS4 = 60 * 60 * 1e3;
38711
- var records3 = [];
38896
+ var WINDOW_MS5 = 60 * 60 * 1e3;
38897
+ var records4 = [];
38712
38898
  function noteTurnEnd(door, role, outcome, now) {
38713
- records3.push({ door, role, outcome, atMs: now });
38899
+ records4.push({ door, role, outcome, atMs: now });
38714
38900
  }
38715
38901
  function readDeliveryCensus(door, now) {
38716
- records3 = records3.filter((r) => now - r.atMs <= WINDOW_MS4);
38717
- const mine = records3.filter((r) => r.door === door);
38902
+ records4 = records4.filter((r) => now - r.atMs <= WINDOW_MS5);
38903
+ const mine = records4.filter((r) => r.door === door);
38718
38904
  const undelivered = mine.filter((r) => r.outcome === "undelivered");
38719
38905
  return {
38720
38906
  turnEnds: mine.length,
@@ -39429,9 +39615,9 @@ var WaGateway = class {
39429
39615
  };
39430
39616
 
39431
39617
  // app/lib/whatsapp/gateway/duplicate-sender-audit.ts
39432
- function findDuplicateSenderGroups(records4) {
39618
+ function findDuplicateSenderGroups(records5) {
39433
39619
  const buckets2 = /* @__PURE__ */ new Map();
39434
- for (const r of records4) {
39620
+ for (const r of records5) {
39435
39621
  if (r.channel !== "whatsapp" || !r.senderId) continue;
39436
39622
  const canonical = normalizeTarget(r.senderId);
39437
39623
  if (!canonical) continue;
@@ -39452,9 +39638,9 @@ function logDuplicateSenderGroups(groups) {
39452
39638
  );
39453
39639
  }
39454
39640
  }
39455
- function findCrossAccountSenderGroups(records4) {
39641
+ function findCrossAccountSenderGroups(records5) {
39456
39642
  const byCanonical = /* @__PURE__ */ new Map();
39457
- for (const r of records4) {
39643
+ for (const r of records5) {
39458
39644
  if (r.channel !== "whatsapp" || !r.senderId || !r.accountId) continue;
39459
39645
  const canonical = normalizeTarget(r.senderId);
39460
39646
  if (!canonical) continue;
@@ -40982,14 +41168,14 @@ function makeFileDelivery(opts) {
40982
41168
  }
40983
41169
 
40984
41170
  // app/lib/webchat/file-delivery.ts
40985
- var TAG71 = "[webchat-adaptor]";
41171
+ var TAG72 = "[webchat-adaptor]";
40986
41172
  function platformRoot2() {
40987
41173
  return process.env.MAXY_PLATFORM_ROOT || "";
40988
41174
  }
40989
41175
  function makeWebchatSendFile(entry3) {
40990
41176
  return async (filePath) => {
40991
41177
  if (!entry3.accountId) {
40992
- console.error(`${TAG71} file-delivery reject reason=no-account sender=${entry3.senderId}`);
41178
+ console.error(`${TAG72} file-delivery reject reason=no-account sender=${entry3.senderId}`);
40993
41179
  return { ok: false, error: "no-account" };
40994
41180
  }
40995
41181
  const accountDir = resolve50(platformRoot2(), "..", "data/accounts", entry3.accountId);
@@ -40997,14 +41183,14 @@ function makeWebchatSendFile(entry3) {
40997
41183
  const resolved = realpathSync9(filePath);
40998
41184
  const accountResolved = realpathSync9(accountDir);
40999
41185
  if (!resolved.startsWith(accountResolved + "/")) {
41000
- console.error(`${TAG71} file-delivery reject reason=outside_account_directory sender=${entry3.senderId}`);
41186
+ console.error(`${TAG72} file-delivery reject reason=outside_account_directory sender=${entry3.senderId}`);
41001
41187
  return { ok: false, error: "outside-account" };
41002
41188
  }
41003
41189
  return { ok: true };
41004
41190
  } catch (err) {
41005
41191
  const code = err.code;
41006
41192
  console.error(
41007
- `${TAG71} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry3.senderId}`
41193
+ `${TAG72} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry3.senderId}`
41008
41194
  );
41009
41195
  return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
41010
41196
  }
@@ -41013,7 +41199,7 @@ function makeWebchatSendFile(entry3) {
41013
41199
  function makeWebchatFileDelivery(entry3) {
41014
41200
  return makeFileDelivery({
41015
41201
  entry: entry3,
41016
- tag: TAG71,
41202
+ tag: TAG72,
41017
41203
  channel: "webchat",
41018
41204
  sendFile: makeWebchatSendFile(entry3),
41019
41205
  deferUntilVerdict: true
@@ -41092,7 +41278,7 @@ function buildPublicWebchatSpawnRequest(input) {
41092
41278
  }
41093
41279
 
41094
41280
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
41095
- var TAG72 = "[whatsapp-adaptor]";
41281
+ var TAG73 = "[whatsapp-adaptor]";
41096
41282
  var SEND_USER_FILE2 = "SendUserFile";
41097
41283
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
41098
41284
  function platformRoot3() {
@@ -41110,7 +41296,7 @@ function makeWhatsAppSendFile(entry3, maxyAccountId) {
41110
41296
  });
41111
41297
  if (result.ok) return { ok: true };
41112
41298
  console.error(
41113
- `${TAG72} file-delivery reject reason=send-failed sender=${entry3.senderId} status=${result.status} message=${result.error}`
41299
+ `${TAG73} file-delivery reject reason=send-failed sender=${entry3.senderId} status=${result.status} message=${result.error}`
41114
41300
  );
41115
41301
  return { ok: false, error: result.error };
41116
41302
  };
@@ -41118,7 +41304,7 @@ function makeWhatsAppSendFile(entry3, maxyAccountId) {
41118
41304
  function makeWhatsAppFileDelivery(entry3, maxyAccountId) {
41119
41305
  const shared = makeFileDelivery({
41120
41306
  entry: entry3,
41121
- tag: TAG72,
41307
+ tag: TAG73,
41122
41308
  channel: "whatsapp",
41123
41309
  sendFile: makeWhatsAppSendFile(entry3, maxyAccountId)
41124
41310
  });
@@ -41153,7 +41339,7 @@ function makeWhatsAppFileDelivery(entry3, maxyAccountId) {
41153
41339
  if (!delivered) {
41154
41340
  const fileField = call2.filePath !== void 0 ? ` file=${call2.filePath}` : "";
41155
41341
  console.error(
41156
- `${TAG72} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
41342
+ `${TAG73} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
41157
41343
  );
41158
41344
  }
41159
41345
  }
@@ -41319,7 +41505,7 @@ function whatsappDoorLabel(doorId) {
41319
41505
  }
41320
41506
 
41321
41507
  // app/lib/channel-delivery/refusal-notice.ts
41322
- var TAG73 = "[channel-delivery]";
41508
+ var TAG74 = "[channel-delivery]";
41323
41509
  var notified = /* @__PURE__ */ new Set();
41324
41510
  function refusalNoticeText(reason) {
41325
41511
  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.`;
@@ -41328,19 +41514,19 @@ async function sendRefusalNotice(input) {
41328
41514
  if (notified.has(input.doorId)) return;
41329
41515
  const fields = `channel=${input.channel} sender=${input.sender} reason=${input.reason}`;
41330
41516
  if (!input.send) {
41331
- console.error(`${TAG73} op=refusal-notice-failed ${fields} error=no-target`);
41517
+ console.error(`${TAG74} op=refusal-notice-failed ${fields} error=no-target`);
41332
41518
  return;
41333
41519
  }
41334
41520
  try {
41335
41521
  await input.send(guardOutbound(input.sender, refusalNoticeText(input.reason)));
41336
41522
  } catch (e) {
41337
41523
  console.error(
41338
- `${TAG73} op=refusal-notice-failed ${fields} error=${e instanceof Error ? e.message : String(e)}`
41524
+ `${TAG74} op=refusal-notice-failed ${fields} error=${e instanceof Error ? e.message : String(e)}`
41339
41525
  );
41340
41526
  return;
41341
41527
  }
41342
41528
  notified.add(input.doorId);
41343
- console.error(`${TAG73} op=refusal-notice ${fields} status=${input.status}`);
41529
+ console.error(`${TAG74} op=refusal-notice ${fields} status=${input.status}`);
41344
41530
  }
41345
41531
  function clearRefusalNotice(doorId) {
41346
41532
  notified.delete(doorId);
@@ -41614,6 +41800,19 @@ function createTelegramChannelRoutes(deps) {
41614
41800
  return app81;
41615
41801
  }
41616
41802
 
41803
+ // app/lib/telegram/operator-sent-suppression.ts
41804
+ var OPERATOR_SENT_LATCH_MS = 3e5;
41805
+ var armed2 = /* @__PURE__ */ new Map();
41806
+ function armOperatorSent(key2, now) {
41807
+ armed2.set(key2, now);
41808
+ }
41809
+ function takeOperatorSent(key2, now) {
41810
+ const at = armed2.get(key2);
41811
+ if (at === void 0) return false;
41812
+ armed2.delete(key2);
41813
+ return now - at <= OPERATOR_SENT_LATCH_MS;
41814
+ }
41815
+
41617
41816
  // app/lib/telegram/gateway/telegram-spawn-census.ts
41618
41817
  var byBot = /* @__PURE__ */ new Map();
41619
41818
  function bucket(botId) {
@@ -41841,6 +42040,11 @@ var TelegramGateway = class {
41841
42040
  const willDeliverNow = this.hub.isReady(key2);
41842
42041
  const source = input.source ?? "user";
41843
42042
  this.replies.set(key2, input.reply);
42043
+ if (source === "operator-sent") {
42044
+ const at = Date.now();
42045
+ armOperatorSent(key2, at);
42046
+ noteManualRecorded(key2, at);
42047
+ }
41844
42048
  let standingRules;
41845
42049
  if (input.role === "admin" && this.deps.fetchStandingRules) {
41846
42050
  const res = await this.deps.fetchStandingRules(input.accountId);
@@ -42012,10 +42216,10 @@ function buildTelegramSpawnRequest(input) {
42012
42216
  }
42013
42217
 
42014
42218
  // server/telegram-ensure-session.ts
42015
- var TAG74 = "[telegram-inbound]";
42219
+ var TAG75 = "[telegram-inbound]";
42016
42220
  function reportReused(botId, channelKey, result) {
42017
42221
  if (!result.reused) return;
42018
- console.error(`${TAG74} op=spawn-reused botId=${botId} key=${channelKey} sessionId=${result.sessionId}`);
42222
+ console.error(`${TAG75} op=spawn-reused botId=${botId} key=${channelKey} sessionId=${result.sessionId}`);
42019
42223
  }
42020
42224
  function buildTelegramEnsureSession(deps) {
42021
42225
  return async (input) => {
@@ -42079,10 +42283,10 @@ function buildTelegramEnsureSession(deps) {
42079
42283
  return;
42080
42284
  }
42081
42285
  console.error(
42082
- `${TAG74} op=telegram-spawn-door botId=${input.botId} role=specialist door=rc specialist=${input.specialist}`
42286
+ `${TAG75} op=telegram-spawn-door botId=${input.botId} role=specialist door=rc specialist=${input.specialist}`
42083
42287
  );
42084
42288
  console.error(
42085
- `${TAG74} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=specialist specialist=${input.specialist}`
42289
+ `${TAG75} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=specialist specialist=${input.specialist}`
42086
42290
  );
42087
42291
  reportReused(input.botId, req.channelKey, result2);
42088
42292
  noteSpawnOk2(input.botId);
@@ -42117,10 +42321,10 @@ function buildTelegramEnsureSession(deps) {
42117
42321
  return;
42118
42322
  }
42119
42323
  console.error(
42120
- `${TAG74} op=telegram-spawn-door botId=${input.botId} role=public door=rc agent=${input.agentSlug}`
42324
+ `${TAG75} op=telegram-spawn-door botId=${input.botId} role=public door=rc agent=${input.agentSlug}`
42121
42325
  );
42122
42326
  console.error(
42123
- `${TAG74} op=spawn botId=${input.botId} sessionId=${result2.sessionId} targetAccountId=${input.accountId} role=public`
42327
+ `${TAG75} op=spawn botId=${input.botId} sessionId=${result2.sessionId} targetAccountId=${input.accountId} role=public`
42124
42328
  );
42125
42329
  reportReused(input.botId, req.channelKey, result2);
42126
42330
  noteSpawnOk2(input.botId);
@@ -42144,7 +42348,7 @@ function buildTelegramEnsureSession(deps) {
42144
42348
  return;
42145
42349
  }
42146
42350
  console.error(
42147
- `${TAG74} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=admin`
42351
+ `${TAG75} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=admin`
42148
42352
  );
42149
42353
  reportReused(input.botId, req.channelKey, result);
42150
42354
  noteSpawnOk2(input.botId);
@@ -42163,7 +42367,7 @@ function buildTelegramEnsureSession(deps) {
42163
42367
  import { realpathSync as realpathSync10 } from "fs";
42164
42368
  import { readFile as readFile15, stat as stat10 } from "fs/promises";
42165
42369
  import { resolve as resolve51, basename as basename18 } from "path";
42166
- var TAG75 = "[telegram:outbound]";
42370
+ var TAG76 = "[telegram:outbound]";
42167
42371
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
42168
42372
  async function sendTelegramDocument(input) {
42169
42373
  const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
@@ -42180,10 +42384,10 @@ async function sendTelegramDocument(input) {
42180
42384
  } catch (err) {
42181
42385
  const code = err.code;
42182
42386
  if (code === "ENOENT") {
42183
- console.error(`${TAG75} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
42387
+ console.error(`${TAG76} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
42184
42388
  return { ok: false, status: 500, error: `Account directory not resolvable for ${maxyAccountId}` };
42185
42389
  }
42186
- console.error(`${TAG75} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
42390
+ console.error(`${TAG76} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
42187
42391
  return { ok: false, status: 500, error: String(err) };
42188
42392
  }
42189
42393
  let resolvedPath;
@@ -42192,14 +42396,14 @@ async function sendTelegramDocument(input) {
42192
42396
  } catch (err) {
42193
42397
  const code = err.code;
42194
42398
  if (code === "ENOENT") {
42195
- console.error(`${TAG75} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
42399
+ console.error(`${TAG76} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
42196
42400
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
42197
42401
  }
42198
- console.error(`${TAG75} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
42402
+ console.error(`${TAG76} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
42199
42403
  return { ok: false, status: 500, error: String(err) };
42200
42404
  }
42201
42405
  if (!resolvedPath.startsWith(accountResolved + "/")) {
42202
- console.error(`${TAG75} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
42406
+ console.error(`${TAG76} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
42203
42407
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
42204
42408
  }
42205
42409
  const fileStat = await stat10(resolvedPath);
@@ -42232,24 +42436,24 @@ async function sendTelegramDocument(input) {
42232
42436
  error = e instanceof Error ? e.message : String(e);
42233
42437
  }
42234
42438
  console.error(
42235
- `${TAG75} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok2}` + (messageId !== void 0 ? ` id=${messageId}` : "")
42439
+ `${TAG76} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok2}` + (messageId !== void 0 ? ` id=${messageId}` : "")
42236
42440
  );
42237
42441
  return ok2 ? { ok: true, messageId } : { ok: false, status: 500, error };
42238
42442
  }
42239
42443
 
42240
42444
  // app/lib/telegram/outbound/file-delivery.ts
42241
- var TAG76 = "[telegram:outbound]";
42445
+ var TAG77 = "[telegram:outbound]";
42242
42446
  function platformRoot4() {
42243
42447
  return process.env.MAXY_PLATFORM_ROOT || "";
42244
42448
  }
42245
42449
  function makeTelegramSendFile(entry3, botToken) {
42246
42450
  return async (filePath, caption) => {
42247
42451
  if (!botToken) {
42248
- console.error(`${TAG76} file-delivery reject reason=no-bot-token sender=${entry3.senderId} role=${entry3.role}`);
42452
+ console.error(`${TAG77} file-delivery reject reason=no-bot-token sender=${entry3.senderId} role=${entry3.role}`);
42249
42453
  return { ok: false, error: "no-bot-token" };
42250
42454
  }
42251
42455
  if (entry3.replyTarget == null) {
42252
- console.error(`${TAG76} file-delivery reject reason=no-reply-target sender=${entry3.senderId} role=${entry3.role}`);
42456
+ console.error(`${TAG77} file-delivery reject reason=no-reply-target sender=${entry3.senderId} role=${entry3.role}`);
42253
42457
  return { ok: false, error: "no-reply-target" };
42254
42458
  }
42255
42459
  const result = await sendTelegramDocument({
@@ -42266,7 +42470,7 @@ function makeTelegramSendFile(entry3, botToken) {
42266
42470
  function makeTelegramFileDelivery(entry3, botToken) {
42267
42471
  return makeFileDelivery({
42268
42472
  entry: entry3,
42269
- tag: TAG76,
42473
+ tag: TAG77,
42270
42474
  channel: "telegram",
42271
42475
  sendFile: makeTelegramSendFile(entry3, botToken)
42272
42476
  });
@@ -42334,23 +42538,23 @@ function noteCardCrossChat(sessionId) {
42334
42538
  entry2(sessionId).crossChat += 1;
42335
42539
  }
42336
42540
  function cardCensusLines() {
42337
- let armed2 = 0;
42541
+ let armed3 = 0;
42338
42542
  let suppressed = 0;
42339
42543
  let delivered = 0;
42340
42544
  let persisted = 0;
42341
42545
  let sent = 0;
42342
42546
  let crossChat = 0;
42343
42547
  for (const e of sessions3.values()) {
42344
- armed2 += e.armed;
42548
+ armed3 += e.armed;
42345
42549
  suppressed += e.suppressed;
42346
42550
  delivered += e.delivered;
42347
42551
  persisted += e.persisted;
42348
42552
  sent += e.sent;
42349
42553
  crossChat += e.crossChat;
42350
42554
  }
42351
- const silent = Math.max(0, armed2 - suppressed - delivered);
42555
+ const silent = Math.max(0, armed3 - suppressed - delivered);
42352
42556
  return [
42353
- `[telegram-card-suppression-census] sessions=${sessions3.size} armed=${armed2} suppressed=${suppressed} silent-turns=${silent} sent=${sent} persisted=${persisted} cross-chat=${crossChat}`
42557
+ `[telegram-card-suppression-census] sessions=${sessions3.size} armed=${armed3} suppressed=${suppressed} silent-turns=${silent} sent=${sent} persisted=${persisted} cross-chat=${crossChat}`
42354
42558
  ];
42355
42559
  }
42356
42560
 
@@ -42403,6 +42607,7 @@ function startTelegramNativeFileFollower(input) {
42403
42607
  let lastChatActionAt = Number.NEGATIVE_INFINITY;
42404
42608
  let turnOpen = false;
42405
42609
  let cardSentThisTurn = false;
42610
+ let operatorSentThisTurn = false;
42406
42611
  let heartbeat = null;
42407
42612
  const send = (driver2) => {
42408
42613
  lastChatActionAt = Date.now();
@@ -42427,7 +42632,15 @@ function startTelegramNativeFileFollower(input) {
42427
42632
  lastChatActionAt = Number.NEGATIVE_INFINITY;
42428
42633
  };
42429
42634
  const onTurnStart = (kind) => {
42430
- if (kind === "inbound") cardSentThisTurn = false;
42635
+ if (kind === "inbound") {
42636
+ cardSentThisTurn = false;
42637
+ operatorSentThisTurn = takeOperatorSent(channelKey, Date.now());
42638
+ if (operatorSentThisTurn) {
42639
+ console.error(
42640
+ `[telegram-public] op=operator-sent-armed sessionId=${input.sessionId.slice(0, 8)} key=${channelKey}`
42641
+ );
42642
+ }
42643
+ }
42431
42644
  if (turnOpen) return;
42432
42645
  turnOpen = true;
42433
42646
  stopHeartbeat();
@@ -42566,6 +42779,13 @@ function startTelegramNativeFileFollower(input) {
42566
42779
  // Ungated by design: whatever woke the turn, the reader and the chats show
42567
42780
  // the same thing. Task 2666 adds the one exception above.
42568
42781
  onAnswer: async (answer) => {
42782
+ if (operatorSentThisTurn) {
42783
+ noteManualSuppressed(channelKey, Date.now());
42784
+ console.error(
42785
+ `[telegram-public] op=text-suppressed sessionId=${input.sessionId.slice(0, 8)} messageId=${answer.messageId} reason=operator-sent`
42786
+ );
42787
+ return;
42788
+ }
42569
42789
  if (cardSentThisTurn) {
42570
42790
  noteCardSuppressed(input.sessionId);
42571
42791
  console.error(
@@ -42707,7 +42927,7 @@ function adminFanTargets(p) {
42707
42927
 
42708
42928
  // app/lib/channel-pty-bridge/public-session-end-review.ts
42709
42929
  import { randomUUID as randomUUID14 } from "crypto";
42710
- var TAG77 = "[public-session-review]";
42930
+ var TAG78 = "[public-session-review]";
42711
42931
  var CONSUMED_CAP = 500;
42712
42932
  var consumed = /* @__PURE__ */ new Set();
42713
42933
  function consumeOnce(sessionId) {
@@ -42734,7 +42954,7 @@ async function fetchJsonl(sessionId) {
42734
42954
  return await res.text();
42735
42955
  } catch (err) {
42736
42956
  console.error(
42737
- `${TAG77} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
42957
+ `${TAG78} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
42738
42958
  );
42739
42959
  return null;
42740
42960
  }
@@ -42758,7 +42978,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
42758
42978
  const res = await fetch(url);
42759
42979
  if (!res.ok) {
42760
42980
  console.error(
42761
- `${TAG77} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
42981
+ `${TAG78} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
42762
42982
  );
42763
42983
  return [];
42764
42984
  }
@@ -42766,7 +42986,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
42766
42986
  return Array.isArray(body.writes) ? body.writes : [];
42767
42987
  } catch (err) {
42768
42988
  console.error(
42769
- `${TAG77} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
42989
+ `${TAG78} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
42770
42990
  );
42771
42991
  return [];
42772
42992
  }
@@ -42795,7 +43015,7 @@ function composeInitialMessage(input) {
42795
43015
  }
42796
43016
  async function dispatchReviewer(input, initialMessage) {
42797
43017
  const sessionId = randomUUID14();
42798
- console.log(`${TAG77} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
43018
+ console.log(`${TAG78} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
42799
43019
  const spawned = await managerRcSpawn({
42800
43020
  sessionId,
42801
43021
  initialMessage,
@@ -42819,21 +43039,21 @@ async function firePublicSessionEndReview(input) {
42819
43039
  const dispatchedAt = Date.now();
42820
43040
  if (consumed.has(input.sessionId)) {
42821
43041
  console.log(
42822
- `${TAG77} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
43042
+ `${TAG78} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
42823
43043
  );
42824
43044
  return;
42825
43045
  }
42826
43046
  const jsonl = await fetchJsonl(input.sessionId);
42827
43047
  if (!jsonl) {
42828
43048
  console.log(
42829
- `${TAG77} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
43049
+ `${TAG78} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
42830
43050
  );
42831
43051
  return;
42832
43052
  }
42833
43053
  const operatorTurns = countOperatorTurns(jsonl);
42834
43054
  if (operatorTurns === 0) {
42835
43055
  console.log(
42836
- `${TAG77} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
43056
+ `${TAG78} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
42837
43057
  );
42838
43058
  return;
42839
43059
  }
@@ -42847,19 +43067,19 @@ async function firePublicSessionEndReview(input) {
42847
43067
  });
42848
43068
  if (!consumeOnce(input.sessionId)) {
42849
43069
  console.log(
42850
- `${TAG77} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
43070
+ `${TAG78} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
42851
43071
  );
42852
43072
  return;
42853
43073
  }
42854
43074
  const dispatched = await dispatchReviewer(input, initialMessage);
42855
43075
  if (!dispatched.ok) {
42856
43076
  console.error(
42857
- `${TAG77} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
43077
+ `${TAG78} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
42858
43078
  );
42859
43079
  return;
42860
43080
  }
42861
43081
  console.log(
42862
- `${TAG77} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
43082
+ `${TAG78} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
42863
43083
  );
42864
43084
  }
42865
43085
 
@@ -43128,10 +43348,10 @@ function* walkTelegramSidecars() {
43128
43348
  }
43129
43349
 
43130
43350
  // server/telegram-group-census.ts
43131
- var TAG78 = "[telegram-audit]";
43351
+ var TAG79 = "[telegram-audit]";
43132
43352
  var PRESENT_STATUSES = /* @__PURE__ */ new Set(["member", "administrator", "creator"]);
43133
43353
  function buildGroupCensusLine(c) {
43134
- return `${TAG78} op=group-census bots=${c.bots} groups=${c.groups} present=${c.present} absent=${c.absent} privacyOff=${c.privacyOff}`;
43354
+ return `${TAG79} op=group-census bots=${c.bots} groups=${c.groups} present=${c.present} absent=${c.absent} privacyOff=${c.privacyOff}`;
43135
43355
  }
43136
43356
  async function runTelegramGroupCensus(accounts, deps) {
43137
43357
  const counts = { bots: 0, groups: 0, present: 0, absent: 0, privacyOff: 0 };
@@ -43162,10 +43382,10 @@ async function runTelegramGroupCensus(accounts, deps) {
43162
43382
  }
43163
43383
 
43164
43384
  // server/telegram-channel-census.ts
43165
- var TAG79 = "[telegram-audit]";
43385
+ var TAG80 = "[telegram-audit]";
43166
43386
  var ADMIN_STATUSES = /* @__PURE__ */ new Set(["administrator", "creator"]);
43167
43387
  function buildChannelCensusLine(c) {
43168
- return `${TAG79} op=channel-census bots=${c.bots} channels=${c.channels} admin=${c.admin} notAdmin=${c.notAdmin} noPostRight=${c.noPostRight}`;
43388
+ return `${TAG80} op=channel-census bots=${c.bots} channels=${c.channels} admin=${c.admin} notAdmin=${c.notAdmin} noPostRight=${c.noPostRight}`;
43169
43389
  }
43170
43390
  async function runTelegramChannelCensus(accounts, deps) {
43171
43391
  const counts = { bots: 0, channels: 0, admin: 0, notAdmin: 0, noPostRight: 0 };
@@ -43197,7 +43417,7 @@ async function runTelegramChannelCensus(accounts, deps) {
43197
43417
  }
43198
43418
 
43199
43419
  // server/telegram-graph-duplicate-census.ts
43200
- var TAG80 = "[telegram-audit]";
43420
+ var TAG81 = "[telegram-audit]";
43201
43421
  var DUPLICATE_QUERY = `
43202
43422
  MATCH (m:Message)
43203
43423
  WHERE m.accountId = $accountId AND m.channel = 'telegram'
@@ -43224,12 +43444,12 @@ async function runTelegramGraphDuplicateCensus(accountIds, deps) {
43224
43444
  const sessions4 = asSessions(Array.isArray(row.sessions) ? row.sessions : []);
43225
43445
  const named = sessions4.length > 0 ? ` ids=${sessions4.join(",")}` : "";
43226
43446
  lines.push(
43227
- `${TAG80} op=graph-duplicate accountId=${accountId} messages=${messages} sessions=${sessions4.length}${named}`
43447
+ `${TAG81} op=graph-duplicate accountId=${accountId} messages=${messages} sessions=${sessions4.length}${named}`
43228
43448
  );
43229
43449
  } catch (err) {
43230
43450
  const reason = err instanceof Error ? err.message : String(err);
43231
43451
  lines.push(
43232
- `${TAG80} FAIL op=graph-duplicate accountId=${accountId} reason=${reason.slice(0, 100)}`
43452
+ `${TAG81} FAIL op=graph-duplicate accountId=${accountId} reason=${reason.slice(0, 100)}`
43233
43453
  );
43234
43454
  }
43235
43455
  }
@@ -43237,9 +43457,9 @@ async function runTelegramGraphDuplicateCensus(accountIds, deps) {
43237
43457
  }
43238
43458
 
43239
43459
  // server/telegram-identity-census.ts
43240
- var TAG81 = "[telegram-audit]";
43460
+ var TAG82 = "[telegram-audit]";
43241
43461
  function buildIdentityCensusLine(c) {
43242
- return `${TAG81} op=identity-census bots=${c.bots} ids=${c.ids} bound=${c.bound} unbound=${c.unbound} ambiguous=${c.ambiguous}`;
43462
+ return `${TAG82} op=identity-census bots=${c.bots} ids=${c.ids} bound=${c.bound} unbound=${c.unbound} ambiguous=${c.ambiguous}`;
43243
43463
  }
43244
43464
  async function runTelegramIdentityCensus(accounts, deps) {
43245
43465
  const counts = { bots: 0, ids: 0, bound: 0, unbound: 0, ambiguous: 0 };
@@ -44213,16 +44433,16 @@ function runDuplicateSenderAudit() {
44213
44433
  const configDir = process.env.CLAUDE_CONFIG_DIR;
44214
44434
  if (!configDir) return;
44215
44435
  const projectsRoot = join76(configDir, "projects");
44216
- const records4 = [];
44436
+ const records5 = [];
44217
44437
  for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
44218
44438
  if (isSubagent || archived) continue;
44219
44439
  const sessionId = jsonlPath.slice(jsonlPath.lastIndexOf("/") + 1).replace(/\.jsonl$/, "");
44220
44440
  const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
44221
44441
  const meta = readSidecarMeta(metaPath);
44222
- records4.push({ sessionId, channel: meta.channel, senderId: meta.senderId, accountId: meta.accountId });
44442
+ records5.push({ sessionId, channel: meta.channel, senderId: meta.senderId, accountId: meta.accountId });
44223
44443
  }
44224
- logDuplicateSenderGroups(findDuplicateSenderGroups(records4));
44225
- logCrossAccountSenderGroups(findCrossAccountSenderGroups(records4));
44444
+ logDuplicateSenderGroups(findDuplicateSenderGroups(records5));
44445
+ logCrossAccountSenderGroups(findCrossAccountSenderGroups(records5));
44226
44446
  const outbound = waGateway.takeOutboundAccountCensus();
44227
44447
  console.error(
44228
44448
  `[whatsapp-native] op=outbound-account-mismatch sends=${outbound.sends} mismatched=${outbound.mismatched}`
@@ -45584,6 +45804,13 @@ registerLoop({
45584
45804
  `[telegram-instruct-census] op=rejected detail=${err instanceof Error ? err.message : String(err)}`
45585
45805
  );
45586
45806
  }
45807
+ try {
45808
+ runTelegramManualSendCensus(Date.now());
45809
+ } catch (err) {
45810
+ console.error(
45811
+ `[telegram-manual-census] op=rejected detail=${err instanceof Error ? err.message : String(err)}`
45812
+ );
45813
+ }
45587
45814
  try {
45588
45815
  runTelegramNotifyPublishCensus(Date.now());
45589
45816
  } catch (err) {
@@ -46080,12 +46307,12 @@ startAuthHealthHeartbeat();
46080
46307
  const loopHist = monitorEventLoopDelay({ resolution: 50 });
46081
46308
  loopHist.enable();
46082
46309
  const LOUD_THRESHOLD_MS = 1e3;
46083
- const WINDOW_MS5 = 3e4;
46310
+ const WINDOW_MS6 = 3e4;
46084
46311
  registerLoop({
46085
46312
  name: "event-loop-lag",
46086
- intervalMs: WINDOW_MS5,
46313
+ intervalMs: WINDOW_MS6,
46087
46314
  run: () => {
46088
- const w = readAndResetWindow(loopHist, WINDOW_MS5);
46315
+ const w = readAndResetWindow(loopHist, WINDOW_MS6);
46089
46316
  const line = formatLagWindow(w);
46090
46317
  if (w.maxMs >= LOUD_THRESHOLD_MS) console.error(line);
46091
46318
  else console.log(line);
@@ -46343,6 +46570,7 @@ registerLoop({
46343
46570
  });
46344
46571
  startGraphHealthTimer();
46345
46572
  startSharedFolderCensus();
46573
+ startAccountsStateCensus();
46346
46574
  startCredRefreshCensus();
46347
46575
  void migrateUploads().catch((err) => console.error(`[uploads-migrate] failed err="${err instanceof Error ? err.message : String(err)}"`)).finally(() => {
46348
46576
  void startFileWatcher().catch((err) => {