@rubytech/create-maxy-code 0.1.436 → 0.1.438

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 (251) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/active-rules/dist/index.d.ts +14 -0
  3. package/payload/platform/lib/active-rules/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/active-rules/dist/index.js +56 -9
  5. package/payload/platform/lib/active-rules/dist/index.js.map +1 -1
  6. package/payload/platform/lib/active-rules/src/index.test.ts +106 -0
  7. package/payload/platform/lib/active-rules/src/index.ts +98 -9
  8. package/payload/platform/lib/admin-access-password/__tests__/index.test.ts +31 -0
  9. package/payload/platform/lib/admin-access-password/dist/index.d.ts +8 -0
  10. package/payload/platform/lib/admin-access-password/dist/index.d.ts.map +1 -1
  11. package/payload/platform/lib/admin-access-password/dist/index.js +5 -1
  12. package/payload/platform/lib/admin-access-password/dist/index.js.map +1 -1
  13. package/payload/platform/lib/admin-access-password/src/index.ts +14 -1
  14. package/payload/platform/lib/admins-write/__tests__/index.test.ts +27 -0
  15. package/payload/platform/lib/admins-write/dist/index.d.ts +8 -0
  16. package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -1
  17. package/payload/platform/lib/admins-write/dist/index.js +5 -1
  18. package/payload/platform/lib/admins-write/dist/index.js.map +1 -1
  19. package/payload/platform/lib/admins-write/src/index.ts +14 -1
  20. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  21. package/payload/platform/plugins/admin/PLUGIN.md +4 -2
  22. package/payload/platform/plugins/admin/mcp/dist/credential-identity.d.ts +23 -0
  23. package/payload/platform/plugins/admin/mcp/dist/credential-identity.d.ts.map +1 -0
  24. package/payload/platform/plugins/admin/mcp/dist/credential-identity.js +35 -0
  25. package/payload/platform/plugins/admin/mcp/dist/credential-identity.js.map +1 -0
  26. package/payload/platform/plugins/admin/mcp/dist/index.js +110 -57
  27. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  28. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +22 -0
  29. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -1
  30. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +62 -2
  31. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -1
  32. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +6 -6
  33. package/payload/platform/plugins/docs/references/admin-identity-gate.md +40 -0
  34. package/payload/platform/plugins/docs/references/admin-session.md +2 -0
  35. package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
  36. package/payload/platform/plugins/docs/references/internals.md +3 -3
  37. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/profile.md +1 -1
  38. package/payload/platform/plugins/memory/mcp/dist/index.js +12 -3
  39. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  40. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-progress.test.d.ts +2 -0
  41. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-progress.test.d.ts.map +1 -0
  42. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-progress.test.js +79 -0
  43. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-progress.test.js.map +1 -0
  44. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-write-phase-progress.test.d.ts +2 -0
  45. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-write-phase-progress.test.d.ts.map +1 -0
  46. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-write-phase-progress.test.js +50 -0
  47. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-write-phase-progress.test.js.map +1 -0
  48. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-progress.test.d.ts +2 -0
  49. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-progress.test.d.ts.map +1 -0
  50. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-progress.test.js +114 -0
  51. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-progress.test.js.map +1 -0
  52. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js +5 -0
  53. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js.map +1 -1
  54. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +15 -0
  55. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  56. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +17 -17
  57. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  58. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +21 -1
  59. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  60. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts +1 -1
  61. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -1
  62. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +19 -4
  63. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -1
  64. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
  65. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +4 -2
  66. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  67. package/payload/platform/scripts/remediate-glsmith-identity.sh +500 -0
  68. package/payload/platform/scripts/seed-neo4j.sh +77 -12
  69. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  70. package/payload/platform/services/claude-session-manager/dist/http-server.js +36 -10
  71. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  72. package/payload/platform/services/claude-session-manager/dist/identity-audit.d.ts +51 -0
  73. package/payload/platform/services/claude-session-manager/dist/identity-audit.d.ts.map +1 -0
  74. package/payload/platform/services/claude-session-manager/dist/identity-audit.js +103 -0
  75. package/payload/platform/services/claude-session-manager/dist/identity-audit.js.map +1 -0
  76. package/payload/platform/services/claude-session-manager/dist/index.js +33 -0
  77. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  78. package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.d.ts +7 -0
  79. package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.d.ts.map +1 -1
  80. package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.js +9 -0
  81. package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.js.map +1 -1
  82. package/payload/platform/services/claude-session-manager/dist/mcp-user-binding.d.ts +25 -0
  83. package/payload/platform/services/claude-session-manager/dist/mcp-user-binding.d.ts.map +1 -0
  84. package/payload/platform/services/claude-session-manager/dist/mcp-user-binding.js +42 -0
  85. package/payload/platform/services/claude-session-manager/dist/mcp-user-binding.js.map +1 -0
  86. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts +4 -1
  87. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -1
  88. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map +1 -1
  89. package/payload/platform/services/claude-session-manager/dist/redundant-operator-seat-audit.d.ts +44 -0
  90. package/payload/platform/services/claude-session-manager/dist/redundant-operator-seat-audit.d.ts.map +1 -0
  91. package/payload/platform/services/claude-session-manager/dist/redundant-operator-seat-audit.js +127 -0
  92. package/payload/platform/services/claude-session-manager/dist/redundant-operator-seat-audit.js.map +1 -0
  93. package/payload/server/{chunk-LMLVOZ6G.js → chunk-UGPQCVIW.js} +11 -38
  94. package/payload/server/maxy-edge.js +1 -1
  95. package/payload/server/public/assets/{AdminLoginScreens-CMh1pdOp.js → AdminLoginScreens-DeR71H29.js} +1 -1
  96. package/payload/server/public/assets/AdminShell-DlAVQUda.js +2 -0
  97. package/payload/server/public/assets/{Checkbox-OCfZCTCO.js → Checkbox-B629mOPp.js} +1 -1
  98. package/payload/server/public/assets/{admin-Bi2uYnAg.js → admin-C15h-NL0.js} +1 -1
  99. package/payload/server/public/assets/{arc-Bm9oSOth.js → arc-qKfXIt8v.js} +1 -1
  100. package/payload/server/public/assets/architecture-YZFGNWBL-FPHT7s-C.js +1 -0
  101. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-C2dK88g3.js → architectureDiagram-Q4EWVU46-DkVrGn7G.js} +1 -1
  102. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DfLSsjCJ.js → blockDiagram-DXYQGD6D-DqavswGH.js} +1 -1
  103. package/payload/server/public/assets/browser-B7xQLIRK.js +1 -0
  104. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Bxkmp6OL.js → c4Diagram-AHTNJAMY-B7fyKiXe.js} +1 -1
  105. package/payload/server/public/assets/calendar-A-vGm6pL.js +1 -0
  106. package/payload/server/public/assets/channel-j70eMvcL.js +1 -0
  107. package/payload/server/public/assets/chat-DAae6Z51.js +1 -0
  108. package/payload/server/public/assets/chevron-left-u4tjq7fI.js +1 -0
  109. package/payload/server/public/assets/{chunk-2KRD3SAO-BVNDFwNH.js → chunk-2KRD3SAO-DsgMtGkv.js} +1 -1
  110. package/payload/server/public/assets/{chunk-336JU56O-Daz6nMta.js → chunk-336JU56O-CkdISfNt.js} +2 -2
  111. package/payload/server/public/assets/chunk-426QAEUC-D-NcAlTW.js +1 -0
  112. package/payload/server/public/assets/{chunk-4BX2VUAB-DTldfvwP.js → chunk-4BX2VUAB-CYpiF2bl.js} +1 -1
  113. package/payload/server/public/assets/{chunk-4TB4RGXK-DhM8pwRG.js → chunk-4TB4RGXK-CWyc_F7v.js} +1 -1
  114. package/payload/server/public/assets/{chunk-55IACEB6-CzTsbIHo.js → chunk-55IACEB6-BbzKMw2Z.js} +1 -1
  115. package/payload/server/public/assets/{chunk-5FUZZQ4R-BrcObZhe.js → chunk-5FUZZQ4R-u19Nai9e.js} +1 -1
  116. package/payload/server/public/assets/{chunk-5PVQY5BW-SFqTuS11.js → chunk-5PVQY5BW-Ba8by6Uy.js} +1 -1
  117. package/payload/server/public/assets/{chunk-67CJDMHE-EIbrSGOo.js → chunk-67CJDMHE-BwUuxtyb.js} +1 -1
  118. package/payload/server/public/assets/{chunk-7N4EOEYR-B1D4Jorr.js → chunk-7N4EOEYR-BRH9iM9L.js} +1 -1
  119. package/payload/server/public/assets/{chunk-AA7GKIK3-BKHiX4cC.js → chunk-AA7GKIK3-BGoztgLL.js} +1 -1
  120. package/payload/server/public/assets/{chunk-BSJP7CBP-xBzu29m7.js → chunk-BSJP7CBP-C5GDAZTl.js} +1 -1
  121. package/payload/server/public/assets/{chunk-CIAEETIT-x2x0gMpL.js → chunk-CIAEETIT-ChY32VPS.js} +1 -1
  122. package/payload/server/public/assets/{chunk-EDXVE4YY-D5UkD28M.js → chunk-EDXVE4YY-CPSD3p6p.js} +1 -1
  123. package/payload/server/public/assets/{chunk-ENJZ2VHE-Dbz67a7E.js → chunk-ENJZ2VHE-D1oL-iUr.js} +1 -1
  124. package/payload/server/public/assets/{chunk-FMBD7UC4-XfWvuu2F.js → chunk-FMBD7UC4-CPvYEr54.js} +1 -1
  125. package/payload/server/public/assets/{chunk-FOC6F5B3-DfOEDS4q.js → chunk-FOC6F5B3-9Yyj7Vbz.js} +1 -1
  126. package/payload/server/public/assets/{chunk-ICPOFSXX-D_WEZZvg.js → chunk-ICPOFSXX-BaYgh8FR.js} +2 -2
  127. package/payload/server/public/assets/{chunk-K5T4RW27-7O627Xi1.js → chunk-K5T4RW27-5ucpeRvm.js} +1 -1
  128. package/payload/server/public/assets/{chunk-KGLVRYIC-BXI3XDP9.js → chunk-KGLVRYIC-DW-FHk7T.js} +1 -1
  129. package/payload/server/public/assets/{chunk-LIHQZDEY-B9cFNN0l.js → chunk-LIHQZDEY-CGGVfAyG.js} +1 -1
  130. package/payload/server/public/assets/{chunk-ORNJ4GCN-BR3UNDTn.js → chunk-ORNJ4GCN-Bv8EUa5d.js} +1 -1
  131. package/payload/server/public/assets/{chunk-OYMX7WX6-2wKIEtFZ.js → chunk-OYMX7WX6-C1mXJnyw.js} +1 -1
  132. package/payload/server/public/assets/chunk-QZHKN3VN-CAwbQBtf.js +1 -0
  133. package/payload/server/public/assets/{chunk-U2HBQHQK-augM3AXB.js → chunk-U2HBQHQK-PwSQz6EP.js} +1 -1
  134. package/payload/server/public/assets/{chunk-X2U36JSP-BAM9q9ZQ.js → chunk-X2U36JSP--NPHTBlU.js} +1 -1
  135. package/payload/server/public/assets/{chunk-XPW4576I-DxPpji5P.js → chunk-XPW4576I-Bbc7Da32.js} +1 -1
  136. package/payload/server/public/assets/{chunk-YZCP3GAM-BD3ClFXP.js → chunk-YZCP3GAM-D-9v8e--.js} +1 -1
  137. package/payload/server/public/assets/{chunk-ZZ45TVLE-CqxhLPep.js → chunk-ZZ45TVLE-CMPg670Q.js} +1 -1
  138. package/payload/server/public/assets/classDiagram-6PBFFD2Q-Ct6Rn0vt.js +1 -0
  139. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B_2XCIFv.js +1 -0
  140. package/payload/server/public/assets/clone-DjPWA_NH.js +1 -0
  141. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-xywWX-9i.js → cose-bilkent-S5V4N54A-NVSb1Bhy.js} +1 -1
  142. package/payload/server/public/assets/{dagre-KV5264BT-ChRpqqiH.js → dagre-KV5264BT-V2Ai1DFl.js} +1 -1
  143. package/payload/server/public/assets/{dagre-BaHjDQ-8.js → dagre-QfLcO-1j.js} +1 -1
  144. package/payload/server/public/assets/data-BNWpKx1d.js +1 -0
  145. package/payload/server/public/assets/{diagram-5BDNPKRD-KjwTpyBi.js → diagram-5BDNPKRD-Caw21693.js} +1 -1
  146. package/payload/server/public/assets/{diagram-G4DWMVQ6-DZO9njAF.js → diagram-G4DWMVQ6-CgwVkT2q.js} +1 -1
  147. package/payload/server/public/assets/{diagram-MMDJMWI5-Bus-U-TI.js → diagram-MMDJMWI5-DFUrLZhs.js} +1 -1
  148. package/payload/server/public/assets/{diagram-TYMM5635-CfJodFgq.js → diagram-TYMM5635-CEEiEuZJ.js} +1 -1
  149. package/payload/server/public/assets/{erDiagram-SMLLAGMA-Bt5zqWiW.js → erDiagram-SMLLAGMA-CG10NrT3.js} +1 -1
  150. package/payload/server/public/assets/file-download-4mKJxUi-.js +1 -0
  151. package/payload/server/public/assets/{flatten-Bpu1L63d.js → flatten-DAO0sfCM.js} +1 -1
  152. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-B64Q590S.js → flowDiagram-DWJPFMVM-BqkV9Okv.js} +1 -1
  153. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BWdgCEpH.js → ganttDiagram-T4ZO3ILL-DRiCDQF6.js} +1 -1
  154. package/payload/server/public/assets/gitGraph-7Q5UKJZL-B8Hx7f-D.js +1 -0
  155. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-Djq2B0WT.js → gitGraphDiagram-UUTBAWPF-BZV2voyy.js} +1 -1
  156. package/payload/server/public/assets/{graph-CrJ-itbq.js → graph-D0orIRAh.js} +3 -3
  157. package/payload/server/public/assets/{graph-labels-CmCWaKJT.js → graph-labels-B8YAdo3Z.js} +1 -1
  158. package/payload/server/public/assets/{graphlib-CNJh9_lx.js → graphlib-DGPThX8M.js} +1 -1
  159. package/payload/server/public/assets/info-OMHHGYJF-BicCJTqU.js +1 -0
  160. package/payload/server/public/assets/infoDiagram-42DDH7IO-tf-8rOez.js +2 -0
  161. package/payload/server/public/assets/{isEmpty-DkDEVnMZ.js → isEmpty-Cb_U81C6.js} +1 -1
  162. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-V3ACkWCW.js → ishikawaDiagram-UXIWVN3A-BKsAxSvp.js} +1 -1
  163. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-9T0qZeH3.js → journeyDiagram-VCZTEJTY-CmliuOd-.js} +1 -1
  164. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CxZ0ukx3.js → kanban-definition-6JOO6SKY-DAlElMb-.js} +1 -1
  165. package/payload/server/public/assets/{line-DUHKCQZT.js → line-DKZqLKAC.js} +1 -1
  166. package/payload/server/public/assets/{linear-BgIBblun.js → linear-CbSGdh1x.js} +1 -1
  167. package/payload/server/public/assets/{maximize-2-_cJWIgx2.js → maximize-2-CxstTgoK.js} +1 -1
  168. package/payload/server/public/assets/{mermaid-parser.core-CyHygs0-.js → mermaid-parser.core-DFvbao9C.js} +2 -2
  169. package/payload/server/public/assets/{mermaid.core-L55fJBSx.js → mermaid.core-C0vEBxDm.js} +3 -3
  170. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DczaUX_E.js → mindmap-definition-QFDTVHPH-CShASUKd.js} +1 -1
  171. package/payload/server/public/assets/operator-DZzuotDB.js +1 -0
  172. package/payload/server/public/assets/{ordinal-DK3QISRR.js → ordinal-H0tVmBDP.js} +1 -1
  173. package/payload/server/public/assets/packet-4T2RLAQJ-Ckn_5hq0.js +1 -0
  174. package/payload/server/public/assets/{page-DbUaFfk6.js → page-3g0A19Rv.js} +6 -6
  175. package/payload/server/public/assets/page-Ck_pLhAk.js +1 -0
  176. package/payload/server/public/assets/{pdf-render-COLA4aPx.js → pdf-render-jAQU0nXt.js} +1 -1
  177. package/payload/server/public/assets/pie-ZZUOXDRM-DzlGQW3J.js +1 -0
  178. package/payload/server/public/assets/{pieDiagram-DEJITSTG-Da5TdnNt.js → pieDiagram-DEJITSTG-CBZHpDAJ.js} +1 -1
  179. package/payload/server/public/assets/{public-CYhDdDJd.js → public-WFUQw9AD.js} +1 -1
  180. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DX7J81uh.js → quadrantDiagram-34T5L4WZ-n3bB3K91.js} +1 -1
  181. package/payload/server/public/assets/radar-PYXPWWZC-BHo2YD8b.js +1 -0
  182. package/payload/server/public/assets/{reduce-uzWdoyL7.js → reduce-DAvwat-a.js} +1 -1
  183. package/payload/server/public/assets/{requirementDiagram-MS252O5E-DJQGrZPG.js → requirementDiagram-MS252O5E-DiDvQK3c.js} +1 -1
  184. package/payload/server/public/assets/rotate-ccw-COGQTi7n.js +1 -0
  185. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-Ba8fGz96.js → sankeyDiagram-XADWPNL6-DWDQQZmd.js} +1 -1
  186. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BmDPC7qx.js → sequenceDiagram-FGHM5R23-DmAqRlBT.js} +1 -1
  187. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-1VkptYFz.js → stateDiagram-FHFEXIEX-D4R-n6bZ.js} +1 -1
  188. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BcFRbGRu.js +1 -0
  189. package/payload/server/public/assets/tasks-B97VpwEJ.js +1 -0
  190. package/payload/server/public/assets/{time-entry-format-D477WAes.js → time-entry-format-C09rDHo9.js} +1 -1
  191. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-B92U3T_f.js → timeline-definition-GMOUNBTQ-CjiHGP4l.js} +1 -1
  192. package/payload/server/public/assets/treeView-SZITEDCU-BtRr5vXp.js +1 -0
  193. package/payload/server/public/assets/treemap-W4RFUUIX-D8p0sbyo.js +1 -0
  194. package/payload/server/public/assets/useCopyFeedback-CSk9KjhY.js +1 -0
  195. package/payload/server/public/assets/useSubAccountSwitcher-DLJFISqf.js +9 -0
  196. package/payload/server/public/assets/{useSubAccountSwitcher-BKCWKo97.css → useSubAccountSwitcher-Dzmd7Aip.css} +1 -1
  197. package/payload/server/public/assets/{useVoiceRecorder-CZPtVpM-.js → useVoiceRecorder-DCyJQkFK.js} +2 -2
  198. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Y6Wkihrs.js → vennDiagram-DHZGUBPP-CBdDQ7c-.js} +1 -1
  199. package/payload/server/public/assets/wardley-RL74JXVD-DgMn-9ZJ.js +1 -0
  200. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-iJpfal2z.js → wardleyDiagram-NUSXRM2D-BlIQPKLb.js} +1 -1
  201. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-S0bv7oH0.js → xychartDiagram-5P7HB3ND-CslKgh9B.js} +1 -1
  202. package/payload/server/public/browser.html +5 -5
  203. package/payload/server/public/calendar.html +6 -6
  204. package/payload/server/public/chat.html +13 -12
  205. package/payload/server/public/data.html +11 -11
  206. package/payload/server/public/graph.html +11 -10
  207. package/payload/server/public/index.html +14 -14
  208. package/payload/server/public/operator.html +15 -14
  209. package/payload/server/public/public.html +13 -12
  210. package/payload/server/public/tasks.html +5 -4
  211. package/payload/server/server.js +703 -555
  212. package/payload/server/public/assets/AdminShell-X9Bg3AKc.js +0 -2
  213. package/payload/server/public/assets/architecture-YZFGNWBL-Bbnxm4F4.js +0 -1
  214. package/payload/server/public/assets/browser-B0ieZy-b.js +0 -1
  215. package/payload/server/public/assets/calendar-CRL2_9Fl.js +0 -1
  216. package/payload/server/public/assets/channel-CatPTq-5.js +0 -1
  217. package/payload/server/public/assets/chat-DUc7bt5W.js +0 -1
  218. package/payload/server/public/assets/chevron-left-B2DKUxLk.js +0 -1
  219. package/payload/server/public/assets/chunk-426QAEUC-CqLMwOdA.js +0 -1
  220. package/payload/server/public/assets/chunk-QZHKN3VN-CjWy8NJ7.js +0 -1
  221. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DHhR5rdN.js +0 -1
  222. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DH4pAdfu.js +0 -1
  223. package/payload/server/public/assets/clone-DCKzwjWW.js +0 -1
  224. package/payload/server/public/assets/data-C1ftMulP.js +0 -1
  225. package/payload/server/public/assets/file-download-CHTyUQqS.js +0 -1
  226. package/payload/server/public/assets/gitGraph-7Q5UKJZL-Ps5HDzLh.js +0 -1
  227. package/payload/server/public/assets/info-OMHHGYJF-gtlpwIig.js +0 -1
  228. package/payload/server/public/assets/infoDiagram-42DDH7IO-CPrWlrsa.js +0 -2
  229. package/payload/server/public/assets/operator-B3Mi3TW0.js +0 -1
  230. package/payload/server/public/assets/packet-4T2RLAQJ-DrSn1e0n.js +0 -1
  231. package/payload/server/public/assets/page-C1G57kPZ.js +0 -1
  232. package/payload/server/public/assets/pie-ZZUOXDRM-C09i6N-g.js +0 -1
  233. package/payload/server/public/assets/radar-PYXPWWZC-CMZN2KQH.js +0 -1
  234. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BdgkPHuh.js +0 -1
  235. package/payload/server/public/assets/tasks-0OcRiBdQ.js +0 -1
  236. package/payload/server/public/assets/treeView-SZITEDCU-CeuUM5oL.js +0 -1
  237. package/payload/server/public/assets/treemap-W4RFUUIX-C--fAaJv.js +0 -1
  238. package/payload/server/public/assets/useCopyFeedback-CpfVOO26.js +0 -1
  239. package/payload/server/public/assets/useSubAccountSwitcher-Bo7bnFtC.js +0 -9
  240. package/payload/server/public/assets/wardley-RL74JXVD-4WPJ2UA9.js +0 -1
  241. /package/payload/server/public/assets/{_baseFor-Q4DeIqQC.js → _baseFor-D3BqAOBr.js} +0 -0
  242. /package/payload/server/public/assets/{array-WaOTgZu7.js → array-iYP9fzJp.js} +0 -0
  243. /package/payload/server/public/assets/{cytoscape.esm-CMmjo4hO.js → cytoscape.esm-BQSBzPsB.js} +0 -0
  244. /package/payload/server/public/assets/{defaultLocale-BxqJVxq5.js → defaultLocale-BrdA1gDi.js} +0 -0
  245. /package/payload/server/public/assets/{dist-Dx-cpU53.js → dist-Cvgxm7qv.js} +0 -0
  246. /package/payload/server/public/assets/{init-DX7c5Edh.js → init-Bx0S6pkp.js} +0 -0
  247. /package/payload/server/public/assets/{katex-B2dtGfSp.js → katex-BsiH_a6d.js} +0 -0
  248. /package/payload/server/public/assets/{path-CnSyroar.js → path-BLSgTy3w.js} +0 -0
  249. /package/payload/server/public/assets/{preload-helper-DqboRNgS.js → preload-helper-ClBMtM1g.js} +0 -0
  250. /package/payload/server/public/assets/{rough.esm-eLccZ4OJ.js → rough.esm-BMOtDaaS.js} +0 -0
  251. /package/payload/server/public/assets/{src-Dqs1wlnb.js → src-DV4LKv2E.js} +0 -0
@@ -1,4 +1,12 @@
1
1
  import type { CreatedBy } from "../../../../../lib/graph-write/dist/index.js";
2
+ /** Task 1567 — the name-required invariant for every owner-creating seed path.
3
+ * A name is a placeholder (and so must be refused, never persisted) when, after
4
+ * trimming, it is empty or equal to the literal seed placeholder "Owner". A
5
+ * real name that merely contains the word — "Owner Jones" — is not a
6
+ * placeholder. This is the single predicate the sub-account seed, account_create
7
+ * and admin-add all route through, and the same condition the standing
8
+ * placeholder-owner audit detects on already-persisted nodes. */
9
+ export declare function isPlaceholderOwnerName(name: string): boolean;
2
10
  export interface AccountSummary {
3
11
  accountId: string;
4
12
  role: string;
@@ -14,6 +22,20 @@ export declare function isHouseAccount(accountsDir: string, id: string): boolean
14
22
  * (should not happen on a correctly-provisioned install — callers treat
15
23
  * null as "no house configured yet" rather than throwing). */
16
24
  export declare function findHouseAccount(accountsDir: string): AccountSummary | null;
25
+ export interface AdminAddClientRefusal {
26
+ /** stderr diagnostic line. */
27
+ logLine: string;
28
+ /** operator-facing refusal message (prefixed with the tool TAG by the caller). */
29
+ message: string;
30
+ }
31
+ /** Task 1568 — admin-add mints a per-account operator seat (users.json PIN,
32
+ * account.json admins[], graph AdminUser/Person). Since Task 999 admin access is
33
+ * install-wide: a house admin reaches every sub-account with no seat on it
34
+ * (canAccessAdmin never consults admins[]). On a client sub-account a seat
35
+ * therefore grants nothing and only fragments identity, so admin-add is refused
36
+ * there. The house account is unaffected: it is where operators legitimately
37
+ * hold PIN logins and admins[] rows. Returns null on the house (proceed). */
38
+ export declare function refuseClientAdminAdd(isHouse: boolean, accountId: string): AdminAddClientRefusal | null;
17
39
  /** Task 1311 — reads the install-wide WhatsApp/Telegram channel-routing
18
40
  * pointer off the house account's account.json. Returns null when absent
19
41
  * (default: inbound routes to house, unchanged from pre-1311 behavior) or
@@ -1 +1 @@
1
- {"version":3,"file":"account-lifecycle.d.ts","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8CAA8C,CAAC;AAI9E,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;oCAEoC;AACpC,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAoBzE;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED;;+DAE+D;AAC/D,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAE3E;AAED;;;;;+DAK+D;AAC/D,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU7E;AAED;;;;0EAI0E;AAC1E,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAO7F;AAED;;;;mEAImE;AACnE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAanE;AAED;;;;sEAIsE;AACtE,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAKlG;AAED;;;;;;;;;;;;4EAY4E;AAC5E,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB9F;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,YAAY,GAAG,eAAe,CAAA;CAAE,CAAC;AAEzE;;;;;;;;;qBASqB;AACrB,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,CAcjH;AAED;;iFAEiF;AACjF,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAO3C;AAED;;;yBAGyB;AACzB,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAoBT;AAED;iEACiE;AACjE,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAClH;AAED;;;;;;;0BAO0B;AAC1B,eAAO,MAAM,WAAW,EAAE,SAAoD,CAAC;AAE/E;;;;;;;;;;;;;;;;;;+BAkB+B;AAC/B,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB7D;AAED;;mEAEmE;AACnE,eAAO,MAAM,yBAAyB,2DAA2D,CAAC;AAClG,eAAO,MAAM,oBAAoB,sDAAsD,CAAC"}
1
+ {"version":3,"file":"account-lifecycle.d.ts","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,17 @@ import { execFileSync } from "node:child_process";
7
7
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
8
8
  import { join, resolve } from "node:path";
9
9
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
+ /** Task 1567 — the name-required invariant for every owner-creating seed path.
11
+ * A name is a placeholder (and so must be refused, never persisted) when, after
12
+ * trimming, it is empty or equal to the literal seed placeholder "Owner". A
13
+ * real name that merely contains the word — "Owner Jones" — is not a
14
+ * placeholder. This is the single predicate the sub-account seed, account_create
15
+ * and admin-add all route through, and the same condition the standing
16
+ * placeholder-owner audit detects on already-persisted nodes. */
17
+ export function isPlaceholderOwnerName(name) {
18
+ const trimmed = name.trim();
19
+ return trimmed === "" || trimmed === "Owner";
20
+ }
10
21
  /** Enumerate the registry: every UUID dir with a parseable account.json, with
11
22
  * its role. An unparseable account.json is excluded (corruption discipline,
12
23
  * mirrors account-enumeration). */
@@ -44,6 +55,21 @@ export function isHouseAccount(accountsDir, id) {
44
55
  export function findHouseAccount(accountsDir) {
45
56
  return listAccountsFromDir(accountsDir).find((a) => a.isHouse) ?? null;
46
57
  }
58
+ /** Task 1568 — admin-add mints a per-account operator seat (users.json PIN,
59
+ * account.json admins[], graph AdminUser/Person). Since Task 999 admin access is
60
+ * install-wide: a house admin reaches every sub-account with no seat on it
61
+ * (canAccessAdmin never consults admins[]). On a client sub-account a seat
62
+ * therefore grants nothing and only fragments identity, so admin-add is refused
63
+ * there. The house account is unaffected: it is where operators legitimately
64
+ * hold PIN logins and admins[] rows. Returns null on the house (proceed). */
65
+ export function refuseClientAdminAdd(isHouse, accountId) {
66
+ if (isHouse)
67
+ return null;
68
+ return {
69
+ logLine: `[admin-add] op=refused-on-client reason=install-wide-access-no-seat account=${accountId.slice(0, 8)}`,
70
+ message: "Admin access is install-wide. A house admin already reaches this sub-account with no seat on it, so no per-sub-account admin is created. The sub-account's one identity, its business owner, is set when the account is created.",
71
+ };
72
+ }
47
73
  /** Task 1311 — reads the install-wide WhatsApp/Telegram channel-routing
48
74
  * pointer off the house account's account.json. Returns null when absent
49
75
  * (default: inbound routes to house, unchanged from pre-1311 behavior) or
@@ -235,6 +261,26 @@ export const SEED_ORIGIN = { agent: "system", source: "installer" };
235
261
  * verified post-condition. */
236
262
  export async function seedAccountGraphRoot(args) {
237
263
  const { session, accountId, ownerUserId, ownerName, createdAt } = args;
264
+ // Task 1567 — the business owner's name is a hard precondition of the seed.
265
+ // Refuse before any write so a nameless create leaves no partial rootless
266
+ // scaffold behind (no LocalBusiness, no AdminUser, no Person). Mirrors the
267
+ // empty-fullName throw writeAdminUserAndPerson already enforces; here it is
268
+ // reached at the seed point, and it additionally refuses the "Owner"
269
+ // placeholder the old default persisted.
270
+ const trimmedName = ownerName.trim();
271
+ if (isPlaceholderOwnerName(ownerName)) {
272
+ throw new Error(`seedAccountGraphRoot: a real business-owner name is required (got ${JSON.stringify(ownerName)})`);
273
+ }
274
+ // Split on first whitespace, same rule as writeAdminUserAndPerson: head →
275
+ // givenName, tail trimmed → familyName (null if empty). Single-token names
276
+ // land as givenName only.
277
+ const firstSpace = trimmedName.search(/\s/);
278
+ const givenName = firstSpace === -1 ? trimmedName : trimmedName.slice(0, firstSpace).trim();
279
+ const familyName = firstSpace === -1 ? null : trimmedName.slice(firstSpace + 1).trim() || null;
280
+ // The owner :Person carries the same admin-personal shape the house owner's
281
+ // Person gets (writeAdminUserAndPerson). embedding is null here — a fresh
282
+ // sub-account is unpopulated, and the standing reindex embeds it; the seed
283
+ // must not depend on an embedder to keep it unit-testable and boot-safe.
238
284
  const res = await session.run(`MERGE (lb:LocalBusiness {accountId: $accountId})
239
285
  ON CREATE SET lb.createdBySource = 'installer',
240
286
  lb.createdByAgent = 'system',
@@ -242,14 +288,28 @@ export async function seedAccountGraphRoot(args) {
242
288
  MERGE (au:AdminUser {userId: $ownerUserId})
243
289
  ON CREATE SET au.accountId = $accountId,
244
290
  au.role = 'owner',
245
- au.name = $ownerName,
246
291
  au.createdAt = $createdAt
247
292
  ON MATCH SET au.accountId = COALESCE(au.accountId, $accountId)
248
293
  MERGE (au)-[r:ADMIN_OF]->(lb)
249
294
  ON CREATE SET r.role = 'owner', r.grantedAt = $createdAt
295
+ // Task 1567 — the owner Person, created in the same statement so the name
296
+ // is never a field backfilled later. MERGE-keyed on (accountId, ownerUserId)
297
+ // so the whole seed stays idempotent to match the LocalBusiness/AdminUser
298
+ // MERGEs above it — a plain CREATE would duplicate the Person on any replay.
299
+ MERGE (au)-[:OWNS]->(owner:Person {accountId: $accountId, userId: $ownerUserId})
300
+ ON CREATE SET owner.givenName = $givenName,
301
+ owner.familyName = $familyName,
302
+ owner.role = 'admin-personal',
303
+ owner.scope = 'admin',
304
+ owner.embedding = null,
305
+ owner.createdAt = $createdAt
250
306
  WITH lb.accountId AS acct
251
307
  RETURN COUNT { MATCH (b:LocalBusiness {accountId: acct}) } > 0 AS hasBusiness,
252
- COUNT { MATCH (a:AdminUser {accountId: acct}) } > 0 AS hasAdmin`, { accountId, ownerUserId, ownerName, createdAt });
308
+ COUNT { MATCH (a:AdminUser {accountId: acct}) } > 0 AS hasAdmin`,
309
+ // Task 1570 — the owner name lives only on the owned Person. ownerName is the
310
+ // trimmed value that feeds the givenName/familyName split; the AdminUser
311
+ // carries no name.
312
+ { accountId, ownerUserId, ownerName: trimmedName, givenName, familyName, createdAt });
253
313
  const rec = res.records[0];
254
314
  return {
255
315
  businessSeeded: Boolean(rec?.get("hasBusiness")),
@@ -1 +1 @@
1
- {"version":3,"file":"account-lifecycle.js","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account-lifecycle.js","sourceRoot":"","sources":["../../src/tools/account-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
4
- content-hash: sha256:bd08bc341313562b1dd2139010b1120b23d7aab130e1d32c3e800678ad559f7c
4
+ content-hash: sha256:359dfc8bdd471d36f84a6cf223c8a1621dd66f00245f5e8f8e2674642036601b
5
5
  brand: maxy-code
6
6
  product-name: Maxy
7
7
  ---
@@ -2737,7 +2737,7 @@ either is a regression.
2737
2737
  |---|---|---|
2738
2738
  | `/session` | Admin cookie session: PIN-gated mint, validate, rotate. | `GET /`, `POST /` |
2739
2739
  | `/sessions` | Legacy admin-server conversation routes. No UI consumer remains after the ConversationsModal was retired; the surviving handlers are deletion candidates and not described here. | (legacy, no live caller) |
2740
- | `/sidebar-sessions` | Sole data path for the sidebar Sessions list. One JSONL on disk equals one row. The row's delete button is the only way a row disappears. Each row carries `sessionId`, `title`, `startedAt`, `live`, `isSubagent`, `pid: number \| null` (basename of the matched `sessions/<pid>.json`), and `projectDir` (the directory holding the JSONL — consumed by the delete route). The payload also carries top-level `accountId` so the pane renders the full UUID label whose first ~8 chars prefix-match the truncated Remote Control daemon entry in claude.ai/code. The legacy `rcUrl` field is gone — the row's external-link affordance now POSTs `/session-rc-spawn` to start a fresh local `claude --remote-control <name> --session-id <sid>` PTY on every click. | `GET /` |
2740
+ | `/sidebar-sessions` | Sole data path for the sidebar Sessions list. One JSONL on disk equals one row. The row's delete button is the only way a row disappears. Each row carries `sessionId`, `title`, `startedAt`, `live`, `isSubagent`, `pid: number \| null` (basename of the matched `sessions/<pid>.json`), and `projectDir` (the directory holding the JSONL — consumed by the delete route). The payload also carries top-level `accountId` so the pane renders the full UUID label whose first ~8 chars prefix-match the truncated Remote Control daemon entry in claude.ai/code. The legacy `rcUrl` field is gone — the row's external-link affordance now POSTs `/session-rc-spawn` to start a fresh local `claude --remote-control <name> --session-id <sid>` PTY on every click. **WhatsApp and Telegram sessions (admin + public) appear in this list**, colour-coded by channel (WhatsApp green `#25D366`, Telegram blue `#229ED9`, via a `conv-channel-*` left accent bar) and carrying the standard row kebab (Usage & cost, Reset/re-seat, Rename, Archive, Delete) so the operator can inspect and re-seat a channel session's params. Each row's `channel` is the authoritative `.meta.json` sidecar value (`whatsapp`/`telegram`/`webchat`/`browser`/null), not the JSONL-envelope channel. The only role/channel excluded from the list is `public`+`webchat` (its dedicated reader is the sole surface); the exclusion predicate is `isSessionListExcluded`, decoupled from the reader's inclusion predicate `isReaderChannelSession` (unchanged), so the WhatsApp/Telegram reader panels are unaffected — a store-only WhatsApp conversation with no session JSONL stays reader-only. Observability rides `[admin-sessions-list] … channelInList=whatsapp:<n>,telegram:<n>,webchat:<n> unknownChannel=<n> excludedPublicWebchat=<n>`. | `GET /` |
2741
2741
  | `/session-delete` | POST `{ sessionId }`. Thin proxy over the manager: POST `/:id/stop` (idempotent) then DELETE `/:id`. Sends **no** accountId. The manager resolves the session by scanning every slug under `projects/` for its JSONL (`slugForExistingJsonl`, covering `archive/`), exactly as the sidebar list enumerates rows, and gates on file existence alone — so any session the operator can see is deletable. This replaces the earlier accountId-derived slug and the `!row` term, which narrowed resolution below the list and left a session under a non-house slug with no watcher row (e.g. a legacy `-home-admin` session on a single-account install) listed yet un-deletable. Account scope is a view filter, not a delete gate — admin access is install-wide, so there is nothing to scope against. The sibling `/session-stop` (the standalone End control) resolves identically and also forwards no accountId. | `POST /` |
2742
2742
  | `/session-rc-spawn` | POST `{ sessionId?, name? }`. Fire-and-forget `claude --remote-control [name] [--session-id <sid>]`. Present `sessionId` resumes; absent starts a fresh session (also used by the sidebar's "New session" button — it no longer opens claude.ai/code directly). Proxies to the manager's `/rc-spawn`, which waits up to **60 s** (raised from 12 s) for the spawned PTY to bind and returns `{ spawnedPid, sessionId, bridgeSessionId, slug, outcome, reason }`. For a webchat-bound spawn (every admin-gated host's "New session", returning a same-origin `/chat?session=<id>` target — `resolveRcSpawnOutcome` → `sameOrigin:true`) the Sidebar navigates the **current** tab via `window.location.assign`, replacing the dashboard in place (back returns to it); only a claude.ai/code slug (`sameOrigin:false`, the bare-admin resume bridge, never a new-session outcome) navigates a separately-opened tab. On `timeout` or `spawn-failed` it shows an error modal (reason + sessionId) and **never** opens a bare claude.ai/code tab. The new process registers itself as its own Remote Control entry in claude.ai/code. | `POST /` |
2743
2743
  | `/claude-sessions` | **Spawn surface only**. `POST /` is the Sidebar new-session-with-prompt path, cookie-auth only (the recorder loopback caller was removed; LinkedIn ingest moved to `/rc-spawn`). The former UI-facing handlers (SSE row feed, list, resume, stop, rename, archive, delete, `/:id/meta`, `/:id/input`, `/:id/log`) were removed — the maxy dashboard no longer manages or displays sessions. | `POST /` |
@@ -2750,7 +2750,7 @@ either is a regression.
2750
2750
 
2751
2751
  **`/chat` composer transport button.** The button at the end of the input shows a **microphone** while the box is empty — tap it to record a voice note. Type a character, or attach a file, and the same button becomes the **send** arrow; while the agent is replying it becomes a **stop** square. So an empty composer offers voice, a composer with something to send offers send, and a running turn offers stop — one button, three states. The microphone-device chooser (which input to record from) stays in the row of icons below the box. This is the same on the admin `/chat`, the public chat, and the maxy-lite webchat.
2752
2752
 
2753
- **`/chat` Conversations flyout + zero-sessions splash.** The admin `/chat` `HeaderMenu` carries a **Conversations** item (rendered only when `onOpenConversations` is wired, which scopes it to `/chat`) that opens an in-chat session-management pane (`app/chat/SessionList.tsx`) hosted by `chat/page.tsx` — distinct from, and sharing endpoints with, the `Sidebar` Sessions list (which stays hidden on narrow viewports). It enumerates the admin's own webchat sessions via `GET /api/admin/sidebar-sessions` (carries the per-row `live` marker and `archived` flag, install-wide), lists them newest-first with the live one marked and archived rows folded under a collapsible subsection, and offers per-row resume (`/chat?session=<id>`), rename, archive/unarchive, two-tap delete, an **End** control on the live row (`session-stop`), plus a New-session control (`session-rc-spawn`) and a copyable full id. The pane header carries the title and a top-right close (X) control that dismisses the flyout (no back-chevron/"Chat" label); the New-session control floats at the pane's bottom-left. When the canonical pointer is `known:false` (`canonical-empty`) and enumeration returns zero rows, the surface renders a splash (brand logo + New session) instead of a dead bootstrap thread; a freshly-spawned New session is `known:true`, so its greeting is preserved. Client breadcrumbs: `[chat-conversations] op=enumerate owned=<n>` and `op=action name=<open|rename|archive|delete|stop> sessionId=<id8>` (`op=action-failed … status=…` on a non-2xx). Detail lives in `.docs/admin-webchat-native-channel.md` ("Conversations flyout + zero-sessions splash").
2753
+ **`/chat` Conversations flyout + zero-sessions splash.** The admin `/chat` `HeaderMenu` carries a **Conversations** item (rendered only when `onOpenConversations` is wired, which scopes it to `/chat`) that opens an in-chat session-management pane (`app/chat/SessionList.tsx`) hosted by `chat/page.tsx` — distinct from, and sharing endpoints with, the `Sidebar` Sessions list (which stays hidden on narrow viewports). It enumerates the admin's own webchat sessions via `GET /api/admin/sidebar-sessions` (carries the per-row `live` marker and `archived` flag, install-wide), **filtering out `channel:'whatsapp'`/`'telegram'` rows** (those are channel-bound, cannot be chatted in webchat, and live in the WhatsApp/Telegram reader panels; the same endpoint now also feeds the sidebar Sessions list, which does show them), lists the remainder newest-first with the live one marked and archived rows folded under a collapsible subsection, and offers per-row resume (`/chat?session=<id>`), rename, archive/unarchive, two-tap delete, an **End** control on the live row (`session-stop`), plus a New-session control (`session-rc-spawn`) and a copyable full id. The pane header carries the title and a top-right close (X) control that dismisses the flyout (no back-chevron/"Chat" label); the New-session control floats at the pane's bottom-left. When the canonical pointer is `known:false` (`canonical-empty`) and enumeration returns zero rows, the surface renders a splash (brand logo + New session) instead of a dead bootstrap thread; a freshly-spawned New session is `known:true`, so its greeting is preserved. Client breadcrumbs: `[chat-conversations] op=enumerate owned=<n>` and `op=action name=<open|rename|archive|delete|stop> sessionId=<id8>` (`op=action-failed … status=…` on a non-2xx). Detail lives in `.docs/admin-webchat-native-channel.md` ("Conversations flyout + zero-sessions splash").
2754
2754
 
2755
2755
  **`/chat` header always shows the account name.** The admin/operator `HeaderMenu` centre always renders the active account's name alongside the brand logo, on every chat surface, regardless of the active conversation — `headerTitle = businessName || brand.productName`. There is no per-conversation header title: the active conversation's label stays in the sidebar and the Conversations flyout (each resolves its own per-session title independently), never in the header. The account name is `LocalBusiness.name`, resolved by `fetchAccountName(accountId)` in `neo4j-store.ts` with **no theme gate** — unlike `fetchBranding` (which returns null unless a colour/logo property is also set, and stays the source for actual theming). The house account is seeded with `LocalBusiness.name = <brand productName>` (`seed-neo4j.sh`, coalesced ON MATCH so a re-seed backfills a name-less node and never clobbers a name the business-profile recorder set later), so the sub-account picker's house row shows the product name (e.g. "SiteDesk"), not the raw accountId UUID. When a name is absent the resolver emits `[branding] account-name-fallback accountId=<id8>… reason=no-name`, and the picker falls back to the accountId; the seed + backfill is the name source. The lite variant header is unchanged.
2756
2756
 
@@ -3871,11 +3871,11 @@ Standing rules the operator gives over a channel are `Preference` nodes. Two mec
3871
3871
 
3872
3872
  **Reconcile by meaning on write (`profile-update`).** The existence check is two-tier. First an exact `(category, key)` match — the classic fast path, unchanged. When that misses, a semantic-neighbour lookup runs over the `preference_embedding` vector index, scoped to the same `accountId` and `scope:'admin'`, non-trashed. If the nearest neighbour is at or above `SAME_SUBJECT_COSINE` (0.85), the write lands on that node through the existing reinforce/update/contradict curve instead of minting a duplicate; the matched neighbours are returned in the tool result so the agent can classify future writes against real prior state. The confidence constants are unchanged — only the join key widens, so a rephrasing under a new `key` no longer forks the rule and a correction demotes the prior node rather than adding a contradictory one.
3873
3873
 
3874
- **Inject the active rule-set every turn (channel gateways).** The one definition of "this account's active rules" is `@maxy/active-rules`: a bounded, floor-filtered (`confidence >= 0.4`), confidence-ordered candidate query, then semantic de-confliction (one winner per subject at the same 0.85 cosine), capped to `INJECTION_RULE_CAP` (12). `profile-read` exposes this set as `activeRules`, and the three inbound gateways (`wa-gateway`, `telegram-gateway`, `webchat-gateway`) fetch it for the effective session account and attach it as `standingRules` on the inbound frame; the channel builders prepend it as a `## Standing rules` block above `## Context`. Both channel-server inbound paths carry the block: the single-target SSE loop parses the whole frame, and the unified multi-target door reader (`runDoorStream`,) reconstructs it through the shared `doorInboundPayload` seam whose `parseFrame` closures read `standingRules` off the frame. The read is by code, not agent discretion, so a rule set last week is applied this week regardless of how many times the session compacted. Telegram and webchat inject for `role:'admin'` only — a public visitor never receives operator rules.
3874
+ **Inject the active rule-set every turn (channel gateways).** The one definition of "this account's active rules" is `@maxy/active-rules`: the account owner's admin-scope preferences (resolve the seeded `AdminUser {role:'owner'}`, then select through that owner's `UserProfile` via `HAS_PREFERENCE`), a bounded, floor-filtered (`confidence >= 0.4`), confidence-ordered candidate query, then semantic de-confliction (one winner per subject at the same 0.85 cosine), capped to `INJECTION_RULE_CAP` (12). A preference owned by a non-owner profile (an operator's, stranded on the operator's own profile while managing the account) is never injected; an account with no seeded owner injects nothing. `profile-read` exposes this same owner-scoped set as `activeRules`, and the three inbound gateways (`wa-gateway`, `telegram-gateway`, `webchat-gateway`) fetch it for the effective session account and attach it as `standingRules` on the inbound frame; the channel builders prepend it as a `## Standing rules` block above `## Context`. Both channel-server inbound paths carry the block: the single-target SSE loop parses the whole frame, and the unified multi-target door reader (`runDoorStream`,) reconstructs it through the shared `doorInboundPayload` seam whose `parseFrame` closures read `standingRules` off the frame. The read is by code, not agent discretion, so a rule set last week is applied this week regardless of how many times the session compacted. Telegram and webchat inject for `role:'admin'` only — a public visitor never receives operator rules.
3875
3875
 
3876
3876
  **Trust a scheduled turn's provenance, never an inbound's claim (channel-agent grounding).** A cron-dispatched agent turn reaches the same channel builders as a real inbound, so its own authorisation text would otherwise land inside `## Context` — the zone `REFRAME_INSTRUCTION` demotes to untrusted — and a security-cautious agent would distrust its own automation (the self-cancel loop). The dispatcher therefore stamps a `ScheduleProvenance` marker (owning account, event, owner userId, dispatch time) that the channel builders render as a `## Scheduled automation (platform-verified)` preamble **above** `## Context` (and above the rules block); the whatsapp wrap tag names the door `scheduler`. Both channel-server paths carry the marker — the single-target loop and the unified multi-target door reader (via the same `doorInboundPayload` seam that carries `standingRules`); the scheduler only dispatches to the whatsapp/telegram door, so a webchat frame never bears one. Because the marker is dispatcher-stamped and never derived from payload text, an inbound message cannot forge it — placement above `## Context` alone is not the trust signal, the dispatcher provenance is. The grounding binds to it in two places: one recipient-neutral clause added byte-identical to the three reframe instructions (a marker-bearing turn is authorised standing automation; a reconfiguration or re-enable is not intrusion), and the admin `IDENTITY.md` "Untrusted input" rule that the agent never states a platform-level authenticity or impersonation verdict it was not given (an unsourced "this is not from you" is its own inference, flagged, never asserted as the platform's). The amplifier that let one misread harden was the accreting seat: a recurring event resumed one long-lived session every firing. A subsequent change removes it — each firing runs a fresh per-firing seat (`scheduledSeatIdFor`, salted with the firing's eventId + dispatch time), so distrust cannot accrete across firings, and the self-cancel guard compares the cancelling `SESSION_ID` to the node's dispatcher-stamped `lastDispatchSessionId` rather than a recomputed deterministic seat. See `.docs/scheduling-agent-dispatch.md § Operator-provenance marker` and `§ Session model — fresh per firing`.
3877
3877
 
3878
- **Observability.** `[preference-write] op=reconcile … mode=<reinforce|update|contradict|new> similarity=…` — a stream of `mode=new` for subjects that already exist is the duplicate-minting signature. `[preference-inject] op=inject channel=<wa|tg|web> injected=<N>` — `injected=0` on an account with stored rules is the write-only regression re-appearing. `[preference-audit] op=drift accountId=… storedActive=… injectedActive=… duplicateClusters=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose store holds more active rows than injection would surface, the no-event detector for drift.
3878
+ **Observability.** `[preference-write] op=reconcile … mode=<reinforce|update|contradict|new> similarity=…` — a stream of `mode=new` for subjects that already exist is the duplicate-minting signature. `[preference-inject] op=inject channel=<wa|tg|web> injected=<N> owner=<8> source=<owner|no-owner|owner-error|fetch-error>` — injection selects only the account owner's admin preferences, so `source=owner injected=0` is a genuinely empty owner, `source=no-owner` is an account with no seeded owner (injects nothing, a missed owner seeding), and `injected=0 source=owner` on an account whose owner holds rules is the write-only regression re-appearing. `[preference-audit] op=drift accountId=… storedActive=… injectedActive=… duplicateClusters=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose store holds more active rows than injection would surface, the no-event detector for drift.
3879
3879
 
3880
3880
  ---
3881
3881
 
@@ -4019,7 +4019,7 @@ The doctrine is observed at the storage primitive: writes to `:Person`, `:UserPr
4019
4019
 
4020
4020
  Two surfaces emit the lifecycle: agent-driven actions call `work-create`/`work-update`/`work-complete` over MCP (`work-create` accepts `kind`, the canonical `inputsProvided` call-shape record, `inputs` + `inputSchema` for the operator-meaningful form payload, and `raisedDuringConversationKey` to resolve the `RAISED_DURING` edge). Shell-driven actions wrap their script invocation in [platform/ui/app/lib/cloudflare-task-tracker.ts](../../../ui/app/lib/cloudflare-task-tracker.ts) (cloudflare is the first; installer / brand-publish / OAuth-login deferred). Both surfaces emit the same `[task] action-start|step|done` log lines so operators can grep one channel uniformly. Both also call the central `redactSecrets` primitive ([platform/lib/task-secrets/](../../../lib/task-secrets/)) to strip schema-tagged secret keys before persisting `inputs.<field>` props on the Task — see `.docs/neo4j.md § Audit Task input contract` for the contract that replaces per-kind allow-lists.
4021
4021
 
4022
- For estate-agent client work surfaced on the `/tasks` operator page, `work-create` also accepts optional `clientName`, `propertyAddress`, and `fee` (GBP major units) — plain props recorded on the `:Task`, omitted when absent. Billable time against a task is recorded separately as `:TimeEntry` nodes (`(:TimeEntry)-[:LOGGED_AGAINST]->(:Task)`): a running timer is the single account entry with `endedAt=null`, and each task carries a denormalized `secondsLogged` sum for list rendering. The platform records only the time; it computes no charges. See [`.docs/admin-tasks-page.md`](../../../../.docs/admin-tasks-page.md).
4022
+ For estate-agent client work surfaced on the `/tasks` operator page, `work-create` also accepts optional `clientName`, `propertyAddress`, and `fee` (GBP major units) — plain props recorded on the `:Task`, omitted when absent. Billable time against a task is recorded separately as `:TimeEntry` nodes (`(:TimeEntry)-[:LOGGED_AGAINST]->(:Task)`): a running timer is a `:TimeEntry` with `endedAt=null`, timers on different tasks run concurrently, and each task carries a denormalized `secondsLogged` sum for list rendering. A **manual** time correction (the `/tasks` page Edit/Reset controls, `task-time-adjust` route) sets `secondsLogged` to an absolute value and appends an append-only `:TimeAdjustment {adjustmentId, taskId, accountId, at, previousSeconds, newSeconds, delta, sourceSessionKey}` audit record — property-scoped, no edge. So the `[timeentry-census]` invariant is `secondsLogged == sum(TimeEntry.seconds) + sum(TimeAdjustment.delta)`, and an adjust is rejected while the task has an open `:TimeEntry`. The platform records only the time; it computes no charges. See [`.docs/admin-tasks-page.md`](../../../../.docs/admin-tasks-page.md).
4023
4023
 
4024
4024
  Two surfaces feed the gate. (1) **Workflow path:** `memory-write` accepts an optional `producedByTaskId` parameter. When set, an inbound `:PRODUCED` edge from that Task is composed into the write's relationships before the gate runs — the typical agent-side pattern is to call `work-create` at the start of an autonomous flow, capture `taskId`, and pass it as `producedByTaskId` on every subsequent `memory-write` for a gated label. The gate verifies Task and write share the same `accountId`; mismatch is rejected loud. (2) **Direct-ask path:** the admin server resolves the active `:AdminConversation`'s `sessionId` UUID and stamps it as `SESSION_NODE_ID` in the spawn env at PTY-spawn time. The same stamp propagates onto specialist subagent spawns the admin dispatches so listing-curator, content-producer, database-operator etc. inherit the same conversation anchor. The `contact-create` and `memory-write` wrappers call `injectConversationProvenance` (exported from [`@maxy/graph-write`](../../../lib/graph-write/src/conversation-provenance.ts)) which MATCHes `(c:Conversation {sessionId, accountId})` — account isolation is part of the natural key, not a separate gate — and prepends the synthetic `:PRODUCED` edge (composed by Neo4j elementId, which the helper reads off the MATCH). No agent-visible schema field changes. `memory-write` uses the env-stamp only as a fallback when `producedByTaskId` is unset; `contact-create` has no `producedByTaskId` parameter today and relies on the env-stamp alone. Autonomous (cron-driven) specialists with no parent conversation legitimately have no env-stamp; those must thread `producedByTaskId`.
4025
4025
 
@@ -51,6 +51,46 @@ Deterministic per-admin `userId` on loopback is deliberately out of scope:
51
51
  making it deterministic again means a per-session identity step, which is exactly
52
52
  the deadlock the retirement removed.
53
53
 
54
+ ## Credential-write identity binding
55
+
56
+ The admin MCP credential tools (`remote-auth-set-password`, `admin-update-pin`,
57
+ `remote-auth-revoke-password`) write the `accessHash` and `pin` fields on
58
+ `users.json` rows. Their operating identity is `process.env.USER_ID`, which the
59
+ admin MCP inherits from its parent `claude` PTY.
60
+
61
+ A prior defect stamped the wrong identity onto that env. The `/rc-spawn` handler
62
+ built the child `USER_ID` as `overrideUserId ?? deps.userId`, where `deps.userId`
63
+ is the boot-time owner. An authenticated non-owner admin whose spawn request
64
+ carried the authenticated `adminUserId` but no per-session `userId` therefore ran
65
+ with the owner's `USER_ID`, and a credential tool called with no explicit
66
+ `userId` rewrote the owner's row. The audit recorded the owner as the actor, so
67
+ it read as an owner self-change.
68
+
69
+ The binding now holds:
70
+
71
+ - `USER_ID` at `/rc-spawn` is `resolvedAdminUserId (authenticated admin) ??
72
+ overrideUserId ?? ''`. The owner fallback is removed. An authenticated admin
73
+ always wins; an unbound sender with no admin identity gets empty `USER_ID`, a
74
+ documented-legal value. This changes the earlier accepted boundary: an unbound
75
+ channel sender's rc-spawn no longer inherits the owner `userId` at the env
76
+ layer. The `claude rc` daemon env (not a per-session conversation) is
77
+ unchanged and still carries the boot owner `userId`.
78
+ - A credential tool with no explicit `userId` resolves to the verified caller
79
+ (`USER_ID`, empty treated as no identity). With no verified identity it errors
80
+ and writes nothing; it never substitutes an ambient env value for a missing
81
+ target.
82
+ - Writing a *different* admin's credential requires an explicit `userId` and
83
+ `confirm: true`. Self writes and same-identity writes need no confirm. This
84
+ keeps the "any admin may rotate any admin's PIN with an explicit userId" trust
85
+ model while making a cross-identity change a deliberate two-signal act.
86
+ - Every credential write records `actor` (the verified caller), `target` (the
87
+ row written), and `explicitUserId` in `users-audit.log`. The standing
88
+ signatures: `[users-audit] … actor=<x> target=<y> explicitUserId=<bool>` and,
89
+ per spawn, `[identity-audit] op=mcp-user-binding seat=<x> mcpUserId=<y>
90
+ match=<bool>`. The alarms are `actor != target && explicitUserId=false` and
91
+ `match=false` — a self-targeted write that hit another row, or an authenticated
92
+ admin whose stamped `USER_ID` differs from that admin.
93
+
54
94
  ## Boot-time binding drift gate
55
95
 
56
96
  The channel-admin binding is reassembled at read time from three independently
@@ -97,6 +97,8 @@ The metadata pane surfaces two distinct identifier values. The three-id model wa
97
97
 
98
98
  **Re-seat mints a new id.** Model, mode, and effort are inception-only levers — claude reads them when a session is born and the `claude rc` daemon exposes no per-session switch — so changing one on an *existing* session forks it: `/api/admin/session-reseat` pre-mints a fresh `sessionId`, the manager `/rc-spawn` fork branch runs `--resume <old> --fork-session --session-id <new> --model <model>` (copying history into the new id), and the operator lands on the fork. The fork pins the chosen model and, when supplied, the chosen `permissionMode` (pushed as `--permission-mode`, one of the 5 composer-writable modes) and `effort` (`low|medium|high|xhigh`, merged into the per-spawn inline `--settings.effortLevel`); each is validated against its allowlist before reaching the argv (a present-but-disallowed value is a 400). Both `/chat`'s composer pickers and the dashboard's per-row Re-seat control drive this one fork, for webchat and WhatsApp/Telegram sessions alike. On `/chat`, every picker now carries the operator's *current* model, mode, and effort and overrides only the picked lever, so changing one never silently resets the other two (a non-writable current mode is omitted rather than rejected). When a re-seated webchat session runs in `default` ("Ask permissions") mode, a tool the agent attempts that needs approval surfaces an Allow/Deny prompt in `/chat` and blocks until the operator answers — over Claude Code's channel permission relay, the only interactive ask surface (WhatsApp stays text-only). The full write path is in [`admin-webchat-native-channel.md`](../../../.docs/admin-webchat-native-channel.md).
99
99
 
100
+ **The footer reads model/effort/turn-count from one shared JSONL resolver.** The `/chat` footer's model / effort / turn-count are the manager `GET /:sessionId/meta` enrichment, read from the session's JSONL tail. That read resolves the JSONL by the **same all-slug scan** the transcript uses — `findExistingJsonlForSessionId`, covering top-level and `archive/` under every project slug — not the single house-cwd slug. Previously the enrichment gated on the `spawnCwd` slug, so a sub-account session rc-spawned in a different cwd (its JSONL under a different slug) reported `model:null turns:0` while its transcript rendered; the transcript already scanned all slugs. `findSessionProjectDir` (the transcript resolver) delegates to the same function, so the two are one resolver — a session findable on disk is never reported `model:null`. The standing line is `[session-meta] op=enrich session=<id8> resolver=scan jsonl=<found|absent> model=… effort=… turns=…`.
101
+
100
102
  | Operator label | What it is | Manager wire field | Log key |
101
103
  |---|---|---|---|
102
104
  | `sessionId` | Claude's session. Two phases: bridge suffix (`session_xxx`, the URL segment in `claude.ai/code/<session_xxx>`, set when claude prints the `/remote-control` URL) and JSONL basename UUID (claude's intrinsic id on disk, bound when the first turn flushes the JSONL). Both phases coexist on a live row after URL capture; the manager wire emits the bridge form when set, falling back to the JSONL basename in the pre-URL-capture window. The resolver routes either phase to the same row, so callers never need to choose. | `sessionId` (collapsed from the earlier three-field surface `sessionId` + `claudeSessionId` + `bridgeSessionId`) | `sessionId=` |
@@ -56,7 +56,7 @@ either is a regression.
56
56
  |---|---|---|
57
57
  | `/session` | Admin cookie session: PIN-gated mint, validate, rotate. | `GET /`, `POST /` |
58
58
  | `/sessions` | Legacy admin-server conversation routes. No UI consumer remains after the ConversationsModal was retired; the surviving handlers are deletion candidates and not described here. | (legacy, no live caller) |
59
- | `/sidebar-sessions` | Sole data path for the sidebar Sessions list. One JSONL on disk equals one row. The row's delete button is the only way a row disappears. Each row carries `sessionId`, `title`, `startedAt`, `live`, `isSubagent`, `pid: number \| null` (basename of the matched `sessions/<pid>.json`), and `projectDir` (the directory holding the JSONL — consumed by the delete route). The payload also carries top-level `accountId` so the pane renders the full UUID label whose first ~8 chars prefix-match the truncated Remote Control daemon entry in claude.ai/code. The legacy `rcUrl` field is gone — the row's external-link affordance now POSTs `/session-rc-spawn` to start a fresh local `claude --remote-control <name> --session-id <sid>` PTY on every click. | `GET /` |
59
+ | `/sidebar-sessions` | Sole data path for the sidebar Sessions list. One JSONL on disk equals one row. The row's delete button is the only way a row disappears. Each row carries `sessionId`, `title`, `startedAt`, `live`, `isSubagent`, `pid: number \| null` (basename of the matched `sessions/<pid>.json`), and `projectDir` (the directory holding the JSONL — consumed by the delete route). The payload also carries top-level `accountId` so the pane renders the full UUID label whose first ~8 chars prefix-match the truncated Remote Control daemon entry in claude.ai/code. The legacy `rcUrl` field is gone — the row's external-link affordance now POSTs `/session-rc-spawn` to start a fresh local `claude --remote-control <name> --session-id <sid>` PTY on every click. **WhatsApp and Telegram sessions (admin + public) appear in this list**, colour-coded by channel (WhatsApp green `#25D366`, Telegram blue `#229ED9`, via a `conv-channel-*` left accent bar) and carrying the standard row kebab (Usage & cost, Reset/re-seat, Rename, Archive, Delete) so the operator can inspect and re-seat a channel session's params. Each row's `channel` is the authoritative `.meta.json` sidecar value (`whatsapp`/`telegram`/`webchat`/`browser`/null), not the JSONL-envelope channel. The only role/channel excluded from the list is `public`+`webchat` (its dedicated reader is the sole surface); the exclusion predicate is `isSessionListExcluded`, decoupled from the reader's inclusion predicate `isReaderChannelSession` (unchanged), so the WhatsApp/Telegram reader panels are unaffected — a store-only WhatsApp conversation with no session JSONL stays reader-only. Observability rides `[admin-sessions-list] … channelInList=whatsapp:<n>,telegram:<n>,webchat:<n> unknownChannel=<n> excludedPublicWebchat=<n>`. | `GET /` |
60
60
  | `/session-delete` | POST `{ sessionId }`. Thin proxy over the manager: POST `/:id/stop` (idempotent) then DELETE `/:id`. Sends **no** accountId. The manager resolves the session by scanning every slug under `projects/` for its JSONL (`slugForExistingJsonl`, covering `archive/`), exactly as the sidebar list enumerates rows, and gates on file existence alone — so any session the operator can see is deletable. This replaces the earlier accountId-derived slug and the `!row` term, which narrowed resolution below the list and left a session under a non-house slug with no watcher row (e.g. a legacy `-home-admin` session on a single-account install) listed yet un-deletable. Account scope is a view filter, not a delete gate — admin access is install-wide, so there is nothing to scope against. The sibling `/session-stop` (the standalone End control) resolves identically and also forwards no accountId. | `POST /` |
61
61
  | `/session-rc-spawn` | POST `{ sessionId?, name? }`. Fire-and-forget `claude --remote-control [name] [--session-id <sid>]`. Present `sessionId` resumes; absent starts a fresh session (also used by the sidebar's "New session" button — it no longer opens claude.ai/code directly). Proxies to the manager's `/rc-spawn`, which waits up to **60 s** (raised from 12 s) for the spawned PTY to bind and returns `{ spawnedPid, sessionId, bridgeSessionId, slug, outcome, reason }`. For a webchat-bound spawn (every admin-gated host's "New session", returning a same-origin `/chat?session=<id>` target — `resolveRcSpawnOutcome` → `sameOrigin:true`) the Sidebar navigates the **current** tab via `window.location.assign`, replacing the dashboard in place (back returns to it); only a claude.ai/code slug (`sameOrigin:false`, the bare-admin resume bridge, never a new-session outcome) navigates a separately-opened tab. On `timeout` or `spawn-failed` it shows an error modal (reason + sessionId) and **never** opens a bare claude.ai/code tab. The new process registers itself as its own Remote Control entry in claude.ai/code. | `POST /` |
62
62
  | `/claude-sessions` | **Spawn surface only**. `POST /` is the Sidebar new-session-with-prompt path, cookie-auth only (the recorder loopback caller was removed; LinkedIn ingest moved to `/rc-spawn`). The former UI-facing handlers (SSE row feed, list, resume, stop, rename, archive, delete, `/:id/meta`, `/:id/input`, `/:id/log`) were removed — the maxy dashboard no longer manages or displays sessions. | `POST /` |
@@ -69,7 +69,7 @@ either is a regression.
69
69
 
70
70
  **`/chat` composer transport button.** The button at the end of the input shows a **microphone** while the box is empty — tap it to record a voice note. Type a character, or attach a file, and the same button becomes the **send** arrow; while the agent is replying it becomes a **stop** square. So an empty composer offers voice, a composer with something to send offers send, and a running turn offers stop — one button, three states. The microphone-device chooser (which input to record from) stays in the row of icons below the box. This is the same on the admin `/chat`, the public chat, and the maxy-lite webchat.
71
71
 
72
- **`/chat` Conversations flyout + zero-sessions splash.** The admin `/chat` `HeaderMenu` carries a **Conversations** item (rendered only when `onOpenConversations` is wired, which scopes it to `/chat`) that opens an in-chat session-management pane (`app/chat/SessionList.tsx`) hosted by `chat/page.tsx` — distinct from, and sharing endpoints with, the `Sidebar` Sessions list (which stays hidden on narrow viewports). It enumerates the admin's own webchat sessions via `GET /api/admin/sidebar-sessions` (carries the per-row `live` marker and `archived` flag, install-wide), lists them newest-first with the live one marked and archived rows folded under a collapsible subsection, and offers per-row resume (`/chat?session=<id>`), rename, archive/unarchive, two-tap delete, an **End** control on the live row (`session-stop`), plus a New-session control (`session-rc-spawn`) and a copyable full id. The pane header carries the title and a top-right close (X) control that dismisses the flyout (no back-chevron/"Chat" label); the New-session control floats at the pane's bottom-left. When the canonical pointer is `known:false` (`canonical-empty`) and enumeration returns zero rows, the surface renders a splash (brand logo + New session) instead of a dead bootstrap thread; a freshly-spawned New session is `known:true`, so its greeting is preserved. Client breadcrumbs: `[chat-conversations] op=enumerate owned=<n>` and `op=action name=<open|rename|archive|delete|stop> sessionId=<id8>` (`op=action-failed … status=…` on a non-2xx). Detail lives in `.docs/admin-webchat-native-channel.md` ("Conversations flyout + zero-sessions splash").
72
+ **`/chat` Conversations flyout + zero-sessions splash.** The admin `/chat` `HeaderMenu` carries a **Conversations** item (rendered only when `onOpenConversations` is wired, which scopes it to `/chat`) that opens an in-chat session-management pane (`app/chat/SessionList.tsx`) hosted by `chat/page.tsx` — distinct from, and sharing endpoints with, the `Sidebar` Sessions list (which stays hidden on narrow viewports). It enumerates the admin's own webchat sessions via `GET /api/admin/sidebar-sessions` (carries the per-row `live` marker and `archived` flag, install-wide), **filtering out `channel:'whatsapp'`/`'telegram'` rows** (those are channel-bound, cannot be chatted in webchat, and live in the WhatsApp/Telegram reader panels; the same endpoint now also feeds the sidebar Sessions list, which does show them), lists the remainder newest-first with the live one marked and archived rows folded under a collapsible subsection, and offers per-row resume (`/chat?session=<id>`), rename, archive/unarchive, two-tap delete, an **End** control on the live row (`session-stop`), plus a New-session control (`session-rc-spawn`) and a copyable full id. The pane header carries the title and a top-right close (X) control that dismisses the flyout (no back-chevron/"Chat" label); the New-session control floats at the pane's bottom-left. When the canonical pointer is `known:false` (`canonical-empty`) and enumeration returns zero rows, the surface renders a splash (brand logo + New session) instead of a dead bootstrap thread; a freshly-spawned New session is `known:true`, so its greeting is preserved. Client breadcrumbs: `[chat-conversations] op=enumerate owned=<n>` and `op=action name=<open|rename|archive|delete|stop> sessionId=<id8>` (`op=action-failed … status=…` on a non-2xx). Detail lives in `.docs/admin-webchat-native-channel.md` ("Conversations flyout + zero-sessions splash").
73
73
 
74
74
  **`/chat` header always shows the account name.** The admin/operator `HeaderMenu` centre always renders the active account's name alongside the brand logo, on every chat surface, regardless of the active conversation — `headerTitle = businessName || brand.productName`. There is no per-conversation header title: the active conversation's label stays in the sidebar and the Conversations flyout (each resolves its own per-session title independently), never in the header. The account name is `LocalBusiness.name`, resolved by `fetchAccountName(accountId)` in `neo4j-store.ts` with **no theme gate** — unlike `fetchBranding` (which returns null unless a colour/logo property is also set, and stays the source for actual theming). The house account is seeded with `LocalBusiness.name = <brand productName>` (`seed-neo4j.sh`, coalesced ON MATCH so a re-seed backfills a name-less node and never clobbers a name the business-profile recorder set later), so the sub-account picker's house row shows the product name (e.g. "SiteDesk"), not the raw accountId UUID. When a name is absent the resolver emits `[branding] account-name-fallback accountId=<id8>… reason=no-name`, and the picker falls back to the accountId; the seed + backfill is the name source. The lite variant header is unchanged.
75
75
 
@@ -400,11 +400,11 @@ Standing rules the operator gives over a channel are `Preference` nodes. Two mec
400
400
 
401
401
  **Reconcile by meaning on write (`profile-update`).** The existence check is two-tier. First an exact `(category, key)` match — the classic fast path, unchanged. When that misses, a semantic-neighbour lookup runs over the `preference_embedding` vector index, scoped to the same `accountId` and `scope:'admin'`, non-trashed. If the nearest neighbour is at or above `SAME_SUBJECT_COSINE` (0.85), the write lands on that node through the existing reinforce/update/contradict curve instead of minting a duplicate; the matched neighbours are returned in the tool result so the agent can classify future writes against real prior state. The confidence constants are unchanged — only the join key widens, so a rephrasing under a new `key` no longer forks the rule and a correction demotes the prior node rather than adding a contradictory one.
402
402
 
403
- **Inject the active rule-set every turn (channel gateways).** The one definition of "this account's active rules" is `@maxy/active-rules`: a bounded, floor-filtered (`confidence >= 0.4`), confidence-ordered candidate query, then semantic de-confliction (one winner per subject at the same 0.85 cosine), capped to `INJECTION_RULE_CAP` (12). `profile-read` exposes this set as `activeRules`, and the three inbound gateways (`wa-gateway`, `telegram-gateway`, `webchat-gateway`) fetch it for the effective session account and attach it as `standingRules` on the inbound frame; the channel builders prepend it as a `## Standing rules` block above `## Context`. Both channel-server inbound paths carry the block: the single-target SSE loop parses the whole frame, and the unified multi-target door reader (`runDoorStream`,) reconstructs it through the shared `doorInboundPayload` seam whose `parseFrame` closures read `standingRules` off the frame. The read is by code, not agent discretion, so a rule set last week is applied this week regardless of how many times the session compacted. Telegram and webchat inject for `role:'admin'` only — a public visitor never receives operator rules.
403
+ **Inject the active rule-set every turn (channel gateways).** The one definition of "this account's active rules" is `@maxy/active-rules`: the account owner's admin-scope preferences (resolve the seeded `AdminUser {role:'owner'}`, then select through that owner's `UserProfile` via `HAS_PREFERENCE`), a bounded, floor-filtered (`confidence >= 0.4`), confidence-ordered candidate query, then semantic de-confliction (one winner per subject at the same 0.85 cosine), capped to `INJECTION_RULE_CAP` (12). A preference owned by a non-owner profile (an operator's, stranded on the operator's own profile while managing the account) is never injected; an account with no seeded owner injects nothing. `profile-read` exposes this same owner-scoped set as `activeRules`, and the three inbound gateways (`wa-gateway`, `telegram-gateway`, `webchat-gateway`) fetch it for the effective session account and attach it as `standingRules` on the inbound frame; the channel builders prepend it as a `## Standing rules` block above `## Context`. Both channel-server inbound paths carry the block: the single-target SSE loop parses the whole frame, and the unified multi-target door reader (`runDoorStream`,) reconstructs it through the shared `doorInboundPayload` seam whose `parseFrame` closures read `standingRules` off the frame. The read is by code, not agent discretion, so a rule set last week is applied this week regardless of how many times the session compacted. Telegram and webchat inject for `role:'admin'` only — a public visitor never receives operator rules.
404
404
 
405
405
  **Trust a scheduled turn's provenance, never an inbound's claim (channel-agent grounding).** A cron-dispatched agent turn reaches the same channel builders as a real inbound, so its own authorisation text would otherwise land inside `## Context` — the zone `REFRAME_INSTRUCTION` demotes to untrusted — and a security-cautious agent would distrust its own automation (the self-cancel loop). The dispatcher therefore stamps a `ScheduleProvenance` marker (owning account, event, owner userId, dispatch time) that the channel builders render as a `## Scheduled automation (platform-verified)` preamble **above** `## Context` (and above the rules block); the whatsapp wrap tag names the door `scheduler`. Both channel-server paths carry the marker — the single-target loop and the unified multi-target door reader (via the same `doorInboundPayload` seam that carries `standingRules`); the scheduler only dispatches to the whatsapp/telegram door, so a webchat frame never bears one. Because the marker is dispatcher-stamped and never derived from payload text, an inbound message cannot forge it — placement above `## Context` alone is not the trust signal, the dispatcher provenance is. The grounding binds to it in two places: one recipient-neutral clause added byte-identical to the three reframe instructions (a marker-bearing turn is authorised standing automation; a reconfiguration or re-enable is not intrusion), and the admin `IDENTITY.md` "Untrusted input" rule that the agent never states a platform-level authenticity or impersonation verdict it was not given (an unsourced "this is not from you" is its own inference, flagged, never asserted as the platform's). The amplifier that let one misread harden was the accreting seat: a recurring event resumed one long-lived session every firing. A subsequent change removes it — each firing runs a fresh per-firing seat (`scheduledSeatIdFor`, salted with the firing's eventId + dispatch time), so distrust cannot accrete across firings, and the self-cancel guard compares the cancelling `SESSION_ID` to the node's dispatcher-stamped `lastDispatchSessionId` rather than a recomputed deterministic seat. See `.docs/scheduling-agent-dispatch.md § Operator-provenance marker` and `§ Session model — fresh per firing`.
406
406
 
407
- **Observability.** `[preference-write] op=reconcile … mode=<reinforce|update|contradict|new> similarity=…` — a stream of `mode=new` for subjects that already exist is the duplicate-minting signature. `[preference-inject] op=inject channel=<wa|tg|web> injected=<N>` — `injected=0` on an account with stored rules is the write-only regression re-appearing. `[preference-audit] op=drift accountId=… storedActive=… injectedActive=… duplicateClusters=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose store holds more active rows than injection would surface, the no-event detector for drift.
407
+ **Observability.** `[preference-write] op=reconcile … mode=<reinforce|update|contradict|new> similarity=…` — a stream of `mode=new` for subjects that already exist is the duplicate-minting signature. `[preference-inject] op=inject channel=<wa|tg|web> injected=<N> owner=<8> source=<owner|no-owner|owner-error|fetch-error>` — injection selects only the account owner's admin preferences, so `source=owner injected=0` is a genuinely empty owner, `source=no-owner` is an account with no seeded owner (injects nothing, a missed owner seeding), and `injected=0 source=owner` on an account whose owner holds rules is the write-only regression re-appearing. `[preference-audit] op=drift accountId=… storedActive=… injectedActive=… duplicateClusters=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose store holds more active rows than injection would surface, the no-event detector for drift.
408
408
 
409
409
  ---
410
410
 
@@ -548,7 +548,7 @@ The doctrine is observed at the storage primitive: writes to `:Person`, `:UserPr
548
548
 
549
549
  Two surfaces emit the lifecycle: agent-driven actions call `work-create`/`work-update`/`work-complete` over MCP (`work-create` accepts `kind`, the canonical `inputsProvided` call-shape record, `inputs` + `inputSchema` for the operator-meaningful form payload, and `raisedDuringConversationKey` to resolve the `RAISED_DURING` edge). Shell-driven actions wrap their script invocation in [platform/ui/app/lib/cloudflare-task-tracker.ts](../../../ui/app/lib/cloudflare-task-tracker.ts) (cloudflare is the first; installer / brand-publish / OAuth-login deferred). Both surfaces emit the same `[task] action-start|step|done` log lines so operators can grep one channel uniformly. Both also call the central `redactSecrets` primitive ([platform/lib/task-secrets/](../../../lib/task-secrets/)) to strip schema-tagged secret keys before persisting `inputs.<field>` props on the Task — see `.docs/neo4j.md § Audit Task input contract` for the contract that replaces per-kind allow-lists.
550
550
 
551
- For estate-agent client work surfaced on the `/tasks` operator page, `work-create` also accepts optional `clientName`, `propertyAddress`, and `fee` (GBP major units) — plain props recorded on the `:Task`, omitted when absent. Billable time against a task is recorded separately as `:TimeEntry` nodes (`(:TimeEntry)-[:LOGGED_AGAINST]->(:Task)`): a running timer is the single account entry with `endedAt=null`, and each task carries a denormalized `secondsLogged` sum for list rendering. The platform records only the time; it computes no charges. See [`.docs/admin-tasks-page.md`](../../../../.docs/admin-tasks-page.md).
551
+ For estate-agent client work surfaced on the `/tasks` operator page, `work-create` also accepts optional `clientName`, `propertyAddress`, and `fee` (GBP major units) — plain props recorded on the `:Task`, omitted when absent. Billable time against a task is recorded separately as `:TimeEntry` nodes (`(:TimeEntry)-[:LOGGED_AGAINST]->(:Task)`): a running timer is a `:TimeEntry` with `endedAt=null`, timers on different tasks run concurrently, and each task carries a denormalized `secondsLogged` sum for list rendering. A **manual** time correction (the `/tasks` page Edit/Reset controls, `task-time-adjust` route) sets `secondsLogged` to an absolute value and appends an append-only `:TimeAdjustment {adjustmentId, taskId, accountId, at, previousSeconds, newSeconds, delta, sourceSessionKey}` audit record — property-scoped, no edge. So the `[timeentry-census]` invariant is `secondsLogged == sum(TimeEntry.seconds) + sum(TimeAdjustment.delta)`, and an adjust is rejected while the task has an open `:TimeEntry`. The platform records only the time; it computes no charges. See [`.docs/admin-tasks-page.md`](../../../../.docs/admin-tasks-page.md).
552
552
 
553
553
  Two surfaces feed the gate. (1) **Workflow path:** `memory-write` accepts an optional `producedByTaskId` parameter. When set, an inbound `:PRODUCED` edge from that Task is composed into the write's relationships before the gate runs — the typical agent-side pattern is to call `work-create` at the start of an autonomous flow, capture `taskId`, and pass it as `producedByTaskId` on every subsequent `memory-write` for a gated label. The gate verifies Task and write share the same `accountId`; mismatch is rejected loud. (2) **Direct-ask path:** the admin server resolves the active `:AdminConversation`'s `sessionId` UUID and stamps it as `SESSION_NODE_ID` in the spawn env at PTY-spawn time. The same stamp propagates onto specialist subagent spawns the admin dispatches so listing-curator, content-producer, database-operator etc. inherit the same conversation anchor. The `contact-create` and `memory-write` wrappers call `injectConversationProvenance` (exported from [`@maxy/graph-write`](../../../lib/graph-write/src/conversation-provenance.ts)) which MATCHes `(c:Conversation {sessionId, accountId})` — account isolation is part of the natural key, not a separate gate — and prepends the synthetic `:PRODUCED` edge (composed by Neo4j elementId, which the helper reads off the MATCH). No agent-visible schema field changes. `memory-write` uses the env-stamp only as a fallback when `producedByTaskId` is unset; `contact-create` has no `producedByTaskId` parameter today and relies on the env-stamp alone. Autonomous (cron-driven) specialists with no parent conversation legitimately have no env-stamp; those must thread `producedByTaskId`.
554
554
 
@@ -86,7 +86,7 @@ The parser converts:
86
86
  ```cypher
87
87
  MATCH (au:AdminUser {userId: $ownerUserId})-[:HAS_PROFILE]->(up:UserProfile)
88
88
  RETURN
89
- au.name, au.userId,
89
+ au.userId,
90
90
  up.givenName, up.familyName, up.headline, up.description,
91
91
  up.websites, up.linkedinProfileUpdatedAt
92
92
  ```
@@ -1217,13 +1217,17 @@ if (!readOnly) {
1217
1217
  });
1218
1218
  eagerTool(server, "memory-reindex", "Rebuild vector embeddings for all nodes missing embeddings. Run after seeding or bulk updates.", {
1219
1219
  targetAccountId: z.string().optional().describe(XACCT_DESC),
1220
- }, async ({ targetAccountId }) => {
1220
+ }, async ({ targetAccountId }, extra) => {
1221
1221
  const scoped = resolveToolAccount("memory-reindex", targetAccountId);
1222
1222
  if ("reject" in scoped)
1223
1223
  return scoped.reject;
1224
1224
  const accountId = scoped.accountId;
1225
+ // Task 1560 — stream a progress notification per embed POST so a long
1226
+ // reindex resets Claude Code's stdio idle timer and is not aborted
1227
+ // mid-run. No-ops when the client sent no progressToken.
1228
+ const reportProgress = makeEmbedProgressReporter(extra);
1225
1229
  try {
1226
- const result = await memoryReindex(accountId);
1230
+ const result = await memoryReindex(accountId, reportProgress);
1227
1231
  return {
1228
1232
  content: [
1229
1233
  {
@@ -1467,11 +1471,15 @@ if (!readOnly) {
1467
1471
  .optional()
1468
1472
  .describe("Skill-run UUID for provenance stamping. Falls back to SESSION_ID env var when absent."),
1469
1473
  targetAccountId: z.string().optional().describe(XACCT_DESC),
1470
- }, async ({ archiveType, ownerNodeId, rows, sessionId: sessionIdOverride, targetAccountId }) => {
1474
+ }, async ({ archiveType, ownerNodeId, rows, sessionId: sessionIdOverride, targetAccountId }, extra) => {
1471
1475
  const scoped = resolveToolAccount("memory-archive-write", targetAccountId);
1472
1476
  if ("reject" in scoped)
1473
1477
  return scoped.reject;
1474
1478
  const accountId = scoped.accountId;
1479
+ // Task 1560 — stream a progress notification per embed POST so a large
1480
+ // archive import resets Claude Code's stdio idle timer and is not aborted
1481
+ // mid-run. No-ops when the client sent no progressToken.
1482
+ const reportProgress = makeEmbedProgressReporter(extra);
1475
1483
  try {
1476
1484
  const result = await memoryArchiveWrite({
1477
1485
  archiveType,
@@ -1479,6 +1487,7 @@ if (!readOnly) {
1479
1487
  accountId,
1480
1488
  rows: rows,
1481
1489
  sessionId: resolveSessionId(sessionIdOverride),
1490
+ reportProgress,
1482
1491
  });
1483
1492
  return {
1484
1493
  content: [{