@rubytech/create-maxy-code 0.1.182 → 0.1.184

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 (170) hide show
  1. package/dist/__tests__/bundler-rewrite-platform-lib.test.js +68 -0
  2. package/dist/bundler-rewrite-platform-lib.js +29 -0
  3. package/package.json +1 -1
  4. package/payload/platform/lib/graph-search/dist/index.d.ts +10 -0
  5. package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/graph-search/dist/index.js +57 -30
  7. package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
  8. package/payload/platform/lib/graph-search/src/__tests__/hybrid.test.ts +95 -2
  9. package/payload/platform/lib/graph-search/src/__tests__/vector-index-coverage.test.ts +198 -0
  10. package/payload/platform/lib/graph-search/src/index.ts +73 -29
  11. package/payload/platform/neo4j/schema.cypher +36 -0
  12. package/payload/platform/plugins/admin/PLUGIN.md +2 -0
  13. package/payload/platform/plugins/admin/hooks/admin-authoring-observer.sh +155 -0
  14. package/payload/platform/plugins/admin/skills/access-manager/SKILL.md +7 -5
  15. package/payload/platform/plugins/admin/skills/access-manager/references/operations.md +27 -55
  16. package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +178 -0
  17. package/payload/platform/plugins/admin/skills/specialist-management/SKILL.md +3 -2
  18. package/payload/platform/plugins/aeo/mcp/dist/index.js +3 -0
  19. package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -1
  20. package/payload/platform/plugins/contacts/PLUGIN.md +1 -0
  21. package/payload/platform/plugins/contacts/mcp/dist/index.js +3 -0
  22. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  23. package/payload/platform/plugins/docs/references/access-control.md +29 -22
  24. package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.d.ts +2 -0
  25. package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.d.ts.map +1 -0
  26. package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.js +56 -0
  27. package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.js.map +1 -0
  28. package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
  29. package/payload/platform/plugins/memory/PLUGIN.md +6 -1
  30. package/payload/platform/plugins/memory/mcp/dist/index.js +91 -7
  31. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +30 -1
  33. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -1
  34. package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts +15 -0
  35. package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts.map +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts +10 -0
  37. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -1
  38. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +35 -4
  39. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -1
  40. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +61 -0
  41. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
  42. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.d.ts +2 -0
  43. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.d.ts.map +1 -0
  44. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.js +125 -0
  45. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.js.map +1 -0
  46. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  47. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +65 -5
  48. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  49. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.d.ts +15 -0
  50. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.d.ts.map +1 -0
  51. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.js +45 -0
  52. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.js.map +1 -0
  53. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +24 -1
  54. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  56. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +16 -0
  57. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  58. package/payload/platform/plugins/memory/references/graph-primitives.md +1 -0
  59. package/payload/platform/plugins/memory/references/schema-base.md +4 -2
  60. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +24 -0
  61. package/payload/platform/plugins/scheduling/PLUGIN.md +1 -0
  62. package/payload/platform/plugins/scheduling/mcp/dist/index.js +3 -0
  63. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  64. package/payload/platform/plugins/work/PLUGIN.md +1 -0
  65. package/payload/platform/plugins/work/mcp/dist/index.js +4 -0
  66. package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
  67. package/payload/platform/plugins/workflows/PLUGIN.md +1 -0
  68. package/payload/platform/plugins/workflows/mcp/dist/index.js +3 -0
  69. package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
  70. package/payload/platform/scripts/__tests__/agents-md-bootstrap.test.sh +111 -0
  71. package/payload/platform/scripts/lib/agents-md-bootstrap.sh +49 -0
  72. package/payload/platform/scripts/setup-account.sh +18 -14
  73. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +8 -0
  74. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
  75. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
  76. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  77. package/payload/platform/services/claude-session-manager/dist/http-server.js +58 -0
  78. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  79. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +41 -4
  80. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  81. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +172 -53
  82. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  83. package/payload/platform/templates/agents/admin/AGENTS.md +18 -7
  84. package/payload/platform/templates/agents/admin/IDENTITY.md +2 -2
  85. package/payload/platform/templates/specialists/agents/archive-ingest-operator.md +1 -1
  86. package/payload/platform/templates/specialists/agents/citation-auditor.md +1 -1
  87. package/payload/platform/templates/specialists/agents/compiled-truth-rewriter.md +1 -1
  88. package/payload/platform/templates/specialists/agents/content-producer.md +3 -1
  89. package/payload/platform/templates/specialists/agents/database-operator.md +2 -2
  90. package/payload/platform/templates/specialists/agents/librarian.md +1 -1
  91. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  92. package/payload/platform/templates/specialists/agents/project-manager.md +1 -1
  93. package/payload/platform/templates/specialists/agents/public-session-reviewer.md +40 -0
  94. package/payload/platform/templates/specialists/agents/research-assistant.md +1 -1
  95. package/payload/platform/templates/specialists/agents/typed-edge-classifier.md +1 -1
  96. package/payload/server/public/assets/ChatInput-DzacFNMk.css +1 -0
  97. package/payload/server/public/assets/{Checkbox-YrQovXpN.js → Checkbox-BJKNkUYu.js} +1 -1
  98. package/payload/server/public/assets/{admin-DH5jp-1u.js → admin-BNy4N2d6.js} +1 -1
  99. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Do2zYONG.js → architectureDiagram-Q4EWVU46-DZoOzmjE.js} +1 -1
  100. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DpIX_bRu.js → blockDiagram-DXYQGD6D-DG3yufCR.js} +1 -1
  101. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Cz335ZSY.js → c4Diagram-AHTNJAMY-BBx5wXuZ.js} +1 -1
  102. package/payload/server/public/assets/channel-DdtUtt5g.js +1 -0
  103. package/payload/server/public/assets/{chunk-336JU56O-Dmknn3Wo.js → chunk-336JU56O-DwP7g5ns.js} +2 -2
  104. package/payload/server/public/assets/{chunk-426QAEUC-ClfGjizN.js → chunk-426QAEUC-DXzGdc22.js} +1 -1
  105. package/payload/server/public/assets/{chunk-4TB4RGXK-BPKctJ5c.js → chunk-4TB4RGXK-1LDRjk0E.js} +1 -1
  106. package/payload/server/public/assets/{chunk-5FUZZQ4R-BWDCO9zw.js → chunk-5FUZZQ4R-BHjWhwhn.js} +1 -1
  107. package/payload/server/public/assets/{chunk-5PVQY5BW-BE34_Db0.js → chunk-5PVQY5BW-CwPHRVN8.js} +1 -1
  108. package/payload/server/public/assets/{chunk-EDXVE4YY-BysA-_6v.js → chunk-EDXVE4YY-7443hrYZ.js} +1 -1
  109. package/payload/server/public/assets/{chunk-ENJZ2VHE-BMVyMsqA.js → chunk-ENJZ2VHE-BTnmmnmX.js} +1 -1
  110. package/payload/server/public/assets/{chunk-ICPOFSXX-C5KRdLV4.js → chunk-ICPOFSXX-CPnMJplL.js} +1 -1
  111. package/payload/server/public/assets/{chunk-OYMX7WX6-DVZD-AVc.js → chunk-OYMX7WX6-DCQ5Q4RS.js} +1 -1
  112. package/payload/server/public/assets/{chunk-U2HBQHQK-DYmQdx8x.js → chunk-U2HBQHQK-BmfZG8e4.js} +1 -1
  113. package/payload/server/public/assets/{chunk-X2U36JSP-DrZenIl3.js → chunk-X2U36JSP-Da2yJL2u.js} +1 -1
  114. package/payload/server/public/assets/{chunk-YZCP3GAM-CDuGbvMq.js → chunk-YZCP3GAM-BdUmMUJ6.js} +1 -1
  115. package/payload/server/public/assets/{chunk-ZZ45TVLE-COztbiu5.js → chunk-ZZ45TVLE-abkGirrZ.js} +1 -1
  116. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CKk2DFZe.js +1 -0
  117. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E--YvfDQCh.js +1 -0
  118. package/payload/server/public/assets/clone-Cw6UfPGM.js +1 -0
  119. package/payload/server/public/assets/{dagre-Cv9Ib2OM.js → dagre-BVVuAEsU.js} +1 -1
  120. package/payload/server/public/assets/{dagre-KV5264BT-CoEd61KZ.js → dagre-KV5264BT-VG2kNj19.js} +1 -1
  121. package/payload/server/public/assets/data-uBpwaldK.js +1 -0
  122. package/payload/server/public/assets/{diagram-5BDNPKRD-WXNzw3uW.js → diagram-5BDNPKRD-DRiVy69K.js} +1 -1
  123. package/payload/server/public/assets/{diagram-G4DWMVQ6-C6IuQ2VV.js → diagram-G4DWMVQ6-DtpiRTYX.js} +1 -1
  124. package/payload/server/public/assets/{diagram-MMDJMWI5-CdJdir5A.js → diagram-MMDJMWI5-Da3t99fY.js} +1 -1
  125. package/payload/server/public/assets/{diagram-TYMM5635-DxWKQH5T.js → diagram-TYMM5635-BzQmLTJQ.js} +1 -1
  126. package/payload/server/public/assets/{erDiagram-SMLLAGMA-C9fvs33K.js → erDiagram-SMLLAGMA-D3Ll-GDw.js} +1 -1
  127. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BO7Yrbqv.js → flowDiagram-DWJPFMVM-D17bje6d.js} +1 -1
  128. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CQJyWBZq.js → ganttDiagram-T4ZO3ILL-BjeQ-Bun.js} +1 -1
  129. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DZfQHrYw.js → gitGraphDiagram-UUTBAWPF-LtX4pvZS.js} +1 -1
  130. package/payload/server/public/assets/{graph-labels-BMU8LsOD.js → graph-labels-Cn9jAE6I.js} +1 -1
  131. package/payload/server/public/assets/graph-tvTPvBaz.js +1 -0
  132. package/payload/server/public/assets/{graphlib-C40H-Q5E.js → graphlib-CzwsfZOw.js} +1 -1
  133. package/payload/server/public/assets/{infoDiagram-42DDH7IO-CakfV5f9.js → infoDiagram-42DDH7IO-DOgH4i52.js} +1 -1
  134. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CPuBPyfa.js → ishikawaDiagram-UXIWVN3A-DYqy5BFj.js} +1 -1
  135. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-zpZm2Mmo.js → journeyDiagram-VCZTEJTY-CsFpIULJ.js} +1 -1
  136. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DfJduQCo.js → kanban-definition-6JOO6SKY-5uZuArw7.js} +1 -1
  137. package/payload/server/public/assets/{lib-Bnh57com.js → lib-p4ylk2XS.js} +1 -1
  138. package/payload/server/public/assets/{line-BpYQqO5R.js → line-BY8PmCSW.js} +1 -1
  139. package/payload/server/public/assets/{mermaid-parser.core-C5Mwi14D.js → mermaid-parser.core-CJ_9xqwK.js} +1 -1
  140. package/payload/server/public/assets/{mermaid.core-D8qBtvvT.js → mermaid.core-CXznBkX7.js} +3 -3
  141. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-Bli9JhIN.js → mindmap-definition-QFDTVHPH-DZbHBud5.js} +1 -1
  142. package/payload/server/public/assets/{page-0lzgQWnS.js → page-B3VYaBcf.js} +1 -1
  143. package/payload/server/public/assets/{page-C29S3cBt.js → page-CfN7nw1J.js} +1 -1
  144. package/payload/server/public/assets/{pieDiagram-DEJITSTG-MsLXTSLg.js → pieDiagram-DEJITSTG-QmOi4DRG.js} +1 -1
  145. package/payload/server/public/assets/public-Bbew78md.js +7 -0
  146. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-De1sh2K0.js → quadrantDiagram-34T5L4WZ-Cpidq6Xj.js} +1 -1
  147. package/payload/server/public/assets/{requirementDiagram-MS252O5E-D5Sfg1t_.js → requirementDiagram-MS252O5E-CBqtfbzR.js} +1 -1
  148. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DHA3LIbv.js → sankeyDiagram-XADWPNL6-4BiY4dV9.js} +1 -1
  149. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BE_QlG1B.js → sequenceDiagram-FGHM5R23-DVlL-l2u.js} +1 -1
  150. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-NIwp3F4u.js → stateDiagram-FHFEXIEX-CAm9dW68.js} +1 -1
  151. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B2EzXi50.js +1 -0
  152. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CPE9jyDF.js → timeline-definition-GMOUNBTQ-CD-tZc4y.js} +1 -1
  153. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CKfoyYmH.js → vennDiagram-DHZGUBPP-Davcm0TF.js} +1 -1
  154. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-B9yJek-o.js → wardleyDiagram-NUSXRM2D-40LBYw_Y.js} +1 -1
  155. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DuCcSLqX.js → xychartDiagram-5P7HB3ND-DEPT-__v.js} +1 -1
  156. package/payload/server/public/data.html +5 -5
  157. package/payload/server/public/graph.html +6 -6
  158. package/payload/server/public/index.html +8 -8
  159. package/payload/server/public/public.html +5 -5
  160. package/payload/server/server.js +1254 -337
  161. package/payload/server/public/assets/ChatInput-CJ50oqWt.css +0 -1
  162. package/payload/server/public/assets/channel-PC4_IjI1.js +0 -1
  163. package/payload/server/public/assets/classDiagram-6PBFFD2Q-B_pMa-SX.js +0 -1
  164. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BdBAgF20.js +0 -1
  165. package/payload/server/public/assets/clone-CkNt9L9r.js +0 -1
  166. package/payload/server/public/assets/data-CtanHhM2.js +0 -1
  167. package/payload/server/public/assets/graph-6DPD6be1.js +0 -1
  168. package/payload/server/public/assets/public-DltgEfA9.js +0 -7
  169. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-pfk2F6st.js +0 -1
  170. /package/payload/server/public/assets/{ChatInput-CJo_77bp.js → ChatInput-CvRaT05l.js} +0 -0
@@ -816,8 +816,8 @@ var serveStatic = (options = { root: "" }) => {
816
816
  };
817
817
 
818
818
  // server/index.ts
819
- import { readFileSync as readFileSync20, existsSync as existsSync24, watchFile } from "fs";
820
- import { resolve as resolve20, join as join15, basename as basename5 } from "path";
819
+ import { readFileSync as readFileSync21, existsSync as existsSync24, watchFile } from "fs";
820
+ import { resolve as resolve22, join as join15, basename as basename5 } from "path";
821
821
  import { homedir as homedir3 } from "os";
822
822
  import { monitorEventLoopDelay } from "perf_hooks";
823
823
 
@@ -1275,7 +1275,7 @@ var credsSaveQueue = Promise.resolve();
1275
1275
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
1276
1276
  console.error(`${TAG2} draining credential save queue\u2026`);
1277
1277
  const timer2 = new Promise(
1278
- (resolve21) => setTimeout(() => resolve21("timeout"), timeoutMs)
1278
+ (resolve23) => setTimeout(() => resolve23("timeout"), timeoutMs)
1279
1279
  );
1280
1280
  const result = await Promise.race([
1281
1281
  credsSaveQueue.then(() => "drained"),
@@ -1403,11 +1403,11 @@ async function createWaSocket(opts) {
1403
1403
  return sock;
1404
1404
  }
1405
1405
  async function waitForConnection(sock) {
1406
- return new Promise((resolve21, reject) => {
1406
+ return new Promise((resolve23, reject) => {
1407
1407
  const handler = (update) => {
1408
1408
  if (update.connection === "open") {
1409
1409
  sock.ev.off("connection.update", handler);
1410
- resolve21();
1410
+ resolve23();
1411
1411
  }
1412
1412
  if (update.connection === "close") {
1413
1413
  sock.ev.off("connection.update", handler);
@@ -1521,14 +1521,14 @@ ${inspected}`;
1521
1521
  return inspect2(err, INSPECT_OPTS2);
1522
1522
  }
1523
1523
  function withTimeout(label, promise, timeoutMs) {
1524
- return new Promise((resolve21, reject) => {
1524
+ return new Promise((resolve23, reject) => {
1525
1525
  const timer2 = setTimeout(() => {
1526
1526
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
1527
1527
  }, timeoutMs);
1528
1528
  promise.then(
1529
1529
  (value) => {
1530
1530
  clearTimeout(timer2);
1531
- resolve21(value);
1531
+ resolve23(value);
1532
1532
  },
1533
1533
  (err) => {
1534
1534
  clearTimeout(timer2);
@@ -2063,8 +2063,8 @@ async function persistWhatsAppMessage(input) {
2063
2063
  const { givenName, familyName } = splitName(input.pushName);
2064
2064
  const prev = sessionWriteLocks.get(input.cacheKey);
2065
2065
  let release;
2066
- const mine = new Promise((resolve21) => {
2067
- release = resolve21;
2066
+ const mine = new Promise((resolve23) => {
2067
+ release = resolve23;
2068
2068
  });
2069
2069
  const chained = (prev ?? Promise.resolve()).then(() => mine);
2070
2070
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -3100,11 +3100,11 @@ async function connectWithReconnect(conn) {
3100
3100
  console.error(
3101
3101
  `${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
3102
3102
  );
3103
- await new Promise((resolve21) => {
3104
- const timer2 = setTimeout(resolve21, delay);
3103
+ await new Promise((resolve23) => {
3104
+ const timer2 = setTimeout(resolve23, delay);
3105
3105
  conn.abortController.signal.addEventListener("abort", () => {
3106
3106
  clearTimeout(timer2);
3107
- resolve21();
3107
+ resolve23();
3108
3108
  }, { once: true });
3109
3109
  });
3110
3110
  }
@@ -3112,16 +3112,16 @@ async function connectWithReconnect(conn) {
3112
3112
  }
3113
3113
  }
3114
3114
  function waitForDisconnectEvent(conn) {
3115
- return new Promise((resolve21) => {
3115
+ return new Promise((resolve23) => {
3116
3116
  if (!conn.sock) {
3117
- resolve21();
3117
+ resolve23();
3118
3118
  return;
3119
3119
  }
3120
3120
  const sock = conn.sock;
3121
3121
  const handler = (update) => {
3122
3122
  if (update.connection === "close") {
3123
3123
  sock.ev.off("connection.update", handler);
3124
- resolve21();
3124
+ resolve23();
3125
3125
  }
3126
3126
  };
3127
3127
  sock.ev.on("connection.update", handler);
@@ -3383,8 +3383,8 @@ async function handleInboundMessage(conn, msg) {
3383
3383
  const conversationKey = isGroup ? remoteJid : senderPhone;
3384
3384
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
3385
3385
  let resolvePending;
3386
- const sttPending = new Promise((resolve21) => {
3387
- resolvePending = resolve21;
3386
+ const sttPending = new Promise((resolve23) => {
3387
+ resolvePending = resolve23;
3388
3388
  });
3389
3389
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
3390
3390
  try {
@@ -3788,20 +3788,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
3788
3788
 
3789
3789
  // server/routes/health.ts
3790
3790
  function checkPort(port2, timeoutMs = 500) {
3791
- return new Promise((resolve21) => {
3791
+ return new Promise((resolve23) => {
3792
3792
  const socket = createConnection2(port2, "127.0.0.1");
3793
3793
  socket.setTimeout(timeoutMs);
3794
3794
  socket.once("connect", () => {
3795
3795
  socket.destroy();
3796
- resolve21(true);
3796
+ resolve23(true);
3797
3797
  });
3798
3798
  socket.once("error", () => {
3799
3799
  socket.destroy();
3800
- resolve21(false);
3800
+ resolve23(false);
3801
3801
  });
3802
3802
  socket.once("timeout", () => {
3803
3803
  socket.destroy();
3804
- resolve21(false);
3804
+ resolve23(false);
3805
3805
  });
3806
3806
  });
3807
3807
  }
@@ -4114,7 +4114,9 @@ async function managerSpawn(opts) {
4114
4114
  aboutOwner: opts.aboutOwner,
4115
4115
  dormantPlugins: opts.dormantPlugins,
4116
4116
  activePlugins: opts.activePlugins,
4117
- specialistDomains: opts.specialistDomains
4117
+ specialistDomains: opts.specialistDomains,
4118
+ sliceToken: opts.sliceToken,
4119
+ personId: opts.personId
4118
4120
  })
4119
4121
  }).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
4120
4122
  if ("__throw" in res) {
@@ -4145,6 +4147,169 @@ function managerLogFollowUrl(sessionId) {
4145
4147
  return `${managerBase()}/${sessionId}/log?follow=1`;
4146
4148
  }
4147
4149
 
4150
+ // app/lib/channel-pty-bridge/public-session-end-review.ts
4151
+ var TAG14 = "[public-session-review]";
4152
+ var CONSUMED_CAP = 500;
4153
+ var consumed = /* @__PURE__ */ new Set();
4154
+ function consumeOnce(sessionId) {
4155
+ if (consumed.has(sessionId)) return false;
4156
+ consumed.add(sessionId);
4157
+ if (consumed.size > CONSUMED_CAP) {
4158
+ const oldest = consumed.values().next().value;
4159
+ if (oldest !== void 0) consumed.delete(oldest);
4160
+ }
4161
+ return true;
4162
+ }
4163
+ function managerBase2() {
4164
+ const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "public-session-end-review:manager" });
4165
+ return `http://127.0.0.1:${port2}`;
4166
+ }
4167
+ function uiBase() {
4168
+ const port2 = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "public-session-end-review:ui" });
4169
+ return `http://127.0.0.1:${port2}`;
4170
+ }
4171
+ async function fetchJsonl(sessionId) {
4172
+ try {
4173
+ const res = await fetch(`${managerBase2()}/${sessionId}/log?download=1`);
4174
+ if (!res.ok) return null;
4175
+ return await res.text();
4176
+ } catch (err) {
4177
+ console.error(
4178
+ `${TAG14} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
4179
+ );
4180
+ return null;
4181
+ }
4182
+ }
4183
+ function countOperatorTurns(jsonl) {
4184
+ let count = 0;
4185
+ for (const raw of jsonl.split("\n")) {
4186
+ const line = raw.trim();
4187
+ if (!line) continue;
4188
+ try {
4189
+ const obj = JSON.parse(line);
4190
+ if (obj.type === "user") count += 1;
4191
+ } catch {
4192
+ }
4193
+ }
4194
+ return count;
4195
+ }
4196
+ async function fetchPriorWrites(sliceToken, accountId) {
4197
+ const url = `${uiBase()}/api/admin/public-session-context?sliceToken=${encodeURIComponent(sliceToken)}&accountId=${encodeURIComponent(accountId)}`;
4198
+ try {
4199
+ const res = await fetch(url);
4200
+ if (!res.ok) {
4201
+ console.error(
4202
+ `${TAG14} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
4203
+ );
4204
+ return [];
4205
+ }
4206
+ const body = await res.json();
4207
+ return Array.isArray(body.writes) ? body.writes : [];
4208
+ } catch (err) {
4209
+ console.error(
4210
+ `${TAG14} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
4211
+ );
4212
+ return [];
4213
+ }
4214
+ }
4215
+ function composeInitialMessage(input) {
4216
+ const priorJson = JSON.stringify(input.priorWrites, null, 2);
4217
+ return [
4218
+ `The gated public-agent session for agent "${input.agentSlug}" has just been reaped. Review the transcript and decide what is worth saving to the visitor's per-visitor memory slice. Dispatch \`database-operator\` for each individual write via the Task tool. Do not call memory-write or memory-update directly.`,
4219
+ "",
4220
+ "<slice-token>",
4221
+ input.sliceToken,
4222
+ "</slice-token>",
4223
+ "",
4224
+ "<person-id>",
4225
+ input.personId ?? "(unknown)",
4226
+ "</person-id>",
4227
+ "",
4228
+ "<prior-writes>",
4229
+ priorJson,
4230
+ "</prior-writes>",
4231
+ "",
4232
+ "<transcript>",
4233
+ input.jsonl,
4234
+ "</transcript>"
4235
+ ].join("\n");
4236
+ }
4237
+ async function dispatchReviewer(input, initialMessage) {
4238
+ try {
4239
+ const res = await fetch(`${managerBase2()}/spawn`, {
4240
+ method: "POST",
4241
+ headers: { "content-type": "application/json" },
4242
+ body: JSON.stringify({
4243
+ senderId: `public-session-reviewer-${input.sessionId}`,
4244
+ role: "admin",
4245
+ channel: "webchat",
4246
+ accountId: input.accountId,
4247
+ specialist: "public-session-reviewer",
4248
+ hidden: true,
4249
+ initialMessage,
4250
+ sliceToken: input.sliceToken,
4251
+ personId: input.personId ?? void 0
4252
+ })
4253
+ });
4254
+ if (!res.ok) {
4255
+ return { ok: false, reason: `spawn-status-${res.status}` };
4256
+ }
4257
+ const body = await res.json();
4258
+ return { ok: true, managerSessionId: body.sessionId ?? "unknown" };
4259
+ } catch (err) {
4260
+ return { ok: false, reason: `spawn-threw-${err instanceof Error ? err.message : String(err)}` };
4261
+ }
4262
+ }
4263
+ async function firePublicSessionEndReview(input) {
4264
+ const sliceShort = input.sliceToken.slice(0, 8);
4265
+ const personField = input.personId ?? "none";
4266
+ const dispatchedAt = Date.now();
4267
+ if (consumed.has(input.sessionId)) {
4268
+ console.log(
4269
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
4270
+ );
4271
+ return;
4272
+ }
4273
+ const jsonl = await fetchJsonl(input.sessionId);
4274
+ if (!jsonl) {
4275
+ console.log(
4276
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
4277
+ );
4278
+ return;
4279
+ }
4280
+ const operatorTurns = countOperatorTurns(jsonl);
4281
+ if (operatorTurns === 0) {
4282
+ console.log(
4283
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
4284
+ );
4285
+ return;
4286
+ }
4287
+ const priorWrites = await fetchPriorWrites(input.sliceToken, input.accountId);
4288
+ const initialMessage = composeInitialMessage({
4289
+ agentSlug: input.agentSlug,
4290
+ sliceToken: input.sliceToken,
4291
+ personId: input.personId,
4292
+ jsonl,
4293
+ priorWrites
4294
+ });
4295
+ if (!consumeOnce(input.sessionId)) {
4296
+ console.log(
4297
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
4298
+ );
4299
+ return;
4300
+ }
4301
+ const dispatched = await dispatchReviewer(input, initialMessage);
4302
+ if (!dispatched.ok) {
4303
+ console.error(
4304
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
4305
+ );
4306
+ return;
4307
+ }
4308
+ console.log(
4309
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
4310
+ );
4311
+ }
4312
+
4148
4313
  // app/lib/channel-pty-bridge/follower.ts
4149
4314
  function startFollower(opts) {
4150
4315
  const abort = new AbortController();
@@ -4235,12 +4400,15 @@ function publicIdleMs() {
4235
4400
  return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(30 * 6e4));
4236
4401
  }
4237
4402
  var index = /* @__PURE__ */ new Map();
4238
- function indexKey(accountId, agentSlug, senderId, liveMemory) {
4239
- return `${accountId}:${agentSlug}:${liveMemory ? "lm1" : "lm0"}:${senderId}`;
4403
+ function indexKey(accountId, agentSlug, senderId, liveMemory, sliceToken = "") {
4404
+ const slice = sliceToken && sliceToken.length > 0 ? sliceToken : "open";
4405
+ return `${accountId}:${agentSlug}:${liveMemory ? "lm1" : "lm0"}:${slice}:${senderId}`;
4240
4406
  }
4241
4407
  async function ensureEntry(input) {
4242
4408
  startReaper();
4243
- const key = indexKey(input.accountId, input.agentSlug, input.senderId, input.liveMemory);
4409
+ const sliceTokenValue = input.sliceToken ?? "";
4410
+ const personIdValue = input.personId ?? null;
4411
+ const key = indexKey(input.accountId, input.agentSlug, input.senderId, input.liveMemory, sliceTokenValue);
4244
4412
  const existing = index.get(key);
4245
4413
  const tag = tagFor(input.channel);
4246
4414
  if (existing) {
@@ -4264,7 +4432,9 @@ async function ensureEntry(input) {
4264
4432
  accountId: input.accountId,
4265
4433
  agentSlug: input.agentSlug,
4266
4434
  liveMemory: input.liveMemory,
4267
- attachmentDir
4435
+ attachmentDir,
4436
+ sliceToken: sliceTokenValue.length > 0 ? sliceTokenValue : void 0,
4437
+ personId: personIdValue ?? void 0
4268
4438
  });
4269
4439
  if ("error" in spawned) {
4270
4440
  if (spawned.error !== "claude-auth-dead") {
@@ -4287,7 +4457,9 @@ async function ensureEntry(input) {
4287
4457
  pendingTurnText: "",
4288
4458
  followerAbort: null,
4289
4459
  followerRunning: false,
4290
- liveMemory: input.liveMemory
4460
+ liveMemory: input.liveMemory,
4461
+ sliceToken: sliceTokenValue,
4462
+ personId: personIdValue
4291
4463
  };
4292
4464
  entry.followerAbort = startFollower({
4293
4465
  entry,
@@ -4314,14 +4486,26 @@ async function writeInput(entry, text) {
4314
4486
  `${tag} session-resolved senderId=${entry.senderId} action=respawn reason=session-gone-${result.status} sessionId=${entry.sessionId.slice(0, 8)}`
4315
4487
  );
4316
4488
  if (entry.followerAbort) entry.followerAbort.abort();
4317
- index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.liveMemory));
4489
+ if (entry.role === "public" && entry.sliceToken.length > 0) {
4490
+ void firePublicSessionEndReview({
4491
+ sessionId: entry.sessionId,
4492
+ sliceToken: entry.sliceToken,
4493
+ personId: entry.personId,
4494
+ accountId: entry.accountId,
4495
+ agentSlug: entry.agentSlug,
4496
+ dispatchFor: "exit"
4497
+ });
4498
+ }
4499
+ index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.liveMemory, entry.sliceToken));
4318
4500
  const respawned = await ensureEntry({
4319
4501
  accountId: entry.accountId,
4320
4502
  senderId: entry.senderId,
4321
4503
  role: entry.role,
4322
4504
  channel: entry.channel,
4323
4505
  agentSlug: entry.agentSlug,
4324
- liveMemory: entry.liveMemory
4506
+ liveMemory: entry.liveMemory,
4507
+ sliceToken: entry.sliceToken.length > 0 ? entry.sliceToken : void 0,
4508
+ personId: entry.personId
4325
4509
  });
4326
4510
  if (!respawned) return false;
4327
4511
  for (const cb of entry.subscribers) respawned.subscribers.add(cb);
@@ -4359,16 +4543,18 @@ async function dispatchOnce(input) {
4359
4543
  role: input.role,
4360
4544
  channel: input.channel,
4361
4545
  agentSlug: input.agentSlug,
4362
- liveMemory: input.liveMemory
4546
+ liveMemory: input.liveMemory,
4547
+ sliceToken: input.sliceToken,
4548
+ personId: input.personId
4363
4549
  });
4364
4550
  if (!entry) return { error: "spawn-failed" };
4365
4551
  entry.lastInboundAt = Date.now();
4366
- let resolve21;
4552
+ let resolve23;
4367
4553
  const turnPromise = new Promise((r) => {
4368
- resolve21 = r;
4554
+ resolve23 = r;
4369
4555
  });
4370
4556
  const listener = (text) => {
4371
- resolve21(text);
4557
+ resolve23(text);
4372
4558
  };
4373
4559
  entry.subscribers.add(listener);
4374
4560
  const writeOk = await writeInput(entry, input.text);
@@ -4418,11 +4604,75 @@ function startReaper() {
4418
4604
  );
4419
4605
  if (entry.followerAbort) entry.followerAbort.abort();
4420
4606
  index.delete(key);
4607
+ if (entry.role === "public" && entry.sliceToken.length > 0) {
4608
+ void firePublicSessionEndReview({
4609
+ sessionId: entry.sessionId,
4610
+ sliceToken: entry.sliceToken,
4611
+ personId: entry.personId,
4612
+ accountId: entry.accountId,
4613
+ agentSlug: entry.agentSlug,
4614
+ dispatchFor: "eviction"
4615
+ });
4616
+ }
4421
4617
  void managerDelete(entry.sessionId);
4422
4618
  }
4423
4619
  }, 6e4);
4424
4620
  if (reaperHandle.unref) reaperHandle.unref();
4425
4621
  }
4622
+ function handlePublicSessionExit(sessionId) {
4623
+ let found = null;
4624
+ for (const [key2, entry2] of index) {
4625
+ if (entry2.sessionId === sessionId) {
4626
+ found = { key: key2, entry: entry2 };
4627
+ break;
4628
+ }
4629
+ }
4630
+ if (!found) {
4631
+ console.error(`[public-session-exit] sessionId=${sessionId} result=no-entry`);
4632
+ return;
4633
+ }
4634
+ const { key, entry } = found;
4635
+ if (entry.role !== "public" || entry.sliceToken.length === 0) {
4636
+ return;
4637
+ }
4638
+ if (entry.followerAbort) entry.followerAbort.abort();
4639
+ index.delete(key);
4640
+ void firePublicSessionEndReview({
4641
+ sessionId: entry.sessionId,
4642
+ sliceToken: entry.sliceToken,
4643
+ personId: entry.personId,
4644
+ accountId: entry.accountId,
4645
+ agentSlug: entry.agentSlug,
4646
+ dispatchFor: "exit"
4647
+ });
4648
+ }
4649
+
4650
+ // app/lib/access-session-store.ts
4651
+ import { randomUUID as randomUUID4 } from "crypto";
4652
+ var sessions = /* @__PURE__ */ new Map();
4653
+ function createAccessSession(input) {
4654
+ const sessionId = randomUUID4();
4655
+ const session = {
4656
+ ...input,
4657
+ sessionId,
4658
+ createdAt: Date.now()
4659
+ };
4660
+ sessions.set(sessionId, session);
4661
+ return session;
4662
+ }
4663
+ function getAccessSession(sessionId) {
4664
+ return sessions.get(sessionId) ?? null;
4665
+ }
4666
+ function evictAccessSessionsByGrant(grantId) {
4667
+ let dropped = 0;
4668
+ for (const [sessionId, session] of sessions) {
4669
+ if (session.grantId === grantId) {
4670
+ sessions.delete(sessionId);
4671
+ dropped += 1;
4672
+ }
4673
+ }
4674
+ return dropped;
4675
+ }
4426
4676
 
4427
4677
  // server/routes/chat.ts
4428
4678
  var WEBCHAT_TAG = "[webchat-adaptor]";
@@ -4545,13 +4795,37 @@ ${lines}]`;
4545
4795
  return c.json({ error: "No default agent configured" }, 500);
4546
4796
  }
4547
4797
  let liveMemory = false;
4798
+ let accessMode = "open";
4548
4799
  try {
4549
- liveMemory = resolveAgentConfig(account.accountDir, agentSlug).liveMemory;
4800
+ const config = resolveAgentConfig(account.accountDir, agentSlug);
4801
+ liveMemory = config.liveMemory;
4802
+ accessMode = config.accessMode;
4550
4803
  } catch (err) {
4551
4804
  const m = err instanceof Error ? err.message : String(err);
4552
4805
  console.error(`${WEBCHAT_TAG} reject reason=agent-config-failed agentSlug=${agentSlug} message=${m}`);
4553
4806
  return c.json({ error: "Agent config invalid" }, 500);
4554
4807
  }
4808
+ let sliceToken;
4809
+ let personId = null;
4810
+ if (accessMode === "gated" || accessMode === "paid") {
4811
+ const cookieHeader = c.req.header("cookie") ?? "";
4812
+ const accessSessionId = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="))?.slice("__access_session=".length);
4813
+ const session = accessSessionId ? getAccessSession(accessSessionId) : null;
4814
+ if (!session) {
4815
+ console.error(
4816
+ `${WEBCHAT_TAG} reject reason=gate-required agentSlug=${agentSlug} senderId=${session_key.slice(0, 8)} cookie=${accessSessionId ? "unknown-id" : "absent"}`
4817
+ );
4818
+ return c.json({ error: "gate-required", agentSlug }, 401);
4819
+ }
4820
+ if (session.agentSlug !== agentSlug) {
4821
+ console.error(
4822
+ `${WEBCHAT_TAG} reject reason=gate-agent-mismatch agentSlug=${agentSlug} cookieAgent=${session.agentSlug}`
4823
+ );
4824
+ return c.json({ error: "gate-required", agentSlug }, 401);
4825
+ }
4826
+ sliceToken = session.sliceToken;
4827
+ personId = session.personId;
4828
+ }
4555
4829
  const sk = session_key.slice(0, 8);
4556
4830
  console.error(
4557
4831
  `${WEBCHAT_TAG} inbound from=${sk} bytes=${Buffer.byteLength(fullMessage, "utf8")} attachments=${attachmentCount} accountId=${account.accountId}`
@@ -4566,6 +4840,8 @@ ${lines}]`;
4566
4840
  channel: "webchat",
4567
4841
  agentSlug,
4568
4842
  liveMemory,
4843
+ sliceToken,
4844
+ personId,
4569
4845
  text: fullMessage,
4570
4846
  timeoutMs: webchatTurnTimeoutMs()
4571
4847
  });
@@ -4617,15 +4893,15 @@ import { readFile as readFile2, stat as stat3 } from "fs/promises";
4617
4893
  import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
4618
4894
 
4619
4895
  // app/lib/whatsapp/login.ts
4620
- import { randomUUID as randomUUID4 } from "crypto";
4621
- var TAG14 = "[whatsapp:login]";
4896
+ import { randomUUID as randomUUID5 } from "crypto";
4897
+ var TAG15 = "[whatsapp:login]";
4622
4898
  var ACTIVE_LOGIN_TTL_MS = 3 * 6e4;
4623
4899
  var activeLogins = /* @__PURE__ */ new Map();
4624
4900
  function closeSocket(sock) {
4625
4901
  try {
4626
4902
  sock.ws?.close?.();
4627
4903
  } catch (err) {
4628
- console.warn(`${TAG14} socket close error during cleanup: ${String(err)}`);
4904
+ console.warn(`${TAG15} socket close error during cleanup: ${String(err)}`);
4629
4905
  }
4630
4906
  }
4631
4907
  function resetActiveLogin(accountId) {
@@ -4648,7 +4924,7 @@ async function loginConnectionLoop(accountId, login) {
4648
4924
  const current = activeLogins.get(accountId);
4649
4925
  if (current?.id === login.id) {
4650
4926
  current.connected = true;
4651
- console.error(`${TAG14} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
4927
+ console.error(`${TAG15} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
4652
4928
  }
4653
4929
  return;
4654
4930
  } catch (err) {
@@ -4658,7 +4934,7 @@ async function loginConnectionLoop(accountId, login) {
4658
4934
  if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
4659
4935
  if (attempt >= LOGIN_MAX_RECONNECTS) {
4660
4936
  console.error(
4661
- `${TAG14} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
4937
+ `${TAG15} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
4662
4938
  );
4663
4939
  current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
4664
4940
  } else {
@@ -4670,7 +4946,7 @@ async function loginConnectionLoop(accountId, login) {
4670
4946
  attempt++;
4671
4947
  const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
4672
4948
  console.error(
4673
- `${TAG14} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
4949
+ `${TAG15} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
4674
4950
  );
4675
4951
  closeSocket(current.sock);
4676
4952
  await new Promise((r) => setTimeout(r, delay));
@@ -4681,7 +4957,7 @@ async function loginConnectionLoop(accountId, login) {
4681
4957
  current.sock = newSock;
4682
4958
  } catch (sockErr) {
4683
4959
  console.error(
4684
- `${TAG14} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
4960
+ `${TAG15} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
4685
4961
  );
4686
4962
  current.error = `Reconnection failed: ${String(sockErr)}`;
4687
4963
  return;
@@ -4695,7 +4971,7 @@ async function startLogin(opts) {
4695
4971
  const hasAuth = await authExists(authDir);
4696
4972
  const selfId = readSelfId(authDir);
4697
4973
  console.error(
4698
- `${TAG14} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
4974
+ `${TAG15} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
4699
4975
  );
4700
4976
  if (hasAuth && !force) {
4701
4977
  const who = selfId.e164 ?? selfId.jid ?? "unknown";
@@ -4707,7 +4983,7 @@ async function startLogin(opts) {
4707
4983
  await clearAuth(authDir);
4708
4984
  const existing = activeLogins.get(accountId);
4709
4985
  if (existing && isLoginFresh(existing) && existing.qrDataUrl && !force) {
4710
- console.error(`${TAG14} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
4986
+ console.error(`${TAG15} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
4711
4987
  return {
4712
4988
  qrDataUrl: existing.qrDataUrl,
4713
4989
  qrRaw: existing.qr,
@@ -4715,13 +4991,13 @@ async function startLogin(opts) {
4715
4991
  };
4716
4992
  }
4717
4993
  if (existing) {
4718
- console.error(`${TAG14} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
4994
+ console.error(`${TAG15} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
4719
4995
  }
4720
4996
  resetActiveLogin(accountId);
4721
4997
  let resolveQr = null;
4722
4998
  let rejectQr = null;
4723
- const qrPromise = new Promise((resolve21, reject) => {
4724
- resolveQr = resolve21;
4999
+ const qrPromise = new Promise((resolve23, reject) => {
5000
+ resolveQr = resolve23;
4725
5001
  rejectQr = reject;
4726
5002
  });
4727
5003
  const qrTimer = setTimeout(
@@ -4737,14 +5013,14 @@ async function startLogin(opts) {
4737
5013
  onQr: (qr2) => {
4738
5014
  loginQrCount++;
4739
5015
  if (pendingQr) {
4740
- console.error(`${TAG14} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
5016
+ console.error(`${TAG15} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
4741
5017
  return;
4742
5018
  }
4743
5019
  pendingQr = qr2;
4744
5020
  const current = activeLogins.get(accountId);
4745
5021
  if (current && !current.qr) current.qr = qr2;
4746
5022
  clearTimeout(qrTimer);
4747
- console.error(`${TAG14} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
5023
+ console.error(`${TAG15} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
4748
5024
  resolveQr?.(qr2);
4749
5025
  }
4750
5026
  });
@@ -4756,7 +5032,7 @@ async function startLogin(opts) {
4756
5032
  const login = {
4757
5033
  accountId,
4758
5034
  authDir,
4759
- id: randomUUID4(),
5035
+ id: randomUUID5(),
4760
5036
  sock,
4761
5037
  startedAt: Date.now(),
4762
5038
  connected: false
@@ -4764,7 +5040,7 @@ async function startLogin(opts) {
4764
5040
  activeLogins.set(accountId, login);
4765
5041
  if (pendingQr && !login.qr) login.qr = pendingQr;
4766
5042
  loginConnectionLoop(accountId, login).catch((err) => {
4767
- console.error(`${TAG14} loginConnectionLoop unexpected error: ${String(err)}`);
5043
+ console.error(`${TAG15} loginConnectionLoop unexpected error: ${String(err)}`);
4768
5044
  const current = activeLogins.get(accountId);
4769
5045
  if (current?.id === login.id) {
4770
5046
  current.error = `Unexpected login error: ${String(err)}`;
@@ -4789,7 +5065,7 @@ async function waitForLogin(opts) {
4789
5065
  const { accountId, timeoutMs = 6e4 } = opts;
4790
5066
  const login = activeLogins.get(accountId);
4791
5067
  console.error(
4792
- `${TAG14} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
5068
+ `${TAG15} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
4793
5069
  );
4794
5070
  if (!login) {
4795
5071
  return { connected: false, message: "No active WhatsApp login in progress." };
@@ -4802,7 +5078,7 @@ async function waitForLogin(opts) {
4802
5078
  while (Date.now() < deadline) {
4803
5079
  if (login.connected) {
4804
5080
  const selfId = readSelfId(login.authDir);
4805
- console.error(`${TAG14} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
5081
+ console.error(`${TAG15} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
4806
5082
  const sock = login.sock;
4807
5083
  const authDir = login.authDir;
4808
5084
  activeLogins.delete(accountId);
@@ -4822,7 +5098,7 @@ async function waitForLogin(opts) {
4822
5098
  await new Promise((r) => setTimeout(r, 1e3));
4823
5099
  }
4824
5100
  const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
4825
- console.error(`${TAG14} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
5101
+ console.error(`${TAG15} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
4826
5102
  resetActiveLogin(accountId);
4827
5103
  return { connected: false, message: "Login timed out. Try generating a new QR." };
4828
5104
  }
@@ -4830,7 +5106,7 @@ async function waitForLogin(opts) {
4830
5106
  // app/lib/whatsapp/config-persist.ts
4831
5107
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
4832
5108
  import { resolve as resolve5, join as join4 } from "path";
4833
- var TAG15 = "[whatsapp:config]";
5109
+ var TAG16 = "[whatsapp:config]";
4834
5110
  function configPath(accountDir) {
4835
5111
  return resolve5(accountDir, "account.json");
4836
5112
  }
@@ -4847,9 +5123,9 @@ function reloadManagerConfig(accountDir) {
4847
5123
  try {
4848
5124
  const config = readConfig(accountDir);
4849
5125
  reloadConfig(config);
4850
- console.error(`${TAG15} reloaded manager config`);
5126
+ console.error(`${TAG16} reloaded manager config`);
4851
5127
  } catch (err) {
4852
- console.error(`${TAG15} manager config reload failed: ${String(err)}`);
5128
+ console.error(`${TAG16} manager config reload failed: ${String(err)}`);
4853
5129
  }
4854
5130
  }
4855
5131
  var E164_PATTERN = /^\+\d{7,15}$/;
@@ -4875,25 +5151,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
4875
5151
  const adminPhones = wa.adminPhones;
4876
5152
  if (!adminPhones.includes(normalized)) {
4877
5153
  adminPhones.push(normalized);
4878
- console.error(`${TAG15} added selfPhone=${normalized} to adminPhones`);
5154
+ console.error(`${TAG16} added selfPhone=${normalized} to adminPhones`);
4879
5155
  }
4880
5156
  } else {
4881
- console.error(`${TAG15} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
5157
+ console.error(`${TAG16} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
4882
5158
  }
4883
5159
  const parsed = WhatsAppConfigSchema.safeParse(wa);
4884
5160
  if (!parsed.success) {
4885
5161
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
4886
- console.error(`${TAG15} validation failed after pairing: ${msg}`);
5162
+ console.error(`${TAG16} validation failed after pairing: ${msg}`);
4887
5163
  return { ok: false, error: `Validation failed: ${msg}` };
4888
5164
  }
4889
5165
  config.whatsapp = parsed.data;
4890
5166
  writeConfig(accountDir, config);
4891
- console.error(`${TAG15} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
5167
+ console.error(`${TAG16} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
4892
5168
  reloadManagerConfig(accountDir);
4893
5169
  return { ok: true };
4894
5170
  } catch (err) {
4895
5171
  const msg = err instanceof Error ? err.message : String(err);
4896
- console.error(`${TAG15} persist failed account=${accountId}: ${msg}`);
5172
+ console.error(`${TAG16} persist failed account=${accountId}: ${msg}`);
4897
5173
  return { ok: false, error: msg };
4898
5174
  }
4899
5175
  }
@@ -4923,12 +5199,12 @@ function addAdminPhone(accountDir, phone) {
4923
5199
  }
4924
5200
  config.whatsapp = parsed.data;
4925
5201
  writeConfig(accountDir, config);
4926
- console.error(`${TAG15} added admin phone=${normalized}`);
5202
+ console.error(`${TAG16} added admin phone=${normalized}`);
4927
5203
  reloadManagerConfig(accountDir);
4928
5204
  return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
4929
5205
  } catch (err) {
4930
5206
  const msg = err instanceof Error ? err.message : String(err);
4931
- console.error(`${TAG15} addAdminPhone failed: ${msg}`);
5207
+ console.error(`${TAG16} addAdminPhone failed: ${msg}`);
4932
5208
  return { ok: false, error: msg };
4933
5209
  }
4934
5210
  }
@@ -4956,12 +5232,12 @@ function removeAdminPhone(accountDir, phone) {
4956
5232
  }
4957
5233
  config.whatsapp = parsed.data;
4958
5234
  writeConfig(accountDir, config);
4959
- console.error(`${TAG15} removed admin phone=${normalized}`);
5235
+ console.error(`${TAG16} removed admin phone=${normalized}`);
4960
5236
  reloadManagerConfig(accountDir);
4961
5237
  return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
4962
5238
  } catch (err) {
4963
5239
  const msg = err instanceof Error ? err.message : String(err);
4964
- console.error(`${TAG15} removeAdminPhone failed: ${msg}`);
5240
+ console.error(`${TAG16} removeAdminPhone failed: ${msg}`);
4965
5241
  return { ok: false, error: msg };
4966
5242
  }
4967
5243
  }
@@ -4999,12 +5275,12 @@ function setPublicAgent(accountDir, slug) {
4999
5275
  }
5000
5276
  config.whatsapp = parsed.data;
5001
5277
  writeConfig(accountDir, config);
5002
- console.error(`${TAG15} publicAgent set to ${trimmed}`);
5278
+ console.error(`${TAG16} publicAgent set to ${trimmed}`);
5003
5279
  reloadManagerConfig(accountDir);
5004
5280
  return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
5005
5281
  } catch (err) {
5006
5282
  const msg = err instanceof Error ? err.message : String(err);
5007
- console.error(`${TAG15} setPublicAgent failed: ${msg}`);
5283
+ console.error(`${TAG16} setPublicAgent failed: ${msg}`);
5008
5284
  return { ok: false, error: msg };
5009
5285
  }
5010
5286
  }
@@ -5068,12 +5344,12 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
5068
5344
  }
5069
5345
  config.whatsapp = parsed.data;
5070
5346
  writeConfig(accountDir, config);
5071
- console.error(`${TAG15} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
5347
+ console.error(`${TAG16} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
5072
5348
  reloadManagerConfig(accountDir);
5073
5349
  return { ok: true, message: `Per-group public agent set to "${trimmedSlug}" for group ${trimmedGroup}.` };
5074
5350
  } catch (err) {
5075
5351
  const msg = err instanceof Error ? err.message : String(err);
5076
- console.error(`${TAG15} setGroupPublicAgent failed: ${msg}`);
5352
+ console.error(`${TAG16} setGroupPublicAgent failed: ${msg}`);
5077
5353
  return { ok: false, error: msg };
5078
5354
  }
5079
5355
  }
@@ -5100,12 +5376,12 @@ function unsetGroupPublicAgent(accountDir, accountId, groupJid) {
5100
5376
  }
5101
5377
  config.whatsapp = parsed.data;
5102
5378
  writeConfig(accountDir, config);
5103
- console.error(`${TAG15} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
5379
+ console.error(`${TAG16} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
5104
5380
  reloadManagerConfig(accountDir);
5105
5381
  return { ok: true, message: `Per-group public agent override removed for group ${trimmedGroup}.` };
5106
5382
  } catch (err) {
5107
5383
  const msg = err instanceof Error ? err.message : String(err);
5108
- console.error(`${TAG15} unsetGroupPublicAgent failed: ${msg}`);
5384
+ console.error(`${TAG16} unsetGroupPublicAgent failed: ${msg}`);
5109
5385
  return { ok: false, error: msg };
5110
5386
  }
5111
5387
  }
@@ -5126,17 +5402,17 @@ function updateConfig(accountDir, fields) {
5126
5402
  const parsed = WhatsAppConfigSchema.safeParse(wa);
5127
5403
  if (!parsed.success) {
5128
5404
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
5129
- console.error(`${TAG15} update validation failed: ${msg}`);
5405
+ console.error(`${TAG16} update validation failed: ${msg}`);
5130
5406
  return { ok: false, error: `Validation failed: ${msg}` };
5131
5407
  }
5132
5408
  config.whatsapp = parsed.data;
5133
5409
  writeConfig(accountDir, config);
5134
- console.error(`${TAG15} updated fields=[${fieldNames.join(",")}]`);
5410
+ console.error(`${TAG16} updated fields=[${fieldNames.join(",")}]`);
5135
5411
  reloadManagerConfig(accountDir);
5136
5412
  return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
5137
5413
  } catch (err) {
5138
5414
  const msg = err instanceof Error ? err.message : String(err);
5139
- console.error(`${TAG15} updateConfig failed: ${msg}`);
5415
+ console.error(`${TAG16} updateConfig failed: ${msg}`);
5140
5416
  return { ok: false, error: msg };
5141
5417
  }
5142
5418
  }
@@ -5262,17 +5538,17 @@ function serializeWhatsAppSchema() {
5262
5538
  }
5263
5539
 
5264
5540
  // server/routes/whatsapp.ts
5265
- var TAG16 = "[whatsapp:api]";
5541
+ var TAG17 = "[whatsapp:api]";
5266
5542
  var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
5267
5543
  var app3 = new Hono();
5268
5544
  app3.get("/status", (c) => {
5269
5545
  try {
5270
5546
  const status = getStatus();
5271
5547
  const summary = status.map((a) => `${a.accountId}:${a.connected ? "up" : "down"}`).join(", ");
5272
- console.error(`${TAG16} status accounts=${status.length} [${summary}]`);
5548
+ console.error(`${TAG17} status accounts=${status.length} [${summary}]`);
5273
5549
  return c.json({ accounts: status });
5274
5550
  } catch (err) {
5275
- console.error(`${TAG16} status error: ${String(err)}`);
5551
+ console.error(`${TAG17} status error: ${String(err)}`);
5276
5552
  return c.json({ error: String(err) }, 500);
5277
5553
  }
5278
5554
  });
@@ -5283,10 +5559,10 @@ app3.post("/login/start", async (c) => {
5283
5559
  const force = body.force ?? false;
5284
5560
  const authDir = join5(MAXY_DIR, "credentials", "whatsapp", accountId);
5285
5561
  const result = await startLogin({ accountId, authDir, force });
5286
- console.error(`${TAG16} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
5562
+ console.error(`${TAG17} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
5287
5563
  return c.json(result);
5288
5564
  } catch (err) {
5289
- console.error(`${TAG16} login/start error: ${String(err)}`);
5565
+ console.error(`${TAG17} login/start error: ${String(err)}`);
5290
5566
  return c.json({ error: String(err) }, 500);
5291
5567
  }
5292
5568
  });
@@ -5301,7 +5577,7 @@ app3.post("/login/wait", async (c) => {
5301
5577
  try {
5302
5578
  await registerLoginSocket(accountId, result.sock, result.authDir);
5303
5579
  } catch (regErr) {
5304
- console.error(`${TAG16} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
5580
+ console.error(`${TAG17} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
5305
5581
  }
5306
5582
  try {
5307
5583
  const account = resolveAccount();
@@ -5309,16 +5585,16 @@ app3.post("/login/wait", async (c) => {
5309
5585
  const persistResult = persistAfterPairing(account.accountDir, accountId, result.selfPhone ?? null);
5310
5586
  configPersisted = persistResult.ok;
5311
5587
  if (!persistResult.ok) {
5312
- console.error(`${TAG16} config persist failed account=${accountId}: ${persistResult.error}`);
5588
+ console.error(`${TAG17} config persist failed account=${accountId}: ${persistResult.error}`);
5313
5589
  }
5314
5590
  } else {
5315
- console.error(`${TAG16} config persist skipped \u2014 no account resolved`);
5591
+ console.error(`${TAG17} config persist skipped \u2014 no account resolved`);
5316
5592
  }
5317
5593
  } catch (persistErr) {
5318
- console.error(`${TAG16} config persist error account=${accountId}: ${String(persistErr)}`);
5594
+ console.error(`${TAG17} config persist error account=${accountId}: ${String(persistErr)}`);
5319
5595
  }
5320
5596
  }
5321
- console.error(`${TAG16} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
5597
+ console.error(`${TAG17} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
5322
5598
  return c.json({
5323
5599
  connected: result.connected,
5324
5600
  message: result.message,
@@ -5326,7 +5602,7 @@ app3.post("/login/wait", async (c) => {
5326
5602
  configPersisted
5327
5603
  });
5328
5604
  } catch (err) {
5329
- console.error(`${TAG16} login/wait error: ${String(err)}`);
5605
+ console.error(`${TAG17} login/wait error: ${String(err)}`);
5330
5606
  return c.json({ error: String(err) }, 500);
5331
5607
  }
5332
5608
  });
@@ -5337,7 +5613,7 @@ app3.post("/disconnect", async (c) => {
5337
5613
  await stopConnection(accountId);
5338
5614
  return c.json({ disconnected: true, accountId });
5339
5615
  } catch (err) {
5340
- console.error(`${TAG16} disconnect error: ${String(err)}`);
5616
+ console.error(`${TAG17} disconnect error: ${String(err)}`);
5341
5617
  return c.json({ error: String(err) }, 500);
5342
5618
  }
5343
5619
  });
@@ -5348,7 +5624,7 @@ app3.post("/reconnect", async (c) => {
5348
5624
  await startConnection(accountId);
5349
5625
  return c.json({ reconnecting: true, accountId });
5350
5626
  } catch (err) {
5351
- console.error(`${TAG16} reconnect error: ${String(err)}`);
5627
+ console.error(`${TAG17} reconnect error: ${String(err)}`);
5352
5628
  return c.json({ error: String(err) }, 500);
5353
5629
  }
5354
5630
  });
@@ -5367,7 +5643,7 @@ app3.post("/send", async (c) => {
5367
5643
  const result = await sendTextMessage(sock, to, text, { accountId });
5368
5644
  return c.json(result);
5369
5645
  } catch (err) {
5370
- console.error(`${TAG16} send error: ${String(err)}`);
5646
+ console.error(`${TAG17} send error: ${String(err)}`);
5371
5647
  return c.json({ error: String(err) }, 500);
5372
5648
  }
5373
5649
  });
@@ -5388,7 +5664,7 @@ app3.post("/config", async (c) => {
5388
5664
  return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
5389
5665
  }
5390
5666
  const result = addAdminPhone(account.accountDir, phone);
5391
- console.error(`${TAG16} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
5667
+ console.error(`${TAG17} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
5392
5668
  return c.json(result, result.ok ? 200 : 400);
5393
5669
  }
5394
5670
  case "remove-admin-phone": {
@@ -5396,12 +5672,12 @@ app3.post("/config", async (c) => {
5396
5672
  return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
5397
5673
  }
5398
5674
  const result = removeAdminPhone(account.accountDir, phone);
5399
- console.error(`${TAG16} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
5675
+ console.error(`${TAG17} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
5400
5676
  return c.json(result, result.ok ? 200 : 400);
5401
5677
  }
5402
5678
  case "list-admin-phones": {
5403
5679
  const phones = readAdminPhones(account.accountDir);
5404
- console.error(`${TAG16} config action=list-admin-phones count=${phones.length}`);
5680
+ console.error(`${TAG17} config action=list-admin-phones count=${phones.length}`);
5405
5681
  return c.json({ ok: true, phones });
5406
5682
  }
5407
5683
  case "set-public-agent": {
@@ -5409,14 +5685,14 @@ app3.post("/config", async (c) => {
5409
5685
  return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
5410
5686
  }
5411
5687
  const result = setPublicAgent(account.accountDir, slug);
5412
- console.error(`${TAG16} config action=set-public-agent slug=${slug} ok=${result.ok}`);
5688
+ console.error(`${TAG17} config action=set-public-agent slug=${slug} ok=${result.ok}`);
5413
5689
  return c.json(result, result.ok ? 200 : 400);
5414
5690
  }
5415
5691
  case "get-public-agent": {
5416
5692
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
5417
5693
  const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
5418
5694
  const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
5419
- console.error(`${TAG16} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
5695
+ console.error(`${TAG17} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
5420
5696
  return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
5421
5697
  }
5422
5698
  case "set-group-public-agent": {
@@ -5428,7 +5704,7 @@ app3.post("/config", async (c) => {
5428
5704
  }
5429
5705
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
5430
5706
  const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
5431
- console.error(`${TAG16} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
5707
+ console.error(`${TAG17} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
5432
5708
  return c.json(result, result.ok ? 200 : 400);
5433
5709
  }
5434
5710
  case "unset-group-public-agent": {
@@ -5437,7 +5713,7 @@ app3.post("/config", async (c) => {
5437
5713
  }
5438
5714
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
5439
5715
  const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
5440
- console.error(`${TAG16} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
5716
+ console.error(`${TAG17} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
5441
5717
  return c.json(result, result.ok ? 200 : 400);
5442
5718
  }
5443
5719
  case "list-public-agents": {
@@ -5454,26 +5730,26 @@ app3.post("/config", async (c) => {
5454
5730
  const config = JSON.parse(readFileSync6(configPath2, "utf-8"));
5455
5731
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
5456
5732
  } catch {
5457
- console.error(`${TAG16} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
5733
+ console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
5458
5734
  }
5459
5735
  }
5460
5736
  } catch (err) {
5461
- console.error(`${TAG16} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
5737
+ console.error(`${TAG17} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
5462
5738
  }
5463
5739
  }
5464
- console.error(`${TAG16} config action=list-public-agents count=${agents.length}`);
5740
+ console.error(`${TAG17} config action=list-public-agents count=${agents.length}`);
5465
5741
  return c.json({ ok: true, agents });
5466
5742
  }
5467
5743
  case "schema": {
5468
5744
  const text = serializeWhatsAppSchema();
5469
- console.error(`${TAG16} config action=schema`);
5745
+ console.error(`${TAG17} config action=schema`);
5470
5746
  return c.json({ ok: true, text });
5471
5747
  }
5472
5748
  case "list-groups": {
5473
5749
  const groupAccountId = accountId ?? "default";
5474
5750
  const sock = getSocket(groupAccountId);
5475
5751
  if (!sock) {
5476
- console.error(`${TAG16} config action=list-groups error="not connected" accountId=${groupAccountId}`);
5752
+ console.error(`${TAG17} config action=list-groups error="not connected" accountId=${groupAccountId}`);
5477
5753
  return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
5478
5754
  }
5479
5755
  try {
@@ -5483,10 +5759,10 @@ app3.post("/config", async (c) => {
5483
5759
  name: g.subject ?? g.id,
5484
5760
  participantCount: Array.isArray(g.participants) ? g.participants.length : 0
5485
5761
  }));
5486
- console.error(`${TAG16} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
5762
+ console.error(`${TAG17} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
5487
5763
  return c.json({ ok: true, groups });
5488
5764
  } catch (err) {
5489
- console.error(`${TAG16} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
5765
+ console.error(`${TAG17} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
5490
5766
  return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
5491
5767
  }
5492
5768
  }
@@ -5496,12 +5772,12 @@ app3.post("/config", async (c) => {
5496
5772
  }
5497
5773
  const result = updateConfig(account.accountDir, fields);
5498
5774
  const fieldNames = Object.keys(fields);
5499
- console.error(`${TAG16} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
5775
+ console.error(`${TAG17} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
5500
5776
  return c.json(result, result.ok ? 200 : 400);
5501
5777
  }
5502
5778
  case "get-config": {
5503
5779
  const waConfig = getConfig(account.accountDir);
5504
- console.error(`${TAG16} config action=get-config`);
5780
+ console.error(`${TAG17} config action=get-config`);
5505
5781
  return c.json({ ok: true, config: waConfig });
5506
5782
  }
5507
5783
  default:
@@ -5511,7 +5787,7 @@ app3.post("/config", async (c) => {
5511
5787
  );
5512
5788
  }
5513
5789
  } catch (err) {
5514
- console.error(`${TAG16} config error: ${String(err)}`);
5790
+ console.error(`${TAG17} config error: ${String(err)}`);
5515
5791
  return c.json({ ok: false, error: String(err) }, 500);
5516
5792
  }
5517
5793
  });
@@ -5533,16 +5809,16 @@ app3.post("/send-document", async (c) => {
5533
5809
  const accountResolved = realpathSync2(accountDir);
5534
5810
  if (!resolvedPath.startsWith(accountResolved + "/")) {
5535
5811
  const sanitised = filePath.replace(accountDir, "<account>/");
5536
- console.error(`${TAG16} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
5812
+ console.error(`${TAG17} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
5537
5813
  return c.json({ error: "Access denied: file is outside the account directory" }, 403);
5538
5814
  }
5539
5815
  } catch (err) {
5540
5816
  const code = err.code;
5541
5817
  if (code === "ENOENT") {
5542
- console.error(`${TAG16} send-document ENOENT path=${filePath}`);
5818
+ console.error(`${TAG17} send-document ENOENT path=${filePath}`);
5543
5819
  return c.json({ error: `File not found: ${filePath}` }, 404);
5544
5820
  }
5545
- console.error(`${TAG16} send-document path error: ${String(err)}`);
5821
+ console.error(`${TAG17} send-document path error: ${String(err)}`);
5546
5822
  return c.json({ error: String(err) }, 500);
5547
5823
  }
5548
5824
  const fileStat = await stat3(resolvedPath);
@@ -5564,11 +5840,11 @@ app3.post("/send-document", async (c) => {
5564
5840
  caption
5565
5841
  }, { accountId });
5566
5842
  console.error(
5567
- `${TAG16} send-document to=${to} size=${fileStat.size} mime=${mimetype} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
5843
+ `${TAG17} send-document to=${to} size=${fileStat.size} mime=${mimetype} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
5568
5844
  );
5569
5845
  return c.json(result);
5570
5846
  } catch (err) {
5571
- console.error(`${TAG16} send-document error: ${String(err)}`);
5847
+ console.error(`${TAG17} send-document error: ${String(err)}`);
5572
5848
  return c.json({ error: String(err) }, 500);
5573
5849
  }
5574
5850
  });
@@ -5578,11 +5854,11 @@ app3.get("/activity", (c) => {
5578
5854
  const result = getChannelActivity(accountId);
5579
5855
  const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
5580
5856
  console.error(
5581
- `${TAG16} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
5857
+ `${TAG17} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
5582
5858
  );
5583
5859
  return c.json(result);
5584
5860
  } catch (err) {
5585
- console.error(`${TAG16} activity error: ${String(err)}`);
5861
+ console.error(`${TAG17} activity error: ${String(err)}`);
5586
5862
  return c.json({ error: String(err) }, 500);
5587
5863
  }
5588
5864
  });
@@ -5601,10 +5877,10 @@ app3.get("/conversations", (c) => {
5601
5877
  };
5602
5878
  });
5603
5879
  conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
5604
- console.error(`${TAG16} conversations account=${accountId} count=${conversations.length}`);
5880
+ console.error(`${TAG17} conversations account=${accountId} count=${conversations.length}`);
5605
5881
  return c.json({ conversations });
5606
5882
  } catch (err) {
5607
- console.error(`${TAG16} conversations error: ${String(err)}`);
5883
+ console.error(`${TAG17} conversations error: ${String(err)}`);
5608
5884
  return c.json({ error: String(err) }, 500);
5609
5885
  }
5610
5886
  });
@@ -5619,10 +5895,10 @@ app3.get("/messages", (c) => {
5619
5895
  const limit = limitParam ? parseInt(limitParam, 10) : void 0;
5620
5896
  const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
5621
5897
  const messages = getMessages(accountId, jid, effectiveLimit);
5622
- console.error(`${TAG16} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
5898
+ console.error(`${TAG17} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
5623
5899
  return c.json({ messages });
5624
5900
  } catch (err) {
5625
- console.error(`${TAG16} messages error: ${String(err)}`);
5901
+ console.error(`${TAG17} messages error: ${String(err)}`);
5626
5902
  return c.json({ error: String(err) }, 500);
5627
5903
  }
5628
5904
  });
@@ -5703,7 +5979,7 @@ app3.get("/conversation-graph-state", async (c) => {
5703
5979
  }
5704
5980
  } catch (err) {
5705
5981
  const msg = err instanceof Error ? err.message : String(err);
5706
- console.error(`${TAG16} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
5982
+ console.error(`${TAG17} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
5707
5983
  return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
5708
5984
  }
5709
5985
  const ms = Date.now() - t0;
@@ -5716,7 +5992,7 @@ app3.get("/conversation-graph-state", async (c) => {
5716
5992
  ms
5717
5993
  });
5718
5994
  } catch (err) {
5719
- console.error(`${TAG16} conversation-graph-state error: ${String(err)}`);
5995
+ console.error(`${TAG17} conversation-graph-state error: ${String(err)}`);
5720
5996
  return c.json({ error: String(err) }, 500);
5721
5997
  }
5722
5998
  });
@@ -5728,12 +6004,12 @@ app3.get("/group-info", async (c) => {
5728
6004
  return c.json({ error: "Missing required parameter: jid" }, 400);
5729
6005
  }
5730
6006
  if (!isGroupJid(jid)) {
5731
- console.error(`${TAG16} group-info error="not a group JID" jid=${jid} account=${accountId}`);
6007
+ console.error(`${TAG17} group-info error="not a group JID" jid=${jid} account=${accountId}`);
5732
6008
  return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
5733
6009
  }
5734
6010
  const sock = getSocket(accountId);
5735
6011
  if (!sock) {
5736
- console.error(`${TAG16} group-info error="not connected" account=${accountId}`);
6012
+ console.error(`${TAG17} group-info error="not connected" account=${accountId}`);
5737
6013
  return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
5738
6014
  }
5739
6015
  const meta = await sock.groupMetadata(jid);
@@ -5746,10 +6022,10 @@ app3.get("/group-info", async (c) => {
5746
6022
  participantCount: meta.participants.length,
5747
6023
  participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
5748
6024
  };
5749
- console.error(`${TAG16} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
6025
+ console.error(`${TAG17} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
5750
6026
  return c.json(result);
5751
6027
  } catch (err) {
5752
- console.error(`${TAG16} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
6028
+ console.error(`${TAG17} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
5753
6029
  return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
5754
6030
  }
5755
6031
  });
@@ -5758,7 +6034,7 @@ var whatsapp_default = app3;
5758
6034
  // server/routes/onboarding.ts
5759
6035
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
5760
6036
  import { openSync, closeSync, writeFileSync as writeFileSync5, writeSync, existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync } from "fs";
5761
- import { createHash, randomUUID as randomUUID5 } from "crypto";
6037
+ import { createHash, randomUUID as randomUUID6 } from "crypto";
5762
6038
 
5763
6039
  // ../lib/admins-write/src/index.ts
5764
6040
  import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
@@ -6034,7 +6310,7 @@ app4.post("/set-pin", async (c) => {
6034
6310
  const hash = hashPin(body.pin);
6035
6311
  const account = resolveAccount();
6036
6312
  const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
6037
- const userId = existingOwnerUserId ?? randomUUID5();
6313
+ const userId = existingOwnerUserId ?? randomUUID6();
6038
6314
  if (existingOwnerUserId) {
6039
6315
  console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
6040
6316
  } else {
@@ -6295,7 +6571,7 @@ var client_error_default = app5;
6295
6571
  import { readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync8 } from "fs";
6296
6572
  import { join as join8 } from "path";
6297
6573
  import { createHash as createHash2 } from "crypto";
6298
- import { randomUUID as randomUUID6 } from "crypto";
6574
+ import { randomUUID as randomUUID7 } from "crypto";
6299
6575
  function hashPin2(pin) {
6300
6576
  return createHash2("sha256").update(pin).digest("hex");
6301
6577
  }
@@ -6352,7 +6628,7 @@ async function resolveUserIdentity(accountId, userId) {
6352
6628
  async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
6353
6629
  const account = resolveAccount();
6354
6630
  const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
6355
- const signedSessionToken = randomUUID6();
6631
+ const signedSessionToken = randomUUID7();
6356
6632
  const cacheKey = fingerprintSessionKey(signedSessionToken);
6357
6633
  registerSession(cacheKey, "admin", accountId, void 0, userId, userName, role);
6358
6634
  if (userId) setWantsPriorConversation(cacheKey);
@@ -7223,7 +7499,7 @@ app14.get("/", requireAdminSession, async (c) => {
7223
7499
  if (!userId) return c.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, 401);
7224
7500
  try {
7225
7501
  const flushed = await listAdminSessions(accountId, userId, 20);
7226
- const sessions = flushed.map((r) => ({
7502
+ const sessions2 = flushed.map((r) => ({
7227
7503
  sessionId: r.sessionId,
7228
7504
  cacheKey: null,
7229
7505
  name: r.name,
@@ -7231,15 +7507,15 @@ app14.get("/", requireAdminSession, async (c) => {
7231
7507
  phase: "flushed",
7232
7508
  channel: r.channel
7233
7509
  })).sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).slice(0, 20);
7234
- const channelCounts = sessions.reduce((acc, s) => {
7510
+ const channelCounts = sessions2.reduce((acc, s) => {
7235
7511
  const k = s.channel ?? "unknown";
7236
7512
  acc[k] = (acc[k] ?? 0) + 1;
7237
7513
  return acc;
7238
7514
  }, {});
7239
7515
  console.error(
7240
- `[conversations-list] render rows=${sessions.length} channels=${JSON.stringify(channelCounts)}`
7516
+ `[conversations-list] render rows=${sessions2.length} channels=${JSON.stringify(channelCounts)}`
7241
7517
  );
7242
- return c.json({ sessions });
7518
+ return c.json({ sessions: sessions2 });
7243
7519
  } catch (err) {
7244
7520
  console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);
7245
7521
  return c.json({ error: "Failed to fetch sessions" }, 500);
@@ -7298,11 +7574,11 @@ app14.delete("/:id", requireAdminSession, async (c) => {
7298
7574
  const owned = await verifyConversationOwnership(sessionId, accountId);
7299
7575
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
7300
7576
  const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-conversation-delete" });
7301
- const managerBase3 = `http://127.0.0.1:${managerPort}`;
7577
+ const managerBase4 = `http://127.0.0.1:${managerPort}`;
7302
7578
  const outcome = await cascadeAdminConversationDelete({
7303
7579
  sessionId,
7304
7580
  accountId,
7305
- managerBase: managerBase3
7581
+ managerBase: managerBase4
7306
7582
  });
7307
7583
  if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
7308
7584
  if (outcome.reason === "pty-still-alive") {
@@ -7948,7 +8224,7 @@ function resolveTunnelUrl() {
7948
8224
  if (!state) return null;
7949
8225
  return `https://${hostname2}.${state.domain}`;
7950
8226
  }
7951
- var TAG17 = "[claude-session-manager:wrapper]";
8227
+ var TAG18 = "[claude-session-manager:wrapper]";
7952
8228
  async function refuseIfClaudeAuthDead(c, route, sessionId) {
7953
8229
  const auth = await ensureAuth();
7954
8230
  if (auth.status !== "dead" && auth.status !== "missing") return null;
@@ -7960,7 +8236,7 @@ async function refuseIfClaudeAuthDead(c, route, sessionId) {
7960
8236
  503
7961
8237
  );
7962
8238
  }
7963
- function managerBase2() {
8239
+ function managerBase3() {
7964
8240
  const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "claude-session-manager:wrapper" });
7965
8241
  return `http://127.0.0.1:${port2}`;
7966
8242
  }
@@ -7971,12 +8247,12 @@ async function performSpawnWithInitialMessage(args) {
7971
8247
  const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
7972
8248
  const ownerMs = Date.now() - ownerStart;
7973
8249
  const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
7974
- console.log(`${TAG17} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
8250
+ console.log(`${TAG18} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
7975
8251
  const dormantPlugins = computeDormantPlugins(args.senderId);
7976
8252
  const activePlugins = computeActivePlugins(args.senderId);
7977
8253
  const specialistDomains = computeSpecialistDomains(args.senderId);
7978
8254
  const tunnelUrl = resolveTunnelUrl();
7979
- console.log(`${TAG17} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
8255
+ console.log(`${TAG18} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
7980
8256
  const upstreamPayload = JSON.stringify({
7981
8257
  senderId: args.senderId,
7982
8258
  // Task 205 — pass userId through to the manager so it lands as
@@ -8003,24 +8279,24 @@ async function performSpawnWithInitialMessage(args) {
8003
8279
  // unshapely values.
8004
8280
  conversationNodeId: args.conversationNodeId
8005
8281
  });
8006
- console.log(`${TAG17} forward-spawn-start managerBase=${managerBase2()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
8282
+ console.log(`${TAG18} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
8007
8283
  const forwardStart = Date.now();
8008
- const upstream = await fetch(`${managerBase2()}/spawn`, {
8284
+ const upstream = await fetch(`${managerBase3()}/spawn`, {
8009
8285
  method: "POST",
8010
8286
  headers: { "content-type": "application/json" },
8011
8287
  body: upstreamPayload
8012
8288
  }).catch((err) => {
8013
- console.error(`${TAG17} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
8289
+ console.error(`${TAG18} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
8014
8290
  return null;
8015
8291
  });
8016
8292
  if (!upstream) return {
8017
8293
  response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
8018
8294
  claudeSessionId: null
8019
8295
  };
8020
- console.log(`${TAG17} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
8296
+ console.log(`${TAG18} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
8021
8297
  if (args.initialMessage) {
8022
8298
  const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
8023
- console.log(`${TAG17} initial-message-inlined bytes=${inputBytes}`);
8299
+ console.log(`${TAG18} initial-message-inlined bytes=${inputBytes}`);
8024
8300
  }
8025
8301
  const bodyText = await upstream.text().catch(() => "");
8026
8302
  let claudeSessionId = null;
@@ -8069,7 +8345,7 @@ app15.post("/", async (c) => {
8069
8345
  authSurface = "cookie";
8070
8346
  senderId = getAccountIdForSession(cacheKey) ?? "";
8071
8347
  if (!senderId) {
8072
- console.error(`${TAG17} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
8348
+ console.error(`${TAG18} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
8073
8349
  return c.json({ error: "admin-account-not-resolved" }, 500);
8074
8350
  }
8075
8351
  userId = getUserIdForSession(cacheKey) ?? void 0;
@@ -8080,9 +8356,9 @@ app15.post("/", async (c) => {
8080
8356
  }
8081
8357
  const lookupSessionId = body.adminSessionId;
8082
8358
  const operatorMeta = await fetch(
8083
- `${managerBase2()}/${encodeURIComponent(lookupSessionId)}/meta`
8359
+ `${managerBase3()}/${encodeURIComponent(lookupSessionId)}/meta`
8084
8360
  ).then((r) => r.ok ? r.json() : null).catch((err) => {
8085
- console.error(`${TAG17} fetch-failed op=operator-meta-lookup message=${err instanceof Error ? err.message : String(err)}`);
8361
+ console.error(`${TAG18} fetch-failed op=operator-meta-lookup message=${err instanceof Error ? err.message : String(err)}`);
8086
8362
  return null;
8087
8363
  });
8088
8364
  const operatorSenderId = operatorMeta && typeof operatorMeta.senderId === "string" && operatorMeta.senderId.length > 0 ? operatorMeta.senderId : null;
@@ -8097,7 +8373,7 @@ app15.post("/", async (c) => {
8097
8373
  const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
8098
8374
  const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
8099
8375
  const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
8100
- console.log(`${TAG17} spawn-request-in surface=${authSurface} accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
8376
+ console.log(`${TAG18} spawn-request-in surface=${authSurface} accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
8101
8377
  const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
8102
8378
  const { response, claudeSessionId } = await performSpawnWithInitialMessage({
8103
8379
  senderId,
@@ -8116,14 +8392,14 @@ app15.post("/", async (c) => {
8116
8392
  claudeSessionId,
8117
8393
  senderId
8118
8394
  );
8119
- console.log(`${TAG17} route-done surface=${authSurface} status=${response.status} route-ms=${Date.now() - routeStart}`);
8395
+ console.log(`${TAG18} route-done surface=${authSurface} status=${response.status} route-ms=${Date.now() - routeStart}`);
8120
8396
  return response;
8121
8397
  });
8122
8398
  app15.get("/events", async (c) => {
8123
- const upstream = await fetch(`${managerBase2()}/events`, {
8399
+ const upstream = await fetch(`${managerBase3()}/events`, {
8124
8400
  headers: { accept: "text/event-stream" }
8125
8401
  }).catch((err) => {
8126
- console.error(`${TAG17} fetch-failed op=events message=${err instanceof Error ? err.message : String(err)}`);
8402
+ console.error(`${TAG18} fetch-failed op=events message=${err instanceof Error ? err.message : String(err)}`);
8127
8403
  return null;
8128
8404
  });
8129
8405
  if (!upstream || !upstream.body) return c.json({ error: "manager-unreachable" }, 503);
@@ -8142,9 +8418,9 @@ app15.get("/", async (c) => {
8142
8418
  const senderId = getAccountIdForSession(cacheKey) ?? "";
8143
8419
  if (!senderId) return c.json({ error: "admin-account-not-resolved" }, 500);
8144
8420
  const upstream = await fetch(
8145
- `${managerBase2()}/list?senderId=${encodeURIComponent(senderId)}`
8421
+ `${managerBase3()}/list?senderId=${encodeURIComponent(senderId)}`
8146
8422
  ).catch((err) => {
8147
- console.error(`${TAG17} fetch-failed op=list message=${err instanceof Error ? err.message : String(err)}`);
8423
+ console.error(`${TAG18} fetch-failed op=list message=${err instanceof Error ? err.message : String(err)}`);
8148
8424
  return null;
8149
8425
  });
8150
8426
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8173,7 +8449,7 @@ app15.post("/resume", async (c) => {
8173
8449
  const activePlugins = computeActivePlugins(senderId);
8174
8450
  const specialistDomains = computeSpecialistDomains(senderId);
8175
8451
  const tunnelUrl = resolveTunnelUrl();
8176
- const upstream = await fetch(`${managerBase2()}/resume`, {
8452
+ const upstream = await fetch(`${managerBase3()}/resume`, {
8177
8453
  method: "POST",
8178
8454
  headers: { "content-type": "application/json" },
8179
8455
  body: JSON.stringify({
@@ -8198,7 +8474,7 @@ app15.post("/resume", async (c) => {
8198
8474
  conversationNodeId: getSessionIdForSession(cacheKey)
8199
8475
  })
8200
8476
  }).catch((err) => {
8201
- console.error(`${TAG17} fetch-failed op=resume message=${err instanceof Error ? err.message : String(err)}`);
8477
+ console.error(`${TAG18} fetch-failed op=resume message=${err instanceof Error ? err.message : String(err)}`);
8202
8478
  return null;
8203
8479
  });
8204
8480
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8223,10 +8499,10 @@ app15.post("/resume", async (c) => {
8223
8499
  app15.post("/:sessionId/stop", async (c) => {
8224
8500
  const sessionId = c.req.param("sessionId");
8225
8501
  const upstream = await fetch(
8226
- `${managerBase2()}/${encodeURIComponent(sessionId)}/stop`,
8502
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/stop`,
8227
8503
  { method: "POST" }
8228
8504
  ).catch((err) => {
8229
- console.error(`${TAG17} fetch-failed op=stop message=${err instanceof Error ? err.message : String(err)}`);
8505
+ console.error(`${TAG18} fetch-failed op=stop message=${err instanceof Error ? err.message : String(err)}`);
8230
8506
  return null;
8231
8507
  });
8232
8508
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8236,14 +8512,14 @@ app15.post("/:sessionId/rename", async (c) => {
8236
8512
  const sessionId = c.req.param("sessionId");
8237
8513
  const body = await c.req.text();
8238
8514
  const upstream = await fetch(
8239
- `${managerBase2()}/${encodeURIComponent(sessionId)}/rename`,
8515
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/rename`,
8240
8516
  {
8241
8517
  method: "POST",
8242
8518
  headers: { "content-type": "application/json" },
8243
8519
  body
8244
8520
  }
8245
8521
  ).catch((err) => {
8246
- console.error(`${TAG17} fetch-failed op=rename message=${err instanceof Error ? err.message : String(err)}`);
8522
+ console.error(`${TAG18} fetch-failed op=rename message=${err instanceof Error ? err.message : String(err)}`);
8247
8523
  return null;
8248
8524
  });
8249
8525
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8258,10 +8534,10 @@ app15.delete("/:sessionId", async (c) => {
8258
8534
  if (!sessionId) {
8259
8535
  console.log(`[admin-conversation-delete] reason=no-conversation-mapping claudeSessionId=${claudeSessionId.slice(0, 8)}`);
8260
8536
  const upstream = await fetch(
8261
- `${managerBase2()}/${encodeURIComponent(claudeSessionId)}`,
8537
+ `${managerBase3()}/${encodeURIComponent(claudeSessionId)}`,
8262
8538
  { method: "DELETE" }
8263
8539
  ).catch((err) => {
8264
- console.error(`${TAG17} fetch-failed op=delete message=${err instanceof Error ? err.message : String(err)}`);
8540
+ console.error(`${TAG18} fetch-failed op=delete message=${err instanceof Error ? err.message : String(err)}`);
8265
8541
  return null;
8266
8542
  });
8267
8543
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8277,7 +8553,7 @@ app15.delete("/:sessionId", async (c) => {
8277
8553
  const outcome = await cascadeAdminConversationDelete({
8278
8554
  sessionId,
8279
8555
  accountId,
8280
- managerBase: managerBase2(),
8556
+ managerBase: managerBase3(),
8281
8557
  claudeSessionIds: all
8282
8558
  });
8283
8559
  if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
@@ -8316,14 +8592,14 @@ app15.post("/:sessionId/archive", async (c) => {
8316
8592
  const sessionId = c.req.param("sessionId");
8317
8593
  const body = await c.req.text();
8318
8594
  const upstream = await fetch(
8319
- `${managerBase2()}/${encodeURIComponent(sessionId)}/archive`,
8595
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/archive`,
8320
8596
  {
8321
8597
  method: "POST",
8322
8598
  headers: { "content-type": "application/json" },
8323
8599
  body
8324
8600
  }
8325
8601
  ).catch((err) => {
8326
- console.error(`${TAG17} fetch-failed op=archive message=${err instanceof Error ? err.message : String(err)}`);
8602
+ console.error(`${TAG18} fetch-failed op=archive message=${err instanceof Error ? err.message : String(err)}`);
8327
8603
  return null;
8328
8604
  });
8329
8605
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8332,9 +8608,9 @@ app15.post("/:sessionId/archive", async (c) => {
8332
8608
  app15.get("/:sessionId/meta", async (c) => {
8333
8609
  const sessionId = c.req.param("sessionId");
8334
8610
  const upstream = await fetch(
8335
- `${managerBase2()}/${encodeURIComponent(sessionId)}/meta`
8611
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/meta`
8336
8612
  ).catch((err) => {
8337
- console.error(`${TAG17} fetch-failed op=meta message=${err instanceof Error ? err.message : String(err)}`);
8613
+ console.error(`${TAG18} fetch-failed op=meta message=${err instanceof Error ? err.message : String(err)}`);
8338
8614
  return null;
8339
8615
  });
8340
8616
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8344,14 +8620,14 @@ app15.post("/:sessionId/input", async (c) => {
8344
8620
  const sessionId = c.req.param("sessionId");
8345
8621
  const rawBody = await c.req.text();
8346
8622
  const upstream = await fetch(
8347
- `${managerBase2()}/${encodeURIComponent(sessionId)}/input`,
8623
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/input`,
8348
8624
  {
8349
8625
  method: "POST",
8350
8626
  headers: { "content-type": "application/json" },
8351
8627
  body: rawBody
8352
8628
  }
8353
8629
  ).catch((err) => {
8354
- console.error(`${TAG17} fetch-failed op=input message=${err instanceof Error ? err.message : String(err)}`);
8630
+ console.error(`${TAG18} fetch-failed op=input message=${err instanceof Error ? err.message : String(err)}`);
8355
8631
  return null;
8356
8632
  });
8357
8633
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8364,9 +8640,9 @@ app15.get("/:sessionId/log", async (c) => {
8364
8640
  if (c.req.query("download") === "1") search.set("download", "1");
8365
8641
  const qs = search.toString() ? `?${search.toString()}` : "";
8366
8642
  const upstream = await fetch(
8367
- `${managerBase2()}/${encodeURIComponent(sessionId)}/log${qs}`
8643
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/log${qs}`
8368
8644
  ).catch((err) => {
8369
- console.error(`${TAG17} fetch-failed op=log message=${err instanceof Error ? err.message : String(err)}`);
8645
+ console.error(`${TAG18} fetch-failed op=log message=${err instanceof Error ? err.message : String(err)}`);
8370
8646
  return null;
8371
8647
  });
8372
8648
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8375,7 +8651,7 @@ app15.get("/:sessionId/log", async (c) => {
8375
8651
  var claude_sessions_default = app15;
8376
8652
 
8377
8653
  // server/routes/admin/log-ingest.ts
8378
- var TAG18 = "[log-ingest]";
8654
+ var TAG19 = "[log-ingest]";
8379
8655
  var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
8380
8656
  var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
8381
8657
  var MAX_LINE_BYTES = 4096;
@@ -8386,7 +8662,7 @@ function isLoopbackAddr(addr) {
8386
8662
  app16.post("/", async (c) => {
8387
8663
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
8388
8664
  if (!isLoopbackAddr(remoteAddr)) {
8389
- console.error(`${TAG18} reject reason=non-loopback remoteAddr=${remoteAddr}`);
8665
+ console.error(`${TAG19} reject reason=non-loopback remoteAddr=${remoteAddr}`);
8390
8666
  return c.json({ error: "log-ingest-loopback-only" }, 403);
8391
8667
  }
8392
8668
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -8395,7 +8671,7 @@ app16.post("/", async (c) => {
8395
8671
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
8396
8672
  const offender = tokens.find((t) => !isLoopbackAddr(t));
8397
8673
  if (offender !== void 0) {
8398
- console.error(`${TAG18} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
8674
+ console.error(`${TAG19} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
8399
8675
  return c.json({ error: "log-ingest-loopback-only" }, 403);
8400
8676
  }
8401
8677
  }
@@ -8437,18 +8713,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
8437
8713
  ]);
8438
8714
  var app17 = new Hono();
8439
8715
  app17.post("/", async (c) => {
8440
- const TAG22 = "[admin:events]";
8716
+ const TAG28 = "[admin:events]";
8441
8717
  let body;
8442
8718
  try {
8443
8719
  body = await c.req.json();
8444
8720
  } catch (err) {
8445
8721
  const detail = err instanceof Error ? err.message : String(err);
8446
- console.error(`${TAG22} reject reason=body-not-json detail=${detail}`);
8722
+ console.error(`${TAG28} reject reason=body-not-json detail=${detail}`);
8447
8723
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
8448
8724
  }
8449
8725
  const event = typeof body.event === "string" ? body.event : "";
8450
8726
  if (!ALLOWED_EVENTS.has(event)) {
8451
- console.error(`${TAG22} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
8727
+ console.error(`${TAG28} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
8452
8728
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
8453
8729
  }
8454
8730
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -9326,6 +9602,27 @@ function pickFulltextIndex(allowedScopes) {
9326
9602
  function isPublicOnlyScope(allowedScopes) {
9327
9603
  return Array.isArray(allowedScopes) && allowedScopes.length === 1 && allowedScopes[0] === "public";
9328
9604
  }
9605
+ function buildScopeAndSliceClause(allowedScopes, sliceToken, alias = "node") {
9606
+ const hasSlice = typeof sliceToken === "string" && sliceToken.length > 0;
9607
+ const hasScopes = Array.isArray(allowedScopes) && allowedScopes.length > 0;
9608
+ if (!hasSlice && !hasScopes) return { clause: "", params: {} };
9609
+ if (hasSlice && hasScopes) {
9610
+ return {
9611
+ clause: `AND (${alias}.sliceToken = $sliceToken OR ${alias}.scope IS NULL OR ${alias}.scope IN $allowedScopes)`,
9612
+ params: { sliceToken, allowedScopes }
9613
+ };
9614
+ }
9615
+ if (hasSlice) {
9616
+ return {
9617
+ clause: `AND (${alias}.sliceToken = $sliceToken OR ${alias}.scope IS NULL)`,
9618
+ params: { sliceToken }
9619
+ };
9620
+ }
9621
+ return {
9622
+ clause: `AND (${alias}.scope IS NULL OR ${alias}.scope IN $allowedScopes)`,
9623
+ params: { allowedScopes }
9624
+ };
9625
+ }
9329
9626
  function escapeLucene(query) {
9330
9627
  return query.replace(/[+\-&|!(){}[\]^"~*?:\\/]/g, "\\$&");
9331
9628
  }
@@ -9361,8 +9658,9 @@ function buildKeywordFilter(keywords, keywordMatch = "any") {
9361
9658
  };
9362
9659
  }
9363
9660
  async function bm25Only(session, params) {
9364
- const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch, labels } = params;
9365
- const scopeClause = allowedScopes ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)" : "";
9661
+ const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch, labels, sliceToken } = params;
9662
+ const scope = buildScopeAndSliceClause(allowedScopes, sliceToken);
9663
+ const scopeClause = scope.clause;
9366
9664
  const agentClause = agentSlug ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents" : "";
9367
9665
  const labelClause = labels && labels.length > 0 ? "AND any(l IN labels(node) WHERE l IN $labels)" : "";
9368
9666
  const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
@@ -9386,7 +9684,7 @@ async function bm25Only(session, params) {
9386
9684
  query: escaped,
9387
9685
  accountId,
9388
9686
  limit: int(limit),
9389
- ...allowedScopes ? { allowedScopes } : {},
9687
+ ...scope.params,
9390
9688
  ...agentSlug ? { agentSlug } : {},
9391
9689
  ...labels && labels.length > 0 ? { labels } : {},
9392
9690
  ...keywordFilter?.params ?? {}
@@ -9418,6 +9716,7 @@ async function hybrid(session, embed2, params) {
9418
9716
  accountId,
9419
9717
  limit,
9420
9718
  allowedScopes,
9719
+ sliceToken,
9421
9720
  keywords,
9422
9721
  keywordMatch = "any",
9423
9722
  agentSlug,
@@ -9496,22 +9795,18 @@ async function hybrid(session, embed2, params) {
9496
9795
  if (effectiveLabels && effectiveLabels.length > 0) {
9497
9796
  indexesToQuery = effectiveLabels.map((l) => labelToIndex.get(l)).filter((idx) => idx !== void 0);
9498
9797
  if (indexesToQuery.length === 0) {
9499
- return {
9500
- mode: useRrf ? "rrf" : "hybrid",
9501
- results: [],
9502
- expandMs: 0,
9503
- rawMerged: 0,
9504
- suppressed: 0,
9505
- bm25Bypass: 0,
9506
- threshold: params.vectorThreshold ?? null,
9507
- stageCounts: emptyStageCounts
9508
- };
9798
+ const missing = effectiveLabels.filter((l) => !labelToIndex.has(l));
9799
+ process.stderr.write(
9800
+ `[graph-search] labels-without-vector-index=${missing.join(",")} accountId=${(accountId ?? "").slice(0, 8)} \u2014 falling through to BM25-only for these labels
9801
+ `
9802
+ );
9509
9803
  }
9510
9804
  } else {
9511
9805
  indexesToQuery = [...new Set(labelToIndex.values())];
9512
9806
  }
9513
- const scopeClause = allowedScopes ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)" : "";
9514
- const scopeParams = allowedScopes ? { allowedScopes } : {};
9807
+ const hybridScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
9808
+ const scopeClause = hybridScope.clause;
9809
+ const scopeParams = hybridScope.params;
9515
9810
  const agentClause = agentSlug ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents" : "";
9516
9811
  const agentParams = agentSlug ? { agentSlug } : {};
9517
9812
  const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
@@ -9626,7 +9921,8 @@ async function hybrid(session, embed2, params) {
9626
9921
  query: kw,
9627
9922
  accountId,
9628
9923
  limit,
9629
- allowedScopes
9924
+ allowedScopes,
9925
+ sliceToken
9630
9926
  });
9631
9927
  if (kwHits.length === 0) continue;
9632
9928
  const rawScores = kwHits.map((h) => h.score);
@@ -9635,21 +9931,21 @@ async function hybrid(session, embed2, params) {
9635
9931
  mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
9636
9932
  }
9637
9933
  }
9638
- const propScopeClause = allowedScopes ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)" : "";
9934
+ const propScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
9639
9935
  const propResult = await session.run(
9640
9936
  `MATCH (node)
9641
9937
  WHERE node.accountId = $accountId
9642
9938
  AND ${(0, import_dist.notTrashed)("node")}
9643
9939
  AND node.keywords IS NOT NULL
9644
9940
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
9645
- ${propScopeClause}
9941
+ ${propScope.clause}
9646
9942
  RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
9647
9943
  LIMIT $limit`,
9648
9944
  {
9649
9945
  accountId,
9650
9946
  kwSubs: keywordSubscriptions,
9651
9947
  limit: int(limit),
9652
- ...allowedScopes ? { allowedScopes } : {}
9948
+ ...propScope.params
9653
9949
  }
9654
9950
  );
9655
9951
  for (const record of propResult.records) {
@@ -9677,7 +9973,8 @@ async function hybrid(session, embed2, params) {
9677
9973
  query: kw,
9678
9974
  accountId,
9679
9975
  limit,
9680
- allowedScopes
9976
+ allowedScopes,
9977
+ sliceToken
9681
9978
  });
9682
9979
  if (kwHits.length === 0) continue;
9683
9980
  const rawScores = kwHits.map((h) => h.score);
@@ -9686,21 +9983,21 @@ async function hybrid(session, embed2, params) {
9686
9983
  mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
9687
9984
  }
9688
9985
  }
9689
- const propScopeClause = allowedScopes ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)" : "";
9986
+ const propScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
9690
9987
  const propResult = await session.run(
9691
9988
  `MATCH (node)
9692
9989
  WHERE node.accountId = $accountId
9693
9990
  AND ${(0, import_dist.notTrashed)("node")}
9694
9991
  AND node.keywords IS NOT NULL
9695
9992
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
9696
- ${propScopeClause}
9993
+ ${propScope.clause}
9697
9994
  RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
9698
9995
  LIMIT $limit`,
9699
9996
  {
9700
9997
  accountId,
9701
9998
  kwSubs: keywordSubscriptions,
9702
9999
  limit: int(limit),
9703
- ...allowedScopes ? { allowedScopes } : {}
10000
+ ...propScope.params
9704
10001
  }
9705
10002
  );
9706
10003
  for (const record of propResult.records) {
@@ -10731,7 +11028,7 @@ app21.post("/", requireAdminSession, async (c) => {
10731
11028
  return c.json({ error: "AdminConversation node is missing sessionId" }, 500);
10732
11029
  }
10733
11030
  const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "graph-page-cascade" });
10734
- const managerBase3 = `http://127.0.0.1:${managerPort}`;
11031
+ const managerBase4 = `http://127.0.0.1:${managerPort}`;
10735
11032
  try {
10736
11033
  await session.close();
10737
11034
  } catch {
@@ -10739,7 +11036,7 @@ app21.post("/", requireAdminSession, async (c) => {
10739
11036
  const outcome = await cascadeAdminConversationDelete({
10740
11037
  sessionId,
10741
11038
  accountId,
10742
- managerBase: managerBase3
11039
+ managerBase: managerBase4
10743
11040
  });
10744
11041
  const elapsed2 = Date.now() - started;
10745
11042
  const labelSummary2 = preflightLabels.join(",");
@@ -11848,7 +12145,7 @@ app31.get("/", async (c) => {
11848
12145
  var health_default2 = app31;
11849
12146
 
11850
12147
  // server/routes/admin/linkedin-ingest.ts
11851
- var TAG19 = "[linkedin-ingest-route]";
12148
+ var TAG20 = "[linkedin-ingest-route]";
11852
12149
  var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
11853
12150
  var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
11854
12151
  var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
@@ -11947,25 +12244,25 @@ app32.post("/", requireAdminSession, async (c) => {
11947
12244
  try {
11948
12245
  body = await c.req.json();
11949
12246
  } catch {
11950
- console.error(TAG19 + " rejected status=400 reason=schema:body-not-json");
12247
+ console.error(TAG20 + " rejected status=400 reason=schema:body-not-json");
11951
12248
  return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
11952
12249
  }
11953
12250
  const v = validate(body);
11954
12251
  if (!v.ok) {
11955
- console.error(TAG19 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
12252
+ console.error(TAG20 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
11956
12253
  return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
11957
12254
  }
11958
12255
  const envelope = v.envelope;
11959
12256
  const cacheKey = c.var.cacheKey ?? "";
11960
12257
  const senderId = getAccountIdForSession(cacheKey) ?? "";
11961
12258
  if (!senderId) {
11962
- console.error(TAG19 + " rejected status=500 reason=admin-account-not-resolved");
12259
+ console.error(TAG20 + " rejected status=500 reason=admin-account-not-resolved");
11963
12260
  return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
11964
12261
  }
11965
12262
  const userId = getUserIdForSession(cacheKey) ?? void 0;
11966
12263
  const payloadBytes = JSON.stringify(envelope).length;
11967
12264
  console.log(
11968
- TAG19 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
12265
+ TAG20 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
11969
12266
  );
11970
12267
  const initialMessage = buildInitialMessage(envelope);
11971
12268
  const spawnStart = Date.now();
@@ -11987,12 +12284,12 @@ app32.post("/", requireAdminSession, async (c) => {
11987
12284
  if (!response.ok) {
11988
12285
  const detail = await response.text().catch(() => "");
11989
12286
  console.error(
11990
- TAG19 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + response.status + " ms=" + (Date.now() - spawnStart) + " detail=" + detail.slice(0, 200)
12287
+ TAG20 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + response.status + " ms=" + (Date.now() - spawnStart) + " detail=" + detail.slice(0, 200)
11991
12288
  );
11992
12289
  return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: response.status, detail: detail.slice(0, 500) }, 502);
11993
12290
  }
11994
12291
  console.log(
11995
- TAG19 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + (claudeSessionId ?? "unknown") + " ms=" + (Date.now() - spawnStart)
12292
+ TAG20 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + (claudeSessionId ?? "unknown") + " ms=" + (Date.now() - spawnStart)
11996
12293
  );
11997
12294
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: claudeSessionId }, 202);
11998
12295
  });
@@ -12000,7 +12297,7 @@ var linkedin_ingest_default = app32;
12000
12297
 
12001
12298
  // server/routes/admin/post-turn-context.ts
12002
12299
  import neo4j3 from "neo4j-driver";
12003
- var TAG20 = "[post-turn-context]";
12300
+ var TAG21 = "[post-turn-context]";
12004
12301
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
12005
12302
  "embedding",
12006
12303
  "passwordHash",
@@ -12042,7 +12339,7 @@ var app33 = new Hono();
12042
12339
  app33.get("/", async (c) => {
12043
12340
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12044
12341
  if (!isLoopbackAddr2(remoteAddr)) {
12045
- console.error(`${TAG20} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12342
+ console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12046
12343
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
12047
12344
  }
12048
12345
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -12051,7 +12348,7 @@ app33.get("/", async (c) => {
12051
12348
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12052
12349
  const offender = tokens.find((t) => !isLoopbackAddr2(t));
12053
12350
  if (offender !== void 0) {
12054
- console.error(`${TAG20} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12351
+ console.error(`${TAG21} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12055
12352
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
12056
12353
  }
12057
12354
  }
@@ -12083,7 +12380,7 @@ app33.get("/", async (c) => {
12083
12380
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
12084
12381
  const total = Date.now() - started;
12085
12382
  console.log(
12086
- `${TAG20} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
12383
+ `${TAG21} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
12087
12384
  );
12088
12385
  return c.json({
12089
12386
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -12092,7 +12389,7 @@ app33.get("/", async (c) => {
12092
12389
  const elapsed = Date.now() - started;
12093
12390
  const message = err instanceof Error ? err.message : String(err);
12094
12391
  console.error(
12095
- `${TAG20} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
12392
+ `${TAG21} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
12096
12393
  );
12097
12394
  return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
12098
12395
  } finally {
@@ -12101,41 +12398,659 @@ app33.get("/", async (c) => {
12101
12398
  });
12102
12399
  var post_turn_context_default = app33;
12103
12400
 
12104
- // server/routes/admin/index.ts
12401
+ // server/routes/admin/public-session-context.ts
12402
+ import neo4j4 from "neo4j-driver";
12403
+ var TAG22 = "[public-session-context]";
12404
+ var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
12405
+ "embedding",
12406
+ "passwordHash",
12407
+ "magicToken",
12408
+ "otpCode",
12409
+ "cacheKey"
12410
+ ]);
12411
+ function isLoopbackAddr3(addr) {
12412
+ return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12413
+ }
12414
+ function convertValue2(value) {
12415
+ if (neo4j4.isDateTime(value) || neo4j4.isDate(value) || neo4j4.isLocalDateTime(value) || neo4j4.isLocalTime(value) || neo4j4.isTime(value) || neo4j4.isDuration(value)) {
12416
+ return value.toString();
12417
+ }
12418
+ if (neo4j4.isInt(value)) {
12419
+ return value.inSafeRange() ? value.toNumber() : value.toString();
12420
+ }
12421
+ if (Array.isArray(value)) {
12422
+ return value.map((v) => convertValue2(v));
12423
+ }
12424
+ if (value !== null && typeof value === "object" && value.constructor === Object) {
12425
+ const out = {};
12426
+ for (const [k, v] of Object.entries(value)) {
12427
+ out[k] = convertValue2(v);
12428
+ }
12429
+ return out;
12430
+ }
12431
+ return value;
12432
+ }
12433
+ function pruneProperties2(raw) {
12434
+ const out = {};
12435
+ for (const [k, v] of Object.entries(raw)) {
12436
+ if (STRIPPED_PROPERTIES3.has(k)) continue;
12437
+ out[k] = convertValue2(v);
12438
+ }
12439
+ return out;
12440
+ }
12105
12441
  var app34 = new Hono();
12106
- app34.route("/session", session_default);
12107
- app34.route("/new-session-failure", new_session_failure_default);
12108
- app34.route("/new-session-submit", new_session_submit_default);
12109
- app34.route("/logs", logs_default);
12110
- app34.route("/claude-info", claude_info_default);
12111
- app34.route("/claude-capabilities", claude_capabilities_default);
12112
- app34.route("/attachment", attachment_default);
12113
- app34.route("/agents", agents_default);
12114
- app34.route("/sessions", sessions_default);
12115
- app34.route("/claude-sessions", claude_sessions_default);
12116
- app34.route("/log-ingest", log_ingest_default);
12117
- app34.route("/events", events_default);
12118
- app34.route("/files", files_default);
12119
- app34.route("/graph-search", graph_search_default);
12120
- app34.route("/graph-subgraph", graph_subgraph_default);
12121
- app34.route("/graph-delete", graph_delete_default);
12122
- app34.route("/graph-restore", graph_restore_default);
12123
- app34.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12124
- app34.route("/graph-default-view", graph_default_view_default);
12125
- app34.route("/session-defaults", session_defaults_default);
12126
- app34.route("/file-attach", file_attach_default);
12127
- app34.route("/sidebar-artefacts", sidebar_artefacts_default);
12128
- app34.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12129
- app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12130
- app34.route("/system-stats", system_stats_default);
12131
- app34.route("/health-brand", health_default2);
12132
- app34.route("/linkedin-ingest", linkedin_ingest_default);
12133
- app34.route("/post-turn-context", post_turn_context_default);
12134
- var admin_default = app34;
12442
+ app34.get("/", async (c) => {
12443
+ const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12444
+ if (!isLoopbackAddr3(remoteAddr)) {
12445
+ console.error(`${TAG22} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12446
+ return c.json({ error: "public-session-context-loopback-only" }, 403);
12447
+ }
12448
+ const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
12449
+ const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
12450
+ if (xffRaw.length > 0) {
12451
+ const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12452
+ const offender = tokens.find((t) => !isLoopbackAddr3(t));
12453
+ if (offender !== void 0) {
12454
+ console.error(`${TAG22} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12455
+ return c.json({ error: "public-session-context-loopback-only" }, 403);
12456
+ }
12457
+ }
12458
+ const sliceToken = (c.req.query("sliceToken") ?? "").trim();
12459
+ const accountId = (c.req.query("accountId") ?? "").trim();
12460
+ if (!sliceToken) return c.json({ error: "sliceToken required" }, 400);
12461
+ if (!accountId) return c.json({ error: "accountId required" }, 400);
12462
+ const session = getSession();
12463
+ const started = Date.now();
12464
+ try {
12465
+ const cypher = `
12466
+ MATCH (n)
12467
+ WHERE n.sliceToken = $sliceToken AND n.accountId = $accountId
12468
+ RETURN elementId(n) AS elementId,
12469
+ labels(n) AS labels,
12470
+ properties(n) AS properties,
12471
+ coalesce(n.createdAt, n.observedAt, '') AS sortKey
12472
+ `;
12473
+ const res = await session.executeRead(
12474
+ (tx) => tx.run(cypher, { sliceToken, accountId })
12475
+ );
12476
+ const writes = res.records.map((r) => ({
12477
+ elementId: r.get("elementId"),
12478
+ labels: r.get("labels"),
12479
+ properties: pruneProperties2(r.get("properties")),
12480
+ sortKey: String(r.get("sortKey") ?? "")
12481
+ }));
12482
+ writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
12483
+ const total = Date.now() - started;
12484
+ console.log(
12485
+ `${TAG22} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
12486
+ );
12487
+ return c.json({
12488
+ writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
12489
+ });
12490
+ } catch (err) {
12491
+ const elapsed = Date.now() - started;
12492
+ const message = err instanceof Error ? err.message : String(err);
12493
+ console.error(
12494
+ `${TAG22} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
12495
+ );
12496
+ return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
12497
+ } finally {
12498
+ await session.close();
12499
+ }
12500
+ });
12501
+ var public_session_context_default = app34;
12135
12502
 
12136
- // server/routes/sites.ts
12137
- import { existsSync as existsSync21, readFileSync as readFileSync16, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
12503
+ // server/routes/admin/public-session-exit.ts
12504
+ var TAG23 = "[public-session-exit-route]";
12505
+ function isLoopbackAddr4(addr) {
12506
+ return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12507
+ }
12508
+ var app35 = new Hono();
12509
+ app35.post("/", async (c) => {
12510
+ const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12511
+ if (!isLoopbackAddr4(remoteAddr)) {
12512
+ console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12513
+ return c.json({ error: "public-session-exit-loopback-only" }, 403);
12514
+ }
12515
+ const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
12516
+ const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
12517
+ if (xffRaw.length > 0) {
12518
+ const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12519
+ const offender = tokens.find((t) => !isLoopbackAddr4(t));
12520
+ if (offender !== void 0) {
12521
+ console.error(`${TAG23} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12522
+ return c.json({ error: "public-session-exit-loopback-only" }, 403);
12523
+ }
12524
+ }
12525
+ let body;
12526
+ try {
12527
+ body = await c.req.json();
12528
+ } catch {
12529
+ return c.json({ error: "invalid-json" }, 400);
12530
+ }
12531
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId.trim() : "";
12532
+ if (!sessionId) return c.json({ error: "sessionId required" }, 400);
12533
+ handlePublicSessionExit(sessionId);
12534
+ return c.json({ ok: true });
12535
+ });
12536
+ var public_session_exit_default = app35;
12537
+
12538
+ // server/routes/admin/access-session-evict.ts
12539
+ var TAG24 = "[access-session-evict]";
12540
+ function isLoopbackAddr5(addr) {
12541
+ return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12542
+ }
12543
+ var app36 = new Hono();
12544
+ app36.post("/", async (c) => {
12545
+ const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12546
+ if (!isLoopbackAddr5(remoteAddr)) {
12547
+ console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12548
+ return c.json({ error: "access-session-evict-loopback-only" }, 403);
12549
+ }
12550
+ const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
12551
+ const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
12552
+ if (xffRaw.length > 0) {
12553
+ const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12554
+ const offender = tokens.find((t) => !isLoopbackAddr5(t));
12555
+ if (offender !== void 0) {
12556
+ console.error(`${TAG24} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12557
+ return c.json({ error: "access-session-evict-loopback-only" }, 403);
12558
+ }
12559
+ }
12560
+ let body;
12561
+ try {
12562
+ body = await c.req.json();
12563
+ } catch {
12564
+ return c.json({ error: "invalid-json" }, 400);
12565
+ }
12566
+ const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
12567
+ if (!grantId) return c.json({ error: "grantId required" }, 400);
12568
+ const dropped = evictAccessSessionsByGrant(grantId);
12569
+ console.log(`${TAG24} grantId=${grantId} dropped=${dropped}`);
12570
+ return c.json({ ok: true, dropped });
12571
+ });
12572
+ var access_session_evict_default = app36;
12573
+
12574
+ // server/routes/admin/index.ts
12575
+ var app37 = new Hono();
12576
+ app37.route("/session", session_default);
12577
+ app37.route("/new-session-failure", new_session_failure_default);
12578
+ app37.route("/new-session-submit", new_session_submit_default);
12579
+ app37.route("/logs", logs_default);
12580
+ app37.route("/claude-info", claude_info_default);
12581
+ app37.route("/claude-capabilities", claude_capabilities_default);
12582
+ app37.route("/attachment", attachment_default);
12583
+ app37.route("/agents", agents_default);
12584
+ app37.route("/sessions", sessions_default);
12585
+ app37.route("/claude-sessions", claude_sessions_default);
12586
+ app37.route("/log-ingest", log_ingest_default);
12587
+ app37.route("/events", events_default);
12588
+ app37.route("/files", files_default);
12589
+ app37.route("/graph-search", graph_search_default);
12590
+ app37.route("/graph-subgraph", graph_subgraph_default);
12591
+ app37.route("/graph-delete", graph_delete_default);
12592
+ app37.route("/graph-restore", graph_restore_default);
12593
+ app37.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12594
+ app37.route("/graph-default-view", graph_default_view_default);
12595
+ app37.route("/session-defaults", session_defaults_default);
12596
+ app37.route("/file-attach", file_attach_default);
12597
+ app37.route("/sidebar-artefacts", sidebar_artefacts_default);
12598
+ app37.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12599
+ app37.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12600
+ app37.route("/system-stats", system_stats_default);
12601
+ app37.route("/health-brand", health_default2);
12602
+ app37.route("/linkedin-ingest", linkedin_ingest_default);
12603
+ app37.route("/post-turn-context", post_turn_context_default);
12604
+ app37.route("/public-session-context", public_session_context_default);
12605
+ app37.route("/public-session-exit", public_session_exit_default);
12606
+ app37.route("/access-session-evict", access_session_evict_default);
12607
+ var admin_default = app37;
12608
+
12609
+ // app/lib/access-gate.ts
12610
+ import neo4j5 from "neo4j-driver";
12611
+ import { readFileSync as readFileSync16 } from "fs";
12138
12612
  import { resolve as resolve18 } from "path";
12613
+ import { randomUUID as randomUUID8 } from "crypto";
12614
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
12615
+ var driver = null;
12616
+ function readPassword() {
12617
+ if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
12618
+ const passwordFile = resolve18(PLATFORM_ROOT7, "config/.neo4j-password");
12619
+ try {
12620
+ return readFileSync16(passwordFile, "utf-8").trim();
12621
+ } catch {
12622
+ throw new Error(
12623
+ `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
12624
+ );
12625
+ }
12626
+ }
12627
+ function getDriver() {
12628
+ if (!driver) {
12629
+ const uri = process.env.NEO4J_URI;
12630
+ if (!uri) {
12631
+ throw new Error(
12632
+ "[ui/access-gate] NEO4J_URI unset \u2014 refusing to default to bolt://localhost:7687"
12633
+ );
12634
+ }
12635
+ const user = process.env.NEO4J_USER ?? "neo4j";
12636
+ const password = readPassword();
12637
+ console.error(`[ui/access-gate] resolved neo4j_uri=${uri}`);
12638
+ driver = neo4j5.driver(uri, neo4j5.auth.basic(user, password), {
12639
+ maxConnectionPoolSize: 5
12640
+ });
12641
+ }
12642
+ return driver;
12643
+ }
12644
+ function getSession3() {
12645
+ return getDriver().session();
12646
+ }
12647
+ process.on("SIGINT", async () => {
12648
+ if (driver) {
12649
+ await driver.close();
12650
+ driver = null;
12651
+ }
12652
+ });
12653
+ var rateLimitMap = /* @__PURE__ */ new Map();
12654
+ var ACCESS_MAX_ATTEMPTS = 5;
12655
+ var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
12656
+ var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
12657
+ function checkAccessRateLimit(ip, agentSlug) {
12658
+ const key = `${ip}:${agentSlug}`;
12659
+ const entry = rateLimitMap.get(key);
12660
+ if (!entry) return null;
12661
+ const now = Date.now();
12662
+ if (entry.lockedUntil && now < entry.lockedUntil) {
12663
+ const remainingMs = entry.lockedUntil - now;
12664
+ const remainingS = Math.ceil(remainingMs / 1e3);
12665
+ return `Too many attempts. Try again in ${remainingS}s`;
12666
+ }
12667
+ if (now - entry.firstAttempt > ACCESS_WINDOW_MS) {
12668
+ rateLimitMap.delete(key);
12669
+ return null;
12670
+ }
12671
+ return null;
12672
+ }
12673
+ function recordAccessFailedAttempt(ip, agentSlug) {
12674
+ const key = `${ip}:${agentSlug}`;
12675
+ const now = Date.now();
12676
+ const entry = rateLimitMap.get(key);
12677
+ if (!entry || now - entry.firstAttempt > ACCESS_WINDOW_MS) {
12678
+ rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
12679
+ return;
12680
+ }
12681
+ entry.attempts++;
12682
+ if (entry.attempts >= ACCESS_MAX_ATTEMPTS) {
12683
+ entry.lockedUntil = now + ACCESS_LOCKOUT_MS;
12684
+ }
12685
+ }
12686
+ function clearAccessRateLimit(ip, agentSlug) {
12687
+ rateLimitMap.delete(`${ip}:${agentSlug}`);
12688
+ }
12689
+ var contactRateMap = /* @__PURE__ */ new Map();
12690
+ var REQUEST_LINK_MAX = 3;
12691
+ var REQUEST_LINK_WINDOW_MS = 60 * 60 * 1e3;
12692
+ function checkRequestLinkRateLimit(contactValue) {
12693
+ const entry = contactRateMap.get(contactValue);
12694
+ if (!entry) return null;
12695
+ const now = Date.now();
12696
+ if (now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
12697
+ contactRateMap.delete(contactValue);
12698
+ return null;
12699
+ }
12700
+ if (entry.count >= REQUEST_LINK_MAX) {
12701
+ const remainingMs = REQUEST_LINK_WINDOW_MS - (now - entry.windowStart);
12702
+ const remainingM = Math.ceil(remainingMs / 6e4);
12703
+ return `Too many requests. Try again in ${remainingM} minute(s)`;
12704
+ }
12705
+ return null;
12706
+ }
12707
+ function recordRequestLinkAttempt(contactValue) {
12708
+ const now = Date.now();
12709
+ const entry = contactRateMap.get(contactValue);
12710
+ if (!entry || now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
12711
+ contactRateMap.set(contactValue, { count: 1, windowStart: now });
12712
+ return;
12713
+ }
12714
+ entry.count++;
12715
+ }
12716
+ function maskContact(value) {
12717
+ if (value.includes("@")) {
12718
+ const [local, domain] = value.split("@");
12719
+ return `${local[0]}***@${domain}`;
12720
+ }
12721
+ return "****";
12722
+ }
12723
+ function readGrant(record) {
12724
+ const g = record.get("g").properties;
12725
+ const personId = record.get("personId");
12726
+ return {
12727
+ grantId: record.get("grantId"),
12728
+ agentSlug: String(g.agentSlug),
12729
+ accountId: String(g.accountId),
12730
+ contactValue: String(g.contactValue),
12731
+ displayName: record.get("givenName") ?? null,
12732
+ personId,
12733
+ expiresAt: g.expiresAt ? new Date(String(g.expiresAt)).getTime() : null,
12734
+ status: String(g.status),
12735
+ sliceToken: String(g.sliceToken ?? "")
12736
+ };
12737
+ }
12738
+ async function findGrantByMagicToken(token) {
12739
+ const session = getSession3();
12740
+ try {
12741
+ const result = await session.run(
12742
+ `MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant {magicToken: $token})
12743
+ RETURN g, p.givenName AS givenName,
12744
+ elementId(g) AS grantId,
12745
+ elementId(p) AS personId`,
12746
+ { token }
12747
+ );
12748
+ if (result.records.length === 0) return null;
12749
+ return readGrant(result.records[0]);
12750
+ } finally {
12751
+ await session.close();
12752
+ }
12753
+ }
12754
+ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
12755
+ const session = getSession3();
12756
+ try {
12757
+ const result = await session.run(
12758
+ `MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant {
12759
+ contactValue: $contactValue,
12760
+ agentSlug: $agentSlug,
12761
+ accountId: $accountId
12762
+ })
12763
+ WHERE g.status IN ['invited', 'active']
12764
+ RETURN g, p.givenName AS givenName,
12765
+ elementId(g) AS grantId,
12766
+ elementId(p) AS personId`,
12767
+ { contactValue, agentSlug, accountId }
12768
+ );
12769
+ if (result.records.length === 0) return null;
12770
+ return readGrant(result.records[0]);
12771
+ } finally {
12772
+ await session.close();
12773
+ }
12774
+ }
12775
+ async function consumeMagicTokenAndActivate(grantId) {
12776
+ const session = getSession3();
12777
+ try {
12778
+ const result = await session.run(
12779
+ `MATCH (g:AccessGrant) WHERE elementId(g) = $grantId
12780
+ SET g.magicToken = null,
12781
+ g.magicTokenExpiresAt = null,
12782
+ g.status = "active"
12783
+ RETURN count(g) AS affected`,
12784
+ { grantId }
12785
+ );
12786
+ const affected = result.records[0]?.get("affected");
12787
+ const count = typeof affected === "number" ? affected : affected?.toNumber?.() ?? 0;
12788
+ if (count === 0) {
12789
+ throw new Error(`[access-gate] consumeMagicTokenAndActivate: no grant matched grantId=${grantId}`);
12790
+ }
12791
+ } finally {
12792
+ await session.close();
12793
+ }
12794
+ }
12795
+ async function generateNewMagicToken(grantId) {
12796
+ const token = randomUUID8();
12797
+ const session = getSession3();
12798
+ try {
12799
+ const result = await session.run(
12800
+ `MATCH (g:AccessGrant) WHERE elementId(g) = $grantId
12801
+ SET g.magicToken = $token,
12802
+ g.magicTokenExpiresAt = datetime() + duration('PT15M')
12803
+ RETURN count(g) AS affected`,
12804
+ { grantId, token }
12805
+ );
12806
+ const affected = result.records[0]?.get("affected");
12807
+ const count = typeof affected === "number" ? affected : affected?.toNumber?.() ?? 0;
12808
+ if (count === 0) {
12809
+ throw new Error(`[access-gate] generateNewMagicToken: no grant matched grantId=${grantId}`);
12810
+ }
12811
+ } finally {
12812
+ await session.close();
12813
+ }
12814
+ return token;
12815
+ }
12816
+
12817
+ // server/routes/access/verify-token.ts
12818
+ var TAG25 = "[access-verify]";
12819
+ var MINT_TAG = "[access-session-mint]";
12820
+ var COOKIE_NAME = "__access_session";
12821
+ var app38 = new Hono();
12822
+ app38.post("/", async (c) => {
12823
+ const ip = c.var.clientIp || "unknown";
12824
+ let body;
12825
+ try {
12826
+ body = await c.req.json();
12827
+ } catch {
12828
+ return c.json({ error: "invalid-json" }, 400);
12829
+ }
12830
+ const token = typeof body.token === "string" ? body.token.trim() : "";
12831
+ const agentSlug = typeof body.agentSlug === "string" ? body.agentSlug.trim() : "";
12832
+ if (!token || !agentSlug) {
12833
+ return c.json({ error: "token and agentSlug required" }, 400);
12834
+ }
12835
+ const rateMsg = checkAccessRateLimit(ip, agentSlug);
12836
+ if (rateMsg) {
12837
+ console.error(`${TAG25} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
12838
+ return c.json({ error: rateMsg }, 429);
12839
+ }
12840
+ const grant = await findGrantByMagicToken(token);
12841
+ if (!grant) {
12842
+ recordAccessFailedAttempt(ip, agentSlug);
12843
+ console.error(`${TAG25} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
12844
+ return c.json({ error: "invalid-or-expired-link" }, 401);
12845
+ }
12846
+ if (grant.agentSlug !== agentSlug) {
12847
+ recordAccessFailedAttempt(ip, agentSlug);
12848
+ console.error(
12849
+ `${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
12850
+ );
12851
+ return c.json({ error: "invalid-or-expired-link" }, 401);
12852
+ }
12853
+ if (grant.status === "expired" || grant.status === "revoked") {
12854
+ recordAccessFailedAttempt(ip, agentSlug);
12855
+ console.error(
12856
+ `${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
12857
+ );
12858
+ return c.json({ error: "access-no-longer-valid" }, 401);
12859
+ }
12860
+ if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
12861
+ recordAccessFailedAttempt(ip, agentSlug);
12862
+ console.error(
12863
+ `${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
12864
+ );
12865
+ return c.json({ error: "access-no-longer-valid" }, 401);
12866
+ }
12867
+ if (!grant.sliceToken) {
12868
+ console.error(
12869
+ `${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
12870
+ );
12871
+ return c.json({ error: "grant-misconfigured" }, 500);
12872
+ }
12873
+ const session = createAccessSession({
12874
+ grantId: grant.grantId,
12875
+ sliceToken: grant.sliceToken,
12876
+ agentSlug: grant.agentSlug,
12877
+ accountId: grant.accountId,
12878
+ personId: grant.personId,
12879
+ contactValue: grant.contactValue
12880
+ });
12881
+ try {
12882
+ await consumeMagicTokenAndActivate(grant.grantId);
12883
+ } catch (err) {
12884
+ console.error(
12885
+ `${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
12886
+ );
12887
+ return c.json({ error: "verification-failed" }, 500);
12888
+ }
12889
+ clearAccessRateLimit(ip, agentSlug);
12890
+ console.log(`${TAG25} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
12891
+ console.log(
12892
+ `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
12893
+ );
12894
+ c.header(
12895
+ "Set-Cookie",
12896
+ `${COOKIE_NAME}=${session.sessionId}; HttpOnly; Secure; SameSite=Lax; Path=/`
12897
+ );
12898
+ return c.json({
12899
+ ok: true,
12900
+ agentSlug,
12901
+ displayName: grant.displayName
12902
+ });
12903
+ });
12904
+ var verify_token_default = app38;
12905
+
12906
+ // app/lib/access-email.ts
12907
+ import { spawn as spawn2 } from "child_process";
12908
+ import { resolve as resolve19 } from "path";
12909
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
12910
+ var SEND_SCRIPT = resolve19(
12911
+ PLATFORM_ROOT8,
12912
+ "plugins",
12913
+ "email",
12914
+ "mcp",
12915
+ "dist",
12916
+ "scripts",
12917
+ "send-transactional.js"
12918
+ );
12919
+ async function sendMagicLinkEmail(payload) {
12920
+ return new Promise((resolveResult) => {
12921
+ const child = spawn2("node", [SEND_SCRIPT], {
12922
+ stdio: ["pipe", "pipe", "pipe"],
12923
+ env: process.env
12924
+ });
12925
+ let stdout = "";
12926
+ let stderr = "";
12927
+ let settled = false;
12928
+ const timeout = setTimeout(() => {
12929
+ if (settled) return;
12930
+ settled = true;
12931
+ try {
12932
+ child.kill("SIGTERM");
12933
+ } catch {
12934
+ }
12935
+ resolveResult({ ok: false, error: "send-transactional timeout (30s)" });
12936
+ }, 3e4);
12937
+ child.stdout.on("data", (chunk) => {
12938
+ stdout += chunk.toString();
12939
+ });
12940
+ child.stderr.on("data", (chunk) => {
12941
+ stderr += chunk.toString();
12942
+ });
12943
+ child.on("error", (err) => {
12944
+ if (settled) return;
12945
+ settled = true;
12946
+ clearTimeout(timeout);
12947
+ resolveResult({ ok: false, error: `spawn-failed: ${err.message}` });
12948
+ });
12949
+ child.on("exit", (code) => {
12950
+ if (settled) return;
12951
+ settled = true;
12952
+ clearTimeout(timeout);
12953
+ if (code === 0) {
12954
+ const match = stdout.match(/^ok messageId=(.+)$/m);
12955
+ resolveResult({ ok: true, messageId: match?.[1]?.trim() ?? "unknown" });
12956
+ } else {
12957
+ resolveResult({ ok: false, error: stderr.trim() || `exit-${code}` });
12958
+ }
12959
+ });
12960
+ child.stdin.write(JSON.stringify(payload));
12961
+ child.stdin.end();
12962
+ });
12963
+ }
12964
+
12965
+ // server/routes/access/request-magic-link.ts
12966
+ var TAG26 = "[access-request-link]";
12967
+ var app39 = new Hono();
12968
+ var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
12969
+ app39.post("/", async (c) => {
12970
+ let body;
12971
+ try {
12972
+ body = await c.req.json();
12973
+ } catch {
12974
+ return c.json({ error: "invalid-json" }, 400);
12975
+ }
12976
+ const contactValue = typeof body.contactValue === "string" ? body.contactValue.trim().toLowerCase() : "";
12977
+ const agentSlug = typeof body.agentSlug === "string" ? body.agentSlug.trim() : "";
12978
+ if (!contactValue || !agentSlug) {
12979
+ return c.json({ error: "contactValue and agentSlug required" }, 400);
12980
+ }
12981
+ const rateMsg = checkRequestLinkRateLimit(contactValue);
12982
+ if (rateMsg) {
12983
+ console.error(`${TAG26} contactValue=${maskContact(contactValue)} result=rate-limited`);
12984
+ return c.json({ error: rateMsg }, 429);
12985
+ }
12986
+ recordRequestLinkAttempt(contactValue);
12987
+ const accountId = process.env.ACCOUNT_ID ?? "";
12988
+ if (!accountId) {
12989
+ console.error(`${TAG26} contactValue=${maskContact(contactValue)} result=no-account-id`);
12990
+ return c.json({ message: VISITOR_MESSAGE }, 200);
12991
+ }
12992
+ const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
12993
+ if (!grant) {
12994
+ console.log(`${TAG26} contactValue=${maskContact(contactValue)} result=notfound`);
12995
+ return c.json({ message: VISITOR_MESSAGE }, 200);
12996
+ }
12997
+ let token;
12998
+ try {
12999
+ token = await generateNewMagicToken(grant.grantId);
13000
+ } catch (err) {
13001
+ console.error(
13002
+ `${TAG26} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
13003
+ );
13004
+ return c.json({ message: VISITOR_MESSAGE }, 200);
13005
+ }
13006
+ const host = c.req.header("host") ?? "";
13007
+ const proto = (c.req.header("x-forwarded-proto") ?? "https").toLowerCase();
13008
+ const magicUrl = `${proto}://${host}/${agentSlug}?token=${token}`;
13009
+ const subject = `Your link to ${grant.agentSlug}`;
13010
+ const bodyText = [
13011
+ grant.displayName ? `Hi ${grant.displayName},` : "Hello,",
13012
+ "",
13013
+ `Here's a fresh link to access ${grant.agentSlug}:`,
13014
+ "",
13015
+ magicUrl,
13016
+ "",
13017
+ "This link expires in 15 minutes."
13018
+ ].join("\n");
13019
+ const htmlBody = `
13020
+ <p>${grant.displayName ? `Hi ${grant.displayName},` : "Hello,"}</p>
13021
+ <p>Here's a fresh link to access <strong>${grant.agentSlug}</strong>:</p>
13022
+ <p><a href="${magicUrl}" style="display:inline-block;padding:12px 24px;background:#000;color:#fff;text-decoration:none;border-radius:6px;">Open ${grant.agentSlug}</a></p>
13023
+ <p style="color:#888;font-size:14px;">This link expires in 15 minutes.</p>
13024
+ `;
13025
+ const sendResult = await sendMagicLinkEmail({
13026
+ accountId,
13027
+ to: grant.contactValue,
13028
+ subject,
13029
+ body: bodyText,
13030
+ html: htmlBody
13031
+ });
13032
+ if (!sendResult.ok) {
13033
+ console.error(
13034
+ `${TAG26} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
13035
+ );
13036
+ return c.json({ message: VISITOR_MESSAGE }, 200);
13037
+ }
13038
+ console.log(
13039
+ `${TAG26} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
13040
+ );
13041
+ return c.json({ message: VISITOR_MESSAGE }, 200);
13042
+ });
13043
+ var request_magic_link_default = app39;
13044
+
13045
+ // server/routes/access/index.ts
13046
+ var app40 = new Hono();
13047
+ app40.route("/verify-token", verify_token_default);
13048
+ app40.route("/request-magic-link", request_magic_link_default);
13049
+ var access_default = app40;
13050
+
13051
+ // server/routes/sites.ts
13052
+ import { existsSync as existsSync21, readFileSync as readFileSync17, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
13053
+ import { resolve as resolve20 } from "path";
12139
13054
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
12140
13055
  var MIME = {
12141
13056
  ".html": "text/html; charset=utf-8",
@@ -12166,8 +13081,8 @@ function getExt(p) {
12166
13081
  if (idx < p.lastIndexOf("/")) return "";
12167
13082
  return p.slice(idx).toLowerCase();
12168
13083
  }
12169
- var app35 = new Hono();
12170
- app35.get("/:rel{.*}", (c) => {
13084
+ var app41 = new Hono();
13085
+ app41.get("/:rel{.*}", (c) => {
12171
13086
  const reqPath = c.req.path;
12172
13087
  const rawRel = c.req.param("rel") ?? "";
12173
13088
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -12192,8 +13107,8 @@ app35.get("/:rel{.*}", (c) => {
12192
13107
  }
12193
13108
  segments.push(seg);
12194
13109
  }
12195
- const rootDir = resolve18(account.accountDir, "sites");
12196
- let filePath = segments.length === 0 ? rootDir : resolve18(rootDir, ...segments);
13110
+ const rootDir = resolve20(account.accountDir, "sites");
13111
+ let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
12197
13112
  if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
12198
13113
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
12199
13114
  return c.text("Forbidden", 403);
@@ -12213,7 +13128,7 @@ app35.get("/:rel{.*}", (c) => {
12213
13128
  return c.redirect(target, 301);
12214
13129
  }
12215
13130
  if (stat7?.isDirectory()) {
12216
- filePath = resolve18(filePath, "index.html");
13131
+ filePath = resolve20(filePath, "index.html");
12217
13132
  }
12218
13133
  if (!filePath.startsWith(rootDir + "/")) {
12219
13134
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
@@ -12238,7 +13153,7 @@ app35.get("/:rel{.*}", (c) => {
12238
13153
  }
12239
13154
  let body;
12240
13155
  try {
12241
- body = readFileSync16(realPath);
13156
+ body = readFileSync17(realPath);
12242
13157
  } catch (err) {
12243
13158
  const code = err?.code;
12244
13159
  if (code === "EISDIR") {
@@ -12270,11 +13185,11 @@ app35.get("/:rel{.*}", (c) => {
12270
13185
  "X-Content-Type-Options": "nosniff"
12271
13186
  });
12272
13187
  });
12273
- var sites_default = app35;
13188
+ var sites_default = app41;
12274
13189
 
12275
13190
  // app/lib/visitor-token.ts
12276
13191
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
12277
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "fs";
13192
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync6 } from "fs";
12278
13193
  import { dirname as dirname5 } from "path";
12279
13194
  var TOKEN_PREFIX = "v1.";
12280
13195
  var SECRET_BYTES = 32;
@@ -12283,7 +13198,7 @@ var cachedSecret = null;
12283
13198
  function getSecret() {
12284
13199
  if (cachedSecret) return cachedSecret;
12285
13200
  try {
12286
- const hex2 = readFileSync17(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
13201
+ const hex2 = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
12287
13202
  if (hex2.length === SECRET_BYTES * 2) {
12288
13203
  cachedSecret = Buffer.from(hex2, "hex");
12289
13204
  return cachedSecret;
@@ -12297,7 +13212,7 @@ function getSecret() {
12297
13212
  console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
12298
13213
  } catch {
12299
13214
  }
12300
- const hex = readFileSync17(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
13215
+ const hex = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
12301
13216
  cachedSecret = Buffer.from(hex, "hex");
12302
13217
  return cachedSecret;
12303
13218
  }
@@ -12350,7 +13265,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
12350
13265
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
12351
13266
 
12352
13267
  // app/lib/brand-config.ts
12353
- import { existsSync as existsSync22, readFileSync as readFileSync18 } from "fs";
13268
+ import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
12354
13269
  import { join as join14 } from "path";
12355
13270
  var cached2 = null;
12356
13271
  var cachedAttempted = false;
@@ -12362,7 +13277,7 @@ function readBrandConfig() {
12362
13277
  const brandPath = join14(platformRoot, "config", "brand.json");
12363
13278
  if (!existsSync22(brandPath)) return null;
12364
13279
  try {
12365
- cached2 = JSON.parse(readFileSync18(brandPath, "utf-8"));
13280
+ cached2 = JSON.parse(readFileSync19(brandPath, "utf-8"));
12366
13281
  return cached2;
12367
13282
  } catch {
12368
13283
  return null;
@@ -12370,7 +13285,7 @@ function readBrandConfig() {
12370
13285
  }
12371
13286
 
12372
13287
  // server/routes/visitor-consent.ts
12373
- var app36 = new Hono();
13288
+ var app42 = new Hono();
12374
13289
  var CONSENT_COOKIE_NAME = "mxy_consent";
12375
13290
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
12376
13291
  var DEFAULT_CONSENT_COPY = {
@@ -12415,17 +13330,17 @@ function siteSlugFromReferer(referer) {
12415
13330
  return "";
12416
13331
  }
12417
13332
  }
12418
- app36.options("/consent", (c) => {
13333
+ app42.options("/consent", (c) => {
12419
13334
  const origin = getOrigin(c);
12420
13335
  setCorsHeaders(c, origin);
12421
13336
  return c.body(null, 204);
12422
13337
  });
12423
- app36.options("/brand-config", (c) => {
13338
+ app42.options("/brand-config", (c) => {
12424
13339
  const origin = getOrigin(c);
12425
13340
  setCorsHeaders(c, origin);
12426
13341
  return c.body(null, 204);
12427
13342
  });
12428
- app36.post("/consent", async (c) => {
13343
+ app42.post("/consent", async (c) => {
12429
13344
  const origin = getOrigin(c);
12430
13345
  setCorsHeaders(c, origin);
12431
13346
  let raw;
@@ -12465,7 +13380,7 @@ app36.post("/consent", async (c) => {
12465
13380
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
12466
13381
  return c.body(null, 204);
12467
13382
  });
12468
- app36.get("/brand-config", (c) => {
13383
+ app42.get("/brand-config", (c) => {
12469
13384
  const origin = getOrigin(c);
12470
13385
  setCorsHeaders(c, origin);
12471
13386
  const brand = readBrandConfig();
@@ -12475,7 +13390,7 @@ app36.get("/brand-config", (c) => {
12475
13390
  c.header("Cache-Control", "public, max-age=300");
12476
13391
  return c.json({ consent: { copy, palette } });
12477
13392
  });
12478
- var visitor_consent_default = app36;
13393
+ var visitor_consent_default = app42;
12479
13394
 
12480
13395
  // server/routes/listings.ts
12481
13396
  function getCookie(headerValue, name) {
@@ -12502,8 +13417,8 @@ function appendConsentParams(pageUrl, token) {
12502
13417
  }
12503
13418
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
12504
13419
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
12505
- var app37 = new Hono();
12506
- app37.get("/:slug/click", async (c) => {
13420
+ var app43 = new Hono();
13421
+ app43.get("/:slug/click", async (c) => {
12507
13422
  const slug = c.req.param("slug") ?? "";
12508
13423
  const rawSession = c.req.query("session") ?? "";
12509
13424
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -12567,10 +13482,10 @@ app37.get("/:slug/click", async (c) => {
12567
13482
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
12568
13483
  return c.redirect(redirectUrl, 302);
12569
13484
  });
12570
- var listings_default = app37;
13485
+ var listings_default = app43;
12571
13486
 
12572
13487
  // server/routes/visitor-event.ts
12573
- var app38 = new Hono();
13488
+ var app44 = new Hono();
12574
13489
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
12575
13490
  var buckets = /* @__PURE__ */ new Map();
12576
13491
  var RATE_LIMIT = 60;
@@ -12637,12 +13552,12 @@ function originAllowed(origin, allowlist) {
12637
13552
  return false;
12638
13553
  }
12639
13554
  }
12640
- app38.options("/event", (c) => {
13555
+ app44.options("/event", (c) => {
12641
13556
  const origin = getOrigin2(c);
12642
13557
  setCorsHeaders2(c, origin);
12643
13558
  return c.body(null, 204);
12644
13559
  });
12645
- app38.post("/event", async (c) => {
13560
+ app44.post("/event", async (c) => {
12646
13561
  const origin = getOrigin2(c);
12647
13562
  setCorsHeaders2(c, origin);
12648
13563
  const ua = c.req.header("user-agent") ?? "";
@@ -12847,7 +13762,7 @@ async function writeEvent(opts) {
12847
13762
  );
12848
13763
  }
12849
13764
  }
12850
- var visitor_event_default = app38;
13765
+ var visitor_event_default = app44;
12851
13766
 
12852
13767
  // app/lib/graph-health.ts
12853
13768
  var HOUR_MS = 60 * 60 * 1e3;
@@ -12931,7 +13846,7 @@ function startGraphHealthTimer() {
12931
13846
  }
12932
13847
 
12933
13848
  // app/lib/whatsapp/inbound/claude-bridge.ts
12934
- var TAG21 = "[whatsapp-adaptor]";
13849
+ var TAG27 = "[whatsapp-adaptor]";
12935
13850
  function whatsappTurnTimeoutMs() {
12936
13851
  return Number(process.env.WHATSAPP_PTY_TURN_TIMEOUT_MS ?? String(5 * 6e4));
12937
13852
  }
@@ -12954,7 +13869,7 @@ async function dispatchToClaude(input) {
12954
13869
  await input.reply(result.turnText);
12955
13870
  } catch (err) {
12956
13871
  const m = err instanceof Error ? err.message : String(err);
12957
- console.error(`${TAG21} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
13872
+ console.error(`${TAG27} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
12958
13873
  }
12959
13874
  }
12960
13875
  function startReaper2() {
@@ -12991,8 +13906,8 @@ function broadcastAdminShutdown(reason) {
12991
13906
 
12992
13907
  // ../lib/entitlement/src/index.ts
12993
13908
  import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
12994
- import { existsSync as existsSync23, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
12995
- import { resolve as resolve19 } from "path";
13909
+ import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
13910
+ import { resolve as resolve21 } from "path";
12996
13911
 
12997
13912
  // ../lib/entitlement/src/canonicalize.ts
12998
13913
  function canonicalize(value) {
@@ -13027,7 +13942,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
13027
13942
  var GRACE_DAYS = 7;
13028
13943
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
13029
13944
  function pubkeyPath(brand) {
13030
- return resolve19(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
13945
+ return resolve21(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
13031
13946
  }
13032
13947
  var memo = null;
13033
13948
  function memoKey(mtimeMs, account) {
@@ -13039,7 +13954,7 @@ function resolveEntitlement(brand, account) {
13039
13954
  if (brand.commercialMode !== true) {
13040
13955
  return logResolved(implicitTrust(account), null);
13041
13956
  }
13042
- const entitlementPath = resolve19(brand.configDir, "entitlement.json");
13957
+ const entitlementPath = resolve21(brand.configDir, "entitlement.json");
13043
13958
  if (!existsSync23(entitlementPath)) {
13044
13959
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
13045
13960
  }
@@ -13055,7 +13970,7 @@ function resolveEntitlement(brand, account) {
13055
13970
  function verifyAndResolve(brand, entitlementPath, account) {
13056
13971
  let pubkeyPem;
13057
13972
  try {
13058
- pubkeyPem = readFileSync19(pubkeyPath(brand), "utf-8");
13973
+ pubkeyPem = readFileSync20(pubkeyPath(brand), "utf-8");
13059
13974
  } catch (err) {
13060
13975
  return logResolved(anonymousFallback("pubkey-missing"), {
13061
13976
  reason: "pubkey-missing"
@@ -13069,7 +13984,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
13069
13984
  }
13070
13985
  let envelope;
13071
13986
  try {
13072
- envelope = JSON.parse(readFileSync19(entitlementPath, "utf-8"));
13987
+ envelope = JSON.parse(readFileSync20(entitlementPath, "utf-8"));
13073
13988
  } catch {
13074
13989
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
13075
13990
  }
@@ -13229,15 +14144,15 @@ function clientFrom(c) {
13229
14144
  c.req.header("x-forwarded-for")
13230
14145
  );
13231
14146
  }
13232
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
13233
- var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join15(PLATFORM_ROOT7, "config", "brand.json") : "";
14147
+ var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
14148
+ var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join15(PLATFORM_ROOT9, "config", "brand.json") : "";
13234
14149
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
13235
14150
  if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
13236
14151
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
13237
14152
  }
13238
14153
  if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13239
14154
  try {
13240
- const parsed = JSON.parse(readFileSync20(BRAND_JSON_PATH, "utf-8"));
14155
+ const parsed = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
13241
14156
  BRAND = { ...BRAND, ...parsed };
13242
14157
  } catch (err) {
13243
14158
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -13260,7 +14175,7 @@ var ALIAS_DOMAINS_PATH = join15(homedir3(), BRAND.configDir, "alias-domains.json
13260
14175
  function loadAliasDomains() {
13261
14176
  try {
13262
14177
  if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
13263
- const parsed = JSON.parse(readFileSync20(ALIAS_DOMAINS_PATH, "utf-8"));
14178
+ const parsed = JSON.parse(readFileSync21(ALIAS_DOMAINS_PATH, "utf-8"));
13264
14179
  if (!Array.isArray(parsed)) {
13265
14180
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
13266
14181
  return null;
@@ -13284,9 +14199,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
13284
14199
  function isPublicHost(host) {
13285
14200
  return host.startsWith("public.") || aliasDomains.has(host);
13286
14201
  }
13287
- var app39 = new Hono();
13288
- app39.use("*", clientIpMiddleware);
13289
- app39.use("*", async (c, next) => {
14202
+ var app45 = new Hono();
14203
+ app45.use("*", clientIpMiddleware);
14204
+ app45.use("*", async (c, next) => {
13290
14205
  await next();
13291
14206
  c.header("X-Content-Type-Options", "nosniff");
13292
14207
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -13296,7 +14211,7 @@ app39.use("*", async (c, next) => {
13296
14211
  );
13297
14212
  });
13298
14213
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
13299
- app39.use("*", async (c, next) => {
14214
+ app45.use("*", async (c, next) => {
13300
14215
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
13301
14216
  await next();
13302
14217
  return;
@@ -13329,7 +14244,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
13329
14244
  "/sites/"
13330
14245
  ];
13331
14246
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
13332
- app39.use("*", async (c, next) => {
14247
+ app45.use("*", async (c, next) => {
13333
14248
  const host = (c.req.header("host") ?? "").split(":")[0];
13334
14249
  if (!isPublicHost(host)) {
13335
14250
  await next();
@@ -13369,7 +14284,7 @@ function resolveRemoteAuthOpts() {
13369
14284
  return brandLoginOpts;
13370
14285
  }
13371
14286
  var MAX_LOGIN_BODY = 8 * 1024;
13372
- app39.post("/__remote-auth/login", async (c) => {
14287
+ app45.post("/__remote-auth/login", async (c) => {
13373
14288
  const client = clientFrom(c);
13374
14289
  const clientIp = client.ip || "unknown";
13375
14290
  if (!requestIsTlsTerminated(c)) {
@@ -13414,7 +14329,7 @@ app39.post("/__remote-auth/login", async (c) => {
13414
14329
  }
13415
14330
  });
13416
14331
  });
13417
- app39.get("/__remote-auth/logout", (c) => {
14332
+ app45.get("/__remote-auth/logout", (c) => {
13418
14333
  const client = clientFrom(c);
13419
14334
  const clientIp = client.ip || "unknown";
13420
14335
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -13427,7 +14342,7 @@ app39.get("/__remote-auth/logout", (c) => {
13427
14342
  }
13428
14343
  });
13429
14344
  });
13430
- app39.post("/__remote-auth/change-password", async (c) => {
14345
+ app45.post("/__remote-auth/change-password", async (c) => {
13431
14346
  const client = clientFrom(c);
13432
14347
  const clientIp = client.ip || "unknown";
13433
14348
  const rateLimited = checkRateLimit(client);
@@ -13478,13 +14393,13 @@ app39.post("/__remote-auth/change-password", async (c) => {
13478
14393
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
13479
14394
  }
13480
14395
  });
13481
- app39.get("/__remote-auth/setup", (c) => {
14396
+ app45.get("/__remote-auth/setup", (c) => {
13482
14397
  if (isRemoteAuthConfigured()) {
13483
14398
  return c.redirect("/");
13484
14399
  }
13485
14400
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
13486
14401
  });
13487
- app39.post("/__remote-auth/set-initial-password", async (c) => {
14402
+ app45.post("/__remote-auth/set-initial-password", async (c) => {
13488
14403
  if (isRemoteAuthConfigured()) {
13489
14404
  return c.redirect("/");
13490
14405
  }
@@ -13522,10 +14437,10 @@ app39.post("/__remote-auth/set-initial-password", async (c) => {
13522
14437
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
13523
14438
  }
13524
14439
  });
13525
- app39.get("/api/remote-auth/status", (c) => {
14440
+ app45.get("/api/remote-auth/status", (c) => {
13526
14441
  return c.json({ configured: isRemoteAuthConfigured() });
13527
14442
  });
13528
- app39.post("/api/remote-auth/set-password", async (c) => {
14443
+ app45.post("/api/remote-auth/set-password", async (c) => {
13529
14444
  let body;
13530
14445
  try {
13531
14446
  body = await c.req.json();
@@ -13556,9 +14471,9 @@ app39.post("/api/remote-auth/set-password", async (c) => {
13556
14471
  return c.json({ error: "Failed to save password" }, 500);
13557
14472
  }
13558
14473
  });
13559
- app39.route("/api/_client-error", client_error_default);
14474
+ app45.route("/api/_client-error", client_error_default);
13560
14475
  console.log("[client-error-route] mounted");
13561
- app39.use("*", async (c, next) => {
14476
+ app45.use("*", async (c, next) => {
13562
14477
  const host = (c.req.header("host") ?? "").split(":")[0];
13563
14478
  const path2 = c.req.path;
13564
14479
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -13591,11 +14506,12 @@ app39.use("*", async (c, next) => {
13591
14506
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
13592
14507
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
13593
14508
  });
13594
- app39.route("/api/health", health_default);
13595
- app39.route("/api/chat", chat_default);
13596
- app39.route("/api/whatsapp", whatsapp_default);
13597
- app39.route("/api/onboarding", onboarding_default);
13598
- app39.route("/api/admin", admin_default);
14509
+ app45.route("/api/health", health_default);
14510
+ app45.route("/api/chat", chat_default);
14511
+ app45.route("/api/whatsapp", whatsapp_default);
14512
+ app45.route("/api/onboarding", onboarding_default);
14513
+ app45.route("/api/admin", admin_default);
14514
+ app45.route("/api/access", access_default);
13599
14515
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
13600
14516
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
13601
14517
  var IMAGE_MIME = {
@@ -13607,7 +14523,7 @@ var IMAGE_MIME = {
13607
14523
  ".svg": "image/svg+xml",
13608
14524
  ".ico": "image/x-icon"
13609
14525
  };
13610
- app39.get("/agent-assets/:slug/:filename", (c) => {
14526
+ app45.get("/agent-assets/:slug/:filename", (c) => {
13611
14527
  const slug = c.req.param("slug");
13612
14528
  const filename = c.req.param("filename");
13613
14529
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -13623,8 +14539,8 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
13623
14539
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
13624
14540
  return c.text("Not found", 404);
13625
14541
  }
13626
- const filePath = resolve20(account.accountDir, "agents", slug, "assets", filename);
13627
- const expectedDir = resolve20(account.accountDir, "agents", slug, "assets");
14542
+ const filePath = resolve22(account.accountDir, "agents", slug, "assets", filename);
14543
+ const expectedDir = resolve22(account.accountDir, "agents", slug, "assets");
13628
14544
  if (!filePath.startsWith(expectedDir + "/")) {
13629
14545
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
13630
14546
  return c.text("Forbidden", 403);
@@ -13636,13 +14552,13 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
13636
14552
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13637
14553
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13638
14554
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
13639
- const body = readFileSync20(filePath);
14555
+ const body = readFileSync21(filePath);
13640
14556
  return c.body(body, 200, {
13641
14557
  "Content-Type": contentType,
13642
14558
  "Cache-Control": "public, max-age=3600"
13643
14559
  });
13644
14560
  });
13645
- app39.get("/generated/:filename", (c) => {
14561
+ app45.get("/generated/:filename", (c) => {
13646
14562
  const filename = c.req.param("filename");
13647
14563
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
13648
14564
  console.error(`[generated] serve file=${filename} status=403`);
@@ -13653,8 +14569,8 @@ app39.get("/generated/:filename", (c) => {
13653
14569
  console.error(`[generated] serve file=${filename} status=404`);
13654
14570
  return c.text("Not found", 404);
13655
14571
  }
13656
- const filePath = resolve20(account.accountDir, "generated", filename);
13657
- const expectedDir = resolve20(account.accountDir, "generated");
14572
+ const filePath = resolve22(account.accountDir, "generated", filename);
14573
+ const expectedDir = resolve22(account.accountDir, "generated");
13658
14574
  if (!filePath.startsWith(expectedDir + "/")) {
13659
14575
  console.error(`[generated] serve file=${filename} status=403`);
13660
14576
  return c.text("Forbidden", 403);
@@ -13666,22 +14582,22 @@ app39.get("/generated/:filename", (c) => {
13666
14582
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13667
14583
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13668
14584
  console.log(`[generated] serve file=${filename} status=200`);
13669
- const body = readFileSync20(filePath);
14585
+ const body = readFileSync21(filePath);
13670
14586
  return c.body(body, 200, {
13671
14587
  "Content-Type": contentType,
13672
14588
  "Cache-Control": "public, max-age=86400"
13673
14589
  });
13674
14590
  });
13675
- app39.route("/sites", sites_default);
13676
- app39.route("/listings", listings_default);
13677
- app39.route("/v", visitor_event_default);
13678
- app39.route("/v", visitor_consent_default);
14591
+ app45.route("/sites", sites_default);
14592
+ app45.route("/listings", listings_default);
14593
+ app45.route("/v", visitor_event_default);
14594
+ app45.route("/v", visitor_consent_default);
13679
14595
  var htmlCache = /* @__PURE__ */ new Map();
13680
14596
  var brandLogoPath = "/brand/maxy-monochrome.png";
13681
14597
  var brandIconPath = "/brand/maxy-monochrome.png";
13682
14598
  if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13683
14599
  try {
13684
- const fullBrand = JSON.parse(readFileSync20(BRAND_JSON_PATH, "utf-8"));
14600
+ const fullBrand = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
13685
14601
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
13686
14602
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
13687
14603
  } catch {
@@ -13697,10 +14613,10 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
13697
14613
  })}</script>`;
13698
14614
  function readInstalledVersion() {
13699
14615
  try {
13700
- if (!PLATFORM_ROOT7) return "unknown";
13701
- const versionFile = join15(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
14616
+ if (!PLATFORM_ROOT9) return "unknown";
14617
+ const versionFile = join15(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
13702
14618
  if (!existsSync24(versionFile)) return "unknown";
13703
- const content = readFileSync20(versionFile, "utf-8").trim();
14619
+ const content = readFileSync21(versionFile, "utf-8").trim();
13704
14620
  return content || "unknown";
13705
14621
  } catch {
13706
14622
  return "unknown";
@@ -13741,7 +14657,7 @@ var clientErrorReporterScript = `<script>
13741
14657
  function cachedHtml(file) {
13742
14658
  let html = htmlCache.get(file);
13743
14659
  if (!html) {
13744
- html = readFileSync20(resolve20(process.cwd(), "public", file), "utf-8");
14660
+ html = readFileSync21(resolve22(process.cwd(), "public", file), "utf-8");
13745
14661
  const productNameEsc = escapeHtml(BRAND.productName);
13746
14662
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
13747
14663
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -13761,12 +14677,12 @@ function loadBrandingCache(agentSlug) {
13761
14677
  try {
13762
14678
  const accountJsonPath = join15(configDir2, "account.json");
13763
14679
  if (!existsSync24(accountJsonPath)) return null;
13764
- const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14680
+ const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
13765
14681
  const accountId = account.accountId;
13766
14682
  if (!accountId) return null;
13767
14683
  const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
13768
14684
  if (!existsSync24(cachePath)) return null;
13769
- return JSON.parse(readFileSync20(cachePath, "utf-8"));
14685
+ return JSON.parse(readFileSync21(cachePath, "utf-8"));
13770
14686
  } catch {
13771
14687
  return null;
13772
14688
  }
@@ -13776,7 +14692,7 @@ function resolveDefaultSlug() {
13776
14692
  const configDir2 = join15(homedir3(), BRAND.configDir);
13777
14693
  const accountJsonPath = join15(configDir2, "account.json");
13778
14694
  if (!existsSync24(accountJsonPath)) return null;
13779
- const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14695
+ const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
13780
14696
  return account.defaultAgent || null;
13781
14697
  } catch {
13782
14698
  return null;
@@ -13812,7 +14728,7 @@ function brandedPublicHtml(agentSlug) {
13812
14728
  function escapeHtml(s) {
13813
14729
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13814
14730
  }
13815
- app39.get("/", (c) => {
14731
+ app45.get("/", (c) => {
13816
14732
  const host = (c.req.header("host") ?? "").split(":")[0];
13817
14733
  if (isPublicHost(host)) {
13818
14734
  const defaultSlug = resolveDefaultSlug();
@@ -13820,12 +14736,12 @@ app39.get("/", (c) => {
13820
14736
  }
13821
14737
  return c.html(cachedHtml("index.html"));
13822
14738
  });
13823
- app39.get("/public", (c) => {
14739
+ app45.get("/public", (c) => {
13824
14740
  const host = (c.req.header("host") ?? "").split(":")[0];
13825
14741
  if (isPublicHost(host)) return c.text("Not found", 404);
13826
14742
  return c.html(cachedHtml("public.html"));
13827
14743
  });
13828
- app39.get("/chat", (c) => {
14744
+ app45.get("/chat", (c) => {
13829
14745
  const host = (c.req.header("host") ?? "").split(":")[0];
13830
14746
  if (isPublicHost(host)) return c.text("Not found", 404);
13831
14747
  return c.html(cachedHtml("public.html"));
@@ -13844,12 +14760,12 @@ async function logViewerFetch(c, next) {
13844
14760
  duration_ms: Date.now() - start
13845
14761
  });
13846
14762
  }
13847
- app39.use("/vnc-viewer.html", logViewerFetch);
13848
- app39.use("/vnc-popout.html", logViewerFetch);
13849
- app39.get("/vnc-popout.html", (c) => {
14763
+ app45.use("/vnc-viewer.html", logViewerFetch);
14764
+ app45.use("/vnc-popout.html", logViewerFetch);
14765
+ app45.get("/vnc-popout.html", (c) => {
13850
14766
  let html = htmlCache.get("vnc-popout.html");
13851
14767
  if (!html) {
13852
- html = readFileSync20(resolve20(process.cwd(), "public", "vnc-popout.html"), "utf-8");
14768
+ html = readFileSync21(resolve22(process.cwd(), "public", "vnc-popout.html"), "utf-8");
13853
14769
  const name = escapeHtml(BRAND.productName);
13854
14770
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
13855
14771
  html = html.replace("</head>", ` ${brandScript}
@@ -13859,7 +14775,7 @@ app39.get("/vnc-popout.html", (c) => {
13859
14775
  }
13860
14776
  return c.html(html);
13861
14777
  });
13862
- app39.post("/api/vnc/client-event", async (c) => {
14778
+ app45.post("/api/vnc/client-event", async (c) => {
13863
14779
  let body;
13864
14780
  try {
13865
14781
  body = await c.req.json();
@@ -13880,25 +14796,25 @@ app39.post("/api/vnc/client-event", async (c) => {
13880
14796
  });
13881
14797
  return c.json({ ok: true });
13882
14798
  });
13883
- app39.get("/g/:slug", (c) => {
14799
+ app45.get("/g/:slug", (c) => {
13884
14800
  return c.html(brandedPublicHtml());
13885
14801
  });
13886
- app39.get("/graph", (c) => {
14802
+ app45.get("/graph", (c) => {
13887
14803
  const host = (c.req.header("host") ?? "").split(":")[0];
13888
14804
  if (isPublicHost(host)) return c.text("Not found", 404);
13889
14805
  return c.html(cachedHtml("graph.html"));
13890
14806
  });
13891
- app39.get("/sessions", (c) => {
14807
+ app45.get("/sessions", (c) => {
13892
14808
  const host = (c.req.header("host") ?? "").split(":")[0];
13893
14809
  if (isPublicHost(host)) return c.text("Not found", 404);
13894
14810
  return c.html(cachedHtml("sessions.html"));
13895
14811
  });
13896
- app39.get("/data", (c) => {
14812
+ app45.get("/data", (c) => {
13897
14813
  const host = (c.req.header("host") ?? "").split(":")[0];
13898
14814
  if (isPublicHost(host)) return c.text("Not found", 404);
13899
14815
  return c.html(cachedHtml("data.html"));
13900
14816
  });
13901
- app39.get("/:slug", async (c, next) => {
14817
+ app45.get("/:slug", async (c, next) => {
13902
14818
  const slug = c.req.param("slug");
13903
14819
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
13904
14820
  const branding = loadBrandingCache(slug);
@@ -13908,15 +14824,15 @@ app39.get("/:slug", async (c, next) => {
13908
14824
  await next();
13909
14825
  });
13910
14826
  if (brandFaviconPath !== "/favicon.ico") {
13911
- app39.get("/favicon.ico", (c) => {
14827
+ app45.get("/favicon.ico", (c) => {
13912
14828
  c.header("Cache-Control", "public, max-age=300");
13913
14829
  return c.redirect(brandFaviconPath, 302);
13914
14830
  });
13915
14831
  }
13916
- app39.use("/*", serveStatic({ root: "./public" }));
14832
+ app45.use("/*", serveStatic({ root: "./public" }));
13917
14833
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
13918
14834
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
13919
- var httpServer = serve({ fetch: app39.fetch, port, hostname });
14835
+ var httpServer = serve({ fetch: app45.fetch, port, hostname });
13920
14836
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
13921
14837
  {
13922
14838
  const loopHist = monitorEventLoopDelay({ resolution: 50 });
@@ -13940,6 +14856,7 @@ var SUBAPP_MANIFEST = [
13940
14856
  { prefix: "/api/whatsapp", file: "server/routes/whatsapp.ts", subapp: whatsapp_default },
13941
14857
  { prefix: "/api/onboarding", file: "server/routes/onboarding.ts", subapp: onboarding_default },
13942
14858
  { prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
14859
+ { prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
13943
14860
  { prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
13944
14861
  { prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
13945
14862
  { prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
@@ -13951,7 +14868,7 @@ for (const m of SUBAPP_MANIFEST) {
13951
14868
  }
13952
14869
  try {
13953
14870
  const registered = [];
13954
- for (const r of app39.routes ?? []) {
14871
+ for (const r of app45.routes ?? []) {
13955
14872
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
13956
14873
  if (AGENT_SLUG_PATTERN.test(r.path)) {
13957
14874
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -13974,7 +14891,7 @@ try {
13974
14891
  (async () => {
13975
14892
  try {
13976
14893
  if (!existsSync24(USERS_FILE)) return;
13977
- const usersRaw = readFileSync20(USERS_FILE, "utf-8").trim();
14894
+ const usersRaw = readFileSync21(USERS_FILE, "utf-8").trim();
13978
14895
  if (!usersRaw) return;
13979
14896
  const users = JSON.parse(usersRaw);
13980
14897
  const userId = users[0]?.userId;
@@ -14066,7 +14983,7 @@ if (bootAccountConfig?.whatsapp) {
14066
14983
  }
14067
14984
  init({
14068
14985
  configDir: configDirForWhatsApp,
14069
- platformRoot: resolve20(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
14986
+ platformRoot: resolve22(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
14070
14987
  accountConfig: bootAccountConfig,
14071
14988
  onMessage: async (msg) => {
14072
14989
  if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {