@rubytech/create-maxy-code 0.1.416 → 0.1.419

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 (282) hide show
  1. package/dist/__tests__/brand-port-uniqueness.test.js +74 -0
  2. package/dist/__tests__/outlook-client-id-env.test.js +46 -0
  3. package/dist/index.js +31 -0
  4. package/dist/known-brands.js +1 -0
  5. package/dist/port-resolution.js +8 -1
  6. package/package.json +1 -1
  7. package/payload/platform/config/brand-registry.json +8 -0
  8. package/payload/platform/config/brand.json +1 -0
  9. package/payload/platform/lib/account-enumeration/dist/__tests__/is-provisioned.test.d.ts +2 -0
  10. package/payload/platform/lib/account-enumeration/dist/__tests__/is-provisioned.test.d.ts.map +1 -0
  11. package/payload/platform/lib/account-enumeration/dist/__tests__/is-provisioned.test.js +81 -0
  12. package/payload/platform/lib/account-enumeration/dist/__tests__/is-provisioned.test.js.map +1 -0
  13. package/payload/platform/lib/account-enumeration/dist/index.d.ts +17 -0
  14. package/payload/platform/lib/account-enumeration/dist/index.d.ts.map +1 -1
  15. package/payload/platform/lib/account-enumeration/dist/index.js +46 -9
  16. package/payload/platform/lib/account-enumeration/dist/index.js.map +1 -1
  17. package/payload/platform/lib/account-enumeration/src/__tests__/is-provisioned.test.ts +86 -0
  18. package/payload/platform/lib/account-enumeration/src/index.ts +45 -9
  19. package/payload/platform/lib/account-scope/dist/index.d.ts +14 -5
  20. package/payload/platform/lib/account-scope/dist/index.d.ts.map +1 -1
  21. package/payload/platform/lib/account-scope/dist/index.js +14 -5
  22. package/payload/platform/lib/account-scope/dist/index.js.map +1 -1
  23. package/payload/platform/lib/account-scope/src/index.ts +22 -5
  24. package/payload/platform/lib/active-rules/dist/index.d.ts +35 -0
  25. package/payload/platform/lib/active-rules/dist/index.d.ts.map +1 -0
  26. package/payload/platform/lib/active-rules/dist/index.js +94 -0
  27. package/payload/platform/lib/active-rules/dist/index.js.map +1 -0
  28. package/payload/platform/lib/active-rules/package.json +7 -0
  29. package/payload/platform/lib/active-rules/src/index.test.ts +89 -0
  30. package/payload/platform/lib/active-rules/src/index.ts +133 -0
  31. package/payload/platform/lib/active-rules/tsconfig.json +9 -0
  32. package/payload/platform/package.json +4 -3
  33. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +55 -10
  34. package/payload/platform/plugins/docs/references/internals.md +10 -0
  35. package/payload/platform/plugins/docs/references/memory-guide.md +2 -0
  36. package/payload/platform/plugins/docs/references/outlook-guide.md +42 -9
  37. package/payload/platform/plugins/email/references/email-reference.md +1 -1
  38. package/payload/platform/plugins/filesystem/PLUGIN.md +1 -1
  39. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.js +16 -2
  40. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.js.map +1 -1
  41. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts +1 -1
  42. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts.map +1 -1
  43. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js +8 -3
  44. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js.map +1 -1
  45. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.js +2 -2
  46. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.js.map +1 -1
  47. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  48. package/payload/platform/plugins/memory/mcp/dist/index-testable.d.ts.map +1 -1
  49. package/payload/platform/plugins/memory/mcp/dist/index-testable.js +14 -5
  50. package/payload/platform/plugins/memory/mcp/dist/index-testable.js.map +1 -1
  51. package/payload/platform/plugins/memory/mcp/dist/index.js +5 -2
  52. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  53. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j-constraint.d.ts +29 -0
  54. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j-constraint.d.ts.map +1 -0
  55. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j-constraint.js +44 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j-constraint.js.map +1 -0
  57. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/owner-person-embed-text.test.d.ts +2 -0
  58. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/owner-person-embed-text.test.d.ts.map +1 -0
  59. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/owner-person-embed-text.test.js +22 -0
  60. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/owner-person-embed-text.test.js.map +1 -0
  61. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts +2 -0
  62. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts.map +1 -0
  63. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js +49 -0
  64. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js.map +1 -0
  65. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-bootstrap.test.js +105 -0
  66. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-bootstrap.test.js.map +1 -1
  67. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-embed.test.d.ts +2 -0
  68. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-embed.test.d.ts.map +1 -0
  69. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-embed.test.js +78 -0
  70. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-embed.test.js.map +1 -0
  71. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts +2 -0
  72. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts.map +1 -0
  73. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js +99 -0
  74. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js.map +1 -0
  75. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.js +2 -2
  76. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.js.map +1 -1
  77. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +0 -5
  78. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  79. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +2 -12
  80. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  81. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +3 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
  83. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +11 -3
  84. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  85. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +22 -0
  86. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  87. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +212 -24
  88. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  89. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  90. package/payload/platform/plugins/outlook/PLUGIN.md +40 -7
  91. package/payload/platform/plugins/outlook/mcp/dist/__tests__/account-register.test.d.ts +2 -0
  92. package/payload/platform/plugins/outlook/mcp/dist/__tests__/account-register.test.d.ts.map +1 -0
  93. package/payload/platform/plugins/outlook/mcp/dist/__tests__/account-register.test.js +137 -0
  94. package/payload/platform/plugins/outlook/mcp/dist/__tests__/account-register.test.js.map +1 -0
  95. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.d.ts +2 -0
  96. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.d.ts.map +1 -0
  97. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js +210 -0
  98. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js.map +1 -0
  99. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.d.ts +2 -0
  100. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.d.ts.map +1 -0
  101. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +110 -0
  102. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js.map +1 -0
  103. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-retry.test.d.ts +2 -0
  104. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-retry.test.d.ts.map +1 -0
  105. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-retry.test.js +108 -0
  106. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-retry.test.js.map +1 -0
  107. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js +13 -0
  108. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js.map +1 -1
  109. package/payload/platform/plugins/outlook/mcp/dist/__tests__/message.test.d.ts +2 -0
  110. package/payload/platform/plugins/outlook/mcp/dist/__tests__/message.test.d.ts.map +1 -0
  111. package/payload/platform/plugins/outlook/mcp/dist/__tests__/message.test.js +32 -0
  112. package/payload/platform/plugins/outlook/mcp/dist/__tests__/message.test.js.map +1 -0
  113. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pending-store.test.d.ts +2 -0
  114. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pending-store.test.d.ts.map +1 -0
  115. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pending-store.test.js +67 -0
  116. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pending-store.test.js.map +1 -0
  117. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js +7 -5
  118. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js.map +1 -1
  119. package/payload/platform/plugins/outlook/mcp/dist/__tests__/write-tools.test.d.ts +2 -0
  120. package/payload/platform/plugins/outlook/mcp/dist/__tests__/write-tools.test.d.ts.map +1 -0
  121. package/payload/platform/plugins/outlook/mcp/dist/__tests__/write-tools.test.js +141 -0
  122. package/payload/platform/plugins/outlook/mcp/dist/__tests__/write-tools.test.js.map +1 -0
  123. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +69 -0
  124. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts.map +1 -0
  125. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js +129 -0
  126. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -0
  127. package/payload/platform/plugins/outlook/mcp/dist/auth/pending-store.d.ts +39 -0
  128. package/payload/platform/plugins/outlook/mcp/dist/auth/pending-store.d.ts.map +1 -0
  129. package/payload/platform/plugins/outlook/mcp/dist/auth/pending-store.js +101 -0
  130. package/payload/platform/plugins/outlook/mcp/dist/auth/pending-store.js.map +1 -0
  131. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts.map +1 -1
  132. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js +3 -2
  133. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js.map +1 -1
  134. package/payload/platform/plugins/outlook/mcp/dist/index.js +187 -5
  135. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  136. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +34 -1
  137. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
  138. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +96 -7
  139. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
  140. package/payload/platform/plugins/outlook/mcp/dist/lib/message.d.ts +31 -0
  141. package/payload/platform/plugins/outlook/mcp/dist/lib/message.d.ts.map +1 -0
  142. package/payload/platform/plugins/outlook/mcp/dist/lib/message.js +30 -0
  143. package/payload/platform/plugins/outlook/mcp/dist/lib/message.js.map +1 -0
  144. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts +41 -17
  145. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts.map +1 -1
  146. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js +87 -28
  147. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js.map +1 -1
  148. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-cancel.d.ts +17 -0
  149. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-cancel.d.ts.map +1 -0
  150. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-cancel.js +38 -0
  151. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-cancel.js.map +1 -0
  152. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts +42 -0
  153. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts.map +1 -0
  154. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js +59 -0
  155. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js.map +1 -0
  156. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-freebusy.d.ts +34 -0
  157. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-freebusy.d.ts.map +1 -0
  158. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-freebusy.js +46 -0
  159. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-freebusy.js.map +1 -0
  160. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-respond.d.ts +15 -0
  161. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-respond.d.ts.map +1 -0
  162. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-respond.js +27 -0
  163. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-respond.js.map +1 -0
  164. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-update.d.ts +11 -0
  165. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-update.d.ts.map +1 -0
  166. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-update.js +20 -0
  167. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-update.js.map +1 -0
  168. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.d.ts +17 -0
  169. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.d.ts.map +1 -0
  170. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.js +26 -0
  171. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.js.map +1 -0
  172. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts +11 -0
  173. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -0
  174. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +26 -0
  175. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -0
  176. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.d.ts +14 -0
  177. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.d.ts.map +1 -0
  178. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js +26 -0
  179. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js.map +1 -0
  180. package/payload/platform/plugins/outlook/mcp/scripts/verify-doc-impl.sh +2 -2
  181. package/payload/platform/plugins/outlook/references/auth.md +51 -54
  182. package/payload/platform/plugins/outlook/references/graph-surfaces.md +97 -4
  183. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +34 -9
  184. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +10 -5
  185. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  186. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +9 -1
  187. package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +3 -0
  188. package/payload/platform/scripts/lib/provision-account-dir.sh +9 -1
  189. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +1 -1
  190. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -1
  191. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +5 -1
  192. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -1
  193. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  194. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +9 -0
  195. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  196. package/payload/platform/services/claude-session-manager/dist/config.d.ts +18 -0
  197. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  198. package/payload/platform/services/claude-session-manager/dist/config.js +16 -0
  199. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  200. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +1 -0
  201. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  202. package/payload/platform/services/claude-session-manager/dist/http-server.js +36 -4
  203. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  204. package/payload/platform/services/claude-session-manager/dist/index.js +14 -0
  205. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  206. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts +46 -0
  207. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -0
  208. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js +96 -0
  209. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map +1 -0
  210. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +33 -0
  211. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  212. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +61 -7
  213. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  214. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  215. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +5 -1
  216. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  217. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
  218. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  219. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +20 -0
  220. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  221. package/payload/platform/services/telegram-channel/dist/instructions.d.ts +9 -0
  222. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -0
  223. package/payload/platform/services/telegram-channel/dist/instructions.js +100 -0
  224. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -0
  225. package/payload/platform/services/telegram-channel/dist/notification.d.ts +93 -0
  226. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -0
  227. package/payload/platform/services/telegram-channel/dist/notification.js +119 -0
  228. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -0
  229. package/payload/platform/services/telegram-channel/dist/server.d.ts +40 -0
  230. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -0
  231. package/payload/platform/services/telegram-channel/dist/server.js +291 -0
  232. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -0
  233. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +28 -0
  234. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -0
  235. package/payload/platform/services/telegram-channel/dist/turn-follow.js +188 -0
  236. package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -0
  237. package/payload/platform/services/webchat-channel/dist/notification.d.ts +5 -0
  238. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  239. package/payload/platform/services/webchat-channel/dist/notification.js +6 -1
  240. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  241. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +6 -0
  242. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  243. package/payload/platform/services/whatsapp-channel/dist/notification.js +8 -1
  244. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  245. package/payload/platform/templates/agents/passive/IDENTITY.md +21 -0
  246. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  247. package/payload/server/public/assets/{AdminLoginScreens-CBlE6FB4.js → AdminLoginScreens-B1PkOF66.js} +1 -1
  248. package/payload/server/public/assets/AdminShell-D8wNArGQ.js +2 -0
  249. package/payload/server/public/assets/{Checkbox-B0pbYJDQ.js → Checkbox-Bm9Er726.js} +1 -1
  250. package/payload/server/public/assets/{Transcript-CYaAPBFX.js → Transcript-CBIyQ8T5.js} +1 -1
  251. package/payload/server/public/assets/{admin-CVh9HO-f.js → admin-CWM9G_yA.js} +1 -1
  252. package/payload/server/public/assets/{browser-C4f_D4Ih.js → browser-D8QhNffV.js} +1 -1
  253. package/payload/server/public/assets/{calendar-WUxQ4fVo.js → calendar-qob5K4t_.js} +1 -1
  254. package/payload/server/public/assets/chat-BNrxVUF6.js +1 -0
  255. package/payload/server/public/assets/chevron-left-BQThFVrd.js +1 -0
  256. package/payload/server/public/assets/data-C-EqNbRi.js +1 -0
  257. package/payload/server/public/assets/{graph-BNXE6mKP.js → graph-DZ2HMpIB.js} +1 -1
  258. package/payload/server/public/assets/{graph-labels-Csnovm_c.js → graph-labels-BFG4bjXQ.js} +1 -1
  259. package/payload/server/public/assets/{maximize-2-WRUJsg5n.js → maximize-2-DkZ40lSl.js} +1 -1
  260. package/payload/server/public/assets/{operator-CXtygMh6.js → operator-FrXrSsBj.js} +1 -1
  261. package/payload/server/public/assets/page-CHWMI9Tm.js +32 -0
  262. package/payload/server/public/assets/{page-IQDLdebG.js → page-CXKV01gT.js} +1 -1
  263. package/payload/server/public/assets/{public-DSeYu61E.js → public-2p1pJ4WF.js} +1 -1
  264. package/payload/server/public/assets/{rotate-ccw-BayMWwfc.js → rotate-ccw-D3o_bMBA.js} +1 -1
  265. package/payload/server/public/assets/useSubAccountSwitcher-8dLvF3Ux.js +9 -0
  266. package/payload/server/public/assets/useSubAccountSwitcher-BUvlETHN.css +1 -0
  267. package/payload/server/public/browser.html +4 -4
  268. package/payload/server/public/calendar.html +5 -5
  269. package/payload/server/public/chat.html +9 -9
  270. package/payload/server/public/data.html +8 -8
  271. package/payload/server/public/graph.html +9 -9
  272. package/payload/server/public/index.html +11 -11
  273. package/payload/server/public/operator.html +11 -11
  274. package/payload/server/public/public.html +9 -9
  275. package/payload/server/server.js +245 -40
  276. package/payload/server/public/assets/AdminShell-d1WQ3lLu.js +0 -2
  277. package/payload/server/public/assets/chat-NJ2HXhOf.js +0 -1
  278. package/payload/server/public/assets/chevron-left-Da-iZGi_.js +0 -1
  279. package/payload/server/public/assets/data-ZUdT44Ba.js +0 -1
  280. package/payload/server/public/assets/page-DjpQp_ir.js +0 -32
  281. package/payload/server/public/assets/useSubAccountSwitcher-C9aLg8yI.js +0 -9
  282. package/payload/server/public/assets/useSubAccountSwitcher-CRZfyJXR.css +0 -1
@@ -0,0 +1,74 @@
1
+ // Standing check: no two brands may fight for the same host resource when
2
+ // co-resident on one device. Two namespaces are at stake:
3
+ //
4
+ // 1. Host TCP ports. neo4jPort / ttydPort / rfbPort / websockifyPort /
5
+ // cdpPort / claudeSessionManagerPort all bind TCP on the same host, so they
6
+ // must be globally unique across every (brand, field) pair — not merely
7
+ // unique within each field. A future brand whose ttydPort equals another
8
+ // brand's neo4jPort would EADDRINUSE at runtime with no build event, even
9
+ // though each field is individually unique.
10
+ // 2. X display numbers. vncDisplay is an Xvnc display index (:NN and its
11
+ // /tmp/.X<NN>-lock), a namespace distinct from TCP ports, so it must be
12
+ // unique across brands on its own.
13
+ //
14
+ // This reconciles all brands/*/brand.json on every CI run. Path resolution
15
+ // mirrors known-brand-hostnames.test.ts.
16
+ import test from "node:test";
17
+ import assert from "node:assert/strict";
18
+ import { readdirSync, readFileSync, existsSync } from "node:fs";
19
+ import { fileURLToPath } from "node:url";
20
+ import { dirname, resolve, join } from "node:path";
21
+ // dist/__tests__/brand-port-uniqueness.test.js → maxy-code/brands
22
+ const here = dirname(fileURLToPath(import.meta.url));
23
+ const BRANDS_DIR = resolve(here, "../../../../brands");
24
+ // Fields that bind a host TCP port (one shared namespace across all fields).
25
+ const TCP_PORT_FIELDS = [
26
+ "neo4jPort",
27
+ "ttydPort",
28
+ "rfbPort",
29
+ "websockifyPort",
30
+ "cdpPort",
31
+ "claudeSessionManagerPort",
32
+ ];
33
+ // X display index — its own namespace, unique per brand.
34
+ const DISPLAY_FIELD = "vncDisplay";
35
+ const ALL_FIELDS = [...TCP_PORT_FIELDS, DISPLAY_FIELD];
36
+ function brandManifests() {
37
+ return readdirSync(BRANDS_DIR, { withFileTypes: true })
38
+ .filter((e) => e.isDirectory())
39
+ .map((e) => ({ name: e.name, path: join(BRANDS_DIR, e.name, "brand.json") }))
40
+ .filter((b) => existsSync(b.path))
41
+ .map((b) => ({ name: b.name, manifest: JSON.parse(readFileSync(b.path, "utf-8")) }));
42
+ }
43
+ test("brands/ is discoverable and every brand declares all port fields", () => {
44
+ const brands = brandManifests();
45
+ assert.ok(brands.length > 0, `no brand.json found under ${BRANDS_DIR}`);
46
+ for (const { name, manifest } of brands) {
47
+ for (const field of ALL_FIELDS) {
48
+ assert.equal(typeof manifest[field], "number", `brands/${name}/brand.json is missing numeric ${field}`);
49
+ }
50
+ }
51
+ });
52
+ test("host TCP ports are globally unique across every (brand, field)", () => {
53
+ const brands = brandManifests();
54
+ const seen = new Map(); // port -> "brand.field" that claimed it
55
+ for (const { name, manifest } of brands) {
56
+ for (const field of TCP_PORT_FIELDS) {
57
+ const value = manifest[field];
58
+ const owner = `${name}.${field}`;
59
+ const prior = seen.get(value);
60
+ assert.equal(prior, undefined, `TCP port collision: ${prior} and ${owner} both bind ${value} — they would EADDRINUSE when co-resident`);
61
+ seen.set(value, owner);
62
+ }
63
+ }
64
+ });
65
+ test("vncDisplay is unique across brands", () => {
66
+ const brands = brandManifests();
67
+ const seen = new Map();
68
+ for (const { name, manifest } of brands) {
69
+ const value = manifest[DISPLAY_FIELD];
70
+ const prior = seen.get(value);
71
+ assert.equal(prior, undefined, `vncDisplay collision: brands "${prior}" and "${name}" both use :${value}`);
72
+ seen.set(value, name);
73
+ }
74
+ });
@@ -0,0 +1,46 @@
1
+ // acceptance gate for OUTLOOK_CLIENT_ID stamping in the claude-session-manager
2
+ // systemd unit (Task 1473).
3
+ //
4
+ // The outlook MCP reads process.env.OUTLOOK_CLIENT_ID; the session manager is
5
+ // the process that spawns claude -> MCP, so the client ID must land on this
6
+ // unit's Environment= block. Two invariants:
7
+ // (a) a non-empty brand outlookClientId emits BOTH
8
+ // `Environment=OUTLOOK_CLIENT_ID=<id>` and
9
+ // `Environment=OUTLOOK_TENANT_ID=common` (multi-tenant /common app).
10
+ // (b) an empty / absent brand value emits NEITHER line — the plugin stays
11
+ // inert on that brand exactly as before (no regression), and the MCP's
12
+ // module-init no-throw path keeps the registry intact.
13
+ import test from "node:test";
14
+ import assert from "node:assert/strict";
15
+ import { buildClaudeSessionManagerUnitFile } from "../port-resolution.js";
16
+ const ACCOUNT_ID = "12345678-9abc-def0-1234-56789abcdef0";
17
+ const CLIENT_ID = "0fa68fe2-8bec-4007-a0c7-776636fbcf97";
18
+ function build(outlookClientId) {
19
+ return buildClaudeSessionManagerUnitFile({
20
+ productName: "SiteDesk",
21
+ brandHostname: "sitedesk-code",
22
+ installDir: "/home/me/sitedesk-code",
23
+ persistDir: "/home/me/.sitedesk-code",
24
+ claudeSessionManagerPort: 19402,
25
+ brandPort: 19300,
26
+ maxyUiInternalPort: 19301,
27
+ accountId: ACCOUNT_ID,
28
+ cdpPort: 9224,
29
+ outlookClientId,
30
+ });
31
+ }
32
+ test("stamps OUTLOOK_CLIENT_ID + OUTLOOK_TENANT_ID for a non-empty brand value", () => {
33
+ const unit = build(CLIENT_ID);
34
+ assert.match(unit, new RegExp(`^Environment=OUTLOOK_CLIENT_ID=${CLIENT_ID}$`, "m"), "OUTLOOK_CLIENT_ID line missing or malformed");
35
+ assert.match(unit, /^Environment=OUTLOOK_TENANT_ID=common$/m, "OUTLOOK_TENANT_ID=common line missing");
36
+ });
37
+ test("stamps NEITHER line when the brand value is empty", () => {
38
+ const unit = build("");
39
+ assert.doesNotMatch(unit, /OUTLOOK_CLIENT_ID/, "unexpected OUTLOOK_CLIENT_ID on empty brand");
40
+ assert.doesNotMatch(unit, /OUTLOOK_TENANT_ID/, "unexpected OUTLOOK_TENANT_ID on empty brand");
41
+ });
42
+ test("stamps NEITHER line when the brand value is absent", () => {
43
+ const unit = build(undefined);
44
+ assert.doesNotMatch(unit, /OUTLOOK_CLIENT_ID/, "unexpected OUTLOOK_CLIENT_ID on absent brand");
45
+ assert.doesNotMatch(unit, /OUTLOOK_TENANT_ID/, "unexpected OUTLOOK_TENANT_ID on absent brand");
46
+ });
package/dist/index.js CHANGED
@@ -3579,6 +3579,32 @@ function installServiceDarwin() {
3579
3579
  else
3580
3580
  envContent = envContent.trimEnd() + (envContent.length > 0 ? "\n" : "") + `NODE_BIN=${nodeBin}\n`;
3581
3581
  }
3582
+ // Task 1473 — darwin has no systemd EnvironmentFile primitive, so the vendor
3583
+ // Outlook app env is mirrored into .env (the wrapper sources it before exec).
3584
+ // Symmetric with the Linux unit, which is rebuilt wholesale each run: a
3585
+ // non-empty brand outlookClientId upserts BOTH vars together; empty / absent
3586
+ // STRIPS both lines. Stripping matters because .env persists across installs —
3587
+ // without it, a brand whose outlookClientId is later blanked (vendor-app
3588
+ // rotation/revocation) would keep sourcing a stale, now-invalid client ID and
3589
+ // keep reporting clientIdConfigured=true against its current empty intent.
3590
+ if (BRAND.outlookClientId) {
3591
+ for (const [key, value] of [
3592
+ ["OUTLOOK_CLIENT_ID", BRAND.outlookClientId],
3593
+ ["OUTLOOK_TENANT_ID", "common"],
3594
+ ]) {
3595
+ const re = new RegExp(`^${key}=.*$`, "m");
3596
+ if (re.test(envContent))
3597
+ envContent = envContent.replace(re, `${key}=${value}`);
3598
+ else
3599
+ envContent = envContent.trimEnd() + (envContent.length > 0 ? "\n" : "") + `${key}=${value}\n`;
3600
+ }
3601
+ }
3602
+ else {
3603
+ for (const key of ["OUTLOOK_CLIENT_ID", "OUTLOOK_TENANT_ID"]) {
3604
+ envContent = envContent.replace(new RegExp(`^${key}=.*\\n?`, "m"), "");
3605
+ }
3606
+ }
3607
+ logFile(` [installer] op=outlook-client-id brand=${BRAND.hostname} stamped=${BRAND.outlookClientId ? "true" : "false"}`);
3582
3608
  writeFileSync(envPath, envContent);
3583
3609
  logFile(` .env: DISPLAY_MODE=${DISPLAY_MODE}, EMBED_MODEL=${EMBED_MODEL}, EMBED_DIMENSIONS=${EMBED_DIMS}, NEO4J_URI=bolt://localhost:${NEO4J_PORT}, PORT=${PORT}, MAXY_UI_INTERNAL_PORT=${PORT} (darwin-collapsed), CLAUDE_SESSION_MANAGER_PORT=${BRAND.claudeSessionManagerPort}, HOSTNAME=0.0.0.0, ACCOUNT_ID=${installAccountId.slice(0, 8)}…, CLAUDE_CONFIG_DIR=${persistDir}/.claude, CLAUDE_BIN=${canonicalClaude ?? "unresolved(PATH)"}, NODE_BIN=${nodeBin}, PATH=${darwinServiceEnvPath}`);
3584
3610
  }
@@ -4161,9 +4187,14 @@ function installService() {
4161
4187
  maxyUiInternalPort: MAXY_UI_INTERNAL_PORT,
4162
4188
  accountId: installAccountId,
4163
4189
  cdpPort: CDP_PORT_BRAND,
4190
+ outlookClientId: BRAND.outlookClientId,
4164
4191
  });
4165
4192
  writeFileSync(join(serviceDir, claudeSessionManagerUnitName), claudeSessionManagerUnit);
4166
4193
  logFile(` ${claudeSessionManagerUnitName}: CLAUDE_SESSION_MANAGER_PORT=${CLAUDE_SESSION_MANAGER_PORT_BRAND} MAXY_UI_INTERNAL_PORT=${MAXY_UI_INTERNAL_PORT}`);
4194
+ // Task 1473 — standing install-time signal reconciling brand.json intent
4195
+ // (a non-empty outlookClientId) against what the unit shipped. `stamped=false`
4196
+ // while the brand has a value is the mis-stamp signature.
4197
+ logFile(` [installer] op=outlook-client-id brand=${BRAND.hostname} stamped=${BRAND.outlookClientId ? "true" : "false"}`);
4167
4198
  // Task 250 — write the brand-agnostic `claude-ptys.slice` unit. Every
4168
4199
  // brand's session manager drops its per-spawn scope units under this
4169
4200
  // slice. Writing it once per install is idempotent (the content is
@@ -18,6 +18,7 @@ export const KNOWN_BRAND_HOSTNAMES = [
18
18
  "realagent",
19
19
  "realagent-code",
20
20
  "sitedesk-code",
21
+ "property-administrators",
21
22
  "maxy-2",
22
23
  "maxy-3",
23
24
  "maxy-4",
@@ -128,6 +128,13 @@ export function buildClaudeSessionManagerUnitFile(o) {
128
128
  if (!o.accountId) {
129
129
  throw new Error("buildClaudeSessionManagerUnitFile: accountId is required — caller must resolve the on-disk account UUID before stamping the systemd unit.");
130
130
  }
131
+ // Task 1473 — a non-empty brand outlookClientId adds the vendor Outlook app
132
+ // env. Both lines land together so OUTLOOK_TENANT_ID (multi-tenant /common)
133
+ // never trails a bare client ID. Empty / absent brands stamp neither line —
134
+ // the plugin stays inert, no regression.
135
+ const outlookEnv = o.outlookClientId
136
+ ? `\nEnvironment=OUTLOOK_CLIENT_ID=${o.outlookClientId}\nEnvironment=OUTLOOK_TENANT_ID=common`
137
+ : "";
131
138
  return `[Unit]
132
139
  Description=${o.productName} Claude Session Manager
133
140
  After=network.target
@@ -149,7 +156,7 @@ Environment=PLATFORM_ROOT=${o.installDir}/platform
149
156
  Environment=MAXY_BRAND_PORT=${o.brandPort}
150
157
  Environment=MAXY_UI_INTERNAL_PORT=${o.maxyUiInternalPort}
151
158
  Environment=PLATFORM_PORT=${o.maxyUiInternalPort}
152
- Environment=CDP_PORT=${o.cdpPort}
159
+ Environment=CDP_PORT=${o.cdpPort}${outlookEnv}
153
160
  EnvironmentFile=-${o.persistDir}/.env
154
161
  StandardOutput=append:${o.persistDir}/logs/server.log
155
162
  StandardError=append:${o.persistDir}/logs/server.log
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.416",
3
+ "version": "0.1.419",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -8,6 +8,14 @@
8
8
  "websockifyPort": 6080,
9
9
  "cdpPort": 9222
10
10
  },
11
+ {
12
+ "hostname": "property-administrators",
13
+ "configDir": ".property-administrators",
14
+ "vncDisplay": 102,
15
+ "rfbPort": 5903,
16
+ "websockifyPort": 6083,
17
+ "cdpPort": 9225
18
+ },
11
19
  {
12
20
  "hostname": "realagent-code",
13
21
  "configDir": ".realagent-code",
@@ -14,6 +14,7 @@
14
14
  "websockifyPort": 6080,
15
15
  "cdpPort": 9222,
16
16
  "claudeSessionManagerPort": 19400,
17
+ "outlookClientId": "",
17
18
  "commercialMode": false,
18
19
  "vertical": "schema-knowledge-work",
19
20
  "shipsPremiumBundles": ["venture-studio", "writer-craft", "teaching", "management-consulting"],
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=is-provisioned.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-provisioned.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/is-provisioned.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_test_1 = __importDefault(require("node:test"));
7
+ const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const node_fs_1 = require("node:fs");
9
+ const node_os_1 = require("node:os");
10
+ const node_path_1 = require("node:path");
11
+ const index_js_1 = require("../index.js");
12
+ const BOOT_ID = "12345678-9abc-def0-1234-56789abcdef0";
13
+ const LATER_ID = "99999999-aaaa-bbbb-cccc-dddddddddddd";
14
+ /** One valid account dir (BOOT_ID) that existed at "boot". */
15
+ function makeFixture() {
16
+ const root = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "acct-prov-"));
17
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(root, BOOT_ID));
18
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, BOOT_ID, "account.json"), JSON.stringify({ id: BOOT_ID }));
19
+ return root;
20
+ }
21
+ /** Provision a second account dir on disk AFTER boot (simulates account_create). */
22
+ function provisionLater(root) {
23
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(root, LATER_ID));
24
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, LATER_ID, "account.json"), JSON.stringify({ id: LATER_ID }));
25
+ }
26
+ (0, node_test_1.default)("isProvisionedAccount: a created-after-boot id resolves via re-scan on miss", () => {
27
+ (0, index_js_1._resetEnumerationCache)();
28
+ const root = makeFixture();
29
+ try {
30
+ // Warm the cache at "boot" — only BOOT_ID is present.
31
+ strict_1.default.deepEqual((0, index_js_1.enumerateValidAccountIds)(root), [BOOT_ID]);
32
+ // A sub-account is provisioned after the snapshot.
33
+ provisionLater(root);
34
+ // Miss against the cached set must re-scan disk and find it — no restart.
35
+ strict_1.default.equal((0, index_js_1.isProvisionedAccount)(root, LATER_ID), true);
36
+ }
37
+ finally {
38
+ (0, node_fs_1.rmSync)(root, { recursive: true });
39
+ }
40
+ });
41
+ (0, node_test_1.default)("isProvisionedAccount: a truly-absent id still returns false after re-scan", () => {
42
+ (0, index_js_1._resetEnumerationCache)();
43
+ const root = makeFixture();
44
+ try {
45
+ (0, index_js_1.enumerateValidAccountIds)(root); // warm
46
+ const bogus = "00000000-0000-0000-0000-000000000000";
47
+ strict_1.default.equal((0, index_js_1.isProvisionedAccount)(root, bogus), false);
48
+ }
49
+ finally {
50
+ (0, node_fs_1.rmSync)(root, { recursive: true });
51
+ }
52
+ });
53
+ (0, node_test_1.default)("isProvisionedAccount: a warmed hit returns true without re-scanning", () => {
54
+ (0, index_js_1._resetEnumerationCache)();
55
+ const root = makeFixture();
56
+ try {
57
+ (0, index_js_1.enumerateValidAccountIds)(root); // warm = [BOOT_ID]
58
+ // Provision a second dir AFTER warming.
59
+ provisionLater(root);
60
+ // HIT: BOOT_ID is already in the cached set → true, and the cache must NOT
61
+ // be refreshed (no readdirSync). Proof: the cached set still hides LATER_ID.
62
+ strict_1.default.equal((0, index_js_1.isProvisionedAccount)(root, BOOT_ID), true);
63
+ strict_1.default.deepEqual((0, index_js_1.enumerateValidAccountIds)(root), [BOOT_ID], "a hit must not refresh the cache — LATER_ID must stay hidden");
64
+ }
65
+ finally {
66
+ (0, node_fs_1.rmSync)(root, { recursive: true });
67
+ }
68
+ });
69
+ (0, node_test_1.default)("isProvisionedAccount: cold cache scans disk and answers", () => {
70
+ (0, index_js_1._resetEnumerationCache)();
71
+ const root = makeFixture();
72
+ try {
73
+ // No warm call — cache is cold. A miss (cold) must scan and answer both ways.
74
+ strict_1.default.equal((0, index_js_1.isProvisionedAccount)(root, BOOT_ID), true);
75
+ strict_1.default.equal((0, index_js_1.isProvisionedAccount)(root, LATER_ID), false);
76
+ }
77
+ finally {
78
+ (0, node_fs_1.rmSync)(root, { recursive: true });
79
+ }
80
+ });
81
+ //# sourceMappingURL=is-provisioned.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-provisioned.test.js","sourceRoot":"","sources":["../../src/__tests__/is-provisioned.test.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,23 @@
6
6
  * be hiding).
7
7
  */
8
8
  export declare function enumerateValidAccountIds(accountsDir: string): string[];
9
+ /**
10
+ * Membership test with re-scan-on-miss: is `id` a currently-provisioned account
11
+ * under `accountsDir`?
12
+ *
13
+ * Hit path — `id` is in the cached set — returns true with no I/O (the common
14
+ * case; the cache still serves it). Miss path — `id` is absent from the cached
15
+ * set, or the cache is cold — drops the stale entry and re-scans `accountsDir`
16
+ * once (refreshing the cache) before answering.
17
+ *
18
+ * This closes the created-after-boot gap (Task 1471): a sub-account provisioned
19
+ * by the runtime `account_create` lifecycle tool after a consumer process booted
20
+ * is invisible to that process's boot-time cached set. Without the re-scan, the
21
+ * cross-account resolver rejects the id as not-provisioned even though its dir
22
+ * and `account.json` exist on disk. A single `readdirSync` on the miss finds it;
23
+ * a genuinely-absent id pays the same one scan and correctly returns false.
24
+ */
25
+ export declare function isProvisionedAccount(accountsDir: string, id: string): boolean;
9
26
  /**
10
27
  * Returns the single `role:"house"` account UUID under `accountsDir`.
11
28
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAgCtE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAsBjE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAqBvE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAS9C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,mCAAmC,GAC3C,SAAS,GACT,oBAAoB,GACpB,UAAU,CAAC;AAEf,MAAM,MAAM,sBAAsB,GAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9C;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,mCAAmC,CAAC;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEN,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,MAAM,EAAE,GAChB,sBAAsB,CAWxB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwCA;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAgCtE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAO7E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAsBjE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAqBvE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAS9C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,mCAAmC,GAC3C,SAAS,GACT,oBAAoB,GACpB,UAAU,CAAC;AAEf,MAAM,MAAM,sBAAsB,GAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9C;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,mCAAmC,CAAC;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEN,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,MAAM,EAAE,GAChB,sBAAsB,CAWxB"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enumerateValidAccountIds = enumerateValidAccountIds;
4
+ exports.isProvisionedAccount = isProvisionedAccount;
4
5
  exports.resolveHouseAccountId = resolveHouseAccountId;
5
6
  exports.resolveHouseOrSoleAccountId = resolveHouseOrSoleAccountId;
6
7
  exports.getAccountsDirFromEnv = getAccountsDirFromEnv;
@@ -16,16 +17,27 @@ exports.validateAccountIdEnv = validateAccountIdEnv;
16
17
  * (`WHERE n.accountId = $accountId`) silently hides leaks from every UI;
17
18
  * an unenforced writer can leak indefinitely with zero downstream symptom.
18
19
  *
19
- * Two consumers:
20
- * 1. `writeNodeWithEdges` (platform/lib/graph-write/) central write-time
21
- * floor; rejects any write whose `props.accountId` is not in the set
22
- * returned here, unless `createdBy.agent === 'system'` (bootstrap).
23
- * 2. `resolvePlatformAccountId` (platform/ui/app/lib/whatsapp/platform-account-id.ts)
24
- * WhatsApp writer-side helper; loud-throws on zero or multi accounts.
20
+ * Consumers of the enumerated set:
21
+ * - The cross-account resolver's injected validity check (the `memory` and
22
+ * `filesystem` MCP plugins) the check that gates a `targetAccountId`.
23
+ * These call `isProvisionedAccount` (re-scan-on-miss), not the cached list
24
+ * directly, so a sub-account created at runtime is not masked by the boot
25
+ * snapshot (Task 1471).
26
+ * - `resolvePlatformAccountId` (platform/ui/app/lib/whatsapp/platform-account-id.ts)
27
+ * — WhatsApp writer-side helper; loud-throws on zero or multi accounts.
28
+ * - Boot-time env-vs-disk validation (`validateAccountIdEnv`).
25
29
  *
26
- * Per-process cache keyed on `accountsDir` Phase 0 invariant has accounts
27
- * created at install time, so adding/removing accounts requires a service
28
- * restart anyway.
30
+ * NOTE: `writeNodeWithEdges` (platform/lib/graph-write/) does NOT enumerate.
31
+ * Its account floor compares `props.accountId` against `process.env.ACCOUNT_ID`
32
+ * (the spawning process's boot-validated id), deliberately staying out of the
33
+ * `node:fs` import chain so the ESM payload bundle emits no `__require("fs")`
34
+ * shim. So the write gate does not share the cache's staleness.
35
+ *
36
+ * Per-process cache keyed on `accountsDir`. The install-time invariant (accounts
37
+ * created only at install) no longer holds: the runtime `account_create`
38
+ * lifecycle tool provisions sub-accounts during a live session. `enumerate…`
39
+ * keeps its cached-list semantics for the hit path; `isProvisionedAccount`
40
+ * re-scans on a miss so a created-after-boot account resolves without restart.
29
41
  */
30
42
  const node_fs_1 = require("node:fs");
31
43
  const node_path_1 = require("node:path");
@@ -73,6 +85,31 @@ function enumerateValidAccountIds(accountsDir) {
73
85
  cache.set(accountsDir, valid);
74
86
  return valid;
75
87
  }
88
+ /**
89
+ * Membership test with re-scan-on-miss: is `id` a currently-provisioned account
90
+ * under `accountsDir`?
91
+ *
92
+ * Hit path — `id` is in the cached set — returns true with no I/O (the common
93
+ * case; the cache still serves it). Miss path — `id` is absent from the cached
94
+ * set, or the cache is cold — drops the stale entry and re-scans `accountsDir`
95
+ * once (refreshing the cache) before answering.
96
+ *
97
+ * This closes the created-after-boot gap (Task 1471): a sub-account provisioned
98
+ * by the runtime `account_create` lifecycle tool after a consumer process booted
99
+ * is invisible to that process's boot-time cached set. Without the re-scan, the
100
+ * cross-account resolver rejects the id as not-provisioned even though its dir
101
+ * and `account.json` exist on disk. A single `readdirSync` on the miss finds it;
102
+ * a genuinely-absent id pays the same one scan and correctly returns false.
103
+ */
104
+ function isProvisionedAccount(accountsDir, id) {
105
+ const cached = cache.get(accountsDir);
106
+ if (cached?.includes(id))
107
+ return true;
108
+ // Miss: `id` is not in the cached set (or the cache is cold). Drop the stale
109
+ // snapshot and re-scan disk once so a real, on-disk account is not masked.
110
+ cache.delete(accountsDir);
111
+ return enumerateValidAccountIds(accountsDir).includes(id);
112
+ }
76
113
  /**
77
114
  * Returns the single `role:"house"` account UUID under `accountsDir`.
78
115
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAoCA,4DAgCC;AAoBD,sDAsBC;AAeD,kEAqBC;AAUD,sDASC;AAMD,wDAEC;AA8BD,oDAcC;AAzND;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qCAAoD;AACpD,yCAAoC;AAEpC,MAAM,OAAO,GACX,iEAAiE,CAAC;AAEpE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;AAE1C;;;;;;GAMG;AACH,SAAgB,wBAAwB,CAAC,WAAmB;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAExC,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,IAAA,qBAAW,EAAC,WAAW,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YAC3B,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,MAAM,UAAU,GAAG,IAAA,mBAAO,EAAC,WAAW,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;YACjD,IAAI,IAAI,KAAK,QAAQ;gBAAE,SAAS;YAChC,kEAAkE;YAClE,kCAAkC;QACpC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,qBAAqB,CAAC,WAAmB;IACvD,MAAM,KAAK,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,IAAA,mBAAO,EAAC,WAAW,EAAE,EAAE,EAAE,cAAc,CAAC,CAAC;QAC5D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,UAAU,EAAE,OAAO,CAAC,CAEvD,CAAC;YACF,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,oDAAoD,MAAM,CAAC,MAAM,UAAU,WAAW,GAAG;YACvF,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,oDAAoD;YACnF,6CAA6C,CAChD,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,2BAA2B,CAAC,WAAmB;IAC7D,MAAM,KAAK,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,IAAA,mBAAO,EAAC,WAAW,EAAE,EAAE,EAAE,cAAc,CAAC,CAAC;QAC5D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,UAAU,EAAE,OAAO,CAAC,CAEvD,CAAC;YACF,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;QACjD,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/D,MAAM,IAAI,KAAK,CACb,0DAA0D,MAAM,CAAC,MAAM,UAAU,KAAK,CAAC,MAAM,GAAG;QAC9F,SAAS,WAAW,yEAAyE;QAC7F,6CAA6C,CAChD,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,qBAAqB;IACnC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,4EAA4E;YAC1E,kFAAkF,CACrF,CAAC;IACJ,CAAC;IACD,OAAO,IAAA,mBAAO,EAAC,IAAI,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,SAAgB,sBAAsB;IACpC,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AA8BD,SAAgB,oBAAoB,CAClC,QAA4B,EAC5B,OAAiB;IAEjB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAChE,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC/E,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import {
7
+ enumerateValidAccountIds,
8
+ isProvisionedAccount,
9
+ _resetEnumerationCache,
10
+ } from "../index.js";
11
+
12
+ const BOOT_ID = "12345678-9abc-def0-1234-56789abcdef0";
13
+ const LATER_ID = "99999999-aaaa-bbbb-cccc-dddddddddddd";
14
+
15
+ /** One valid account dir (BOOT_ID) that existed at "boot". */
16
+ function makeFixture(): string {
17
+ const root = mkdtempSync(join(tmpdir(), "acct-prov-"));
18
+ mkdirSync(join(root, BOOT_ID));
19
+ writeFileSync(join(root, BOOT_ID, "account.json"), JSON.stringify({ id: BOOT_ID }));
20
+ return root;
21
+ }
22
+
23
+ /** Provision a second account dir on disk AFTER boot (simulates account_create). */
24
+ function provisionLater(root: string): void {
25
+ mkdirSync(join(root, LATER_ID));
26
+ writeFileSync(join(root, LATER_ID, "account.json"), JSON.stringify({ id: LATER_ID }));
27
+ }
28
+
29
+ test("isProvisionedAccount: a created-after-boot id resolves via re-scan on miss", () => {
30
+ _resetEnumerationCache();
31
+ const root = makeFixture();
32
+ try {
33
+ // Warm the cache at "boot" — only BOOT_ID is present.
34
+ assert.deepEqual(enumerateValidAccountIds(root), [BOOT_ID]);
35
+ // A sub-account is provisioned after the snapshot.
36
+ provisionLater(root);
37
+ // Miss against the cached set must re-scan disk and find it — no restart.
38
+ assert.equal(isProvisionedAccount(root, LATER_ID), true);
39
+ } finally {
40
+ rmSync(root, { recursive: true });
41
+ }
42
+ });
43
+
44
+ test("isProvisionedAccount: a truly-absent id still returns false after re-scan", () => {
45
+ _resetEnumerationCache();
46
+ const root = makeFixture();
47
+ try {
48
+ enumerateValidAccountIds(root); // warm
49
+ const bogus = "00000000-0000-0000-0000-000000000000";
50
+ assert.equal(isProvisionedAccount(root, bogus), false);
51
+ } finally {
52
+ rmSync(root, { recursive: true });
53
+ }
54
+ });
55
+
56
+ test("isProvisionedAccount: a warmed hit returns true without re-scanning", () => {
57
+ _resetEnumerationCache();
58
+ const root = makeFixture();
59
+ try {
60
+ enumerateValidAccountIds(root); // warm = [BOOT_ID]
61
+ // Provision a second dir AFTER warming.
62
+ provisionLater(root);
63
+ // HIT: BOOT_ID is already in the cached set → true, and the cache must NOT
64
+ // be refreshed (no readdirSync). Proof: the cached set still hides LATER_ID.
65
+ assert.equal(isProvisionedAccount(root, BOOT_ID), true);
66
+ assert.deepEqual(
67
+ enumerateValidAccountIds(root),
68
+ [BOOT_ID],
69
+ "a hit must not refresh the cache — LATER_ID must stay hidden",
70
+ );
71
+ } finally {
72
+ rmSync(root, { recursive: true });
73
+ }
74
+ });
75
+
76
+ test("isProvisionedAccount: cold cache scans disk and answers", () => {
77
+ _resetEnumerationCache();
78
+ const root = makeFixture();
79
+ try {
80
+ // No warm call — cache is cold. A miss (cold) must scan and answer both ways.
81
+ assert.equal(isProvisionedAccount(root, BOOT_ID), true);
82
+ assert.equal(isProvisionedAccount(root, LATER_ID), false);
83
+ } finally {
84
+ rmSync(root, { recursive: true });
85
+ }
86
+ });
@@ -8,16 +8,27 @@
8
8
  * (`WHERE n.accountId = $accountId`) silently hides leaks from every UI;
9
9
  * an unenforced writer can leak indefinitely with zero downstream symptom.
10
10
  *
11
- * Two consumers:
12
- * 1. `writeNodeWithEdges` (platform/lib/graph-write/) central write-time
13
- * floor; rejects any write whose `props.accountId` is not in the set
14
- * returned here, unless `createdBy.agent === 'system'` (bootstrap).
15
- * 2. `resolvePlatformAccountId` (platform/ui/app/lib/whatsapp/platform-account-id.ts)
16
- * WhatsApp writer-side helper; loud-throws on zero or multi accounts.
11
+ * Consumers of the enumerated set:
12
+ * - The cross-account resolver's injected validity check (the `memory` and
13
+ * `filesystem` MCP plugins) the check that gates a `targetAccountId`.
14
+ * These call `isProvisionedAccount` (re-scan-on-miss), not the cached list
15
+ * directly, so a sub-account created at runtime is not masked by the boot
16
+ * snapshot (Task 1471).
17
+ * - `resolvePlatformAccountId` (platform/ui/app/lib/whatsapp/platform-account-id.ts)
18
+ * — WhatsApp writer-side helper; loud-throws on zero or multi accounts.
19
+ * - Boot-time env-vs-disk validation (`validateAccountIdEnv`).
17
20
  *
18
- * Per-process cache keyed on `accountsDir` Phase 0 invariant has accounts
19
- * created at install time, so adding/removing accounts requires a service
20
- * restart anyway.
21
+ * NOTE: `writeNodeWithEdges` (platform/lib/graph-write/) does NOT enumerate.
22
+ * Its account floor compares `props.accountId` against `process.env.ACCOUNT_ID`
23
+ * (the spawning process's boot-validated id), deliberately staying out of the
24
+ * `node:fs` import chain so the ESM payload bundle emits no `__require("fs")`
25
+ * shim. So the write gate does not share the cache's staleness.
26
+ *
27
+ * Per-process cache keyed on `accountsDir`. The install-time invariant (accounts
28
+ * created only at install) no longer holds: the runtime `account_create`
29
+ * lifecycle tool provisions sub-accounts during a live session. `enumerate…`
30
+ * keeps its cached-list semantics for the hit path; `isProvisionedAccount`
31
+ * re-scans on a miss so a created-after-boot account resolves without restart.
21
32
  */
22
33
  import { readdirSync, readFileSync } from "node:fs";
23
34
  import { resolve } from "node:path";
@@ -68,6 +79,31 @@ export function enumerateValidAccountIds(accountsDir: string): string[] {
68
79
  return valid;
69
80
  }
70
81
 
82
+ /**
83
+ * Membership test with re-scan-on-miss: is `id` a currently-provisioned account
84
+ * under `accountsDir`?
85
+ *
86
+ * Hit path — `id` is in the cached set — returns true with no I/O (the common
87
+ * case; the cache still serves it). Miss path — `id` is absent from the cached
88
+ * set, or the cache is cold — drops the stale entry and re-scans `accountsDir`
89
+ * once (refreshing the cache) before answering.
90
+ *
91
+ * This closes the created-after-boot gap (Task 1471): a sub-account provisioned
92
+ * by the runtime `account_create` lifecycle tool after a consumer process booted
93
+ * is invisible to that process's boot-time cached set. Without the re-scan, the
94
+ * cross-account resolver rejects the id as not-provisioned even though its dir
95
+ * and `account.json` exist on disk. A single `readdirSync` on the miss finds it;
96
+ * a genuinely-absent id pays the same one scan and correctly returns false.
97
+ */
98
+ export function isProvisionedAccount(accountsDir: string, id: string): boolean {
99
+ const cached = cache.get(accountsDir);
100
+ if (cached?.includes(id)) return true;
101
+ // Miss: `id` is not in the cached set (or the cache is cold). Drop the stale
102
+ // snapshot and re-scan disk once so a real, on-disk account is not masked.
103
+ cache.delete(accountsDir);
104
+ return enumerateValidAccountIds(accountsDir).includes(id);
105
+ }
106
+
71
107
  /**
72
108
  * Returns the single `role:"house"` account UUID under `accountsDir`.
73
109
  *