@rubytech/create-sitedesk-code 0.1.521 → 0.1.522

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 (255) hide show
  1. package/dist/index.js +17 -3
  2. package/package.json +1 -1
  3. package/payload/platform/lib/account-schema-regions/package-lock.json +12 -0
  4. package/payload/platform/lib/active-rules/package-lock.json +12 -0
  5. package/payload/platform/lib/mcp-lifeline/package-lock.json +12 -0
  6. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +43 -6
  7. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +6 -0
  8. package/payload/platform/plugins/docs/references/admin-ui.md +10 -0
  9. package/payload/platform/plugins/docs/references/internals.md +4 -0
  10. package/payload/platform/plugins/docs/references/outlook-guide.md +27 -4
  11. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  12. package/payload/platform/plugins/outlook/PLUGIN.md +9 -1
  13. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts +2 -0
  14. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts.map +1 -0
  15. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js +151 -0
  16. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js.map +1 -0
  17. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts +2 -0
  18. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts.map +1 -0
  19. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js +151 -0
  20. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js.map +1 -0
  21. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +38 -4
  22. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js.map +1 -1
  23. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts +2 -0
  24. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts.map +1 -0
  25. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js +146 -0
  26. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js.map +1 -0
  27. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts +2 -0
  28. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts.map +1 -0
  29. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js +120 -0
  30. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js.map +1 -0
  31. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js +8 -8
  32. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js.map +1 -1
  33. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts +2 -0
  34. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts.map +1 -0
  35. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js +27 -0
  36. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js.map +1 -0
  37. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts +2 -0
  38. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts.map +1 -0
  39. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js +70 -0
  40. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js.map +1 -0
  41. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts +2 -0
  42. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts.map +1 -0
  43. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js +152 -0
  44. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js.map +1 -0
  45. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts +57 -0
  46. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts.map +1 -0
  47. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js +163 -0
  48. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js.map +1 -0
  49. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +12 -0
  50. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts.map +1 -1
  51. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js +21 -1
  52. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -1
  53. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts +35 -4
  54. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts.map +1 -1
  55. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js +92 -10
  56. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js.map +1 -1
  57. package/payload/platform/plugins/outlook/mcp/dist/index.js +106 -3
  58. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  59. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +49 -0
  60. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
  61. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +126 -8
  62. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
  63. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +13 -0
  64. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -1
  65. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +9 -0
  66. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -1
  67. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts +42 -8
  68. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts.map +1 -1
  69. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js +59 -19
  70. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js.map +1 -1
  71. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts +47 -0
  72. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts.map +1 -0
  73. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js +67 -0
  74. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js.map +1 -0
  75. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts +12 -0
  76. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts.map +1 -0
  77. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js +18 -0
  78. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js.map +1 -0
  79. package/payload/platform/plugins/outlook/references/auth.md +15 -2
  80. package/payload/platform/plugins/outlook/references/graph-surfaces.md +8 -0
  81. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +21 -1
  82. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  83. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  84. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  85. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  86. package/payload/server/{chunk-AZLT4LVO.js → chunk-K7CJGZPU.js} +419 -189
  87. package/payload/server/{manager-CQBSUJXR.js → manager-RKL7JZ4Y.js} +3 -1
  88. package/payload/server/public/activity.html +5 -5
  89. package/payload/server/public/agents.html +4 -4
  90. package/payload/server/public/assets/{AdminLoginScreens-Dar_MMJm.js → AdminLoginScreens-zc2_qqh-.js} +1 -1
  91. package/payload/server/public/assets/AdminLoginScreens-zc2_qqh-.js.br +0 -0
  92. package/payload/server/public/assets/AdminLoginScreens-zc2_qqh-.js.gz +0 -0
  93. package/payload/server/public/assets/{AdminShell-CpaI2F_p.js → AdminShell-IGUdTx5q.js} +1 -1
  94. package/payload/server/public/assets/AdminShell-IGUdTx5q.js.br +0 -0
  95. package/payload/server/public/assets/{AdminShell-CpaI2F_p.js.gz → AdminShell-IGUdTx5q.js.gz} +0 -0
  96. package/payload/server/public/assets/{activity-B1rt4GIy.js → activity-DVgO8YuM.js} +1 -1
  97. package/payload/server/public/assets/activity-DVgO8YuM.js.br +0 -0
  98. package/payload/server/public/assets/activity-DVgO8YuM.js.gz +0 -0
  99. package/payload/server/public/assets/admin-xtpM7fgl.js +1 -0
  100. package/payload/server/public/assets/admin-xtpM7fgl.js.br +0 -0
  101. package/payload/server/public/assets/admin-xtpM7fgl.js.gz +0 -0
  102. package/payload/server/public/assets/{agents-BXq8woVf.js → agents-P0nTIxo-.js} +1 -1
  103. package/payload/server/public/assets/agents-P0nTIxo-.js.br +0 -0
  104. package/payload/server/public/assets/agents-P0nTIxo-.js.gz +0 -0
  105. package/payload/server/public/assets/{browser-1QSCPl3X.js → browser-Mw7DY3ft.js} +1 -1
  106. package/payload/server/public/assets/browser-Mw7DY3ft.js.br +0 -0
  107. package/payload/server/public/assets/browser-Mw7DY3ft.js.gz +0 -0
  108. package/payload/server/public/assets/{calendar-D009qWVV.js → calendar-B9gvL_1_.js} +1 -1
  109. package/payload/server/public/assets/calendar-B9gvL_1_.js.br +0 -0
  110. package/payload/server/public/assets/calendar-B9gvL_1_.js.gz +0 -0
  111. package/payload/server/public/assets/chat-DBGqh4wg.js +1 -0
  112. package/payload/server/public/assets/chat-DBGqh4wg.js.br +2 -0
  113. package/payload/server/public/assets/chat-DBGqh4wg.js.gz +0 -0
  114. package/payload/server/public/assets/chevron-left-Bl4vpOM0.js +1 -0
  115. package/payload/server/public/assets/chevron-left-Bl4vpOM0.js.br +0 -0
  116. package/payload/server/public/assets/chevron-right-BExigCnp.js +1 -0
  117. package/payload/server/public/assets/chevron-right-BExigCnp.js.br +0 -0
  118. package/payload/server/public/assets/clock-D8TJvgJC.js +1 -0
  119. package/payload/server/public/assets/clock-D8TJvgJC.js.br +0 -0
  120. package/payload/server/public/assets/data-HfFSxaC2.js +1 -0
  121. package/payload/server/public/assets/data-HfFSxaC2.js.br +0 -0
  122. package/payload/server/public/assets/data-HfFSxaC2.js.gz +0 -0
  123. package/payload/server/public/assets/{file-text-DwGAfCsd.js → file-text-dYBnXNEX.js} +1 -1
  124. package/payload/server/public/assets/file-text-dYBnXNEX.js.br +0 -0
  125. package/payload/server/public/assets/file-text-dYBnXNEX.js.gz +0 -0
  126. package/payload/server/public/assets/{graph-D-DGlfhW.js → graph-CDyzd1Et.js} +1 -1
  127. package/payload/server/public/assets/graph-CDyzd1Et.js.br +0 -0
  128. package/payload/server/public/assets/graph-CDyzd1Et.js.gz +0 -0
  129. package/payload/server/public/assets/{graph-labels-BbX3fXzl.js → graph-labels-D1LkKiJr.js} +1 -1
  130. package/payload/server/public/assets/graph-labels-D1LkKiJr.js.br +0 -0
  131. package/payload/server/public/assets/graph-labels-D1LkKiJr.js.gz +0 -0
  132. package/payload/server/public/assets/{maximize-2-C_-KapxH.js → maximize-2-M-iSaznd.js} +1 -1
  133. package/payload/server/public/assets/maximize-2-M-iSaznd.js.br +0 -0
  134. package/payload/server/public/assets/maximize-2-M-iSaznd.js.gz +0 -0
  135. package/payload/server/public/assets/{operator-CFrZVcv6.js → operator-9jfCv1Fg.js} +1 -1
  136. package/payload/server/public/assets/operator-9jfCv1Fg.js.br +0 -0
  137. package/payload/server/public/assets/operator-9jfCv1Fg.js.gz +0 -0
  138. package/payload/server/public/assets/{page-E74MYB18.js → page-CZ-73FAk.js} +1 -1
  139. package/payload/server/public/assets/page-CZ-73FAk.js.br +0 -0
  140. package/payload/server/public/assets/page-CZ-73FAk.js.gz +0 -0
  141. package/payload/server/public/assets/{page-CHt62f1_.js → page-CjwSt7kx.js} +1 -1
  142. package/payload/server/public/assets/page-CjwSt7kx.js.br +0 -0
  143. package/payload/server/public/assets/page-CjwSt7kx.js.gz +0 -0
  144. package/payload/server/public/assets/{public-CI6K1sNW.js → public-CN-ltC8x.js} +1 -1
  145. package/payload/server/public/assets/public-CN-ltC8x.js.br +0 -0
  146. package/payload/server/public/assets/public-CN-ltC8x.js.gz +0 -0
  147. package/payload/server/public/assets/{rotate-ccw-PWu_QGrD.js → rotate-ccw-Buopsc22.js} +1 -1
  148. package/payload/server/public/assets/rotate-ccw-Buopsc22.js.br +0 -0
  149. package/payload/server/public/assets/rotate-ccw-Buopsc22.js.gz +0 -0
  150. package/payload/server/public/assets/{routines-Cp3_XS7S.js → routines-C-yr8ddT.js} +1 -1
  151. package/payload/server/public/assets/routines-C-yr8ddT.js.br +0 -0
  152. package/payload/server/public/assets/routines-C-yr8ddT.js.gz +0 -0
  153. package/payload/server/public/assets/{skills-g8HvunPA.js → skills-DiiuVzDK.js} +1 -1
  154. package/payload/server/public/assets/skills-DiiuVzDK.js.br +0 -0
  155. package/payload/server/public/assets/skills-DiiuVzDK.js.gz +0 -0
  156. package/payload/server/public/assets/tasks-CnHIfn2_.js +1 -0
  157. package/payload/server/public/assets/tasks-CnHIfn2_.js.br +0 -0
  158. package/payload/server/public/assets/tasks-CnHIfn2_.js.gz +0 -0
  159. package/payload/server/public/assets/{time-entry-format-BXWuQ6Sw.js → time-entry-format-BBokCF-D.js} +1 -1
  160. package/payload/server/public/assets/time-entry-format-BBokCF-D.js.br +0 -0
  161. package/payload/server/public/assets/time-entry-format-BBokCF-D.js.gz +0 -0
  162. package/payload/server/public/assets/{triangle-alert-BNshRC13.js → triangle-alert-C_AcpTW5.js} +1 -1
  163. package/payload/server/public/assets/triangle-alert-C_AcpTW5.js.br +0 -0
  164. package/payload/server/public/assets/triangle-alert-C_AcpTW5.js.gz +0 -0
  165. package/payload/server/public/assets/{useCopyFeedback-CwTZ9DqH.js → useCopyFeedback-TdimmPVR.js} +1 -1
  166. package/payload/server/public/assets/useCopyFeedback-TdimmPVR.js.br +0 -0
  167. package/payload/server/public/assets/useCopyFeedback-TdimmPVR.js.gz +0 -0
  168. package/payload/server/public/assets/{useSubAccountSwitcher-BuUYMDWl.js → useSubAccountSwitcher-BwHqV55p.js} +1 -1
  169. package/payload/server/public/assets/useSubAccountSwitcher-BwHqV55p.js.br +0 -0
  170. package/payload/server/public/assets/{useSubAccountSwitcher-BuUYMDWl.js.gz → useSubAccountSwitcher-BwHqV55p.js.gz} +0 -0
  171. package/payload/server/public/assets/{useSubAccountSwitcher-dzDnmWq9.css → useSubAccountSwitcher-CsrPYqtm.css} +1 -1
  172. package/payload/server/public/assets/useSubAccountSwitcher-CsrPYqtm.css.br +0 -0
  173. package/payload/server/public/assets/{useSubAccountSwitcher-dzDnmWq9.css.gz → useSubAccountSwitcher-CsrPYqtm.css.gz} +0 -0
  174. package/payload/server/public/assets/{useVoiceRecorder-BGqV5R6u.js → useVoiceRecorder-BT2738ZS.js} +1 -1
  175. package/payload/server/public/assets/useVoiceRecorder-BT2738ZS.js.br +0 -0
  176. package/payload/server/public/assets/useVoiceRecorder-BT2738ZS.js.gz +0 -0
  177. package/payload/server/public/assets/{wrench-DQ045Pyk.js → wrench-B51s5kGA.js} +1 -1
  178. package/payload/server/public/assets/wrench-B51s5kGA.js.br +0 -0
  179. package/payload/server/public/assets/wrench-B51s5kGA.js.gz +0 -0
  180. package/payload/server/public/browser.html +4 -4
  181. package/payload/server/public/calendar.html +7 -7
  182. package/payload/server/public/chat.html +13 -13
  183. package/payload/server/public/data.html +11 -11
  184. package/payload/server/public/graph.html +9 -9
  185. package/payload/server/public/index.html +14 -14
  186. package/payload/server/public/operator.html +14 -14
  187. package/payload/server/public/public.html +13 -13
  188. package/payload/server/public/routines.html +6 -6
  189. package/payload/server/public/skills.html +5 -5
  190. package/payload/server/public/tasks.html +6 -6
  191. package/payload/server/server.js +1006 -606
  192. package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.br +0 -0
  193. package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.gz +0 -0
  194. package/payload/server/public/assets/AdminShell-CpaI2F_p.js.br +0 -0
  195. package/payload/server/public/assets/activity-B1rt4GIy.js.br +0 -0
  196. package/payload/server/public/assets/activity-B1rt4GIy.js.gz +0 -0
  197. package/payload/server/public/assets/admin-DE3lLNje.js +0 -1
  198. package/payload/server/public/assets/admin-DE3lLNje.js.br +0 -0
  199. package/payload/server/public/assets/admin-DE3lLNje.js.gz +0 -0
  200. package/payload/server/public/assets/agents-BXq8woVf.js.br +0 -0
  201. package/payload/server/public/assets/agents-BXq8woVf.js.gz +0 -0
  202. package/payload/server/public/assets/browser-1QSCPl3X.js.br +0 -0
  203. package/payload/server/public/assets/browser-1QSCPl3X.js.gz +0 -0
  204. package/payload/server/public/assets/calendar-D009qWVV.js.br +0 -0
  205. package/payload/server/public/assets/calendar-D009qWVV.js.gz +0 -0
  206. package/payload/server/public/assets/chat-CUg1fTqJ.js +0 -1
  207. package/payload/server/public/assets/chat-CUg1fTqJ.js.br +0 -1
  208. package/payload/server/public/assets/chat-CUg1fTqJ.js.gz +0 -0
  209. package/payload/server/public/assets/chevron-left-C2JEy7uU.js +0 -1
  210. package/payload/server/public/assets/chevron-left-C2JEy7uU.js.br +0 -0
  211. package/payload/server/public/assets/chevron-right-DWa047Ys.js +0 -1
  212. package/payload/server/public/assets/chevron-right-DWa047Ys.js.br +0 -0
  213. package/payload/server/public/assets/clock-C3OqfCnn.js +0 -1
  214. package/payload/server/public/assets/clock-C3OqfCnn.js.br +0 -0
  215. package/payload/server/public/assets/clock-C3OqfCnn.js.gz +0 -0
  216. package/payload/server/public/assets/data-Bu_yCScC.js +0 -1
  217. package/payload/server/public/assets/data-Bu_yCScC.js.br +0 -0
  218. package/payload/server/public/assets/data-Bu_yCScC.js.gz +0 -0
  219. package/payload/server/public/assets/file-text-DwGAfCsd.js.br +0 -0
  220. package/payload/server/public/assets/file-text-DwGAfCsd.js.gz +0 -0
  221. package/payload/server/public/assets/graph-D-DGlfhW.js.br +0 -0
  222. package/payload/server/public/assets/graph-D-DGlfhW.js.gz +0 -0
  223. package/payload/server/public/assets/graph-labels-BbX3fXzl.js.br +0 -0
  224. package/payload/server/public/assets/graph-labels-BbX3fXzl.js.gz +0 -0
  225. package/payload/server/public/assets/maximize-2-C_-KapxH.js.br +0 -0
  226. package/payload/server/public/assets/maximize-2-C_-KapxH.js.gz +0 -0
  227. package/payload/server/public/assets/operator-CFrZVcv6.js.br +0 -0
  228. package/payload/server/public/assets/operator-CFrZVcv6.js.gz +0 -0
  229. package/payload/server/public/assets/page-CHt62f1_.js.br +0 -0
  230. package/payload/server/public/assets/page-CHt62f1_.js.gz +0 -0
  231. package/payload/server/public/assets/page-E74MYB18.js.br +0 -0
  232. package/payload/server/public/assets/page-E74MYB18.js.gz +0 -0
  233. package/payload/server/public/assets/public-CI6K1sNW.js.br +0 -0
  234. package/payload/server/public/assets/public-CI6K1sNW.js.gz +0 -0
  235. package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.br +0 -0
  236. package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.gz +0 -0
  237. package/payload/server/public/assets/routines-Cp3_XS7S.js.br +0 -0
  238. package/payload/server/public/assets/routines-Cp3_XS7S.js.gz +0 -0
  239. package/payload/server/public/assets/skills-g8HvunPA.js.br +0 -0
  240. package/payload/server/public/assets/skills-g8HvunPA.js.gz +0 -0
  241. package/payload/server/public/assets/tasks-CMC_jDVm.js +0 -1
  242. package/payload/server/public/assets/tasks-CMC_jDVm.js.br +0 -0
  243. package/payload/server/public/assets/tasks-CMC_jDVm.js.gz +0 -0
  244. package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.br +0 -2
  245. package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.gz +0 -0
  246. package/payload/server/public/assets/triangle-alert-BNshRC13.js.br +0 -0
  247. package/payload/server/public/assets/triangle-alert-BNshRC13.js.gz +0 -0
  248. package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.br +0 -0
  249. package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.gz +0 -0
  250. package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.br +0 -0
  251. package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.br +0 -0
  252. package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.br +0 -0
  253. package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.gz +0 -0
  254. package/payload/server/public/assets/wrench-DQ045Pyk.js.br +0 -0
  255. package/payload/server/public/assets/wrench-DQ045Pyk.js.gz +0 -0
package/dist/index.js CHANGED
@@ -5398,16 +5398,30 @@ try {
5398
5398
  console.log("================================================================");
5399
5399
  console.log("");
5400
5400
  if (isLinux()) {
5401
+ const edgeDecision = pickEdgeBindDecision(networkInterfaces());
5401
5402
  // Only true when the edge binds 0.0.0.0. On a host classified to loopback
5402
5403
  // (Task 2082) nothing answers on the LAN address, so printing this line
5403
5404
  // there would be a false instruction; the SSH-forward recipe below is the
5404
5405
  // whole story for that host class.
5405
- if (pickEdgeBindDecision(networkInterfaces()).hostname === "0.0.0.0") {
5406
+ if (edgeDecision.hostname === "0.0.0.0") {
5406
5407
  console.log(` Same network (Pi / LAN): http://${DEVICE_HOSTNAME}.local:${PORT}`);
5407
5408
  console.log("");
5408
5409
  }
5409
- console.log(` Remote access (Hetzner / cloud) on your local machine run:`);
5410
- console.log(` ssh -L ${PORT}:localhost:${PORT} -L ${BRAND.websockifyPort}:localhost:${BRAND.websockifyPort} admin@<server-ipv4>`);
5410
+ // Provider-neutral. This recipe is identical on every cloud host with no
5411
+ // LAN to the operator, so naming one provider mis-describes the others: it
5412
+ // was printed verbatim on a netcup RS 8000 install (2026-07-28) while
5413
+ // saying "Hetzner". The host class, not the vendor, is what selects it.
5414
+ //
5415
+ // Print the host's own public address and the user that owns the install
5416
+ // rather than `admin@<server-ipv4>`. Both are already known here —
5417
+ // `publicAddresses` is what the loopback bind decision was just made from —
5418
+ // and a placeholder in a copy-pasted line is one more thing to get wrong.
5419
+ // Falls back to the placeholder only when the interface table shows no
5420
+ // public IPv4, which is the 1:1-NAT case the decision cannot see.
5421
+ const sshUser = userInfo().username;
5422
+ const sshTarget = `${sshUser}@${edgeDecision.publicAddresses[0] ?? "<server-ipv4>"}`;
5423
+ console.log(` Remote access (cloud server, no LAN to you): on your local machine run`);
5424
+ console.log(` ssh -L ${PORT}:localhost:${PORT} -L ${BRAND.websockifyPort}:localhost:${BRAND.websockifyPort} ${sshTarget}`);
5411
5425
  console.log(` Then open:`);
5412
5426
  console.log(` Dashboard: http://localhost:${PORT}`);
5413
5427
  console.log(` VNC browser (Claude OAuth + Cloudflare): http://localhost:${BRAND.websockifyPort}/vnc.html`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-sitedesk-code",
3
- "version": "0.1.521",
3
+ "version": "0.1.522",
4
4
  "description": "Install SiteDesk — automated back office for independent building contractors",
5
5
  "bin": {
6
6
  "create-sitedesk-code": "./dist/index.js"
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@maxy/account-schema-regions",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "@maxy/account-schema-regions",
9
+ "version": "0.1.0"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@maxy/active-rules",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "@maxy/active-rules",
9
+ "version": "0.1.0"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@maxy/mcp-lifeline",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "@maxy/mcp-lifeline",
9
+ "version": "0.1.0"
10
+ }
11
+ }
12
+ }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what SiteDesk 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:5704ae2d754fce29e10cf6bb2ee38da35a78387a0ca67134d5a105f1cd004abb
4
+ content-hash: sha256:3e9150568da700b668d3284d2edf7c780e6bc13323f5851c88371f559407b120
5
5
  brand: sitedesk-code
6
6
  product-name: SiteDesk
7
7
  ---
@@ -1355,7 +1355,7 @@ Take another snapshot immediately before any subsequent platform upgrade.
1355
1355
 
1356
1356
  ## 4. Run the installer
1357
1357
 
1358
- Run as the `admin` user (do not use `sudo`; the installer escalates internally where it needs to). The `--hostname` flag is required on a cloud VM it becomes the Cloudflare-fronted hostname and the systemd unit name.
1358
+ Run as the `admin` user (do not use `sudo`; the installer escalates internally where it needs to). The `--hostname` flag is required on a cloud VM. It sets the **OS** hostname via `hostnamectl` and names the Cloudflare-fronted host; it does **not** name the systemd units, which stay brand-derived. Measured on a netcup install 2026-07-28: passing `--hostname orchard` to `create-realagent-code` produced `hostname` = `orchard` alongside units still named `realagent-code.service`, `realagent-code-edge.service`, `realagent-code-claude-session-manager.service` and `realagent-code-rss-sampler.service`. Pass the same value on every re-run, so the flag rather than OS detection decides it.
1359
1359
 
1360
1360
  ```bash
1361
1361
  npx -y @rubytech/create-maxy-code@latest --hostname <hostname>
@@ -2388,7 +2388,7 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
2388
2388
 
2389
2389
  1. **No app to register.** The client ID ships with the brand (`brand.json#outlookClientId`), and the installer stamps it onto the session-manager env, so there is zero operator Azure work. On a brand that ships an empty value the plugin stays inert until its vendor app exists — see `platform/plugins/outlook/references/auth.md` for the one-time-per-brand vendor provisioning runbook (a vendor step, not an operator or end-user step), including setting **Allow public client flows** to Yes.
2390
2390
  2. **Per account: register the Outlook account** — in admin chat, ask the agent to "register my Outlook account". The agent runs `outlook-account-register`, which returns a short user code and the URL `microsoft.com/devicelogin` and relays both to you.
2391
- 3. **Consent on any device** — open `microsoft.com/devicelogin` on your phone or laptop, enter the code, sign in to your Microsoft account, and consent to the requested scopes (`offline_access`, `User.Read`, `Mail.ReadWrite`, `Mail.Send`, `Calendars.ReadWrite`, `Contacts.Read`). `Mail.Send` enables sending and `Calendars.ReadWrite` enables calendar control; a mailbox last consented on an older read-only scope set must re-register before send or any calendar write succeeds. Nothing else to do: the server finishes the sign-in automatically once you consent, usually within about half a minute, and the mailbox is then connected.
2391
+ 3. **Consent on any device** — open `microsoft.com/devicelogin` on your phone or laptop, enter the code, sign in to your Microsoft account, and consent to the requested scopes (`offline_access`, `User.Read`, `Mail.ReadWrite`, `Mail.Send`, `Mail.ReadWrite.Shared`, `Mail.Send.Shared`, `Calendars.ReadWrite`, `Contacts.Read`). `Mail.Send` enables sending, `Calendars.ReadWrite` enables calendar control, and the two `.Shared` scopes let this mailbox reach a shared mailbox it has Full Access to; a mailbox last consented on an older scope set must re-register before send, any calendar write, or any shared mailbox succeeds. Nothing else to do: the server finishes the sign-in automatically once you consent, usually within about half a minute, and the mailbox is then connected.
2392
2392
  4. **Done.** Subsequent tool calls (mail, calendar, contacts) use the persisted refresh token transparently.
2393
2393
  5. **Several mailboxes on one account.** Register again to add a second mailbox; it is stored alongside the first, not in place of it. Every operational tool then takes an optional `mailbox` argument — an email address or graphUserId. With one mailbox, omit it. With two or more, name it; omitting it returns a refusal listing the connected mailboxes. `outlook-mailbox-list` shows every connected mailbox. Only one registration runs at a time per account; starting another while one is live returns an in-progress signal.
2394
2394
 
@@ -2415,12 +2415,32 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
2415
2415
  | `outlook-calendar-freebusy` | Availability for one or more addresses over a window. |
2416
2416
  | `outlook-contacts-list` | Top contacts. Default top=50. |
2417
2417
  | `outlook-mailbox-info` | Health probe for one mailbox — auth state, refresh-window, folder count. Name the mailbox with `mailbox` when several are attached. |
2418
- | `outlook-mailbox-list` | Every connected mailbox with its email, scopes, and token health. Local only — no network — so it also flags a mailbox whose stored credentials cannot be read. |
2418
+ | `outlook-mailbox-list` | Every connected mailbox with its email, scopes, and token health, signed-in and shared alike (`kind`, and `via` for a shared one). Local only by default — no network — so it also flags a mailbox whose stored credentials cannot be read. `verify:true` additionally probes each shared mailbox and reports `reachable`. |
2419
+ | `outlook-shared-mailbox-add` | Attach a shared mailbox (no sign-in, no licence) through the Full Access of a mailbox that is already connected. `address` is the shared mailbox; `via` names the connected mailbox when more than one is attached. Proves the access before storing anything, so a refusal is the real answer. |
2420
+ | `outlook-shared-mailbox-remove` | Detach a shared mailbox. Drops the pointer only; no credential is touched. |
2419
2421
  | `outlook-mail-send` | Send a message. No attachments → `POST /me/sendMail`; with attachments → draft, attach, send (`/me/sendMail` carries attachments inline and cannot fit a large file). to/cc/bcc arrays, subject, body, isHtml, attachments (account-dir paths, 25 MB/file). Success asserted on Graph 202; no message id on either route. Needs `Mail.Send` (+ `Mail.ReadWrite` to attach). |
2420
2422
  | `outlook-draft` | Create a draft in Drafts, either a new message or a threaded reply. Returns the draft id; never sends. Attachments are attached before the id is returned, so the draft is ready to send. Needs `Mail.ReadWrite`. **New message** (no `replyToMessageId`): `POST /me/messages`, requires `to` + `subject`, same body model as `outlook-mail-send`, threads under nothing. **Threaded reply** (`replyToMessageId` = the id of the message being answered): `POST /me/messages/{id}/createReply` — the draft carries the parent's conversationId, so it lands under the original. This is the only way to draft a reply without sending it: `outlook-mail-reply` threads but always sends. Graph sets the recipients and `RE:` subject from the original and composes the body above its own quote, matching what `outlook-mail-reply` sends, so the threaded path is **plain text only** — `to`, `subject`, and `isHtml: true` are **refused** (not ignored) when replying. `replyAll` includes everyone on the original; `cc`/`bcc` add recipients. |
2421
2423
  | `outlook-draft-edit` | Update an existing draft in place (`PATCH /me/messages/{draftId}`) and/or add and remove attachments. Graph drafts are mutable — pass draftId plus the fields to change (to/cc/bcc/subject/body/isHtml/attachments/removeAttachments). `attachments` adds; `removeAttachments` takes files off by name or attachment id; both together replace a file in one call. Sent messages cannot be edited. Needs `Mail.ReadWrite`. |
2422
2424
  | `outlook-draft-send` | Send an existing draft by id (`POST /me/messages/{id}/send`). Graph consumes the draft, returns 202. Needs `Mail.Send`. |
2423
2425
 
2426
+ ## Shared mailboxes
2427
+
2428
+ A shared mailbox (`team@`, `info@`, `accounts@`) has no sign-in of its own, so nobody can connect it the way a normal mailbox is connected. **It does not need a licence and should never be given one for this.** Whoever has Full Access to it connects their own mailbox in the normal way, then attaches the shared one to it:
2429
+
2430
+ ```
2431
+ outlook-shared-mailbox-add address=team@yourcompany.com
2432
+ ```
2433
+
2434
+ Add `via=<your address>` if this account has more than one mailbox connected. From then on the shared address works as the `mailbox` argument on any mail tool, and `outlook-mailbox-list` shows it as `kind:delegated`.
2435
+
2436
+ The attach call checks the access before it stores anything, so what it says back is the real state of things:
2437
+
2438
+ - **"does not have Full Access"** — the delegation is missing. Grant it in the Microsoft 365 admin centre: Teams & groups → Shared mailboxes → the mailbox → Members / Read and manage. Nothing is purchased.
2439
+ - **"no mailbox with this address in the tenant"** — the address is wrong, or it is a distribution list. A distribution list has no mailbox and cannot be attached.
2440
+ - **"connected without the Mail.ReadWrite.Shared scope"** — your mailbox was connected before the plugin asked for the shared scopes. Register it again and it will work.
2441
+
2442
+ Mail only: calendar and contacts tools refuse on a shared mailbox and say so. If a shared mailbox stops answering, the delegation was probably removed in Exchange — `outlook-mailbox-list verify:true` reports which ones are still reachable.
2443
+
2424
2444
  ## Drafting to a Contact
2425
2445
 
2426
2446
  When the admin agent drafts or sends a message to a known contact, it reads that contact's record first and applies any routing the record carries, for example a preferred send-to address, showing the result for your approval before anything is sent.
@@ -2440,7 +2460,10 @@ All log lines start with `[outlook-mcp]` and write to `server.log`. They are key
2440
2460
  | /me failed post-token | `me-failed account=<id> status=<N>` |
2441
2461
  | Auth ok | `auth-ok account=<id> graphUserId=<id> mail=<addr> scopes=<csv> tokenExpSec=<N>` |
2442
2462
  | Registration already in flight | `register-in-progress account=<id>` (start refused; one device-code flow at a time) |
2443
- | Credential resolve | `cred-resolve account=<id> mailbox=<id-or-empty> source=arg|sole|none|refused` — names the resolved mailbox and how it was chosen; `source=refused` is the ambiguity/not-found refusal |
2463
+ | Credential resolve | `cred-resolve account=<id> mailbox=<id-or-empty> source=arg|sole|none|refused kind=signed-in|delegated via=<delegator-or-empty>` — names the resolved mailbox and how it was chosen; `source=refused` is the ambiguity/not-found refusal, and `kind=delegated` means the call ran against a shared mailbox through `via` |
2464
+ | Shared mailbox attached | `shared-add account=<id> address=<addr> via=<key> outcome=ok|ok-reattached|no-scope|forbidden|not-found` — every refusal path is named, so a failed attach is diagnosable without reproducing it |
2465
+ | Shared mailbox detached | `shared-remove account=<id> address=<addr> outcome=ok|absent` |
2466
+ | Calendar/contacts on a shared mailbox | `shared-tool-refused account=<id> tool=<tool> prefix=<users-path>` — refused before any request is built |
2444
2467
  | Token refreshed | `token-refreshed account=<id> oldExpSec=<N> newExpSec=<N>` |
2445
2468
  | Refresh failed | `token-refresh-failed account=<id> reason=<err>` (terminal) |
2446
2469
  | Mail list | `mail-list op=resolve account=<id> folderIn=<name> folderResolved=<token> asRawId=<bool> count=<N> elapsedMs=<N>` (on error, `error=<msg>` replaces `count`) |
@@ -2458,7 +2481,7 @@ All log lines start with `[outlook-mcp]` and write to `server.log`. They are key
2458
2481
  | Scope insufficient | `scope-insufficient tool=<t> account=<id> missing=Calendars.ReadWrite` (stale read-only consent) |
2459
2482
  | Contacts list | `contacts-list account=<id> count=<N> elapsedMs=<N>` |
2460
2483
  | Mailbox info | `mailbox-info account=<id> tokenWithinRefreshWindow=<bool> folderCount=<N>` |
2461
- | Mailbox list | `mailbox-list account=<id> count=<N> unreadable=<N>` (unreadable>0 flags an orphaned or corrupt mailbox store) |
2484
+ | Mailbox list | `mailbox-list account=<id> count=<N> unreadable=<N> addressable=<N> delegated=<N> verified=<N>` — `count` covers signed-in plus delegated entries, `addressable` counts only signed-in ones that are usable by their email, `delegated` counts the attached shared mailboxes, and `verified` is how many of those were probed (0 unless `verify:true`). `unreadable>0` flags an orphaned or corrupt mailbox store |
2462
2485
  | Graph error | `graph-error account=<id> status=<N> code=<graphErrorCode> retryAfterMs=<N-or-null>` — writes flow through the same `callGraph` loop, so a failed write logs `graph-error` too |
2463
2486
  | On-prem rejected | `on-prem-rejected account=<id> mailServer=<host>` (terminal) |
2464
2487
  | Scope insufficient | `scope-insufficient account=<id> tool=<t> missing=Mail.Send` (terminal — re-register to grant send) |
@@ -3902,6 +3925,16 @@ authoritative. This section names the surfaces and what backs each.
3902
3925
  > - **Dashboard home** renders on `/` (a greeting, stat cards, a Live-now column,
3903
3926
  > a Recent artefacts card, and a Channels column), consuming existing endpoints
3904
3927
  > only.
3928
+ > - **Channels card rows.** A WhatsApp row shows a profile picture,
3929
+ > the conversation's name, its last message, and the time, matching the layout
3930
+ > of the WhatsApp client. The name is the one WhatsApp itself holds: a group
3931
+ > shows its group subject, a 1:1 shows the contact name saved on the paired
3932
+ > phone. A group row carries an extra `Group` chip. When no name has been
3933
+ > captured yet, a group falls back to its group id and a 1:1 to its phone
3934
+ > number; neither ever shows a group member's name or the install's own
3935
+ > WhatsApp display name. The existing `Public` chip (the message came from
3936
+ > outside) and `House` / `Client` chip (which account owns the thread) are
3937
+ > unchanged.
3905
3938
  > - **Per-surface header.** Each surface carries its own header (`PageHeader`):
3906
3939
  > the sidebar collapse toggle, the page title, and a Claude status pill on the
3907
3940
  > right (Connected / Connect, driven by `/api/health`). The pill replaces the
@@ -5032,8 +5065,12 @@ For estate-agent client work surfaced on the `/tasks` operator page, `work-creat
5032
5065
 
5033
5066
  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`.
5034
5067
 
5068
+ A task also carries a **workspace** edge, distinct from the two provenance ones above: `(t:Task)-[:WORKED_ON_IN]->(c:Conversation)` is the session an operator works the task in, written only by the `/tasks` page Session control. `RAISED_DURING` and `PART_OF` say where a task was *raised*, so neither can answer "which chat do I continue this task in" — a task raised in an unrelated conversation would resume that one. At most one such edge exists per task: the control's link route deletes any edge to a different Conversation before merging the new one, which is how a resume the session manager redirected to a live fork re-points itself rather than staying on the dead source. The `:Task` also carries `sessionSpawnRequestedAt`, stamped before the spawn and removed when the link lands, so a session that was created but never linked is countable rather than invisible.
5069
+
5035
5070
  Operator audit cyphers:
5036
5071
  - "What entities did this conversation's actions produce?" — `MATCH (c:AdminConversation {sessionId:$id})<-[:RAISED_DURING]-(t:Task)-[:PRODUCED]->(e) RETURN labels(e), e.name, t.kind, t.status`
5072
+ - "Which session is this task worked in?" — `MATCH (t:Task {taskId:$id})-[:WORKED_ON_IN]->(c:Conversation) RETURN c.sessionId, c.name`
5073
+ - "Which tasks were marked for a session spawn that never linked?" — `MATCH (t:Task {accountId:$acct}) WHERE t.sessionSpawnRequestedAt IS NOT NULL AND NOT (t)-[:WORKED_ON_IN]->(:Conversation) RETURN t.taskId, t.name, t.sessionSpawnRequestedAt`
5037
5074
  - "What cloudflare resources did this tunnel-login produce?" — `MATCH (t:Task {kind:'cloudflare-tunnel-login', status:'completed'})-[:PRODUCED]->(r) RETURN t.taskId, r.tunnelId, r.hostnameValue ORDER BY t.completedAt DESC`
5038
5075
 
5039
5076
  See `.docs/neo4j.md § Process provenance doctrine` for the full enforcement contract, observability surface, and out-of-scope deferrals.
@@ -9,6 +9,12 @@ Invoked by the admin agent directly.
9
9
 
10
10
  This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
11
11
 
12
+ ## 2026-07-28 (0.1.522)
13
+
14
+ - You can now connect a shared Outlook mailbox you have been given access to, without needing a separate licence for it.
15
+ - WhatsApp conversations now show the real chat name, contact avatar and last message, instead of guessing from the first message.
16
+ - Open tasks now have a Session button that opens or resumes a chat session named for that task.
17
+
12
18
  ## 2026-07-28 (0.1.521)
13
19
 
14
20
  - Starting a new session now opens a naming modal first, and New-session controls read white on black across every brand.
@@ -712,6 +712,16 @@ authoritative. This section names the surfaces and what backs each.
712
712
  > - **Dashboard home** renders on `/` (a greeting, stat cards, a Live-now column,
713
713
  > a Recent artefacts card, and a Channels column), consuming existing endpoints
714
714
  > only.
715
+ > - **Channels card rows.** A WhatsApp row shows a profile picture,
716
+ > the conversation's name, its last message, and the time, matching the layout
717
+ > of the WhatsApp client. The name is the one WhatsApp itself holds: a group
718
+ > shows its group subject, a 1:1 shows the contact name saved on the paired
719
+ > phone. A group row carries an extra `Group` chip. When no name has been
720
+ > captured yet, a group falls back to its group id and a 1:1 to its phone
721
+ > number; neither ever shows a group member's name or the install's own
722
+ > WhatsApp display name. The existing `Public` chip (the message came from
723
+ > outside) and `House` / `Client` chip (which account owns the thread) are
724
+ > unchanged.
715
725
  > - **Per-surface header.** Each surface carries its own header (`PageHeader`):
716
726
  > the sidebar collapse toggle, the page title, and a Claude status pill on the
717
727
  > right (Connected / Connect, driven by `/api/health`). The pill replaces the
@@ -560,8 +560,12 @@ For estate-agent client work surfaced on the `/tasks` operator page, `work-creat
560
560
 
561
561
  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`.
562
562
 
563
+ A task also carries a **workspace** edge, distinct from the two provenance ones above: `(t:Task)-[:WORKED_ON_IN]->(c:Conversation)` is the session an operator works the task in, written only by the `/tasks` page Session control. `RAISED_DURING` and `PART_OF` say where a task was *raised*, so neither can answer "which chat do I continue this task in" — a task raised in an unrelated conversation would resume that one. At most one such edge exists per task: the control's link route deletes any edge to a different Conversation before merging the new one, which is how a resume the session manager redirected to a live fork re-points itself rather than staying on the dead source. The `:Task` also carries `sessionSpawnRequestedAt`, stamped before the spawn and removed when the link lands, so a session that was created but never linked is countable rather than invisible.
564
+
563
565
  Operator audit cyphers:
564
566
  - "What entities did this conversation's actions produce?" — `MATCH (c:AdminConversation {sessionId:$id})<-[:RAISED_DURING]-(t:Task)-[:PRODUCED]->(e) RETURN labels(e), e.name, t.kind, t.status`
567
+ - "Which session is this task worked in?" — `MATCH (t:Task {taskId:$id})-[:WORKED_ON_IN]->(c:Conversation) RETURN c.sessionId, c.name`
568
+ - "Which tasks were marked for a session spawn that never linked?" — `MATCH (t:Task {accountId:$acct}) WHERE t.sessionSpawnRequestedAt IS NOT NULL AND NOT (t)-[:WORKED_ON_IN]->(:Conversation) RETURN t.taskId, t.name, t.sessionSpawnRequestedAt`
565
569
  - "What cloudflare resources did this tunnel-login produce?" — `MATCH (t:Task {kind:'cloudflare-tunnel-login', status:'completed'})-[:PRODUCED]->(r) RETURN t.taskId, r.tunnelId, r.hostnameValue ORDER BY t.completedAt DESC`
566
570
 
567
571
  See `.docs/neo4j.md § Process provenance doctrine` for the full enforcement contract, observability surface, and out-of-scope deferrals.
@@ -6,7 +6,7 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
6
6
 
7
7
  1. **No app to register.** The client ID ships with the brand (`brand.json#outlookClientId`), and the installer stamps it onto the session-manager env, so there is zero operator Azure work. On a brand that ships an empty value the plugin stays inert until its vendor app exists — see `platform/plugins/outlook/references/auth.md` for the one-time-per-brand vendor provisioning runbook (a vendor step, not an operator or end-user step), including setting **Allow public client flows** to Yes.
8
8
  2. **Per account: register the Outlook account** — in admin chat, ask the agent to "register my Outlook account". The agent runs `outlook-account-register`, which returns a short user code and the URL `microsoft.com/devicelogin` and relays both to you.
9
- 3. **Consent on any device** — open `microsoft.com/devicelogin` on your phone or laptop, enter the code, sign in to your Microsoft account, and consent to the requested scopes (`offline_access`, `User.Read`, `Mail.ReadWrite`, `Mail.Send`, `Calendars.ReadWrite`, `Contacts.Read`). `Mail.Send` enables sending and `Calendars.ReadWrite` enables calendar control; a mailbox last consented on an older read-only scope set must re-register before send or any calendar write succeeds. Nothing else to do: the server finishes the sign-in automatically once you consent, usually within about half a minute, and the mailbox is then connected.
9
+ 3. **Consent on any device** — open `microsoft.com/devicelogin` on your phone or laptop, enter the code, sign in to your Microsoft account, and consent to the requested scopes (`offline_access`, `User.Read`, `Mail.ReadWrite`, `Mail.Send`, `Mail.ReadWrite.Shared`, `Mail.Send.Shared`, `Calendars.ReadWrite`, `Contacts.Read`). `Mail.Send` enables sending, `Calendars.ReadWrite` enables calendar control, and the two `.Shared` scopes let this mailbox reach a shared mailbox it has Full Access to; a mailbox last consented on an older scope set must re-register before send, any calendar write, or any shared mailbox succeeds. Nothing else to do: the server finishes the sign-in automatically once you consent, usually within about half a minute, and the mailbox is then connected.
10
10
  4. **Done.** Subsequent tool calls (mail, calendar, contacts) use the persisted refresh token transparently.
11
11
  5. **Several mailboxes on one account.** Register again to add a second mailbox; it is stored alongside the first, not in place of it. Every operational tool then takes an optional `mailbox` argument — an email address or graphUserId. With one mailbox, omit it. With two or more, name it; omitting it returns a refusal listing the connected mailboxes. `outlook-mailbox-list` shows every connected mailbox. Only one registration runs at a time per account; starting another while one is live returns an in-progress signal.
12
12
 
@@ -33,12 +33,32 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
33
33
  | `outlook-calendar-freebusy` | Availability for one or more addresses over a window. |
34
34
  | `outlook-contacts-list` | Top contacts. Default top=50. |
35
35
  | `outlook-mailbox-info` | Health probe for one mailbox — auth state, refresh-window, folder count. Name the mailbox with `mailbox` when several are attached. |
36
- | `outlook-mailbox-list` | Every connected mailbox with its email, scopes, and token health. Local only — no network — so it also flags a mailbox whose stored credentials cannot be read. |
36
+ | `outlook-mailbox-list` | Every connected mailbox with its email, scopes, and token health, signed-in and shared alike (`kind`, and `via` for a shared one). Local only by default — no network — so it also flags a mailbox whose stored credentials cannot be read. `verify:true` additionally probes each shared mailbox and reports `reachable`. |
37
+ | `outlook-shared-mailbox-add` | Attach a shared mailbox (no sign-in, no licence) through the Full Access of a mailbox that is already connected. `address` is the shared mailbox; `via` names the connected mailbox when more than one is attached. Proves the access before storing anything, so a refusal is the real answer. |
38
+ | `outlook-shared-mailbox-remove` | Detach a shared mailbox. Drops the pointer only; no credential is touched. |
37
39
  | `outlook-mail-send` | Send a message. No attachments → `POST /me/sendMail`; with attachments → draft, attach, send (`/me/sendMail` carries attachments inline and cannot fit a large file). to/cc/bcc arrays, subject, body, isHtml, attachments (account-dir paths, 25 MB/file). Success asserted on Graph 202; no message id on either route. Needs `Mail.Send` (+ `Mail.ReadWrite` to attach). |
38
40
  | `outlook-draft` | Create a draft in Drafts, either a new message or a threaded reply. Returns the draft id; never sends. Attachments are attached before the id is returned, so the draft is ready to send. Needs `Mail.ReadWrite`. **New message** (no `replyToMessageId`): `POST /me/messages`, requires `to` + `subject`, same body model as `outlook-mail-send`, threads under nothing. **Threaded reply** (`replyToMessageId` = the id of the message being answered): `POST /me/messages/{id}/createReply` — the draft carries the parent's conversationId, so it lands under the original. This is the only way to draft a reply without sending it: `outlook-mail-reply` threads but always sends. Graph sets the recipients and `RE:` subject from the original and composes the body above its own quote, matching what `outlook-mail-reply` sends, so the threaded path is **plain text only** — `to`, `subject`, and `isHtml: true` are **refused** (not ignored) when replying. `replyAll` includes everyone on the original; `cc`/`bcc` add recipients. |
39
41
  | `outlook-draft-edit` | Update an existing draft in place (`PATCH /me/messages/{draftId}`) and/or add and remove attachments. Graph drafts are mutable — pass draftId plus the fields to change (to/cc/bcc/subject/body/isHtml/attachments/removeAttachments). `attachments` adds; `removeAttachments` takes files off by name or attachment id; both together replace a file in one call. Sent messages cannot be edited. Needs `Mail.ReadWrite`. |
40
42
  | `outlook-draft-send` | Send an existing draft by id (`POST /me/messages/{id}/send`). Graph consumes the draft, returns 202. Needs `Mail.Send`. |
41
43
 
44
+ ## Shared mailboxes
45
+
46
+ A shared mailbox (`team@`, `info@`, `accounts@`) has no sign-in of its own, so nobody can connect it the way a normal mailbox is connected. **It does not need a licence and should never be given one for this.** Whoever has Full Access to it connects their own mailbox in the normal way, then attaches the shared one to it:
47
+
48
+ ```
49
+ outlook-shared-mailbox-add address=team@yourcompany.com
50
+ ```
51
+
52
+ Add `via=<your address>` if this account has more than one mailbox connected. From then on the shared address works as the `mailbox` argument on any mail tool, and `outlook-mailbox-list` shows it as `kind:delegated`.
53
+
54
+ The attach call checks the access before it stores anything, so what it says back is the real state of things:
55
+
56
+ - **"does not have Full Access"** — the delegation is missing. Grant it in the Microsoft 365 admin centre: Teams & groups → Shared mailboxes → the mailbox → Members / Read and manage. Nothing is purchased.
57
+ - **"no mailbox with this address in the tenant"** — the address is wrong, or it is a distribution list. A distribution list has no mailbox and cannot be attached.
58
+ - **"connected without the Mail.ReadWrite.Shared scope"** — your mailbox was connected before the plugin asked for the shared scopes. Register it again and it will work.
59
+
60
+ Mail only: calendar and contacts tools refuse on a shared mailbox and say so. If a shared mailbox stops answering, the delegation was probably removed in Exchange — `outlook-mailbox-list verify:true` reports which ones are still reachable.
61
+
42
62
  ## Drafting to a Contact
43
63
 
44
64
  When the admin agent drafts or sends a message to a known contact, it reads that contact's record first and applies any routing the record carries, for example a preferred send-to address, showing the result for your approval before anything is sent.
@@ -58,7 +78,10 @@ All log lines start with `[outlook-mcp]` and write to `server.log`. They are key
58
78
  | /me failed post-token | `me-failed account=<id> status=<N>` |
59
79
  | Auth ok | `auth-ok account=<id> graphUserId=<id> mail=<addr> scopes=<csv> tokenExpSec=<N>` |
60
80
  | Registration already in flight | `register-in-progress account=<id>` (start refused; one device-code flow at a time) |
61
- | Credential resolve | `cred-resolve account=<id> mailbox=<id-or-empty> source=arg|sole|none|refused` — names the resolved mailbox and how it was chosen; `source=refused` is the ambiguity/not-found refusal |
81
+ | Credential resolve | `cred-resolve account=<id> mailbox=<id-or-empty> source=arg|sole|none|refused kind=signed-in|delegated via=<delegator-or-empty>` — names the resolved mailbox and how it was chosen; `source=refused` is the ambiguity/not-found refusal, and `kind=delegated` means the call ran against a shared mailbox through `via` |
82
+ | Shared mailbox attached | `shared-add account=<id> address=<addr> via=<key> outcome=ok|ok-reattached|no-scope|forbidden|not-found` — every refusal path is named, so a failed attach is diagnosable without reproducing it |
83
+ | Shared mailbox detached | `shared-remove account=<id> address=<addr> outcome=ok|absent` |
84
+ | Calendar/contacts on a shared mailbox | `shared-tool-refused account=<id> tool=<tool> prefix=<users-path>` — refused before any request is built |
62
85
  | Token refreshed | `token-refreshed account=<id> oldExpSec=<N> newExpSec=<N>` |
63
86
  | Refresh failed | `token-refresh-failed account=<id> reason=<err>` (terminal) |
64
87
  | Mail list | `mail-list op=resolve account=<id> folderIn=<name> folderResolved=<token> asRawId=<bool> count=<N> elapsedMs=<N>` (on error, `error=<msg>` replaces `count`) |
@@ -76,7 +99,7 @@ All log lines start with `[outlook-mcp]` and write to `server.log`. They are key
76
99
  | Scope insufficient | `scope-insufficient tool=<t> account=<id> missing=Calendars.ReadWrite` (stale read-only consent) |
77
100
  | Contacts list | `contacts-list account=<id> count=<N> elapsedMs=<N>` |
78
101
  | Mailbox info | `mailbox-info account=<id> tokenWithinRefreshWindow=<bool> folderCount=<N>` |
79
- | Mailbox list | `mailbox-list account=<id> count=<N> unreadable=<N>` (unreadable>0 flags an orphaned or corrupt mailbox store) |
102
+ | Mailbox list | `mailbox-list account=<id> count=<N> unreadable=<N> addressable=<N> delegated=<N> verified=<N>` — `count` covers signed-in plus delegated entries, `addressable` counts only signed-in ones that are usable by their email, `delegated` counts the attached shared mailboxes, and `verified` is how many of those were probed (0 unless `verify:true`). `unreadable>0` flags an orphaned or corrupt mailbox store |
80
103
  | Graph error | `graph-error account=<id> status=<N> code=<graphErrorCode> retryAfterMs=<N-or-null>` — writes flow through the same `callGraph` loop, so a failed write logs `graph-error` too |
81
104
  | On-prem rejected | `on-prem-rejected account=<id> mailServer=<host>` (terminal) |
82
105
  | Scope insufficient | `scope-insufficient account=<id> tool=<t> missing=Mail.Send` (terminal — re-register to grant send) |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "outlook",
3
- "description": "Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, send); contacts are read-only; calendar is read + control. Per-account OAuth device-code flow; no client secret. One account can hold several mailboxes at once; every operational tool takes an optional mailbox selector (email or graphUserId) and refuses ambiguously when two or more are attached and none is named. Tools — outlook-account-register / outlook-account-register-poll: device-code register (start + poll); outlook-mail-list / outlook-mail-search: inbox triage (~255-char preview) with a date window (list) or KQL from:/subject: filters (search) and a nextCursor for paging beyond the newest page; outlook-mail-fetch-body: full body of one message by id; outlook-mail-attachment: list any message's attachments (received or draft), or download one file attachment's bytes to the account uploads folder; outlook-mail-reply: in-thread reply (replyAll, cc/bcc, attachments); outlook-mail-delete: move to Deleted Items (recoverable); outlook-mail-ingest: write operator-reviewed messages into the business graph as :ConversationArchive threads (per-message ingest/discard, conversationId threading, closed-set participant resolution); outlook-mail-otp-extract: poll for a one-time code; outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send: compose, edit, and send, all with attachments — outlook-draft takes an optional replyToMessageId to draft a threaded reply, unsent, under the original conversation; outlook-calendar-list / outlook-calendar-event: read calendar; outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy: control calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count for one mailbox; outlook-mailbox-list: every connected mailbox.",
3
+ "description": "Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, send); contacts are read-only; calendar is read + control. Per-account OAuth device-code flow; no client secret. One account can hold several mailboxes at once; every operational tool takes an optional mailbox selector (email or graphUserId) and refuses ambiguously when two or more are attached and none is named. Tools — outlook-account-register / outlook-account-register-poll: device-code register (start + poll); outlook-mail-list / outlook-mail-search: inbox triage (~255-char preview) with a date window (list) or KQL from:/subject: filters (search) and a nextCursor for paging beyond the newest page; outlook-mail-fetch-body: full body of one message by id; outlook-mail-attachment: list any message's attachments (received or draft), or download one file attachment's bytes to the account uploads folder; outlook-mail-reply: in-thread reply (replyAll, cc/bcc, attachments); outlook-mail-delete: move to Deleted Items (recoverable); outlook-mail-ingest: write operator-reviewed messages into the business graph as :ConversationArchive threads (per-message ingest/discard, conversationId threading, closed-set participant resolution); outlook-mail-otp-extract: poll for a one-time code; outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send: compose, edit, and send, all with attachments — outlook-draft takes an optional replyToMessageId to draft a threaded reply, unsent, under the original conversation; outlook-calendar-list / outlook-calendar-event: read calendar; outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy: control calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count for one mailbox; outlook-mailbox-list: every connected mailbox, signed-in and shared, with an opt-in reachability probe; outlook-shared-mailbox-add / outlook-shared-mailbox-remove: attach or detach a shared mailbox (no licence, no sign-in of its own) through the signed-in mailbox's Full Access, access proved before anything is stored.",
4
4
  "version": "0.1.0",
5
5
  "author": {
6
6
  "name": "Rubytech LLC"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: outlook
3
- description: Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, send); contacts are read-only; calendar is read + control. Per-account OAuth device-code flow; no client secret. One account can hold several mailboxes at once; every operational tool takes an optional mailbox selector (email or graphUserId) and refuses ambiguously when two or more are attached and none is named. Tools — outlook-account-register / outlook-account-register-poll: device-code register (start + poll); outlook-mail-list / outlook-mail-search: inbox triage (~255-char preview) with a date window (list) or KQL from:/subject: filters (search) and a nextCursor for paging beyond the newest page; outlook-mail-fetch-body: full body of one message by id; outlook-mail-attachment: list any message's attachments (received or draft), or download one file attachment's bytes to the account uploads folder; outlook-mail-reply: in-thread reply (replyAll, cc/bcc, attachments); outlook-mail-delete: move to Deleted Items (recoverable); outlook-mail-ingest: write operator-reviewed messages into the business graph as :ConversationArchive threads (per-message ingest/discard, conversationId threading, closed-set participant resolution); outlook-mail-otp-extract: poll for a one-time code; outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send: compose, edit, and send, all with attachments — outlook-draft takes an optional replyToMessageId to draft a threaded reply, unsent, under the original conversation; outlook-calendar-list / outlook-calendar-event: read calendar; outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy: control calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count for one mailbox; outlook-mailbox-list: every connected mailbox.
3
+ description: Microsoft 365 / Outlook.com via Microsoft Graph. Mail is read (list, search, full-body), reply, delete, and compose (draft, draft-edit, send); contacts are read-only; calendar is read + control. Per-account OAuth device-code flow; no client secret. One account can hold several mailboxes at once; every operational tool takes an optional mailbox selector (email or graphUserId) and refuses ambiguously when two or more are attached and none is named. Tools — outlook-account-register / outlook-account-register-poll: device-code register (start + poll); outlook-mail-list / outlook-mail-search: inbox triage (~255-char preview) with a date window (list) or KQL from:/subject: filters (search) and a nextCursor for paging beyond the newest page; outlook-mail-fetch-body: full body of one message by id; outlook-mail-attachment: list any message's attachments (received or draft), or download one file attachment's bytes to the account uploads folder; outlook-mail-reply: in-thread reply (replyAll, cc/bcc, attachments); outlook-mail-delete: move to Deleted Items (recoverable); outlook-mail-ingest: write operator-reviewed messages into the business graph as :ConversationArchive threads (per-message ingest/discard, conversationId threading, closed-set participant resolution); outlook-mail-otp-extract: poll for a one-time code; outlook-mail-send / outlook-draft / outlook-draft-edit / outlook-draft-send: compose, edit, and send, all with attachments — outlook-draft takes an optional replyToMessageId to draft a threaded reply, unsent, under the original conversation; outlook-calendar-list / outlook-calendar-event: read calendar; outlook-calendar-create / outlook-calendar-update / outlook-calendar-cancel / outlook-calendar-respond / outlook-calendar-freebusy: control calendar; outlook-contacts-list: contacts; outlook-mailbox-info: auth state + folder count for one mailbox; outlook-mailbox-list: every connected mailbox, signed-in and shared, with an opt-in reachability probe; outlook-shared-mailbox-add / outlook-shared-mailbox-remove: attach or detach a shared mailbox (no licence, no sign-in of its own) through the signed-in mailbox's Full Access, access proved before anything is stored.
4
4
  tools:
5
5
  - name: outlook-account-register
6
6
  publicAllowlist: false
@@ -98,6 +98,14 @@ tools:
98
98
  publicAllowlist: false
99
99
  adminAllowlist: false
100
100
  riskClass: read
101
+ - name: outlook-shared-mailbox-add
102
+ publicAllowlist: false
103
+ adminAllowlist: false
104
+ riskClass: write_local
105
+ - name: outlook-shared-mailbox-remove
106
+ publicAllowlist: false
107
+ adminAllowlist: false
108
+ riskClass: write_local
101
109
  always: false
102
110
  embed: ["admin"]
103
111
  metadata: {"platform":{"optional":true}}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=delegated-resolve.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegated-resolve.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/delegated-resolve.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,151 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { MailboxRegistry, credResolveEvent } from "../auth/mailbox-registry.js";
7
+ import { DelegatedStore } from "../auth/delegated-store.js";
8
+ import { TokenStore } from "../auth/token-store.js";
9
+ function makeAccountsDir() {
10
+ const dir = mkdtempSync(join(tmpdir(), "outlook-delegated-resolve-"));
11
+ return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
12
+ }
13
+ function connect(dir, graphUserId, mail) {
14
+ new TokenStore("acct-A", dir, graphUserId).store("access", "refresh", 3600, {
15
+ graphUserId,
16
+ mail,
17
+ scopes: ["Mail.ReadWrite", "Mail.ReadWrite.Shared"],
18
+ });
19
+ }
20
+ test("a delegated address resolves to the delegator's store and the shared prefix", () => {
21
+ const { dir, cleanup } = makeAccountsDir();
22
+ try {
23
+ connect(dir, "user-1", "admin@muvin.co.uk");
24
+ new DelegatedStore("acct-A", dir).add("team@muvin.co.uk", "user-1", 1);
25
+ const resolved = new MailboxRegistry("acct-A", dir).resolve("team@muvin.co.uk");
26
+ assert.equal(resolved.kind, "ok");
27
+ if (resolved.kind !== "ok")
28
+ return;
29
+ assert.equal(resolved.pathPrefix, "/users/team%40muvin.co.uk");
30
+ assert.equal(resolved.via, "admin@muvin.co.uk");
31
+ assert.equal(resolved.delegatedAddress, "team@muvin.co.uk");
32
+ assert.equal(resolved.store.read().mail, "admin@muvin.co.uk");
33
+ }
34
+ finally {
35
+ cleanup();
36
+ }
37
+ });
38
+ test("a delegated address resolves case-insensitively", () => {
39
+ const { dir, cleanup } = makeAccountsDir();
40
+ try {
41
+ connect(dir, "user-1", "admin@muvin.co.uk");
42
+ new DelegatedStore("acct-A", dir).add("team@muvin.co.uk", "user-1", 1);
43
+ const resolved = new MailboxRegistry("acct-A", dir).resolve("TEAM@Muvin.CO.UK");
44
+ assert.equal(resolved.kind, "ok");
45
+ if (resolved.kind !== "ok")
46
+ return;
47
+ assert.equal(resolved.pathPrefix, "/users/team%40muvin.co.uk");
48
+ }
49
+ finally {
50
+ cleanup();
51
+ }
52
+ });
53
+ test("a signed-in mailbox still resolves with the own-mailbox prefix", () => {
54
+ const { dir, cleanup } = makeAccountsDir();
55
+ try {
56
+ connect(dir, "user-1", "admin@muvin.co.uk");
57
+ const resolved = new MailboxRegistry("acct-A", dir).resolve("admin@muvin.co.uk");
58
+ assert.equal(resolved.kind, "ok");
59
+ if (resolved.kind !== "ok")
60
+ return;
61
+ assert.equal(resolved.pathPrefix, "/me");
62
+ assert.equal(resolved.via, null);
63
+ assert.equal(resolved.delegatedAddress, null);
64
+ }
65
+ finally {
66
+ cleanup();
67
+ }
68
+ });
69
+ test("a delegated record never becomes the sole-mailbox default", () => {
70
+ const { dir, cleanup } = makeAccountsDir();
71
+ try {
72
+ connect(dir, "user-1", "admin@muvin.co.uk");
73
+ new DelegatedStore("acct-A", dir).add("team@muvin.co.uk", "user-1", 1);
74
+ const resolved = new MailboxRegistry("acct-A", dir).resolve();
75
+ assert.equal(resolved.kind, "ok");
76
+ if (resolved.kind !== "ok")
77
+ return;
78
+ assert.equal(resolved.pathPrefix, "/me");
79
+ assert.equal(resolved.via, null);
80
+ }
81
+ finally {
82
+ cleanup();
83
+ }
84
+ });
85
+ test("a delegated record does not make a single-mailbox account ambiguous", () => {
86
+ const { dir, cleanup } = makeAccountsDir();
87
+ try {
88
+ connect(dir, "user-1", "admin@muvin.co.uk");
89
+ new DelegatedStore("acct-A", dir).add("team@muvin.co.uk", "user-1", 1);
90
+ new DelegatedStore("acct-A", dir).add("info@muvin.co.uk", "user-1", 2);
91
+ assert.equal(new MailboxRegistry("acct-A", dir).resolve().kind, "ok");
92
+ }
93
+ finally {
94
+ cleanup();
95
+ }
96
+ });
97
+ test("a delegated record whose delegator is gone refuses by naming the delegator", () => {
98
+ const { dir, cleanup } = makeAccountsDir();
99
+ try {
100
+ connect(dir, "user-1", "admin@muvin.co.uk");
101
+ new DelegatedStore("acct-A", dir).add("team@muvin.co.uk", "user-gone", 1);
102
+ const resolved = new MailboxRegistry("acct-A", dir).resolve("team@muvin.co.uk");
103
+ assert.equal(resolved.kind, "delegator-missing");
104
+ if (resolved.kind !== "delegator-missing")
105
+ return;
106
+ assert.equal(resolved.via, "user-gone");
107
+ assert.equal(resolved.selector, "team@muvin.co.uk");
108
+ }
109
+ finally {
110
+ cleanup();
111
+ }
112
+ });
113
+ test("an unknown address lists the delegated addresses too", () => {
114
+ const { dir, cleanup } = makeAccountsDir();
115
+ try {
116
+ connect(dir, "user-1", "admin@muvin.co.uk");
117
+ new DelegatedStore("acct-A", dir).add("team@muvin.co.uk", "user-1", 1);
118
+ const resolved = new MailboxRegistry("acct-A", dir).resolve("nobody@muvin.co.uk");
119
+ assert.equal(resolved.kind, "notfound");
120
+ if (resolved.kind !== "notfound")
121
+ return;
122
+ assert.deepEqual(resolved.identities.sort(), [
123
+ "admin@muvin.co.uk",
124
+ "team@muvin.co.uk (delegated)",
125
+ ]);
126
+ }
127
+ finally {
128
+ cleanup();
129
+ }
130
+ });
131
+ test("cred-resolve names the kind and the delegator", () => {
132
+ const { dir, cleanup } = makeAccountsDir();
133
+ try {
134
+ connect(dir, "user-1", "admin@muvin.co.uk");
135
+ new DelegatedStore("acct-A", dir).add("team@muvin.co.uk", "user-1", 1);
136
+ const registry = new MailboxRegistry("acct-A", dir);
137
+ const delegated = credResolveEvent(registry.resolve("team@muvin.co.uk"), "acct-A", "team@muvin.co.uk");
138
+ assert.equal(delegated.kind, "delegated");
139
+ assert.equal(delegated.via, "admin@muvin.co.uk");
140
+ assert.equal(delegated.mailbox, "team@muvin.co.uk");
141
+ assert.equal(delegated.source, "arg");
142
+ const own = credResolveEvent(registry.resolve(), "acct-A");
143
+ assert.equal(own.kind, "signed-in");
144
+ assert.equal(own.via, "");
145
+ assert.equal(own.source, "sole");
146
+ }
147
+ finally {
148
+ cleanup();
149
+ }
150
+ });
151
+ //# sourceMappingURL=delegated-resolve.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegated-resolve.test.js","sourceRoot":"","sources":["../../src/__tests__/delegated-resolve.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=delegated-store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegated-store.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/delegated-store.test.ts"],"names":[],"mappings":""}