@rubytech/create-realagent-code 0.1.593 → 0.1.595

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 (253) hide show
  1. package/package.json +2 -2
  2. package/payload/platform/lib/telegram-button-labels/dist/index.d.ts +19 -0
  3. package/payload/platform/lib/telegram-button-labels/dist/index.d.ts.map +1 -0
  4. package/payload/platform/lib/telegram-button-labels/dist/index.js +84 -0
  5. package/payload/platform/lib/telegram-button-labels/dist/index.js.map +1 -0
  6. package/payload/platform/lib/telegram-button-labels/src/__tests__/store.test.ts +67 -0
  7. package/payload/platform/lib/telegram-button-labels/src/index.ts +88 -0
  8. package/payload/platform/lib/telegram-button-labels/tsconfig.json +9 -0
  9. package/payload/platform/lib/telegram-button-labels/vitest.config.ts +9 -0
  10. package/payload/platform/package.json +3 -2
  11. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +12 -3
  12. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +10 -0
  13. package/payload/platform/plugins/cloudflare/PLUGIN.md +2 -0
  14. package/payload/platform/plugins/docs/references/internals.md +1 -1
  15. package/payload/platform/plugins/docs/references/telegram-guide.md +10 -1
  16. package/payload/platform/scripts/__tests__/agent-identity-reconcile.test.sh +110 -0
  17. package/payload/platform/scripts/__tests__/check-orphan-dist.test.sh +87 -0
  18. package/payload/platform/scripts/check-orphan-dist.mjs +143 -0
  19. package/payload/platform/scripts/lib/agent-identity-reconcile.sh +122 -0
  20. package/payload/platform/scripts/lib/provision-account-dir.sh +8 -26
  21. package/payload/platform/scripts/setup-account.sh +15 -0
  22. package/payload/platform/services/claude-session-manager/dist/index.js +5 -0
  23. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  24. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +5 -0
  25. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  26. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +42 -1
  27. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  28. package/payload/platform/templates/agents/public/IDENTITY.md +3 -3
  29. package/payload/server/server.js +543 -409
  30. package/payload/platform/lib/anthropic-key/dist/index.d.ts +0 -22
  31. package/payload/platform/lib/anthropic-key/dist/index.d.ts.map +0 -1
  32. package/payload/platform/lib/anthropic-key/dist/index.js +0 -232
  33. package/payload/platform/lib/anthropic-key/dist/index.js.map +0 -1
  34. package/payload/platform/lib/graph-search/dist/query-expansion.d.ts +0 -37
  35. package/payload/platform/lib/graph-search/dist/query-expansion.d.ts.map +0 -1
  36. package/payload/platform/lib/graph-search/dist/query-expansion.js +0 -101
  37. package/payload/platform/lib/graph-search/dist/query-expansion.js.map +0 -1
  38. package/payload/platform/lib/oauth-llm/dist/index.d.ts +0 -116
  39. package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +0 -1
  40. package/payload/platform/lib/oauth-llm/dist/index.js +0 -386
  41. package/payload/platform/lib/oauth-llm/dist/index.js.map +0 -1
  42. package/payload/platform/lib/screening-patterns/dist/index.d.ts +0 -29
  43. package/payload/platform/lib/screening-patterns/dist/index.d.ts.map +0 -1
  44. package/payload/platform/lib/screening-patterns/dist/index.js +0 -48
  45. package/payload/platform/lib/screening-patterns/dist/index.js.map +0 -1
  46. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts +0 -2
  47. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts.map +0 -1
  48. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js +0 -34
  49. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js.map +0 -1
  50. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.d.ts +0 -39
  51. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.d.ts.map +0 -1
  52. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.js +0 -249
  53. package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.js.map +0 -1
  54. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts +0 -6
  55. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts.map +0 -1
  56. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js +0 -32
  57. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js.map +0 -1
  58. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +0 -283
  59. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +0 -1
  60. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +0 -1155
  61. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +0 -1
  62. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.d.ts +0 -90
  63. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.d.ts.map +0 -1
  64. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.js +0 -551
  65. package/payload/platform/plugins/cloudflare/mcp/dist/lib/setup-orchestrator.js.map +0 -1
  66. package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.d.ts +0 -17
  67. package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.d.ts.map +0 -1
  68. package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.js +0 -186
  69. package/payload/platform/plugins/email/mcp/dist/lib/claude-bridge.js.map +0 -1
  70. package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts +0 -29
  71. package/payload/platform/plugins/email/mcp/dist/lib/screening.d.ts.map +0 -1
  72. package/payload/platform/plugins/email/mcp/dist/lib/screening.js +0 -105
  73. package/payload/platform/plugins/email/mcp/dist/lib/screening.js.map +0 -1
  74. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts +0 -38
  75. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts.map +0 -1
  76. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js +0 -817
  77. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js.map +0 -1
  78. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.d.ts +0 -30
  79. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.d.ts.map +0 -1
  80. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js +0 -215
  81. package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js.map +0 -1
  82. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.d.ts +0 -19
  83. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.d.ts.map +0 -1
  84. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.js +0 -151
  85. package/payload/platform/plugins/email/mcp/dist/tools/email-auto-respond-config.js.map +0 -1
  86. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts +0 -6
  87. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts.map +0 -1
  88. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js +0 -89
  89. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js.map +0 -1
  90. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.d.ts +0 -2
  91. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.d.ts.map +0 -1
  92. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.js +0 -90
  93. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.js.map +0 -1
  94. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.d.ts +0 -2
  95. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.d.ts.map +0 -1
  96. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +0 -226
  97. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +0 -1
  98. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts +0 -60
  99. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts.map +0 -1
  100. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js +0 -169
  101. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js.map +0 -1
  102. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +0 -248
  103. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +0 -1
  104. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +0 -824
  105. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +0 -1
  106. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +0 -63
  107. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +0 -1
  108. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +0 -210
  109. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +0 -1
  110. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts +0 -35
  111. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts.map +0 -1
  112. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js +0 -28
  113. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js.map +0 -1
  114. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.d.ts +0 -2
  115. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.d.ts.map +0 -1
  116. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.js +0 -68
  117. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights-emit.test.js.map +0 -1
  118. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.d.ts +0 -2
  119. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.d.ts.map +0 -1
  120. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.js +0 -88
  121. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write-emit.test.js.map +0 -1
  122. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.d.ts +0 -2
  123. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.d.ts.map +0 -1
  124. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.js +0 -58
  125. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-classify-emit.test.js.map +0 -1
  126. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.d.ts +0 -2
  127. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.d.ts.map +0 -1
  128. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js +0 -64
  129. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js.map +0 -1
  130. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.d.ts +0 -2
  131. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.d.ts.map +0 -1
  132. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.js +0 -70
  133. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-edit-attachment-emit.test.js.map +0 -1
  134. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.d.ts +0 -2
  135. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.d.ts.map +0 -1
  136. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.js +0 -48
  137. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-emit.test.js.map +0 -1
  138. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.d.ts +0 -2
  139. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.d.ts.map +0 -1
  140. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.js +0 -74
  141. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-emit.test.js.map +0 -1
  142. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.d.ts +0 -2
  143. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.d.ts.map +0 -1
  144. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.js +0 -47
  145. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-extract-emit.test.js.map +0 -1
  146. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.d.ts +0 -2
  147. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.d.ts.map +0 -1
  148. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.js +0 -65
  149. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-web-emit.test.js.map +0 -1
  150. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.d.ts +0 -2
  151. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.d.ts.map +0 -1
  152. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.js +0 -43
  153. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex-emit.test.js.map +0 -1
  154. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.d.ts +0 -2
  155. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.d.ts.map +0 -1
  156. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.js +0 -65
  157. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-rename-attachment-emit.test.js.map +0 -1
  158. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.d.ts +0 -2
  159. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.d.ts.map +0 -1
  160. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.js +0 -49
  161. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-restore-emit.test.js.map +0 -1
  162. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.d.ts +0 -2
  163. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.d.ts.map +0 -1
  164. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js +0 -79
  165. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js.map +0 -1
  166. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.d.ts +0 -2
  167. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.d.ts.map +0 -1
  168. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.js +0 -329
  169. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.js.map +0 -1
  170. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.d.ts +0 -2
  171. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.d.ts.map +0 -1
  172. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js +0 -81
  173. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js.map +0 -1
  174. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts +0 -34
  175. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts.map +0 -1
  176. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js +0 -62
  177. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js.map +0 -1
  178. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.d.ts +0 -61
  179. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.d.ts.map +0 -1
  180. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js +0 -102
  181. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js.map +0 -1
  182. package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.d.ts +0 -35
  183. package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.d.ts.map +0 -1
  184. package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.js +0 -73
  185. package/payload/platform/plugins/memory/mcp/dist/tools/memory-signals-recent.js.map +0 -1
  186. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +0 -29
  187. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts.map +0 -1
  188. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +0 -22
  189. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js.map +0 -1
  190. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +0 -30
  191. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +0 -1
  192. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +0 -130
  193. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +0 -1
  194. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts +0 -65
  195. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts.map +0 -1
  196. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js +0 -262
  197. package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js.map +0 -1
  198. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/normalise-start-date.test.d.ts +0 -2
  199. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/normalise-start-date.test.d.ts.map +0 -1
  200. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/normalise-start-date.test.js +0 -100
  201. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/normalise-start-date.test.js.map +0 -1
  202. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.d.ts +0 -2
  203. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.d.ts.map +0 -1
  204. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.js +0 -22
  205. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/scheduled-seat-id.test.js.map +0 -1
  206. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.d.ts +0 -11
  207. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.d.ts.map +0 -1
  208. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.js +0 -19
  209. package/payload/platform/plugins/scheduling/mcp/dist/lib/scheduled-seat-id.js.map +0 -1
  210. package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.d.ts +0 -8
  211. package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.d.ts.map +0 -1
  212. package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.js +0 -83
  213. package/payload/platform/plugins/url-get/mcp/dist/lib/summarise.js.map +0 -1
  214. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.d.ts +0 -151
  215. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.d.ts.map +0 -1
  216. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.js +0 -299
  217. package/payload/platform/plugins/workflows/mcp/dist/lib/llm-call.js.map +0 -1
  218. package/payload/platform/services/claude-session-manager/dist/account-admin-census.d.ts +0 -39
  219. package/payload/platform/services/claude-session-manager/dist/account-admin-census.d.ts.map +0 -1
  220. package/payload/platform/services/claude-session-manager/dist/account-admin-census.js +0 -122
  221. package/payload/platform/services/claude-session-manager/dist/account-admin-census.js.map +0 -1
  222. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts +0 -39
  223. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts.map +0 -1
  224. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js +0 -133
  225. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js.map +0 -1
  226. package/payload/platform/services/claude-session-manager/dist/input-postcondition.d.ts +0 -22
  227. package/payload/platform/services/claude-session-manager/dist/input-postcondition.d.ts.map +0 -1
  228. package/payload/platform/services/claude-session-manager/dist/input-postcondition.js +0 -50
  229. package/payload/platform/services/claude-session-manager/dist/input-postcondition.js.map +0 -1
  230. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts +0 -30
  231. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts.map +0 -1
  232. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js +0 -204
  233. package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js.map +0 -1
  234. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts +0 -80
  235. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts.map +0 -1
  236. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js +0 -306
  237. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js.map +0 -1
  238. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +0 -49
  239. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +0 -1
  240. package/payload/platform/services/claude-session-manager/dist/session-store.js +0 -52
  241. package/payload/platform/services/claude-session-manager/dist/session-store.js.map +0 -1
  242. package/payload/platform/services/claude-session-manager/dist/systemd-spawn.d.ts +0 -83
  243. package/payload/platform/services/claude-session-manager/dist/systemd-spawn.d.ts.map +0 -1
  244. package/payload/platform/services/claude-session-manager/dist/systemd-spawn.js +0 -345
  245. package/payload/platform/services/claude-session-manager/dist/systemd-spawn.js.map +0 -1
  246. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +0 -104
  247. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +0 -1
  248. package/payload/platform/services/telegram-channel/dist/turn-follow.js +0 -408
  249. package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +0 -1
  250. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +0 -104
  251. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +0 -1
  252. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +0 -393
  253. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +0 -1
@@ -1,817 +0,0 @@
1
- /**
2
- * Cron script: email-auto-respond
3
- *
4
- * Polls IMAP for new messages on EmailAccounts with autoRespond enabled.
5
- * For each new message, invokes the assigned public agent via the Anthropic
6
- * API and sends the reply via SMTP with threading headers.
7
- *
8
- * PID lock at $HOME/{configDir}/email-auto-respond.lock prevents overlapping runs.
9
- *
10
- * PLATFORM_ROOT=... node dist/scripts/email-auto-respond.js
11
- *
12
- * ┌─────────────────────────────────────────────────────────────────┐
13
- * │ CRON (every 1 min) │
14
- * │ \-> email-auto-respond.ts │
15
- * │ ├── PID lock │
16
- * │ ├── Neo4j: EmailAccounts with autoRespond=true │
17
- * │ │ └── Skip if pollInterval hasn't elapsed │
18
- * │ └── For each account: │
19
- * │ ├── Read rate limit counters from Neo4j │
20
- * │ │ └── Reset if clock hour/day boundary passed │
21
- * │ ├── IMAP: fetch UID > lastRespondedUid │
22
- * │ ├── For each new message: │
23
- * │ │ ├── Skip auto-replies (RFC 3834) │
24
- * │ │ ├── Skip own address │
25
- * │ │ ├── Skip duplicate sender (1 reply/sender/poll) │
26
- * │ │ ├── Check hourly/daily cap → break if hit │
27
- * │ │ │ └── Log + Task node (does NOT disable) │
28
- * │ │ ├── Assemble system prompt (IDENTITY+SOUL+KNOW) │
29
- * │ │ ├── Anthropic API → generate reply │
30
- * │ │ ├── SMTP: send with In-Reply-To/References │
31
- * │ │ ├── Increment send counters │
32
- * │ │ └── Update lastRespondedUid (per-email) │
33
- * │ ├── Update lastPollAt │
34
- * │ └── Circuit breaker (3 fails → disable + Task node) │
35
- * └─────────────────────────────────────────────────────────────────┘
36
- */
37
- import { dispatchEmailToClaude, emailTurnTimeoutMs } from "../lib/claude-bridge.js";
38
- import neo4j from "neo4j-driver";
39
- import { randomUUID } from "node:crypto";
40
- import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync, appendFileSync } from "node:fs";
41
- import { resolve } from "node:path";
42
- import { homedir } from "node:os";
43
- import { fetchForAutoRespond } from "../lib/imap.js";
44
- import { sendEmail } from "../lib/smtp.js";
45
- import { readPassword } from "../lib/credentials.js";
46
- import { storeOutboundEmail, stripAngleBrackets } from "../lib/graph.js";
47
- import { writeNodeWithEdges } from "../../../../../lib/graph-write/dist/index.js";
48
- /**
49
- * Look up the screening verdict for an email by its Message-ID.
50
- * Returns { found: false } if the Email node doesn't exist yet
51
- * (email-fetch hasn't processed it) or if messageId is null.
52
- */
53
- async function lookupScreening(driver, messageId, accountId) {
54
- if (!messageId)
55
- return { found: false, screening: null, promptInjectionFlag: false };
56
- const normalised = stripAngleBrackets(messageId);
57
- if (!normalised)
58
- return { found: false, screening: null, promptInjectionFlag: false };
59
- const session = driver.session();
60
- try {
61
- const result = await session.run(`MATCH (e:Email {messageId: $messageId, accountId: $accountId})
62
- RETURN e.screening AS screening, e.promptInjectionFlag AS promptInjectionFlag
63
- LIMIT 1`, { messageId: normalised, accountId });
64
- if (result.records.length === 0) {
65
- return { found: false, screening: null, promptInjectionFlag: false };
66
- }
67
- const rec = result.records[0];
68
- return {
69
- found: true,
70
- screening: rec.get("screening") ?? null,
71
- promptInjectionFlag: rec.get("promptInjectionFlag") === true,
72
- };
73
- }
74
- finally {
75
- await session.close();
76
- }
77
- }
78
- // ---------------------------------------------------------------------------
79
- // Constants
80
- // ---------------------------------------------------------------------------
81
- const CIRCUIT_BREAKER_THRESHOLD = 3;
82
- const MAX_EMAILS_PER_POLL = 10;
83
- const DEFAULT_HOURLY_LIMIT = 20;
84
- const DEFAULT_DAILY_LIMIT = 100;
85
- // Brand-aware config directory — reads configDir from brand.json stamped at install time.
86
- // No fallback: if brand.json is missing or incomplete, the platform wasn't properly installed.
87
- function resolveConfigDir() {
88
- const platformRoot = process.env.PLATFORM_ROOT;
89
- if (!platformRoot) {
90
- throw new Error("PLATFORM_ROOT environment variable is required");
91
- }
92
- const brandPath = resolve(platformRoot, "config", "brand.json");
93
- if (!existsSync(brandPath)) {
94
- throw new Error(`brand.json not found at ${brandPath} — platform not properly installed`);
95
- }
96
- try {
97
- const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
98
- if (!brand.configDir) {
99
- throw new Error(`brand.json at ${brandPath} is missing the configDir field`);
100
- }
101
- return brand.configDir;
102
- }
103
- catch (err) {
104
- if (err instanceof SyntaxError) {
105
- throw new Error(`brand.json at ${brandPath} is not valid JSON: ${err.message}`);
106
- }
107
- throw err;
108
- }
109
- }
110
- const CONFIG_DIR = resolve(homedir(), resolveConfigDir());
111
- const LOCK_DIR = CONFIG_DIR;
112
- const LOCK_FILE = resolve(LOCK_DIR, "email-auto-respond.lock");
113
- const LOG_DIR = resolve(CONFIG_DIR, "logs");
114
- const LOG_FILE = resolve(LOG_DIR, "email-auto-respond.log");
115
- // ---------------------------------------------------------------------------
116
- // Logging
117
- // ---------------------------------------------------------------------------
118
- function log(level, message) {
119
- const ts = new Date().toISOString();
120
- const line = `[${ts}] [${level}] ${message}\n`;
121
- mkdirSync(LOG_DIR, { recursive: true });
122
- appendFileSync(LOG_FILE, line);
123
- if (level === "error") {
124
- console.error(line.trim());
125
- }
126
- }
127
- // ---------------------------------------------------------------------------
128
- // Lock file
129
- // ---------------------------------------------------------------------------
130
- function acquireLock() {
131
- mkdirSync(LOCK_DIR, { recursive: true });
132
- if (existsSync(LOCK_FILE)) {
133
- const storedPid = readFileSync(LOCK_FILE, "utf-8").trim();
134
- try {
135
- process.kill(Number(storedPid), 0);
136
- return false; // Process still running
137
- }
138
- catch {
139
- unlinkSync(LOCK_FILE); // Stale lock
140
- }
141
- }
142
- writeFileSync(LOCK_FILE, String(process.pid));
143
- return true;
144
- }
145
- function releaseLock() {
146
- try {
147
- if (existsSync(LOCK_FILE)) {
148
- const storedPid = readFileSync(LOCK_FILE, "utf-8").trim();
149
- if (storedPid === String(process.pid)) {
150
- unlinkSync(LOCK_FILE);
151
- }
152
- }
153
- }
154
- catch {
155
- // Best-effort cleanup
156
- }
157
- }
158
- // ---------------------------------------------------------------------------
159
- // Neo4j connection (standalone)
160
- // ---------------------------------------------------------------------------
161
- function connectNeo4j() {
162
- const platformRoot = process.env.PLATFORM_ROOT;
163
- if (!platformRoot) {
164
- throw new Error("PLATFORM_ROOT environment variable is required");
165
- }
166
- let password;
167
- if (process.env.NEO4J_PASSWORD) {
168
- password = process.env.NEO4J_PASSWORD;
169
- }
170
- else {
171
- const passwordFile = resolve(platformRoot, "config/.neo4j-password");
172
- try {
173
- password = readFileSync(passwordFile, "utf-8").trim();
174
- }
175
- catch {
176
- throw new Error(`Neo4j password not found at ${passwordFile}`);
177
- }
178
- }
179
- // NEO4J_URI must be explicit. Crontab installer prefixes
180
- // NEO4J_URI=bolt://localhost:${NEO4J_PORT} so the cron path is safe, but the
181
- // throw-on-unset catches manual runs that forget to source .env.
182
- const uri = process.env.NEO4J_URI;
183
- if (!uri) {
184
- throw new Error("[email/auto-respond] NEO4J_URI unset — refusing to default to bolt://localhost:7687");
185
- }
186
- const user = process.env.NEO4J_USER ?? "neo4j";
187
- console.error(`[email/auto-respond] resolved neo4j_uri=${uri}`);
188
- return neo4j.driver(uri, neo4j.auth.basic(user, password));
189
- }
190
- async function getAutoRespondAccounts(driver) {
191
- const session = driver.session();
192
- try {
193
- const result = await session.run(`MATCH (e:EmailAccount {autoRespond: true}) RETURN e`);
194
- return result.records.map((record) => {
195
- const props = record.get("e").properties;
196
- return {
197
- accountId: props.accountId,
198
- config: {
199
- email: props.email,
200
- agentAddress: props.agentAddress ?? props.email,
201
- imapHost: props.imapHost,
202
- imapPort: neo4j.integer.toNumber(props.imapPort),
203
- imapSecurity: props.imapSecurity,
204
- smtpHost: props.smtpHost,
205
- smtpPort: neo4j.integer.toNumber(props.smtpPort),
206
- smtpSecurity: props.smtpSecurity,
207
- },
208
- autoRespond: {
209
- autoRespond: true,
210
- respondingAgentSlug: props.respondingAgentSlug ?? null,
211
- pollIntervalMinutes: props.pollIntervalMinutes
212
- ? neo4j.integer.toNumber(props.pollIntervalMinutes)
213
- : 5,
214
- lastRespondedUid: props.lastRespondedUid
215
- ? neo4j.integer.toNumber(props.lastRespondedUid)
216
- : 0,
217
- lastPollAt: props.lastPollAt ?? null,
218
- consecutiveFailures: props.consecutiveFailures
219
- ? neo4j.integer.toNumber(props.consecutiveFailures)
220
- : 0,
221
- autoRespondHourlyLimit: props.autoRespondHourlyLimit
222
- ? neo4j.integer.toNumber(props.autoRespondHourlyLimit)
223
- : DEFAULT_HOURLY_LIMIT,
224
- autoRespondDailyLimit: props.autoRespondDailyLimit
225
- ? neo4j.integer.toNumber(props.autoRespondDailyLimit)
226
- : DEFAULT_DAILY_LIMIT,
227
- },
228
- };
229
- });
230
- }
231
- finally {
232
- await session.close();
233
- }
234
- }
235
- /**
236
- * Extract the bare email address from a From header. RFC 5322 allows
237
- * `"Display Name" <addr@example.com>` and `addr@example.com` forms; the
238
- * bridge keys sessions by the bare address only.
239
- */
240
- function stripSenderAddress(from) {
241
- const m = from.match(/<([^>]+)>/);
242
- if (m)
243
- return m[1].trim().toLowerCase();
244
- return from.trim().toLowerCase();
245
- }
246
- /**
247
- * Build a threaded Re: subject line.
248
- * Adds "Re: " prefix only if not already present.
249
- */
250
- function replySubject(originalSubject) {
251
- const trimmed = originalSubject.trim();
252
- if (/^re:\s/i.test(trimmed))
253
- return trimmed;
254
- return `Re: ${trimmed}`;
255
- }
256
- // ---------------------------------------------------------------------------
257
- // Neo4j update helpers
258
- // ---------------------------------------------------------------------------
259
- async function updateLastRespondedUid(driver, accountId, uid) {
260
- const session = driver.session();
261
- try {
262
- await session.run(`MATCH (e:EmailAccount {accountId: $accountId})
263
- SET e.lastRespondedUid = $uid`, { accountId, uid: neo4j.int(uid) });
264
- }
265
- finally {
266
- await session.close();
267
- }
268
- }
269
- async function updateLastPollAt(driver, accountId) {
270
- const session = driver.session();
271
- try {
272
- await session.run(`MATCH (e:EmailAccount {accountId: $accountId})
273
- SET e.lastPollAt = $now, e.consecutiveFailures = 0`, { accountId, now: new Date().toISOString() });
274
- }
275
- finally {
276
- await session.close();
277
- }
278
- }
279
- async function incrementFailures(driver, accountId) {
280
- const session = driver.session();
281
- try {
282
- const result = await session.run(`MATCH (e:EmailAccount {accountId: $accountId})
283
- SET e.consecutiveFailures = COALESCE(e.consecutiveFailures, 0) + 1
284
- RETURN e.consecutiveFailures AS failures`, { accountId });
285
- return neo4j.integer.toNumber(result.records[0]?.get("failures") ?? 0);
286
- }
287
- finally {
288
- await session.close();
289
- }
290
- }
291
- async function disableAutoRespond(driver, accountId, reason) {
292
- const session = driver.session();
293
- try {
294
- await session.run(`MATCH (e:EmailAccount {accountId: $accountId})
295
- SET e.autoRespond = false`, { accountId });
296
- }
297
- finally {
298
- await session.close();
299
- }
300
- // Write doctrine: admin notification Task must
301
- // be created with at least one adjacency. The Task is semantically
302
- // about the EmailAccount it disabled — link AFFECTS → EmailAccount.
303
- // If the EmailAccount was deleted between circuit-breaker-trip and
304
- // this write, writeNodeWithEdges throws 'unresolved-target'; log and
305
- // continue — the cron loop must survive a missing target node.
306
- const taskSession = driver.session();
307
- try {
308
- const eaResult = await taskSession.run(`MATCH (ea:EmailAccount {accountId: $accountId}) RETURN elementId(ea) AS id LIMIT 1`, { accountId });
309
- if (eaResult.records.length === 0) {
310
- log("warn", `Account ${accountId}: EmailAccount not found — skipping admin Task creation for disabled auto-respond`);
311
- return;
312
- }
313
- await writeNodeWithEdges({
314
- session: taskSession,
315
- labels: ["Task"],
316
- props: {
317
- taskId: randomUUID(),
318
- accountId,
319
- title: "Email auto-respond disabled",
320
- description: `Auto-respond was automatically disabled after ${CIRCUIT_BREAKER_THRESHOLD} consecutive failures. Reason: ${reason}. Re-enable via "enable auto-respond" after resolving the issue.`,
321
- status: "open",
322
- priority: "high",
323
- createdAt: new Date().toISOString(),
324
- scope: "admin",
325
- },
326
- relationships: [
327
- {
328
- type: "AFFECTS",
329
- direction: "outgoing",
330
- targetNodeId: eaResult.records[0].get("id"),
331
- },
332
- ],
333
- createdBy: { source: "email-auto-respond" },
334
- });
335
- }
336
- catch (err) {
337
- log("error", `Account ${accountId}: failed to create admin Task for disabled auto-respond — ${err instanceof Error ? err.message : String(err)}`);
338
- }
339
- finally {
340
- await taskSession.close();
341
- }
342
- }
343
- /**
344
- * Compute the next UTC clock-hour boundary from a given time.
345
- * E.g., if now is 14:37 UTC, returns the ISO string for 15:00 UTC.
346
- */
347
- function nextHourBoundary(now) {
348
- const next = new Date(now);
349
- next.setUTCMinutes(0, 0, 0);
350
- next.setUTCHours(next.getUTCHours() + 1);
351
- return next.toISOString();
352
- }
353
- /**
354
- * Compute the next UTC midnight boundary from a given time.
355
- */
356
- function nextDayBoundary(now) {
357
- const next = new Date(now);
358
- next.setUTCHours(0, 0, 0, 0);
359
- next.setUTCDate(next.getUTCDate() + 1);
360
- return next.toISOString();
361
- }
362
- /**
363
- * Read rate limit counters from the EmailAccount node.
364
- * Returns defaults (0 counts, null boundaries) if not yet set.
365
- */
366
- async function readRateLimitState(driver, accountId) {
367
- const session = driver.session();
368
- try {
369
- const result = await session.run(`MATCH (e:EmailAccount {accountId: $accountId})
370
- RETURN e.autoRespondSentThisHour AS sentThisHour,
371
- e.autoRespondSentToday AS sentToday,
372
- e.autoRespondHourResetAt AS hourResetAt,
373
- e.autoRespondDayResetAt AS dayResetAt`, { accountId });
374
- if (result.records.length === 0) {
375
- return { sentThisHour: 0, sentToday: 0, hourResetAt: null, dayResetAt: null };
376
- }
377
- const rec = result.records[0];
378
- return {
379
- sentThisHour: rec.get("sentThisHour") ? neo4j.integer.toNumber(rec.get("sentThisHour")) : 0,
380
- sentToday: rec.get("sentToday") ? neo4j.integer.toNumber(rec.get("sentToday")) : 0,
381
- hourResetAt: rec.get("hourResetAt") ?? null,
382
- dayResetAt: rec.get("dayResetAt") ?? null,
383
- };
384
- }
385
- finally {
386
- await session.close();
387
- }
388
- }
389
- /**
390
- * Reset counters if the clock hour or day boundary has passed.
391
- * Initialises boundaries on first run.
392
- * Returns the updated in-memory state (does NOT re-read from Neo4j).
393
- */
394
- async function resetCountersIfNeeded(driver, accountId, state, now) {
395
- let hourReset = false;
396
- let dayReset = false;
397
- // First run — no boundaries set yet
398
- if (!state.hourResetAt) {
399
- state.hourResetAt = nextHourBoundary(now);
400
- state.sentThisHour = 0;
401
- hourReset = true;
402
- }
403
- else if (now >= new Date(state.hourResetAt)) {
404
- state.sentThisHour = 0;
405
- state.hourResetAt = nextHourBoundary(now);
406
- hourReset = true;
407
- }
408
- if (!state.dayResetAt) {
409
- state.dayResetAt = nextDayBoundary(now);
410
- state.sentToday = 0;
411
- dayReset = true;
412
- }
413
- else if (now >= new Date(state.dayResetAt)) {
414
- state.sentToday = 0;
415
- state.dayResetAt = nextDayBoundary(now);
416
- dayReset = true;
417
- }
418
- if (hourReset || dayReset) {
419
- const session = driver.session();
420
- try {
421
- await session.run(`MATCH (e:EmailAccount {accountId: $accountId})
422
- SET e.autoRespondSentThisHour = $sentThisHour,
423
- e.autoRespondSentToday = $sentToday,
424
- e.autoRespondHourResetAt = $hourResetAt,
425
- e.autoRespondDayResetAt = $dayResetAt`, {
426
- accountId,
427
- sentThisHour: neo4j.int(state.sentThisHour),
428
- sentToday: neo4j.int(state.sentToday),
429
- hourResetAt: state.hourResetAt,
430
- dayResetAt: state.dayResetAt,
431
- });
432
- }
433
- catch (err) {
434
- log("error", `Account ${accountId}: failed to reset rate limit counters — ${err instanceof Error ? err.message : String(err)}`);
435
- }
436
- finally {
437
- await session.close();
438
- }
439
- }
440
- return state;
441
- }
442
- /**
443
- * Increment both hourly and daily send counters atomically.
444
- */
445
- async function incrementSendCounters(driver, accountId) {
446
- const session = driver.session();
447
- try {
448
- await session.run(`MATCH (e:EmailAccount {accountId: $accountId})
449
- SET e.autoRespondSentThisHour = COALESCE(e.autoRespondSentThisHour, 0) + 1,
450
- e.autoRespondSentToday = COALESCE(e.autoRespondSentToday, 0) + 1`, { accountId });
451
- }
452
- catch (err) {
453
- log("error", `Account ${accountId}: failed to increment send counters — ${err instanceof Error ? err.message : String(err)}`);
454
- }
455
- finally {
456
- await session.close();
457
- }
458
- }
459
- /**
460
- * Create an admin Task node to notify the admin that a rate limit was hit.
461
- * Dedup: skips creation if an open rate-limit Task already exists for this account.
462
- */
463
- async function createRateLimitTask(driver, accountId, limitType, count, limit) {
464
- const session = driver.session();
465
- try {
466
- // Check for an existing open rate-limit Task to avoid duplicates across poll cycles
467
- const existing = await session.run(`MATCH (t:Task {accountId: $accountId, status: "open"})
468
- WHERE t.title STARTS WITH "Email auto-respond"
469
- AND t.title ENDS WITH "rate limit reached"
470
- RETURN t LIMIT 1`, { accountId });
471
- if (existing.records.length > 0) {
472
- log("info", `Account ${accountId}: rate limit Task already exists — skipping duplicate creation`);
473
- return;
474
- }
475
- // Write doctrine: AFFECTS → EmailAccount. The
476
- // dedup check above already ran; if EmailAccount is missing, log
477
- // and bail — cron must survive.
478
- const eaResult = await session.run(`MATCH (ea:EmailAccount {accountId: $accountId}) RETURN elementId(ea) AS id LIMIT 1`, { accountId });
479
- if (eaResult.records.length === 0) {
480
- log("warn", `Account ${accountId}: EmailAccount not found — skipping rate-limit Task creation`);
481
- return;
482
- }
483
- await writeNodeWithEdges({
484
- session,
485
- labels: ["Task"],
486
- props: {
487
- taskId: randomUUID(),
488
- accountId,
489
- title: `Email auto-respond ${limitType} rate limit reached`,
490
- description: `Auto-respond sent ${count} replies this ${limitType === "hourly" ? "hour" : "day"}, reaching the ${limitType} limit of ${limit}. Remaining emails in this poll cycle were skipped. The limit resets automatically at the next ${limitType === "hourly" ? "clock hour" : "midnight UTC"}. Adjust limits via "configure auto-respond" if needed.`,
491
- status: "open",
492
- priority: "medium",
493
- createdAt: new Date().toISOString(),
494
- scope: "admin",
495
- },
496
- relationships: [
497
- {
498
- type: "AFFECTS",
499
- direction: "outgoing",
500
- targetNodeId: eaResult.records[0].get("id"),
501
- },
502
- ],
503
- createdBy: { source: "email-auto-respond" },
504
- });
505
- }
506
- catch (err) {
507
- log("error", `Account ${accountId}: failed to create rate limit Task node — ${err instanceof Error ? err.message : String(err)}`);
508
- }
509
- finally {
510
- await session.close();
511
- }
512
- }
513
- // ---------------------------------------------------------------------------
514
- // Process a single email
515
- // ---------------------------------------------------------------------------
516
- async function processEmail(msg, emailConfig, emailPassword, driver, accountId) {
517
- // Build the dispatch payload from the email. Subject/From/Date are
518
- // prefixed so claude has the threading context inside the PTY turn.
519
- const userMessage = [
520
- `From: ${msg.from}`,
521
- `Subject: ${msg.subject}`,
522
- `Date: ${msg.date}`,
523
- "",
524
- msg.body,
525
- ].join("\n");
526
- // Email dispatches through the shared PTY-bridge pattern
527
- // (out-of-process variant that queries the manager's senderId index
528
- // because cron runs as a separate Node process). The per-sender PTY
529
- // persists across cron invocations, so a multi-email thread continues
530
- // a single claude session keyed by the sender's email address.
531
- const senderEmail = stripSenderAddress(msg.from);
532
- const dispatchResult = await dispatchEmailToClaude({
533
- accountId,
534
- senderEmail,
535
- text: `<incoming-email>\n${userMessage}\n</incoming-email>`,
536
- timeoutMs: emailTurnTimeoutMs(),
537
- });
538
- if ("error" in dispatchResult) {
539
- log("warn", `Auto-respond unavailable for UID ${msg.uid} from ${msg.from}: dispatch ${dispatchResult.error}`);
540
- return;
541
- }
542
- const replyText = dispatchResult.turnText;
543
- if (!replyText.trim()) {
544
- log("warn", `Empty response from agent for email UID ${msg.uid} from ${msg.from}`);
545
- return;
546
- }
547
- // Build threading headers
548
- const inReplyTo = msg.messageId || undefined;
549
- const references = [msg.references, msg.messageId].filter(Boolean).join(" ") || undefined;
550
- // Append AI disclosure footer (EU AI Act Article 50 — proactive identification)
551
- const replyBody = replyText + "\n\n---\nThis reply was generated by an AI assistant.";
552
- // Send reply
553
- const subject = replySubject(msg.subject);
554
- const { messageId: outboundMessageId } = await sendEmail(emailConfig, emailPassword, {
555
- to: msg.from,
556
- subject,
557
- body: replyBody,
558
- inReplyTo,
559
- references,
560
- headers: {
561
- "Auto-Submitted": "auto-replied",
562
- "X-AI-Generated": "true",
563
- },
564
- });
565
- log("info", `Replied to UID ${msg.uid} from ${msg.from} — subject: ${msg.subject}`);
566
- // Store outbound reply as an Email node for audit trail
567
- if (msg.messageId) {
568
- try {
569
- // Look up the original email's emailId by messageId
570
- const normalisedOrigMsgId = stripAngleBrackets(msg.messageId);
571
- const lookupSession = driver.session();
572
- let originalEmailId = null;
573
- try {
574
- const lookupResult = await lookupSession.run(`MATCH (e:Email {messageId: $messageId, accountId: $accountId})
575
- RETURN e.emailId AS emailId LIMIT 1`, { messageId: normalisedOrigMsgId, accountId });
576
- originalEmailId = lookupResult.records[0]?.get("emailId");
577
- }
578
- finally {
579
- await lookupSession.close();
580
- }
581
- if (originalEmailId) {
582
- const result = await storeOutboundEmail({
583
- messageId: outboundMessageId,
584
- inReplyTo: msg.messageId,
585
- toAddresses: [msg.from],
586
- fromAddress: emailConfig.agentAddress,
587
- subject,
588
- bodyPreview: replyBody,
589
- accountId,
590
- originalEmailId,
591
- }, driver);
592
- log("info", `Stored outbound Email node ${result.emailId} with REPLY_TO edge`);
593
- }
594
- else {
595
- log("warn", `Original email not found in graph for messageId=${msg.messageId} — outbound reply not stored`);
596
- }
597
- }
598
- catch (err) {
599
- log("error", `Failed to store outbound Email node: ${err instanceof Error ? err.message : String(err)}`);
600
- }
601
- }
602
- }
603
- // ---------------------------------------------------------------------------
604
- // Process a single account
605
- // ---------------------------------------------------------------------------
606
- async function processAccount(account, driver) {
607
- const { accountId, config: emailConfig, autoRespond: arConfig } = account;
608
- // Check poll interval
609
- if (arConfig.lastPollAt) {
610
- const elapsed = Date.now() - new Date(arConfig.lastPollAt).getTime();
611
- const intervalMs = arConfig.pollIntervalMinutes * 60_000;
612
- if (elapsed < intervalMs) {
613
- return; // Not time to poll yet
614
- }
615
- }
616
- // Replies are produced by a per-sender claude PTY session
617
- // spawned via the loopback claude-session-manager. The PTY-bridge
618
- // (../lib/claude-bridge.ts) handles role=public + tool-surface gating
619
- //, so the legacy per-account agentSlug / agentModel /
620
- // assembleSystemPrompt path is obsolete: claude's binary loads its own
621
- // identity. The autoRespond flag on the EmailAccount node is the sole
622
- // on/off knob.
623
- // Read email password
624
- let emailPassword;
625
- try {
626
- emailPassword = readPassword();
627
- }
628
- catch (err) {
629
- log("error", `Account ${accountId}: email password not available — ${err instanceof Error ? err.message : String(err)}`);
630
- const failures = await incrementFailures(driver, accountId);
631
- if (failures >= CIRCUIT_BREAKER_THRESHOLD) {
632
- await disableAutoRespond(driver, accountId, "Email password file missing");
633
- }
634
- return;
635
- }
636
- // Fetch new messages via IMAP
637
- let messages;
638
- try {
639
- messages = await fetchForAutoRespond(emailConfig, emailPassword, {
640
- sinceUid: arConfig.lastRespondedUid,
641
- agentAddress: emailConfig.agentAddress,
642
- limit: MAX_EMAILS_PER_POLL,
643
- });
644
- }
645
- catch (err) {
646
- log("error", `Account ${accountId}: IMAP fetch failed — ${err instanceof Error ? err.message : String(err)}`);
647
- const failures = await incrementFailures(driver, accountId);
648
- if (failures >= CIRCUIT_BREAKER_THRESHOLD) {
649
- await disableAutoRespond(driver, accountId, `IMAP fetch failed: ${err instanceof Error ? err.message : String(err)}`);
650
- log("error", `Account ${accountId}: auto-respond DISABLED after ${failures} failures`);
651
- }
652
- return;
653
- }
654
- log("info", `Account ${accountId}: fetched ${messages.length} new messages`);
655
- if (messages.length === 0) {
656
- await updateLastPollAt(driver, accountId);
657
- return;
658
- }
659
- // ── Rate limit state ──────────────────────────────────────────────
660
- const hourlyLimit = arConfig.autoRespondHourlyLimit;
661
- const dailyLimit = arConfig.autoRespondDailyLimit;
662
- const now = new Date();
663
- let rateLimitState;
664
- try {
665
- rateLimitState = await readRateLimitState(driver, accountId);
666
- rateLimitState = await resetCountersIfNeeded(driver, accountId, rateLimitState, now);
667
- }
668
- catch (err) {
669
- // Fail-open: if we can't read counters, proceed without rate limiting.
670
- // Bounded by MAX_EMAILS_PER_POLL (10) per poll cycle.
671
- log("error", `Account ${accountId}: failed to read rate limit state — ${err instanceof Error ? err.message : String(err)}. Proceeding without rate limiting.`);
672
- rateLimitState = { sentThisHour: 0, sentToday: 0, hourResetAt: null, dayResetAt: null };
673
- }
674
- // ── Sender dedup (one reply per sender per poll cycle) ────────────
675
- const repliedSenders = new Set();
676
- // ── Process each message sequentially ─────────────────────────────
677
- for (const msg of messages) {
678
- // Skip auto-replies (loop prevention)
679
- if (msg.isAutoReply) {
680
- log("info", `Skipping auto-reply from ${msg.from} (UID ${msg.uid})`);
681
- await updateLastRespondedUid(driver, accountId, msg.uid);
682
- continue;
683
- }
684
- // Skip emails from the agent's own address (loop prevention)
685
- if (msg.from.toLowerCase() === emailConfig.agentAddress.toLowerCase()) {
686
- log("info", `Skipping own email from ${msg.from} (UID ${msg.uid})`);
687
- await updateLastRespondedUid(driver, accountId, msg.uid);
688
- continue;
689
- }
690
- // Skip emails with no extractable body
691
- if (!msg.body || msg.body === "(body unavailable)") {
692
- log("warn", `Skipping email with no body from ${msg.from} (UID ${msg.uid})`);
693
- await updateLastRespondedUid(driver, accountId, msg.uid);
694
- continue;
695
- }
696
- // Sender dedup — skip if we already replied to this sender in this poll cycle
697
- const senderKey = msg.from.toLowerCase();
698
- if (repliedSenders.has(senderKey)) {
699
- log("info", `Skipping duplicate sender ${msg.from} (UID ${msg.uid}) — already replied this poll cycle`);
700
- await updateLastRespondedUid(driver, accountId, msg.uid);
701
- continue;
702
- }
703
- // Screening gate — check the Email node's classification in Neo4j
704
- try {
705
- const check = await lookupScreening(driver, msg.messageId, accountId);
706
- if (!check.found) {
707
- // Email not yet in Neo4j — email-fetch hasn't classified it.
708
- // Skip without advancing UID. If later emails in this batch advance
709
- // the UID past this one, this email won't be retried — but it will
710
- // still be stored by email-fetch with its screening verdict. The
711
- // security posture (no reply without screening) takes precedence
712
- // over reply completeness.
713
- log("info", `Skipping UID ${msg.uid} from ${msg.from} — Email node not yet classified (email-fetch pending)`);
714
- continue;
715
- }
716
- if (check.promptInjectionFlag) {
717
- log("warn", `Skipping UID ${msg.uid} from ${msg.from} — prompt injection flag set`);
718
- await updateLastRespondedUid(driver, accountId, msg.uid);
719
- continue;
720
- }
721
- if (check.screening !== "clean") {
722
- log("info", `Skipping UID ${msg.uid} from ${msg.from} — screening=${check.screening ?? "absent"}`);
723
- await updateLastRespondedUid(driver, accountId, msg.uid);
724
- continue;
725
- }
726
- }
727
- catch (err) {
728
- log("error", `Account ${accountId}: screening lookup failed for UID ${msg.uid} — ${err instanceof Error ? err.message : String(err)}`);
729
- const failures = await incrementFailures(driver, accountId);
730
- if (failures >= CIRCUIT_BREAKER_THRESHOLD) {
731
- await disableAutoRespond(driver, accountId, `Screening lookup failed: ${err instanceof Error ? err.message : String(err)}`);
732
- log("error", `Account ${accountId}: auto-respond DISABLED after ${failures} failures (screening lookup)`);
733
- return;
734
- }
735
- continue;
736
- }
737
- // Rate limit check — hourly
738
- if (rateLimitState.sentThisHour >= hourlyLimit) {
739
- log("warn", `Account ${accountId}: hourly rate limit reached (${rateLimitState.sentThisHour}/${hourlyLimit}). Skipping remaining emails.`);
740
- await createRateLimitTask(driver, accountId, "hourly", rateLimitState.sentThisHour, hourlyLimit);
741
- break;
742
- }
743
- // Rate limit check — daily
744
- if (rateLimitState.sentToday >= dailyLimit) {
745
- log("warn", `Account ${accountId}: daily rate limit reached (${rateLimitState.sentToday}/${dailyLimit}). Skipping remaining emails.`);
746
- await createRateLimitTask(driver, accountId, "daily", rateLimitState.sentToday, dailyLimit);
747
- break;
748
- }
749
- try {
750
- await processEmail(msg, emailConfig, emailPassword, driver, accountId);
751
- await updateLastRespondedUid(driver, accountId, msg.uid);
752
- await incrementSendCounters(driver, accountId);
753
- rateLimitState.sentThisHour++;
754
- rateLimitState.sentToday++;
755
- repliedSenders.add(senderKey);
756
- // Reset failure counter on success
757
- await updateLastPollAt(driver, accountId);
758
- }
759
- catch (err) {
760
- log("error", `Account ${accountId}: failed to process email UID ${msg.uid} from ${msg.from} — ${err instanceof Error ? err.message : String(err)}`);
761
- const failures = await incrementFailures(driver, accountId);
762
- if (failures >= CIRCUIT_BREAKER_THRESHOLD) {
763
- await disableAutoRespond(driver, accountId, `Failed to process email from ${msg.from}: ${err instanceof Error ? err.message : String(err)}`);
764
- log("error", `Account ${accountId}: auto-respond DISABLED after ${failures} failures`);
765
- return; // Stop processing this account
766
- }
767
- // Continue to next message — the UID is NOT advanced so this email will be retried
768
- continue;
769
- }
770
- }
771
- // Final poll timestamp update
772
- await updateLastPollAt(driver, accountId);
773
- }
774
- // ---------------------------------------------------------------------------
775
- // Main
776
- // ---------------------------------------------------------------------------
777
- async function main() {
778
- if (!acquireLock()) {
779
- return; // Another instance is running
780
- }
781
- let driver = null;
782
- try {
783
- driver = connectNeo4j();
784
- const accounts = await getAutoRespondAccounts(driver);
785
- if (accounts.length === 0) {
786
- releaseLock();
787
- return; // No auto-respond accounts — silent exit
788
- }
789
- log("info", `Processing ${accounts.length} auto-respond account(s)`);
790
- for (const account of accounts) {
791
- try {
792
- await processAccount(account, driver);
793
- }
794
- catch (err) {
795
- log("error", `Unexpected error processing account ${account.accountId}: ${err instanceof Error ? err.message : String(err)}`);
796
- }
797
- }
798
- }
799
- catch (err) {
800
- log("error", `Fatal error: ${err instanceof Error ? err.message : String(err)}`);
801
- }
802
- finally {
803
- if (driver) {
804
- try {
805
- await driver.close();
806
- }
807
- catch { /* best effort */ }
808
- }
809
- releaseLock();
810
- }
811
- }
812
- main().catch((err) => {
813
- log("error", `Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
814
- releaseLock();
815
- process.exit(1);
816
- });
817
- //# sourceMappingURL=email-auto-respond.js.map