@rubytech/create-sitedesk-code 0.1.513 → 0.1.515

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 (199) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-07-27-task-2016-disabled-agent-routing.md +624 -0
  3. package/payload/platform/docs/superpowers/plans/2026-07-27-task-2052-account-machinery-write-fence.md +250 -0
  4. package/payload/platform/docs/superpowers/specs/2026-07-27-task-2016-disabled-agent-routing-design.md +139 -0
  5. package/payload/platform/docs/superpowers/specs/2026-07-27-task-2028-declared-file-write-deny-design.md +2 -1
  6. package/payload/platform/docs/superpowers/specs/2026-07-27-task-2052-account-machinery-write-fence-design.md +150 -0
  7. package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +41 -0
  8. package/payload/platform/plugins/admin/hooks/fs-schema-guard-bash-post.sh +8 -0
  9. package/payload/platform/plugins/admin/hooks/fs-schema-guard.sh +40 -1
  10. package/payload/platform/plugins/admin/mcp/dist/index.js +14 -16
  11. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  12. package/payload/platform/plugins/admin/mcp/dist/specialist-registry.d.ts +14 -0
  13. package/payload/platform/plugins/admin/mcp/dist/specialist-registry.d.ts.map +1 -0
  14. package/payload/platform/plugins/admin/mcp/dist/specialist-registry.js +57 -0
  15. package/payload/platform/plugins/admin/mcp/dist/specialist-registry.js.map +1 -0
  16. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +15 -3
  17. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
  18. package/payload/platform/plugins/cloudflare/bin/schema-exposed-dirs.mjs +1 -1
  19. package/payload/platform/plugins/docs/references/admin-ui.md +14 -2
  20. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +3 -3
  21. package/payload/platform/scripts/__tests__/agents-md-bootstrap.test.sh +47 -0
  22. package/payload/platform/scripts/lib/agents-md-bootstrap.sh +58 -1
  23. package/payload/platform/services/claude-session-manager/dist/config.d.ts +48 -12
  24. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  25. package/payload/platform/services/claude-session-manager/dist/config.js +65 -15
  26. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  27. package/payload/platform/services/claude-session-manager/dist/index.js +7 -4
  28. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  29. package/payload/platform/services/claude-session-manager/dist/rootless-client-audit.d.ts.map +1 -1
  30. package/payload/platform/services/claude-session-manager/dist/rootless-client-audit.js +17 -2
  31. package/payload/platform/services/claude-session-manager/dist/rootless-client-audit.js.map +1 -1
  32. package/payload/platform/templates/account-schema/SCHEMA.md +17 -0
  33. package/payload/server/public/activity.html +5 -5
  34. package/payload/server/public/agents.html +4 -4
  35. package/payload/server/public/assets/{AdminLoginScreens-bksN0XPe.js → AdminLoginScreens-Cs1jSnOF.js} +1 -1
  36. package/payload/server/public/assets/AdminLoginScreens-Cs1jSnOF.js.br +0 -0
  37. package/payload/server/public/assets/AdminLoginScreens-Cs1jSnOF.js.gz +0 -0
  38. package/payload/server/public/assets/{AdminShell-BMPEWNZk.js → AdminShell-DkrURZnF.js} +1 -1
  39. package/payload/server/public/assets/AdminShell-DkrURZnF.js.br +0 -0
  40. package/payload/server/public/assets/AdminShell-DkrURZnF.js.gz +0 -0
  41. package/payload/server/public/assets/{activity-ByWYF5dC.js → activity-BgZISMuE.js} +1 -1
  42. package/payload/server/public/assets/activity-BgZISMuE.js.br +0 -0
  43. package/payload/server/public/assets/activity-BgZISMuE.js.gz +0 -0
  44. package/payload/server/public/assets/{admin-6QN-k3zQ.js → admin-DjFysc3-.js} +1 -1
  45. package/payload/server/public/assets/admin-DjFysc3-.js.br +0 -0
  46. package/payload/server/public/assets/admin-DjFysc3-.js.gz +0 -0
  47. package/payload/server/public/assets/{agents-C0rTfrYd.js → agents-Bx-MSZW6.js} +1 -1
  48. package/payload/server/public/assets/agents-Bx-MSZW6.js.br +0 -0
  49. package/payload/server/public/assets/agents-Bx-MSZW6.js.gz +0 -0
  50. package/payload/server/public/assets/{browser-4tTaem8N.js → browser-1I7syhHu.js} +1 -1
  51. package/payload/server/public/assets/browser-1I7syhHu.js.br +0 -0
  52. package/payload/server/public/assets/browser-1I7syhHu.js.gz +0 -0
  53. package/payload/server/public/assets/{calendar-BEjnkKG1.js → calendar-D_04JIo1.js} +1 -1
  54. package/payload/server/public/assets/calendar-D_04JIo1.js.br +0 -0
  55. package/payload/server/public/assets/calendar-D_04JIo1.js.gz +0 -0
  56. package/payload/server/public/assets/chat-BFH3FM5a.js +1 -0
  57. package/payload/server/public/assets/chat-BFH3FM5a.js.br +0 -0
  58. package/payload/server/public/assets/chat-BFH3FM5a.js.gz +0 -0
  59. package/payload/server/public/assets/chevron-left-Ux9-qLkk.js +1 -0
  60. package/payload/server/public/assets/chevron-right-BwIuCfyG.js +1 -0
  61. package/payload/server/public/assets/chevron-right-BwIuCfyG.js.br +0 -0
  62. package/payload/server/public/assets/clock-DhJdZRhC.js +1 -0
  63. package/payload/server/public/assets/clock-DhJdZRhC.js.br +0 -0
  64. package/payload/server/public/assets/data-ChY_uSIk.js +1 -0
  65. package/payload/server/public/assets/data-ChY_uSIk.js.br +0 -0
  66. package/payload/server/public/assets/data-ChY_uSIk.js.gz +0 -0
  67. package/payload/server/public/assets/{file-text-DMKw3nIk.js → file-text-DkvKqHMx.js} +1 -1
  68. package/payload/server/public/assets/file-text-DkvKqHMx.js.br +0 -0
  69. package/payload/server/public/assets/file-text-DkvKqHMx.js.gz +0 -0
  70. package/payload/server/public/assets/{graph-C3vW8lvw.js → graph-CCZhrwZX.js} +1 -1
  71. package/payload/server/public/assets/graph-CCZhrwZX.js.br +0 -0
  72. package/payload/server/public/assets/graph-CCZhrwZX.js.gz +0 -0
  73. package/payload/server/public/assets/{graph-labels-BZgd0L6n.js → graph-labels-BJ7puQkX.js} +1 -1
  74. package/payload/server/public/assets/graph-labels-BJ7puQkX.js.br +0 -0
  75. package/payload/server/public/assets/graph-labels-BJ7puQkX.js.gz +0 -0
  76. package/payload/server/public/assets/{maximize-2-la3IBA3M.js → maximize-2-Xdj804U1.js} +1 -1
  77. package/payload/server/public/assets/maximize-2-Xdj804U1.js.br +0 -0
  78. package/payload/server/public/assets/maximize-2-Xdj804U1.js.gz +0 -0
  79. package/payload/server/public/assets/{operator-D9NgVL9_.js → operator-BkfNpBs7.js} +1 -1
  80. package/payload/server/public/assets/operator-BkfNpBs7.js.br +0 -0
  81. package/payload/server/public/assets/operator-BkfNpBs7.js.gz +0 -0
  82. package/payload/server/public/assets/{page-DC11gesX.js → page-BoYc9c4a.js} +1 -1
  83. package/payload/server/public/assets/page-BoYc9c4a.js.br +0 -0
  84. package/payload/server/public/assets/page-BoYc9c4a.js.gz +0 -0
  85. package/payload/server/public/assets/{page-BWFHRIAH.js → page-D5n8ShE7.js} +1 -1
  86. package/payload/server/public/assets/page-D5n8ShE7.js.br +0 -0
  87. package/payload/server/public/assets/page-D5n8ShE7.js.gz +0 -0
  88. package/payload/server/public/assets/{public-DQbbLHQN.js → public-CX5XSrxD.js} +1 -1
  89. package/payload/server/public/assets/public-CX5XSrxD.js.br +0 -0
  90. package/payload/server/public/assets/public-CX5XSrxD.js.gz +0 -0
  91. package/payload/server/public/assets/{rotate-ccw-OQD5si8N.js → rotate-ccw-dh_OhL23.js} +1 -1
  92. package/payload/server/public/assets/rotate-ccw-dh_OhL23.js.br +0 -0
  93. package/payload/server/public/assets/rotate-ccw-dh_OhL23.js.gz +0 -0
  94. package/payload/server/public/assets/{routines-asrBGZUR.js → routines-bb7dxcrr.js} +1 -1
  95. package/payload/server/public/assets/routines-bb7dxcrr.js.br +0 -0
  96. package/payload/server/public/assets/routines-bb7dxcrr.js.gz +0 -0
  97. package/payload/server/public/assets/{skills-D4ECwoxa.js → skills-TeMwxXmJ.js} +1 -1
  98. package/payload/server/public/assets/skills-TeMwxXmJ.js.br +0 -0
  99. package/payload/server/public/assets/skills-TeMwxXmJ.js.gz +0 -0
  100. package/payload/server/public/assets/{tasks-U3QdbUsO.js → tasks-BqBBJlkg.js} +1 -1
  101. package/payload/server/public/assets/tasks-BqBBJlkg.js.br +0 -0
  102. package/payload/server/public/assets/tasks-BqBBJlkg.js.gz +0 -0
  103. package/payload/server/public/assets/{time-entry-format-BgdTKgYr.js → time-entry-format-Cy_T-wiD.js} +1 -1
  104. package/payload/server/public/assets/time-entry-format-Cy_T-wiD.js.br +3 -0
  105. package/payload/server/public/assets/time-entry-format-Cy_T-wiD.js.gz +0 -0
  106. package/payload/server/public/assets/{triangle-alert-Ck_3VhT9.js → triangle-alert-D-bSbPnX.js} +1 -1
  107. package/payload/server/public/assets/triangle-alert-D-bSbPnX.js.br +1 -0
  108. package/payload/server/public/assets/triangle-alert-D-bSbPnX.js.gz +0 -0
  109. package/payload/server/public/assets/{useCopyFeedback-BdUwyNpa.js → useCopyFeedback-L7L55nFl.js} +1 -1
  110. package/payload/server/public/assets/useCopyFeedback-L7L55nFl.js.br +0 -0
  111. package/payload/server/public/assets/useCopyFeedback-L7L55nFl.js.gz +0 -0
  112. package/payload/server/public/assets/useSubAccountSwitcher-BYl2c7xA.css +1 -0
  113. package/payload/server/public/assets/useSubAccountSwitcher-BYl2c7xA.css.br +0 -0
  114. package/payload/server/public/assets/useSubAccountSwitcher-BYl2c7xA.css.gz +0 -0
  115. package/payload/server/public/assets/{useVoiceRecorder-ChnBrzbZ.js → useVoiceRecorder-MuZ7lpet.js} +1 -1
  116. package/payload/server/public/assets/useVoiceRecorder-MuZ7lpet.js.br +0 -0
  117. package/payload/server/public/assets/useVoiceRecorder-MuZ7lpet.js.gz +0 -0
  118. package/payload/server/public/assets/{wrench-Cpee3C3J.js → wrench-DUG1SZQB.js} +1 -1
  119. package/payload/server/public/assets/wrench-DUG1SZQB.js.br +0 -0
  120. package/payload/server/public/assets/wrench-DUG1SZQB.js.gz +0 -0
  121. package/payload/server/public/brand-defaults.css +2 -0
  122. package/payload/server/public/browser.html +4 -4
  123. package/payload/server/public/calendar.html +7 -7
  124. package/payload/server/public/chat.html +13 -13
  125. package/payload/server/public/data.html +11 -11
  126. package/payload/server/public/graph.html +9 -9
  127. package/payload/server/public/index.html +14 -14
  128. package/payload/server/public/operator.html +14 -14
  129. package/payload/server/public/public.html +13 -13
  130. package/payload/server/public/routines.html +6 -6
  131. package/payload/server/public/skills.html +5 -5
  132. package/payload/server/public/tasks.html +6 -6
  133. package/payload/server/server.js +138 -19
  134. package/payload/server/public/assets/AdminLoginScreens-bksN0XPe.js.br +0 -0
  135. package/payload/server/public/assets/AdminLoginScreens-bksN0XPe.js.gz +0 -0
  136. package/payload/server/public/assets/AdminShell-BMPEWNZk.js.br +0 -0
  137. package/payload/server/public/assets/AdminShell-BMPEWNZk.js.gz +0 -0
  138. package/payload/server/public/assets/activity-ByWYF5dC.js.br +0 -0
  139. package/payload/server/public/assets/activity-ByWYF5dC.js.gz +0 -0
  140. package/payload/server/public/assets/admin-6QN-k3zQ.js.br +0 -0
  141. package/payload/server/public/assets/admin-6QN-k3zQ.js.gz +0 -0
  142. package/payload/server/public/assets/agents-C0rTfrYd.js.br +0 -0
  143. package/payload/server/public/assets/agents-C0rTfrYd.js.gz +0 -0
  144. package/payload/server/public/assets/browser-4tTaem8N.js.br +0 -0
  145. package/payload/server/public/assets/browser-4tTaem8N.js.gz +0 -0
  146. package/payload/server/public/assets/calendar-BEjnkKG1.js.br +0 -0
  147. package/payload/server/public/assets/calendar-BEjnkKG1.js.gz +0 -0
  148. package/payload/server/public/assets/chat-DBgzjDIE.js +0 -1
  149. package/payload/server/public/assets/chat-DBgzjDIE.js.br +0 -0
  150. package/payload/server/public/assets/chat-DBgzjDIE.js.gz +0 -0
  151. package/payload/server/public/assets/chevron-left-DqYv3oFh.js +0 -1
  152. package/payload/server/public/assets/chevron-right-CQfPGsFb.js +0 -1
  153. package/payload/server/public/assets/chevron-right-CQfPGsFb.js.br +0 -0
  154. package/payload/server/public/assets/clock-Dn6FHB51.js +0 -1
  155. package/payload/server/public/assets/clock-Dn6FHB51.js.br +0 -0
  156. package/payload/server/public/assets/clock-Dn6FHB51.js.gz +0 -0
  157. package/payload/server/public/assets/data-D_e6Vdfd.js +0 -1
  158. package/payload/server/public/assets/data-D_e6Vdfd.js.br +0 -0
  159. package/payload/server/public/assets/data-D_e6Vdfd.js.gz +0 -0
  160. package/payload/server/public/assets/file-text-DMKw3nIk.js.br +0 -0
  161. package/payload/server/public/assets/file-text-DMKw3nIk.js.gz +0 -0
  162. package/payload/server/public/assets/graph-C3vW8lvw.js.br +0 -0
  163. package/payload/server/public/assets/graph-C3vW8lvw.js.gz +0 -0
  164. package/payload/server/public/assets/graph-labels-BZgd0L6n.js.br +0 -0
  165. package/payload/server/public/assets/graph-labels-BZgd0L6n.js.gz +0 -0
  166. package/payload/server/public/assets/maximize-2-la3IBA3M.js.br +0 -0
  167. package/payload/server/public/assets/maximize-2-la3IBA3M.js.gz +0 -0
  168. package/payload/server/public/assets/operator-D9NgVL9_.js.br +0 -0
  169. package/payload/server/public/assets/operator-D9NgVL9_.js.gz +0 -0
  170. package/payload/server/public/assets/page-BWFHRIAH.js.br +0 -0
  171. package/payload/server/public/assets/page-BWFHRIAH.js.gz +0 -0
  172. package/payload/server/public/assets/page-DC11gesX.js.br +0 -0
  173. package/payload/server/public/assets/page-DC11gesX.js.gz +0 -0
  174. package/payload/server/public/assets/public-DQbbLHQN.js.br +0 -0
  175. package/payload/server/public/assets/public-DQbbLHQN.js.gz +0 -0
  176. package/payload/server/public/assets/rotate-ccw-OQD5si8N.js.br +0 -0
  177. package/payload/server/public/assets/rotate-ccw-OQD5si8N.js.gz +0 -0
  178. package/payload/server/public/assets/routines-asrBGZUR.js.br +0 -0
  179. package/payload/server/public/assets/routines-asrBGZUR.js.gz +0 -0
  180. package/payload/server/public/assets/skills-D4ECwoxa.js.br +0 -0
  181. package/payload/server/public/assets/skills-D4ECwoxa.js.gz +0 -0
  182. package/payload/server/public/assets/tasks-U3QdbUsO.js.br +0 -0
  183. package/payload/server/public/assets/tasks-U3QdbUsO.js.gz +0 -0
  184. package/payload/server/public/assets/time-entry-format-BgdTKgYr.js.br +0 -0
  185. package/payload/server/public/assets/time-entry-format-BgdTKgYr.js.gz +0 -0
  186. package/payload/server/public/assets/triangle-alert-Ck_3VhT9.js.br +0 -0
  187. package/payload/server/public/assets/triangle-alert-Ck_3VhT9.js.gz +0 -0
  188. package/payload/server/public/assets/useCopyFeedback-BdUwyNpa.js.br +0 -0
  189. package/payload/server/public/assets/useCopyFeedback-BdUwyNpa.js.gz +0 -0
  190. package/payload/server/public/assets/useSubAccountSwitcher-DOWQXDT8.css +0 -1
  191. package/payload/server/public/assets/useSubAccountSwitcher-DOWQXDT8.css.br +0 -0
  192. package/payload/server/public/assets/useSubAccountSwitcher-DOWQXDT8.css.gz +0 -0
  193. package/payload/server/public/assets/useVoiceRecorder-ChnBrzbZ.js.br +0 -0
  194. package/payload/server/public/assets/useVoiceRecorder-ChnBrzbZ.js.gz +0 -0
  195. package/payload/server/public/assets/wrench-Cpee3C3J.js.br +0 -2
  196. package/payload/server/public/assets/wrench-Cpee3C3J.js.gz +0 -0
  197. /package/payload/server/public/assets/{useSubAccountSwitcher-CJKjEPNN.js → useSubAccountSwitcher-DH0P6QMV.js} +0 -0
  198. /package/payload/server/public/assets/{useSubAccountSwitcher-CJKjEPNN.js.br → useSubAccountSwitcher-DH0P6QMV.js.br} +0 -0
  199. /package/payload/server/public/assets/{useSubAccountSwitcher-CJKjEPNN.js.gz → useSubAccountSwitcher-DH0P6QMV.js.gz} +0 -0
@@ -3219,7 +3219,7 @@ var WhatsAppConfigSchema = z.object({
3219
3219
  z.string(),
3220
3220
  z.object({ managesAccount: z.string(), mode: z.enum(["active", "passive"]).optional() })
3221
3221
  ])
3222
- ).optional().describe("Account-manager bindings: E.164 phone \u2192 sub-account. The value is a bare sub-account UUID (mode 'active', the default: a full admin session) or { managesAccount, mode } where mode 'passive' selects a constrained requirement-intake spawn (only work-create, no reply). A bare string and an object without mode both resolve to 'active'. Managed via add-account-manager / remove-account-manager. A phone here must not also be in adminPhones."),
3222
+ ).optional().describe("Account-manager bindings: E.164 phone \u2192 sub-account. The value is a bare sub-account UUID (mode 'active', the default: a full admin session) or { managesAccount, mode } where mode 'passive' selects a constrained requirement-intake spawn (only work-create, no reply). A bare string and an object without mode both resolve to 'active'. Managed via add-account-manager / remove-account-manager. A phone here must not also be in adminPhones. Read only from the account whose account.json the manager loads its config from (the house); add-account-manager is refused on any other account, and a map left there is inert and purged at boot."),
3223
3223
  allowFrom: z.array(z.string()).optional().describe("Phone numbers allowed to message the public agent when DM policy is 'allowlist'. Add '*' for open access. Does not affect admin routing \u2014 use adminPhones for that."),
3224
3224
  sendReadReceipts: z.boolean().optional().default(true).describe("Whether to send read receipts (blue ticks) when messages are received. Disabling may feel less responsive but preserves privacy."),
3225
3225
  mediaMaxMb: z.number().int().positive().optional().default(50).describe("Maximum file size in MB for media the agent will download and process."),
@@ -5104,6 +5104,9 @@ function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
5104
5104
  const routed = readChannelRoutingOverride(accountsDir, houseId);
5105
5105
  return routed ?? houseId;
5106
5106
  }
5107
+ function resolveHouseAccountId(accountsDir = ACCOUNTS_DIR) {
5108
+ return resolveHouseId(accountsDir);
5109
+ }
5107
5110
 
5108
5111
  // app/lib/whatsapp/account-id-shape.ts
5109
5112
  var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -6277,10 +6280,12 @@ async function startConnection(accountId) {
6277
6280
  return;
6278
6281
  }
6279
6282
  let platformAccountId;
6283
+ let houseAccountId;
6280
6284
  try {
6281
6285
  platformAccountId = resolvePlatformAccountId();
6286
+ houseAccountId = resolveHouseAccountId();
6282
6287
  console.error(
6283
- `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId}`
6288
+ `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId} house=${houseAccountId}`
6284
6289
  );
6285
6290
  } catch (err) {
6286
6291
  const reason = err instanceof Error ? err.message : String(err);
@@ -6293,6 +6298,7 @@ async function startConnection(accountId) {
6293
6298
  const conn = {
6294
6299
  accountId,
6295
6300
  platformAccountId,
6301
+ houseAccountId,
6296
6302
  accountName: whatsAppConfig.accounts?.[accountId]?.name,
6297
6303
  authDir,
6298
6304
  sock: null,
@@ -6370,6 +6376,10 @@ function resolveInboundPersistAccount(args) {
6370
6376
  warnNonUuidAccount("inbound-persist", account);
6371
6377
  return account;
6372
6378
  }
6379
+ function isManagerRoutedToHouse(args) {
6380
+ const result = args.accessResult;
6381
+ return result?.allowed === true && result.reason === "account-manager" && result.effectiveAccountId === args.houseAccountId;
6382
+ }
6373
6383
  function resolveConversationPersistAccount(sendAccountId, counterpartyPhone) {
6374
6384
  const conn = findConnectionByKey(Array.from(connections2.values()), sendAccountId);
6375
6385
  if (!conn) {
@@ -6399,10 +6409,12 @@ function listConnectionPlatformAccountIds() {
6399
6409
  async function registerLoginSocket(accountId, sock, authDir) {
6400
6410
  if (!configDir) throw new Error("WhatsApp manager not initialized");
6401
6411
  let platformAccountId;
6412
+ let houseAccountId;
6402
6413
  try {
6403
6414
  platformAccountId = resolvePlatformAccountId();
6415
+ houseAccountId = resolveHouseAccountId();
6404
6416
  console.error(
6405
- `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId}`
6417
+ `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId} house=${houseAccountId}`
6406
6418
  );
6407
6419
  } catch (err) {
6408
6420
  const reason = err instanceof Error ? err.message : String(err);
@@ -6415,6 +6427,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
6415
6427
  const conn = {
6416
6428
  accountId,
6417
6429
  platformAccountId,
6430
+ houseAccountId,
6418
6431
  accountName: whatsAppConfig.accounts?.[accountId]?.name,
6419
6432
  authDir,
6420
6433
  sock,
@@ -7079,7 +7092,10 @@ async function handleInboundMessage(conn, msg) {
7079
7092
  `[whatsapp-native] op=account-manager-reject senderId=${senderPhone} reason=unresolved-effective-account`
7080
7093
  );
7081
7094
  }
7082
- if (accessResult.allowed && accessResult.reason === "account-manager" && accessResult.effectiveAccountId === conn.accountId) {
7095
+ if (isManagerRoutedToHouse({
7096
+ accessResult,
7097
+ houseAccountId: conn.houseAccountId
7098
+ })) {
7083
7099
  console.error(
7084
7100
  `[whatsapp-native] op=escalation-tripwire senderId=${senderPhone} reason=account-manager-routed-to-house effectiveAccount=${accessResult.effectiveAccountId}`
7085
7101
  );
@@ -8107,7 +8123,7 @@ function createScheduleInjectRoutes(deps) {
8107
8123
  const houseAccountId = account.accountId;
8108
8124
  console.error(`${TAG21} op=received eventId=${eventId} channel=${channel} destination=${destination} account=${houseAccountId}`);
8109
8125
  if (channel === "whatsapp") {
8110
- const effectiveAccount2 = deps.effectiveAccountFor(houseAccountId, account.accountDir, destination);
8126
+ const effectiveAccount2 = deps.effectiveAccountFor(account.accountDir, destination);
8111
8127
  if (effectiveAccount2 === null) {
8112
8128
  console.error(`${TAG21} op=schedule-account-manager-reject eventId=${eventId} destination=${destination} reason=unresolved-effective-account`);
8113
8129
  return c.json({ ok: false, error: "account-manager-unresolved" }, 403);
@@ -8425,6 +8441,54 @@ function purgeNonSocketAdminPhonesAtBoot() {
8425
8441
  purgeNonSocketAdminPhones(acct.accountDir, socketOwner);
8426
8442
  }
8427
8443
  }
8444
+ function purgeNonSocketAccountManagers(accountDir, socketOwnerId) {
8445
+ try {
8446
+ const config = readConfig(accountDir);
8447
+ const wa = config.whatsapp;
8448
+ if (!wa || typeof wa !== "object") return { removed: [] };
8449
+ const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(resolve9(accountDir));
8450
+ if (targetId === socketOwnerId) return { removed: [] };
8451
+ const map = wa.accountManagers;
8452
+ if (!map || typeof map !== "object" || Array.isArray(map)) return { removed: [] };
8453
+ const removed = Object.entries(map).map(([phone, entry]) => ({
8454
+ phone,
8455
+ managesAccount: typeof entry === "string" ? entry : entry && typeof entry === "object" && typeof entry.managesAccount === "string" ? entry.managesAccount : ""
8456
+ }));
8457
+ if (removed.length === 0) return { removed: [] };
8458
+ delete wa.accountManagers;
8459
+ writeConfig(accountDir, config);
8460
+ for (const { phone, managesAccount } of removed) {
8461
+ console.error(`[admin-identity] op=purge-nonsocket-accountmanager accountId=${targetId} phone=${phone} managesAccount=${managesAccount} removed=true`);
8462
+ }
8463
+ return { removed };
8464
+ } catch (err) {
8465
+ console.error(`[admin-identity] purge-nonsocket-accountmanager failed dir=${accountDir}: ${String(err)}`);
8466
+ return { removed: [] };
8467
+ }
8468
+ }
8469
+ function purgeNonSocketAccountManagersAtBoot() {
8470
+ let socketOwner;
8471
+ try {
8472
+ socketOwner = resolveHouseOrSoleAccountId(ACCOUNTS_DIR);
8473
+ } catch (err) {
8474
+ console.error(`[admin-identity] purge-nonsocket-accountmanager skipped \u2014 cannot resolve socket owner: ${String(err)}`);
8475
+ return;
8476
+ }
8477
+ const configOwner = resolveAccount()?.accountId ?? null;
8478
+ if (configOwner !== socketOwner) {
8479
+ console.error(`[admin-identity] purge-nonsocket-accountmanager skipped \u2014 resolver disagreement house=${socketOwner} configLoadedFrom=${configOwner ?? "unresolved"}`);
8480
+ return;
8481
+ }
8482
+ let accounts;
8483
+ try {
8484
+ accounts = listValidAccounts();
8485
+ } catch {
8486
+ return;
8487
+ }
8488
+ for (const acct of accounts) {
8489
+ purgeNonSocketAccountManagers(acct.accountDir, socketOwner);
8490
+ }
8491
+ }
8428
8492
  function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
8429
8493
  const normalized = phone.trim();
8430
8494
  if (!E164_PATTERN.test(normalized)) {
@@ -8443,8 +8507,31 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
8443
8507
  if (!validIds.includes(sub)) {
8444
8508
  return { ok: false, error: `Sub-account "${sub}" is not in this install's account registry. Check the sub-account id and try again.` };
8445
8509
  }
8510
+ let houseId;
8511
+ try {
8512
+ houseId = resolveHouseOrSoleAccountId(dirname2(resolve9(accountDir)));
8513
+ } catch (err) {
8514
+ return { ok: false, error: `Could not resolve the house account to validate the binding: ${err instanceof Error ? err.message : String(err)}. Try again.` };
8515
+ }
8516
+ if (sub === houseId) {
8517
+ console.error(`${TAG22} set-account-manager rejected phone=${normalized} reason=house-account-binding`);
8518
+ return {
8519
+ ok: false,
8520
+ reason: "house-account-binding",
8521
+ error: `${sub} is the house account, not a sub-account. An account manager bound to it would get house-owner admin scope. To make ${normalized} a house admin, add it to admin phones instead.`
8522
+ };
8523
+ }
8446
8524
  try {
8447
8525
  const config = readConfig(accountDir);
8526
+ const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(resolve9(accountDir));
8527
+ if (targetId !== houseId) {
8528
+ console.error(`${TAG22} set-account-manager rejected phone=${normalized} reason=not-socket-account`);
8529
+ return {
8530
+ ok: false,
8531
+ reason: "not-socket-account",
8532
+ error: `The account-manager routing table is read from ${houseId}'s account.json only, so a binding stored on ${targetId} is never consulted. Bind ${normalized} on ${houseId} instead.`
8533
+ };
8534
+ }
8448
8535
  if (!config.whatsapp || typeof config.whatsapp !== "object") {
8449
8536
  config.whatsapp = {};
8450
8537
  }
@@ -9622,7 +9709,8 @@ app2.post("/config", async (c) => {
9622
9709
  }
9623
9710
  const managerMode = mode === "passive" ? "passive" : "active";
9624
9711
  const result = setAccountManager(account.accountDir, phone, managesAccount, managerMode);
9625
- console.error(`${TAG26} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} mode=${managerMode} ok=${result.ok}`);
9712
+ const managerReasonTail = !result.ok && result.reason ? ` reason=${result.reason}` : "";
9713
+ console.error(`${TAG26} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} mode=${managerMode} ok=${result.ok}${managerReasonTail}`);
9626
9714
  return c.json(result, result.ok ? 200 : 400);
9627
9715
  }
9628
9716
  case "remove-account-manager": {
@@ -17031,6 +17119,7 @@ var session_upload_default = app17;
17031
17119
  // server/routes/admin/agents.ts
17032
17120
  import { resolve as resolve23 } from "path";
17033
17121
  import { readdirSync as readdirSync22, readFileSync as readFileSync33, existsSync as existsSync31, rmSync as rmSync2, renameSync as renameSync9, mkdirSync as mkdirSync7, writeFileSync as writeFileSync13 } from "fs";
17122
+ import { execFileSync as execFileSync6 } from "child_process";
17034
17123
 
17035
17124
  // ../services/claude-session-manager/src/tool-surface.ts
17036
17125
  import { readFileSync as readFileSync32, readdirSync as readdirSync21, statSync as statSync15, existsSync as existsSync30 } from "fs";
@@ -17780,6 +17869,17 @@ async function resolveWritableAccount(c, op, slug) {
17780
17869
  }
17781
17870
  return account;
17782
17871
  }
17872
+ function reconcileRoutingProse(accountDir, accountId, op, slug) {
17873
+ const script = resolve23(PLATFORM_ROOT, "scripts", "lib", "agents-md-bootstrap.sh");
17874
+ try {
17875
+ execFileSync6("bash", [script, accountDir], { encoding: "utf-8", timeout: 1e4 });
17876
+ return true;
17877
+ } catch (err) {
17878
+ const msg = err instanceof Error ? err.message : String(err);
17879
+ console.error(`[admin/agents] op=agents-md-reconcile source=${op} accountId=${accountId.slice(0, 8)} slug=${slug} outcome=failed reason="${msg}"`);
17880
+ return false;
17881
+ }
17882
+ }
17783
17883
  app18.post("/:slug/disable", async (c) => {
17784
17884
  const slug = c.req.param("slug");
17785
17885
  if (SLUG_UNSAFE(slug)) return c.json({ error: "Invalid agent slug" }, 400);
@@ -17807,8 +17907,9 @@ app18.post("/:slug/disable", async (c) => {
17807
17907
  console.error(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason="${msg}"`);
17808
17908
  return c.json({ error: `Disable failed, disabled store unreadable or unwritable: ${msg}` }, 500);
17809
17909
  }
17810
- console.log(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=ok moved=${moved}`);
17811
- return c.json({ ok: true, moved });
17910
+ const routingReconciled = reconcileRoutingProse(resolved.accountDir, resolved.accountId, "disable", slug);
17911
+ console.log(`[admin/agents] op=disable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=ok moved=${moved} routingReconciled=${routingReconciled}`);
17912
+ return c.json({ ok: true, moved, routingReconciled });
17812
17913
  });
17813
17914
  app18.post("/:slug/enable", async (c) => {
17814
17915
  const slug = c.req.param("slug");
@@ -17837,8 +17938,9 @@ app18.post("/:slug/enable", async (c) => {
17837
17938
  console.error(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=failed reason="${msg}"`);
17838
17939
  return c.json({ error: `Enable failed: ${msg}` }, 500);
17839
17940
  }
17840
- console.log(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=ok restored=${restored}`);
17841
- return c.json({ ok: true, restored });
17941
+ const routingReconciled = reconcileRoutingProse(resolved.accountDir, resolved.accountId, "enable", slug);
17942
+ console.log(`[admin/agents] op=enable accountId=${resolved.accountId.slice(0, 8)} slug=${slug} outcome=ok restored=${restored} routingReconciled=${routingReconciled}`);
17943
+ return c.json({ ok: true, restored, routingReconciled });
17842
17944
  });
17843
17945
  app18.delete("/:slug", async (c) => {
17844
17946
  const slug = c.req.param("slug");
@@ -26555,7 +26657,7 @@ var admin_users_list_default = app56;
26555
26657
  import { readdirSync as readdirSync27 } from "fs";
26556
26658
  var app57 = new Hono();
26557
26659
  var TAG47 = "[activity]";
26558
- function resolveHouseAccountId(accounts) {
26660
+ function resolveHouseAccountId2(accounts) {
26559
26661
  const houses = accounts.filter((a) => a.config.role === "house");
26560
26662
  if (houses.length === 1) return { accountId: houses[0].accountId, reason: "house" };
26561
26663
  if (houses.length > 1) return { accountId: null, reason: "multiple-houses" };
@@ -26580,7 +26682,7 @@ app57.get("/", requireAdminSession, async (c) => {
26580
26682
  const cacheKey = c.var.cacheKey;
26581
26683
  const caller2 = getAccountIdForSession(cacheKey) ?? null;
26582
26684
  const valid = listValidAccounts();
26583
- const house = resolveHouseAccountId(valid);
26685
+ const house = resolveHouseAccountId2(valid);
26584
26686
  const isHouse = caller2 !== null && house.accountId !== null && caller2 === house.accountId;
26585
26687
  console.log(
26586
26688
  `${TAG47} op=request req=${req} caller=${short(caller2)} house=${isHouse} houseVia=${house.reason}`
@@ -31749,6 +31851,22 @@ function warnOnChannelAdminBindingDrift() {
31749
31851
  }
31750
31852
  }
31751
31853
 
31854
+ // server/routes/channel/effective-account.ts
31855
+ function resolveScheduledEffectiveAccount(deps, accountDir, destination) {
31856
+ const managed = managedAccountFor(deps.readAccountManagers(accountDir), destination);
31857
+ if (managed === null) return deps.resolvePlatformAccountId();
31858
+ if (managed.mode === "passive") return null;
31859
+ if (!deps.isValidAccountId(managed.account)) return null;
31860
+ return managed.account;
31861
+ }
31862
+ function resolveScheduledEffectiveAccountForInstall(accountDir, destination) {
31863
+ return resolveScheduledEffectiveAccount(
31864
+ { readAccountManagers, resolvePlatformAccountId, isValidAccountId },
31865
+ accountDir,
31866
+ destination
31867
+ );
31868
+ }
31869
+
31752
31870
  // server/lib/booking-site-accounts.ts
31753
31871
  import { existsSync as existsSync46, readFileSync as readFileSync46 } from "fs";
31754
31872
  import { resolve as resolve43 } from "path";
@@ -32481,13 +32599,13 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
32481
32599
  const sent = await sendTelegramText(botToken, chatId, text);
32482
32600
  if (!sent.ok) throw new Error(sent.error ?? "telegram send failed");
32483
32601
  },
32484
- effectiveAccountFor: (accountId, accountDir, destination) => {
32485
- const managed = managedAccountFor(readAccountManagers(accountDir), destination);
32486
- if (managed === null) return accountId;
32487
- if (managed.mode === "passive") return null;
32488
- if (!isValidAccountId(managed.account)) return null;
32489
- return managed.account;
32490
- }
32602
+ // Task 2051 the four branches moved into their own module so they have a
32603
+ // test, and so the non-manager branch resolves the channel account through
32604
+ // `resolvePlatformAccountId` (the function the live inbound path uses) rather
32605
+ // than returning the house id this closure used to be handed. See
32606
+ // effective-account.ts for why `resolveAccount()` was the wrong side to move,
32607
+ // and why the real dependencies are bound there rather than assembled here.
32608
+ effectiveAccountFor: resolveScheduledEffectiveAccountForInstall
32491
32609
  });
32492
32610
  app69.route("/api/channel/schedule-inject", scheduleInjectRoutes);
32493
32611
  app69.use("*", clientIpMiddleware);
@@ -33789,6 +33907,7 @@ for (const acct of listValidAccounts()) {
33789
33907
  cleanupLeakedPremiumSubs(acct.accountDir, acct.config, acct.accountId);
33790
33908
  }
33791
33909
  purgeNonSocketAdminPhonesAtBoot();
33910
+ purgeNonSocketAccountManagersAtBoot();
33792
33911
  void preloadEmbedModel();
33793
33912
  warnOnChannelAdminBindingDrift();
33794
33913
  var bootEnabled = Array.isArray(bootAccountConfig?.enabledPlugins) ? bootAccountConfig.enabledPlugins : [];
@@ -1 +0,0 @@
1
- import{B as e,W as t}from"./useSubAccountSwitcher-CJKjEPNN.js";import"./useVoiceRecorder-ChnBrzbZ.js";import"./AdminShell-BMPEWNZk.js";import{n}from"./page-DC11gesX.js";import"./useCopyFeedback-BdUwyNpa.js";var r=t(),i=e();(0,r.createRoot)(document.getElementById(`root`)).render((0,i.jsx)(n,{}));
@@ -1 +0,0 @@
1
- import{P as e}from"./useSubAccountSwitcher-CJKjEPNN.js";var t=e(`chevron-left`,[[`path`,{d:`m15 18-6-6 6-6`,key:`1wnfg3`}]]);export{t};
@@ -1 +0,0 @@
1
- import{P as e}from"./useSubAccountSwitcher-CJKjEPNN.js";var t=e(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]);export{t};
@@ -1 +0,0 @@
1
- import{P as e}from"./useSubAccountSwitcher-CJKjEPNN.js";var t=e(`clock`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 6v6l4 2`,key:`mmk7yg`}]]);export{t};
@@ -1 +0,0 @@
1
- import{B as e,W as t}from"./useSubAccountSwitcher-CJKjEPNN.js";import"./useVoiceRecorder-ChnBrzbZ.js";import{t as n}from"./page-BWFHRIAH.js";import"./graph-labels-BZgd0L6n.js";var r=t(),i=e();(0,r.createRoot)(document.getElementById(`root`)).render((0,i.jsx)(n,{}));