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,475 @@
1
+ /**
2
+ * @fileoverview Claude Code SDK session manager for a single agent.
3
+ *
4
+ * Each agent has ONE ClaudeHandler shared across all Slack conversations.
5
+ * Each Slack thread gets its own isolated working directory so Claude's
6
+ * memory files (written to .claude/memory/ inside cwd) are per-thread.
7
+ *
8
+ * Directory layout:
9
+ * /tmp/agents/{slug}/ ← agent root (CLAUDE.md lives here)
10
+ * /tmp/agents/{slug}/sessions/{key}/ ← per-thread cwd
11
+ * CLAUDE.md ← copy of agent CLAUDE.md (skills)
12
+ * .claude/memory/ ← per-thread memory files
13
+ *
14
+ * @module runner/claude-handler
15
+ */
16
+
17
+ import * as fs from 'fs';
18
+ import * as fsp from 'fs/promises';
19
+ import * as path from 'path';
20
+ import * as crypto from 'crypto';
21
+ import { query, type SDKMessage, type SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
22
+ import type { ContentBlockParam } from '@anthropic-ai/sdk/resources';
23
+ import type { Agent, McpServer, McpServerConfig, McpServerType, McpStdioConfig, Permission } from '@slackhive/shared';
24
+ import {
25
+ getSession,
26
+ upsertSession,
27
+ cleanupStaleSessions,
28
+ } from './db';
29
+ import { agentLogger } from './logger';
30
+ import { McpProcessManager } from './mcp-process-manager.js';
31
+ import type { Logger } from 'winston';
32
+
33
+ const SESSION_MAX_AGE_MS = 30 * 60 * 1_000;
34
+ const SESSION_CLEANUP_INTERVAL_MS = 10 * 60 * 1_000;
35
+
36
+ export class ClaudeHandler {
37
+ private readonly agent: Agent;
38
+ private readonly mcpServers: McpServer[];
39
+ private readonly permissions: Permission | null;
40
+ private readonly workDir: string;
41
+ private readonly sessionsDir: string;
42
+ private readonly log: Logger;
43
+ private readonly envVarValues: Record<string, string>;
44
+ private readonly mcpManager: McpProcessManager;
45
+
46
+ /** In-memory cache: sessionKey → Claude session ID */
47
+ private sessionCache: Map<string, string> = new Map();
48
+
49
+ private cleanupTimer: NodeJS.Timeout | null = null;
50
+
51
+ /**
52
+ * @param {Agent} agent - The agent configuration record.
53
+ * @param {McpServer[]} mcpServers - MCP servers assigned to this agent.
54
+ * @param {Permission | null} permissions - Tool allow/deny lists, or null for defaults.
55
+ * @param {string} workDir - Root working directory for this agent (e.g. `/tmp/agents/{slug}`).
56
+ * @param {Record<string, string>} envVarValues - Platform env vars for resolving MCP envRefs.
57
+ */
58
+ constructor(
59
+ agent: Agent,
60
+ mcpServers: McpServer[],
61
+ permissions: Permission | null,
62
+ workDir: string,
63
+ envVarValues: Record<string, string> = {}
64
+ ) {
65
+ this.agent = agent;
66
+ this.mcpServers = mcpServers;
67
+ this.permissions = permissions;
68
+ this.workDir = workDir;
69
+ this.sessionsDir = path.join(workDir, 'sessions');
70
+ this.log = agentLogger(agent.slug);
71
+ this.envVarValues = envVarValues;
72
+ // Allocate a stable port range per agent (14000 + slot * 50)
73
+ const slugHash = agent.slug.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0);
74
+ const basePort = 14000 + (slugHash % 200) * 50;
75
+ this.mcpManager = new McpProcessManager(agent.slug, workDir, basePort);
76
+ }
77
+
78
+ /**
79
+ * Sets up the sessions directory and starts the periodic stale-session cleanup timer.
80
+ * Must be called once before any `streamQuery` calls.
81
+ *
82
+ * @returns {void}
83
+ */
84
+ initialize(): void {
85
+ fs.mkdirSync(this.sessionsDir, { recursive: true });
86
+ this.cleanupTimer = setInterval(() => this.runSessionCleanup(), SESSION_CLEANUP_INTERVAL_MS);
87
+ this.log.debug('ClaudeHandler initialized', { workDir: this.workDir, sessionsDir: this.sessionsDir });
88
+ // Start persistent MCP proxies for all stdio servers
89
+ this.startMcpProxies().catch((err) =>
90
+ this.log.error('Failed to start MCP proxies', { error: (err as Error).message })
91
+ );
92
+ }
93
+
94
+ private async startMcpProxies(): Promise<void> {
95
+ const stdioServers = this.mcpServers.filter(
96
+ (s) => s.type === 'stdio' || (!('url' in (s.config as object)) && ('command' in (s.config as object)))
97
+ );
98
+ await Promise.all(
99
+ stdioServers.map((s) =>
100
+ this.mcpManager
101
+ .startServer(s.name, s.config as McpStdioConfig, this.envVarValues)
102
+ .catch((err) => this.log.error('MCP proxy start failed', { server: s.name, error: (err as Error).message }))
103
+ )
104
+ );
105
+ this.log.info('Agent started', {
106
+ mcpServers: this.mcpServers.map((s) => s.name),
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Stops the cleanup timer and clears the in-memory session cache.
112
+ * Called when the agent is stopped or reloaded.
113
+ *
114
+ * @returns {void}
115
+ */
116
+ destroy(): void {
117
+ if (this.cleanupTimer) {
118
+ clearInterval(this.cleanupTimer);
119
+ this.cleanupTimer = null;
120
+ }
121
+ this.sessionCache.clear();
122
+ this.mcpManager.stopAll().catch(() => {});
123
+ }
124
+
125
+ /**
126
+ * Derives a deterministic session key from Slack identifiers.
127
+ * The key is used as both a DB lookup key and a working-directory name.
128
+ *
129
+ * @param {string} userId - Slack user ID (e.g. `U12345678`).
130
+ * @param {string} channelId - Slack channel or DM ID.
131
+ * @param {string} [threadTs] - Thread timestamp; omit for top-level DMs.
132
+ * @returns {string} Composite key: `{userId}-{channelId}-{threadTs|'direct'}`.
133
+ */
134
+ getSessionKey(userId: string, channelId: string, threadTs?: string): string {
135
+ return `${userId}-${channelId}-${threadTs ?? 'direct'}`;
136
+ }
137
+
138
+ /**
139
+ * Returns the isolated working directory for a session.
140
+ * Creates it on first access and copies CLAUDE.md from the agent root.
141
+ */
142
+ private getSessionWorkDir(sessionKey: string): string {
143
+ // Sanitize key for use as a directory name
144
+ const safeName = sessionKey.replace(/[^a-zA-Z0-9_-]/g, '_');
145
+ const sessionDir = path.join(this.sessionsDir, safeName);
146
+
147
+ if (!fs.existsSync(sessionDir)) {
148
+ fs.mkdirSync(sessionDir, { recursive: true });
149
+
150
+ // Copy CLAUDE.md into the session dir so the SDK reads it as project instructions
151
+ const agentClaudeMd = path.join(this.workDir, 'CLAUDE.md');
152
+ if (fs.existsSync(agentClaudeMd)) {
153
+ fs.copyFileSync(agentClaudeMd, path.join(sessionDir, 'CLAUDE.md'));
154
+ }
155
+
156
+ // Copy .claude/commands/ (skill slash commands) into the session dir
157
+ const agentCommandsDir = path.join(this.workDir, '.claude', 'commands');
158
+ if (fs.existsSync(agentCommandsDir)) {
159
+ const sessionCommandsDir = path.join(sessionDir, '.claude', 'commands');
160
+ fs.mkdirSync(sessionCommandsDir, { recursive: true });
161
+ for (const file of fs.readdirSync(agentCommandsDir)) {
162
+ fs.copyFileSync(
163
+ path.join(agentCommandsDir, file),
164
+ path.join(sessionCommandsDir, file)
165
+ );
166
+ }
167
+ }
168
+
169
+ // Create memory dir for per-thread memory files (outside .claude/ to avoid SDK sensitive-file blocking)
170
+ fs.mkdirSync(path.join(sessionDir, 'memory'), { recursive: true });
171
+ this.log.debug('Session work dir created', { sessionKey, sessionDir });
172
+ }
173
+
174
+ return sessionDir;
175
+ }
176
+
177
+ /**
178
+ * Streams a Claude Code SDK query for the given session and yields raw SDK messages.
179
+ *
180
+ * Session resumption: If a Claude session ID exists for this `sessionKey` (in-memory
181
+ * cache or persisted in DB), the query resumes that conversation. On a stale-session
182
+ * error the handler transparently retries once as a fresh session.
183
+ *
184
+ * Callers should check `abortController.signal.aborted` between yields and break early
185
+ * if the user has sent a new message (the Slack handler cancels in-flight requests this way).
186
+ *
187
+ * @param {string} prompt - The user message to send to Claude.
188
+ * @param {string} sessionKey - Session key from {@link getSessionKey}.
189
+ * @param {AbortController} [abortController] - Optional controller to cancel the stream.
190
+ * @yields {SDKMessage} Raw messages from the Claude Code SDK.
191
+ * @throws {Error} On unrecoverable SDK errors (re-thrown after logging).
192
+ */
193
+ async *streamQuery(
194
+ prompt: string | ContentBlockParam[],
195
+ sessionKey: string,
196
+ abortController?: AbortController
197
+ ): AsyncGenerator<SDKMessage, void, unknown> {
198
+ // Compute current MCP hash to detect config changes
199
+ const currentMcpHash = crypto
200
+ .createHash('sha1')
201
+ .update(JSON.stringify(this.mcpServers.map((s) => ({ name: s.name, config: s.config }))))
202
+ .digest('hex');
203
+
204
+ // Resolve existing Claude session ID — invalidate if MCPs changed
205
+ let claudeSessionId = this.sessionCache.get(sessionKey);
206
+ if (!claudeSessionId) {
207
+ const persisted = await getSession(this.agent.id, sessionKey);
208
+ if (persisted?.claudeSessionId) {
209
+ if (persisted.mcpHash && persisted.mcpHash !== currentMcpHash) {
210
+ this.log.info('MCP config changed, starting fresh session', { sessionKey });
211
+ } else {
212
+ claudeSessionId = persisted.claudeSessionId;
213
+ this.sessionCache.set(sessionKey, claudeSessionId);
214
+ }
215
+ }
216
+ }
217
+
218
+ const sessionWorkDir = this.getSessionWorkDir(sessionKey);
219
+ const options = this.buildSdkOptions(sessionWorkDir, claudeSessionId, abortController);
220
+
221
+ this.log.debug('Streaming query', {
222
+ sessionKey,
223
+ resume: claudeSessionId ? 'yes' : 'new',
224
+ cwd: sessionWorkDir,
225
+ mcpServers: this.mcpServers.map((m) => m.name),
226
+ });
227
+
228
+ let newSessionId: string | undefined;
229
+
230
+ // Wrap array content into an AsyncIterable<SDKUserMessage> for multimodal prompts
231
+ const sdkPrompt: string | AsyncIterable<SDKUserMessage> = Array.isArray(prompt)
232
+ ? (async function* () {
233
+ yield {
234
+ type: 'user' as const,
235
+ message: { role: 'user' as const, content: prompt },
236
+ parent_tool_use_id: null,
237
+ };
238
+ })()
239
+ : prompt;
240
+
241
+ // Stream directly for real-time progressive updates.
242
+ // If the session is stale, we catch the error before any messages are yielded
243
+ // and transparently retry as a fresh session.
244
+ const stream = async function* (opts: Record<string, unknown>): AsyncGenerator<SDKMessage> {
245
+ yield* query({ prompt: sdkPrompt, options: opts });
246
+ };
247
+
248
+ let activeOptions = options;
249
+ let retried = false;
250
+
251
+ outer: while (true) {
252
+ try {
253
+ for await (const message of stream(activeOptions)) {
254
+ if (message.type === 'system' && (message as any).subtype === 'init') {
255
+ newSessionId = (message as any).session_id;
256
+ if (newSessionId) {
257
+ this.sessionCache.set(sessionKey, newSessionId);
258
+ await upsertSession(this.agent.id, sessionKey, newSessionId, currentMcpHash);
259
+ this.log.debug('Session created', { sessionKey, sessionId: newSessionId, cwd: sessionWorkDir });
260
+ }
261
+ }
262
+ yield message;
263
+ }
264
+ break; // completed successfully
265
+ } catch (err) {
266
+ const errMsg = err instanceof Error ? err.message : String(err);
267
+ // Retry once on stale session — only if we haven't already retried
268
+ if (!retried && claudeSessionId && (errMsg.includes('No conversation found') || errMsg.includes('session') || errMsg.includes('exited with code 1'))) {
269
+ this.log.warn('Stale session, retrying as new', { sessionKey, staleSessionId: claudeSessionId });
270
+ this.sessionCache.delete(sessionKey);
271
+ claudeSessionId = undefined;
272
+ newSessionId = undefined;
273
+ const freshOptions = { ...activeOptions };
274
+ delete freshOptions.resume;
275
+ activeOptions = freshOptions;
276
+ retried = true;
277
+ continue outer;
278
+ }
279
+ this.log.error('Claude query failed', { sessionKey, error: errMsg });
280
+ throw err;
281
+ }
282
+ }
283
+
284
+ await upsertSession(this.agent.id, sessionKey, newSessionId ?? claudeSessionId, currentMcpHash);
285
+ }
286
+
287
+ /**
288
+ * Resolves a raw MCP server config from the DB into one ready for the SDK:
289
+ * - Merges envRefs (references to platform env vars) into the env object
290
+ * - For inline TypeScript MCPs (tsSource): writes the source to disk and
291
+ * rewrites config to use `tsx <scriptPath>`
292
+ *
293
+ * @param {string} serverName - MCP server name, used for the script filename.
294
+ * @param {McpServerConfig} config - Raw config from the DB.
295
+ * @returns {McpServerConfig} Resolved config safe to pass to the SDK.
296
+ */
297
+ private resolveServerConfig(serverName: string, config: McpServerConfig, serverType: McpServerType): McpServerConfig {
298
+ const c = config as McpStdioConfig & Record<string, unknown>;
299
+
300
+ if (c.tsSource) {
301
+ const scriptDir = path.join(this.workDir, '.mcp-scripts');
302
+ const scriptPath = path.join(scriptDir, `${serverName}.ts`);
303
+ fs.mkdirSync(scriptDir, { recursive: true });
304
+ fs.writeFileSync(scriptPath, c.tsSource as string, 'utf8');
305
+ const resolvedEnv = this.resolveEnvRefs(c);
306
+ return {
307
+ command: '/app/node_modules/.bin/tsx',
308
+ args: [scriptPath],
309
+ env: { NODE_PATH: '/app/node_modules', ...resolvedEnv },
310
+ } as McpServerConfig;
311
+ }
312
+
313
+ if (c.envRefs && Object.keys(c.envRefs as object).length > 0) {
314
+ const resolvedEnv = this.resolveEnvRefs(c);
315
+ const { envRefs: _, tsSource: __, ...rest } = c;
316
+ const resolved: Record<string, unknown> = { ...rest };
317
+
318
+ // Inject type so the SDK can distinguish HTTP/SSE from stdio
319
+ if (serverType === 'http' || serverType === 'sse') resolved.type = serverType;
320
+
321
+ // For HTTP/SSE servers, resolve envRefs into headers
322
+ if (resolved.headers && typeof resolved.headers === 'object') {
323
+ const resolvedHeaders: Record<string, string> = {};
324
+ for (const [key, value] of Object.entries(resolved.headers as Record<string, string>)) {
325
+ const envKey = (c.envRefs as Record<string, string>)[key];
326
+ if (envKey && this.envVarValues[envKey]) {
327
+ resolvedHeaders[key] = value ? `${value}${this.envVarValues[envKey]}` : this.envVarValues[envKey];
328
+ } else {
329
+ resolvedHeaders[key] = value;
330
+ }
331
+ }
332
+ resolved.headers = resolvedHeaders;
333
+ }
334
+
335
+ if (Object.keys(resolvedEnv).length > 0) resolved.env = resolvedEnv;
336
+ return resolved as unknown as McpServerConfig;
337
+ }
338
+
339
+ // Passthrough — inject type for HTTP/SSE so SDK recognises the transport
340
+ if (serverType === 'http' || serverType === 'sse') {
341
+ return { ...config, type: serverType } as unknown as McpServerConfig;
342
+ }
343
+
344
+ return config;
345
+ }
346
+
347
+ /**
348
+ * Merges inline env with resolved envRefs into a single env object.
349
+ */
350
+ private resolveEnvRefs(c: McpStdioConfig & Record<string, unknown>): Record<string, string> {
351
+ const merged: Record<string, string> = { ...(c.env ?? {}) };
352
+ const refs = (c.envRefs ?? {}) as Record<string, string>;
353
+ for (const [subKey, storeKey] of Object.entries(refs)) {
354
+ const val = this.envVarValues[storeKey];
355
+ if (val !== undefined) {
356
+ merged[subKey] = val;
357
+ } else {
358
+ this.log.warn('MCP envRef not found in env vars store', { serverName: 'unknown', storeKey, subKey });
359
+ }
360
+ }
361
+ return merged;
362
+ }
363
+
364
+ /**
365
+ * Builds the options object passed to the Claude Code SDK `query()` call.
366
+ *
367
+ * Merges agent permissions (allowed/denied tools) with MCP server prefixes
368
+ * so that only explicitly permitted tools are available to the agent.
369
+ * Default allowed tool is `['Read']` when no permissions are configured.
370
+ *
371
+ * @param {string} sessionWorkDir - Per-session working directory path.
372
+ * @param {string | undefined} claudeSessionId - Existing session ID to resume, or undefined for a new session.
373
+ * @param {AbortController} [abortController] - Optional abort controller injected into SDK options.
374
+ * @returns {Record<string, unknown>} Options object for `query({ prompt, options })`.
375
+ */
376
+ private buildSdkOptions(
377
+ sessionWorkDir: string,
378
+ claudeSessionId: string | undefined,
379
+ abortController?: AbortController
380
+ ): Record<string, unknown> {
381
+ const options: Record<string, unknown> = {
382
+ permissionMode: 'acceptEdits',
383
+ settingSources: ['project'],
384
+ cwd: sessionWorkDir,
385
+ abortController: abortController ?? new AbortController(),
386
+ };
387
+
388
+ const rawAllowed: string[] = this.permissions?.allowedTools?.length
389
+ ? this.permissions.allowedTools
390
+ : [];
391
+ const denied: string[] = this.permissions?.deniedTools ?? [];
392
+ const mcpToolPrefixes = this.mcpServers.map((s) => `mcp__${s.name}`);
393
+
394
+ // Read and Write are always available — cannot be overridden.
395
+ const alwaysAllowed = ['Read', 'Write'];
396
+
397
+ // Separate Bash(pattern) rules from plain tool names.
398
+ // e.g. "Bash(git *)" is a permission rule, "Bash" is a plain tool name.
399
+ const bashRules = rawAllowed.filter((t) => t.startsWith('Bash('));
400
+ const plainTools = rawAllowed.filter((t) => !t.startsWith('Bash('));
401
+
402
+ // If there are Bash rules, add plain "Bash" to the tool list so the model can see/use it,
403
+ // but scope auto-execution via options.permissions.allow patterns.
404
+ const hasBashRules = bashRules.length > 0;
405
+ const baseTools = hasBashRules ? [...plainTools, 'Bash'] : plainTools;
406
+
407
+ const availableTools = [...new Set([...alwaysAllowed, ...baseTools, ...mcpToolPrefixes])].filter(
408
+ (tool) => !denied.includes(tool)
409
+ );
410
+
411
+ // `tools` controls which tools the model can see/use.
412
+ // `allowedTools` controls auto-execution (no prompting). Both use plain tool names.
413
+ options.tools = availableTools;
414
+ options.allowedTools = availableTools;
415
+
416
+ // Bash(pattern) rules go into settings.permissions.allow — this is the only place
417
+ // the SDK supports command-level scoping (not in allowedTools/tools).
418
+ if (hasBashRules) {
419
+ const agentNamespace = `/tmp/agents/${this.agent.slug}`;
420
+ // Block access to /tmp/agents/ broadly, then allow back in via the allow list
421
+ // which is scoped to this agent's namespace only.
422
+ const bashDeny = [
423
+ ...denied,
424
+ 'Bash(rm *)', 'Bash(curl *)', 'Bash(wget *)', 'Bash(chmod *)', 'Bash(sudo *)', 'Bash(kill *)',
425
+ // Block access to ALL agent namespaces — the allow list re-opens only this agent's own
426
+ `Bash(cat /tmp/agents/*)`, `Bash(ls /tmp/agents/*)`,
427
+ `Bash(find /tmp/agents/*)`, `Bash(grep * /tmp/agents/*)`,
428
+ `Bash(python3 /tmp/agents/*)`, `Bash(python3 -m pytest /tmp/agents/*)`,
429
+ `Bash(pytest /tmp/agents/*)`, `Bash(git clone * /tmp/agents/*)`,
430
+ `Bash(git -C /tmp/agents/*)`, `Bash(git log /tmp/agents/*)`,
431
+ `Bash(/graphify /tmp/agents/*)`, `Bash(graphify /tmp/agents/*)`
432
+ ];
433
+ // Re-allow this agent's own namespace — substitute {agent} placeholder with actual slug.
434
+ const bashAllow = bashRules.map(r => r.replace(/\{agent\}/g, this.agent.slug));
435
+ options.settings = {
436
+ permissions: {
437
+ allow: bashAllow,
438
+ deny: bashDeny,
439
+ },
440
+ };
441
+ }
442
+
443
+ if (this.mcpServers.length > 0) {
444
+ options.mcpServers = Object.fromEntries(
445
+ this.mcpServers.map((server) => [server.name, this.resolveServerConfig(server.name, server.config, server.type)])
446
+ );
447
+ this.log.debug('MCP servers configured', { servers: this.mcpServers.map((s) => s.name) });
448
+ }
449
+
450
+ if (claudeSessionId) {
451
+ options.resume = claudeSessionId;
452
+ }
453
+
454
+ return options;
455
+ }
456
+
457
+ /**
458
+ * Deletes sessions inactive longer than SESSION_MAX_AGE_MS from the DB
459
+ * and clears the in-memory cache so stale IDs are not accidentally reused.
460
+ * Runs on an interval set in {@link initialize}; failures are logged and swallowed.
461
+ *
462
+ * @returns {Promise<void>}
463
+ */
464
+ private async runSessionCleanup(): Promise<void> {
465
+ try {
466
+ const deleted = await cleanupStaleSessions(this.agent.id, SESSION_MAX_AGE_MS);
467
+ if (deleted > 0) {
468
+ this.log.info('Cleaned up stale sessions', { count: deleted });
469
+ this.sessionCache.clear();
470
+ }
471
+ } catch (error) {
472
+ this.log.warn('Session cleanup failed', { error });
473
+ }
474
+ }
475
+ }