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.
- package/.dockerignore +14 -0
- package/.env.example +44 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +65 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +38 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +27 -0
- package/.github/dependabot.yml +20 -0
- package/.github/workflows/audit.yml +149 -0
- package/.github/workflows/ci.yml +135 -0
- package/CHANGELOG.md +52 -0
- package/CODE_OF_CONDUCT.md +37 -0
- package/CONTRIBUTING.md +204 -0
- package/LICENSE +21 -0
- package/README.md +19 -0
- package/SECURITY.md +47 -0
- package/apps/runner/Dockerfile +33 -0
- package/apps/runner/dist/__tests__/channel-restrictions.test.d.ts +8 -0
- package/apps/runner/dist/__tests__/channel-restrictions.test.js +63 -0
- package/apps/runner/dist/__tests__/channel-restrictions.test.js.map +1 -0
- package/apps/runner/dist/__tests__/claude-handler-resolve.test.d.ts +20 -0
- package/apps/runner/dist/__tests__/claude-handler-resolve.test.js +178 -0
- package/apps/runner/dist/__tests__/claude-handler-resolve.test.js.map +1 -0
- package/apps/runner/dist/__tests__/compile-claude-md.test.d.ts +13 -0
- package/apps/runner/dist/__tests__/compile-claude-md.test.js +144 -0
- package/apps/runner/dist/__tests__/compile-claude-md.test.js.map +1 -0
- package/apps/runner/dist/__tests__/memory-sync.test.d.ts +11 -0
- package/apps/runner/dist/__tests__/memory-sync.test.js +56 -0
- package/apps/runner/dist/__tests__/memory-sync.test.js.map +1 -0
- package/apps/runner/dist/__tests__/slack-file-support.test.d.ts +9 -0
- package/apps/runner/dist/__tests__/slack-file-support.test.js +271 -0
- package/apps/runner/dist/__tests__/slack-file-support.test.js.map +1 -0
- package/apps/runner/dist/__tests__/slack-formatting.test.d.ts +12 -0
- package/apps/runner/dist/__tests__/slack-formatting.test.js +400 -0
- package/apps/runner/dist/__tests__/slack-formatting.test.js.map +1 -0
- package/apps/runner/dist/__tests__/thread-context.test.d.ts +12 -0
- package/apps/runner/dist/__tests__/thread-context.test.js +182 -0
- package/apps/runner/dist/__tests__/thread-context.test.js.map +1 -0
- package/apps/runner/dist/agent-runner.d.ts +118 -0
- package/apps/runner/dist/agent-runner.js +352 -0
- package/apps/runner/dist/agent-runner.js.map +1 -0
- package/apps/runner/dist/claude-handler.d.ts +122 -0
- package/apps/runner/dist/claude-handler.js +402 -0
- package/apps/runner/dist/claude-handler.js.map +1 -0
- package/apps/runner/dist/compile-claude-md.d.ts +59 -0
- package/apps/runner/dist/compile-claude-md.js +291 -0
- package/apps/runner/dist/compile-claude-md.js.map +1 -0
- package/apps/runner/dist/correction-handler.d.ts +46 -0
- package/apps/runner/dist/correction-handler.js +162 -0
- package/apps/runner/dist/correction-handler.js.map +1 -0
- package/apps/runner/dist/correction-manager.d.ts +53 -0
- package/apps/runner/dist/correction-manager.js +241 -0
- package/apps/runner/dist/correction-manager.js.map +1 -0
- package/apps/runner/dist/db.d.ts +193 -0
- package/apps/runner/dist/db.js +492 -0
- package/apps/runner/dist/db.js.map +1 -0
- package/apps/runner/dist/index.d.ts +9 -0
- package/apps/runner/dist/index.js +43 -0
- package/apps/runner/dist/index.js.map +1 -0
- package/apps/runner/dist/job-scheduler.d.ts +57 -0
- package/apps/runner/dist/job-scheduler.js +150 -0
- package/apps/runner/dist/job-scheduler.js.map +1 -0
- package/apps/runner/dist/logger.d.ts +32 -0
- package/apps/runner/dist/logger.js +52 -0
- package/apps/runner/dist/logger.js.map +1 -0
- package/apps/runner/dist/mcp-process-manager.d.ts +38 -0
- package/apps/runner/dist/mcp-process-manager.js +189 -0
- package/apps/runner/dist/mcp-process-manager.js.map +1 -0
- package/apps/runner/dist/memory-mcp.d.ts +14 -0
- package/apps/runner/dist/memory-mcp.js +88 -0
- package/apps/runner/dist/memory-mcp.js.map +1 -0
- package/apps/runner/dist/memory-watcher.d.ts +78 -0
- package/apps/runner/dist/memory-watcher.js +220 -0
- package/apps/runner/dist/memory-watcher.js.map +1 -0
- package/apps/runner/dist/slack-handler.d.ts +120 -0
- package/apps/runner/dist/slack-handler.js +843 -0
- package/apps/runner/dist/slack-handler.js.map +1 -0
- package/apps/runner/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/apps/runner/package.json +42 -0
- package/apps/runner/src/__tests__/channel-restrictions.test.ts +75 -0
- package/apps/runner/src/__tests__/claude-handler-resolve.test.ts +160 -0
- package/apps/runner/src/__tests__/compile-claude-md.test.ts +139 -0
- package/apps/runner/src/__tests__/memory-sync.test.ts +59 -0
- package/apps/runner/src/__tests__/slack-file-support.test.ts +376 -0
- package/apps/runner/src/__tests__/slack-formatting.test.ts +495 -0
- package/apps/runner/src/__tests__/thread-context.test.ts +215 -0
- package/apps/runner/src/agent-runner.ts +397 -0
- package/apps/runner/src/claude-handler.ts +475 -0
- package/apps/runner/src/compile-claude-md.ts +283 -0
- package/apps/runner/src/correction-handler.ts +191 -0
- package/apps/runner/src/correction-manager.ts +285 -0
- package/apps/runner/src/db.ts +604 -0
- package/apps/runner/src/index.ts +46 -0
- package/apps/runner/src/job-scheduler.ts +165 -0
- package/apps/runner/src/logger.ts +49 -0
- package/apps/runner/src/mcp-process-manager.ts +195 -0
- package/apps/runner/src/memory-mcp.ts +85 -0
- package/apps/runner/src/memory-watcher.ts +215 -0
- package/apps/runner/src/slack-handler.ts +929 -0
- package/apps/runner/tsconfig.json +17 -0
- package/apps/runner/vitest.config.mts +17 -0
- package/apps/web/.eslintrc.json +3 -0
- package/apps/web/.next/app-build-manifest.json +323 -0
- package/apps/web/.next/app-path-routes-manifest.json +46 -0
- package/apps/web/.next/build-manifest.json +33 -0
- package/apps/web/.next/cache/.previewinfo +1 -0
- package/apps/web/.next/cache/.rscinfo +1 -0
- package/apps/web/.next/cache/webpack/client-production/0.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/1.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/2.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/3.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/4.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/index.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/apps/web/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/apps/web/.next/cache/webpack/edge-server-production/1.pack +0 -0
- package/apps/web/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/apps/web/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
- package/apps/web/.next/cache/webpack/server-production/0.pack +0 -0
- package/apps/web/.next/cache/webpack/server-production/1.pack +0 -0
- package/apps/web/.next/cache/webpack/server-production/2.pack +0 -0
- package/apps/web/.next/cache/webpack/server-production/index.pack +0 -0
- package/apps/web/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/apps/web/.next/diagnostics/build-diagnostics.json +6 -0
- package/apps/web/.next/diagnostics/framework.json +1 -0
- package/apps/web/.next/package.json +1 -0
- package/apps/web/.next/react-loadable-manifest.json +1 -0
- package/apps/web/.next/server/app/_not-found/page.js +2 -0
- package/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/agents/[slug]/page.js +4 -0
- package/apps/web/.next/server/app/agents/[slug]/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/agents/[slug]/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/agents/new/page.js +2 -0
- package/apps/web/.next/server/app/agents/new/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/agents/new/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/access/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/access/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/access/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js +6 -0
- package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/claude-md/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/logs/route.js +3 -0
- package/apps/web/.next/server/app/api/agents/[id]/logs/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/logs/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/manifest/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/mcps/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/permissions/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/reload/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/reload/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/reload/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/restrictions/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/route.js +33 -0
- package/apps/web/.next/server/app/api/agents/[id]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/slack-info/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/start/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/start/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/start/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/stop/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/stop/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/stop/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/route.js +91 -0
- package/apps/web/.next/server/app/api/agents/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/login/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/login/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/logout/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/logout/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/me/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/me/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/users/[id]/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/users/[id]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/users/[id]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/users/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/users/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/users/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/env-vars/[key]/route.js +1 -0
- package/apps/web/.next/server/app/api/env-vars/[key]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/env-vars/[key]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/env-vars/route.js +1 -0
- package/apps/web/.next/server/app/api/env-vars/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/env-vars/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/route.js +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/runs/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/jobs/route.js +1 -0
- package/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/jobs/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/route.js +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/test/route.js +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/test/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/test/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/mcps/route.js +1 -0
- package/apps/web/.next/server/app/api/mcps/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/mcps/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/settings/route.js +1 -0
- package/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/settings/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/icon.svg/route.js +1 -0
- package/apps/web/.next/server/app/icon.svg/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/jobs/page.js +2 -0
- package/apps/web/.next/server/app/jobs/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/jobs/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/login/page.js +2 -0
- package/apps/web/.next/server/app/login/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/login/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/page.js +2 -0
- package/apps/web/.next/server/app/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/settings/env-vars/page.js +2 -0
- package/apps/web/.next/server/app/settings/env-vars/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/settings/env-vars/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/settings/mcps/page.js +2 -0
- package/apps/web/.next/server/app/settings/mcps/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/settings/mcps/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/settings/page.js +2 -0
- package/apps/web/.next/server/app/settings/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app-paths-manifest.json +46 -0
- package/apps/web/.next/server/chunks/1157.js +9 -0
- package/apps/web/.next/server/chunks/2287.js +1 -0
- package/apps/web/.next/server/chunks/3444.js +1 -0
- package/apps/web/.next/server/chunks/383.js +6 -0
- package/apps/web/.next/server/chunks/4012.js +58 -0
- package/apps/web/.next/server/chunks/6791.js +1 -0
- package/apps/web/.next/server/chunks/7171.js +1 -0
- package/apps/web/.next/server/chunks/8819.js +22 -0
- package/apps/web/.next/server/edge-runtime-webpack.js +2 -0
- package/apps/web/.next/server/edge-runtime-webpack.js.map +1 -0
- package/apps/web/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/apps/web/.next/server/middleware-build-manifest.js +1 -0
- package/apps/web/.next/server/middleware-manifest.json +32 -0
- package/apps/web/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/apps/web/.next/server/next-font-manifest.js +1 -0
- package/apps/web/.next/server/next-font-manifest.json +1 -0
- package/apps/web/.next/server/pages/_app.js +1 -0
- package/apps/web/.next/server/pages/_app.js.nft.json +1 -0
- package/apps/web/.next/server/pages/_document.js +1 -0
- package/apps/web/.next/server/pages/_document.js.nft.json +1 -0
- package/apps/web/.next/server/pages/_error.js +19 -0
- package/apps/web/.next/server/pages/_error.js.nft.json +1 -0
- package/apps/web/.next/server/pages-manifest.json +5 -0
- package/apps/web/.next/server/server-reference-manifest.js +1 -0
- package/apps/web/.next/server/server-reference-manifest.json +1 -0
- package/apps/web/.next/server/src/middleware.js +14 -0
- package/apps/web/.next/server/src/middleware.js.map +1 -0
- package/apps/web/.next/server/webpack-runtime.js +1 -0
- package/apps/web/.next/static/chunks/18-90b700ea37b686a2.js +1 -0
- package/apps/web/.next/static/chunks/87c73c54-24122e7b92478d00.js +1 -0
- package/apps/web/.next/static/chunks/9664-af80478aa73ba424.js +1 -0
- package/apps/web/.next/static/chunks/app/_not-found/page-b9cee17ed89ca24a.js +1 -0
- package/apps/web/.next/static/chunks/app/agents/[slug]/page-18369fc3fe1a9a7b.js +1 -0
- package/apps/web/.next/static/chunks/app/agents/new/page-bf11cf8901c7e2cd.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/access/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/claude-md/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/logs/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/manifest/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/mcps/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/[memId]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/permissions/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/reload/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/restrictions/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/[skillId]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/slack-info/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/restore/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/start/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/stop/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/login/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/logout/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/me/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/users/[id]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/users/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/env-vars/[key]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/env-vars/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/jobs/[id]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/jobs/[id]/runs/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/jobs/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/mcps/[id]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/mcps/[id]/test/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/mcps/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/settings/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/jobs/page-f5aa89a47c50efd8.js +1 -0
- package/apps/web/.next/static/chunks/app/layout-2079f4964aa7314e.js +1 -0
- package/apps/web/.next/static/chunks/app/login/layout-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/login/page-aa259283dc38e8f9.js +1 -0
- package/apps/web/.next/static/chunks/app/page-e83437b608104dff.js +1 -0
- package/apps/web/.next/static/chunks/app/settings/env-vars/page-06479dbdfb78b76b.js +1 -0
- package/apps/web/.next/static/chunks/app/settings/mcps/page-75650686ed6490c7.js +1 -0
- package/apps/web/.next/static/chunks/app/settings/page-e1e62fc41ff6cddd.js +1 -0
- package/apps/web/.next/static/chunks/framework-811407f832a33072.js +1 -0
- package/apps/web/.next/static/chunks/main-3f1cddbdd67b1546.js +1 -0
- package/apps/web/.next/static/chunks/main-app-cebd8a6a5ccbf72d.js +1 -0
- package/apps/web/.next/static/chunks/pages/_app-50fa07b56b2d29ac.js +1 -0
- package/apps/web/.next/static/chunks/pages/_error-fed8688bdd23f211.js +1 -0
- package/apps/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/apps/web/.next/static/chunks/webpack-6c05566dba553c97.js +1 -0
- package/apps/web/.next/static/css/15371687405525e2.css +5 -0
- package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_buildManifest.js +1 -0
- package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_ssgManifest.js +1 -0
- package/apps/web/.next/trace +5 -0
- package/apps/web/.next/types/app/agents/[slug]/page.ts +84 -0
- package/apps/web/.next/types/app/agents/new/page.ts +84 -0
- package/apps/web/.next/types/app/api/agents/[id]/access/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/claude-md/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/logs/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/manifest/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/mcps/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/memories/[memId]/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/memories/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/permissions/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/reload/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/restrictions/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/skills/[skillId]/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/skills/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/slack-info/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/snapshots/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/start/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/stop/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/login/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/logout/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/me/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/users/[id]/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/users/route.ts +347 -0
- package/apps/web/.next/types/app/api/env-vars/[key]/route.ts +347 -0
- package/apps/web/.next/types/app/api/env-vars/route.ts +347 -0
- package/apps/web/.next/types/app/api/jobs/[id]/route.ts +347 -0
- package/apps/web/.next/types/app/api/jobs/[id]/runs/route.ts +347 -0
- package/apps/web/.next/types/app/api/jobs/route.ts +347 -0
- package/apps/web/.next/types/app/api/mcps/[id]/route.ts +347 -0
- package/apps/web/.next/types/app/api/mcps/[id]/test/route.ts +347 -0
- package/apps/web/.next/types/app/api/mcps/route.ts +347 -0
- package/apps/web/.next/types/app/api/settings/route.ts +347 -0
- package/apps/web/.next/types/app/jobs/page.ts +84 -0
- package/apps/web/.next/types/app/login/layout.ts +84 -0
- package/apps/web/.next/types/app/login/page.ts +84 -0
- package/apps/web/.next/types/app/page.ts +84 -0
- package/apps/web/.next/types/app/settings/env-vars/page.ts +84 -0
- package/apps/web/.next/types/app/settings/mcps/page.ts +84 -0
- package/apps/web/.next/types/app/settings/page.ts +84 -0
- package/apps/web/.next/types/cache-life.d.ts +141 -0
- package/apps/web/.next/types/package.json +1 -0
- package/apps/web/.next/types/routes.d.ts +114 -0
- package/apps/web/.next/types/validator.ts +448 -0
- package/apps/web/Dockerfile +37 -0
- package/apps/web/next-env.d.ts +6 -0
- package/apps/web/next.config.js +6 -0
- package/apps/web/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/apps/web/package.json +48 -0
- package/apps/web/postcss.config.js +3 -0
- package/apps/web/public/logo.svg +17 -0
- package/apps/web/src/app/agents/[slug]/page.tsx +2235 -0
- package/apps/web/src/app/agents/new/page.tsx +1161 -0
- package/apps/web/src/app/api/agents/[id]/access/route.ts +76 -0
- package/apps/web/src/app/api/agents/[id]/claude-md/route.ts +111 -0
- package/apps/web/src/app/api/agents/[id]/logs/route.ts +84 -0
- package/apps/web/src/app/api/agents/[id]/manifest/route.ts +32 -0
- package/apps/web/src/app/api/agents/[id]/mcps/route.ts +73 -0
- package/apps/web/src/app/api/agents/[id]/memories/[memId]/route.ts +31 -0
- package/apps/web/src/app/api/agents/[id]/memories/route.ts +56 -0
- package/apps/web/src/app/api/agents/[id]/permissions/route.ts +74 -0
- package/apps/web/src/app/api/agents/[id]/reload/route.ts +33 -0
- package/apps/web/src/app/api/agents/[id]/restrictions/route.ts +85 -0
- package/apps/web/src/app/api/agents/[id]/route.ts +81 -0
- package/apps/web/src/app/api/agents/[id]/skills/[skillId]/route.ts +52 -0
- package/apps/web/src/app/api/agents/[id]/skills/route.ts +80 -0
- package/apps/web/src/app/api/agents/[id]/slack-info/route.ts +38 -0
- package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +61 -0
- package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/route.ts +53 -0
- package/apps/web/src/app/api/agents/[id]/snapshots/route.ts +84 -0
- package/apps/web/src/app/api/agents/[id]/start/route.ts +35 -0
- package/apps/web/src/app/api/agents/[id]/stop/route.ts +35 -0
- package/apps/web/src/app/api/agents/route.ts +99 -0
- package/apps/web/src/app/api/auth/login/route.ts +39 -0
- package/apps/web/src/app/api/auth/logout/route.ts +21 -0
- package/apps/web/src/app/api/auth/me/route.ts +24 -0
- package/apps/web/src/app/api/auth/users/[id]/route.ts +48 -0
- package/apps/web/src/app/api/auth/users/route.ts +63 -0
- package/apps/web/src/app/api/env-vars/[key]/route.ts +66 -0
- package/apps/web/src/app/api/env-vars/route.ts +59 -0
- package/apps/web/src/app/api/jobs/[id]/route.ts +51 -0
- package/apps/web/src/app/api/jobs/[id]/runs/route.ts +24 -0
- package/apps/web/src/app/api/jobs/route.ts +42 -0
- package/apps/web/src/app/api/mcps/[id]/route.ts +60 -0
- package/apps/web/src/app/api/mcps/[id]/test/route.ts +195 -0
- package/apps/web/src/app/api/mcps/route.ts +72 -0
- package/apps/web/src/app/api/settings/route.ts +42 -0
- package/apps/web/src/app/globals.css +124 -0
- package/apps/web/src/app/icon.svg +17 -0
- package/apps/web/src/app/jobs/page.tsx +543 -0
- package/apps/web/src/app/layout-shell.tsx +89 -0
- package/apps/web/src/app/layout.tsx +18 -0
- package/apps/web/src/app/login/layout.tsx +9 -0
- package/apps/web/src/app/login/page.tsx +150 -0
- package/apps/web/src/app/page.tsx +573 -0
- package/apps/web/src/app/settings/env-vars/page.tsx +216 -0
- package/apps/web/src/app/settings/mcps/page.tsx +763 -0
- package/apps/web/src/app/settings/page.tsx +528 -0
- package/apps/web/src/app/sidebar.tsx +345 -0
- package/apps/web/src/lib/__tests__/api-guard.test.ts +189 -0
- package/apps/web/src/lib/__tests__/auth.test.ts +262 -0
- package/apps/web/src/lib/__tests__/boss-registry.test.ts +323 -0
- package/apps/web/src/lib/__tests__/compile.test.ts +161 -0
- package/apps/web/src/lib/__tests__/db-agent-hierarchy.test.ts +136 -0
- package/apps/web/src/lib/__tests__/db-env-vars.test.ts +216 -0
- package/apps/web/src/lib/__tests__/db-restrictions.test.ts +117 -0
- package/apps/web/src/lib/__tests__/db.integration.test.ts +271 -0
- package/apps/web/src/lib/__tests__/diff.test.ts +102 -0
- package/apps/web/src/lib/__tests__/mcp-mask.test.ts +274 -0
- package/apps/web/src/lib/__tests__/skill-templates.test.ts +237 -0
- package/apps/web/src/lib/__tests__/slack-manifest.test.ts +105 -0
- package/apps/web/src/lib/api-guard.ts +68 -0
- package/apps/web/src/lib/auth-context.tsx +71 -0
- package/apps/web/src/lib/auth.ts +128 -0
- package/apps/web/src/lib/boss-registry.ts +90 -0
- package/apps/web/src/lib/compile.ts +51 -0
- package/apps/web/src/lib/db.ts +1196 -0
- package/apps/web/src/lib/diff.ts +43 -0
- package/apps/web/src/lib/mcp-mask.ts +91 -0
- package/apps/web/src/lib/portal.tsx +23 -0
- package/apps/web/src/lib/skill-templates.ts +148 -0
- package/apps/web/src/lib/slack-manifest.ts +85 -0
- package/apps/web/src/middleware.ts +68 -0
- package/apps/web/tailwind.config.js +6 -0
- package/apps/web/tsconfig.json +23 -0
- package/apps/web/vitest.config.mts +21 -0
- package/cli/.claude/settings.local.json +6 -0
- package/cli/README.md +281 -0
- package/cli/node_modules/.package-lock.json +427 -0
- package/cli/node_modules/commander/LICENSE +22 -0
- package/cli/node_modules/commander/Readme.md +1157 -0
- package/cli/node_modules/commander/esm.mjs +16 -0
- package/cli/node_modules/commander/index.js +24 -0
- package/cli/node_modules/commander/lib/argument.js +149 -0
- package/cli/node_modules/commander/lib/command.js +2509 -0
- package/cli/node_modules/commander/lib/error.js +39 -0
- package/cli/node_modules/commander/lib/help.js +520 -0
- package/cli/node_modules/commander/lib/option.js +330 -0
- package/cli/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/cli/node_modules/commander/package-support.json +16 -0
- package/cli/node_modules/commander/package.json +84 -0
- package/cli/node_modules/commander/typings/esm.d.mts +3 -0
- package/cli/node_modules/commander/typings/index.d.ts +969 -0
- package/cli/package-lock.json +449 -0
- package/cli/package.json +44 -0
- package/cli/src/commands/init.ts +514 -0
- package/cli/src/commands/manage.ts +115 -0
- package/cli/src/index.ts +63 -0
- package/cli/tsconfig.json +14 -0
- package/docker-compose.yml +122 -0
- package/docs/agents/boss-agents.mdx +108 -0
- package/docs/agents/creating-agents.mdx +132 -0
- package/docs/agents/memory.mdx +113 -0
- package/docs/agents/tools.mdx +103 -0
- package/docs/configuration/env-vars.mdx +166 -0
- package/docs/configuration/mcp-servers.mdx +203 -0
- package/docs/configuration/slack-app.mdx +175 -0
- package/docs/docs.json +79 -0
- package/docs/favicon.svg +17 -0
- package/docs/features/history.mdx +60 -0
- package/docs/features/import-export.mdx +77 -0
- package/docs/features/logs.mdx +131 -0
- package/docs/features/multi-workspace.mdx +90 -0
- package/docs/features/scheduled-jobs.mdx +231 -0
- package/docs/features/users.mdx +92 -0
- package/docs/introduction.mdx +160 -0
- package/docs/logo/dark.svg +17 -0
- package/docs/logo/light.svg +17 -0
- package/docs/logo/wide-dark.svg +12 -0
- package/docs/logo/wide-light.svg +12 -0
- package/docs/quickstart.mdx +270 -0
- package/docs/self-hosting/docker.mdx +151 -0
- package/docs/self-hosting/production.mdx +176 -0
- package/package.json +20 -36
- package/packages/shared/dist/index.d.ts +8 -0
- package/packages/shared/dist/index.d.ts.map +1 -0
- package/packages/shared/dist/index.js +24 -0
- package/packages/shared/dist/index.js.map +1 -0
- package/packages/shared/dist/types.d.ts +584 -0
- package/packages/shared/dist/types.d.ts.map +1 -0
- package/packages/shared/dist/types.js +39 -0
- package/packages/shared/dist/types.js.map +1 -0
- package/packages/shared/package.json +15 -0
- package/packages/shared/src/db/schema.sql +354 -0
- package/packages/shared/src/index.ts +8 -0
- package/packages/shared/src/types.ts +683 -0
- package/packages/shared/tsconfig.json +17 -0
- package/scripts/dev.sh +45 -0
- /package/{dist → cli/dist}/commands/init.d.ts +0 -0
- /package/{dist → cli/dist}/commands/init.js +0 -0
- /package/{dist → cli/dist}/commands/manage.d.ts +0 -0
- /package/{dist → cli/dist}/commands/manage.js +0 -0
- /package/{dist → cli/dist}/index.d.ts +0 -0
- /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
|
+
}
|