@rubytech/create-maxy-code 0.1.533 → 0.1.535

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 (234) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -1
  3. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +13 -0
  4. package/payload/platform/plugins/docs/references/admin-ui.md +2 -0
  5. package/payload/platform/plugins/email/mcp/dist/__tests__/signature.test.js +40 -1
  6. package/payload/platform/plugins/email/mcp/dist/__tests__/signature.test.js.map +1 -1
  7. package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts.map +1 -1
  8. package/payload/platform/plugins/email/mcp/dist/lib/compose.js +4 -1
  9. package/payload/platform/plugins/email/mcp/dist/lib/compose.js.map +1 -1
  10. package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts +27 -0
  11. package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts.map +1 -1
  12. package/payload/platform/plugins/email/mcp/dist/lib/signature.js +62 -2
  13. package/payload/platform/plugins/email/mcp/dist/lib/signature.js.map +1 -1
  14. package/payload/platform/plugins/memory/skills/preference-audit/SKILL.md +11 -1
  15. package/payload/platform/plugins/whatsapp/PLUGIN.md +10 -2
  16. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +41 -3
  17. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  18. package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +17 -1
  19. package/payload/platform/scripts/__tests__/vnc-cdp-liveness.test.sh +14 -12
  20. package/payload/platform/scripts/vnc.sh +24 -19
  21. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  22. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  23. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  24. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  25. package/payload/platform/services/claude-session-manager/dist/http-server.js +35 -1
  26. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  27. package/payload/platform/services/claude-session-manager/dist/index.js +13 -4
  28. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  29. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts +1 -1
  30. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -1
  31. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js +46 -3
  32. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map +1 -1
  33. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +9 -0
  34. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  35. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +7 -0
  36. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  37. package/payload/platform/services/claude-session-manager/dist/specialist-roster.d.ts +12 -0
  38. package/payload/platform/services/claude-session-manager/dist/specialist-roster.d.ts.map +1 -0
  39. package/payload/platform/services/claude-session-manager/dist/specialist-roster.js +75 -0
  40. package/payload/platform/services/claude-session-manager/dist/specialist-roster.js.map +1 -0
  41. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +5 -0
  42. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  43. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +10 -2
  44. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  45. package/payload/server/{chunk-AJC3SQSF.js → chunk-4QCYFTAI.js} +198 -8
  46. package/payload/server/{manager-PLRHVALC.js → manager-ZNHQQCED.js} +1 -1
  47. package/payload/server/public/activity.html +5 -5
  48. package/payload/server/public/agents.html +4 -4
  49. package/payload/server/public/assets/AdminLoginScreens-DxjuTFjL.js +1 -0
  50. package/payload/server/public/assets/AdminLoginScreens-DxjuTFjL.js.br +0 -0
  51. package/payload/server/public/assets/AdminLoginScreens-DxjuTFjL.js.gz +0 -0
  52. package/payload/server/public/assets/AdminShell-B6H3J5C2.js +2 -0
  53. package/payload/server/public/assets/AdminShell-B6H3J5C2.js.br +0 -0
  54. package/payload/server/public/assets/AdminShell-B6H3J5C2.js.gz +0 -0
  55. package/payload/server/public/assets/CdpScreencastSurface-BRSv89v-.js +1 -0
  56. package/payload/server/public/assets/CdpScreencastSurface-BRSv89v-.js.br +0 -0
  57. package/payload/server/public/assets/CdpScreencastSurface-BRSv89v-.js.gz +0 -0
  58. package/payload/server/public/assets/{activity-0IcMRF5a.js → activity-DKeRUjQ9.js} +1 -1
  59. package/payload/server/public/assets/activity-DKeRUjQ9.js.br +0 -0
  60. package/payload/server/public/assets/activity-DKeRUjQ9.js.gz +0 -0
  61. package/payload/server/public/assets/admin-BxRWjeyZ.js +1 -0
  62. package/payload/server/public/assets/admin-BxRWjeyZ.js.br +0 -0
  63. package/payload/server/public/assets/admin-BxRWjeyZ.js.gz +0 -0
  64. package/payload/server/public/assets/{agents-xjw-fJjo.js → agents-CrR_OMe8.js} +1 -1
  65. package/payload/server/public/assets/agents-CrR_OMe8.js.br +0 -0
  66. package/payload/server/public/assets/agents-CrR_OMe8.js.gz +0 -0
  67. package/payload/server/public/assets/{browser-ByWKTnUQ.js → browser-sfwnP1kH.js} +1 -1
  68. package/payload/server/public/assets/browser-sfwnP1kH.js.br +0 -0
  69. package/payload/server/public/assets/browser-sfwnP1kH.js.gz +0 -0
  70. package/payload/server/public/assets/{calendar-DX2tE1B2.js → calendar-ByLH0Q5p.js} +1 -1
  71. package/payload/server/public/assets/calendar-ByLH0Q5p.js.br +0 -0
  72. package/payload/server/public/assets/calendar-ByLH0Q5p.js.gz +0 -0
  73. package/payload/server/public/assets/chat-C3xYZlGo.js +1 -0
  74. package/payload/server/public/assets/chat-C3xYZlGo.js.br +2 -0
  75. package/payload/server/public/assets/chat-C3xYZlGo.js.gz +0 -0
  76. package/payload/server/public/assets/chevron-left-C0HG8MBj.js +1 -0
  77. package/payload/server/public/assets/chevron-right-B49Xpptm.js +1 -0
  78. package/payload/server/public/assets/chevron-right-B49Xpptm.js.br +0 -0
  79. package/payload/server/public/assets/clock-CJq9vvt8.js +1 -0
  80. package/payload/server/public/assets/clock-CJq9vvt8.js.br +0 -0
  81. package/payload/server/public/assets/clock-CJq9vvt8.js.gz +0 -0
  82. package/payload/server/public/assets/data-DmeTKd-P.js +1 -0
  83. package/payload/server/public/assets/data-DmeTKd-P.js.br +0 -0
  84. package/payload/server/public/assets/data-DmeTKd-P.js.gz +0 -0
  85. package/payload/server/public/assets/{file-download-ChX5qAB2.js → file-download-BWe9Muoh.js} +1 -1
  86. package/payload/server/public/assets/file-download-BWe9Muoh.js.br +0 -0
  87. package/payload/server/public/assets/file-download-BWe9Muoh.js.gz +0 -0
  88. package/payload/server/public/assets/{file-text-DziWy8hF.js → file-text-5osg-EVb.js} +1 -1
  89. package/payload/server/public/assets/file-text-5osg-EVb.js.br +0 -0
  90. package/payload/server/public/assets/file-text-5osg-EVb.js.gz +0 -0
  91. package/payload/server/public/assets/{graph-Ci8RmSp3.js → graph-CAUo1OTY.js} +2 -2
  92. package/payload/server/public/assets/graph-CAUo1OTY.js.br +0 -0
  93. package/payload/server/public/assets/graph-CAUo1OTY.js.gz +0 -0
  94. package/payload/server/public/assets/{graph-labels-JkRVzaVL.js → graph-labels-TJ-3ljQo.js} +1 -1
  95. package/payload/server/public/assets/graph-labels-TJ-3ljQo.js.br +0 -0
  96. package/payload/server/public/assets/graph-labels-TJ-3ljQo.js.gz +0 -0
  97. package/payload/server/public/assets/{maximize-2-DnINkTp0.js → maximize-2-S9W3q1O4.js} +1 -1
  98. package/payload/server/public/assets/maximize-2-S9W3q1O4.js.br +0 -0
  99. package/payload/server/public/assets/maximize-2-S9W3q1O4.js.gz +0 -0
  100. package/payload/server/public/assets/operator-CzOJfank.js +1 -0
  101. package/payload/server/public/assets/operator-CzOJfank.js.br +0 -0
  102. package/payload/server/public/assets/operator-CzOJfank.js.gz +0 -0
  103. package/payload/server/public/assets/page-BUFW-PWF.js +32 -0
  104. package/payload/server/public/assets/page-BUFW-PWF.js.br +0 -0
  105. package/payload/server/public/assets/page-BUFW-PWF.js.gz +0 -0
  106. package/payload/server/public/assets/page-jT4queFM.js +1 -0
  107. package/payload/server/public/assets/page-jT4queFM.js.br +0 -0
  108. package/payload/server/public/assets/page-jT4queFM.js.gz +0 -0
  109. package/payload/server/public/assets/public-Cx2ddU9a.js +1 -0
  110. package/payload/server/public/assets/public-Cx2ddU9a.js.br +0 -0
  111. package/payload/server/public/assets/public-Cx2ddU9a.js.gz +0 -0
  112. package/payload/server/public/assets/{rotate-ccw-CfaIgRAI.js → rotate-ccw-C2qSSBFA.js} +1 -1
  113. package/payload/server/public/assets/rotate-ccw-C2qSSBFA.js.br +0 -0
  114. package/payload/server/public/assets/rotate-ccw-C2qSSBFA.js.gz +0 -0
  115. package/payload/server/public/assets/{routines-BIlcD8sn.js → routines-80uEwjng.js} +1 -1
  116. package/payload/server/public/assets/routines-80uEwjng.js.br +0 -0
  117. package/payload/server/public/assets/routines-80uEwjng.js.gz +0 -0
  118. package/payload/server/public/assets/{skills-DGID2WqF.js → skills-CmsYXVd3.js} +1 -1
  119. package/payload/server/public/assets/skills-CmsYXVd3.js.br +0 -0
  120. package/payload/server/public/assets/skills-CmsYXVd3.js.gz +0 -0
  121. package/payload/server/public/assets/tasks-CFfvn-KE.js +3 -0
  122. package/payload/server/public/assets/tasks-CFfvn-KE.js.br +0 -0
  123. package/payload/server/public/assets/tasks-CFfvn-KE.js.gz +0 -0
  124. package/payload/server/public/assets/{time-entry-format-DNl35lg9.js → time-entry-format-BD99oR05.js} +1 -1
  125. package/payload/server/public/assets/time-entry-format-BD99oR05.js.br +0 -0
  126. package/payload/server/public/assets/time-entry-format-BD99oR05.js.gz +0 -0
  127. package/payload/server/public/assets/{triangle-alert-DI_VASJO.js → triangle-alert-CuRAK3jk.js} +1 -1
  128. package/payload/server/public/assets/triangle-alert-CuRAK3jk.js.br +0 -0
  129. package/payload/server/public/assets/triangle-alert-CuRAK3jk.js.gz +0 -0
  130. package/payload/server/public/assets/{useCopyFeedback-Bu0ZQZQM.js → useCopyFeedback-BGzzIsSD.js} +1 -1
  131. package/payload/server/public/assets/useCopyFeedback-BGzzIsSD.js.br +0 -0
  132. package/payload/server/public/assets/useCopyFeedback-BGzzIsSD.js.gz +0 -0
  133. package/payload/server/public/assets/{useSubAccountSwitcher-CZBZ3iV8.css → useSubAccountSwitcher-BDQ8Wsdt.css} +1 -1
  134. package/payload/server/public/assets/useSubAccountSwitcher-BDQ8Wsdt.css.br +0 -0
  135. package/payload/server/public/assets/{useSubAccountSwitcher-CZBZ3iV8.css.gz → useSubAccountSwitcher-BDQ8Wsdt.css.gz} +0 -0
  136. package/payload/server/public/assets/useSubAccountSwitcher-D1VtIEp2.js +14 -0
  137. package/payload/server/public/assets/useSubAccountSwitcher-D1VtIEp2.js.br +0 -0
  138. package/payload/server/public/assets/useSubAccountSwitcher-D1VtIEp2.js.gz +0 -0
  139. package/payload/server/public/assets/useVoiceRecorder-fpwWw2VW.js +2 -0
  140. package/payload/server/public/assets/useVoiceRecorder-fpwWw2VW.js.br +0 -0
  141. package/payload/server/public/assets/useVoiceRecorder-fpwWw2VW.js.gz +0 -0
  142. package/payload/server/public/assets/{wrench-p4gW_ImW.js → wrench-DxSsCrue.js} +1 -1
  143. package/payload/server/public/assets/wrench-DxSsCrue.js.br +2 -0
  144. package/payload/server/public/assets/wrench-DxSsCrue.js.gz +0 -0
  145. package/payload/server/public/browser.html +5 -5
  146. package/payload/server/public/calendar.html +7 -7
  147. package/payload/server/public/chat.html +13 -13
  148. package/payload/server/public/data.html +12 -12
  149. package/payload/server/public/graph.html +9 -9
  150. package/payload/server/public/index.html +16 -16
  151. package/payload/server/public/operator.html +15 -15
  152. package/payload/server/public/public.html +13 -13
  153. package/payload/server/public/routines.html +7 -7
  154. package/payload/server/public/skills.html +5 -5
  155. package/payload/server/public/tasks.html +6 -6
  156. package/payload/server/server.js +460 -28
  157. package/payload/server/public/assets/AdminLoginScreens-CGRty7xT.js +0 -1
  158. package/payload/server/public/assets/AdminLoginScreens-CGRty7xT.js.br +0 -0
  159. package/payload/server/public/assets/AdminLoginScreens-CGRty7xT.js.gz +0 -0
  160. package/payload/server/public/assets/AdminShell-BQy9Ob22.js +0 -2
  161. package/payload/server/public/assets/AdminShell-BQy9Ob22.js.br +0 -0
  162. package/payload/server/public/assets/AdminShell-BQy9Ob22.js.gz +0 -0
  163. package/payload/server/public/assets/CdpScreencastSurface-BrDZji8F.js +0 -1
  164. package/payload/server/public/assets/CdpScreencastSurface-BrDZji8F.js.br +0 -0
  165. package/payload/server/public/assets/CdpScreencastSurface-BrDZji8F.js.gz +0 -0
  166. package/payload/server/public/assets/activity-0IcMRF5a.js.br +0 -0
  167. package/payload/server/public/assets/activity-0IcMRF5a.js.gz +0 -0
  168. package/payload/server/public/assets/admin-CKy-bmrs.js +0 -1
  169. package/payload/server/public/assets/admin-CKy-bmrs.js.br +0 -0
  170. package/payload/server/public/assets/admin-CKy-bmrs.js.gz +0 -0
  171. package/payload/server/public/assets/agents-xjw-fJjo.js.br +0 -0
  172. package/payload/server/public/assets/agents-xjw-fJjo.js.gz +0 -0
  173. package/payload/server/public/assets/browser-ByWKTnUQ.js.br +0 -0
  174. package/payload/server/public/assets/browser-ByWKTnUQ.js.gz +0 -0
  175. package/payload/server/public/assets/calendar-DX2tE1B2.js.br +0 -0
  176. package/payload/server/public/assets/calendar-DX2tE1B2.js.gz +0 -0
  177. package/payload/server/public/assets/chat-DkNDthRg.js +0 -1
  178. package/payload/server/public/assets/chat-DkNDthRg.js.br +0 -0
  179. package/payload/server/public/assets/chat-DkNDthRg.js.gz +0 -0
  180. package/payload/server/public/assets/chevron-left-CPwk14nY.js +0 -1
  181. package/payload/server/public/assets/chevron-right-Bz6INVTF.js +0 -1
  182. package/payload/server/public/assets/chevron-right-Bz6INVTF.js.br +0 -0
  183. package/payload/server/public/assets/clock-3YLNejcB.js +0 -1
  184. package/payload/server/public/assets/clock-3YLNejcB.js.br +0 -0
  185. package/payload/server/public/assets/clock-3YLNejcB.js.gz +0 -0
  186. package/payload/server/public/assets/data-DiDeaOUN.js +0 -1
  187. package/payload/server/public/assets/data-DiDeaOUN.js.br +0 -0
  188. package/payload/server/public/assets/data-DiDeaOUN.js.gz +0 -0
  189. package/payload/server/public/assets/file-download-ChX5qAB2.js.br +0 -0
  190. package/payload/server/public/assets/file-download-ChX5qAB2.js.gz +0 -0
  191. package/payload/server/public/assets/file-text-DziWy8hF.js.br +0 -0
  192. package/payload/server/public/assets/file-text-DziWy8hF.js.gz +0 -0
  193. package/payload/server/public/assets/graph-Ci8RmSp3.js.br +0 -0
  194. package/payload/server/public/assets/graph-Ci8RmSp3.js.gz +0 -0
  195. package/payload/server/public/assets/graph-labels-JkRVzaVL.js.br +0 -0
  196. package/payload/server/public/assets/graph-labels-JkRVzaVL.js.gz +0 -0
  197. package/payload/server/public/assets/maximize-2-DnINkTp0.js.br +0 -0
  198. package/payload/server/public/assets/maximize-2-DnINkTp0.js.gz +0 -0
  199. package/payload/server/public/assets/operator-CFOJBXCH.js +0 -1
  200. package/payload/server/public/assets/operator-CFOJBXCH.js.br +0 -0
  201. package/payload/server/public/assets/operator-CFOJBXCH.js.gz +0 -0
  202. package/payload/server/public/assets/page-BGv8bLm5.js +0 -32
  203. package/payload/server/public/assets/page-BGv8bLm5.js.br +0 -0
  204. package/payload/server/public/assets/page-BGv8bLm5.js.gz +0 -0
  205. package/payload/server/public/assets/page-DXTCBZRa.js +0 -1
  206. package/payload/server/public/assets/page-DXTCBZRa.js.br +0 -0
  207. package/payload/server/public/assets/page-DXTCBZRa.js.gz +0 -0
  208. package/payload/server/public/assets/public-yBY2wTJN.js +0 -1
  209. package/payload/server/public/assets/public-yBY2wTJN.js.br +0 -0
  210. package/payload/server/public/assets/public-yBY2wTJN.js.gz +0 -0
  211. package/payload/server/public/assets/rotate-ccw-CfaIgRAI.js.br +0 -0
  212. package/payload/server/public/assets/rotate-ccw-CfaIgRAI.js.gz +0 -0
  213. package/payload/server/public/assets/routines-BIlcD8sn.js.br +0 -0
  214. package/payload/server/public/assets/routines-BIlcD8sn.js.gz +0 -0
  215. package/payload/server/public/assets/skills-DGID2WqF.js.br +0 -0
  216. package/payload/server/public/assets/skills-DGID2WqF.js.gz +0 -0
  217. package/payload/server/public/assets/tasks-BukjwR7A.js +0 -3
  218. package/payload/server/public/assets/tasks-BukjwR7A.js.br +0 -0
  219. package/payload/server/public/assets/tasks-BukjwR7A.js.gz +0 -0
  220. package/payload/server/public/assets/time-entry-format-DNl35lg9.js.br +0 -0
  221. package/payload/server/public/assets/time-entry-format-DNl35lg9.js.gz +0 -0
  222. package/payload/server/public/assets/triangle-alert-DI_VASJO.js.br +0 -0
  223. package/payload/server/public/assets/triangle-alert-DI_VASJO.js.gz +0 -0
  224. package/payload/server/public/assets/useCopyFeedback-Bu0ZQZQM.js.br +0 -0
  225. package/payload/server/public/assets/useCopyFeedback-Bu0ZQZQM.js.gz +0 -0
  226. package/payload/server/public/assets/useSubAccountSwitcher-B6Ns2Z43.js +0 -14
  227. package/payload/server/public/assets/useSubAccountSwitcher-B6Ns2Z43.js.br +0 -0
  228. package/payload/server/public/assets/useSubAccountSwitcher-B6Ns2Z43.js.gz +0 -0
  229. package/payload/server/public/assets/useSubAccountSwitcher-CZBZ3iV8.css.br +0 -0
  230. package/payload/server/public/assets/useVoiceRecorder-CbVwXEjY.js +0 -2
  231. package/payload/server/public/assets/useVoiceRecorder-CbVwXEjY.js.br +0 -0
  232. package/payload/server/public/assets/useVoiceRecorder-CbVwXEjY.js.gz +0 -0
  233. package/payload/server/public/assets/wrench-p4gW_ImW.js.br +0 -0
  234. package/payload/server/public/assets/wrench-p4gW_ImW.js.gz +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.533",
3
+ "version": "0.1.535",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -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:f159a3720f378b27bb85e44a248bf4ebefe0f315e91cbddc815407e2ef3d6c16
4
+ content-hash: sha256:ff3dda12bf5ebb18d5b1b59af834831d11ba3f8de72e9ac781ed0080c27cd528
5
5
  brand: maxy-code
6
6
  product-name: Maxy
7
7
  ---
@@ -3361,6 +3361,8 @@ either is a regression.
3361
3361
 
3362
3362
  **WhatsApp reader is read/write (WhatsApp-Web surface).** A WhatsApp store conversation in the operator Conversations reader (`OperatorConversations`) and the admin root channel viewer (`app/page.tsx`) no longer opens the plain read-only DM chrome — it opens `app/whatsapp/WhatsappWebConversation.tsx`, a WhatsApp-Web-styled surface: a conversation header, the delivered-only store transcript reskinned green/white under a `.wa-web` root class, and a reply composer (`app/whatsapp/ReplyComposer.tsx`). The operator can type text, insert emoji (a codepoint-data picker — no OS input, so the `check-no-emoji-in-jsx` gate stays green), record a voice note (MediaRecorder → the existing audio send path), attach a file **from device storage** (a modal browsing the DATA_ROOT tree via `GET /api/admin/files`, sent by reference — the OS file dialog is no longer the reader's picker), reply to a specific message (a per-bubble affordance that quotes the original in the composer and threads natively on WhatsApp), and search the loaded transcript (a header control that filters to matching turns and highlights the match). The header carries only that Search control — the former Video / Phone / menu icons are gone, because no call or presence data exists behind them. Sent files honour the webchat allowlist (`SUPPORTED_MIME_SET` / 50 MB / 5 files); a device-file reference is authorised server-side (`authorizeReplyFileRef`) against the send's own account (the conversation owner) **or** the session's pinned account (`getAccountIdForSession` — the exact scope the DeviceFilePicker used to offer it), so a house/managing-admin session can attach a managed-client file the picker listed. Because a reply sends bytes OUT to an external WhatsApp recipient, any other account, a `public` partition, and any install-wide / non-account path are refused fail-closed; the cross-account admission's authority is the session's pinned account, never the caller-supplied body `accountId` (the own-account admission matches the body `accountId`, which the route already trusts via `isValidAccountId` and the existing-conversation guard). A scope refusal returns a distinct HTTP 403 reason (`file-ref-cross-account` / `file-ref-public` / `file-ref-foreign`) and copy, never "device file not found" (reserved for a genuine resolve/stat miss). Per-ref `[wa-manual-send] op=reply-file-ref … via=own|session|deny reason=<…> ok=<bool>` logs the decision before any send; `via=deny ok=true` is the exfiltration signature that must never appear. **Send is reply-only and DM-only:** `POST /api/whatsapp-reader/reply` (`requireAdminSession`) refuses any target with no existing stored conversation (`reason=no-existing-conversation`, HTTP 403) — a cold first-contact send bans the account, so the composer is never a cold-contact path, enforced server-side — and refuses `@g.us` groups (`reason=group-send-blocked`), matching the agent's observe-only group invariant; a group thread shows a read-only note in place of the composer. The endpoint is the sole store writer for a manual send: right after each send resolves it appends the record itself, stamped `origin:'operator-manual'` (attributed "You", distinct from an assistant reply), because the Baileys echo that normally persists the store is gated on message text (`manager.ts:906`) and would leave an uncaptioned media send delivered-but-invisible. The record's `messageId` matches the echo's derivation so the async text echo's own persist dedups to a no-op; the send is confirmed by re-read (`op=recorded present=<bool>`). A sent send does not spawn an assistant turn (echo dispatch stays suppressed). Attachments deliver as WhatsApp media/documents and render **inline** in the store reader (image thumbnail / pdf preview / audio player / download chip), because the manual-send path records the message with the servable `attachmentId`. The `.wa-web` skin is scoped to WhatsApp store threads only, so `/chat`, the admin agent session view, and the public visitor webchat are unchanged; the visitor webchat gains no send capability. The in-bubble delivered-tick is cosmetic (no read-receipt data is stored); inbound bubbles keep their sender name so group threads stay attributable, and both directions carry a WhatsApp callout tail (`.wa-web`-scoped only). Non-WhatsApp reader rows (Telegram/webchat session JSONLs) keep the plain read-only `Transcript`. Observability rides `[wa-manual-send]` lines correlated by a per-request `sendId`. **Composer sizing matches the webchat composer:** the reply textarea auto-grows to fit multi-line content (measured from `scrollHeight`, clamped to a one-row minimum and a `viewport/3` maximum, scrollable past the cap with the scrollbar hidden), and a top-border grab handle drags (pointer) or nudges (ArrowUp/ArrowDown, keyboard-focusable) to an explicit height in the same clamp; a manual drag pins the height and overrides auto-grow until the field is cleared, which releases it back to auto-grow. A completed manual resize logs `[operator-ui] op=wa-composer-resize mode=drag|key h=<px>`; a bare click on the handle (no drag) logs nothing.
3363
3363
 
3364
+ **Shared locations show a map.** When someone shares a location on WhatsApp, the conversation shows a small map with a pin on the spot, above the coordinates themselves. Selecting the map opens the full map on OpenStreetMap in a new tab. A location shared as a place name with no coordinates shows the name only, because there is no point to put a pin on. The map images are fetched by your browser from OpenStreetMap, so the shared coordinates reach that service when a location bubble is on screen.
3365
+
3364
3366
  **`/chat` live activity line.** While a turn is in flight the transcript tail shows one ephemeral `ChatActivity` line (mounted only while `busy`, admin/operator only). It tracks real activity, not a timer: while a `Task` subagent runs it shows that subagent's headline (`agentType · description`, prefixed `N agents ·` when ≥2 are concurrent), sourced from `agent-<hex>.meta.json` via named `activity` SSE events the admin reader pushes from the session's `subagents/` dir; with no subagent active it shows a neutral word that advances only on a real turn arrival. The line carries a turn-elapsed clock and flips to a `stalled` state once nothing has been written for 5 minutes (`now − lastEmitAt`), so the operator can tell a wedge from progress without SSH. It is never added to the persisted timeline. Detail and the `[webchat-activity]` observability live in [`admin-webchat-native-channel.md`](../../../.docs/admin-webchat-native-channel.md).
3365
3367
 
3366
3368
  **maxy title for public sessions.** A `role=public` webchat spawn never produces a useful Claude Code `ai-title` (an anonymous one-line visitor turn), so every public row would otherwise read identically. The webchat route (`chat.ts`) composes a deterministic title — `Web · <senderId[:8]>[ · <personId>] · <UTC YYYY-MM-DD HH:mm>` (personId present only for gated visitors) — and threads it through the native webchat gateway's public spawn (`handleInbound` → `buildPublicWebchatSpawnRequest` → `managerSpawn`) to the manager `POST /public-spawn` body as `name`. `/spawn` validates it with `validateUserTitle` and, for `role=public` only, writes it into `UserTitleStore` so it occupies the operator-rename tier and wins over `ai-title`. Admin (`/rc-spawn`) and WhatsApp titling are unchanged. Observability: every public spawn logs `[spawn] role=public … title="…"` (or `title=missing`); the manager's row builder emits `[public-title] sessionId=… unexpected titleSource=<ai|null>` on the next list read for any public row that did not resolve from the user tier.
@@ -9,6 +9,19 @@ 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-31 (0.1.535)
13
+
14
+ - You can type a full stop and other punctuation into the sign-in screen. Before this, a full stop deleted the character next to it instead of typing, so an email address could not be entered.
15
+ - You can paste into the sign-in screen, so an email address can be pasted rather than typed out.
16
+
17
+ ## 2026-07-31 (0.1.534)
18
+
19
+ - A shared WhatsApp location now shows as a map with its coordinates, and the assistant can follow a live location as it updates.
20
+ - You can register a named party, such as a supplier or subcontractor, so their messages are recognised and they can be messaged directly.
21
+ - The assistant can notify an account's manager, with the recipients worked out on the server rather than supplied by the caller.
22
+ - Asking about a WhatsApp conversation without naming an account now means your own account rather than the main one.
23
+ - Signing in now runs on the device's own display, fixing a sign-in that could not complete.
24
+
12
25
  ## 2026-07-31 (0.1.533)
13
26
 
14
27
  - WhatsApp sending now goes out on the account's own connection, and settings changes apply to the account making them rather than always the main one. Each outbound line names the account it went out on.
@@ -83,6 +83,8 @@ either is a regression.
83
83
 
84
84
  **WhatsApp reader is read/write (WhatsApp-Web surface).** A WhatsApp store conversation in the operator Conversations reader (`OperatorConversations`) and the admin root channel viewer (`app/page.tsx`) no longer opens the plain read-only DM chrome — it opens `app/whatsapp/WhatsappWebConversation.tsx`, a WhatsApp-Web-styled surface: a conversation header, the delivered-only store transcript reskinned green/white under a `.wa-web` root class, and a reply composer (`app/whatsapp/ReplyComposer.tsx`). The operator can type text, insert emoji (a codepoint-data picker — no OS input, so the `check-no-emoji-in-jsx` gate stays green), record a voice note (MediaRecorder → the existing audio send path), attach a file **from device storage** (a modal browsing the DATA_ROOT tree via `GET /api/admin/files`, sent by reference — the OS file dialog is no longer the reader's picker), reply to a specific message (a per-bubble affordance that quotes the original in the composer and threads natively on WhatsApp), and search the loaded transcript (a header control that filters to matching turns and highlights the match). The header carries only that Search control — the former Video / Phone / menu icons are gone, because no call or presence data exists behind them. Sent files honour the webchat allowlist (`SUPPORTED_MIME_SET` / 50 MB / 5 files); a device-file reference is authorised server-side (`authorizeReplyFileRef`) against the send's own account (the conversation owner) **or** the session's pinned account (`getAccountIdForSession` — the exact scope the DeviceFilePicker used to offer it), so a house/managing-admin session can attach a managed-client file the picker listed. Because a reply sends bytes OUT to an external WhatsApp recipient, any other account, a `public` partition, and any install-wide / non-account path are refused fail-closed; the cross-account admission's authority is the session's pinned account, never the caller-supplied body `accountId` (the own-account admission matches the body `accountId`, which the route already trusts via `isValidAccountId` and the existing-conversation guard). A scope refusal returns a distinct HTTP 403 reason (`file-ref-cross-account` / `file-ref-public` / `file-ref-foreign`) and copy, never "device file not found" (reserved for a genuine resolve/stat miss). Per-ref `[wa-manual-send] op=reply-file-ref … via=own|session|deny reason=<…> ok=<bool>` logs the decision before any send; `via=deny ok=true` is the exfiltration signature that must never appear. **Send is reply-only and DM-only:** `POST /api/whatsapp-reader/reply` (`requireAdminSession`) refuses any target with no existing stored conversation (`reason=no-existing-conversation`, HTTP 403) — a cold first-contact send bans the account, so the composer is never a cold-contact path, enforced server-side — and refuses `@g.us` groups (`reason=group-send-blocked`), matching the agent's observe-only group invariant; a group thread shows a read-only note in place of the composer. The endpoint is the sole store writer for a manual send: right after each send resolves it appends the record itself, stamped `origin:'operator-manual'` (attributed "You", distinct from an assistant reply), because the Baileys echo that normally persists the store is gated on message text (`manager.ts:906`) and would leave an uncaptioned media send delivered-but-invisible. The record's `messageId` matches the echo's derivation so the async text echo's own persist dedups to a no-op; the send is confirmed by re-read (`op=recorded present=<bool>`). A sent send does not spawn an assistant turn (echo dispatch stays suppressed). Attachments deliver as WhatsApp media/documents and render **inline** in the store reader (image thumbnail / pdf preview / audio player / download chip), because the manual-send path records the message with the servable `attachmentId`. The `.wa-web` skin is scoped to WhatsApp store threads only, so `/chat`, the admin agent session view, and the public visitor webchat are unchanged; the visitor webchat gains no send capability. The in-bubble delivered-tick is cosmetic (no read-receipt data is stored); inbound bubbles keep their sender name so group threads stay attributable, and both directions carry a WhatsApp callout tail (`.wa-web`-scoped only). Non-WhatsApp reader rows (Telegram/webchat session JSONLs) keep the plain read-only `Transcript`. Observability rides `[wa-manual-send]` lines correlated by a per-request `sendId`. **Composer sizing matches the webchat composer:** the reply textarea auto-grows to fit multi-line content (measured from `scrollHeight`, clamped to a one-row minimum and a `viewport/3` maximum, scrollable past the cap with the scrollbar hidden), and a top-border grab handle drags (pointer) or nudges (ArrowUp/ArrowDown, keyboard-focusable) to an explicit height in the same clamp; a manual drag pins the height and overrides auto-grow until the field is cleared, which releases it back to auto-grow. A completed manual resize logs `[operator-ui] op=wa-composer-resize mode=drag|key h=<px>`; a bare click on the handle (no drag) logs nothing.
85
85
 
86
+ **Shared locations show a map.** When someone shares a location on WhatsApp, the conversation shows a small map with a pin on the spot, above the coordinates themselves. Selecting the map opens the full map on OpenStreetMap in a new tab. A location shared as a place name with no coordinates shows the name only, because there is no point to put a pin on. The map images are fetched by your browser from OpenStreetMap, so the shared coordinates reach that service when a location bubble is on screen.
87
+
86
88
  **`/chat` live activity line.** While a turn is in flight the transcript tail shows one ephemeral `ChatActivity` line (mounted only while `busy`, admin/operator only). It tracks real activity, not a timer: while a `Task` subagent runs it shows that subagent's headline (`agentType · description`, prefixed `N agents ·` when ≥2 are concurrent), sourced from `agent-<hex>.meta.json` via named `activity` SSE events the admin reader pushes from the session's `subagents/` dir; with no subagent active it shows a neutral word that advances only on a real turn arrival. The line carries a turn-elapsed clock and flips to a `stalled` state once nothing has been written for 5 minutes (`now − lastEmitAt`), so the operator can tell a wedge from progress without SSH. It is never added to the persisted timeline. Detail and the `[webchat-activity]` observability live in [`admin-webchat-native-channel.md`](../../../.docs/admin-webchat-native-channel.md).
87
89
 
88
90
  **maxy title for public sessions.** A `role=public` webchat spawn never produces a useful Claude Code `ai-title` (an anonymous one-line visitor turn), so every public row would otherwise read identically. The webchat route (`chat.ts`) composes a deterministic title — `Web · <senderId[:8]>[ · <personId>] · <UTC YYYY-MM-DD HH:mm>` (personId present only for gated visitors) — and threads it through the native webchat gateway's public spawn (`handleInbound` → `buildPublicWebchatSpawnRequest` → `managerSpawn`) to the manager `POST /public-spawn` body as `name`. `/spawn` validates it with `validateUserTitle` and, for `role=public` only, writes it into `UserTitleStore` so it occupies the operator-rename tier and wins over `ai-title`. Admin (`/rc-spawn`) and WhatsApp titling are unchanged. Observability: every public spawn logs `[spawn] role=public … title="…"` (or `title=missing`); the manager's row builder emits `[public-title] sessionId=… unexpected titleSource=<ai|null>` on the next list read for any public row that did not resolve from the user tier.
@@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
2
  import { mkdtempSync, writeFileSync, rmSync, realpathSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
- import { loadSignature, applySignature, htmlFromPlainText, formatSignatureNote, } from '../lib/signature.js';
5
+ import { loadSignature, applySignature, htmlFromPlainText, formatSignatureNote, rootTypography, } from '../lib/signature.js';
6
6
  describe('Task 1952 — email signature load + apply', () => {
7
7
  let dir = '';
8
8
  beforeEach(() => { dir = realpathSync(mkdtempSync(join(tmpdir(), 'sig-'))); });
@@ -129,4 +129,43 @@ describe('Task 2193 — htmlFromPlainText', () => {
129
129
  expect(htmlFromPlainText('')).toBe('<p></p>');
130
130
  });
131
131
  });
132
+ // Task 2237 — the wrap div carried font-family alone, taken from
133
+ // email-signature.font. The GLS asset styles its own text at 12px/#000000, so a
134
+ // derived body rendered at the client default (~16px) beside a 12px signature:
135
+ // the mismatch the operator flagged three times. STYLED_ASSET is that asset's
136
+ // real root element, verbatim from the account directory.
137
+ const STYLED_ASSET = '<table cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:1.4;color:#000000;">\n<tr><td>Regards,</td></tr></table>';
138
+ describe('Task 2237 — rootTypography', () => {
139
+ it('copies only the four typography properties, dropping border-collapse', () => {
140
+ expect(rootTypography(STYLED_ASSET)).toBe('font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:1.4;color:#000000');
141
+ });
142
+ it('returns null when the root declares no style', () => {
143
+ expect(rootTypography('<table><tr><td>hi</td></tr></table>')).toBeNull();
144
+ });
145
+ it('returns null when the style carries no typography', () => {
146
+ expect(rootTypography('<div style="border-collapse:collapse;">x</div>')).toBeNull();
147
+ });
148
+ it('returns null for an absent asset', () => {
149
+ expect(rootTypography(null)).toBeNull();
150
+ });
151
+ });
152
+ describe('Task 2237 — applySignature wrap', () => {
153
+ it("wraps a derived body in the asset's own typography", () => {
154
+ const out = applySignature({ body: 'Hello' }, { text: null, html: STYLED_ASSET, font: 'Arial, Helvetica, sans-serif' });
155
+ expect(out.html).toContain('<div style="font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:1.4;color:#000000">');
156
+ expect(out.derivedHtml).toBe(true);
157
+ expect(out.wrap).toEqual({ family: true, size: true, lineHeight: true, color: true, assetStyled: true });
158
+ });
159
+ it('falls back to family-only when the asset declares no root style', () => {
160
+ const out = applySignature({ body: 'Hello' }, { text: null, html: '<p>Regards</p>', font: 'Georgia, serif' });
161
+ expect(out.html).toContain('<div style="font-family: Georgia, serif">');
162
+ expect(out.wrap).toEqual({ family: true, size: false, lineHeight: false, color: false, assetStyled: false });
163
+ });
164
+ it('derives nothing and wraps nothing when no html signature is stored', () => {
165
+ const out = applySignature({ body: 'Hello' }, { text: 'Regards', html: null, font: 'Georgia, serif' });
166
+ expect(out.html).toBeUndefined();
167
+ expect(out.derivedHtml).toBe(false);
168
+ expect(out.wrap.assetStyled).toBe(false);
169
+ });
170
+ });
132
171
  //# sourceMappingURL=signature.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signature.test.js","sourceRoot":"","sources":["../../src/__tests__/signature.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signature.test.js","sourceRoot":"","sources":["../../src/__tests__/signature.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/lib/compose.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAkC,MAAM,WAAW,CAAC;AAG7E,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAC;AAExB,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CASxF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,CAAC,CAuExB"}
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/lib/compose.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAkC,MAAM,WAAW,CAAC;AAG7E,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAC;AAExB,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CASxF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,CAAC,CA0ExB"}
@@ -76,7 +76,10 @@ export async function composeDraft(config, password, accountId, params) {
76
76
  const assetNote = sigDir ? join(sigDir, SIGNATURE_BASENAME) : "none";
77
77
  // Task 2193 — the parts are reported separately. One flag set from either part
78
78
  // could not tell a plain-text draft from a branded one.
79
- console.error(`[email-draft] op=signature accountId=${accountId.slice(0, 8)} text=${signed.textApplied} html=${signed.htmlApplied} derivedHtml=${signed.derivedHtml} asset=${assetNote}`);
79
+ // Task 2237 the wrap fields are read back off the composed html, not the
80
+ // inputs. `assetStyled=true` with `wrapSize=false` is the font mismatch that
81
+ // shipped three times: a body at the client default beside a 12px signature.
82
+ console.error(`[email-draft] op=signature accountId=${accountId.slice(0, 8)} text=${signed.textApplied} html=${signed.htmlApplied} derivedHtml=${signed.derivedHtml} wrapFamily=${signed.wrap.family} wrapSize=${signed.wrap.size} wrapLineHeight=${signed.wrap.lineHeight} wrapColor=${signed.wrap.color} assetStyled=${signed.wrap.assetStyled} asset=${assetNote}`);
80
83
  const sendOptions = {
81
84
  to: params.to,
82
85
  subject: resolvedSubject,
@@ -1 +1 @@
1
- {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/lib/compose.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/lib/compose.ts"],"names":[],"mappings":"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"}
@@ -75,6 +75,32 @@ export interface SignatureFlags {
75
75
  * by the caller. */
76
76
  derivedHtml: boolean;
77
77
  }
78
+ /** Which typography the composed wrap actually carries. Task 2237 — measured on
79
+ * the emitted div, not inferred from the inputs, so the log cannot claim a
80
+ * property that never landed. */
81
+ export interface WrapFlags {
82
+ family: boolean;
83
+ size: boolean;
84
+ lineHeight: boolean;
85
+ color: boolean;
86
+ /** The stored `.html` declares a style attribute on its root element. */
87
+ assetStyled: boolean;
88
+ }
89
+ /**
90
+ * Task 2237 — the typography declared on the stored HTML signature's root
91
+ * element, as a CSS declaration list.
92
+ *
93
+ * Task 2193 wrapped a derived body in `font-family` alone, taken from
94
+ * `email-signature.font`. The GLS asset styles its own text at 12px/#000000, so
95
+ * a derived body rendered at the client default (~16px) beside a 12px signature
96
+ * — the mismatch the operator flagged three times. Copying the asset's own
97
+ * declarations makes body and signature agree by construction, for every
98
+ * account, without widening the `.font` contract to carry a size.
99
+ *
100
+ * Returns null when there is no asset, no root style attribute, or no
101
+ * typography in it; the caller then falls back to the family-only wrap.
102
+ */
103
+ export declare function rootTypography(html: string | null): string | null;
78
104
  /**
79
105
  * Append the signature to a draft's body/html deterministically, with no model
80
106
  * judgement. The plain-text signature is appended to body whenever present.
@@ -97,6 +123,7 @@ export declare function applySignature(input: {
97
123
  }, sig: Signature): {
98
124
  body: string;
99
125
  html?: string;
126
+ wrap: WrapFlags;
100
127
  } & SignatureFlags;
101
128
  /**
102
129
  * Task 2193 — the signature sentence both draft tools append to their result.
@@ -1 +1 @@
1
- {"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/lib/signature.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAUD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CASvE;AAED;uEACuE;AACvE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAQD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,cAAc,GACpB,mBAAmB,CA8BrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,WAAW,EAAE,OAAO,CAAC;IACrB,wDAAwD;IACxD,WAAW,EAAE,OAAO,CAAC;IACrB;yBACqB;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACtC,GAAG,EAAE,SAAS,GACb;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAwBlD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAcjE"}
1
+ {"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/lib/signature.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAUD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CASvE;AAED;uEACuE;AACvE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAQD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,cAAc,GACpB,mBAAmB,CA8BrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,WAAW,EAAE,OAAO,CAAC;IACrB,wDAAwD;IACxD,WAAW,EAAE,OAAO,CAAC;IACrB;yBACqB;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAOD;;kCAEkC;AAClC,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,yEAAyE;IACzE,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAgBjE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACtC,GAAG,EAAE,SAAS,GACb;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG,cAAc,CA4CnE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAcjE"}
@@ -112,6 +112,45 @@ export function htmlFromPlainText(text) {
112
112
  .map((paragraph) => `<p>${paragraph.replace(/\n/g, "<br>")}</p>`)
113
113
  .join("");
114
114
  }
115
+ /** The typography the wrap div may copy from the stored signature asset.
116
+ * An allowlist, not the whole style attribute: the real GLS asset root also
117
+ * declares `border-collapse:collapse`, which is layout, not typography. */
118
+ const TYPOGRAPHY_PROPERTIES = ["font-family", "font-size", "line-height", "color"];
119
+ /**
120
+ * Task 2237 — the typography declared on the stored HTML signature's root
121
+ * element, as a CSS declaration list.
122
+ *
123
+ * Task 2193 wrapped a derived body in `font-family` alone, taken from
124
+ * `email-signature.font`. The GLS asset styles its own text at 12px/#000000, so
125
+ * a derived body rendered at the client default (~16px) beside a 12px signature
126
+ * — the mismatch the operator flagged three times. Copying the asset's own
127
+ * declarations makes body and signature agree by construction, for every
128
+ * account, without widening the `.font` contract to carry a size.
129
+ *
130
+ * Returns null when there is no asset, no root style attribute, or no
131
+ * typography in it; the caller then falls back to the family-only wrap.
132
+ */
133
+ export function rootTypography(html) {
134
+ if (html === null)
135
+ return null;
136
+ const root = html.match(/<[a-zA-Z][^>]*\sstyle\s*=\s*"([^"]*)"/);
137
+ if (!root)
138
+ return null;
139
+ const kept = [];
140
+ for (const declaration of root[1].split(";")) {
141
+ const colon = declaration.indexOf(":");
142
+ if (colon < 0)
143
+ continue;
144
+ const property = declaration.slice(0, colon).trim().toLowerCase();
145
+ const value = declaration.slice(colon + 1).trim();
146
+ if (value.length === 0)
147
+ continue;
148
+ if (TYPOGRAPHY_PROPERTIES.includes(property)) {
149
+ kept.push(`${property}:${value}`);
150
+ }
151
+ }
152
+ return kept.length > 0 ? kept.join(";") : null;
153
+ }
115
154
  /**
116
155
  * Append the signature to a draft's body/html deterministically, with no model
117
156
  * judgement. The plain-text signature is appended to body whenever present.
@@ -134,6 +173,17 @@ export function applySignature(input, sig) {
134
173
  let textApplied = false;
135
174
  let htmlApplied = false;
136
175
  let derivedHtml = false;
176
+ // Task 2237 — the asset's own root typography wins over the family-only
177
+ // `.font` wrap, so body and signature render alike. `.font` stays the
178
+ // fallback for an asset that declares no root style.
179
+ const typography = rootTypography(sig.html);
180
+ const wrap = {
181
+ family: false,
182
+ size: false,
183
+ lineHeight: false,
184
+ color: false,
185
+ assetStyled: typography !== null,
186
+ };
137
187
  if (sig.text !== null) {
138
188
  body = `${body}\n\n${sig.text}`;
139
189
  textApplied = true;
@@ -147,11 +197,21 @@ export function applySignature(input, sig) {
147
197
  html = `${html}${sig.html}`;
148
198
  htmlApplied = true;
149
199
  }
150
- if (sig.font !== null) {
200
+ if (typography !== null) {
201
+ html = `<div style="${typography}">${html}</div>`;
202
+ // Read back off the string actually emitted. No allowlisted property
203
+ // contains another as a substring, so these matches are exact.
204
+ wrap.family = typography.includes("font-family:");
205
+ wrap.size = typography.includes("font-size:");
206
+ wrap.lineHeight = typography.includes("line-height:");
207
+ wrap.color = typography.includes("color:");
208
+ }
209
+ else if (sig.font !== null) {
151
210
  html = `<div style="font-family: ${sig.font}">${html}</div>`;
211
+ wrap.family = true;
152
212
  }
153
213
  }
154
- return { body, html, textApplied, htmlApplied, derivedHtml };
214
+ return { body, html, textApplied, htmlApplied, derivedHtml, wrap };
155
215
  }
156
216
  /**
157
217
  * Task 2193 — the signature sentence both draft tools append to their result.
@@ -1 +1 @@
1
- {"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/lib/signature.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/lib/signature.ts"],"names":[],"mappings":"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"}
@@ -38,7 +38,11 @@ A preference is invariant-class when its value describes a document layout, a na
38
38
 
39
39
  A preference is soft when it is a genuine standing choice with no owning skill to enforce it: a contact, a timezone, a one-invoice-per-message rule, a skip-size numeral, a no-proactive-reminders rule. Soft preferences stay in the injected store.
40
40
 
41
- For every invariant, the plan names the owning skill and whether that skill already applies it deterministically or a migration is still pending. A preference whose owning skill you cannot name is reported as unassigned rather than silently left in place. The email signature is the worked first instance: `email-draft` and `email-draft-edit` apply the account's saved signature asset deterministically, so its preference is retired once the asset is in place.
41
+ For every invariant, the plan names the owning skill and whether that skill already applies it deterministically or a migration is still pending. A preference whose owning skill you cannot name is reported as unassigned rather than silently left in place.
42
+
43
+ Naming an owner is not enough to retire a preference. Before retiring, confirm the named owner actually applies the rule's substance, and name the template, tool step or asset that enforces it. A preference whose owner is named but unverified is reported as `unassigned` and stays stored: an unenforced invariant deleted from the store is enforced nowhere at all.
44
+
45
+ The email signature is the worked instance, and it is narrower than it looks. `email-draft` and `email-draft-edit` apply the account's saved signature asset deterministically, so a preference restating the *signature's own* contents is retired once the asset is in place. A preference about the *body* the signature sits under — its font, size, line height or colour — has no such owner: the asset styles itself, not the message body. Retiring one of those deletes the only copy of the rule. That is not hypothetical: on 2026-07-24 this audit retired a confidence-1.0 body-font rule into a signature-template preference carrying no font content, and the mismatch it prevented shipped twice more before anyone noticed.
42
46
 
43
47
  ## Dry run first, always
44
48
 
@@ -57,6 +61,12 @@ Close every audit with one line, whether it was a dry run or applied:
57
61
 
58
62
  `[preference-audit] account=<id> reworded=<n> merged=<n> retired=<n> restated-to-capability=<n>`
59
63
 
64
+ Every retirement additionally emits one line, so the decision survives the deletion it describes:
65
+
66
+ `[preference-audit] op=retire account=<id8> key=<k> owner=<skill> ownerVerified=<bool>`
67
+
68
+ `ownerVerified=false` is a rule retired to an owner that does not apply it. That line is the alarm, and it is visible at the moment it happens rather than when someone notices the rule stopped being followed.
69
+
60
70
  Count what the plan proposes on a dry run, and what was applied on a real run.
61
71
 
62
72
  The classification pass emits its own line on the audit's periodic tick, so an invariant-shaped preference accumulating unmigrated surfaces on its own and not only on demand:
@@ -46,6 +46,10 @@ tools:
46
46
  publicAllowlist: false
47
47
  adminAllowlist: false
48
48
  riskClass: external
49
+ - name: whatsapp-notify-manager
50
+ publicAllowlist: true
51
+ adminAllowlist: true
52
+ riskClass: external
49
53
  - name: whatsapp-reply
50
54
  publicAllowlist: false
51
55
  adminAllowlist: false
@@ -106,7 +110,9 @@ A native admin channel session runs a per-sender `whatsapp-channel` MCP server (
106
110
 
107
111
  ## Agent-initiated admin send
108
112
 
109
- `whatsapp-send-admin` is the one outbound path that does **not** require a bound inbound conversation, so a webchat session or a scheduled run can push a text summary over WhatsApp. It is hard-gated to the cold-send-ban carve-out: the destination must already be a **registered recipient** — on the house `whatsapp.adminPhones` list (managed via `whatsapp-config add-admin-phone`) **or** a registered account manager in `whatsapp.accountManagers` (managed via `whatsapp-config add-account-manager`). Both lists are read from the socket-owning house account (`resolveAccount`), and the send rides the **calling session's own** paired socket: a sub-account session transmits on its own line, an unsigned caller falls back to the house line, and a session whose account has no socket gets a 503 naming the key miss rather than a silent fall-back to the house. A destination in neither list is refused with `[whatsapp:outbound] op=send-refused reason=not-admin-phone` and no send is attempted — never a cold first contact. An **account-manager** phone (in `accountManagers`, which may overlap `adminPhones` — a phone can hold both roles, and the number it messaged decides its inbound scope) **is** a valid `send-admin` destination regardless of its binding mode: the operator registered it, so it is not a cold stranger. `passive` governs the manager's **inbound** behaviour only (no auto-reply, intake-only) and never gates this outbound send. A successful send logs its admit basis and both accounts (`op=send-admin-ok via=admin-phone|account-manager callerAccount=… socketAccount=…`), and every `[whatsapp:outbound]` line carries `account=`. A send that carries no caller identity falls back to the house socket, emits `op=outbound-caller-unsigned` at WARN and increments `outboundSocketReconcile.unsignedFallbacks` on `GET /api/whatsapp/status`, because a send on the wrong line is otherwise a successful write with no event. The two accounts on the success line are for a human to read and are deliberately not compared: they are different namespaces, since the house connection's key is the credential dirname while the caller is a platform UUID. Delivery reuses `sendTextMessage`, so the sent id is tracked for echo suppression and the send is recorded as outbound activity. Text only; for files use the bound `reply-document` surface.
113
+ `whatsapp-send-admin` is the one outbound path that does **not** require a bound inbound conversation, so a webchat session or a scheduled run can push a text summary over WhatsApp. It is hard-gated to the cold-send-ban carve-out: the destination must already be a **registered recipient** — on the house `whatsapp.adminPhones` list (managed via `whatsapp-config add-admin-phone`), a registered account manager in `whatsapp.accountManagers` (managed via `whatsapp-config add-account-manager`), **or** a registered party in `whatsapp.registeredParties` (managed via `whatsapp-config add-registered-party`, logged `via=registered-party`). A registered party is admitted on the same grounds as an account manager: an operator-registered destination is not a cold stranger, and being public on the inbound path does not make an outbound send cold. Both lists are read from the socket-owning house account (`resolveAccount`), and the send rides the **calling session's own** paired socket: a sub-account session transmits on its own line, an unsigned caller falls back to the house line, and a session whose account has no socket gets a 503 naming the key miss rather than a silent fall-back to the house. A destination in neither list is refused with `[whatsapp:outbound] op=send-refused reason=not-admin-phone` and no send is attempted — never a cold first contact. An **account-manager** phone (in `accountManagers`, which may overlap `adminPhones` — a phone can hold both roles, and the number it messaged decides its inbound scope) **is** a valid `send-admin` destination regardless of its binding mode: the operator registered it, so it is not a cold stranger. `passive` governs the manager's **inbound** behaviour only (no auto-reply, intake-only) and never gates this outbound send. A successful send logs its admit basis and both accounts (`op=send-admin-ok via=admin-phone|account-manager callerAccount=… socketAccount=…`), and every `[whatsapp:outbound]` line carries `account=`. A send that carries no caller identity falls back to the house socket, emits `op=outbound-caller-unsigned` at WARN and increments `outboundSocketReconcile.unsignedFallbacks` on `GET /api/whatsapp/status`, because a send on the wrong line is otherwise a successful write with no event. The two accounts on the success line are for a human to read and are deliberately not compared: they are different namespaces, since the house connection's key is the credential dirname while the caller is a platform UUID. Delivery reuses `sendTextMessage`, so the sent id is tracked for echo suppression and the send is recorded as outbound activity. Text only; for files use the bound `reply-document` surface.
114
+
115
+ `whatsapp-notify-manager` is the only WhatsApp tool a **public** session may call. It exists so a session handling a stranger's message can tell the operator without being handed a general send. It takes **no recipient**: the destination is computed on the server as every `whatsapp.accountManagers` key whose bound account equals the calling session's own account, so text authored by an unknown sender can never choose or influence who is contacted. The caller supplies only `senderDisplay`, `summary` and `draftLocation`, which are placed verbatim into a fixed message shape and never interpreted. It refuses with `no-registered-manager` when no manager binds to the calling account, rather than falling back to the house admin list or the house socket, and the send rides the calling account's own paired socket exactly as `whatsapp-send-admin` does. This is precisely why `whatsapp-send-admin` stays `publicAllowlist: false`: it accepts a caller-chosen destination and a free-form body, which is the shape an injected instruction would exploit.
110
116
 
111
117
  ## Self-chat command lane
112
118
 
@@ -128,7 +134,9 @@ Every `messages.upsert` event (both `notify` and `append`, both `fromMe` directi
128
134
 
129
135
  **Sub-account recall.** Recall is account-scoped as a read-time projection: a sub-account session sees only the house-store threads whose counterparty is one of its bound account-manager phones (the same filter the operator dashboard uses, shared via `counterpartyBoundToSubAccount`). The write path and the store key are unchanged — messages stay house-keyed; "sub-account scope" is a projection, not a move. `whatsapp-conversation-graph-state` defaults its `accountId` to the calling session's own `ACCOUNT_ID`, so a sub-account recalls its own history with no argument while the house admin may still pass any account id explicitly. Body search is not restored by this projection (it has been absent since the on-disk store cutover).
130
136
 
131
- **Config action authority.** The three account-manager actions (`add-account-manager`, `remove-account-manager`, `list-account-managers`) are per-account **bindings** and scope to the caller: any session may bind a phone to its **own** account, `list` returns only that account's bindings, and naming another account is refused 403 with `reason: "binding-account-scope-mismatch"`. Removing a phone that holds no binding is refused for the same reason, because there is no binding value to prove the phone is yours. A house admin is unrestricted and unfiltered on all three. Every other action on this route is install-wide and stays house-only. The bindings are written to the house `account.json` regardless of who wrote them, because that is the file the inbound gate reads and `purgeNonSocketAccountManagersAtBoot` deletes a map written anywhere else.
137
+ **Three inbound sender classes.** A DM reaching a paired number is one of three things. An **operator** is a phone on the house `adminPhones` list or a registered account manager; both get an admin session. A **registered party** is a known non-operator counterparty — a driver, a supplier, a contractor — registered with `add-registered-party` against the account whose number they message; they get the **public** agent, carrying an operator-chosen `party` label, and are never granted admin. Anyone else is an unknown visitor and takes whatever the account's DM policy allows. The registration applies only on the registered account's **own** number: reached on the house number a registered party takes the house DM policy like any stranger, which is deliberate a counterparty must not reach a sub-account through the house line. The label rides to the spawned session's environment as `WA_SENDER_PARTY` and appears on the `[whatsapp:manager] inbound` line as `party=`; the platform never interprets it, so what a label means is the account's own skill to decide. A phone cannot hold a party registration **and** an admin or manager registration at once: either write is refused `party-registration-shadowed`, because the operator arms are checked first and the party registration would sit unreachable. A registration that is shadowed later out of band (a hand-edited `account.json`) emits `[whatsapp:manager] op=registered-party-shadowed` on the next inbound and is counted by `registeredPartyReconcile` on `GET /api/whatsapp/status`, alongside a registration naming a deleted account. Because a registered party is public, they inherit the two public-only dispatch gates: managed-service mode suppresses their turn, and a message outside business hours gets the after-hours auto-reply rather than a dispatch.
138
+
139
+ **Config action authority.** The three registered-party actions (`add-registered-party`, `remove-registered-party`, `list-registered-parties`) scope to the caller exactly as the three account-manager actions below do: any session may register a counterparty on its **own** account, `list` returns only that account's registrations, and naming another account is refused 403. The three account-manager actions (`add-account-manager`, `remove-account-manager`, `list-account-managers`) are per-account **bindings** and scope to the caller: any session may bind a phone to its **own** account, `list` returns only that account's bindings, and naming another account is refused 403 with `reason: "binding-account-scope-mismatch"`. Removing a phone that holds no binding is refused for the same reason, because there is no binding value to prove the phone is yours. A house admin is unrestricted and unfiltered on all three. Every other action on this route is install-wide and stays house-only. The bindings are written to the house `account.json` regardless of who wrote them, because that is the file the inbound gate reads and `purgeNonSocketAccountManagersAtBoot` deletes a map written anywhere else.
132
140
 
133
141
  **Caller-scope enforcement.** The three read tools — `whatsapp-conversation-graph-state`, `whatsapp-conversations`, `whatsapp-messages` — and `whatsapp-send-admin` forward the caller's own account (`ACCOUNT_ID`) and its house-admin scope (`HOUSE_ADMIN_SCOPE`) to the route as headers, distinct from the caller-controlled `accountId` argument. The route enforces caller-scope: a house admin reads any account; any other session (a sub-account, or a specialist under any account) may read only its own account and gets a 403 if it passes a different `accountId`. This closes the override where a sub-account could pass an explicit house or sibling id and read that store. It defends only the loopback route against the agent-controlled argument; the route still trusts localhost, so forging these headers on a direct loopback call remains possible — closing that is a separate, tracked follow-up.
134
142
 
@@ -172,6 +172,9 @@ lifelineTool(server, "whatsapp-config", [
172
172
  " - add-account-manager (bind a phone to a sub-account — its messages route to that sub-account; requires `phone` and `managesAccount`. `mode` selects the behaviour: 'active' (default) spawns a full admin session that can reply and manage the sub-account; 'passive' spawns a constrained requirement-intake agent that files exactly one task per message and never replies)",
173
173
  " - remove-account-manager (clear a phone's account-manager binding; requires `phone`)",
174
174
  " - list-account-managers (show phone → sub-account bindings)",
175
+ " - add-registered-party (register a known non-operator counterparty — a driver, a supplier, a contractor — on an account's own number; requires `phone`, `account` and `party`. Their messages to THAT account's number reach its PUBLIC agent carrying the label, and they are NOT granted admin. Use this, not add-account-manager, for anyone who is not an operator of the account)",
176
+ " - remove-registered-party (clear a phone's registered-party registration; requires `phone`)",
177
+ " - list-registered-parties (show phone → account and label)",
175
178
  " - set-public-agent (set the top-level public-agent slug for non-admin DMs; requires `slug`)",
176
179
  " - get-public-agent (show the resolved public-agent slug for DMs)",
177
180
  " - list-public-agents (slug and display name for each available public agent)",
@@ -185,6 +188,7 @@ lifelineTool(server, "whatsapp-config", [
185
188
  "Groups are observation-only — the agent never responds in a group, so there is no per-group config. list-groups still lists them for reference.",
186
189
  "Public-agent precedence at routing time (DMs): per-account default → top-level default.",
187
190
  "A phone may be a house admin AND a sub-account manager at the same time — both registrations are accepted, and the number the sender messaged decides which scope they get: the house number gives house scope, the sub-account's own number gives that sub-account's scope. A manager bound to a sub-account with no number of its own is reached through the house number. A manager phone is not reachable by whatsapp-send-admin (message it directly instead).",
191
+ "Three inbound sender classes exist: an OPERATOR (an admin phone or an account manager — admin session), a REGISTERED PARTY (a known counterparty — public session carrying its label), and an unknown visitor (whatever the DM policy allows). A registered party is never granted admin, and the registration applies only on the registered account's own number: on the house number they take the house DM policy like anyone else. A phone cannot hold a party registration and an admin or manager registration at the same time — either write is refused with reason 'party-registration-shadowed', because the operator arms are checked first and the party registration would never be reached.",
188
192
  "Phone numbers must be E.164 format (e.g. +441234567890).",
189
193
  ].join("\n"), {
190
194
  action: z.enum([
@@ -194,6 +198,9 @@ lifelineTool(server, "whatsapp-config", [
194
198
  "add-account-manager",
195
199
  "remove-account-manager",
196
200
  "list-account-managers",
201
+ "add-registered-party",
202
+ "remove-registered-party",
203
+ "list-registered-parties",
197
204
  "set-public-agent",
198
205
  "get-public-agent",
199
206
  "list-public-agents",
@@ -208,6 +215,8 @@ lifelineTool(server, "whatsapp-config", [
208
215
  phone: z.string().optional().describe("Phone number in E.164 format (required for add/remove admin phone and add/remove account manager)"),
209
216
  managesAccount: z.string().optional().describe("Sub-account UUID this phone should manage (required for add-account-manager). The sender's WhatsApp messages spawn a session scoped to that sub-account."),
210
217
  mode: z.enum(["active", "passive"]).optional().describe("Account-manager mode for add-account-manager (default 'active'). 'active' = a full admin session that can reply and manage the sub-account. 'passive' = a constrained requirement-intake agent: it interprets each message, files exactly one task scoped to the sub-account, and never replies."),
218
+ account: z.string().optional().describe("Account UUID whose own number this phone is a known counterparty on (required for add-registered-party). Their messages to THAT account's number reach its public agent; on the house number they take the house DM policy instead."),
219
+ party: z.string().optional().describe("Label for this counterparty (required for add-registered-party), e.g. 'driver'. Lowercase letters, digits and hyphens, 1-64 characters. The platform carries it to the session and never interprets it; the account's own skill decides what it means."),
211
220
  slug: z.string().optional().describe("Agent slug (required for set-public-agent)"),
212
221
  fields: z.string().optional().describe('JSON string of config fields to update (required for update-config, e.g. \'{"dmPolicy":"open","allowFrom":["*"]}\')'),
213
222
  accountId: z.string().optional().describe('Account ID. Defaults to the calling session’s own account; the house admin may pass another account id explicitly. Used by list-groups, set-dispatch-inbound, set-account-policy and get-account-policy.'),
@@ -217,7 +226,7 @@ lifelineTool(server, "whatsapp-config", [
217
226
  adminPhones: z.array(z.string()).optional(),
218
227
  allowFrom: z.array(z.string()).optional(),
219
228
  }).optional().describe("For set-account-policy: the fields to set on this account's own inbound policy. At least one required. dmPolicy 'open' requires allowFrom to contain '*' in the same call."),
220
- }, async ({ action, phone, managesAccount, mode, slug, fields, accountId, dispatchInbound, policy }) => {
229
+ }, async ({ action, phone, managesAccount, mode, account, party, slug, fields, accountId, dispatchInbound, policy }) => {
221
230
  try {
222
231
  // Parse fields JSON string for update-config
223
232
  let parsedFields;
@@ -229,7 +238,7 @@ lifelineTool(server, "whatsapp-config", [
229
238
  return textResult('Invalid JSON in "fields" parameter. Expected a JSON object, e.g. {"dmPolicy":"open","allowFrom":["*"]}', true);
230
239
  }
231
240
  }
232
- const result = await callApi("whatsapp-config", "/api/whatsapp/config", "POST", { action, phone, managesAccount, mode, slug, fields: parsedFields, accountId, dispatchInbound, policy }, callerScopeHeaders(process.env));
241
+ const result = await callApi("whatsapp-config", "/api/whatsapp/config", "POST", { action, phone, managesAccount, mode, account, party, slug, fields: parsedFields, accountId, dispatchInbound, policy }, callerScopeHeaders(process.env));
233
242
  if (!result.ok)
234
243
  return textResult(result.error ?? "Config operation failed.", true);
235
244
  // Task 2235 — an inherited value and a chosen one read identically, so the
@@ -265,6 +274,15 @@ lifelineTool(server, "whatsapp-config", [
265
274
  return ` ${p} → ${sub}${mgrMode === "passive" ? " (passive)" : ""}`;
266
275
  }).join("\n")}`);
267
276
  }
277
+ // Task 2234 — a registered party is a PUBLIC sender, so nothing else in a
278
+ // listing distinguishes them from a stranger. The label is what does.
279
+ if (action === "list-registered-parties") {
280
+ const parties = (result.registeredParties ?? {});
281
+ const entries = Object.entries(parties);
282
+ if (entries.length === 0)
283
+ return textResult("No registered parties. Use add-registered-party to register a known counterparty (a driver, a supplier) so they are distinguishable from a stranger.");
284
+ return textResult(`Registered parties:\n${entries.map(([p, v]) => ` ${p} → ${v.account} (${v.party})`).join("\n")}`);
285
+ }
268
286
  if (action === "get-public-agent") {
269
287
  const agentSlug = result.slug;
270
288
  const source = result.source;
@@ -304,7 +322,7 @@ lifelineTool(server, "whatsapp-config", [
304
322
  }
305
323
  });
306
324
  // ─── whatsapp-send-admin ──────────────────────────────────────────────
307
- lifelineTool(server, "whatsapp-send-admin", "Send a WhatsApp text message to a registered recipient — a phone on this install's admin-phone list, OR a registered account manager. Refuses any other number (the cold-send-ban guard — never cold-contact a stranger). Needs no bound inbound conversation, so use it to push a summary or notification over WhatsApp from a webchat session or a scheduled run. Register a recipient first with whatsapp-config (add-admin-phone or add-account-manager).", {
325
+ lifelineTool(server, "whatsapp-send-admin", "Send a WhatsApp text message to a registered recipient — a phone on this install's admin-phone list, a registered account manager, OR a registered party. Refuses any other number (the cold-send-ban guard — never cold-contact a stranger). Needs no bound inbound conversation, so use it to push a summary or notification over WhatsApp from a webchat session or a scheduled run. Register a recipient first with whatsapp-config (add-admin-phone, add-account-manager or add-registered-party).", {
308
326
  phone: z.string().describe("Destination in E.164 (e.g. +441234567890) — must already be a registered admin phone or account manager"),
309
327
  text: z.string().describe("Message body to send"),
310
328
  accountId: z.string().optional().describe('Ignored — the send rides the calling session\'s own WhatsApp socket, resolved from the session identity rather than from this argument. Present only for backward compatibility.'),
@@ -323,6 +341,26 @@ lifelineTool(server, "whatsapp-send-admin", "Send a WhatsApp text message to a r
323
341
  return textResult(`Send failed: ${err instanceof Error ? err.message : String(err)}`, true);
324
342
  }
325
343
  });
344
+ // ─── whatsapp-notify-manager ──────────────────────────────────────────
345
+ lifelineTool(server, "whatsapp-notify-manager", "Tell this account's registered account managers that an inbound WhatsApp message arrived and where the drafted reply is waiting. You do NOT choose the recipient: the destination is every account manager registered against your own account, resolved on the server. Use it after handling a message from someone who is not an admin and not a manager. Refuses when no account manager is registered for this account.", {
346
+ senderDisplay: z.string().describe("Who the message came from, as the manager should see it — a name, or the number"),
347
+ summary: z.string().describe("What arrived, in one or two sentences, in the manager's language"),
348
+ draftLocation: z.string().describe("Where the drafted reply was parked, so the manager can find it"),
349
+ }, async ({ senderDisplay, summary, draftLocation }) => {
350
+ try {
351
+ // Task 2252 — caller scope decides BOTH the recipient set and the
352
+ // transmitting socket, so the headers are load-bearing here, not
353
+ // cosmetic: without them the route falls back to the house account,
354
+ // which has no manager bound to itself and is refused.
355
+ const result = await callApi("whatsapp-notify-manager", "/api/whatsapp/notify-manager", "POST", { senderDisplay, summary, draftLocation }, callerScopeHeaders(process.env));
356
+ if (result.error)
357
+ return textResult(`Notify failed: ${result.error}`, true);
358
+ return textResult(`Notified ${result.delivered?.length ?? 0} account manager(s).`);
359
+ }
360
+ catch (err) {
361
+ return textResult(`Notify failed: ${err instanceof Error ? err.message : String(err)}`, true);
362
+ }
363
+ });
326
364
  // ─── whatsapp-reply ──────────────────────────────────────────────────
327
365
  lifelineTool(server, "whatsapp-reply", "Reply with a WhatsApp text message INTO AN EXISTING CONVERSATION on your own account's WhatsApp socket. Reply-only: it refuses any recipient you have no prior conversation with, so it can never start a new chat (starting new chats from a linked device is what WhatsApp penalises). Use it to answer a contact whose message you read with whatsapp-messages on an observe (account-manager) socket. The message goes out over your own account's socket, not the house socket.", {
328
366
  remoteJid: z.string().describe("The conversation JID to reply into (e.g. 447500000000@s.whatsapp.net) — must be a DM with an existing conversation. Group JIDs are refused."),