@rubytech/create-maxy-code 0.1.536 → 0.1.537

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 (199) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/hooks/__tests__/mailbox-inject.test.sh +11 -5
  3. package/payload/platform/plugins/admin/hooks/mailbox-inject.sh +12 -7
  4. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +47 -4
  5. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +5 -0
  6. package/payload/platform/plugins/docs/references/admin-ui.md +46 -3
  7. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +6 -6
  8. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  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 +67 -4
  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.js +23 -2
  13. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  14. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +6 -0
  15. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  16. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +7 -1
  17. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  18. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +8 -2
  19. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  20. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +32 -27
  21. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  22. package/payload/server/public/activity.html +5 -5
  23. package/payload/server/public/agents.html +4 -4
  24. package/payload/server/public/assets/{AdminLoginScreens-BpHVvUsH.js → AdminLoginScreens-CYFf2awh.js} +1 -1
  25. package/payload/server/public/assets/AdminLoginScreens-CYFf2awh.js.br +0 -0
  26. package/payload/server/public/assets/AdminLoginScreens-CYFf2awh.js.gz +0 -0
  27. package/payload/server/public/assets/AdminShell-7l3ubYFZ.js +2 -0
  28. package/payload/server/public/assets/AdminShell-7l3ubYFZ.js.br +0 -0
  29. package/payload/server/public/assets/AdminShell-7l3ubYFZ.js.gz +0 -0
  30. package/payload/server/public/assets/{activity-B3WEyiTh.js → activity-BH65sm9Q.js} +1 -1
  31. package/payload/server/public/assets/activity-BH65sm9Q.js.br +0 -0
  32. package/payload/server/public/assets/activity-BH65sm9Q.js.gz +0 -0
  33. package/payload/server/public/assets/admin-CQtEjRGU.js +1 -0
  34. package/payload/server/public/assets/admin-CQtEjRGU.js.br +0 -0
  35. package/payload/server/public/assets/admin-CQtEjRGU.js.gz +0 -0
  36. package/payload/server/public/assets/{agents-CmYDQbdM.js → agents-CjhOWwl5.js} +1 -1
  37. package/payload/server/public/assets/agents-CjhOWwl5.js.br +0 -0
  38. package/payload/server/public/assets/agents-CjhOWwl5.js.gz +0 -0
  39. package/payload/server/public/assets/{browser-CP8TnIUm.js → browser-DGXpfL99.js} +1 -1
  40. package/payload/server/public/assets/browser-DGXpfL99.js.br +0 -0
  41. package/payload/server/public/assets/browser-DGXpfL99.js.gz +0 -0
  42. package/payload/server/public/assets/calendar-Cu4yjEuX.js +1 -0
  43. package/payload/server/public/assets/calendar-Cu4yjEuX.js.br +0 -0
  44. package/payload/server/public/assets/calendar-Cu4yjEuX.js.gz +0 -0
  45. package/payload/server/public/assets/chat-0na4XVZC.js +1 -0
  46. package/payload/server/public/assets/chat-0na4XVZC.js.br +0 -0
  47. package/payload/server/public/assets/chat-0na4XVZC.js.gz +0 -0
  48. package/payload/server/public/assets/chevron-left-DHzJobWp.js +1 -0
  49. package/payload/server/public/assets/chevron-left-DHzJobWp.js.br +0 -0
  50. package/payload/server/public/assets/chevron-right-yg4DMfLT.js +1 -0
  51. package/payload/server/public/assets/chevron-right-yg4DMfLT.js.br +0 -0
  52. package/payload/server/public/assets/clock-BIDzihvp.js +1 -0
  53. package/payload/server/public/assets/clock-BIDzihvp.js.br +0 -0
  54. package/payload/server/public/assets/clock-BIDzihvp.js.gz +0 -0
  55. package/payload/server/public/assets/data-Ql9qG3YO.js +1 -0
  56. package/payload/server/public/assets/data-Ql9qG3YO.js.br +0 -0
  57. package/payload/server/public/assets/data-Ql9qG3YO.js.gz +0 -0
  58. package/payload/server/public/assets/{file-text-lsHlk4lR.js → file-text-CbQoZfJD.js} +1 -1
  59. package/payload/server/public/assets/file-text-CbQoZfJD.js.br +0 -0
  60. package/payload/server/public/assets/file-text-CbQoZfJD.js.gz +0 -0
  61. package/payload/server/public/assets/{graph-B0awsgVj.js → graph-7414Kjn9.js} +3 -3
  62. package/payload/server/public/assets/graph-7414Kjn9.js.br +0 -0
  63. package/payload/server/public/assets/graph-7414Kjn9.js.gz +0 -0
  64. package/payload/server/public/assets/{graph-labels-Ci5Jz1oY.js → graph-labels-D695CsC9.js} +1 -1
  65. package/payload/server/public/assets/graph-labels-D695CsC9.js.br +0 -0
  66. package/payload/server/public/assets/graph-labels-D695CsC9.js.gz +0 -0
  67. package/payload/server/public/assets/{maximize-2-DRndYysc.js → maximize-2-CD0gTjW_.js} +1 -1
  68. package/payload/server/public/assets/maximize-2-CD0gTjW_.js.br +0 -0
  69. package/payload/server/public/assets/maximize-2-CD0gTjW_.js.gz +0 -0
  70. package/payload/server/public/assets/operator-CLw4PZs5.js +1 -0
  71. package/payload/server/public/assets/operator-CLw4PZs5.js.br +0 -0
  72. package/payload/server/public/assets/operator-CLw4PZs5.js.gz +0 -0
  73. package/payload/server/public/assets/{page-RkwjxAgF.js → page-BNRFmFc-.js} +5 -5
  74. package/payload/server/public/assets/page-BNRFmFc-.js.br +0 -0
  75. package/payload/server/public/assets/page-BNRFmFc-.js.gz +0 -0
  76. package/payload/server/public/assets/page-BRr295G2.js +1 -0
  77. package/payload/server/public/assets/page-BRr295G2.js.br +0 -0
  78. package/payload/server/public/assets/page-BRr295G2.js.gz +0 -0
  79. package/payload/server/public/assets/play-CbRU6q0T.js +1 -0
  80. package/payload/server/public/assets/play-CbRU6q0T.js.br +0 -0
  81. package/payload/server/public/assets/play-CbRU6q0T.js.gz +0 -0
  82. package/payload/server/public/assets/{public-BNAVdC2B.js → public-CqbKJ-Q3.js} +1 -1
  83. package/payload/server/public/assets/public-CqbKJ-Q3.js.br +0 -0
  84. package/payload/server/public/assets/public-CqbKJ-Q3.js.gz +0 -0
  85. package/payload/server/public/assets/{rotate-ccw-D6DZb7tR.js → rotate-ccw-CjliWBou.js} +1 -1
  86. package/payload/server/public/assets/rotate-ccw-CjliWBou.js.br +0 -0
  87. package/payload/server/public/assets/rotate-ccw-CjliWBou.js.gz +0 -0
  88. package/payload/server/public/assets/routines-yJuRuBfn.js +2 -0
  89. package/payload/server/public/assets/routines-yJuRuBfn.js.br +0 -0
  90. package/payload/server/public/assets/routines-yJuRuBfn.js.gz +0 -0
  91. package/payload/server/public/assets/{skills-9S_nNIpO.js → skills-ipK9BJFb.js} +1 -1
  92. package/payload/server/public/assets/skills-ipK9BJFb.js.br +0 -0
  93. package/payload/server/public/assets/skills-ipK9BJFb.js.gz +0 -0
  94. package/payload/server/public/assets/tasks-CGcJHQ3X.js +3 -0
  95. package/payload/server/public/assets/tasks-CGcJHQ3X.js.br +0 -0
  96. package/payload/server/public/assets/tasks-CGcJHQ3X.js.gz +0 -0
  97. package/payload/server/public/assets/{triangle-alert-BcpCZDHe.js → triangle-alert-CjsLPsBD.js} +1 -1
  98. package/payload/server/public/assets/triangle-alert-CjsLPsBD.js.br +0 -0
  99. package/payload/server/public/assets/triangle-alert-CjsLPsBD.js.gz +0 -0
  100. package/payload/server/public/assets/{useCopyFeedback-DqTnYnz5.js → useCopyFeedback-umIgCxI_.js} +1 -1
  101. package/payload/server/public/assets/useCopyFeedback-umIgCxI_.js.br +0 -0
  102. package/payload/server/public/assets/useCopyFeedback-umIgCxI_.js.gz +0 -0
  103. package/payload/server/public/assets/{useSubAccountSwitcher-CYEDydoq.css → useSubAccountSwitcher-D8jXV7R5.css} +1 -1
  104. package/payload/server/public/assets/useSubAccountSwitcher-D8jXV7R5.css.br +0 -0
  105. package/payload/server/public/assets/{useSubAccountSwitcher-CYEDydoq.css.gz → useSubAccountSwitcher-D8jXV7R5.css.gz} +0 -0
  106. package/payload/server/public/assets/useSubAccountSwitcher-UMg53kKF.js +14 -0
  107. package/payload/server/public/assets/useSubAccountSwitcher-UMg53kKF.js.br +0 -0
  108. package/payload/server/public/assets/useSubAccountSwitcher-UMg53kKF.js.gz +0 -0
  109. package/payload/server/public/assets/useVoiceRecorder-tzfqfXni.js +2 -0
  110. package/payload/server/public/assets/useVoiceRecorder-tzfqfXni.js.br +0 -0
  111. package/payload/server/public/assets/useVoiceRecorder-tzfqfXni.js.gz +0 -0
  112. package/payload/server/public/assets/{wrench-DvUj2R3p.js → wrench-DtfKfR4s.js} +1 -1
  113. package/payload/server/public/assets/wrench-DtfKfR4s.js.br +0 -0
  114. package/payload/server/public/assets/wrench-DtfKfR4s.js.gz +0 -0
  115. package/payload/server/public/browser.html +4 -4
  116. package/payload/server/public/calendar.html +7 -7
  117. package/payload/server/public/chat.html +13 -13
  118. package/payload/server/public/data.html +11 -11
  119. package/payload/server/public/graph.html +9 -9
  120. package/payload/server/public/index.html +14 -14
  121. package/payload/server/public/operator.html +14 -14
  122. package/payload/server/public/public.html +13 -13
  123. package/payload/server/public/routines.html +6 -6
  124. package/payload/server/public/skills.html +5 -5
  125. package/payload/server/public/tasks.html +6 -6
  126. package/payload/server/server.js +692 -439
  127. package/payload/server/public/assets/AdminLoginScreens-BpHVvUsH.js.br +0 -0
  128. package/payload/server/public/assets/AdminLoginScreens-BpHVvUsH.js.gz +0 -0
  129. package/payload/server/public/assets/AdminShell-BT7VQIu7.js +0 -2
  130. package/payload/server/public/assets/AdminShell-BT7VQIu7.js.br +0 -0
  131. package/payload/server/public/assets/AdminShell-BT7VQIu7.js.gz +0 -0
  132. package/payload/server/public/assets/activity-B3WEyiTh.js.br +0 -0
  133. package/payload/server/public/assets/activity-B3WEyiTh.js.gz +0 -0
  134. package/payload/server/public/assets/admin-BEKL9Bq_.js +0 -1
  135. package/payload/server/public/assets/admin-BEKL9Bq_.js.br +0 -0
  136. package/payload/server/public/assets/admin-BEKL9Bq_.js.gz +0 -0
  137. package/payload/server/public/assets/agents-CmYDQbdM.js.br +0 -0
  138. package/payload/server/public/assets/agents-CmYDQbdM.js.gz +0 -0
  139. package/payload/server/public/assets/browser-CP8TnIUm.js.br +0 -0
  140. package/payload/server/public/assets/browser-CP8TnIUm.js.gz +0 -0
  141. package/payload/server/public/assets/calendar-DM6Bl8Ra.js +0 -1
  142. package/payload/server/public/assets/calendar-DM6Bl8Ra.js.br +0 -0
  143. package/payload/server/public/assets/calendar-DM6Bl8Ra.js.gz +0 -0
  144. package/payload/server/public/assets/chat-BJWdNwCN.js +0 -1
  145. package/payload/server/public/assets/chat-BJWdNwCN.js.br +0 -2
  146. package/payload/server/public/assets/chat-BJWdNwCN.js.gz +0 -0
  147. package/payload/server/public/assets/chevron-left-DU8Es8Xq.js +0 -1
  148. package/payload/server/public/assets/chevron-right-D8HTOkyy.js +0 -1
  149. package/payload/server/public/assets/chevron-right-D8HTOkyy.js.br +0 -0
  150. package/payload/server/public/assets/clock-BdNuUyUU.js +0 -1
  151. package/payload/server/public/assets/clock-BdNuUyUU.js.br +0 -0
  152. package/payload/server/public/assets/clock-BdNuUyUU.js.gz +0 -0
  153. package/payload/server/public/assets/data-EjLIuy-W.js +0 -1
  154. package/payload/server/public/assets/data-EjLIuy-W.js.br +0 -0
  155. package/payload/server/public/assets/data-EjLIuy-W.js.gz +0 -0
  156. package/payload/server/public/assets/file-text-lsHlk4lR.js.br +0 -0
  157. package/payload/server/public/assets/file-text-lsHlk4lR.js.gz +0 -0
  158. package/payload/server/public/assets/graph-B0awsgVj.js.br +0 -0
  159. package/payload/server/public/assets/graph-B0awsgVj.js.gz +0 -0
  160. package/payload/server/public/assets/graph-labels-Ci5Jz1oY.js.br +0 -0
  161. package/payload/server/public/assets/graph-labels-Ci5Jz1oY.js.gz +0 -0
  162. package/payload/server/public/assets/maximize-2-DRndYysc.js.br +0 -0
  163. package/payload/server/public/assets/maximize-2-DRndYysc.js.gz +0 -0
  164. package/payload/server/public/assets/operator-tiI-kz6v.js +0 -1
  165. package/payload/server/public/assets/operator-tiI-kz6v.js.br +0 -0
  166. package/payload/server/public/assets/operator-tiI-kz6v.js.gz +0 -0
  167. package/payload/server/public/assets/page-RkwjxAgF.js.br +0 -0
  168. package/payload/server/public/assets/page-RkwjxAgF.js.gz +0 -0
  169. package/payload/server/public/assets/page-ZG7SXMO0.js +0 -1
  170. package/payload/server/public/assets/page-ZG7SXMO0.js.br +0 -0
  171. package/payload/server/public/assets/page-ZG7SXMO0.js.gz +0 -0
  172. package/payload/server/public/assets/public-BNAVdC2B.js.br +0 -0
  173. package/payload/server/public/assets/public-BNAVdC2B.js.gz +0 -0
  174. package/payload/server/public/assets/rotate-ccw-D6DZb7tR.js.br +0 -0
  175. package/payload/server/public/assets/rotate-ccw-D6DZb7tR.js.gz +0 -0
  176. package/payload/server/public/assets/routines-BVcPovu-.js +0 -2
  177. package/payload/server/public/assets/routines-BVcPovu-.js.br +0 -0
  178. package/payload/server/public/assets/routines-BVcPovu-.js.gz +0 -0
  179. package/payload/server/public/assets/skills-9S_nNIpO.js.br +0 -0
  180. package/payload/server/public/assets/skills-9S_nNIpO.js.gz +0 -0
  181. package/payload/server/public/assets/tasks-D-D2efaL.js +0 -3
  182. package/payload/server/public/assets/tasks-D-D2efaL.js.br +0 -0
  183. package/payload/server/public/assets/tasks-D-D2efaL.js.gz +0 -0
  184. package/payload/server/public/assets/time-entry-format-CKpHcWWs.js +0 -1
  185. package/payload/server/public/assets/time-entry-format-CKpHcWWs.js.br +0 -0
  186. package/payload/server/public/assets/time-entry-format-CKpHcWWs.js.gz +0 -0
  187. package/payload/server/public/assets/triangle-alert-BcpCZDHe.js.br +0 -0
  188. package/payload/server/public/assets/triangle-alert-BcpCZDHe.js.gz +0 -0
  189. package/payload/server/public/assets/useCopyFeedback-DqTnYnz5.js.br +0 -0
  190. package/payload/server/public/assets/useCopyFeedback-DqTnYnz5.js.gz +0 -0
  191. package/payload/server/public/assets/useSubAccountSwitcher-CYEDydoq.css.br +0 -0
  192. package/payload/server/public/assets/useSubAccountSwitcher-ny9tP8am.js +0 -14
  193. package/payload/server/public/assets/useSubAccountSwitcher-ny9tP8am.js.br +0 -0
  194. package/payload/server/public/assets/useSubAccountSwitcher-ny9tP8am.js.gz +0 -0
  195. package/payload/server/public/assets/useVoiceRecorder-DcgtZl-4.js +0 -2
  196. package/payload/server/public/assets/useVoiceRecorder-DcgtZl-4.js.br +0 -0
  197. package/payload/server/public/assets/useVoiceRecorder-DcgtZl-4.js.gz +0 -0
  198. package/payload/server/public/assets/wrench-DvUj2R3p.js.br +0 -2
  199. package/payload/server/public/assets/wrench-DvUj2R3p.js.gz +0 -0
@@ -2385,11 +2385,11 @@ function staticCacheHeaders(root) {
2385
2385
  }
2386
2386
 
2387
2387
  // server/lib/static-mounts.ts
2388
- function mountStaticFiles(app76, root) {
2389
- app76.use("/*", staticCacheHeaders(root));
2390
- app76.use("/assets/*", assetsVary);
2391
- app76.use("/assets/*", serveStatic({ root, precompressed: true }));
2392
- app76.use("/*", serveStatic({ root }));
2388
+ function mountStaticFiles(app77, root) {
2389
+ app77.use("/*", staticCacheHeaders(root));
2390
+ app77.use("/assets/*", assetsVary);
2391
+ app77.use("/assets/*", serveStatic({ root, precompressed: true }));
2392
+ app77.use("/*", serveStatic({ root }));
2393
2393
  }
2394
2394
  var assetsVary = async (c, next) => {
2395
2395
  await next();
@@ -3589,8 +3589,8 @@ function webchatTurnTimeoutMs() {
3589
3589
  return Number(process.env.WEBCHAT_TURN_TIMEOUT_MS ?? String(2 * 6e4));
3590
3590
  }
3591
3591
  function createChatRoutes(deps) {
3592
- const app76 = new Hono();
3593
- app76.post("/", async (c) => {
3592
+ const app77 = new Hono();
3593
+ app77.post("/", async (c) => {
3594
3594
  console.log(`[chat-route] entered route=public method=POST`);
3595
3595
  const contentType = c.req.header("content-type") ?? "";
3596
3596
  const account = resolveAccount();
@@ -3862,7 +3862,7 @@ ${result.result.text}` : result.result.text;
3862
3862
  }
3863
3863
  });
3864
3864
  });
3865
- return app76;
3865
+ return app77;
3866
3866
  }
3867
3867
 
3868
3868
  // server/routes/channel/schedule-inject.ts
@@ -4806,8 +4806,8 @@ function transcriptBoundary(sessionKey) {
4806
4806
  }
4807
4807
  }
4808
4808
  function createScheduleInjectRoutes(deps) {
4809
- const app76 = new Hono();
4810
- app76.post("/", async (c) => {
4809
+ const app77 = new Hono();
4810
+ app77.post("/", async (c) => {
4811
4811
  const env = c.env;
4812
4812
  const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
4813
4813
  if (!isLoopbackAddr(remoteAddr)) {
@@ -4918,7 +4918,7 @@ function createScheduleInjectRoutes(deps) {
4918
4918
  console.error(`${TAG5} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
4919
4919
  return c.json({ ok: true, sessionKey: sessionId, ...transcriptBoundary(sessionId) }, 200);
4920
4920
  });
4921
- return app76;
4921
+ return app77;
4922
4922
  }
4923
4923
 
4924
4924
  // server/index.ts
@@ -5844,7 +5844,7 @@ function setDispatchInbound(accountDir, accountId, value, ctx) {
5844
5844
  const id = accountId.trim();
5845
5845
  if (!id) return { ok: false, reason: "missing-account-id", error: "Missing account id." };
5846
5846
  const verdict = resolveKey(id, ctx);
5847
- const failure = policyKeyFailure(verdict, id, { refuseHouse: false });
5847
+ const failure = policyKeyFailure(verdict, id, null);
5848
5848
  if (failure) {
5849
5849
  console.error(`${TAG10} op=set-dispatch-inbound account=${id} ok=false reason=${failure.reason}`);
5850
5850
  return failure;
@@ -5880,16 +5880,9 @@ function setDispatchInbound(accountDir, accountId, value, ctx) {
5880
5880
  return { ok: false, reason: "write-failed", error: msg };
5881
5881
  }
5882
5882
  }
5883
- function policyKeyFailure(verdict, accountId, opts) {
5883
+ function policyKeyFailure(verdict, accountId, houseFailure) {
5884
5884
  if (verdict.kind === "self") return null;
5885
- if (verdict.kind === "house") {
5886
- if (!opts.refuseHouse) return null;
5887
- return {
5888
- ok: false,
5889
- reason: "house-policy-is-top-level",
5890
- error: `Account "${accountId}" is the house. Its dmPolicy, adminPhones and allowFrom are the TOP-LEVEL WhatsApp config, not a per-account override \u2014 set them with the "update-config" action (adminPhones also has add-admin-phone / remove-admin-phone). Writing a per-account slice for the house would create a second home for the same three fields.`
5891
- };
5892
- }
5885
+ if (verdict.kind === "house") return houseFailure;
5893
5886
  if (verdict.kind === "ambiguous") {
5894
5887
  return {
5895
5888
  ok: false,
@@ -5903,6 +5896,13 @@ function policyKeyFailure(verdict, accountId, opts) {
5903
5896
  error: `Account "${accountId}" has no WhatsApp credential directory, so it has no config slice the inbound gate would read. Pair the account first.`
5904
5897
  };
5905
5898
  }
5899
+ function policyHouseFailure(accountId) {
5900
+ return {
5901
+ ok: false,
5902
+ reason: "house-policy-is-top-level",
5903
+ error: `Account "${accountId}" is the house. Its dmPolicy, adminPhones and allowFrom are the TOP-LEVEL WhatsApp config, not a per-account override \u2014 set them with the "update-config" action (adminPhones also has add-admin-phone / remove-admin-phone). Writing a per-account slice for the house would create a second home for the same three fields.`
5904
+ };
5905
+ }
5906
5906
  function resolveKey(accountId, ctx) {
5907
5907
  return resolveAccountConfigKey({
5908
5908
  targetAccountId: accountId,
@@ -5925,7 +5925,7 @@ function setAccountPolicy(accountDir, accountId, policy, ctx) {
5925
5925
  };
5926
5926
  }
5927
5927
  const verdict = resolveKey(id, ctx);
5928
- const failure = policyKeyFailure(verdict, id, { refuseHouse: true });
5928
+ const failure = policyKeyFailure(verdict, id, policyHouseFailure(id));
5929
5929
  if (failure) {
5930
5930
  console.error(`${TAG10} op=set-account-policy account=${id} ok=false reason=${failure.reason}`);
5931
5931
  return failure;
@@ -5963,7 +5963,7 @@ function readAccountPolicy(accountDir, accountId, ctx) {
5963
5963
  const id = accountId.trim();
5964
5964
  if (!id) return { ok: false, reason: "missing-account-id", error: "Missing account id." };
5965
5965
  const verdict = resolveKey(id, ctx);
5966
- const failure = policyKeyFailure(verdict, id, { refuseHouse: false });
5966
+ const failure = policyKeyFailure(verdict, id, null);
5967
5967
  if (failure) return failure;
5968
5968
  const key = verdict.key;
5969
5969
  try {
@@ -6035,14 +6035,43 @@ function readAccountManagers(accountDir) {
6035
6035
  return {};
6036
6036
  }
6037
6037
  }
6038
- function setPublicAgent(accountDir, slug) {
6038
+ function publicAgentHouseFailure(accountId) {
6039
+ return {
6040
+ ok: false,
6041
+ reason: "house-public-agent-is-top-level",
6042
+ error: `Account "${accountId}" is the house. Its publicAgent and publicAgentEnabled are the TOP-LEVEL WhatsApp config \u2014 set them by calling this action without an accountId. Writing a per-account slice for the house would create a second home for the same field.`
6043
+ };
6044
+ }
6045
+ function publicAgentKey(target) {
6046
+ if (target.scope === "top-level") return { key: null };
6047
+ const id = target.accountId.trim();
6048
+ if (!id) return { ok: false, reason: "missing-account-id", error: "Missing account id." };
6049
+ const verdict = resolveKey(id, target.ctx);
6050
+ const failure = policyKeyFailure(verdict, id, publicAgentHouseFailure(id));
6051
+ if (failure) return failure;
6052
+ return { key: verdict.key };
6053
+ }
6054
+ function publicAgentScopeLabel(target) {
6055
+ return target.scope === "top-level" ? "the top-level default" : `account ${target.accountId}`;
6056
+ }
6057
+ function setPublicAgent(accountDir, slug, target) {
6039
6058
  const trimmed = slug.trim();
6040
6059
  if (!trimmed) {
6041
- return { ok: false, error: "Agent slug cannot be empty." };
6060
+ return { ok: false, reason: "missing-slug", error: "Agent slug cannot be empty." };
6042
6061
  }
6043
- const agentConfigPath = join12(accountDir, "agents", trimmed, "config.json");
6062
+ const resolvedKey = publicAgentKey(target);
6063
+ if ("ok" in resolvedKey) {
6064
+ console.error(`${TAG10} op=set-public-agent accountId=${publicAgentScopeLabel(target)} ok=false reason=${resolvedKey.reason}`);
6065
+ return resolvedKey;
6066
+ }
6067
+ const key = resolvedKey.key;
6068
+ const agentConfigPath = join12(target.agentsDir, "agents", trimmed, "config.json");
6044
6069
  if (!existsSync6(agentConfigPath)) {
6045
- return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
6070
+ return {
6071
+ ok: false,
6072
+ reason: "agent-not-found",
6073
+ error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.`
6074
+ };
6046
6075
  }
6047
6076
  try {
6048
6077
  const config = readConfig(accountDir);
@@ -6050,46 +6079,58 @@ function setPublicAgent(accountDir, slug) {
6050
6079
  config.whatsapp = {};
6051
6080
  }
6052
6081
  const wa = config.whatsapp;
6053
- wa.publicAgent = trimmed;
6082
+ if (key) {
6083
+ if (!wa.accounts || typeof wa.accounts !== "object") wa.accounts = {};
6084
+ const accounts = wa.accounts;
6085
+ if (!accounts[key] || typeof accounts[key] !== "object") accounts[key] = {};
6086
+ accounts[key].publicAgent = trimmed;
6087
+ } else {
6088
+ wa.publicAgent = trimmed;
6089
+ }
6054
6090
  const parsed = WhatsAppConfigSchema.safeParse(wa);
6055
6091
  if (!parsed.success) {
6056
6092
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
6057
- return { ok: false, error: `Validation failed: ${msg}` };
6093
+ return { ok: false, reason: "validation-failed", error: `Validation failed: ${msg}` };
6058
6094
  }
6059
6095
  config.whatsapp = parsed.data;
6060
6096
  writeConfig(accountDir, config);
6061
- console.error(`${TAG10} publicAgent set to ${trimmed}`);
6097
+ console.error(
6098
+ `${TAG10} op=set-public-agent accountId=${target.scope === "top-level" ? "top-level" : target.accountId} key=${key ?? "top-level"} slug=${trimmed} ok=true`
6099
+ );
6062
6100
  reloadManagerConfig(accountDir);
6063
- return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
6101
+ return {
6102
+ ok: true,
6103
+ key,
6104
+ message: `Public agent for ${publicAgentScopeLabel(target)} set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.`
6105
+ };
6064
6106
  } catch (err) {
6065
6107
  const msg = err instanceof Error ? err.message : String(err);
6066
- console.error(`${TAG10} setPublicAgent failed: ${msg}`);
6067
- return { ok: false, error: msg };
6108
+ console.error(`${TAG10} op=set-public-agent ok=false reason=write-failed detail=${msg}`);
6109
+ return { ok: false, reason: "write-failed", error: msg };
6068
6110
  }
6069
6111
  }
6070
- function setPublicAgentEnabled(accountDir, enabled, opts) {
6071
- const accountId = opts?.accountId;
6072
- const scope = accountId ? `account ${accountId}` : "the top-level default";
6112
+ function setPublicAgentEnabled(accountDir, enabled, target) {
6113
+ const scope = publicAgentScopeLabel(target);
6114
+ const resolvedKey = publicAgentKey(target);
6115
+ if ("ok" in resolvedKey) {
6116
+ console.error(`${TAG10} op=set-public-agent-enabled accountId=${scope} ok=false reason=${resolvedKey.reason}`);
6117
+ return resolvedKey;
6118
+ }
6119
+ const key = resolvedKey.key;
6073
6120
  if (enabled) {
6074
- const resolved = accountId ? resolvePublicAgent(accountDir, { accountId }) : (() => {
6075
- try {
6076
- const wa = readConfig(accountDir).whatsapp;
6077
- const top = wa?.publicAgent;
6078
- return typeof top === "string" && top.trim() ? { slug: top.trim(), source: "top-level" } : null;
6079
- } catch {
6080
- return null;
6081
- }
6082
- })();
6121
+ const resolved = key ? resolvePublicAgent(accountDir, { accountId: key, isHouse: false }) : resolvePublicAgent(accountDir, { accountId: "", isHouse: true });
6083
6122
  if (!resolved) {
6084
6123
  return {
6085
6124
  ok: false,
6125
+ reason: "no-public-agent",
6086
6126
  error: `Cannot enable public answering for ${scope}: no public agent is configured. Set one with set-public-agent first.`
6087
6127
  };
6088
6128
  }
6089
- const agentConfigPath = join12(accountDir, "agents", resolved.slug, "config.json");
6129
+ const agentConfigPath = join12(target.agentsDir, "agents", resolved.slug, "config.json");
6090
6130
  if (!existsSync6(agentConfigPath)) {
6091
6131
  return {
6092
6132
  ok: false,
6133
+ reason: "agent-not-found",
6093
6134
  error: `Cannot enable public answering for ${scope}: the configured agent "${resolved.slug}" has no config.json at ${agentConfigPath}. Point publicAgent at an existing agent first.`
6094
6135
  };
6095
6136
  }
@@ -6100,33 +6141,34 @@ function setPublicAgentEnabled(accountDir, enabled, opts) {
6100
6141
  config.whatsapp = {};
6101
6142
  }
6102
6143
  const wa = config.whatsapp;
6103
- if (accountId) {
6144
+ if (key) {
6104
6145
  if (!wa.accounts || typeof wa.accounts !== "object") wa.accounts = {};
6105
6146
  const accounts = wa.accounts;
6106
- if (!accounts[accountId] || typeof accounts[accountId] !== "object") {
6107
- accounts[accountId] = {};
6108
- }
6109
- accounts[accountId].publicAgentEnabled = enabled;
6147
+ if (!accounts[key] || typeof accounts[key] !== "object") accounts[key] = {};
6148
+ accounts[key].publicAgentEnabled = enabled;
6110
6149
  } else {
6111
6150
  wa.publicAgentEnabled = enabled;
6112
6151
  }
6113
6152
  const parsed = WhatsAppConfigSchema.safeParse(wa);
6114
6153
  if (!parsed.success) {
6115
6154
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
6116
- return { ok: false, error: `Validation failed: ${msg}` };
6155
+ return { ok: false, reason: "validation-failed", error: `Validation failed: ${msg}` };
6117
6156
  }
6118
6157
  config.whatsapp = parsed.data;
6119
6158
  writeConfig(accountDir, config);
6120
- console.error(`${TAG10} publicAgentEnabled set to ${enabled} for ${scope}`);
6159
+ console.error(
6160
+ `${TAG10} op=set-public-agent-enabled accountId=${target.scope === "top-level" ? "top-level" : target.accountId} key=${key ?? "top-level"} enabled=${enabled} ok=true`
6161
+ );
6121
6162
  reloadManagerConfig(accountDir);
6122
6163
  return {
6123
6164
  ok: true,
6165
+ key,
6124
6166
  message: enabled ? `Public answering enabled for ${scope}. Non-admin WhatsApp senders will be answered by the configured public agent.` : `Public answering disabled for ${scope}. Non-admin WhatsApp senders receive no reply; account managers are still notified.`
6125
6167
  };
6126
6168
  } catch (err) {
6127
6169
  const msg = err instanceof Error ? err.message : String(err);
6128
- console.error(`${TAG10} setPublicAgentEnabled failed: ${msg}`);
6129
- return { ok: false, error: msg };
6170
+ console.error(`${TAG10} op=set-public-agent-enabled ok=false reason=write-failed detail=${msg}`);
6171
+ return { ok: false, reason: "write-failed", error: msg };
6130
6172
  }
6131
6173
  }
6132
6174
  function resolvePublicAgent(accountDir, opts) {
@@ -6140,6 +6182,7 @@ function resolvePublicAgent(accountDir, opts) {
6140
6182
  if (typeof accountSlug === "string" && accountSlug.trim()) {
6141
6183
  return { slug: accountSlug.trim(), source: "account" };
6142
6184
  }
6185
+ if (!opts.isHouse) return null;
6143
6186
  const topSlug = wa.publicAgent;
6144
6187
  if (typeof topSlug === "string" && topSlug.trim()) {
6145
6188
  return { slug: topSlug.trim(), source: "top-level" };
@@ -6159,6 +6202,7 @@ function resolvePublicAgentEnabled(accountDir, opts) {
6159
6202
  if (typeof account?.publicAgentEnabled === "boolean") {
6160
6203
  return { enabled: account.publicAgentEnabled, source: "account" };
6161
6204
  }
6205
+ if (!opts.isHouse) return { enabled: false, source: "default" };
6162
6206
  if (typeof wa.publicAgentEnabled === "boolean") {
6163
6207
  return { enabled: wa.publicAgentEnabled, source: "top-level" };
6164
6208
  }
@@ -7440,6 +7484,65 @@ app3.post("/config", async (c) => {
7440
7484
  console.error(`${TAG15} config action=set-account-policy account=${target} ok=${result.ok}${result.ok ? "" : ` reason=${result.reason}`}`);
7441
7485
  return c.json(result, result.ok ? 200 : 400);
7442
7486
  }
7487
+ if (action === "set-public-agent" || action === "get-public-agent" || action === "set-public-agent-enabled" || action === "list-public-agents") {
7488
+ const acct = resolveAccount();
7489
+ if (!acct) return c.json({ ok: false, error: "No account configured." }, 500);
7490
+ const resolved = resolveLifecycleAccountId(c, accountId);
7491
+ const topLevel = resolved.targetSource === "house-default";
7492
+ if (topLevel) {
7493
+ const denial2 = houseSocketScopeDenial(c, `config action=${action}`);
7494
+ if (denial2) return denial2;
7495
+ } else {
7496
+ const denial2 = selfOrHouseScopeDenial(c, resolved.accountId, `config action=${action}`, resolved.targetSource);
7497
+ if (denial2) return denial2;
7498
+ }
7499
+ const targetDir = topLevel ? acct.accountDir : listValidAccounts().find((a) => a.accountId === resolved.accountId)?.accountDir;
7500
+ if (!targetDir) {
7501
+ return c.json({ ok: false, error: `Account "${resolved.accountId}" is not provisioned on this install.` }, 400);
7502
+ }
7503
+ const writeTarget = topLevel ? { scope: "top-level", agentsDir: targetDir } : { scope: "account", accountId: resolved.accountId, agentsDir: targetDir, ctx: accountPolicyContext() };
7504
+ if (action === "set-public-agent") {
7505
+ if (!slug || typeof slug !== "string") {
7506
+ return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
7507
+ }
7508
+ const result = setPublicAgent(acct.accountDir, slug, writeTarget);
7509
+ return c.json(result, result.ok ? 200 : 400);
7510
+ }
7511
+ if (action === "set-public-agent-enabled") {
7512
+ if (typeof enabled !== "boolean") {
7513
+ return c.json({ ok: false, error: 'Missing required field "enabled" (true or false).' }, 400);
7514
+ }
7515
+ const result = setPublicAgentEnabled(acct.accountDir, enabled, writeTarget);
7516
+ return c.json(result, result.ok ? 200 : 400);
7517
+ }
7518
+ if (action === "get-public-agent") {
7519
+ const readKey2 = topLevel ? "default" : resolved.accountId;
7520
+ const resolvedAgent = resolvePublicAgent(acct.accountDir, { accountId: readKey2, isHouse: topLevel });
7521
+ console.error(`${TAG15} config action=get-public-agent accountId=${topLevel ? "top-level" : resolved.accountId} slug=${resolvedAgent?.slug ?? "none"} source=${resolvedAgent?.source ?? "none"}`);
7522
+ return c.json({ ok: true, slug: resolvedAgent?.slug ?? null, source: resolvedAgent?.source ?? null });
7523
+ }
7524
+ const agentsDir = resolve7(targetDir, "agents");
7525
+ const agents = [];
7526
+ if (existsSync8(agentsDir)) {
7527
+ try {
7528
+ for (const entry of readdirSync7(agentsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
7529
+ if (!entry.isDirectory() || entry.name === "admin") continue;
7530
+ const configPath3 = resolve7(agentsDir, entry.name, "config.json");
7531
+ if (!existsSync8(configPath3)) continue;
7532
+ try {
7533
+ const parsed = JSON.parse(readFileSync10(configPath3, "utf-8"));
7534
+ agents.push({ slug: entry.name, displayName: parsed.displayName ?? entry.name });
7535
+ } catch {
7536
+ console.error(`${TAG15} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
7537
+ }
7538
+ }
7539
+ } catch (err) {
7540
+ console.error(`${TAG15} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
7541
+ }
7542
+ }
7543
+ console.error(`${TAG15} config action=list-public-agents accountId=${topLevel ? "top-level" : resolved.accountId} count=${agents.length}`);
7544
+ return c.json({ ok: true, agents });
7545
+ }
7443
7546
  if (action === "add-account-manager" || action === "remove-account-manager" || action === "list-account-managers") {
7444
7547
  const acct = resolveAccount();
7445
7548
  if (!acct) return c.json({ ok: false, error: "No account configured." }, 500);
@@ -7551,57 +7654,10 @@ app3.post("/config", async (c) => {
7551
7654
  // Task 2233 — `add-account-manager`, `remove-account-manager` and
7552
7655
  // `list-account-managers` are handled above this switch, on the
7553
7656
  // per-binding gate. They remain valid actions; only their gate moved.
7554
- case "set-public-agent": {
7555
- if (!slug || typeof slug !== "string") {
7556
- return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
7557
- }
7558
- const result = setPublicAgent(account.accountDir, slug);
7559
- console.error(`${TAG15} config action=set-public-agent slug=${slug} ok=${result.ok}`);
7560
- return c.json(result, result.ok ? 200 : 400);
7561
- }
7562
- case "get-public-agent": {
7563
- const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
7564
- const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount });
7565
- console.error(`${TAG15} config action=get-public-agent accountId=${targetAccount} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
7566
- return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
7567
- }
7568
- // Task 2262 — the switch that turns public answering on and off. No
7569
- // matching getter: `get-config` already returns the whole WhatsApp config,
7570
- // so the field is readable the moment it exists. An absent/blank accountId
7571
- // writes the install-wide default; a present one writes that account only.
7572
- case "set-public-agent-enabled": {
7573
- if (typeof enabled !== "boolean") {
7574
- return c.json({ ok: false, error: 'Missing required field "enabled" (true or false).' }, 400);
7575
- }
7576
- const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : void 0;
7577
- const result = setPublicAgentEnabled(account.accountDir, enabled, targetAccount ? { accountId: targetAccount } : void 0);
7578
- console.error(`${TAG15} config action=set-public-agent-enabled accountId=${targetAccount ?? "top-level"} enabled=${enabled} ok=${result.ok}`);
7579
- return c.json(result, result.ok ? 200 : 400);
7580
- }
7581
- case "list-public-agents": {
7582
- const agentsDir = resolve7(account.accountDir, "agents");
7583
- const agents = [];
7584
- if (existsSync8(agentsDir)) {
7585
- try {
7586
- const entries2 = readdirSync7(agentsDir, { withFileTypes: true });
7587
- for (const entry of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
7588
- if (!entry.isDirectory() || entry.name === "admin") continue;
7589
- const configPath3 = resolve7(agentsDir, entry.name, "config.json");
7590
- if (!existsSync8(configPath3)) continue;
7591
- try {
7592
- const config = JSON.parse(readFileSync10(configPath3, "utf-8"));
7593
- agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
7594
- } catch {
7595
- console.error(`${TAG15} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
7596
- }
7597
- }
7598
- } catch (err) {
7599
- console.error(`${TAG15} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
7600
- }
7601
- }
7602
- console.error(`${TAG15} config action=list-public-agents count=${agents.length}`);
7603
- return c.json({ ok: true, agents });
7604
- }
7657
+ // Task 2271 — `set-public-agent`, `get-public-agent`,
7658
+ // `set-public-agent-enabled` and `list-public-agents` are handled above
7659
+ // this switch, on the per-account scope gate. They remain valid actions;
7660
+ // only their gate and their target moved.
7605
7661
  case "schema": {
7606
7662
  const text = serializeWhatsAppSchema();
7607
7663
  console.error(`${TAG15} config action=schema`);
@@ -12342,8 +12398,8 @@ async function reapplyLeversViaManager() {
12342
12398
  var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
12343
12399
  var sendSeq = 0;
12344
12400
  function createWebchatRoutes(deps) {
12345
- const app76 = new Hono();
12346
- app76.post("/send", requireAdminSession, async (c) => {
12401
+ const app77 = new Hono();
12402
+ app77.post("/send", requireAdminSession, async (c) => {
12347
12403
  let accountId;
12348
12404
  try {
12349
12405
  accountId = resolvePlatformAccountId();
@@ -12541,7 +12597,7 @@ ${note}` : note;
12541
12597
  if (turnTimer.unref) turnTimer.unref();
12542
12598
  return c.json({ ok: true });
12543
12599
  });
12544
- app76.post("/interrupt", requireAdminSession, async (c) => {
12600
+ app77.post("/interrupt", requireAdminSession, async (c) => {
12545
12601
  const cacheKey = c.get("cacheKey");
12546
12602
  const requesterUserId = (cacheKey ? getUserIdForSession(cacheKey) : void 0) ?? null;
12547
12603
  const primaryUserId = resolvePrimaryAdminUserId();
@@ -12569,7 +12625,7 @@ ${note}` : note;
12569
12625
  }
12570
12626
  return c.json({ ok: true, stopped: outcome.stopped, intId: outcome.intId, deadChild: outcome.deadChild }, 200);
12571
12627
  });
12572
- app76.post("/settings", requireAdminSession, async (c) => {
12628
+ app77.post("/settings", requireAdminSession, async (c) => {
12573
12629
  const body = await c.req.json().catch(() => null);
12574
12630
  const op = body?.op;
12575
12631
  const value = body?.value;
@@ -12605,7 +12661,7 @@ ${note}` : note;
12605
12661
  console.log(`[webchat:settings] op=${op} outcome=accepted accountId=${account.accountId.slice(0, 8)} value=${value} settingsApplied=${settingsApplied}`);
12606
12662
  return c.json({ ok: true, settingsApplied });
12607
12663
  });
12608
- app76.get("/session", requireAdminSession, async (c) => {
12664
+ app77.get("/session", requireAdminSession, async (c) => {
12609
12665
  let accountId;
12610
12666
  try {
12611
12667
  accountId = resolvePlatformAccountId();
@@ -12678,7 +12734,7 @@ ${note}` : note;
12678
12734
  const indicators = await fetchComposerIndicators(sessionId);
12679
12735
  return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
12680
12736
  });
12681
- app76.post("/permission-verdict", requireAdminSession, async (c) => {
12737
+ app77.post("/permission-verdict", requireAdminSession, async (c) => {
12682
12738
  const body = await c.req.json().catch(() => null);
12683
12739
  const sessionId = body?.sessionId;
12684
12740
  const requestId = body?.request_id;
@@ -12695,7 +12751,7 @@ ${note}` : note;
12695
12751
  const ok = deps.resolvePermissionVerdict?.(`session:${sessionId}`, requestId, behavior) ?? false;
12696
12752
  return c.json({ ok });
12697
12753
  });
12698
- return app76;
12754
+ return app77;
12699
12755
  }
12700
12756
 
12701
12757
  // server/routes/webchat-greeting.ts
@@ -16826,18 +16882,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
16826
16882
  ]);
16827
16883
  var app24 = new Hono();
16828
16884
  app24.post("/", async (c) => {
16829
- const TAG51 = "[admin:events]";
16885
+ const TAG52 = "[admin:events]";
16830
16886
  let body;
16831
16887
  try {
16832
16888
  body = await c.req.json();
16833
16889
  } catch (err) {
16834
16890
  const detail = err instanceof Error ? err.message : String(err);
16835
- console.error(`${TAG51} reject reason=body-not-json detail=${detail}`);
16891
+ console.error(`${TAG52} reject reason=body-not-json detail=${detail}`);
16836
16892
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
16837
16893
  }
16838
16894
  const event = typeof body.event === "string" ? body.event : "";
16839
16895
  if (!ALLOWED_EVENTS.has(event)) {
16840
- console.error(`${TAG51} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
16896
+ console.error(`${TAG52} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
16841
16897
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
16842
16898
  }
16843
16899
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -22283,7 +22339,8 @@ async function managerSpawn(opts) {
22283
22339
  previousContext: opts.previousContext,
22284
22340
  initialMessage: opts.initialMessage,
22285
22341
  passive: opts.passive,
22286
- targetAccountId: opts.targetAccountId
22342
+ targetAccountId: opts.targetAccountId,
22343
+ agentAccountId: opts.agentAccountId
22287
22344
  })
22288
22345
  }).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
22289
22346
  if ("__throw" in res) {
@@ -24788,6 +24845,32 @@ async function computeTaskList(accountId) {
24788
24845
  `${TAG29} op=session-orphans account=${accountId} orphans=${orphans} detail="task marked for a session spawn that never linked; the spawned session is unreachable from its row"`
24789
24846
  );
24790
24847
  }
24848
+ let orphanEntries = 0;
24849
+ let orphanAdjustments = 0;
24850
+ try {
24851
+ const orphanTime = await session.run(
24852
+ `OPTIONAL MATCH (te:TimeEntry {accountId: $accountId})
24853
+ WHERE NOT EXISTS { MATCH (:Task {taskId: te.taskId, accountId: $accountId}) }
24854
+ WITH count(te) AS orphanEntries
24855
+ OPTIONAL MATCH (ta:TimeAdjustment {accountId: $accountId})
24856
+ WHERE NOT EXISTS { MATCH (:Task {taskId: ta.taskId, accountId: $accountId}) }
24857
+ RETURN orphanEntries, count(ta) AS orphanAdjustments`,
24858
+ { accountId }
24859
+ );
24860
+ orphanEntries = orphanTime.records.length > 0 ? toNum(orphanTime.records[0].get("orphanEntries")) : 0;
24861
+ orphanAdjustments = orphanTime.records.length > 0 ? toNum(orphanTime.records[0].get("orphanAdjustments")) : 0;
24862
+ } catch (err) {
24863
+ console.error(
24864
+ `${TAG29} op=orphan-time-records-census-failed account=${accountId} error="${err instanceof Error ? err.message : String(err)}"`
24865
+ );
24866
+ orphanEntries = -1;
24867
+ orphanAdjustments = -1;
24868
+ }
24869
+ if (orphanEntries > 0 || orphanAdjustments > 0) {
24870
+ console.error(
24871
+ `${TAG29} op=orphan-time-records account=${accountId} orphanEntries=${orphanEntries} orphanAdjustments=${orphanAdjustments} detail="time records whose task no longer exists; something removed the task without its records"`
24872
+ );
24873
+ }
24791
24874
  const linkedIds = [...open3, ...completed].map((r) => r.sessionId).filter((s) => s !== null);
24792
24875
  let dangling = null;
24793
24876
  const cfg = claudeConfigDir();
@@ -25100,6 +25183,112 @@ app57.post("/", requireAdminSession, async (c) => {
25100
25183
  });
25101
25184
  var task_time_adjust_default = app57;
25102
25185
 
25186
+ // server/routes/admin/task-delete.ts
25187
+ import { randomUUID as randomUUID13 } from "crypto";
25188
+ var TAG34 = "[task-delete]";
25189
+ var app58 = new Hono();
25190
+ app58.post("/", requireAdminSession, async (c) => {
25191
+ const cacheKey = c.var.cacheKey;
25192
+ const accountId = getAccountIdForSession(cacheKey);
25193
+ if (!accountId) return c.json({ error: "Account not found for session" }, 401);
25194
+ let body;
25195
+ try {
25196
+ body = await c.req.json();
25197
+ } catch {
25198
+ console.error(`${TAG34} op=reject reason=bad-json`);
25199
+ return c.json({ error: "invalid-json" }, 400);
25200
+ }
25201
+ const deletionId = randomUUID13();
25202
+ const taskId = typeof body.taskId === "string" ? body.taskId : "";
25203
+ if (!taskId) {
25204
+ const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
25205
+ console.error(`${TAG34} op=reject reason=${reason} deletionId=${deletionId} accountId=${accountId}`);
25206
+ return c.json({ error: "taskId required" }, 400);
25207
+ }
25208
+ console.log(
25209
+ `${TAG34} op=request deletionId=${deletionId} taskId=${taskId} accountId=${accountId} confirmed=${body.confirmed === true}`
25210
+ );
25211
+ const session = getSession();
25212
+ try {
25213
+ const guard = await session.run(
25214
+ `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
25215
+ OPTIONAL MATCH (te:TimeEntry {taskId: $taskId, accountId: $accountId})
25216
+ OPTIONAL MATCH (open:TimeEntry {taskId: $taskId, accountId: $accountId})
25217
+ WHERE open.endedAt IS NULL
25218
+ OPTIONAL MATCH (ta:TimeAdjustment {taskId: $taskId, accountId: $accountId})
25219
+ RETURN count(DISTINCT te) AS timeEntries,
25220
+ count(DISTINCT open) AS openCount,
25221
+ count(DISTINCT ta) AS adjustments`,
25222
+ { taskId, accountId }
25223
+ );
25224
+ if (guard.records.length === 0) {
25225
+ console.error(`${TAG34} op=reject reason=no-task deletionId=${deletionId} taskId=${taskId} accountId=${accountId}`);
25226
+ return c.json({ error: "task-not-found" }, 404);
25227
+ }
25228
+ const timeEntries = toNum(guard.records[0].get("timeEntries"));
25229
+ const openCount = toNum(guard.records[0].get("openCount"));
25230
+ const adjustments = toNum(guard.records[0].get("adjustments"));
25231
+ console.log(
25232
+ `${TAG34} op=guard deletionId=${deletionId} taskId=${taskId} openCount=${openCount} timeEntries=${timeEntries} adjustments=${adjustments}`
25233
+ );
25234
+ if (openCount > 0) {
25235
+ console.error(
25236
+ `${TAG34} op=reject reason=running deletionId=${deletionId} taskId=${taskId} openCount=${openCount}`
25237
+ );
25238
+ return c.json({ error: "task-timer-running" }, 409);
25239
+ }
25240
+ const write = await session.run(
25241
+ `OPTIONAL MATCH (te:TimeEntry {taskId: $taskId, accountId: $accountId})
25242
+ WITH collect(te) AS entries
25243
+ OPTIONAL MATCH (ta:TimeAdjustment {taskId: $taskId, accountId: $accountId})
25244
+ WITH entries, collect(ta) AS adjustments
25245
+ OPTIONAL MATCH (t:Task {taskId: $taskId, accountId: $accountId})
25246
+ WITH entries, adjustments, collect(t) AS tasks
25247
+ FOREACH (n IN entries | DETACH DELETE n)
25248
+ FOREACH (n IN adjustments | DETACH DELETE n)
25249
+ FOREACH (n IN tasks | DETACH DELETE n)
25250
+ RETURN size(tasks) AS tasksDeleted,
25251
+ size(entries) AS timeEntriesDeleted,
25252
+ size(adjustments) AS adjustmentsDeleted`,
25253
+ { taskId, accountId }
25254
+ );
25255
+ const tasksDeleted = toNum(write.records[0]?.get("tasksDeleted"));
25256
+ const timeEntriesDeleted = toNum(write.records[0]?.get("timeEntriesDeleted"));
25257
+ const adjustmentsDeleted = toNum(write.records[0]?.get("adjustmentsDeleted"));
25258
+ const after = await session.run(
25259
+ `OPTIONAL MATCH (t:Task {taskId: $taskId, accountId: $accountId})
25260
+ WITH count(t) AS taskLeft
25261
+ OPTIONAL MATCH (te:TimeEntry {taskId: $taskId, accountId: $accountId})
25262
+ WITH taskLeft, count(te) AS entriesLeft
25263
+ OPTIONAL MATCH (ta:TimeAdjustment {taskId: $taskId, accountId: $accountId})
25264
+ RETURN taskLeft, entriesLeft, count(ta) AS adjustmentsLeft`,
25265
+ { taskId, accountId }
25266
+ );
25267
+ const taskLeft = toNum(after.records[0]?.get("taskLeft"));
25268
+ const entriesLeft = toNum(after.records[0]?.get("entriesLeft"));
25269
+ const adjustmentsLeft = toNum(after.records[0]?.get("adjustmentsLeft"));
25270
+ const remaining = taskLeft + entriesLeft + adjustmentsLeft;
25271
+ console.log(
25272
+ `${TAG34} op=persisted deletionId=${deletionId} taskId=${taskId} tasksDeleted=${tasksDeleted} timeEntriesDeleted=${timeEntriesDeleted} adjustmentsDeleted=${adjustmentsDeleted} remaining=${remaining}`
25273
+ );
25274
+ if (remaining > 0) {
25275
+ console.error(
25276
+ `${TAG34} op=incomplete deletionId=${deletionId} taskId=${taskId} taskLeft=${taskLeft} entriesLeft=${entriesLeft} adjustmentsLeft=${adjustmentsLeft} detail="the delete returned but rows for this taskId survive"`
25277
+ );
25278
+ }
25279
+ invalidate("tasks-list", accountId);
25280
+ return c.json({ ok: true, tasksDeleted, timeEntriesDeleted, adjustmentsDeleted, remaining });
25281
+ } catch (err) {
25282
+ console.error(
25283
+ `${TAG34} op=error deletionId=${deletionId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`
25284
+ );
25285
+ return c.json({ error: "task-delete-failed" }, 500);
25286
+ } finally {
25287
+ await session.close();
25288
+ }
25289
+ });
25290
+ var task_delete_default = app58;
25291
+
25103
25292
  // app/lib/admin-roster.ts
25104
25293
  import { existsSync as existsSync42, readdirSync as readdirSync26, readFileSync as readFileSync40 } from "fs";
25105
25294
  import { resolve as resolve33 } from "path";
@@ -25185,9 +25374,9 @@ async function resolveAssignableAdmins(accountId, accountsDir = ACCOUNTS_DIR) {
25185
25374
  }
25186
25375
 
25187
25376
  // server/routes/admin/task-assign.ts
25188
- var TAG34 = "[task-assign]";
25189
- var app58 = new Hono();
25190
- app58.post("/", requireAdminSession, async (c) => {
25377
+ var TAG35 = "[task-assign]";
25378
+ var app59 = new Hono();
25379
+ app59.post("/", requireAdminSession, async (c) => {
25191
25380
  const cacheKey = c.var.cacheKey;
25192
25381
  const accountId = getAccountIdForSession(cacheKey);
25193
25382
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
@@ -25200,7 +25389,7 @@ app58.post("/", requireAdminSession, async (c) => {
25200
25389
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
25201
25390
  if (!taskId) {
25202
25391
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
25203
- console.error(`${TAG34} op=assign rejected reason=${reason} account=${accountId}`);
25392
+ console.error(`${TAG35} op=assign rejected reason=${reason} account=${accountId}`);
25204
25393
  return c.json({ error: "taskId required" }, 400);
25205
25394
  }
25206
25395
  const rawAssignee = body.assigneeUserId;
@@ -25212,7 +25401,7 @@ app58.post("/", requireAdminSession, async (c) => {
25212
25401
  const { ids } = await resolveAllowedAssigneeIds(accountId);
25213
25402
  if (!ids.has(assigneeUserId)) {
25214
25403
  console.error(
25215
- `${TAG34} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=not-in-roster`
25404
+ `${TAG35} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=not-in-roster`
25216
25405
  );
25217
25406
  return c.json({ error: "assignee-not-in-roster" }, 403);
25218
25407
  }
@@ -25234,7 +25423,7 @@ app58.post("/", requireAdminSession, async (c) => {
25234
25423
  `MATCH (t:Task {taskId: $taskId, accountId: $accountId}) SET t.updatedAt = $now`,
25235
25424
  { taskId, accountId, now }
25236
25425
  );
25237
- console.log(`${TAG34} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=none assigned=false source=route`);
25426
+ console.log(`${TAG35} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=none assigned=false source=route`);
25238
25427
  invalidate("tasks-list", accountId);
25239
25428
  return c.json({ ok: true, assigneeUserId: null, assigneeName: null });
25240
25429
  }
@@ -25250,7 +25439,7 @@ app58.post("/", requireAdminSession, async (c) => {
25250
25439
  );
25251
25440
  if (set.records.length === 0) {
25252
25441
  console.error(
25253
- `${TAG34} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=no-adminuser-node`
25442
+ `${TAG35} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=no-adminuser-node`
25254
25443
  );
25255
25444
  return c.json({ error: "assignee-no-admin-node" }, 409);
25256
25445
  }
@@ -25258,27 +25447,27 @@ app58.post("/", requireAdminSession, async (c) => {
25258
25447
  const family = set.records[0].get("familyName") ?? null;
25259
25448
  const assigneeName = given && given.trim().length > 0 ? family && family.trim().length > 0 ? `${given.trim()} ${family.trim()}` : given.trim() : null;
25260
25449
  console.log(
25261
- `${TAG34} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=true source=route`
25450
+ `${TAG35} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=true source=route`
25262
25451
  );
25263
25452
  invalidate("tasks-list", accountId);
25264
25453
  return c.json({ ok: true, assigneeUserId, assigneeName });
25265
25454
  } catch (err) {
25266
- console.error(`${TAG34} op=assign error="${err instanceof Error ? err.message : String(err)}"`);
25455
+ console.error(`${TAG35} op=assign error="${err instanceof Error ? err.message : String(err)}"`);
25267
25456
  return c.json({ error: "task-assign-failed" }, 500);
25268
25457
  } finally {
25269
25458
  await session.close();
25270
25459
  }
25271
25460
  });
25272
- var task_assign_default = app58;
25461
+ var task_assign_default = app59;
25273
25462
 
25274
25463
  // server/routes/admin/task-session-open.ts
25275
- var TAG35 = "[task-session]";
25276
- var app59 = new Hono();
25277
- app59.post("/", requireAdminSession, async (c) => {
25464
+ var TAG36 = "[task-session]";
25465
+ var app60 = new Hono();
25466
+ app60.post("/", requireAdminSession, async (c) => {
25278
25467
  const cacheKey = c.var.cacheKey;
25279
25468
  const accountId = getAccountIdForSession(cacheKey);
25280
25469
  if (!accountId) {
25281
- console.error(`${TAG35} op=request auth-rejected reason="no account for session"`);
25470
+ console.error(`${TAG36} op=request auth-rejected reason="no account for session"`);
25282
25471
  return c.json({ error: "Account not found for session" }, 401);
25283
25472
  }
25284
25473
  let body;
@@ -25290,7 +25479,7 @@ app59.post("/", requireAdminSession, async (c) => {
25290
25479
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
25291
25480
  if (!taskId) {
25292
25481
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
25293
- console.error(`${TAG35} op=request rejected reason=${reason} account=${accountId}`);
25482
+ console.error(`${TAG36} op=request rejected reason=${reason} account=${accountId}`);
25294
25483
  return c.json({ error: "taskId required" }, 400);
25295
25484
  }
25296
25485
  const session = getSession();
@@ -25304,14 +25493,14 @@ app59.post("/", requireAdminSession, async (c) => {
25304
25493
  { taskId, accountId }
25305
25494
  );
25306
25495
  if (found.records.length === 0) {
25307
- console.error(`${TAG35} op=request taskId=${taskId} account=${accountId} result=task-not-found`);
25496
+ console.error(`${TAG36} op=request taskId=${taskId} account=${accountId} result=task-not-found`);
25308
25497
  return c.json({ error: "task-not-found" }, 404);
25309
25498
  }
25310
25499
  const title = found.records[0].get("name") ?? "";
25311
25500
  const sessionId = found.records[0].get("sessionId") ?? null;
25312
25501
  const description = found.records[0].get("description") ?? "";
25313
25502
  const notes = found.records[0].get("notes") ?? "";
25314
- console.log(`${TAG35} op=request taskId=${taskId} account=${accountId} linked=${sessionId ?? "none"}`);
25503
+ console.log(`${TAG36} op=request taskId=${taskId} account=${accountId} linked=${sessionId ?? "none"}`);
25315
25504
  if (sessionId !== null) return c.json({ sessionId, title, description, notes });
25316
25505
  const now = (/* @__PURE__ */ new Date()).toISOString();
25317
25506
  const stamped = await session.run(
@@ -25321,25 +25510,25 @@ app59.post("/", requireAdminSession, async (c) => {
25321
25510
  { taskId, accountId, now }
25322
25511
  );
25323
25512
  const at = stamped.records[0]?.get("at") ?? void 0;
25324
- console.log(`${TAG35} op=marker taskId=${taskId} stamped=${at !== void 0}`);
25513
+ console.log(`${TAG36} op=marker taskId=${taskId} stamped=${at !== void 0}`);
25325
25514
  return c.json({ sessionId: null, title, description, notes });
25326
25515
  } catch (err) {
25327
- console.error(`${TAG35} op=request taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
25516
+ console.error(`${TAG36} op=request taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
25328
25517
  return c.json({ error: "task-session-open-failed" }, 500);
25329
25518
  } finally {
25330
25519
  await session.close();
25331
25520
  }
25332
25521
  });
25333
- var task_session_open_default = app59;
25522
+ var task_session_open_default = app60;
25334
25523
 
25335
25524
  // server/routes/admin/task-session-link.ts
25336
- var TAG36 = "[task-session]";
25337
- var app60 = new Hono();
25338
- app60.post("/", requireAdminSession, async (c) => {
25525
+ var TAG37 = "[task-session]";
25526
+ var app61 = new Hono();
25527
+ app61.post("/", requireAdminSession, async (c) => {
25339
25528
  const cacheKey = c.var.cacheKey;
25340
25529
  const accountId = getAccountIdForSession(cacheKey);
25341
25530
  if (!accountId) {
25342
- console.error(`${TAG36} op=link auth-rejected reason="no account for session"`);
25531
+ console.error(`${TAG37} op=link auth-rejected reason="no account for session"`);
25343
25532
  return c.json({ error: "Account not found for session" }, 401);
25344
25533
  }
25345
25534
  let body;
@@ -25351,12 +25540,12 @@ app60.post("/", requireAdminSession, async (c) => {
25351
25540
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
25352
25541
  if (!taskId) {
25353
25542
  const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
25354
- console.error(`${TAG36} op=link rejected reason=${reason} account=${accountId}`);
25543
+ console.error(`${TAG37} op=link rejected reason=${reason} account=${accountId}`);
25355
25544
  return c.json({ error: "taskId required" }, 400);
25356
25545
  }
25357
25546
  const sessionId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : "";
25358
25547
  if (!sessionId) {
25359
- console.error(`${TAG36} op=link rejected reason=missing-sessionId taskId=${taskId} account=${accountId}`);
25548
+ console.error(`${TAG37} op=link rejected reason=missing-sessionId taskId=${taskId} account=${accountId}`);
25360
25549
  return c.json({ error: "sessionId required" }, 400);
25361
25550
  }
25362
25551
  const session = getSession();
@@ -25370,13 +25559,13 @@ app60.post("/", requireAdminSession, async (c) => {
25370
25559
  { taskId, accountId, sessionId }
25371
25560
  );
25372
25561
  if (state.records.length === 0) {
25373
- console.error(`${TAG36} op=link taskId=${taskId} account=${accountId} result=task-not-found`);
25562
+ console.error(`${TAG37} op=link taskId=${taskId} account=${accountId} result=task-not-found`);
25374
25563
  return c.json({ error: "task-not-found" }, 404);
25375
25564
  }
25376
25565
  const conversationExists = state.records[0].get("conversationExists") === true;
25377
25566
  const priorSessionIds = (state.records[0].get("priorSessionIds") ?? []).filter((s) => typeof s === "string");
25378
25567
  if (!conversationExists) {
25379
- console.error(`${TAG36} op=link-failed taskId=${taskId} sessionId=${sessionId} reason=conversation-absent`);
25568
+ console.error(`${TAG37} op=link-failed taskId=${taskId} sessionId=${sessionId} reason=conversation-absent`);
25380
25569
  return c.json({ linked: false, created: false, repointedFrom: null });
25381
25570
  }
25382
25571
  const stale = priorSessionIds.filter((s) => s !== sessionId);
@@ -25402,50 +25591,50 @@ app60.post("/", requireAdminSession, async (c) => {
25402
25591
  const repointedFrom = stale.length > 0 ? stale[0] : null;
25403
25592
  if (!linked) {
25404
25593
  console.error(
25405
- `${TAG36} op=link-failed taskId=${taskId} sessionId=${sessionId} reason=edge-absent-after-merge linkedTo=${linkedTo ?? "none"}`
25594
+ `${TAG37} op=link-failed taskId=${taskId} sessionId=${sessionId} reason=edge-absent-after-merge linkedTo=${linkedTo ?? "none"}`
25406
25595
  );
25407
25596
  return c.json({ linked: false, created: false, repointedFrom });
25408
25597
  }
25409
25598
  console.log(
25410
- `${TAG36} op=link taskId=${taskId} sessionId=${sessionId} created=${created} repointedFrom=${repointedFrom ?? "none"}`
25599
+ `${TAG37} op=link taskId=${taskId} sessionId=${sessionId} created=${created} repointedFrom=${repointedFrom ?? "none"}`
25411
25600
  );
25412
25601
  invalidate("tasks-list", accountId);
25413
25602
  return c.json({ linked: true, created, repointedFrom });
25414
25603
  } catch (err) {
25415
25604
  console.error(
25416
- `${TAG36} op=link-failed taskId=${taskId} sessionId=${sessionId} reason="${err instanceof Error ? err.message : String(err)}"`
25605
+ `${TAG37} op=link-failed taskId=${taskId} sessionId=${sessionId} reason="${err instanceof Error ? err.message : String(err)}"`
25417
25606
  );
25418
25607
  return c.json({ error: "task-session-link-failed" }, 500);
25419
25608
  } finally {
25420
25609
  await session.close();
25421
25610
  }
25422
25611
  });
25423
- var task_session_link_default = app60;
25612
+ var task_session_link_default = app61;
25424
25613
 
25425
25614
  // server/routes/admin/admin-users-list.ts
25426
- var TAG37 = "[admin-users]";
25427
- var app61 = new Hono();
25428
- app61.get("/", requireAdminSession, async (c) => {
25615
+ var TAG38 = "[admin-users]";
25616
+ var app62 = new Hono();
25617
+ app62.get("/", requireAdminSession, async (c) => {
25429
25618
  const cacheKey = c.var.cacheKey;
25430
25619
  const accountId = getAccountIdForSession(cacheKey);
25431
25620
  if (!accountId) {
25432
- console.error(`${TAG37} op=list auth-rejected reason="no account for session"`);
25621
+ console.error(`${TAG38} op=list auth-rejected reason="no account for session"`);
25433
25622
  return c.json({ error: "Account not found for session" }, 401);
25434
25623
  }
25435
25624
  try {
25436
25625
  const users = await resolveAssignableAdmins(accountId);
25437
- console.log(`${TAG37} op=list account=${accountId} count=${users.length}`);
25626
+ console.log(`${TAG38} op=list account=${accountId} count=${users.length}`);
25438
25627
  return c.json({ ok: true, users });
25439
25628
  } catch (err) {
25440
- console.error(`${TAG37} op=list error="${err instanceof Error ? err.message : String(err)}"`);
25629
+ console.error(`${TAG38} op=list error="${err instanceof Error ? err.message : String(err)}"`);
25441
25630
  return c.json({ error: "admin-users-list-failed" }, 500);
25442
25631
  }
25443
25632
  });
25444
- var admin_users_list_default = app61;
25633
+ var admin_users_list_default = app62;
25445
25634
 
25446
25635
  // server/routes/admin/dispatch-destinations.ts
25447
25636
  import { resolve as resolve34 } from "path";
25448
- var TAG38 = "[admin:routines]";
25637
+ var TAG39 = "[admin:routines]";
25449
25638
  function scopeFor(d, houseDir, houseAccountId) {
25450
25639
  if (d.channel !== "whatsapp") return null;
25451
25640
  if (d.label === "Account manager") return boundSubAccountFor(houseDir, d.destination);
@@ -25459,11 +25648,11 @@ function telephoneVariants(destination) {
25459
25648
  function scrubDigitRuns(message) {
25460
25649
  return message.replace(/\d{7,}/g, "<redacted>");
25461
25650
  }
25462
- var app62 = new Hono();
25463
- app62.get("/", requireAdminSession, async (c) => {
25651
+ var app63 = new Hono();
25652
+ app63.get("/", requireAdminSession, async (c) => {
25464
25653
  const accountId = getAccountIdForSession(c.var.cacheKey);
25465
25654
  if (!accountId) {
25466
- console.error(`${TAG38} op=destinations auth-rejected reason="no account for session"`);
25655
+ console.error(`${TAG39} op=destinations auth-rejected reason="no account for session"`);
25467
25656
  return c.json({ error: "Account not found for session" }, 401);
25468
25657
  }
25469
25658
  let houseAccountId = null;
@@ -25471,7 +25660,7 @@ app62.get("/", requireAdminSession, async (c) => {
25471
25660
  houseAccountId = resolveHouseOrSoleAccountId(ACCOUNTS_DIR);
25472
25661
  } catch (err) {
25473
25662
  const message = err instanceof Error ? err.message : String(err);
25474
- console.error(`${TAG38} op=destinations account=${accountId.slice(0, 8)} outcome=house-unresolved reason="${message}"`);
25663
+ console.error(`${TAG39} op=destinations account=${accountId.slice(0, 8)} outcome=house-unresolved reason="${message}"`);
25475
25664
  }
25476
25665
  const destinations = listDispatchDestinations({ accountsDir: ACCOUNTS_DIR, houseAccountId, accountId });
25477
25666
  const houseDir = houseAccountId === null ? null : resolve34(ACCOUNTS_DIR, houseAccountId);
@@ -25522,16 +25711,16 @@ app62.get("/", requireAdminSession, async (c) => {
25522
25711
  const scopes = [...byScope.keys()];
25523
25712
  const nameScope = scopes.length === 0 ? "none" : scopes.every((s) => s === houseAccountId) ? "house" : scopes.some((s) => s === houseAccountId) ? "mixed" : "account";
25524
25713
  console.log(
25525
- `${TAG38} op=destinations account=${accountId.slice(0, 8)} count=${destinations.length} named=${named} unnamed=${destinations.length - named} nameScope=${nameScope}` + (houseAccountId === null ? " outcome=house-unresolved" : "") + (namesUnresolved === null ? "" : ` outcome=names-unresolved reason="${namesUnresolved}"`)
25714
+ `${TAG39} op=destinations account=${accountId.slice(0, 8)} count=${destinations.length} named=${named} unnamed=${destinations.length - named} nameScope=${nameScope}` + (houseAccountId === null ? " outcome=house-unresolved" : "") + (namesUnresolved === null ? "" : ` outcome=names-unresolved reason="${namesUnresolved}"`)
25526
25715
  );
25527
25716
  return c.json({ destinations });
25528
25717
  });
25529
- var dispatch_destinations_default = app62;
25718
+ var dispatch_destinations_default = app63;
25530
25719
 
25531
25720
  // server/routes/admin/activity.ts
25532
25721
  import { readdirSync as readdirSync27 } from "fs";
25533
- var app63 = new Hono();
25534
- var TAG39 = "[activity]";
25722
+ var app64 = new Hono();
25723
+ var TAG40 = "[activity]";
25535
25724
  function resolveHouseAccountId2(accounts) {
25536
25725
  const houses = accounts.filter((a) => a.config.role === "house");
25537
25726
  if (houses.length === 1) return { accountId: houses[0].accountId, reason: "house" };
@@ -25552,7 +25741,7 @@ function countAccountDirsOnDisk() {
25552
25741
  return null;
25553
25742
  }
25554
25743
  }
25555
- app63.get("/", requireAdminSession, async (c) => {
25744
+ app64.get("/", requireAdminSession, async (c) => {
25556
25745
  const req = requestToken();
25557
25746
  const cacheKey = c.var.cacheKey;
25558
25747
  const caller2 = getAccountIdForSession(cacheKey) ?? null;
@@ -25560,19 +25749,19 @@ app63.get("/", requireAdminSession, async (c) => {
25560
25749
  const house = resolveHouseAccountId2(valid);
25561
25750
  const isHouse = caller2 !== null && house.accountId !== null && caller2 === house.accountId;
25562
25751
  console.log(
25563
- `${TAG39} op=request req=${req} caller=${short(caller2)} house=${isHouse} houseVia=${house.reason}`
25752
+ `${TAG40} op=request req=${req} caller=${short(caller2)} house=${isHouse} houseVia=${house.reason}`
25564
25753
  );
25565
25754
  if (!isHouse) {
25566
25755
  const reason = house.accountId === null ? house.reason : "not-house";
25567
25756
  console.error(
25568
- `${TAG39} op=gate req=${req} decision=deny reason=${reason} caller=${short(caller2)} houseAcct=${short(house.accountId)} accounts=${valid.length}`
25757
+ `${TAG40} op=gate req=${req} decision=deny reason=${reason} caller=${short(caller2)} houseAcct=${short(house.accountId)} accounts=${valid.length}`
25569
25758
  );
25570
25759
  return c.json({ error: "house-only" }, 403);
25571
25760
  }
25572
- console.log(`${TAG39} op=gate req=${req} decision=allow caller=${short(caller2)} via=${house.reason}`);
25761
+ console.log(`${TAG40} op=gate req=${req} decision=allow caller=${short(caller2)} via=${house.reason}`);
25573
25762
  const onDisk = countAccountDirsOnDisk();
25574
25763
  console.log(
25575
- `${TAG39} op=accounts req=${req} onDisk=${onDisk ?? "unknown"} valid=${valid.length} ids=${valid.map((a) => short(a.accountId)).join(",")}`
25764
+ `${TAG40} op=accounts req=${req} onDisk=${onDisk ?? "unknown"} valid=${valid.length} ids=${valid.map((a) => short(a.accountId)).join(",")}`
25576
25765
  );
25577
25766
  const start = Date.now();
25578
25767
  let res;
@@ -25588,17 +25777,17 @@ app63.get("/", requireAdminSession, async (c) => {
25588
25777
  });
25589
25778
  } catch (err) {
25590
25779
  console.error(
25591
- `${TAG39} op=manager-fail req=${req} reason=unreachable err=${err instanceof Error ? err.message : String(err)}`
25780
+ `${TAG40} op=manager-fail req=${req} reason=unreachable err=${err instanceof Error ? err.message : String(err)}`
25592
25781
  );
25593
25782
  return c.json({ error: "manager-unreachable" }, 502);
25594
25783
  }
25595
25784
  if (res.status !== 200) {
25596
- console.error(`${TAG39} op=manager-fail req=${req} reason=status status=${res.status}`);
25785
+ console.error(`${TAG40} op=manager-fail req=${req} reason=status status=${res.status}`);
25597
25786
  return c.json({ error: `manager-status-${res.status}` }, 502);
25598
25787
  }
25599
25788
  const body = await res.json().catch(() => null);
25600
25789
  if (!body || !Array.isArray(body.rows)) {
25601
- console.error(`${TAG39} op=manager-fail req=${req} reason=bad-json`);
25790
+ console.error(`${TAG40} op=manager-fail req=${req} reason=bad-json`);
25602
25791
  return c.json({ error: "manager-bad-json" }, 502);
25603
25792
  }
25604
25793
  const rows = body.rows;
@@ -25609,10 +25798,10 @@ app63.get("/", requireAdminSession, async (c) => {
25609
25798
  const ageNull = rows.filter((r) => r.ageMs === null).length;
25610
25799
  const sidecarNull = rows.filter((r) => r.sidecarMissing).length;
25611
25800
  console.log(
25612
- `${TAG39} op=census req=${req} ageMs=${censusAgeMs ?? "none"} live=${census?.live ?? "none"} cpuNull=${census?.cpuNull ?? "none"} rows=${rows.length} ageNull=${ageNull} sidecarNull=${sidecarNull}`
25801
+ `${TAG40} op=census req=${req} ageMs=${censusAgeMs ?? "none"} live=${census?.live ?? "none"} cpuNull=${census?.cpuNull ?? "none"} rows=${rows.length} ageNull=${ageNull} sidecarNull=${sidecarNull}`
25613
25802
  );
25614
25803
  console.log(
25615
- `${TAG39} op=response req=${req} rows=${rows.length} accounts=${valid.length} untagged=${untaggedCount} meteringFailed=${meteringFailed} ms=${Date.now() - start}`
25804
+ `${TAG40} op=response req=${req} rows=${rows.length} accounts=${valid.length} untagged=${untaggedCount} meteringFailed=${meteringFailed} ms=${Date.now() - start}`
25616
25805
  );
25617
25806
  const names = {};
25618
25807
  await Promise.all(
@@ -25631,7 +25820,7 @@ app63.get("/", requireAdminSession, async (c) => {
25631
25820
  return acc;
25632
25821
  }, {});
25633
25822
  console.log(
25634
- `${TAG39} op=loops req=${req} armed=${armed2} rendered=${loopRows.length} procPct=${cpu.processPct?.toFixed(1) ?? "null"} attributedPct=${cpu.attributedPct?.toFixed(1) ?? "null"} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
25823
+ `${TAG40} op=loops req=${req} armed=${armed2} rendered=${loopRows.length} procPct=${cpu.processPct?.toFixed(1) ?? "null"} attributedPct=${cpu.attributedPct?.toFixed(1) ?? "null"} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
25635
25824
  );
25636
25825
  return c.json({
25637
25826
  rows,
@@ -25661,69 +25850,70 @@ app63.get("/", requireAdminSession, async (c) => {
25661
25850
  }
25662
25851
  });
25663
25852
  });
25664
- var activity_default = app63;
25853
+ var activity_default = app64;
25665
25854
 
25666
25855
  // server/routes/admin/index.ts
25667
- var app64 = new Hono();
25668
- app64.route("/session", session_default);
25669
- app64.route("/accounts", accounts_default);
25670
- app64.route("/logs", logs_default);
25671
- app64.route("/claude-info", claude_info_default);
25672
- app64.route("/attachment", attachment_default);
25673
- app64.route("/session-upload", session_upload_default);
25674
- app64.route("/agents", agents_default);
25675
- app64.route("/sessions", sessions_default);
25676
- app64.route("/claude-sessions", claude_sessions_default);
25677
- app64.route("/log-ingest", log_ingest_default);
25678
- app64.route("/events", events_default);
25679
- app64.route("/files", files_default);
25680
- app64.route("/graph-search", graph_search_default);
25681
- app64.route("/graph-subgraph", graph_subgraph_default);
25682
- app64.route("/graph-delete", graph_delete_default);
25683
- app64.route("/graph-restore", graph_restore_default);
25684
- app64.route("/graph-labels-in-graph", graph_labels_in_graph_default);
25685
- app64.route("/graph-default-view", graph_default_view_default);
25686
- app64.route("/sidebar-artefacts", sidebar_artefacts_default);
25687
- app64.route("/sidebar-sessions", sidebar_sessions_default);
25688
- app64.route("/session-delete", session_delete_default);
25689
- app64.route("/session-stop", session_stop_default);
25690
- app64.route("/session-archive", session_archive_default);
25691
- app64.route("/session-rename", session_rename_default);
25692
- app64.route("/session-usage", session_usage_default);
25693
- app64.route("/browser", browser_default);
25694
- app64.route("/session-rc-spawn", session_rc_spawn_default);
25695
- app64.route("/session-reseat", session_reseat_default);
25696
- app64.route("/system-stats", system_stats_default);
25697
- app64.route("/health-brand", health_default2);
25698
- app64.route("/linkedin-ingest", linkedin_ingest_default);
25699
- app64.route("/post-turn-context", post_turn_context_default);
25700
- app64.route("/public-session-context", public_session_context_default);
25701
- app64.route("/public-session-exit", public_session_exit_default);
25702
- app64.route("/access-session-evict", access_session_evict_default);
25703
- app64.route("/enrol-person", enrol_person_default);
25704
- app64.route("/calendar", calendar_default);
25705
- app64.route("/data-portal", data_portal_default);
25706
- app64.route("/routines", routines_default);
25707
- app64.route("/skills", skills_default);
25708
- app64.route("/tasks-list", tasks_list_default);
25709
- app64.route("/task-timer-start", task_timer_start_default);
25710
- app64.route("/task-timer-stop", task_timer_stop_default);
25711
- app64.route("/task-complete", task_complete_default);
25712
- app64.route("/task-time-adjust", task_time_adjust_default);
25713
- app64.route("/task-assign", task_assign_default);
25714
- app64.route("/task-session-open", task_session_open_default);
25715
- app64.route("/task-session-link", task_session_link_default);
25716
- app64.route("/admin-users-list", admin_users_list_default);
25717
- app64.route("/dispatch-destinations", dispatch_destinations_default);
25718
- app64.route("/activity", activity_default);
25719
- var admin_default = app64;
25856
+ var app65 = new Hono();
25857
+ app65.route("/session", session_default);
25858
+ app65.route("/accounts", accounts_default);
25859
+ app65.route("/logs", logs_default);
25860
+ app65.route("/claude-info", claude_info_default);
25861
+ app65.route("/attachment", attachment_default);
25862
+ app65.route("/session-upload", session_upload_default);
25863
+ app65.route("/agents", agents_default);
25864
+ app65.route("/sessions", sessions_default);
25865
+ app65.route("/claude-sessions", claude_sessions_default);
25866
+ app65.route("/log-ingest", log_ingest_default);
25867
+ app65.route("/events", events_default);
25868
+ app65.route("/files", files_default);
25869
+ app65.route("/graph-search", graph_search_default);
25870
+ app65.route("/graph-subgraph", graph_subgraph_default);
25871
+ app65.route("/graph-delete", graph_delete_default);
25872
+ app65.route("/graph-restore", graph_restore_default);
25873
+ app65.route("/graph-labels-in-graph", graph_labels_in_graph_default);
25874
+ app65.route("/graph-default-view", graph_default_view_default);
25875
+ app65.route("/sidebar-artefacts", sidebar_artefacts_default);
25876
+ app65.route("/sidebar-sessions", sidebar_sessions_default);
25877
+ app65.route("/session-delete", session_delete_default);
25878
+ app65.route("/session-stop", session_stop_default);
25879
+ app65.route("/session-archive", session_archive_default);
25880
+ app65.route("/session-rename", session_rename_default);
25881
+ app65.route("/session-usage", session_usage_default);
25882
+ app65.route("/browser", browser_default);
25883
+ app65.route("/session-rc-spawn", session_rc_spawn_default);
25884
+ app65.route("/session-reseat", session_reseat_default);
25885
+ app65.route("/system-stats", system_stats_default);
25886
+ app65.route("/health-brand", health_default2);
25887
+ app65.route("/linkedin-ingest", linkedin_ingest_default);
25888
+ app65.route("/post-turn-context", post_turn_context_default);
25889
+ app65.route("/public-session-context", public_session_context_default);
25890
+ app65.route("/public-session-exit", public_session_exit_default);
25891
+ app65.route("/access-session-evict", access_session_evict_default);
25892
+ app65.route("/enrol-person", enrol_person_default);
25893
+ app65.route("/calendar", calendar_default);
25894
+ app65.route("/data-portal", data_portal_default);
25895
+ app65.route("/routines", routines_default);
25896
+ app65.route("/skills", skills_default);
25897
+ app65.route("/tasks-list", tasks_list_default);
25898
+ app65.route("/task-timer-start", task_timer_start_default);
25899
+ app65.route("/task-timer-stop", task_timer_stop_default);
25900
+ app65.route("/task-complete", task_complete_default);
25901
+ app65.route("/task-time-adjust", task_time_adjust_default);
25902
+ app65.route("/task-delete", task_delete_default);
25903
+ app65.route("/task-assign", task_assign_default);
25904
+ app65.route("/task-session-open", task_session_open_default);
25905
+ app65.route("/task-session-link", task_session_link_default);
25906
+ app65.route("/admin-users-list", admin_users_list_default);
25907
+ app65.route("/dispatch-destinations", dispatch_destinations_default);
25908
+ app65.route("/activity", activity_default);
25909
+ var admin_default = app65;
25720
25910
 
25721
25911
  // server/routes/access/verify-token.ts
25722
- var TAG40 = "[access-verify]";
25912
+ var TAG41 = "[access-verify]";
25723
25913
  var MINT_TAG = "[access-session-mint]";
25724
25914
  var COOKIE_NAME = "__access_session";
25725
- var app65 = new Hono();
25726
- app65.post("/", async (c) => {
25915
+ var app66 = new Hono();
25916
+ app66.post("/", async (c) => {
25727
25917
  const ip = c.var.clientIp || "unknown";
25728
25918
  let body;
25729
25919
  try {
@@ -25738,39 +25928,39 @@ app65.post("/", async (c) => {
25738
25928
  }
25739
25929
  const rateMsg = checkAccessRateLimit(ip, agentSlug);
25740
25930
  if (rateMsg) {
25741
- console.error(`${TAG40} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
25931
+ console.error(`${TAG41} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
25742
25932
  return c.json({ error: rateMsg }, 429);
25743
25933
  }
25744
25934
  const grant = await findGrantByMagicToken(token);
25745
25935
  if (!grant) {
25746
25936
  recordAccessFailedAttempt(ip, agentSlug);
25747
- console.error(`${TAG40} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
25937
+ console.error(`${TAG41} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
25748
25938
  return c.json({ error: "invalid-or-expired-link" }, 401);
25749
25939
  }
25750
25940
  if (grant.agentSlug !== agentSlug) {
25751
25941
  recordAccessFailedAttempt(ip, agentSlug);
25752
25942
  console.error(
25753
- `${TAG40} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
25943
+ `${TAG41} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
25754
25944
  );
25755
25945
  return c.json({ error: "invalid-or-expired-link" }, 401);
25756
25946
  }
25757
25947
  if (grant.status === "expired" || grant.status === "revoked") {
25758
25948
  recordAccessFailedAttempt(ip, agentSlug);
25759
25949
  console.error(
25760
- `${TAG40} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
25950
+ `${TAG41} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
25761
25951
  );
25762
25952
  return c.json({ error: "access-no-longer-valid" }, 401);
25763
25953
  }
25764
25954
  if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
25765
25955
  recordAccessFailedAttempt(ip, agentSlug);
25766
25956
  console.error(
25767
- `${TAG40} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
25957
+ `${TAG41} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
25768
25958
  );
25769
25959
  return c.json({ error: "access-no-longer-valid" }, 401);
25770
25960
  }
25771
25961
  if (!grant.sliceToken) {
25772
25962
  console.error(
25773
- `${TAG40} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
25963
+ `${TAG41} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
25774
25964
  );
25775
25965
  return c.json({ error: "grant-misconfigured" }, 500);
25776
25966
  }
@@ -25786,12 +25976,12 @@ app65.post("/", async (c) => {
25786
25976
  await consumeMagicTokenAndActivate(grant.grantId);
25787
25977
  } catch (err) {
25788
25978
  console.error(
25789
- `${TAG40} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
25979
+ `${TAG41} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
25790
25980
  );
25791
25981
  return c.json({ error: "verification-failed" }, 500);
25792
25982
  }
25793
25983
  clearAccessRateLimit(ip, agentSlug);
25794
- console.log(`${TAG40} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
25984
+ console.log(`${TAG41} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
25795
25985
  console.log(
25796
25986
  `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
25797
25987
  );
@@ -25805,7 +25995,7 @@ app65.post("/", async (c) => {
25805
25995
  displayName: grant.displayName
25806
25996
  });
25807
25997
  });
25808
- var verify_token_default = app65;
25998
+ var verify_token_default = app66;
25809
25999
 
25810
26000
  // app/lib/access-email.ts
25811
26001
  import { spawn as spawn2 } from "child_process";
@@ -25867,10 +26057,10 @@ async function sendMagicLinkEmail(payload) {
25867
26057
  }
25868
26058
 
25869
26059
  // server/routes/access/request-magic-link.ts
25870
- var TAG41 = "[access-request-link]";
25871
- var app66 = new Hono();
26060
+ var TAG42 = "[access-request-link]";
26061
+ var app67 = new Hono();
25872
26062
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
25873
- app66.post("/", async (c) => {
26063
+ app67.post("/", async (c) => {
25874
26064
  let body;
25875
26065
  try {
25876
26066
  body = await c.req.json();
@@ -25884,18 +26074,18 @@ app66.post("/", async (c) => {
25884
26074
  }
25885
26075
  const rateMsg = checkRequestLinkRateLimit(contactValue);
25886
26076
  if (rateMsg) {
25887
- console.error(`${TAG41} contactValue=${maskContact(contactValue)} result=rate-limited`);
26077
+ console.error(`${TAG42} contactValue=${maskContact(contactValue)} result=rate-limited`);
25888
26078
  return c.json({ error: rateMsg }, 429);
25889
26079
  }
25890
26080
  recordRequestLinkAttempt(contactValue);
25891
26081
  const accountId = process.env.ACCOUNT_ID ?? "";
25892
26082
  if (!accountId) {
25893
- console.error(`${TAG41} contactValue=${maskContact(contactValue)} result=no-account-id`);
26083
+ console.error(`${TAG42} contactValue=${maskContact(contactValue)} result=no-account-id`);
25894
26084
  return c.json({ message: VISITOR_MESSAGE }, 200);
25895
26085
  }
25896
26086
  const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
25897
26087
  if (!grant) {
25898
- console.log(`${TAG41} contactValue=${maskContact(contactValue)} result=notfound`);
26088
+ console.log(`${TAG42} contactValue=${maskContact(contactValue)} result=notfound`);
25899
26089
  return c.json({ message: VISITOR_MESSAGE }, 200);
25900
26090
  }
25901
26091
  let token;
@@ -25903,7 +26093,7 @@ app66.post("/", async (c) => {
25903
26093
  token = await generateNewMagicToken(grant.grantId);
25904
26094
  } catch (err) {
25905
26095
  console.error(
25906
- `${TAG41} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
26096
+ `${TAG42} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
25907
26097
  );
25908
26098
  return c.json({ message: VISITOR_MESSAGE }, 200);
25909
26099
  }
@@ -25935,22 +26125,22 @@ app66.post("/", async (c) => {
25935
26125
  });
25936
26126
  if (!sendResult.ok) {
25937
26127
  console.error(
25938
- `${TAG41} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
26128
+ `${TAG42} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
25939
26129
  );
25940
26130
  return c.json({ message: VISITOR_MESSAGE }, 200);
25941
26131
  }
25942
26132
  console.log(
25943
- `${TAG41} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
26133
+ `${TAG42} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
25944
26134
  );
25945
26135
  return c.json({ message: VISITOR_MESSAGE }, 200);
25946
26136
  });
25947
- var request_magic_link_default = app66;
26137
+ var request_magic_link_default = app67;
25948
26138
 
25949
26139
  // server/routes/access/index.ts
25950
- var app67 = new Hono();
25951
- app67.route("/verify-token", verify_token_default);
25952
- app67.route("/request-magic-link", request_magic_link_default);
25953
- var access_default = app67;
26140
+ var app68 = new Hono();
26141
+ app68.route("/verify-token", verify_token_default);
26142
+ app68.route("/request-magic-link", request_magic_link_default);
26143
+ var access_default = app68;
25954
26144
 
25955
26145
  // server/routes/sites.ts
25956
26146
  import { existsSync as existsSync43, readFileSync as readFileSync41, realpathSync as realpathSync8, statSync as statSync22 } from "fs";
@@ -25985,8 +26175,8 @@ function getExt(p) {
25985
26175
  if (idx < p.lastIndexOf("/")) return "";
25986
26176
  return p.slice(idx).toLowerCase();
25987
26177
  }
25988
- var app68 = new Hono();
25989
- app68.get("/:rel{.*}", (c) => {
26178
+ var app69 = new Hono();
26179
+ app69.get("/:rel{.*}", (c) => {
25990
26180
  const reqPath = c.req.path;
25991
26181
  const rawRel = c.req.param("rel") ?? "";
25992
26182
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -26089,7 +26279,7 @@ app68.get("/:rel{.*}", (c) => {
26089
26279
  "X-Content-Type-Options": "nosniff"
26090
26280
  });
26091
26281
  });
26092
- var sites_default = app68;
26282
+ var sites_default = app69;
26093
26283
 
26094
26284
  // app/lib/visitor-token.ts
26095
26285
  import { createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "crypto";
@@ -26189,7 +26379,7 @@ function readBrandConfig() {
26189
26379
  }
26190
26380
 
26191
26381
  // server/routes/visitor-consent.ts
26192
- var app69 = new Hono();
26382
+ var app70 = new Hono();
26193
26383
  var CONSENT_COOKIE_NAME = "mxy_consent";
26194
26384
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
26195
26385
  var DEFAULT_CONSENT_COPY = {
@@ -26234,17 +26424,17 @@ function siteSlugFromReferer(referer) {
26234
26424
  return "";
26235
26425
  }
26236
26426
  }
26237
- app69.options("/consent", (c) => {
26427
+ app70.options("/consent", (c) => {
26238
26428
  const origin = getOrigin(c);
26239
26429
  setCorsHeaders(c, origin);
26240
26430
  return c.body(null, 204);
26241
26431
  });
26242
- app69.options("/brand-config", (c) => {
26432
+ app70.options("/brand-config", (c) => {
26243
26433
  const origin = getOrigin(c);
26244
26434
  setCorsHeaders(c, origin);
26245
26435
  return c.body(null, 204);
26246
26436
  });
26247
- app69.post("/consent", async (c) => {
26437
+ app70.post("/consent", async (c) => {
26248
26438
  const origin = getOrigin(c);
26249
26439
  setCorsHeaders(c, origin);
26250
26440
  let raw;
@@ -26284,7 +26474,7 @@ app69.post("/consent", async (c) => {
26284
26474
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
26285
26475
  return c.body(null, 204);
26286
26476
  });
26287
- app69.get("/brand-config", (c) => {
26477
+ app70.get("/brand-config", (c) => {
26288
26478
  const origin = getOrigin(c);
26289
26479
  setCorsHeaders(c, origin);
26290
26480
  const brand = readBrandConfig();
@@ -26294,7 +26484,7 @@ app69.get("/brand-config", (c) => {
26294
26484
  c.header("Cache-Control", "public, max-age=300");
26295
26485
  return c.json({ consent: { copy, palette } });
26296
26486
  });
26297
- var visitor_consent_default = app69;
26487
+ var visitor_consent_default = app70;
26298
26488
 
26299
26489
  // server/routes/listings.ts
26300
26490
  function getCookie(headerValue, name) {
@@ -26321,8 +26511,8 @@ function appendConsentParams(pageUrl, token) {
26321
26511
  }
26322
26512
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
26323
26513
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
26324
- var app70 = new Hono();
26325
- app70.get("/:slug/click", async (c) => {
26514
+ var app71 = new Hono();
26515
+ app71.get("/:slug/click", async (c) => {
26326
26516
  const slug = c.req.param("slug") ?? "";
26327
26517
  const rawSession = c.req.query("session") ?? "";
26328
26518
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -26386,10 +26576,10 @@ app70.get("/:slug/click", async (c) => {
26386
26576
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
26387
26577
  return c.redirect(redirectUrl, 302);
26388
26578
  });
26389
- var listings_default = app70;
26579
+ var listings_default = app71;
26390
26580
 
26391
26581
  // server/routes/visitor-event.ts
26392
- var app71 = new Hono();
26582
+ var app72 = new Hono();
26393
26583
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
26394
26584
  var buckets = /* @__PURE__ */ new Map();
26395
26585
  var RATE_LIMIT = 60;
@@ -26465,12 +26655,12 @@ function originAllowed(origin, allowlist) {
26465
26655
  return false;
26466
26656
  }
26467
26657
  }
26468
- app71.options("/event", (c) => {
26658
+ app72.options("/event", (c) => {
26469
26659
  const origin = getOrigin2(c);
26470
26660
  setCorsHeaders2(c, origin);
26471
26661
  return c.body(null, 204);
26472
26662
  });
26473
- app71.post("/event", async (c) => {
26663
+ app72.post("/event", async (c) => {
26474
26664
  const origin = getOrigin2(c);
26475
26665
  setCorsHeaders2(c, origin);
26476
26666
  const ua = c.req.header("user-agent") ?? "";
@@ -26675,7 +26865,7 @@ async function writeEvent(opts) {
26675
26865
  );
26676
26866
  }
26677
26867
  }
26678
- var visitor_event_default = app71;
26868
+ var visitor_event_default = app72;
26679
26869
 
26680
26870
  // server/routes/session.ts
26681
26871
  import { resolve as resolve37 } from "path";
@@ -26721,8 +26911,8 @@ function withVisitorCookie(response, visitorId) {
26721
26911
  headers
26722
26912
  });
26723
26913
  }
26724
- var app72 = new Hono();
26725
- app72.post("/", async (c) => {
26914
+ var app73 = new Hono();
26915
+ app73.post("/", async (c) => {
26726
26916
  let body;
26727
26917
  try {
26728
26918
  body = await c.req.json();
@@ -26869,20 +27059,20 @@ app72.post("/", async (c) => {
26869
27059
  newVisitorId
26870
27060
  );
26871
27061
  });
26872
- var session_default2 = app72;
27062
+ var session_default2 = app73;
26873
27063
 
26874
27064
  // server/routes/calendar-public.ts
26875
- var app73 = new Hono();
27065
+ var app74 = new Hono();
26876
27066
  function setCors(c) {
26877
27067
  c.header("Access-Control-Allow-Origin", "*");
26878
27068
  c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
26879
27069
  c.header("Access-Control-Allow-Headers", "content-type");
26880
27070
  }
26881
- app73.options("/free-busy", (c) => {
27071
+ app74.options("/free-busy", (c) => {
26882
27072
  setCors(c);
26883
27073
  return c.body(null, 204);
26884
27074
  });
26885
- app73.get("/free-busy", async (c) => {
27075
+ app74.get("/free-busy", async (c) => {
26886
27076
  setCors(c);
26887
27077
  const from = c.req.query("from");
26888
27078
  const to = c.req.query("to");
@@ -26938,15 +27128,15 @@ app73.get("/free-busy", async (c) => {
26938
27128
  await session.close();
26939
27129
  }
26940
27130
  });
26941
- var calendar_public_default = app73;
27131
+ var calendar_public_default = app74;
26942
27132
 
26943
27133
  // server/routes/portal-fetch.ts
26944
27134
  import { createReadStream as createReadStream3 } from "fs";
26945
27135
  import { stat as stat8, readFile as readFile11, realpath } from "fs/promises";
26946
27136
  import { join as join49, resolve as resolve38, sep as sep11 } from "path";
26947
27137
  import { Readable as Readable3 } from "stream";
26948
- var app74 = new Hono();
26949
- var TAG42 = "[portal-fetch]";
27138
+ var app75 = new Hono();
27139
+ var TAG43 = "[portal-fetch]";
26950
27140
  async function verifySignature(secret, accountId, relPath, expiresAtMs, signature, nowMs) {
26951
27141
  if (!secret || !signature) return false;
26952
27142
  if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) return false;
@@ -27001,14 +27191,14 @@ async function readAccountSecret(accountId) {
27001
27191
  try {
27002
27192
  text = await readFile11(path, "utf8");
27003
27193
  } catch {
27004
- console.error(`${TAG42} op=no-secret account="${accountId}" path="${path}"`);
27194
+ console.error(`${TAG43} op=no-secret account="${accountId}" path="${path}"`);
27005
27195
  return "";
27006
27196
  }
27007
27197
  for (const line of text.split("\n")) {
27008
27198
  const m = line.match(/^PORTAL_FETCH_SECRET=(.*)$/);
27009
27199
  if (m) return m[1].trim();
27010
27200
  }
27011
- console.error(`${TAG42} op=no-secret account="${accountId}" reason=key-absent path="${path}"`);
27201
+ console.error(`${TAG43} op=no-secret account="${accountId}" reason=key-absent path="${path}"`);
27012
27202
  return "";
27013
27203
  }
27014
27204
  function isValidAccountId2(value) {
@@ -27068,16 +27258,16 @@ async function handle(c, headOnly) {
27068
27258
  const sig = c.req.query("sig") ?? "";
27069
27259
  const owner = c.req.query("owner") ?? "";
27070
27260
  console.log(
27071
- `${TAG42} op=request account=${q(accountId)} path=${q(relPath)} owner=${q(owner)} head=${headOnly}`
27261
+ `${TAG43} op=request account=${q(accountId)} path=${q(relPath)} owner=${q(owner)} head=${headOnly}`
27072
27262
  );
27073
27263
  if (!isValidAccountId2(accountId)) {
27074
- console.error(`${TAG42} op=denied account=${q(accountId)} reason=account-id`);
27264
+ console.error(`${TAG43} op=denied account=${q(accountId)} reason=account-id`);
27075
27265
  return c.json({ ok: false, error: "denied" }, 401);
27076
27266
  }
27077
27267
  const secret = await readAccountSecret(accountId);
27078
27268
  const verified = owner ? await verifyOwnUploadSignature(secret, accountId, owner, relPath, exp, sig, Date.now()) : await verifySignature(secret, accountId, relPath, exp, sig, Date.now());
27079
27269
  if (!verified) {
27080
- console.error(`${TAG42} op=denied account=${q(accountId)} reason=secret`);
27270
+ console.error(`${TAG43} op=denied account=${q(accountId)} reason=secret`);
27081
27271
  return c.json({ ok: false, error: "denied" }, 401);
27082
27272
  }
27083
27273
  const target = await resolveFetchTarget(
@@ -27087,14 +27277,14 @@ async function handle(c, headOnly) {
27087
27277
  );
27088
27278
  if (!target.ok) {
27089
27279
  const op = target.reason === "enoent" ? "miss" : "denied";
27090
- console.error(`${TAG42} op=${op} account=${accountId} reason=${target.reason}`);
27280
+ console.error(`${TAG43} op=${op} account=${accountId} reason=${target.reason}`);
27091
27281
  return c.json({ ok: false, error: "not found" }, 404);
27092
27282
  }
27093
27283
  if (headOnly) {
27094
- console.log(`${TAG42} op=served account=${accountId} bytes=${target.sizeBytes} head=true`);
27284
+ console.log(`${TAG43} op=served account=${accountId} bytes=${target.sizeBytes} head=true`);
27095
27285
  return c.body(null, 200);
27096
27286
  }
27097
- console.log(`${TAG42} op=served account=${accountId} bytes=${target.sizeBytes}`);
27287
+ console.log(`${TAG43} op=served account=${accountId} bytes=${target.sizeBytes}`);
27098
27288
  return new Response(Readable3.toWeb(createReadStream3(target.absolute)), {
27099
27289
  status: 200,
27100
27290
  headers: {
@@ -27108,12 +27298,12 @@ async function handle(c, headOnly) {
27108
27298
  }
27109
27299
  });
27110
27300
  }
27111
- app74.get("/fetch", (c) => handle(c, false));
27112
- app74.on("HEAD", "/fetch", (c) => handle(c, true));
27113
- var portal_fetch_default = app74;
27301
+ app75.get("/fetch", (c) => handle(c, false));
27302
+ app75.on("HEAD", "/fetch", (c) => handle(c, true));
27303
+ var portal_fetch_default = app75;
27114
27304
 
27115
27305
  // app/lib/timeentry-census.ts
27116
- var TAG43 = "[timeentry-census]";
27306
+ var TAG44 = "[timeentry-census]";
27117
27307
  function reconcileTimeEntries(rows, now, horizonMs) {
27118
27308
  const openEntries = rows.openEntries.length;
27119
27309
  let oldestOpenAgeMs = 0;
@@ -27153,12 +27343,12 @@ async function runTimeEntryCensusOnce(session, now, horizonMs) {
27153
27343
  sumAdjustments: toNum(r.get("sumAdjustments"))
27154
27344
  }));
27155
27345
  const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
27156
- const line = `${TAG43} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
27346
+ const line = `${TAG44} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
27157
27347
  if (finding.alarm) console.error(`${line} alarm=true`);
27158
27348
  else console.log(line);
27159
27349
  return finding;
27160
27350
  } catch (err) {
27161
- console.error(`${TAG43} error="${err instanceof Error ? err.message : String(err)}"`);
27351
+ console.error(`${TAG44} error="${err instanceof Error ? err.message : String(err)}"`);
27162
27352
  return null;
27163
27353
  }
27164
27354
  }
@@ -27172,13 +27362,13 @@ function startTimeEntryCensus(openSession, opts = {}) {
27172
27362
  try {
27173
27363
  session = openSession();
27174
27364
  } catch (err) {
27175
- console.error(`${TAG43} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
27365
+ console.error(`${TAG44} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
27176
27366
  return;
27177
27367
  }
27178
27368
  try {
27179
27369
  await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
27180
27370
  } catch (err) {
27181
- console.error(`${TAG43} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
27371
+ console.error(`${TAG44} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
27182
27372
  } finally {
27183
27373
  try {
27184
27374
  await session.close();
@@ -27239,7 +27429,7 @@ function formatCensusLine(f) {
27239
27429
  }
27240
27430
 
27241
27431
  // app/lib/ledger-census.ts
27242
- var TAG44 = "[ledger-census]";
27432
+ var TAG45 = "[ledger-census]";
27243
27433
  var DAY_MS = 864e5;
27244
27434
  function countWriteRejects24h(logDir, now) {
27245
27435
  let entries2;
@@ -27315,7 +27505,7 @@ async function runLedgerCensusOnce(session, now) {
27315
27505
  else console.log(line);
27316
27506
  return finding;
27317
27507
  } catch (err) {
27318
- console.error(`${TAG44} error="${err instanceof Error ? err.message : String(err)}"`);
27508
+ console.error(`${TAG45} error="${err instanceof Error ? err.message : String(err)}"`);
27319
27509
  return null;
27320
27510
  }
27321
27511
  }
@@ -27328,7 +27518,7 @@ function startLedgerCensus(openSession, opts = {}) {
27328
27518
  session = openSession();
27329
27519
  } catch (err) {
27330
27520
  console.error(
27331
- `${TAG44} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`
27521
+ `${TAG45} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`
27332
27522
  );
27333
27523
  return;
27334
27524
  }
@@ -27336,7 +27526,7 @@ function startLedgerCensus(openSession, opts = {}) {
27336
27526
  await runLedgerCensusOnce(session, Date.now());
27337
27527
  } catch (err) {
27338
27528
  console.error(
27339
- `${TAG44} tick-failed error="${err instanceof Error ? err.message : String(err)}"`
27529
+ `${TAG45} tick-failed error="${err instanceof Error ? err.message : String(err)}"`
27340
27530
  );
27341
27531
  } finally {
27342
27532
  try {
@@ -27354,7 +27544,7 @@ function startLedgerCensus(openSession, opts = {}) {
27354
27544
  }
27355
27545
 
27356
27546
  // app/lib/conversation-audit.ts
27357
- var TAG45 = "[conversation-audit]";
27547
+ var TAG46 = "[conversation-audit]";
27358
27548
  async function runConversationAudit() {
27359
27549
  const session = getSession();
27360
27550
  try {
@@ -27391,12 +27581,12 @@ async function runConversationAudit() {
27391
27581
  multiAnchored: Number(multiRec?.get("multiAnchored") ?? 0)
27392
27582
  };
27393
27583
  console.error(
27394
- `${TAG45} channel=whatsapp total=${result.total} senders=${result.senders} multiAnchored=${result.multiAnchored} unreconciled=${result.unreconciled} unpairable=${result.unpairable}`
27584
+ `${TAG46} channel=whatsapp total=${result.total} senders=${result.senders} multiAnchored=${result.multiAnchored} unreconciled=${result.unreconciled} unpairable=${result.unpairable}`
27395
27585
  );
27396
27586
  return result;
27397
27587
  } catch (err) {
27398
27588
  const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
27399
- console.error(`${TAG45} op=failed reason=${reason}`);
27589
+ console.error(`${TAG46} op=failed reason=${reason}`);
27400
27590
  return null;
27401
27591
  } finally {
27402
27592
  await session.close();
@@ -28290,8 +28480,8 @@ var streamSSE = (c, cb, onError) => {
28290
28480
 
28291
28481
  // app/lib/whatsapp/gateway/routes.ts
28292
28482
  function createWaChannelRoutes(deps) {
28293
- const app76 = new Hono();
28294
- app76.get("/wa-channel/inbound", (c) => {
28483
+ const app77 = new Hono();
28484
+ app77.get("/wa-channel/inbound", (c) => {
28295
28485
  const senderId = c.req.query("senderId");
28296
28486
  if (!senderId) return c.json({ error: "senderId required" }, 400);
28297
28487
  return streamSSE(c, async (stream2) => {
@@ -28309,7 +28499,7 @@ function createWaChannelRoutes(deps) {
28309
28499
  }
28310
28500
  });
28311
28501
  });
28312
- app76.post("/wa-channel/reply", async (c) => {
28502
+ app77.post("/wa-channel/reply", async (c) => {
28313
28503
  const body = await c.req.json().catch(() => null);
28314
28504
  const senderId = body?.senderId;
28315
28505
  const text = body?.text;
@@ -28331,7 +28521,7 @@ function createWaChannelRoutes(deps) {
28331
28521
  deps.onReplyDispatch?.(senderId, Date.now());
28332
28522
  return c.json({ ok: true });
28333
28523
  });
28334
- app76.post("/wa-channel/reply-document", async (c) => {
28524
+ app77.post("/wa-channel/reply-document", async (c) => {
28335
28525
  const body = await c.req.json().catch(() => null);
28336
28526
  const senderId = body?.senderId;
28337
28527
  const files = body?.files;
@@ -28370,7 +28560,7 @@ function createWaChannelRoutes(deps) {
28370
28560
  }
28371
28561
  return c.json({ ok: true, results });
28372
28562
  });
28373
- app76.post("/wa-channel/ready", async (c) => {
28563
+ app77.post("/wa-channel/ready", async (c) => {
28374
28564
  const body = await c.req.json().catch(() => null);
28375
28565
  const senderId = body?.senderId;
28376
28566
  if (typeof senderId !== "string") {
@@ -28379,7 +28569,7 @@ function createWaChannelRoutes(deps) {
28379
28569
  deps.onReady?.(senderId);
28380
28570
  return c.json({ ok: true });
28381
28571
  });
28382
- app76.post("/wa-channel/received", async (c) => {
28572
+ app77.post("/wa-channel/received", async (c) => {
28383
28573
  const body = await c.req.json().catch(() => null);
28384
28574
  const senderId = body?.senderId;
28385
28575
  const waMessageId = body?.waMessageId;
@@ -28389,7 +28579,7 @@ function createWaChannelRoutes(deps) {
28389
28579
  deps.onReceived?.(senderId, waMessageId);
28390
28580
  return c.json({ ok: true });
28391
28581
  });
28392
- app76.post("/wa-channel/turn-end", async (c) => {
28582
+ app77.post("/wa-channel/turn-end", async (c) => {
28393
28583
  const body = await c.req.json().catch(() => null);
28394
28584
  const senderId = body?.senderId;
28395
28585
  const sessionId = body?.sessionId;
@@ -28435,7 +28625,7 @@ function createWaChannelRoutes(deps) {
28435
28625
  console.error(`[whatsapp-native] op=turn-end ${decision} delivered=none`);
28436
28626
  return c.json({ ok: true, delivered: "none" });
28437
28627
  });
28438
- return app76;
28628
+ return app77;
28439
28629
  }
28440
28630
 
28441
28631
  // app/lib/whatsapp/gateway/wa-gateway.ts
@@ -28858,7 +29048,7 @@ function formatPublicAgentRouteLine(p) {
28858
29048
  // app/lib/whatsapp/inbound/public-agent-registry-audit.ts
28859
29049
  function auditPublicAgentRegistry(deps) {
28860
29050
  const accounts = deps.enabledAccounts();
28861
- const unresolvable = accounts.filter((a) => a.slug === null || !deps.agentExists(a.slug));
29051
+ const unresolvable = accounts.filter((a) => a.slug === null || !deps.agentExists(a.accountId, a.slug));
28862
29052
  return { checked: accounts.length, unresolvable };
28863
29053
  }
28864
29054
  function runPublicAgentRegistryAudit(deps) {
@@ -28873,6 +29063,22 @@ function runPublicAgentRegistryAudit(deps) {
28873
29063
  );
28874
29064
  }
28875
29065
 
29066
+ // app/lib/whatsapp/inbound/public-agent-slice-audit.ts
29067
+ function auditPublicAgentSlices(deps) {
29068
+ const accounts = deps.enabledAccounts();
29069
+ const unconfigured = accounts.filter((a) => !a.house && a.ownSlug === null).map((a) => ({ accountId: a.accountId }));
29070
+ return { checked: accounts.length, unconfigured };
29071
+ }
29072
+ function runPublicAgentSliceAudit(deps) {
29073
+ const result = auditPublicAgentSlices(deps);
29074
+ for (const row of result.unconfigured) {
29075
+ console.error(`[whatsapp:public-agent] op=slice-unconfigured account=${row.accountId}`);
29076
+ }
29077
+ console.error(
29078
+ `[whatsapp:public-agent] op=slice-audit checked=${result.checked} unconfigured=${result.unconfigured.length}`
29079
+ );
29080
+ }
29081
+
28876
29082
  // app/lib/webchat/gateway/inbound-hub.ts
28877
29083
  var InboundHub2 = class {
28878
29084
  keys = /* @__PURE__ */ new Map();
@@ -29047,8 +29253,8 @@ var InboundHub2 = class {
29047
29253
 
29048
29254
  // app/lib/webchat/gateway/routes.ts
29049
29255
  function createWebchatChannelRoutes(deps) {
29050
- const app76 = new Hono();
29051
- app76.get("/webchat-channel/inbound", (c) => {
29256
+ const app77 = new Hono();
29257
+ app77.get("/webchat-channel/inbound", (c) => {
29052
29258
  const key = c.req.query("key");
29053
29259
  if (!key) return c.json({ error: "key required" }, 400);
29054
29260
  const role = c.req.query("role");
@@ -29073,7 +29279,7 @@ function createWebchatChannelRoutes(deps) {
29073
29279
  }
29074
29280
  });
29075
29281
  });
29076
- app76.post("/webchat-channel/reply", async (c) => {
29282
+ app77.post("/webchat-channel/reply", async (c) => {
29077
29283
  const body = await c.req.json().catch(() => null);
29078
29284
  const key = body?.key;
29079
29285
  const text = body?.text;
@@ -29083,7 +29289,7 @@ function createWebchatChannelRoutes(deps) {
29083
29289
  deps.onReply?.(key, text);
29084
29290
  return c.json({ ok: true });
29085
29291
  });
29086
- app76.post("/webchat-channel/connector-auth", async (c) => {
29292
+ app77.post("/webchat-channel/connector-auth", async (c) => {
29087
29293
  const body = await c.req.json().catch(() => null);
29088
29294
  const key = body?.key;
29089
29295
  const sessionId = body?.sessionId;
@@ -29096,7 +29302,7 @@ function createWebchatChannelRoutes(deps) {
29096
29302
  const result = await deps.onConnectorAuth({ key, sessionId, name, completed });
29097
29303
  return c.json(result);
29098
29304
  });
29099
- app76.post("/webchat-channel/ready", async (c) => {
29305
+ app77.post("/webchat-channel/ready", async (c) => {
29100
29306
  const body = await c.req.json().catch(() => null);
29101
29307
  const key = body?.key;
29102
29308
  if (typeof key !== "string") {
@@ -29105,7 +29311,7 @@ function createWebchatChannelRoutes(deps) {
29105
29311
  deps.onReady?.(key);
29106
29312
  return c.json({ ok: true });
29107
29313
  });
29108
- app76.post("/webchat-channel/permission-request", async (c) => {
29314
+ app77.post("/webchat-channel/permission-request", async (c) => {
29109
29315
  const body = await c.req.json().catch(() => null);
29110
29316
  const key = body?.key;
29111
29317
  const requestId = body?.request_id;
@@ -29119,7 +29325,7 @@ function createWebchatChannelRoutes(deps) {
29119
29325
  const verdict = await deps.awaitPermissionVerdict({ key, requestId, toolName, description, inputPreview });
29120
29326
  return c.json(verdict);
29121
29327
  });
29122
- app76.post("/webchat-channel/received", async (c) => {
29328
+ app77.post("/webchat-channel/received", async (c) => {
29123
29329
  const body = await c.req.json().catch(() => null);
29124
29330
  const key = body?.key;
29125
29331
  const messageId = body?.messageId;
@@ -29129,7 +29335,7 @@ function createWebchatChannelRoutes(deps) {
29129
29335
  deps.onReceived?.(key, messageId);
29130
29336
  return c.json({ ok: true });
29131
29337
  });
29132
- app76.post("/webchat-channel/turn-end", async (c) => {
29338
+ app77.post("/webchat-channel/turn-end", async (c) => {
29133
29339
  const body = await c.req.json().catch(() => null);
29134
29340
  const key = body?.key;
29135
29341
  const sessionId = body?.sessionId;
@@ -29149,7 +29355,7 @@ function createWebchatChannelRoutes(deps) {
29149
29355
  deps.onTurnEnd?.(key, role);
29150
29356
  return c.json({ ok: true });
29151
29357
  });
29152
- return app76;
29358
+ return app77;
29153
29359
  }
29154
29360
 
29155
29361
  // app/lib/webchat/gateway/webchat-gateway.ts
@@ -29908,14 +30114,14 @@ function makeFileDelivery(opts) {
29908
30114
  }
29909
30115
 
29910
30116
  // app/lib/webchat/file-delivery.ts
29911
- var TAG46 = "[webchat-adaptor]";
30117
+ var TAG47 = "[webchat-adaptor]";
29912
30118
  function platformRoot2() {
29913
30119
  return process.env.MAXY_PLATFORM_ROOT || "";
29914
30120
  }
29915
30121
  function makeWebchatSendFile(entry) {
29916
30122
  return async (filePath) => {
29917
30123
  if (!entry.accountId) {
29918
- console.error(`${TAG46} file-delivery reject reason=no-account sender=${entry.senderId}`);
30124
+ console.error(`${TAG47} file-delivery reject reason=no-account sender=${entry.senderId}`);
29919
30125
  return { ok: false, error: "no-account" };
29920
30126
  }
29921
30127
  const accountDir = resolve41(platformRoot2(), "..", "data/accounts", entry.accountId);
@@ -29923,14 +30129,14 @@ function makeWebchatSendFile(entry) {
29923
30129
  const resolved = realpathSync9(filePath);
29924
30130
  const accountResolved = realpathSync9(accountDir);
29925
30131
  if (!resolved.startsWith(accountResolved + "/")) {
29926
- console.error(`${TAG46} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
30132
+ console.error(`${TAG47} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
29927
30133
  return { ok: false, error: "outside-account" };
29928
30134
  }
29929
30135
  return { ok: true };
29930
30136
  } catch (err) {
29931
30137
  const code = err.code;
29932
30138
  console.error(
29933
- `${TAG46} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
30139
+ `${TAG47} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
29934
30140
  );
29935
30141
  return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
29936
30142
  }
@@ -29939,7 +30145,7 @@ function makeWebchatSendFile(entry) {
29939
30145
  function makeWebchatFileDelivery(entry) {
29940
30146
  return makeFileDelivery({
29941
30147
  entry,
29942
- tag: TAG46,
30148
+ tag: TAG47,
29943
30149
  channel: "webchat",
29944
30150
  sendFile: makeWebchatSendFile(entry),
29945
30151
  deferUntilVerdict: true
@@ -30001,7 +30207,7 @@ function buildPublicWebchatSpawnRequest(input) {
30001
30207
  }
30002
30208
 
30003
30209
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
30004
- var TAG47 = "[whatsapp-adaptor]";
30210
+ var TAG48 = "[whatsapp-adaptor]";
30005
30211
  var SEND_USER_FILE2 = "SendUserFile";
30006
30212
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
30007
30213
  function platformRoot3() {
@@ -30019,7 +30225,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
30019
30225
  });
30020
30226
  if (result.ok) return { ok: true };
30021
30227
  console.error(
30022
- `${TAG47} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
30228
+ `${TAG48} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
30023
30229
  );
30024
30230
  return { ok: false, error: result.error };
30025
30231
  };
@@ -30027,7 +30233,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
30027
30233
  function makeWhatsAppFileDelivery(entry, maxyAccountId) {
30028
30234
  const shared = makeFileDelivery({
30029
30235
  entry,
30030
- tag: TAG47,
30236
+ tag: TAG48,
30031
30237
  channel: "whatsapp",
30032
30238
  sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
30033
30239
  });
@@ -30062,7 +30268,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
30062
30268
  if (!delivered) {
30063
30269
  const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
30064
30270
  console.error(
30065
- `${TAG47} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
30271
+ `${TAG48} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
30066
30272
  );
30067
30273
  }
30068
30274
  }
@@ -30245,8 +30451,8 @@ var InboundHub3 = class {
30245
30451
 
30246
30452
  // app/lib/telegram/gateway/routes.ts
30247
30453
  function createTelegramChannelRoutes(deps) {
30248
- const app76 = new Hono();
30249
- app76.get("/tg-channel/inbound", (c) => {
30454
+ const app77 = new Hono();
30455
+ app77.get("/tg-channel/inbound", (c) => {
30250
30456
  const key = c.req.query("key");
30251
30457
  if (!key) return c.json({ error: "key required" }, 400);
30252
30458
  return streamSSE(c, async (stream2) => {
@@ -30264,7 +30470,7 @@ function createTelegramChannelRoutes(deps) {
30264
30470
  }
30265
30471
  });
30266
30472
  });
30267
- app76.post("/tg-channel/reply", async (c) => {
30473
+ app77.post("/tg-channel/reply", async (c) => {
30268
30474
  const body = await c.req.json().catch(() => null);
30269
30475
  const key = body?.key;
30270
30476
  const text = body?.text;
@@ -30280,7 +30486,7 @@ function createTelegramChannelRoutes(deps) {
30280
30486
  console.error(`[telegram-native] op=reply key=${key} bytes=${Buffer.byteLength(text, "utf8")}`);
30281
30487
  return c.json({ ok: true });
30282
30488
  });
30283
- app76.post("/tg-channel/ready", async (c) => {
30489
+ app77.post("/tg-channel/ready", async (c) => {
30284
30490
  const body = await c.req.json().catch(() => null);
30285
30491
  const key = body?.key;
30286
30492
  if (typeof key !== "string") {
@@ -30289,7 +30495,7 @@ function createTelegramChannelRoutes(deps) {
30289
30495
  deps.onReady?.(key);
30290
30496
  return c.json({ ok: true });
30291
30497
  });
30292
- app76.post("/tg-channel/received", async (c) => {
30498
+ app77.post("/tg-channel/received", async (c) => {
30293
30499
  const body = await c.req.json().catch(() => null);
30294
30500
  const key = body?.key;
30295
30501
  const messageId = body?.messageId;
@@ -30299,7 +30505,7 @@ function createTelegramChannelRoutes(deps) {
30299
30505
  deps.onReceived?.(key, messageId);
30300
30506
  return c.json({ ok: true });
30301
30507
  });
30302
- app76.post("/tg-channel/turn-end", async (c) => {
30508
+ app77.post("/tg-channel/turn-end", async (c) => {
30303
30509
  const body = await c.req.json().catch(() => null);
30304
30510
  const key = body?.key;
30305
30511
  const sessionId = body?.sessionId;
@@ -30332,7 +30538,7 @@ function createTelegramChannelRoutes(deps) {
30332
30538
  console.error(`[telegram-native] op=turn-end key=${key} sessionId=${sid} replied=no delivered=none`);
30333
30539
  return c.json({ ok: true, delivered: "none" });
30334
30540
  });
30335
- return app76;
30541
+ return app77;
30336
30542
  }
30337
30543
 
30338
30544
  // app/lib/telegram/gateway/telegram-gateway.ts
@@ -30483,7 +30689,7 @@ function buildTelegramSpawnRequest(input) {
30483
30689
  import { realpathSync as realpathSync10 } from "fs";
30484
30690
  import { readFile as readFile14, stat as stat10 } from "fs/promises";
30485
30691
  import { resolve as resolve42, basename as basename16 } from "path";
30486
- var TAG48 = "[telegram:outbound]";
30692
+ var TAG49 = "[telegram:outbound]";
30487
30693
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
30488
30694
  async function sendTelegramDocument(input) {
30489
30695
  const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
@@ -30500,10 +30706,10 @@ async function sendTelegramDocument(input) {
30500
30706
  } catch (err) {
30501
30707
  const code = err.code;
30502
30708
  if (code === "ENOENT") {
30503
- console.error(`${TAG48} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
30709
+ console.error(`${TAG49} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
30504
30710
  return { ok: false, status: 500, error: `Account directory not resolvable for ${maxyAccountId}` };
30505
30711
  }
30506
- console.error(`${TAG48} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
30712
+ console.error(`${TAG49} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
30507
30713
  return { ok: false, status: 500, error: String(err) };
30508
30714
  }
30509
30715
  let resolvedPath;
@@ -30512,14 +30718,14 @@ async function sendTelegramDocument(input) {
30512
30718
  } catch (err) {
30513
30719
  const code = err.code;
30514
30720
  if (code === "ENOENT") {
30515
- console.error(`${TAG48} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
30721
+ console.error(`${TAG49} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
30516
30722
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
30517
30723
  }
30518
- console.error(`${TAG48} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
30724
+ console.error(`${TAG49} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
30519
30725
  return { ok: false, status: 500, error: String(err) };
30520
30726
  }
30521
30727
  if (!resolvedPath.startsWith(accountResolved + "/")) {
30522
- console.error(`${TAG48} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
30728
+ console.error(`${TAG49} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
30523
30729
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
30524
30730
  }
30525
30731
  const fileStat = await stat10(resolvedPath);
@@ -30552,13 +30758,13 @@ async function sendTelegramDocument(input) {
30552
30758
  error = e instanceof Error ? e.message : String(e);
30553
30759
  }
30554
30760
  console.error(
30555
- `${TAG48} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
30761
+ `${TAG49} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
30556
30762
  );
30557
30763
  return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
30558
30764
  }
30559
30765
 
30560
30766
  // app/lib/telegram/outbound/file-delivery.ts
30561
- var TAG49 = "[telegram:outbound]";
30767
+ var TAG50 = "[telegram:outbound]";
30562
30768
  function platformRoot4() {
30563
30769
  return process.env.MAXY_PLATFORM_ROOT || "";
30564
30770
  }
@@ -30570,11 +30776,11 @@ function makeTelegramSendFile(entry) {
30570
30776
  botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
30571
30777
  }
30572
30778
  if (!botToken) {
30573
- console.error(`${TAG49} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
30779
+ console.error(`${TAG50} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
30574
30780
  return { ok: false, error: "no-bot-token" };
30575
30781
  }
30576
30782
  if (entry.replyTarget == null) {
30577
- console.error(`${TAG49} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
30783
+ console.error(`${TAG50} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
30578
30784
  return { ok: false, error: "no-reply-target" };
30579
30785
  }
30580
30786
  const result = await sendTelegramDocument({
@@ -30591,7 +30797,7 @@ function makeTelegramSendFile(entry) {
30591
30797
  function makeTelegramFileDelivery(entry) {
30592
30798
  return makeFileDelivery({
30593
30799
  entry,
30594
- tag: TAG49,
30800
+ tag: TAG50,
30595
30801
  channel: "telegram",
30596
30802
  sendFile: makeTelegramSendFile(entry)
30597
30803
  });
@@ -30631,8 +30837,8 @@ function startTelegramNativeFileFollower(input) {
30631
30837
  }
30632
30838
 
30633
30839
  // app/lib/channel-pty-bridge/public-session-end-review.ts
30634
- import { randomUUID as randomUUID13 } from "crypto";
30635
- var TAG50 = "[public-session-review]";
30840
+ import { randomUUID as randomUUID14 } from "crypto";
30841
+ var TAG51 = "[public-session-review]";
30636
30842
  var CONSUMED_CAP = 500;
30637
30843
  var consumed = /* @__PURE__ */ new Set();
30638
30844
  function consumeOnce(sessionId) {
@@ -30659,7 +30865,7 @@ async function fetchJsonl(sessionId) {
30659
30865
  return await res.text();
30660
30866
  } catch (err) {
30661
30867
  console.error(
30662
- `${TAG50} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
30868
+ `${TAG51} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
30663
30869
  );
30664
30870
  return null;
30665
30871
  }
@@ -30683,7 +30889,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
30683
30889
  const res = await fetch(url);
30684
30890
  if (!res.ok) {
30685
30891
  console.error(
30686
- `${TAG50} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
30892
+ `${TAG51} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
30687
30893
  );
30688
30894
  return [];
30689
30895
  }
@@ -30691,7 +30897,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
30691
30897
  return Array.isArray(body.writes) ? body.writes : [];
30692
30898
  } catch (err) {
30693
30899
  console.error(
30694
- `${TAG50} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
30900
+ `${TAG51} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
30695
30901
  );
30696
30902
  return [];
30697
30903
  }
@@ -30719,8 +30925,8 @@ function composeInitialMessage(input) {
30719
30925
  ].join("\n");
30720
30926
  }
30721
30927
  async function dispatchReviewer(input, initialMessage) {
30722
- const sessionId = randomUUID13();
30723
- console.log(`${TAG50} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
30928
+ const sessionId = randomUUID14();
30929
+ console.log(`${TAG51} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
30724
30930
  const spawned = await managerRcSpawn({
30725
30931
  sessionId,
30726
30932
  initialMessage,
@@ -30740,21 +30946,21 @@ async function firePublicSessionEndReview(input) {
30740
30946
  const dispatchedAt = Date.now();
30741
30947
  if (consumed.has(input.sessionId)) {
30742
30948
  console.log(
30743
- `${TAG50} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
30949
+ `${TAG51} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
30744
30950
  );
30745
30951
  return;
30746
30952
  }
30747
30953
  const jsonl = await fetchJsonl(input.sessionId);
30748
30954
  if (!jsonl) {
30749
30955
  console.log(
30750
- `${TAG50} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
30956
+ `${TAG51} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
30751
30957
  );
30752
30958
  return;
30753
30959
  }
30754
30960
  const operatorTurns = countOperatorTurns(jsonl);
30755
30961
  if (operatorTurns === 0) {
30756
30962
  console.log(
30757
- `${TAG50} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
30963
+ `${TAG51} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
30758
30964
  );
30759
30965
  return;
30760
30966
  }
@@ -30768,19 +30974,19 @@ async function firePublicSessionEndReview(input) {
30768
30974
  });
30769
30975
  if (!consumeOnce(input.sessionId)) {
30770
30976
  console.log(
30771
- `${TAG50} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
30977
+ `${TAG51} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
30772
30978
  );
30773
30979
  return;
30774
30980
  }
30775
30981
  const dispatched = await dispatchReviewer(input, initialMessage);
30776
30982
  if (!dispatched.ok) {
30777
30983
  console.error(
30778
- `${TAG50} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
30984
+ `${TAG51} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
30779
30985
  );
30780
30986
  return;
30781
30987
  }
30782
30988
  console.log(
30783
- `${TAG50} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
30989
+ `${TAG51} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
30784
30990
  );
30785
30991
  }
30786
30992
 
@@ -31237,7 +31443,7 @@ startManagerEventsSubscriber();
31237
31443
  function isPublicHost(host) {
31238
31444
  return host.startsWith("public.") || aliasDomains.has(host);
31239
31445
  }
31240
- var app75 = new Hono();
31446
+ var app76 = new Hono();
31241
31447
  function resolveChannelOverride(accountId, channel, senderId) {
31242
31448
  try {
31243
31449
  const dir = listValidAccounts().find((a) => a.accountId === accountId)?.accountDir;
@@ -31373,6 +31579,11 @@ var waGateway = new WaGateway({
31373
31579
  channel: "whatsapp",
31374
31580
  accountId: effectiveAccountId,
31375
31581
  targetAccountId: effectiveAccountId,
31582
+ // Task 2271 — this slug came from THIS account's own slice
31583
+ // (`resolvePublicAgent` with `isHouse` false), so the spawn must read
31584
+ // that account's own agents directory, run in its directory and file its
31585
+ // session under it.
31586
+ agentAccountId: effectiveAccountId,
31376
31587
  agentSlug: publicAgentSlug,
31377
31588
  sessionId: publicSessionId,
31378
31589
  personId: personId ?? void 0,
@@ -31441,7 +31652,7 @@ var waGateway = new WaGateway({
31441
31652
  nativeFileFollowers.set(senderId, ac);
31442
31653
  }
31443
31654
  });
31444
- app75.route("/", waGateway.routes());
31655
+ app76.route("/", waGateway.routes());
31445
31656
  waGateway.startSweeper();
31446
31657
  registerLoop({
31447
31658
  name: "whatsapp-link-census",
@@ -31578,7 +31789,7 @@ var webchatGateway = new WebchatGateway({
31578
31789
  firePublicSessionEndReview: (input) => firePublicSessionEndReview(input)
31579
31790
  });
31580
31791
  setWebchatGateway(webchatGateway);
31581
- app75.route("/", webchatGateway.routes());
31792
+ app76.route("/", webchatGateway.routes());
31582
31793
  webchatGateway.startSweeper();
31583
31794
  webchatGateway.startPublicReaper();
31584
31795
  var telegramFileFollowers = /* @__PURE__ */ new Map();
@@ -31622,7 +31833,7 @@ var telegramGateway = new TelegramGateway({
31622
31833
  }
31623
31834
  });
31624
31835
  setTelegramGateway(telegramGateway);
31625
- app75.route("/", telegramGateway.routes());
31836
+ app76.route("/", telegramGateway.routes());
31626
31837
  telegramGateway.startSweeper();
31627
31838
  var chatRoutes = createChatRoutes({
31628
31839
  handleInbound: (input) => webchatGateway.handleInbound(input),
@@ -31661,19 +31872,19 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
31661
31872
  // and why the real dependencies are bound there rather than assembled here.
31662
31873
  effectiveAccountFor: resolveScheduledEffectiveAccountForInstall
31663
31874
  });
31664
- app75.route("/api/channel/schedule-inject", scheduleInjectRoutes);
31665
- app75.use("*", clientIpMiddleware);
31875
+ app76.route("/api/channel/schedule-inject", scheduleInjectRoutes);
31876
+ app76.use("*", clientIpMiddleware);
31666
31877
  function allowsSameOriginFraming(path) {
31667
31878
  return path === "/vnc-viewer.html" || path.startsWith("/api/admin/attachment/") || path.startsWith("/api/public-reader/attachment/") || path === "/api/admin/files/download" || path === "/api/admin/session-upload";
31668
31879
  }
31669
- app75.use("*", async (c, next) => {
31880
+ app76.use("*", async (c, next) => {
31670
31881
  await next();
31671
31882
  c.header("X-Content-Type-Options", "nosniff");
31672
31883
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
31673
31884
  c.header("X-Frame-Options", allowsSameOriginFraming(c.req.path) ? "SAMEORIGIN" : "DENY");
31674
31885
  });
31675
31886
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
31676
- app75.use("*", async (c, next) => {
31887
+ app76.use("*", async (c, next) => {
31677
31888
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
31678
31889
  await next();
31679
31890
  return;
@@ -31691,7 +31902,7 @@ app75.use("*", async (c, next) => {
31691
31902
  });
31692
31903
  }
31693
31904
  });
31694
- app75.use("*", async (c, next) => {
31905
+ app76.use("*", async (c, next) => {
31695
31906
  const host = (c.req.header("host") ?? "").split(":")[0];
31696
31907
  if (isOperatorHost(host, getOperatorDomains()) || !isPublicHost(host)) {
31697
31908
  await next();
@@ -31729,7 +31940,7 @@ function resolveRemoteAuthOpts(c) {
31729
31940
  return { ...brandLoginOpts, origin };
31730
31941
  }
31731
31942
  var MAX_LOGIN_BODY = 8 * 1024;
31732
- app75.post("/__remote-auth/login", async (c) => {
31943
+ app76.post("/__remote-auth/login", async (c) => {
31733
31944
  const client = clientFrom(c);
31734
31945
  const clientIp = client.ip || "unknown";
31735
31946
  if (!requestIsTlsTerminated(c)) {
@@ -31774,7 +31985,7 @@ app75.post("/__remote-auth/login", async (c) => {
31774
31985
  }
31775
31986
  });
31776
31987
  });
31777
- app75.get("/__remote-auth/logout", (c) => {
31988
+ app76.get("/__remote-auth/logout", (c) => {
31778
31989
  const client = clientFrom(c);
31779
31990
  const clientIp = client.ip || "unknown";
31780
31991
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -31787,7 +31998,7 @@ app75.get("/__remote-auth/logout", (c) => {
31787
31998
  }
31788
31999
  });
31789
32000
  });
31790
- app75.post("/__remote-auth/change-password", async (c) => {
32001
+ app76.post("/__remote-auth/change-password", async (c) => {
31791
32002
  const client = clientFrom(c);
31792
32003
  const clientIp = client.ip || "unknown";
31793
32004
  const rateLimited = checkRateLimit(client);
@@ -31846,13 +32057,13 @@ app75.post("/__remote-auth/change-password", async (c) => {
31846
32057
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "change", changeError: "Failed to save password", redirect }), 200);
31847
32058
  }
31848
32059
  });
31849
- app75.get("/__remote-auth/setup", (c) => {
32060
+ app76.get("/__remote-auth/setup", (c) => {
31850
32061
  if (isRemoteAuthConfigured()) {
31851
32062
  return c.redirect("/");
31852
32063
  }
31853
32064
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup" }), 200);
31854
32065
  });
31855
- app75.post("/__remote-auth/set-initial-password", async (c) => {
32066
+ app76.post("/__remote-auth/set-initial-password", async (c) => {
31856
32067
  if (isRemoteAuthConfigured()) {
31857
32068
  return c.redirect("/");
31858
32069
  }
@@ -31890,10 +32101,10 @@ app75.post("/__remote-auth/set-initial-password", async (c) => {
31890
32101
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
31891
32102
  }
31892
32103
  });
31893
- app75.get("/api/remote-auth/status", (c) => {
32104
+ app76.get("/api/remote-auth/status", (c) => {
31894
32105
  return c.json({ configured: isRemoteAuthConfigured() });
31895
32106
  });
31896
- app75.post("/api/remote-auth/set-password", async (c) => {
32107
+ app76.post("/api/remote-auth/set-password", async (c) => {
31897
32108
  let body;
31898
32109
  try {
31899
32110
  body = await c.req.json();
@@ -31932,10 +32143,10 @@ app75.post("/api/remote-auth/set-password", async (c) => {
31932
32143
  return c.json({ error: "Failed to save password" }, 500);
31933
32144
  }
31934
32145
  });
31935
- app75.route("/api/_client-error", client_error_default);
32146
+ app76.route("/api/_client-error", client_error_default);
31936
32147
  console.log("[client-error-route] mounted");
31937
32148
  var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
31938
- app75.use("*", async (c, next) => {
32149
+ app76.use("*", async (c, next) => {
31939
32150
  const host = (c.req.header("host") ?? "").split(":")[0];
31940
32151
  const path = c.req.path;
31941
32152
  if (path === "/favicon.ico" || path.startsWith("/assets/") || path.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
@@ -31979,30 +32190,30 @@ app75.use("*", async (c, next) => {
31979
32190
  }
31980
32191
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path }), 200);
31981
32192
  });
31982
- app75.route("/api/health", health_default);
31983
- app75.route("/api/chat", chatRoutes);
31984
- app75.route("/api/whatsapp", whatsapp_default);
31985
- app75.route("/api/storage", storage_broker_default);
31986
- app75.route("/api/whatsapp-reader", whatsapp_reader_default);
31987
- app75.route("/api/session-reader", session_reader_default);
31988
- app75.route("/api/operator-conversations", operator_conversations_default);
31989
- app75.route("/api/public-reader", public_reader_default);
31990
- app75.route("/api/webchat", createWebchatRoutes({
32193
+ app76.route("/api/health", health_default);
32194
+ app76.route("/api/chat", chatRoutes);
32195
+ app76.route("/api/whatsapp", whatsapp_default);
32196
+ app76.route("/api/storage", storage_broker_default);
32197
+ app76.route("/api/whatsapp-reader", whatsapp_reader_default);
32198
+ app76.route("/api/session-reader", session_reader_default);
32199
+ app76.route("/api/operator-conversations", operator_conversations_default);
32200
+ app76.route("/api/public-reader", public_reader_default);
32201
+ app76.route("/api/webchat", createWebchatRoutes({
31991
32202
  handleInbound: (input) => webchatGateway.handleInbound(input),
31992
32203
  // Task 940 — the permission relay's pointer read + verdict write.
31993
32204
  pendingPromptFor: (key) => webchatGateway.pendingPromptFor(key),
31994
32205
  deliveryFailureFor: (key) => webchatGateway.deliveryFailureFor(key),
31995
32206
  resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
31996
32207
  }));
31997
- app75.route("/api/webchat/greeting", webchat_greeting_default);
31998
- app75.route("/api/telegram", telegram_default);
31999
- app75.route("/api/quickbooks", quickbooks_default);
32000
- app75.route("/api/onboarding", onboarding_default);
32001
- app75.route("/api/admin", admin_default);
32002
- app75.route("/api/access", access_default);
32003
- app75.route("/api/session", session_default2);
32004
- app75.route("/api/calendar", calendar_public_default);
32005
- app75.route("/api/portal", portal_fetch_default);
32208
+ app76.route("/api/webchat/greeting", webchat_greeting_default);
32209
+ app76.route("/api/telegram", telegram_default);
32210
+ app76.route("/api/quickbooks", quickbooks_default);
32211
+ app76.route("/api/onboarding", onboarding_default);
32212
+ app76.route("/api/admin", admin_default);
32213
+ app76.route("/api/access", access_default);
32214
+ app76.route("/api/session", session_default2);
32215
+ app76.route("/api/calendar", calendar_public_default);
32216
+ app76.route("/api/portal", portal_fetch_default);
32006
32217
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
32007
32218
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
32008
32219
  var IMAGE_MIME = {
@@ -32014,7 +32225,7 @@ var IMAGE_MIME = {
32014
32225
  ".svg": "image/svg+xml",
32015
32226
  ".ico": "image/x-icon"
32016
32227
  };
32017
- app75.get("/agent-assets/:slug/:filename", (c) => {
32228
+ app76.get("/agent-assets/:slug/:filename", (c) => {
32018
32229
  const slug = c.req.param("slug");
32019
32230
  const filename = c.req.param("filename");
32020
32231
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -32049,7 +32260,7 @@ app75.get("/agent-assets/:slug/:filename", (c) => {
32049
32260
  "Cache-Control": "public, max-age=3600"
32050
32261
  });
32051
32262
  });
32052
- app75.get("/generated/:filename", (c) => {
32263
+ app76.get("/generated/:filename", (c) => {
32053
32264
  const filename = c.req.param("filename");
32054
32265
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
32055
32266
  console.error(`[generated] serve file=${filename} status=403`);
@@ -32079,10 +32290,10 @@ app75.get("/generated/:filename", (c) => {
32079
32290
  "Cache-Control": "public, max-age=86400"
32080
32291
  });
32081
32292
  });
32082
- app75.route("/sites", sites_default);
32083
- app75.route("/listings", listings_default);
32084
- app75.route("/v", visitor_event_default);
32085
- app75.route("/v", visitor_consent_default);
32293
+ app76.route("/sites", sites_default);
32294
+ app76.route("/listings", listings_default);
32295
+ app76.route("/v", visitor_event_default);
32296
+ app76.route("/v", visitor_consent_default);
32086
32297
  var htmlCache = /* @__PURE__ */ new Map();
32087
32298
  var brandLogoPath = "/brand/maxy-monochrome.png";
32088
32299
  var brandIconPath = "/brand/maxy-monochrome.png";
@@ -32207,7 +32418,7 @@ function brandedPublicHtml(agentSlug) {
32207
32418
  function agentUnavailableHtml() {
32208
32419
  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>`;
32209
32420
  }
32210
- app75.get("/", (c) => {
32421
+ app76.get("/", (c) => {
32211
32422
  const host = (c.req.header("host") ?? "").split(":")[0];
32212
32423
  const klass = classifyHost(host, getOperatorDomains(), isPublicHost);
32213
32424
  if (klass === "operator") {
@@ -32229,12 +32440,12 @@ app75.get("/", (c) => {
32229
32440
  console.log(`[host-class] host=${host} class=admin served=index.html`);
32230
32441
  return c.html(cachedHtml("index.html"));
32231
32442
  });
32232
- app75.get("/public", (c) => {
32443
+ app76.get("/public", (c) => {
32233
32444
  const host = (c.req.header("host") ?? "").split(":")[0];
32234
32445
  if (isPublicHost(host)) return c.text("Not found", 404);
32235
32446
  return c.html(cachedHtml("public.html"));
32236
32447
  });
32237
- app75.get("/public-chat", (c) => {
32448
+ app76.get("/public-chat", (c) => {
32238
32449
  const host = (c.req.header("host") ?? "").split(":")[0];
32239
32450
  if (isPublicHost(host)) return c.text("Not found", 404);
32240
32451
  return c.html(cachedHtml("public.html"));
@@ -32253,9 +32464,9 @@ async function logViewerFetch(c, next) {
32253
32464
  duration_ms: Date.now() - start
32254
32465
  });
32255
32466
  }
32256
- app75.use("/vnc-viewer.html", logViewerFetch);
32257
- app75.use("/vnc-popout.html", logViewerFetch);
32258
- app75.get("/vnc-popout.html", (c) => {
32467
+ app76.use("/vnc-viewer.html", logViewerFetch);
32468
+ app76.use("/vnc-popout.html", logViewerFetch);
32469
+ app76.get("/vnc-popout.html", (c) => {
32259
32470
  let html = htmlCache.get("vnc-popout.html");
32260
32471
  if (!html) {
32261
32472
  html = readFileSync47(resolve45(process.cwd(), "public", "vnc-popout.html"), "utf-8");
@@ -32268,7 +32479,7 @@ app75.get("/vnc-popout.html", (c) => {
32268
32479
  }
32269
32480
  return c.html(html);
32270
32481
  });
32271
- app75.post("/api/vnc/client-event", async (c) => {
32482
+ app76.post("/api/vnc/client-event", async (c) => {
32272
32483
  let body;
32273
32484
  try {
32274
32485
  body = await c.req.json();
@@ -32289,11 +32500,11 @@ app75.post("/api/vnc/client-event", async (c) => {
32289
32500
  });
32290
32501
  return c.json({ ok: true });
32291
32502
  });
32292
- app75.get("/g/:slug", (c) => {
32503
+ app76.get("/g/:slug", (c) => {
32293
32504
  return c.html(brandedPublicHtml(resolveDefaultSlug() ?? void 0));
32294
32505
  });
32295
32506
  for (const pwa of PWA_SURFACES) {
32296
- app75.get(pwa.manifestPath, (c) => {
32507
+ app76.get(pwa.manifestPath, (c) => {
32297
32508
  const manifest = buildManifest(pwa, {
32298
32509
  productName: BRAND.productName,
32299
32510
  appIcon192: brandAppIcon192Path,
@@ -32309,7 +32520,7 @@ for (const pwa of PWA_SURFACES) {
32309
32520
  return c.body(JSON.stringify(manifest));
32310
32521
  });
32311
32522
  }
32312
- app75.get("/sw.js", (c) => {
32523
+ app76.get("/sw.js", (c) => {
32313
32524
  if (SW_SOURCE == null) {
32314
32525
  console.error("[pwa] op=sw status=500 reason=sw-source-missing");
32315
32526
  return c.text("Service worker unavailable", 500);
@@ -32318,12 +32529,12 @@ app75.get("/sw.js", (c) => {
32318
32529
  console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
32319
32530
  return c.body(SW_SOURCE);
32320
32531
  });
32321
- app75.get("/graph", (c) => {
32532
+ app76.get("/graph", (c) => {
32322
32533
  const host = (c.req.header("host") ?? "").split(":")[0];
32323
32534
  if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
32324
32535
  return c.html(cachedHtml("graph.html"));
32325
32536
  });
32326
- app75.get("/chat", (c) => {
32537
+ app76.get("/chat", (c) => {
32327
32538
  const host = (c.req.header("host") ?? "").split(":")[0];
32328
32539
  if (isOperatorHost(host, getOperatorDomains())) {
32329
32540
  console.log(`[host-class] host=${host} class=operator served=operator.html`);
@@ -32332,47 +32543,47 @@ app75.get("/chat", (c) => {
32332
32543
  if (isPublicHost(host)) return c.text("Not found", 404);
32333
32544
  return c.html(cachedHtml("chat.html"));
32334
32545
  });
32335
- app75.get("/data", (c) => {
32546
+ app76.get("/data", (c) => {
32336
32547
  const host = (c.req.header("host") ?? "").split(":")[0];
32337
32548
  if (isPublicHost(host)) return c.text("Not found", 404);
32338
32549
  return c.html(cachedHtml("data.html"));
32339
32550
  });
32340
- app75.get("/tasks", (c) => {
32551
+ app76.get("/tasks", (c) => {
32341
32552
  const host = (c.req.header("host") ?? "").split(":")[0];
32342
32553
  if (isPublicHost(host)) return c.text("Not found", 404);
32343
32554
  return c.html(cachedHtml("tasks.html"));
32344
32555
  });
32345
- app75.get("/activity", (c) => {
32556
+ app76.get("/activity", (c) => {
32346
32557
  const host = (c.req.header("host") ?? "").split(":")[0];
32347
32558
  if (isPublicHost(host)) return c.text("Not found", 404);
32348
32559
  return c.html(cachedHtml("activity.html"));
32349
32560
  });
32350
- app75.get("/calendar", (c) => {
32561
+ app76.get("/calendar", (c) => {
32351
32562
  const host = (c.req.header("host") ?? "").split(":")[0];
32352
32563
  if (isPublicHost(host)) return c.text("Not found", 404);
32353
32564
  return c.html(cachedHtml("calendar.html"));
32354
32565
  });
32355
- app75.get("/routines", (c) => {
32566
+ app76.get("/routines", (c) => {
32356
32567
  const host = (c.req.header("host") ?? "").split(":")[0];
32357
32568
  if (isPublicHost(host)) return c.text("Not found", 404);
32358
32569
  return c.html(cachedHtml("routines.html"));
32359
32570
  });
32360
- app75.get("/skills", (c) => {
32571
+ app76.get("/skills", (c) => {
32361
32572
  const host = (c.req.header("host") ?? "").split(":")[0];
32362
32573
  if (isPublicHost(host)) return c.text("Not found", 404);
32363
32574
  return c.html(cachedHtml("skills.html"));
32364
32575
  });
32365
- app75.get("/agents", (c) => {
32576
+ app76.get("/agents", (c) => {
32366
32577
  const host = (c.req.header("host") ?? "").split(":")[0];
32367
32578
  if (isPublicHost(host)) return c.text("Not found", 404);
32368
32579
  return c.html(cachedHtml("agents.html"));
32369
32580
  });
32370
- app75.get("/browser", (c) => {
32581
+ app76.get("/browser", (c) => {
32371
32582
  const host = (c.req.header("host") ?? "").split(":")[0];
32372
32583
  if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
32373
32584
  return c.html(cachedHtml("browser.html"));
32374
32585
  });
32375
- app75.get("/:slug", async (c, next) => {
32586
+ app76.get("/:slug", async (c, next) => {
32376
32587
  const slug = c.req.param("slug");
32377
32588
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
32378
32589
  const account = resolveAccount();
@@ -32387,13 +32598,13 @@ app75.get("/:slug", async (c, next) => {
32387
32598
  await next();
32388
32599
  });
32389
32600
  if (brandFaviconPath !== "/favicon.ico") {
32390
- app75.get("/favicon.ico", (c) => {
32601
+ app76.get("/favicon.ico", (c) => {
32391
32602
  c.header("Cache-Control", "public, max-age=300");
32392
32603
  return c.redirect(brandFaviconPath, 302);
32393
32604
  });
32394
32605
  }
32395
- mountStaticFiles(app75, "./public");
32396
- app75.all("*", (c) => {
32606
+ mountStaticFiles(app76, "./public");
32607
+ app76.all("*", (c) => {
32397
32608
  const host = (c.req.header("host") ?? "").split(":")[0];
32398
32609
  const path = c.req.path;
32399
32610
  if (isPublicHost(host)) {
@@ -32407,7 +32618,7 @@ app75.all("*", (c) => {
32407
32618
  });
32408
32619
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
32409
32620
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
32410
- var httpServer = serve({ fetch: app75.fetch, port, hostname });
32621
+ var httpServer = serve({ fetch: app76.fetch, port, hostname });
32411
32622
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
32412
32623
  {
32413
32624
  const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join53(__dirname, "..");
@@ -32804,7 +33015,7 @@ for (const m of SUBAPP_MANIFEST) {
32804
33015
  }
32805
33016
  try {
32806
33017
  const registered = [];
32807
- for (const r of app75.routes ?? []) {
33018
+ for (const r of app76.routes ?? []) {
32808
33019
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
32809
33020
  if (AGENT_SLUG_PATTERN.test(r.path)) {
32810
33021
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -32880,6 +33091,7 @@ registerLoop({
32880
33091
  run: () => runAdminUserReconcileTick()
32881
33092
  });
32882
33093
  var INGEST_AUDIT_INTERVAL_MS = 15 * 60 * 1e3;
33094
+ var isHouseAccountKey = (houseAccountId, accountId) => accountId === houseAccountId || !isProvisionedAccount(ACCOUNTS_DIR, accountId);
32883
33095
  var runIngestAuditTick = () => {
32884
33096
  try {
32885
33097
  runIngestAudit();
@@ -32905,17 +33117,57 @@ var runIngestAuditTick = () => {
32905
33117
  const ids = /* @__PURE__ */ new Set([house.accountId]);
32906
33118
  const accounts = wa?.accounts;
32907
33119
  for (const id of Object.keys(accounts ?? {})) ids.add(id);
32908
- return Array.from(ids).map((accountId) => ({
32909
- accountId,
32910
- enabled: resolvePublicAgentEnabled(house.accountDir, { accountId }).enabled,
32911
- slug: resolvePublicAgent(house.accountDir, { accountId })?.slug ?? null
32912
- })).filter((row) => row.enabled).map(({ accountId, slug }) => ({ accountId, slug }));
33120
+ const rows = Array.from(ids).map((accountId) => {
33121
+ const isHouse = isHouseAccountKey(house.accountId, accountId);
33122
+ return {
33123
+ accountId,
33124
+ isHouse,
33125
+ enabled: resolvePublicAgentEnabled(house.accountDir, { accountId, isHouse }).enabled,
33126
+ slug: resolvePublicAgent(house.accountDir, { accountId, isHouse })?.slug ?? null
33127
+ };
33128
+ });
33129
+ const seenHouse = { taken: false };
33130
+ return rows.filter((row) => row.enabled).filter((row) => {
33131
+ if (!row.isHouse) return true;
33132
+ if (seenHouse.taken) return false;
33133
+ seenHouse.taken = true;
33134
+ return true;
33135
+ }).map(({ accountId, slug }) => ({ accountId, slug }));
32913
33136
  },
32914
- agentExists: (slug) => house ? existsSync50(join53(house.accountDir, "agents", slug, "config.json")) : false
33137
+ agentExists: (accountId, slug) => {
33138
+ if (!house) return false;
33139
+ const dir = isHouseAccountKey(house.accountId, accountId) ? house.accountDir : join53(ACCOUNTS_DIR, accountId);
33140
+ return existsSync50(join53(dir, "agents", slug, "config.json"));
33141
+ }
32915
33142
  });
32916
33143
  } catch (err) {
32917
33144
  console.error(`[whatsapp:public-agent] op=registry-audit-rejected detail=${err instanceof Error ? err.message : String(err)}`);
32918
33145
  }
33146
+ try {
33147
+ const house = resolveAccount();
33148
+ runPublicAgentSliceAudit({
33149
+ enabledAccounts: () => {
33150
+ if (!house) return [];
33151
+ const wa = getConfig(house.accountDir);
33152
+ const accounts = wa?.accounts;
33153
+ const ids = /* @__PURE__ */ new Set([house.accountId]);
33154
+ for (const id of Object.keys(accounts ?? {})) ids.add(id);
33155
+ return Array.from(ids).map((accountId) => {
33156
+ const isHouse = isHouseAccountKey(house.accountId, accountId);
33157
+ const slice = accounts?.[accountId] ?? {};
33158
+ const ownSlug = typeof slice.publicAgent === "string" && slice.publicAgent.trim() ? slice.publicAgent.trim() : null;
33159
+ return {
33160
+ accountId,
33161
+ house: isHouse,
33162
+ ownSlug,
33163
+ enabled: resolvePublicAgentEnabled(house.accountDir, { accountId, isHouse }).enabled
33164
+ };
33165
+ }).filter((row) => row.enabled).map(({ accountId, house: isHouse, ownSlug }) => ({ accountId, house: isHouse, ownSlug }));
33166
+ }
33167
+ });
33168
+ } catch (err) {
33169
+ console.error(`[whatsapp:public-agent] op=slice-audit-rejected detail=${err instanceof Error ? err.message : String(err)}`);
33170
+ }
32919
33171
  };
32920
33172
  registerLoop({
32921
33173
  name: "wa-ingest-audit",
@@ -33001,7 +33253,7 @@ if (bootAccount) {
33001
33253
  migrateRemovedConfigKeys(bootAccount.accountDir);
33002
33254
  }
33003
33255
  var bootAccountConfig = bootAccount?.config;
33004
- var bootPublicAgent = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: bootAccount.accountId })?.slug ?? null : null;
33256
+ var bootPublicAgent = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: bootAccount.accountId, isHouse: true })?.slug ?? null : null;
33005
33257
  var bootEntitlement = bootAccountConfig ? resolveEntitlement(
33006
33258
  { configDir: MAXY_DIR, platformRoot: PLATFORM_ROOT, commercialMode: COMMERCIAL_MODE },
33007
33259
  {
@@ -33143,9 +33395,10 @@ init({
33143
33395
  }
33144
33396
  const houseForRoute = resolveAccount();
33145
33397
  const routeAccountId = msg.effectiveAccountId;
33398
+ const routeIsHouse = houseForRoute ? routeAccountId === houseForRoute.accountId : false;
33146
33399
  const route = decidePublicAgentRoute({
33147
- resolveEnabled: () => houseForRoute ? resolvePublicAgentEnabled(houseForRoute.accountDir, { accountId: routeAccountId }) : { enabled: false, source: "default" },
33148
- resolveSlug: () => houseForRoute ? resolvePublicAgent(houseForRoute.accountDir, { accountId: routeAccountId }) : null
33400
+ resolveEnabled: () => houseForRoute ? resolvePublicAgentEnabled(houseForRoute.accountDir, { accountId: routeAccountId, isHouse: routeIsHouse }) : { enabled: false, source: "default" },
33401
+ resolveSlug: () => houseForRoute ? resolvePublicAgent(houseForRoute.accountDir, { accountId: routeAccountId, isHouse: routeIsHouse }) : null
33149
33402
  });
33150
33403
  console.error(
33151
33404
  formatPublicAgentRouteLine({