@desplega.ai/agent-swarm 1.20.0 → 1.51.2
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/README.md +271 -169
- package/openapi.json +5015 -0
- package/package.json +40 -7
- package/plugin/commands/close-issue.md +7 -3
- package/plugin/commands/create-pr.md +18 -12
- package/plugin/commands/implement-issue.md +7 -3
- package/plugin/commands/respond-github.md +8 -4
- package/plugin/commands/review-pr.md +44 -10
- package/plugin/commands/start-leader.md +1 -3
- package/plugin/commands/start-worker.md +1 -3
- package/plugin/commands/work-on-task.md +22 -3
- package/plugin/pi-skills/close-issue/SKILL.md +90 -0
- package/plugin/pi-skills/create-pr/SKILL.md +99 -0
- package/plugin/pi-skills/implement-issue/SKILL.md +135 -0
- package/plugin/pi-skills/investigate-sentry-issue/SKILL.md +138 -0
- package/plugin/pi-skills/respond-github/SKILL.md +98 -0
- package/plugin/pi-skills/review-offered-task/SKILL.md +45 -0
- package/plugin/pi-skills/review-pr/SKILL.md +261 -0
- package/plugin/pi-skills/start-leader/SKILL.md +121 -0
- package/plugin/pi-skills/start-worker/SKILL.md +60 -0
- package/plugin/pi-skills/swarm-chat/SKILL.md +82 -0
- package/plugin/pi-skills/todos/SKILL.md +66 -0
- package/plugin/pi-skills/work-on-task/SKILL.md +65 -0
- package/plugin/skills/artifacts/examples/approval-flow.ts +34 -0
- package/plugin/skills/artifacts/examples/hono-dashboard.ts +31 -0
- package/plugin/skills/artifacts/examples/multi-artifact.ts +20 -0
- package/plugin/skills/artifacts/examples/static-report.sh +17 -0
- package/plugin/skills/artifacts/skill.md +71 -0
- package/src/agentmail/app.ts +65 -0
- package/src/agentmail/handlers.ts +262 -0
- package/src/agentmail/index.ts +9 -0
- package/src/agentmail/templates.ts +111 -0
- package/src/agentmail/types.ts +51 -0
- package/src/artifact-sdk/browser-sdk.ts +30 -0
- package/src/artifact-sdk/index.ts +2 -0
- package/src/artifact-sdk/localtunnel.d.ts +20 -0
- package/src/artifact-sdk/port.ts +12 -0
- package/src/artifact-sdk/server.ts +156 -0
- package/src/artifact-sdk/tunnel.ts +19 -0
- package/src/be/chunking.ts +193 -0
- package/src/be/db-queries/oauth.ts +90 -0
- package/src/be/db-queries/tracker.ts +182 -0
- package/src/be/db.ts +3327 -784
- package/src/be/embedding.ts +80 -0
- package/src/be/migrations/001_initial.sql +409 -0
- package/src/be/migrations/002_one_time_schedules.sql +59 -0
- package/src/be/migrations/003_workflows.sql +51 -0
- package/src/be/migrations/004_workflow_source.sql +81 -0
- package/src/be/migrations/005_epic_next_steps.sql +2 -0
- package/src/be/migrations/006_vcs_provider.sql +94 -0
- package/src/be/migrations/007_task_dir.sql +2 -0
- package/src/be/migrations/008_workflow_redesign.sql +85 -0
- package/src/be/migrations/009_tracker_integration.sql +144 -0
- package/src/be/migrations/010_step_diagnostics.sql +1 -0
- package/src/be/migrations/011_step_next_port.sql +1 -0
- package/src/be/migrations/012_trigger_schema.sql +1 -0
- package/src/be/migrations/013_task_output_schema.sql +2 -0
- package/src/be/migrations/014_prompt_templates.sql +33 -0
- package/src/be/migrations/015_workflow_workspace.sql +3 -0
- package/src/be/migrations/016_active_session_runner_session.sql +4 -0
- package/src/be/migrations/017_channel_activity_cursors.sql +6 -0
- package/src/be/migrations/018_fix_seed_double_version.sql +30 -0
- package/src/be/migrations/runner.ts +188 -0
- package/src/be/seed.ts +62 -0
- package/src/cli.tsx +231 -299
- package/src/commands/artifact.ts +241 -0
- package/src/commands/onboard/compose-generator.ts +169 -0
- package/src/commands/onboard/env-generator.ts +79 -0
- package/src/commands/onboard/manifest.ts +37 -0
- package/src/commands/onboard/presets.ts +85 -0
- package/src/commands/onboard/service-names.ts +47 -0
- package/src/commands/onboard/steps/core-credentials.tsx +111 -0
- package/src/commands/onboard/steps/custom-templates.tsx +168 -0
- package/src/commands/onboard/steps/generate.tsx +154 -0
- package/src/commands/onboard/steps/harness-credentials.tsx +195 -0
- package/src/commands/onboard/steps/harness.tsx +21 -0
- package/src/commands/onboard/steps/health-check.tsx +171 -0
- package/src/commands/onboard/steps/integration-github.tsx +105 -0
- package/src/commands/onboard/steps/integration-gitlab.tsx +79 -0
- package/src/commands/onboard/steps/integration-menu.tsx +58 -0
- package/src/commands/onboard/steps/integration-sentry.tsx +79 -0
- package/src/commands/onboard/steps/integration-slack.tsx +165 -0
- package/src/commands/onboard/steps/post-connect.tsx +145 -0
- package/src/commands/onboard/steps/post-dashboard.tsx +34 -0
- package/src/commands/onboard/steps/post-task.tsx +103 -0
- package/src/commands/onboard/steps/prereq-check.tsx +178 -0
- package/src/commands/onboard/steps/review.tsx +82 -0
- package/src/commands/onboard/steps/start.tsx +97 -0
- package/src/commands/onboard/templates.ts +34 -0
- package/src/commands/onboard/types.ts +259 -0
- package/src/commands/onboard.tsx +425 -0
- package/src/commands/runner.ts +1540 -630
- package/src/commands/setup.tsx +23 -38
- package/src/commands/shared/client-config.ts +41 -0
- package/src/commands/templates.ts +172 -0
- package/src/github/app.ts +8 -0
- package/src/github/handlers.ts +384 -151
- package/src/github/index.ts +1 -0
- package/src/github/mentions-aliases.test.ts +73 -0
- package/src/github/mentions.test.ts +3 -3
- package/src/github/mentions.ts +32 -6
- package/src/github/templates.ts +398 -0
- package/src/github/types.ts +1 -0
- package/src/gitlab/auth.ts +63 -0
- package/src/gitlab/handlers.ts +368 -0
- package/src/gitlab/index.ts +19 -0
- package/src/gitlab/reactions.ts +104 -0
- package/src/gitlab/templates.ts +140 -0
- package/src/gitlab/types.ts +130 -0
- package/src/heartbeat/heartbeat.ts +434 -0
- package/src/heartbeat/index.ts +1 -0
- package/src/heartbeat/templates.ts +30 -0
- package/src/hooks/hook.ts +555 -4
- package/src/hooks/tool-loop-detection.test.ts +158 -0
- package/src/hooks/tool-loop-detection.ts +167 -0
- package/src/http/active-sessions.ts +199 -0
- package/src/http/agents.ts +328 -0
- package/src/http/config.ts +191 -0
- package/src/http/core.ts +309 -0
- package/src/http/db-query.ts +91 -0
- package/src/http/ecosystem.ts +63 -0
- package/src/http/epics.ts +460 -0
- package/src/http/index.ts +216 -0
- package/src/http/mcp.ts +77 -0
- package/src/http/memory.ts +168 -0
- package/src/http/openapi.ts +109 -0
- package/src/http/poll.ts +299 -0
- package/src/http/prompt-templates.ts +412 -0
- package/src/http/repos.ts +195 -0
- package/src/http/route-def.ts +123 -0
- package/src/http/schedules.ts +426 -0
- package/src/http/session-data.ts +241 -0
- package/src/http/stats.ts +174 -0
- package/src/http/tasks.ts +468 -0
- package/src/http/trackers/index.ts +10 -0
- package/src/http/trackers/linear.ts +187 -0
- package/src/http/types.ts +12 -0
- package/src/http/utils.ts +87 -0
- package/src/http/webhooks.ts +432 -0
- package/src/http/workflows.ts +530 -0
- package/src/http.ts +1 -1890
- package/src/linear/README.md +65 -0
- package/src/linear/app.ts +48 -0
- package/src/linear/client.ts +18 -0
- package/src/linear/index.ts +1 -0
- package/src/linear/oauth.ts +35 -0
- package/src/linear/outbound.ts +212 -0
- package/src/linear/sync.ts +567 -0
- package/src/linear/templates.ts +47 -0
- package/src/linear/types.ts +7 -0
- package/src/linear/webhook.ts +104 -0
- package/src/oauth/README.md +66 -0
- package/src/oauth/index.ts +6 -0
- package/src/oauth/wrapper.ts +204 -0
- package/src/prompts/base-prompt.ts +150 -265
- package/src/prompts/defaults.ts +196 -0
- package/src/prompts/registry.ts +57 -0
- package/src/prompts/resolver.ts +296 -0
- package/src/prompts/session-templates.ts +604 -0
- package/src/providers/claude-adapter.ts +442 -0
- package/src/providers/index.ts +24 -0
- package/src/providers/pi-mono-adapter.ts +442 -0
- package/src/providers/pi-mono-extension.ts +624 -0
- package/src/providers/pi-mono-mcp-client.ts +124 -0
- package/src/providers/types.ts +75 -0
- package/src/scheduler/scheduler.test.ts +2 -0
- package/src/scheduler/scheduler.ts +231 -40
- package/src/server.ts +97 -6
- package/src/slack/HEURISTICS.md +105 -0
- package/src/slack/actions.ts +133 -0
- package/src/slack/app.ts +7 -0
- package/src/slack/assistant.ts +118 -0
- package/src/slack/blocks.ts +233 -0
- package/src/slack/channel-activity.ts +177 -0
- package/src/slack/commands.ts +31 -17
- package/src/slack/files.ts +1 -1
- package/src/slack/handlers.test.ts +114 -1
- package/src/slack/handlers.ts +230 -55
- package/src/slack/responses.ts +120 -67
- package/src/slack/router.ts +17 -99
- package/src/slack/templates.ts +55 -0
- package/src/slack/thread-buffer.ts +213 -0
- package/src/slack/watcher.ts +119 -4
- package/src/tests/agent-activity.test.ts +247 -0
- package/src/tests/agentmail-filters.test.ts +97 -0
- package/src/tests/artifact-sdk.test.ts +800 -0
- package/src/tests/base-prompt.test.ts +264 -0
- package/src/tests/build-pi-skills.test.ts +127 -0
- package/src/tests/channel-activity.test.ts +363 -0
- package/src/tests/claude-adapter.test.ts +126 -0
- package/src/tests/context-versioning.test.ts +425 -0
- package/src/tests/db-queries-oauth.test.ts +197 -0
- package/src/tests/db-queries-tracker.test.ts +230 -0
- package/src/tests/epics.test.ts +3 -3
- package/src/tests/error-tracker.test.ts +368 -0
- package/src/tests/fetch-resolved-env.test.ts +167 -0
- package/src/tests/generate-default-claude-md.test.ts +9 -1
- package/src/tests/generate-identity-templates.test.ts +124 -0
- package/src/tests/gitlab-auth.test.ts +109 -0
- package/src/tests/gitlab-handlers.test.ts +691 -0
- package/src/tests/gitlab-vcs-db.test.ts +177 -0
- package/src/tests/heartbeat.test.ts +364 -0
- package/src/tests/http-api-integration.test.ts +1698 -0
- package/src/tests/linear-outbound-sync.test.ts +200 -0
- package/src/tests/linear-webhook.test.ts +406 -0
- package/src/tests/match-route.test.ts +187 -0
- package/src/tests/memory.test.ts +737 -0
- package/src/tests/migration-runner-regressions.test.ts +86 -0
- package/src/tests/model-control.test.ts +338 -0
- package/src/tests/oauth-wrapper.test.ts +147 -0
- package/src/tests/onboard-compose.test.ts +138 -0
- package/src/tests/onboard-env.test.ts +174 -0
- package/src/tests/onboard-manifest.test.ts +137 -0
- package/src/tests/pi-mono-adapter.test.ts +234 -0
- package/src/tests/pool-session-logs.test.ts +199 -0
- package/src/tests/progress-dedup.test.ts +98 -0
- package/src/tests/prompt-template-github.test.ts +682 -0
- package/src/tests/prompt-template-remaining.test.ts +504 -0
- package/src/tests/prompt-template-resolver.test.ts +621 -0
- package/src/tests/prompt-template-session.test.ts +363 -0
- package/src/tests/prompt-templates-db.test.ts +616 -0
- package/src/tests/provider-adapter.test.ts +122 -0
- package/src/tests/provider-command-format.test.ts +98 -0
- package/src/tests/reload-config.test.ts +170 -0
- package/src/tests/runner-polling-api.test.ts +25 -20
- package/src/tests/scheduled-tasks.test.ts +104 -0
- package/src/tests/scheduler-backoff.test.ts +166 -0
- package/src/tests/self-improvement.test.ts +541 -0
- package/src/tests/session-attach.test.ts +536 -0
- package/src/tests/session-costs.test.ts +267 -1
- package/src/tests/slack-actions.test.ts +133 -0
- package/src/tests/slack-assistant.test.ts +136 -0
- package/src/tests/slack-blocks.test.ts +246 -0
- package/src/tests/slack-metadata-inheritance.test.ts +243 -0
- package/src/tests/slack-queue-offline.test.ts +174 -0
- package/src/tests/slack-router.test.ts +181 -0
- package/src/tests/slack-thread-buffer.test.ts +305 -0
- package/src/tests/slack-thread-followups.test.ts +298 -0
- package/src/tests/slack-watcher.test.ts +101 -0
- package/src/tests/structured-output.test.ts +307 -0
- package/src/tests/swarm-repos.test.ts +198 -0
- package/src/tests/task-cancellation.test.ts +6 -4
- package/src/tests/task-working-dir.test.ts +176 -0
- package/src/tests/template-fetch.test.ts +490 -0
- package/src/tests/tool-annotations.test.ts +371 -0
- package/src/tests/tracker-tools.test.ts +184 -0
- package/src/tests/update-profile-agentid.test.ts +248 -0
- package/src/tests/update-profile-api.test.ts +143 -3
- package/src/tests/update-profile-auth.test.ts +195 -0
- package/src/tests/validation-adapters.test.ts +86 -0
- package/src/tests/vcs-provider.test.ts +27 -0
- package/src/tests/workflow-agent-task.test.ts +196 -0
- package/src/tests/workflow-async-v2.test.ts +508 -0
- package/src/tests/workflow-convergence.test.ts +541 -0
- package/src/tests/workflow-definition-validation.test.ts +366 -0
- package/src/tests/workflow-engine-v2.test.ts +691 -0
- package/src/tests/workflow-executors.test.ts +736 -0
- package/src/tests/workflow-http-v2.test.ts +599 -0
- package/src/tests/workflow-integration-io.test.ts +902 -0
- package/src/tests/workflow-io-schemas.test.ts +624 -0
- package/src/tests/workflow-registry.test.ts +592 -0
- package/src/tests/workflow-retry-v2.test.ts +401 -0
- package/src/tests/workflow-retry-validation.test.ts +282 -0
- package/src/tests/workflow-schedule-trigger.test.ts +104 -0
- package/src/tests/workflow-template.test.ts +288 -0
- package/src/tests/workflow-trigger-schema.test.ts +359 -0
- package/src/tests/workflow-triggers-v2.test.ts +264 -0
- package/src/tests/workflow-versions.test.ts +208 -0
- package/src/tests/workflow-workspace.test.ts +272 -0
- package/src/tests/x402-client.test.ts +117 -0
- package/src/tests/x402-config.test.ts +182 -0
- package/src/tests/x402-spending-tracker.test.ts +185 -0
- package/src/tools/cancel-task.ts +2 -0
- package/src/tools/context-diff.ts +171 -0
- package/src/tools/context-history.ts +138 -0
- package/src/tools/create-channel.ts +1 -0
- package/src/tools/db-query.ts +78 -0
- package/src/tools/delete-channel.ts +132 -0
- package/src/tools/epics/assign-task-to-epic.ts +1 -0
- package/src/tools/epics/create-epic.ts +3 -2
- package/src/tools/epics/delete-epic.ts +2 -0
- package/src/tools/epics/get-epic-details.ts +2 -0
- package/src/tools/epics/list-epics.ts +2 -0
- package/src/tools/epics/unassign-task-from-epic.ts +1 -0
- package/src/tools/epics/update-epic.ts +7 -4
- package/src/tools/get-swarm.ts +2 -0
- package/src/tools/get-task-details.ts +2 -0
- package/src/tools/get-tasks.ts +27 -1
- package/src/tools/inject-learning.ts +106 -0
- package/src/tools/join-swarm.ts +17 -7
- package/src/tools/list-channels.ts +2 -0
- package/src/tools/list-services.ts +2 -0
- package/src/tools/memory-get.ts +56 -0
- package/src/tools/memory-search.ts +131 -0
- package/src/tools/my-agent-info.ts +2 -0
- package/src/tools/poll-task.ts +2 -20
- package/src/tools/post-message.ts +1 -0
- package/src/tools/prompt-templates/delete.ts +86 -0
- package/src/tools/prompt-templates/get.ts +89 -0
- package/src/tools/prompt-templates/index.ts +5 -0
- package/src/tools/prompt-templates/list.ts +95 -0
- package/src/tools/prompt-templates/preview.ts +84 -0
- package/src/tools/prompt-templates/set.ts +117 -0
- package/src/tools/read-messages.ts +2 -0
- package/src/tools/register-agentmail-inbox.ts +166 -0
- package/src/tools/register-service.ts +2 -0
- package/src/tools/schedules/create-schedule.ts +134 -24
- package/src/tools/schedules/delete-schedule.ts +2 -0
- package/src/tools/schedules/list-schedules.ts +20 -4
- package/src/tools/schedules/run-schedule-now.ts +1 -0
- package/src/tools/schedules/update-schedule.ts +49 -17
- package/src/tools/send-task.ts +132 -10
- package/src/tools/slack-download-file.ts +4 -2
- package/src/tools/slack-list-channels.ts +2 -0
- package/src/tools/slack-post.ts +2 -0
- package/src/tools/slack-read.ts +2 -0
- package/src/tools/slack-reply.ts +2 -0
- package/src/tools/slack-upload-file.ts +2 -0
- package/src/tools/store-progress.ts +205 -4
- package/src/tools/swarm-config/delete-config.ts +87 -0
- package/src/tools/swarm-config/get-config.ts +108 -0
- package/src/tools/swarm-config/index.ts +4 -0
- package/src/tools/swarm-config/list-config.ts +99 -0
- package/src/tools/swarm-config/set-config.ts +118 -0
- package/src/tools/task-action.ts +50 -5
- package/src/tools/task-dedup.ts +97 -0
- package/src/tools/templates.ts +53 -0
- package/src/tools/tool-config.ts +124 -0
- package/src/tools/tracker/index.ts +6 -0
- package/src/tools/tracker/tracker-link-epic.ts +64 -0
- package/src/tools/tracker/tracker-link-task.ts +64 -0
- package/src/tools/tracker/tracker-map-agent.ts +57 -0
- package/src/tools/tracker/tracker-status.ts +56 -0
- package/src/tools/tracker/tracker-sync-status.ts +42 -0
- package/src/tools/tracker/tracker-unlink.ts +41 -0
- package/src/tools/unregister-service.ts +2 -0
- package/src/tools/update-profile.ts +172 -17
- package/src/tools/update-service-status.ts +2 -0
- package/src/tools/utils.ts +10 -1
- package/src/tools/workflows/create-workflow.ts +129 -0
- package/src/tools/workflows/delete-workflow.ts +42 -0
- package/src/tools/workflows/get-workflow-run.ts +59 -0
- package/src/tools/workflows/get-workflow.ts +53 -0
- package/src/tools/workflows/index.ts +9 -0
- package/src/tools/workflows/list-workflow-runs.ts +48 -0
- package/src/tools/workflows/list-workflows.ts +42 -0
- package/src/tools/workflows/retry-workflow-run.ts +40 -0
- package/src/tools/workflows/trigger-workflow.ts +96 -0
- package/src/tools/workflows/update-workflow.ts +133 -0
- package/src/tracker/types.ts +51 -0
- package/src/types.ts +530 -14
- package/src/utils/credentials.test.ts +156 -0
- package/src/utils/credentials.ts +50 -0
- package/src/utils/error-tracker.ts +190 -0
- package/src/vcs/index.ts +15 -0
- package/src/vcs/types.ts +5 -0
- package/src/workflows/checkpoint.ts +121 -0
- package/src/workflows/cooldown.ts +28 -0
- package/src/workflows/definition.ts +235 -0
- package/src/workflows/engine.ts +580 -0
- package/src/workflows/event-bus.ts +29 -0
- package/src/workflows/executors/agent-task.ts +103 -0
- package/src/workflows/executors/base.ts +86 -0
- package/src/workflows/executors/code-match.ts +88 -0
- package/src/workflows/executors/index.ts +16 -0
- package/src/workflows/executors/notify.ts +93 -0
- package/src/workflows/executors/property-match.ts +104 -0
- package/src/workflows/executors/raw-llm.ts +83 -0
- package/src/workflows/executors/registry.ts +76 -0
- package/src/workflows/executors/script.ts +103 -0
- package/src/workflows/executors/validate.ts +215 -0
- package/src/workflows/executors/vcs.ts +58 -0
- package/src/workflows/index.ts +61 -0
- package/src/workflows/input.ts +46 -0
- package/src/workflows/json-schema-validator.ts +118 -0
- package/src/workflows/recovery.ts +139 -0
- package/src/workflows/resume.ts +229 -0
- package/src/workflows/retry-poller.ts +216 -0
- package/src/workflows/template.ts +74 -0
- package/src/workflows/templates.ts +86 -0
- package/src/workflows/triggers.ts +124 -0
- package/src/workflows/validation.ts +104 -0
- package/src/workflows/version.ts +44 -0
- package/src/x402/cli.ts +140 -0
- package/src/x402/client.ts +192 -0
- package/src/x402/config.ts +131 -0
- package/src/x402/index.ts +37 -0
- package/src/x402/openfort-signer.ts +83 -0
- package/src/x402/spending-tracker.ts +109 -0
- package/templates/official/coder/CLAUDE.md +49 -0
- package/templates/official/coder/IDENTITY.md +28 -0
- package/templates/official/coder/SOUL.md +43 -0
- package/templates/official/coder/TOOLS.md +40 -0
- package/templates/official/coder/config.json +23 -0
- package/templates/official/coder/start-up.sh +23 -0
- package/templates/official/content-reviewer/CLAUDE.md +68 -0
- package/templates/official/content-reviewer/IDENTITY.md +28 -0
- package/templates/official/content-reviewer/SOUL.md +44 -0
- package/templates/official/content-reviewer/TOOLS.md +37 -0
- package/templates/official/content-reviewer/config.json +23 -0
- package/templates/official/content-reviewer/start-up.sh +23 -0
- package/templates/official/content-strategist/CLAUDE.md +63 -0
- package/templates/official/content-strategist/IDENTITY.md +33 -0
- package/templates/official/content-strategist/SOUL.md +48 -0
- package/templates/official/content-strategist/TOOLS.md +47 -0
- package/templates/official/content-strategist/config.json +23 -0
- package/templates/official/content-strategist/start-up.sh +23 -0
- package/templates/official/content-writer/CLAUDE.md +72 -0
- package/templates/official/content-writer/IDENTITY.md +30 -0
- package/templates/official/content-writer/SOUL.md +46 -0
- package/templates/official/content-writer/TOOLS.md +44 -0
- package/templates/official/content-writer/config.json +23 -0
- package/templates/official/content-writer/start-up.sh +23 -0
- package/templates/official/forward-deployed-engineer/CLAUDE.md +54 -0
- package/templates/official/forward-deployed-engineer/IDENTITY.md +37 -0
- package/templates/official/forward-deployed-engineer/SOUL.md +55 -0
- package/templates/official/forward-deployed-engineer/config.json +21 -0
- package/templates/official/lead/CLAUDE.md +33 -0
- package/templates/official/lead/IDENTITY.md +36 -0
- package/templates/official/lead/SOUL.md +51 -0
- package/templates/official/lead/config.json +22 -0
- package/templates/official/researcher/CLAUDE.md +46 -0
- package/templates/official/researcher/IDENTITY.md +28 -0
- package/templates/official/researcher/SOUL.md +43 -0
- package/templates/official/researcher/config.json +21 -0
- package/templates/official/reviewer/CLAUDE.md +63 -0
- package/templates/official/reviewer/IDENTITY.md +28 -0
- package/templates/official/reviewer/SOUL.md +45 -0
- package/templates/official/reviewer/config.json +21 -0
- package/templates/official/tester/CLAUDE.md +53 -0
- package/templates/official/tester/IDENTITY.md +28 -0
- package/templates/official/tester/SOUL.md +55 -0
- package/templates/official/tester/config.json +21 -0
- package/templates/schema.ts +35 -0
- package/.claude/settings.local.json +0 -115
- package/.dockerignore +0 -61
- package/.editorconfig +0 -15
- package/.env.docker.example +0 -39
- package/.env.example +0 -40
- package/.github/workflows/ci.yml +0 -76
- package/.github/workflows/docker-and-deploy.yml +0 -117
- package/.wts-config.json +0 -4
- package/.wts-setup.ts +0 -102
- package/CLAUDE.md +0 -104
- package/CONTRIBUTING.md +0 -270
- package/DEPLOYMENT.md +0 -605
- package/Dockerfile +0 -57
- package/Dockerfile.worker +0 -157
- package/FAQ.md +0 -19
- package/MCP.md +0 -406
- package/UI.md +0 -40
- package/assets/agent-swarm-logo-orange.png +0 -0
- package/assets/agent-swarm-logo.png +0 -0
- package/assets/agent-swarm.mp4 +0 -0
- package/assets/agent-swarm.png +0 -0
- package/biome.json +0 -39
- package/deploy/DEPLOY.md +0 -60
- package/deploy/agent-swarm.service +0 -17
- package/deploy/docker-push.ts +0 -30
- package/deploy/install.ts +0 -85
- package/deploy/prod-db.ts +0 -42
- package/deploy/uninstall.ts +0 -12
- package/deploy/update.ts +0 -21
- package/docker-compose.example.yml +0 -159
- package/docker-entrypoint.sh +0 -352
- package/ecosystem.config.cjs +0 -66
- package/plugin/README.md +0 -1
- package/plugin/hooks/hooks.json +0 -71
- package/pyproject.toml +0 -9
- package/scripts/generate-mcp-docs.ts +0 -415
- package/slack-manifest.json +0 -71
- package/src/tests/get-inbox-message.test.ts +0 -145
- package/src/tools/get-inbox-message.ts +0 -89
- package/src/tools/inbox-delegate.ts +0 -113
- package/thoughts/shared/plans/2025-12-18-slack-integration.md +0 -1195
- package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +0 -732
- package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +0 -361
- package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +0 -501
- package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +0 -560
- package/thoughts/shared/plans/2025-12-23-runner-level-polling.md +0 -934
- package/thoughts/shared/plans/2025-12-23-runner-session-logs.md +0 -1000
- package/thoughts/shared/plans/2025-12-23-worker-lead-spawn-triggers.md +0 -568
- package/thoughts/shared/plans/2026-01-09-inverse-teleport.md +0 -1516
- package/thoughts/shared/plans/2026-01-12-agent-rename-pm2-control.md +0 -1133
- package/thoughts/shared/plans/2026-01-12-github-app-integration.md +0 -380
- package/thoughts/shared/plans/2026-01-12-lead-inbox-model.md +0 -876
- package/thoughts/shared/plans/2026-01-12-ralph-wiggum-integration.md +0 -463
- package/thoughts/shared/plans/2026-01-13-agent-concurrency.md +0 -691
- package/thoughts/shared/plans/2026-01-13-github-assignment-handling.md +0 -690
- package/thoughts/shared/plans/2026-01-13-prevent-duplicate-trigger-processing.md +0 -1071
- package/thoughts/shared/plans/2026-01-14-fix-slack-thread-context.md +0 -507
- package/thoughts/shared/plans/2026-01-15-scheduled-tasks-implementation.md +0 -565
- package/thoughts/shared/plans/2026-01-15-usage-cost-tracking-ui.md +0 -1479
- package/thoughts/shared/plans/2026-01-16-epics-feature-implementation.md +0 -1230
- package/thoughts/shared/research/.gitkeep +0 -0
- package/thoughts/shared/research/2025-01-09-inverse-teleport-plan-review.md +0 -420
- package/thoughts/shared/research/2025-12-18-slack-integration.md +0 -442
- package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +0 -339
- package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +0 -390
- package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +0 -376
- package/thoughts/shared/research/2025-12-22-runner-loop-architecture.md +0 -582
- package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +0 -264
- package/thoughts/shared/research/2026-01-13-lead-duplicate-trigger-processing.md +0 -223
- package/thoughts/shared/research/2026-01-14-lead-slack-thread-context.md +0 -277
- package/thoughts/shared/research/2026-01-15-ai-tracker-agent-swarm-integration.md +0 -376
- package/thoughts/shared/research/2026-01-15-auto-starting-processes-in-worker-containers.md +0 -787
- package/thoughts/shared/research/2026-01-15-scheduled-tasks.md +0 -390
- package/thoughts/shared/research/2026-01-16-epics-feature-research.md +0 -437
- package/thoughts/taras/plans/2026-01-22-agent-swarm-schemas.md +0 -98
- package/thoughts/taras/plans/2026-01-28-per-worker-claude-md.md +0 -617
- package/thoughts/taras/plans/2026-01-28-sentry-cli-integration.md +0 -214
- package/thoughts/taras/research/2026-01-22-vercel-cli-integration.md +0 -287
- package/thoughts/taras/research/2026-01-27-excessive-polling-issue.md +0 -311
- package/thoughts/taras/research/2026-01-28-per-worker-claude-md.md +0 -383
- package/thoughts/taras/research/2026-01-28-sentry-cli-integration.md +0 -240
- package/tsconfig.json +0 -37
- package/ui/CLAUDE.md +0 -49
- package/ui/bun.lock +0 -771
- package/ui/index.html +0 -22
- package/ui/package-lock.json +0 -5290
- package/ui/package.json +0 -33
- package/ui/pnpm-lock.yaml +0 -3341
- package/ui/postcss.config.js +0 -6
- package/ui/public/logo.png +0 -0
- package/ui/src/App.tsx +0 -63
- package/ui/src/components/ActivityFeed.tsx +0 -440
- package/ui/src/components/AgentDetailPanel.tsx +0 -733
- package/ui/src/components/AgentsPanel.tsx +0 -815
- package/ui/src/components/ChatPanel.tsx +0 -1920
- package/ui/src/components/ConfigModal.tsx +0 -253
- package/ui/src/components/Dashboard.tsx +0 -832
- package/ui/src/components/EditAgentProfileModal.tsx +0 -433
- package/ui/src/components/EpicDetailPage.tsx +0 -741
- package/ui/src/components/EpicsPanel.tsx +0 -566
- package/ui/src/components/Header.tsx +0 -160
- package/ui/src/components/JsonViewer.tsx +0 -171
- package/ui/src/components/ScheduledTaskDetailPanel.tsx +0 -517
- package/ui/src/components/ScheduledTasksPanel.tsx +0 -639
- package/ui/src/components/ServicesPanel.tsx +0 -622
- package/ui/src/components/SessionLogPanel.tsx +0 -1219
- package/ui/src/components/StatsBar.tsx +0 -321
- package/ui/src/components/StatusBadge.tsx +0 -168
- package/ui/src/components/TaskDetailPanel.tsx +0 -903
- package/ui/src/components/TasksPanel.tsx +0 -614
- package/ui/src/components/UsageCharts.tsx +0 -216
- package/ui/src/components/UsageTab.tsx +0 -394
- package/ui/src/hooks/queries.ts +0 -353
- package/ui/src/hooks/useAutoScroll.ts +0 -83
- package/ui/src/index.css +0 -257
- package/ui/src/lib/api.ts +0 -268
- package/ui/src/lib/config.ts +0 -35
- package/ui/src/lib/contentPreview.ts +0 -208
- package/ui/src/lib/theme.ts +0 -214
- package/ui/src/lib/utils.ts +0 -88
- package/ui/src/main.tsx +0 -28
- package/ui/src/types/api.ts +0 -323
- package/ui/src/vite-env.d.ts +0 -1
- package/ui/tailwind.config.js +0 -37
- package/ui/tsconfig.json +0 -31
- package/ui/vite.config.ts +0 -35
- /package/{thoughts/shared/plans → templates/community}/.gitkeep +0 -0
package/src/slack/router.ts
CHANGED
|
@@ -1,78 +1,18 @@
|
|
|
1
1
|
import { getAgentById, getAgentWorkingOnThread, getAllAgents } from "../be/db";
|
|
2
2
|
import type { AgentMatch } from "./types";
|
|
3
3
|
|
|
4
|
-
interface ThreadContext {
|
|
4
|
+
export interface ThreadContext {
|
|
5
5
|
channelId: string;
|
|
6
6
|
threadTs: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
// Common 3-letter words to exclude from matching
|
|
10
|
-
const COMMON_WORDS = new Set([
|
|
11
|
-
"the",
|
|
12
|
-
"and",
|
|
13
|
-
"for",
|
|
14
|
-
"are",
|
|
15
|
-
"but",
|
|
16
|
-
"not",
|
|
17
|
-
"you",
|
|
18
|
-
"all",
|
|
19
|
-
"can",
|
|
20
|
-
"had",
|
|
21
|
-
"her",
|
|
22
|
-
"was",
|
|
23
|
-
"one",
|
|
24
|
-
"our",
|
|
25
|
-
"out",
|
|
26
|
-
"has",
|
|
27
|
-
"his",
|
|
28
|
-
"how",
|
|
29
|
-
"its",
|
|
30
|
-
"let",
|
|
31
|
-
"may",
|
|
32
|
-
"new",
|
|
33
|
-
"now",
|
|
34
|
-
"old",
|
|
35
|
-
"see",
|
|
36
|
-
"way",
|
|
37
|
-
"who",
|
|
38
|
-
"boy",
|
|
39
|
-
"did",
|
|
40
|
-
"get",
|
|
41
|
-
"say",
|
|
42
|
-
"she",
|
|
43
|
-
"too",
|
|
44
|
-
"use",
|
|
45
|
-
"hey",
|
|
46
|
-
"hi",
|
|
47
|
-
"hello",
|
|
48
|
-
"please",
|
|
49
|
-
"help",
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Check if a word is suitable for agent name matching.
|
|
54
|
-
* Allows 3+ char words if they're not common words.
|
|
55
|
-
* Always allows uppercase words (CEO, CTO, etc).
|
|
56
|
-
*/
|
|
57
|
-
function isMatchableWord(word: string): boolean {
|
|
58
|
-
if (word.length < 3) return false;
|
|
59
|
-
// Always allow fully uppercase words (acronyms like CEO, CTO)
|
|
60
|
-
if (word === word.toUpperCase() && word.length >= 3) return true;
|
|
61
|
-
// Allow 3+ char words that aren't common
|
|
62
|
-
if (word.length >= 3 && !COMMON_WORDS.has(word.toLowerCase())) return true;
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
9
|
/**
|
|
67
10
|
* Routes a Slack message to the appropriate agent(s) based on mentions.
|
|
68
11
|
*
|
|
69
12
|
* Routing rules:
|
|
70
13
|
* - `swarm#<uuid>` → exact agent by ID
|
|
71
14
|
* - `swarm#all` → all non-lead agents
|
|
72
|
-
* -
|
|
73
|
-
* - Multiple partial matches → route to lead (let lead decide)
|
|
74
|
-
* - Thread follow-up (no match but agent working on thread) → route to that agent
|
|
75
|
-
* - Bot @mention only → lead agent
|
|
15
|
+
* - Everything else → lead agent
|
|
76
16
|
*/
|
|
77
17
|
export function routeMessage(
|
|
78
18
|
text: string,
|
|
@@ -104,46 +44,28 @@ export function routeMessage(
|
|
|
104
44
|
}
|
|
105
45
|
}
|
|
106
46
|
|
|
107
|
-
//
|
|
108
|
-
if (matches.length === 0) {
|
|
109
|
-
for (const agent of agents) {
|
|
110
|
-
const nameWords = agent.name.split(/\s+/).filter(isMatchableWord);
|
|
111
|
-
for (const word of nameWords) {
|
|
112
|
-
const regex = new RegExp(`\\b${escapeRegex(word)}\\b`, "i");
|
|
113
|
-
if (regex.test(text)) {
|
|
114
|
-
if (!matches.some((m) => m.agent.id === agent.id)) {
|
|
115
|
-
matches.push({ agent, matchedText: word });
|
|
116
|
-
}
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// If multiple agents matched a partial name, route to lead instead (let lead decide)
|
|
123
|
-
if (matches.length > 1) {
|
|
124
|
-
const lead = agents.find((a) => a.isLead);
|
|
125
|
-
if (lead) {
|
|
126
|
-
const matchedWords = matches.map((m) => m.matchedText).join(", ");
|
|
127
|
-
const matchedAgents = matches.map((m) => m.agent.name).join(", ");
|
|
128
|
-
return [
|
|
129
|
-
{
|
|
130
|
-
agent: lead,
|
|
131
|
-
matchedText: `ambiguous match "${matchedWords}" (could be: ${matchedAgents})`,
|
|
132
|
-
},
|
|
133
|
-
];
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Thread follow-up: If no matches and we're in a thread, check if an agent is working on it
|
|
47
|
+
// Thread follow-up — route to agent already working in this thread
|
|
139
48
|
if (matches.length === 0 && threadContext) {
|
|
140
49
|
const workingAgent = getAgentWorkingOnThread(threadContext.channelId, threadContext.threadTs);
|
|
141
50
|
if (workingAgent && workingAgent.status !== "offline") {
|
|
51
|
+
console.log(
|
|
52
|
+
`[Slack] Thread follow-up: routing to agent ${workingAgent.name} (${workingAgent.id}) in thread ${threadContext.threadTs}`,
|
|
53
|
+
);
|
|
142
54
|
matches.push({ agent: workingAgent, matchedText: "thread follow-up" });
|
|
55
|
+
} else if (workingAgent) {
|
|
56
|
+
// Agent is offline but thread has history — route to lead without requiring @mention
|
|
57
|
+
console.log(
|
|
58
|
+
`[Slack] Thread follow-up: agent ${workingAgent.name} is offline, routing to lead`,
|
|
59
|
+
);
|
|
60
|
+
const allAgents = getAllAgents();
|
|
61
|
+
const lead = allAgents.find((a) => a.isLead && a.status !== "offline");
|
|
62
|
+
if (lead) {
|
|
63
|
+
matches.push({ agent: lead, matchedText: "thread follow-up (lead fallback)" });
|
|
64
|
+
}
|
|
143
65
|
}
|
|
144
66
|
}
|
|
145
67
|
|
|
146
|
-
//
|
|
68
|
+
// Default to lead for everything else
|
|
147
69
|
if (matches.length === 0 && botMentioned) {
|
|
148
70
|
const lead = agents.find((a) => a.isLead);
|
|
149
71
|
if (lead) {
|
|
@@ -154,10 +76,6 @@ export function routeMessage(
|
|
|
154
76
|
return matches;
|
|
155
77
|
}
|
|
156
78
|
|
|
157
|
-
function escapeRegex(str: string): string {
|
|
158
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
159
|
-
}
|
|
160
|
-
|
|
161
79
|
/**
|
|
162
80
|
* Extracts the task description from a message, removing bot mentions and agent references.
|
|
163
81
|
*/
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack event prompt template definitions.
|
|
3
|
+
*
|
|
4
|
+
* Each template is registered at module load time via registerTemplate().
|
|
5
|
+
* Handlers import this module for the side-effect of registration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { registerTemplate } from "../prompts/registry";
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Assistant messages (Slack UI messages, not agent prompts)
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
registerTemplate({
|
|
15
|
+
eventType: "slack.assistant.greeting",
|
|
16
|
+
header: "",
|
|
17
|
+
defaultBody: "Hi! I'm your Agent Swarm assistant. How can I help?",
|
|
18
|
+
variables: [],
|
|
19
|
+
category: "system",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
registerTemplate({
|
|
23
|
+
eventType: "slack.assistant.suggested_prompts",
|
|
24
|
+
header: "",
|
|
25
|
+
defaultBody: `Try these:
|
|
26
|
+
- Check status: What's the current status of all agents?
|
|
27
|
+
- Assign a task: Can you help me with...
|
|
28
|
+
- List recent tasks: Show me the most recent tasks`,
|
|
29
|
+
variables: [],
|
|
30
|
+
category: "system",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
registerTemplate({
|
|
34
|
+
eventType: "slack.assistant.offline",
|
|
35
|
+
header: "",
|
|
36
|
+
defaultBody:
|
|
37
|
+
"No agents are available right now. Your request has been queued and will be processed when agents come back online.",
|
|
38
|
+
variables: [],
|
|
39
|
+
category: "system",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
registerTemplate({
|
|
43
|
+
eventType: "slack.message.thread_context",
|
|
44
|
+
header: "",
|
|
45
|
+
defaultBody: `<thread_context>
|
|
46
|
+
{{thread_messages}}
|
|
47
|
+
</thread_context>`,
|
|
48
|
+
variables: [
|
|
49
|
+
{
|
|
50
|
+
name: "thread_messages",
|
|
51
|
+
description: "Formatted thread messages (user: text pairs)",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
category: "system",
|
|
55
|
+
});
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createTaskExtended,
|
|
3
|
+
getLatestActiveTaskInThread,
|
|
4
|
+
getLeadAgent,
|
|
5
|
+
getMostRecentTaskInThread,
|
|
6
|
+
} from "../be/db";
|
|
7
|
+
import { getSlackApp } from "./app";
|
|
8
|
+
import { buildBufferFlushBlocks } from "./blocks";
|
|
9
|
+
|
|
10
|
+
interface BufferedMessage {
|
|
11
|
+
text: string;
|
|
12
|
+
userId: string;
|
|
13
|
+
ts: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface BufferedThread {
|
|
17
|
+
channelId: string;
|
|
18
|
+
threadTs: string;
|
|
19
|
+
messages: BufferedMessage[];
|
|
20
|
+
timer: Timer;
|
|
21
|
+
slackUserId: string; // original requester (first message sender)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const threadBuffers = new Map<string, BufferedThread>();
|
|
25
|
+
|
|
26
|
+
const BUFFER_TIMEOUT_MS = Number(process.env.ADDITIVE_SLACK_BUFFER_MS) || 10_000;
|
|
27
|
+
|
|
28
|
+
function makeKey(channelId: string, threadTs: string): string {
|
|
29
|
+
return `${channelId}:${threadTs}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Add a message to the thread buffer. Resets the debounce timer.
|
|
34
|
+
*/
|
|
35
|
+
export function bufferThreadMessage(
|
|
36
|
+
channelId: string,
|
|
37
|
+
threadTs: string,
|
|
38
|
+
text: string,
|
|
39
|
+
userId: string,
|
|
40
|
+
ts: string,
|
|
41
|
+
): void {
|
|
42
|
+
const key = makeKey(channelId, threadTs);
|
|
43
|
+
const existing = threadBuffers.get(key);
|
|
44
|
+
|
|
45
|
+
if (existing) {
|
|
46
|
+
// Append to existing buffer, reset timer
|
|
47
|
+
existing.messages.push({ text, userId, ts });
|
|
48
|
+
clearTimeout(existing.timer);
|
|
49
|
+
existing.timer = setTimeout(() => flushBuffer(key, false), BUFFER_TIMEOUT_MS);
|
|
50
|
+
console.log(
|
|
51
|
+
`[Slack] Buffer append: ${key} (${existing.messages.length} messages, timer reset to ${BUFFER_TIMEOUT_MS}ms)`,
|
|
52
|
+
);
|
|
53
|
+
} else {
|
|
54
|
+
// Create new buffer entry
|
|
55
|
+
const timer = setTimeout(() => flushBuffer(key, false), BUFFER_TIMEOUT_MS);
|
|
56
|
+
threadBuffers.set(key, {
|
|
57
|
+
channelId,
|
|
58
|
+
threadTs,
|
|
59
|
+
messages: [{ text, userId, ts }],
|
|
60
|
+
timer,
|
|
61
|
+
slackUserId: userId,
|
|
62
|
+
});
|
|
63
|
+
console.log(`[Slack] Buffer created: ${key} (timer set to ${BUFFER_TIMEOUT_MS}ms)`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check if a thread currently has a pending buffer.
|
|
69
|
+
*/
|
|
70
|
+
export function isThreadBuffered(channelId: string, threadTs: string): boolean {
|
|
71
|
+
return threadBuffers.has(makeKey(channelId, threadTs));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get the number of messages currently in the buffer for a thread key.
|
|
76
|
+
*/
|
|
77
|
+
export function getBufferMessageCount(key: string): number {
|
|
78
|
+
return threadBuffers.get(key)?.messages.length ?? 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Instantly flush the buffer (used by !now command). Clears the debounce timer
|
|
83
|
+
* and flushes with immediate=true (no dependsOn).
|
|
84
|
+
*/
|
|
85
|
+
export async function instantFlush(key: string): Promise<void> {
|
|
86
|
+
const buffer = threadBuffers.get(key);
|
|
87
|
+
if (buffer) {
|
|
88
|
+
clearTimeout(buffer.timer);
|
|
89
|
+
console.log(`[Slack] Instant flush triggered: ${key}`);
|
|
90
|
+
}
|
|
91
|
+
await flushBuffer(key, true);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Fetch thread context from Slack for the buffer flush task description.
|
|
96
|
+
*/
|
|
97
|
+
async function getThreadContextForBuffer(channelId: string, threadTs: string): Promise<string> {
|
|
98
|
+
const app = getSlackApp();
|
|
99
|
+
if (!app) return "";
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const result = await app.client.conversations.replies({
|
|
103
|
+
channel: channelId,
|
|
104
|
+
ts: threadTs,
|
|
105
|
+
limit: 20,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const messages = result.messages || [];
|
|
109
|
+
if (messages.length === 0) return "";
|
|
110
|
+
|
|
111
|
+
const formatted = messages
|
|
112
|
+
.filter((m) => m.text)
|
|
113
|
+
.map((m) => {
|
|
114
|
+
const msg = m as Record<string, unknown>;
|
|
115
|
+
const isBotMessage = msg.bot_id !== undefined || msg.subtype === "bot_message";
|
|
116
|
+
if (isBotMessage) {
|
|
117
|
+
const truncated = m.text && m.text.length > 500 ? `${m.text.slice(0, 500)}...` : m.text;
|
|
118
|
+
return `[Agent]: ${truncated}`;
|
|
119
|
+
}
|
|
120
|
+
return `<@${m.user}>: ${m.text}`;
|
|
121
|
+
})
|
|
122
|
+
.join("\n");
|
|
123
|
+
|
|
124
|
+
return formatted;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error("[Slack] Failed to fetch thread context for buffer:", error);
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Flush the buffer: concatenate messages, create task with optional dependency chaining.
|
|
133
|
+
* @param key - The buffer key (channelId:threadTs)
|
|
134
|
+
* @param immediate - If true, skip dependency chaining (used by !now)
|
|
135
|
+
*/
|
|
136
|
+
async function flushBuffer(key: string, immediate = false): Promise<void> {
|
|
137
|
+
const buffer = threadBuffers.get(key);
|
|
138
|
+
if (!buffer || buffer.messages.length === 0) {
|
|
139
|
+
threadBuffers.delete(key);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log(
|
|
144
|
+
`[Slack] Flushing buffer: ${key} (${buffer.messages.length} messages, immediate=${immediate})`,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Build combined task description
|
|
148
|
+
const combinedText = buffer.messages.map((m) => m.text).join("\n---\n");
|
|
149
|
+
const description = `[Thread follow-up — ${buffer.messages.length} message(s) buffered]\n\n${combinedText}`;
|
|
150
|
+
|
|
151
|
+
// Find the latest active task in this thread for dependency chaining
|
|
152
|
+
const latestActiveTask = getLatestActiveTaskInThread(buffer.channelId, buffer.threadTs);
|
|
153
|
+
if (latestActiveTask) {
|
|
154
|
+
console.log(
|
|
155
|
+
`[Slack] Dependency chaining: latest active task ${latestActiveTask.id} (status: ${latestActiveTask.status})`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const lead = getLeadAgent();
|
|
160
|
+
|
|
161
|
+
// Thread context for the task
|
|
162
|
+
const threadContext = await getThreadContextForBuffer(buffer.channelId, buffer.threadTs);
|
|
163
|
+
const fullDescription = threadContext
|
|
164
|
+
? `<thread_context>\n${threadContext}\n</thread_context>\n\n${description}`
|
|
165
|
+
: description;
|
|
166
|
+
|
|
167
|
+
// Always pending. If !now was used (immediate=true), no dependency.
|
|
168
|
+
// Otherwise, depend on the latest active task so it queues naturally.
|
|
169
|
+
const dependsOn = !immediate && latestActiveTask ? [latestActiveTask.id] : undefined;
|
|
170
|
+
|
|
171
|
+
const mostRecentTask = getMostRecentTaskInThread(buffer.channelId, buffer.threadTs);
|
|
172
|
+
const task = createTaskExtended(fullDescription, {
|
|
173
|
+
agentId: lead?.id,
|
|
174
|
+
source: "slack",
|
|
175
|
+
slackChannelId: buffer.channelId,
|
|
176
|
+
slackThreadTs: buffer.threadTs,
|
|
177
|
+
slackUserId: buffer.slackUserId,
|
|
178
|
+
dependsOn,
|
|
179
|
+
parentTaskId: mostRecentTask?.id,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
console.log(
|
|
183
|
+
`[Slack] Buffer flushed → task ${task.id} (dependsOn: ${dependsOn ? dependsOn.join(", ") : "none"})`,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Slack feedback with Block Kit
|
|
187
|
+
const app = getSlackApp();
|
|
188
|
+
if (app) {
|
|
189
|
+
const hasDependency = !immediate && !!latestActiveTask;
|
|
190
|
+
const blocks = buildBufferFlushBlocks({
|
|
191
|
+
messageCount: buffer.messages.length,
|
|
192
|
+
taskId: task.id,
|
|
193
|
+
hasDependency,
|
|
194
|
+
});
|
|
195
|
+
const fallbackText = hasDependency
|
|
196
|
+
? `${buffer.messages.length} follow-up message(s) queued pending completion of current task`
|
|
197
|
+
: `${buffer.messages.length} follow-up message(s) batched into task`;
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
await app.client.chat.postMessage({
|
|
201
|
+
channel: buffer.channelId,
|
|
202
|
+
thread_ts: buffer.threadTs,
|
|
203
|
+
text: fallbackText,
|
|
204
|
+
// biome-ignore lint/suspicious/noExplicitAny: Block Kit objects
|
|
205
|
+
blocks: blocks as any,
|
|
206
|
+
});
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error("[Slack] Failed to post buffer flush feedback:", error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
threadBuffers.delete(key);
|
|
213
|
+
}
|
package/src/slack/watcher.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { getCompletedSlackTasks, getInProgressSlackTasks } from "../be/db";
|
|
2
2
|
import { getSlackApp } from "./app";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
sendProgressUpdate,
|
|
5
|
+
sendTaskResponse,
|
|
6
|
+
updateProgressInPlace,
|
|
7
|
+
updateToFinal,
|
|
8
|
+
} from "./responses";
|
|
4
9
|
|
|
5
10
|
let watcherInterval: ReturnType<typeof setInterval> | null = null;
|
|
6
11
|
let isProcessing = false;
|
|
@@ -18,6 +23,48 @@ const pendingSends = new Set<string>();
|
|
|
18
23
|
const lastSendTime = new Map<string, number>();
|
|
19
24
|
const MIN_SEND_INTERVAL = 1000; // Don't send for same task within 1 second
|
|
20
25
|
|
|
26
|
+
// Track task message timestamps for chat.update (taskId -> message info)
|
|
27
|
+
// This is THE message that evolves: assignment → progress → completion
|
|
28
|
+
const taskMessages = new Map<string, { channelId: string; threadTs: string; messageTs: string }>();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Register the initial message ts for a task (called by handlers.ts after posting assignment).
|
|
32
|
+
* This allows the watcher to update the same message through the task lifecycle.
|
|
33
|
+
*/
|
|
34
|
+
export function registerTaskMessage(
|
|
35
|
+
taskId: string,
|
|
36
|
+
channelId: string,
|
|
37
|
+
threadTs: string,
|
|
38
|
+
messageTs: string,
|
|
39
|
+
): void {
|
|
40
|
+
taskMessages.set(taskId, { channelId, threadTs, messageTs });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if a channel is a DM (assistant thread). DM channels start with "D".
|
|
45
|
+
*/
|
|
46
|
+
function isDMChannel(channelId: string): boolean {
|
|
47
|
+
return channelId.startsWith("D");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set the assistant thread status (typing indicator) for DM channels.
|
|
52
|
+
*/
|
|
53
|
+
async function setAssistantStatus(channelId: string, threadTs: string, status: string) {
|
|
54
|
+
const app = getSlackApp();
|
|
55
|
+
if (!app) return;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await app.client.assistant.threads.setStatus({
|
|
59
|
+
channel_id: channelId,
|
|
60
|
+
thread_ts: threadTs,
|
|
61
|
+
status,
|
|
62
|
+
});
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`[Slack] Failed to set assistant status:`, error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
21
68
|
/**
|
|
22
69
|
* Start watching for Slack task updates and sending responses.
|
|
23
70
|
*/
|
|
@@ -52,6 +99,51 @@ export function startTaskWatcher(intervalMs = 3000): void {
|
|
|
52
99
|
const lastSent = lastSendTime.get(progressKey);
|
|
53
100
|
if (lastSent && now - lastSent < MIN_SEND_INTERVAL) continue;
|
|
54
101
|
|
|
102
|
+
const isDM = task.slackChannelId && isDMChannel(task.slackChannelId);
|
|
103
|
+
|
|
104
|
+
if (isDM && task.slackChannelId && task.slackThreadTs) {
|
|
105
|
+
// DM/assistant thread: use setStatus for typing indicator
|
|
106
|
+
const progressText = task.progress || "Processing your request...";
|
|
107
|
+
if (progressText !== sentProgress.get(task.id)) {
|
|
108
|
+
pendingSends.add(progressKey);
|
|
109
|
+
sentProgress.set(task.id, progressText);
|
|
110
|
+
lastSendTime.set(progressKey, now);
|
|
111
|
+
try {
|
|
112
|
+
await setAssistantStatus(task.slackChannelId, task.slackThreadTs, progressText);
|
|
113
|
+
console.log(`[Slack] Set assistant status for task ${task.id.slice(0, 8)}`);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
sentProgress.delete(task.id);
|
|
116
|
+
lastSendTime.delete(progressKey);
|
|
117
|
+
console.error(`[Slack] Failed to set assistant status:`, error);
|
|
118
|
+
} finally {
|
|
119
|
+
pendingSends.delete(progressKey);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Channel thread: use chat.update on the evolving message
|
|
126
|
+
const tracked = taskMessages.get(task.id);
|
|
127
|
+
|
|
128
|
+
// If we have a tracked message but haven't sent any progress yet,
|
|
129
|
+
// update assignment message to "In Progress" state immediately
|
|
130
|
+
if (tracked && !sentProgress.has(task.id) && !task.progress) {
|
|
131
|
+
pendingSends.add(progressKey);
|
|
132
|
+
sentProgress.set(task.id, "__in_progress__");
|
|
133
|
+
lastSendTime.set(progressKey, now);
|
|
134
|
+
try {
|
|
135
|
+
await updateProgressInPlace(task, "Starting...", tracked.messageTs);
|
|
136
|
+
console.log(`[Slack] Updated to in-progress for task ${task.id.slice(0, 8)}`);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
sentProgress.delete(task.id);
|
|
139
|
+
lastSendTime.delete(progressKey);
|
|
140
|
+
console.error(`[Slack] Failed to update to in-progress:`, error);
|
|
141
|
+
} finally {
|
|
142
|
+
pendingSends.delete(progressKey);
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
55
147
|
const lastSentProgress = sentProgress.get(task.id);
|
|
56
148
|
// Only send if progress exists and is different from last sent
|
|
57
149
|
if (task.progress && task.progress !== lastSentProgress) {
|
|
@@ -60,8 +152,23 @@ export function startTaskWatcher(intervalMs = 3000): void {
|
|
|
60
152
|
sentProgress.set(task.id, task.progress);
|
|
61
153
|
lastSendTime.set(progressKey, now);
|
|
62
154
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
155
|
+
if (tracked) {
|
|
156
|
+
// Update the existing message in-place via chat.update
|
|
157
|
+
await updateProgressInPlace(task, task.progress, tracked.messageTs);
|
|
158
|
+
console.log(`[Slack] Updated progress in-place for task ${task.id.slice(0, 8)}`);
|
|
159
|
+
} else {
|
|
160
|
+
// No tracked message (e.g., multi-task assignment or server restart)
|
|
161
|
+
// Post a new progress message and track its ts
|
|
162
|
+
const messageTs = await sendProgressUpdate(task, task.progress);
|
|
163
|
+
if (messageTs && task.slackChannelId && task.slackThreadTs) {
|
|
164
|
+
taskMessages.set(task.id, {
|
|
165
|
+
channelId: task.slackChannelId,
|
|
166
|
+
threadTs: task.slackThreadTs,
|
|
167
|
+
messageTs,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
console.log(`[Slack] Sent initial progress for task ${task.id.slice(0, 8)}`);
|
|
171
|
+
}
|
|
65
172
|
} catch (error) {
|
|
66
173
|
// If send fails, clear markers so we can retry
|
|
67
174
|
sentProgress.delete(task.id);
|
|
@@ -88,7 +195,15 @@ export function startTaskWatcher(intervalMs = 3000): void {
|
|
|
88
195
|
notifiedCompletions.set(task.id, now);
|
|
89
196
|
lastSendTime.set(completionKey, now);
|
|
90
197
|
try {
|
|
91
|
-
|
|
198
|
+
const tracked = taskMessages.get(task.id);
|
|
199
|
+
if (tracked) {
|
|
200
|
+
// Channel thread: update the same message to its final state
|
|
201
|
+
await updateToFinal(task, tracked.messageTs);
|
|
202
|
+
taskMessages.delete(task.id);
|
|
203
|
+
} else {
|
|
204
|
+
// DM or untracked: post completion as a new message
|
|
205
|
+
await sendTaskResponse(task);
|
|
206
|
+
}
|
|
92
207
|
// Clean up progress tracking
|
|
93
208
|
sentProgress.delete(task.id);
|
|
94
209
|
console.log(`[Slack] Sent ${task.status} response for task ${task.id.slice(0, 8)}`);
|