@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/handlers.ts
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import type { App } from "@slack/bolt";
|
|
2
2
|
import type { WebClient } from "@slack/web-api";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createTask,
|
|
5
|
+
createTaskExtended,
|
|
6
|
+
getAgentById,
|
|
7
|
+
getAgentWorkingOnThread,
|
|
8
|
+
getLeadAgent,
|
|
9
|
+
getMostRecentTaskInThread,
|
|
10
|
+
getTasksByAgentId,
|
|
11
|
+
} from "../be/db";
|
|
12
|
+
import { resolveTemplate } from "../prompts/resolver";
|
|
13
|
+
import { workflowEventBus } from "../workflows/event-bus";
|
|
14
|
+
import { buildAssignmentSummaryBlocks } from "./blocks";
|
|
15
|
+
import type { SlackFile } from "./files";
|
|
4
16
|
import { extractTaskFromMessage, routeMessage } from "./router";
|
|
17
|
+
// Side-effect import: registers all Slack event templates in the in-memory registry
|
|
18
|
+
import "./templates";
|
|
19
|
+
import { bufferThreadMessage, getBufferMessageCount, instantFlush } from "./thread-buffer";
|
|
20
|
+
import { registerTaskMessage } from "./watcher";
|
|
5
21
|
|
|
6
22
|
// User filtering configuration from environment variables
|
|
7
23
|
const allowedEmailDomains = (process.env.SLACK_ALLOWED_EMAIL_DOMAINS || "")
|
|
@@ -132,6 +148,7 @@ interface MessageEvent {
|
|
|
132
148
|
channel: string;
|
|
133
149
|
ts: string;
|
|
134
150
|
thread_ts?: string;
|
|
151
|
+
files?: SlackFile[];
|
|
135
152
|
}
|
|
136
153
|
|
|
137
154
|
interface ThreadMessage {
|
|
@@ -210,17 +227,43 @@ async function getThreadContext(
|
|
|
210
227
|
}
|
|
211
228
|
}
|
|
212
229
|
|
|
213
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Format a file size in bytes to a human-readable string.
|
|
232
|
+
*/
|
|
233
|
+
export function formatFileSize(bytes: number): string {
|
|
234
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
235
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
236
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
237
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Build a text representation of file attachments for inclusion in messages.
|
|
242
|
+
* Each file is formatted as: [File: filename.ext (mimetype, size) id=FILE_ID]
|
|
243
|
+
*/
|
|
244
|
+
export function buildAttachmentText(files: SlackFile[]): string {
|
|
245
|
+
return files
|
|
246
|
+
.map((f) => `[File: ${f.name} (${f.mimetype}, ${formatFileSize(f.size)}) id=${f.id}]`)
|
|
247
|
+
.join("\n");
|
|
248
|
+
}
|
|
214
249
|
|
|
215
250
|
/**
|
|
216
|
-
*
|
|
251
|
+
* Build the effective message text from the original text and any file attachments.
|
|
252
|
+
* - Text only: returns the text as-is
|
|
253
|
+
* - Files only: returns the attachment metadata
|
|
254
|
+
* - Both: returns text followed by attachment metadata
|
|
217
255
|
*/
|
|
218
|
-
function
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
256
|
+
export function buildEffectiveText(text: string | undefined, files?: SlackFile[]): string {
|
|
257
|
+
const hasText = !!text?.trim();
|
|
258
|
+
const hasFiles = files && files.length > 0;
|
|
259
|
+
|
|
260
|
+
if (hasText && hasFiles) {
|
|
261
|
+
return `${text}\n\n${buildAttachmentText(files)}`;
|
|
222
262
|
}
|
|
223
|
-
|
|
263
|
+
if (hasFiles) {
|
|
264
|
+
return buildAttachmentText(files);
|
|
265
|
+
}
|
|
266
|
+
return text || "";
|
|
224
267
|
}
|
|
225
268
|
|
|
226
269
|
// Message deduplication (prevents duplicate event processing)
|
|
@@ -275,7 +318,11 @@ export function registerMessageHandler(app: App): void {
|
|
|
275
318
|
}
|
|
276
319
|
|
|
277
320
|
const msg = event as MessageEvent;
|
|
278
|
-
|
|
321
|
+
const hasText = !!msg.text?.trim();
|
|
322
|
+
const hasFiles = !!(msg.files && msg.files.length > 0);
|
|
323
|
+
|
|
324
|
+
// Require either text or files, and always require a user
|
|
325
|
+
if ((!hasText && !hasFiles) || !msg.user) return;
|
|
279
326
|
|
|
280
327
|
// Deduplicate events (Slack can send same event twice)
|
|
281
328
|
const messageKey = `${msg.channel}:${msg.ts}`;
|
|
@@ -289,29 +336,141 @@ export function registerMessageHandler(app: App): void {
|
|
|
289
336
|
return;
|
|
290
337
|
}
|
|
291
338
|
|
|
339
|
+
// Emit workflow trigger event for Slack messages
|
|
340
|
+
workflowEventBus.emit("slack.message", {
|
|
341
|
+
channel: msg.channel,
|
|
342
|
+
text: msg.text,
|
|
343
|
+
user: msg.user,
|
|
344
|
+
ts: msg.ts,
|
|
345
|
+
threadTs: msg.thread_ts,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Build effective text that includes attachment metadata
|
|
349
|
+
const effectiveText = buildEffectiveText(msg.text, msg.files);
|
|
350
|
+
|
|
292
351
|
// Get bot's user ID
|
|
293
352
|
const authResult = await client.auth.test();
|
|
294
353
|
const botUserId = authResult.user_id as string;
|
|
295
354
|
|
|
296
|
-
// Check if bot was mentioned
|
|
297
|
-
const botMentioned = msg.text
|
|
355
|
+
// Check if bot was mentioned (in original text only)
|
|
356
|
+
const botMentioned = !!msg.text?.includes(`<@${botUserId}>`);
|
|
357
|
+
|
|
358
|
+
// ADDITIVE_SLACK: Check for !now command in threads
|
|
359
|
+
const additiveSlack = process.env.ADDITIVE_SLACK === "true";
|
|
360
|
+
if (additiveSlack && msg.thread_ts) {
|
|
361
|
+
const stripped = effectiveText.replace(/<@[A-Z0-9]+>/g, "").trim();
|
|
362
|
+
if (stripped.startsWith("!now")) {
|
|
363
|
+
const nowMessage = stripped.replace(/^!now\s*/, "").trim();
|
|
364
|
+
const threadKey = `${msg.channel}:${msg.thread_ts}`;
|
|
365
|
+
|
|
366
|
+
console.log(
|
|
367
|
+
`[Slack] !now command detected in thread ${threadKey}${nowMessage ? ` with message: "${nowMessage}"` : ""}`,
|
|
368
|
+
);
|
|
298
369
|
|
|
299
|
-
|
|
370
|
+
if (nowMessage) {
|
|
371
|
+
bufferThreadMessage(msg.channel, msg.thread_ts, nowMessage, msg.user, msg.ts);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Instant flush — no dependency
|
|
375
|
+
await instantFlush(threadKey);
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
await client.reactions.add({ channel: msg.channel, name: "zap", timestamp: msg.ts });
|
|
379
|
+
} catch (e) {
|
|
380
|
+
console.log(`[Slack] Reaction failed: ${e instanceof Error ? e.message : e}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ADDITIVE_SLACK: Buffer non-mention thread messages
|
|
388
|
+
if (additiveSlack && !botMentioned && msg.thread_ts) {
|
|
389
|
+
// Check if this thread has any swarm activity (existing tasks)
|
|
390
|
+
const hasSwarmActivity = getAgentWorkingOnThread(msg.channel, msg.thread_ts) !== null;
|
|
391
|
+
|
|
392
|
+
if (hasSwarmActivity) {
|
|
393
|
+
const threadKey = `${msg.channel}:${msg.thread_ts}`;
|
|
394
|
+
bufferThreadMessage(msg.channel, msg.thread_ts, effectiveText, msg.user, msg.ts);
|
|
395
|
+
|
|
396
|
+
// Slack feedback: react with :eyes: on first buffer, :heavy_plus_sign: on appends
|
|
397
|
+
const count = getBufferMessageCount(threadKey);
|
|
398
|
+
console.log(
|
|
399
|
+
`[Slack] Additive buffer: ${threadKey} (message #${count}, reaction: ${count === 1 ? "eyes" : "heavy_plus_sign"})`,
|
|
400
|
+
);
|
|
401
|
+
try {
|
|
402
|
+
await client.reactions.add({
|
|
403
|
+
channel: msg.channel,
|
|
404
|
+
name: count === 1 ? "eyes" : "heavy_plus_sign",
|
|
405
|
+
timestamp: msg.ts,
|
|
406
|
+
});
|
|
407
|
+
} catch (e) {
|
|
408
|
+
console.log(`[Slack] Reaction failed: ${e instanceof Error ? e.message : e}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return; // Don't process further — buffer will flush
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Route message to agents (use original text for routing to preserve mention/name matching)
|
|
416
|
+
const routingText = msg.text || effectiveText;
|
|
300
417
|
const routingThreadContext = msg.thread_ts
|
|
301
418
|
? { channelId: msg.channel, threadTs: msg.thread_ts }
|
|
302
419
|
: undefined;
|
|
303
|
-
|
|
304
|
-
// Route message to agents
|
|
305
|
-
const matches = routeMessage(msg.text, botUserId, botMentioned, routingThreadContext);
|
|
420
|
+
const matches = routeMessage(routingText, botUserId, botMentioned, routingThreadContext);
|
|
306
421
|
|
|
307
422
|
if (matches.length === 0) {
|
|
308
|
-
|
|
309
|
-
|
|
423
|
+
if (!botMentioned) return;
|
|
424
|
+
|
|
425
|
+
// Bot was mentioned but no online agents matched — queue the request
|
|
426
|
+
if (!checkRateLimit(msg.user)) {
|
|
310
427
|
await say({
|
|
311
|
-
text: ":satellite:
|
|
428
|
+
text: ":satellite: _You're sending too many requests. Please slow down._",
|
|
312
429
|
thread_ts: msg.thread_ts || msg.ts,
|
|
313
430
|
});
|
|
431
|
+
return;
|
|
314
432
|
}
|
|
433
|
+
|
|
434
|
+
const taskDescription = extractTaskFromMessage(effectiveText, botUserId);
|
|
435
|
+
if (!taskDescription) {
|
|
436
|
+
await say({
|
|
437
|
+
text: ":satellite: _Please provide a task description after mentioning an agent._",
|
|
438
|
+
thread_ts: msg.thread_ts || msg.ts,
|
|
439
|
+
});
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const threadTs = msg.thread_ts || msg.ts;
|
|
444
|
+
const threadContext = await getThreadContext(
|
|
445
|
+
client,
|
|
446
|
+
msg.channel,
|
|
447
|
+
msg.thread_ts,
|
|
448
|
+
msg.ts,
|
|
449
|
+
botUserId,
|
|
450
|
+
);
|
|
451
|
+
let fullTaskDescription: string;
|
|
452
|
+
if (threadContext) {
|
|
453
|
+
const ctxResult = resolveTemplate("slack.message.thread_context", {
|
|
454
|
+
thread_messages: threadContext,
|
|
455
|
+
});
|
|
456
|
+
fullTaskDescription = `${ctxResult.text}\n\n${taskDescription}`;
|
|
457
|
+
} else {
|
|
458
|
+
fullTaskDescription = taskDescription;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const lead = getLeadAgent();
|
|
462
|
+
createTaskExtended(fullTaskDescription, {
|
|
463
|
+
agentId: lead?.id,
|
|
464
|
+
source: "slack",
|
|
465
|
+
slackChannelId: msg.channel,
|
|
466
|
+
slackThreadTs: threadTs,
|
|
467
|
+
slackUserId: msg.user,
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
await say({
|
|
471
|
+
text: ":satellite: _No agents are online right now. Your request has been queued and will be processed when agents come back up._",
|
|
472
|
+
thread_ts: threadTs,
|
|
473
|
+
});
|
|
315
474
|
return;
|
|
316
475
|
}
|
|
317
476
|
|
|
@@ -324,8 +483,8 @@ export function registerMessageHandler(app: App): void {
|
|
|
324
483
|
return;
|
|
325
484
|
}
|
|
326
485
|
|
|
327
|
-
// Extract task description
|
|
328
|
-
const taskDescription = extractTaskFromMessage(
|
|
486
|
+
// Extract task description (using effective text which includes attachment metadata)
|
|
487
|
+
const taskDescription = extractTaskFromMessage(effectiveText, botUserId);
|
|
329
488
|
if (!taskDescription) {
|
|
330
489
|
await say({
|
|
331
490
|
text: ":satellite: _Please provide a task description after mentioning an agent._",
|
|
@@ -345,39 +504,41 @@ export function registerMessageHandler(app: App): void {
|
|
|
345
504
|
msg.ts,
|
|
346
505
|
botUserId,
|
|
347
506
|
);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
507
|
+
let fullTaskDescription: string;
|
|
508
|
+
if (threadContext) {
|
|
509
|
+
const ctxResult = resolveTemplate("slack.message.thread_context", {
|
|
510
|
+
thread_messages: threadContext,
|
|
511
|
+
});
|
|
512
|
+
fullTaskDescription = `${ctxResult.text}\n\n${taskDescription}`;
|
|
513
|
+
} else {
|
|
514
|
+
fullTaskDescription = taskDescription;
|
|
515
|
+
}
|
|
516
|
+
const results: {
|
|
517
|
+
assigned: Array<{ agentName: string; taskId: string }>;
|
|
518
|
+
queued: Array<{ agentName: string; taskId: string }>;
|
|
519
|
+
failed: Array<{ agentName: string; reason: string }>;
|
|
520
|
+
} = { assigned: [], queued: [], failed: [] };
|
|
361
521
|
|
|
362
522
|
for (const match of matches) {
|
|
363
523
|
const agent = getAgentById(match.agent.id);
|
|
364
524
|
|
|
365
525
|
if (!agent) {
|
|
366
|
-
results.failed.push(
|
|
526
|
+
results.failed.push({ agentName: match.agent.name, reason: "not found" });
|
|
367
527
|
continue;
|
|
368
528
|
}
|
|
369
529
|
|
|
370
530
|
try {
|
|
371
|
-
|
|
531
|
+
const latestTask = getMostRecentTaskInThread(msg.channel, threadTs);
|
|
372
532
|
if (agent.isLead) {
|
|
373
|
-
|
|
533
|
+
const task = createTaskExtended(fullTaskDescription, {
|
|
534
|
+
agentId: agent.id,
|
|
374
535
|
source: "slack",
|
|
375
536
|
slackChannelId: msg.channel,
|
|
376
537
|
slackThreadTs: threadTs,
|
|
377
538
|
slackUserId: msg.user,
|
|
378
|
-
|
|
539
|
+
parentTaskId: latestTask?.id,
|
|
379
540
|
});
|
|
380
|
-
results.assigned.push(
|
|
541
|
+
results.assigned.push({ agentName: agent.name, taskId: task.id });
|
|
381
542
|
continue;
|
|
382
543
|
}
|
|
383
544
|
|
|
@@ -396,32 +557,46 @@ export function registerMessageHandler(app: App): void {
|
|
|
396
557
|
);
|
|
397
558
|
|
|
398
559
|
if (inProgressInThread) {
|
|
399
|
-
results.queued.push(
|
|
560
|
+
results.queued.push({ agentName: agent.name, taskId: task.id });
|
|
400
561
|
} else {
|
|
401
|
-
results.assigned.push(
|
|
562
|
+
results.assigned.push({ agentName: agent.name, taskId: task.id });
|
|
402
563
|
}
|
|
403
564
|
} catch {
|
|
404
|
-
results.failed.push(
|
|
565
|
+
results.failed.push({ agentName: agent.name, reason: "error" });
|
|
405
566
|
}
|
|
406
567
|
}
|
|
407
568
|
|
|
408
|
-
// Send consolidated summary
|
|
409
|
-
const
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
569
|
+
// Send consolidated summary with Block Kit
|
|
570
|
+
const totalResults = results.assigned.length + results.queued.length + results.failed.length;
|
|
571
|
+
if (totalResults > 0) {
|
|
572
|
+
// Build plain-text fallback
|
|
573
|
+
const parts: string[] = [];
|
|
574
|
+
if (results.assigned.length > 0) {
|
|
575
|
+
const names = results.assigned.map((a) => `${a.agentName}`).join(", ");
|
|
576
|
+
parts.push(`Task assigned to: ${names}`);
|
|
577
|
+
}
|
|
578
|
+
if (results.queued.length > 0) {
|
|
579
|
+
const names = results.queued.map((q) => `${q.agentName}`).join(", ");
|
|
580
|
+
parts.push(`Task queued for: ${names}`);
|
|
581
|
+
}
|
|
582
|
+
if (results.failed.length > 0) {
|
|
583
|
+
const names = results.failed.map((f) => `${f.agentName}`).join(", ");
|
|
584
|
+
parts.push(`Could not assign to: ${names}`);
|
|
585
|
+
}
|
|
419
586
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
587
|
+
const resp = await say({
|
|
588
|
+
text: parts.join(". "),
|
|
589
|
+
blocks: buildAssignmentSummaryBlocks(results),
|
|
423
590
|
thread_ts: msg.thread_ts || msg.ts,
|
|
424
591
|
});
|
|
592
|
+
|
|
593
|
+
// Register the assignment message so the watcher can update it in-place
|
|
594
|
+
// (assignment → progress → completion all in one evolving message)
|
|
595
|
+
if (resp?.ts) {
|
|
596
|
+
for (const { taskId } of results.assigned) {
|
|
597
|
+
registerTaskMessage(taskId, msg.channel, threadTs, resp.ts);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
425
600
|
}
|
|
426
601
|
});
|
|
427
602
|
|
package/src/slack/responses.ts
CHANGED
|
@@ -2,36 +2,18 @@ import type { WebClient } from "@slack/web-api";
|
|
|
2
2
|
import { getAgentById } from "../be/db";
|
|
3
3
|
import type { Agent, AgentTask } from "../types";
|
|
4
4
|
import { getSlackApp } from "./app";
|
|
5
|
+
import {
|
|
6
|
+
buildCancelledBlocks,
|
|
7
|
+
buildCompletedBlocks,
|
|
8
|
+
buildFailedBlocks,
|
|
9
|
+
buildProgressBlocks,
|
|
10
|
+
markdownToSlack,
|
|
11
|
+
} from "./blocks";
|
|
5
12
|
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
// Re-export for backward compatibility
|
|
14
|
+
export { markdownToSlack } from "./blocks";
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
* Convert GitHub-flavored markdown to Slack mrkdwn format.
|
|
11
|
-
*
|
|
12
|
-
* Key differences:
|
|
13
|
-
* - GitHub: **bold**, *italic*, ~~strike~~, [text](url)
|
|
14
|
-
* - Slack: *bold*, _italic_, ~strike~, <url|text>
|
|
15
|
-
*/
|
|
16
|
-
export function markdownToSlack(text: string): string {
|
|
17
|
-
return (
|
|
18
|
-
text
|
|
19
|
-
// Headers to bold (# Header -> *Header*)
|
|
20
|
-
.replace(/^#{1,6}\s+(.+)$/gm, "*$1*")
|
|
21
|
-
// Bold **text** -> *text* (must be before italic)
|
|
22
|
-
.replace(/\*\*(.+?)\*\*/g, "*$1*")
|
|
23
|
-
// Italic *text* -> _text_ (single asterisks, after bold is converted)
|
|
24
|
-
.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "_$1_")
|
|
25
|
-
// Strikethrough ~~text~~ -> ~text~
|
|
26
|
-
.replace(/~~(.+?)~~/g, "~$1~")
|
|
27
|
-
// Links [text](url) -> <url|text>
|
|
28
|
-
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>")
|
|
29
|
-
// Inline code already works the same
|
|
30
|
-
// Bullet points already work the same
|
|
31
|
-
// Remove excessive blank lines
|
|
32
|
-
.replace(/\n{3,}/g, "\n\n")
|
|
33
|
-
);
|
|
34
|
-
}
|
|
16
|
+
const isDev = process.env.ENV === "development";
|
|
35
17
|
|
|
36
18
|
/**
|
|
37
19
|
* Get the display name for an agent, with (dev) prefix if in development mode.
|
|
@@ -40,17 +22,6 @@ function getAgentDisplayName(agent: Agent): string {
|
|
|
40
22
|
return isDev ? `(dev) ${agent.name}` : agent.name;
|
|
41
23
|
}
|
|
42
24
|
|
|
43
|
-
/**
|
|
44
|
-
* Get a link to the task in the dashboard, or just the task ID if no APP_URL.
|
|
45
|
-
*/
|
|
46
|
-
function getTaskLink(taskId: string): string {
|
|
47
|
-
const shortId = taskId.slice(0, 8);
|
|
48
|
-
if (appUrl) {
|
|
49
|
-
return `<${appUrl}?tab=tasks&task=${taskId}&expand=true|\`${shortId}\`>`;
|
|
50
|
-
}
|
|
51
|
-
return `\`${shortId}\``;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
25
|
/**
|
|
55
26
|
* Send a task completion message to Slack with the agent's persona.
|
|
56
27
|
*/
|
|
@@ -72,28 +43,31 @@ export async function sendTaskResponse(task: AgentTask): Promise<boolean> {
|
|
|
72
43
|
}
|
|
73
44
|
|
|
74
45
|
const client = app.client;
|
|
75
|
-
const
|
|
76
|
-
const footer = `_Check the full logs at ${taskLink}_`;
|
|
46
|
+
const agentName = agent.name;
|
|
77
47
|
|
|
78
48
|
try {
|
|
79
49
|
if (task.status === "completed") {
|
|
80
|
-
const output = task.output || "
|
|
50
|
+
const output = task.output || "Task completed.";
|
|
81
51
|
const slackOutput = markdownToSlack(output);
|
|
52
|
+
const blocks = buildCompletedBlocks({ agentName, taskId: task.id, body: slackOutput });
|
|
82
53
|
await sendWithPersona(client, {
|
|
83
54
|
channel: task.slackChannelId,
|
|
84
55
|
thread_ts: task.slackThreadTs,
|
|
85
|
-
text:
|
|
56
|
+
text: slackOutput,
|
|
86
57
|
username: getAgentDisplayName(agent),
|
|
87
58
|
icon_emoji: getAgentEmoji(agent),
|
|
59
|
+
blocks,
|
|
88
60
|
});
|
|
89
61
|
} else if (task.status === "failed") {
|
|
90
62
|
const reason = task.failureReason || "Unknown error";
|
|
63
|
+
const blocks = buildFailedBlocks({ agentName, taskId: task.id, reason });
|
|
91
64
|
await sendWithPersona(client, {
|
|
92
65
|
channel: task.slackChannelId,
|
|
93
66
|
thread_ts: task.slackThreadTs,
|
|
94
|
-
text:
|
|
67
|
+
text: `Task failed: ${reason}`,
|
|
95
68
|
username: getAgentDisplayName(agent),
|
|
96
69
|
icon_emoji: getAgentEmoji(agent),
|
|
70
|
+
blocks,
|
|
97
71
|
});
|
|
98
72
|
}
|
|
99
73
|
|
|
@@ -105,33 +79,110 @@ export async function sendTaskResponse(task: AgentTask): Promise<boolean> {
|
|
|
105
79
|
}
|
|
106
80
|
|
|
107
81
|
/**
|
|
108
|
-
* Send a progress update to Slack.
|
|
82
|
+
* Send a progress update to Slack. Returns the message ts for chat.update tracking.
|
|
109
83
|
*/
|
|
110
|
-
export async function sendProgressUpdate(
|
|
84
|
+
export async function sendProgressUpdate(
|
|
85
|
+
task: AgentTask,
|
|
86
|
+
progress: string,
|
|
87
|
+
): Promise<string | undefined> {
|
|
111
88
|
const app = getSlackApp();
|
|
112
89
|
if (!app || !task.slackChannelId || !task.slackThreadTs) {
|
|
113
|
-
return
|
|
90
|
+
return undefined;
|
|
114
91
|
}
|
|
115
92
|
|
|
116
|
-
if (!task.agentId) return
|
|
93
|
+
if (!task.agentId) return undefined;
|
|
117
94
|
|
|
118
95
|
const agent = getAgentById(task.agentId);
|
|
119
|
-
if (!agent) return
|
|
96
|
+
if (!agent) return undefined;
|
|
120
97
|
|
|
121
|
-
const
|
|
122
|
-
const footer = `_Check progress at ${taskLink}_`;
|
|
98
|
+
const blocks = buildProgressBlocks({ agentName: agent.name, taskId: task.id, progress });
|
|
123
99
|
|
|
124
100
|
try {
|
|
125
|
-
await sendWithPersona(app.client, {
|
|
101
|
+
return await sendWithPersona(app.client, {
|
|
126
102
|
channel: task.slackChannelId,
|
|
127
103
|
thread_ts: task.slackThreadTs,
|
|
128
|
-
text:
|
|
104
|
+
text: progress,
|
|
129
105
|
username: getAgentDisplayName(agent),
|
|
130
106
|
icon_emoji: getAgentEmoji(agent),
|
|
107
|
+
blocks,
|
|
131
108
|
});
|
|
132
|
-
return true;
|
|
133
109
|
} catch (error) {
|
|
134
110
|
console.error(`[Slack] Failed to send progress update:`, error);
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Update an existing progress message in-place via chat.update.
|
|
117
|
+
*/
|
|
118
|
+
export async function updateProgressInPlace(
|
|
119
|
+
task: AgentTask,
|
|
120
|
+
progress: string,
|
|
121
|
+
messageTs: string,
|
|
122
|
+
): Promise<boolean> {
|
|
123
|
+
const app = getSlackApp();
|
|
124
|
+
if (!app || !task.slackChannelId || !task.agentId) return false;
|
|
125
|
+
|
|
126
|
+
const agent = getAgentById(task.agentId);
|
|
127
|
+
if (!agent) return false;
|
|
128
|
+
|
|
129
|
+
const blocks = buildProgressBlocks({ agentName: agent.name, taskId: task.id, progress });
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
await app.client.chat.update({
|
|
133
|
+
channel: task.slackChannelId,
|
|
134
|
+
ts: messageTs,
|
|
135
|
+
text: progress,
|
|
136
|
+
// biome-ignore lint/suspicious/noExplicitAny: Block Kit objects
|
|
137
|
+
blocks: blocks as any,
|
|
138
|
+
});
|
|
139
|
+
return true;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(`[Slack] Failed to update progress in-place:`, error);
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Update the task message to its final state (completed/failed) via chat.update.
|
|
148
|
+
* Uses full blocks (not compact) since this is the only message for the task.
|
|
149
|
+
*/
|
|
150
|
+
export async function updateToFinal(task: AgentTask, messageTs: string): Promise<boolean> {
|
|
151
|
+
const app = getSlackApp();
|
|
152
|
+
if (!app || !task.slackChannelId || !task.agentId) return false;
|
|
153
|
+
|
|
154
|
+
const agent = getAgentById(task.agentId);
|
|
155
|
+
if (!agent) return false;
|
|
156
|
+
|
|
157
|
+
const agentName = agent.name;
|
|
158
|
+
let blocks: unknown[];
|
|
159
|
+
let text: string;
|
|
160
|
+
|
|
161
|
+
if (task.status === "completed") {
|
|
162
|
+
const output = task.output || "Task completed.";
|
|
163
|
+
const slackOutput = markdownToSlack(output);
|
|
164
|
+
blocks = buildCompletedBlocks({ agentName, taskId: task.id, body: slackOutput });
|
|
165
|
+
text = slackOutput;
|
|
166
|
+
} else if (task.status === "cancelled") {
|
|
167
|
+
blocks = buildCancelledBlocks({ agentName, taskId: task.id });
|
|
168
|
+
text = "Task cancelled";
|
|
169
|
+
} else {
|
|
170
|
+
const reason = task.failureReason || "Unknown error";
|
|
171
|
+
blocks = buildFailedBlocks({ agentName, taskId: task.id, reason });
|
|
172
|
+
text = `Task failed: ${reason}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
await app.client.chat.update({
|
|
177
|
+
channel: task.slackChannelId,
|
|
178
|
+
ts: messageTs,
|
|
179
|
+
text,
|
|
180
|
+
// biome-ignore lint/suspicious/noExplicitAny: Block Kit objects
|
|
181
|
+
blocks: blocks as any,
|
|
182
|
+
});
|
|
183
|
+
return true;
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error(`[Slack] Failed to update task message to final state:`, error);
|
|
135
186
|
return false;
|
|
136
187
|
}
|
|
137
188
|
}
|
|
@@ -144,24 +195,26 @@ async function sendWithPersona(
|
|
|
144
195
|
text: string;
|
|
145
196
|
username: string;
|
|
146
197
|
icon_emoji: string;
|
|
198
|
+
blocks?: unknown[];
|
|
147
199
|
},
|
|
148
|
-
): Promise<
|
|
149
|
-
|
|
200
|
+
): Promise<string | undefined> {
|
|
201
|
+
const blocks = options.blocks ?? [
|
|
202
|
+
{ type: "section", text: { type: "mrkdwn", text: options.text } },
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
// Skip persona overrides in DM channels (assistant threads use the app's own identity)
|
|
206
|
+
const isDM = options.channel.startsWith("D");
|
|
207
|
+
|
|
208
|
+
const result = await client.chat.postMessage({
|
|
150
209
|
channel: options.channel,
|
|
151
210
|
thread_ts: options.thread_ts,
|
|
152
211
|
text: options.text, // Fallback for notifications
|
|
153
|
-
username: options.username,
|
|
154
|
-
|
|
155
|
-
blocks:
|
|
156
|
-
{
|
|
157
|
-
type: "section",
|
|
158
|
-
text: {
|
|
159
|
-
type: "mrkdwn",
|
|
160
|
-
text: options.text,
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
],
|
|
212
|
+
...(isDM ? {} : { username: options.username, icon_emoji: options.icon_emoji }),
|
|
213
|
+
// biome-ignore lint/suspicious/noExplicitAny: Block Kit objects are typed as plain JSON
|
|
214
|
+
blocks: blocks as any,
|
|
164
215
|
});
|
|
216
|
+
|
|
217
|
+
return result.ts;
|
|
165
218
|
}
|
|
166
219
|
|
|
167
220
|
function getAgentEmoji(agent: Agent): string {
|