@rubytech/create-siteoffice-code 0.1.259 → 0.1.264

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 (177) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +8 -8
  3. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +14 -5
  4. package/payload/platform/plugins/docs/references/admin-ui.md +10 -3
  5. package/payload/platform/plugins/docs/references/deployment.md +1 -1
  6. package/payload/platform/plugins/docs/references/platform.md +2 -0
  7. package/payload/platform/scripts/check-no-esm-require.mjs +4 -0
  8. package/payload/platform/scripts/smoke-boot-services.sh +4 -4
  9. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  10. package/payload/platform/services/claude-session-manager/dist/http-server.js +47 -0
  11. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  12. package/payload/platform/services/claude-session-manager/dist/index.d.ts +1 -1
  13. package/payload/platform/services/claude-session-manager/dist/index.d.ts.map +1 -1
  14. package/payload/platform/services/claude-session-manager/dist/index.js +1 -0
  15. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  16. package/payload/platform/services/claude-session-manager/dist/input-postcondition.d.ts +22 -0
  17. package/payload/platform/services/claude-session-manager/dist/input-postcondition.d.ts.map +1 -0
  18. package/payload/platform/services/claude-session-manager/dist/input-postcondition.js +50 -0
  19. package/payload/platform/services/claude-session-manager/dist/input-postcondition.js.map +1 -0
  20. package/payload/platform/services/claude-session-manager/dist/install-log-sink.d.ts +2 -0
  21. package/payload/platform/services/claude-session-manager/dist/install-log-sink.d.ts.map +1 -0
  22. package/payload/platform/services/claude-session-manager/dist/install-log-sink.js +6 -0
  23. package/payload/platform/services/claude-session-manager/dist/install-log-sink.js.map +1 -0
  24. package/payload/platform/services/claude-session-manager/dist/log-sink.d.ts +12 -0
  25. package/payload/platform/services/claude-session-manager/dist/log-sink.d.ts.map +1 -0
  26. package/payload/platform/services/claude-session-manager/dist/log-sink.js +48 -0
  27. package/payload/platform/services/claude-session-manager/dist/log-sink.js.map +1 -0
  28. package/payload/server/{chunk-76HRO7NX.js → chunk-ZC63DFWF.js} +12 -4
  29. package/payload/server/maxy-edge.js +1 -1
  30. package/payload/server/public/assets/AdminShell-8JLQM1Mz.js +1 -0
  31. package/payload/server/public/assets/{Checkbox-DmDxpqVv.js → Checkbox-CuFJh7lI.js} +1 -1
  32. package/payload/server/public/assets/{admin-COUV-jgt.js → admin-D4nsdmKU.js} +1 -1
  33. package/payload/server/public/assets/{arc-B2CweJq3.js → arc-BW1WhwmV.js} +1 -1
  34. package/payload/server/public/assets/architecture-YZFGNWBL-xHbVjatf.js +1 -0
  35. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DP2o-MFV.js → architectureDiagram-Q4EWVU46-D-ODjJS5.js} +1 -1
  36. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DO4mcYDJ.js → blockDiagram-DXYQGD6D-DFuVe8f9.js} +1 -1
  37. package/payload/server/public/assets/browser-DIKmvDl5.js +1 -0
  38. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Sy1giHbj.js → c4Diagram-AHTNJAMY-CeVLfznT.js} +1 -1
  39. package/payload/server/public/assets/channel-Db8J_1Rl.js +1 -0
  40. package/payload/server/public/assets/{chunk-2KRD3SAO-CKsCYCsN.js → chunk-2KRD3SAO-CKn-bEkI.js} +1 -1
  41. package/payload/server/public/assets/{chunk-336JU56O-C0-P-aUF.js → chunk-336JU56O-BTaLfR3a.js} +2 -2
  42. package/payload/server/public/assets/chunk-426QAEUC-BzTbm4pP.js +1 -0
  43. package/payload/server/public/assets/{chunk-4BX2VUAB-B8bqAmBa.js → chunk-4BX2VUAB-B2mSmC97.js} +1 -1
  44. package/payload/server/public/assets/{chunk-4TB4RGXK-D1k0VSlW.js → chunk-4TB4RGXK-SiUyOXVG.js} +1 -1
  45. package/payload/server/public/assets/{chunk-55IACEB6-B-p_QNqz.js → chunk-55IACEB6-JLT9m7Hd.js} +1 -1
  46. package/payload/server/public/assets/{chunk-5FUZZQ4R-D6U6tV_j.js → chunk-5FUZZQ4R-BcQ33LnT.js} +1 -1
  47. package/payload/server/public/assets/{chunk-5PVQY5BW-CYK76xfs.js → chunk-5PVQY5BW-CT7C1Tik.js} +1 -1
  48. package/payload/server/public/assets/{chunk-67CJDMHE-BC9js-lf.js → chunk-67CJDMHE-CCdP3Pdx.js} +1 -1
  49. package/payload/server/public/assets/{chunk-7N4EOEYR-4j2OqKkv.js → chunk-7N4EOEYR-bXgVzdM9.js} +1 -1
  50. package/payload/server/public/assets/{chunk-AA7GKIK3-Coen-fXN.js → chunk-AA7GKIK3-CYeDvY8B.js} +1 -1
  51. package/payload/server/public/assets/{chunk-BSJP7CBP-CAiOBvec.js → chunk-BSJP7CBP-U0Fi-Ers.js} +1 -1
  52. package/payload/server/public/assets/{chunk-CIAEETIT-AJzzpZVb.js → chunk-CIAEETIT-C8QrV6Gg.js} +1 -1
  53. package/payload/server/public/assets/{chunk-EDXVE4YY-BL4BKozX.js → chunk-EDXVE4YY-CK3uI2u6.js} +1 -1
  54. package/payload/server/public/assets/{chunk-ENJZ2VHE-mhAFG8UD.js → chunk-ENJZ2VHE-Ddp8Otnw.js} +1 -1
  55. package/payload/server/public/assets/{chunk-FMBD7UC4-H231gZA_.js → chunk-FMBD7UC4-DxAQS3UB.js} +1 -1
  56. package/payload/server/public/assets/{chunk-FOC6F5B3-Cl3ZZjYG.js → chunk-FOC6F5B3-CyIK3zeY.js} +1 -1
  57. package/payload/server/public/assets/{chunk-ICPOFSXX-DOEzvzJa.js → chunk-ICPOFSXX-DHTxymk_.js} +2 -2
  58. package/payload/server/public/assets/{chunk-K5T4RW27-C_ipbUDD.js → chunk-K5T4RW27-7GcDzjyJ.js} +1 -1
  59. package/payload/server/public/assets/{chunk-KGLVRYIC-CTsDNSCU.js → chunk-KGLVRYIC-B7pAr9hf.js} +1 -1
  60. package/payload/server/public/assets/{chunk-LIHQZDEY-DvSXhkGf.js → chunk-LIHQZDEY-DEoU2jrz.js} +1 -1
  61. package/payload/server/public/assets/{chunk-ORNJ4GCN-p574NOI7.js → chunk-ORNJ4GCN-BwMFuN24.js} +1 -1
  62. package/payload/server/public/assets/{chunk-OYMX7WX6-BlEgFM6U.js → chunk-OYMX7WX6-Heg3hegz.js} +1 -1
  63. package/payload/server/public/assets/chunk-QZHKN3VN-CHE_iZO7.js +1 -0
  64. package/payload/server/public/assets/{chunk-U2HBQHQK-B2bDK0jv.js → chunk-U2HBQHQK-Cw2PR7aJ.js} +1 -1
  65. package/payload/server/public/assets/{chunk-X2U36JSP-D69BxKFw.js → chunk-X2U36JSP-C7vBCAQQ.js} +1 -1
  66. package/payload/server/public/assets/{chunk-XPW4576I-Dm-PcyUi.js → chunk-XPW4576I-BA0a8Ygs.js} +1 -1
  67. package/payload/server/public/assets/{chunk-YZCP3GAM-Be8RnXgx.js → chunk-YZCP3GAM-BS8Mybh1.js} +1 -1
  68. package/payload/server/public/assets/{chunk-ZZ45TVLE-Ck8PCTa4.js → chunk-ZZ45TVLE-DmnbQp47.js} +1 -1
  69. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DjquNGFr.js +1 -0
  70. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CcE8sWEP.js +1 -0
  71. package/payload/server/public/assets/clone-xa7JZYoY.js +1 -0
  72. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-CmkW2Eaj.js → cose-bilkent-S5V4N54A-Df_JmB8E.js} +1 -1
  73. package/payload/server/public/assets/{dagre-Dqp-ns8F.js → dagre-D4i6r8zi.js} +1 -1
  74. package/payload/server/public/assets/{dagre-KV5264BT-ZgWWXPLc.js → dagre-KV5264BT-znWS6Wh2.js} +1 -1
  75. package/payload/server/public/assets/{data-gy6QH9c1.js → data-CHqF4bkS.js} +1 -1
  76. package/payload/server/public/assets/{diagram-5BDNPKRD-CTX5-ScM.js → diagram-5BDNPKRD-B6tQBN1Y.js} +1 -1
  77. package/payload/server/public/assets/{diagram-G4DWMVQ6-BovIsO6H.js → diagram-G4DWMVQ6-t73WpYIY.js} +1 -1
  78. package/payload/server/public/assets/{diagram-MMDJMWI5-DcETsQy-.js → diagram-MMDJMWI5-Cm63UOJm.js} +1 -1
  79. package/payload/server/public/assets/{diagram-TYMM5635-yyq6peoZ.js → diagram-TYMM5635-DuTSLjVB.js} +1 -1
  80. package/payload/server/public/assets/{dist-DB-VPj_8.js → dist-BmvZ8OaX.js} +1 -1
  81. package/payload/server/public/assets/{erDiagram-SMLLAGMA-CiNToftB.js → erDiagram-SMLLAGMA-sbHunitX.js} +1 -1
  82. package/payload/server/public/assets/{flatten-BtFI066E.js → flatten-Bo6YRmWl.js} +1 -1
  83. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-Xnl3SpIM.js → flowDiagram-DWJPFMVM-BWOrPuqz.js} +1 -1
  84. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-C1iyWe0f.js → ganttDiagram-T4ZO3ILL-WaLMGard.js} +1 -1
  85. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CmHdoS27.js +1 -0
  86. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-D97pbMQb.js → gitGraphDiagram-UUTBAWPF-CsbIgKDn.js} +1 -1
  87. package/payload/server/public/assets/{graph-qz5tFKqU.js → graph-kwyPyf_b.js} +2 -2
  88. package/payload/server/public/assets/{graph-labels-cZu4pK16.js → graph-labels-pX3uIQys.js} +1 -1
  89. package/payload/server/public/assets/{graphlib-Lq8ijgON.js → graphlib-B8yKFZvc.js} +1 -1
  90. package/payload/server/public/assets/info-OMHHGYJF-DTA9msO-.js +1 -0
  91. package/payload/server/public/assets/infoDiagram-42DDH7IO-Bd55TzsD.js +2 -0
  92. package/payload/server/public/assets/{isEmpty-D6QovjYR.js → isEmpty-D6Kr-M1M.js} +1 -1
  93. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-B8XBdjJn.js → ishikawaDiagram-UXIWVN3A-BFdOsA9f.js} +1 -1
  94. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CZYbiOaQ.js → journeyDiagram-VCZTEJTY-CMQ5XWO9.js} +1 -1
  95. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-B1PybFoh.js → kanban-definition-6JOO6SKY-_RpNNRN7.js} +1 -1
  96. package/payload/server/public/assets/{line-D-tw3hHp.js → line-SkFxjGJ8.js} +1 -1
  97. package/payload/server/public/assets/{linear-BHhXD3cd.js → linear-COm19-p7.js} +1 -1
  98. package/payload/server/public/assets/{mermaid-parser.core-C9RAnysF.js → mermaid-parser.core-Ddok2IAe.js} +2 -2
  99. package/payload/server/public/assets/{mermaid.core-B532LT1r.js → mermaid.core-CihAjXBG.js} +3 -3
  100. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DGlgeeTV.js → mindmap-definition-QFDTVHPH-CJsq3hrO.js} +1 -1
  101. package/payload/server/public/assets/{ordinal-Bl-aM5b9.js → ordinal-BDi6f4xk.js} +1 -1
  102. package/payload/server/public/assets/packet-4T2RLAQJ-6GjqIKgu.js +1 -0
  103. package/payload/server/public/assets/pie-ZZUOXDRM-T72DJ5JR.js +1 -0
  104. package/payload/server/public/assets/{pieDiagram-DEJITSTG-DV9FIWko.js → pieDiagram-DEJITSTG-CSgg8lBB.js} +1 -1
  105. package/payload/server/public/assets/{public-Bu2_Xi0a.js → public-BA9P-3ZV.js} +3 -3
  106. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-Betwya4l.js → quadrantDiagram-34T5L4WZ-TtHxMQtS.js} +1 -1
  107. package/payload/server/public/assets/radar-PYXPWWZC-x-6xW1dE.js +1 -0
  108. package/payload/server/public/assets/{reduce-BD4xUd2c.js → reduce-CGi9ik8i.js} +1 -1
  109. package/payload/server/public/assets/{requirementDiagram-MS252O5E-Cq3vODdg.js → requirementDiagram-MS252O5E-DdYupKtL.js} +1 -1
  110. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-x8krXWcS.js → sankeyDiagram-XADWPNL6-BVfdyUnO.js} +1 -1
  111. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-i-_uH-Yl.js → sequenceDiagram-FGHM5R23-aFLOua6n.js} +1 -1
  112. package/payload/server/public/assets/{src-C1jfwBq0.js → src-DLEIsjER.js} +1 -1
  113. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-il4KqSgI.js → stateDiagram-FHFEXIEX-_3Ix8SPZ.js} +1 -1
  114. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-eVQQEyxx.js +1 -0
  115. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DATdZkA5.js → timeline-definition-GMOUNBTQ-Bs96WqZ7.js} +1 -1
  116. package/payload/server/public/assets/treeView-SZITEDCU-5-6wEryJ.js +1 -0
  117. package/payload/server/public/assets/treemap-W4RFUUIX-DF48Cc86.js +1 -0
  118. package/payload/server/public/assets/{useSelectionMode-A5KItZ2T.js → useSelectionMode-Da9Glxvu.js} +1 -1
  119. package/payload/server/public/assets/{useSelectionMode-C-Ojh7W9.css → useSelectionMode-L9uAHstA.css} +1 -1
  120. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BJh9tJTt.js → vennDiagram-DHZGUBPP-DE_SxsN2.js} +1 -1
  121. package/payload/server/public/assets/wardley-RL74JXVD-duKIa6Hq.js +1 -0
  122. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-EMN1Hdfg.js → wardleyDiagram-NUSXRM2D-DTCdKs6v.js} +1 -1
  123. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DbUWXa7T.js → xychartDiagram-5P7HB3ND-CNIrxADs.js} +1 -1
  124. package/payload/server/public/brand/maxy-black.png +0 -0
  125. package/payload/server/public/brand/maxy-monochrome.png +0 -0
  126. package/payload/server/public/brand/maxy.png +0 -0
  127. package/payload/server/public/brand/siteoffice-black.png +0 -0
  128. package/payload/server/public/brand/siteoffice-favicon-180.png +0 -0
  129. package/payload/server/public/brand/siteoffice-favicon-32.png +0 -0
  130. package/payload/server/public/brand/siteoffice-favicon-512.png +0 -0
  131. package/payload/server/public/brand/siteoffice-horizontal-dark.png +0 -0
  132. package/payload/server/public/brand/siteoffice-horizontal.png +0 -0
  133. package/payload/server/public/brand/siteoffice-icon.svg +8 -0
  134. package/payload/server/public/brand/siteoffice-monochrome.png +0 -0
  135. package/payload/server/public/brand/siteoffice-square.png +0 -0
  136. package/payload/server/public/brand/siteoffice-stacked.png +0 -0
  137. package/payload/server/public/brand/siteoffice-white.png +0 -0
  138. package/payload/server/public/brand-constants.json +2 -2
  139. package/payload/server/public/brand-defaults.css +4 -4
  140. package/payload/server/public/browser.html +18 -0
  141. package/payload/server/public/data.html +6 -6
  142. package/payload/server/public/graph.html +7 -7
  143. package/payload/server/public/index.html +6 -6
  144. package/payload/server/public/public.html +5 -5
  145. package/payload/server/server-init-line-stamper.cjs +26 -0
  146. package/payload/server/server-init.cjs +26 -12
  147. package/payload/server/server.js +343 -252
  148. package/payload/server/public/assets/AdminShell-T-YknnBn.js +0 -1
  149. package/payload/server/public/assets/architecture-YZFGNWBL-Dnn6Hc65.js +0 -1
  150. package/payload/server/public/assets/channel-CEpR_0rE.js +0 -1
  151. package/payload/server/public/assets/chunk-426QAEUC-DFjEt3Zb.js +0 -1
  152. package/payload/server/public/assets/chunk-QZHKN3VN-DpF06ZZQ.js +0 -1
  153. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CYbXvKLI.js +0 -1
  154. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DEyHzRhq.js +0 -1
  155. package/payload/server/public/assets/clone-y8gexbBy.js +0 -1
  156. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CNs-LD5i.js +0 -1
  157. package/payload/server/public/assets/info-OMHHGYJF-DsTNigSS.js +0 -1
  158. package/payload/server/public/assets/infoDiagram-42DDH7IO-C_OarRTA.js +0 -2
  159. package/payload/server/public/assets/packet-4T2RLAQJ-DGES22b-.js +0 -1
  160. package/payload/server/public/assets/pie-ZZUOXDRM-ChKeDbzt.js +0 -1
  161. package/payload/server/public/assets/radar-PYXPWWZC-FGG5Fs7N.js +0 -1
  162. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B6zNJ6Tv.js +0 -1
  163. package/payload/server/public/assets/treeView-SZITEDCU-VAQQdbtf.js +0 -1
  164. package/payload/server/public/assets/treemap-W4RFUUIX-DKchO3zI.js +0 -1
  165. package/payload/server/public/assets/wardley-RL74JXVD-CBGtx0bS.js +0 -1
  166. package/payload/server/public/brand/favicon.ico +0 -0
  167. package/payload/server/public/brand/maxy-horizontal.png +0 -0
  168. package/payload/server/public/brand/maxy-square.png +0 -0
  169. /package/payload/server/public/assets/{_baseFor-Cs8Y-rGh.js → _baseFor-Cam2PbSt.js} +0 -0
  170. /package/payload/server/public/assets/{array-iHZP4KWJ.js → array-DYRGGQae.js} +0 -0
  171. /package/payload/server/public/assets/{chunk-DD-I1_y5.js → chunk-CnGqDkHZ.js} +0 -0
  172. /package/payload/server/public/assets/{cytoscape.esm-BR2GOQ8_.js → cytoscape.esm-nWsJMTNI.js} +0 -0
  173. /package/payload/server/public/assets/{defaultLocale-B9aLeOTg.js → defaultLocale-Du1XY3Dp.js} +0 -0
  174. /package/payload/server/public/assets/{init-BNFRgqHM.js → init-B5BXBRcm.js} +0 -0
  175. /package/payload/server/public/assets/{katex-B-EfS3nw.js → katex-HOUACuRw.js} +0 -0
  176. /package/payload/server/public/assets/{path-DmWWdwp7.js → path-CNO468J-.js} +0 -0
  177. /package/payload/server/public/assets/{rough.esm-Ci7Kjt46.js → rough.esm-DRO6hWPh.js} +0 -0
@@ -93,7 +93,7 @@ import {
93
93
  vncLog,
94
94
  walkPremiumBundles,
95
95
  writeAdminUserAndPerson
96
- } from "./chunk-76HRO7NX.js";
96
+ } from "./chunk-ZC63DFWF.js";
97
97
  import {
98
98
  __commonJS,
99
99
  __require,
@@ -9268,11 +9268,34 @@ async function managerDelete(sessionId) {
9268
9268
  await fetch(`${managerBase()}/${sessionId}`, { method: "DELETE" }).catch(() => {
9269
9269
  });
9270
9270
  }
9271
+ async function managerLogDownload(sessionId) {
9272
+ const res = await fetch(`${managerBase()}/${sessionId}/log?download=1`).catch(() => null);
9273
+ if (!res || !res.ok) return null;
9274
+ return res.text().catch(() => null);
9275
+ }
9271
9276
  function managerLogFollowUrl(sessionId, opts) {
9272
9277
  const base = `${managerBase()}/${sessionId}/log?follow=1`;
9273
9278
  return opts?.boundary ? `${base}&boundary=1` : base;
9274
9279
  }
9275
9280
 
9281
+ // app/lib/channel-pty-bridge/timeout-classify.ts
9282
+ function classifyTimeout(ndjson, writeAtMs) {
9283
+ for (const line of ndjson.split("\n")) {
9284
+ if (!line) continue;
9285
+ let rec;
9286
+ try {
9287
+ rec = JSON.parse(line);
9288
+ } catch {
9289
+ continue;
9290
+ }
9291
+ if (rec.type !== "assistant") continue;
9292
+ if (rec.message?.stop_reason !== "end_turn") continue;
9293
+ const ts = rec.timestamp ? Date.parse(rec.timestamp) : NaN;
9294
+ if (Number.isFinite(ts) && ts > writeAtMs) return "relay-missed";
9295
+ }
9296
+ return "no-turn";
9297
+ }
9298
+
9276
9299
  // app/lib/channel-pty-bridge/admin-session-id.ts
9277
9300
  import { createHash } from "crypto";
9278
9301
  function adminSessionIdFor(accountId, senderId) {
@@ -9970,6 +9993,7 @@ async function dispatchOnce(input) {
9970
9993
  resolve27(text);
9971
9994
  };
9972
9995
  entry.subscribers.add(listener);
9996
+ const writeAtMs = Date.now();
9973
9997
  const writeOk = await writeInput(entry, input.text);
9974
9998
  if (!writeOk) {
9975
9999
  entry.subscribers.delete(listener);
@@ -9991,6 +10015,14 @@ async function dispatchOnce(input) {
9991
10015
  return { turnText };
9992
10016
  } catch (err) {
9993
10017
  const message = err instanceof Error ? err.message : String(err);
10018
+ if (message === "timeout") {
10019
+ const ndjson = await managerLogDownload(entry.sessionId) ?? "";
10020
+ const cause = classifyTimeout(ndjson, writeAtMs);
10021
+ console.error(
10022
+ `${tag} op=turn-timeout cause=${cause} senderId=${input.senderId} sessionId=${entry.sessionId.slice(0, 8)}`
10023
+ );
10024
+ return { error: "timeout", cause };
10025
+ }
9994
10026
  console.error(
9995
10027
  `${tag} reject reason=turn-${message} senderId=${input.senderId} sessionId=${entry.sessionId.slice(0, 8)}`
9996
10028
  );
@@ -10060,6 +10092,21 @@ function handlePublicSessionExit(sessionId) {
10060
10092
  });
10061
10093
  }
10062
10094
 
10095
+ // app/lib/channel-pty-bridge/inbound-outcome.ts
10096
+ var TAG17 = "[whatsapp-adaptor]";
10097
+ var tallies = /* @__PURE__ */ new Map();
10098
+ function recordInboundOutcome(senderId, outcome) {
10099
+ let t = tallies.get(senderId);
10100
+ if (!t) {
10101
+ t = { "relay-ok": 0, "timeout-no-turn": 0, "timeout-relay-missed": 0 };
10102
+ tallies.set(senderId, t);
10103
+ }
10104
+ t[outcome] += 1;
10105
+ console.error(
10106
+ `${TAG17} op=inbound-outcome sender=${senderId} relay-ok=${t["relay-ok"]} timeout-no-turn=${t["timeout-no-turn"]} timeout-relay-missed=${t["timeout-relay-missed"]}`
10107
+ );
10108
+ }
10109
+
10063
10110
  // app/lib/access-session-store.ts
10064
10111
  import { randomUUID as randomUUID5 } from "crypto";
10065
10112
  var sessions = /* @__PURE__ */ new Map();
@@ -10332,12 +10379,12 @@ import { readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync
10332
10379
  // app/lib/whatsapp/login.ts
10333
10380
  var import_qrcode = __toESM(require_lib(), 1);
10334
10381
  import { randomUUID as randomUUID6 } from "crypto";
10335
- var TAG17 = "[whatsapp:login]";
10382
+ var TAG18 = "[whatsapp:login]";
10336
10383
  async function renderQrTerminal(qr) {
10337
10384
  try {
10338
10385
  return await import_qrcode.default.toString(qr, { type: "utf8" });
10339
10386
  } catch (err) {
10340
- console.error(`${TAG17} terminal QR render failed: ${String(err)}`);
10387
+ console.error(`${TAG18} terminal QR render failed: ${String(err)}`);
10341
10388
  return void 0;
10342
10389
  }
10343
10390
  }
@@ -10347,7 +10394,7 @@ function closeSocket(sock) {
10347
10394
  try {
10348
10395
  sock.ws?.close?.();
10349
10396
  } catch (err) {
10350
- console.warn(`${TAG17} socket close error during cleanup: ${String(err)}`);
10397
+ console.warn(`${TAG18} socket close error during cleanup: ${String(err)}`);
10351
10398
  }
10352
10399
  }
10353
10400
  function resetActiveLogin(accountId) {
@@ -10370,7 +10417,7 @@ async function loginConnectionLoop(accountId, login) {
10370
10417
  const current = activeLogins.get(accountId);
10371
10418
  if (current?.id === login.id) {
10372
10419
  current.connected = true;
10373
- console.error(`${TAG17} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
10420
+ console.error(`${TAG18} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
10374
10421
  }
10375
10422
  return;
10376
10423
  } catch (err) {
@@ -10380,7 +10427,7 @@ async function loginConnectionLoop(accountId, login) {
10380
10427
  if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
10381
10428
  if (attempt >= LOGIN_MAX_RECONNECTS) {
10382
10429
  console.error(
10383
- `${TAG17} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
10430
+ `${TAG18} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
10384
10431
  );
10385
10432
  current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
10386
10433
  } else {
@@ -10392,7 +10439,7 @@ async function loginConnectionLoop(accountId, login) {
10392
10439
  attempt++;
10393
10440
  const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
10394
10441
  console.error(
10395
- `${TAG17} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
10442
+ `${TAG18} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
10396
10443
  );
10397
10444
  closeSocket(current.sock);
10398
10445
  await new Promise((r) => setTimeout(r, delay));
@@ -10403,7 +10450,7 @@ async function loginConnectionLoop(accountId, login) {
10403
10450
  current.sock = newSock;
10404
10451
  } catch (sockErr) {
10405
10452
  console.error(
10406
- `${TAG17} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
10453
+ `${TAG18} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
10407
10454
  );
10408
10455
  current.error = `Reconnection failed: ${String(sockErr)}`;
10409
10456
  return;
@@ -10417,7 +10464,7 @@ async function startLogin(opts) {
10417
10464
  const hasAuth = await authExists(authDir);
10418
10465
  const selfId = readSelfId(authDir);
10419
10466
  console.error(
10420
- `${TAG17} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
10467
+ `${TAG18} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
10421
10468
  );
10422
10469
  if (hasAuth && !force) {
10423
10470
  const who = selfId.e164 ?? selfId.jid ?? "unknown";
@@ -10429,7 +10476,7 @@ async function startLogin(opts) {
10429
10476
  await clearAuth(authDir);
10430
10477
  const existing = activeLogins.get(accountId);
10431
10478
  if (existing && isLoginFresh(existing) && existing.qrDataUrl && !force) {
10432
- console.error(`${TAG17} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
10479
+ console.error(`${TAG18} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
10433
10480
  return {
10434
10481
  qrDataUrl: existing.qrDataUrl,
10435
10482
  qrRaw: existing.qr,
@@ -10438,7 +10485,7 @@ async function startLogin(opts) {
10438
10485
  };
10439
10486
  }
10440
10487
  if (existing) {
10441
- console.error(`${TAG17} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
10488
+ console.error(`${TAG18} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
10442
10489
  }
10443
10490
  resetActiveLogin(accountId);
10444
10491
  let resolveQr = null;
@@ -10460,14 +10507,14 @@ async function startLogin(opts) {
10460
10507
  onQr: (qr2) => {
10461
10508
  loginQrCount++;
10462
10509
  if (pendingQr) {
10463
- console.error(`${TAG17} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
10510
+ console.error(`${TAG18} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
10464
10511
  return;
10465
10512
  }
10466
10513
  pendingQr = qr2;
10467
10514
  const current = activeLogins.get(accountId);
10468
10515
  if (current && !current.qr) current.qr = qr2;
10469
10516
  clearTimeout(qrTimer);
10470
- console.error(`${TAG17} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
10517
+ console.error(`${TAG18} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
10471
10518
  resolveQr?.(qr2);
10472
10519
  }
10473
10520
  });
@@ -10487,7 +10534,7 @@ async function startLogin(opts) {
10487
10534
  activeLogins.set(accountId, login);
10488
10535
  if (pendingQr && !login.qr) login.qr = pendingQr;
10489
10536
  loginConnectionLoop(accountId, login).catch((err) => {
10490
- console.error(`${TAG17} loginConnectionLoop unexpected error: ${String(err)}`);
10537
+ console.error(`${TAG18} loginConnectionLoop unexpected error: ${String(err)}`);
10491
10538
  const current = activeLogins.get(accountId);
10492
10539
  if (current?.id === login.id) {
10493
10540
  current.error = `Unexpected login error: ${String(err)}`;
@@ -10513,7 +10560,7 @@ async function waitForLogin(opts) {
10513
10560
  const { accountId, timeoutMs = 6e4 } = opts;
10514
10561
  const login = activeLogins.get(accountId);
10515
10562
  console.error(
10516
- `${TAG17} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
10563
+ `${TAG18} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
10517
10564
  );
10518
10565
  if (!login) {
10519
10566
  return { connected: false, message: "No active WhatsApp login in progress." };
@@ -10526,7 +10573,7 @@ async function waitForLogin(opts) {
10526
10573
  while (Date.now() < deadline) {
10527
10574
  if (login.connected) {
10528
10575
  const selfId = readSelfId(login.authDir);
10529
- console.error(`${TAG17} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
10576
+ console.error(`${TAG18} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
10530
10577
  const sock = login.sock;
10531
10578
  const authDir = login.authDir;
10532
10579
  activeLogins.delete(accountId);
@@ -10546,7 +10593,7 @@ async function waitForLogin(opts) {
10546
10593
  await new Promise((r) => setTimeout(r, 1e3));
10547
10594
  }
10548
10595
  const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
10549
- console.error(`${TAG17} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
10596
+ console.error(`${TAG18} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
10550
10597
  resetActiveLogin(accountId);
10551
10598
  return { connected: false, message: "Login timed out. Try generating a new QR." };
10552
10599
  }
@@ -10554,7 +10601,7 @@ async function waitForLogin(opts) {
10554
10601
  // app/lib/whatsapp/config-persist.ts
10555
10602
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
10556
10603
  import { resolve as resolve6, join as join4 } from "path";
10557
- var TAG18 = "[whatsapp:config]";
10604
+ var TAG19 = "[whatsapp:config]";
10558
10605
  function configPath(accountDir) {
10559
10606
  return resolve6(accountDir, "account.json");
10560
10607
  }
@@ -10571,9 +10618,9 @@ function reloadManagerConfig(accountDir) {
10571
10618
  try {
10572
10619
  const config = readConfig(accountDir);
10573
10620
  reloadConfig(config);
10574
- console.error(`${TAG18} reloaded manager config`);
10621
+ console.error(`${TAG19} reloaded manager config`);
10575
10622
  } catch (err) {
10576
- console.error(`${TAG18} manager config reload failed: ${String(err)}`);
10623
+ console.error(`${TAG19} manager config reload failed: ${String(err)}`);
10577
10624
  }
10578
10625
  }
10579
10626
  var E164_PATTERN = /^\+\d{7,15}$/;
@@ -10599,25 +10646,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
10599
10646
  const adminPhones = wa.adminPhones;
10600
10647
  if (!adminPhones.includes(normalized)) {
10601
10648
  adminPhones.push(normalized);
10602
- console.error(`${TAG18} added selfPhone=${normalized} to adminPhones`);
10649
+ console.error(`${TAG19} added selfPhone=${normalized} to adminPhones`);
10603
10650
  }
10604
10651
  } else {
10605
- console.error(`${TAG18} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
10652
+ console.error(`${TAG19} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
10606
10653
  }
10607
10654
  const parsed = WhatsAppConfigSchema.safeParse(wa);
10608
10655
  if (!parsed.success) {
10609
10656
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
10610
- console.error(`${TAG18} validation failed after pairing: ${msg}`);
10657
+ console.error(`${TAG19} validation failed after pairing: ${msg}`);
10611
10658
  return { ok: false, error: `Validation failed: ${msg}` };
10612
10659
  }
10613
10660
  config.whatsapp = parsed.data;
10614
10661
  writeConfig(accountDir, config);
10615
- console.error(`${TAG18} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
10662
+ console.error(`${TAG19} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
10616
10663
  reloadManagerConfig(accountDir);
10617
10664
  return { ok: true };
10618
10665
  } catch (err) {
10619
10666
  const msg = err instanceof Error ? err.message : String(err);
10620
- console.error(`${TAG18} persist failed account=${accountId}: ${msg}`);
10667
+ console.error(`${TAG19} persist failed account=${accountId}: ${msg}`);
10621
10668
  return { ok: false, error: msg };
10622
10669
  }
10623
10670
  }
@@ -10647,12 +10694,12 @@ function addAdminPhone(accountDir, phone) {
10647
10694
  }
10648
10695
  config.whatsapp = parsed.data;
10649
10696
  writeConfig(accountDir, config);
10650
- console.error(`${TAG18} added admin phone=${normalized}`);
10697
+ console.error(`${TAG19} added admin phone=${normalized}`);
10651
10698
  reloadManagerConfig(accountDir);
10652
10699
  return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
10653
10700
  } catch (err) {
10654
10701
  const msg = err instanceof Error ? err.message : String(err);
10655
- console.error(`${TAG18} addAdminPhone failed: ${msg}`);
10702
+ console.error(`${TAG19} addAdminPhone failed: ${msg}`);
10656
10703
  return { ok: false, error: msg };
10657
10704
  }
10658
10705
  }
@@ -10680,12 +10727,12 @@ function removeAdminPhone(accountDir, phone) {
10680
10727
  }
10681
10728
  config.whatsapp = parsed.data;
10682
10729
  writeConfig(accountDir, config);
10683
- console.error(`${TAG18} removed admin phone=${normalized}`);
10730
+ console.error(`${TAG19} removed admin phone=${normalized}`);
10684
10731
  reloadManagerConfig(accountDir);
10685
10732
  return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
10686
10733
  } catch (err) {
10687
10734
  const msg = err instanceof Error ? err.message : String(err);
10688
- console.error(`${TAG18} removeAdminPhone failed: ${msg}`);
10735
+ console.error(`${TAG19} removeAdminPhone failed: ${msg}`);
10689
10736
  return { ok: false, error: msg };
10690
10737
  }
10691
10738
  }
@@ -10723,12 +10770,12 @@ function setPublicAgent(accountDir, slug) {
10723
10770
  }
10724
10771
  config.whatsapp = parsed.data;
10725
10772
  writeConfig(accountDir, config);
10726
- console.error(`${TAG18} publicAgent set to ${trimmed}`);
10773
+ console.error(`${TAG19} publicAgent set to ${trimmed}`);
10727
10774
  reloadManagerConfig(accountDir);
10728
10775
  return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
10729
10776
  } catch (err) {
10730
10777
  const msg = err instanceof Error ? err.message : String(err);
10731
- console.error(`${TAG18} setPublicAgent failed: ${msg}`);
10778
+ console.error(`${TAG19} setPublicAgent failed: ${msg}`);
10732
10779
  return { ok: false, error: msg };
10733
10780
  }
10734
10781
  }
@@ -10792,12 +10839,12 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
10792
10839
  }
10793
10840
  config.whatsapp = parsed.data;
10794
10841
  writeConfig(accountDir, config);
10795
- console.error(`${TAG18} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
10842
+ console.error(`${TAG19} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
10796
10843
  reloadManagerConfig(accountDir);
10797
10844
  return { ok: true, message: `Per-group public agent set to "${trimmedSlug}" for group ${trimmedGroup}.` };
10798
10845
  } catch (err) {
10799
10846
  const msg = err instanceof Error ? err.message : String(err);
10800
- console.error(`${TAG18} setGroupPublicAgent failed: ${msg}`);
10847
+ console.error(`${TAG19} setGroupPublicAgent failed: ${msg}`);
10801
10848
  return { ok: false, error: msg };
10802
10849
  }
10803
10850
  }
@@ -10824,12 +10871,12 @@ function unsetGroupPublicAgent(accountDir, accountId, groupJid) {
10824
10871
  }
10825
10872
  config.whatsapp = parsed.data;
10826
10873
  writeConfig(accountDir, config);
10827
- console.error(`${TAG18} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
10874
+ console.error(`${TAG19} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
10828
10875
  reloadManagerConfig(accountDir);
10829
10876
  return { ok: true, message: `Per-group public agent override removed for group ${trimmedGroup}.` };
10830
10877
  } catch (err) {
10831
10878
  const msg = err instanceof Error ? err.message : String(err);
10832
- console.error(`${TAG18} unsetGroupPublicAgent failed: ${msg}`);
10879
+ console.error(`${TAG19} unsetGroupPublicAgent failed: ${msg}`);
10833
10880
  return { ok: false, error: msg };
10834
10881
  }
10835
10882
  }
@@ -10850,17 +10897,17 @@ function updateConfig(accountDir, fields) {
10850
10897
  const parsed = WhatsAppConfigSchema.safeParse(wa);
10851
10898
  if (!parsed.success) {
10852
10899
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
10853
- console.error(`${TAG18} update validation failed: ${msg}`);
10900
+ console.error(`${TAG19} update validation failed: ${msg}`);
10854
10901
  return { ok: false, error: `Validation failed: ${msg}` };
10855
10902
  }
10856
10903
  config.whatsapp = parsed.data;
10857
10904
  writeConfig(accountDir, config);
10858
- console.error(`${TAG18} updated fields=[${fieldNames.join(",")}]`);
10905
+ console.error(`${TAG19} updated fields=[${fieldNames.join(",")}]`);
10859
10906
  reloadManagerConfig(accountDir);
10860
10907
  return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
10861
10908
  } catch (err) {
10862
10909
  const msg = err instanceof Error ? err.message : String(err);
10863
- console.error(`${TAG18} updateConfig failed: ${msg}`);
10910
+ console.error(`${TAG19} updateConfig failed: ${msg}`);
10864
10911
  return { ok: false, error: msg };
10865
10912
  }
10866
10913
  }
@@ -10986,17 +11033,17 @@ function serializeWhatsAppSchema() {
10986
11033
  }
10987
11034
 
10988
11035
  // server/routes/whatsapp.ts
10989
- var TAG19 = "[whatsapp:api]";
11036
+ var TAG20 = "[whatsapp:api]";
10990
11037
  var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
10991
11038
  var app3 = new Hono();
10992
11039
  app3.get("/status", (c) => {
10993
11040
  try {
10994
11041
  const status = getStatus();
10995
11042
  const summary = status.map((a) => `${a.accountId}:${a.connected ? "up" : "down"}`).join(", ");
10996
- console.error(`${TAG19} status accounts=${status.length} [${summary}]`);
11043
+ console.error(`${TAG20} status accounts=${status.length} [${summary}]`);
10997
11044
  return c.json({ accounts: status });
10998
11045
  } catch (err) {
10999
- console.error(`${TAG19} status error: ${String(err)}`);
11046
+ console.error(`${TAG20} status error: ${String(err)}`);
11000
11047
  return c.json({ error: String(err) }, 500);
11001
11048
  }
11002
11049
  });
@@ -11007,10 +11054,10 @@ app3.post("/login/start", async (c) => {
11007
11054
  const force = body.force ?? false;
11008
11055
  const authDir = join5(MAXY_DIR, "credentials", "whatsapp", accountId);
11009
11056
  const result = await startLogin({ accountId, authDir, force });
11010
- console.error(`${TAG19} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
11057
+ console.error(`${TAG20} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
11011
11058
  return c.json(result);
11012
11059
  } catch (err) {
11013
- console.error(`${TAG19} login/start error: ${String(err)}`);
11060
+ console.error(`${TAG20} login/start error: ${String(err)}`);
11014
11061
  return c.json({ error: String(err) }, 500);
11015
11062
  }
11016
11063
  });
@@ -11025,7 +11072,7 @@ app3.post("/login/wait", async (c) => {
11025
11072
  try {
11026
11073
  await registerLoginSocket(accountId, result.sock, result.authDir);
11027
11074
  } catch (regErr) {
11028
- console.error(`${TAG19} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
11075
+ console.error(`${TAG20} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
11029
11076
  }
11030
11077
  try {
11031
11078
  const account = resolveAccount();
@@ -11033,16 +11080,16 @@ app3.post("/login/wait", async (c) => {
11033
11080
  const persistResult = persistAfterPairing(account.accountDir, accountId, result.selfPhone ?? null);
11034
11081
  configPersisted = persistResult.ok;
11035
11082
  if (!persistResult.ok) {
11036
- console.error(`${TAG19} config persist failed account=${accountId}: ${persistResult.error}`);
11083
+ console.error(`${TAG20} config persist failed account=${accountId}: ${persistResult.error}`);
11037
11084
  }
11038
11085
  } else {
11039
- console.error(`${TAG19} config persist skipped \u2014 no account resolved`);
11086
+ console.error(`${TAG20} config persist skipped \u2014 no account resolved`);
11040
11087
  }
11041
11088
  } catch (persistErr) {
11042
- console.error(`${TAG19} config persist error account=${accountId}: ${String(persistErr)}`);
11089
+ console.error(`${TAG20} config persist error account=${accountId}: ${String(persistErr)}`);
11043
11090
  }
11044
11091
  }
11045
- console.error(`${TAG19} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
11092
+ console.error(`${TAG20} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
11046
11093
  return c.json({
11047
11094
  connected: result.connected,
11048
11095
  message: result.message,
@@ -11050,7 +11097,7 @@ app3.post("/login/wait", async (c) => {
11050
11097
  configPersisted
11051
11098
  });
11052
11099
  } catch (err) {
11053
- console.error(`${TAG19} login/wait error: ${String(err)}`);
11100
+ console.error(`${TAG20} login/wait error: ${String(err)}`);
11054
11101
  return c.json({ error: String(err) }, 500);
11055
11102
  }
11056
11103
  });
@@ -11061,7 +11108,7 @@ app3.post("/disconnect", async (c) => {
11061
11108
  await stopConnection(accountId);
11062
11109
  return c.json({ disconnected: true, accountId });
11063
11110
  } catch (err) {
11064
- console.error(`${TAG19} disconnect error: ${String(err)}`);
11111
+ console.error(`${TAG20} disconnect error: ${String(err)}`);
11065
11112
  return c.json({ error: String(err) }, 500);
11066
11113
  }
11067
11114
  });
@@ -11072,7 +11119,7 @@ app3.post("/reconnect", async (c) => {
11072
11119
  await startConnection(accountId);
11073
11120
  return c.json({ reconnecting: true, accountId });
11074
11121
  } catch (err) {
11075
- console.error(`${TAG19} reconnect error: ${String(err)}`);
11122
+ console.error(`${TAG20} reconnect error: ${String(err)}`);
11076
11123
  return c.json({ error: String(err) }, 500);
11077
11124
  }
11078
11125
  });
@@ -11091,7 +11138,7 @@ app3.post("/send", async (c) => {
11091
11138
  const result = await sendTextMessage(sock, to, text, { accountId });
11092
11139
  return c.json(result);
11093
11140
  } catch (err) {
11094
- console.error(`${TAG19} send error: ${String(err)}`);
11141
+ console.error(`${TAG20} send error: ${String(err)}`);
11095
11142
  return c.json({ error: String(err) }, 500);
11096
11143
  }
11097
11144
  });
@@ -11112,7 +11159,7 @@ app3.post("/config", async (c) => {
11112
11159
  return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
11113
11160
  }
11114
11161
  const result = addAdminPhone(account.accountDir, phone);
11115
- console.error(`${TAG19} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
11162
+ console.error(`${TAG20} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
11116
11163
  return c.json(result, result.ok ? 200 : 400);
11117
11164
  }
11118
11165
  case "remove-admin-phone": {
@@ -11120,12 +11167,12 @@ app3.post("/config", async (c) => {
11120
11167
  return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
11121
11168
  }
11122
11169
  const result = removeAdminPhone(account.accountDir, phone);
11123
- console.error(`${TAG19} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
11170
+ console.error(`${TAG20} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
11124
11171
  return c.json(result, result.ok ? 200 : 400);
11125
11172
  }
11126
11173
  case "list-admin-phones": {
11127
11174
  const phones = readAdminPhones(account.accountDir);
11128
- console.error(`${TAG19} config action=list-admin-phones count=${phones.length}`);
11175
+ console.error(`${TAG20} config action=list-admin-phones count=${phones.length}`);
11129
11176
  return c.json({ ok: true, phones });
11130
11177
  }
11131
11178
  case "set-public-agent": {
@@ -11133,14 +11180,14 @@ app3.post("/config", async (c) => {
11133
11180
  return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
11134
11181
  }
11135
11182
  const result = setPublicAgent(account.accountDir, slug);
11136
- console.error(`${TAG19} config action=set-public-agent slug=${slug} ok=${result.ok}`);
11183
+ console.error(`${TAG20} config action=set-public-agent slug=${slug} ok=${result.ok}`);
11137
11184
  return c.json(result, result.ok ? 200 : 400);
11138
11185
  }
11139
11186
  case "get-public-agent": {
11140
11187
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
11141
11188
  const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
11142
11189
  const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
11143
- console.error(`${TAG19} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
11190
+ console.error(`${TAG20} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
11144
11191
  return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
11145
11192
  }
11146
11193
  case "set-group-public-agent": {
@@ -11152,7 +11199,7 @@ app3.post("/config", async (c) => {
11152
11199
  }
11153
11200
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
11154
11201
  const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
11155
- console.error(`${TAG19} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
11202
+ console.error(`${TAG20} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
11156
11203
  return c.json(result, result.ok ? 200 : 400);
11157
11204
  }
11158
11205
  case "unset-group-public-agent": {
@@ -11161,7 +11208,7 @@ app3.post("/config", async (c) => {
11161
11208
  }
11162
11209
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
11163
11210
  const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
11164
- console.error(`${TAG19} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
11211
+ console.error(`${TAG20} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
11165
11212
  return c.json(result, result.ok ? 200 : 400);
11166
11213
  }
11167
11214
  case "list-public-agents": {
@@ -11178,26 +11225,26 @@ app3.post("/config", async (c) => {
11178
11225
  const config = JSON.parse(readFileSync6(configPath2, "utf-8"));
11179
11226
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
11180
11227
  } catch {
11181
- console.error(`${TAG19} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
11228
+ console.error(`${TAG20} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
11182
11229
  }
11183
11230
  }
11184
11231
  } catch (err) {
11185
- console.error(`${TAG19} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
11232
+ console.error(`${TAG20} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
11186
11233
  }
11187
11234
  }
11188
- console.error(`${TAG19} config action=list-public-agents count=${agents.length}`);
11235
+ console.error(`${TAG20} config action=list-public-agents count=${agents.length}`);
11189
11236
  return c.json({ ok: true, agents });
11190
11237
  }
11191
11238
  case "schema": {
11192
11239
  const text = serializeWhatsAppSchema();
11193
- console.error(`${TAG19} config action=schema`);
11240
+ console.error(`${TAG20} config action=schema`);
11194
11241
  return c.json({ ok: true, text });
11195
11242
  }
11196
11243
  case "list-groups": {
11197
11244
  const groupAccountId = accountId ?? "default";
11198
11245
  const sock = getSocket(groupAccountId);
11199
11246
  if (!sock) {
11200
- console.error(`${TAG19} config action=list-groups error="not connected" accountId=${groupAccountId}`);
11247
+ console.error(`${TAG20} config action=list-groups error="not connected" accountId=${groupAccountId}`);
11201
11248
  return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
11202
11249
  }
11203
11250
  try {
@@ -11207,10 +11254,10 @@ app3.post("/config", async (c) => {
11207
11254
  name: g.subject ?? g.id,
11208
11255
  participantCount: Array.isArray(g.participants) ? g.participants.length : 0
11209
11256
  }));
11210
- console.error(`${TAG19} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
11257
+ console.error(`${TAG20} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
11211
11258
  return c.json({ ok: true, groups });
11212
11259
  } catch (err) {
11213
- console.error(`${TAG19} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
11260
+ console.error(`${TAG20} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
11214
11261
  return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
11215
11262
  }
11216
11263
  }
@@ -11220,12 +11267,12 @@ app3.post("/config", async (c) => {
11220
11267
  }
11221
11268
  const result = updateConfig(account.accountDir, fields);
11222
11269
  const fieldNames = Object.keys(fields);
11223
- console.error(`${TAG19} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
11270
+ console.error(`${TAG20} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
11224
11271
  return c.json(result, result.ok ? 200 : 400);
11225
11272
  }
11226
11273
  case "get-config": {
11227
11274
  const waConfig = getConfig(account.accountDir);
11228
- console.error(`${TAG19} config action=get-config`);
11275
+ console.error(`${TAG20} config action=get-config`);
11229
11276
  return c.json({ ok: true, config: waConfig });
11230
11277
  }
11231
11278
  default:
@@ -11235,7 +11282,7 @@ app3.post("/config", async (c) => {
11235
11282
  );
11236
11283
  }
11237
11284
  } catch (err) {
11238
- console.error(`${TAG19} config error: ${String(err)}`);
11285
+ console.error(`${TAG20} config error: ${String(err)}`);
11239
11286
  return c.json({ ok: false, error: String(err) }, 500);
11240
11287
  }
11241
11288
  });
@@ -11256,7 +11303,7 @@ app3.post("/send-document", async (c) => {
11256
11303
  recordRouteDocumentOutbound(to, filePath);
11257
11304
  return c.json({ success: true, messageId: result.messageId });
11258
11305
  } catch (err) {
11259
- console.error(`${TAG19} send-document error: ${String(err)}`);
11306
+ console.error(`${TAG20} send-document error: ${String(err)}`);
11260
11307
  return c.json({ error: String(err) }, 500);
11261
11308
  }
11262
11309
  });
@@ -11266,11 +11313,11 @@ app3.get("/activity", (c) => {
11266
11313
  const result = getChannelActivity(accountId);
11267
11314
  const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
11268
11315
  console.error(
11269
- `${TAG19} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
11316
+ `${TAG20} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
11270
11317
  );
11271
11318
  return c.json(result);
11272
11319
  } catch (err) {
11273
- console.error(`${TAG19} activity error: ${String(err)}`);
11320
+ console.error(`${TAG20} activity error: ${String(err)}`);
11274
11321
  return c.json({ error: String(err) }, 500);
11275
11322
  }
11276
11323
  });
@@ -11289,10 +11336,10 @@ app3.get("/conversations", (c) => {
11289
11336
  };
11290
11337
  });
11291
11338
  conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
11292
- console.error(`${TAG19} conversations account=${accountId} count=${conversations.length}`);
11339
+ console.error(`${TAG20} conversations account=${accountId} count=${conversations.length}`);
11293
11340
  return c.json({ conversations });
11294
11341
  } catch (err) {
11295
- console.error(`${TAG19} conversations error: ${String(err)}`);
11342
+ console.error(`${TAG20} conversations error: ${String(err)}`);
11296
11343
  return c.json({ error: String(err) }, 500);
11297
11344
  }
11298
11345
  });
@@ -11307,10 +11354,10 @@ app3.get("/messages", (c) => {
11307
11354
  const limit = limitParam ? parseInt(limitParam, 10) : void 0;
11308
11355
  const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
11309
11356
  const messages = getMessages(accountId, jid, effectiveLimit);
11310
- console.error(`${TAG19} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
11357
+ console.error(`${TAG20} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
11311
11358
  return c.json({ messages });
11312
11359
  } catch (err) {
11313
- console.error(`${TAG19} messages error: ${String(err)}`);
11360
+ console.error(`${TAG20} messages error: ${String(err)}`);
11314
11361
  return c.json({ error: String(err) }, 500);
11315
11362
  }
11316
11363
  });
@@ -11391,7 +11438,7 @@ app3.get("/conversation-graph-state", async (c) => {
11391
11438
  }
11392
11439
  } catch (err) {
11393
11440
  const msg = err instanceof Error ? err.message : String(err);
11394
- console.error(`${TAG19} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
11441
+ console.error(`${TAG20} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
11395
11442
  return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
11396
11443
  }
11397
11444
  const ms = Date.now() - t0;
@@ -11404,7 +11451,7 @@ app3.get("/conversation-graph-state", async (c) => {
11404
11451
  ms
11405
11452
  });
11406
11453
  } catch (err) {
11407
- console.error(`${TAG19} conversation-graph-state error: ${String(err)}`);
11454
+ console.error(`${TAG20} conversation-graph-state error: ${String(err)}`);
11408
11455
  return c.json({ error: String(err) }, 500);
11409
11456
  }
11410
11457
  });
@@ -11416,12 +11463,12 @@ app3.get("/group-info", async (c) => {
11416
11463
  return c.json({ error: "Missing required parameter: jid" }, 400);
11417
11464
  }
11418
11465
  if (!isGroupJid(jid)) {
11419
- console.error(`${TAG19} group-info error="not a group JID" jid=${jid} account=${accountId}`);
11466
+ console.error(`${TAG20} group-info error="not a group JID" jid=${jid} account=${accountId}`);
11420
11467
  return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
11421
11468
  }
11422
11469
  const sock = getSocket(accountId);
11423
11470
  if (!sock) {
11424
- console.error(`${TAG19} group-info error="not connected" account=${accountId}`);
11471
+ console.error(`${TAG20} group-info error="not connected" account=${accountId}`);
11425
11472
  return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
11426
11473
  }
11427
11474
  const meta = await sock.groupMetadata(jid);
@@ -11434,10 +11481,10 @@ app3.get("/group-info", async (c) => {
11434
11481
  participantCount: meta.participants.length,
11435
11482
  participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
11436
11483
  };
11437
- console.error(`${TAG19} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
11484
+ console.error(`${TAG20} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
11438
11485
  return c.json(result);
11439
11486
  } catch (err) {
11440
- console.error(`${TAG19} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
11487
+ console.error(`${TAG20} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
11441
11488
  return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
11442
11489
  }
11443
11490
  });
@@ -12095,7 +12142,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName, rol
12095
12142
  } catch (err) {
12096
12143
  console.error(`[session] eager-ensureConversation FAILED at boot: ${err instanceof Error ? err.message : String(err)}`);
12097
12144
  }
12098
- console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} sessionId=${initialSessionId ?? "unbound"} cacheKey=${cacheKey.slice(0, 8)}`);
12145
+ console.log(`[session] admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} sessionId=${initialSessionId ?? "unbound"} cacheKey=${cacheKey.slice(0, 8)}`);
12099
12146
  console.log(`[admin-session] role=${role ?? "null"} cacheKey=${cacheKey.slice(0, 8)} phase=create eager-ensured=${initialSessionId ? "true" : "false"}`);
12100
12147
  return {
12101
12148
  session_key: signedSessionToken,
@@ -12997,7 +13044,7 @@ app11.post("/:id/resume", async (c) => {
12997
13044
  try {
12998
13045
  neo4jMessages = await getRecentMessages(sessionId);
12999
13046
  } catch (err) {
13000
- console.error(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} getRecentMessages failed: sessionId=${sessionId.slice(0, 8)}\u2026 error=${err instanceof Error ? err.message : String(err)}`);
13047
+ console.error(`[admin-resume] getRecentMessages failed: sessionId=${sessionId.slice(0, 8)}\u2026 error=${err instanceof Error ? err.message : String(err)}`);
13001
13048
  return c.json({ error: "Failed to load conversation messages" }, 500);
13002
13049
  }
13003
13050
  if (persistedAgentSessionId) {
@@ -13191,7 +13238,7 @@ app11.post("/:id/resume", async (c) => {
13191
13238
  } catch {
13192
13239
  }
13193
13240
  const age = formatAge(updatedAt);
13194
- console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} reason=${reason} sessionId=${sessionId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} syntheticHidden=${syntheticHidden} jsonlMissing=${jsonlMissing} healMissing=${jsonlHealMissing} cacheKey=${cacheKey.slice(0, 8)}\u2026`);
13241
+ console.log(`[admin-resume] reason=${reason} sessionId=${sessionId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} syntheticHidden=${syntheticHidden} jsonlMissing=${jsonlMissing} healMissing=${jsonlHealMissing} cacheKey=${cacheKey.slice(0, 8)}\u2026`);
13195
13242
  console.log(`[admin-session-restore] cacheKey=${cacheKey.slice(0, 8)} sessionId=${sessionId.slice(0, 8)} messageCount=${rehydrated.length} ms=${Date.now() - t0}`);
13196
13243
  return c.json({
13197
13244
  sessionId,
@@ -13544,7 +13591,7 @@ function resolveTunnelUrl() {
13544
13591
  if (!state) return null;
13545
13592
  return `https://${hostname2}.${state.domain}`;
13546
13593
  }
13547
- var TAG20 = "[claude-session-manager:wrapper]";
13594
+ var TAG21 = "[claude-session-manager:wrapper]";
13548
13595
  async function refuseIfClaudeAuthDead(c, route, sessionId) {
13549
13596
  const auth = await ensureAuth();
13550
13597
  if (auth.status !== "dead" && auth.status !== "missing") return null;
@@ -13566,12 +13613,12 @@ async function performSpawnWithInitialMessage(args) {
13566
13613
  const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
13567
13614
  const ownerMs = Date.now() - ownerStart;
13568
13615
  const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
13569
- console.log(`${TAG20} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
13616
+ console.log(`${TAG21} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
13570
13617
  const dormantPlugins = computeDormantPlugins(args.senderId);
13571
13618
  const activePlugins = computeActivePlugins(args.senderId);
13572
13619
  const specialistDomains = computeSpecialistDomains(args.senderId);
13573
13620
  const tunnelUrl = resolveTunnelUrl();
13574
- console.log(`${TAG20} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
13621
+ console.log(`${TAG21} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
13575
13622
  const upstreamPayload = JSON.stringify({
13576
13623
  senderId: args.senderId,
13577
13624
  // Task 205 — pass userId through to the manager so it lands as
@@ -13598,24 +13645,24 @@ async function performSpawnWithInitialMessage(args) {
13598
13645
  // unshapely values.
13599
13646
  conversationNodeId: args.conversationNodeId
13600
13647
  });
13601
- console.log(`${TAG20} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
13648
+ console.log(`${TAG21} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
13602
13649
  const forwardStart = Date.now();
13603
13650
  const upstream = await fetch(`${managerBase3()}/public-spawn`, {
13604
13651
  method: "POST",
13605
13652
  headers: { "content-type": "application/json" },
13606
13653
  body: upstreamPayload
13607
13654
  }).catch((err) => {
13608
- console.error(`${TAG20} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
13655
+ console.error(`${TAG21} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
13609
13656
  return null;
13610
13657
  });
13611
13658
  if (!upstream) return {
13612
13659
  response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
13613
13660
  claudeSessionId: null
13614
13661
  };
13615
- console.log(`${TAG20} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
13662
+ console.log(`${TAG21} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
13616
13663
  if (args.initialMessage) {
13617
13664
  const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
13618
- console.log(`${TAG20} initial-message-inlined bytes=${inputBytes}`);
13665
+ console.log(`${TAG21} initial-message-inlined bytes=${inputBytes}`);
13619
13666
  }
13620
13667
  const bodyText = await upstream.text().catch(() => "");
13621
13668
  let claudeSessionId = null;
@@ -13650,7 +13697,7 @@ app12.post("/", async (c) => {
13650
13697
  if (refusal) return refusal;
13651
13698
  const senderId = getAccountIdForSession(cacheKey) ?? "";
13652
13699
  if (!senderId) {
13653
- console.error(`${TAG20} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
13700
+ console.error(`${TAG21} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
13654
13701
  return c.json({ error: "admin-account-not-resolved" }, 500);
13655
13702
  }
13656
13703
  const userId = getUserIdForSession(cacheKey) ?? void 0;
@@ -13659,7 +13706,7 @@ app12.post("/", async (c) => {
13659
13706
  const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
13660
13707
  const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
13661
13708
  const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
13662
- console.log(`${TAG20} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
13709
+ console.log(`${TAG21} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
13663
13710
  const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
13664
13711
  const { response, claudeSessionId } = await performSpawnWithInitialMessage({
13665
13712
  senderId,
@@ -13678,13 +13725,13 @@ app12.post("/", async (c) => {
13678
13725
  claudeSessionId,
13679
13726
  senderId
13680
13727
  );
13681
- console.log(`${TAG20} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
13728
+ console.log(`${TAG21} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
13682
13729
  return response;
13683
13730
  });
13684
13731
  var claude_sessions_default = app12;
13685
13732
 
13686
13733
  // server/routes/admin/log-ingest.ts
13687
- var TAG21 = "[log-ingest]";
13734
+ var TAG22 = "[log-ingest]";
13688
13735
  var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
13689
13736
  var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
13690
13737
  var MAX_LINE_BYTES = 4096;
@@ -13695,7 +13742,7 @@ function isLoopbackAddr(addr) {
13695
13742
  app13.post("/", async (c) => {
13696
13743
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
13697
13744
  if (!isLoopbackAddr(remoteAddr)) {
13698
- console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
13745
+ console.error(`${TAG22} reject reason=non-loopback remoteAddr=${remoteAddr}`);
13699
13746
  return c.json({ error: "log-ingest-loopback-only" }, 403);
13700
13747
  }
13701
13748
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -13704,7 +13751,7 @@ app13.post("/", async (c) => {
13704
13751
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
13705
13752
  const offender = tokens.find((t) => !isLoopbackAddr(t));
13706
13753
  if (offender !== void 0) {
13707
- console.error(`${TAG21} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
13754
+ console.error(`${TAG22} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
13708
13755
  return c.json({ error: "log-ingest-loopback-only" }, 403);
13709
13756
  }
13710
13757
  }
@@ -13746,18 +13793,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
13746
13793
  ]);
13747
13794
  var app14 = new Hono();
13748
13795
  app14.post("/", async (c) => {
13749
- const TAG31 = "[admin:events]";
13796
+ const TAG32 = "[admin:events]";
13750
13797
  let body;
13751
13798
  try {
13752
13799
  body = await c.req.json();
13753
13800
  } catch (err) {
13754
13801
  const detail = err instanceof Error ? err.message : String(err);
13755
- console.error(`${TAG31} reject reason=body-not-json detail=${detail}`);
13802
+ console.error(`${TAG32} reject reason=body-not-json detail=${detail}`);
13756
13803
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
13757
13804
  }
13758
13805
  const event = typeof body.event === "string" ? body.event : "";
13759
13806
  if (!ALLOWED_EVENTS.has(event)) {
13760
- console.error(`${TAG31} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
13807
+ console.error(`${TAG32} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
13761
13808
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
13762
13809
  }
13763
13810
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -17783,7 +17830,7 @@ var health_default2 = app27;
17783
17830
 
17784
17831
  // server/routes/admin/linkedin-ingest.ts
17785
17832
  import { randomUUID as randomUUID9 } from "crypto";
17786
- var TAG22 = "[linkedin-ingest-route]";
17833
+ var TAG23 = "[linkedin-ingest-route]";
17787
17834
  var UUID = /^[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}$/;
17788
17835
  var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
17789
17836
  var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
@@ -17887,29 +17934,29 @@ app28.post("/", requireAdminSession, async (c) => {
17887
17934
  try {
17888
17935
  body = await c.req.json();
17889
17936
  } catch {
17890
- console.error(TAG22 + " rejected status=400 reason=schema:body-not-json");
17937
+ console.error(TAG23 + " rejected status=400 reason=schema:body-not-json");
17891
17938
  return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
17892
17939
  }
17893
17940
  const v = validate(body);
17894
17941
  if (!v.ok) {
17895
- console.error(TAG22 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
17942
+ console.error(TAG23 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
17896
17943
  return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
17897
17944
  }
17898
17945
  const envelope = v.envelope;
17899
17946
  const cacheKey = c.var.cacheKey ?? "";
17900
17947
  const senderId = getAccountIdForSession(cacheKey) ?? "";
17901
17948
  if (!senderId) {
17902
- console.error(TAG22 + " rejected status=500 reason=admin-account-not-resolved");
17949
+ console.error(TAG23 + " rejected status=500 reason=admin-account-not-resolved");
17903
17950
  return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
17904
17951
  }
17905
17952
  const payloadBytes = JSON.stringify(envelope).length;
17906
17953
  console.log(
17907
- TAG22 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
17954
+ TAG23 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
17908
17955
  );
17909
17956
  const initialMessage = buildInitialMessage(envelope);
17910
17957
  const spawnStart = Date.now();
17911
17958
  const sessionId = randomUUID9();
17912
- console.log(TAG22 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
17959
+ console.log(TAG23 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
17913
17960
  const spawned = await managerRcSpawn({
17914
17961
  sessionId,
17915
17962
  initialMessage,
@@ -17918,12 +17965,12 @@ app28.post("/", requireAdminSession, async (c) => {
17918
17965
  });
17919
17966
  if ("error" in spawned) {
17920
17967
  console.error(
17921
- TAG22 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
17968
+ TAG23 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
17922
17969
  );
17923
17970
  return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
17924
17971
  }
17925
17972
  console.log(
17926
- TAG22 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
17973
+ TAG23 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
17927
17974
  );
17928
17975
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
17929
17976
  });
@@ -17931,7 +17978,7 @@ var linkedin_ingest_default = app28;
17931
17978
 
17932
17979
  // server/routes/admin/post-turn-context.ts
17933
17980
  import neo4j2 from "neo4j-driver";
17934
- var TAG23 = "[post-turn-context]";
17981
+ var TAG24 = "[post-turn-context]";
17935
17982
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
17936
17983
  "embedding",
17937
17984
  "passwordHash",
@@ -17973,7 +18020,7 @@ var app29 = new Hono();
17973
18020
  app29.get("/", async (c) => {
17974
18021
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
17975
18022
  if (!isLoopbackAddr2(remoteAddr)) {
17976
- console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
18023
+ console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
17977
18024
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
17978
18025
  }
17979
18026
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -17982,7 +18029,7 @@ app29.get("/", async (c) => {
17982
18029
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
17983
18030
  const offender = tokens.find((t) => !isLoopbackAddr2(t));
17984
18031
  if (offender !== void 0) {
17985
- console.error(`${TAG23} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
18032
+ console.error(`${TAG24} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
17986
18033
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
17987
18034
  }
17988
18035
  }
@@ -18014,7 +18061,7 @@ app29.get("/", async (c) => {
18014
18061
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
18015
18062
  const total = Date.now() - started;
18016
18063
  console.log(
18017
- `${TAG23} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
18064
+ `${TAG24} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
18018
18065
  );
18019
18066
  return c.json({
18020
18067
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -18023,7 +18070,7 @@ app29.get("/", async (c) => {
18023
18070
  const elapsed = Date.now() - started;
18024
18071
  const message = err instanceof Error ? err.message : String(err);
18025
18072
  console.error(
18026
- `${TAG23} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
18073
+ `${TAG24} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
18027
18074
  );
18028
18075
  return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
18029
18076
  } finally {
@@ -18034,7 +18081,7 @@ var post_turn_context_default = app29;
18034
18081
 
18035
18082
  // server/routes/admin/public-session-context.ts
18036
18083
  import neo4j3 from "neo4j-driver";
18037
- var TAG24 = "[public-session-context]";
18084
+ var TAG25 = "[public-session-context]";
18038
18085
  var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
18039
18086
  "embedding",
18040
18087
  "passwordHash",
@@ -18076,7 +18123,7 @@ var app30 = new Hono();
18076
18123
  app30.get("/", async (c) => {
18077
18124
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
18078
18125
  if (!isLoopbackAddr3(remoteAddr)) {
18079
- console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
18126
+ console.error(`${TAG25} reject reason=non-loopback remoteAddr=${remoteAddr}`);
18080
18127
  return c.json({ error: "public-session-context-loopback-only" }, 403);
18081
18128
  }
18082
18129
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -18085,7 +18132,7 @@ app30.get("/", async (c) => {
18085
18132
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
18086
18133
  const offender = tokens.find((t) => !isLoopbackAddr3(t));
18087
18134
  if (offender !== void 0) {
18088
- console.error(`${TAG24} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
18135
+ console.error(`${TAG25} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
18089
18136
  return c.json({ error: "public-session-context-loopback-only" }, 403);
18090
18137
  }
18091
18138
  }
@@ -18116,7 +18163,7 @@ app30.get("/", async (c) => {
18116
18163
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
18117
18164
  const total = Date.now() - started;
18118
18165
  console.log(
18119
- `${TAG24} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
18166
+ `${TAG25} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
18120
18167
  );
18121
18168
  return c.json({
18122
18169
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -18125,7 +18172,7 @@ app30.get("/", async (c) => {
18125
18172
  const elapsed = Date.now() - started;
18126
18173
  const message = err instanceof Error ? err.message : String(err);
18127
18174
  console.error(
18128
- `${TAG24} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
18175
+ `${TAG25} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
18129
18176
  );
18130
18177
  return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
18131
18178
  } finally {
@@ -18135,7 +18182,7 @@ app30.get("/", async (c) => {
18135
18182
  var public_session_context_default = app30;
18136
18183
 
18137
18184
  // server/routes/admin/public-session-exit.ts
18138
- var TAG25 = "[public-session-exit-route]";
18185
+ var TAG26 = "[public-session-exit-route]";
18139
18186
  function isLoopbackAddr4(addr) {
18140
18187
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
18141
18188
  }
@@ -18143,7 +18190,7 @@ var app31 = new Hono();
18143
18190
  app31.post("/", async (c) => {
18144
18191
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
18145
18192
  if (!isLoopbackAddr4(remoteAddr)) {
18146
- console.error(`${TAG25} reject reason=non-loopback remoteAddr=${remoteAddr}`);
18193
+ console.error(`${TAG26} reject reason=non-loopback remoteAddr=${remoteAddr}`);
18147
18194
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
18148
18195
  }
18149
18196
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -18152,7 +18199,7 @@ app31.post("/", async (c) => {
18152
18199
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
18153
18200
  const offender = tokens.find((t) => !isLoopbackAddr4(t));
18154
18201
  if (offender !== void 0) {
18155
- console.error(`${TAG25} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
18202
+ console.error(`${TAG26} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
18156
18203
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
18157
18204
  }
18158
18205
  }
@@ -18170,7 +18217,7 @@ app31.post("/", async (c) => {
18170
18217
  var public_session_exit_default = app31;
18171
18218
 
18172
18219
  // server/routes/admin/access-session-evict.ts
18173
- var TAG26 = "[access-session-evict]";
18220
+ var TAG27 = "[access-session-evict]";
18174
18221
  function isLoopbackAddr5(addr) {
18175
18222
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
18176
18223
  }
@@ -18178,7 +18225,7 @@ var app32 = new Hono();
18178
18225
  app32.post("/", async (c) => {
18179
18226
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
18180
18227
  if (!isLoopbackAddr5(remoteAddr)) {
18181
- console.error(`${TAG26} reject reason=non-loopback remoteAddr=${remoteAddr}`);
18228
+ console.error(`${TAG27} reject reason=non-loopback remoteAddr=${remoteAddr}`);
18182
18229
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
18183
18230
  }
18184
18231
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -18187,7 +18234,7 @@ app32.post("/", async (c) => {
18187
18234
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
18188
18235
  const offender = tokens.find((t) => !isLoopbackAddr5(t));
18189
18236
  if (offender !== void 0) {
18190
- console.error(`${TAG26} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
18237
+ console.error(`${TAG27} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
18191
18238
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
18192
18239
  }
18193
18240
  }
@@ -18200,7 +18247,7 @@ app32.post("/", async (c) => {
18200
18247
  const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
18201
18248
  if (!grantId) return c.json({ error: "grantId required" }, 400);
18202
18249
  const dropped = evictAccessSessionsByGrant(grantId);
18203
- console.log(`${TAG26} grantId=${grantId} dropped=${dropped}`);
18250
+ console.log(`${TAG27} grantId=${grantId} dropped=${dropped}`);
18204
18251
  return c.json({ ok: true, dropped });
18205
18252
  });
18206
18253
  var access_session_evict_default = app32;
@@ -18208,7 +18255,7 @@ var access_session_evict_default = app32;
18208
18255
  // server/routes/admin/identity.ts
18209
18256
  var import_dist4 = __toESM(require_dist3(), 1);
18210
18257
  var app33 = new Hono();
18211
- var TAG27 = "[admin-identity]";
18258
+ var TAG28 = "[admin-identity]";
18212
18259
  function managerBase5() {
18213
18260
  const port2 = (0, import_dist4.requirePortEnv)("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-identity" });
18214
18261
  return `http://127.0.0.1:${port2}`;
@@ -18239,55 +18286,88 @@ app33.post("/validate", async (c) => {
18239
18286
  const verdict = validatePin(pin, USERS_FILE);
18240
18287
  if (verdict.kind !== "match") {
18241
18288
  if (verdict.kind === "miss") fireStop();
18242
- console.log(`${TAG27} op=endpoint-validate result=${verdict.kind}${sessionId ? ` sessionId=${sessionId.slice(0, 8)}` : ""}`);
18289
+ console.log(`${TAG28} op=endpoint-validate result=${verdict.kind}${sessionId ? ` sessionId=${sessionId.slice(0, 8)}` : ""}`);
18243
18290
  return c.json({ kind: verdict.kind });
18244
18291
  }
18245
18292
  const { userId } = verdict;
18246
18293
  const accounts = resolveUserAccounts(userId);
18247
18294
  if (accounts.length === 0) {
18248
18295
  fireStop();
18249
- console.log(`${TAG27} op=endpoint-validate result=no-account userId=${userId.slice(0, 8)}`);
18296
+ console.log(`${TAG28} op=endpoint-validate result=no-account userId=${userId.slice(0, 8)}`);
18250
18297
  return c.json({ kind: "miss" });
18251
18298
  }
18252
18299
  const accountId = accounts[0].accountId;
18253
18300
  const { userName } = await resolveUserIdentity(accountId, userId);
18254
18301
  const aboutOwner = await resolveOwnerProfileBlock(accountId, userId);
18255
- console.log(`${TAG27} op=endpoint-validate result=match userId=${userId.slice(0, 8)} aboutOwner=${aboutOwner.ok ? "ok" : `unresolved:${aboutOwner.reason}`}`);
18302
+ console.log(`${TAG28} op=endpoint-validate result=match userId=${userId.slice(0, 8)} aboutOwner=${aboutOwner.ok ? "ok" : `unresolved:${aboutOwner.reason}`}`);
18256
18303
  return c.json({ kind: "match", userId, userName, aboutOwner });
18257
18304
  });
18258
18305
  var identity_default = app33;
18259
18306
 
18260
- // server/routes/admin/index.ts
18307
+ // server/routes/admin/browser.ts
18261
18308
  var app34 = new Hono();
18262
- app34.route("/session", session_default);
18263
- app34.route("/identity", identity_default);
18264
- app34.route("/logs", logs_default);
18265
- app34.route("/claude-info", claude_info_default);
18266
- app34.route("/attachment", attachment_default);
18267
- app34.route("/agents", agents_default);
18268
- app34.route("/sessions", sessions_default);
18269
- app34.route("/claude-sessions", claude_sessions_default);
18270
- app34.route("/log-ingest", log_ingest_default);
18271
- app34.route("/events", events_default);
18272
- app34.route("/files", files_default);
18273
- app34.route("/graph-search", graph_search_default);
18274
- app34.route("/graph-subgraph", graph_subgraph_default);
18275
- app34.route("/graph-delete", graph_delete_default);
18276
- app34.route("/graph-restore", graph_restore_default);
18277
- app34.route("/graph-labels-in-graph", graph_labels_in_graph_default);
18278
- app34.route("/graph-default-view", graph_default_view_default);
18279
- app34.route("/sidebar-artefacts", sidebar_artefacts_default);
18280
- app34.route("/sidebar-sessions", sidebar_sessions_default);
18281
- app34.route("/session-delete", session_delete_default);
18282
- app34.route("/session-rc-spawn", session_rc_spawn_default);
18283
- app34.route("/system-stats", system_stats_default);
18284
- app34.route("/health-brand", health_default2);
18285
- app34.route("/linkedin-ingest", linkedin_ingest_default);
18286
- app34.route("/post-turn-context", post_turn_context_default);
18287
- app34.route("/public-session-context", public_session_context_default);
18288
- app34.route("/public-session-exit", public_session_exit_default);
18289
- app34.route("/access-session-evict", access_session_evict_default);
18290
- var admin_default = app34;
18309
+ app34.post("/launch", requireAdminSession, async (c) => {
18310
+ try {
18311
+ const transport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
18312
+ console.error(`[admin/browser/launch] op=request transport=${transport}`);
18313
+ if (transport === "vnc") {
18314
+ const vncOk = await ensureVnc();
18315
+ console.error(`[admin/browser/launch] op=vnc-ensured ok=${vncOk}`);
18316
+ if (!vncOk) {
18317
+ console.error("[admin/browser/launch] op=vnc-failed");
18318
+ return c.json({ ok: false, error: "VNC failed to start" }, 502);
18319
+ }
18320
+ }
18321
+ const cdpOk = await ensureCdp(transport);
18322
+ console.error(`[admin/browser/launch] op=cdp-ensured ok=${cdpOk}`);
18323
+ if (!cdpOk) {
18324
+ console.error("[admin/browser/launch] op=cdp-failed");
18325
+ return c.json({ ok: false, error: "Chrome failed to start" }, 502);
18326
+ }
18327
+ console.error(`[admin/browser/launch] op=ready transport=${transport}`);
18328
+ return c.json({ ok: true, transport });
18329
+ } catch (err) {
18330
+ console.error("[admin/browser/launch] Failed to start browser:", err);
18331
+ return c.json(
18332
+ { ok: false, error: err instanceof Error ? err.message : "Unknown error" },
18333
+ 500
18334
+ );
18335
+ }
18336
+ });
18337
+ var browser_default = app34;
18338
+
18339
+ // server/routes/admin/index.ts
18340
+ var app35 = new Hono();
18341
+ app35.route("/session", session_default);
18342
+ app35.route("/identity", identity_default);
18343
+ app35.route("/logs", logs_default);
18344
+ app35.route("/claude-info", claude_info_default);
18345
+ app35.route("/attachment", attachment_default);
18346
+ app35.route("/agents", agents_default);
18347
+ app35.route("/sessions", sessions_default);
18348
+ app35.route("/claude-sessions", claude_sessions_default);
18349
+ app35.route("/log-ingest", log_ingest_default);
18350
+ app35.route("/events", events_default);
18351
+ app35.route("/files", files_default);
18352
+ app35.route("/graph-search", graph_search_default);
18353
+ app35.route("/graph-subgraph", graph_subgraph_default);
18354
+ app35.route("/graph-delete", graph_delete_default);
18355
+ app35.route("/graph-restore", graph_restore_default);
18356
+ app35.route("/graph-labels-in-graph", graph_labels_in_graph_default);
18357
+ app35.route("/graph-default-view", graph_default_view_default);
18358
+ app35.route("/sidebar-artefacts", sidebar_artefacts_default);
18359
+ app35.route("/sidebar-sessions", sidebar_sessions_default);
18360
+ app35.route("/session-delete", session_delete_default);
18361
+ app35.route("/browser", browser_default);
18362
+ app35.route("/session-rc-spawn", session_rc_spawn_default);
18363
+ app35.route("/system-stats", system_stats_default);
18364
+ app35.route("/health-brand", health_default2);
18365
+ app35.route("/linkedin-ingest", linkedin_ingest_default);
18366
+ app35.route("/post-turn-context", post_turn_context_default);
18367
+ app35.route("/public-session-context", public_session_context_default);
18368
+ app35.route("/public-session-exit", public_session_exit_default);
18369
+ app35.route("/access-session-evict", access_session_evict_default);
18370
+ var admin_default = app35;
18291
18371
 
18292
18372
  // app/lib/access-gate.ts
18293
18373
  import neo4j4 from "neo4j-driver";
@@ -18498,11 +18578,11 @@ async function generateNewMagicToken(grantId) {
18498
18578
  }
18499
18579
 
18500
18580
  // server/routes/access/verify-token.ts
18501
- var TAG28 = "[access-verify]";
18581
+ var TAG29 = "[access-verify]";
18502
18582
  var MINT_TAG = "[access-session-mint]";
18503
18583
  var COOKIE_NAME = "__access_session";
18504
- var app35 = new Hono();
18505
- app35.post("/", async (c) => {
18584
+ var app36 = new Hono();
18585
+ app36.post("/", async (c) => {
18506
18586
  const ip = c.var.clientIp || "unknown";
18507
18587
  let body;
18508
18588
  try {
@@ -18517,39 +18597,39 @@ app35.post("/", async (c) => {
18517
18597
  }
18518
18598
  const rateMsg = checkAccessRateLimit(ip, agentSlug);
18519
18599
  if (rateMsg) {
18520
- console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
18600
+ console.error(`${TAG29} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
18521
18601
  return c.json({ error: rateMsg }, 429);
18522
18602
  }
18523
18603
  const grant = await findGrantByMagicToken(token);
18524
18604
  if (!grant) {
18525
18605
  recordAccessFailedAttempt(ip, agentSlug);
18526
- console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
18606
+ console.error(`${TAG29} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
18527
18607
  return c.json({ error: "invalid-or-expired-link" }, 401);
18528
18608
  }
18529
18609
  if (grant.agentSlug !== agentSlug) {
18530
18610
  recordAccessFailedAttempt(ip, agentSlug);
18531
18611
  console.error(
18532
- `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
18612
+ `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
18533
18613
  );
18534
18614
  return c.json({ error: "invalid-or-expired-link" }, 401);
18535
18615
  }
18536
18616
  if (grant.status === "expired" || grant.status === "revoked") {
18537
18617
  recordAccessFailedAttempt(ip, agentSlug);
18538
18618
  console.error(
18539
- `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
18619
+ `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
18540
18620
  );
18541
18621
  return c.json({ error: "access-no-longer-valid" }, 401);
18542
18622
  }
18543
18623
  if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
18544
18624
  recordAccessFailedAttempt(ip, agentSlug);
18545
18625
  console.error(
18546
- `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
18626
+ `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
18547
18627
  );
18548
18628
  return c.json({ error: "access-no-longer-valid" }, 401);
18549
18629
  }
18550
18630
  if (!grant.sliceToken) {
18551
18631
  console.error(
18552
- `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
18632
+ `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
18553
18633
  );
18554
18634
  return c.json({ error: "grant-misconfigured" }, 500);
18555
18635
  }
@@ -18565,12 +18645,12 @@ app35.post("/", async (c) => {
18565
18645
  await consumeMagicTokenAndActivate(grant.grantId);
18566
18646
  } catch (err) {
18567
18647
  console.error(
18568
- `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
18648
+ `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
18569
18649
  );
18570
18650
  return c.json({ error: "verification-failed" }, 500);
18571
18651
  }
18572
18652
  clearAccessRateLimit(ip, agentSlug);
18573
- console.log(`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
18653
+ console.log(`${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
18574
18654
  console.log(
18575
18655
  `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
18576
18656
  );
@@ -18584,7 +18664,7 @@ app35.post("/", async (c) => {
18584
18664
  displayName: grant.displayName
18585
18665
  });
18586
18666
  });
18587
- var verify_token_default = app35;
18667
+ var verify_token_default = app36;
18588
18668
 
18589
18669
  // app/lib/access-email.ts
18590
18670
  import { spawn as spawn2 } from "child_process";
@@ -18646,10 +18726,10 @@ async function sendMagicLinkEmail(payload) {
18646
18726
  }
18647
18727
 
18648
18728
  // server/routes/access/request-magic-link.ts
18649
- var TAG29 = "[access-request-link]";
18650
- var app36 = new Hono();
18729
+ var TAG30 = "[access-request-link]";
18730
+ var app37 = new Hono();
18651
18731
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
18652
- app36.post("/", async (c) => {
18732
+ app37.post("/", async (c) => {
18653
18733
  let body;
18654
18734
  try {
18655
18735
  body = await c.req.json();
@@ -18663,18 +18743,18 @@ app36.post("/", async (c) => {
18663
18743
  }
18664
18744
  const rateMsg = checkRequestLinkRateLimit(contactValue);
18665
18745
  if (rateMsg) {
18666
- console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=rate-limited`);
18746
+ console.error(`${TAG30} contactValue=${maskContact(contactValue)} result=rate-limited`);
18667
18747
  return c.json({ error: rateMsg }, 429);
18668
18748
  }
18669
18749
  recordRequestLinkAttempt(contactValue);
18670
18750
  const accountId = process.env.ACCOUNT_ID ?? "";
18671
18751
  if (!accountId) {
18672
- console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=no-account-id`);
18752
+ console.error(`${TAG30} contactValue=${maskContact(contactValue)} result=no-account-id`);
18673
18753
  return c.json({ message: VISITOR_MESSAGE }, 200);
18674
18754
  }
18675
18755
  const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
18676
18756
  if (!grant) {
18677
- console.log(`${TAG29} contactValue=${maskContact(contactValue)} result=notfound`);
18757
+ console.log(`${TAG30} contactValue=${maskContact(contactValue)} result=notfound`);
18678
18758
  return c.json({ message: VISITOR_MESSAGE }, 200);
18679
18759
  }
18680
18760
  let token;
@@ -18682,7 +18762,7 @@ app36.post("/", async (c) => {
18682
18762
  token = await generateNewMagicToken(grant.grantId);
18683
18763
  } catch (err) {
18684
18764
  console.error(
18685
- `${TAG29} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
18765
+ `${TAG30} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
18686
18766
  );
18687
18767
  return c.json({ message: VISITOR_MESSAGE }, 200);
18688
18768
  }
@@ -18714,22 +18794,22 @@ app36.post("/", async (c) => {
18714
18794
  });
18715
18795
  if (!sendResult.ok) {
18716
18796
  console.error(
18717
- `${TAG29} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
18797
+ `${TAG30} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
18718
18798
  );
18719
18799
  return c.json({ message: VISITOR_MESSAGE }, 200);
18720
18800
  }
18721
18801
  console.log(
18722
- `${TAG29} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
18802
+ `${TAG30} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
18723
18803
  );
18724
18804
  return c.json({ message: VISITOR_MESSAGE }, 200);
18725
18805
  });
18726
- var request_magic_link_default = app36;
18806
+ var request_magic_link_default = app37;
18727
18807
 
18728
18808
  // server/routes/access/index.ts
18729
- var app37 = new Hono();
18730
- app37.route("/verify-token", verify_token_default);
18731
- app37.route("/request-magic-link", request_magic_link_default);
18732
- var access_default = app37;
18809
+ var app38 = new Hono();
18810
+ app38.route("/verify-token", verify_token_default);
18811
+ app38.route("/request-magic-link", request_magic_link_default);
18812
+ var access_default = app38;
18733
18813
 
18734
18814
  // server/routes/sites.ts
18735
18815
  import { existsSync as existsSync19, readFileSync as readFileSync18, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
@@ -18764,8 +18844,8 @@ function getExt(p) {
18764
18844
  if (idx < p.lastIndexOf("/")) return "";
18765
18845
  return p.slice(idx).toLowerCase();
18766
18846
  }
18767
- var app38 = new Hono();
18768
- app38.get("/:rel{.*}", (c) => {
18847
+ var app39 = new Hono();
18848
+ app39.get("/:rel{.*}", (c) => {
18769
18849
  const reqPath = c.req.path;
18770
18850
  const rawRel = c.req.param("rel") ?? "";
18771
18851
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -18868,7 +18948,7 @@ app38.get("/:rel{.*}", (c) => {
18868
18948
  "X-Content-Type-Options": "nosniff"
18869
18949
  });
18870
18950
  });
18871
- var sites_default = app38;
18951
+ var sites_default = app39;
18872
18952
 
18873
18953
  // app/lib/visitor-token.ts
18874
18954
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
@@ -18968,7 +19048,7 @@ function readBrandConfig() {
18968
19048
  }
18969
19049
 
18970
19050
  // server/routes/visitor-consent.ts
18971
- var app39 = new Hono();
19051
+ var app40 = new Hono();
18972
19052
  var CONSENT_COOKIE_NAME = "mxy_consent";
18973
19053
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
18974
19054
  var DEFAULT_CONSENT_COPY = {
@@ -19013,17 +19093,17 @@ function siteSlugFromReferer(referer) {
19013
19093
  return "";
19014
19094
  }
19015
19095
  }
19016
- app39.options("/consent", (c) => {
19096
+ app40.options("/consent", (c) => {
19017
19097
  const origin = getOrigin(c);
19018
19098
  setCorsHeaders(c, origin);
19019
19099
  return c.body(null, 204);
19020
19100
  });
19021
- app39.options("/brand-config", (c) => {
19101
+ app40.options("/brand-config", (c) => {
19022
19102
  const origin = getOrigin(c);
19023
19103
  setCorsHeaders(c, origin);
19024
19104
  return c.body(null, 204);
19025
19105
  });
19026
- app39.post("/consent", async (c) => {
19106
+ app40.post("/consent", async (c) => {
19027
19107
  const origin = getOrigin(c);
19028
19108
  setCorsHeaders(c, origin);
19029
19109
  let raw;
@@ -19063,7 +19143,7 @@ app39.post("/consent", async (c) => {
19063
19143
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
19064
19144
  return c.body(null, 204);
19065
19145
  });
19066
- app39.get("/brand-config", (c) => {
19146
+ app40.get("/brand-config", (c) => {
19067
19147
  const origin = getOrigin(c);
19068
19148
  setCorsHeaders(c, origin);
19069
19149
  const brand = readBrandConfig();
@@ -19073,7 +19153,7 @@ app39.get("/brand-config", (c) => {
19073
19153
  c.header("Cache-Control", "public, max-age=300");
19074
19154
  return c.json({ consent: { copy, palette } });
19075
19155
  });
19076
- var visitor_consent_default = app39;
19156
+ var visitor_consent_default = app40;
19077
19157
 
19078
19158
  // server/routes/listings.ts
19079
19159
  function getCookie(headerValue, name) {
@@ -19100,8 +19180,8 @@ function appendConsentParams(pageUrl, token) {
19100
19180
  }
19101
19181
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
19102
19182
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
19103
- var app40 = new Hono();
19104
- app40.get("/:slug/click", async (c) => {
19183
+ var app41 = new Hono();
19184
+ app41.get("/:slug/click", async (c) => {
19105
19185
  const slug = c.req.param("slug") ?? "";
19106
19186
  const rawSession = c.req.query("session") ?? "";
19107
19187
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -19165,10 +19245,10 @@ app40.get("/:slug/click", async (c) => {
19165
19245
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
19166
19246
  return c.redirect(redirectUrl, 302);
19167
19247
  });
19168
- var listings_default = app40;
19248
+ var listings_default = app41;
19169
19249
 
19170
19250
  // server/routes/visitor-event.ts
19171
- var app41 = new Hono();
19251
+ var app42 = new Hono();
19172
19252
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
19173
19253
  var buckets = /* @__PURE__ */ new Map();
19174
19254
  var RATE_LIMIT = 60;
@@ -19235,12 +19315,12 @@ function originAllowed(origin, allowlist) {
19235
19315
  return false;
19236
19316
  }
19237
19317
  }
19238
- app41.options("/event", (c) => {
19318
+ app42.options("/event", (c) => {
19239
19319
  const origin = getOrigin2(c);
19240
19320
  setCorsHeaders2(c, origin);
19241
19321
  return c.body(null, 204);
19242
19322
  });
19243
- app41.post("/event", async (c) => {
19323
+ app42.post("/event", async (c) => {
19244
19324
  const origin = getOrigin2(c);
19245
19325
  setCorsHeaders2(c, origin);
19246
19326
  const ua = c.req.header("user-agent") ?? "";
@@ -19445,7 +19525,7 @@ async function writeEvent(opts) {
19445
19525
  );
19446
19526
  }
19447
19527
  }
19448
- var visitor_event_default = app41;
19528
+ var visitor_event_default = app42;
19449
19529
 
19450
19530
  // server/routes/session.ts
19451
19531
  import { resolve as resolve23 } from "path";
@@ -19482,8 +19562,8 @@ function withVisitorCookie(response, visitorId) {
19482
19562
  headers
19483
19563
  });
19484
19564
  }
19485
- var app42 = new Hono();
19486
- app42.post("/", async (c) => {
19565
+ var app43 = new Hono();
19566
+ app43.post("/", async (c) => {
19487
19567
  let body;
19488
19568
  try {
19489
19569
  body = await c.req.json();
@@ -19687,7 +19767,7 @@ app42.post("/", async (c) => {
19687
19767
  newVisitorId
19688
19768
  );
19689
19769
  });
19690
- var session_default2 = app42;
19770
+ var session_default2 = app43;
19691
19771
 
19692
19772
  // app/lib/graph-health.ts
19693
19773
  var import_dist5 = __toESM(require_dist4(), 1);
@@ -19910,7 +19990,7 @@ async function startFileWatcher(opts = {}) {
19910
19990
  }
19911
19991
 
19912
19992
  // app/lib/whatsapp/inbound/claude-bridge.ts
19913
- var TAG30 = "[whatsapp-adaptor]";
19993
+ var TAG31 = "[whatsapp-adaptor]";
19914
19994
  function whatsappTurnTimeoutMs() {
19915
19995
  return Number(process.env.WHATSAPP_PTY_TURN_TIMEOUT_MS ?? String(5 * 6e4));
19916
19996
  }
@@ -19927,7 +20007,7 @@ ${note}` : note;
19927
20007
  async function dispatchToClaude(input) {
19928
20008
  const hasFileMedia = !!input.mediaPath && input.mediaType !== "audio";
19929
20009
  const mediaField = hasFileMedia ? `media=${input.mediaType} mediaPath=${input.mediaPath}` : input.mediaType === "audio" ? "media=audio mediaPath=transcribed" : "media=none";
19930
- console.error(`${TAG30} inbound-media ${mediaField} role=${input.role} senderId=${input.senderId}`);
20010
+ console.error(`${TAG31} inbound-media ${mediaField} role=${input.role} senderId=${input.senderId}`);
19931
20011
  const text = composeTurn(input);
19932
20012
  if (!text.trim()) return;
19933
20013
  const result = await dispatchOnce({
@@ -19940,13 +20020,19 @@ async function dispatchToClaude(input) {
19940
20020
  timeoutMs: whatsappTurnTimeoutMs()
19941
20021
  });
19942
20022
  if ("error" in result) {
20023
+ if (result.error === "timeout" && result.cause === "no-turn") {
20024
+ recordInboundOutcome(input.senderId, "timeout-no-turn");
20025
+ } else if (result.error === "timeout" && result.cause === "relay-missed") {
20026
+ recordInboundOutcome(input.senderId, "timeout-relay-missed");
20027
+ }
19943
20028
  return;
19944
20029
  }
20030
+ recordInboundOutcome(input.senderId, "relay-ok");
19945
20031
  try {
19946
20032
  await input.reply(result.turnText);
19947
20033
  } catch (err) {
19948
20034
  const m = err instanceof Error ? err.message : String(err);
19949
- console.error(`${TAG30} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
20035
+ console.error(`${TAG31} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
19950
20036
  }
19951
20037
  }
19952
20038
  function startReaper2() {
@@ -20276,9 +20362,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
20276
20362
  function isPublicHost(host) {
20277
20363
  return host.startsWith("public.") || aliasDomains.has(host);
20278
20364
  }
20279
- var app43 = new Hono();
20280
- app43.use("*", clientIpMiddleware);
20281
- app43.use("*", async (c, next) => {
20365
+ var app44 = new Hono();
20366
+ app44.use("*", clientIpMiddleware);
20367
+ app44.use("*", async (c, next) => {
20282
20368
  await next();
20283
20369
  c.header("X-Content-Type-Options", "nosniff");
20284
20370
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -20288,7 +20374,7 @@ app43.use("*", async (c, next) => {
20288
20374
  );
20289
20375
  });
20290
20376
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
20291
- app43.use("*", async (c, next) => {
20377
+ app44.use("*", async (c, next) => {
20292
20378
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
20293
20379
  await next();
20294
20380
  return;
@@ -20306,7 +20392,7 @@ app43.use("*", async (c, next) => {
20306
20392
  });
20307
20393
  }
20308
20394
  });
20309
- app43.use("*", async (c, next) => {
20395
+ app44.use("*", async (c, next) => {
20310
20396
  const host = (c.req.header("host") ?? "").split(":")[0];
20311
20397
  if (!isPublicHost(host)) {
20312
20398
  await next();
@@ -20337,7 +20423,7 @@ function resolveRemoteAuthOpts() {
20337
20423
  return brandLoginOpts;
20338
20424
  }
20339
20425
  var MAX_LOGIN_BODY = 8 * 1024;
20340
- app43.post("/__remote-auth/login", async (c) => {
20426
+ app44.post("/__remote-auth/login", async (c) => {
20341
20427
  const client = clientFrom(c);
20342
20428
  const clientIp = client.ip || "unknown";
20343
20429
  if (!requestIsTlsTerminated(c)) {
@@ -20382,7 +20468,7 @@ app43.post("/__remote-auth/login", async (c) => {
20382
20468
  }
20383
20469
  });
20384
20470
  });
20385
- app43.get("/__remote-auth/logout", (c) => {
20471
+ app44.get("/__remote-auth/logout", (c) => {
20386
20472
  const client = clientFrom(c);
20387
20473
  const clientIp = client.ip || "unknown";
20388
20474
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -20395,7 +20481,7 @@ app43.get("/__remote-auth/logout", (c) => {
20395
20481
  }
20396
20482
  });
20397
20483
  });
20398
- app43.post("/__remote-auth/change-password", async (c) => {
20484
+ app44.post("/__remote-auth/change-password", async (c) => {
20399
20485
  const client = clientFrom(c);
20400
20486
  const clientIp = client.ip || "unknown";
20401
20487
  const rateLimited = checkRateLimit(client);
@@ -20446,13 +20532,13 @@ app43.post("/__remote-auth/change-password", async (c) => {
20446
20532
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
20447
20533
  }
20448
20534
  });
20449
- app43.get("/__remote-auth/setup", (c) => {
20535
+ app44.get("/__remote-auth/setup", (c) => {
20450
20536
  if (isRemoteAuthConfigured()) {
20451
20537
  return c.redirect("/");
20452
20538
  }
20453
20539
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
20454
20540
  });
20455
- app43.post("/__remote-auth/set-initial-password", async (c) => {
20541
+ app44.post("/__remote-auth/set-initial-password", async (c) => {
20456
20542
  if (isRemoteAuthConfigured()) {
20457
20543
  return c.redirect("/");
20458
20544
  }
@@ -20490,10 +20576,10 @@ app43.post("/__remote-auth/set-initial-password", async (c) => {
20490
20576
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
20491
20577
  }
20492
20578
  });
20493
- app43.get("/api/remote-auth/status", (c) => {
20579
+ app44.get("/api/remote-auth/status", (c) => {
20494
20580
  return c.json({ configured: isRemoteAuthConfigured() });
20495
20581
  });
20496
- app43.post("/api/remote-auth/set-password", async (c) => {
20582
+ app44.post("/api/remote-auth/set-password", async (c) => {
20497
20583
  let body;
20498
20584
  try {
20499
20585
  body = await c.req.json();
@@ -20524,9 +20610,9 @@ app43.post("/api/remote-auth/set-password", async (c) => {
20524
20610
  return c.json({ error: "Failed to save password" }, 500);
20525
20611
  }
20526
20612
  });
20527
- app43.route("/api/_client-error", client_error_default);
20613
+ app44.route("/api/_client-error", client_error_default);
20528
20614
  console.log("[client-error-route] mounted");
20529
- app43.use("*", async (c, next) => {
20615
+ app44.use("*", async (c, next) => {
20530
20616
  const host = (c.req.header("host") ?? "").split(":")[0];
20531
20617
  const path2 = c.req.path;
20532
20618
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -20559,13 +20645,13 @@ app43.use("*", async (c, next) => {
20559
20645
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
20560
20646
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
20561
20647
  });
20562
- app43.route("/api/health", health_default);
20563
- app43.route("/api/chat", chat_default);
20564
- app43.route("/api/whatsapp", whatsapp_default);
20565
- app43.route("/api/onboarding", onboarding_default);
20566
- app43.route("/api/admin", admin_default);
20567
- app43.route("/api/access", access_default);
20568
- app43.route("/api/session", session_default2);
20648
+ app44.route("/api/health", health_default);
20649
+ app44.route("/api/chat", chat_default);
20650
+ app44.route("/api/whatsapp", whatsapp_default);
20651
+ app44.route("/api/onboarding", onboarding_default);
20652
+ app44.route("/api/admin", admin_default);
20653
+ app44.route("/api/access", access_default);
20654
+ app44.route("/api/session", session_default2);
20569
20655
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
20570
20656
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
20571
20657
  var IMAGE_MIME = {
@@ -20577,7 +20663,7 @@ var IMAGE_MIME = {
20577
20663
  ".svg": "image/svg+xml",
20578
20664
  ".ico": "image/x-icon"
20579
20665
  };
20580
- app43.get("/agent-assets/:slug/:filename", (c) => {
20666
+ app44.get("/agent-assets/:slug/:filename", (c) => {
20581
20667
  const slug = c.req.param("slug");
20582
20668
  const filename = c.req.param("filename");
20583
20669
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -20612,7 +20698,7 @@ app43.get("/agent-assets/:slug/:filename", (c) => {
20612
20698
  "Cache-Control": "public, max-age=3600"
20613
20699
  });
20614
20700
  });
20615
- app43.get("/generated/:filename", (c) => {
20701
+ app44.get("/generated/:filename", (c) => {
20616
20702
  const filename = c.req.param("filename");
20617
20703
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
20618
20704
  console.error(`[generated] serve file=${filename} status=403`);
@@ -20642,10 +20728,10 @@ app43.get("/generated/:filename", (c) => {
20642
20728
  "Cache-Control": "public, max-age=86400"
20643
20729
  });
20644
20730
  });
20645
- app43.route("/sites", sites_default);
20646
- app43.route("/listings", listings_default);
20647
- app43.route("/v", visitor_event_default);
20648
- app43.route("/v", visitor_consent_default);
20731
+ app44.route("/sites", sites_default);
20732
+ app44.route("/listings", listings_default);
20733
+ app44.route("/v", visitor_event_default);
20734
+ app44.route("/v", visitor_consent_default);
20649
20735
  var htmlCache = /* @__PURE__ */ new Map();
20650
20736
  var brandLogoPath = "/brand/maxy-monochrome.png";
20651
20737
  var brandIconPath = "/brand/maxy-monochrome.png";
@@ -20776,7 +20862,7 @@ function escapeHtml(s) {
20776
20862
  function agentUnavailableHtml() {
20777
20863
  return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
20778
20864
  }
20779
- app43.get("/", (c) => {
20865
+ app44.get("/", (c) => {
20780
20866
  const host = (c.req.header("host") ?? "").split(":")[0];
20781
20867
  if (isPublicHost(host)) {
20782
20868
  const defaultSlug = resolveDefaultSlug();
@@ -20792,12 +20878,12 @@ app43.get("/", (c) => {
20792
20878
  }
20793
20879
  return c.html(cachedHtml("index.html"));
20794
20880
  });
20795
- app43.get("/public", (c) => {
20881
+ app44.get("/public", (c) => {
20796
20882
  const host = (c.req.header("host") ?? "").split(":")[0];
20797
20883
  if (isPublicHost(host)) return c.text("Not found", 404);
20798
20884
  return c.html(cachedHtml("public.html"));
20799
20885
  });
20800
- app43.get("/chat", (c) => {
20886
+ app44.get("/chat", (c) => {
20801
20887
  const host = (c.req.header("host") ?? "").split(":")[0];
20802
20888
  if (isPublicHost(host)) return c.text("Not found", 404);
20803
20889
  return c.html(cachedHtml("public.html"));
@@ -20816,9 +20902,9 @@ async function logViewerFetch(c, next) {
20816
20902
  duration_ms: Date.now() - start
20817
20903
  });
20818
20904
  }
20819
- app43.use("/vnc-viewer.html", logViewerFetch);
20820
- app43.use("/vnc-popout.html", logViewerFetch);
20821
- app43.get("/vnc-popout.html", (c) => {
20905
+ app44.use("/vnc-viewer.html", logViewerFetch);
20906
+ app44.use("/vnc-popout.html", logViewerFetch);
20907
+ app44.get("/vnc-popout.html", (c) => {
20822
20908
  let html = htmlCache.get("vnc-popout.html");
20823
20909
  if (!html) {
20824
20910
  html = readFileSync22(resolve26(process.cwd(), "public", "vnc-popout.html"), "utf-8");
@@ -20831,7 +20917,7 @@ app43.get("/vnc-popout.html", (c) => {
20831
20917
  }
20832
20918
  return c.html(html);
20833
20919
  });
20834
- app43.post("/api/vnc/client-event", async (c) => {
20920
+ app44.post("/api/vnc/client-event", async (c) => {
20835
20921
  let body;
20836
20922
  try {
20837
20923
  body = await c.req.json();
@@ -20852,25 +20938,30 @@ app43.post("/api/vnc/client-event", async (c) => {
20852
20938
  });
20853
20939
  return c.json({ ok: true });
20854
20940
  });
20855
- app43.get("/g/:slug", (c) => {
20941
+ app44.get("/g/:slug", (c) => {
20856
20942
  return c.html(brandedPublicHtml());
20857
20943
  });
20858
- app43.get("/graph", (c) => {
20944
+ app44.get("/graph", (c) => {
20859
20945
  const host = (c.req.header("host") ?? "").split(":")[0];
20860
20946
  if (isPublicHost(host)) return c.text("Not found", 404);
20861
20947
  return c.html(cachedHtml("graph.html"));
20862
20948
  });
20863
- app43.get("/sessions", (c) => {
20949
+ app44.get("/sessions", (c) => {
20864
20950
  const host = (c.req.header("host") ?? "").split(":")[0];
20865
20951
  if (isPublicHost(host)) return c.text("Not found", 404);
20866
20952
  return c.html(cachedHtml("sessions.html"));
20867
20953
  });
20868
- app43.get("/data", (c) => {
20954
+ app44.get("/data", (c) => {
20869
20955
  const host = (c.req.header("host") ?? "").split(":")[0];
20870
20956
  if (isPublicHost(host)) return c.text("Not found", 404);
20871
20957
  return c.html(cachedHtml("data.html"));
20872
20958
  });
20873
- app43.get("/:slug", async (c, next) => {
20959
+ app44.get("/browser", (c) => {
20960
+ const host = (c.req.header("host") ?? "").split(":")[0];
20961
+ if (isPublicHost(host)) return c.text("Not found", 404);
20962
+ return c.html(cachedHtml("browser.html"));
20963
+ });
20964
+ app44.get("/:slug", async (c, next) => {
20874
20965
  const slug = c.req.param("slug");
20875
20966
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
20876
20967
  const account = resolveAccount();
@@ -20885,13 +20976,13 @@ app43.get("/:slug", async (c, next) => {
20885
20976
  await next();
20886
20977
  });
20887
20978
  if (brandFaviconPath !== "/favicon.ico") {
20888
- app43.get("/favicon.ico", (c) => {
20979
+ app44.get("/favicon.ico", (c) => {
20889
20980
  c.header("Cache-Control", "public, max-age=300");
20890
20981
  return c.redirect(brandFaviconPath, 302);
20891
20982
  });
20892
20983
  }
20893
- app43.use("/*", serveStatic({ root: "./public" }));
20894
- app43.all("*", (c) => {
20984
+ app44.use("/*", serveStatic({ root: "./public" }));
20985
+ app44.all("*", (c) => {
20895
20986
  const host = (c.req.header("host") ?? "").split(":")[0];
20896
20987
  const path2 = c.req.path;
20897
20988
  if (isPublicHost(host)) {
@@ -20905,7 +20996,7 @@ app43.all("*", (c) => {
20905
20996
  });
20906
20997
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
20907
20998
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
20908
- var httpServer = serve({ fetch: app43.fetch, port, hostname });
20999
+ var httpServer = serve({ fetch: app44.fetch, port, hostname });
20909
21000
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
20910
21001
  startAuthHealthHeartbeat();
20911
21002
  {
@@ -20942,7 +21033,7 @@ for (const m of SUBAPP_MANIFEST) {
20942
21033
  }
20943
21034
  try {
20944
21035
  const registered = [];
20945
- for (const r of app43.routes ?? []) {
21036
+ for (const r of app44.routes ?? []) {
20946
21037
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
20947
21038
  if (AGENT_SLUG_PATTERN.test(r.path)) {
20948
21039
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });