@rubytech/create-maxy-code 0.1.500 → 0.1.501

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 (184) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-style/dist/index.d.ts +9 -0
  3. package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/graph-style/dist/index.js +152 -6
  5. package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
  6. package/payload/platform/lib/graph-style/src/__tests__/caption-coverage.test.ts +69 -0
  7. package/payload/platform/lib/graph-style/src/__tests__/coverage-source.ts +73 -0
  8. package/payload/platform/lib/graph-style/src/__tests__/icons.test.ts +20 -3
  9. package/payload/platform/lib/graph-style/src/index.ts +166 -5
  10. package/payload/platform/plugins/admin/PLUGIN.md +1 -0
  11. package/payload/platform/plugins/admin/skills/agent-builder/SKILL.md +15 -0
  12. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +51 -1
  13. package/payload/platform/plugins/admin/skills/summarise/SKILL.md +37 -0
  14. package/payload/platform/plugins/docs/references/admin-ui.md +50 -0
  15. package/payload/platform/plugins/docs/references/summarise-skill.md +7 -0
  16. package/payload/server/public/activity.html +6 -6
  17. package/payload/server/public/assets/{AdminLoginScreens-CKg_Dlpv.js → AdminLoginScreens-C3LZOPIY.js} +1 -1
  18. package/payload/server/public/assets/AdminShell-BlbUNAPc.js +2 -0
  19. package/payload/server/public/assets/{activity-CKUYOLd2.js → activity-CVdVSw5V.js} +1 -1
  20. package/payload/server/public/assets/admin-If1QLlS3.js +1 -0
  21. package/payload/server/public/assets/{arc-CpWWSum8.js → arc-CvlzUHOf.js} +1 -1
  22. package/payload/server/public/assets/architecture-YZFGNWBL-BQc779DH.js +1 -0
  23. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-D2EzaII0.js → architectureDiagram-Q4EWVU46-I1Hs0RJA.js} +1 -1
  24. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-8kDpaNSZ.js → blockDiagram-DXYQGD6D-Df5jTGtl.js} +1 -1
  25. package/payload/server/public/assets/{bot-DESMpxSQ.js → bot-8b26IlQY.js} +1 -1
  26. package/payload/server/public/assets/{browser-CpLq_DIo.js → browser-F40DXfKA.js} +1 -1
  27. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CbwmZpK3.js → c4Diagram-AHTNJAMY-B6WY5IDv.js} +1 -1
  28. package/payload/server/public/assets/{calendar-ClOwyuz-.js → calendar-DZiHiBwV.js} +1 -1
  29. package/payload/server/public/assets/channel-AqnC3SHu.js +1 -0
  30. package/payload/server/public/assets/chat-uD28FVCS.js +1 -0
  31. package/payload/server/public/assets/chevron-left-lBxD57tD.js +1 -0
  32. package/payload/server/public/assets/chevron-right-C-lDig9Y.js +1 -0
  33. package/payload/server/public/assets/{chunk-2KRD3SAO-PFheZ9mw.js → chunk-2KRD3SAO-BsIIFDkc.js} +1 -1
  34. package/payload/server/public/assets/{chunk-336JU56O-CCFOp5VK.js → chunk-336JU56O-j9z1VtyC.js} +2 -2
  35. package/payload/server/public/assets/chunk-426QAEUC-CO02gU5d.js +1 -0
  36. package/payload/server/public/assets/{chunk-4BX2VUAB-Hz4JWOf2.js → chunk-4BX2VUAB-C9i7prxq.js} +1 -1
  37. package/payload/server/public/assets/{chunk-4TB4RGXK-4xeV0s85.js → chunk-4TB4RGXK-DH0QVGcv.js} +1 -1
  38. package/payload/server/public/assets/{chunk-55IACEB6-4QKgBpoh.js → chunk-55IACEB6-Dnd_YCPA.js} +1 -1
  39. package/payload/server/public/assets/{chunk-5FUZZQ4R-ms3K6Z5z.js → chunk-5FUZZQ4R-D5sSz3yN.js} +1 -1
  40. package/payload/server/public/assets/{chunk-5PVQY5BW-CIC4GIGi.js → chunk-5PVQY5BW-CJrwWjAT.js} +1 -1
  41. package/payload/server/public/assets/{chunk-67CJDMHE-B-XrWVmV.js → chunk-67CJDMHE-SjP6_hUw.js} +1 -1
  42. package/payload/server/public/assets/{chunk-7N4EOEYR-C7ECOUYQ.js → chunk-7N4EOEYR-fAuhVJD0.js} +1 -1
  43. package/payload/server/public/assets/{chunk-AA7GKIK3-DOZ6Fxwk.js → chunk-AA7GKIK3-DZxa7KVg.js} +1 -1
  44. package/payload/server/public/assets/{chunk-BSJP7CBP-4cJ_b0c6.js → chunk-BSJP7CBP-DjeG3eQ-.js} +1 -1
  45. package/payload/server/public/assets/{chunk-CIAEETIT--mhZipWM.js → chunk-CIAEETIT-RYQULNJm.js} +1 -1
  46. package/payload/server/public/assets/{chunk-EDXVE4YY-jZpVwob_.js → chunk-EDXVE4YY-Cahapnon.js} +1 -1
  47. package/payload/server/public/assets/{chunk-ENJZ2VHE-Nn-p-xIA.js → chunk-ENJZ2VHE-1146X1L4.js} +1 -1
  48. package/payload/server/public/assets/{chunk-FMBD7UC4-DyNpcKOS.js → chunk-FMBD7UC4-BCFumtcP.js} +1 -1
  49. package/payload/server/public/assets/{chunk-FOC6F5B3-C0P8q1uz.js → chunk-FOC6F5B3-CkrtX8Zt.js} +1 -1
  50. package/payload/server/public/assets/{chunk-ICPOFSXX-CdXQ5SfI.js → chunk-ICPOFSXX-Ba1y3DaW.js} +2 -2
  51. package/payload/server/public/assets/{chunk-K5T4RW27-BVZJKKsT.js → chunk-K5T4RW27-CF-PI3uQ.js} +1 -1
  52. package/payload/server/public/assets/{chunk-KGLVRYIC-QlVWTe0V.js → chunk-KGLVRYIC-SFFC6ZC-.js} +1 -1
  53. package/payload/server/public/assets/{chunk-LIHQZDEY-CpGVZutP.js → chunk-LIHQZDEY-DbOLEP4R.js} +1 -1
  54. package/payload/server/public/assets/{chunk-ORNJ4GCN-WVcS6fUC.js → chunk-ORNJ4GCN-VB963bkN.js} +1 -1
  55. package/payload/server/public/assets/{chunk-OYMX7WX6-B7hiSTed.js → chunk-OYMX7WX6-DWYY1pQa.js} +1 -1
  56. package/payload/server/public/assets/chunk-QZHKN3VN-BiFMbK8t.js +1 -0
  57. package/payload/server/public/assets/{chunk-U2HBQHQK-DJPnZxGy.js → chunk-U2HBQHQK-DAH1diYP.js} +1 -1
  58. package/payload/server/public/assets/{chunk-X2U36JSP-DilBs1Yc.js → chunk-X2U36JSP-Dmw9coXz.js} +1 -1
  59. package/payload/server/public/assets/{chunk-XPW4576I-B5-uyrJ0.js → chunk-XPW4576I-DaAUON1-.js} +1 -1
  60. package/payload/server/public/assets/{chunk-YZCP3GAM-BNO4z0Mv.js → chunk-YZCP3GAM-BUcfkEef.js} +1 -1
  61. package/payload/server/public/assets/{chunk-ZZ45TVLE-BzHMQpip.js → chunk-ZZ45TVLE-BLD-vFRO.js} +1 -1
  62. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BdRqzScB.js +1 -0
  63. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-ChPLgHbS.js +1 -0
  64. package/payload/server/public/assets/clock-DqRNsY40.js +1 -0
  65. package/payload/server/public/assets/clone-TKtrjWBC.js +1 -0
  66. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-3DWYmn1o.js → cose-bilkent-S5V4N54A-CoZX0uki.js} +1 -1
  67. package/payload/server/public/assets/{dagre-Cpy31xJW.js → dagre-Bmjs8zJA.js} +1 -1
  68. package/payload/server/public/assets/{dagre-KV5264BT-C8fyxHFd.js → dagre-KV5264BT-BZCP2wnq.js} +1 -1
  69. package/payload/server/public/assets/data-BsFyBN85.js +1 -0
  70. package/payload/server/public/assets/{diagram-5BDNPKRD-ycyhnLvr.js → diagram-5BDNPKRD-8Nt9a4xX.js} +1 -1
  71. package/payload/server/public/assets/{diagram-G4DWMVQ6-BkeYicIt.js → diagram-G4DWMVQ6-vh5tcYqJ.js} +1 -1
  72. package/payload/server/public/assets/{diagram-MMDJMWI5-BcGZU-Q3.js → diagram-MMDJMWI5-DGf--jc8.js} +1 -1
  73. package/payload/server/public/assets/{diagram-TYMM5635-DuBDCFb_.js → diagram-TYMM5635-CsO7TIdw.js} +1 -1
  74. package/payload/server/public/assets/{dist-BYc_arNy.js → dist-cdnE4DqK.js} +1 -1
  75. package/payload/server/public/assets/{erDiagram-SMLLAGMA-DmkeOzvp.js → erDiagram-SMLLAGMA-4a78yV8_.js} +1 -1
  76. package/payload/server/public/assets/file-text-B1HjPFbM.js +1 -0
  77. package/payload/server/public/assets/{flatten-QjnJ1Gcl.js → flatten-BtIc9jX7.js} +1 -1
  78. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-CkE-pjuB.js → flowDiagram-DWJPFMVM-BoQBXs6t.js} +1 -1
  79. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-Cjiy3kvE.js → ganttDiagram-T4ZO3ILL-CvzqwCC8.js} +1 -1
  80. package/payload/server/public/assets/gitGraph-7Q5UKJZL-DfcaTE9L.js +1 -0
  81. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CsI4lKZm.js → gitGraphDiagram-UUTBAWPF-D_nBxay_.js} +1 -1
  82. package/payload/server/public/assets/graph-CIC-mgKR.js +51 -0
  83. package/payload/server/public/assets/graph-labels-BnpHCHaQ.js +1 -0
  84. package/payload/server/public/assets/{graphlib-BkXxC6D5.js → graphlib-D0NdsKJI.js} +1 -1
  85. package/payload/server/public/assets/info-OMHHGYJF-CD-u6gw9.js +1 -0
  86. package/payload/server/public/assets/infoDiagram-42DDH7IO-C6gPQCvO.js +2 -0
  87. package/payload/server/public/assets/{isEmpty-BavJI9Cn.js → isEmpty-Bu4fZ8Kf.js} +1 -1
  88. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-D8lZtjRc.js → ishikawaDiagram-UXIWVN3A-auW-s0i0.js} +1 -1
  89. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-kLE7SiR_.js → journeyDiagram-VCZTEJTY-3Q6AWK4A.js} +1 -1
  90. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-ove6JuD3.js → kanban-definition-6JOO6SKY-B4MPFp98.js} +1 -1
  91. package/payload/server/public/assets/{line-CZMSoxM6.js → line-26MdeG5Z.js} +1 -1
  92. package/payload/server/public/assets/{linear-Be8IK9hK.js → linear-Dq9s_bDO.js} +1 -1
  93. package/payload/server/public/assets/{maximize-2-BbzPm7o8.js → maximize-2-BL7QVTcQ.js} +1 -1
  94. package/payload/server/public/assets/{mermaid-parser.core-BBADqbnp.js → mermaid-parser.core-vLEpOXgf.js} +2 -2
  95. package/payload/server/public/assets/{mermaid.core-mH3N7K0I.js → mermaid.core-Durk4m6D.js} +3 -3
  96. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BjwwwWil.js → mindmap-definition-QFDTVHPH-DOIg-TyG.js} +1 -1
  97. package/payload/server/public/assets/{operator-DglDYmdV.js → operator-B62h_TaC.js} +1 -1
  98. package/payload/server/public/assets/{ordinal-BHeIKG-K.js → ordinal-C2TEWKNu.js} +1 -1
  99. package/payload/server/public/assets/packet-4T2RLAQJ-BaLYeIrU.js +1 -0
  100. package/payload/server/public/assets/page-BUUBseJZ.js +32 -0
  101. package/payload/server/public/assets/page-Bw-tYjw7.js +1 -0
  102. package/payload/server/public/assets/{pdf-render-fJz4vym9.js → pdf-render-C257cidb.js} +1 -1
  103. package/payload/server/public/assets/pie-ZZUOXDRM-CL-XJtk-.js +1 -0
  104. package/payload/server/public/assets/{pieDiagram-DEJITSTG-D-lXWWta.js → pieDiagram-DEJITSTG-DtgBHZfi.js} +1 -1
  105. package/payload/server/public/assets/{public-DqJjXR4p.js → public-BI21tOkm.js} +1 -1
  106. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-13QyG_AO.js → quadrantDiagram-34T5L4WZ-BTc5Qn2y.js} +1 -1
  107. package/payload/server/public/assets/radar-PYXPWWZC-Cn-g6F_h.js +1 -0
  108. package/payload/server/public/assets/{reduce-Fm685to3.js → reduce-BGpN5lgn.js} +1 -1
  109. package/payload/server/public/assets/{requirementDiagram-MS252O5E-Bx45Bgn4.js → requirementDiagram-MS252O5E-BNgOO5YW.js} +1 -1
  110. package/payload/server/public/assets/{rotate-ccw-L42fL8ey.js → rotate-ccw-C_20Bc3E.js} +1 -1
  111. package/payload/server/public/assets/routines-Ci3N8wqV.js +1 -0
  112. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-Cj-Gq25v.js → sankeyDiagram-XADWPNL6-MyUATmVt.js} +1 -1
  113. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-B8RDRq93.js → sequenceDiagram-FGHM5R23-CBJrF5Vi.js} +1 -1
  114. package/payload/server/public/assets/skills-CiflzOko.js +1 -0
  115. package/payload/server/public/assets/{src-D3VYuW_Z.js → src-DekKlUCp.js} +1 -1
  116. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DoyFF1Kl.js → stateDiagram-FHFEXIEX-6wbUjn1Q.js} +1 -1
  117. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BdMdfBeC.js +1 -0
  118. package/payload/server/public/assets/{tasks-GNtvZL8F.js → tasks-DC2Yf2Ev.js} +1 -1
  119. package/payload/server/public/assets/{time-entry-format-BreNpFLJ.js → time-entry-format-CNcjs2HV.js} +1 -1
  120. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-Df4RQXm6.js → timeline-definition-GMOUNBTQ-BHjTMCGY.js} +1 -1
  121. package/payload/server/public/assets/treeView-SZITEDCU-CmZoOGlC.js +1 -0
  122. package/payload/server/public/assets/treemap-W4RFUUIX-H27WFS-y.js +1 -0
  123. package/payload/server/public/assets/{triangle-alert-DumCWC08.js → triangle-alert-CngnWtjD.js} +1 -1
  124. package/payload/server/public/assets/{useCopyFeedback-DHCtz8qX.js → useCopyFeedback-DZ9Bw5CI.js} +1 -1
  125. package/payload/server/public/assets/{useSubAccountSwitcher-Bc7XHr0L.js → useSubAccountSwitcher-CHwQhkaI.js} +1 -1
  126. package/payload/server/public/assets/{useSubAccountSwitcher-DPHQJO7E.css → useSubAccountSwitcher-yFQqnpQ8.css} +1 -1
  127. package/payload/server/public/assets/useVoiceRecorder-C3G6aqZr.js +2 -0
  128. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-D2o5og9l.js → vennDiagram-DHZGUBPP-yo3-cf0U.js} +1 -1
  129. package/payload/server/public/assets/wardley-RL74JXVD-DNpWIvU4.js +1 -0
  130. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BDueObbS.js → wardleyDiagram-NUSXRM2D-DZlyFzrg.js} +1 -1
  131. package/payload/server/public/assets/{wrench-iWkROE1V.js → wrench-dky2-13x.js} +1 -1
  132. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-BTYs4Fgh.js → xychartDiagram-5P7HB3ND-B5HV5h80.js} +1 -1
  133. package/payload/server/public/browser.html +5 -5
  134. package/payload/server/public/calendar.html +8 -8
  135. package/payload/server/public/chat.html +16 -15
  136. package/payload/server/public/data.html +13 -12
  137. package/payload/server/public/graph.html +10 -10
  138. package/payload/server/public/index.html +17 -16
  139. package/payload/server/public/operator.html +17 -16
  140. package/payload/server/public/public.html +16 -15
  141. package/payload/server/public/routines.html +7 -7
  142. package/payload/server/public/skills.html +19 -0
  143. package/payload/server/public/tasks.html +7 -7
  144. package/payload/server/server.js +672 -396
  145. package/payload/server/public/assets/AdminShell-BFVVkWFC.js +0 -2
  146. package/payload/server/public/assets/admin-wtSUU5N0.js +0 -1
  147. package/payload/server/public/assets/architecture-YZFGNWBL-D-AX2VuA.js +0 -1
  148. package/payload/server/public/assets/channel-DZ5sh6YQ.js +0 -1
  149. package/payload/server/public/assets/chat-DRuIxGtE.js +0 -1
  150. package/payload/server/public/assets/chevron-left-BzIg4in6.js +0 -1
  151. package/payload/server/public/assets/chevron-right-BcFSa9C3.js +0 -1
  152. package/payload/server/public/assets/chunk-426QAEUC-CcjBLp4a.js +0 -1
  153. package/payload/server/public/assets/chunk-QZHKN3VN-B2WjDYBE.js +0 -1
  154. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CT3jNrze.js +0 -1
  155. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DJRsjAkC.js +0 -1
  156. package/payload/server/public/assets/clock-Dr3NGtjR.js +0 -1
  157. package/payload/server/public/assets/clone-FcEDnoMT.js +0 -1
  158. package/payload/server/public/assets/data-BdZCvyvN.js +0 -1
  159. package/payload/server/public/assets/gitGraph-7Q5UKJZL-D1uhnP9U.js +0 -1
  160. package/payload/server/public/assets/graph-B7niLM97.js +0 -51
  161. package/payload/server/public/assets/graph-labels-DykmXYHA.js +0 -1
  162. package/payload/server/public/assets/info-OMHHGYJF-oNm27GAV.js +0 -1
  163. package/payload/server/public/assets/infoDiagram-42DDH7IO-LxTftqyS.js +0 -2
  164. package/payload/server/public/assets/packet-4T2RLAQJ-eQNVGiK3.js +0 -1
  165. package/payload/server/public/assets/page-C-5sTAE-.js +0 -32
  166. package/payload/server/public/assets/page-C8G5p4jr.js +0 -1
  167. package/payload/server/public/assets/pie-ZZUOXDRM-E1u-48rt.js +0 -1
  168. package/payload/server/public/assets/radar-PYXPWWZC-Cn2cGN90.js +0 -1
  169. package/payload/server/public/assets/routines-JOV6xdYh.js +0 -1
  170. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Bml-X4-H.js +0 -1
  171. package/payload/server/public/assets/treeView-SZITEDCU-CimabhXy.js +0 -1
  172. package/payload/server/public/assets/treemap-W4RFUUIX-Cg264rpM.js +0 -1
  173. package/payload/server/public/assets/useVoiceRecorder-Cj-VZDKO.js +0 -2
  174. package/payload/server/public/assets/wardley-RL74JXVD-tbgfCg0E.js +0 -1
  175. /package/payload/server/public/assets/{_baseFor-Bvwvhrz4.js → _baseFor-Be3EKK3K.js} +0 -0
  176. /package/payload/server/public/assets/{array-D3d-WgFW.js → array-X0JlPOfd.js} +0 -0
  177. /package/payload/server/public/assets/{chunk-fQC6bEKA.js → chunk-CCr-iYLO.js} +0 -0
  178. /package/payload/server/public/assets/{cytoscape.esm-Cun-OM6a.js → cytoscape.esm-CI2IWMLB.js} +0 -0
  179. /package/payload/server/public/assets/{defaultLocale-CisKV5Ew.js → defaultLocale-B6RGN4id.js} +0 -0
  180. /package/payload/server/public/assets/{init-D9ZOVaZ3.js → init-BmUWJJHz.js} +0 -0
  181. /package/payload/server/public/assets/{katex-C-3ZDK8o.js → katex-BjZuzSQn.js} +0 -0
  182. /package/payload/server/public/assets/{path-BMOgQSLM.js → path-CoPyR7c2.js} +0 -0
  183. /package/payload/server/public/assets/{preload-helper-DL2DwvxV.js → preload-helper-BQrMkyGX.js} +0 -0
  184. /package/payload/server/public/assets/{rough.esm-BrTFrov0.js → rough.esm-jSt7UMyi.js} +0 -0
@@ -42,11 +42,12 @@ export const GRAPH_LABEL_COLOURS: Record<string, string> = {
42
42
  // JobLogic service-delivery ontology (Task 1084; supersedes the Task 650/773
43
43
  // homegrown set). Muted brick-red / earthy family — the service register sits
44
44
  // apart from people-terracotta, viewing-amber, and task-rose bands so
45
- // sitedesk legends read as their own group. Top-level (operator-entry)
46
- // labels only; child labels (QuoteLine, Milestone, Valuation, VariationNote,
47
- // JobCost, Contact, PurchaseOrderLine, InvoiceLine, InvoicePayment, Credit)
48
- // intentionally stay fallback-grey. :Engineer is an additional label on
49
- // Person and takes the Person colour; :Supplier is an additional label on
45
+ // sitedesk legends read as their own group. These are the top-level
46
+ // (operator-entry) labels; the child labels (QuoteLine, Milestone, Valuation,
47
+ // VariationNote, JobCost, Contact, PurchaseOrderLine, InvoiceLine,
48
+ // InvoicePayment, Credit) are coloured in the Task 1965 block appended at the
49
+ // end of this object, not here. :Engineer is an additional label on Person
50
+ // and takes the Person colour; :Supplier is an additional label on
50
51
  // Organization and takes the tobacco Organization colour. All hexes here are
51
52
  // pairwise-unique across the registry.
52
53
  Job: '#8C3B2E',
@@ -156,6 +157,93 @@ export const GRAPH_LABEL_COLOURS: Record<string, string> = {
156
157
  // (this object's key order), so a mid-table insert would silently change
157
158
  // the rendered shape of every label after it.
158
159
  CashEntry: '#7D5A2B',
160
+
161
+ // --- Task 1965: colour every node type that can appear in the graph ---
162
+ // Appended at the END so SHAPE_BY_LABEL indices for the labels above are
163
+ // unchanged. Grouped by family band; each hex is pairwise-unique across the
164
+ // whole registry (except the pre-existing UserMessage/Preference twin).
165
+
166
+ // Ledger / money children (brown-gold, beside Invoice and CashEntry).
167
+ InvoiceLine: '#7A5326',
168
+ InvoicePayment: '#8A6230',
169
+ QuoteLine: '#9A7038',
170
+ PurchaseOrderLine: '#A67E42',
171
+ JobCost: '#6A4A22',
172
+ Credit: '#B48A48',
173
+ Retainer: '#C29A55',
174
+ Sale: '#8E5E2A',
175
+ Valuation: '#A06A34',
176
+ Order: '#B87C3E',
177
+ DiningOrder: '#C9954E',
178
+
179
+ // Documents / content (steel-blue, hue-adjacent to the business slates).
180
+ Content: '#58708C',
181
+ Report: '#6D8299',
182
+ Page: '#849AAF',
183
+ Post: '#93A7BA',
184
+ VariationNote: '#47617B',
185
+ FileArtifact: '#A9BBCC',
186
+
187
+ // Marketing (bright teal-green, apart from workflow and research teals).
188
+ SocialPost: '#2FA69A',
189
+ Campaign: '#3FB8A0',
190
+ BrandDeal: '#57C2AE',
191
+ Recommendation: '#74CDBB',
192
+
193
+ // CRM people (terracotta, beside Person and Customer).
194
+ Contact: '#C97E52',
195
+ Lead: '#A85E38',
196
+ Position: '#96522E',
197
+ Audience: '#E0B088',
198
+
199
+ // Places / transport / retail (forest-emerald, apart from knowledge moss).
200
+ PostalAddress: '#2E8B57',
201
+ Room: '#3A9B63',
202
+ StoreLocation: '#46A96F',
203
+ Vehicle: '#54B77B',
204
+ Route: '#62C187',
205
+ Stop: '#2A7A4C',
206
+ Shipment: '#6FCB93',
207
+ Reservation: '#7ED4A0',
208
+ TableReservation: '#8CDBAC',
209
+ Menu: '#1F6B41',
210
+ MenuItem: '#98E0B6',
211
+ Product: '#34925E',
212
+ StockMovement: '#4CA574',
213
+
214
+ // Work / delivery (indigo, apart from the strategy indigo band).
215
+ Milestone: '#4A5FA8',
216
+ Deliverable: '#6274C0',
217
+ TimeEntry: '#8189CE',
218
+ Engagement: '#9AA0DA',
219
+ Case: '#3B4E92',
220
+ Meeting: '#5566B4',
221
+
222
+ // Idea (amber).
223
+ Idea: '#E0A82E',
224
+
225
+ // Web analytics (sky-cyan, apart from the research teal).
226
+ Session: '#2AA7C4',
227
+ PageView: '#3DB4CE',
228
+ Click: '#55C0D6',
229
+ ScrollMilestone: '#70CCDE',
230
+ TimelineEvent: '#1E93B0',
231
+ AnonVisitor: '#88D6E6',
232
+ AEOAudit: '#46B0CB',
233
+
234
+ // WhatsApp transport (brand green).
235
+ WhatsAppConversation: '#128C7E',
236
+ WhatsAppMessage: '#1FA894',
237
+
238
+ // Cloudflare / chain infra (grey-blue).
239
+ CloudflareHostname: '#5B6E82',
240
+ CloudflareTunnel: '#6E8296',
241
+ Chain: '#47586A',
242
+ ChainLink: '#8496AA',
243
+
244
+ // Voice (violet).
245
+ VoiceProfile: '#9B59B6',
246
+ VoiceEdit: '#B07CC9',
159
247
  }
160
248
 
161
249
  export const FALLBACK_LABEL_COLOUR = '#94A3B8'
@@ -251,6 +339,18 @@ export function shapeForLabel(label: string): GraphNodeShape {
251
339
  return SHAPE_BY_LABEL[label] ?? FALLBACK_LABEL_SHAPE
252
340
  }
253
341
 
342
+ /**
343
+ * Node labels written by code with no schema node-type-table row and no
344
+ * typed-edge declaration. They still render as graph nodes, so they must carry
345
+ * a colour + glyph like any other node type. The reconciliation gate in
346
+ * __tests__/icons.test.ts folds this set into the node-label universe. Add a
347
+ * label here when the runtime `op=icon-fallback` census names one that no
348
+ * schema table or typed edge declares.
349
+ */
350
+ export const CODE_EMITTED_NODE_LABELS: ReadonlySet<string> = Object.freeze(
351
+ new Set(['FileArtifact']),
352
+ ) as ReadonlySet<string>
353
+
254
354
  // ---------------------------------------------------------------------------
255
355
  // Icon registry — a third visual channel alongside colour + shape.
256
356
  //
@@ -325,6 +425,67 @@ const LABEL_ICONS: Record<string, string> = {
325
425
  Finding: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"></path><path d="M9 18h6"></path><path d="M10 22h4"></path>',
326
426
  Hypothesis: '<path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"></path><path d="M6.453 15h11.094"></path><path d="M8.5 2h7"></path>',
327
427
  CashEntry: '<rect width="20" height="12" x="2" y="6" rx="2"></rect><circle cx="12" cy="12" r="2"></circle><path d="M6 12h.01"></path><path d="M18 12h.01"></path>',
428
+ // --- Task 1965: glyphs for the 60 previously-uncovered node labels ---
429
+ InvoiceLine: '<path d="M13 16H8" /><path d="M14 8H8" /><path d="M16 12H8" /><path d="M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" />',
430
+ InvoicePayment: '<path d="M11 15h2a2 2 0 1 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 17" /><path d="m7 21 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a2 2 0 0 0-2.75-2.91l-4.2 3.9" /><path d="m2 16 6 6" /><circle cx="16" cy="9" r="2.9" /><circle cx="6" cy="5" r="3" />',
431
+ QuoteLine: '<path d="M3 5h.01" /><path d="M3 12h.01" /><path d="M3 19h.01" /><path d="M8 5h13" /><path d="M8 12h13" /><path d="M8 19h13" />',
432
+ PurchaseOrderLine: '<path d="M11 5h10" /><path d="M11 12h10" /><path d="M11 19h10" /><path d="M4 4h1v5" /><path d="M4 9h2" /><path d="M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02" />',
433
+ JobCost: '<rect width="20" height="12" x="2" y="6" rx="2" /><circle cx="12" cy="12" r="2" /><path d="M6 12h.01M18 12h.01" />',
434
+ Credit: '<path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8" /><path d="M12 18V6" />',
435
+ Retainer: '<path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1" /><path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4" />',
436
+ Sale: '<path d="M12 17V7" /><path d="M16 8h-6a2 2 0 0 0 0 4h4a2 2 0 0 1 0 4H8" /><path d="M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" />',
437
+ Valuation: '<path d="M12 3v18" /><path d="m19 8 3 8a5 5 0 0 1-6 0zV7" /><path d="M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1" /><path d="m5 8 3 8a5 5 0 0 1-6 0zV7" /><path d="M7 21h10" />',
438
+ Order: '<path d="M16 10a4 4 0 0 1-8 0" /><path d="M3.103 6.034h17.794" /><path d="M3.4 5.467a2 2 0 0 0-.4 1.2V20a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6.667a2 2 0 0 0-.4-1.2l-2-2.667A2 2 0 0 0 17 2H7a2 2 0 0 0-1.6.8z" />',
439
+ DiningOrder: '<path d="M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2" /><path d="M7 2v20" /><path d="M21 15V2a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7" />',
440
+ Content: '<path d="M15 18h-5" /><path d="M18 14h-8" /><path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-4 0v-9a2 2 0 0 1 2-2h2" /><rect width="8" height="4" x="10" y="6" rx="1" />',
441
+ Report: '<rect width="8" height="4" x="8" y="2" rx="1" ry="1" /><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" /><path d="M12 11h4" /><path d="M12 16h4" /><path d="M8 11h.01" /><path d="M8 16h.01" />',
442
+ Page: '<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" />',
443
+ Post: '<path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" /><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z" />',
444
+ SocialPost: '<path d="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z" /><path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14" /><path d="M8 6v8" />',
445
+ VariationNote: '<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M9 10h6" /><path d="M12 13V7" /><path d="M9 17h6" />',
446
+ Contact: '<path d="M16 2v2" /><path d="M17.915 22a6 6 0 0 0-12 0" /><path d="M8 2v2" /><circle cx="12" cy="12" r="4" /><rect x="3" y="4" width="18" height="18" rx="2" />',
447
+ Lead: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><circle cx="9" cy="7" r="4" /><line x1="19" x2="19" y1="8" y2="14" /><line x1="22" x2="16" y1="11" y2="11" />',
448
+ Position: '<path d="M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" /><rect width="20" height="14" x="2" y="6" rx="2" />',
449
+ Audience: '<path d="M18 21a8 8 0 0 0-16 0" /><circle cx="10" cy="8" r="5" /><path d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3" />',
450
+ PostalAddress: '<path d="M18 8c0 3.613-3.869 7.429-5.393 8.795a1 1 0 0 1-1.214 0C9.87 15.429 6 11.613 6 8a6 6 0 0 1 12 0" /><circle cx="12" cy="8" r="2" /><path d="M8.714 14h-3.71a1 1 0 0 0-.948.683l-2.004 6A1 1 0 0 0 3 22h18a1 1 0 0 0 .948-1.316l-2-6a1 1 0 0 0-.949-.684h-3.712" />',
451
+ Room: '<path d="M11 20H2" /><path d="M11 4.562v16.157a1 1 0 0 0 1.242.97L19 20V5.562a2 2 0 0 0-1.515-1.94l-4-1A2 2 0 0 0 11 4.561z" /><path d="M11 4H8a2 2 0 0 0-2 2v14" /><path d="M14 12h.01" /><path d="M22 20h-3" />',
452
+ StoreLocation: '<path d="M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5" /><path d="M17.774 10.31a1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.451 0 1.12 1.12 0 0 0-1.548 0 2.5 2.5 0 0 1-3.452 0 1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.77-3.248l2.889-4.184A2 2 0 0 1 7 2h10a2 2 0 0 1 1.653.873l2.895 4.192a2.5 2.5 0 0 1-3.774 3.244" /><path d="M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05" />',
453
+ Vehicle: '<path d="M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 16 10s-1.3-1.4-2.2-2.3c-.5-.4-1.1-.7-1.8-.7H5c-.6 0-1.1.4-1.4.9l-1.4 2.9A3.7 3.7 0 0 0 2 12v4c0 .6.4 1 1 1h2" /><circle cx="7" cy="17" r="2" /><path d="M9 17h6" /><circle cx="17" cy="17" r="2" />',
454
+ Route: '<circle cx="6" cy="19" r="3" /><path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15" /><circle cx="18" cy="5" r="3" />',
455
+ Stop: '<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" /><circle cx="12" cy="10" r="3" />',
456
+ Shipment: '<path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" /><path d="M12 22V12" /><polyline points="3.29 7 12 12 20.71 7" /><path d="m7.5 4.27 9 5.15" />',
457
+ Reservation: '<path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" /><path d="m9 16 2 2 4-4" />',
458
+ TableReservation: '<path d="m16 2-2.3 2.3a3 3 0 0 0 0 4.2l1.8 1.8a3 3 0 0 0 4.2 0L22 8" /><path d="M15 15 3.3 3.3a4.2 4.2 0 0 0 0 6l7.3 7.3c.7.7 2 .7 2.8 0L15 15Zm0 0 7 7" /><path d="m2.1 21.8 6.4-6.3" /><path d="m19 5-7 7" />',
459
+ Menu: '<path d="M12 5v16" /><path d="M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z" />',
460
+ MenuItem: '<path d="M7 21h10" /><path d="M12 21a9 9 0 0 0 9-9H3a9 9 0 0 0 9 9Z" /><path d="M11.38 12a2.4 2.4 0 0 1-.4-4.77 2.4 2.4 0 0 1 3.2-2.77 2.4 2.4 0 0 1 3.47-.63 2.4 2.4 0 0 1 3.37 3.37 2.4 2.4 0 0 1-1.1 3.7 2.51 2.51 0 0 1 .03 1.1" /><path d="m13 12 4-4" /><path d="M10.9 7.25A3.99 3.99 0 0 0 4 10c0 .73.2 1.41.54 2" />',
461
+ Product: '<path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z" /><path d="m3.3 7 8.7 5 8.7-5" /><path d="M12 22V12" />',
462
+ StockMovement: '<path d="m16 3 4 4-4 4" /><path d="M20 7H4" /><path d="m8 21-4-4 4-4" /><path d="M4 17h16" />',
463
+ Milestone: '<path d="M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528" />',
464
+ Deliverable: '<path d="M12 22V12" /><path d="m16 17 2 2 4-4" /><path d="M21 11.127V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.729l7 4a2 2 0 0 0 2 .001l1.32-.753" /><path d="M3.29 7 12 12l8.71-5" /><path d="m7.5 4.27 8.997 5.148" />',
465
+ TimeEntry: '<line x1="10" x2="14" y1="2" y2="2" /><line x1="12" x2="15" y1="14" y2="11" /><circle cx="12" cy="14" r="8" />',
466
+ Engagement: '<path d="m11 17 2 2a1 1 0 1 0 3-3" /><path d="m14 14 2.5 2.5a1 1 0 1 0 3-3l-3.88-3.88a3 3 0 0 0-4.24 0l-.88.88a1 1 0 1 1-3-3l2.81-2.81a5.79 5.79 0 0 1 7.06-.87l.47.28a2 2 0 0 0 1.42.25L21 4" /><path d="m21 3 1 11h-2" /><path d="M3 3 2 14l6.5 6.5a1 1 0 1 0 3-3" /><path d="M3 4h8" />',
467
+ Case: '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />',
468
+ Meeting: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><path d="M16 3.128a4 4 0 0 1 0 7.744" /><path d="M22 21v-2a4 4 0 0 0-3-3.87" /><circle cx="9" cy="7" r="4" />',
469
+ Idea: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5" /><path d="M9 18h6" /><path d="M10 22h4" />',
470
+ Campaign: '<path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09" /><path d="M9 12a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.4 22.4 0 0 1-4 2z" /><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05" />',
471
+ BrandDeal: '<path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="m9 12 2 2 4-4" />',
472
+ Recommendation: '<path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z" /><path d="M7 10v12" />',
473
+ Session: '<rect width="20" height="14" x="2" y="3" rx="2" /><line x1="8" x2="16" y1="21" y2="21" /><line x1="12" x2="12" y1="17" y2="21" />',
474
+ PageView: '<path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" /><circle cx="12" cy="12" r="3" />',
475
+ Click: '<path d="M14 4.1 12 6" /><path d="m5.1 8-2.9-.8" /><path d="m6 12-1.9 2" /><path d="M7.2 2.2 8 5.1" /><path d="M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z" />',
476
+ ScrollMilestone: '<path d="m7 6 5 5 5-5" /><path d="m7 13 5 5 5-5" />',
477
+ TimelineEvent: '<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" /><path d="M3 3v5h5" /><path d="M12 7v5l4 2" />',
478
+ AnonVisitor: '<path d="M9 10h.01" /><path d="M15 10h.01" /><path d="M12 2a8 8 0 0 0-8 8v12l3-3 2.5 2.5L12 19l2.5 2.5L17 19l3 3V10a8 8 0 0 0-8-8z" />',
479
+ AEOAudit: '<path d="m8 11 2 2 4-4" /><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" />',
480
+ WhatsAppConversation: '<path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719" />',
481
+ WhatsAppMessage: '<path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z" /><path d="M7 11h10" /><path d="M7 15h6" /><path d="M7 7h8" />',
482
+ CloudflareHostname: '<circle cx="12" cy="12" r="10" /><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" /><path d="M2 12h20" />',
483
+ CloudflareTunnel: '<rect x="16" y="16" width="6" height="6" rx="1" /><rect x="2" y="16" width="6" height="6" rx="1" /><rect x="9" y="2" width="6" height="6" rx="1" /><path d="M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3" /><path d="M12 12V8" />',
484
+ Chain: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />',
485
+ ChainLink: '<path d="M9 17H7A5 5 0 0 1 7 7h2" /><path d="M15 7h2a5 5 0 1 1 0 10h-2" /><line x1="8" x2="16" y1="12" y2="12" />',
486
+ FileArtifact: '<path d="M13.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v11.5" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /><path d="M8 12v-1" /><path d="M8 18v-2" /><path d="M8 7V6" /><circle cx="8" cy="20" r="2" />',
487
+ VoiceProfile: '<path d="M12 19v3" /><path d="M19 10v2a7 7 0 0 1-14 0v-2" /><rect x="9" y="2" width="6" height="13" rx="3" />',
488
+ VoiceEdit: '<path d="M2 10v3" /><path d="M6 6v11" /><path d="M10 3v18" /><path d="M14 8v7" /><path d="M18 5v13" /><path d="M22 10v3" />',
328
489
  }
329
490
 
330
491
  /**
@@ -184,6 +184,7 @@ Tools are available via the `admin` MCP server.
184
184
  | Investigate a bug or unexpected behaviour | User asks to debug, fix a bug, find the root cause, or investigate an error | `skills/investigate/SKILL.md` |
185
185
  | Capture work as a task before implementing | User asks to "task it", create a task, or describes work that should be a task file before a sprint | `skills/task/SKILL.md` |
186
186
  | Run an insight pass on this session | Operator asks to run an insight pass, review this session, capture what we learned, or do a retrospective on the conversation | `skills/insight/SKILL.md` |
187
+ | Summarise the last turn | User asks to summarise, recap, or give the "in short" version of what the agent just said or asked | `skills/summarise/SKILL.md` |
187
188
 
188
189
  ## Hooks
189
190
 
@@ -28,6 +28,21 @@ Load `references/agent-pattern.md`. It is the canonical shape: the two kinds of
28
28
  deny-by-default tool rule, model-choice guidance, and the three-part contract body. Use it
29
29
  throughout. Do not paraphrase the pattern from memory; the reference is the source.
30
30
 
31
+ ## Step 0: Check for a deliverable catalogue
32
+
33
+ When the operator asks what can be built here, rather than naming a specific agent, first look for
34
+ a shipped suggestion catalogue. A premium bundle can ship a `DELIVERABLES.md` at its root listing
35
+ deliverables it supports beyond the agents it already ships. Get the enabled bundles from
36
+ `capabilities-here`, then for each one read `$PLATFORM_ROOT/../premium-plugins/<bundle>/DELIVERABLES.md`
37
+ if it exists, the bundle root where its `BUNDLE.md` and `agents/` live. Offer the operator the entries you find, grouped by bundle. If
38
+ no enabled bundle ships one, skip this step and go to Step 1.
39
+
40
+ The catalogue is a suggestion list, not a shipped roster. Each entry is authored bespoke for this
41
+ account when the operator picks it, because a generic version carries none of the customer's own
42
+ determinism. On a pick, run Steps 1 through 7 to author the skill and agent into the account's own
43
+ plugins tree, and name the agent for the customer's actual deliverable rather than the catalogue's
44
+ ontology label, exactly as the catalogue instructs. The ontology name is grounding, not a file name.
45
+
31
46
  ## Step 1: Name the deliverable
32
47
 
33
48
  Ask: **"What is the one thing this agent hands back?"**
@@ -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:8be6993be76afa65716e56cd2dcb17c87f6588109d64ca81e38db74af1d4d48c
4
+ content-hash: sha256:70e0125f4ead8ba83c01b87f6a9ccc15f51d9492c5dd55bd4f83106769280a7e
5
5
  brand: maxy-code
6
6
  product-name: Maxy
7
7
  ---
@@ -3045,6 +3045,37 @@ routines exist for the account is the account-scope-miss signature; the logged
3045
3045
  `cron=` on `outcome=ok` confirms the persisted expression without opening the
3046
3046
  graph.
3047
3047
 
3048
+ ### Skills
3049
+
3050
+ The Skills surface (`/skills`) is the admin view of the account's operator-
3051
+ authored skills — the `SKILL.md` files the store-skill tool writes under
3052
+ `<accountDir>/plugins/<plugin>/skills/<skill>/` (the durable per-account plugin
3053
+ root, never the immutable `PLATFORM_ROOT/plugins` tree). It renders one card per
3054
+ skill (name, owning plugin, frontmatter description, reference count); a card
3055
+ opens a **read-only** detail modal showing the frontmatter, the SKILL.md body,
3056
+ and the reference filenames. The page is read-only by design — skills are
3057
+ authored and edited through the chat interface — with one exception: a **delete**
3058
+ behind an explicit confirm step in the modal. A confirmed delete removes the
3059
+ skill directory, and the whole plugin directory when that was its last skill.
3060
+
3061
+ The route (`server/routes/admin/skills.ts`) walks only the account tree, so
3062
+ shipped platform skills never appear. Plugin and skill path params are kebab-case
3063
+ validated (`/^[a-z0-9][a-z0-9-]*$/`) — the traversal guard; anything else 400s.
3064
+
3065
+ | Route | Behaviour |
3066
+ |-------|-----------|
3067
+ | `GET /api/admin/skills` | Enumerates `plugins/*/skills/*/SKILL.md`, returns `{plugin, skill, description, refCount}` per skill, sorted plugin then skill. A per-entry read error logs `op=list-skip` rather than silently shortening the list. |
3068
+ | `GET /api/admin/skills/:plugin/:skill` | One skill's parsed frontmatter (`name`, `description`, `publicEmbed`), the SKILL.md body, and the reference filenames. Unknown skill 404s. |
3069
+ | `DELETE /api/admin/skills/:plugin/:skill` | Removes the skill dir; removes the plugin dir when its `skills/` empties. Returns `{ok, skillDirGone, pluginDirRemoved}` measured after the rm. |
3070
+
3071
+ Observability rides `[admin:skills]`: `op=list accountId=<8> plugins=<n>
3072
+ skills=<n>` (the surface's own heartbeat — `skills=0` against a populated
3073
+ `plugins/*/skills/` is the broken-walk signature), `op=get plugin=<…> skill=<…>
3074
+ found=<bool>`, and `op=delete plugin=<…> skill=<…> outcome=ok
3075
+ skillDirGone=<bool> pluginDirRemoved=<bool>` (`skillDirGone=false` with
3076
+ `outcome=ok` is a named failure) / `outcome=rejected reason=<invalid-name|
3077
+ not-found|rm-error>`.
3078
+
3048
3079
  ### Graph
3049
3080
 
3050
3081
  | Mount | Purpose |
@@ -3056,6 +3087,25 @@ graph.
3056
3087
  | `/graph-labels-in-graph` | Distinct label list for the filter dropdown. |
3057
3088
  | `/graph-default-view` | Account-scoped saved view (zoom, focal id, filters). |
3058
3089
 
3090
+ **Node visual encoding.** Every node type that can appear on the canvas renders
3091
+ a coloured disc with a white Lucide glyph, sized by incident-edge count, with a
3092
+ matching legend swatch. Colour, shape, and glyph are three per-label registries
3093
+ in `platform/lib/graph-style/src/index.ts`. Coverage is reconciled against the
3094
+ true node-label universe, not the colour registry itself: the standing test
3095
+ `__tests__/icons.test.ts` assembles the universe from the `Neo4j Label` column
3096
+ of every `schema-*.md` node-type table, the `sourceLabel`/`targetLabel` set in
3097
+ `typed-edge-schema.ts`, and the `CODE_EMITTED_NODE_LABELS` list, then fails the
3098
+ build if any member lacks a colour or glyph. Labels that only ever stack on a
3099
+ base node (`ADDITIONAL_BASE_LABELS`) inherit the base node's colour and are
3100
+ excluded. A node type carries a coloured glyph even when its text caption is its
3101
+ bare type: the glyph and the caption are independent channels (caption coverage
3102
+ is the separate `caption-coverage.test.ts` gate). At runtime, a per-session
3103
+ `op=icon-fallback` census (emitted from `app/graph/page.tsx` across the initial
3104
+ render and every live-update poll) names any label that reached the canvas with
3105
+ no glyph; an empty census is the healthy signal, a non-empty one means a
3106
+ code-emitted label that no static source declares and should be added to
3107
+ `CODE_EMITTED_NODE_LABELS`.
3108
+
3059
3109
  ### Artefacts and files
3060
3110
 
3061
3111
  | Mount | Purpose |
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: summarise
3
+ description: Use when the user types /summarise, or asks for a summary, recap, or "in short" version of what the agent just said or asked. Restates the agent's own immediately preceding turn under the plainly rules: precise, concise, plain English, every claim already evidenced in that turn. When that turn concluded an investigation, the summary carries exactly two things: the diagnosis and the proposed resolution.
4
+ ---
5
+
6
+ # summarise
7
+
8
+ Invoked by the admin agent directly.
9
+
10
+ The subject is the agent's own last turn, not the codebase, not the conversation as a whole, and not the user's message. Whatever was just said or asked gets restated so a reader who skipped the turn ends up with the same working understanding, in a fraction of the words.
11
+
12
+ ## What success looks like
13
+
14
+ The summary is precise, concise, plain English, with every claim already evidenced in the turn being summarised. It also satisfies the shipped `plainly` skill (`skills/plainly/SKILL.md`): spoken register, no acronyms, no formulas, no AI tells, no em-dashes. Read `plainly` before writing; do not summarise from memory of what it says.
15
+
16
+ Three sentences is the ceiling for an ordinary turn. If the turn ended in a question, the summary states the question and what turns on the answer, and nothing else.
17
+
18
+ ## Investigations
19
+
20
+ When the preceding turn concluded an investigation, the summary has exactly two parts, in this order:
21
+
22
+ 1. **Diagnosis.** What is actually wrong, and the evidence that establishes it. Name the cause, not the layer where the symptom surfaced.
23
+ 2. **Proposed resolution.** What would fix that cause, and its scope boundary.
24
+
25
+ Both parts are mandatory. A diagnosis with no proposed resolution is an unfinished summary; a proposed resolution with no diagnosis is speculation wearing a summary's clothes. Neither part introduces a finding, a fix, or a caveat that was absent from the turn being summarised. If the investigation reached a diagnosis but no resolution, say that in the resolution slot rather than inventing one.
26
+
27
+ ## Constraints
28
+
29
+ - **No new content.** The summary is a compression, never an extension. Anything not in the turn being summarised does not belong here.
30
+ - **Drop the scaffolding, keep the load.** File paths, tool names, and step-by-step narration go unless the reader needs them to act. The outcome stays.
31
+ - **No hedging.** The original turn either established a claim or it did not. Do not soften a confirmed finding into "appears to", and do not harden a speculation into a fact.
32
+ - **No follow-up offers.** End on the summary.
33
+
34
+ ## When NOT to use
35
+
36
+ - The user wants the summary of a document, a file, or someone else's text: that is ordinary reading work, and the `plainly` skill fits better.
37
+ - The last turn was already one or two plain sentences: there is nothing to compress, so say so and stop.
@@ -118,6 +118,37 @@ routines exist for the account is the account-scope-miss signature; the logged
118
118
  `cron=` on `outcome=ok` confirms the persisted expression without opening the
119
119
  graph.
120
120
 
121
+ ### Skills
122
+
123
+ The Skills surface (`/skills`) is the admin view of the account's operator-
124
+ authored skills — the `SKILL.md` files the store-skill tool writes under
125
+ `<accountDir>/plugins/<plugin>/skills/<skill>/` (the durable per-account plugin
126
+ root, never the immutable `PLATFORM_ROOT/plugins` tree). It renders one card per
127
+ skill (name, owning plugin, frontmatter description, reference count); a card
128
+ opens a **read-only** detail modal showing the frontmatter, the SKILL.md body,
129
+ and the reference filenames. The page is read-only by design — skills are
130
+ authored and edited through the chat interface — with one exception: a **delete**
131
+ behind an explicit confirm step in the modal. A confirmed delete removes the
132
+ skill directory, and the whole plugin directory when that was its last skill.
133
+
134
+ The route (`server/routes/admin/skills.ts`) walks only the account tree, so
135
+ shipped platform skills never appear. Plugin and skill path params are kebab-case
136
+ validated (`/^[a-z0-9][a-z0-9-]*$/`) — the traversal guard; anything else 400s.
137
+
138
+ | Route | Behaviour |
139
+ |-------|-----------|
140
+ | `GET /api/admin/skills` | Enumerates `plugins/*/skills/*/SKILL.md`, returns `{plugin, skill, description, refCount}` per skill, sorted plugin then skill. A per-entry read error logs `op=list-skip` rather than silently shortening the list. |
141
+ | `GET /api/admin/skills/:plugin/:skill` | One skill's parsed frontmatter (`name`, `description`, `publicEmbed`), the SKILL.md body, and the reference filenames. Unknown skill 404s. |
142
+ | `DELETE /api/admin/skills/:plugin/:skill` | Removes the skill dir; removes the plugin dir when its `skills/` empties. Returns `{ok, skillDirGone, pluginDirRemoved}` measured after the rm. |
143
+
144
+ Observability rides `[admin:skills]`: `op=list accountId=<8> plugins=<n>
145
+ skills=<n>` (the surface's own heartbeat — `skills=0` against a populated
146
+ `plugins/*/skills/` is the broken-walk signature), `op=get plugin=<…> skill=<…>
147
+ found=<bool>`, and `op=delete plugin=<…> skill=<…> outcome=ok
148
+ skillDirGone=<bool> pluginDirRemoved=<bool>` (`skillDirGone=false` with
149
+ `outcome=ok` is a named failure) / `outcome=rejected reason=<invalid-name|
150
+ not-found|rm-error>`.
151
+
121
152
  ### Graph
122
153
 
123
154
  | Mount | Purpose |
@@ -129,6 +160,25 @@ graph.
129
160
  | `/graph-labels-in-graph` | Distinct label list for the filter dropdown. |
130
161
  | `/graph-default-view` | Account-scoped saved view (zoom, focal id, filters). |
131
162
 
163
+ **Node visual encoding.** Every node type that can appear on the canvas renders
164
+ a coloured disc with a white Lucide glyph, sized by incident-edge count, with a
165
+ matching legend swatch. Colour, shape, and glyph are three per-label registries
166
+ in `platform/lib/graph-style/src/index.ts`. Coverage is reconciled against the
167
+ true node-label universe, not the colour registry itself: the standing test
168
+ `__tests__/icons.test.ts` assembles the universe from the `Neo4j Label` column
169
+ of every `schema-*.md` node-type table, the `sourceLabel`/`targetLabel` set in
170
+ `typed-edge-schema.ts`, and the `CODE_EMITTED_NODE_LABELS` list, then fails the
171
+ build if any member lacks a colour or glyph. Labels that only ever stack on a
172
+ base node (`ADDITIONAL_BASE_LABELS`) inherit the base node's colour and are
173
+ excluded. A node type carries a coloured glyph even when its text caption is its
174
+ bare type: the glyph and the caption are independent channels (caption coverage
175
+ is the separate `caption-coverage.test.ts` gate). At runtime, a per-session
176
+ `op=icon-fallback` census (emitted from `app/graph/page.tsx` across the initial
177
+ render and every live-update poll) names any label that reached the canvas with
178
+ no glyph; an empty census is the healthy signal, a non-empty one means a
179
+ code-emitted label that no static source declares and should be added to
180
+ `CODE_EMITTED_NODE_LABELS`.
181
+
132
182
  ### Artefacts and files
133
183
 
134
184
  | Mount | Purpose |
@@ -0,0 +1,7 @@
1
+ # summarise skill
2
+
3
+ A workflow skill bundled in the admin plugin on every {{productName}} install.
4
+
5
+ **`summarise`** restates the agent's own immediately-preceding turn under the plainly rules: precise, concise, plain English, every claim already evidenced in the turn. Three-sentence ceiling for an ordinary turn. When that turn concluded an investigation, the summary carries exactly two parts in order: the diagnosis (the cause, with its evidence) and the proposed resolution (or an explicit note that none was reached). Triggers: "/summarise", "summarise", "recap", "in short".
6
+
7
+ It never adds content, never hedges, and never ends on a follow-up offer. For summarising someone else's document or text, `plainly` fits better; a turn already one or two plain sentences needs no compression.
@@ -5,12 +5,12 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Activity — Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/activity-CKUYOLd2.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/chunk-fQC6bEKA.js">
10
- <link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-Bc7XHr0L.js">
11
- <link rel="modulepreload" crossorigin href="/assets/AdminShell-BFVVkWFC.js">
12
- <link rel="modulepreload" crossorigin href="/assets/triangle-alert-DumCWC08.js">
13
- <link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-DPHQJO7E.css">
8
+ <script type="module" crossorigin src="/assets/activity-CVdVSw5V.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/chunk-CCr-iYLO.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-CHwQhkaI.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/AdminShell-BlbUNAPc.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/triangle-alert-CngnWtjD.js">
13
+ <link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-yFQqnpQ8.css">
14
14
  <link rel="stylesheet" href="/brand-defaults.css">
15
15
  </head>
16
16
  <body>
@@ -1 +1 @@
1
- import{o as e}from"./chunk-fQC6bEKA.js";import{B as t,G as n,I as r,R as i,V as a}from"./useSubAccountSwitcher-Bc7XHr0L.js";import{D as o}from"./useVoiceRecorder-Cj-VZDKO.js";import{S as s,b as c,x as l}from"./AdminShell-BFVVkWFC.js";var u=`admin-landing-redirected`,d=`/graph`;function f(e){return e.variant===`operator`?!1:e.appState===`chat`&&!e.alreadyRedirected}var p=e(n(),1);function m(e=`admin`){let[t,n]=(0,p.useState)(`loading`),[r,i]=(0,p.useState)(``),[c,l]=(0,p.useState)(``),[m,h]=(0,p.useState)(``),[g,_]=(0,p.useState)(!1),[v,y]=(0,p.useState)(!1),[b,x]=(0,p.useState)(!1),[S,C]=(0,p.useState)(!1),[w,T]=(0,p.useState)(!1),[E,D]=(0,p.useState)(null),[O,k]=(0,p.useState)(null),[A,j]=(0,p.useState)(void 0),[M,N]=(0,p.useState)(null),[P,F]=(0,p.useState)(void 0),[I,L]=(0,p.useState)(null),[R,z]=(0,p.useState)(null),[B,V]=(0,p.useState)(void 0),H=(0,p.useRef)(void 0),[U,W]=(0,p.useState)(!1),[G,K]=(0,p.useState)(!0);(0,p.useEffect)(()=>{typeof window>`u`||fetch(`/api/remote-auth/status`).then(e=>e.ok?e.json():null).then(e=>{e?.configured&&W(!0)}).catch(()=>{})},[]);let q=(0,p.useRef)(null),J=(0,p.useRef)(null);(0,p.useEffect)(()=>{async function e(){let e=null;try{e=sessionStorage.getItem(`maxy-admin-session-key`)}catch{}if(!e)return!1;try{let t=await fetch(`/api/admin/session?session_key=${encodeURIComponent(e)}`);if(t.status===401){try{sessionStorage.removeItem(`maxy-admin-session-key`)}catch{}return!1}if(!t.ok)return!1;let r=await t.json();D(r.session_key),z(r.sessionId??null),j(r.businessName),N(r.role??null),F(r.userName===void 0?null:r.userName),L(r.avatar??null);let i=o(r.thinkingView);return H.current=i,V(i),n(`chat`),!0}catch(e){return console.error(`[admin] session restore failed:`,e),!1}}async function t(r=2){try{let i=await fetch(`/api/health`);if(!i.ok){if(r>0)return await new Promise(e=>setTimeout(e,1500)),t(r-1);console.error(`[admin] health check returned ${i.status} after retries`),n(`set-pin`);return}let a=await i.json();if(!a.pin_configured){n(`set-pin`);return}K(!!a.claude_authenticated);let o=!1;try{o=new URLSearchParams(window.location.search).get(`connect`)===`claude`}catch{}if(o){try{let e=new URL(window.location.href);e.searchParams.delete(`connect`),window.history.replaceState(null,``,e.pathname+e.search+e.hash)}catch{}if(!a.claude_authenticated){n(`connect-claude`);return}}if(await e())return;n(`enter-pin`)}catch(e){if(r>0)return await new Promise(e=>setTimeout(e,1500)),t(r-1);console.error(`[admin] health check failed:`,e),n(`set-pin`)}}t()},[]),(0,p.useEffect)(()=>{t===`chat`&&fetch(`/api/admin/claude-info`).then(e=>{if(e.ok)return e.json()}).then(e=>{e&&k(e)}).catch(()=>{})},[t]),(0,p.useEffect)(()=>{if(typeof window>`u`)return;let n=!1;try{n=sessionStorage.getItem(u)===`1`}catch{}if(f({appState:t,alreadyRedirected:n,variant:e})){try{sessionStorage.setItem(u,`1`)}catch{}console.info(`[admin-ui] landing-redirect target=${d}`),window.location.replace(d)}},[t,e]);let Y=(0,p.useRef)(null);(0,p.useEffect)(()=>{if(t!==`chat`)return;let e=setInterval(async()=>{try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();K(t.auth_status!==`dead`&&t.auth_status!==`missing`)}}catch{}if(E)try{let e=await fetch(`/api/admin/session?session_key=${encodeURIComponent(E)}`);if(e.status!==401)return;let t=(await e.clone().json().catch(()=>null))?.code??`unknown-401`;if(t===`remote-auth-required`){a(`heartbeat`,`/api/admin/session`);return}console.warn(`[admin-auth] outcome=heartbeat-detected-expiry code=${t}`),Y.current?.()}catch{}},300*1e3);return()=>clearInterval(e)},[t,E]),(0,p.useEffect)(()=>{t===`connect-claude`&&fetch(`/api/health`).then(e=>e.ok?e.json():null).then(e=>{e?.claude_authenticated&&(K(!0),n(`enter-pin`))}).catch(()=>{})},[t]);async function X(e,t){y(!0);try{let r=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...t?{accountId:t}:{}})});if(!r.ok){let e=await r.json().catch(()=>({}));if(r.status===401&&e?.code===`remote-auth-required`){a(`enter-pin`,`/api/admin/session`);return}h(e.error||`Invalid PIN`);return}let s=await r.json();D(s.session_key),z(s.sessionId??null),j(s.businessName),N(s.role??null),F(s.userName===void 0?null:s.userName),L(s.avatar??null);let c=o(s.thinkingView);H.current=c,V(c);try{sessionStorage.setItem(`maxy-admin-session-key`,s.session_key)}catch{}i(``),n(`chat`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}}let Z=(0,p.useCallback)(async e=>{if(e.preventDefault(),v)return;h(``);let t=c.trim();if(!t){h(`Please enter your name.`);return}if(r.length<4){h(`PIN must be at least 4 characters.`);return}let o=r;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:o,name:t})});if(!e.ok){let t=await e.json().catch(()=>({}));if(e.status===401&&t?.code===`remote-auth-required`){a(`set-pin`,`/api/onboarding/set-pin`);return}if(e.status===409){console.log(`[admin] PIN already configured — re-checking health`);try{let e=await fetch(`/api/health`);if(e.ok){let r=await e.json();r.pin_configured&&r.claude_authenticated?n(`enter-pin`):r.pin_configured?n(`connect-claude`):h(t.error||`Failed to set PIN.`)}else n(`enter-pin`)}catch{n(`enter-pin`)}return}h(t.error||`Failed to set PIN.`);return}let r=await fetch(`/api/health`);if((r.ok?await r.json():null)?.claude_authenticated){await X(o);return}i(``),n(`connect-claude`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}},[r,v,c]),Q=(0,p.useCallback)(async e=>{e.preventDefault(),h(``),await X(r)},[r]),ee=(0,p.useCallback)(async()=>{T(!0);try{if(!await s())return console.warn(`[admin-ui] claude-disconnect not verified — credentials may persist; staying put`),!1;K(!1),D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}return n(`connect-claude`),!0}finally{T(!1)}},[]),$=(0,p.useCallback)(()=>{D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}i(``),h(``),n(`enter-pin`)},[]);return(0,p.useEffect)(()=>{Y.current=$},[$]),{appState:t,setAppState:n,pin:r,setPin:i,operatorName:c,setOperatorName:l,pinError:m,setPinError:h,showPin:g,setShowPin:_,pinLoading:v,authPolling:b,setAuthPolling:x,authLoading:S,setAuthLoading:C,disconnecting:w,cacheKey:E,setCacheKey:D,claudeInfo:O,setClaudeInfo:k,businessName:A,role:M,userName:P,userAvatar:I,sessionId:R,setSessionId:z,expandAll:B,setExpandAll:V,expandAllDefaultRef:H,remoteAuthEnabled:U,claudeConnected:G,pinInputRef:q,setPinFormRef:J,handleSetPin:Z,handleLogin:Q,handleDisconnect:ee,handleLogout:$}}var h=t();function g({inputRef:e,value:t,onChange:n,onComplete:r,showPin:i,autoFocus:a}){let o=(0,p.useRef)([]);function s(e,r){r.key===`Backspace`?(r.preventDefault(),t[e]?n(t.slice(0,e)+t.slice(e+1)):e>0&&(n(t.slice(0,e-1)+t.slice(e)),o.current[e-1]?.focus())):r.key===`ArrowLeft`&&e>0?o.current[e-1]?.focus():r.key===`ArrowRight`&&e<5?o.current[e+1]?.focus():r.key===`Enter`&&(r.preventDefault(),r.currentTarget.form?.requestSubmit())}function c(e,i){let a=i.nativeEvent.data;if(!a||!/^\d$/.test(a))return;let s=t.split(``);for(s[e]=a;s.length<e;)s.push(``);let c=s.join(``).replace(/\D/g,``).slice(0,6);n(c),c.length===6?r?.(c):e<5&&o.current[e+1]?.focus()}function l(e){e.preventDefault();let t=e.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,6);t&&(n(t),t.length===6?r?.(t):o.current[t.length]?.focus())}return(0,h.jsx)(`div`,{className:`pin-field`,children:Array.from({length:6}).map((n,r)=>(0,h.jsx)(`input`,{ref:t=>{o.current[r]=t,r===0&&e&&(e.current=t)},type:`text`,inputMode:`numeric`,className:`pin-box${t[r]?` pin-box-filled`:``}`,value:t[r]?i?t[r]:`•`:``,onKeyDown:e=>s(r,e),onInput:e=>c(r,e),onPaste:l,onFocus:e=>e.target.select(),autoFocus:a&&r===0,autoComplete:`off`,maxLength:1,"aria-label":`PIN digit ${r+1}`},r))})}function _(e){let{pin:t,setPin:n,showPin:a,setShowPin:o,pinLoading:s,pinError:u,pinInputRef:d,setPinFormRef:f,onSubmit:p,operatorName:m,setOperatorName:_}=e;return(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:i,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,r.productName]}),(0,h.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,r.productName,` who you are, then choose a PIN.`]}),(0,h.jsxs)(`form`,{ref:f,onSubmit:p,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(`input`,{type:`text`,className:`connect-name-input`,placeholder:`Your full name`,value:m,onChange:e=>_(e.target.value),autoComplete:`name`,autoFocus:!0,required:!0,"aria-label":`Your full name`}),(0,h.jsx)(`div`,{style:{width:38,flexShrink:0},"aria-hidden":`true`})]}),(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:t,onChange:n,onComplete:()=>{},showPin:a}),(0,h.jsx)(c,{variant:`send`,type:`submit`,disabled:!t||!m.trim(),loading:s,"aria-label":`Set PIN`,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(l,{checked:a,onChange:()=>o(e=>!e),label:`Show PIN`})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})})}function v(e){let{pin:t,setPin:n,showPin:a,setShowPin:o,pinLoading:s,pinError:u,pinInputRef:d,onSubmit:f,remoteAuthEnabled:p,onSignOutRemote:m}=e;return(0,h.jsxs)(`div`,{className:`connect-page`,children:[p&&m&&(0,h.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:m,children:`Sign out`}),(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:i,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsx)(`h1`,{className:`connect-title`,children:r.productName}),(0,h.jsxs)(`form`,{onSubmit:f,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:t,onChange:n,onComplete:()=>{},showPin:a,autoFocus:!0}),(0,h.jsx)(c,{variant:`send`,type:`submit`,disabled:!t,loading:s,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(`div`,{className:`pin-options`,children:(0,h.jsx)(l,{checked:a,onChange:()=>o(e=>!e),label:`Show PIN`})})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})]})}function y(e){let{authPolling:t,setAuthPolling:n,authLoading:a,setAuthLoading:o,pinError:s,setPinError:l,setAppState:u}=e,[d,f]=(0,p.useState)(!1),[m,g]=(0,p.useState)(!1),[_,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(``),[x,S]=(0,p.useState)(``);async function C(){S(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`paste-code`,code:y})})).json();e.accepted?(S(`Code submitted. Finishing sign-in…`),b(``)):S(e.error||`Could not submit code.`)}catch(e){console.error(`[admin] paste-code error:`,e),S(`Could not submit code.`)}}async function w(){g(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`launch-browser`})})).json();e.launched?f(!0):e.error&&l(e.error)}catch(e){console.error(`[admin] browser launch error:`,e),l(`Could not launch browser.`)}g(!1)}async function T(){o(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){n(!0),f(!0),o(!1),v(e.transport||``);for(let e=0;e<120;e++)if(await new Promise(e=>setTimeout(e,2e3)),(await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`wait`})})).json()).authenticated){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),u(`enter-pin`);return}l(`Timed out waiting for sign-in. Try again.`),n(!1)}else e.error&&l(e.error)}catch(e){console.error(`[admin] auth flow error:`,e),l(`Could not start auth flow.`)}o(!1)}async function E(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),n(!1),l(``)}return t||d?(0,h.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100dvh`,overflow:`auto`},children:[(0,h.jsxs)(`header`,{className:`chat-header`,style:{paddingBottom:`12px`,flexShrink:0,position:`relative`,maxWidth:`680px`,width:`100%`,margin:`0 auto`,padding:`24px 20px 12px`},children:[t?(0,h.jsx)(`button`,{onClick:E,style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Cancel`,children:`✕`}):(0,h.jsx)(`button`,{onClick:()=>f(!1),style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Close browser`,children:`✕`}),(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`chat-logo`}),(0,h.jsx)(`h1`,{className:`chat-tagline`,children:`Connect Claude`}),(0,h.jsx)(`p`,{className:`chat-intro`,children:t?`Sign in and authorize in the browser below.`:`Open your email or prepare your accounts, then sign in.`}),!t&&(0,h.jsx)(`div`,{style:{marginTop:`12px`},children:(0,h.jsx)(c,{variant:`primary`,onClick:T,disabled:a,children:a?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`})}),t&&_===`native`&&(0,h.jsxs)(`div`,{style:{marginTop:`14px`,maxWidth:`420px`,margin:`14px auto 0`,display:`flex`,flexDirection:`column`,gap:`8px`},children:[(0,h.jsx)(`label`,{style:{fontSize:`12px`,color:`#999`,lineHeight:1.4},children:`If the browser didn't finish sign-in, paste the code or full redirect URL from Claude's page:`}),(0,h.jsxs)(`div`,{style:{display:`flex`,gap:`8px`},children:[(0,h.jsx)(`input`,{type:`text`,value:y,onChange:e=>b(e.target.value),placeholder:`Paste code or redirect URL`,style:{flex:1,padding:`8px 10px`,fontSize:`13px`,border:`1px solid #ccc`,borderRadius:`6px`}}),(0,h.jsx)(c,{variant:`primary`,onClick:C,disabled:!y.trim(),children:`Submit`})]}),x&&(0,h.jsx)(`p`,{style:{fontSize:`12px`,color:`#999`,margin:0},children:x})]})]}),(0,h.jsx)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,minHeight:0,gap:`10px`,padding:`0 0 16px`},children:(0,h.jsx)(`iframe`,{src:`/vnc-viewer.html`,style:{flex:1,width:`100%`,minHeight:0,border:`none`,background:`#111`,display:`block`},title:`Claude Sign-in`})}),s&&(0,h.jsx)(`p`,{className:`admin-pin-error`,style:{textAlign:`center`,padding:`0 20px 16px`},children:s})]}):(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsxs)(`div`,{className:`connect-logos`,children:[(0,h.jsx)(`div`,{className:`connect-logo-wrap`,children:(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`connect-logo`})}),(0,h.jsx)(`svg`,{className:`connect-arrow`,viewBox:`0 0 48 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,h.jsx)(`path`,{d:`M0 12h44m0 0l-8-8m8 8l-8 8`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,h.jsxs)(`div`,{className:`connect-logo-wrap`,children:[(0,h.jsx)(`img`,{src:i,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsx)(`span`,{className:`connect-logo-label`,children:r.productName})]})]}),(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,r.productName]}),(0,h.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,h.jsx)(c,{variant:`primary`,onClick:T,disabled:a,children:a?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`}),(0,h.jsx)(`p`,{style:{marginTop:`6px`,fontSize:`11px`,color:`#999`,maxWidth:`300px`,textAlign:`center`,lineHeight:`1.4`},children:`First time? You may need to sign into your email and Anthropic account in the browser before connecting.`}),(0,h.jsx)(`button`,{onClick:w,disabled:m,style:{marginTop:`12px`,background:`none`,border:`none`,color:`var(--color-primary, #666)`,fontSize:`13px`,cursor:`pointer`,textDecoration:`underline`,textUnderlineOffset:`3px`},children:m?`Launching…`:`Open browser first`}),s&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:s})]})})}function b({auth:e}){return e.appState===`loading`?(0,h.jsx)(`div`,{className:`connect-page`}):e.appState===`set-pin`?(0,h.jsx)(_,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,setPinFormRef:e.setPinFormRef,onSubmit:e.handleSetPin,operatorName:e.operatorName,setOperatorName:e.setOperatorName}):e.appState===`connect-claude`?(0,h.jsx)(y,{authPolling:e.authPolling,setAuthPolling:e.setAuthPolling,authLoading:e.authLoading,setAuthLoading:e.setAuthLoading,pinError:e.pinError,setPinError:e.setPinError,setAppState:e.setAppState}):e.appState===`enter-pin`?(0,h.jsx)(v,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,onSubmit:e.handleLogin,remoteAuthEnabled:e.remoteAuthEnabled,onSignOutRemote:()=>{console.info(`[admin-ui] remote-auth sign-out → /__remote-auth/logout`),window.location.href=`/__remote-auth/logout`}}):null}export{m as n,b as t};
1
+ import{o as e}from"./chunk-CCr-iYLO.js";import{B as t,G as n,I as r,R as i,V as a}from"./useSubAccountSwitcher-CHwQhkaI.js";import{E as o}from"./useVoiceRecorder-C3G6aqZr.js";import{C as s,S as c,x as l}from"./AdminShell-BlbUNAPc.js";var u=`admin-landing-redirected`,d=`/graph`;function f(e){return e.variant===`operator`?!1:e.appState===`chat`&&!e.alreadyRedirected}var p=e(n(),1);function m(e=`admin`){let[t,n]=(0,p.useState)(`loading`),[r,i]=(0,p.useState)(``),[c,l]=(0,p.useState)(``),[m,h]=(0,p.useState)(``),[g,_]=(0,p.useState)(!1),[v,y]=(0,p.useState)(!1),[b,x]=(0,p.useState)(!1),[S,C]=(0,p.useState)(!1),[w,T]=(0,p.useState)(!1),[E,D]=(0,p.useState)(null),[O,k]=(0,p.useState)(null),[A,j]=(0,p.useState)(void 0),[M,N]=(0,p.useState)(null),[P,F]=(0,p.useState)(void 0),[I,L]=(0,p.useState)(null),[R,z]=(0,p.useState)(null),[B,V]=(0,p.useState)(void 0),H=(0,p.useRef)(void 0),[U,W]=(0,p.useState)(!1),[G,K]=(0,p.useState)(!0);(0,p.useEffect)(()=>{typeof window>`u`||fetch(`/api/remote-auth/status`).then(e=>e.ok?e.json():null).then(e=>{e?.configured&&W(!0)}).catch(()=>{})},[]);let q=(0,p.useRef)(null),J=(0,p.useRef)(null);(0,p.useEffect)(()=>{async function e(){let e=null;try{e=sessionStorage.getItem(`maxy-admin-session-key`)}catch{}if(!e)return!1;try{let t=await fetch(`/api/admin/session?session_key=${encodeURIComponent(e)}`);if(t.status===401){try{sessionStorage.removeItem(`maxy-admin-session-key`)}catch{}return!1}if(!t.ok)return!1;let r=await t.json();D(r.session_key),z(r.sessionId??null),j(r.businessName),N(r.role??null),F(r.userName===void 0?null:r.userName),L(r.avatar??null);let i=o(r.thinkingView);return H.current=i,V(i),n(`chat`),!0}catch(e){return console.error(`[admin] session restore failed:`,e),!1}}async function t(r=2){try{let i=await fetch(`/api/health`);if(!i.ok){if(r>0)return await new Promise(e=>setTimeout(e,1500)),t(r-1);console.error(`[admin] health check returned ${i.status} after retries`),n(`set-pin`);return}let a=await i.json();if(!a.pin_configured){n(`set-pin`);return}K(!!a.claude_authenticated);let o=!1;try{o=new URLSearchParams(window.location.search).get(`connect`)===`claude`}catch{}if(o){try{let e=new URL(window.location.href);e.searchParams.delete(`connect`),window.history.replaceState(null,``,e.pathname+e.search+e.hash)}catch{}if(!a.claude_authenticated){n(`connect-claude`);return}}if(await e())return;n(`enter-pin`)}catch(e){if(r>0)return await new Promise(e=>setTimeout(e,1500)),t(r-1);console.error(`[admin] health check failed:`,e),n(`set-pin`)}}t()},[]),(0,p.useEffect)(()=>{t===`chat`&&fetch(`/api/admin/claude-info`).then(e=>{if(e.ok)return e.json()}).then(e=>{e&&k(e)}).catch(()=>{})},[t]),(0,p.useEffect)(()=>{if(typeof window>`u`)return;let n=!1;try{n=sessionStorage.getItem(u)===`1`}catch{}if(f({appState:t,alreadyRedirected:n,variant:e})){try{sessionStorage.setItem(u,`1`)}catch{}console.info(`[admin-ui] landing-redirect target=${d}`),window.location.replace(d)}},[t,e]);let Y=(0,p.useRef)(null);(0,p.useEffect)(()=>{if(t!==`chat`)return;let e=setInterval(async()=>{try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();K(t.auth_status!==`dead`&&t.auth_status!==`missing`)}}catch{}if(E)try{let e=await fetch(`/api/admin/session?session_key=${encodeURIComponent(E)}`);if(e.status!==401)return;let t=(await e.clone().json().catch(()=>null))?.code??`unknown-401`;if(t===`remote-auth-required`){a(`heartbeat`,`/api/admin/session`);return}console.warn(`[admin-auth] outcome=heartbeat-detected-expiry code=${t}`),Y.current?.()}catch{}},300*1e3);return()=>clearInterval(e)},[t,E]),(0,p.useEffect)(()=>{t===`connect-claude`&&fetch(`/api/health`).then(e=>e.ok?e.json():null).then(e=>{e?.claude_authenticated&&(K(!0),n(`enter-pin`))}).catch(()=>{})},[t]);async function X(e,t){y(!0);try{let r=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...t?{accountId:t}:{}})});if(!r.ok){let e=await r.json().catch(()=>({}));if(r.status===401&&e?.code===`remote-auth-required`){a(`enter-pin`,`/api/admin/session`);return}h(e.error||`Invalid PIN`);return}let s=await r.json();D(s.session_key),z(s.sessionId??null),j(s.businessName),N(s.role??null),F(s.userName===void 0?null:s.userName),L(s.avatar??null);let c=o(s.thinkingView);H.current=c,V(c);try{sessionStorage.setItem(`maxy-admin-session-key`,s.session_key)}catch{}i(``),n(`chat`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}}let Z=(0,p.useCallback)(async e=>{if(e.preventDefault(),v)return;h(``);let t=c.trim();if(!t){h(`Please enter your name.`);return}if(r.length<4){h(`PIN must be at least 4 characters.`);return}let o=r;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:o,name:t})});if(!e.ok){let t=await e.json().catch(()=>({}));if(e.status===401&&t?.code===`remote-auth-required`){a(`set-pin`,`/api/onboarding/set-pin`);return}if(e.status===409){console.log(`[admin] PIN already configured — re-checking health`);try{let e=await fetch(`/api/health`);if(e.ok){let r=await e.json();r.pin_configured&&r.claude_authenticated?n(`enter-pin`):r.pin_configured?n(`connect-claude`):h(t.error||`Failed to set PIN.`)}else n(`enter-pin`)}catch{n(`enter-pin`)}return}h(t.error||`Failed to set PIN.`);return}let r=await fetch(`/api/health`);if((r.ok?await r.json():null)?.claude_authenticated){await X(o);return}i(``),n(`connect-claude`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}},[r,v,c]),Q=(0,p.useCallback)(async e=>{e.preventDefault(),h(``),await X(r)},[r]),ee=(0,p.useCallback)(async()=>{T(!0);try{if(!await s())return console.warn(`[admin-ui] claude-disconnect not verified — credentials may persist; staying put`),!1;K(!1),D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}return n(`connect-claude`),!0}finally{T(!1)}},[]),$=(0,p.useCallback)(()=>{D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}i(``),h(``),n(`enter-pin`)},[]);return(0,p.useEffect)(()=>{Y.current=$},[$]),{appState:t,setAppState:n,pin:r,setPin:i,operatorName:c,setOperatorName:l,pinError:m,setPinError:h,showPin:g,setShowPin:_,pinLoading:v,authPolling:b,setAuthPolling:x,authLoading:S,setAuthLoading:C,disconnecting:w,cacheKey:E,setCacheKey:D,claudeInfo:O,setClaudeInfo:k,businessName:A,role:M,userName:P,userAvatar:I,sessionId:R,setSessionId:z,expandAll:B,setExpandAll:V,expandAllDefaultRef:H,remoteAuthEnabled:U,claudeConnected:G,pinInputRef:q,setPinFormRef:J,handleSetPin:Z,handleLogin:Q,handleDisconnect:ee,handleLogout:$}}var h=t();function g({inputRef:e,value:t,onChange:n,onComplete:r,showPin:i,autoFocus:a}){let o=(0,p.useRef)([]);function s(e,r){r.key===`Backspace`?(r.preventDefault(),t[e]?n(t.slice(0,e)+t.slice(e+1)):e>0&&(n(t.slice(0,e-1)+t.slice(e)),o.current[e-1]?.focus())):r.key===`ArrowLeft`&&e>0?o.current[e-1]?.focus():r.key===`ArrowRight`&&e<5?o.current[e+1]?.focus():r.key===`Enter`&&(r.preventDefault(),r.currentTarget.form?.requestSubmit())}function c(e,i){let a=i.nativeEvent.data;if(!a||!/^\d$/.test(a))return;let s=t.split(``);for(s[e]=a;s.length<e;)s.push(``);let c=s.join(``).replace(/\D/g,``).slice(0,6);n(c),c.length===6?r?.(c):e<5&&o.current[e+1]?.focus()}function l(e){e.preventDefault();let t=e.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,6);t&&(n(t),t.length===6?r?.(t):o.current[t.length]?.focus())}return(0,h.jsx)(`div`,{className:`pin-field`,children:Array.from({length:6}).map((n,r)=>(0,h.jsx)(`input`,{ref:t=>{o.current[r]=t,r===0&&e&&(e.current=t)},type:`text`,inputMode:`numeric`,className:`pin-box${t[r]?` pin-box-filled`:``}`,value:t[r]?i?t[r]:`•`:``,onKeyDown:e=>s(r,e),onInput:e=>c(r,e),onPaste:l,onFocus:e=>e.target.select(),autoFocus:a&&r===0,autoComplete:`off`,maxLength:1,"aria-label":`PIN digit ${r+1}`},r))})}function _(e){let{pin:t,setPin:n,showPin:a,setShowPin:o,pinLoading:s,pinError:u,pinInputRef:d,setPinFormRef:f,onSubmit:p,operatorName:m,setOperatorName:_}=e;return(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:i,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,r.productName]}),(0,h.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,r.productName,` who you are, then choose a PIN.`]}),(0,h.jsxs)(`form`,{ref:f,onSubmit:p,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(`input`,{type:`text`,className:`connect-name-input`,placeholder:`Your full name`,value:m,onChange:e=>_(e.target.value),autoComplete:`name`,autoFocus:!0,required:!0,"aria-label":`Your full name`}),(0,h.jsx)(`div`,{style:{width:38,flexShrink:0},"aria-hidden":`true`})]}),(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:t,onChange:n,onComplete:()=>{},showPin:a}),(0,h.jsx)(l,{variant:`send`,type:`submit`,disabled:!t||!m.trim(),loading:s,"aria-label":`Set PIN`,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(c,{checked:a,onChange:()=>o(e=>!e),label:`Show PIN`})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})})}function v(e){let{pin:t,setPin:n,showPin:a,setShowPin:o,pinLoading:s,pinError:u,pinInputRef:d,onSubmit:f,remoteAuthEnabled:p,onSignOutRemote:m}=e;return(0,h.jsxs)(`div`,{className:`connect-page`,children:[p&&m&&(0,h.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:m,children:`Sign out`}),(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:i,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsx)(`h1`,{className:`connect-title`,children:r.productName}),(0,h.jsxs)(`form`,{onSubmit:f,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:t,onChange:n,onComplete:()=>{},showPin:a,autoFocus:!0}),(0,h.jsx)(l,{variant:`send`,type:`submit`,disabled:!t,loading:s,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(`div`,{className:`pin-options`,children:(0,h.jsx)(c,{checked:a,onChange:()=>o(e=>!e),label:`Show PIN`})})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})]})}function y(e){let{authPolling:t,setAuthPolling:n,authLoading:a,setAuthLoading:o,pinError:s,setPinError:c,setAppState:u}=e,[d,f]=(0,p.useState)(!1),[m,g]=(0,p.useState)(!1),[_,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(``),[x,S]=(0,p.useState)(``);async function C(){S(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`paste-code`,code:y})})).json();e.accepted?(S(`Code submitted. Finishing sign-in…`),b(``)):S(e.error||`Could not submit code.`)}catch(e){console.error(`[admin] paste-code error:`,e),S(`Could not submit code.`)}}async function w(){g(!0),c(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`launch-browser`})})).json();e.launched?f(!0):e.error&&c(e.error)}catch(e){console.error(`[admin] browser launch error:`,e),c(`Could not launch browser.`)}g(!1)}async function T(){o(!0),c(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){n(!0),f(!0),o(!1),v(e.transport||``);for(let e=0;e<120;e++)if(await new Promise(e=>setTimeout(e,2e3)),(await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`wait`})})).json()).authenticated){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),u(`enter-pin`);return}c(`Timed out waiting for sign-in. Try again.`),n(!1)}else e.error&&c(e.error)}catch(e){console.error(`[admin] auth flow error:`,e),c(`Could not start auth flow.`)}o(!1)}async function E(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),n(!1),c(``)}return t||d?(0,h.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100dvh`,overflow:`auto`},children:[(0,h.jsxs)(`header`,{className:`chat-header`,style:{paddingBottom:`12px`,flexShrink:0,position:`relative`,maxWidth:`680px`,width:`100%`,margin:`0 auto`,padding:`24px 20px 12px`},children:[t?(0,h.jsx)(`button`,{onClick:E,style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Cancel`,children:`✕`}):(0,h.jsx)(`button`,{onClick:()=>f(!1),style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Close browser`,children:`✕`}),(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`chat-logo`}),(0,h.jsx)(`h1`,{className:`chat-tagline`,children:`Connect Claude`}),(0,h.jsx)(`p`,{className:`chat-intro`,children:t?`Sign in and authorize in the browser below.`:`Open your email or prepare your accounts, then sign in.`}),!t&&(0,h.jsx)(`div`,{style:{marginTop:`12px`},children:(0,h.jsx)(l,{variant:`primary`,onClick:T,disabled:a,children:a?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`})}),t&&_===`native`&&(0,h.jsxs)(`div`,{style:{marginTop:`14px`,maxWidth:`420px`,margin:`14px auto 0`,display:`flex`,flexDirection:`column`,gap:`8px`},children:[(0,h.jsx)(`label`,{style:{fontSize:`12px`,color:`#999`,lineHeight:1.4},children:`If the browser didn't finish sign-in, paste the code or full redirect URL from Claude's page:`}),(0,h.jsxs)(`div`,{style:{display:`flex`,gap:`8px`},children:[(0,h.jsx)(`input`,{type:`text`,value:y,onChange:e=>b(e.target.value),placeholder:`Paste code or redirect URL`,style:{flex:1,padding:`8px 10px`,fontSize:`13px`,border:`1px solid #ccc`,borderRadius:`6px`}}),(0,h.jsx)(l,{variant:`primary`,onClick:C,disabled:!y.trim(),children:`Submit`})]}),x&&(0,h.jsx)(`p`,{style:{fontSize:`12px`,color:`#999`,margin:0},children:x})]})]}),(0,h.jsx)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,minHeight:0,gap:`10px`,padding:`0 0 16px`},children:(0,h.jsx)(`iframe`,{src:`/vnc-viewer.html`,style:{flex:1,width:`100%`,minHeight:0,border:`none`,background:`#111`,display:`block`},title:`Claude Sign-in`})}),s&&(0,h.jsx)(`p`,{className:`admin-pin-error`,style:{textAlign:`center`,padding:`0 20px 16px`},children:s})]}):(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsxs)(`div`,{className:`connect-logos`,children:[(0,h.jsx)(`div`,{className:`connect-logo-wrap`,children:(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`connect-logo`})}),(0,h.jsx)(`svg`,{className:`connect-arrow`,viewBox:`0 0 48 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,h.jsx)(`path`,{d:`M0 12h44m0 0l-8-8m8 8l-8 8`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,h.jsxs)(`div`,{className:`connect-logo-wrap`,children:[(0,h.jsx)(`img`,{src:i,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsx)(`span`,{className:`connect-logo-label`,children:r.productName})]})]}),(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,r.productName]}),(0,h.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,h.jsx)(l,{variant:`primary`,onClick:T,disabled:a,children:a?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`}),(0,h.jsx)(`p`,{style:{marginTop:`6px`,fontSize:`11px`,color:`#999`,maxWidth:`300px`,textAlign:`center`,lineHeight:`1.4`},children:`First time? You may need to sign into your email and Anthropic account in the browser before connecting.`}),(0,h.jsx)(`button`,{onClick:w,disabled:m,style:{marginTop:`12px`,background:`none`,border:`none`,color:`var(--color-primary, #666)`,fontSize:`13px`,cursor:`pointer`,textDecoration:`underline`,textUnderlineOffset:`3px`},children:m?`Launching…`:`Open browser first`}),s&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:s})]})})}function b({auth:e}){return e.appState===`loading`?(0,h.jsx)(`div`,{className:`connect-page`}):e.appState===`set-pin`?(0,h.jsx)(_,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,setPinFormRef:e.setPinFormRef,onSubmit:e.handleSetPin,operatorName:e.operatorName,setOperatorName:e.setOperatorName}):e.appState===`connect-claude`?(0,h.jsx)(y,{authPolling:e.authPolling,setAuthPolling:e.setAuthPolling,authLoading:e.authLoading,setAuthLoading:e.setAuthLoading,pinError:e.pinError,setPinError:e.setPinError,setAppState:e.setAppState}):e.appState===`enter-pin`?(0,h.jsx)(v,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,onSubmit:e.handleLogin,remoteAuthEnabled:e.remoteAuthEnabled,onSignOutRemote:()=>{console.info(`[admin-ui] remote-auth sign-out → /__remote-auth/logout`),window.location.href=`/__remote-auth/logout`}}):null}export{m as n,b as t};