@rubytech/create-realagent-code 0.1.582 → 0.1.584

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 (311) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/routine-templates/dist/index.d.ts +1 -1
  3. package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/routine-templates/dist/index.js +5 -1
  5. package/payload/platform/lib/routine-templates/dist/index.js.map +1 -1
  6. package/payload/platform/lib/routine-templates/src/index.ts +4 -0
  7. package/payload/platform/lib/telegram-managed-bot/dist/index.d.ts +269 -0
  8. package/payload/platform/lib/telegram-managed-bot/dist/index.d.ts.map +1 -0
  9. package/payload/platform/lib/telegram-managed-bot/dist/index.js +351 -0
  10. package/payload/platform/lib/telegram-managed-bot/dist/index.js.map +1 -0
  11. package/payload/platform/lib/telegram-managed-bot/src/__tests__/managed-bot.test.ts +251 -0
  12. package/payload/platform/lib/telegram-managed-bot/src/index.ts +529 -0
  13. package/payload/platform/lib/telegram-managed-bot/tsconfig.json +9 -0
  14. package/payload/platform/lib/telegram-managed-bot/vitest.config.ts +9 -0
  15. package/payload/platform/lib/telegram-reach/dist/index.d.ts +86 -0
  16. package/payload/platform/lib/telegram-reach/dist/index.d.ts.map +1 -0
  17. package/payload/platform/lib/telegram-reach/dist/index.js +246 -0
  18. package/payload/platform/lib/telegram-reach/dist/index.js.map +1 -0
  19. package/payload/platform/lib/telegram-reach/src/__tests__/telegram-reach.test.ts +268 -0
  20. package/payload/platform/lib/telegram-reach/src/index.ts +296 -0
  21. package/payload/platform/lib/telegram-reach/tsconfig.json +9 -0
  22. package/payload/platform/lib/telegram-reach/vitest.config.ts +9 -0
  23. package/payload/platform/package.json +2 -2
  24. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  25. package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +9 -0
  26. package/payload/platform/plugins/admin/hooks/__tests__/preference-consult-directive.test.sh +26 -0
  27. package/payload/platform/plugins/admin/hooks/fs-schema-guard.sh +20 -3
  28. package/payload/platform/plugins/admin/hooks/preference-consult-directive.sh +50 -5
  29. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +146 -8
  30. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +16 -0
  31. package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.d.ts +2 -0
  32. package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.d.ts.map +1 -0
  33. package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.js +100 -0
  34. package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.js.map +1 -0
  35. package/payload/platform/plugins/contacts/mcp/dist/index.js +11 -5
  36. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  37. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +5 -0
  38. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
  39. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +21 -4
  40. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
  41. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +5 -0
  42. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
  43. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +29 -1
  44. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
  45. package/payload/platform/plugins/contacts/mcp/vitest.config.ts +14 -0
  46. package/payload/platform/plugins/docs/references/admin-ui.md +17 -3
  47. package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +15 -0
  48. package/payload/platform/plugins/docs/references/telegram-guide.md +113 -4
  49. package/payload/platform/plugins/filesystem/PLUGIN.md +2 -2
  50. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.d.ts +2 -0
  51. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.d.ts.map +1 -0
  52. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.js +108 -0
  53. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.js.map +1 -0
  54. package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.d.ts +17 -0
  55. package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.d.ts.map +1 -0
  56. package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.js +162 -0
  57. package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.js.map +1 -0
  58. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.d.ts +2 -0
  59. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.d.ts.map +1 -0
  60. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.js +116 -0
  61. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.js.map +1 -0
  62. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.d.ts.map +1 -1
  63. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js +9 -0
  64. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js.map +1 -1
  65. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.d.ts.map +1 -1
  66. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js +10 -0
  67. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js.map +1 -1
  68. package/payload/platform/plugins/scheduling/PLUGIN.md +8 -2
  69. package/payload/platform/plugins/scheduling/mcp/dist/index.js +7 -2
  70. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  71. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js +53 -6
  72. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js.map +1 -1
  73. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js +38 -0
  74. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js.map +1 -1
  75. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +5 -3
  76. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -1
  77. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js +18 -3
  78. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -1
  79. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +30 -0
  80. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
  81. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +112 -39
  82. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
  83. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +14 -1
  84. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  85. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +258 -8
  86. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -1
  87. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +206 -3
  88. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
  89. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +29 -21
  90. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  91. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +94 -14
  92. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  93. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  94. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +29 -1
  95. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  96. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +30 -0
  97. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
  98. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +69 -6
  99. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
  100. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +12 -0
  101. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts +36 -5
  102. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -1
  103. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +103 -10
  104. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -1
  105. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  106. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -1
  107. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  108. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  109. package/payload/platform/services/claude-session-manager/dist/http-server.js +42 -4
  110. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  111. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  112. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +19 -5
  113. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  114. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +9 -0
  115. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  116. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +5 -1
  117. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  118. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
  119. package/payload/platform/services/telegram-channel/dist/instructions.js +13 -0
  120. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
  121. package/payload/platform/services/telegram-channel/dist/notification.d.ts +13 -0
  122. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  123. package/payload/platform/services/telegram-channel/dist/notification.js +13 -2
  124. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  125. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +52 -0
  126. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  127. package/payload/platform/services/whatsapp-channel/dist/notification.js +64 -1
  128. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  129. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  130. package/payload/platform/services/whatsapp-channel/dist/server.js +14 -2
  131. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  132. package/payload/platform/templates/account-schema/SCHEMA.md +17 -10
  133. package/payload/platform/templates/agents/admin/IDENTITY.md +5 -0
  134. package/payload/platform/templates/specialists/agents/data-manager.md +2 -2
  135. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  136. package/payload/server/{chunk-BFRB2U75.js → chunk-CV6AEMKH.js} +1 -0
  137. package/payload/server/{manager-CTVTF4YB.js → manager-FQSQSSJ3.js} +1 -1
  138. package/payload/server/public/activity.html +6 -6
  139. package/payload/server/public/agents.html +5 -5
  140. package/payload/server/public/assets/{AdminLoginScreens-AnJZfgFz.js → AdminLoginScreens-BLsDYWVi.js} +1 -1
  141. package/payload/server/public/assets/AdminLoginScreens-BLsDYWVi.js.br +0 -0
  142. package/payload/server/public/assets/AdminLoginScreens-BLsDYWVi.js.gz +0 -0
  143. package/payload/server/public/assets/{AdminShell-CEVqyQqg.js → AdminShell-BZS8jzFA.js} +1 -1
  144. package/payload/server/public/assets/AdminShell-BZS8jzFA.js.br +0 -0
  145. package/payload/server/public/assets/AdminShell-BZS8jzFA.js.gz +0 -0
  146. package/payload/server/public/assets/{activity-czUaUl8I.js → activity-DOjvoLdd.js} +1 -1
  147. package/payload/server/public/assets/activity-DOjvoLdd.js.br +0 -0
  148. package/payload/server/public/assets/activity-DOjvoLdd.js.gz +0 -0
  149. package/payload/server/public/assets/{admin-B0iRcfuk.js → admin-JrSNg29g.js} +1 -1
  150. package/payload/server/public/assets/admin-JrSNg29g.js.br +0 -0
  151. package/payload/server/public/assets/admin-JrSNg29g.js.gz +0 -0
  152. package/payload/server/public/assets/{agents-BocONCeQ.js → agents--9WMJVLC.js} +1 -1
  153. package/payload/server/public/assets/agents--9WMJVLC.js.br +0 -0
  154. package/payload/server/public/assets/agents--9WMJVLC.js.gz +0 -0
  155. package/payload/server/public/assets/{browser-Cy-rJm3t.js → browser-DiiMmTrM.js} +1 -1
  156. package/payload/server/public/assets/browser-DiiMmTrM.js.br +0 -0
  157. package/payload/server/public/assets/browser-DiiMmTrM.js.gz +0 -0
  158. package/payload/server/public/assets/{calendar-DUTKxWtd.js → calendar-CazmCzgO.js} +1 -1
  159. package/payload/server/public/assets/calendar-CazmCzgO.js.br +0 -0
  160. package/payload/server/public/assets/calendar-CazmCzgO.js.gz +0 -0
  161. package/payload/server/public/assets/chat-CB0TcyXX.js +1 -0
  162. package/payload/server/public/assets/chat-CB0TcyXX.js.br +0 -0
  163. package/payload/server/public/assets/chat-CB0TcyXX.js.gz +0 -0
  164. package/payload/server/public/assets/chevron-left-0tVo23Dh.js +1 -0
  165. package/payload/server/public/assets/chevron-left-0tVo23Dh.js.br +0 -0
  166. package/payload/server/public/assets/clock-D2cZ0sge.js +1 -0
  167. package/payload/server/public/assets/clock-D2cZ0sge.js.br +0 -0
  168. package/payload/server/public/assets/clock-D2cZ0sge.js.gz +0 -0
  169. package/payload/server/public/assets/{copy-DE9qmAs5.js → copy-C87QIN_o.js} +1 -1
  170. package/payload/server/public/assets/copy-C87QIN_o.js.br +0 -0
  171. package/payload/server/public/assets/copy-C87QIN_o.js.gz +0 -0
  172. package/payload/server/public/assets/data-CK7AMxPt.js +1 -0
  173. package/payload/server/public/assets/data-CK7AMxPt.js.br +0 -0
  174. package/payload/server/public/assets/data-CK7AMxPt.js.gz +0 -0
  175. package/payload/server/public/assets/{field-DE2tEfTo.js → field-C3kQgGWE.js} +1 -1
  176. package/payload/server/public/assets/field-C3kQgGWE.js.br +0 -0
  177. package/payload/server/public/assets/field-C3kQgGWE.js.gz +0 -0
  178. package/payload/server/public/assets/{file-text-BHfL2gqo.js → file-text-rWowl9RN.js} +1 -1
  179. package/payload/server/public/assets/file-text-rWowl9RN.js.br +0 -0
  180. package/payload/server/public/assets/file-text-rWowl9RN.js.gz +0 -0
  181. package/payload/server/public/assets/{graph-08S_BofN.js → graph-CVJKz1Uo.js} +1 -1
  182. package/payload/server/public/assets/graph-CVJKz1Uo.js.br +0 -0
  183. package/payload/server/public/assets/graph-CVJKz1Uo.js.gz +0 -0
  184. package/payload/server/public/assets/{graph-labels-C640Er5R.js → graph-labels-kneGnsHB.js} +1 -1
  185. package/payload/server/public/assets/graph-labels-kneGnsHB.js.br +0 -0
  186. package/payload/server/public/assets/graph-labels-kneGnsHB.js.gz +0 -0
  187. package/payload/server/public/assets/{jsx-runtime-FxjwOUbA.css → jsx-runtime-rMaiYjNu.css} +1 -1
  188. package/payload/server/public/assets/jsx-runtime-rMaiYjNu.css.br +0 -0
  189. package/payload/server/public/assets/{jsx-runtime-FxjwOUbA.css.gz → jsx-runtime-rMaiYjNu.css.gz} +0 -0
  190. package/payload/server/public/assets/{operator-DH5Kw3fL.js → operator-BGvB4hu4.js} +1 -1
  191. package/payload/server/public/assets/operator-BGvB4hu4.js.br +0 -0
  192. package/payload/server/public/assets/operator-BGvB4hu4.js.gz +0 -0
  193. package/payload/server/public/assets/{page-BDpa4HWT.js → page-DBQLOasI.js} +1 -1
  194. package/payload/server/public/assets/page-DBQLOasI.js.br +0 -0
  195. package/payload/server/public/assets/page-DBQLOasI.js.gz +0 -0
  196. package/payload/server/public/assets/{page-yn7ivq8O.js → page-DyVAu7tm.js} +1 -1
  197. package/payload/server/public/assets/page-DyVAu7tm.js.br +0 -0
  198. package/payload/server/public/assets/page-DyVAu7tm.js.gz +0 -0
  199. package/payload/server/public/assets/play-DgP7vl0r.js +1 -0
  200. package/payload/server/public/assets/play-DgP7vl0r.js.br +0 -0
  201. package/payload/server/public/assets/play-DgP7vl0r.js.gz +0 -0
  202. package/payload/server/public/assets/{public-D_qjqet7.js → public-BvUQR3hd.js} +1 -1
  203. package/payload/server/public/assets/public-BvUQR3hd.js.br +0 -0
  204. package/payload/server/public/assets/public-BvUQR3hd.js.gz +0 -0
  205. package/payload/server/public/assets/{rotate-ccw-Ixkq1iUA.js → rotate-ccw-Dnn7gbPh.js} +1 -1
  206. package/payload/server/public/assets/rotate-ccw-Dnn7gbPh.js.br +0 -0
  207. package/payload/server/public/assets/rotate-ccw-Dnn7gbPh.js.gz +0 -0
  208. package/payload/server/public/assets/{routines-Cxjlmcp7.js → routines-N8NmxPOZ.js} +1 -1
  209. package/payload/server/public/assets/routines-N8NmxPOZ.js.br +0 -0
  210. package/payload/server/public/assets/routines-N8NmxPOZ.js.gz +0 -0
  211. package/payload/server/public/assets/{skills-DEo9hSVX.js → skills-B2CRgO-U.js} +1 -1
  212. package/payload/server/public/assets/skills-B2CRgO-U.js.br +0 -0
  213. package/payload/server/public/assets/skills-B2CRgO-U.js.gz +0 -0
  214. package/payload/server/public/assets/{tasks-BxApOIXI.js → tasks-8QLGtlSj.js} +1 -1
  215. package/payload/server/public/assets/tasks-8QLGtlSj.js.br +0 -0
  216. package/payload/server/public/assets/tasks-8QLGtlSj.js.gz +0 -0
  217. package/payload/server/public/assets/{triangle-alert-BYzMcXv8.js → triangle-alert-el772yUQ.js} +1 -1
  218. package/payload/server/public/assets/triangle-alert-el772yUQ.js.br +0 -0
  219. package/payload/server/public/assets/triangle-alert-el772yUQ.js.gz +0 -0
  220. package/payload/server/public/assets/{useCopyFeedback-Bk2HVcjy.js → useCopyFeedback-DfNAu8hQ.js} +1 -1
  221. package/payload/server/public/assets/useCopyFeedback-DfNAu8hQ.js.br +0 -0
  222. package/payload/server/public/assets/useCopyFeedback-DfNAu8hQ.js.gz +0 -0
  223. package/payload/server/public/assets/{useMediaQuery-DEti-vAx.js → useMediaQuery-B469O83-.js} +1 -1
  224. package/payload/server/public/assets/useMediaQuery-B469O83-.js.br +0 -0
  225. package/payload/server/public/assets/{useMediaQuery-DEti-vAx.js.gz → useMediaQuery-B469O83-.js.gz} +0 -0
  226. package/payload/server/public/assets/{useVoiceRecorder-kQ92rL-f.js → useVoiceRecorder-Bb-51cJv.js} +1 -1
  227. package/payload/server/public/assets/useVoiceRecorder-Bb-51cJv.js.br +0 -0
  228. package/payload/server/public/assets/useVoiceRecorder-Bb-51cJv.js.gz +0 -0
  229. package/payload/server/public/assets/{wrench-BhLkHffv.js → wrench-Ck4phkdB.js} +1 -1
  230. package/payload/server/public/assets/wrench-Ck4phkdB.js.br +0 -0
  231. package/payload/server/public/assets/wrench-Ck4phkdB.js.gz +0 -0
  232. package/payload/server/public/browser.html +5 -5
  233. package/payload/server/public/calendar.html +8 -8
  234. package/payload/server/public/chat.html +14 -14
  235. package/payload/server/public/data.html +12 -12
  236. package/payload/server/public/field.html +3 -3
  237. package/payload/server/public/graph.html +10 -10
  238. package/payload/server/public/index.html +14 -14
  239. package/payload/server/public/operator.html +15 -15
  240. package/payload/server/public/public.html +14 -14
  241. package/payload/server/public/routines.html +8 -8
  242. package/payload/server/public/skills.html +6 -6
  243. package/payload/server/public/tasks.html +7 -7
  244. package/payload/server/server.js +3114 -1371
  245. package/payload/server/public/assets/AdminLoginScreens-AnJZfgFz.js.br +0 -0
  246. package/payload/server/public/assets/AdminLoginScreens-AnJZfgFz.js.gz +0 -0
  247. package/payload/server/public/assets/AdminShell-CEVqyQqg.js.br +0 -0
  248. package/payload/server/public/assets/AdminShell-CEVqyQqg.js.gz +0 -0
  249. package/payload/server/public/assets/activity-czUaUl8I.js.br +0 -0
  250. package/payload/server/public/assets/activity-czUaUl8I.js.gz +0 -0
  251. package/payload/server/public/assets/admin-B0iRcfuk.js.br +0 -0
  252. package/payload/server/public/assets/admin-B0iRcfuk.js.gz +0 -0
  253. package/payload/server/public/assets/agents-BocONCeQ.js.br +0 -0
  254. package/payload/server/public/assets/agents-BocONCeQ.js.gz +0 -0
  255. package/payload/server/public/assets/browser-Cy-rJm3t.js.br +0 -0
  256. package/payload/server/public/assets/browser-Cy-rJm3t.js.gz +0 -0
  257. package/payload/server/public/assets/calendar-DUTKxWtd.js.br +0 -0
  258. package/payload/server/public/assets/calendar-DUTKxWtd.js.gz +0 -0
  259. package/payload/server/public/assets/chat-Cis7sHsZ.js +0 -1
  260. package/payload/server/public/assets/chat-Cis7sHsZ.js.br +0 -0
  261. package/payload/server/public/assets/chat-Cis7sHsZ.js.gz +0 -0
  262. package/payload/server/public/assets/chevron-left-Bhhx8j2r.js +0 -1
  263. package/payload/server/public/assets/chevron-left-Bhhx8j2r.js.br +0 -0
  264. package/payload/server/public/assets/clock-Ca3nx7Cc.js +0 -1
  265. package/payload/server/public/assets/clock-Ca3nx7Cc.js.br +0 -0
  266. package/payload/server/public/assets/clock-Ca3nx7Cc.js.gz +0 -0
  267. package/payload/server/public/assets/copy-DE9qmAs5.js.br +0 -0
  268. package/payload/server/public/assets/copy-DE9qmAs5.js.gz +0 -0
  269. package/payload/server/public/assets/data-CkaAEeP1.js +0 -1
  270. package/payload/server/public/assets/data-CkaAEeP1.js.br +0 -0
  271. package/payload/server/public/assets/data-CkaAEeP1.js.gz +0 -0
  272. package/payload/server/public/assets/field-DE2tEfTo.js.br +0 -0
  273. package/payload/server/public/assets/field-DE2tEfTo.js.gz +0 -0
  274. package/payload/server/public/assets/file-text-BHfL2gqo.js.br +0 -0
  275. package/payload/server/public/assets/file-text-BHfL2gqo.js.gz +0 -0
  276. package/payload/server/public/assets/graph-08S_BofN.js.br +0 -0
  277. package/payload/server/public/assets/graph-08S_BofN.js.gz +0 -0
  278. package/payload/server/public/assets/graph-labels-C640Er5R.js.br +0 -0
  279. package/payload/server/public/assets/graph-labels-C640Er5R.js.gz +0 -0
  280. package/payload/server/public/assets/jsx-runtime-FxjwOUbA.css.br +0 -0
  281. package/payload/server/public/assets/operator-DH5Kw3fL.js.br +0 -0
  282. package/payload/server/public/assets/operator-DH5Kw3fL.js.gz +0 -0
  283. package/payload/server/public/assets/page-BDpa4HWT.js.br +0 -0
  284. package/payload/server/public/assets/page-BDpa4HWT.js.gz +0 -0
  285. package/payload/server/public/assets/page-yn7ivq8O.js.br +0 -0
  286. package/payload/server/public/assets/page-yn7ivq8O.js.gz +0 -0
  287. package/payload/server/public/assets/play-CMN7ht-S.js +0 -1
  288. package/payload/server/public/assets/play-CMN7ht-S.js.br +0 -0
  289. package/payload/server/public/assets/play-CMN7ht-S.js.gz +0 -0
  290. package/payload/server/public/assets/public-D_qjqet7.js.br +0 -0
  291. package/payload/server/public/assets/public-D_qjqet7.js.gz +0 -0
  292. package/payload/server/public/assets/rotate-ccw-Ixkq1iUA.js.br +0 -0
  293. package/payload/server/public/assets/rotate-ccw-Ixkq1iUA.js.gz +0 -0
  294. package/payload/server/public/assets/routines-Cxjlmcp7.js.br +0 -0
  295. package/payload/server/public/assets/routines-Cxjlmcp7.js.gz +0 -0
  296. package/payload/server/public/assets/skills-DEo9hSVX.js.br +0 -0
  297. package/payload/server/public/assets/skills-DEo9hSVX.js.gz +0 -0
  298. package/payload/server/public/assets/tasks-BxApOIXI.js.br +0 -0
  299. package/payload/server/public/assets/tasks-BxApOIXI.js.gz +0 -0
  300. package/payload/server/public/assets/triangle-alert-BYzMcXv8.js.br +0 -0
  301. package/payload/server/public/assets/triangle-alert-BYzMcXv8.js.gz +0 -0
  302. package/payload/server/public/assets/useCopyFeedback-Bk2HVcjy.js.br +0 -0
  303. package/payload/server/public/assets/useCopyFeedback-Bk2HVcjy.js.gz +0 -0
  304. package/payload/server/public/assets/useMediaQuery-DEti-vAx.js.br +0 -0
  305. package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js.br +0 -0
  306. package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js.gz +0 -0
  307. package/payload/server/public/assets/wrench-BhLkHffv.js.br +0 -0
  308. package/payload/server/public/assets/wrench-BhLkHffv.js.gz +0 -0
  309. /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js → jsx-runtime-BmNepPEn.js} +0 -0
  310. /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js.br → jsx-runtime-BmNepPEn.js.br} +0 -0
  311. /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js.gz → jsx-runtime-BmNepPEn.js.gz} +0 -0
@@ -10,11 +10,16 @@ export function normalizeNameQuery(name) {
10
10
  return name.trim().replace(/\s+/g, " ");
11
11
  }
12
12
  export async function contactLookup(params) {
13
- const { email, telephone, name, accountId, limit = 10 } = params;
13
+ const { email, telephone, telegramUserId, name, accountId, limit = 10 } = params;
14
14
  const session = getSession();
15
15
  try {
16
16
  let query;
17
17
  let queryParams;
18
+ // Set ONLY by the branch below that actually matches on the id. A caller
19
+ // that passes both an email and a telegramUserId takes the email branch, and
20
+ // logging a hit for a key the query never touched would report the
21
+ // telegramUserId path as working on the day it stopped.
22
+ let matchedOnTelegramUserId = false;
18
23
  const trashFilter = notTrashed("p");
19
24
  const prefFilter = `${notTrashed("pref")} AND coalesce(pref.notApplicable, false) = false`;
20
25
  if (email) {
@@ -41,6 +46,19 @@ export async function contactLookup(params) {
41
46
  `;
42
47
  queryParams = { telephone: telephone.trim(), accountId, limit: neo4j.int(limit) };
43
48
  }
49
+ else if (telegramUserId !== undefined) {
50
+ query = `
51
+ MATCH (p:Person {accountId: $accountId})
52
+ WHERE p.telegramUserId = $telegramUserId AND ${trashFilter}
53
+ WITH p, elementId(p) AS nodeId
54
+ LIMIT $limit
55
+ OPTIONAL MATCH (p)-[:HAS_PREFERENCE]->(pref:Preference)
56
+ WHERE ${prefFilter}
57
+ RETURN p, nodeId, collect(pref) AS prefs
58
+ `;
59
+ queryParams = { telegramUserId, accountId, limit: neo4j.int(limit) };
60
+ matchedOnTelegramUserId = true;
61
+ }
44
62
  else if (name) {
45
63
  query = `
46
64
  MATCH (p:Person {accountId: $accountId})
@@ -68,6 +86,15 @@ export async function contactLookup(params) {
68
86
  queryParams = { accountId, limit: neo4j.int(limit) };
69
87
  }
70
88
  const result = await session.run(query, queryParams);
89
+ // Task 2625 — a write with no read is the failure this line catches: an id
90
+ // stored on every Telegram contact and never matched means the lookup branch
91
+ // is missing or the property name drifted, and neither of those throws.
92
+ // Emitted only when the query actually matched on the id, so neither a
93
+ // lookup by email nor one that merely carried the field can dilute the hit
94
+ // rate this is read as.
95
+ if (matchedOnTelegramUserId) {
96
+ console.error(`[contacts] op=lookup key=telegramUserId hit=${result.records.length > 0}`);
97
+ }
71
98
  return result.records.map((record) => {
72
99
  const p = record.get("p").properties;
73
100
  const prefNodes = record.get("prefs") ?? [];
@@ -84,6 +111,7 @@ export async function contactLookup(params) {
84
111
  familyName: p.familyName,
85
112
  email: p.email,
86
113
  telephone: p.telephone,
114
+ telegramUserId: p.telegramUserId,
87
115
  jobTitle: p.jobTitle,
88
116
  source: p.source,
89
117
  status: p.status,
@@ -1 +1 @@
1
- {"version":3,"file":"contact-lookup.js","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"contact-lookup.js","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,cAAc,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAc1E,yEAAyE;AACzE,gFAAgF;AAChF,0EAA0E;AAC1E,8EAA8E;AAC9E,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAyBD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;IAEjF,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,IAAI,KAAa,CAAC;QAClB,IAAI,WAAoC,CAAC;QACzC,yEAAyE;QACzE,6EAA6E;QAC7E,mEAAmE;QACnE,wDAAwD;QACxD,IAAI,uBAAuB,GAAG,KAAK,CAAC;QAEpC,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,UAAU,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,kDAAkD,CAAC;QAC3F,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,GAAG;;qCAEuB,WAAW;;;;gBAIhC,UAAU;;OAEnB,CAAC;YACF,WAAW,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACnF,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACrB,KAAK,GAAG;;6CAE+B,WAAW;;;;gBAIxC,UAAU;;OAEnB,CAAC;YACF,WAAW,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpF,CAAC;aAAM,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YACxC,KAAK,GAAG;;uDAEyC,WAAW;;;;gBAIlD,UAAU;;OAEnB,CAAC;YACF,WAAW,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACrE,uBAAuB,GAAG,IAAI,CAAC;QACjC,CAAC;aAAM,IAAI,IAAI,EAAE,CAAC;YAChB,KAAK,GAAG;;;iBAGG,WAAW;;;;gBAIZ,UAAU;;OAEnB,CAAC;YACF,WAAW,GAAG,EAAE,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACvF,CAAC;aAAM,CAAC;YACN,KAAK,GAAG;;gBAEE,WAAW;;;;;gBAKX,UAAU;;OAEnB,CAAC;YACF,WAAW,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAErD,2EAA2E;QAC3E,6EAA6E;QAC7E,wEAAwE;QACxE,uEAAuE;QACvE,2EAA2E;QAC3E,wBAAwB;QACxB,IAAI,uBAAuB,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,+CAA+C,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC;QAED,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACnC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC;YACrC,MAAM,SAAS,GAAI,MAAM,CAAC,GAAG,CAAC,OAAO,CAAoD,IAAI,EAAE,CAAC;YAChG,MAAM,WAAW,GAAuB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC5D,QAAQ,EAAE,CAAC,CAAC,UAAU,CAAC,QAAkB;gBACzC,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC,GAAa;gBAC/B,KAAK,EAAE,CAAC,CAAC,UAAU,CAAC,KAAe;gBACnC,UAAU,EAAG,CAAC,CAAC,UAAU,CAAC,UAA4B,IAAI,IAAI;gBAC9D,UAAU,EAAG,CAAC,CAAC,UAAU,CAAC,UAA4B,IAAI,IAAI;aAC/D,CAAC,CAAC,CAAC;YACJ,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAW;gBACtC,SAAS,EAAE,CAAC,CAAC,SAAmB;gBAChC,UAAU,EAAE,CAAC,CAAC,UAAgC;gBAC9C,KAAK,EAAE,CAAC,CAAC,KAA2B;gBACpC,SAAS,EAAE,CAAC,CAAC,SAA+B;gBAC5C,cAAc,EAAE,CAAC,CAAC,cAAoC;gBACtD,QAAQ,EAAE,CAAC,CAAC,QAA8B;gBAC1C,MAAM,EAAE,CAAC,CAAC,MAAgB;gBAC1B,MAAM,EAAE,CAAC,CAAC,MAAgB;gBAC1B,SAAS,EAAE,CAAC,CAAC,SAAmB;gBAChC,UAAU,EAAE,CAA4B;gBACxC,WAAW;aACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,iFAAiF;AACjF,+DAA+D;AAC/D,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC;IACzC,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU;IAC3D,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW;CAC7C,CAAC,CAAC;AAEH,MAAM,UAAU,oBAAoB,CAAC,OAAwB;IAC3D,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK;YAC5B,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG;YAChB,CAAC,CAAC,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,GAAG;gBACpB,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,cAAc,MAAM,CAAC,CAAC,MAAM,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC;QAC1I,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CACrD,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CACT,CAAC,0BAA0B,CAAC,GAAG,CAAC,CAAC,CAAC;YAClC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,SAAS,CAAC,CAC7E,CAAC;QACF,MAAM,SAAS,GACb,WAAW,CAAC,MAAM,GAAG,CAAC;YACpB,CAAC,CAAC,oBAAoB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAClF,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,SAAS,GACb,CAAC,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;YACtB,CAAC,CAAC,qBAAqB,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAChF,CAAC,CAAC,EAAE,CAAC;QACT,OAAO,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,EAAE,CAAC;IACrD,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ // Scoped to src, matching plugins/telegram/mcp: without it vitest also
7
+ // collects dist/__tests__/*.test.js, which `tsc -p` emits because this
8
+ // plugin's tsconfig includes src/__tests__. Every test would then run twice,
9
+ // and a stale compiled copy can pass while the source it was built from
10
+ // fails.
11
+ include: ["src/**/__tests__/**/*.test.ts"],
12
+ testTimeout: 10_000,
13
+ },
14
+ });
@@ -805,17 +805,31 @@ another account's directory, for every session including a house-scoped one, and
805
805
  the Bash snapshot pair watches the account's own layout. All of them live in the
806
806
  account's `.claude/settings.json`, and a hook that is not registered there runs
807
807
  never and emits nothing, so the silence reads identical to a clean account. The
808
- same pass therefore checks the five registrations per account and reports any
808
+ same pass therefore checks the six registrations per account and reports any
809
809
  absence as
810
810
  `[fs-reconcile] op=hook-registration account=<id8> missing=<matcher>:<hook>,…`,
811
811
  with `hooks-missing=0 hooks-unaudited=0` on the summary line as the per-cycle
812
812
  proof the fence is wired everywhere. An account whose settings cannot be read
813
813
  counts as unaudited rather than clean, so an install where none of them can be
814
- read never reports itself as fenced. The refusal itself is agent-facing and lands
815
- in the session's own child log as
814
+ read never reports itself as fenced. The sixth row is the per-turn filing
815
+ directive, whose template entry carries no matcher at all, so that row is
816
+ reported by its event as `UserPromptSubmit:preference-consult-directive.sh`. The
817
+ refusal itself is agent-facing and lands in the session's own child log as
816
818
  `[fs-guard] blocked path=<abs> reason=cross-account target=<id8>`, not in
817
819
  `server.log`.
818
820
 
821
+ The same pass also emits
822
+ `[fs-reconcile] op=entity-subdirs account=<id8> entities=<n> archive=<m> stray=<s>`
823
+ for every account on every cycle, not only on drift: `archive/` is the one folder
824
+ permitted under an entity folder, a write that wrongly succeeds emits nothing,
825
+ and `stray` above zero is the only signature an over-wide rule leaves.
826
+ `entities` is on the same line because `stray=0` cannot otherwise be told from a
827
+ walker that read nothing. The `file-write` and `file-edit` tools carry the same
828
+ five-reason fence as the native route; their refusals log the same
829
+ `[fs-guard] blocked` line into `mcp-filesystem-<sessionId>.log`, the plugin
830
+ server's own tee sink, because `lib/mcp-spawn-tee` mirrors only its
831
+ `[mcp-helper]` lifecycle lines into `server.log`.
832
+
819
833
  ### Graph
820
834
 
821
835
  | Mount | Purpose |
@@ -111,6 +111,21 @@ for that door alone.
111
111
  census prints every cycle, including when the counts agree: an absent line means
112
112
  the census stopped, never that delivery is healthy.
113
113
 
114
+ ## Which channels can start a conversation
115
+
116
+ Answering an inbound and starting a new conversation are different powers, and
117
+ only the first is available on every door.
118
+
119
+ | Channel | Can start a conversation with | Cannot |
120
+ |---|---|---|
121
+ | WhatsApp | a registered admin phone, a registered account manager, or a registered party, through `whatsapp-send-admin` | any other number. The cold-send tools were removed after one such send tripped WhatsApp's anti-automation enforcement and restricted the account, and no setting re-enables them. |
122
+ | Telegram | any chat that has already messaged the bot | a chat that has not. Telegram itself refuses it. |
123
+ | Email | any address, once a mailbox is attached | nothing, once a mailbox is attached |
124
+ | Admin webchat | nobody. It is a browser chat the operator opens | anything outbound |
125
+
126
+ The admin identity carries the same rule under its `## Channel reality` section,
127
+ so an agent that has loaded no plugin skill still knows it. The two must agree.
128
+
114
129
  ## Which door an answer reaches
115
130
 
116
131
  The door the message arrived on, and no other. A question asked on WhatsApp is
@@ -10,6 +10,24 @@ The Telegram plugin connects {{productName}} to a Telegram bot. Once set up, you
10
10
 
11
11
  ## Setup
12
12
 
13
+ ### Adding a second bot: no BotFather at all
14
+
15
+ Once you have one working admin bot, you never need BotFather again to add another. Tell
16
+ {{productName}}: "create a new Telegram bot." It sends you a single button. Tap it, give the bot a
17
+ name, and Telegram hands the bot straight to {{productName}} — it stores the credentials, locks the
18
+ bot so only you can reach it, and connects it. No token is shown to anyone and nothing is copied or
19
+ pasted.
20
+
21
+ One thing has to be switched on first, once per managing bot: open @BotFather, go to its Mini App,
22
+ select your admin bot and turn on management of other bots. {{productName}} checks this before
23
+ sending the button and tells you if it is missing, so you never end up with a button that does
24
+ nothing.
25
+
26
+ A bot made this way arrives switched off: it answers nobody until you tell {{productName}} which
27
+ assistant should run on it.
28
+
29
+ Your **first** bot still comes from BotFather, below, and so does any bot you already own.
30
+
13
31
  ### Step 1: Create a Telegram bot
14
32
 
15
33
  1. Open Telegram and search for `@BotFather`
@@ -21,11 +39,59 @@ The Telegram plugin connects {{productName}} to a Telegram bot. Once set up, you
21
39
 
22
40
  Tell {{productName}}: "Set up Telegram" or "Configure the Telegram bot." Setup runs entirely in the admin chat — there is no settings page.
23
41
 
24
- {{productName}} asks for your bot token, then asks whether this is an **admin bot** (only your numeric Telegram IDs may message it) or a **public bot** (customer-facing, with a DM policy). It verifies the token, registers the bot's webhook on your brand's edge, stores the token under your brand's config (never in a shared system directory), and — for a public bot — sets the agent that will answer. The bot is now connected.
42
+ {{productName}} asks for your bot token, then asks what kind of bot this is:
43
+
44
+ - an **admin bot** — only your numeric Telegram IDs may message it, and it answers as your admin agent;
45
+ - a **public bot** — customer-facing, with a DM policy, answering as one of your account's agents;
46
+ - a **specialist bot** — bound to one of your own specialists, so messaging it runs that specialist with its own instructions and its own tools rather than your admin agent. You name the specialist and the numeric IDs allowed to reach it. Both are required: there is no open setting for this kind, because the specialist has real tools rather than the reply-only surface a public bot gets.
47
+
48
+ It verifies the token, registers the bot's webhook on your brand's edge, stores the token under your brand's config (never in a shared system directory), and sets whichever agent or specialist will answer. The bot is now connected.
25
49
 
26
50
  ### Step 3: Start the bot
27
51
 
28
- In Telegram, open your bot and send `/start`, then a message. {{productName}} receives it and replies in the same chat. Admin-bot messages from your registered IDs reach the admin agent; public-bot messages reach the public agent you selected.
52
+ In Telegram, open your bot and send `/start`, then a message. {{productName}} receives it and replies in the same chat. Admin-bot messages from your registered IDs reach the admin agent; public-bot messages reach the public agent you selected; specialist-bot messages reach that specialist, in a conversation that stays the same one from one day to the next.
53
+
54
+ ### Step 4 (optional): Use the bot in a group
55
+
56
+ Only a **public** bot can work in a group. An admin bot added to a group answers nobody, on purpose: it runs your own agent, with your own standing rules and tools, and a group is a room other people read.
57
+
58
+ Three things have to be true:
59
+
60
+ 1. **Group privacy is on.** In BotFather: `/mybots`, pick the bot, Bot Settings, Group Privacy, Turn on. With privacy on the bot sees only messages that name it — a command, an @mention, or a reply to something it said. It cannot read the rest of the conversation. {{productName}} refuses to connect a bot with privacy off and tells you this step.
61
+ 2. **The bot is in the group.** A group admin adds it, the same way any member is added.
62
+ 3. **You allowlist the group.** Tell {{productName}}: "Let the bot answer in this group," and give it the group's chat ID (see Getting a Chat ID below). Until you do, the bot stays silent there.
63
+
64
+ Once a group is allowlisted, **anyone in that group can talk to the bot** — not only the people on your DM allowlist. Allowlisting a room is a decision about the room, not about its members.
65
+
66
+ Everyone in the group shares one conversation, and each message tells {{productName}} who sent it. If the group uses topics, an answer comes back inside the topic the question was asked in.
67
+
68
+ ### Step 5 (optional): Use the bot in a channel
69
+
70
+ A channel is a broadcast: a post goes to every subscriber, and it cannot be taken back. So this one
71
+ is worth reading twice before you set it up.
72
+
73
+ Only a **public** bot can work in a channel, for the same reason only a public bot can work in a
74
+ group.
75
+
76
+ Two things have to be true:
77
+
78
+ 1. **The bot is a channel admin with permission to post.** A channel admin adds it and ticks "Post
79
+ Messages". Without that tick the bot can still read the channel but every attempt to post fails
80
+ quietly.
81
+ 2. **You allowlist the channel.** Tell {{productName}}: "Let the bot use this channel," and give it
82
+ the channel's chat ID. Until you do, the bot neither reads that channel nor posts to it.
83
+
84
+ Unlike a group, allowlisting a channel decides **both directions at once**. Once a channel is on the
85
+ list, {{productName}} can post to it. A channel that is not on the list is refused before anything
86
+ is sent, and you are told which channel and why.
87
+
88
+ Posts in a channel have no author — Telegram does not say who wrote one — so {{productName}} sees
89
+ the post as coming from the channel itself, by name.
90
+
91
+ **Comments.** If your channel has a linked discussion group, people can comment under each post.
92
+ Allowlist that group too (Step 4) and {{productName}} can read the comments and answer them under
93
+ the right post. Somebody who comments anonymously shows up as the group rather than as a person,
94
+ because that is all Telegram sends.
29
95
 
30
96
  ## Sending Messages via {{productName}}
31
97
 
@@ -37,13 +103,32 @@ Once connected, tell {{productName}} to send messages on your behalf:
37
103
 
38
104
  {{productName}} needs a chat ID or username to target a specific person or group. For groups, you'll need to add the bot to the group first.
39
105
 
106
+ ## Who your bot can message
107
+
108
+ Telegram does not let a bot start a conversation. Anyone who has messaged one of your bots can be
109
+ messaged back at any time; anyone who never has must write to the bot first. That is Telegram's own
110
+ rule and nothing here can get around it.
111
+
112
+ Ask {{productName}}: "Who can you reach on Telegram?" It lists every bot you have registered, the
113
+ people who have messaged each one and when they last wrote, and anyone on a bot's admin list who has
114
+ never messaged. Being on the admin list is not the same as having opened the chat, so those people
115
+ are listed separately as not reachable yet.
116
+
117
+ That list is built from the messages already stored on this machine. Every message that arrives is
118
+ written down, and both the answer to "who can you reach" and the hourly self-check read that same
119
+ store, so what {{productName}} tells you and what actually arrived cannot drift apart.
120
+
121
+ If {{productName}} ever says a Telegram message cannot be sent, ask it to check that list first.
122
+
40
123
  ## Getting a Chat ID
41
124
 
42
125
  To message a specific person or group, {{productName}} needs their chat ID. The easiest way:
43
126
 
44
127
  1. Have the person (or yourself) send any message to your bot
45
- 2. Ask {{productName}}: "What chat IDs have messaged the bot recently?"
46
- 3. {{productName}} will look up the message history and show you the IDs
128
+ 2. Ask {{productName}}: "Who can you reach on Telegram?"
129
+ 3. {{productName}} will show you every chat that has messaged, with its ID
130
+
131
+ A group's ID is a long negative number, like `-1001234567890`. To find it, @mention the bot once in the group and then ask {{productName}} the same question — the group will be in the list even though the bot is not yet allowed to answer there.
47
132
 
48
133
  ## Message History
49
134
 
@@ -55,6 +140,10 @@ Ask {{productName}}: "What messages has the bot received?" or "Show recent Teleg
55
140
 
56
141
  **Can't send to a group:** The bot must be a member of the group. Add it via the group settings in Telegram, then try again.
57
142
 
143
+ **"That channel isn't on the allowlist":** {{productName}} refuses to post to a channel or group you haven't allowlisted, and it refuses before anything is sent. Give it the chat ID and say you want the bot to use it. This applies to public bots only — an admin bot cannot post to any channel or group at all.
144
+
145
+ **The bot reads a channel but can't post to it:** It is a channel admin without the "Post Messages" permission. Turn that on in the channel's admin settings.
146
+
58
147
  **Messages not arriving:** Make sure the bot hasn't been blocked. Try sending `/start` to the bot directly.
59
148
 
60
149
  ## Sending from the dashboard
@@ -136,6 +225,26 @@ One limit, and it is Telegram's. A message sent on behalf of a business account
136
225
  that opens a small web page, so a checklist and a job-sheet button arrive as two messages rather than
137
226
  one.
138
227
 
228
+ ## Your customers' messages
229
+
230
+ Connecting the bot to your business account does more than unlock checklists. From that moment,
231
+ messages your customers send to the business account reach {{productName}} too, and it can answer
232
+ them in the customer's own chat.
233
+
234
+ Those conversations are kept entirely separate from your own. A customer talking to your business
235
+ account never lands in the chat you use with {{productName}}, even if it is somebody who also
236
+ messages you directly, and {{productName}} answers a customer as your public assistant rather than as
237
+ your personal one. Your own notes and standing instructions are never visible in a customer's
238
+ conversation.
239
+
240
+ When a customer edits a message, the message you are looking at changes to match. When they delete
241
+ one, it stays in view but crossed out and marked deleted, so you can still see what was withdrawn.
242
+
243
+ **The one thing worth knowing: Telegram gives you 24 hours.** You may reply to a customer only within
244
+ 24 hours of their last message. After that Telegram closes the door and there is no way to reopen it
245
+ except for the customer to write again. {{productName}} will refuse a late reply and tell you the
246
+ window has closed, rather than letting it fail silently.
247
+
139
248
  Five of the 185 are deliberately refused, because they would break your own
140
249
  inbound messages: setting or deleting the webhook by hand, polling Telegram for
141
250
  updates instead, and the two that move the bot off Telegram's cloud servers.
@@ -71,8 +71,8 @@ Every path is confined to the resolved account directory. A path that escapes it
71
71
  ## Tools
72
72
 
73
73
  - `file-read` — Read a UTF-8 file. Args: `path`, optional `targetAccountId`.
74
- - `file-write` — Create or overwrite a UTF-8 file, making parent directories. Args: `path`, `content`, optional `targetAccountId`.
75
- - `file-edit` — Replace one unique occurrence of `oldString` with `newString`. Fails if `oldString` is missing or not unique. Args: `path`, `oldString`, `newString`, optional `targetAccountId`.
74
+ - `file-write` — Create or overwrite a UTF-8 file, making parent directories. Args: `path`, `content`, optional `targetAccountId`. Refused before anything is created when the path breaks the target account's own `SCHEMA.md`: an unlisted top-level entry, one of the account's machinery entries, a plugin-declared file, a path deeper than the bucket allows, or a name outside `[a-z0-9-]`. The five reasons are the ones the native write guard gives, and each refusal logs `[fs-guard] blocked path=<rel> reason=<r>` to this server's own `mcp-filesystem-<sessionId>.log`. An account with no `SCHEMA.md`, and the shared folder (which is not an account), are exempt.
75
+ - `file-edit` — Replace one unique occurrence of `oldString` with `newString`. Fails if `oldString` is missing or not unique. Args: `path`, `oldString`, `newString`, optional `targetAccountId`. Carries the same five schema refusals as `file-write`, so a file already sitting at a path the schema forbids is not editable here either.
76
76
  - `file-list` — List a directory's entries with their type. Args: optional `path` (defaults to the account root), optional `targetAccountId`.
77
77
  - `file-glob` — List files matching a glob (`**`, `*`, `?`). Args: `pattern`, optional `targetAccountId`.
78
78
  - `file-grep` — Search file contents for a JavaScript regular expression, returning `file:line:preview`. Args: `pattern`, optional `path`, optional `targetAccountId`.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=account-schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-schema.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/account-schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, writeFileSync, rmSync, readFileSync, realpathSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { checkAccountSchema } from "../account-schema.js";
7
+ let root;
8
+ // Six levels up from src/lib/__tests__/ reaches platform/. Counted from this
9
+ // file's own URL, not from cwd, because vitest runs from the package dir.
10
+ const TEMPLATE = fileURLToPath(new URL("../../../../../../templates/account-schema/SCHEMA.md", import.meta.url));
11
+ beforeEach(() => {
12
+ root = mkdtempSync(join(tmpdir(), "acct-schema-"));
13
+ });
14
+ afterEach(() => rmSync(root, { recursive: true, force: true }));
15
+ const seed = () => writeFileSync(join(root, "SCHEMA.md"), readFileSync(TEMPLATE, "utf-8"));
16
+ describe("checkAccountSchema — fail-open", () => {
17
+ it("allows every path when SCHEMA.md is absent", () => {
18
+ expect(checkAccountSchema(root, "nonsense/x.md")).toBeNull();
19
+ expect(checkAccountSchema(root, "projects/acme/insights/x.md")).toBeNull();
20
+ });
21
+ it("allows every path when the allowed fence is empty", () => {
22
+ writeFileSync(join(root, "SCHEMA.md"), "# empty\n\n```allowed-top-level\n```\n");
23
+ expect(checkAccountSchema(root, "nonsense/x.md")).toBeNull();
24
+ });
25
+ });
26
+ describe("checkAccountSchema — the five refusals", () => {
27
+ beforeEach(seed);
28
+ it("refuses a first segment outside the allowed set", () => {
29
+ expect(checkAccountSchema(root, "nonsense/x.md")?.reason).toBe("top-level");
30
+ });
31
+ it("refuses the account's own machinery and quotes the fence reason", () => {
32
+ const r = checkAccountSchema(root, "secrets/x");
33
+ expect(r?.reason).toBe("account-machinery");
34
+ expect(r?.message).toContain("provisioned credentials");
35
+ });
36
+ it("refuses an over-deep entity path", () => {
37
+ expect(checkAccountSchema(root, "projects/acme-corp/insights/x.md")?.reason).toBe("over-deep");
38
+ });
39
+ it("refuses a bad name", () => {
40
+ expect(checkAccountSchema(root, "projects/ACME Corp/x.md")?.reason).toBe("bad-name");
41
+ });
42
+ it("allows a flat entity file and an archived one", () => {
43
+ expect(checkAccountSchema(root, "projects/acme-corp/quote.pdf")).toBeNull();
44
+ expect(checkAccountSchema(root, "projects/acme-corp/archive/quote-v1.pdf")).toBeNull();
45
+ expect(checkAccountSchema(root, "contacts/jane-doe/archive/profile.pdf")).toBeNull();
46
+ });
47
+ it("refuses a second folder beneath archive", () => {
48
+ expect(checkAccountSchema(root, "projects/acme-corp/archive/old/quote.pdf")?.reason).toBe("over-deep");
49
+ });
50
+ it("refuses any other entity subfolder at the same depth", () => {
51
+ expect(checkAccountSchema(root, "projects/acme-corp/invoices/quote.pdf")?.reason).toBe("over-deep");
52
+ });
53
+ it("keeps documents at one folder deep", () => {
54
+ expect(checkAccountSchema(root, "documents/site-survey/notes.md")).toBeNull();
55
+ expect(checkAccountSchema(root, "documents/a/b/notes.md")?.reason).toBe("over-deep");
56
+ });
57
+ it("leaves tool-owned buckets unbounded", () => {
58
+ expect(checkAccountSchema(root, "output/deep/nested/x.png")).toBeNull();
59
+ });
60
+ // The hook resolves its target before counting segments, so a dot segment or a
61
+ // doubled slash never inflates the depth there. Counting raw segments here
62
+ // would refuse a legitimate flat write.
63
+ it("normalises a dot segment rather than counting it as depth", () => {
64
+ expect(checkAccountSchema(root, "projects/acme-corp/./quote.pdf")).toBeNull();
65
+ expect(checkAccountSchema(root, "./projects/acme-corp/quote.pdf")).toBeNull();
66
+ });
67
+ // An absolute path inside the account passes containedPath on a real install,
68
+ // where the accounts root is not a symlink, and reaches this check. Splitting
69
+ // it raw makes the first segment a filesystem root name and refuses a
70
+ // legitimate write with a message naming 'Users'. The absolute path is built
71
+ // from the REAL root because that is what containedPath roots the write at,
72
+ // and os.tmpdir() is a symlink on macOS.
73
+ it("judges an absolute in-account path by its in-account portion", () => {
74
+ const real = realpathSync(root);
75
+ expect(checkAccountSchema(root, join(real, "projects/acme-corp/quote.pdf"))).toBeNull();
76
+ expect(checkAccountSchema(root, join(real, "projects/acme-corp/insights/x.md"))?.reason).toBe("over-deep");
77
+ });
78
+ // A path that resolves outside the account is containedPath's refusal, not
79
+ // this module's; judging it here would produce a second, differently-worded
80
+ // verdict for one condition.
81
+ it("leaves an escaping path to the containment check", () => {
82
+ expect(checkAccountSchema(root, "../elsewhere/x.md")).toBeNull();
83
+ });
84
+ it("emits one [fs-guard] blocked line per refusal", () => {
85
+ const seen = [];
86
+ const orig = process.stderr.write;
87
+ process.stderr.write = ((s) => {
88
+ seen.push(String(s));
89
+ return true;
90
+ });
91
+ try {
92
+ checkAccountSchema(root, "nonsense/x.md");
93
+ }
94
+ finally {
95
+ process.stderr.write = orig;
96
+ }
97
+ expect(seen.join("")).toContain("[fs-guard] blocked path=nonsense/x.md reason=top-level");
98
+ });
99
+ });
100
+ describe("checkAccountSchema — declared files", () => {
101
+ it("refuses a first segment named in the declared-files fence", () => {
102
+ writeFileSync(join(root, "SCHEMA.md"), "```allowed-top-level\nprojects\nportal.json\n```\n\n```declared-files\nportal.json\tdata-portal\n```\n");
103
+ const r = checkAccountSchema(root, "portal.json");
104
+ expect(r?.reason).toBe("declared-file");
105
+ expect(r?.message).toContain("data-portal");
106
+ });
107
+ });
108
+ //# sourceMappingURL=account-schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-schema.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/account-schema.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,IAAI,IAAY,CAAC;AACjB,6EAA6E;AAC7E,0EAA0E;AAC1E,MAAM,QAAQ,GAAG,aAAa,CAC5B,IAAI,GAAG,CAAC,sDAAsD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CACjF,CAAC;AAEF,UAAU,CAAC,GAAG,EAAE;IACd,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AACH,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAEhE,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAE3F,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC7D,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,6BAA6B,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,wCAAwC,CAAC,CAAC;QACjF,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,UAAU,CAAC,IAAI,CAAC,CAAC;IAEjB,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,CAAC,GAAG,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC5C,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,kCAAkC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,8BAA8B,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5E,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,yCAAyC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvF,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,uCAAuC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,0CAA0C,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CACvF,WAAW,CACZ,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,uCAAuC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CACpF,WAAW,CACZ,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,gCAAgC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9E,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,wBAAwB,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,0BAA0B,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,+EAA+E;IAC/E,2EAA2E;IAC3E,wCAAwC;IACxC,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,gCAAgC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9E,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,gCAAgC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,8EAA8E;IAC9E,8EAA8E;IAC9E,sEAAsE;IACtE,6EAA6E;IAC7E,4EAA4E;IAC5E,yCAAyC;IACzC,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,8BAA8B,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACxF,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,kCAAkC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAC3F,WAAW,CACZ,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,2EAA2E;IAC3E,4EAA4E;IAC5E,6BAA6B;IAC7B,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE;YACpC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC,CAAgB,CAAC;QAClB,IAAI,CAAC;YACH,kBAAkB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAC5C,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC;QAC9B,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,wDAAwD,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,aAAa,CACX,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EACvB,wGAAwG,CACzG,CAAC;QACF,MAAM,CAAC,GAAG,kBAAkB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAClD,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACxC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ export type SchemaReason = "top-level" | "account-machinery" | "declared-file" | "over-deep" | "bad-name";
2
+ export interface SchemaRefusal {
3
+ reason: SchemaReason;
4
+ /** Agent-facing text, with no leading label; each tool prefixes its own name. */
5
+ message: string;
6
+ }
7
+ /**
8
+ * Apply the account's own file schema to the caller's `path`. Returns null to
9
+ * allow.
10
+ *
11
+ * `root` is the resolved ACCOUNT directory, so a cross-account write is checked
12
+ * against the target account's SCHEMA.md, which is the layout that governs where
13
+ * the bytes land. Callers skip this entirely for the shared root: data/shared is
14
+ * not an account and carries no account layout contract.
15
+ */
16
+ export declare function checkAccountSchema(root: string, requestedPath: string): SchemaRefusal | null;
17
+ //# sourceMappingURL=account-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-schema.d.ts","sourceRoot":"","sources":["../../src/lib/account-schema.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,mBAAmB,GACnB,eAAe,GACf,WAAW,GACX,UAAU,CAAC;AAEf,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;CACjB;AAoED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAqF5F"}