slackhive 0.1.37 → 0.1.39

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 (542) hide show
  1. package/.dockerignore +14 -0
  2. package/.env.example +44 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +65 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +38 -0
  6. package/.github/PULL_REQUEST_TEMPLATE.md +27 -0
  7. package/.github/dependabot.yml +20 -0
  8. package/.github/workflows/audit.yml +149 -0
  9. package/.github/workflows/ci.yml +135 -0
  10. package/CHANGELOG.md +52 -0
  11. package/CODE_OF_CONDUCT.md +37 -0
  12. package/CONTRIBUTING.md +204 -0
  13. package/LICENSE +21 -0
  14. package/README.md +19 -0
  15. package/SECURITY.md +47 -0
  16. package/apps/runner/Dockerfile +33 -0
  17. package/apps/runner/dist/__tests__/channel-restrictions.test.d.ts +8 -0
  18. package/apps/runner/dist/__tests__/channel-restrictions.test.js +63 -0
  19. package/apps/runner/dist/__tests__/channel-restrictions.test.js.map +1 -0
  20. package/apps/runner/dist/__tests__/claude-handler-resolve.test.d.ts +20 -0
  21. package/apps/runner/dist/__tests__/claude-handler-resolve.test.js +178 -0
  22. package/apps/runner/dist/__tests__/claude-handler-resolve.test.js.map +1 -0
  23. package/apps/runner/dist/__tests__/compile-claude-md.test.d.ts +13 -0
  24. package/apps/runner/dist/__tests__/compile-claude-md.test.js +144 -0
  25. package/apps/runner/dist/__tests__/compile-claude-md.test.js.map +1 -0
  26. package/apps/runner/dist/__tests__/memory-sync.test.d.ts +11 -0
  27. package/apps/runner/dist/__tests__/memory-sync.test.js +56 -0
  28. package/apps/runner/dist/__tests__/memory-sync.test.js.map +1 -0
  29. package/apps/runner/dist/__tests__/slack-file-support.test.d.ts +9 -0
  30. package/apps/runner/dist/__tests__/slack-file-support.test.js +271 -0
  31. package/apps/runner/dist/__tests__/slack-file-support.test.js.map +1 -0
  32. package/apps/runner/dist/__tests__/slack-formatting.test.d.ts +12 -0
  33. package/apps/runner/dist/__tests__/slack-formatting.test.js +400 -0
  34. package/apps/runner/dist/__tests__/slack-formatting.test.js.map +1 -0
  35. package/apps/runner/dist/__tests__/thread-context.test.d.ts +12 -0
  36. package/apps/runner/dist/__tests__/thread-context.test.js +182 -0
  37. package/apps/runner/dist/__tests__/thread-context.test.js.map +1 -0
  38. package/apps/runner/dist/agent-runner.d.ts +118 -0
  39. package/apps/runner/dist/agent-runner.js +352 -0
  40. package/apps/runner/dist/agent-runner.js.map +1 -0
  41. package/apps/runner/dist/claude-handler.d.ts +122 -0
  42. package/apps/runner/dist/claude-handler.js +402 -0
  43. package/apps/runner/dist/claude-handler.js.map +1 -0
  44. package/apps/runner/dist/compile-claude-md.d.ts +59 -0
  45. package/apps/runner/dist/compile-claude-md.js +291 -0
  46. package/apps/runner/dist/compile-claude-md.js.map +1 -0
  47. package/apps/runner/dist/correction-handler.d.ts +46 -0
  48. package/apps/runner/dist/correction-handler.js +162 -0
  49. package/apps/runner/dist/correction-handler.js.map +1 -0
  50. package/apps/runner/dist/correction-manager.d.ts +53 -0
  51. package/apps/runner/dist/correction-manager.js +241 -0
  52. package/apps/runner/dist/correction-manager.js.map +1 -0
  53. package/apps/runner/dist/db.d.ts +193 -0
  54. package/apps/runner/dist/db.js +492 -0
  55. package/apps/runner/dist/db.js.map +1 -0
  56. package/apps/runner/dist/index.d.ts +9 -0
  57. package/apps/runner/dist/index.js +43 -0
  58. package/apps/runner/dist/index.js.map +1 -0
  59. package/apps/runner/dist/job-scheduler.d.ts +57 -0
  60. package/apps/runner/dist/job-scheduler.js +150 -0
  61. package/apps/runner/dist/job-scheduler.js.map +1 -0
  62. package/apps/runner/dist/logger.d.ts +32 -0
  63. package/apps/runner/dist/logger.js +52 -0
  64. package/apps/runner/dist/logger.js.map +1 -0
  65. package/apps/runner/dist/mcp-process-manager.d.ts +38 -0
  66. package/apps/runner/dist/mcp-process-manager.js +189 -0
  67. package/apps/runner/dist/mcp-process-manager.js.map +1 -0
  68. package/apps/runner/dist/memory-mcp.d.ts +14 -0
  69. package/apps/runner/dist/memory-mcp.js +88 -0
  70. package/apps/runner/dist/memory-mcp.js.map +1 -0
  71. package/apps/runner/dist/memory-watcher.d.ts +78 -0
  72. package/apps/runner/dist/memory-watcher.js +220 -0
  73. package/apps/runner/dist/memory-watcher.js.map +1 -0
  74. package/apps/runner/dist/slack-handler.d.ts +120 -0
  75. package/apps/runner/dist/slack-handler.js +843 -0
  76. package/apps/runner/dist/slack-handler.js.map +1 -0
  77. package/apps/runner/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  78. package/apps/runner/package.json +42 -0
  79. package/apps/runner/src/__tests__/channel-restrictions.test.ts +75 -0
  80. package/apps/runner/src/__tests__/claude-handler-resolve.test.ts +160 -0
  81. package/apps/runner/src/__tests__/compile-claude-md.test.ts +139 -0
  82. package/apps/runner/src/__tests__/memory-sync.test.ts +59 -0
  83. package/apps/runner/src/__tests__/slack-file-support.test.ts +376 -0
  84. package/apps/runner/src/__tests__/slack-formatting.test.ts +495 -0
  85. package/apps/runner/src/__tests__/thread-context.test.ts +215 -0
  86. package/apps/runner/src/agent-runner.ts +397 -0
  87. package/apps/runner/src/claude-handler.ts +475 -0
  88. package/apps/runner/src/compile-claude-md.ts +283 -0
  89. package/apps/runner/src/correction-handler.ts +191 -0
  90. package/apps/runner/src/correction-manager.ts +285 -0
  91. package/apps/runner/src/db.ts +604 -0
  92. package/apps/runner/src/index.ts +46 -0
  93. package/apps/runner/src/job-scheduler.ts +165 -0
  94. package/apps/runner/src/logger.ts +49 -0
  95. package/apps/runner/src/mcp-process-manager.ts +195 -0
  96. package/apps/runner/src/memory-mcp.ts +85 -0
  97. package/apps/runner/src/memory-watcher.ts +215 -0
  98. package/apps/runner/src/slack-handler.ts +929 -0
  99. package/apps/runner/tsconfig.json +17 -0
  100. package/apps/runner/vitest.config.mts +17 -0
  101. package/apps/web/.eslintrc.json +3 -0
  102. package/apps/web/.next/app-build-manifest.json +323 -0
  103. package/apps/web/.next/app-path-routes-manifest.json +46 -0
  104. package/apps/web/.next/build-manifest.json +33 -0
  105. package/apps/web/.next/cache/.previewinfo +1 -0
  106. package/apps/web/.next/cache/.rscinfo +1 -0
  107. package/apps/web/.next/cache/webpack/client-production/0.pack +0 -0
  108. package/apps/web/.next/cache/webpack/client-production/1.pack +0 -0
  109. package/apps/web/.next/cache/webpack/client-production/2.pack +0 -0
  110. package/apps/web/.next/cache/webpack/client-production/3.pack +0 -0
  111. package/apps/web/.next/cache/webpack/client-production/4.pack +0 -0
  112. package/apps/web/.next/cache/webpack/client-production/index.pack +0 -0
  113. package/apps/web/.next/cache/webpack/client-production/index.pack.old +0 -0
  114. package/apps/web/.next/cache/webpack/edge-server-production/0.pack +0 -0
  115. package/apps/web/.next/cache/webpack/edge-server-production/1.pack +0 -0
  116. package/apps/web/.next/cache/webpack/edge-server-production/index.pack +0 -0
  117. package/apps/web/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  118. package/apps/web/.next/cache/webpack/server-production/0.pack +0 -0
  119. package/apps/web/.next/cache/webpack/server-production/1.pack +0 -0
  120. package/apps/web/.next/cache/webpack/server-production/2.pack +0 -0
  121. package/apps/web/.next/cache/webpack/server-production/index.pack +0 -0
  122. package/apps/web/.next/cache/webpack/server-production/index.pack.old +0 -0
  123. package/apps/web/.next/diagnostics/build-diagnostics.json +6 -0
  124. package/apps/web/.next/diagnostics/framework.json +1 -0
  125. package/apps/web/.next/package.json +1 -0
  126. package/apps/web/.next/react-loadable-manifest.json +1 -0
  127. package/apps/web/.next/server/app/_not-found/page.js +2 -0
  128. package/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -0
  129. package/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  130. package/apps/web/.next/server/app/agents/[slug]/page.js +4 -0
  131. package/apps/web/.next/server/app/agents/[slug]/page.js.nft.json +1 -0
  132. package/apps/web/.next/server/app/agents/[slug]/page_client-reference-manifest.js +1 -0
  133. package/apps/web/.next/server/app/agents/new/page.js +2 -0
  134. package/apps/web/.next/server/app/agents/new/page.js.nft.json +1 -0
  135. package/apps/web/.next/server/app/agents/new/page_client-reference-manifest.js +1 -0
  136. package/apps/web/.next/server/app/api/agents/[id]/access/route.js +1 -0
  137. package/apps/web/.next/server/app/api/agents/[id]/access/route.js.nft.json +1 -0
  138. package/apps/web/.next/server/app/api/agents/[id]/access/route_client-reference-manifest.js +1 -0
  139. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js +6 -0
  140. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js.nft.json +1 -0
  141. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route_client-reference-manifest.js +1 -0
  142. package/apps/web/.next/server/app/api/agents/[id]/logs/route.js +3 -0
  143. package/apps/web/.next/server/app/api/agents/[id]/logs/route.js.nft.json +1 -0
  144. package/apps/web/.next/server/app/api/agents/[id]/logs/route_client-reference-manifest.js +1 -0
  145. package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js +1 -0
  146. package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js.nft.json +1 -0
  147. package/apps/web/.next/server/app/api/agents/[id]/manifest/route_client-reference-manifest.js +1 -0
  148. package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js +1 -0
  149. package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js.nft.json +1 -0
  150. package/apps/web/.next/server/app/api/agents/[id]/mcps/route_client-reference-manifest.js +1 -0
  151. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js +1 -0
  152. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js.nft.json +1 -0
  153. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route_client-reference-manifest.js +1 -0
  154. package/apps/web/.next/server/app/api/agents/[id]/memories/route.js +1 -0
  155. package/apps/web/.next/server/app/api/agents/[id]/memories/route.js.nft.json +1 -0
  156. package/apps/web/.next/server/app/api/agents/[id]/memories/route_client-reference-manifest.js +1 -0
  157. package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js +1 -0
  158. package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js.nft.json +1 -0
  159. package/apps/web/.next/server/app/api/agents/[id]/permissions/route_client-reference-manifest.js +1 -0
  160. package/apps/web/.next/server/app/api/agents/[id]/reload/route.js +1 -0
  161. package/apps/web/.next/server/app/api/agents/[id]/reload/route.js.nft.json +1 -0
  162. package/apps/web/.next/server/app/api/agents/[id]/reload/route_client-reference-manifest.js +1 -0
  163. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js +1 -0
  164. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js.nft.json +1 -0
  165. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route_client-reference-manifest.js +1 -0
  166. package/apps/web/.next/server/app/api/agents/[id]/route.js +33 -0
  167. package/apps/web/.next/server/app/api/agents/[id]/route.js.nft.json +1 -0
  168. package/apps/web/.next/server/app/api/agents/[id]/route_client-reference-manifest.js +1 -0
  169. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js +1 -0
  170. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js.nft.json +1 -0
  171. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route_client-reference-manifest.js +1 -0
  172. package/apps/web/.next/server/app/api/agents/[id]/skills/route.js +1 -0
  173. package/apps/web/.next/server/app/api/agents/[id]/skills/route.js.nft.json +1 -0
  174. package/apps/web/.next/server/app/api/agents/[id]/skills/route_client-reference-manifest.js +1 -0
  175. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js +1 -0
  176. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js.nft.json +1 -0
  177. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route_client-reference-manifest.js +1 -0
  178. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js +1 -0
  179. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js.nft.json +1 -0
  180. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route_client-reference-manifest.js +1 -0
  181. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js +1 -0
  182. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js.nft.json +1 -0
  183. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route_client-reference-manifest.js +1 -0
  184. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js +1 -0
  185. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js.nft.json +1 -0
  186. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route_client-reference-manifest.js +1 -0
  187. package/apps/web/.next/server/app/api/agents/[id]/start/route.js +1 -0
  188. package/apps/web/.next/server/app/api/agents/[id]/start/route.js.nft.json +1 -0
  189. package/apps/web/.next/server/app/api/agents/[id]/start/route_client-reference-manifest.js +1 -0
  190. package/apps/web/.next/server/app/api/agents/[id]/stop/route.js +1 -0
  191. package/apps/web/.next/server/app/api/agents/[id]/stop/route.js.nft.json +1 -0
  192. package/apps/web/.next/server/app/api/agents/[id]/stop/route_client-reference-manifest.js +1 -0
  193. package/apps/web/.next/server/app/api/agents/route.js +91 -0
  194. package/apps/web/.next/server/app/api/agents/route.js.nft.json +1 -0
  195. package/apps/web/.next/server/app/api/agents/route_client-reference-manifest.js +1 -0
  196. package/apps/web/.next/server/app/api/auth/login/route.js +1 -0
  197. package/apps/web/.next/server/app/api/auth/login/route.js.nft.json +1 -0
  198. package/apps/web/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -0
  199. package/apps/web/.next/server/app/api/auth/logout/route.js +1 -0
  200. package/apps/web/.next/server/app/api/auth/logout/route.js.nft.json +1 -0
  201. package/apps/web/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -0
  202. package/apps/web/.next/server/app/api/auth/me/route.js +1 -0
  203. package/apps/web/.next/server/app/api/auth/me/route.js.nft.json +1 -0
  204. package/apps/web/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -0
  205. package/apps/web/.next/server/app/api/auth/users/[id]/route.js +1 -0
  206. package/apps/web/.next/server/app/api/auth/users/[id]/route.js.nft.json +1 -0
  207. package/apps/web/.next/server/app/api/auth/users/[id]/route_client-reference-manifest.js +1 -0
  208. package/apps/web/.next/server/app/api/auth/users/route.js +1 -0
  209. package/apps/web/.next/server/app/api/auth/users/route.js.nft.json +1 -0
  210. package/apps/web/.next/server/app/api/auth/users/route_client-reference-manifest.js +1 -0
  211. package/apps/web/.next/server/app/api/env-vars/[key]/route.js +1 -0
  212. package/apps/web/.next/server/app/api/env-vars/[key]/route.js.nft.json +1 -0
  213. package/apps/web/.next/server/app/api/env-vars/[key]/route_client-reference-manifest.js +1 -0
  214. package/apps/web/.next/server/app/api/env-vars/route.js +1 -0
  215. package/apps/web/.next/server/app/api/env-vars/route.js.nft.json +1 -0
  216. package/apps/web/.next/server/app/api/env-vars/route_client-reference-manifest.js +1 -0
  217. package/apps/web/.next/server/app/api/jobs/[id]/route.js +1 -0
  218. package/apps/web/.next/server/app/api/jobs/[id]/route.js.nft.json +1 -0
  219. package/apps/web/.next/server/app/api/jobs/[id]/route_client-reference-manifest.js +1 -0
  220. package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js +1 -0
  221. package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js.nft.json +1 -0
  222. package/apps/web/.next/server/app/api/jobs/[id]/runs/route_client-reference-manifest.js +1 -0
  223. package/apps/web/.next/server/app/api/jobs/route.js +1 -0
  224. package/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -0
  225. package/apps/web/.next/server/app/api/jobs/route_client-reference-manifest.js +1 -0
  226. package/apps/web/.next/server/app/api/mcps/[id]/route.js +1 -0
  227. package/apps/web/.next/server/app/api/mcps/[id]/route.js.nft.json +1 -0
  228. package/apps/web/.next/server/app/api/mcps/[id]/route_client-reference-manifest.js +1 -0
  229. package/apps/web/.next/server/app/api/mcps/[id]/test/route.js +1 -0
  230. package/apps/web/.next/server/app/api/mcps/[id]/test/route.js.nft.json +1 -0
  231. package/apps/web/.next/server/app/api/mcps/[id]/test/route_client-reference-manifest.js +1 -0
  232. package/apps/web/.next/server/app/api/mcps/route.js +1 -0
  233. package/apps/web/.next/server/app/api/mcps/route.js.nft.json +1 -0
  234. package/apps/web/.next/server/app/api/mcps/route_client-reference-manifest.js +1 -0
  235. package/apps/web/.next/server/app/api/settings/route.js +1 -0
  236. package/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -0
  237. package/apps/web/.next/server/app/api/settings/route_client-reference-manifest.js +1 -0
  238. package/apps/web/.next/server/app/icon.svg/route.js +1 -0
  239. package/apps/web/.next/server/app/icon.svg/route.js.nft.json +1 -0
  240. package/apps/web/.next/server/app/jobs/page.js +2 -0
  241. package/apps/web/.next/server/app/jobs/page.js.nft.json +1 -0
  242. package/apps/web/.next/server/app/jobs/page_client-reference-manifest.js +1 -0
  243. package/apps/web/.next/server/app/login/page.js +2 -0
  244. package/apps/web/.next/server/app/login/page.js.nft.json +1 -0
  245. package/apps/web/.next/server/app/login/page_client-reference-manifest.js +1 -0
  246. package/apps/web/.next/server/app/page.js +2 -0
  247. package/apps/web/.next/server/app/page.js.nft.json +1 -0
  248. package/apps/web/.next/server/app/page_client-reference-manifest.js +1 -0
  249. package/apps/web/.next/server/app/settings/env-vars/page.js +2 -0
  250. package/apps/web/.next/server/app/settings/env-vars/page.js.nft.json +1 -0
  251. package/apps/web/.next/server/app/settings/env-vars/page_client-reference-manifest.js +1 -0
  252. package/apps/web/.next/server/app/settings/mcps/page.js +2 -0
  253. package/apps/web/.next/server/app/settings/mcps/page.js.nft.json +1 -0
  254. package/apps/web/.next/server/app/settings/mcps/page_client-reference-manifest.js +1 -0
  255. package/apps/web/.next/server/app/settings/page.js +2 -0
  256. package/apps/web/.next/server/app/settings/page.js.nft.json +1 -0
  257. package/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -0
  258. package/apps/web/.next/server/app-paths-manifest.json +46 -0
  259. package/apps/web/.next/server/chunks/1157.js +9 -0
  260. package/apps/web/.next/server/chunks/2287.js +1 -0
  261. package/apps/web/.next/server/chunks/3444.js +1 -0
  262. package/apps/web/.next/server/chunks/383.js +6 -0
  263. package/apps/web/.next/server/chunks/4012.js +58 -0
  264. package/apps/web/.next/server/chunks/6791.js +1 -0
  265. package/apps/web/.next/server/chunks/7171.js +1 -0
  266. package/apps/web/.next/server/chunks/8819.js +22 -0
  267. package/apps/web/.next/server/edge-runtime-webpack.js +2 -0
  268. package/apps/web/.next/server/edge-runtime-webpack.js.map +1 -0
  269. package/apps/web/.next/server/interception-route-rewrite-manifest.js +1 -0
  270. package/apps/web/.next/server/middleware-build-manifest.js +1 -0
  271. package/apps/web/.next/server/middleware-manifest.json +32 -0
  272. package/apps/web/.next/server/middleware-react-loadable-manifest.js +1 -0
  273. package/apps/web/.next/server/next-font-manifest.js +1 -0
  274. package/apps/web/.next/server/next-font-manifest.json +1 -0
  275. package/apps/web/.next/server/pages/_app.js +1 -0
  276. package/apps/web/.next/server/pages/_app.js.nft.json +1 -0
  277. package/apps/web/.next/server/pages/_document.js +1 -0
  278. package/apps/web/.next/server/pages/_document.js.nft.json +1 -0
  279. package/apps/web/.next/server/pages/_error.js +19 -0
  280. package/apps/web/.next/server/pages/_error.js.nft.json +1 -0
  281. package/apps/web/.next/server/pages-manifest.json +5 -0
  282. package/apps/web/.next/server/server-reference-manifest.js +1 -0
  283. package/apps/web/.next/server/server-reference-manifest.json +1 -0
  284. package/apps/web/.next/server/src/middleware.js +14 -0
  285. package/apps/web/.next/server/src/middleware.js.map +1 -0
  286. package/apps/web/.next/server/webpack-runtime.js +1 -0
  287. package/apps/web/.next/static/chunks/18-90b700ea37b686a2.js +1 -0
  288. package/apps/web/.next/static/chunks/87c73c54-24122e7b92478d00.js +1 -0
  289. package/apps/web/.next/static/chunks/9664-af80478aa73ba424.js +1 -0
  290. package/apps/web/.next/static/chunks/app/_not-found/page-b9cee17ed89ca24a.js +1 -0
  291. package/apps/web/.next/static/chunks/app/agents/[slug]/page-18369fc3fe1a9a7b.js +1 -0
  292. package/apps/web/.next/static/chunks/app/agents/new/page-bf11cf8901c7e2cd.js +1 -0
  293. package/apps/web/.next/static/chunks/app/api/agents/[id]/access/route-07f0f73ac9839899.js +1 -0
  294. package/apps/web/.next/static/chunks/app/api/agents/[id]/claude-md/route-07f0f73ac9839899.js +1 -0
  295. package/apps/web/.next/static/chunks/app/api/agents/[id]/logs/route-07f0f73ac9839899.js +1 -0
  296. package/apps/web/.next/static/chunks/app/api/agents/[id]/manifest/route-07f0f73ac9839899.js +1 -0
  297. package/apps/web/.next/static/chunks/app/api/agents/[id]/mcps/route-07f0f73ac9839899.js +1 -0
  298. package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/[memId]/route-07f0f73ac9839899.js +1 -0
  299. package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/route-07f0f73ac9839899.js +1 -0
  300. package/apps/web/.next/static/chunks/app/api/agents/[id]/permissions/route-07f0f73ac9839899.js +1 -0
  301. package/apps/web/.next/static/chunks/app/api/agents/[id]/reload/route-07f0f73ac9839899.js +1 -0
  302. package/apps/web/.next/static/chunks/app/api/agents/[id]/restrictions/route-07f0f73ac9839899.js +1 -0
  303. package/apps/web/.next/static/chunks/app/api/agents/[id]/route-07f0f73ac9839899.js +1 -0
  304. package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/[skillId]/route-07f0f73ac9839899.js +1 -0
  305. package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/route-07f0f73ac9839899.js +1 -0
  306. package/apps/web/.next/static/chunks/app/api/agents/[id]/slack-info/route-07f0f73ac9839899.js +1 -0
  307. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/restore/route-07f0f73ac9839899.js +1 -0
  308. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/route-07f0f73ac9839899.js +1 -0
  309. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/route-07f0f73ac9839899.js +1 -0
  310. package/apps/web/.next/static/chunks/app/api/agents/[id]/start/route-07f0f73ac9839899.js +1 -0
  311. package/apps/web/.next/static/chunks/app/api/agents/[id]/stop/route-07f0f73ac9839899.js +1 -0
  312. package/apps/web/.next/static/chunks/app/api/agents/route-07f0f73ac9839899.js +1 -0
  313. package/apps/web/.next/static/chunks/app/api/auth/login/route-07f0f73ac9839899.js +1 -0
  314. package/apps/web/.next/static/chunks/app/api/auth/logout/route-07f0f73ac9839899.js +1 -0
  315. package/apps/web/.next/static/chunks/app/api/auth/me/route-07f0f73ac9839899.js +1 -0
  316. package/apps/web/.next/static/chunks/app/api/auth/users/[id]/route-07f0f73ac9839899.js +1 -0
  317. package/apps/web/.next/static/chunks/app/api/auth/users/route-07f0f73ac9839899.js +1 -0
  318. package/apps/web/.next/static/chunks/app/api/env-vars/[key]/route-07f0f73ac9839899.js +1 -0
  319. package/apps/web/.next/static/chunks/app/api/env-vars/route-07f0f73ac9839899.js +1 -0
  320. package/apps/web/.next/static/chunks/app/api/jobs/[id]/route-07f0f73ac9839899.js +1 -0
  321. package/apps/web/.next/static/chunks/app/api/jobs/[id]/runs/route-07f0f73ac9839899.js +1 -0
  322. package/apps/web/.next/static/chunks/app/api/jobs/route-07f0f73ac9839899.js +1 -0
  323. package/apps/web/.next/static/chunks/app/api/mcps/[id]/route-07f0f73ac9839899.js +1 -0
  324. package/apps/web/.next/static/chunks/app/api/mcps/[id]/test/route-07f0f73ac9839899.js +1 -0
  325. package/apps/web/.next/static/chunks/app/api/mcps/route-07f0f73ac9839899.js +1 -0
  326. package/apps/web/.next/static/chunks/app/api/settings/route-07f0f73ac9839899.js +1 -0
  327. package/apps/web/.next/static/chunks/app/jobs/page-f5aa89a47c50efd8.js +1 -0
  328. package/apps/web/.next/static/chunks/app/layout-2079f4964aa7314e.js +1 -0
  329. package/apps/web/.next/static/chunks/app/login/layout-07f0f73ac9839899.js +1 -0
  330. package/apps/web/.next/static/chunks/app/login/page-aa259283dc38e8f9.js +1 -0
  331. package/apps/web/.next/static/chunks/app/page-e83437b608104dff.js +1 -0
  332. package/apps/web/.next/static/chunks/app/settings/env-vars/page-06479dbdfb78b76b.js +1 -0
  333. package/apps/web/.next/static/chunks/app/settings/mcps/page-75650686ed6490c7.js +1 -0
  334. package/apps/web/.next/static/chunks/app/settings/page-e1e62fc41ff6cddd.js +1 -0
  335. package/apps/web/.next/static/chunks/framework-811407f832a33072.js +1 -0
  336. package/apps/web/.next/static/chunks/main-3f1cddbdd67b1546.js +1 -0
  337. package/apps/web/.next/static/chunks/main-app-cebd8a6a5ccbf72d.js +1 -0
  338. package/apps/web/.next/static/chunks/pages/_app-50fa07b56b2d29ac.js +1 -0
  339. package/apps/web/.next/static/chunks/pages/_error-fed8688bdd23f211.js +1 -0
  340. package/apps/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  341. package/apps/web/.next/static/chunks/webpack-6c05566dba553c97.js +1 -0
  342. package/apps/web/.next/static/css/15371687405525e2.css +5 -0
  343. package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_buildManifest.js +1 -0
  344. package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_ssgManifest.js +1 -0
  345. package/apps/web/.next/trace +5 -0
  346. package/apps/web/.next/types/app/agents/[slug]/page.ts +84 -0
  347. package/apps/web/.next/types/app/agents/new/page.ts +84 -0
  348. package/apps/web/.next/types/app/api/agents/[id]/access/route.ts +347 -0
  349. package/apps/web/.next/types/app/api/agents/[id]/claude-md/route.ts +347 -0
  350. package/apps/web/.next/types/app/api/agents/[id]/logs/route.ts +347 -0
  351. package/apps/web/.next/types/app/api/agents/[id]/manifest/route.ts +347 -0
  352. package/apps/web/.next/types/app/api/agents/[id]/mcps/route.ts +347 -0
  353. package/apps/web/.next/types/app/api/agents/[id]/memories/[memId]/route.ts +347 -0
  354. package/apps/web/.next/types/app/api/agents/[id]/memories/route.ts +347 -0
  355. package/apps/web/.next/types/app/api/agents/[id]/permissions/route.ts +347 -0
  356. package/apps/web/.next/types/app/api/agents/[id]/reload/route.ts +347 -0
  357. package/apps/web/.next/types/app/api/agents/[id]/restrictions/route.ts +347 -0
  358. package/apps/web/.next/types/app/api/agents/[id]/route.ts +347 -0
  359. package/apps/web/.next/types/app/api/agents/[id]/skills/[skillId]/route.ts +347 -0
  360. package/apps/web/.next/types/app/api/agents/[id]/skills/route.ts +347 -0
  361. package/apps/web/.next/types/app/api/agents/[id]/slack-info/route.ts +347 -0
  362. package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +347 -0
  363. package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/route.ts +347 -0
  364. package/apps/web/.next/types/app/api/agents/[id]/snapshots/route.ts +347 -0
  365. package/apps/web/.next/types/app/api/agents/[id]/start/route.ts +347 -0
  366. package/apps/web/.next/types/app/api/agents/[id]/stop/route.ts +347 -0
  367. package/apps/web/.next/types/app/api/agents/route.ts +347 -0
  368. package/apps/web/.next/types/app/api/auth/login/route.ts +347 -0
  369. package/apps/web/.next/types/app/api/auth/logout/route.ts +347 -0
  370. package/apps/web/.next/types/app/api/auth/me/route.ts +347 -0
  371. package/apps/web/.next/types/app/api/auth/users/[id]/route.ts +347 -0
  372. package/apps/web/.next/types/app/api/auth/users/route.ts +347 -0
  373. package/apps/web/.next/types/app/api/env-vars/[key]/route.ts +347 -0
  374. package/apps/web/.next/types/app/api/env-vars/route.ts +347 -0
  375. package/apps/web/.next/types/app/api/jobs/[id]/route.ts +347 -0
  376. package/apps/web/.next/types/app/api/jobs/[id]/runs/route.ts +347 -0
  377. package/apps/web/.next/types/app/api/jobs/route.ts +347 -0
  378. package/apps/web/.next/types/app/api/mcps/[id]/route.ts +347 -0
  379. package/apps/web/.next/types/app/api/mcps/[id]/test/route.ts +347 -0
  380. package/apps/web/.next/types/app/api/mcps/route.ts +347 -0
  381. package/apps/web/.next/types/app/api/settings/route.ts +347 -0
  382. package/apps/web/.next/types/app/jobs/page.ts +84 -0
  383. package/apps/web/.next/types/app/login/layout.ts +84 -0
  384. package/apps/web/.next/types/app/login/page.ts +84 -0
  385. package/apps/web/.next/types/app/page.ts +84 -0
  386. package/apps/web/.next/types/app/settings/env-vars/page.ts +84 -0
  387. package/apps/web/.next/types/app/settings/mcps/page.ts +84 -0
  388. package/apps/web/.next/types/app/settings/page.ts +84 -0
  389. package/apps/web/.next/types/cache-life.d.ts +141 -0
  390. package/apps/web/.next/types/package.json +1 -0
  391. package/apps/web/.next/types/routes.d.ts +114 -0
  392. package/apps/web/.next/types/validator.ts +448 -0
  393. package/apps/web/Dockerfile +37 -0
  394. package/apps/web/next-env.d.ts +6 -0
  395. package/apps/web/next.config.js +6 -0
  396. package/apps/web/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  397. package/apps/web/package.json +48 -0
  398. package/apps/web/postcss.config.js +3 -0
  399. package/apps/web/public/logo.svg +17 -0
  400. package/apps/web/src/app/agents/[slug]/page.tsx +2235 -0
  401. package/apps/web/src/app/agents/new/page.tsx +1161 -0
  402. package/apps/web/src/app/api/agents/[id]/access/route.ts +76 -0
  403. package/apps/web/src/app/api/agents/[id]/claude-md/route.ts +111 -0
  404. package/apps/web/src/app/api/agents/[id]/logs/route.ts +84 -0
  405. package/apps/web/src/app/api/agents/[id]/manifest/route.ts +32 -0
  406. package/apps/web/src/app/api/agents/[id]/mcps/route.ts +73 -0
  407. package/apps/web/src/app/api/agents/[id]/memories/[memId]/route.ts +31 -0
  408. package/apps/web/src/app/api/agents/[id]/memories/route.ts +56 -0
  409. package/apps/web/src/app/api/agents/[id]/permissions/route.ts +74 -0
  410. package/apps/web/src/app/api/agents/[id]/reload/route.ts +33 -0
  411. package/apps/web/src/app/api/agents/[id]/restrictions/route.ts +85 -0
  412. package/apps/web/src/app/api/agents/[id]/route.ts +81 -0
  413. package/apps/web/src/app/api/agents/[id]/skills/[skillId]/route.ts +52 -0
  414. package/apps/web/src/app/api/agents/[id]/skills/route.ts +80 -0
  415. package/apps/web/src/app/api/agents/[id]/slack-info/route.ts +38 -0
  416. package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +61 -0
  417. package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/route.ts +53 -0
  418. package/apps/web/src/app/api/agents/[id]/snapshots/route.ts +84 -0
  419. package/apps/web/src/app/api/agents/[id]/start/route.ts +35 -0
  420. package/apps/web/src/app/api/agents/[id]/stop/route.ts +35 -0
  421. package/apps/web/src/app/api/agents/route.ts +99 -0
  422. package/apps/web/src/app/api/auth/login/route.ts +39 -0
  423. package/apps/web/src/app/api/auth/logout/route.ts +21 -0
  424. package/apps/web/src/app/api/auth/me/route.ts +24 -0
  425. package/apps/web/src/app/api/auth/users/[id]/route.ts +48 -0
  426. package/apps/web/src/app/api/auth/users/route.ts +63 -0
  427. package/apps/web/src/app/api/env-vars/[key]/route.ts +66 -0
  428. package/apps/web/src/app/api/env-vars/route.ts +59 -0
  429. package/apps/web/src/app/api/jobs/[id]/route.ts +51 -0
  430. package/apps/web/src/app/api/jobs/[id]/runs/route.ts +24 -0
  431. package/apps/web/src/app/api/jobs/route.ts +42 -0
  432. package/apps/web/src/app/api/mcps/[id]/route.ts +60 -0
  433. package/apps/web/src/app/api/mcps/[id]/test/route.ts +195 -0
  434. package/apps/web/src/app/api/mcps/route.ts +72 -0
  435. package/apps/web/src/app/api/settings/route.ts +42 -0
  436. package/apps/web/src/app/globals.css +124 -0
  437. package/apps/web/src/app/icon.svg +17 -0
  438. package/apps/web/src/app/jobs/page.tsx +543 -0
  439. package/apps/web/src/app/layout-shell.tsx +89 -0
  440. package/apps/web/src/app/layout.tsx +18 -0
  441. package/apps/web/src/app/login/layout.tsx +9 -0
  442. package/apps/web/src/app/login/page.tsx +150 -0
  443. package/apps/web/src/app/page.tsx +573 -0
  444. package/apps/web/src/app/settings/env-vars/page.tsx +216 -0
  445. package/apps/web/src/app/settings/mcps/page.tsx +763 -0
  446. package/apps/web/src/app/settings/page.tsx +528 -0
  447. package/apps/web/src/app/sidebar.tsx +345 -0
  448. package/apps/web/src/lib/__tests__/api-guard.test.ts +189 -0
  449. package/apps/web/src/lib/__tests__/auth.test.ts +262 -0
  450. package/apps/web/src/lib/__tests__/boss-registry.test.ts +323 -0
  451. package/apps/web/src/lib/__tests__/compile.test.ts +161 -0
  452. package/apps/web/src/lib/__tests__/db-agent-hierarchy.test.ts +136 -0
  453. package/apps/web/src/lib/__tests__/db-env-vars.test.ts +216 -0
  454. package/apps/web/src/lib/__tests__/db-restrictions.test.ts +117 -0
  455. package/apps/web/src/lib/__tests__/db.integration.test.ts +271 -0
  456. package/apps/web/src/lib/__tests__/diff.test.ts +102 -0
  457. package/apps/web/src/lib/__tests__/mcp-mask.test.ts +274 -0
  458. package/apps/web/src/lib/__tests__/skill-templates.test.ts +237 -0
  459. package/apps/web/src/lib/__tests__/slack-manifest.test.ts +105 -0
  460. package/apps/web/src/lib/api-guard.ts +68 -0
  461. package/apps/web/src/lib/auth-context.tsx +71 -0
  462. package/apps/web/src/lib/auth.ts +128 -0
  463. package/apps/web/src/lib/boss-registry.ts +90 -0
  464. package/apps/web/src/lib/compile.ts +51 -0
  465. package/apps/web/src/lib/db.ts +1196 -0
  466. package/apps/web/src/lib/diff.ts +43 -0
  467. package/apps/web/src/lib/mcp-mask.ts +91 -0
  468. package/apps/web/src/lib/portal.tsx +23 -0
  469. package/apps/web/src/lib/skill-templates.ts +148 -0
  470. package/apps/web/src/lib/slack-manifest.ts +85 -0
  471. package/apps/web/src/middleware.ts +68 -0
  472. package/apps/web/tailwind.config.js +6 -0
  473. package/apps/web/tsconfig.json +23 -0
  474. package/apps/web/vitest.config.mts +21 -0
  475. package/cli/.claude/settings.local.json +6 -0
  476. package/cli/README.md +281 -0
  477. package/cli/node_modules/.package-lock.json +427 -0
  478. package/cli/node_modules/commander/LICENSE +22 -0
  479. package/cli/node_modules/commander/Readme.md +1157 -0
  480. package/cli/node_modules/commander/esm.mjs +16 -0
  481. package/cli/node_modules/commander/index.js +24 -0
  482. package/cli/node_modules/commander/lib/argument.js +149 -0
  483. package/cli/node_modules/commander/lib/command.js +2509 -0
  484. package/cli/node_modules/commander/lib/error.js +39 -0
  485. package/cli/node_modules/commander/lib/help.js +520 -0
  486. package/cli/node_modules/commander/lib/option.js +330 -0
  487. package/cli/node_modules/commander/lib/suggestSimilar.js +101 -0
  488. package/cli/node_modules/commander/package-support.json +16 -0
  489. package/cli/node_modules/commander/package.json +84 -0
  490. package/cli/node_modules/commander/typings/esm.d.mts +3 -0
  491. package/cli/node_modules/commander/typings/index.d.ts +969 -0
  492. package/cli/package-lock.json +449 -0
  493. package/cli/package.json +44 -0
  494. package/cli/src/commands/init.ts +514 -0
  495. package/cli/src/commands/manage.ts +115 -0
  496. package/cli/src/index.ts +63 -0
  497. package/cli/tsconfig.json +14 -0
  498. package/docker-compose.yml +122 -0
  499. package/docs/agents/boss-agents.mdx +108 -0
  500. package/docs/agents/creating-agents.mdx +132 -0
  501. package/docs/agents/memory.mdx +113 -0
  502. package/docs/agents/tools.mdx +103 -0
  503. package/docs/configuration/env-vars.mdx +166 -0
  504. package/docs/configuration/mcp-servers.mdx +203 -0
  505. package/docs/configuration/slack-app.mdx +175 -0
  506. package/docs/docs.json +79 -0
  507. package/docs/favicon.svg +17 -0
  508. package/docs/features/history.mdx +60 -0
  509. package/docs/features/import-export.mdx +77 -0
  510. package/docs/features/logs.mdx +131 -0
  511. package/docs/features/multi-workspace.mdx +90 -0
  512. package/docs/features/scheduled-jobs.mdx +231 -0
  513. package/docs/features/users.mdx +92 -0
  514. package/docs/introduction.mdx +160 -0
  515. package/docs/logo/dark.svg +17 -0
  516. package/docs/logo/light.svg +17 -0
  517. package/docs/logo/wide-dark.svg +12 -0
  518. package/docs/logo/wide-light.svg +12 -0
  519. package/docs/quickstart.mdx +270 -0
  520. package/docs/self-hosting/docker.mdx +151 -0
  521. package/docs/self-hosting/production.mdx +176 -0
  522. package/package.json +20 -36
  523. package/packages/shared/dist/index.d.ts +8 -0
  524. package/packages/shared/dist/index.d.ts.map +1 -0
  525. package/packages/shared/dist/index.js +24 -0
  526. package/packages/shared/dist/index.js.map +1 -0
  527. package/packages/shared/dist/types.d.ts +584 -0
  528. package/packages/shared/dist/types.d.ts.map +1 -0
  529. package/packages/shared/dist/types.js +39 -0
  530. package/packages/shared/dist/types.js.map +1 -0
  531. package/packages/shared/package.json +15 -0
  532. package/packages/shared/src/db/schema.sql +354 -0
  533. package/packages/shared/src/index.ts +8 -0
  534. package/packages/shared/src/types.ts +683 -0
  535. package/packages/shared/tsconfig.json +17 -0
  536. package/scripts/dev.sh +45 -0
  537. /package/{dist → cli/dist}/commands/init.d.ts +0 -0
  538. /package/{dist → cli/dist}/commands/init.js +0 -0
  539. /package/{dist → cli/dist}/commands/manage.d.ts +0 -0
  540. /package/{dist → cli/dist}/commands/manage.js +0 -0
  541. /package/{dist → cli/dist}/index.d.ts +0 -0
  542. /package/{dist → cli/dist}/index.js +0 -0
@@ -0,0 +1,215 @@
1
+ /**
2
+ * @fileoverview Unit tests for thread context building in slack-handler.ts.
3
+ *
4
+ * Covers:
5
+ * - Real usernames and IDs resolved via users.info
6
+ * - Bot messages labeled with agent name
7
+ * - Forwarded message attachments (text, pretext, image_url, source)
8
+ * - Files shared in thread history (images, other files)
9
+ * - Username resolution cached across messages (single API call per user)
10
+ * - users.info failure falls back to user ID
11
+ */
12
+
13
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
14
+ import { buildPrompt } from '../slack-handler';
15
+ import type { Logger } from 'winston';
16
+
17
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
18
+
19
+ const nopLog = {
20
+ debug: () => {},
21
+ info: () => {},
22
+ warn: () => {},
23
+ error: () => {},
24
+ } as unknown as Logger;
25
+
26
+ const fakeAgent = {
27
+ id: 'agent-1',
28
+ name: 'Gilfoyle',
29
+ slackBotUserId: 'UBOT001',
30
+ } as any;
31
+
32
+ function makeClient(messages: any[], userMap: Record<string, { display_name?: string; real_name?: string }> = {}) {
33
+ return {
34
+ conversations: {
35
+ replies: vi.fn().mockResolvedValue({ messages }),
36
+ },
37
+ users: {
38
+ info: vi.fn().mockImplementation(({ user }: { user: string }) => {
39
+ const u = userMap[user];
40
+ if (!u) return Promise.reject(new Error('user not found'));
41
+ return Promise.resolve({ user: u });
42
+ }),
43
+ },
44
+ };
45
+ }
46
+
47
+ // ─── Tests ────────────────────────────────────────────────────────────────────
48
+
49
+ describe('buildPrompt — thread context', () => {
50
+ it('resolves display names and user IDs for human messages', async () => {
51
+ const messages = [
52
+ { user: 'U001', text: 'hello there', ts: '1.0' },
53
+ { user: 'U002', text: 'hey @Gilfoyle', ts: '2.0' },
54
+ { user: 'U002', text: 'current message', ts: '3.0' }, // last message excluded
55
+ ];
56
+ const client = makeClient(messages, {
57
+ U001: { display_name: 'Aman' },
58
+ U002: { display_name: 'Kush' },
59
+ });
60
+
61
+ const result = await buildPrompt(client, 'C001', '1.0', 'current message', fakeAgent, nopLog);
62
+ const text = typeof result === 'string' ? result : '';
63
+
64
+ expect(text).toContain('Aman (U001): hello there');
65
+ expect(text).toContain('Kush (U002): hey');
66
+ });
67
+
68
+ it('labels bot messages with agent name', async () => {
69
+ const messages = [
70
+ { bot_id: 'B001', text: 'I am a bot response', ts: '1.0' },
71
+ { user: 'U001', text: 'follow up', ts: '2.0' }, // last excluded
72
+ ];
73
+ const client = makeClient(messages, {});
74
+
75
+ const result = await buildPrompt(client, 'C001', '1.0', 'follow up', fakeAgent, nopLog);
76
+ const text = typeof result === 'string' ? result : '';
77
+
78
+ expect(text).toContain('Gilfoyle: I am a bot response');
79
+ expect(text).not.toContain('User:');
80
+ });
81
+
82
+ it('falls back to user ID when users.info fails', async () => {
83
+ const messages = [
84
+ { user: 'U999', text: 'mystery message', ts: '1.0' },
85
+ { user: 'U001', text: 'next', ts: '2.0' }, // last excluded
86
+ ];
87
+ const client = makeClient(messages, {}); // U999 not in map → throws
88
+
89
+ const result = await buildPrompt(client, 'C001', '1.0', 'next', fakeAgent, nopLog);
90
+ const text = typeof result === 'string' ? result : '';
91
+
92
+ expect(text).toContain('U999: mystery message');
93
+ });
94
+
95
+ it('only calls users.info for unique users across the thread', async () => {
96
+ const messages = [
97
+ { user: 'U001', text: 'first message', ts: '1.0' },
98
+ { user: 'U002', text: 'second message', ts: '2.0' },
99
+ { user: 'U001', text: 'current', ts: '3.0' }, // last excluded
100
+ ];
101
+ const client = makeClient(messages, {
102
+ U001: { display_name: 'Aman' },
103
+ U002: { display_name: 'Kush' },
104
+ });
105
+
106
+ const result = await buildPrompt(client, 'C001', '1.0', 'current', fakeAgent, nopLog);
107
+ const text = typeof result === 'string' ? result : '';
108
+
109
+ // Both users resolved correctly
110
+ expect(text).toContain('Aman (U001): first message');
111
+ expect(text).toContain('Kush (U002): second message');
112
+ // Only 2 unique users → max 2 API calls
113
+ expect(client.users.info).toHaveBeenCalledTimes(2);
114
+ });
115
+
116
+ it('includes forwarded message attachment text and source', async () => {
117
+ const messages = [
118
+ {
119
+ user: 'U001', text: 'check this out', ts: '1.0',
120
+ attachments: [{
121
+ author_name: 'Kush',
122
+ text: 'why you need to be hi/hello?',
123
+ pretext: 'Forwarded message:',
124
+ }],
125
+ },
126
+ { user: 'U001', text: 'current', ts: '2.0' }, // last excluded
127
+ ];
128
+ const client = makeClient(messages, { U001: { display_name: 'Aman' } });
129
+
130
+ const result = await buildPrompt(client, 'C001', '1.0', 'current', fakeAgent, nopLog);
131
+ const text = typeof result === 'string' ? result : '';
132
+
133
+ expect(text).toContain('check this out');
134
+ expect(text).toContain('[Forwarded from Kush]');
135
+ expect(text).toContain('Forwarded message:');
136
+ expect(text).toContain('why you need to be hi/hello?');
137
+ });
138
+
139
+ it('includes image_url from forwarded attachments', async () => {
140
+ const messages = [
141
+ {
142
+ user: 'U001', text: 'see screenshot', ts: '1.0',
143
+ attachments: [{
144
+ text: 'look at this',
145
+ image_url: 'https://files.slack.com/screenshot.png',
146
+ }],
147
+ },
148
+ { user: 'U001', text: 'current', ts: '2.0' },
149
+ ];
150
+ const client = makeClient(messages, { U001: { display_name: 'Aman' } });
151
+
152
+ const result = await buildPrompt(client, 'C001', '1.0', 'current', fakeAgent, nopLog);
153
+ const text = typeof result === 'string' ? result : '';
154
+
155
+ expect(text).toContain('[Attached image: https://files.slack.com/screenshot.png]');
156
+ });
157
+
158
+ it('uses fallback text when attachment has no text', async () => {
159
+ const messages = [
160
+ {
161
+ user: 'U001', text: 'fwd', ts: '1.0',
162
+ attachments: [{ fallback: 'fallback content here' }],
163
+ },
164
+ { user: 'U001', text: 'current', ts: '2.0' },
165
+ ];
166
+ const client = makeClient(messages, { U001: { display_name: 'Aman' } });
167
+
168
+ const result = await buildPrompt(client, 'C001', '1.0', 'current', fakeAgent, nopLog);
169
+ const text = typeof result === 'string' ? result : '';
170
+
171
+ expect(text).toContain('fallback content here');
172
+ });
173
+
174
+ it('notes images shared in thread history', async () => {
175
+ const messages = [
176
+ {
177
+ user: 'U001', text: '', ts: '1.0',
178
+ files: [{ id: 'F001', name: 'screenshot.png', mimetype: 'image/png' }],
179
+ },
180
+ { user: 'U001', text: 'current', ts: '2.0' },
181
+ ];
182
+ const client = makeClient(messages, { U001: { display_name: 'Aman' } });
183
+
184
+ const result = await buildPrompt(client, 'C001', '1.0', 'current', fakeAgent, nopLog);
185
+ const text = typeof result === 'string' ? result : '';
186
+
187
+ expect(text).toContain('[Shared image: screenshot.png]');
188
+ });
189
+
190
+ it('notes non-image files shared in thread history', async () => {
191
+ const messages = [
192
+ {
193
+ user: 'U001', text: '', ts: '1.0',
194
+ files: [{ id: 'F002', name: 'report.csv', mimetype: 'text/csv' }],
195
+ },
196
+ { user: 'U001', text: 'current', ts: '2.0' },
197
+ ];
198
+ const client = makeClient(messages, { U001: { display_name: 'Aman' } });
199
+
200
+ const result = await buildPrompt(client, 'C001', '1.0', 'current', fakeAgent, nopLog);
201
+ const text = typeof result === 'string' ? result : '';
202
+
203
+ expect(text).toContain('[Shared file: report.csv]');
204
+ });
205
+
206
+ it('returns no thread context when threadTs is undefined', async () => {
207
+ const client = makeClient([]);
208
+
209
+ const result = await buildPrompt(client, 'C001', undefined, 'hello', fakeAgent, nopLog);
210
+ const text = typeof result === 'string' ? result : '';
211
+
212
+ expect(text).not.toContain('[Thread context]');
213
+ expect(client.conversations.replies).not.toHaveBeenCalled();
214
+ });
215
+ });
@@ -0,0 +1,397 @@
1
+ /**
2
+ * @fileoverview Agent runner — manages the lifecycle of all Slack bot instances.
3
+ *
4
+ * The AgentRunner is the top-level orchestrator for the runner service.
5
+ * On startup it loads all active agents from the database, starts a Slack
6
+ * Bolt App for each one, and listens on Redis for reload signals from the
7
+ * web UI.
8
+ *
9
+ * Each agent gets:
10
+ * - Its own Bolt App instance (separate Slack socket connection)
11
+ * - Its own ClaudeHandler (session manager, MCP wiring, tool permissions)
12
+ * - Its own MemoryWatcher (syncs learned memories to the database)
13
+ * - A compiled CLAUDE.md in /tmp/agents/{slug}/ (skills + memories)
14
+ *
15
+ * Hot reload flow:
16
+ * 1. User edits skills/MCPs/permissions in the web UI
17
+ * 2. Web API publishes `{ type: 'reload', agentId }` to Redis channel
18
+ * 3. AgentRunner receives the event, stops the agent, recompiles, restarts
19
+ *
20
+ * @module runner/agent-runner
21
+ */
22
+
23
+ import { App, LogLevel } from '@slack/bolt';
24
+ import { createClient, type RedisClientType } from 'redis';
25
+ import type { Agent } from '@slackhive/shared';
26
+ import { AGENT_EVENTS_CHANNEL, type AgentEvent } from '@slackhive/shared';
27
+ import { JobScheduler } from './job-scheduler';
28
+ import {
29
+ getAllAgents,
30
+ getAgentById,
31
+ getAgentMcpServers,
32
+ getAgentPermissions,
33
+ getAgentRestrictions,
34
+ getAgentMemories,
35
+ getAllEnvVarValues,
36
+ updateAgentStatus,
37
+ } from './db';
38
+ import { compileClaudeMd, materializeMemoryFiles } from './compile-claude-md';
39
+ import { ClaudeHandler } from './claude-handler';
40
+ import { MemoryWatcher } from './memory-watcher';
41
+ import { registerSlackHandlers } from './slack-handler';
42
+ import { logger } from './logger';
43
+
44
+ /**
45
+ * Represents a fully initialized running agent.
46
+ * All resources owned by a running agent are held here for cleanup.
47
+ */
48
+ interface RunningAgent {
49
+ agent: Agent;
50
+ app: App;
51
+ claudeHandler: ClaudeHandler;
52
+ memoryWatcher: MemoryWatcher;
53
+ }
54
+
55
+ /**
56
+ * Manages the lifecycle of all Claude Code Slack bot instances.
57
+ *
58
+ * @example
59
+ * const runner = new AgentRunner();
60
+ * await runner.start();
61
+ * // Ctrl+C triggers graceful shutdown
62
+ */
63
+ export class AgentRunner {
64
+ /** Map of agent ID → running agent resources. */
65
+ private runningAgents: Map<string, RunningAgent> = new Map();
66
+
67
+ /** Scheduled job executor. */
68
+ private jobScheduler: JobScheduler;
69
+
70
+ /** Redis subscriber client for hot-reload events. */
71
+ private redisSubscriber: RedisClientType | null = null;
72
+
73
+ constructor() {
74
+ this.jobScheduler = new JobScheduler((agentId: string) => this.getRunningAgent(agentId));
75
+ }
76
+
77
+ /**
78
+ * Returns any running agent by ID, or undefined if not running.
79
+ */
80
+ getRunningAgent(agentId: string): { app: App; claudeHandler: import('./claude-handler').ClaudeHandler } | undefined {
81
+ const ra = this.runningAgents.get(agentId);
82
+ return ra ? { app: ra.app, claudeHandler: ra.claudeHandler } : undefined;
83
+ }
84
+
85
+ /**
86
+ * Starts the runner:
87
+ * 1. Connects to Redis for hot-reload events
88
+ * 2. Loads and starts all active agents from the database
89
+ * 3. Registers graceful shutdown handlers
90
+ *
91
+ * @returns {Promise<void>}
92
+ * @throws {Error} If Redis connection fails.
93
+ */
94
+ async start(): Promise<void> {
95
+ logger.info('AgentRunner starting...');
96
+
97
+ await this.connectRedis();
98
+ await this.loadAllAgents();
99
+ await this.jobScheduler.start();
100
+ this.registerShutdownHandlers();
101
+
102
+ logger.info('AgentRunner started', { agents: this.runningAgents.size });
103
+ }
104
+
105
+ /**
106
+ * Gracefully stops all running agents and disconnects from Redis.
107
+ *
108
+ * @returns {Promise<void>}
109
+ */
110
+ async stop(): Promise<void> {
111
+ logger.info('AgentRunner stopping...');
112
+
113
+ // Stop job scheduler
114
+ await this.jobScheduler.stop();
115
+
116
+ // Stop all agents concurrently
117
+ const stopPromises = Array.from(this.runningAgents.keys()).map((id) =>
118
+ this.stopAgent(id).catch((err) =>
119
+ logger.warn('Error stopping agent during shutdown', { agentId: id, error: err.message })
120
+ )
121
+ );
122
+ await Promise.all(stopPromises);
123
+
124
+ if (this.redisSubscriber) {
125
+ await this.redisSubscriber.unsubscribe();
126
+ await this.redisSubscriber.disconnect();
127
+ this.redisSubscriber = null;
128
+ }
129
+
130
+ logger.info('AgentRunner stopped');
131
+ }
132
+
133
+ // ===========================================================================
134
+ // Agent lifecycle
135
+ // ===========================================================================
136
+
137
+ /**
138
+ * Loads all agents from the database and starts each one.
139
+ *
140
+ * @returns {Promise<void>}
141
+ */
142
+ private async loadAllAgents(): Promise<void> {
143
+ const agents = await getAllAgents();
144
+ logger.info('Loading agents from database', { count: agents.length });
145
+
146
+ // Start agents sequentially to avoid overwhelming Slack's rate limits.
147
+ // Skip agents that are stopped or have placeholder/missing tokens.
148
+ for (const agent of agents) {
149
+ if (agent.enabled === false) {
150
+ logger.info('Skipping disabled agent', { agent: agent.slug });
151
+ continue;
152
+ }
153
+ if (
154
+ !agent.slackBotToken.startsWith('xoxb-') ||
155
+ !agent.slackAppToken.startsWith('xapp-') ||
156
+ agent.slackBotToken.includes('placeholder') ||
157
+ agent.slackAppToken.includes('placeholder')
158
+ ) {
159
+ logger.warn('Skipping agent with invalid/placeholder tokens', { agent: agent.slug });
160
+ await updateAgentStatus(agent.id, 'stopped');
161
+ continue;
162
+ }
163
+ try {
164
+ await this.startAgent(agent);
165
+ } catch (err) {
166
+ logger.error('Failed to start agent', { agent: agent.slug, error: (err as Error).message });
167
+ await updateAgentStatus(agent.id, 'error');
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Starts a single agent:
174
+ * 1. Loads its MCP servers and permissions from the database
175
+ * 2. Compiles CLAUDE.md from skills + memories
176
+ * 3. Materializes memory files to disk
177
+ * 4. Creates Bolt App + ClaudeHandler + MemoryWatcher
178
+ * 5. Registers Slack event handlers
179
+ * 6. Starts the Bolt App (opens Socket Mode connection)
180
+ *
181
+ * @param {Agent} agent - The agent to start.
182
+ * @returns {Promise<void>}
183
+ * @throws {Error} If Slack App fails to start.
184
+ */
185
+ private async startAgent(agent: Agent): Promise<void> {
186
+ if (this.runningAgents.has(agent.id)) {
187
+ logger.warn('Agent already running, skipping start', { agent: agent.slug });
188
+ return;
189
+ }
190
+
191
+ logger.info('Starting agent', { agent: agent.slug });
192
+
193
+ // Load configuration from DB
194
+ const [mcpServers, permissions, restrictions, memories, envVarValues] = await Promise.all([
195
+ getAgentMcpServers(agent.id),
196
+ getAgentPermissions(agent.id),
197
+ getAgentRestrictions(agent.id),
198
+ getAgentMemories(agent.id),
199
+ getAllEnvVarValues(),
200
+ ]);
201
+
202
+ // Compile CLAUDE.md (identity + skills → temp workspace)
203
+ const workDir = await compileClaudeMd(agent);
204
+
205
+ // Materialize memory files so the /recall skill can read them
206
+ materializeMemoryFiles(agent, memories);
207
+
208
+ // Create Claude Code SDK handler
209
+ const claudeHandler = new ClaudeHandler(agent, mcpServers, permissions, workDir, envVarValues);
210
+ claudeHandler.initialize();
211
+
212
+ // Create memory watcher (persists SDK memory writes back to DB)
213
+ const memoryWatcher = new MemoryWatcher(agent);
214
+ memoryWatcher.start();
215
+
216
+ // Create Slack Bolt App in Socket Mode
217
+ const app = new App({
218
+ token: agent.slackBotToken,
219
+ appToken: agent.slackAppToken,
220
+ signingSecret: agent.slackSigningSecret,
221
+ socketMode: true,
222
+ logLevel: process.env.LOG_LEVEL === 'debug' ? LogLevel.DEBUG : LogLevel.WARN,
223
+ });
224
+
225
+ // Register all Slack event listeners
226
+ registerSlackHandlers(app, agent, claudeHandler, restrictions);
227
+
228
+ // Start the Bolt App
229
+ await app.start();
230
+
231
+ // Fetch and store the bot's Slack user ID for @mention construction
232
+ try {
233
+ const authResult = await app.client.auth.test({ token: agent.slackBotToken });
234
+ if (authResult.user_id && authResult.user_id !== agent.slackBotUserId) {
235
+ await updateAgentStatus(agent.id, 'running'); // Will also trigger updateAgentSlackUserId below
236
+ const { updateAgentSlackUserId } = await import('./db');
237
+ await updateAgentSlackUserId(agent.id, authResult.user_id as string);
238
+ agent.slackBotUserId = authResult.user_id as string;
239
+ }
240
+ } catch (err) {
241
+ logger.warn('Failed to fetch bot user ID', { agent: agent.slug, error: err });
242
+ }
243
+
244
+ this.runningAgents.set(agent.id, { agent, app, claudeHandler, memoryWatcher });
245
+ await updateAgentStatus(agent.id, 'running');
246
+
247
+ logger.info('Agent started', {
248
+ agent: agent.slug,
249
+ mcpServers: mcpServers.map((m) => m.name),
250
+ });
251
+ }
252
+
253
+ /**
254
+ * Stops a running agent and cleans up all its resources.
255
+ *
256
+ * @param {string} agentId - UUID of the agent to stop.
257
+ * @returns {Promise<void>}
258
+ */
259
+ private async stopAgent(agentId: string): Promise<void> {
260
+ const running = this.runningAgents.get(agentId);
261
+ if (!running) return;
262
+
263
+ const { agent, app, claudeHandler, memoryWatcher } = running;
264
+ logger.info('Stopping agent', { agent: agent.slug });
265
+
266
+ memoryWatcher.stop();
267
+ claudeHandler.destroy();
268
+
269
+ try {
270
+ await app.stop();
271
+ } catch (err) {
272
+ logger.warn('Error stopping Bolt App', { agent: agent.slug, error: err });
273
+ }
274
+
275
+ this.runningAgents.delete(agentId);
276
+ await updateAgentStatus(agentId, 'stopped');
277
+
278
+ logger.info('Agent stopped', { agent: agent.slug });
279
+ }
280
+
281
+ /**
282
+ * Reloads an agent: stops it, re-fetches its config, recompiles, and restarts.
283
+ * Called when the web UI publishes a reload event.
284
+ *
285
+ * @param {string} agentId - UUID of the agent to reload.
286
+ * @returns {Promise<void>}
287
+ */
288
+ private async reloadAgent(agentId: string): Promise<void> {
289
+ logger.info('Reloading agent', { agentId });
290
+
291
+ await this.stopAgent(agentId);
292
+
293
+ const agent = await getAgentById(agentId);
294
+ if (!agent) {
295
+ logger.warn('Agent not found after reload event', { agentId });
296
+ return;
297
+ }
298
+
299
+ await this.startAgent(agent);
300
+ }
301
+
302
+ // ===========================================================================
303
+ // Redis pub/sub
304
+ // ===========================================================================
305
+
306
+ /**
307
+ * Connects to Redis and subscribes to agent lifecycle events.
308
+ *
309
+ * @returns {Promise<void>}
310
+ * @throws {Error} If REDIS_URL is not set or connection fails.
311
+ */
312
+ private async connectRedis(): Promise<void> {
313
+ const redisUrl = process.env.REDIS_URL;
314
+ if (!redisUrl) {
315
+ logger.warn('REDIS_URL not set — hot reload disabled');
316
+ return;
317
+ }
318
+
319
+ this.redisSubscriber = createClient({ url: redisUrl }) as RedisClientType;
320
+
321
+ this.redisSubscriber.on('error', (err) => {
322
+ logger.warn('Redis subscriber error', { error: err.message });
323
+ });
324
+
325
+ await this.redisSubscriber.connect();
326
+
327
+ await this.redisSubscriber.subscribe(AGENT_EVENTS_CHANNEL, (message) => {
328
+ this.handleAgentEvent(message);
329
+ });
330
+
331
+ logger.info('Redis subscriber connected', { channel: AGENT_EVENTS_CHANNEL });
332
+ }
333
+
334
+ /**
335
+ * Handles an agent lifecycle event received from Redis.
336
+ *
337
+ * @param {string} rawMessage - JSON-encoded AgentEvent.
338
+ * @returns {void}
339
+ */
340
+ private handleAgentEvent(rawMessage: string): void {
341
+ let event: AgentEvent;
342
+ try {
343
+ event = JSON.parse(rawMessage) as AgentEvent;
344
+ } catch {
345
+ logger.warn('Received malformed agent event', { rawMessage });
346
+ return;
347
+ }
348
+
349
+ logger.info('Received agent event', { event });
350
+
351
+ switch (event.type) {
352
+ case 'reload':
353
+ this.reloadAgent(event.agentId).catch((err) =>
354
+ logger.error('Failed to reload agent', { agentId: event.agentId, error: err.message })
355
+ );
356
+ break;
357
+ case 'start':
358
+ getAgentById(event.agentId)
359
+ .then((agent) => agent && this.startAgent(agent))
360
+ .catch((err) =>
361
+ logger.error('Failed to start agent', { agentId: event.agentId, error: err.message })
362
+ );
363
+ break;
364
+ case 'stop':
365
+ this.stopAgent(event.agentId).catch((err) =>
366
+ logger.error('Failed to stop agent', { agentId: event.agentId, error: err.message })
367
+ );
368
+ break;
369
+ case 'reload-jobs':
370
+ this.jobScheduler.reload().catch((err) =>
371
+ logger.error('Failed to reload jobs', { error: (err as Error).message })
372
+ );
373
+ break;
374
+ }
375
+ }
376
+
377
+ // ===========================================================================
378
+ // Graceful shutdown
379
+ // ===========================================================================
380
+
381
+ /**
382
+ * Registers SIGTERM and SIGINT handlers for graceful shutdown.
383
+ * Ensures all agents are cleanly stopped before the process exits.
384
+ *
385
+ * @returns {void}
386
+ */
387
+ private registerShutdownHandlers(): void {
388
+ const shutdown = async (signal: string) => {
389
+ logger.info(`Received ${signal}, shutting down gracefully...`);
390
+ await this.stop();
391
+ process.exit(0);
392
+ };
393
+
394
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
395
+ process.on('SIGINT', () => shutdown('SIGINT'));
396
+ }
397
+ }