@rubytech/create-realagent-code 0.1.593 → 0.1.594

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 (244) hide show
  1. package/package.json +2 -2
  2. package/payload/platform/package.json +1 -0
  3. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +5 -3
  4. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +5 -0
  5. package/payload/platform/plugins/cloudflare/PLUGIN.md +2 -0
  6. package/payload/platform/plugins/docs/references/internals.md +1 -1
  7. package/payload/platform/plugins/docs/references/telegram-guide.md +3 -1
  8. package/payload/platform/scripts/__tests__/agent-identity-reconcile.test.sh +110 -0
  9. package/payload/platform/scripts/__tests__/check-orphan-dist.test.sh +87 -0
  10. package/payload/platform/scripts/check-orphan-dist.mjs +143 -0
  11. package/payload/platform/scripts/lib/agent-identity-reconcile.sh +122 -0
  12. package/payload/platform/scripts/lib/provision-account-dir.sh +8 -26
  13. package/payload/platform/scripts/setup-account.sh +15 -0
  14. package/payload/platform/services/claude-session-manager/dist/index.js +5 -0
  15. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  16. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +5 -0
  17. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  18. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +42 -1
  19. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  20. package/payload/platform/templates/agents/public/IDENTITY.md +3 -3
  21. package/payload/platform/lib/anthropic-key/dist/index.d.ts +0 -22
  22. package/payload/platform/lib/anthropic-key/dist/index.d.ts.map +0 -1
  23. package/payload/platform/lib/anthropic-key/dist/index.js +0 -232
  24. package/payload/platform/lib/anthropic-key/dist/index.js.map +0 -1
  25. package/payload/platform/lib/graph-search/dist/query-expansion.d.ts +0 -37
  26. package/payload/platform/lib/graph-search/dist/query-expansion.d.ts.map +0 -1
  27. package/payload/platform/lib/graph-search/dist/query-expansion.js +0 -101
  28. package/payload/platform/lib/graph-search/dist/query-expansion.js.map +0 -1
  29. package/payload/platform/lib/oauth-llm/dist/index.d.ts +0 -116
  30. package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +0 -1
  31. package/payload/platform/lib/oauth-llm/dist/index.js +0 -386
  32. package/payload/platform/lib/oauth-llm/dist/index.js.map +0 -1
  33. package/payload/platform/lib/screening-patterns/dist/index.d.ts +0 -29
  34. package/payload/platform/lib/screening-patterns/dist/index.d.ts.map +0 -1
  35. package/payload/platform/lib/screening-patterns/dist/index.js +0 -48
  36. package/payload/platform/lib/screening-patterns/dist/index.js.map +0 -1
  37. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts +0 -2
  38. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts.map +0 -1
  39. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js +0 -34
  40. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js.map +0 -1
  41. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.d.ts +0 -39
  42. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.d.ts.map +0 -1
  43. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.js +0 -249
  44. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.js.map +0 -1
  45. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts +0 -6
  46. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts.map +0 -1
  47. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js +0 -32
  48. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js.map +0 -1
  49. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +0 -283
  50. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +0 -1
  51. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +0 -1155
  52. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +0 -1
  53. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.d.ts +0 -90
  54. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.d.ts.map +0 -1
  55. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.js +0 -551
  56. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.js.map +0 -1
  57. package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.d.ts +0 -17
  58. package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.d.ts.map +0 -1
  59. package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.js +0 -186
  60. package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.js.map +0 -1
  61. package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts +0 -29
  62. package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts.map +0 -1
  63. package/payload/platform/plugins/email/mcp/dist/lib/screening.js +0 -105
  64. package/payload/platform/plugins/email/mcp/dist/lib/screening.js.map +0 -1
  65. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts +0 -38
  66. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts.map +0 -1
  67. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js +0 -817
  68. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js.map +0 -1
  69. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.d.ts +0 -30
  70. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.d.ts.map +0 -1
  71. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js +0 -215
  72. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js.map +0 -1
  73. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.d.ts +0 -19
  74. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.d.ts.map +0 -1
  75. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.js +0 -151
  76. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.js.map +0 -1
  77. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts +0 -6
  78. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts.map +0 -1
  79. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js +0 -89
  80. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js.map +0 -1
  81. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.d.ts +0 -2
  82. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.d.ts.map +0 -1
  83. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.js +0 -90
  84. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.js.map +0 -1
  85. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.d.ts +0 -2
  86. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.d.ts.map +0 -1
  87. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +0 -226
  88. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +0 -1
  89. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts +0 -60
  90. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts.map +0 -1
  91. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js +0 -169
  92. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js.map +0 -1
  93. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +0 -248
  94. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +0 -1
  95. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +0 -824
  96. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +0 -1
  97. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +0 -63
  98. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +0 -1
  99. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +0 -210
  100. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +0 -1
  101. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts +0 -35
  102. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts.map +0 -1
  103. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js +0 -28
  104. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js.map +0 -1
  105. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.d.ts +0 -2
  106. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.d.ts.map +0 -1
  107. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.js +0 -68
  108. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.js.map +0 -1
  109. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.d.ts +0 -2
  110. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.d.ts.map +0 -1
  111. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.js +0 -88
  112. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.js.map +0 -1
  113. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.d.ts +0 -2
  114. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.d.ts.map +0 -1
  115. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.js +0 -58
  116. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.js.map +0 -1
  117. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.d.ts +0 -2
  118. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.d.ts.map +0 -1
  119. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js +0 -64
  120. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js.map +0 -1
  121. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.d.ts +0 -2
  122. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.d.ts.map +0 -1
  123. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.js +0 -70
  124. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.js.map +0 -1
  125. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.d.ts +0 -2
  126. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.d.ts.map +0 -1
  127. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.js +0 -48
  128. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.js.map +0 -1
  129. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.d.ts +0 -2
  130. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.d.ts.map +0 -1
  131. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.js +0 -74
  132. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.js.map +0 -1
  133. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.d.ts +0 -2
  134. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.d.ts.map +0 -1
  135. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.js +0 -47
  136. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.js.map +0 -1
  137. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.d.ts +0 -2
  138. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.d.ts.map +0 -1
  139. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.js +0 -65
  140. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.js.map +0 -1
  141. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.d.ts +0 -2
  142. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.d.ts.map +0 -1
  143. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.js +0 -43
  144. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.js.map +0 -1
  145. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.d.ts +0 -2
  146. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.d.ts.map +0 -1
  147. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.js +0 -65
  148. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.js.map +0 -1
  149. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.d.ts +0 -2
  150. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.d.ts.map +0 -1
  151. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.js +0 -49
  152. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.js.map +0 -1
  153. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.d.ts +0 -2
  154. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.d.ts.map +0 -1
  155. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js +0 -79
  156. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js.map +0 -1
  157. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.d.ts +0 -2
  158. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.d.ts.map +0 -1
  159. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.js +0 -329
  160. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.js.map +0 -1
  161. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.d.ts +0 -2
  162. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.d.ts.map +0 -1
  163. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js +0 -81
  164. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js.map +0 -1
  165. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts +0 -34
  166. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts.map +0 -1
  167. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js +0 -62
  168. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js.map +0 -1
  169. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.d.ts +0 -61
  170. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.d.ts.map +0 -1
  171. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js +0 -102
  172. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js.map +0 -1
  173. package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.d.ts +0 -35
  174. package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.d.ts.map +0 -1
  175. package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.js +0 -73
  176. package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.js.map +0 -1
  177. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +0 -29
  178. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts.map +0 -1
  179. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +0 -22
  180. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js.map +0 -1
  181. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +0 -30
  182. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +0 -1
  183. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +0 -130
  184. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +0 -1
  185. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts +0 -65
  186. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts.map +0 -1
  187. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js +0 -262
  188. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js.map +0 -1
  189. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/normalise-start-date.test.d.ts +0 -2
  190. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/normalise-start-date.test.d.ts.map +0 -1
  191. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/normalise-start-date.test.js +0 -100
  192. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/normalise-start-date.test.js.map +0 -1
  193. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.d.ts +0 -2
  194. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.d.ts.map +0 -1
  195. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.js +0 -22
  196. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.js.map +0 -1
  197. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.d.ts +0 -11
  198. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.d.ts.map +0 -1
  199. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.js +0 -19
  200. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.js.map +0 -1
  201. package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.d.ts +0 -8
  202. package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.d.ts.map +0 -1
  203. package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.js +0 -83
  204. package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.js.map +0 -1
  205. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.d.ts +0 -151
  206. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.d.ts.map +0 -1
  207. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.js +0 -299
  208. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.js.map +0 -1
  209. package/payload/platform/services/claude-session-manager/dist/account-admin-census.d.ts +0 -39
  210. package/payload/platform/services/claude-session-manager/dist/account-admin-census.d.ts.map +0 -1
  211. package/payload/platform/services/claude-session-manager/dist/account-admin-census.js +0 -122
  212. package/payload/platform/services/claude-session-manager/dist/account-admin-census.js.map +0 -1
  213. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts +0 -39
  214. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts.map +0 -1
  215. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js +0 -133
  216. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js.map +0 -1
  217. package/payload/platform/services/claude-session-manager/dist/input-postcondition.d.ts +0 -22
  218. package/payload/platform/services/claude-session-manager/dist/input-postcondition.d.ts.map +0 -1
  219. package/payload/platform/services/claude-session-manager/dist/input-postcondition.js +0 -50
  220. package/payload/platform/services/claude-session-manager/dist/input-postcondition.js.map +0 -1
  221. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts +0 -30
  222. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts.map +0 -1
  223. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js +0 -204
  224. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js.map +0 -1
  225. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts +0 -80
  226. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts.map +0 -1
  227. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js +0 -306
  228. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js.map +0 -1
  229. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +0 -49
  230. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +0 -1
  231. package/payload/platform/services/claude-session-manager/dist/session-store.js +0 -52
  232. package/payload/platform/services/claude-session-manager/dist/session-store.js.map +0 -1
  233. package/payload/platform/services/claude-session-manager/dist/systemd-spawn.d.ts +0 -83
  234. package/payload/platform/services/claude-session-manager/dist/systemd-spawn.d.ts.map +0 -1
  235. package/payload/platform/services/claude-session-manager/dist/systemd-spawn.js +0 -345
  236. package/payload/platform/services/claude-session-manager/dist/systemd-spawn.js.map +0 -1
  237. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +0 -104
  238. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +0 -1
  239. package/payload/platform/services/telegram-channel/dist/turn-follow.js +0 -408
  240. package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +0 -1
  241. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +0 -104
  242. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +0 -1
  243. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +0 -393
  244. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-realagent-code",
3
- "version": "0.1.593",
3
+ "version": "0.1.594",
4
4
  "description": "Install Real Agent — Built for agents. By agents.",
5
5
  "bin": {
6
6
  "create-realagent-code": "./dist/index.js"
@@ -13,7 +13,7 @@
13
13
  "build": "tsc",
14
14
  "bundle": "node scripts/bundle.js",
15
15
  "test": "npm run build && node --test 'dist/__tests__/*.test.js'",
16
- "prepublishOnly": "bash ../../platform/scripts/verify-skill-tool-surface.sh && node ../../platform/scripts/check-plugin-tools-mcp-consistency.mjs && node ../../platform/scripts/check-risk-class.mjs && node ../../platform/scripts/check-specialist-tool-surface.mjs && node ../../platform/scripts/check-agent-contract.mjs && node ../../platform/scripts/check-routing-prose-bash.mjs && node ../../platform/scripts/check-no-task-id-leaks.mjs && node ../../platform/scripts/check-no-esm-require.mjs && node ../../platform/scripts/check-no-raw-mcp-registrations.mjs && node ../../platform/scripts/check-skill-load-coverage.mjs && node ../../platform/scripts/check-routine-roster-doc.mjs && node ../../platform/scripts/check-architecture-skill-no-drift.mjs && node ../../platform/ui/scripts/check-brand-fonts.mjs && node ../../platform/ui/scripts/check-route-wiring.mjs && node ../../platform/ui/scripts/check-edge-admin-routes.mjs && npm run build && node --test 'dist/__tests__/*.test.js' && chmod +x dist/index.js && npm run bundle && bash ../../platform/scripts/smoke-boot-services.sh && node ../../platform/ui/scripts/check-bundle-node-imports.mjs --dir=./payload/server/public/assets"
16
+ "prepublishOnly": "bash ../../platform/scripts/verify-skill-tool-surface.sh && node ../../platform/scripts/check-plugin-tools-mcp-consistency.mjs && node ../../platform/scripts/check-risk-class.mjs && node ../../platform/scripts/check-specialist-tool-surface.mjs && node ../../platform/scripts/check-agent-contract.mjs && node ../../platform/scripts/check-routing-prose-bash.mjs && node ../../platform/scripts/check-no-task-id-leaks.mjs && node ../../platform/scripts/check-no-esm-require.mjs && node ../../platform/scripts/check-no-raw-mcp-registrations.mjs && node ../../platform/scripts/check-skill-load-coverage.mjs && node ../../platform/scripts/check-routine-roster-doc.mjs && node ../../platform/scripts/check-architecture-skill-no-drift.mjs && node ../../platform/ui/scripts/check-brand-fonts.mjs && node ../../platform/ui/scripts/check-route-wiring.mjs && node ../../platform/ui/scripts/check-edge-admin-routes.mjs && npm run build && node --test 'dist/__tests__/*.test.js' && chmod +x dist/index.js && node ../../platform/scripts/check-orphan-dist.mjs && npm run bundle && bash ../../platform/scripts/smoke-boot-services.sh && node ../../platform/ui/scripts/check-bundle-node-imports.mjs --dir=./payload/server/public/assets"
17
17
  },
18
18
  "files": [
19
19
  "dist",
@@ -12,6 +12,7 @@
12
12
  "scripts": {
13
13
  "build": "tsc -p lib/account-schema-regions/tsconfig.json && tsc -p lib/account-ingest-token/tsconfig.json && tsc -p lib/models/tsconfig.json && tsc -p lib/agent-slug/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/ledger-core/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p lib/routine-templates/tsconfig.json && tsc -p lib/agent-dispatch-rule/tsconfig.json && tsc -p lib/canonical-instant/tsconfig.json && tsc -p lib/dispatch-read/tsconfig.json && tsc -p lib/dispatch-write/tsconfig.json && tsc -p lib/learning-constants/tsconfig.json && tsc -p lib/telegram-media/tsconfig.json && tsc -p lib/telegram-managed-bot/tsconfig.json && tsc -p lib/telegram-reach/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && tsc -p services/telegram-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
14
14
  "build:lib": "tsc -p lib/account-schema-regions/tsconfig.json && tsc -p lib/account-ingest-token/tsconfig.json && tsc -p lib/models/tsconfig.json && tsc -p lib/agent-slug/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/ledger-core/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p lib/routine-templates/tsconfig.json && tsc -p lib/agent-dispatch-rule/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/canonical-instant/tsconfig.json && tsc -p lib/dispatch-read/tsconfig.json && tsc -p lib/dispatch-write/tsconfig.json && tsc -p lib/telegram-media/tsconfig.json && tsc -p lib/telegram-managed-bot/tsconfig.json && tsc -p lib/telegram-reach/tsconfig.json",
15
+ "clean:dist": "node scripts/check-orphan-dist.mjs --remove",
15
16
  "gen:canonical-tools": "node scripts/generate-canonical-tool-names.mjs",
16
17
  "build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
17
18
  "build:contacts": "tsc -p plugins/contacts/mcp/tsconfig.json",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what Real Agent ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
4
- content-hash: sha256:c66d6d534933072b64b3b80ac132135ee30f82b6eb50a4a7950a6500cf0ca678
4
+ content-hash: sha256:ea35aa77226e01aad93c7b90e9118e4e993a58cb980330b28541dcbb3122a993
5
5
  brand: realagent-code
6
6
  product-name: Real Agent
7
7
  ---
@@ -2866,7 +2866,9 @@ Real Agent asks for your bot token, then asks what kind of bot this is:
2866
2866
 
2867
2867
  - an **admin bot** — only your numeric Telegram IDs may message it, and it answers as your admin agent;
2868
2868
  - a **public bot** — customer-facing, with a DM policy, answering as one of your account's agents;
2869
- - 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.
2869
+ - 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 a specialist carries whatever tools its card gives it, while a public bot is held to a fixed set nobody can widen.
2870
+
2871
+ A public Telegram bot holds five things and nothing else: the reply that carries its answer, cards with tappable buttons, polls and quizzes and map pins, outgoing photos and documents and voice notes, and the ability to open a file a visitor sent it. That set is fixed by the platform for every public agent on every account, so there is no tools setting on an agent to turn any of it on or off. A public bot on webchat or WhatsApp still has the reply alone, because those channels ship no equivalent set.
2870
2872
 
2871
2873
  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.
2872
2874
 
@@ -6455,7 +6457,7 @@ For subagents, the same spawn pushes `--add-dir` for every bundled plugin agents
6455
6457
 
6456
6458
  A boot gate refuses to start the manager when any admin-allowlisted tool `mcp__<plugin>__*` lacks a registered server. The signal is `boot-failed reason=mcp-allowlist-without-server plugin=<p> tool=<t>` followed by `process.exit(1)`. The remediation is a one-line edit to the named `PLUGIN.md`: add the `mcp:` block. The complementary observability emit `mcp-config-allowlist-coverage admin-tools=A admin-registered=R` (where `A === R`) confirms the invariant per boot.
6457
6459
 
6458
- **Whether a plugin's server mounts is decided by the generated manifest, not the `mcp:` block.** A plugin ships its MCP as `mcp/`; the manifest generator ([`scripts/generate-plugin-manifests.mjs`](../../../../scripts/generate-plugin-manifests.mjs), run at bundle time over the payload) reads the plugin's `mcp-manifest:` frontmatter. `auto` emits an `mcpServers` block into the generated `.claude-plugin/plugin.json` — routed through `lib/mcp-spawn-tee`, carrying no per-session env (`LOG_DIR`/`SESSION_ID`/`ACCOUNT_ID`/`NEO4J_*`/`PLATFORM_ROOT` ride the PTY env block described above) — and copies the tee into the plugin's `lib/`; `skip` emits nothing, so Claude Code never spawns the server. The generator recognises two server shapes. A plugin-local `mcp/` (its `hasMcp` check: `mcp/dist/index.js` or `mcp/package.json`) uses `auto` or `skip`. A tool-plugin whose `mcp:` server is a shared `platform/lib/<name>` binary rather than a plugin-local `mcp/` (e.g. `graph`, served by `platform/lib/graph-mcp`) is annotated `mcp-manifest: shared-lib`: the generator emits that plugin's `mcp:` block verbatim into `mcpServers` — command, args (anchored on `${PLATFORM_ROOT}`, routed through the shared `lib/mcp-spawn-tee`), and env — which Claude Code expands at spawn from the PTY env block. No tee copy is made into the plugin, because both the tee and the server resolve from `${PLATFORM_ROOT}` outside the per-plugin cache. `graph` is the one shared-lib tool-plugin; every other tool-plugin ships a plugin-local `mcp/` and uses `auto`. A plugin with an `mcp:` block, no plugin-local `mcp/`, and no `mcp-manifest: shared-lib` annotation is a FATAL at generation time. Premium plugins import platform libs (`mcp-lifeline`, etc.) via a source-tree relative path; the bundler's premium-lib shipper (`packages/create-maxy-code/scripts/bundle.js`) rewrites that path and vendors each referenced `platform/lib/<name>` into `premium-plugins/<bundle>/lib/<name>/`, so the auto-mounted dist resolves its imports. The `mcp-allowlist-without-server` gate above proves only that a plugin's PLUGIN.md `mcp:` block matches its allowlist — it reads source, not the generated manifest — so a plugin left on `skip` passes that gate while its server silently never mounts (its tools are listed but not live). The `tool-registration` gate below closes that blind spot.
6460
+ **Whether a plugin's server mounts is decided by the generated manifest, not the `mcp:` block.** A plugin ships its MCP as `mcp/`; the manifest generator ([`scripts/generate-plugin-manifests.mjs`](../../../../scripts/generate-plugin-manifests.mjs), run at bundle time over the payload) reads the plugin's `mcp-manifest:` frontmatter. `auto` emits an `mcpServers` block into the generated `.claude-plugin/plugin.json` — routed through `lib/mcp-spawn-tee`, carrying no per-session env (`LOG_DIR`/`SESSION_ID`/`ACCOUNT_ID`/`NEO4J_*`/`PLATFORM_ROOT` ride the PTY env block described above) — and copies the tee into the plugin's `lib/`; `skip` emits nothing, so Claude Code never spawns the server. `skip` covers two shapes: a plugin whose canonical surface is a user-scoped MCP, and a plugin that registers zero tools and keeps `mcp/` only as a test host — `cloudflare` is the latter, so its `skip` is not a pointer to a server elsewhere. The generator recognises two server shapes. A plugin-local `mcp/` (its `hasMcp` check: `mcp/dist/index.js` or `mcp/package.json`) uses `auto` or `skip`. A tool-plugin whose `mcp:` server is a shared `platform/lib/<name>` binary rather than a plugin-local `mcp/` (e.g. `graph`, served by `platform/lib/graph-mcp`) is annotated `mcp-manifest: shared-lib`: the generator emits that plugin's `mcp:` block verbatim into `mcpServers` — command, args (anchored on `${PLATFORM_ROOT}`, routed through the shared `lib/mcp-spawn-tee`), and env — which Claude Code expands at spawn from the PTY env block. No tee copy is made into the plugin, because both the tee and the server resolve from `${PLATFORM_ROOT}` outside the per-plugin cache. `graph` is the one shared-lib tool-plugin; every other tool-plugin ships a plugin-local `mcp/` and uses `auto`. A plugin with an `mcp:` block, no plugin-local `mcp/`, and no `mcp-manifest: shared-lib` annotation is a FATAL at generation time. Premium plugins import platform libs (`mcp-lifeline`, etc.) via a source-tree relative path; the bundler's premium-lib shipper (`packages/create-maxy-code/scripts/bundle.js`) rewrites that path and vendors each referenced `platform/lib/<name>` into `premium-plugins/<bundle>/lib/<name>/`, so the auto-mounted dist resolves its imports. The `mcp-allowlist-without-server` gate above proves only that a plugin's PLUGIN.md `mcp:` block matches its allowlist — it reads source, not the generated manifest — so a plugin left on `skip` passes that gate while its server silently never mounts (its tools are listed but not live). The `tool-registration` gate below closes that blind spot.
6459
6461
 
6460
6462
  A second boot gate walks every specialist `.md` under `platform/templates/specialists/agents/`, every bundled `<plugin>/agents/` directory, and the per-account `<accountDir>/specialists/agents/` directory, parses each file's `tools:` frontmatter line (canonical long-prefix names), and classifies every tool name as one of: CC-native (Read, Bash, …), a tool the loaded `PLUGIN.md` set actually serves (matched as the long canonical name in `toolSurface.all`), a third-party MCP bridge (a `mcp__plugin_*` name whose plugin segment is NOT a maxy platform plugin — Playwright etc., upstream-owned, passes unconditionally), `unknown-tool-in-plugin` (maxy plugin namespace served but tool name absent), `unknown-plugin-namespace` (namespace served by nothing), `brand-excluded-plugin` (namespace served by nothing on this brand, **but** the brand's `brand.json#plugins.excluded` list names it), or `malformed-name` (not CC-native and not `mcp__`-shaped). The first three pass. The next two refuse boot with one `boot-failed reason=specialist-tool-drift specialist=<name> tool=<t> drift=<class> path=<…>` line per defect, then `process.exit(1)`. A maxy-plugin `mcp__plugin_*` name is validated against `toolSurface.all`, so a typo or stale long-prefix tool name still refuses boot rather than passing as a bridge; the build-time `check-canonical-tool-names.mjs` gate catches the same drift in instruction files before publish. `brand-excluded-plugin` is a structural pass: it lands in a per-specialist strip-list, the manager continues to boot, and at spawn time `pty-spawner` removes those tool names from the `--agent <name>` spawn's `--allowed-tools` argv. The complementary observability emit `specialist-tool-strip specialist=<name> plugin=<p> tools=<csv> reason=brand-excluded` fires one line per stripped (specialist, plugin) pair so an operator who reads `server.log` sees the brand filter doing work without cross-referencing `brand.json` against the template. The startup-self-test line `startup-self-test specialist-tool-drift=ok inspected=<N> stripped-specialists=<M>` confirms the gate ran and how many specialists carry strip-lists.
6461
6463
 
@@ -9,6 +9,11 @@ Invoked by the admin agent directly.
9
9
 
10
10
  This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
11
11
 
12
+ ## 2026-08-11 (0.1.594)
13
+
14
+ - A public Telegram bot no longer tells people it cannot do things it can. Its own description denied the tools it had actually been given, so it refused work it was equipped for.
15
+ - Code deleted from the platform no longer ships anyway. Removing a source file left its compiled output behind, so retired code was still being packaged and installed.
16
+
12
17
  ## 2026-08-11 (0.1.593)
13
18
 
14
19
  - A public Telegram bot can now use buttons, media and the rest of the channel. It held no tool at all, so every answer it gave was plain text.
@@ -28,6 +28,8 @@ Each installation has its own Cloudflare account. Two auth paths serve two opera
28
28
 
29
29
  The plugin registers no agent-facing MCP tools. Every Cloudflare operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, or calling the Cloudflare API with a reused per-scope narrow token, with stdout/stderr streamed verbatim into the PTY chat (secrets redacted). The OAuth URL printed by `cloudflared tunnel login` is linkified by the native PTY; the operator clicks it in their own browser. The one shell helper is [bin/cf-token.sh](bin/cf-token.sh), a deterministic per-scope token resolver that faithfully relays the Cloudflare token API (it invents no flags and drives no dashboard); there is no wrapper around `cloudflared` or `wrangler`, no orchestrator state machine, no MCP-tool surface.
30
30
 
31
+ The frontmatter carries `mcp-manifest: skip` because the manifest generator requires an annotation from any plugin with an `mcp/` directory, and `skip` is the value that emits no `mcpServers` block. It does not mean the tools live in a user-scoped server somewhere else: there are none. `mcp/` survives only as the test host for `bin/` and for the skill templates' Pages Functions; its entry point registers nothing.
32
+
31
33
  ### Skills
32
34
 
33
35
  | Skill | Purpose |
@@ -501,7 +501,7 @@ For subagents, the same spawn pushes `--add-dir` for every bundled plugin agents
501
501
 
502
502
  A boot gate refuses to start the manager when any admin-allowlisted tool `mcp__<plugin>__*` lacks a registered server. The signal is `boot-failed reason=mcp-allowlist-without-server plugin=<p> tool=<t>` followed by `process.exit(1)`. The remediation is a one-line edit to the named `PLUGIN.md`: add the `mcp:` block. The complementary observability emit `mcp-config-allowlist-coverage admin-tools=A admin-registered=R` (where `A === R`) confirms the invariant per boot.
503
503
 
504
- **Whether a plugin's server mounts is decided by the generated manifest, not the `mcp:` block.** A plugin ships its MCP as `mcp/`; the manifest generator ([`scripts/generate-plugin-manifests.mjs`](../../../../scripts/generate-plugin-manifests.mjs), run at bundle time over the payload) reads the plugin's `mcp-manifest:` frontmatter. `auto` emits an `mcpServers` block into the generated `.claude-plugin/plugin.json` — routed through `lib/mcp-spawn-tee`, carrying no per-session env (`LOG_DIR`/`SESSION_ID`/`ACCOUNT_ID`/`NEO4J_*`/`PLATFORM_ROOT` ride the PTY env block described above) — and copies the tee into the plugin's `lib/`; `skip` emits nothing, so Claude Code never spawns the server. The generator recognises two server shapes. A plugin-local `mcp/` (its `hasMcp` check: `mcp/dist/index.js` or `mcp/package.json`) uses `auto` or `skip`. A tool-plugin whose `mcp:` server is a shared `platform/lib/<name>` binary rather than a plugin-local `mcp/` (e.g. `graph`, served by `platform/lib/graph-mcp`) is annotated `mcp-manifest: shared-lib`: the generator emits that plugin's `mcp:` block verbatim into `mcpServers` — command, args (anchored on `${PLATFORM_ROOT}`, routed through the shared `lib/mcp-spawn-tee`), and env — which Claude Code expands at spawn from the PTY env block. No tee copy is made into the plugin, because both the tee and the server resolve from `${PLATFORM_ROOT}` outside the per-plugin cache. `graph` is the one shared-lib tool-plugin; every other tool-plugin ships a plugin-local `mcp/` and uses `auto`. A plugin with an `mcp:` block, no plugin-local `mcp/`, and no `mcp-manifest: shared-lib` annotation is a FATAL at generation time. Premium plugins import platform libs (`mcp-lifeline`, etc.) via a source-tree relative path; the bundler's premium-lib shipper (`packages/create-maxy-code/scripts/bundle.js`) rewrites that path and vendors each referenced `platform/lib/<name>` into `premium-plugins/<bundle>/lib/<name>/`, so the auto-mounted dist resolves its imports. The `mcp-allowlist-without-server` gate above proves only that a plugin's PLUGIN.md `mcp:` block matches its allowlist — it reads source, not the generated manifest — so a plugin left on `skip` passes that gate while its server silently never mounts (its tools are listed but not live). The `tool-registration` gate below closes that blind spot.
504
+ **Whether a plugin's server mounts is decided by the generated manifest, not the `mcp:` block.** A plugin ships its MCP as `mcp/`; the manifest generator ([`scripts/generate-plugin-manifests.mjs`](../../../../scripts/generate-plugin-manifests.mjs), run at bundle time over the payload) reads the plugin's `mcp-manifest:` frontmatter. `auto` emits an `mcpServers` block into the generated `.claude-plugin/plugin.json` — routed through `lib/mcp-spawn-tee`, carrying no per-session env (`LOG_DIR`/`SESSION_ID`/`ACCOUNT_ID`/`NEO4J_*`/`PLATFORM_ROOT` ride the PTY env block described above) — and copies the tee into the plugin's `lib/`; `skip` emits nothing, so Claude Code never spawns the server. `skip` covers two shapes: a plugin whose canonical surface is a user-scoped MCP, and a plugin that registers zero tools and keeps `mcp/` only as a test host — `cloudflare` is the latter, so its `skip` is not a pointer to a server elsewhere. The generator recognises two server shapes. A plugin-local `mcp/` (its `hasMcp` check: `mcp/dist/index.js` or `mcp/package.json`) uses `auto` or `skip`. A tool-plugin whose `mcp:` server is a shared `platform/lib/<name>` binary rather than a plugin-local `mcp/` (e.g. `graph`, served by `platform/lib/graph-mcp`) is annotated `mcp-manifest: shared-lib`: the generator emits that plugin's `mcp:` block verbatim into `mcpServers` — command, args (anchored on `${PLATFORM_ROOT}`, routed through the shared `lib/mcp-spawn-tee`), and env — which Claude Code expands at spawn from the PTY env block. No tee copy is made into the plugin, because both the tee and the server resolve from `${PLATFORM_ROOT}` outside the per-plugin cache. `graph` is the one shared-lib tool-plugin; every other tool-plugin ships a plugin-local `mcp/` and uses `auto`. A plugin with an `mcp:` block, no plugin-local `mcp/`, and no `mcp-manifest: shared-lib` annotation is a FATAL at generation time. Premium plugins import platform libs (`mcp-lifeline`, etc.) via a source-tree relative path; the bundler's premium-lib shipper (`packages/create-maxy-code/scripts/bundle.js`) rewrites that path and vendors each referenced `platform/lib/<name>` into `premium-plugins/<bundle>/lib/<name>/`, so the auto-mounted dist resolves its imports. The `mcp-allowlist-without-server` gate above proves only that a plugin's PLUGIN.md `mcp:` block matches its allowlist — it reads source, not the generated manifest — so a plugin left on `skip` passes that gate while its server silently never mounts (its tools are listed but not live). The `tool-registration` gate below closes that blind spot.
505
505
 
506
506
  A second boot gate walks every specialist `.md` under `platform/templates/specialists/agents/`, every bundled `<plugin>/agents/` directory, and the per-account `<accountDir>/specialists/agents/` directory, parses each file's `tools:` frontmatter line (canonical long-prefix names), and classifies every tool name as one of: CC-native (Read, Bash, …), a tool the loaded `PLUGIN.md` set actually serves (matched as the long canonical name in `toolSurface.all`), a third-party MCP bridge (a `mcp__plugin_*` name whose plugin segment is NOT a maxy platform plugin — Playwright etc., upstream-owned, passes unconditionally), `unknown-tool-in-plugin` (maxy plugin namespace served but tool name absent), `unknown-plugin-namespace` (namespace served by nothing), `brand-excluded-plugin` (namespace served by nothing on this brand, **but** the brand's `brand.json#plugins.excluded` list names it), or `malformed-name` (not CC-native and not `mcp__`-shaped). The first three pass. The next two refuse boot with one `boot-failed reason=specialist-tool-drift specialist=<name> tool=<t> drift=<class> path=<…>` line per defect, then `process.exit(1)`. A maxy-plugin `mcp__plugin_*` name is validated against `toolSurface.all`, so a typo or stale long-prefix tool name still refuses boot rather than passing as a bridge; the build-time `check-canonical-tool-names.mjs` gate catches the same drift in instruction files before publish. `brand-excluded-plugin` is a structural pass: it lands in a per-specialist strip-list, the manager continues to boot, and at spawn time `pty-spawner` removes those tool names from the `--agent <name>` spawn's `--allowed-tools` argv. The complementary observability emit `specialist-tool-strip specialist=<name> plugin=<p> tools=<csv> reason=brand-excluded` fires one line per stripped (specialist, plugin) pair so an operator who reads `server.log` sees the brand filter doing work without cross-referencing `brand.json` against the template. The startup-self-test line `startup-self-test specialist-tool-drift=ok inspected=<N> stripped-specialists=<M>` confirms the gate ran and how many specialists carry strip-lists.
507
507
 
@@ -43,7 +43,9 @@ Tell {{productName}}: "Set up Telegram" or "Configure the Telegram bot." Setup r
43
43
 
44
44
  - an **admin bot** — only your numeric Telegram IDs may message it, and it answers as your admin agent;
45
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.
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 a specialist carries whatever tools its card gives it, while a public bot is held to a fixed set nobody can widen.
47
+
48
+ A public Telegram bot holds five things and nothing else: the reply that carries its answer, cards with tappable buttons, polls and quizzes and map pins, outgoing photos and documents and voice notes, and the ability to open a file a visitor sent it. That set is fixed by the platform for every public agent on every account, so there is no tools setting on an agent to turn any of it on or off. A public bot on webchat or WhatsApp still has the reply alone, because those channels ship no equivalent set.
47
49
 
48
50
  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.
49
51
 
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env bash
2
+ # Task 2671 — reconcile_all_accounts_identities converges every account's
3
+ # public-agent IDENTITY.md against the shipped template. The failure it exists
4
+ # for is silent: the re-sync it replaces ran for the house account alone, so a
5
+ # sub-account's agent kept a stale prompt indefinitely and nothing said so.
6
+ set -uo pipefail
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PLAT_SCRIPTS="$SCRIPT_DIR/.."
9
+ PASS=0; FAIL=0; FAILED=()
10
+ assert_eq() { if [ "$1" = "$2" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$3: expected [$2] got [$1]"); fi; }
11
+
12
+ . "$PLAT_SCRIPTS/lib/agent-identity-reconcile.sh"
13
+
14
+ T=$(mktemp -d)
15
+ TPL="$T/IDENTITY.md"
16
+ printf '# Public Agent\n\nShipped text.\n' > "$TPL"
17
+ export MAXY_PUBLIC_IDENTITY_TEMPLATE="$TPL"
18
+
19
+ seed_account() {
20
+ # seed_account <accounts_dir> <account_id>
21
+ local root="$1/$2"
22
+ mkdir -p "$root/agents"
23
+ printf '{"accountId":"%s"}\n' "$2" > "$root/account.json"
24
+ echo "$root"
25
+ }
26
+ seed_agent() {
27
+ # seed_agent <account_root> <slug> <body>
28
+ mkdir -p "$1/agents/$2"
29
+ printf '%s' "$3" > "$1/agents/$2/IDENTITY.md"
30
+ }
31
+
32
+ ACCTS="$T/accounts"
33
+ A1="$(seed_account "$ACCTS" aaaaaaaa-0000-0000-0000-000000000001)"
34
+ A2="$(seed_account "$ACCTS" bbbbbbbb-0000-0000-0000-000000000002)"
35
+
36
+ seed_agent "$A1" beagle 'STALE — you have no tools.'
37
+ seed_agent "$A1" admin 'ADMIN identity, never touched.'
38
+ seed_agent "$A1" passive 'PASSIVE identity, never touched.'
39
+ # Byte-identical to the template, so `cp` rather than a command substitution:
40
+ # `$(cat …)` strips the trailing newline and the copy would read as drift.
41
+ mkdir -p "$A2/agents/fares" && cp "$TPL" "$A2/agents/fares/IDENTITY.md"
42
+ # An agent dir with no IDENTITY.md: counted by nothing, created by nothing.
43
+ mkdir -p "$A2/agents/halfmade"
44
+ # A dir with no account.json is not an account.
45
+ mkdir -p "$ACCTS/not-an-account/agents"
46
+
47
+ OUT1="$(reconcile_all_accounts_identities "$ACCTS" 2>&1)"; RC1=$?
48
+
49
+ assert_eq "$RC1" "0" "clean-run-exit-zero"
50
+ assert_eq "$(printf '%s' "$OUT1" | grep -c 'accounts=2 agents=2 divergent=1')" "1" "summary-counts"
51
+ assert_eq "$(cat "$A1/agents/beagle/IDENTITY.md")" "$(cat "$TPL")" "stale-rewritten"
52
+ assert_eq "$(cat "$A1/agents/admin/IDENTITY.md")" "ADMIN identity, never touched." "admin-skipped"
53
+ assert_eq "$(cat "$A1/agents/passive/IDENTITY.md")" "PASSIVE identity, never touched." "passive-skipped"
54
+ assert_eq "$(printf '%s' "$OUT1" | grep -c 'agent=beagle status=updated')" "1" "stale-logged-updated"
55
+ assert_eq "$(printf '%s' "$OUT1" | grep -c 'agent=fares status=already-current')" "1" "current-logged-current"
56
+ assert_eq "$(printf '%s' "$OUT1" | grep -c 'agent=admin\|agent=passive\|agent=halfmade')" "0" "excluded-not-logged"
57
+ assert_eq "$([ -e "$A2/agents/halfmade/IDENTITY.md" ] && echo yes || echo no)" "no" "never-creates"
58
+
59
+ # Second run: same agent count, nothing left divergent. A zero that follows a
60
+ # non-zero is the only zero that proves the pass runs — `divergent=0` on its own
61
+ # is what a stopped counter prints too.
62
+ OUT2="$(reconcile_all_accounts_identities "$ACCTS" 2>&1)"; RC2=$?
63
+ assert_eq "$RC2" "0" "idempotent-exit-zero"
64
+ assert_eq "$(printf '%s' "$OUT2" | grep -c 'accounts=2 agents=2 divergent=0')" "1" "idempotent-summary"
65
+
66
+ # Missing template: reported per agent, counted divergent, never fatal, and the
67
+ # agent's file is left exactly as it was rather than truncated.
68
+ export MAXY_PUBLIC_IDENTITY_TEMPLATE="$T/absent.md"
69
+ BEFORE="$(cat "$A1/agents/beagle/IDENTITY.md")"
70
+ OUT3="$(reconcile_all_accounts_identities "$ACCTS" 2>&1)"; RC3=$?
71
+ assert_eq "$RC3" "0" "no-template-not-fatal"
72
+ assert_eq "$(printf '%s' "$OUT3" | grep -c 'status=no-template')" "2" "no-template-logged-per-agent"
73
+ assert_eq "$(printf '%s' "$OUT3" | grep -c 'accounts=2 agents=2 divergent=2')" "1" "no-template-counted-divergent"
74
+ assert_eq "$(cat "$A1/agents/beagle/IDENTITY.md")" "$BEFORE" "no-template-leaves-file"
75
+
76
+ # An empty destination after a copy aborts the install: that state traps the
77
+ # brand server in STARTING and was observed on a live device.
78
+ export MAXY_PUBLIC_IDENTITY_TEMPLATE="$TPL"
79
+ printf 'drifted again' > "$A1/agents/beagle/IDENTITY.md"
80
+ CP_STUB="$T/bin"; mkdir -p "$CP_STUB"
81
+ printf '#!/usr/bin/env bash\n: > "${@: -1}"\nexit 0\n' > "$CP_STUB/cp"; chmod +x "$CP_STUB/cp"
82
+ OUT4="$(PATH="$CP_STUB:$PATH" reconcile_all_accounts_identities "$ACCTS" 2>&1)"; RC4=$?
83
+ assert_eq "$RC4" "1" "empty-copy-is-fatal"
84
+ assert_eq "$(printf '%s' "$OUT4" | grep -c 'status=empty-after-copy')" "1" "empty-copy-logged"
85
+ assert_eq "$(printf '%s' "$OUT4" | grep -c 'FATAL')" "1" "empty-copy-fatal-line"
86
+
87
+ # setup-account.sh runs under `set -euo pipefail`, and the reconciler is called
88
+ # there as a bare command. A non-zero from any per-agent call therefore kills the
89
+ # whole install, and it kills it on exactly the runs that had work to do — the
90
+ # ones where an agent diverged. Reproduced before the fix: the summary line never
91
+ # printed and the sourcing shell exited 1 after the first `status=updated`.
92
+ export MAXY_PUBLIC_IDENTITY_TEMPLATE="$TPL"
93
+ printf 'drifted under set -e' > "$A1/agents/beagle/IDENTITY.md"
94
+ OUT5="$(bash -c '
95
+ set -euo pipefail
96
+ . "'"$PLAT_SCRIPTS"'/lib/agent-identity-reconcile.sh"
97
+ reconcile_all_accounts_identities "'"$ACCTS"'"
98
+ echo REACHED-END' 2>&1)"; RC5=$?
99
+ assert_eq "$RC5" "0" "set-e-does-not-abort"
100
+ assert_eq "$(printf '%s' "$OUT5" | grep -c 'REACHED-END')" "1" "set-e-reaches-end"
101
+ assert_eq "$(printf '%s' "$OUT5" | grep -c 'accounts=2 agents=2 divergent=1')" "1" "set-e-summary-printed"
102
+
103
+ rm -rf "$T"
104
+
105
+ if [ "$FAIL" -gt 0 ]; then
106
+ printf 'FAIL %d, PASS %d\n' "$FAIL" "$PASS"
107
+ printf ' %s\n' "${FAILED[@]}"
108
+ exit 1
109
+ fi
110
+ printf 'PASS %d\n' "$PASS"
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env bash
2
+ # The orphan-dist gate. A compiled .js whose .ts was deleted must fail; a
3
+ # nested path must map by its relative path, not its basename; a dist whose
4
+ # whole project is gone must be reported as a project orphan rather than one
5
+ # line per file; and a run that finds no dist tree at all must fail rather
6
+ # than report a clean zero, because "measured nothing" and "found nothing"
7
+ # are different answers.
8
+ set -u
9
+ GATE="$(cd "$(dirname "$0")/.." && pwd)/check-orphan-dist.mjs"
10
+ [[ -f "$GATE" ]] || { echo "FAIL: gate script missing at $GATE" >&2; exit 1; }
11
+ PASS=0; FAIL=0
12
+
13
+ ROOT=$(mktemp -d)
14
+ trap 'rm -rf "$ROOT"' EXIT
15
+
16
+ reset_root() { rm -rf "$ROOT/platform"; mkdir -p "$ROOT/platform"; }
17
+
18
+ # The plugins family nests its project under mcp/; lib and services do not.
19
+ fam_base() { # $1=family $2=project $3=dist|src
20
+ if [[ "$1" == plugins ]]; then echo "$ROOT/platform/$1/$2/mcp/$3"; else echo "$ROOT/platform/$1/$2/$3"; fi
21
+ }
22
+ make_out() { local b; b="$(fam_base "$1" "$2" dist)"; mkdir -p "$b/$(dirname "$3")"; printf 'export {};\n' > "$b/$3"; }
23
+ make_src() { local b; b="$(fam_base "$1" "$2" src)"; mkdir -p "$b/$(dirname "$3")"; printf 'export {};\n' > "$b/$3"; }
24
+
25
+ check() { # $1=desc $2=expected-exit $3=expected substring (may be empty)
26
+ local desc="$1" expected="$2" needle="${3:-}" out got
27
+ out=$(node "$GATE" "$ROOT/platform" 2>&1); got=$?
28
+ if [[ "$got" -ne "$expected" ]]; then
29
+ echo "FAIL: $desc — expected exit $expected, got $got"; printf '%s\n' "$out" | sed 's/^/ /'
30
+ FAIL=$((FAIL+1)); return
31
+ fi
32
+ if [[ -n "$needle" ]] && ! printf '%s' "$out" | grep -qF -- "$needle"; then
33
+ echo "FAIL: $desc — output missing [$needle]"; printf '%s\n' "$out" | sed 's/^/ /'
34
+ FAIL=$((FAIL+1)); return
35
+ fi
36
+ echo "PASS: $desc"; PASS=$((PASS+1))
37
+ }
38
+
39
+ reset_root
40
+ make_src plugins alpha index.ts; make_out plugins alpha index.js
41
+ check "a compiled file with its source passes" 0 "orphans=0"
42
+
43
+ reset_root
44
+ make_src plugins alpha lib/nested.ts; make_out plugins alpha lib/nested.js
45
+ check "a nested path maps by relative path, not basename" 0 "orphans=0"
46
+
47
+ reset_root
48
+ make_src lib beta index.ts; make_out lib beta index.js
49
+ make_src services gamma index.ts; make_out services gamma index.js
50
+ check "lib and services trees are scanned too" 0 "trees=2"
51
+
52
+ reset_root
53
+ make_src plugins alpha index.ts; make_out plugins alpha index.js
54
+ make_out plugins alpha lib/gone.js
55
+ check "a compiled file whose source was deleted fails" 1 "orphan=plugins/alpha/mcp/dist/lib/gone.js"
56
+
57
+ reset_root
58
+ make_out lib deleted-project index.js; make_out lib deleted-project helper.js
59
+ check "a dist whose whole project is gone is one project orphan" 1 "orphan-project=lib/deleted-project/dist"
60
+
61
+ reset_root
62
+ check "no dist tree at all fails rather than reporting a clean zero" 1 "trees=0"
63
+
64
+ # --remove deletes exactly what it reported, and leaves the live file alone.
65
+ reset_root
66
+ make_src plugins alpha index.ts; make_out plugins alpha index.js
67
+ make_out plugins alpha lib/gone.js
68
+ printf '{}\n' > "$(fam_base plugins alpha dist)/lib/gone.js.map"
69
+ REMOVE_OUT=$(node "$GATE" "$ROOT/platform" --remove 2>&1); REMOVE_EXIT=$?
70
+ if [[ ! -e "$(fam_base plugins alpha dist)/lib/gone.js" \
71
+ && ! -e "$(fam_base plugins alpha dist)/lib/gone.js.map" \
72
+ && -f "$(fam_base plugins alpha dist)/index.js" ]]; then
73
+ echo "PASS: --remove deletes the orphan and its sidecars, keeps the live file"; PASS=$((PASS+1))
74
+ else
75
+ echo "FAIL: --remove did not delete exactly the orphan set"; FAIL=$((FAIL+1))
76
+ fi
77
+
78
+ # The removal must report the state on disk afterwards, not that a delete ran.
79
+ if printf '%s' "$REMOVE_OUT" | grep -qF -- "removed=1 remaining=0" && [[ "$REMOVE_EXIT" -eq 0 ]]; then
80
+ echo "PASS: --remove re-scans and reports the post-condition"; PASS=$((PASS+1))
81
+ else
82
+ echo "FAIL: --remove did not re-scan — expected 'removed=1 remaining=0' and exit 0, got exit $REMOVE_EXIT"
83
+ printf '%s\n' "$REMOVE_OUT" | sed 's/^/ /'; FAIL=$((FAIL+1))
84
+ fi
85
+
86
+ echo "---"; echo "PASS=$PASS FAIL=$FAIL"
87
+ [[ "$FAIL" -eq 0 ]]
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ // Reconciles every compiled output tree under platform/ against its sources.
3
+ //
4
+ // Why this is a standing check and not a log line: `tsc` never removes output
5
+ // for a source that was deleted, and the bundler copies each tree — `dist`
6
+ // included — into the payload that `npm publish` ships. The deletion emits no
7
+ // event, and rebuilding does not surface the leftover, so nothing short of an
8
+ // independent reconcile can see it.
9
+ //
10
+ // Every project compiles `rootDir: src` to `outDir: dist`, so `dist/<rel>.js`
11
+ // maps to exactly one source, `src/<rel>.ts`. Two shapes of orphan exist:
12
+ // file — the .ts was deleted, the project still exists. `tsc -b --clean`
13
+ // would remove it.
14
+ // project — the whole project was deleted; `dist` survives with no `src` and
15
+ // no tsconfig, so no tsc invocation can ever reach it. Reported
16
+ // once per directory rather than once per file.
17
+ //
18
+ // Usage: node check-orphan-dist.mjs [platformRoot] [--remove]
19
+ // Exit 0 when every .js has a source. Exit 1 on any orphan, and exit 1 when no
20
+ // dist tree was found at all — a zero-orphan pass must never be
21
+ // indistinguishable from having measured nothing.
22
+ //
23
+ // With --remove it deletes exactly the set it just reported (each orphan .js
24
+ // plus its .js.map/.d.ts/.d.ts.map sidecars; a project orphan's whole dist
25
+ // directory) and then re-scans, so the exit code reflects the state on disk
26
+ // afterwards rather than the fact that a delete was called. Sharing one rule
27
+ // between the detector and the repair is what stops the two drifting apart.
28
+
29
+ import { existsSync, readdirSync, rmSync } from "node:fs";
30
+ import { join, resolve, relative, dirname } from "node:path";
31
+ import { fileURLToPath } from "node:url";
32
+
33
+ const args = process.argv.slice(2);
34
+ const remove = args.includes("--remove");
35
+ const rootArg = args.find((a) => !a.startsWith("--"));
36
+ const scriptDir = dirname(fileURLToPath(import.meta.url));
37
+ const platformRoot = resolve(rootArg ?? join(scriptDir, ".."));
38
+
39
+ // Each family maps a project directory to its dist/src pair. `sub` is the
40
+ // directory between the project and its dist/src, empty where there is none.
41
+ const FAMILIES = [
42
+ { dir: "plugins", sub: "mcp" },
43
+ { dir: "lib", sub: "" },
44
+ { dir: "services", sub: "" },
45
+ ];
46
+
47
+ function subdirs(dir) {
48
+ if (!existsSync(dir)) return [];
49
+ return readdirSync(dir, { withFileTypes: true })
50
+ .filter((e) => e.isDirectory())
51
+ .map((e) => e.name)
52
+ .sort();
53
+ }
54
+
55
+ function walkJs(dir, out) {
56
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
57
+ const full = join(dir, entry.name);
58
+ if (entry.isDirectory()) walkJs(full, out);
59
+ else if (entry.name.endsWith(".js")) out.push(full);
60
+ }
61
+ return out;
62
+ }
63
+
64
+ function scan() {
65
+ let trees = 0;
66
+ let files = 0;
67
+ const fileOrphans = [];
68
+ const projectOrphans = [];
69
+
70
+ for (const family of FAMILIES) {
71
+ const familyDir = join(platformRoot, family.dir);
72
+ for (const project of subdirs(familyDir)) {
73
+ const base = family.sub
74
+ ? join(familyDir, project, family.sub)
75
+ : join(familyDir, project);
76
+ const distDir = join(base, "dist");
77
+ if (!existsSync(distDir)) continue;
78
+ trees += 1;
79
+ const srcDir = join(base, "src");
80
+ if (!existsSync(srcDir)) {
81
+ projectOrphans.push(distDir);
82
+ files += walkJs(distDir, []).length;
83
+ continue;
84
+ }
85
+ for (const js of walkJs(distDir, [])) {
86
+ files += 1;
87
+ const rel = js.slice(distDir.length + 1);
88
+ if (!existsSync(join(srcDir, `${rel.slice(0, -3)}.ts`))) fileOrphans.push(js);
89
+ }
90
+ }
91
+ }
92
+ return { trees, files, fileOrphans, projectOrphans, total: fileOrphans.length + projectOrphans.length };
93
+ }
94
+
95
+ const rel = (p) => relative(platformRoot, p);
96
+
97
+ function report(r) {
98
+ console.log(`[orphan-dist] trees=${r.trees} files=${r.files} orphans=${r.total}`);
99
+ for (const p of r.projectOrphans) console.log(`[orphan-dist] orphan-project=${rel(p)}`);
100
+ for (const f of r.fileOrphans) console.log(`[orphan-dist] orphan=${rel(f)}`);
101
+ }
102
+
103
+ const first = scan();
104
+ report(first);
105
+
106
+ if (first.trees === 0) {
107
+ console.error(
108
+ "[orphan-dist] FATAL: no dist tree found under platform/{plugins,lib,services} — " +
109
+ "the check measured nothing rather than finding nothing. Build platform first.",
110
+ );
111
+ process.exit(1);
112
+ }
113
+
114
+ if (remove && first.total > 0) {
115
+ for (const dir of first.projectOrphans) rmSync(dir, { recursive: true, force: true });
116
+ for (const js of first.fileOrphans) {
117
+ const stem = js.slice(0, -3);
118
+ for (const ext of [".js", ".js.map", ".d.ts", ".d.ts.map"]) {
119
+ rmSync(`${stem}${ext}`, { force: true });
120
+ }
121
+ }
122
+ // Re-scan rather than trust the delete. The exit code must reflect what is
123
+ // on disk now, not the fact that rmSync was called.
124
+ const after = scan();
125
+ console.log(`[orphan-dist] removed=${first.total - after.total} remaining=${after.total}`);
126
+ if (after.total > 0) {
127
+ console.error(
128
+ `[orphan-dist] FATAL: ${after.total} orphan(s) survived removal. Check permissions on the ` +
129
+ "paths listed above.",
130
+ );
131
+ process.exit(1);
132
+ }
133
+ process.exit(0);
134
+ }
135
+
136
+ if (first.total > 0) {
137
+ console.error(
138
+ `[orphan-dist] FATAL: ${first.total} compiled output(s) have no source. Their .ts was deleted and ` +
139
+ "the compiler left the .js behind, where the bundler copies it into the published payload. " +
140
+ "Run `npm run clean:dist` in platform/ to remove exactly this set.",
141
+ );
142
+ process.exit(1);
143
+ }
@@ -0,0 +1,122 @@
1
+ # agent-identity-reconcile.sh — converge every account's public-agent IDENTITY.md
2
+ # against the shipped template, and state the divergence count.
3
+ #
4
+ # Task 2671. The re-sync this replaces lived inside provision_account_dir, and
5
+ # setup-account.sh calls that for the house account and no other, so an agent
6
+ # created on a client sub-account keeps whatever identity text it was seeded
7
+ # with for ever. Measured on laptop 192.168.88.10 on 2026-08-11: the house
8
+ # account's agents were refreshed by that morning's install while
9
+ # `5bc2c0e2/agents/beagle` still carried the 2026-07-31 text and
10
+ # `.../fares` the 2026-08-04 one, both of them telling a spawn that now holds
11
+ # five tools that it holds none.
12
+ #
13
+ # This is the identity twin of account-settings-reconcile.sh, written the same
14
+ # way and for the same reason, and it is the file's only writer: the in-place
15
+ # loop was deleted when this landed.
16
+ #
17
+ # IDENTITY.md is Rubytech-controlled and copied verbatim (agent-write.ts:113), so
18
+ # an operator edit here is drift to be corrected, not content to preserve. That
19
+ # is the one place this diverges from the settings reconciler, which leaves an
20
+ # unparseable file alone because it may carry an operator edit worth keeping.
21
+ #
22
+ # Idempotent. Never creates an agent dir, never writes SOUL/KNOWLEDGE/config,
23
+ # never echoes file contents.
24
+ #
25
+ # Sourced by setup-account.sh; also runnable standalone for a manual repair.
26
+
27
+ # ensure_agent_identity <identity_file> <template_file> <account_id> <agent_slug>
28
+ # Returns 0 when the file already matched the template, 1 when it diverged, and
29
+ # 2 when the copy landed empty. Logs exactly one
30
+ # [identity-reconcile] account=<id> agent=<slug> status=<already-current|updated|rewrite-failed|empty-after-copy|no-template>
31
+ ensure_agent_identity() {
32
+ local f="$1" tpl="$2" acct="$3" slug="$4"
33
+
34
+ if [ ! -s "$tpl" ]; then
35
+ echo "[identity-reconcile] account=$acct agent=$slug status=no-template"
36
+ return 1
37
+ fi
38
+
39
+ if cmp -s "$f" "$tpl"; then
40
+ echo "[identity-reconcile] account=$acct agent=$slug status=already-current"
41
+ return 0
42
+ fi
43
+
44
+ if ! cp "$tpl" "$f" 2>/dev/null; then
45
+ echo "[identity-reconcile] account=$acct agent=$slug status=rewrite-failed"
46
+ return 1
47
+ fi
48
+
49
+ # A 0-byte cp traps the brand server in STARTING: the reachability auditor
50
+ # then reports `reachable=false reason=files-missing IDENTITY.md:empty` every
51
+ # minute and the operator sees the splash for ever. Observed on
52
+ # 192.168.88.16 after a re-install, source 1655 bytes and destination 0. This
53
+ # is the one condition the pass refuses to continue past, carried over from
54
+ # the loop it replaces.
55
+ if [ ! -s "$f" ]; then
56
+ echo "[identity-reconcile] account=$acct agent=$slug status=empty-after-copy" >&2
57
+ return 2
58
+ fi
59
+
60
+ # Divergent, like every other non-matching outcome: `divergent=` counts files
61
+ # this pass had to act on, so a run that rewrote one and a run that rewrote
62
+ # none are distinguishable in the summary line.
63
+ echo "[identity-reconcile] account=$acct agent=$slug status=updated"
64
+ return 1
65
+ }
66
+
67
+ # reconcile_all_accounts_identities <accounts_dir>
68
+ # Standing check + durable backfill across every account carrying an
69
+ # account.json. Emits one status line per agent plus one summary line on every
70
+ # run, including a zero count — silence is not evidence of convergence, and
71
+ # `agents=0` on an install that has accounts is a stopped counter rather than a
72
+ # clean pass.
73
+ #
74
+ # Returns 1 only when a copy landed empty, so the caller aborts before the brand
75
+ # server starts. Every other failure is reported and stepped over.
76
+ reconcile_all_accounts_identities() {
77
+ local accts="$1"
78
+ local tpl="${MAXY_PUBLIC_IDENTITY_TEMPLATE:-${TEMPLATES_DIR:-}/agents/public/IDENTITY.md}"
79
+ local n=0 agents=0 divergent=0 fatal=0
80
+ local acct acct_id agent_dir slug rc
81
+
82
+ if [ -d "$accts" ]; then
83
+ for acct in "$accts"/*/; do
84
+ [ -f "$acct/account.json" ] || continue
85
+ n=$((n + 1))
86
+ acct_id="$(basename "$acct")"
87
+ for agent_dir in "$acct/agents"/*/; do
88
+ [ -d "$agent_dir" ] || continue
89
+ slug="$(basename "$agent_dir")"
90
+ # admin has its own Rubytech identity; passive has a third (Task 1483),
91
+ # and re-syncing either from the public visitor template would replace a
92
+ # correct prompt with the wrong one.
93
+ [ "$slug" = "admin" ] && continue
94
+ [ "$slug" = "passive" ] && continue
95
+ # Never create. An agent dir with no IDENTITY.md is a half-made agent,
96
+ # which agent-create's rollback already prevents; seeding one here would
97
+ # make it look whole.
98
+ [ -f "$agent_dir/IDENTITY.md" ] || continue
99
+ agents=$((agents + 1))
100
+ # `|| rc=$?` rather than a bare call followed by `rc=$?`: setup-account.sh
101
+ # runs under `set -e`, and a bare call returning non-zero kills the
102
+ # install the first time any agent diverges — which is every upgrade
103
+ # that has work to do. The `||` puts the call in a tested context, the
104
+ # same shape account-settings-reconcile.sh uses for the same reason.
105
+ rc=0
106
+ ensure_agent_identity "$agent_dir/IDENTITY.md" "$tpl" "$acct_id" "$slug" || rc=$?
107
+ # if-blocks rather than `[ … ] && …` lists: an AND-list whose test fails
108
+ # returns non-zero, and as the last command in this loop body that is
109
+ # another way `set -e` kills the install.
110
+ if [ "$rc" -ne 0 ]; then divergent=$((divergent + 1)); fi
111
+ if [ "$rc" -eq 2 ]; then fatal=1; fi
112
+ done
113
+ done
114
+ fi
115
+
116
+ echo "[identity-reconcile] accounts=$n agents=$agents divergent=$divergent"
117
+ if [ "$fatal" -eq 1 ]; then
118
+ echo " [setup] FATAL a public IDENTITY.md is empty after copy from $tpl ($(wc -c < "$tpl" 2>/dev/null || echo 0) bytes); aborting before the brand server starts" >&2
119
+ return 1
120
+ fi
121
+ return 0
122
+ }